Remove wrong getvfsbytype.3 MLINK. The manpage doesn't document it.
[dragonfly.git] / lib / libc / rpc / getnetconfig.c
1 /*-
2  * Copyright (c) 2009, Sun Microsystems, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without 
6  * modification, are permitted provided that the following conditions are met:
7  * - Redistributions of source code must retain the above copyright notice, 
8  *   this list of conditions and the following disclaimer.
9  * - Redistributions in binary form must reproduce the above copyright notice, 
10  *   this list of conditions and the following disclaimer in the documentation 
11  *   and/or other materials provided with the distribution.
12  * - Neither the name of Sun Microsystems, Inc. nor the names of its 
13  *   contributors may be used to endorse or promote products derived 
14  *   from this software without specific prior written permission.
15  * 
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
26  * POSSIBILITY OF SUCH DAMAGE.
27  *
28  * @(#)getnetconfig.c   1.12 91/12/19 SMI
29  * $NetBSD: getnetconfig.c,v 1.3 2000/07/06 03:10:34 christos Exp $
30  * $FreeBSD: src/lib/libc/rpc/getnetconfig.c,v 1.14 2007/09/20 22:35:24 matteo Exp $
31  */
32
33 /*
34  * Copyright (c) 1989 by Sun Microsystems, Inc.
35  */
36
37 #include "namespace.h"
38 #include "reentrant.h"
39 #include <stdio.h>
40 #include <errno.h>
41 #include <netconfig.h>
42 #include <stddef.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <rpc/rpc.h>
46 #include <unistd.h>
47 #include "un-namespace.h"
48 #include "rpc_com.h"
49
50 /*
51  * The five library routines in this file provide application access to the
52  * system network configuration database, /etc/netconfig.  In addition to the
53  * netconfig database and the routines for accessing it, the environment
54  * variable NETPATH and its corresponding routines in getnetpath.c may also be
55  * used to specify the network transport to be used.
56  */
57
58
59 /*
60  * netconfig errors
61  */
62
63 #define NC_NONETCONFIG  ENOENT
64 #define NC_NOMEM        ENOMEM
65 #define NC_NOTINIT      EINVAL      /* setnetconfig was not called first */
66 #define NC_BADFILE      EBADF       /* format for netconfig file is bad */
67 #define NC_NOTFOUND     ENOPROTOOPT /* specified netid was not found */
68
69 /*
70  * semantics as strings (should be in netconfig.h)
71  */
72 #define NC_TPI_CLTS_S       "tpi_clts"
73 #define NC_TPI_COTS_S       "tpi_cots"
74 #define NC_TPI_COTS_ORD_S   "tpi_cots_ord"
75 #define NC_TPI_RAW_S        "tpi_raw"
76
77 /*
78  * flags as characters (also should be in netconfig.h)
79  */
80 #define NC_NOFLAG_C     '-'
81 #define NC_VISIBLE_C    'v'
82 #define NC_BROADCAST_C  'b'
83
84 /*
85  * Character used to indicate there is no name-to-address lookup library
86  */
87 #define NC_NOLOOKUP     "-"
88
89 static const char * const _nc_errors[] = {
90     "Netconfig database not found",
91     "Not enough memory",
92     "Not initialized",
93     "Netconfig database has invalid format",
94     "Netid not found in netconfig database"
95 };
96
97 struct netconfig_info {
98     int         eof;    /* all entries has been read */
99     int         ref;    /* # of times setnetconfig() has been called */
100     struct netconfig_list       *head;  /* head of the list */
101     struct netconfig_list       *tail;  /* last of the list */
102 };
103
104 struct netconfig_list {
105     char                        *linep; /* hold line read from netconfig */
106     struct netconfig            *ncp;
107     struct netconfig_list       *next;
108 };
109
110 struct netconfig_vars {
111     int   valid;        /* token that indicates a valid netconfig_vars */
112     int   flag;         /* first time flag */
113     struct netconfig_list *nc_configs;  /* pointer to the current netconfig entry */
114 };
115
116 #define NC_VALID        0xfeed
117 #define NC_STORAGE      0xf00d
118 #define NC_INVALID      0
119
120
121 static int *__nc_error(void);
122 static int parse_ncp(char *, struct netconfig *);
123 static struct netconfig *dup_ncp(struct netconfig *);
124
125
126 static FILE *nc_file;           /* for netconfig db */
127 static struct netconfig_info    ni = { 0, 0, NULL, NULL};
128
129 #define MAXNETCONFIGLINE    1000
130
131 static int *
132 __nc_error(void)
133 {
134         static pthread_mutex_t nc_lock = PTHREAD_MUTEX_INITIALIZER;
135         static thread_key_t nc_key = 0;
136         static int nc_error = 0;
137         int error, *nc_addr;
138
139         /*
140          * Use the static `nc_error' if we are the main thread
141          * (including non-threaded programs), or if an allocation
142          * fails.
143          */
144         if (thr_main())
145                 return (&nc_error);
146         if (nc_key == 0) {
147                 error = 0;
148                 mutex_lock(&nc_lock);
149                 if (nc_key == 0)
150                         error = thr_keycreate(&nc_key, free);
151                 mutex_unlock(&nc_lock);
152                 if (error)
153                         return (&nc_error);
154         }
155         if ((nc_addr = (int *)thr_getspecific(nc_key)) == NULL) {
156                 nc_addr = (int *)malloc(sizeof (int));
157                 if (thr_setspecific(nc_key, (void *) nc_addr) != 0) {
158                         if (nc_addr)
159                                 free(nc_addr);
160                         return (&nc_error);
161                 }
162                 *nc_addr = 0;
163         }
164         return (nc_addr);
165 }
166
167 #define nc_error        (*(__nc_error()))
168 /*
169  * A call to setnetconfig() establishes a /etc/netconfig "session".  A session
170  * "handle" is returned on a successful call.  At the start of a session (after
171  * a call to setnetconfig()) searches through the /etc/netconfig database will
172  * proceed from the start of the file.  The session handle must be passed to
173  * getnetconfig() to parse the file.  Each call to getnetconfig() using the
174  * current handle will process one subsequent entry in /etc/netconfig.
175  * setnetconfig() must be called before the first call to getnetconfig().
176  * (Handles are used to allow for nested calls to setnetpath()).
177  *
178  * A new session is established with each call to setnetconfig(), with a new
179  * handle being returned on each call.  Previously established sessions remain
180  * active until endnetconfig() is called with that session's handle as an
181  * argument.
182  *
183  * setnetconfig() need *not* be called before a call to getnetconfigent().
184  * setnetconfig() returns a NULL pointer on failure (for example, if
185  * the netconfig database is not present).
186  */
187 void *
188 setnetconfig(void)
189 {
190     struct netconfig_vars *nc_vars;
191
192     if ((nc_vars = (struct netconfig_vars *)malloc(sizeof
193                 (struct netconfig_vars))) == NULL) {
194         return(NULL);
195     }
196
197     /*
198      * For multiple calls, i.e. nc_file is not NULL, we just return the
199      * handle without reopening the netconfig db.
200      */
201     ni.ref++;
202     if ((nc_file != NULL) || (nc_file = fopen(NETCONFIG, "r")) != NULL) {
203         nc_vars->valid = NC_VALID;
204         nc_vars->flag = 0;
205         nc_vars->nc_configs = ni.head;
206         return ((void *)nc_vars);
207     }
208     ni.ref--;
209     nc_error = NC_NONETCONFIG;
210     free(nc_vars);
211     return (NULL);
212 }
213
214
215 /*
216  * When first called, getnetconfig() returns a pointer to the first entry in
217  * the netconfig database, formatted as a struct netconfig.  On each subsequent
218  * call, getnetconfig() returns a pointer to the next entry in the database.
219  * getnetconfig() can thus be used to search the entire netconfig file.
220  * getnetconfig() returns NULL at end of file.
221  */
222
223 struct netconfig *
224 getnetconfig(void *handlep)
225 {
226     struct netconfig_vars *ncp = (struct netconfig_vars *)handlep;
227     char *stringp;              /* tmp string pointer */
228     struct netconfig_list       *list;
229     struct netconfig *np;
230
231     /*
232      * Verify that handle is valid
233      */
234     if (ncp == NULL || nc_file == NULL) {
235         nc_error = NC_NOTINIT;
236         return (NULL);
237     }
238
239     switch (ncp->valid) {
240     case NC_VALID:
241         /*
242          * If entry has already been read into the list,
243          * we return the entry in the linked list.
244          * If this is the first time call, check if there are any entries in
245          * linked list.  If no entries, we need to read the netconfig db.
246          * If we have been here and the next entry is there, we just return
247          * it.
248          */
249         if (ncp->flag == 0) {   /* first time */
250             ncp->flag = 1;
251             ncp->nc_configs = ni.head;
252             if (ncp->nc_configs != NULL)        /* entry already exist */
253                 return(ncp->nc_configs->ncp);
254         }
255         else if (ncp->nc_configs != NULL && ncp->nc_configs->next != NULL) {
256             ncp->nc_configs = ncp->nc_configs->next;
257             return(ncp->nc_configs->ncp);
258         }
259
260         /*
261          * If we cannot find the entry in the list and is end of file,
262          * we give up.
263          */
264         if (ni.eof == 1)        return(NULL);
265         break;
266     default:
267         nc_error = NC_NOTINIT;
268         return (NULL);
269     }
270
271     stringp = (char *) malloc(MAXNETCONFIGLINE);
272     if (stringp == NULL)
273         return (NULL);
274
275 #ifdef MEM_CHK
276     if (malloc_verify() == 0) {
277         fprintf(stderr, "memory heap corrupted in getnetconfig\n");
278         exit(1);
279     }
280 #endif
281
282     /*
283      * Read a line from netconfig file.
284      */
285     do {
286         if (fgets(stringp, MAXNETCONFIGLINE, nc_file) == NULL) {
287             free(stringp);
288             ni.eof = 1;
289             return (NULL);
290         }
291     } while (*stringp == '#');
292
293     list = (struct netconfig_list *) malloc(sizeof (struct netconfig_list));
294     if (list == NULL) {
295         free(stringp);
296         return(NULL);
297     }
298     np = (struct netconfig *) malloc(sizeof (struct netconfig));
299     if (np == NULL) {
300         free(stringp);
301         free(list);
302         return(NULL);
303     }
304     list->ncp = np;
305     list->next = NULL;
306     list->ncp->nc_lookups = NULL;
307     list->linep = stringp;
308     if (parse_ncp(stringp, list->ncp) == -1) {
309         free(stringp);
310         free(np);
311         free(list);
312         return (NULL);
313     }
314     else {
315         /*
316          * If this is the first entry that's been read, it is the head of
317          * the list.  If not, put the entry at the end of the list.
318          * Reposition the current pointer of the handle to the last entry
319          * in the list.
320          */
321         if (ni.head == NULL) {  /* first entry */
322             ni.head = ni.tail = list;
323         }
324         else {
325             ni.tail->next = list;
326             ni.tail = ni.tail->next;
327         }
328         ncp->nc_configs = ni.tail;
329         return(ni.tail->ncp);
330     }
331 }
332
333 /*
334  * endnetconfig() may be called to "unbind" or "close" the netconfig database
335  * when processing is complete, releasing resources for reuse.  endnetconfig()
336  * may not be called before setnetconfig().  endnetconfig() returns 0 on
337  * success and -1 on failure (for example, if setnetconfig() was not called
338  * previously).
339  */
340 int
341 endnetconfig(void *handlep)
342 {
343     struct netconfig_vars *nc_handlep = (struct netconfig_vars *)handlep;
344
345     struct netconfig_list *q, *p;
346
347     /*
348      * Verify that handle is valid
349      */
350     if (nc_handlep == NULL || (nc_handlep->valid != NC_VALID &&
351             nc_handlep->valid != NC_STORAGE)) {
352         nc_error = NC_NOTINIT;
353         return (-1);
354     }
355
356     /*
357      * Return 0 if anyone still needs it.
358      */
359     nc_handlep->valid = NC_INVALID;
360     nc_handlep->flag = 0;
361     nc_handlep->nc_configs = NULL;
362     if (--ni.ref > 0) {
363         free(nc_handlep);
364         return(0);
365     }
366
367     /*
368      * Noone needs these entries anymore, then frees them.
369      * Make sure all info in netconfig_info structure has been reinitialized.
370      */
371     q = p = ni.head;
372     ni.eof = ni.ref = 0;
373     ni.head = NULL;
374     ni.tail = NULL;
375     while (q) {
376         p = q->next;
377         if (q->ncp->nc_lookups != NULL) free(q->ncp->nc_lookups);
378         free(q->ncp);
379         free(q->linep);
380         free(q);
381         q = p;
382     }
383     free(nc_handlep);
384
385     fclose(nc_file);
386     nc_file = NULL;
387     return (0);
388 }
389
390 /*
391  * getnetconfigent(netid) returns a pointer to the struct netconfig structure
392  * corresponding to netid.  It returns NULL if netid is invalid (that is, does
393  * not name an entry in the netconfig database).  It returns NULL and sets
394  * errno in case of failure (for example, if the netconfig database cannot be
395  * opened).
396  */
397
398 struct netconfig *
399 getnetconfigent(const char *netid)
400 {
401     FILE *file;         /* NETCONFIG db's file pointer */
402     char *linep;        /* holds current netconfig line */
403     char *stringp;      /* temporary string pointer */
404     struct netconfig *ncp = NULL;   /* returned value */
405     struct netconfig_list *list;        /* pointer to cache list */
406
407     nc_error = NC_NOTFOUND;     /* default error. */
408     if (netid == NULL || strlen(netid) == 0) {
409         return (NULL);
410     }
411
412     /*
413      * Look up table if the entries have already been read and parsed in
414      * getnetconfig(), then copy this entry into a buffer and return it.
415      * If we cannot find the entry in the current list and there are more
416      * entries in the netconfig db that has not been read, we then read the
417      * db and try find the match netid.
418      * If all the netconfig db has been read and placed into the list and
419      * there is no match for the netid, return NULL.
420      */
421     if (ni.head != NULL) {
422         for (list = ni.head; list; list = list->next) {
423             if (strcmp(list->ncp->nc_netid, netid) == 0) {
424                 return(dup_ncp(list->ncp));
425             }
426         }
427         if (ni.eof == 1)        /* that's all the entries */
428                 return(NULL);
429     }
430
431
432     if ((file = fopen(NETCONFIG, "r")) == NULL) {
433         nc_error = NC_NONETCONFIG;
434         return (NULL);
435     }
436
437     if ((linep = malloc(MAXNETCONFIGLINE)) == NULL) {
438         fclose(file);
439         nc_error = NC_NOMEM;
440         return (NULL);
441     }
442     do {
443         ptrdiff_t len;
444         char *tmpp;     /* tmp string pointer */
445
446         do {
447             if ((stringp = fgets(linep, MAXNETCONFIGLINE, file)) == NULL) {
448                 break;
449             }
450         } while (*stringp == '#');
451         if (stringp == NULL) {      /* eof */
452             break;
453         }
454         if ((tmpp = strpbrk(stringp, "\t ")) == NULL) { /* can't parse file */
455             nc_error = NC_BADFILE;
456             break;
457         }
458         if (strlen(netid) == (size_t) (len = tmpp - stringp) && /* a match */
459                 strncmp(stringp, netid, (size_t)len) == 0) {
460             if ((ncp = (struct netconfig *)
461                     malloc(sizeof (struct netconfig))) == NULL) {
462                 break;
463             }
464             ncp->nc_lookups = NULL;
465             if (parse_ncp(linep, ncp) == -1) {
466                 free(ncp);
467                 ncp = NULL;
468             }
469             break;
470         }
471     } while (stringp != NULL);
472     if (ncp == NULL) {
473         free(linep);
474     }
475     fclose(file);
476     return(ncp);
477 }
478
479 /*
480  * freenetconfigent(netconfigp) frees the netconfig structure pointed to by
481  * netconfigp (previously returned by getnetconfigent()).
482  */
483
484 void
485 freenetconfigent(struct netconfig *netconfigp)
486 {
487     if (netconfigp != NULL) {
488         free(netconfigp->nc_netid);     /* holds all netconfigp's strings */
489         if (netconfigp->nc_lookups != NULL)
490             free(netconfigp->nc_lookups);
491         free(netconfigp);
492     }
493     return;
494 }
495
496 /*
497  * Parse line and stuff it in a struct netconfig
498  * Typical line might look like:
499  *      udp tpi_cots vb inet udp /dev/udp /usr/lib/ip.so,/usr/local/ip.so
500  *
501  * We return -1 if any of the tokens don't parse, or malloc fails.
502  *
503  * Note that we modify stringp (putting NULLs after tokens) and
504  * we set the ncp's string field pointers to point to these tokens within
505  * stringp.
506  */
507
508 static int
509 parse_ncp(char *stringp,                /* string to parse */
510           struct netconfig *ncp)        /* where to put results */
511 {
512     char    *tokenp;    /* for processing tokens */
513     char    *lasts;
514     char    **nc_lookups;
515
516     nc_error = NC_BADFILE;      /* nearly anything that breaks is for this reason */
517     stringp[strlen(stringp)-1] = '\0';  /* get rid of newline */
518     /* netid */
519     if ((ncp->nc_netid = strtok_r(stringp, "\t ", &lasts)) == NULL) {
520         return (-1);
521     }
522
523     /* semantics */
524     if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL) {
525         return (-1);
526     }
527     if (strcmp(tokenp, NC_TPI_COTS_ORD_S) == 0)
528         ncp->nc_semantics = NC_TPI_COTS_ORD;
529     else if (strcmp(tokenp, NC_TPI_COTS_S) == 0)
530         ncp->nc_semantics = NC_TPI_COTS;
531     else if (strcmp(tokenp, NC_TPI_CLTS_S) == 0)
532         ncp->nc_semantics = NC_TPI_CLTS;
533     else if (strcmp(tokenp, NC_TPI_RAW_S) == 0)
534         ncp->nc_semantics = NC_TPI_RAW;
535     else
536         return (-1);
537
538     /* flags */
539     if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL) {
540         return (-1);
541     }
542     for (ncp->nc_flag = NC_NOFLAG; *tokenp != '\0';
543             tokenp++) {
544         switch (*tokenp) {
545         case NC_NOFLAG_C:
546             break;
547         case NC_VISIBLE_C:
548             ncp->nc_flag |= NC_VISIBLE;
549             break;
550         case NC_BROADCAST_C:
551             ncp->nc_flag |= NC_BROADCAST;
552             break;
553         default:
554             return (-1);
555         }
556     }
557     /* protocol family */
558     if ((ncp->nc_protofmly = strtok_r(NULL, "\t ", &lasts)) == NULL) {
559         return (-1);
560     }
561     /* protocol name */
562     if ((ncp->nc_proto = strtok_r(NULL, "\t ", &lasts)) == NULL) {
563         return (-1);
564     }
565     /* network device */
566     if ((ncp->nc_device = strtok_r(NULL, "\t ", &lasts)) == NULL) {
567         return (-1);
568     }
569     if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL) {
570         return (-1);
571     }
572     if (strcmp(tokenp, NC_NOLOOKUP) == 0) {
573         ncp->nc_nlookups = 0;
574         ncp->nc_lookups = NULL;
575     } else {
576         char *cp;           /* tmp string */
577
578         if (ncp->nc_lookups != NULL)    /* from last visit */
579             free(ncp->nc_lookups);
580         ncp->nc_lookups = NULL;
581         ncp->nc_nlookups = 0;
582         while ((cp = tokenp) != NULL) {
583             if ((nc_lookups = realloc(ncp->nc_lookups,
584                 (ncp->nc_nlookups + 1) * sizeof *ncp->nc_lookups)) == NULL) {
585                     free(ncp->nc_lookups);
586                     ncp->nc_lookups = NULL;
587                     return (-1);
588             }
589             tokenp = _get_next_token(cp, ',');
590             ncp->nc_lookups = nc_lookups;
591             ncp->nc_lookups[ncp->nc_nlookups++] = cp;
592         }
593     }
594     return (0);
595 }
596
597
598 /*
599  * Returns a string describing the reason for failure.
600  */
601 char *
602 nc_sperror(void)
603 {
604     const char *message;
605
606     switch(nc_error) {
607     case NC_NONETCONFIG:
608         message = _nc_errors[0];
609         break;
610     case NC_NOMEM:
611         message = _nc_errors[1];
612         break;
613     case NC_NOTINIT:
614         message = _nc_errors[2];
615         break;
616     case NC_BADFILE:
617         message = _nc_errors[3];
618         break;
619     case NC_NOTFOUND:
620         message = _nc_errors[4];
621         break;
622     default:
623         message = "Unknown network selection error";
624     }
625     /* LINTED const castaway */
626     return ((char *)message);
627 }
628
629 /*
630  * Prints a message onto standard error describing the reason for failure.
631  */
632 void
633 nc_perror(const char *s)
634 {
635     fprintf(stderr, "%s: %s\n", s, nc_sperror());
636 }
637
638 /*
639  * Duplicates the matched netconfig buffer.
640  */
641 static struct netconfig *
642 dup_ncp(struct netconfig *ncp)
643 {
644     struct netconfig    *p;
645     char        *tmp;
646     u_int       i;
647
648     if ((tmp=malloc(MAXNETCONFIGLINE)) == NULL)
649         return(NULL);
650     if ((p=(struct netconfig *)malloc(sizeof(struct netconfig))) == NULL) {
651         free(tmp);
652         return(NULL);
653     }
654     /*
655      * First we dup all the data from matched netconfig buffer.  Then we
656      * adjust some of the member pointer to a pre-allocated buffer where
657      * contains part of the data.
658      * To follow the convention used in parse_ncp(), we store all the
659      * necessary information in the pre-allocated buffer and let each
660      * of the netconfig char pointer member point to the right address
661      * in the buffer.
662      */
663     *p = *ncp;
664     p->nc_netid = (char *)strcpy(tmp,ncp->nc_netid);
665     tmp = strchr(tmp, '\0') + 1;
666     p->nc_protofmly = (char *)strcpy(tmp,ncp->nc_protofmly);
667     tmp = strchr(tmp, '\0') + 1;
668     p->nc_proto = (char *)strcpy(tmp,ncp->nc_proto);
669     tmp = strchr(tmp, '\0') + 1;
670     p->nc_device = (char *)strcpy(tmp,ncp->nc_device);
671     p->nc_lookups = (char **)malloc((size_t)(p->nc_nlookups+1) * sizeof(char *));
672     if (p->nc_lookups == NULL) {
673         free(p->nc_netid);
674         free(p);
675         return(NULL);
676     }
677     for (i=0; i < p->nc_nlookups; i++) {
678         tmp = strchr(tmp, '\0') + 1;
679         p->nc_lookups[i] = (char *)strcpy(tmp,ncp->nc_lookups[i]);
680     }
681     return(p);
682 }