]> git.saurik.com Git - apple/libinfo.git/blobdiff - lookup.subproj/lu_service.c
Libinfo-129.4.tar.gz
[apple/libinfo.git] / lookup.subproj / lu_service.c
index 4dc60e59dbafe0d3f5e35cb13f09f8085685219e..7d7367790d9c86b1afd0177e7e47c7f5dd6bf06b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
 #include <mach/mach.h>
 #include <stdio.h>
 #include <string.h>
-#include "lookup.h"
 #include <rpc/types.h>
 #include <rpc/xdr.h>
-#include "_lu_types.h"
 #include <netdb.h>
+#include <netinet/in.h>
+#include <pthread.h>
+
+#include "_lu_types.h"
+#include "lookup.h"
 #include "lu_utils.h"
-#import <netinet/in.h>
+
+static pthread_mutex_t _service_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#define S_GET_NAME 1
+#define S_GET_PORT 2
+#define S_GET_ENT 3
 
 extern struct servent *_old_getservbyport();
 extern struct servent *_old_getservbyname();
@@ -44,285 +52,503 @@ extern void _old_setservent();
 extern void _old_endservent();
 extern void _old_setservfile();
 
-static lookup_state s_state = LOOKUP_CACHE;
-static struct servent global_s;
-static int global_free = 1;
-static char *s_data = NULL;
-static unsigned s_datalen;
-static int s_nentries;
-static int s_start = 1;
-static XDR s_xdr;
-
 static void
-freeold(void)
+free_service_data(struct servent *s)
 {
        char **aliases;
 
-       if (global_free == 1) return;
+       if (s == NULL) return;
 
-       if (global_s.s_name != NULL) free(global_s.s_name);
-       global_s.s_name = NULL;
+       if (s->s_name != NULL) free(s->s_name);
+       if (s->s_proto != NULL) free(s->s_proto);
 
-       if (global_s.s_proto != NULL) free(global_s.s_proto);
-       global_s.s_proto = NULL;
-
-       aliases = global_s.s_aliases;
+       aliases = s->s_aliases;
        if (aliases != NULL)
        {
                while (*aliases != NULL) free(*aliases++);
-               free(global_s.s_aliases);
-               global_s.s_aliases = NULL;
+               free(s->s_aliases);
        }
+}
 
-       global_free = 1;
+static void
+free_service(struct servent *s)
+{
+       if (s == NULL) return;
+       free_service_data(s);
+       free(s);
 }
 
 static void
