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