1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * PPPoE Server-mode daemon option parsing.
24*0Sstevel@tonic-gate  *
25*0Sstevel@tonic-gate  * Copyright 2000-2002 Sun Microsystems, Inc.  All rights reserved.
26*0Sstevel@tonic-gate  * Use is subject to license terms.
27*0Sstevel@tonic-gate  */
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
30*0Sstevel@tonic-gate 
31*0Sstevel@tonic-gate #include <stdio.h>
32*0Sstevel@tonic-gate #include <stdlib.h>
33*0Sstevel@tonic-gate #include <unistd.h>
34*0Sstevel@tonic-gate #include <assert.h>
35*0Sstevel@tonic-gate #include <ctype.h>
36*0Sstevel@tonic-gate #include <string.h>
37*0Sstevel@tonic-gate #include <sys/types.h>
38*0Sstevel@tonic-gate #include <fcntl.h>
39*0Sstevel@tonic-gate #include <pwd.h>
40*0Sstevel@tonic-gate #include <grp.h>
41*0Sstevel@tonic-gate #include <errno.h>
42*0Sstevel@tonic-gate #include <netdb.h>
43*0Sstevel@tonic-gate #include <stropts.h>
44*0Sstevel@tonic-gate #include <sys/stat.h>
45*0Sstevel@tonic-gate #include <sys/socket.h>
46*0Sstevel@tonic-gate #include <net/if.h>
47*0Sstevel@tonic-gate #include <netinet/in.h>
48*0Sstevel@tonic-gate #include <netinet/if_ether.h>
49*0Sstevel@tonic-gate #include <net/sppptun.h>
50*0Sstevel@tonic-gate 
51*0Sstevel@tonic-gate #include "common.h"
52*0Sstevel@tonic-gate #include "logging.h"
53*0Sstevel@tonic-gate 
54*0Sstevel@tonic-gate #define	MAX_KEYWORD	4096	/* Maximum token length */
55*0Sstevel@tonic-gate #define	MAX_NEST	32	/* Maximum ${$sub} nesting */
56*0Sstevel@tonic-gate #define	MAXARGS		256	/* Maximum number of pppd arguments */
57*0Sstevel@tonic-gate 
58*0Sstevel@tonic-gate /*
59*0Sstevel@tonic-gate  * Client filter entry.  These are linked in *reverse* order so that
60*0Sstevel@tonic-gate  * the DAG created by file inclusion nesting works as expected.  Since
61*0Sstevel@tonic-gate  * the administrator who wrote the configuration expects "first
62*0Sstevel@tonic-gate  * match," this means that tests against the filter list must actually
63*0Sstevel@tonic-gate  * use "last match."
64*0Sstevel@tonic-gate  */
65*0Sstevel@tonic-gate struct filter_entry {
66*0Sstevel@tonic-gate 	struct filter_entry *fe_prev;	/* Previous filter in list */
67*0Sstevel@tonic-gate 	struct ether_addr fe_mac;	/* MAC address */
68*0Sstevel@tonic-gate 	struct ether_addr fe_mask;	/* Mask for above address test */
69*0Sstevel@tonic-gate 	uchar_t fe_isexcept;	/* invert sense; exclude matching clients */
70*0Sstevel@tonic-gate 	uchar_t fe_prevcopy;		/* fe_prev points to copied list */
71*0Sstevel@tonic-gate 	uchar_t fe_unused[2];		/* padding */
72*0Sstevel@tonic-gate };
73*0Sstevel@tonic-gate 
74*0Sstevel@tonic-gate /*
75*0Sstevel@tonic-gate  * Note: I would like to make the strings and filters here const, but
76*0Sstevel@tonic-gate  * I can't because they have to be passed to free() during parsing.  I
77*0Sstevel@tonic-gate  * could work around this with offsetof() or data copies, but it's not
78*0Sstevel@tonic-gate  * worth the effort.
79*0Sstevel@tonic-gate  */
80*0Sstevel@tonic-gate struct service_entry {
81*0Sstevel@tonic-gate 	const char *se_name;		/* Name of service */
82*0Sstevel@tonic-gate 	struct filter_entry *se_flist;	/* Pointer to list of client filters */
83*0Sstevel@tonic-gate 	uint_t se_flags;		/* SEF_* flags (below) */
84*0Sstevel@tonic-gate 	int se_debug;			/* Debug level (0=nodebug) */
85*0Sstevel@tonic-gate 	char *se_server;		/* Server (AC) name */
86*0Sstevel@tonic-gate 	char *se_pppd;			/* Options for pppd */
87*0Sstevel@tonic-gate 	char *se_path;			/* Path to pppd executable */
88*0Sstevel@tonic-gate 	char *se_extra;			/* Extra options */
89*0Sstevel@tonic-gate 	char *se_log;			/* Log file */
90*0Sstevel@tonic-gate 	uid_t se_uid;			/* User ID */
91*0Sstevel@tonic-gate 	gid_t se_gid;			/* Group ID */
92*0Sstevel@tonic-gate };
93*0Sstevel@tonic-gate 
94*0Sstevel@tonic-gate #define	SEF_WILD	0x00000001	/* Offer in wildcard reply */
95*0Sstevel@tonic-gate #define	SEF_NOWILD	0x00000002	/* Don't offer in wildcard */
96*0Sstevel@tonic-gate #define	SEF_CFLIST	0x00000004	/* se_flist copied from global */
97*0Sstevel@tonic-gate #define	SEF_CSERVER	0x00000008	/* se_server copied from global */
98*0Sstevel@tonic-gate #define	SEF_CPPPD	0x00000010	/* se_pppd copied from global */
99*0Sstevel@tonic-gate #define	SEF_CPATH	0x00000020	/* se_path copied from global */
100*0Sstevel@tonic-gate #define	SEF_CEXTRA	0x00000040	/* se_extra copied from global */
101*0Sstevel@tonic-gate #define	SEF_CLOG	0x00000080	/* se_log copied from global */
102*0Sstevel@tonic-gate #define	SEF_UIDSET	0x00000100	/* se_uid has been set */
103*0Sstevel@tonic-gate #define	SEF_GIDSET	0x00000200	/* se_gid has been set */
104*0Sstevel@tonic-gate #define	SEF_DEBUGCLR	0x00000400	/* do not add se_debug from global */
105*0Sstevel@tonic-gate #define	SEF_CDEV	0x00000800	/* copied devs (parse only) */
106*0Sstevel@tonic-gate 
107*0Sstevel@tonic-gate /*
108*0Sstevel@tonic-gate  * One of these is allocated per lower-level stream (device) that is
109*0Sstevel@tonic-gate  * referenced by the configuration files.  The queries are received
110*0Sstevel@tonic-gate  * per device, and this structure allows us to find all of the
111*0Sstevel@tonic-gate  * services that correspond to that device.
112*0Sstevel@tonic-gate  */
113*0Sstevel@tonic-gate struct device_entry {
114*0Sstevel@tonic-gate 	const char *de_name;
115*0Sstevel@tonic-gate 	const struct service_entry **de_services;
116*0Sstevel@tonic-gate 	int de_nservices;
117*0Sstevel@tonic-gate };
118*0Sstevel@tonic-gate 
119*0Sstevel@tonic-gate /*
120*0Sstevel@tonic-gate  * This is the parsed configuration.  While a new configuration is
121*0Sstevel@tonic-gate  * being read, this is kept around until the new configuration is
122*0Sstevel@tonic-gate  * ready, and then it is discarded in one operation.  It has an array
123*0Sstevel@tonic-gate  * of device entries (as above) -- one per referenced lower stream --
124*0Sstevel@tonic-gate  * and a pointer to the allocated parser information.  The latter is
125*0Sstevel@tonic-gate  * kept around because we reuse pointers rather than reallocating and
126*0Sstevel@tonic-gate  * copying the data.  There are thus multiple aliases to the dynamic
127*0Sstevel@tonic-gate  * data, and the "owner" (for purposes of freeing the storage) is
128*0Sstevel@tonic-gate  * considered to be this 'junk' list.
129*0Sstevel@tonic-gate  */
130*0Sstevel@tonic-gate struct option_state {
131*0Sstevel@tonic-gate 	const struct device_entry *os_devices;
132*0Sstevel@tonic-gate 	int os_ndevices;
133*0Sstevel@tonic-gate 	struct per_file *os_pfjunk;	/* Kept for deallocation */
134*0Sstevel@tonic-gate 	char **os_evjunk;		/* ditto */
135*0Sstevel@tonic-gate };
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate /*
138*0Sstevel@tonic-gate  * This is the root pointer to the current parsed options.
139*0Sstevel@tonic-gate  * This cannot be const because it's passed to free() when reparsing
140*0Sstevel@tonic-gate  * options.
141*0Sstevel@tonic-gate  */
142*0Sstevel@tonic-gate static struct option_state *cur_options;
143*0Sstevel@tonic-gate 
144*0Sstevel@tonic-gate /* Global settings for module-wide options. */
145*0Sstevel@tonic-gate static struct service_entry glob_svc;
146*0Sstevel@tonic-gate 
147*0Sstevel@tonic-gate /*
148*0Sstevel@tonic-gate  * *******************************************************************
149*0Sstevel@tonic-gate  * Data structures generated during parsing.
150*0Sstevel@tonic-gate  */
151*0Sstevel@tonic-gate 
152*0Sstevel@tonic-gate /* List of device names attached to one service */
153*0Sstevel@tonic-gate struct device_list {
154*0Sstevel@tonic-gate 	struct device_list *dl_next;
155*0Sstevel@tonic-gate 	const char *dl_name;		/* Name of one device */
156*0Sstevel@tonic-gate };
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate /* Entry for a single defined service. */
159*0Sstevel@tonic-gate struct service_list {
160*0Sstevel@tonic-gate 	struct service_entry sl_entry;	/* Parsed service data */
161*0Sstevel@tonic-gate 	struct service_list *sl_next;	/* Next service entry */
162*0Sstevel@tonic-gate 	struct parse_state *sl_parse;	/* Back pointer to state */
163*0Sstevel@tonic-gate 	struct device_list *sl_dev;	/* List of devices */
164*0Sstevel@tonic-gate 	int sl_serial;			/* Serial number (conflict resolve) */
165*0Sstevel@tonic-gate };
166*0Sstevel@tonic-gate #define	SESERIAL(x)	((struct service_list *)&(x))->sl_serial
167*0Sstevel@tonic-gate #define	ISGLOBAL(x)	((x) == &(x)->sl_parse->ps_cfile->pf_global)
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate /*
170*0Sstevel@tonic-gate  * Structure allocated for each file opened.  File nesting is chained
171*0Sstevel@tonic-gate  * in reverse order so that global option scoping works as expected.
172*0Sstevel@tonic-gate  */
173*0Sstevel@tonic-gate struct per_file {
174*0Sstevel@tonic-gate 	struct per_file *pf_prev;	/* Back chain */
175*0Sstevel@tonic-gate 	struct service_list pf_global;	/* Global (default) service context */
176*0Sstevel@tonic-gate 	struct service_list *pf_svc;	/* List of services */
177*0Sstevel@tonic-gate 	struct service_list *pf_svc_last;
178*0Sstevel@tonic-gate 	FILE *pf_input;			/* File for input */
179*0Sstevel@tonic-gate 	const char *pf_name;		/* File name */
180*0Sstevel@tonic-gate 	int pf_nsvc;			/* Count of services */
181*0Sstevel@tonic-gate };
182*0Sstevel@tonic-gate 
183*0Sstevel@tonic-gate /* State of parser */
184*0Sstevel@tonic-gate enum key_state {
185*0Sstevel@tonic-gate 	ksDefault, ksService, ksDevice, ksClient, ksClientE, ksServer,
186*0Sstevel@tonic-gate 	ksPppd, ksFile, ksPath, ksExtra, ksLog, ksUser, ksGroup
187*0Sstevel@tonic-gate };
188*0Sstevel@tonic-gate 
189*0Sstevel@tonic-gate /*
190*0Sstevel@tonic-gate  * Global parser state.  There is one of these structures, and it
191*0Sstevel@tonic-gate  * exists only while actively parsing configuration files.
192*0Sstevel@tonic-gate  */
193*0Sstevel@tonic-gate struct parse_state {
194*0Sstevel@tonic-gate 	enum key_state ps_state;	/* Parser state */
195*0Sstevel@tonic-gate 	int ps_serial;			/* Service serial number */
196*0Sstevel@tonic-gate 	struct per_file *ps_files;	/* Parsed files */
197*0Sstevel@tonic-gate 	struct per_file *ps_cfile;	/* Current file */
198*0Sstevel@tonic-gate 	struct service_list *ps_csvc;	/* Current service */
199*0Sstevel@tonic-gate 	struct device_list *ps_star;	/* Wildcard device */
200*0Sstevel@tonic-gate 	int ps_flags;			/* PSF_* below */
201*0Sstevel@tonic-gate 	char **ps_evlist;		/* allocated environment variables */
202*0Sstevel@tonic-gate 	int ps_evsize;			/* max length; for realloc */
203*0Sstevel@tonic-gate };
204*0Sstevel@tonic-gate 
205*0Sstevel@tonic-gate #define	PSF_PERDEV	0x0001		/* In a per-device file */
206*0Sstevel@tonic-gate #define	PSF_SETLEVEL	0x0002		/* Set log level along the way */
207*0Sstevel@tonic-gate 
208*0Sstevel@tonic-gate /* Should be in a library somewhere. */
209*0Sstevel@tonic-gate static char *
210*0Sstevel@tonic-gate strsave(const char *str)
211*0Sstevel@tonic-gate {
212*0Sstevel@tonic-gate 	char *newstr;
213*0Sstevel@tonic-gate 
214*0Sstevel@tonic-gate 	if (str == NULL)
215*0Sstevel@tonic-gate 		return (NULL);
216*0Sstevel@tonic-gate 	newstr = (char *)malloc(strlen(str) + 1);
217*0Sstevel@tonic-gate 	if (newstr != NULL)
218*0Sstevel@tonic-gate 		(void) strcpy(newstr, str);
219*0Sstevel@tonic-gate 	return (newstr);
220*0Sstevel@tonic-gate }
221*0Sstevel@tonic-gate 
222*0Sstevel@tonic-gate /*
223*0Sstevel@tonic-gate  * Stop defining current service and revert to global definition.
224*0Sstevel@tonic-gate  * This resolves any implicit references to global options by copying
225*0Sstevel@tonic-gate  * ("inheriting") from the current global state.
226*0Sstevel@tonic-gate  */
227*0Sstevel@tonic-gate static void
228*0Sstevel@tonic-gate close_service(struct service_list *slp)
229*0Sstevel@tonic-gate {
230*0Sstevel@tonic-gate 	struct parse_state *psp;
231*0Sstevel@tonic-gate 	struct per_file *cfile;
232*0Sstevel@tonic-gate 	struct service_entry *sep;
233*0Sstevel@tonic-gate 	struct service_entry *sedefp;
234*0Sstevel@tonic-gate 	struct filter_entry *fep;
235*0Sstevel@tonic-gate 
236*0Sstevel@tonic-gate 	assert(slp != NULL);
237*0Sstevel@tonic-gate 	psp = slp->sl_parse;
238*0Sstevel@tonic-gate 	cfile = psp->ps_cfile;
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate 	/* If no current file, then nothing to close. */
241*0Sstevel@tonic-gate 	if (cfile == NULL)
242*0Sstevel@tonic-gate 		return;
243*0Sstevel@tonic-gate 
244*0Sstevel@tonic-gate 	sep = &slp->sl_entry;
245*0Sstevel@tonic-gate 
246*0Sstevel@tonic-gate 	/*
247*0Sstevel@tonic-gate 	 * Fix up filter pointers to make DAG.  First, locate
248*0Sstevel@tonic-gate 	 * the end of the filter list.
249*0Sstevel@tonic-gate 	 */
250*0Sstevel@tonic-gate 	if (sep->se_flags & SEF_CFLIST) {
251*0Sstevel@tonic-gate 		sep->se_flist = fep = NULL;
252*0Sstevel@tonic-gate 	} else {
253*0Sstevel@tonic-gate 		for (fep = sep->se_flist; fep != NULL; fep = fep->fe_prev)
254*0Sstevel@tonic-gate 			if (fep->fe_prev == NULL || fep->fe_prevcopy) {
255*0Sstevel@tonic-gate 				fep->fe_prev = NULL;
256*0Sstevel@tonic-gate 				break;
257*0Sstevel@tonic-gate 			}
258*0Sstevel@tonic-gate 	}
259*0Sstevel@tonic-gate 	if (slp == &cfile->pf_global) {
260*0Sstevel@tonic-gate 		/*
261*0Sstevel@tonic-gate 		 * If we're in a global context, then we're about to
262*0Sstevel@tonic-gate 		 * open a new service, so it's time to fix up the
263*0Sstevel@tonic-gate 		 * filter list so that it's usable as a reference.
264*0Sstevel@tonic-gate 		 * Loop through files from which we were included, and
265*0Sstevel@tonic-gate 		 * link up filters.  Note: closure may occur more than
266*0Sstevel@tonic-gate 		 * once here.
267*0Sstevel@tonic-gate 		 */
268*0Sstevel@tonic-gate 		/* We don't inherit from ourselves. */
269*0Sstevel@tonic-gate 		cfile = cfile->pf_prev;
270*0Sstevel@tonic-gate 		while (cfile != NULL) {
271*0Sstevel@tonic-gate 			if (fep == NULL) {
272*0Sstevel@tonic-gate 				sep->se_flist = fep =
273*0Sstevel@tonic-gate 				    cfile->pf_global.sl_entry.se_flist;
274*0Sstevel@tonic-gate 				sep->se_flags |= SEF_CFLIST;
275*0Sstevel@tonic-gate 			} else if (fep->fe_prev == NULL) {
276*0Sstevel@tonic-gate 				fep->fe_prev =
277*0Sstevel@tonic-gate 				    cfile->pf_global.sl_entry.se_flist;
278*0Sstevel@tonic-gate 				fep->fe_prevcopy = 1;
279*0Sstevel@tonic-gate 			}
280*0Sstevel@tonic-gate 			cfile = cfile->pf_prev;
281*0Sstevel@tonic-gate 		}
282*0Sstevel@tonic-gate 	} else {
283*0Sstevel@tonic-gate 		/*
284*0Sstevel@tonic-gate 		 * Loop through default options in current and all
285*0Sstevel@tonic-gate 		 * enclosing include files.  Inherit options.
286*0Sstevel@tonic-gate 		 */
287*0Sstevel@tonic-gate 		logdbg("service %s ends", slp->sl_entry.se_name);
288*0Sstevel@tonic-gate 		while (cfile != NULL) {
289*0Sstevel@tonic-gate 			/* Inherit from global service options. */
290*0Sstevel@tonic-gate 			if (slp->sl_dev == NULL) {
291*0Sstevel@tonic-gate 				slp->sl_dev = cfile->pf_global.sl_dev;
292*0Sstevel@tonic-gate 				sep->se_flags |= SEF_CDEV;
293*0Sstevel@tonic-gate 			}
294*0Sstevel@tonic-gate 			sedefp = &cfile->pf_global.sl_entry;
295*0Sstevel@tonic-gate 			if (fep == NULL) {
296*0Sstevel@tonic-gate 				sep->se_flist = fep = sedefp->se_flist;
297*0Sstevel@tonic-gate 				sep->se_flags |= SEF_CFLIST;
298*0Sstevel@tonic-gate 			} else if (fep->fe_prev == NULL) {
299*0Sstevel@tonic-gate 				fep->fe_prev = sedefp->se_flist;
300*0Sstevel@tonic-gate 				fep->fe_prevcopy = 1;
301*0Sstevel@tonic-gate 			}
302*0Sstevel@tonic-gate 			if (sep->se_server == NULL) {
303*0Sstevel@tonic-gate 				sep->se_server = sedefp->se_server;
304*0Sstevel@tonic-gate 				sep->se_flags |= SEF_CSERVER;
305*0Sstevel@tonic-gate 			}
306*0Sstevel@tonic-gate 			if (sep->se_pppd == NULL) {
307*0Sstevel@tonic-gate 				sep->se_pppd = sedefp->se_pppd;
308*0Sstevel@tonic-gate 				sep->se_flags |= SEF_CPPPD;
309*0Sstevel@tonic-gate 			}
310*0Sstevel@tonic-gate 			if (sep->se_path == NULL) {
311*0Sstevel@tonic-gate 				sep->se_path = sedefp->se_path;
312*0Sstevel@tonic-gate 				sep->se_flags |= SEF_CPATH;
313*0Sstevel@tonic-gate 			}
314*0Sstevel@tonic-gate 			if (sep->se_extra == NULL) {
315*0Sstevel@tonic-gate 				sep->se_extra = sedefp->se_extra;
316*0Sstevel@tonic-gate 				sep->se_flags |= SEF_CEXTRA;
317*0Sstevel@tonic-gate 			}
318*0Sstevel@tonic-gate 			if (sep->se_log == NULL) {
319*0Sstevel@tonic-gate 				sep->se_log = sedefp->se_log;
320*0Sstevel@tonic-gate 				sep->se_flags |= SEF_CLOG;
321*0Sstevel@tonic-gate 			}
322*0Sstevel@tonic-gate 			if (!(sep->se_flags & SEF_UIDSET) &&
323*0Sstevel@tonic-gate 			    (sedefp->se_flags & SEF_UIDSET)) {
324*0Sstevel@tonic-gate 				sep->se_uid = sedefp->se_uid;
325*0Sstevel@tonic-gate 				sep->se_flags |= SEF_UIDSET;
326*0Sstevel@tonic-gate 			}
327*0Sstevel@tonic-gate 			if (!(sep->se_flags & SEF_GIDSET) &&
328*0Sstevel@tonic-gate 			    (sedefp->se_flags & SEF_GIDSET)) {
329*0Sstevel@tonic-gate 				sep->se_gid = sedefp->se_gid;
330*0Sstevel@tonic-gate 				sep->se_flags |= SEF_GIDSET;
331*0Sstevel@tonic-gate 			}
332*0Sstevel@tonic-gate 			if (!(sep->se_flags & (SEF_WILD|SEF_NOWILD)))
333*0Sstevel@tonic-gate 				sep->se_flags |= sedefp->se_flags &
334*0Sstevel@tonic-gate 				    (SEF_WILD|SEF_NOWILD);
335*0Sstevel@tonic-gate 			if (!(sep->se_flags & SEF_DEBUGCLR)) {
336*0Sstevel@tonic-gate 				sep->se_debug += sedefp->se_debug;
337*0Sstevel@tonic-gate 				sep->se_flags |= sedefp->se_flags &
338*0Sstevel@tonic-gate 				    SEF_DEBUGCLR;
339*0Sstevel@tonic-gate 			}
340*0Sstevel@tonic-gate 			cfile = cfile->pf_prev;
341*0Sstevel@tonic-gate 		}
342*0Sstevel@tonic-gate 	}
343*0Sstevel@tonic-gate 	/* Revert to global definitions. */
344*0Sstevel@tonic-gate 	psp->ps_csvc = &psp->ps_cfile->pf_global;
345*0Sstevel@tonic-gate }
346*0Sstevel@tonic-gate 
347*0Sstevel@tonic-gate /* Discard a dynamic device list */
348*0Sstevel@tonic-gate static void
349*0Sstevel@tonic-gate free_device_list(struct device_list *dlp)
350*0Sstevel@tonic-gate {
351*0Sstevel@tonic-gate 	struct device_list *dln;
352*0Sstevel@tonic-gate 
353*0Sstevel@tonic-gate 	while (dlp != NULL) {
354*0Sstevel@tonic-gate 		dln = dlp->dl_next;
355*0Sstevel@tonic-gate 		free(dlp);
356*0Sstevel@tonic-gate 		dlp = dln;
357*0Sstevel@tonic-gate 	}
358*0Sstevel@tonic-gate }
359*0Sstevel@tonic-gate 
360*0Sstevel@tonic-gate /*
361*0Sstevel@tonic-gate  * Handle "service <name>" -- finish up previous service definition
362*0Sstevel@tonic-gate  * (if any) by copying from global state where necessary, and start
363*0Sstevel@tonic-gate  * defining new service.
364*0Sstevel@tonic-gate  */
365*0Sstevel@tonic-gate static int
366*0Sstevel@tonic-gate set_service(struct service_list *slp, const char *str)
367*0Sstevel@tonic-gate {
368*0Sstevel@tonic-gate 	struct parse_state *psp;
369*0Sstevel@tonic-gate 	struct per_file *cfile;
370*0Sstevel@tonic-gate 
371*0Sstevel@tonic-gate 	/* Finish current service */
372*0Sstevel@tonic-gate 	close_service(slp);
373*0Sstevel@tonic-gate 
374*0Sstevel@tonic-gate 	/* Start new service */
375*0Sstevel@tonic-gate 	psp = slp->sl_parse;
376*0Sstevel@tonic-gate 	slp = (struct service_list *)calloc(sizeof (*slp) + strlen(str) + 1,
377*0Sstevel@tonic-gate 	    1);
378*0Sstevel@tonic-gate 	if (slp == NULL) {
379*0Sstevel@tonic-gate 		logerr("no memory for service \"%s\"", str);
380*0Sstevel@tonic-gate 		return (-1);
381*0Sstevel@tonic-gate 	}
382*0Sstevel@tonic-gate 
383*0Sstevel@tonic-gate 	/* Add to end of list */
384*0Sstevel@tonic-gate 	cfile = psp->ps_cfile;
385*0Sstevel@tonic-gate 	if (cfile->pf_svc_last == NULL)
386*0Sstevel@tonic-gate 		cfile->pf_svc = slp;
387*0Sstevel@tonic-gate 	else
388*0Sstevel@tonic-gate 		cfile->pf_svc_last->sl_next = slp;
389*0Sstevel@tonic-gate 	cfile->pf_svc_last = slp;
390*0Sstevel@tonic-gate 	cfile->pf_nsvc++;
391*0Sstevel@tonic-gate 
392*0Sstevel@tonic-gate 	/* Fill in initial service entry */
393*0Sstevel@tonic-gate 	slp->sl_entry.se_name = (const char *)(slp+1);
394*0Sstevel@tonic-gate 	(void) strcpy((char *)(slp+1), str);
395*0Sstevel@tonic-gate 	logdbg("service %s begins", slp->sl_entry.se_name);
396*0Sstevel@tonic-gate 	slp->sl_serial = psp->ps_serial++;
397*0Sstevel@tonic-gate 	slp->sl_parse = psp;
398*0Sstevel@tonic-gate 
399*0Sstevel@tonic-gate 	/* This is now the current service that we're defining. */
400*0Sstevel@tonic-gate 	psp->ps_csvc = slp;
401*0Sstevel@tonic-gate 	return (0);
402*0Sstevel@tonic-gate }
403*0Sstevel@tonic-gate 
404*0Sstevel@tonic-gate /*
405*0Sstevel@tonic-gate  * Handle both "wildcard" and "nowildcard" options.
406*0Sstevel@tonic-gate  */
407*0Sstevel@tonic-gate static int
408*0Sstevel@tonic-gate set_wildcard(struct service_list *slp, const char *str)
409*0Sstevel@tonic-gate {
410*0Sstevel@tonic-gate 	/* Allow global context to switch back and forth without error. */
411*0Sstevel@tonic-gate 	if (!ISGLOBAL(slp) &&
412*0Sstevel@tonic-gate 	    (slp->sl_entry.se_flags & (SEF_WILD|SEF_NOWILD))) {
413*0Sstevel@tonic-gate 		logdbg("%s: extra \"%s\" ignored",
414*0Sstevel@tonic-gate 		    slp->sl_parse->ps_cfile->pf_name, str);
415*0Sstevel@tonic-gate 		return (0);
416*0Sstevel@tonic-gate 	}
417*0Sstevel@tonic-gate 	slp->sl_entry.se_flags =
418*0Sstevel@tonic-gate 	    (slp->sl_entry.se_flags & ~(SEF_WILD|SEF_NOWILD)) |
419*0Sstevel@tonic-gate 	    (*str == 'n' ? SEF_NOWILD : SEF_WILD);
420*0Sstevel@tonic-gate 	return (0);
421*0Sstevel@tonic-gate }
422*0Sstevel@tonic-gate 
423*0Sstevel@tonic-gate /*
424*0Sstevel@tonic-gate  * Handle "debug" option.
425*0Sstevel@tonic-gate  */
426*0Sstevel@tonic-gate /*ARGSUSED*/
427*0Sstevel@tonic-gate static int
428*0Sstevel@tonic-gate set_debug(struct service_list *slp, const char *str)
429*0Sstevel@tonic-gate {
430*0Sstevel@tonic-gate 	slp->sl_entry.se_debug++;
431*0Sstevel@tonic-gate 	if (ISGLOBAL(slp) && (slp->sl_parse->ps_flags & PSF_SETLEVEL)) {
432*0Sstevel@tonic-gate 		log_level = slp->sl_entry.se_debug;
433*0Sstevel@tonic-gate 	}
434*0Sstevel@tonic-gate 	return (0);
435*0Sstevel@tonic-gate }
436*0Sstevel@tonic-gate 
437*0Sstevel@tonic-gate /*
438*0Sstevel@tonic-gate  * Handle "nodebug" option.
439*0Sstevel@tonic-gate  */
440*0Sstevel@tonic-gate /*ARGSUSED*/
441*0Sstevel@tonic-gate static int
442*0Sstevel@tonic-gate set_nodebug(struct service_list *slp, const char *str)
443*0Sstevel@tonic-gate {
444*0Sstevel@tonic-gate 	slp->sl_entry.se_flags |= SEF_DEBUGCLR;
445*0Sstevel@tonic-gate 	slp->sl_entry.se_debug = 0;
446*0Sstevel@tonic-gate 	if (ISGLOBAL(slp) && (slp->sl_parse->ps_flags & PSF_SETLEVEL)) {
447*0Sstevel@tonic-gate 		log_level = slp->sl_entry.se_debug;
448*0Sstevel@tonic-gate 	}
449*0Sstevel@tonic-gate 	return (0);
450*0Sstevel@tonic-gate }
451*0Sstevel@tonic-gate 
452*0Sstevel@tonic-gate /*
453*0Sstevel@tonic-gate  * Handle all plain string options; "server", "pppd", "path", "extra",
454*0Sstevel@tonic-gate  * and "log".
455*0Sstevel@tonic-gate  */
456*0Sstevel@tonic-gate static int
457*0Sstevel@tonic-gate set_string(struct service_list *slp, const char *str)
458*0Sstevel@tonic-gate {
459*0Sstevel@tonic-gate 	char **cpp;
460*0Sstevel@tonic-gate 
461*0Sstevel@tonic-gate 	assert(!(slp->sl_entry.se_flags &
462*0Sstevel@tonic-gate 	    (SEF_CSERVER|SEF_CPPPD|SEF_CPATH|SEF_CEXTRA|SEF_CLOG)));
463*0Sstevel@tonic-gate 	switch (slp->sl_parse->ps_state) {
464*0Sstevel@tonic-gate 	case ksServer:
465*0Sstevel@tonic-gate 		cpp = &slp->sl_entry.se_server;
466*0Sstevel@tonic-gate 		break;
467*0Sstevel@tonic-gate 	case ksPppd:
468*0Sstevel@tonic-gate 		cpp = &slp->sl_entry.se_pppd;
469*0Sstevel@tonic-gate 		break;
470*0Sstevel@tonic-gate 	case ksPath:
471*0Sstevel@tonic-gate 		cpp = &slp->sl_entry.se_path;
472*0Sstevel@tonic-gate 		break;
473*0Sstevel@tonic-gate 	case ksExtra:
474*0Sstevel@tonic-gate 		cpp = &slp->sl_entry.se_extra;
475*0Sstevel@tonic-gate 		break;
476*0Sstevel@tonic-gate 	case ksLog:
477*0Sstevel@tonic-gate 		cpp = &slp->sl_entry.se_log;
478*0Sstevel@tonic-gate 		break;
479*0Sstevel@tonic-gate 	default:
480*0Sstevel@tonic-gate 		assert(0);
481*0Sstevel@tonic-gate 		return (-1);
482*0Sstevel@tonic-gate 	}
483*0Sstevel@tonic-gate 	if (*cpp != NULL)
484*0Sstevel@tonic-gate 		free(*cpp);
485*0Sstevel@tonic-gate 	*cpp = strsave(str);
486*0Sstevel@tonic-gate 	return (0);
487*0Sstevel@tonic-gate }
488*0Sstevel@tonic-gate 
489*0Sstevel@tonic-gate /*
490*0Sstevel@tonic-gate  * Handle "file <name>" option.  Close out current service (if any)
491*0Sstevel@tonic-gate  * and begin parsing from new file.
492*0Sstevel@tonic-gate  */
493*0Sstevel@tonic-gate static int
494*0Sstevel@tonic-gate set_file(struct service_list *slp, const char *str)
495*0Sstevel@tonic-gate {
496*0Sstevel@tonic-gate 	FILE *fp;
497*0Sstevel@tonic-gate 	struct per_file *pfp;
498*0Sstevel@tonic-gate 	struct parse_state *psp;
499*0Sstevel@tonic-gate 
500*0Sstevel@tonic-gate 	close_service(slp);
501*0Sstevel@tonic-gate 
502*0Sstevel@tonic-gate 	if ((fp = fopen(str, "r")) == NULL) {
503*0Sstevel@tonic-gate 		logwarn("%s: %s: %s", slp->sl_parse->ps_cfile->pf_name, str,
504*0Sstevel@tonic-gate 		    mystrerror(errno));
505*0Sstevel@tonic-gate 		return (-1);
506*0Sstevel@tonic-gate 	}
507*0Sstevel@tonic-gate 	pfp = (struct per_file *)calloc(sizeof (*pfp) + strlen(str) + 1, 1);
508*0Sstevel@tonic-gate 	if (pfp == NULL) {
509*0Sstevel@tonic-gate 		logerr("no memory for parsing file %s", str);
510*0Sstevel@tonic-gate 		(void) fclose(fp);
511*0Sstevel@tonic-gate 		return (-1);
512*0Sstevel@tonic-gate 	}
513*0Sstevel@tonic-gate 	logdbg("config file %s open", str);
514*0Sstevel@tonic-gate 
515*0Sstevel@tonic-gate 	/* Fill in new file structure. */
516*0Sstevel@tonic-gate 	pfp->pf_name = (const char *)(pfp+1);
517*0Sstevel@tonic-gate 	(void) strcpy((char *)(pfp+1), str);
518*0Sstevel@tonic-gate 	pfp->pf_input = fp;
519*0Sstevel@tonic-gate 	psp = slp->sl_parse;
520*0Sstevel@tonic-gate 	pfp->pf_prev = psp->ps_cfile;
521*0Sstevel@tonic-gate 	psp->ps_cfile = pfp;
522*0Sstevel@tonic-gate 
523*0Sstevel@tonic-gate 	/* Start off in global context for this file. */
524*0Sstevel@tonic-gate 	psp->ps_csvc = &pfp->pf_global;
525*0Sstevel@tonic-gate 	pfp->pf_global.sl_parse = psp;
526*0Sstevel@tonic-gate 	pfp->pf_global.sl_entry.se_name = "<global>";
527*0Sstevel@tonic-gate 	return (0);
528*0Sstevel@tonic-gate }
529*0Sstevel@tonic-gate 
530*0Sstevel@tonic-gate /*
531*0Sstevel@tonic-gate  * Handle "device <list>" option.
532*0Sstevel@tonic-gate  */
533*0Sstevel@tonic-gate static int
534*0Sstevel@tonic-gate set_device(struct service_list *slp, const char *str)
535*0Sstevel@tonic-gate {
536*0Sstevel@tonic-gate 	struct parse_state *psp = slp->sl_parse;
537*0Sstevel@tonic-gate 	struct device_list *dlp;
538*0Sstevel@tonic-gate 	struct device_list *dln;
539*0Sstevel@tonic-gate 	struct device_list **dlpp;
540*0Sstevel@tonic-gate 	const char *cp;
541*0Sstevel@tonic-gate 	int len;
542*0Sstevel@tonic-gate 
543*0Sstevel@tonic-gate 	/* Can't use this option in the per-device files. */
544*0Sstevel@tonic-gate 	if (psp->ps_flags & PSF_PERDEV) {
545*0Sstevel@tonic-gate 		logerr("\"device %s\" ignored in %s", str,
546*0Sstevel@tonic-gate 		    psp->ps_cfile->pf_name);
547*0Sstevel@tonic-gate 		return (0);
548*0Sstevel@tonic-gate 	}
549*0Sstevel@tonic-gate 
550*0Sstevel@tonic-gate 	if (strcmp(str, "*") == 0 || strcmp(str, "all") == 0) {
551*0Sstevel@tonic-gate 		if (!(slp->sl_entry.se_flags & SEF_CDEV))
552*0Sstevel@tonic-gate 			free_device_list(slp->sl_dev);
553*0Sstevel@tonic-gate 		slp->sl_dev = psp->ps_star;
554*0Sstevel@tonic-gate 		slp->sl_entry.se_flags |= SEF_CDEV;
555*0Sstevel@tonic-gate 	} else {
556*0Sstevel@tonic-gate 		dlpp = &dlp;
557*0Sstevel@tonic-gate 		for (;;) {
558*0Sstevel@tonic-gate 			while (isspace(*str) || *str == ',')
559*0Sstevel@tonic-gate 				str++;
560*0Sstevel@tonic-gate 			if (*str == '\0')
561*0Sstevel@tonic-gate 				break;
562*0Sstevel@tonic-gate 			cp = str;
563*0Sstevel@tonic-gate 			while (*str != '\0' && !isspace(*str) && *str != ',')
564*0Sstevel@tonic-gate 				str++;
565*0Sstevel@tonic-gate 			len = str - cp;
566*0Sstevel@tonic-gate 			if ((len == 1 && *cp == '*') ||
567*0Sstevel@tonic-gate 			    (len == 3 && strncmp(cp, "all", 3) == 0)) {
568*0Sstevel@tonic-gate 				logerr("%s: cannot use %.*s in device list",
569*0Sstevel@tonic-gate 				    psp->ps_cfile->pf_name, len, cp);
570*0Sstevel@tonic-gate 				continue;
571*0Sstevel@tonic-gate 			}
572*0Sstevel@tonic-gate 			dln = (struct device_list *)malloc(sizeof (*dln) +
573*0Sstevel@tonic-gate 			    len + 1);
574*0Sstevel@tonic-gate 			if (dln == NULL) {
575*0Sstevel@tonic-gate 				logerr("no memory for device name");
576*0Sstevel@tonic-gate 				break;
577*0Sstevel@tonic-gate 			}
578*0Sstevel@tonic-gate 			dln->dl_name = (const char *)(dln + 1);
579*0Sstevel@tonic-gate 			/* Cannot use strcpy because cp isn't terminated. */
580*0Sstevel@tonic-gate 			(void) memcpy(dln + 1, cp, len);
581*0Sstevel@tonic-gate 			((char *)(dln + 1))[len] = '\0';
582*0Sstevel@tonic-gate 			logdbg("%s: device %s", psp->ps_cfile->pf_name,
583*0Sstevel@tonic-gate 			    dln->dl_name);
584*0Sstevel@tonic-gate 			*dlpp = dln;
585*0Sstevel@tonic-gate 			dlpp = &dln->dl_next;
586*0Sstevel@tonic-gate 		}
587*0Sstevel@tonic-gate 		*dlpp = NULL;
588*0Sstevel@tonic-gate 
589*0Sstevel@tonic-gate 		dlpp = &slp->sl_dev;
590*0Sstevel@tonic-gate 		if (!(slp->sl_entry.se_flags & SEF_CDEV))
591*0Sstevel@tonic-gate 			while (*dlpp != NULL)
592*0Sstevel@tonic-gate 				dlpp = &(*dlpp)->dl_next;
593*0Sstevel@tonic-gate 		*dlpp = dlp;
594*0Sstevel@tonic-gate 		slp->sl_entry.se_flags &= ~SEF_CDEV;
595*0Sstevel@tonic-gate 	}
596*0Sstevel@tonic-gate 
597*0Sstevel@tonic-gate 	return (0);
598*0Sstevel@tonic-gate }
599*0Sstevel@tonic-gate 
600*0Sstevel@tonic-gate /*
601*0Sstevel@tonic-gate  * Handle <list> portion of "client [except] <list>" option.  Attach
602*0Sstevel@tonic-gate  * to list of filters in reverse order.
603*0Sstevel@tonic-gate  */
604*0Sstevel@tonic-gate static int
605*0Sstevel@tonic-gate set_client(struct service_list *slp, const char *str)
606*0Sstevel@tonic-gate {
607*0Sstevel@tonic-gate 	struct parse_state *psp = slp->sl_parse;
608*0Sstevel@tonic-gate 	struct filter_entry *fep;
609*0Sstevel@tonic-gate 	struct filter_entry *fen;
610*0Sstevel@tonic-gate 	const char *cp;
611*0Sstevel@tonic-gate 	int len;
612*0Sstevel@tonic-gate 	char hbuf[MAXHOSTNAMELEN];
613*0Sstevel@tonic-gate 	struct ether_addr ea;
614*0Sstevel@tonic-gate 	struct ether_addr mask;
615*0Sstevel@tonic-gate 	uchar_t *ucp;
616*0Sstevel@tonic-gate 	uchar_t *mcp;
617*0Sstevel@tonic-gate 
618*0Sstevel@tonic-gate 	/* Head of list. */
619*0Sstevel@tonic-gate 	fep = slp->sl_entry.se_flist;
620*0Sstevel@tonic-gate 	for (;;) {
621*0Sstevel@tonic-gate 		while (isspace(*str) || *str == ',')
622*0Sstevel@tonic-gate 			str++;
623*0Sstevel@tonic-gate 		if (*str == '\0')
624*0Sstevel@tonic-gate 			break;
625*0Sstevel@tonic-gate 		cp = str;
626*0Sstevel@tonic-gate 		while (*str != '\0' && !isspace(*str) && *str != ',')
627*0Sstevel@tonic-gate 			str++;
628*0Sstevel@tonic-gate 		len = str - cp;
629*0Sstevel@tonic-gate 		(void) memcpy(hbuf, cp, len);
630*0Sstevel@tonic-gate 		hbuf[len] = '\0';
631*0Sstevel@tonic-gate 		mcp = mask.ether_addr_octet;
632*0Sstevel@tonic-gate 		mcp[0] = mcp[1] = mcp[2] = mcp[3] = mcp[4] = mcp[5] = 0xFF;
633*0Sstevel@tonic-gate 		if (ether_hostton(hbuf, &ea) != 0) {
634*0Sstevel@tonic-gate 			ucp = ea.ether_addr_octet;
635*0Sstevel@tonic-gate 			while (cp < str) {
636*0Sstevel@tonic-gate 				if (ucp >= ea.ether_addr_octet + sizeof (ea))
637*0Sstevel@tonic-gate 					break;
638*0Sstevel@tonic-gate 				if (*cp == '*') {
639*0Sstevel@tonic-gate 					*mcp++ = *ucp++ = 0;
640*0Sstevel@tonic-gate 					cp++;
641*0Sstevel@tonic-gate 				} else {
642*0Sstevel@tonic-gate 					if (!isxdigit(*cp))
643*0Sstevel@tonic-gate 						break;
644*0Sstevel@tonic-gate 					*ucp = hexdecode(*cp++);
645*0Sstevel@tonic-gate 					if (cp < str && isxdigit(*cp)) {
646*0Sstevel@tonic-gate 						*ucp = (*ucp << 4) |
647*0Sstevel@tonic-gate 						    hexdecode(*cp++);
648*0Sstevel@tonic-gate 					}
649*0Sstevel@tonic-gate 					ucp++;
650*0Sstevel@tonic-gate 					*mcp++ = 0xFF;
651*0Sstevel@tonic-gate 				}
652*0Sstevel@tonic-gate 				if (cp < str) {
653*0Sstevel@tonic-gate 					if (*cp != ':' || cp + 1 == str)
654*0Sstevel@tonic-gate 						break;
655*0Sstevel@tonic-gate 					cp++;
656*0Sstevel@tonic-gate 				}
657*0Sstevel@tonic-gate 			}
658*0Sstevel@tonic-gate 			if (cp < str) {
659*0Sstevel@tonic-gate 				logerr("%s: illegal Ethernet address %.*s",
660*0Sstevel@tonic-gate 				    psp->ps_cfile->pf_name, len, cp);
661*0Sstevel@tonic-gate 				continue;
662*0Sstevel@tonic-gate 			}
663*0Sstevel@tonic-gate 		}
664*0Sstevel@tonic-gate 		fen = (struct filter_entry *)malloc(sizeof (*fen));
665*0Sstevel@tonic-gate 		if (fen == NULL) {
666*0Sstevel@tonic-gate 			logerr("unable to allocate memory for filter");
667*0Sstevel@tonic-gate 			break;
668*0Sstevel@tonic-gate 		}
669*0Sstevel@tonic-gate 		fen->fe_isexcept = psp->ps_state == ksClientE;
670*0Sstevel@tonic-gate 		fen->fe_prevcopy = 0;
671*0Sstevel@tonic-gate 		(void) memcpy(&fen->fe_mac, &ea, sizeof (fen->fe_mac));
672*0Sstevel@tonic-gate 		(void) memcpy(&fen->fe_mask, &mask, sizeof (fen->fe_mask));
673*0Sstevel@tonic-gate 		fen->fe_prev = fep;
674*0Sstevel@tonic-gate 		fep = fen;
675*0Sstevel@tonic-gate 	}
676*0Sstevel@tonic-gate 	slp->sl_entry.se_flist = fep;
677*0Sstevel@tonic-gate 	return (0);
678*0Sstevel@tonic-gate }
679*0Sstevel@tonic-gate 
680*0Sstevel@tonic-gate /*
681*0Sstevel@tonic-gate  * Handle "user <name>" option.
682*0Sstevel@tonic-gate  */
683*0Sstevel@tonic-gate static int
684*0Sstevel@tonic-gate set_user(struct service_list *slp, const char *str)
685*0Sstevel@tonic-gate {
686*0Sstevel@tonic-gate 	struct passwd *pw;
687*0Sstevel@tonic-gate 	char *cp;
688*0Sstevel@tonic-gate 	uid_t myuid, uid;
689*0Sstevel@tonic-gate 
690*0Sstevel@tonic-gate 	if ((pw = getpwnam(str)) == NULL) {
691*0Sstevel@tonic-gate 		uid = (uid_t)strtol(str, &cp, 0);
692*0Sstevel@tonic-gate 		if (str == cp || *cp != '\0') {
693*0Sstevel@tonic-gate 			logerr("%s:  bad user name \"%s\"",
694*0Sstevel@tonic-gate 			    slp->sl_parse->ps_cfile->pf_name, str);
695*0Sstevel@tonic-gate 			return (0);
696*0Sstevel@tonic-gate 		}
697*0Sstevel@tonic-gate 	} else {
698*0Sstevel@tonic-gate 		uid = pw->pw_uid;
699*0Sstevel@tonic-gate 	}
700*0Sstevel@tonic-gate 	slp->sl_entry.se_uid = uid;
701*0Sstevel@tonic-gate 	myuid = getuid();
702*0Sstevel@tonic-gate 	if (myuid != 0) {
703*0Sstevel@tonic-gate 		if (myuid == uid)
704*0Sstevel@tonic-gate 			return (0);
705*0Sstevel@tonic-gate 		logdbg("%s:  not root; ignoring attempt to set UID %d (%s)",
706*0Sstevel@tonic-gate 		    slp->sl_parse->ps_cfile->pf_name, uid, str);
707*0Sstevel@tonic-gate 		return (0);
708*0Sstevel@tonic-gate 	}
709*0Sstevel@tonic-gate 	slp->sl_entry.se_flags |= SEF_UIDSET;
710*0Sstevel@tonic-gate 	return (0);
711*0Sstevel@tonic-gate }
712*0Sstevel@tonic-gate 
713*0Sstevel@tonic-gate /*
714*0Sstevel@tonic-gate  * Handle "group <name>" option.
715*0Sstevel@tonic-gate  */
716*0Sstevel@tonic-gate static int
717*0Sstevel@tonic-gate set_group(struct service_list *slp, const char *str)
718*0Sstevel@tonic-gate {
719*0Sstevel@tonic-gate 	struct group *gr;
720*0Sstevel@tonic-gate 	char *cp;
721*0Sstevel@tonic-gate 	gid_t gid;
722*0Sstevel@tonic-gate 
723*0Sstevel@tonic-gate 	if ((gr = getgrnam(str)) == NULL) {
724*0Sstevel@tonic-gate 		gid = (gid_t)strtol(str, &cp, 0);
725*0Sstevel@tonic-gate 		if (str == cp || *cp != '\0') {
726*0Sstevel@tonic-gate 			logerr("%s:  bad group name \"%s\"",
727*0Sstevel@tonic-gate 			    slp->sl_parse->ps_cfile->pf_name, str);
728*0Sstevel@tonic-gate 			return (0);
729*0Sstevel@tonic-gate 		}
730*0Sstevel@tonic-gate 	} else {
731*0Sstevel@tonic-gate 		gid = gr->gr_gid;
732*0Sstevel@tonic-gate 	}
733*0Sstevel@tonic-gate 	slp->sl_entry.se_gid = gid;
734*0Sstevel@tonic-gate 	if (getuid() != 0) {
735*0Sstevel@tonic-gate 		logdbg("%s:  not root; ignoring attempt to set GID %d (%s)",
736*0Sstevel@tonic-gate 		    slp->sl_parse->ps_cfile->pf_name, gid, str);
737*0Sstevel@tonic-gate 		return (0);
738*0Sstevel@tonic-gate 	}
739*0Sstevel@tonic-gate 	slp->sl_entry.se_flags |= SEF_GIDSET;
740*0Sstevel@tonic-gate 	return (0);
741*0Sstevel@tonic-gate }
742*0Sstevel@tonic-gate 
743*0Sstevel@tonic-gate /*
744*0Sstevel@tonic-gate  * This state machine is used to parse the configuration files.  The
745*0Sstevel@tonic-gate  * "kwe_in" is the state in which the keyword is recognized.  The
746*0Sstevel@tonic-gate  * "kwe_out" is the state that the keyword produces.
747*0Sstevel@tonic-gate  */
748*0Sstevel@tonic-gate struct kw_entry {
749*0Sstevel@tonic-gate 	const char *kwe_word;
750*0Sstevel@tonic-gate 	enum key_state kwe_in;
751*0Sstevel@tonic-gate 	enum key_state kwe_out;
752*0Sstevel@tonic-gate 	int (*kwe_func)(struct service_list *slp, const char *str);
753*0Sstevel@tonic-gate };
754*0Sstevel@tonic-gate 
755*0Sstevel@tonic-gate static const struct kw_entry key_list[] = {
756*0Sstevel@tonic-gate 	{ "service",	ksDefault,	ksService,	NULL },
757*0Sstevel@tonic-gate 	{ "device",	ksDefault,	ksDevice,	NULL },
758*0Sstevel@tonic-gate 	{ "client",	ksDefault,	ksClient,	NULL },
759*0Sstevel@tonic-gate 	{ "except",	ksClient,	ksClientE,	NULL },
760*0Sstevel@tonic-gate 	{ "wildcard",	ksDefault,	ksDefault,	set_wildcard },
761*0Sstevel@tonic-gate 	{ "nowildcard",	ksDefault,	ksDefault,	set_wildcard },
762*0Sstevel@tonic-gate 	{ "server",	ksDefault,	ksServer,	NULL },
763*0Sstevel@tonic-gate 	{ "pppd",	ksDefault,	ksPppd,		NULL },
764*0Sstevel@tonic-gate 	{ "debug",	ksDefault,	ksDefault,	set_debug },
765*0Sstevel@tonic-gate 	{ "nodebug",	ksDefault,	ksDefault,	set_nodebug },
766*0Sstevel@tonic-gate 	{ "file",	ksDefault,	ksFile,		NULL },
767*0Sstevel@tonic-gate 	{ "path",	ksDefault,	ksPath,		NULL },
768*0Sstevel@tonic-gate 	{ "extra",	ksDefault,	ksExtra,	NULL },
769*0Sstevel@tonic-gate 	{ "log",	ksDefault,	ksLog,		NULL },
770*0Sstevel@tonic-gate 	{ "user",	ksDefault,	ksUser,		NULL },
771*0Sstevel@tonic-gate 	{ "group",	ksDefault,	ksGroup,	NULL },
772*0Sstevel@tonic-gate 	/* Wildcards only past this point. */
773*0Sstevel@tonic-gate 	{ "",		ksService,	ksDefault,	set_service },
774*0Sstevel@tonic-gate 	{ "",		ksDevice,	ksDefault,	set_device },
775*0Sstevel@tonic-gate 	{ "",		ksClient,	ksDefault,	set_client },
776*0Sstevel@tonic-gate 	{ "",		ksClientE,	ksDefault,	set_client },
777*0Sstevel@tonic-gate 	{ "",		ksServer,	ksDefault,	set_string },
778*0Sstevel@tonic-gate 	{ "",		ksPppd,		ksDefault,	set_string },
779*0Sstevel@tonic-gate 	{ "",		ksFile,		ksDefault,	set_file },
780*0Sstevel@tonic-gate 	{ "",		ksPath,		ksDefault,	set_string },
781*0Sstevel@tonic-gate 	{ "",		ksExtra,	ksDefault,	set_string },
782*0Sstevel@tonic-gate 	{ "",		ksLog,		ksDefault,	set_string },
783*0Sstevel@tonic-gate 	{ "",		ksUser,		ksDefault,	set_user },
784*0Sstevel@tonic-gate 	{ "",		ksGroup,	ksDefault,	set_group },
785*0Sstevel@tonic-gate 	{ NULL, ksDefault, ksDefault, NULL }
786*0Sstevel@tonic-gate };
787*0Sstevel@tonic-gate 
788*0Sstevel@tonic-gate /*
789*0Sstevel@tonic-gate  * Produce a string for the keyword that would have gotten us into the
790*0Sstevel@tonic-gate  * current state.
791*0Sstevel@tonic-gate  */
792*0Sstevel@tonic-gate static const char *
793*0Sstevel@tonic-gate after_key(enum key_state kstate)
794*0Sstevel@tonic-gate {
795*0Sstevel@tonic-gate 	const struct kw_entry *kep;
796*0Sstevel@tonic-gate 
797*0Sstevel@tonic-gate 	for (kep = key_list; kep->kwe_word != NULL; kep++)
798*0Sstevel@tonic-gate 		if (kep->kwe_out == kstate)
799*0Sstevel@tonic-gate 			return (kep->kwe_word);
800*0Sstevel@tonic-gate 	return ("nothing");
801*0Sstevel@tonic-gate }
802*0Sstevel@tonic-gate 
803*0Sstevel@tonic-gate /*
804*0Sstevel@tonic-gate  * Handle end-of-file processing -- close service, close file, revert
805*0Sstevel@tonic-gate  * to global context in previous include file nest level.
806*0Sstevel@tonic-gate  */
807*0Sstevel@tonic-gate static void
808*0Sstevel@tonic-gate file_end(struct parse_state *psp)
809*0Sstevel@tonic-gate {
810*0Sstevel@tonic-gate 	struct per_file *pfp;
811*0Sstevel@tonic-gate 
812*0Sstevel@tonic-gate 	/* Must not be in the middle of parsing a multi-word sequence now. */
813*0Sstevel@tonic-gate 	if (psp->ps_state != ksDefault) {
814*0Sstevel@tonic-gate 		logerr("%s ends with \"%s\"", psp->ps_cfile->pf_name,
815*0Sstevel@tonic-gate 		    after_key(psp->ps_state));
816*0Sstevel@tonic-gate 		psp->ps_state = ksDefault;
817*0Sstevel@tonic-gate 	}
818*0Sstevel@tonic-gate 	close_service(psp->ps_csvc);
819*0Sstevel@tonic-gate 	if ((pfp = psp->ps_cfile) != NULL) {
820*0Sstevel@tonic-gate 		/* Put this file on the list of finished files. */
821*0Sstevel@tonic-gate 		psp->ps_cfile = pfp->pf_prev;
822*0Sstevel@tonic-gate 		pfp->pf_prev = psp->ps_files;
823*0Sstevel@tonic-gate 		psp->ps_files = pfp;
824*0Sstevel@tonic-gate 		if (pfp->pf_input != NULL) {
825*0Sstevel@tonic-gate 			logdbg("file %s closed", pfp->pf_name);
826*0Sstevel@tonic-gate 			(void) fclose(pfp->pf_input);
827*0Sstevel@tonic-gate 			pfp->pf_input = NULL;
828*0Sstevel@tonic-gate 		}
829*0Sstevel@tonic-gate 
830*0Sstevel@tonic-gate 		/* Back up to previous file, if any, and set global context. */
831*0Sstevel@tonic-gate 		if ((pfp = psp->ps_cfile) != NULL)
832*0Sstevel@tonic-gate 			psp->ps_csvc = &pfp->pf_global;
833*0Sstevel@tonic-gate 	}
834*0Sstevel@tonic-gate }
835*0Sstevel@tonic-gate 
836*0Sstevel@tonic-gate /*
837*0Sstevel@tonic-gate  * Dispatch a single keyword against the parser state machine or
838*0Sstevel@tonic-gate  * handle an environment variable assignment.  The input is a string
839*0Sstevel@tonic-gate  * containing the single word to be dispatched.
840*0Sstevel@tonic-gate  */
841*0Sstevel@tonic-gate static int
842*0Sstevel@tonic-gate dispatch_keyword(struct parse_state *psp, const char *keybuf)
843*0Sstevel@tonic-gate {
844*0Sstevel@tonic-gate 	const struct kw_entry *kep;
845*0Sstevel@tonic-gate 	int retv;
846*0Sstevel@tonic-gate 	char *cp;
847*0Sstevel@tonic-gate 	char *env;
848*0Sstevel@tonic-gate 	char **evlist;
849*0Sstevel@tonic-gate 	int len;
850*0Sstevel@tonic-gate 
851*0Sstevel@tonic-gate 	retv = 0;
852*0Sstevel@tonic-gate 	for (kep = key_list; kep->kwe_word != NULL; kep++) {
853*0Sstevel@tonic-gate 		if (kep->kwe_in == psp->ps_state &&
854*0Sstevel@tonic-gate 		    (*kep->kwe_word == '\0' ||
855*0Sstevel@tonic-gate 			strcasecmp(kep->kwe_word, keybuf) == 0)) {
856*0Sstevel@tonic-gate 			if (kep->kwe_func != NULL)
857*0Sstevel@tonic-gate 				retv = (*kep->kwe_func)(psp->ps_csvc, keybuf);
858*0Sstevel@tonic-gate 			psp->ps_state = kep->kwe_out;
859*0Sstevel@tonic-gate 			return (retv);
860*0Sstevel@tonic-gate 		}
861*0Sstevel@tonic-gate 	}
862*0Sstevel@tonic-gate 	if (strchr(keybuf, '=') != NULL) {
863*0Sstevel@tonic-gate 		if ((cp = strsave(keybuf)) == NULL) {
864*0Sstevel@tonic-gate 			logerr("no memory to save %s", keybuf);
865*0Sstevel@tonic-gate 			return (0);
866*0Sstevel@tonic-gate 		}
867*0Sstevel@tonic-gate 		len = (strchr(cp, '=') - cp) + 1;
868*0Sstevel@tonic-gate 		if ((evlist = psp->ps_evlist) == NULL) {
869*0Sstevel@tonic-gate 			psp->ps_evlist = evlist =
870*0Sstevel@tonic-gate 			    (char **)malloc(8 * sizeof (*evlist));
871*0Sstevel@tonic-gate 			if (evlist == NULL) {
872*0Sstevel@tonic-gate 				logerr("no memory for evlist");
873*0Sstevel@tonic-gate 				free(cp);
874*0Sstevel@tonic-gate 				return (0);
875*0Sstevel@tonic-gate 			}
876*0Sstevel@tonic-gate 			psp->ps_evsize = 8;
877*0Sstevel@tonic-gate 			evlist[0] = evlist[1] = NULL;
878*0Sstevel@tonic-gate 		} else {
879*0Sstevel@tonic-gate 			while ((env = *evlist) != NULL) {
880*0Sstevel@tonic-gate 				if (strncmp(cp, env, len) == 0)
881*0Sstevel@tonic-gate 					break;
882*0Sstevel@tonic-gate 				evlist++;
883*0Sstevel@tonic-gate 			}
884*0Sstevel@tonic-gate 			if (env == NULL &&
885*0Sstevel@tonic-gate 			    evlist-psp->ps_evlist >= psp->ps_evsize-1) {
886*0Sstevel@tonic-gate 				evlist = (char **)realloc(psp->ps_evlist,
887*0Sstevel@tonic-gate 				    (psp->ps_evsize + 8) * sizeof (*evlist));
888*0Sstevel@tonic-gate 				if (evlist == NULL) {
889*0Sstevel@tonic-gate 					logerr("cannot realloc evlist to %d",
890*0Sstevel@tonic-gate 					    psp->ps_evsize + 8);
891*0Sstevel@tonic-gate 					free(cp);
892*0Sstevel@tonic-gate 					return (0);
893*0Sstevel@tonic-gate 				}
894*0Sstevel@tonic-gate 				psp->ps_evlist = evlist;
895*0Sstevel@tonic-gate 				evlist += psp->ps_evsize - 1;
896*0Sstevel@tonic-gate 				psp->ps_evsize += 8;
897*0Sstevel@tonic-gate 				evlist[1] = NULL;
898*0Sstevel@tonic-gate 			}
899*0Sstevel@tonic-gate 		}
900*0Sstevel@tonic-gate 		logdbg("setenv \"%s\"", cp);
901*0Sstevel@tonic-gate 		if (*evlist != NULL)
902*0Sstevel@tonic-gate 			free(*evlist);
903*0Sstevel@tonic-gate 		*evlist = cp;
904*0Sstevel@tonic-gate 		return (0);
905*0Sstevel@tonic-gate 	}
906*0Sstevel@tonic-gate 	logerr("%s: unknown keyword '%s'", psp->ps_cfile->pf_name, keybuf);
907*0Sstevel@tonic-gate 	return (-1);
908*0Sstevel@tonic-gate }
909*0Sstevel@tonic-gate 
910*0Sstevel@tonic-gate /*
911*0Sstevel@tonic-gate  * Modified version of standard getenv; looks in locally-stored
912*0Sstevel@tonic-gate  * environment first.  This function exists because we need to be able
913*0Sstevel@tonic-gate  * to revert to the original environment during a reread (SIGHUP), and
914*0Sstevel@tonic-gate  * the putenv() function overwrites that environment.
915*0Sstevel@tonic-gate  */
916*0Sstevel@tonic-gate static char *
917*0Sstevel@tonic-gate my_getenv(struct parse_state *psp, char *estr)
918*0Sstevel@tonic-gate {
919*0Sstevel@tonic-gate 	char **evlist, *ent;
920*0Sstevel@tonic-gate 	int elen;
921*0Sstevel@tonic-gate 
922*0Sstevel@tonic-gate 	if (psp != NULL && (evlist = psp->ps_evlist) != NULL) {
923*0Sstevel@tonic-gate 		elen = strlen(estr);
924*0Sstevel@tonic-gate 		while ((ent = *evlist++) != NULL) {
925*0Sstevel@tonic-gate 			if (strncmp(ent, estr, elen) == 0 &&
926*0Sstevel@tonic-gate 			    ent[elen] == '=')
927*0Sstevel@tonic-gate 				return (ent + elen + 1);
928*0Sstevel@tonic-gate 		}
929*0Sstevel@tonic-gate 	}
930*0Sstevel@tonic-gate 	return (getenv(estr));
931*0Sstevel@tonic-gate }
932*0Sstevel@tonic-gate 
933*0Sstevel@tonic-gate /*
934*0Sstevel@tonic-gate  * Expand an environment variable at the end of current buffer and
935*0Sstevel@tonic-gate  * return pointer to next spot in buffer for character append.  psp
936*0Sstevel@tonic-gate  * context may be null.
937*0Sstevel@tonic-gate  */
938*0Sstevel@tonic-gate static char *
939*0Sstevel@tonic-gate env_replace(struct parse_state *psp, char *keybuf, char kwstate)
940*0Sstevel@tonic-gate {
941*0Sstevel@tonic-gate 	char *cpe;
942*0Sstevel@tonic-gate 	char *cp;
943*0Sstevel@tonic-gate 
944*0Sstevel@tonic-gate 	if ((cp = strrchr(keybuf, kwstate)) != NULL) {
945*0Sstevel@tonic-gate 		if ((cpe = my_getenv(psp, cp + 1)) != NULL) {
946*0Sstevel@tonic-gate 			*cp = '\0';
947*0Sstevel@tonic-gate 			(void) strncat(cp, cpe,
948*0Sstevel@tonic-gate 			    MAX_KEYWORD - (cp - keybuf) - 1);
949*0Sstevel@tonic-gate 			keybuf[MAX_KEYWORD - 1] = '\0';
950*0Sstevel@tonic-gate 			cp += strlen(cp);
951*0Sstevel@tonic-gate 		} else {
952*0Sstevel@tonic-gate 			logerr("unknown variable \"%s\"", cp + 1);
953*0Sstevel@tonic-gate 		}
954*0Sstevel@tonic-gate 	} else {
955*0Sstevel@tonic-gate 		/* Should not occur. */
956*0Sstevel@tonic-gate 		cp = keybuf + strlen(keybuf);
957*0Sstevel@tonic-gate 	}
958*0Sstevel@tonic-gate 	return (cp);
959*0Sstevel@tonic-gate }
960*0Sstevel@tonic-gate 
961*0Sstevel@tonic-gate /*
962*0Sstevel@tonic-gate  * Given a character-at-a-time input function, get a delimited keyword
963*0Sstevel@tonic-gate  * from the input.  This function handles the usual escape sequences,
964*0Sstevel@tonic-gate  * quoting, commenting, and environment variable expansion.
965*0Sstevel@tonic-gate  *
966*0Sstevel@tonic-gate  * The standard wordexp(3C) function isn't used here because the POSIX
967*0Sstevel@tonic-gate  * definition is hard to use, and the Solaris implementation is
968*0Sstevel@tonic-gate  * resource-intensive and insecure.  The "hard-to-use" part is that
969*0Sstevel@tonic-gate  * wordexp expands only variables from the environment, and can't
970*0Sstevel@tonic-gate  * handle an environment overlay.  Instead, the caller must use the
971*0Sstevel@tonic-gate  * feeble putenv/getenv interface, and rewinding to the initial
972*0Sstevel@tonic-gate  * environment without leaking storage is hard.  The Solaris
973*0Sstevel@tonic-gate  * implementation invokes an undocumented extensions via
974*0Sstevel@tonic-gate  * fork/exec("/bin/ksh -\005 %s") for every invocation, and gathers
975*0Sstevel@tonic-gate  * the expanded result with pipe.  This makes it slow to execute and
976*0Sstevel@tonic-gate  * exposes the string being expanded to users with access to "ps -f."
977*0Sstevel@tonic-gate  *
978*0Sstevel@tonic-gate  * psp may be null; it's used only for environment variable expansion.
979*0Sstevel@tonic-gate  * Input "flag" is 1 to ignore EOL, '#', and '$'; 0 for normal file parsing.
980*0Sstevel@tonic-gate  *
981*0Sstevel@tonic-gate  * Returns:
982*0Sstevel@tonic-gate  *	0 - keyword parsed.
983*0Sstevel@tonic-gate  *	1 - end of file; no keyword.
984*0Sstevel@tonic-gate  *	2 - end of file after this keyword.
985*0Sstevel@tonic-gate  */
986*0Sstevel@tonic-gate static int
987*0Sstevel@tonic-gate getkeyword(struct parse_state *psp, char *keybuf, int keymax,
988*0Sstevel@tonic-gate     int (*nextchr)(void *), void *arg, int flag)
989*0Sstevel@tonic-gate {
990*0Sstevel@tonic-gate 	char varnest[MAX_NEST];
991*0Sstevel@tonic-gate 	char *kbp;
992*0Sstevel@tonic-gate 	char *vnp;
993*0Sstevel@tonic-gate 	char chr;
994*0Sstevel@tonic-gate 	int ichr;
995*0Sstevel@tonic-gate 	char kwstate;
996*0Sstevel@tonic-gate 	static const char escstr[] = "a\ab\bf\fn\nr\r";
997*0Sstevel@tonic-gate 	const char *cp;
998*0Sstevel@tonic-gate 
999*0Sstevel@tonic-gate 	keymax--;	/* Account for trailing NUL byte */
1000*0Sstevel@tonic-gate 
1001*0Sstevel@tonic-gate 	kwstate = '\0';
1002*0Sstevel@tonic-gate 	kbp = keybuf;
1003*0Sstevel@tonic-gate 	vnp = varnest;
1004*0Sstevel@tonic-gate 	for (;;) {
1005*0Sstevel@tonic-gate 		ichr = (*nextchr)(arg);
1006*0Sstevel@tonic-gate 		chr = (char)ichr;
1007*0Sstevel@tonic-gate 	tryagain:
1008*0Sstevel@tonic-gate 		switch (kwstate) {
1009*0Sstevel@tonic-gate 		case '\\':	/* Start of unquoted escape sequence */
1010*0Sstevel@tonic-gate 		case '|':	/* Start of escape sequence in double quotes */
1011*0Sstevel@tonic-gate 		case '~':	/* Start of escape sequence in single quotes */
1012*0Sstevel@tonic-gate 			/* Convert the character if we can. */
1013*0Sstevel@tonic-gate 			if (chr == '\n')
1014*0Sstevel@tonic-gate 				chr = '\0';
1015*0Sstevel@tonic-gate 			else if (isalpha(chr) &&
1016*0Sstevel@tonic-gate 			    (cp = strchr(escstr, chr)) != NULL)
1017*0Sstevel@tonic-gate 				chr = cp[1];
1018*0Sstevel@tonic-gate 			/* Revert to previous state */
1019*0Sstevel@tonic-gate 			switch (kwstate) {
1020*0Sstevel@tonic-gate 			case '\\':
1021*0Sstevel@tonic-gate 				kwstate = 'A';
1022*0Sstevel@tonic-gate 				break;
1023*0Sstevel@tonic-gate 			case '|':
1024*0Sstevel@tonic-gate 				kwstate = '"';
1025*0Sstevel@tonic-gate 				break;
1026*0Sstevel@tonic-gate 			case '~':
1027*0Sstevel@tonic-gate 				kwstate = '\'';
1028*0Sstevel@tonic-gate 				break;
1029*0Sstevel@tonic-gate 			}
1030*0Sstevel@tonic-gate 			break;
1031*0Sstevel@tonic-gate 		case '"':	/* In double-quote string */
1032*0Sstevel@tonic-gate 			if (!flag && chr == '$') {
1033*0Sstevel@tonic-gate 				/* Handle variable expansion. */
1034*0Sstevel@tonic-gate 				kwstate = '%';
1035*0Sstevel@tonic-gate 				chr = '\0';
1036*0Sstevel@tonic-gate 				break;
1037*0Sstevel@tonic-gate 			}
1038*0Sstevel@tonic-gate 				/* FALLTHROUGH */
1039*0Sstevel@tonic-gate 		case '\'':	/* In single-quote string */
1040*0Sstevel@tonic-gate 			if (chr == '\\') {
1041*0Sstevel@tonic-gate 				/* Handle start of escape sequence */
1042*0Sstevel@tonic-gate 				kwstate = kwstate == '"' ? '|' : '~';
1043*0Sstevel@tonic-gate 				chr = '\0';
1044*0Sstevel@tonic-gate 				break;
1045*0Sstevel@tonic-gate 			}
1046*0Sstevel@tonic-gate 			if (chr == kwstate) {
1047*0Sstevel@tonic-gate 				/* End of quoted string; revert to normal */
1048*0Sstevel@tonic-gate 				kwstate = 'A';
1049*0Sstevel@tonic-gate 				chr = '\0';
1050*0Sstevel@tonic-gate 			}
1051*0Sstevel@tonic-gate 			break;
1052*0Sstevel@tonic-gate 		case '$':	/* Start of unquoted variable name */
1053*0Sstevel@tonic-gate 		case '%':	/* Start of variable name in quoted string */
1054*0Sstevel@tonic-gate 			if (chr == '{') {
1055*0Sstevel@tonic-gate 				/* Variable name is bracketed. */
1056*0Sstevel@tonic-gate 				kwstate = chr =
1057*0Sstevel@tonic-gate 				    kwstate == '$' ? '{' : '[';
1058*0Sstevel@tonic-gate 				break;
1059*0Sstevel@tonic-gate 			}
1060*0Sstevel@tonic-gate 			*kbp++ = kwstate = kwstate == '$' ? '+' : '*';
1061*0Sstevel@tonic-gate 				/* FALLTHROUGH */
1062*0Sstevel@tonic-gate 		case '+':	/* Gathering unquoted variable name */
1063*0Sstevel@tonic-gate 		case '*':	/* Gathering variable name in quoted string */
1064*0Sstevel@tonic-gate 			if (chr == '$' &&
1065*0Sstevel@tonic-gate 			    vnp < varnest + sizeof (varnest)) {
1066*0Sstevel@tonic-gate 				*vnp++ = kwstate;
1067*0Sstevel@tonic-gate 				kwstate = '$';
1068*0Sstevel@tonic-gate 				chr = '\0';
1069*0Sstevel@tonic-gate 				break;
1070*0Sstevel@tonic-gate 			}
1071*0Sstevel@tonic-gate 			if (!isalnum(chr) && chr != '_' &&
1072*0Sstevel@tonic-gate 			    chr != '.' && chr != '-') {
1073*0Sstevel@tonic-gate 				*kbp = '\0';
1074*0Sstevel@tonic-gate 				kbp = env_replace(psp, keybuf, kwstate);
1075*0Sstevel@tonic-gate 				if (vnp > varnest)
1076*0Sstevel@tonic-gate 					kwstate = *--vnp;
1077*0Sstevel@tonic-gate 				else
1078*0Sstevel@tonic-gate 					kwstate = kwstate == '+' ?
1079*0Sstevel@tonic-gate 					    'A' : '"';
1080*0Sstevel@tonic-gate 				/* Go reinterpret in new context */
1081*0Sstevel@tonic-gate 				goto tryagain;
1082*0Sstevel@tonic-gate 			}
1083*0Sstevel@tonic-gate 			break;
1084*0Sstevel@tonic-gate 		case '{':	/* Gathering bracketed, unquoted var name */
1085*0Sstevel@tonic-gate 		case '[':	/* Gathering bracketed, quoted var name */
1086*0Sstevel@tonic-gate 			if (chr == '}') {
1087*0Sstevel@tonic-gate 				*kbp = '\0';
1088*0Sstevel@tonic-gate 				kbp = env_replace(psp, keybuf, kwstate);
1089*0Sstevel@tonic-gate 				kwstate = kwstate == '{' ? 'A' : '"';
1090*0Sstevel@tonic-gate 				chr = '\0';
1091*0Sstevel@tonic-gate 			}
1092*0Sstevel@tonic-gate 			break;
1093*0Sstevel@tonic-gate 		case '#':	/* Comment before word state */
1094*0Sstevel@tonic-gate 		case '@':	/* Comment after word state */
1095*0Sstevel@tonic-gate 			if (chr == '\n' || chr == '\r' || ichr == EOF) {
1096*0Sstevel@tonic-gate 				/* At end of line, revert to previous state */
1097*0Sstevel@tonic-gate 				kwstate = kwstate == '#' ? '\0' : ' ';
1098*0Sstevel@tonic-gate 				chr = '\0';
1099*0Sstevel@tonic-gate 				break;
1100*0Sstevel@tonic-gate 			}
1101*0Sstevel@tonic-gate 			chr = '\0';
1102*0Sstevel@tonic-gate 			break;
1103*0Sstevel@tonic-gate 		case '\0':	/* Initial state; no word seen yet. */
1104*0Sstevel@tonic-gate 			if (ichr == EOF || isspace(chr)) {
1105*0Sstevel@tonic-gate 				chr = '\0';	/* Skip over leading spaces */
1106*0Sstevel@tonic-gate 				break;
1107*0Sstevel@tonic-gate 			}
1108*0Sstevel@tonic-gate 			if (chr == '#') {
1109*0Sstevel@tonic-gate 				kwstate = '#';
1110*0Sstevel@tonic-gate 				chr = '\0';	/* Skip over comments */
1111*0Sstevel@tonic-gate 				break;
1112*0Sstevel@tonic-gate 			}
1113*0Sstevel@tonic-gate 			/* Start of keyword seen. */
1114*0Sstevel@tonic-gate 			kwstate = 'A';
1115*0Sstevel@tonic-gate 			/* FALLTHROUGH */
1116*0Sstevel@tonic-gate 		default:	/* Middle of keyword parsing. */
1117*0Sstevel@tonic-gate 			if (ichr == EOF)
1118*0Sstevel@tonic-gate 				break;
1119*0Sstevel@tonic-gate 			if (isspace(chr)) {	/* Space terminates word */
1120*0Sstevel@tonic-gate 				kwstate = ' ';
1121*0Sstevel@tonic-gate 				break;
1122*0Sstevel@tonic-gate 			}
1123*0Sstevel@tonic-gate 			if (chr == '"' || chr == '\'' || chr == '\\') {
1124*0Sstevel@tonic-gate 				kwstate = chr;	/* Begin quote or escape */
1125*0Sstevel@tonic-gate 				chr = '\0';
1126*0Sstevel@tonic-gate 				break;
1127*0Sstevel@tonic-gate 			}
1128*0Sstevel@tonic-gate 			if (flag)	/* Allow ignore; for string reparse */
1129*0Sstevel@tonic-gate 				break;
1130*0Sstevel@tonic-gate 			if (chr == '#') {	/* Comment terminates word */
1131*0Sstevel@tonic-gate 				kwstate = '@';	/* Must consume comment also */
1132*0Sstevel@tonic-gate 				chr = '\0';
1133*0Sstevel@tonic-gate 				break;
1134*0Sstevel@tonic-gate 			}
1135*0Sstevel@tonic-gate 			if (chr == '$') {
1136*0Sstevel@tonic-gate 				kwstate = '$';	/* Begin variable expansion */
1137*0Sstevel@tonic-gate 				chr = '\0';
1138*0Sstevel@tonic-gate 			}
1139*0Sstevel@tonic-gate 			break;
1140*0Sstevel@tonic-gate 		}
1141*0Sstevel@tonic-gate 		/*
1142*0Sstevel@tonic-gate 		 * If we've reached a space at the end of the word,
1143*0Sstevel@tonic-gate 		 * then we're done.
1144*0Sstevel@tonic-gate 		 */
1145*0Sstevel@tonic-gate 		if (ichr == EOF || kwstate == ' ')
1146*0Sstevel@tonic-gate 			break;
1147*0Sstevel@tonic-gate 		/*
1148*0Sstevel@tonic-gate 		 * If there's a character to store and space
1149*0Sstevel@tonic-gate 		 * available, then add it to the string
1150*0Sstevel@tonic-gate 		 */
1151*0Sstevel@tonic-gate 		if (chr != '\0' && kbp < keybuf + keymax)
1152*0Sstevel@tonic-gate 			*kbp++ = (char)chr;
1153*0Sstevel@tonic-gate 	}
1154*0Sstevel@tonic-gate 
1155*0Sstevel@tonic-gate 	*kbp = '\0';
1156*0Sstevel@tonic-gate 
1157*0Sstevel@tonic-gate 	if (ichr == EOF) {
1158*0Sstevel@tonic-gate 		return (kwstate == '\0' ? 1 : 2);
1159*0Sstevel@tonic-gate 	}
1160*0Sstevel@tonic-gate 	return (0);
1161*0Sstevel@tonic-gate }
1162*0Sstevel@tonic-gate 
1163*0Sstevel@tonic-gate /*
1164*0Sstevel@tonic-gate  * Fetch words from current file until all files are closed.  Handles
1165*0Sstevel@tonic-gate  * include files.
1166*0Sstevel@tonic-gate  */
1167*0Sstevel@tonic-gate static void
1168*0Sstevel@tonic-gate parse_from_file(struct parse_state *psp)
1169*0Sstevel@tonic-gate {
1170*0Sstevel@tonic-gate 	char keybuf[MAX_KEYWORD];
1171*0Sstevel@tonic-gate 	int retv;
1172*0Sstevel@tonic-gate 
1173*0Sstevel@tonic-gate 	while (psp->ps_cfile != NULL && psp->ps_cfile->pf_input != NULL) {
1174*0Sstevel@tonic-gate 		retv = getkeyword(psp, keybuf, sizeof (keybuf),
1175*0Sstevel@tonic-gate 		    (int (*)(void *))fgetc, (void *)psp->ps_cfile->pf_input,
1176*0Sstevel@tonic-gate 		    0);
1177*0Sstevel@tonic-gate 
1178*0Sstevel@tonic-gate 		if (retv != 1)
1179*0Sstevel@tonic-gate 			(void) dispatch_keyword(psp, keybuf);
1180*0Sstevel@tonic-gate 
1181*0Sstevel@tonic-gate 		if (retv != 0)
1182*0Sstevel@tonic-gate 			file_end(psp);
1183*0Sstevel@tonic-gate 	}
1184*0Sstevel@tonic-gate }
1185*0Sstevel@tonic-gate 
1186*0Sstevel@tonic-gate /*
1187*0Sstevel@tonic-gate  * Open and parse named file.  This is for the predefined
1188*0Sstevel@tonic-gate  * configuration files in /etc/ppp -- it's not an error if any of
1189*0Sstevel@tonic-gate  * these are missing.
1190*0Sstevel@tonic-gate  */
1191*0Sstevel@tonic-gate static void
1192*0Sstevel@tonic-gate parse_file(struct parse_state *psp, const char *fname)
1193*0Sstevel@tonic-gate {
1194*0Sstevel@tonic-gate 	struct stat sb;
1195*0Sstevel@tonic-gate 
1196*0Sstevel@tonic-gate 	/* It's ok if any of these files are missing. */
1197*0Sstevel@tonic-gate 	if (stat(fname, &sb) == -1 && errno == ENOENT)
1198*0Sstevel@tonic-gate 		return;
1199*0Sstevel@tonic-gate 	if (set_file(psp->ps_csvc, fname) == 0)
1200*0Sstevel@tonic-gate 		parse_from_file(psp);
1201*0Sstevel@tonic-gate }
1202*0Sstevel@tonic-gate 
1203*0Sstevel@tonic-gate /*
1204*0Sstevel@tonic-gate  * Dispatch keywords from command line.  Handles any files included
1205*0Sstevel@tonic-gate  * from there.
1206*0Sstevel@tonic-gate  */
1207*0Sstevel@tonic-gate static void
1208*0Sstevel@tonic-gate parse_arg_list(struct parse_state *psp, int argc, char **argv)
1209*0Sstevel@tonic-gate {
1210*0Sstevel@tonic-gate 	/* The first argument (program name) can be null. */
1211*0Sstevel@tonic-gate 	if (--argc <= 0)
1212*0Sstevel@tonic-gate 		return;
1213*0Sstevel@tonic-gate 	while (--argc >= 0) {
1214*0Sstevel@tonic-gate 		(void) dispatch_keyword(psp, *++argv);
1215*0Sstevel@tonic-gate 		if (psp->ps_cfile->pf_input != NULL)
1216*0Sstevel@tonic-gate 			parse_from_file(psp);
1217*0Sstevel@tonic-gate 	}
1218*0Sstevel@tonic-gate }
1219*0Sstevel@tonic-gate 
1220*0Sstevel@tonic-gate /* Count length of dynamic device list */
1221*0Sstevel@tonic-gate static int
1222*0Sstevel@tonic-gate count_devs(struct device_list *dlp)
1223*0Sstevel@tonic-gate {
1224*0Sstevel@tonic-gate 	int ndevs;
1225*0Sstevel@tonic-gate 
1226*0Sstevel@tonic-gate 	ndevs = 0;
1227*0Sstevel@tonic-gate 	for (; dlp != NULL; dlp = dlp->dl_next)
1228*0Sstevel@tonic-gate 		ndevs++;
1229*0Sstevel@tonic-gate 	return (ndevs);
1230*0Sstevel@tonic-gate }
1231*0Sstevel@tonic-gate 
1232*0Sstevel@tonic-gate /* Count number of devices named in entire file. */
1233*0Sstevel@tonic-gate static int
1234*0Sstevel@tonic-gate count_per_file(struct per_file *pfp)
1235*0Sstevel@tonic-gate {
1236*0Sstevel@tonic-gate 	struct service_list *slp;
1237*0Sstevel@tonic-gate 	int ndevs = 0;
1238*0Sstevel@tonic-gate 
1239*0Sstevel@tonic-gate 	for (; pfp != NULL; pfp = pfp->pf_prev) {
1240*0Sstevel@tonic-gate 		ndevs += count_devs(pfp->pf_global.sl_dev);
1241*0Sstevel@tonic-gate 		for (slp = pfp->pf_svc; slp != NULL; slp = slp->sl_next)
1242*0Sstevel@tonic-gate 			if (!(slp->sl_entry.se_flags & SEF_CDEV))
1243*0Sstevel@tonic-gate 				ndevs += count_devs(slp->sl_dev);
1244*0Sstevel@tonic-gate 	}
1245*0Sstevel@tonic-gate 	return (ndevs);
1246*0Sstevel@tonic-gate }
1247*0Sstevel@tonic-gate 
1248*0Sstevel@tonic-gate /* Write device names into linear array. */
1249*0Sstevel@tonic-gate static const char **
1250*0Sstevel@tonic-gate devs_to_list(struct device_list *dlp, const char **dnames)
1251*0Sstevel@tonic-gate {
1252*0Sstevel@tonic-gate 	for (; dlp != NULL; dlp = dlp->dl_next)
1253*0Sstevel@tonic-gate 		*dnames++ = dlp->dl_name;
1254*0Sstevel@tonic-gate 	return (dnames);
1255*0Sstevel@tonic-gate }
1256*0Sstevel@tonic-gate 
1257*0Sstevel@tonic-gate /* Write all device names from file into a linear array. */
1258*0Sstevel@tonic-gate static const char **
1259*0Sstevel@tonic-gate per_file_to_list(struct per_file *pfp, const char **dnames)
1260*0Sstevel@tonic-gate {
1261*0Sstevel@tonic-gate 	struct service_list *slp;
1262*0Sstevel@tonic-gate 
1263*0Sstevel@tonic-gate 	for (; pfp != NULL; pfp = pfp->pf_prev) {
1264*0Sstevel@tonic-gate 		dnames = devs_to_list(pfp->pf_global.sl_dev, dnames);
1265*0Sstevel@tonic-gate 		for (slp = pfp->pf_svc; slp != NULL; slp = slp->sl_next)
1266*0Sstevel@tonic-gate 			if (!(slp->sl_entry.se_flags & SEF_CDEV))
1267*0Sstevel@tonic-gate 				dnames = devs_to_list(slp->sl_dev, dnames);
1268*0Sstevel@tonic-gate 	}
1269*0Sstevel@tonic-gate 	return (dnames);
1270*0Sstevel@tonic-gate }
1271*0Sstevel@tonic-gate 
1272*0Sstevel@tonic-gate /* Compare device names; used with qsort */
1273*0Sstevel@tonic-gate static int
1274*0Sstevel@tonic-gate devcmp(const void *d1, const void *d2)
1275*0Sstevel@tonic-gate {
1276*0Sstevel@tonic-gate 	return (strcmp(*(const char **)d1, *(const char **)d2));
1277*0Sstevel@tonic-gate }
1278*0Sstevel@tonic-gate 
1279*0Sstevel@tonic-gate /*
1280*0Sstevel@tonic-gate  * Get sorted list of unique device names among all defined and
1281*0Sstevel@tonic-gate  * partially defined services in all files.
1282*0Sstevel@tonic-gate  */
1283*0Sstevel@tonic-gate static const char **
1284*0Sstevel@tonic-gate get_unique_devs(struct parse_state *psp)
1285*0Sstevel@tonic-gate {
1286*0Sstevel@tonic-gate 	int ndevs;
1287*0Sstevel@tonic-gate 	const char **dnames;
1288*0Sstevel@tonic-gate 	const char **dnp;
1289*0Sstevel@tonic-gate 	const char **dnf;
1290*0Sstevel@tonic-gate 
1291*0Sstevel@tonic-gate 	/*
1292*0Sstevel@tonic-gate 	 * Count number of explicitly referenced devices among all
1293*0Sstevel@tonic-gate 	 * services (including duplicates).
1294*0Sstevel@tonic-gate 	 */
1295*0Sstevel@tonic-gate 	ndevs = count_per_file(psp->ps_files);
1296*0Sstevel@tonic-gate 	ndevs += count_per_file(psp->ps_cfile);
1297*0Sstevel@tonic-gate 	if (ndevs <= 0) {
1298*0Sstevel@tonic-gate 		return (NULL);
1299*0Sstevel@tonic-gate 	}
1300*0Sstevel@tonic-gate 
1301*0Sstevel@tonic-gate 	/* Sort and trim out duplicate devices. */
1302*0Sstevel@tonic-gate 	dnames = (const char **)malloc((ndevs+1) * sizeof (const char *));
1303*0Sstevel@tonic-gate 	if (dnames == NULL) {
1304*0Sstevel@tonic-gate 		logerr("unable to allocate space for %d devices", ndevs + 1);
1305*0Sstevel@tonic-gate 		return (NULL);
1306*0Sstevel@tonic-gate 	}
1307*0Sstevel@tonic-gate 	dnp = per_file_to_list(psp->ps_files, dnames);
1308*0Sstevel@tonic-gate 	(void) per_file_to_list(psp->ps_cfile, dnp);
1309*0Sstevel@tonic-gate 	qsort(dnames, ndevs, sizeof (const char *), devcmp);
1310*0Sstevel@tonic-gate 	for (dnf = (dnp = dnames) + 1; dnf < dnames+ndevs; dnf++)
1311*0Sstevel@tonic-gate 		if (strcmp(*dnf, *dnp) != 0)
1312*0Sstevel@tonic-gate 			*++dnp = *dnf;
1313*0Sstevel@tonic-gate 	*++dnp = NULL;
1314*0Sstevel@tonic-gate 
1315*0Sstevel@tonic-gate 	/* Return array of pointers to names. */
1316*0Sstevel@tonic-gate 	return (dnames);
1317*0Sstevel@tonic-gate }
1318*0Sstevel@tonic-gate 
1319*0Sstevel@tonic-gate /*
1320*0Sstevel@tonic-gate  * Convert data structures created by parsing process into data
1321*0Sstevel@tonic-gate  * structures used by service dispatch.  This gathers the unique
1322*0Sstevel@tonic-gate  * device (lower stream) names and attaches the services available on
1323*0Sstevel@tonic-gate  * each device to a list while triming duplicate services.
1324*0Sstevel@tonic-gate  */
1325*0Sstevel@tonic-gate static struct option_state *
1326*0Sstevel@tonic-gate organize_state(struct parse_state *psp)
1327*0Sstevel@tonic-gate {
1328*0Sstevel@tonic-gate 	struct per_file *pfp;
1329*0Sstevel@tonic-gate 	struct per_file *pftopp;
1330*0Sstevel@tonic-gate 	struct service_list *slp;
1331*0Sstevel@tonic-gate 	struct device_list *dlp;
1332*0Sstevel@tonic-gate 	int ndevs;
1333*0Sstevel@tonic-gate 	int nsvcs;
1334*0Sstevel@tonic-gate 	const char **dnames;
1335*0Sstevel@tonic-gate 	const char **dnp;
1336*0Sstevel@tonic-gate 	struct device_entry *dep;
1337*0Sstevel@tonic-gate 	struct option_state *osp;
1338*0Sstevel@tonic-gate 	struct service_entry **sepp;
1339*0Sstevel@tonic-gate 	struct service_entry **sebpp;
1340*0Sstevel@tonic-gate 	struct service_entry **se2pp;
1341*0Sstevel@tonic-gate 
1342*0Sstevel@tonic-gate 	/*
1343*0Sstevel@tonic-gate 	 * Parsing is now done.
1344*0Sstevel@tonic-gate 	 */
1345*0Sstevel@tonic-gate 	close_service(psp->ps_csvc);
1346*0Sstevel@tonic-gate 	psp->ps_csvc = NULL;
1347*0Sstevel@tonic-gate 	if ((pfp = psp->ps_cfile) != NULL) {
1348*0Sstevel@tonic-gate 		pfp->pf_prev = psp->ps_files;
1349*0Sstevel@tonic-gate 		psp->ps_files = pfp;
1350*0Sstevel@tonic-gate 		psp->ps_cfile = NULL;
1351*0Sstevel@tonic-gate 	}
1352*0Sstevel@tonic-gate 
1353*0Sstevel@tonic-gate 	/* Link the services from all files together for easy referencing. */
1354*0Sstevel@tonic-gate 	pftopp = psp->ps_files;
1355*0Sstevel@tonic-gate 	for (pfp = pftopp->pf_prev; pfp != NULL; pfp = pfp->pf_prev)
1356*0Sstevel@tonic-gate 		if (pfp->pf_svc != NULL) {
1357*0Sstevel@tonic-gate 			if (pftopp->pf_svc_last == NULL)
1358*0Sstevel@tonic-gate 				pftopp->pf_svc = pfp->pf_svc;
1359*0Sstevel@tonic-gate 			else
1360*0Sstevel@tonic-gate 				pftopp->pf_svc_last->sl_next = pfp->pf_svc;
1361*0Sstevel@tonic-gate 			pftopp->pf_svc_last = pfp->pf_svc_last;
1362*0Sstevel@tonic-gate 			pfp->pf_svc = pfp->pf_svc_last = NULL;
1363*0Sstevel@tonic-gate 		}
1364*0Sstevel@tonic-gate 
1365*0Sstevel@tonic-gate 	/*
1366*0Sstevel@tonic-gate 	 * Count up number of services per device, including
1367*0Sstevel@tonic-gate 	 * duplicates but not including defaults.
1368*0Sstevel@tonic-gate 	 */
1369*0Sstevel@tonic-gate 	nsvcs = 0;
1370*0Sstevel@tonic-gate 	for (slp = psp->ps_files->pf_svc; slp != NULL; slp = slp->sl_next)
1371*0Sstevel@tonic-gate 		for (dlp = slp->sl_dev; dlp != NULL; dlp = dlp->dl_next)
1372*0Sstevel@tonic-gate 			nsvcs++;
1373*0Sstevel@tonic-gate 
1374*0Sstevel@tonic-gate 	/*
1375*0Sstevel@tonic-gate 	 * Get the unique devices referenced by all services.
1376*0Sstevel@tonic-gate 	 */
1377*0Sstevel@tonic-gate 	dnames = get_unique_devs(psp);
1378*0Sstevel@tonic-gate 	if (dnames == NULL) {
1379*0Sstevel@tonic-gate 		logdbg("no devices referenced by any service");
1380*0Sstevel@tonic-gate 		return (NULL);
1381*0Sstevel@tonic-gate 	}
1382*0Sstevel@tonic-gate 	ndevs = 0;
1383*0Sstevel@tonic-gate 	for (dnp = dnames; *dnp != NULL; dnp++)
1384*0Sstevel@tonic-gate 		ndevs++;
1385*0Sstevel@tonic-gate 
1386*0Sstevel@tonic-gate 	/*
1387*0Sstevel@tonic-gate 	 * Allocate room for main structure, device records, and
1388*0Sstevel@tonic-gate 	 * per-device lists.  Worst case is all devices having all
1389*0Sstevel@tonic-gate 	 * services; that's why we allocate for nsvcs * ndevs.
1390*0Sstevel@tonic-gate 	 */
1391*0Sstevel@tonic-gate 	osp = (struct option_state *)malloc(sizeof (*osp) +
1392*0Sstevel@tonic-gate 	    ndevs * sizeof (*dep) + nsvcs * ndevs * sizeof (*sepp));
1393*0Sstevel@tonic-gate 	if (osp == NULL) {
1394*0Sstevel@tonic-gate 		logerr("unable to allocate option state structure");
1395*0Sstevel@tonic-gate 		free(dnames);
1396*0Sstevel@tonic-gate 		return (NULL);
1397*0Sstevel@tonic-gate 	}
1398*0Sstevel@tonic-gate 
1399*0Sstevel@tonic-gate 	/* We're going to succeed now, so steal these over. */
1400*0Sstevel@tonic-gate 	osp->os_devices = dep = (struct device_entry *)(osp+1);
1401*0Sstevel@tonic-gate 	osp->os_pfjunk = psp->ps_files;
1402*0Sstevel@tonic-gate 	psp->ps_files = NULL;
1403*0Sstevel@tonic-gate 	osp->os_evjunk = psp->ps_evlist;
1404*0Sstevel@tonic-gate 	psp->ps_evlist = NULL;
1405*0Sstevel@tonic-gate 
1406*0Sstevel@tonic-gate 	/* Loop over devices, install services, remove duplicates. */
1407*0Sstevel@tonic-gate 	sepp = (struct service_entry **)(dep + ndevs);
1408*0Sstevel@tonic-gate 	for (dnp = dnames; *dnp != NULL; dnp++) {
1409*0Sstevel@tonic-gate 		dep->de_name = *dnp;
1410*0Sstevel@tonic-gate 		dep->de_services = (const struct service_entry **)sepp;
1411*0Sstevel@tonic-gate 		sebpp = sepp;
1412*0Sstevel@tonic-gate 		for (slp = osp->os_pfjunk->pf_svc; slp != NULL;
1413*0Sstevel@tonic-gate 		    slp = slp->sl_next)
1414*0Sstevel@tonic-gate 			for (dlp = slp->sl_dev; dlp != NULL;
1415*0Sstevel@tonic-gate 			    dlp = dlp->dl_next) {
1416*0Sstevel@tonic-gate 				if (dlp->dl_name == *dnp ||
1417*0Sstevel@tonic-gate 				    strcmp(dlp->dl_name, *dnp) == 0) {
1418*0Sstevel@tonic-gate 					for (se2pp = sebpp; se2pp < sepp;
1419*0Sstevel@tonic-gate 					    se2pp++)
1420*0Sstevel@tonic-gate 						if ((*se2pp)->se_name ==
1421*0Sstevel@tonic-gate 						    slp->sl_entry.se_name ||
1422*0Sstevel@tonic-gate 						    strcmp((*se2pp)->
1423*0Sstevel@tonic-gate 							se_name,
1424*0Sstevel@tonic-gate 							slp->sl_entry.
1425*0Sstevel@tonic-gate 							se_name) == 0)
1426*0Sstevel@tonic-gate 							break;
1427*0Sstevel@tonic-gate 					/*
1428*0Sstevel@tonic-gate 					 * We retain a service if it's
1429*0Sstevel@tonic-gate 					 * unique or if its serial
1430*0Sstevel@tonic-gate 					 * number (position in the
1431*0Sstevel@tonic-gate 					 * file) is greater than than
1432*0Sstevel@tonic-gate 					 * any other.
1433*0Sstevel@tonic-gate 					 */
1434*0Sstevel@tonic-gate 					if (se2pp >= sepp)
1435*0Sstevel@tonic-gate 						*sepp++ = &slp->sl_entry;
1436*0Sstevel@tonic-gate 					else if (SESERIAL(**se2pp) <
1437*0Sstevel@tonic-gate 					    SESERIAL(slp->sl_entry))
1438*0Sstevel@tonic-gate 						*se2pp = &slp->sl_entry;
1439*0Sstevel@tonic-gate 				}
1440*0Sstevel@tonic-gate 			}
1441*0Sstevel@tonic-gate 		/* Count up the services on this device. */
1442*0Sstevel@tonic-gate 		dep->de_nservices = (const struct service_entry **)sepp -
1443*0Sstevel@tonic-gate 		    dep->de_services;
1444*0Sstevel@tonic-gate 		/* Ignore devices having no services at all. */
1445*0Sstevel@tonic-gate 		if (dep->de_nservices > 0)
1446*0Sstevel@tonic-gate 			dep++;
1447*0Sstevel@tonic-gate 	}
1448*0Sstevel@tonic-gate 	/* Count up the devices. */
1449*0Sstevel@tonic-gate 	osp->os_ndevices = dep - osp->os_devices;
1450*0Sstevel@tonic-gate 	/* Free the list of device names */
1451*0Sstevel@tonic-gate 	free(dnames);
1452*0Sstevel@tonic-gate 	return (osp);
1453*0Sstevel@tonic-gate }
1454*0Sstevel@tonic-gate 
1455*0Sstevel@tonic-gate /*
1456*0Sstevel@tonic-gate  * Free storage unique to a given service.  Pointers copied from other
1457*0Sstevel@tonic-gate  * services are ignored.
1458*0Sstevel@tonic-gate  */
1459*0Sstevel@tonic-gate static void
1460*0Sstevel@tonic-gate free_service(struct service_list *slp)
1461*0Sstevel@tonic-gate {
1462*0Sstevel@tonic-gate 	struct filter_entry *fep;
1463*0Sstevel@tonic-gate 	struct filter_entry *fen;
1464*0Sstevel@tonic-gate 
1465*0Sstevel@tonic-gate 	if (!(slp->sl_entry.se_flags & SEF_CDEV))
1466*0Sstevel@tonic-gate 		free_device_list(slp->sl_dev);
1467*0Sstevel@tonic-gate 	if (!(slp->sl_entry.se_flags & SEF_CFLIST)) {
1468*0Sstevel@tonic-gate 		fep = slp->sl_entry.se_flist;
1469*0Sstevel@tonic-gate 		while (fep != NULL) {
1470*0Sstevel@tonic-gate 			fen = fep->fe_prevcopy ? NULL : fep->fe_prev;
1471*0Sstevel@tonic-gate 			free(fep);
1472*0Sstevel@tonic-gate 			fep = fen;
1473*0Sstevel@tonic-gate 		}
1474*0Sstevel@tonic-gate 	}
1475*0Sstevel@tonic-gate 	if (!(slp->sl_entry.se_flags & SEF_CPPPD) &&
1476*0Sstevel@tonic-gate 	    slp->sl_entry.se_pppd != NULL)
1477*0Sstevel@tonic-gate 		free(slp->sl_entry.se_pppd);
1478*0Sstevel@tonic-gate 	if (!(slp->sl_entry.se_flags & SEF_CSERVER) &&
1479*0Sstevel@tonic-gate 	    slp->sl_entry.se_server != NULL)
1480*0Sstevel@tonic-gate 		free(slp->sl_entry.se_server);
1481*0Sstevel@tonic-gate 	if (!(slp->sl_entry.se_flags & SEF_CPATH) &&
1482*0Sstevel@tonic-gate 	    slp->sl_entry.se_path != NULL)
1483*0Sstevel@tonic-gate 		free(slp->sl_entry.se_path);
1484*0Sstevel@tonic-gate 	if (!(slp->sl_entry.se_flags & SEF_CEXTRA) &&
1485*0Sstevel@tonic-gate 	    slp->sl_entry.se_extra != NULL)
1486*0Sstevel@tonic-gate 		free(slp->sl_entry.se_extra);
1487*0Sstevel@tonic-gate 	if (!(slp->sl_entry.se_flags & SEF_CLOG) &&
1488*0Sstevel@tonic-gate 	    slp->sl_entry.se_log != NULL)
1489*0Sstevel@tonic-gate 		free(slp->sl_entry.se_log);
1490*0Sstevel@tonic-gate }
1491*0Sstevel@tonic-gate 
1492*0Sstevel@tonic-gate /*
1493*0Sstevel@tonic-gate  * Free a linked list of services.
1494*0Sstevel@tonic-gate  */
1495*0Sstevel@tonic-gate static void
1496*0Sstevel@tonic-gate free_service_list(struct service_list *slp)
1497*0Sstevel@tonic-gate {
1498*0Sstevel@tonic-gate 	struct service_list *sln;
1499*0Sstevel@tonic-gate 
1500*0Sstevel@tonic-gate 	while (slp != NULL) {
1501*0Sstevel@tonic-gate 		free_service(slp);
1502*0Sstevel@tonic-gate 		sln = slp->sl_next;
1503*0Sstevel@tonic-gate 		free(slp);
1504*0Sstevel@tonic-gate 		slp = sln;
1505*0Sstevel@tonic-gate 	}
1506*0Sstevel@tonic-gate }
1507*0Sstevel@tonic-gate 
1508*0Sstevel@tonic-gate /*
1509*0Sstevel@tonic-gate  * Free a linked list of files and all services in those files.
1510*0Sstevel@tonic-gate  */
1511*0Sstevel@tonic-gate static void
1512*0Sstevel@tonic-gate free_file_list(struct per_file *pfp)
1513*0Sstevel@tonic-gate {
1514*0Sstevel@tonic-gate 	struct per_file *pfn;
1515*0Sstevel@tonic-gate 
1516*0Sstevel@tonic-gate 	while (pfp != NULL) {
1517*0Sstevel@tonic-gate 		free_service(&pfp->pf_global);
1518*0Sstevel@tonic-gate 		free_service_list(pfp->pf_svc);
1519*0Sstevel@tonic-gate 		pfn = pfp->pf_prev;
1520*0Sstevel@tonic-gate 		free(pfp);
1521*0Sstevel@tonic-gate 		pfp = pfn;
1522*0Sstevel@tonic-gate 	}
1523*0Sstevel@tonic-gate }
1524*0Sstevel@tonic-gate 
1525*0Sstevel@tonic-gate /*
1526*0Sstevel@tonic-gate  * Free an array of local environment variables.
1527*0Sstevel@tonic-gate  */
1528*0Sstevel@tonic-gate static void
1529*0Sstevel@tonic-gate free_env_list(char **evlist)
1530*0Sstevel@tonic-gate {
1531*0Sstevel@tonic-gate 	char **evp;
1532*0Sstevel@tonic-gate 	char *env;
1533*0Sstevel@tonic-gate 
1534*0Sstevel@tonic-gate 	if ((evp = evlist) != NULL) {
1535*0Sstevel@tonic-gate 		while ((env = *evp++) != NULL)
1536*0Sstevel@tonic-gate 			free(env);
1537*0Sstevel@tonic-gate 		free(evlist);
1538*0Sstevel@tonic-gate 	}
1539*0Sstevel@tonic-gate }
1540*0Sstevel@tonic-gate 
1541*0Sstevel@tonic-gate /*
1542*0Sstevel@tonic-gate  * Add a new device (lower stream) to the list for which we're the
1543*0Sstevel@tonic-gate  * PPPoE server.
1544*0Sstevel@tonic-gate  */
1545*0Sstevel@tonic-gate static void
1546*0Sstevel@tonic-gate add_new_dev(int tunfd, const char *dname)
1547*0Sstevel@tonic-gate {
1548*0Sstevel@tonic-gate 	union ppptun_name ptn;
1549*0Sstevel@tonic-gate 
1550*0Sstevel@tonic-gate 	(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%s:pppoed",
1551*0Sstevel@tonic-gate 	    dname);
1552*0Sstevel@tonic-gate 	if (strioctl(tunfd, PPPTUN_SCTL, &ptn, sizeof (ptn), 0) < 0) {
1553*0Sstevel@tonic-gate 		logerr("PPPTUN_SCTL %s: %s", ptn.ptn_name, mystrerror(errno));
1554*0Sstevel@tonic-gate 	} else {
1555*0Sstevel@tonic-gate 		logdbg("added %s", ptn.ptn_name);
1556*0Sstevel@tonic-gate 	}
1557*0Sstevel@tonic-gate }
1558*0Sstevel@tonic-gate 
1559*0Sstevel@tonic-gate /*
1560*0Sstevel@tonic-gate  * Remove an existing device (lower stream) from the list for which we
1561*0Sstevel@tonic-gate  * were the PPPoE server.
1562*0Sstevel@tonic-gate  */
1563*0Sstevel@tonic-gate static void
1564*0Sstevel@tonic-gate rem_old_dev(int tunfd, const char *dname)
1565*0Sstevel@tonic-gate {
1566*0Sstevel@tonic-gate 	union ppptun_name ptn;
1567*0Sstevel@tonic-gate 
1568*0Sstevel@tonic-gate 	(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%s:pppoed",
1569*0Sstevel@tonic-gate 	    dname);
1570*0Sstevel@tonic-gate 	if (strioctl(tunfd, PPPTUN_DCTL, &ptn, sizeof (ptn), 0) < 0) {
1571*0Sstevel@tonic-gate 		logerr("PPPTUN_DCTL %s: %s", ptn.ptn_name, mystrerror(errno));
1572*0Sstevel@tonic-gate 	} else {
1573*0Sstevel@tonic-gate 		logdbg("removed %s", ptn.ptn_name);
1574*0Sstevel@tonic-gate 	}
1575*0Sstevel@tonic-gate }
1576*0Sstevel@tonic-gate 
1577*0Sstevel@tonic-gate /*
1578*0Sstevel@tonic-gate  * Get a list of all of the devices currently plumbed for PPPoE.  This
1579*0Sstevel@tonic-gate  * is used for supporting the "*" and "all" device aliases.
1580*0Sstevel@tonic-gate  */
1581*0Sstevel@tonic-gate static void
1582*0Sstevel@tonic-gate get_device_list(struct parse_state *psp, int tunfd)
1583*0Sstevel@tonic-gate {
1584*0Sstevel@tonic-gate 	struct device_list *dlp;
1585*0Sstevel@tonic-gate 	struct device_list **dlpp;
1586*0Sstevel@tonic-gate 	struct device_list *dlalt;
1587*0Sstevel@tonic-gate 	struct device_list **dl2pp;
1588*0Sstevel@tonic-gate 	struct device_list *dla;
1589*0Sstevel@tonic-gate 	int i;
1590*0Sstevel@tonic-gate 	union ppptun_name ptn;
1591*0Sstevel@tonic-gate 	char *cp;
1592*0Sstevel@tonic-gate 
1593*0Sstevel@tonic-gate 	/* First pass; just allocate space for all *:pppoe* devices */
1594*0Sstevel@tonic-gate 	dlpp = &psp->ps_star;
1595*0Sstevel@tonic-gate 	dl2pp = &dlalt;
1596*0Sstevel@tonic-gate 	for (i = 0; ; i++) {
1597*0Sstevel@tonic-gate 		ptn.ptn_index = i;
1598*0Sstevel@tonic-gate 		if (strioctl(tunfd, PPPTUN_GNNAME, &ptn, sizeof (ptn),
1599*0Sstevel@tonic-gate 		    sizeof (ptn)) < 0) {
1600*0Sstevel@tonic-gate 			logerr("PPPTUN_GNNAME %d: %s", i, mystrerror(errno));
1601*0Sstevel@tonic-gate 			break;
1602*0Sstevel@tonic-gate 		}
1603*0Sstevel@tonic-gate 		if (ptn.ptn_name[0] == '\0')
1604*0Sstevel@tonic-gate 			break;
1605*0Sstevel@tonic-gate 		if ((cp = strchr(ptn.ptn_name, ':')) == NULL ||
1606*0Sstevel@tonic-gate 		    strncmp(cp, ":pppoe", 6) != 0 ||
1607*0Sstevel@tonic-gate 		    (cp[6] != '\0' && strcmp(cp+6, "d") != 0))
1608*0Sstevel@tonic-gate 			continue;
1609*0Sstevel@tonic-gate 		*cp = '\0';
1610*0Sstevel@tonic-gate 		dlp = (struct device_list *)malloc(sizeof (*dlp) +
1611*0Sstevel@tonic-gate 		    strlen(ptn.ptn_name) + 1);
1612*0Sstevel@tonic-gate 		if (dlp == NULL)
1613*0Sstevel@tonic-gate 			break;
1614*0Sstevel@tonic-gate 		dlp->dl_name = (const char *)(dlp + 1);
1615*0Sstevel@tonic-gate 		(void) strcpy((char *)(dlp + 1), ptn.ptn_name);
1616*0Sstevel@tonic-gate 		if (cp[6] == '\0') {
1617*0Sstevel@tonic-gate 			*dlpp = dlp;
1618*0Sstevel@tonic-gate 			dlpp = &dlp->dl_next;
1619*0Sstevel@tonic-gate 		} else {
1620*0Sstevel@tonic-gate 			*dl2pp = dlp;
1621*0Sstevel@tonic-gate 			dl2pp = &dlp->dl_next;
1622*0Sstevel@tonic-gate 		}
1623*0Sstevel@tonic-gate 	}
1624*0Sstevel@tonic-gate 	*dlpp = NULL;
1625*0Sstevel@tonic-gate 	*dl2pp = NULL;
1626*0Sstevel@tonic-gate 
1627*0Sstevel@tonic-gate 	/* Second pass; eliminate improperly plumbed devices */
1628*0Sstevel@tonic-gate 	for (dlpp = &psp->ps_star; (dlp = *dlpp) != NULL; ) {
1629*0Sstevel@tonic-gate 		for (dla = dlalt; dla != NULL; dla = dla->dl_next)
1630*0Sstevel@tonic-gate 			if (strcmp(dla->dl_name, dlp->dl_name) == 0)
1631*0Sstevel@tonic-gate 			    break;
1632*0Sstevel@tonic-gate 		if (dla == NULL) {
1633*0Sstevel@tonic-gate 			*dlpp = dlp->dl_next;
1634*0Sstevel@tonic-gate 			free(dlp);
1635*0Sstevel@tonic-gate 		} else {
1636*0Sstevel@tonic-gate 			dlpp = &dlp->dl_next;
1637*0Sstevel@tonic-gate 		}
1638*0Sstevel@tonic-gate 	}
1639*0Sstevel@tonic-gate 	free_device_list(dlalt);
1640*0Sstevel@tonic-gate 
1641*0Sstevel@tonic-gate 	/* Add in "*" so we can always handle dynamic plumbing. */
1642*0Sstevel@tonic-gate 	dlp = (struct device_list *)malloc(sizeof (*dlp) + 2);
1643*0Sstevel@tonic-gate 	if (dlp != NULL) {
1644*0Sstevel@tonic-gate 		dlp->dl_name = (const char *)(dlp + 1);
1645*0Sstevel@tonic-gate 		(void) strcpy((char *)(dlp + 1), "*");
1646*0Sstevel@tonic-gate 		dlp->dl_next = psp->ps_star;
1647*0Sstevel@tonic-gate 		psp->ps_star = dlp;
1648*0Sstevel@tonic-gate 	}
1649*0Sstevel@tonic-gate }
1650*0Sstevel@tonic-gate 
1651*0Sstevel@tonic-gate /*
1652*0Sstevel@tonic-gate  * Set logging subsystem back to configured global default values.
1653*0Sstevel@tonic-gate  */
1654*0Sstevel@tonic-gate void
1655*0Sstevel@tonic-gate global_logging(void)
1656*0Sstevel@tonic-gate {
1657*0Sstevel@tonic-gate 	log_for_service(glob_svc.se_log, glob_svc.se_debug);
1658*0Sstevel@tonic-gate }
1659*0Sstevel@tonic-gate 
1660*0Sstevel@tonic-gate /*
1661*0Sstevel@tonic-gate  * Handle SIGHUP -- reparse command line and all configuration files.
1662*0Sstevel@tonic-gate  * When reparsing is complete, free old parsed data and replace with
1663*0Sstevel@tonic-gate  * new.
1664*0Sstevel@tonic-gate  */
1665*0Sstevel@tonic-gate void
1666*0Sstevel@tonic-gate parse_options(int tunfd, int argc, char **argv)
1667*0Sstevel@tonic-gate {
1668*0Sstevel@tonic-gate 	struct parse_state pstate;
1669*0Sstevel@tonic-gate 	struct per_file *argpf;
1670*0Sstevel@tonic-gate 	struct option_state *newopt;
1671*0Sstevel@tonic-gate 	const char **dnames;
1672*0Sstevel@tonic-gate 	const char **dnp;
1673*0Sstevel@tonic-gate 	const struct device_entry *newdep, *newmax;
1674*0Sstevel@tonic-gate 	const struct device_entry *olddep, *oldmax;
1675*0Sstevel@tonic-gate 	int cmpval;
1676*0Sstevel@tonic-gate 	struct service_entry newglobsvc, *mainsvc;
1677*0Sstevel@tonic-gate 
1678*0Sstevel@tonic-gate 	/* Note that all per_file structures must be freeable */
1679*0Sstevel@tonic-gate 	argpf = (struct per_file *)calloc(sizeof (*argpf), 1);
1680*0Sstevel@tonic-gate 	if (argpf == NULL) {
1681*0Sstevel@tonic-gate 		return;
1682*0Sstevel@tonic-gate 	}
1683*0Sstevel@tonic-gate 	(void) memset(&pstate, '\0', sizeof (pstate));
1684*0Sstevel@tonic-gate 	pstate.ps_state = ksDefault;
1685*0Sstevel@tonic-gate 	pstate.ps_cfile = argpf;
1686*0Sstevel@tonic-gate 	pstate.ps_csvc = &argpf->pf_global;
1687*0Sstevel@tonic-gate 	argpf->pf_global.sl_parse = &pstate;
1688*0Sstevel@tonic-gate 	argpf->pf_name = "command line";
1689*0Sstevel@tonic-gate 
1690*0Sstevel@tonic-gate 	/* Default is 1 -- errors only */
1691*0Sstevel@tonic-gate 	argpf->pf_global.sl_entry.se_debug++;
1692*0Sstevel@tonic-gate 	argpf->pf_global.sl_entry.se_name = "<global>";
1693*0Sstevel@tonic-gate 
1694*0Sstevel@tonic-gate 	/* Get list of all devices */
1695*0Sstevel@tonic-gate 	get_device_list(&pstate, tunfd);
1696*0Sstevel@tonic-gate 
1697*0Sstevel@tonic-gate 	/* Parse options from command line and main configuration file. */
1698*0Sstevel@tonic-gate 	pstate.ps_flags |= PSF_SETLEVEL;
1699*0Sstevel@tonic-gate 	parse_arg_list(&pstate, argc, argv);
1700*0Sstevel@tonic-gate 	parse_file(&pstate, "/etc/ppp/pppoe");
1701*0Sstevel@tonic-gate 	pstate.ps_flags &= ~PSF_SETLEVEL;
1702*0Sstevel@tonic-gate 
1703*0Sstevel@tonic-gate 	/*
1704*0Sstevel@tonic-gate 	 * At this point, global options from the main configuration
1705*0Sstevel@tonic-gate 	 * file are pointed to by ps_files, and options from command
1706*0Sstevel@tonic-gate 	 * line are in argpf.  We need to pull three special options
1707*0Sstevel@tonic-gate 	 * from these -- wildcard, debug, and log.  Note that the main
1708*0Sstevel@tonic-gate 	 * options file overrides the command line.  This is
1709*0Sstevel@tonic-gate 	 * intentional.  The semantics are such that the system
1710*0Sstevel@tonic-gate 	 * behaves as though the main configuration file were
1711*0Sstevel@tonic-gate 	 * "included" from the command line, and thus options there
1712*0Sstevel@tonic-gate 	 * override the command line.  This may seem odd, but at least
1713*0Sstevel@tonic-gate 	 * it's self-consistent.
1714*0Sstevel@tonic-gate 	 */
1715*0Sstevel@tonic-gate 	newglobsvc = argpf->pf_global.sl_entry;
1716*0Sstevel@tonic-gate 	if (pstate.ps_files != NULL) {
1717*0Sstevel@tonic-gate 		mainsvc = &pstate.ps_files->pf_global.sl_entry;
1718*0Sstevel@tonic-gate 		if (mainsvc->se_log != NULL)
1719*0Sstevel@tonic-gate 			newglobsvc.se_log = mainsvc->se_log;
1720*0Sstevel@tonic-gate 		if (mainsvc->se_flags & (SEF_WILD|SEF_NOWILD))
1721*0Sstevel@tonic-gate 			newglobsvc.se_flags =
1722*0Sstevel@tonic-gate 			    (newglobsvc.se_flags & ~(SEF_WILD|SEF_NOWILD)) |
1723*0Sstevel@tonic-gate 			    (mainsvc->se_flags & (SEF_WILD|SEF_NOWILD));
1724*0Sstevel@tonic-gate 		if (mainsvc->se_flags & SEF_DEBUGCLR)
1725*0Sstevel@tonic-gate 			newglobsvc.se_debug = 0;
1726*0Sstevel@tonic-gate 		newglobsvc.se_debug += mainsvc->se_debug;
1727*0Sstevel@tonic-gate 	}
1728*0Sstevel@tonic-gate 	glob_svc = newglobsvc;
1729*0Sstevel@tonic-gate 	global_logging();
1730*0Sstevel@tonic-gate 
1731*0Sstevel@tonic-gate 	/* Get the list of devices referenced by configuration above. */
1732*0Sstevel@tonic-gate 	dnames = get_unique_devs(&pstate);
1733*0Sstevel@tonic-gate 	if (dnames != NULL) {
1734*0Sstevel@tonic-gate 		/* Read per-device configuration files. */
1735*0Sstevel@tonic-gate 		pstate.ps_flags |= PSF_PERDEV;
1736*0Sstevel@tonic-gate 		for (dnp = dnames; *dnp != NULL; dnp++)
1737*0Sstevel@tonic-gate 			parse_file(&pstate, *dnp);
1738*0Sstevel@tonic-gate 		pstate.ps_flags &= ~PSF_PERDEV;
1739*0Sstevel@tonic-gate 		free(dnames);
1740*0Sstevel@tonic-gate 	}
1741*0Sstevel@tonic-gate 	file_end(&pstate);
1742*0Sstevel@tonic-gate 
1743*0Sstevel@tonic-gate 	/*
1744*0Sstevel@tonic-gate 	 * Convert parsed data structures into per-device structures.
1745*0Sstevel@tonic-gate 	 * (Invert the table.)
1746*0Sstevel@tonic-gate 	 */
1747*0Sstevel@tonic-gate 	newopt = organize_state(&pstate);
1748*0Sstevel@tonic-gate 
1749*0Sstevel@tonic-gate 	/* If we're going to free the file name, then stop logging there. */
1750*0Sstevel@tonic-gate 	if (newopt == NULL && glob_svc.se_log != NULL) {
1751*0Sstevel@tonic-gate 		glob_svc.se_log = NULL;
1752*0Sstevel@tonic-gate 		global_logging();
1753*0Sstevel@tonic-gate 	}
1754*0Sstevel@tonic-gate 
1755*0Sstevel@tonic-gate 	/*
1756*0Sstevel@tonic-gate 	 * Unless an error has occurred, these pointers are normally
1757*0Sstevel@tonic-gate 	 * all NULL.  Nothing is freed until the file is re-read.
1758*0Sstevel@tonic-gate 	 */
1759*0Sstevel@tonic-gate 	free_file_list(pstate.ps_files);
1760*0Sstevel@tonic-gate 	free_file_list(pstate.ps_cfile);
1761*0Sstevel@tonic-gate 	free_device_list(pstate.ps_star);
1762*0Sstevel@tonic-gate 	free_env_list(pstate.ps_evlist);
1763*0Sstevel@tonic-gate 
1764*0Sstevel@tonic-gate 	/*
1765*0Sstevel@tonic-gate 	 * Match up entries on device list.  Detach devices no longer
1766*0Sstevel@tonic-gate 	 * referenced.  Attach ones now referenced.  (The use of null
1767*0Sstevel@tonic-gate 	 * pointers here may look fishy, but it actually works.
1768*0Sstevel@tonic-gate 	 * NULL>=NULL is always true.)
1769*0Sstevel@tonic-gate 	 */
1770*0Sstevel@tonic-gate 	if (newopt != NULL) {
1771*0Sstevel@tonic-gate 		newdep = newopt->os_devices;
1772*0Sstevel@tonic-gate 		newmax = newdep + newopt->os_ndevices;
1773*0Sstevel@tonic-gate 	} else {
1774*0Sstevel@tonic-gate 		newdep = newmax = NULL;
1775*0Sstevel@tonic-gate 	}
1776*0Sstevel@tonic-gate 	if (cur_options != NULL) {
1777*0Sstevel@tonic-gate 		olddep = cur_options->os_devices;
1778*0Sstevel@tonic-gate 		oldmax = olddep + cur_options->os_ndevices;
1779*0Sstevel@tonic-gate 	} else {
1780*0Sstevel@tonic-gate 		olddep = oldmax = NULL;
1781*0Sstevel@tonic-gate 	}
1782*0Sstevel@tonic-gate 	while ((newdep != NULL && newdep < newmax) ||
1783*0Sstevel@tonic-gate 	    (olddep != NULL && olddep < oldmax)) {
1784*0Sstevel@tonic-gate 		if (newdep < newmax) {
1785*0Sstevel@tonic-gate 			if (olddep >= oldmax) {
1786*0Sstevel@tonic-gate 				add_new_dev(tunfd, newdep->de_name);
1787*0Sstevel@tonic-gate 				newdep++;
1788*0Sstevel@tonic-gate 			} else {
1789*0Sstevel@tonic-gate 				cmpval = strcmp(newdep->de_name,
1790*0Sstevel@tonic-gate 				    olddep->de_name);
1791*0Sstevel@tonic-gate 				if (cmpval < 0) {
1792*0Sstevel@tonic-gate 					/* Brand new device seen. */
1793*0Sstevel@tonic-gate 					add_new_dev(tunfd, newdep->de_name);
1794*0Sstevel@tonic-gate 					newdep++;
1795*0Sstevel@tonic-gate 				} else if (cmpval == 0) {
1796*0Sstevel@tonic-gate 					/* Existing device; skip it. */
1797*0Sstevel@tonic-gate 					newdep++;
1798*0Sstevel@tonic-gate 					olddep++;
1799*0Sstevel@tonic-gate 				}
1800*0Sstevel@tonic-gate 				/* No else clause -- removal is below */
1801*0Sstevel@tonic-gate 			}
1802*0Sstevel@tonic-gate 		}
1803*0Sstevel@tonic-gate 		if (olddep < oldmax) {
1804*0Sstevel@tonic-gate 			if (newdep >= newmax) {
1805*0Sstevel@tonic-gate 				rem_old_dev(tunfd, olddep->de_name);
1806*0Sstevel@tonic-gate 				olddep++;
1807*0Sstevel@tonic-gate 			} else {
1808*0Sstevel@tonic-gate 				cmpval = strcmp(newdep->de_name,
1809*0Sstevel@tonic-gate 				    olddep->de_name);
1810*0Sstevel@tonic-gate 				if (cmpval > 0) {
1811*0Sstevel@tonic-gate 					/* Old device is gone */
1812*0Sstevel@tonic-gate 					rem_old_dev(tunfd, olddep->de_name);
1813*0Sstevel@tonic-gate 					olddep++;
1814*0Sstevel@tonic-gate 				} else if (cmpval == 0) {
1815*0Sstevel@tonic-gate 					/* Existing device; skip it. */
1816*0Sstevel@tonic-gate 					newdep++;
1817*0Sstevel@tonic-gate 					olddep++;
1818*0Sstevel@tonic-gate 				}
1819*0Sstevel@tonic-gate 				/* No else clause -- insert handled above */
1820*0Sstevel@tonic-gate 			}
1821*0Sstevel@tonic-gate 		}
1822*0Sstevel@tonic-gate 	}
1823*0Sstevel@tonic-gate 
1824*0Sstevel@tonic-gate 	/* Discard existing parsed data storage. */
1825*0Sstevel@tonic-gate 	if (cur_options != NULL) {
1826*0Sstevel@tonic-gate 		free_file_list(cur_options->os_pfjunk);
1827*0Sstevel@tonic-gate 		free_env_list(cur_options->os_evjunk);
1828*0Sstevel@tonic-gate 		free(cur_options);
1829*0Sstevel@tonic-gate 	}
1830*0Sstevel@tonic-gate 	/* Install new. */
1831*0Sstevel@tonic-gate 	cur_options = newopt;
1832*0Sstevel@tonic-gate }
1833*0Sstevel@tonic-gate 
1834*0Sstevel@tonic-gate /*
1835*0Sstevel@tonic-gate  * Check if configured filters permit requesting client to use a given
1836*0Sstevel@tonic-gate  * service.  Note -- filters are stored in reverse order in order to
1837*0Sstevel@tonic-gate  * make file-inclusion work as expected.  Thus, the "first match"
1838*0Sstevel@tonic-gate  * filter rule becomes "last match" here.
1839*0Sstevel@tonic-gate  */
1840*0Sstevel@tonic-gate static boolean_t
1841*0Sstevel@tonic-gate allow_service(const struct service_entry *sep, const ppptun_atype *pap)
1842*0Sstevel@tonic-gate {
1843*0Sstevel@tonic-gate 	const struct filter_entry *fep;
1844*0Sstevel@tonic-gate 	const struct filter_entry *lmatch;
1845*0Sstevel@tonic-gate 	boolean_t anynonexcept = B_FALSE;
1846*0Sstevel@tonic-gate 	const uchar_t *upt;
1847*0Sstevel@tonic-gate 	const uchar_t *macp;
1848*0Sstevel@tonic-gate 	const uchar_t *maskp;
1849*0Sstevel@tonic-gate 	int i;
1850*0Sstevel@tonic-gate 
1851*0Sstevel@tonic-gate 	lmatch = NULL;
1852*0Sstevel@tonic-gate 	for (fep = sep->se_flist; fep != NULL; fep = fep->fe_prev) {
1853*0Sstevel@tonic-gate 		anynonexcept |= !fep->fe_isexcept;
1854*0Sstevel@tonic-gate 		upt = pap->pta_pppoe.ptma_mac;
1855*0Sstevel@tonic-gate 		macp = fep->fe_mac.ether_addr_octet;
1856*0Sstevel@tonic-gate 		maskp = fep->fe_mask.ether_addr_octet;
1857*0Sstevel@tonic-gate 		for (i = sizeof (pap->pta_pppoe.ptma_mac); i > 0; i--)
1858*0Sstevel@tonic-gate 			if (((*macp++ ^ *upt++) & *maskp++) != 0)
1859*0Sstevel@tonic-gate 				break;
1860*0Sstevel@tonic-gate 		if (i <= 0)
1861*0Sstevel@tonic-gate 			lmatch = fep;
1862*0Sstevel@tonic-gate 	}
1863*0Sstevel@tonic-gate 
1864*0Sstevel@tonic-gate 	if (lmatch == NULL) {
1865*0Sstevel@tonic-gate 		/*
1866*0Sstevel@tonic-gate 		 * Assume reject by default if any positive-match
1867*0Sstevel@tonic-gate 		 * (non-except) filters are given.  Otherwise, if
1868*0Sstevel@tonic-gate 		 * there are no positive-match filters, then
1869*0Sstevel@tonic-gate 		 * non-matching means accept by default.
1870*0Sstevel@tonic-gate 		 */
1871*0Sstevel@tonic-gate 		return (!anynonexcept);
1872*0Sstevel@tonic-gate 	}
1873*0Sstevel@tonic-gate 	return (!lmatch->fe_isexcept);
1874*0Sstevel@tonic-gate }
1875*0Sstevel@tonic-gate 
1876*0Sstevel@tonic-gate /*
1877*0Sstevel@tonic-gate  * Locate available service(s) based on client request.  Assumes that
1878*0Sstevel@tonic-gate  * outp points to a buffer of at least size PPPOE_MSGMAX.  Creates a
1879*0Sstevel@tonic-gate  * PPPoE response message in outp.  Returns count of matched services
1880*0Sstevel@tonic-gate  * and (through *srvp) a pointer to the last (or only) service.  If
1881*0Sstevel@tonic-gate  * some error is found in the request, an error string is added and -1
1882*0Sstevel@tonic-gate  * is returned; the caller should just send the message without
1883*0Sstevel@tonic-gate  * alteration.
1884*0Sstevel@tonic-gate  */
1885*0Sstevel@tonic-gate int
1886*0Sstevel@tonic-gate locate_service(poep_t *poep, int plen, const char *iname, ppptun_atype *pap,
1887*0Sstevel@tonic-gate     uint32_t *outp, void **srvp)
1888*0Sstevel@tonic-gate {
1889*0Sstevel@tonic-gate 	poep_t *opoe;
1890*0Sstevel@tonic-gate 	const uint8_t *tagp;
1891*0Sstevel@tonic-gate 	const char *cp;
1892*0Sstevel@tonic-gate 	int ttyp;
1893*0Sstevel@tonic-gate 	int tlen;
1894*0Sstevel@tonic-gate 	int nsvcs;
1895*0Sstevel@tonic-gate 	const struct device_entry *dep, *depe;
1896*0Sstevel@tonic-gate 	const struct device_entry *wdep;
1897*0Sstevel@tonic-gate 	const struct service_entry **sepp, **seppe;
1898*0Sstevel@tonic-gate 	const struct service_entry *sep;
1899*0Sstevel@tonic-gate 	char *str;
1900*0Sstevel@tonic-gate 	boolean_t ispadi;
1901*0Sstevel@tonic-gate 
1902*0Sstevel@tonic-gate 	ispadi = poep->poep_code == POECODE_PADI;
1903*0Sstevel@tonic-gate 	opoe = poe_mkheader(outp, ispadi ? POECODE_PADO : POECODE_PADS, 0);
1904*0Sstevel@tonic-gate 
1905*0Sstevel@tonic-gate 	*srvp = NULL;
1906*0Sstevel@tonic-gate 	if (cur_options == NULL)
1907*0Sstevel@tonic-gate 		return (0);
1908*0Sstevel@tonic-gate 
1909*0Sstevel@tonic-gate 	/* Search for named device (lower stream) in tables. */
1910*0Sstevel@tonic-gate 	dep = cur_options->os_devices;
1911*0Sstevel@tonic-gate 	depe = dep + cur_options->os_ndevices;
1912*0Sstevel@tonic-gate 	wdep = NULL;
1913*0Sstevel@tonic-gate 	if ((cp = strchr(iname, ':')) != NULL)
1914*0Sstevel@tonic-gate 		tlen = cp - iname;
1915*0Sstevel@tonic-gate 	else
1916*0Sstevel@tonic-gate 		tlen = strlen(iname);
1917*0Sstevel@tonic-gate 	for (; dep < depe; dep++)
1918*0Sstevel@tonic-gate 		if (strncmp(iname, dep->de_name, tlen) == 0 &&
1919*0Sstevel@tonic-gate 		    dep->de_name[tlen] == '\0')
1920*0Sstevel@tonic-gate 			break;
1921*0Sstevel@tonic-gate 		else if (dep->de_name[0] == '*' && dep->de_name[1] == '\0')
1922*0Sstevel@tonic-gate 			wdep = dep;
1923*0Sstevel@tonic-gate 	if (dep >= depe)
1924*0Sstevel@tonic-gate 		dep = wdep;
1925*0Sstevel@tonic-gate 	/*
1926*0Sstevel@tonic-gate 	 * Return if interface not found.  Zero-service case can't
1927*0Sstevel@tonic-gate 	 * occur, since devices with no services aren't included in
1928*0Sstevel@tonic-gate 	 * the list, but the code is just being safe here.
1929*0Sstevel@tonic-gate 	 */
1930*0Sstevel@tonic-gate 	if (dep == NULL || dep->de_services == NULL || dep->de_nservices <= 0)
1931*0Sstevel@tonic-gate 		return (0);
1932*0Sstevel@tonic-gate 
1933*0Sstevel@tonic-gate 	/*
1934*0Sstevel@tonic-gate 	 * Loop over tags in client message and process them.
1935*0Sstevel@tonic-gate 	 * Services must be matched against our list.  Host-Uniq and
1936*0Sstevel@tonic-gate 	 * Relay-Session-Id must be copied to the reply.  All others
1937*0Sstevel@tonic-gate 	 * must be discarded.
1938*0Sstevel@tonic-gate 	 */
1939*0Sstevel@tonic-gate 	nsvcs = 0;
1940*0Sstevel@tonic-gate 	sepp = dep->de_services;
1941*0Sstevel@tonic-gate 	tagp = (const uint8_t *)(poep + 1);
1942*0Sstevel@tonic-gate 	while (poe_tagcheck(poep, plen, tagp)) {
1943*0Sstevel@tonic-gate 		ttyp = POET_GET_TYPE(tagp);
1944*0Sstevel@tonic-gate 		if (ttyp == POETT_END)
1945*0Sstevel@tonic-gate 			break;
1946*0Sstevel@tonic-gate 		tlen = POET_GET_LENG(tagp);
1947*0Sstevel@tonic-gate 		switch (ttyp) {
1948*0Sstevel@tonic-gate 		case POETT_SERVICE:	/* Service-Name */
1949*0Sstevel@tonic-gate 			/*
1950*0Sstevel@tonic-gate 			 * Allow only one.  (Note that this test works
1951*0Sstevel@tonic-gate 			 * because there's always at least one service
1952*0Sstevel@tonic-gate 			 * per device; otherwise, the device is
1953*0Sstevel@tonic-gate 			 * removed from the list.)
1954*0Sstevel@tonic-gate 			 */
1955*0Sstevel@tonic-gate 			if (sepp != dep->de_services) {
1956*0Sstevel@tonic-gate 				if (nsvcs != -1)
1957*0Sstevel@tonic-gate 					(void) poe_add_str(opoe, POETT_NAMERR,
1958*0Sstevel@tonic-gate 					    "Too many Service-Name tags");
1959*0Sstevel@tonic-gate 				nsvcs = -1;
1960*0Sstevel@tonic-gate 				break;
1961*0Sstevel@tonic-gate 			}
1962*0Sstevel@tonic-gate 			seppe = sepp + dep->de_nservices;
1963*0Sstevel@tonic-gate 			/* Clients's requested service must appear in reply. */
1964*0Sstevel@tonic-gate 			if (tlen != 0 || (ispadi &&
1965*0Sstevel@tonic-gate 				    !(glob_svc.se_flags & SEF_NOWILD)))
1966*0Sstevel@tonic-gate 				(void) poe_tag_copy(opoe, tagp);
1967*0Sstevel@tonic-gate 			if (tlen == 0) {
1968*0Sstevel@tonic-gate 				/*
1969*0Sstevel@tonic-gate 				 * If config specifies "nowild" in a
1970*0Sstevel@tonic-gate 				 * global context, then we don't
1971*0Sstevel@tonic-gate 				 * respond to wildcard PADRs.  The
1972*0Sstevel@tonic-gate 				 * client must know the exact service
1973*0Sstevel@tonic-gate 				 * name to get access.
1974*0Sstevel@tonic-gate 				 */
1975*0Sstevel@tonic-gate 
1976*0Sstevel@tonic-gate 				if (!ispadi && (glob_svc.se_flags & SEF_NOWILD))
1977*0Sstevel@tonic-gate 					sepp = seppe;
1978*0Sstevel@tonic-gate 				while (sepp < seppe) {
1979*0Sstevel@tonic-gate 					sep = *sepp++;
1980*0Sstevel@tonic-gate 					if ((ispadi || !(sep->se_flags &
1981*0Sstevel@tonic-gate 						    SEF_NOWILD)) &&
1982*0Sstevel@tonic-gate 					    allow_service(sep, pap)) {
1983*0Sstevel@tonic-gate 						nsvcs++;
1984*0Sstevel@tonic-gate 						*srvp = (void *)sep;
1985*0Sstevel@tonic-gate 						if (poep->poep_code ==
1986*0Sstevel@tonic-gate 						    POECODE_PADR)
1987*0Sstevel@tonic-gate 							break;
1988*0Sstevel@tonic-gate 						if (sep->se_name[0] == '\0')
1989*0Sstevel@tonic-gate 							continue;
1990*0Sstevel@tonic-gate 						(void) poe_add_str(opoe,
1991*0Sstevel@tonic-gate 						    POETT_SERVICE,
1992*0Sstevel@tonic-gate 						    sep->se_name);
1993*0Sstevel@tonic-gate 					}
1994*0Sstevel@tonic-gate 				}
1995*0Sstevel@tonic-gate 			} else {
1996*0Sstevel@tonic-gate 				/* Requested specific service; find it. */
1997*0Sstevel@tonic-gate 				cp = (char *)POET_DATA(tagp);
1998*0Sstevel@tonic-gate 				while (sepp < seppe) {
1999*0Sstevel@tonic-gate 					sep = *sepp++;
2000*0Sstevel@tonic-gate 					if (strlen(sep->se_name) == tlen &&
2001*0Sstevel@tonic-gate 					    strncasecmp(sep->se_name, cp,
2002*0Sstevel@tonic-gate 						tlen) == 0) {
2003*0Sstevel@tonic-gate 						if (allow_service(sep, pap)) {
2004*0Sstevel@tonic-gate 							nsvcs++;
2005*0Sstevel@tonic-gate 							*srvp = (void *)sep;
2006*0Sstevel@tonic-gate 						}
2007*0Sstevel@tonic-gate 						break;
2008*0Sstevel@tonic-gate 					}
2009*0Sstevel@tonic-gate 				}
2010*0Sstevel@tonic-gate 			}
2011*0Sstevel@tonic-gate 			/*
2012*0Sstevel@tonic-gate 			 * Allow service definition to override
2013*0Sstevel@tonic-gate 			 * AC-Name (concentrator [server] name) field.
2014*0Sstevel@tonic-gate 			 */
2015*0Sstevel@tonic-gate 			if (*srvp != NULL) {
2016*0Sstevel@tonic-gate 				sep = (const struct service_entry *)*srvp;
2017*0Sstevel@tonic-gate 				log_for_service(sep->se_log, sep->se_debug);
2018*0Sstevel@tonic-gate 				str = "Solaris PPPoE";
2019*0Sstevel@tonic-gate 				if (sep->se_server != NULL)
2020*0Sstevel@tonic-gate 					str = sep->se_server;
2021*0Sstevel@tonic-gate 				(void) poe_add_str(opoe, POETT_ACCESS, str);
2022*0Sstevel@tonic-gate 			}
2023*0Sstevel@tonic-gate 			break;
2024*0Sstevel@tonic-gate 		/* Ones we should discard */
2025*0Sstevel@tonic-gate 		case POETT_ACCESS:	/* AC-Name */
2026*0Sstevel@tonic-gate 		case POETT_COOKIE:	/* AC-Cookie */
2027*0Sstevel@tonic-gate 		case POETT_NAMERR:	/* Service-Name-Error */
2028*0Sstevel@tonic-gate 		case POETT_SYSERR:	/* AC-System-Error */
2029*0Sstevel@tonic-gate 		case POETT_GENERR:	/* Generic-Error */
2030*0Sstevel@tonic-gate 		case POETT_HURL:	/* Host-URL */
2031*0Sstevel@tonic-gate 		case POETT_MOTM:	/* Message-Of-The-Minute */
2032*0Sstevel@tonic-gate 		case POETT_RTEADD:	/* IP-Route-Add */
2033*0Sstevel@tonic-gate 		case POETT_VENDOR:	/* Vendor-Specific */
2034*0Sstevel@tonic-gate 		case POETT_MULTI:	/* Multicast-Capable */
2035*0Sstevel@tonic-gate 		default:
2036*0Sstevel@tonic-gate 			break;
2037*0Sstevel@tonic-gate 		/* Ones we should copy */
2038*0Sstevel@tonic-gate 		case POETT_UNIQ:	/* Host-Uniq */
2039*0Sstevel@tonic-gate 		case POETT_RELAY:	/* Relay-Session-Id */
2040*0Sstevel@tonic-gate 			(void) poe_tag_copy(opoe, tagp);
2041*0Sstevel@tonic-gate 			break;
2042*0Sstevel@tonic-gate 		}
2043*0Sstevel@tonic-gate 		tagp = POET_NEXT(tagp);
2044*0Sstevel@tonic-gate 	}
2045*0Sstevel@tonic-gate 	return (nsvcs);
2046*0Sstevel@tonic-gate }
2047*0Sstevel@tonic-gate 
2048*0Sstevel@tonic-gate /*
2049*0Sstevel@tonic-gate  * Like fgetc, but reads from a string.
2050*0Sstevel@tonic-gate  */
2051*0Sstevel@tonic-gate static int
2052*0Sstevel@tonic-gate sgetc(void *arg)
2053*0Sstevel@tonic-gate {
2054*0Sstevel@tonic-gate 	char **cpp = (char **)arg;
2055*0Sstevel@tonic-gate 	if (**cpp == '\0')
2056*0Sstevel@tonic-gate 		return (EOF);
2057*0Sstevel@tonic-gate 	return (*(*cpp)++);
2058*0Sstevel@tonic-gate }
2059*0Sstevel@tonic-gate 
2060*0Sstevel@tonic-gate /*
2061*0Sstevel@tonic-gate  * Given a service structure, launch pppd.  Called by handle_input()
2062*0Sstevel@tonic-gate  * in pppoed.c if locate_service() [above] finds exactly one service
2063*0Sstevel@tonic-gate  * matching a PADR.
2064*0Sstevel@tonic-gate  */
2065*0Sstevel@tonic-gate int
2066*0Sstevel@tonic-gate launch_service(int tunfd, poep_t *poep, void *srvp, struct ppptun_control *ptc)
2067*0Sstevel@tonic-gate {
2068*0Sstevel@tonic-gate 	const struct service_entry *sep = (const struct service_entry *)srvp;
2069*0Sstevel@tonic-gate 	const char *path;
2070*0Sstevel@tonic-gate 	const char *extra;
2071*0Sstevel@tonic-gate 	const char *pppd;
2072*0Sstevel@tonic-gate 	const char *cp;
2073*0Sstevel@tonic-gate 	pid_t pidv;
2074*0Sstevel@tonic-gate 	int newtun;
2075*0Sstevel@tonic-gate 	struct ppptun_peer ptp;
2076*0Sstevel@tonic-gate 	union ppptun_name ptn;
2077*0Sstevel@tonic-gate 	const char *args[MAXARGS];
2078*0Sstevel@tonic-gate 	struct strbuf ctrl;
2079*0Sstevel@tonic-gate 	struct strbuf data;
2080*0Sstevel@tonic-gate 	const char **cpp;
2081*0Sstevel@tonic-gate 	char *sptr;
2082*0Sstevel@tonic-gate 	char *spv;
2083*0Sstevel@tonic-gate 	int slen;
2084*0Sstevel@tonic-gate 	int retv;
2085*0Sstevel@tonic-gate 	char keybuf[MAX_KEYWORD];
2086*0Sstevel@tonic-gate 
2087*0Sstevel@tonic-gate 	assert(sep != NULL);
2088*0Sstevel@tonic-gate 
2089*0Sstevel@tonic-gate 	/* Get tunnel driver connection for new PPP session. */
2090*0Sstevel@tonic-gate 	newtun = open(tunnam, O_RDWR);
2091*0Sstevel@tonic-gate 	if (newtun == -1)
2092*0Sstevel@tonic-gate 		goto syserr;
2093*0Sstevel@tonic-gate 
2094*0Sstevel@tonic-gate 	/* Set this session up for standard PPP and client's address. */
2095*0Sstevel@tonic-gate 	(void) memset(&ptp, '\0', sizeof (ptp));
2096*0Sstevel@tonic-gate 	ptp.ptp_style = PTS_PPPOE;
2097*0Sstevel@tonic-gate 	ptp.ptp_address = ptc->ptc_address;
2098*0Sstevel@tonic-gate 	if (strioctl(newtun, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
2099*0Sstevel@tonic-gate 	    0)
2100*0Sstevel@tonic-gate 		goto syserr;
2101*0Sstevel@tonic-gate 	ptp.ptp_rsessid = ptp.ptp_lsessid;
2102*0Sstevel@tonic-gate 	if (strioctl(newtun, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
2103*0Sstevel@tonic-gate 	    0)
2104*0Sstevel@tonic-gate 		goto syserr;
2105*0Sstevel@tonic-gate 
2106*0Sstevel@tonic-gate 	/* Attach the requested lower stream. */
2107*0Sstevel@tonic-gate 	cp = strchr(ptc->ptc_name, ':');
2108*0Sstevel@tonic-gate 	if (cp == NULL)
2109*0Sstevel@tonic-gate 		cp = ptc->ptc_name + strlen(ptc->ptc_name);
2110*0Sstevel@tonic-gate 	(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%.*s:pppoe",
2111*0Sstevel@tonic-gate 	    cp-ptc->ptc_name, ptc->ptc_name);
2112*0Sstevel@tonic-gate 	if (strioctl(newtun, PPPTUN_SDATA, &ptn, sizeof (ptn), 0) < 0)
2113*0Sstevel@tonic-gate 		goto syserr;
2114*0Sstevel@tonic-gate 	(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%.*s:pppoed",
2115*0Sstevel@tonic-gate 	    cp-ptc->ptc_name, ptc->ptc_name);
2116*0Sstevel@tonic-gate 	if (strioctl(newtun, PPPTUN_SCTL, &ptn, sizeof (ptn), 0) < 0)
2117*0Sstevel@tonic-gate 		goto syserr;
2118*0Sstevel@tonic-gate 
2119*0Sstevel@tonic-gate 	pidv = fork();
2120*0Sstevel@tonic-gate 	if (pidv == (pid_t)-1)
2121*0Sstevel@tonic-gate 		goto syserr;
2122*0Sstevel@tonic-gate 
2123*0Sstevel@tonic-gate 	if (pidv == (pid_t)0) {
2124*0Sstevel@tonic-gate 		/*
2125*0Sstevel@tonic-gate 		 * Use syslog only in order to avoid mixing log messages
2126*0Sstevel@tonic-gate 		 * in regular files.
2127*0Sstevel@tonic-gate 		 */
2128*0Sstevel@tonic-gate 		close_log_files();
2129*0Sstevel@tonic-gate 
2130*0Sstevel@tonic-gate 		if ((path = sep->se_path) == NULL)
2131*0Sstevel@tonic-gate 			path = "/usr/bin/pppd";
2132*0Sstevel@tonic-gate 		if ((extra = sep->se_extra) == NULL)
2133*0Sstevel@tonic-gate 			extra = "plugin pppoe.so directtty";
2134*0Sstevel@tonic-gate 		if ((pppd = sep->se_pppd) == NULL)
2135*0Sstevel@tonic-gate 			pppd = "";
2136*0Sstevel@tonic-gate 
2137*0Sstevel@tonic-gate 		/* Concatenate these. */
2138*0Sstevel@tonic-gate 		slen = strlen(path) + strlen(extra) + strlen(pppd) + 3;
2139*0Sstevel@tonic-gate 		if ((sptr = (char *)malloc(slen)) == NULL)
2140*0Sstevel@tonic-gate 			goto bail_out;
2141*0Sstevel@tonic-gate 		(void) strcpy(sptr, path);
2142*0Sstevel@tonic-gate 		(void) strcat(sptr, " ");
2143*0Sstevel@tonic-gate 		(void) strcat(sptr, extra);
2144*0Sstevel@tonic-gate 		(void) strcat(sptr, " ");
2145*0Sstevel@tonic-gate 		(void) strcat(sptr, pppd);
2146*0Sstevel@tonic-gate 
2147*0Sstevel@tonic-gate 		/* Parse out into arguments */
2148*0Sstevel@tonic-gate 		cpp = args;
2149*0Sstevel@tonic-gate 		spv = sptr;
2150*0Sstevel@tonic-gate 		while (cpp < args + MAXARGS - 1) {
2151*0Sstevel@tonic-gate 			retv = getkeyword(NULL, keybuf, sizeof (keybuf), sgetc,
2152*0Sstevel@tonic-gate 			    (void *)&spv, 1);
2153*0Sstevel@tonic-gate 			if (retv != 1)
2154*0Sstevel@tonic-gate 				*cpp++ = strsave(keybuf);
2155*0Sstevel@tonic-gate 			if (retv != 0)
2156*0Sstevel@tonic-gate 				break;
2157*0Sstevel@tonic-gate 		}
2158*0Sstevel@tonic-gate 		*cpp = NULL;
2159*0Sstevel@tonic-gate 		if (cpp == args)
2160*0Sstevel@tonic-gate 			goto bail_out;
2161*0Sstevel@tonic-gate 
2162*0Sstevel@tonic-gate 		/*
2163*0Sstevel@tonic-gate 		 * Fix tunnel device on stdin/stdout and error file on
2164*0Sstevel@tonic-gate 		 * stderr.
2165*0Sstevel@tonic-gate 		 */
2166*0Sstevel@tonic-gate 		if (newtun != 0 && dup2(newtun, 0) < 0)
2167*0Sstevel@tonic-gate 			goto bail_out;
2168*0Sstevel@tonic-gate 		if (newtun != 1 && dup2(newtun, 1) < 0)
2169*0Sstevel@tonic-gate 			goto bail_out;
2170*0Sstevel@tonic-gate 		if (newtun > 1)
2171*0Sstevel@tonic-gate 			(void) close(newtun);
2172*0Sstevel@tonic-gate 		if (tunfd > 1)
2173*0Sstevel@tonic-gate 			(void) close(tunfd);
2174*0Sstevel@tonic-gate 		(void) close(2);
2175*0Sstevel@tonic-gate 		(void) open("/etc/ppp/pppoe-errors", O_WRONLY | O_APPEND |
2176*0Sstevel@tonic-gate 		    O_CREAT, 0600);
2177*0Sstevel@tonic-gate 
2178*0Sstevel@tonic-gate 		/*
2179*0Sstevel@tonic-gate 		 * Change GID first, for obvious reasons.  Note that
2180*0Sstevel@tonic-gate 		 * we log any problems to syslog, not the errors file.
2181*0Sstevel@tonic-gate 		 * The errors file is intended for problems in the
2182*0Sstevel@tonic-gate 		 * exec'd program.
2183*0Sstevel@tonic-gate 		 */
2184*0Sstevel@tonic-gate 		if ((sep->se_flags & SEF_GIDSET) &&
2185*0Sstevel@tonic-gate 		    setgid(sep->se_gid) == -1) {
2186*0Sstevel@tonic-gate 			cp = mystrerror(errno);
2187*0Sstevel@tonic-gate 			reopen_log();
2188*0Sstevel@tonic-gate 			logerr("setgid(%d): %s", sep->se_gid, cp);
2189*0Sstevel@tonic-gate 			goto logged;
2190*0Sstevel@tonic-gate 		}
2191*0Sstevel@tonic-gate 		if ((sep->se_flags & SEF_UIDSET) &&
2192*0Sstevel@tonic-gate 		    setuid(sep->se_uid) == -1) {
2193*0Sstevel@tonic-gate 			cp = mystrerror(errno);
2194*0Sstevel@tonic-gate 			reopen_log();
2195*0Sstevel@tonic-gate 			logerr("setuid(%d): %s", sep->se_uid, cp);
2196*0Sstevel@tonic-gate 			goto logged;
2197*0Sstevel@tonic-gate 		}
2198*0Sstevel@tonic-gate 
2199*0Sstevel@tonic-gate 		/* Run pppd */
2200*0Sstevel@tonic-gate 		path = args[0];
2201*0Sstevel@tonic-gate 		cp = strrchr(args[0], '/');
2202*0Sstevel@tonic-gate 		if (cp != NULL && cp[1] != '\0')
2203*0Sstevel@tonic-gate 			args[0] = cp+1;
2204*0Sstevel@tonic-gate 		errno = 0;
2205*0Sstevel@tonic-gate 		(void) execv(path, (char * const *)args);
2206*0Sstevel@tonic-gate 		newtun = 0;
2207*0Sstevel@tonic-gate 
2208*0Sstevel@tonic-gate 		/*
2209*0Sstevel@tonic-gate 		 * Exec failure; attempt to log the problem and send a
2210*0Sstevel@tonic-gate 		 * PADT to the client so that he knows the session
2211*0Sstevel@tonic-gate 		 * went south.
2212*0Sstevel@tonic-gate 		 */
2213*0Sstevel@tonic-gate 	bail_out:
2214*0Sstevel@tonic-gate 		cp = mystrerror(errno);
2215*0Sstevel@tonic-gate 		reopen_log();
2216*0Sstevel@tonic-gate 		logerr("\"%s\": %s", (sptr == NULL ? path : sptr), cp);
2217*0Sstevel@tonic-gate 	logged:
2218*0Sstevel@tonic-gate 		poep = poe_mkheader(pkt_output, POECODE_PADT, ptp.ptp_lsessid);
2219*0Sstevel@tonic-gate 		poep->poep_session_id = htons(ptp.ptp_lsessid);
2220*0Sstevel@tonic-gate 		(void) poe_add_str(poep, POETT_SYSERR, cp);
2221*0Sstevel@tonic-gate 		(void) sleep(1);
2222*0Sstevel@tonic-gate 		ctrl.len = sizeof (*ptc);
2223*0Sstevel@tonic-gate 		ctrl.buf = (caddr_t)ptc;
2224*0Sstevel@tonic-gate 		data.len = poe_length(poep) + sizeof (*poep);
2225*0Sstevel@tonic-gate 		data.buf = (caddr_t)poep;
2226*0Sstevel@tonic-gate 		if (putmsg(newtun, &ctrl, &data, 0) < 0) {
2227*0Sstevel@tonic-gate 			logerr("putmsg %s: %s", ptc->ptc_name,
2228*0Sstevel@tonic-gate 			    mystrerror(errno));
2229*0Sstevel@tonic-gate 		}
2230*0Sstevel@tonic-gate 		exit(1);
2231*0Sstevel@tonic-gate 	}
2232*0Sstevel@tonic-gate 
2233*0Sstevel@tonic-gate 	(void) close(newtun);
2234*0Sstevel@tonic-gate 
2235*0Sstevel@tonic-gate 	/* Give session ID to client in reply. */
2236*0Sstevel@tonic-gate 	poep->poep_session_id = htons(ptp.ptp_lsessid);
2237*0Sstevel@tonic-gate 	return (1);
2238*0Sstevel@tonic-gate 
2239*0Sstevel@tonic-gate syserr:
2240*0Sstevel@tonic-gate 	/* Peer doesn't know session ID yet; hope for the best. */
2241*0Sstevel@tonic-gate 	retv = errno;
2242*0Sstevel@tonic-gate 	if (newtun >= 0)
2243*0Sstevel@tonic-gate 		(void) close(newtun);
2244*0Sstevel@tonic-gate 	(void) poe_add_str(poep, POETT_SYSERR, mystrerror(retv));
2245*0Sstevel@tonic-gate 	return (0);
2246*0Sstevel@tonic-gate }
2247*0Sstevel@tonic-gate 
2248*0Sstevel@tonic-gate /*
2249*0Sstevel@tonic-gate  * This is pretty awful -- it uses recursion to print a simple list.
2250*0Sstevel@tonic-gate  * It's just for debug, though, and does a reasonable job of printing
2251*0Sstevel@tonic-gate  * the filters in the right order.
2252*0Sstevel@tonic-gate  */
2253*0Sstevel@tonic-gate static void
2254*0Sstevel@tonic-gate print_filter_list(FILE *fp, struct filter_entry *fep)
2255*0Sstevel@tonic-gate {
2256*0Sstevel@tonic-gate 	if (fep->fe_prev != NULL)
2257*0Sstevel@tonic-gate 		print_filter_list(fp, fep->fe_prev);
2258*0Sstevel@tonic-gate 	(void) fprintf(fp, "\t\t    MAC %s", ehost2(&fep->fe_mac));
2259*0Sstevel@tonic-gate 	(void) fprintf(fp, ", mask %s%s\n", ehost2(&fep->fe_mask),
2260*0Sstevel@tonic-gate 	    (fep->fe_isexcept ? ", except" : ""));
2261*0Sstevel@tonic-gate }
2262*0Sstevel@tonic-gate 
2263*0Sstevel@tonic-gate /*
2264*0Sstevel@tonic-gate  * Write summary of parsed configuration data to given file.
2265*0Sstevel@tonic-gate  */
2266*0Sstevel@tonic-gate void
2267*0Sstevel@tonic-gate dump_configuration(FILE *fp)
2268*0Sstevel@tonic-gate {
2269*0Sstevel@tonic-gate 	const struct device_entry *dep;
2270*0Sstevel@tonic-gate 	const struct service_entry *sep, **sepp;
2271*0Sstevel@tonic-gate 	struct per_file *pfp;
2272*0Sstevel@tonic-gate 	int i, j;
2273*0Sstevel@tonic-gate 
2274*0Sstevel@tonic-gate 	(void) fprintf(fp, "Will%s respond to wildcard queries.\n",
2275*0Sstevel@tonic-gate 	    (glob_svc.se_flags & SEF_NOWILD) ? " not" : "");
2276*0Sstevel@tonic-gate 	(void) fprintf(fp,
2277*0Sstevel@tonic-gate 	    "Global debug level %d, log to %s; current level %d\n",
2278*0Sstevel@tonic-gate 	    glob_svc.se_debug,
2279*0Sstevel@tonic-gate 	    ((glob_svc.se_log == NULL || *glob_svc.se_log == '\0') ?
2280*0Sstevel@tonic-gate 		"syslog" : glob_svc.se_log),
2281*0Sstevel@tonic-gate 	    log_level);
2282*0Sstevel@tonic-gate 	if (cur_options == NULL) {
2283*0Sstevel@tonic-gate 		(void) fprintf(fp, "No current configuration.\n");
2284*0Sstevel@tonic-gate 		return;
2285*0Sstevel@tonic-gate 	}
2286*0Sstevel@tonic-gate 	(void) fprintf(fp, "Current configuration:\n");
2287*0Sstevel@tonic-gate 	(void) fprintf(fp, "    %d device(s):\n", cur_options->os_ndevices);
2288*0Sstevel@tonic-gate 	dep = cur_options->os_devices;
2289*0Sstevel@tonic-gate 	for (i = 0; i < cur_options->os_ndevices; i++, dep++) {
2290*0Sstevel@tonic-gate 		(void) fprintf(fp, "\t%s: %d service(s):\n",
2291*0Sstevel@tonic-gate 		    dep->de_name, dep->de_nservices);
2292*0Sstevel@tonic-gate 		sepp = dep->de_services;
2293*0Sstevel@tonic-gate 		for (j = 0; j < dep->de_nservices; j++, sepp++) {
2294*0Sstevel@tonic-gate 			sep = *sepp;
2295*0Sstevel@tonic-gate 			(void) fprintf(fp, "\t    %s: debug level %d",
2296*0Sstevel@tonic-gate 			    sep->se_name, sep->se_debug);
2297*0Sstevel@tonic-gate 			if (sep->se_flags & SEF_UIDSET)
2298*0Sstevel@tonic-gate 				(void) fprintf(fp, ", UID %ld", sep->se_uid);
2299*0Sstevel@tonic-gate 			if (sep->se_flags & SEF_GIDSET)
2300*0Sstevel@tonic-gate 				(void) fprintf(fp, ", GID %ld", sep->se_gid);
2301*0Sstevel@tonic-gate 			if (sep->se_flags & SEF_WILD)
2302*0Sstevel@tonic-gate 				(void) fprintf(fp, ", wildcard");
2303*0Sstevel@tonic-gate 			else if (sep->se_flags & SEF_NOWILD)
2304*0Sstevel@tonic-gate 				(void) fprintf(fp, ", nowildcard");
2305*0Sstevel@tonic-gate 			else
2306*0Sstevel@tonic-gate 				(void) fprintf(fp, ", wildcard (default)");
2307*0Sstevel@tonic-gate 			(void) putc('\n', fp);
2308*0Sstevel@tonic-gate 			if (sep->se_server != NULL)
2309*0Sstevel@tonic-gate 				(void) fprintf(fp, "\t\tserver \"%s\"\n",
2310*0Sstevel@tonic-gate 				    sep->se_server);
2311*0Sstevel@tonic-gate 			if (sep->se_pppd != NULL)
2312*0Sstevel@tonic-gate 				(void) fprintf(fp, "\t\tpppd \"%s\"\n",
2313*0Sstevel@tonic-gate 				    sep->se_pppd);
2314*0Sstevel@tonic-gate 			if (sep->se_path != NULL)
2315*0Sstevel@tonic-gate 				(void) fprintf(fp, "\t\tpath \"%s\"\n",
2316*0Sstevel@tonic-gate 				    sep->se_path);
2317*0Sstevel@tonic-gate 			if (sep->se_extra != NULL)
2318*0Sstevel@tonic-gate 				(void) fprintf(fp, "\t\textra \"%s\"\n",
2319*0Sstevel@tonic-gate 				    sep->se_extra);
2320*0Sstevel@tonic-gate 			if (sep->se_log != NULL)
2321*0Sstevel@tonic-gate 				(void) fprintf(fp, "\t\tlog \"%s\"\n",
2322*0Sstevel@tonic-gate 				    sep->se_log);
2323*0Sstevel@tonic-gate 			if (sep->se_flist != NULL) {
2324*0Sstevel@tonic-gate 				(void) fprintf(fp, "\t\tfilter list:\n");
2325*0Sstevel@tonic-gate 				print_filter_list(fp, sep->se_flist);
2326*0Sstevel@tonic-gate 			}
2327*0Sstevel@tonic-gate 		}
2328*0Sstevel@tonic-gate 	}
2329*0Sstevel@tonic-gate 	(void) fprintf(fp, "\nConfiguration read from:\n");
2330*0Sstevel@tonic-gate 	for (pfp = cur_options->os_pfjunk; pfp != NULL; pfp = pfp->pf_prev) {
2331*0Sstevel@tonic-gate 		(void) fprintf(fp, "    %s: %d service(s)\n", pfp->pf_name,
2332*0Sstevel@tonic-gate 		    pfp->pf_nsvc);
2333*0Sstevel@tonic-gate 	}
2334*0Sstevel@tonic-gate }
2335