-convert_s(_lu_servent *lu_s)
+free_lu_thread_info_service(void *x)
+{
+       struct lu_thread_info *tdata;
+
+       if (x == NULL) return;
+
+       tdata = (struct lu_thread_info *)x;
+       
+       if (tdata->lu_entry != NULL)
+       {
+               free_service((struct servent *)tdata->lu_entry);
+               tdata->lu_entry = NULL;
+       }
+
+       _lu_data_free_vm_xdr(tdata);
+
+       free(tdata);
+}
+
+static struct servent *
+extract_service(XDR *xdr, const char *proto)
+{
+       struct servent *s;
+       int i, j, nvals, nkeys, status;
+       char *key, **vals;
+
+       if (xdr == NULL) return NULL;
+
+       if (!xdr_int(xdr, &nkeys)) return NULL;
+
+       s = (struct servent *)calloc(1, sizeof(struct servent));
+
+       for (i = 0; i < nkeys; i++)
+       {
+               key = NULL;
+               vals = NULL;
+               nvals = 0;
+
+               status = _lu_xdr_attribute(xdr, &key, &vals, &nvals);
+               if (status < 0)
+               {
+                       free_service(s);
+                       return NULL;
+               }
+
+               if (nvals == 0)
+               {
+                       free(key);
+                       continue;
+               }
+
+               j = 0;
+
+               if ((s->s_name == NULL) && (!strcmp("name", key)))
+               {
+                       s->s_name = vals[0];
+                       if (nvals > 1)
+                       {
+                               s->s_aliases = (char **)calloc(nvals, sizeof(char *));
+                               for (j = 1; j < nvals; j++) s->s_aliases[j-1] = vals[j];
+                       }
+                       j = nvals;
+               }               
+               else if ((s->s_proto == NULL) && (!strcmp("protocol", key)))
+               {
+                       if ((proto == NULL) || (proto[0] == '\0'))
+                       {
+                               s->s_proto = vals[0];
+                               j = 1;
+                       }
+                       else
+                       {
+                               s->s_proto = strdup(proto);
+                       }
+               }
+               else if ((s->s_port == 0) && (!strcmp("port", key)))
+               {
+                       s->s_port = htons(atoi(vals[0]));
+               }
+               
+               free(key);
+               if (vals != NULL)
+               {
+                       for (; j < nvals; j++) free(vals[j]);
+                       free(vals);
+               }
+       }
+
+       if (s->s_name == NULL) s->s_name = strdup("");
+       if (s->s_proto == NULL) s->s_proto = strdup("");
+       if (s->s_aliases == NULL) s->s_aliases = (char **)calloc(1, sizeof(char *));
+
+       return s;
+}
+
+static struct servent *
+copy_service(struct servent *in)
 {
        int i, len;
+       struct servent *s;
 
-       freeold();
+       if (in == NULL) return NULL;
 
-       global_s.s_name = strdup(lu_s->s_names.s_names_val[0]);
+       s = (struct servent *)calloc(1, sizeof(struct servent));
 
-       len = lu_s->s_names.s_names_len - 1;
-       global_s.s_aliases = (char **)malloc((len + 1) * sizeof(char *));
+       s->s_name = LU_COPY_STRING(in->s_name);
 
+       len = 0;
+       if (in->s_aliases != NULL)
+       {
+               for (len = 0; in->s_aliases[len] != NULL; len++);
+       }
+
+       s->s_aliases = (char **)calloc(len + 1, sizeof(char *));
        for (i = 0; i < len; i++)
        {
-               global_s.s_aliases[i] = strdup(lu_s->s_names.s_names_val[i+1]);
+               s->s_aliases[i] = strdup(in->s_aliases[i]);
+       }
+
+       s->s_proto = LU_COPY_STRING(in->s_proto);
+       s->s_port = in->s_port;
+
+       return s;
+}
+
+static void
+recycle_service(struct lu_thread_info *tdata, struct servent *in)
+{
+       struct servent *s;
+
+       if (tdata == NULL) return;
+       s = (struct servent *)tdata->lu_entry;
+
+       if (in == NULL)
+       {
+               free_service(s);
+               tdata->lu_entry = NULL;
+       }
+
+       if (tdata->lu_entry == NULL)
+       {
+               tdata->lu_entry = in;
+               return;
        }
 
-       global_s.s_aliases[len] = NULL;
+       free_service_data(s);
 
-       if (lu_s->s_proto != NULL) global_s.s_proto = strdup(lu_s->s_proto);
-       global_s.s_port = lu_s->s_port;
+       s->s_name = in->s_name;
+       s->s_aliases = in->s_aliases;
+       s->s_proto = in->s_proto;
+       s->s_port = in->s_port;
 
-       global_free = 0;
+       free(in);
 }
 
 static struct servent *
 lu_getservbyport(int port, const char *proto)
 {
-       unsigned datalen;
-       _lu_servent_ptr lu_s;
-       XDR xdr;
+       struct servent *s;
+       unsigned int datalen;
+       XDR outxdr, inxdr;
        static int proc = -1;
        char output_buf[_LU_MAXLUSTRLEN + 3 * BYTES_PER_XDR_UNIT];
-       unit lookup_buf[MAX_INLINE_UNITS];
-       XDR outxdr;
+       char *lookup_buf;
+       int count;
 
        if (proc < 0)
        {
                if (_lookup_link(_lu_port, "getservbyport", &proc) != KERN_SUCCESS)
                {
-                       return (NULL);
+                       return NULL;
                }
        }
 
        /* Encode NULL for xmission to lookupd. */
-       if (!proto) proto = ""; 
+       if (proto == NULL) proto = "";  
 
        xdrmem_create(&outxdr, output_buf, sizeof(output_buf), XDR_ENCODE);
-       if (!xdr_int(&outxdr, &port) || !xdr__lu_string(&outxdr, &proto))
+       if (!xdr_int(&outxdr, &port) || !xdr__lu_string(&outxdr, (_lu_string *)&proto))
        {
                xdr_destroy(&outxdr);
-               return (NULL);
+               return NULL;
        }
 
-       datalen = MAX_INLINE_UNITS;
-       if (_lookup_one(_lu_port, proc, (unit *)output_buf, 
-               xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT,  lookup_buf, &datalen)
+       datalen = 0;
+       lookup_buf = NULL;
+
+       if (_lookup_all(_lu_port, proc, (unit *)output_buf, 
+               xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, &lookup_buf, &datalen)
                != KERN_SUCCESS)
        {
                xdr_destroy(&outxdr);
-               return (NULL);
+               return NULL;
        }
 
        xdr_destroy(&outxdr);
 
        datalen *= BYTES_PER_XDR_UNIT;
-       xdrmem_create(&xdr, lookup_buf, datalen, XDR_DECODE);
-       lu_s = NULL;
-       if (!xdr__lu_servent_ptr(&xdr, &lu_s) || (lu_s == NULL))
+       if ((lookup_buf == NULL) || (datalen == 0)) return NULL;
+
+       xdrmem_create(&inxdr, lookup_buf, datalen, XDR_DECODE);
+
+       count = 0;
+       if (!xdr_int(&inxdr, &count))
+       {
+               xdr_destroy(&inxdr);
+               vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
+               return NULL;
+       }
+
+       if (count == 0)
        {
-               xdr_destroy(&xdr);
-               return (NULL);
+               xdr_destroy(&inxdr);
+               vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
+               return NULL;
        }
 
-       xdr_destroy(&xdr);
+       /*
+        * lookupd will only send back a reply for a service with the protocol specified
+        * if it finds a match.  We pass the protocol name to extract_service, which
+        * copies the requested protocol name into the returned servent.  This is a
+        * bit of a kludge, but since NetInfo / lookupd treat services as single entities
+        * with multiple protocols, we are forced to do some special-case handling. 
+        */
+       s = extract_service(&inxdr, proto);
+       xdr_destroy(&inxdr);
+       vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
 
-       convert_s(lu_s);
-       xdr_free(xdr__lu_servent_ptr, &lu_s);
-       return (&global_s);
+       return s;
 }
 
 static struct servent *
 lu_getservbyname(const char *name, const char *proto)
 {
-       unsigned datalen;
-       unit lookup_buf[MAX_INLINE_UNITS];
+       struct servent *s;
+       unsigned int datalen;
+       char *lookup_buf;
        char output_buf[2 * (_LU_MAXLUSTRLEN + BYTES_PER_XDR_UNIT)];
-       XDR outxdr;
-       XDR inxdr;
-       _lu_servent_ptr lu_s;
+       XDR outxdr, inxdr;
        static int proc = -1;
+       int count;
 
        if (proc < 0)
        {
                if (_lookup_link(_lu_port, "getservbyname", &proc) != KERN_SUCCESS)
                {
-                   return (NULL);
+                   return NULL;
                }
        }
 
        /* Encode NULL for xmission to lookupd. */
-       if (!proto) proto = "";
+       if (proto == NULL) proto = "";
 
        xdrmem_create(&outxdr, output_buf, sizeof(output_buf), XDR_ENCODE);
-       if (!xdr__lu_string(&outxdr, &name) || !xdr__lu_string(&outxdr, &proto))
+       if (!xdr__lu_string(&outxdr, (_lu_string *)&name) ||
+           !xdr__lu_string(&outxdr, (_lu_string *)&proto))
        {
                xdr_destroy(&outxdr);
-               return (NULL);
+               return NULL;
        }
 
-       datalen = MAX_INLINE_UNITS;
-       if (_lookup_one(_lu_port, proc, (unit *)output_buf,
-               xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, lookup_buf, &datalen)
+       datalen = 0;
+       lookup_buf = NULL;
+
+       if (_lookup_all(_lu_port, proc, (unit *)output_buf,
+               xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, &lookup_buf, &datalen)
                != KERN_SUCCESS)
        {
                xdr_destroy(&outxdr);
-               return (NULL);
+               return NULL;
        }
 
        xdr_destroy(&outxdr);
 
        datalen *= BYTES_PER_XDR_UNIT;
-       xdrmem_create(&inxdr, lookup_buf, datalen,
-               XDR_DECODE);
-       lu_s = NULL;
-       if (!xdr__lu_servent_ptr(&inxdr, &lu_s) || (lu_s == NULL))
+       if ((lookup_buf == NULL) || (datalen == 0)) return NULL;
+
+       xdrmem_create(&inxdr, lookup_buf, datalen, XDR_DECODE);
+
+       count = 0;
+       if (!xdr_int(&inxdr, &count))
+       {
+               xdr_destroy(&inxdr);
+               vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
+               return NULL;
+       }
+
+       if (count == 0)
        {
                xdr_destroy(&inxdr);
-               return (NULL);
+               vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
+               return NULL;
        }
 
+       /*
+        * lookupd will only send back a reply for a service with the protocol specified
+        * if it finds a match.  We pass the protocol name to extract_service, which
+        * copies the requested protocol name into the returned servent.  This is a
+        * bit of a kludge, but since NetInfo / lookupd treat services as single entities
+        * with multiple protocols, we are forced to do some special-case handling. 
+        */
+       s = extract_service(&inxdr, proto);
        xdr_destroy(&inxdr);
+       vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
 
-       convert_s(lu_s);
-       xdr_free(xdr__lu_servent_ptr, &lu_s);
-       return (&global_s);
+       return s;
 }
 
 static void
 lu_endservent()
 {
-       s_nentries = 0;
-       if (s_data != NULL)
-       {
-               freeold();
-               vm_deallocate(mach_task_self(), (vm_address_t)s_data, s_datalen);
-               s_data = NULL;
-       }
+       struct lu_thread_info *tdata;
+
+       tdata = _lu_data_create_key(_lu_data_key_service, free_lu_thread_info_service);
+       _lu_data_free_vm_xdr(tdata);
 }
 
 static void
 lu_setservent()
 {
        lu_endservent();
-       s_start = 1;
 }
 
 static struct servent *
 lu_getservent()
 {
+       struct servent *s;
        static int proc = -1;
-       _lu_servent lu_s;
+       struct lu_thread_info *tdata;
 
-       if (s_start == 1)
+       tdata = _lu_data_create_key(_lu_data_key_service, free_lu_thread_info_service);
+       if (tdata == NULL)
+       {
+               tdata = (struct lu_thread_info *)calloc(1, sizeof(struct lu_thread_info));
+               _lu_data_set_key(_lu_data_key_service, tdata);
+       }
+\r
+       if (tdata->lu_vm == NULL)
        {
-               s_start = 0;
-
                if (proc < 0)
                {
                        if (_lookup_link(_lu_port, "getservent", &proc) != KERN_SUCCESS)
                        {
                                lu_endservent();
-                               return (NULL);
+                               return NULL;
                        }
                }
 
-               if (_lookup_all(_lu_port, proc, NULL, 0, &s_data, &s_datalen)
-                       != KERN_SUCCESS)
+               if (_lookup_all(_lu_port, proc, NULL, 0, &(tdata->lu_vm), &(tdata->lu_vm_length)) != KERN_SUCCESS)
                {
                        lu_endservent();
-                       return (NULL);
+                       return NULL;
                }
 
-#ifdef NOTDEF
-/* NOTDEF because OOL buffers are counted in bytes with untyped IPC */
-               s_datalen *= BYTES_PER_XDR_UNIT;
-#endif
+               /* mig stubs measure size in words (4 bytes) */
+               tdata->lu_vm_length *= 4;
+
+               if (tdata->lu_xdr != NULL)
+               {
+                       xdr_destroy(tdata->lu_xdr);
+                       free(tdata->lu_xdr);
+               }
+               tdata->lu_xdr = (XDR *)calloc(1, sizeof(XDR));
 
-               xdrmem_create(&s_xdr, s_data, s_datalen,
-                       XDR_DECODE);
-               if (!xdr_int(&s_xdr, &s_nentries))
+               xdrmem_create(tdata->lu_xdr, tdata->lu_vm, tdata->lu_vm_length, XDR_DECODE);
+               if (!xdr_int(tdata->lu_xdr, &tdata->lu_vm_cursor))
                {
-                       xdr_destroy(&s_xdr);
                        lu_endservent();
-                       return (NULL);
+                       return NULL;
                }
        }
 
-       if (s_nentries == 0)
+       if (tdata->lu_vm_cursor == 0)
        {
-               xdr_destroy(&s_xdr);
                lu_endservent();
-               return (NULL);
+               return NULL;
        }
 
-       bzero(&lu_s, sizeof(lu_s));
-       if (!xdr__lu_servent(&s_xdr, &lu_s))
+       s = extract_service(tdata->lu_xdr, NULL);
+       if (s == NULL)
        {
-               xdr_destroy(&s_xdr);
                lu_endservent();
-               return (NULL);
+               return NULL;
+       }
+
+       tdata->lu_vm_cursor--;
+       
+       return s;
+}
+
+static struct servent *
+getserv(const char *name, const char *proto, int port, int source)
+{
+       struct servent *res = NULL;
+       struct lu_thread_info *tdata;
+
+       tdata = _lu_data_create_key(_lu_data_key_service, free_lu_thread_info_service);
+       if (tdata == NULL)
+       {
+               tdata = (struct lu_thread_info *)calloc(1, sizeof(struct lu_thread_info));
+               _lu_data_set_key(_lu_data_key_service, tdata);
+       }
+
+       if (_lu_running())
+       {
+               switch (source)
+               {
+                       case S_GET_NAME:
+                               res = lu_getservbyname(name, proto);
+                               break;
+                       case S_GET_PORT:
+                               res = lu_getservbyport(port, proto);
+                               break;
+                       case S_GET_ENT:
+                               res = lu_getservent();
+                               break;
+                       default: res = NULL;
+               }
+       }
+       else
+       {
+               pthread_mutex_lock(&_service_lock);
+               switch (source)
+               {
+                       case S_GET_NAME:
+                               res = copy_service(_old_getservbyname(name, proto));
+                               break;
+                       case S_GET_PORT:
+                               res = copy_service(_old_getservbyport(port, proto));
+                               break;
+                       case S_GET_ENT:
+                               res = copy_service(_old_getservent());
+                               break;
+                       default: res = NULL;
+               }
+               pthread_mutex_unlock(&_service_lock);
        }
 
-       s_nentries--;
-       convert_s(&lu_s);
-       xdr_free(xdr__lu_servent, &lu_s);
-       return (&global_s);
+       recycle_service(tdata, res);
+       return (struct servent *)tdata->lu_entry;
 }
 
 struct servent *
 getservbyport(int port, const char *proto)
 {
-       LOOKUP2(lu_getservbyport, _old_getservbyport, port, proto, struct servent);
+       return getserv(NULL, proto, port, S_GET_PORT);
 }
 
 struct servent *
 getservbyname(const char *name, const char *proto)
 {
-       LOOKUP2(lu_getservbyname, _old_getservbyname, name, proto,
-               struct servent);
+       return getserv(name, proto, 0, S_GET_NAME);
 }
 
 struct servent *
 getservent(void)
 {
-       GETENT(lu_getservent, _old_getservent, &s_state, struct servent);
+       return getserv(NULL, NULL, 0, S_GET_ENT);
 }
 
 void
 setservent(int stayopen)
 {
-       SETSTATE(lu_setservent, _old_setservent, &s_state, stayopen);
+       if (_lu_running()) lu_setservent();
+       else _old_setservent();
 }
 
 void
 endservent(void)
 {
-       UNSETSTATE(lu_endservent, _old_endservent, &s_state);
+       if (_lu_running()) lu_endservent();
+       else _old_endservent();
 }