10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
52397Sbasabi * Common Development and Distribution License (the "License").
62397Sbasabi * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
21236Schin
220Sstevel@tonic-gate /*
23*12986SJohn.Zolnowsky@Sun.COM * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
26236Schin /*
27236Schin * logadm/conf.c -- configuration file module
28236Schin */
29236Schin
300Sstevel@tonic-gate #include <stdio.h>
310Sstevel@tonic-gate #include <libintl.h>
320Sstevel@tonic-gate #include <fcntl.h>
330Sstevel@tonic-gate #include <sys/types.h>
340Sstevel@tonic-gate #include <sys/stat.h>
350Sstevel@tonic-gate #include <sys/mman.h>
360Sstevel@tonic-gate #include <ctype.h>
370Sstevel@tonic-gate #include <strings.h>
380Sstevel@tonic-gate #include <unistd.h>
390Sstevel@tonic-gate #include <stdlib.h>
40*12986SJohn.Zolnowsky@Sun.COM #include <limits.h>
410Sstevel@tonic-gate #include "err.h"
420Sstevel@tonic-gate #include "lut.h"
430Sstevel@tonic-gate #include "fn.h"
440Sstevel@tonic-gate #include "opts.h"
450Sstevel@tonic-gate #include "conf.h"
460Sstevel@tonic-gate
470Sstevel@tonic-gate /* forward declarations of functions private to this module */
48*12986SJohn.Zolnowsky@Sun.COM static void fillconflist(int lineno, const char *entry,
490Sstevel@tonic-gate struct opts *opts, const char *com, int flags);
500Sstevel@tonic-gate static void fillargs(char *arg);
510Sstevel@tonic-gate static char *nexttok(char **ptrptr);
52*12986SJohn.Zolnowsky@Sun.COM static void conf_print(FILE *cstream, FILE *tstream);
530Sstevel@tonic-gate
540Sstevel@tonic-gate static const char *Confname; /* name of the confile file */
55*12986SJohn.Zolnowsky@Sun.COM static int Conffd = -1; /* file descriptor for config file */
560Sstevel@tonic-gate static char *Confbuf; /* copy of the config file (a la mmap()) */
57*12986SJohn.Zolnowsky@Sun.COM static int Conflen; /* length of mmap'd config file area */
58*12986SJohn.Zolnowsky@Sun.COM static const char *Timesname; /* name of the timestamps file */
59*12986SJohn.Zolnowsky@Sun.COM static int Timesfd = -1; /* file descriptor for timestamps file */
60*12986SJohn.Zolnowsky@Sun.COM static char *Timesbuf; /* copy of the timestamps file (a la mmap()) */
61*12986SJohn.Zolnowsky@Sun.COM static int Timeslen; /* length of mmap'd timestamps area */
62*12986SJohn.Zolnowsky@Sun.COM static int Singlefile; /* Conf and Times in the same file */
63*12986SJohn.Zolnowsky@Sun.COM static int Changed; /* what changes need to be written back */
64*12986SJohn.Zolnowsky@Sun.COM static int Canchange; /* what changes can be written back */
65*12986SJohn.Zolnowsky@Sun.COM static int Changing; /* what changes have been requested */
66*12986SJohn.Zolnowsky@Sun.COM #define CHG_NONE 0
67*12986SJohn.Zolnowsky@Sun.COM #define CHG_TIMES 1
68*12986SJohn.Zolnowsky@Sun.COM #define CHG_BOTH 3
690Sstevel@tonic-gate
700Sstevel@tonic-gate /*
710Sstevel@tonic-gate * our structured representation of the configuration file
720Sstevel@tonic-gate * is made up of a list of these
730Sstevel@tonic-gate */
74236Schin struct confinfo {
750Sstevel@tonic-gate struct confinfo *cf_next;
760Sstevel@tonic-gate int cf_lineno; /* line number in file */
770Sstevel@tonic-gate const char *cf_entry; /* name of entry, if line has an entry */
780Sstevel@tonic-gate struct opts *cf_opts; /* parsed rhs of entry */
790Sstevel@tonic-gate const char *cf_com; /* any comment text found */
800Sstevel@tonic-gate int cf_flags;
810Sstevel@tonic-gate };
820Sstevel@tonic-gate
830Sstevel@tonic-gate #define CONFF_DELETED 1 /* entry should be deleted on write back */
840Sstevel@tonic-gate
850Sstevel@tonic-gate static struct confinfo *Confinfo; /* the entries in the config file */
860Sstevel@tonic-gate static struct confinfo *Confinfolast; /* end of list */
870Sstevel@tonic-gate static struct lut *Conflut; /* lookup table keyed by entry name */
880Sstevel@tonic-gate static struct fn_list *Confentries; /* list of valid entry names */
890Sstevel@tonic-gate
900Sstevel@tonic-gate /* allocate & fill in another entry in our list */
910Sstevel@tonic-gate static void
fillconflist(int lineno,const char * entry,struct opts * opts,const char * com,int flags)92*12986SJohn.Zolnowsky@Sun.COM fillconflist(int lineno, const char *entry,
930Sstevel@tonic-gate struct opts *opts, const char *com, int flags)
940Sstevel@tonic-gate {
950Sstevel@tonic-gate struct confinfo *cp = MALLOC(sizeof (*cp));
960Sstevel@tonic-gate
970Sstevel@tonic-gate cp->cf_next = NULL;
980Sstevel@tonic-gate cp->cf_lineno = lineno;
990Sstevel@tonic-gate cp->cf_entry = entry;
1000Sstevel@tonic-gate cp->cf_opts = opts;
1010Sstevel@tonic-gate cp->cf_com = com;
1020Sstevel@tonic-gate cp->cf_flags = flags;
1032397Sbasabi if (entry != NULL) {
1040Sstevel@tonic-gate Conflut = lut_add(Conflut, entry, cp);
1050Sstevel@tonic-gate fn_list_adds(Confentries, entry);
1060Sstevel@tonic-gate }
1070Sstevel@tonic-gate if (Confinfo == NULL)
1080Sstevel@tonic-gate Confinfo = Confinfolast = cp;
1090Sstevel@tonic-gate else {
1100Sstevel@tonic-gate Confinfolast->cf_next = cp;
1110Sstevel@tonic-gate Confinfolast = cp;
1120Sstevel@tonic-gate }
1130Sstevel@tonic-gate }
1140Sstevel@tonic-gate
1150Sstevel@tonic-gate static char **Args; /* static buffer for args */
1160Sstevel@tonic-gate static int ArgsN; /* size of our static buffer */
1170Sstevel@tonic-gate static int ArgsI; /* index into Cmdargs as we walk table */
1180Sstevel@tonic-gate #define CONF_ARGS_INC 1024
1190Sstevel@tonic-gate
1200Sstevel@tonic-gate /* callback for lut_walk to build a cmdargs vector */
1210Sstevel@tonic-gate static void
fillargs(char * arg)1220Sstevel@tonic-gate fillargs(char *arg)
1230Sstevel@tonic-gate {
1240Sstevel@tonic-gate if (ArgsI >= ArgsN) {
1250Sstevel@tonic-gate /* need bigger table */
1260Sstevel@tonic-gate Args = REALLOC(Args, sizeof (char *) * (ArgsN + CONF_ARGS_INC));
1270Sstevel@tonic-gate ArgsN += CONF_ARGS_INC;
1280Sstevel@tonic-gate }
1290Sstevel@tonic-gate Args[ArgsI++] = arg;
1300Sstevel@tonic-gate }
1310Sstevel@tonic-gate
1320Sstevel@tonic-gate /* isolate and return the next token */
1330Sstevel@tonic-gate static char *
nexttok(char ** ptrptr)1340Sstevel@tonic-gate nexttok(char **ptrptr)
1350Sstevel@tonic-gate {
1360Sstevel@tonic-gate char *ptr = *ptrptr;
1370Sstevel@tonic-gate char *eptr;
1380Sstevel@tonic-gate char *quote = NULL;
1390Sstevel@tonic-gate
1400Sstevel@tonic-gate while (*ptr && isspace(*ptr))
1410Sstevel@tonic-gate ptr++;
1420Sstevel@tonic-gate
1430Sstevel@tonic-gate if (*ptr == '"' || *ptr == '\'')
1440Sstevel@tonic-gate quote = ptr++;
1450Sstevel@tonic-gate
1460Sstevel@tonic-gate for (eptr = ptr; *eptr; eptr++)
1470Sstevel@tonic-gate if (quote && *eptr == *quote) {
1480Sstevel@tonic-gate /* found end quote */
1490Sstevel@tonic-gate *eptr++ = '\0';
1500Sstevel@tonic-gate *ptrptr = eptr;
1510Sstevel@tonic-gate return (ptr);
1520Sstevel@tonic-gate } else if (!quote && isspace(*eptr)) {
1530Sstevel@tonic-gate /* found end of unquoted area */
1540Sstevel@tonic-gate *eptr++ = '\0';
1550Sstevel@tonic-gate *ptrptr = eptr;
1560Sstevel@tonic-gate return (ptr);
1570Sstevel@tonic-gate }
1580Sstevel@tonic-gate
1592397Sbasabi if (quote != NULL)
1600Sstevel@tonic-gate err(EF_FILE|EF_JMP, "Unbalanced %c quote", *quote);
1610Sstevel@tonic-gate /*NOTREACHED*/
1620Sstevel@tonic-gate
1630Sstevel@tonic-gate *ptrptr = eptr;
1640Sstevel@tonic-gate
1650Sstevel@tonic-gate if (ptr == eptr)
1660Sstevel@tonic-gate return (NULL);
1670Sstevel@tonic-gate else
1680Sstevel@tonic-gate return (ptr);
1690Sstevel@tonic-gate }
1700Sstevel@tonic-gate
1710Sstevel@tonic-gate /*
172*12986SJohn.Zolnowsky@Sun.COM * scan the memory image of a file
173*12986SJohn.Zolnowsky@Sun.COM * returns: 0: error, 1: ok, 3: -P option found
1740Sstevel@tonic-gate */
175*12986SJohn.Zolnowsky@Sun.COM static int
conf_scan(const char * fname,char * buf,int buflen,int timescan,struct opts * cliopts)176*12986SJohn.Zolnowsky@Sun.COM conf_scan(const char *fname, char *buf, int buflen, int timescan,
177*12986SJohn.Zolnowsky@Sun.COM struct opts *cliopts)
1780Sstevel@tonic-gate {
179*12986SJohn.Zolnowsky@Sun.COM int ret = 1;
1800Sstevel@tonic-gate int lineno = 0;
1810Sstevel@tonic-gate char *line;
1820Sstevel@tonic-gate char *eline;
1830Sstevel@tonic-gate char *ebuf;
184*12986SJohn.Zolnowsky@Sun.COM char *entry, *comment;
1850Sstevel@tonic-gate
186*12986SJohn.Zolnowsky@Sun.COM ebuf = &buf[buflen];
1870Sstevel@tonic-gate
188*12986SJohn.Zolnowsky@Sun.COM if (buf[buflen - 1] != '\n')
189*12986SJohn.Zolnowsky@Sun.COM err(EF_WARN|EF_FILE, "file %s doesn't end with newline, "
190*12986SJohn.Zolnowsky@Sun.COM "last line ignored.", fname);
1910Sstevel@tonic-gate
192*12986SJohn.Zolnowsky@Sun.COM for (line = buf; line < ebuf; line = eline) {
193*12986SJohn.Zolnowsky@Sun.COM char *ap;
194*12986SJohn.Zolnowsky@Sun.COM struct opts *opts = NULL;
195*12986SJohn.Zolnowsky@Sun.COM struct confinfo *cp;
1960Sstevel@tonic-gate
1970Sstevel@tonic-gate lineno++;
198*12986SJohn.Zolnowsky@Sun.COM err_fileline(fname, lineno);
1990Sstevel@tonic-gate eline = line;
2000Sstevel@tonic-gate comment = NULL;
2010Sstevel@tonic-gate for (; eline < ebuf; eline++) {
2020Sstevel@tonic-gate /* check for continued lines */
2030Sstevel@tonic-gate if (comment == NULL && *eline == '\\' &&
2040Sstevel@tonic-gate eline + 1 < ebuf && *(eline + 1) == '\n') {
2050Sstevel@tonic-gate *eline = ' ';
2060Sstevel@tonic-gate *(eline + 1) = ' ';
2070Sstevel@tonic-gate lineno++;
208*12986SJohn.Zolnowsky@Sun.COM err_fileline(fname, lineno);
2090Sstevel@tonic-gate continue;
2100Sstevel@tonic-gate }
2110Sstevel@tonic-gate
2120Sstevel@tonic-gate /* check for comments */
2130Sstevel@tonic-gate if (comment == NULL && *eline == '#') {
2140Sstevel@tonic-gate *eline = '\0';
2150Sstevel@tonic-gate comment = (eline + 1);
2160Sstevel@tonic-gate continue;
2170Sstevel@tonic-gate }
2180Sstevel@tonic-gate
2190Sstevel@tonic-gate /* check for end of line */
2200Sstevel@tonic-gate if (*eline == '\n')
2210Sstevel@tonic-gate break;
2220Sstevel@tonic-gate }
2230Sstevel@tonic-gate if (comment >= ebuf)
2240Sstevel@tonic-gate comment = NULL;
225*12986SJohn.Zolnowsky@Sun.COM if (eline >= ebuf) {
226*12986SJohn.Zolnowsky@Sun.COM /* discard trailing unterminated line */
227*12986SJohn.Zolnowsky@Sun.COM continue;
228*12986SJohn.Zolnowsky@Sun.COM }
229*12986SJohn.Zolnowsky@Sun.COM *eline++ = '\0';
2300Sstevel@tonic-gate
231*12986SJohn.Zolnowsky@Sun.COM /*
232*12986SJohn.Zolnowsky@Sun.COM * now we have the entry, if any, at "line"
233*12986SJohn.Zolnowsky@Sun.COM * and the comment, if any, at "comment"
234*12986SJohn.Zolnowsky@Sun.COM */
2350Sstevel@tonic-gate
236*12986SJohn.Zolnowsky@Sun.COM /* entry is first token */
237*12986SJohn.Zolnowsky@Sun.COM entry = nexttok(&line);
238*12986SJohn.Zolnowsky@Sun.COM if (entry == NULL) {
239*12986SJohn.Zolnowsky@Sun.COM /* it's just a comment line */
240*12986SJohn.Zolnowsky@Sun.COM if (!timescan)
241*12986SJohn.Zolnowsky@Sun.COM fillconflist(lineno, entry, NULL, comment, 0);
242*12986SJohn.Zolnowsky@Sun.COM continue;
243*12986SJohn.Zolnowsky@Sun.COM }
244*12986SJohn.Zolnowsky@Sun.COM if (strcmp(entry, "logadm-version") == 0) {
2450Sstevel@tonic-gate /*
246*12986SJohn.Zolnowsky@Sun.COM * we somehow opened some future format
247*12986SJohn.Zolnowsky@Sun.COM * conffile that we likely don't understand.
248*12986SJohn.Zolnowsky@Sun.COM * if the given version is "1" then go on,
249*12986SJohn.Zolnowsky@Sun.COM * otherwise someone is mixing versions
250*12986SJohn.Zolnowsky@Sun.COM * and we can't help them other than to
251*12986SJohn.Zolnowsky@Sun.COM * print an error and exit.
2520Sstevel@tonic-gate */
2530Sstevel@tonic-gate if ((entry = nexttok(&line)) != NULL &&
254*12986SJohn.Zolnowsky@Sun.COM strcmp(entry, "1") != 0)
255*12986SJohn.Zolnowsky@Sun.COM err(0, "%s version not supported "
256*12986SJohn.Zolnowsky@Sun.COM "by this version of logadm.",
257*12986SJohn.Zolnowsky@Sun.COM fname);
258*12986SJohn.Zolnowsky@Sun.COM continue;
259*12986SJohn.Zolnowsky@Sun.COM }
260*12986SJohn.Zolnowsky@Sun.COM
261*12986SJohn.Zolnowsky@Sun.COM /* form an argv array */
262*12986SJohn.Zolnowsky@Sun.COM ArgsI = 0;
263*12986SJohn.Zolnowsky@Sun.COM while (ap = nexttok(&line))
264*12986SJohn.Zolnowsky@Sun.COM fillargs(ap);
265*12986SJohn.Zolnowsky@Sun.COM Args[ArgsI] = NULL;
266*12986SJohn.Zolnowsky@Sun.COM
267*12986SJohn.Zolnowsky@Sun.COM LOCAL_ERR_BEGIN {
268*12986SJohn.Zolnowsky@Sun.COM if (SETJMP) {
269*12986SJohn.Zolnowsky@Sun.COM err(EF_FILE, "cannot process invalid entry %s",
270*12986SJohn.Zolnowsky@Sun.COM entry);
271*12986SJohn.Zolnowsky@Sun.COM ret = 0;
272*12986SJohn.Zolnowsky@Sun.COM LOCAL_ERR_BREAK;
273*12986SJohn.Zolnowsky@Sun.COM }
274*12986SJohn.Zolnowsky@Sun.COM
275*12986SJohn.Zolnowsky@Sun.COM if (timescan) {
276*12986SJohn.Zolnowsky@Sun.COM /* append to config options */
277*12986SJohn.Zolnowsky@Sun.COM cp = lut_lookup(Conflut, entry);
278*12986SJohn.Zolnowsky@Sun.COM if (cp == NULL) {
279*12986SJohn.Zolnowsky@Sun.COM /* orphaned entry */
280*12986SJohn.Zolnowsky@Sun.COM if (opts_count(cliopts, "v"))
281*12986SJohn.Zolnowsky@Sun.COM err(EF_FILE, "stale timestamp "
282*12986SJohn.Zolnowsky@Sun.COM "for %s", entry);
283*12986SJohn.Zolnowsky@Sun.COM LOCAL_ERR_BREAK;
284*12986SJohn.Zolnowsky@Sun.COM }
285*12986SJohn.Zolnowsky@Sun.COM opts = cp->cf_opts;
286*12986SJohn.Zolnowsky@Sun.COM }
287*12986SJohn.Zolnowsky@Sun.COM opts = opts_parse(opts, Args, OPTF_CONF);
288*12986SJohn.Zolnowsky@Sun.COM if (!timescan) {
289*12986SJohn.Zolnowsky@Sun.COM fillconflist(lineno, entry, opts, comment, 0);
290*12986SJohn.Zolnowsky@Sun.COM }
291*12986SJohn.Zolnowsky@Sun.COM LOCAL_ERR_END }
292*12986SJohn.Zolnowsky@Sun.COM
293*12986SJohn.Zolnowsky@Sun.COM if (ret == 1 && opts && opts_optarg(opts, "P") != NULL)
294*12986SJohn.Zolnowsky@Sun.COM ret = 3;
295*12986SJohn.Zolnowsky@Sun.COM }
296*12986SJohn.Zolnowsky@Sun.COM
297*12986SJohn.Zolnowsky@Sun.COM err_fileline(NULL, 0);
298*12986SJohn.Zolnowsky@Sun.COM return (ret);
299*12986SJohn.Zolnowsky@Sun.COM }
300*12986SJohn.Zolnowsky@Sun.COM
301*12986SJohn.Zolnowsky@Sun.COM /*
302*12986SJohn.Zolnowsky@Sun.COM * conf_open -- open the configuration file, lock it if we have write perms
303*12986SJohn.Zolnowsky@Sun.COM */
304*12986SJohn.Zolnowsky@Sun.COM int
conf_open(const char * cfname,const char * tfname,struct opts * cliopts)305*12986SJohn.Zolnowsky@Sun.COM conf_open(const char *cfname, const char *tfname, struct opts *cliopts)
306*12986SJohn.Zolnowsky@Sun.COM {
307*12986SJohn.Zolnowsky@Sun.COM struct stat stbuf1, stbuf2, stbuf3;
308*12986SJohn.Zolnowsky@Sun.COM struct flock flock;
309*12986SJohn.Zolnowsky@Sun.COM int ret;
310*12986SJohn.Zolnowsky@Sun.COM
311*12986SJohn.Zolnowsky@Sun.COM Confname = cfname;
312*12986SJohn.Zolnowsky@Sun.COM Timesname = tfname;
313*12986SJohn.Zolnowsky@Sun.COM Confentries = fn_list_new(NULL);
314*12986SJohn.Zolnowsky@Sun.COM Changed = CHG_NONE;
315*12986SJohn.Zolnowsky@Sun.COM
316*12986SJohn.Zolnowsky@Sun.COM Changing = CHG_TIMES;
317*12986SJohn.Zolnowsky@Sun.COM if (opts_count(cliopts, "Vn") != 0)
318*12986SJohn.Zolnowsky@Sun.COM Changing = CHG_NONE;
319*12986SJohn.Zolnowsky@Sun.COM else if (opts_count(cliopts, "rw") != 0)
320*12986SJohn.Zolnowsky@Sun.COM Changing = CHG_BOTH;
321*12986SJohn.Zolnowsky@Sun.COM
322*12986SJohn.Zolnowsky@Sun.COM Singlefile = strcmp(Confname, Timesname) == 0;
323*12986SJohn.Zolnowsky@Sun.COM if (Singlefile && Changing == CHG_TIMES)
324*12986SJohn.Zolnowsky@Sun.COM Changing = CHG_BOTH;
325*12986SJohn.Zolnowsky@Sun.COM
326*12986SJohn.Zolnowsky@Sun.COM /* special case this so we don't even try locking the file */
327*12986SJohn.Zolnowsky@Sun.COM if (strcmp(Confname, "/dev/null") == 0)
328*12986SJohn.Zolnowsky@Sun.COM return (0);
329*12986SJohn.Zolnowsky@Sun.COM
330*12986SJohn.Zolnowsky@Sun.COM while (Conffd == -1) {
331*12986SJohn.Zolnowsky@Sun.COM Canchange = CHG_BOTH;
332*12986SJohn.Zolnowsky@Sun.COM if ((Conffd = open(Confname, O_RDWR)) < 0) {
333*12986SJohn.Zolnowsky@Sun.COM if (Changing == CHG_BOTH)
334*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS, "open %s", Confname);
335*12986SJohn.Zolnowsky@Sun.COM Canchange = CHG_TIMES;
336*12986SJohn.Zolnowsky@Sun.COM if ((Conffd = open(Confname, O_RDONLY)) < 0)
337*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS, "open %s", Confname);
338*12986SJohn.Zolnowsky@Sun.COM }
3390Sstevel@tonic-gate
340*12986SJohn.Zolnowsky@Sun.COM flock.l_type = (Canchange == CHG_BOTH) ? F_WRLCK : F_RDLCK;
341*12986SJohn.Zolnowsky@Sun.COM flock.l_whence = SEEK_SET;
342*12986SJohn.Zolnowsky@Sun.COM flock.l_start = 0;
343*12986SJohn.Zolnowsky@Sun.COM flock.l_len = 1;
344*12986SJohn.Zolnowsky@Sun.COM if (fcntl(Conffd, F_SETLKW, &flock) < 0)
345*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS, "flock on %s", Confname);
346*12986SJohn.Zolnowsky@Sun.COM
347*12986SJohn.Zolnowsky@Sun.COM /* wait until after file is locked to get filesize */
348*12986SJohn.Zolnowsky@Sun.COM if (fstat(Conffd, &stbuf1) < 0)
349*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS, "fstat on %s", Confname);
350*12986SJohn.Zolnowsky@Sun.COM
351*12986SJohn.Zolnowsky@Sun.COM /* verify that we've got a lock on the active file */
352*12986SJohn.Zolnowsky@Sun.COM if (stat(Confname, &stbuf2) < 0 ||
353*12986SJohn.Zolnowsky@Sun.COM !(stbuf2.st_dev == stbuf1.st_dev &&
354*12986SJohn.Zolnowsky@Sun.COM stbuf2.st_ino == stbuf1.st_ino)) {
355*12986SJohn.Zolnowsky@Sun.COM /* wrong config file, try again */
356*12986SJohn.Zolnowsky@Sun.COM (void) close(Conffd);
357*12986SJohn.Zolnowsky@Sun.COM Conffd = -1;
358*12986SJohn.Zolnowsky@Sun.COM }
359*12986SJohn.Zolnowsky@Sun.COM }
360*12986SJohn.Zolnowsky@Sun.COM
361*12986SJohn.Zolnowsky@Sun.COM while (!Singlefile && Timesfd == -1) {
362*12986SJohn.Zolnowsky@Sun.COM if ((Timesfd = open(Timesname, O_CREAT|O_RDWR, 0644)) < 0) {
363*12986SJohn.Zolnowsky@Sun.COM if (Changing != CHG_NONE)
364*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS, "open %s", Timesname);
365*12986SJohn.Zolnowsky@Sun.COM Canchange = CHG_NONE;
366*12986SJohn.Zolnowsky@Sun.COM if ((Timesfd = open(Timesname, O_RDONLY)) < 0)
367*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS, "open %s", Timesname);
3680Sstevel@tonic-gate }
369*12986SJohn.Zolnowsky@Sun.COM
370*12986SJohn.Zolnowsky@Sun.COM flock.l_type = (Canchange != CHG_NONE) ? F_WRLCK : F_RDLCK;
371*12986SJohn.Zolnowsky@Sun.COM flock.l_whence = SEEK_SET;
372*12986SJohn.Zolnowsky@Sun.COM flock.l_start = 0;
373*12986SJohn.Zolnowsky@Sun.COM flock.l_len = 1;
374*12986SJohn.Zolnowsky@Sun.COM if (fcntl(Timesfd, F_SETLKW, &flock) < 0)
375*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS, "flock on %s", Timesname);
376*12986SJohn.Zolnowsky@Sun.COM
377*12986SJohn.Zolnowsky@Sun.COM /* wait until after file is locked to get filesize */
378*12986SJohn.Zolnowsky@Sun.COM if (fstat(Timesfd, &stbuf2) < 0)
379*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS, "fstat on %s", Timesname);
380*12986SJohn.Zolnowsky@Sun.COM
381*12986SJohn.Zolnowsky@Sun.COM /* verify that we've got a lock on the active file */
382*12986SJohn.Zolnowsky@Sun.COM if (stat(Timesname, &stbuf3) < 0 ||
383*12986SJohn.Zolnowsky@Sun.COM !(stbuf2.st_dev == stbuf3.st_dev &&
384*12986SJohn.Zolnowsky@Sun.COM stbuf2.st_ino == stbuf3.st_ino)) {
385*12986SJohn.Zolnowsky@Sun.COM /* wrong timestamp file, try again */
386*12986SJohn.Zolnowsky@Sun.COM (void) close(Timesfd);
387*12986SJohn.Zolnowsky@Sun.COM Timesfd = -1;
388*12986SJohn.Zolnowsky@Sun.COM continue;
389*12986SJohn.Zolnowsky@Sun.COM }
390*12986SJohn.Zolnowsky@Sun.COM
391*12986SJohn.Zolnowsky@Sun.COM /* check that Timesname isn't an alias for Confname */
392*12986SJohn.Zolnowsky@Sun.COM if (stbuf2.st_dev == stbuf1.st_dev &&
393*12986SJohn.Zolnowsky@Sun.COM stbuf2.st_ino == stbuf1.st_ino)
394*12986SJohn.Zolnowsky@Sun.COM err(0, "Timestamp file %s can't refer to "
395*12986SJohn.Zolnowsky@Sun.COM "Configuration file %s", Timesname, Confname);
3960Sstevel@tonic-gate }
397*12986SJohn.Zolnowsky@Sun.COM
398*12986SJohn.Zolnowsky@Sun.COM Conflen = stbuf1.st_size;
399*12986SJohn.Zolnowsky@Sun.COM Timeslen = stbuf2.st_size;
400*12986SJohn.Zolnowsky@Sun.COM
401*12986SJohn.Zolnowsky@Sun.COM if (Conflen == 0)
402*12986SJohn.Zolnowsky@Sun.COM return (1); /* empty file, don't bother parsing it */
403*12986SJohn.Zolnowsky@Sun.COM
404*12986SJohn.Zolnowsky@Sun.COM if ((Confbuf = (char *)mmap(0, Conflen,
405*12986SJohn.Zolnowsky@Sun.COM PROT_READ | PROT_WRITE, MAP_PRIVATE, Conffd, 0)) == (char *)-1)
406*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS, "mmap on %s", Confname);
407*12986SJohn.Zolnowsky@Sun.COM
408*12986SJohn.Zolnowsky@Sun.COM ret = conf_scan(Confname, Confbuf, Conflen, 0, cliopts);
409*12986SJohn.Zolnowsky@Sun.COM if (ret == 3 && !Singlefile && Canchange == CHG_BOTH) {
410*12986SJohn.Zolnowsky@Sun.COM /*
411*12986SJohn.Zolnowsky@Sun.COM * arrange to transfer any timestamps
412*12986SJohn.Zolnowsky@Sun.COM * from conf_file to timestamps_file
413*12986SJohn.Zolnowsky@Sun.COM */
414*12986SJohn.Zolnowsky@Sun.COM Changing = Changed = CHG_BOTH;
415*12986SJohn.Zolnowsky@Sun.COM }
416*12986SJohn.Zolnowsky@Sun.COM
417*12986SJohn.Zolnowsky@Sun.COM if (Timesfd != -1 && Timeslen != 0) {
418*12986SJohn.Zolnowsky@Sun.COM if ((Timesbuf = (char *)mmap(0, Timeslen,
419*12986SJohn.Zolnowsky@Sun.COM PROT_READ | PROT_WRITE, MAP_PRIVATE,
420*12986SJohn.Zolnowsky@Sun.COM Timesfd, 0)) == (char *)-1)
421*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS, "mmap on %s", Timesname);
422*12986SJohn.Zolnowsky@Sun.COM ret &= conf_scan(Timesname, Timesbuf, Timeslen, 1, cliopts);
423*12986SJohn.Zolnowsky@Sun.COM }
424*12986SJohn.Zolnowsky@Sun.COM
4250Sstevel@tonic-gate /*
4260Sstevel@tonic-gate * possible future enhancement: go through and mark any entries:
4270Sstevel@tonic-gate * logfile -P <date>
4280Sstevel@tonic-gate * as DELETED if the logfile doesn't exist
4290Sstevel@tonic-gate */
430*12986SJohn.Zolnowsky@Sun.COM
431*12986SJohn.Zolnowsky@Sun.COM return (ret);
4320Sstevel@tonic-gate }
4330Sstevel@tonic-gate
4340Sstevel@tonic-gate /*
4350Sstevel@tonic-gate * conf_close -- close the configuration file
4360Sstevel@tonic-gate */
4370Sstevel@tonic-gate void
conf_close(struct opts * opts)4380Sstevel@tonic-gate conf_close(struct opts *opts)
4390Sstevel@tonic-gate {
440*12986SJohn.Zolnowsky@Sun.COM char cuname[PATH_MAX], tuname[PATH_MAX];
441*12986SJohn.Zolnowsky@Sun.COM int cfd, tfd;
442*12986SJohn.Zolnowsky@Sun.COM FILE *cfp = NULL, *tfp = NULL;
443*12986SJohn.Zolnowsky@Sun.COM boolean_t safe_update = B_TRUE;
4440Sstevel@tonic-gate
445*12986SJohn.Zolnowsky@Sun.COM if (Changed == CHG_NONE || opts_count(opts, "n") != 0) {
4460Sstevel@tonic-gate if (opts_count(opts, "v"))
447*12986SJohn.Zolnowsky@Sun.COM (void) out("# %s and %s unchanged\n",
448*12986SJohn.Zolnowsky@Sun.COM Confname, Timesname);
449*12986SJohn.Zolnowsky@Sun.COM goto cleanup;
450*12986SJohn.Zolnowsky@Sun.COM }
451*12986SJohn.Zolnowsky@Sun.COM
452*12986SJohn.Zolnowsky@Sun.COM if (Debug > 1) {
453*12986SJohn.Zolnowsky@Sun.COM (void) fprintf(stderr, "conf_close, saving logadm context:\n");
454*12986SJohn.Zolnowsky@Sun.COM conf_print(stderr, NULL);
4550Sstevel@tonic-gate }
4560Sstevel@tonic-gate
457*12986SJohn.Zolnowsky@Sun.COM cuname[0] = tuname[0] = '\0';
458*12986SJohn.Zolnowsky@Sun.COM LOCAL_ERR_BEGIN {
459*12986SJohn.Zolnowsky@Sun.COM if (SETJMP) {
460*12986SJohn.Zolnowsky@Sun.COM safe_update = B_FALSE;
461*12986SJohn.Zolnowsky@Sun.COM LOCAL_ERR_BREAK;
462*12986SJohn.Zolnowsky@Sun.COM }
463*12986SJohn.Zolnowsky@Sun.COM if (Changed == CHG_BOTH) {
464*12986SJohn.Zolnowsky@Sun.COM if (Canchange != CHG_BOTH)
465*12986SJohn.Zolnowsky@Sun.COM err(EF_JMP, "internal error: attempting "
466*12986SJohn.Zolnowsky@Sun.COM "to update %s without locking", Confname);
467*12986SJohn.Zolnowsky@Sun.COM (void) snprintf(cuname, sizeof (cuname), "%sXXXXXX",
468*12986SJohn.Zolnowsky@Sun.COM Confname);
469*12986SJohn.Zolnowsky@Sun.COM if ((cfd = mkstemp(cuname)) == -1)
470*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS|EF_JMP, "open %s replacement",
471*12986SJohn.Zolnowsky@Sun.COM Confname);
472*12986SJohn.Zolnowsky@Sun.COM if (opts_count(opts, "v"))
473*12986SJohn.Zolnowsky@Sun.COM (void) out("# writing changes to %s\n", cuname);
474*12986SJohn.Zolnowsky@Sun.COM if (fchmod(cfd, 0644) == -1)
475*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS|EF_JMP, "chmod %s", cuname);
476*12986SJohn.Zolnowsky@Sun.COM if ((cfp = fdopen(cfd, "w")) == NULL)
477*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS|EF_JMP, "fdopen on %s", cuname);
478*12986SJohn.Zolnowsky@Sun.COM } else {
479*12986SJohn.Zolnowsky@Sun.COM /* just toss away the configuration data */
480*12986SJohn.Zolnowsky@Sun.COM cfp = fopen("/dev/null", "w");
481*12986SJohn.Zolnowsky@Sun.COM }
482*12986SJohn.Zolnowsky@Sun.COM if (!Singlefile) {
483*12986SJohn.Zolnowsky@Sun.COM if (Canchange == CHG_NONE)
484*12986SJohn.Zolnowsky@Sun.COM err(EF_JMP, "internal error: attempting "
485*12986SJohn.Zolnowsky@Sun.COM "to update %s without locking", Timesname);
486*12986SJohn.Zolnowsky@Sun.COM (void) snprintf(tuname, sizeof (tuname), "%sXXXXXX",
487*12986SJohn.Zolnowsky@Sun.COM Timesname);
488*12986SJohn.Zolnowsky@Sun.COM if ((tfd = mkstemp(tuname)) == -1)
489*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS|EF_JMP, "open %s replacement",
490*12986SJohn.Zolnowsky@Sun.COM Timesname);
491*12986SJohn.Zolnowsky@Sun.COM if (opts_count(opts, "v"))
492*12986SJohn.Zolnowsky@Sun.COM (void) out("# writing changes to %s\n", tuname);
493*12986SJohn.Zolnowsky@Sun.COM if (fchmod(tfd, 0644) == -1)
494*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS|EF_JMP, "chmod %s", tuname);
495*12986SJohn.Zolnowsky@Sun.COM if ((tfp = fdopen(tfd, "w")) == NULL)
496*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS|EF_JMP, "fdopen on %s", tuname);
497*12986SJohn.Zolnowsky@Sun.COM }
498*12986SJohn.Zolnowsky@Sun.COM
499*12986SJohn.Zolnowsky@Sun.COM conf_print(cfp, tfp);
500*12986SJohn.Zolnowsky@Sun.COM if (fclose(cfp) < 0)
501*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS|EF_JMP, "fclose on %s", Confname);
502*12986SJohn.Zolnowsky@Sun.COM if (tfp != NULL && fclose(tfp) < 0)
503*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS|EF_JMP, "fclose on %s", Timesname);
504*12986SJohn.Zolnowsky@Sun.COM LOCAL_ERR_END }
505*12986SJohn.Zolnowsky@Sun.COM
506*12986SJohn.Zolnowsky@Sun.COM if (!safe_update) {
507*12986SJohn.Zolnowsky@Sun.COM if (cuname[0] != 0)
508*12986SJohn.Zolnowsky@Sun.COM (void) unlink(cuname);
509*12986SJohn.Zolnowsky@Sun.COM if (tuname[0] != 0)
510*12986SJohn.Zolnowsky@Sun.COM (void) unlink(tuname);
511*12986SJohn.Zolnowsky@Sun.COM err(EF_JMP, "unsafe to update configuration file "
512*12986SJohn.Zolnowsky@Sun.COM "or timestamps");
513*12986SJohn.Zolnowsky@Sun.COM return;
514*12986SJohn.Zolnowsky@Sun.COM }
515*12986SJohn.Zolnowsky@Sun.COM
516*12986SJohn.Zolnowsky@Sun.COM /* rename updated files into place */
517*12986SJohn.Zolnowsky@Sun.COM if (cuname[0] != '\0')
518*12986SJohn.Zolnowsky@Sun.COM if (rename(cuname, Confname) < 0)
519*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS, "rename %s to %s", cuname, Confname);
520*12986SJohn.Zolnowsky@Sun.COM if (tuname[0] != '\0')
521*12986SJohn.Zolnowsky@Sun.COM if (rename(tuname, Timesname) < 0)
522*12986SJohn.Zolnowsky@Sun.COM err(EF_SYS, "rename %s to %s", tuname, Timesname);
523*12986SJohn.Zolnowsky@Sun.COM Changed = CHG_NONE;
524*12986SJohn.Zolnowsky@Sun.COM
525*12986SJohn.Zolnowsky@Sun.COM cleanup:
5260Sstevel@tonic-gate if (Conffd != -1) {
5270Sstevel@tonic-gate (void) close(Conffd);
5280Sstevel@tonic-gate Conffd = -1;
5290Sstevel@tonic-gate }
530*12986SJohn.Zolnowsky@Sun.COM if (Timesfd != -1) {
531*12986SJohn.Zolnowsky@Sun.COM (void) close(Timesfd);
532*12986SJohn.Zolnowsky@Sun.COM Timesfd = -1;
533*12986SJohn.Zolnowsky@Sun.COM }
5340Sstevel@tonic-gate if (Conflut) {
5350Sstevel@tonic-gate lut_free(Conflut, free);
5360Sstevel@tonic-gate Conflut = NULL;
5370Sstevel@tonic-gate }
5380Sstevel@tonic-gate if (Confentries) {
5390Sstevel@tonic-gate fn_list_free(Confentries);
5400Sstevel@tonic-gate Confentries = NULL;
5410Sstevel@tonic-gate }
5420Sstevel@tonic-gate }
5430Sstevel@tonic-gate
5440Sstevel@tonic-gate /*
5450Sstevel@tonic-gate * conf_lookup -- lookup an entry in the config file
5460Sstevel@tonic-gate */
547*12986SJohn.Zolnowsky@Sun.COM void *
conf_lookup(const char * lhs)5480Sstevel@tonic-gate conf_lookup(const char *lhs)
5490Sstevel@tonic-gate {
5500Sstevel@tonic-gate struct confinfo *cp = lut_lookup(Conflut, lhs);
5510Sstevel@tonic-gate
552*12986SJohn.Zolnowsky@Sun.COM if (cp != NULL)
5530Sstevel@tonic-gate err_fileline(Confname, cp->cf_lineno);
554*12986SJohn.Zolnowsky@Sun.COM return (cp);
5550Sstevel@tonic-gate }
5560Sstevel@tonic-gate
5570Sstevel@tonic-gate /*
5580Sstevel@tonic-gate * conf_opts -- return the parsed opts for an entry
5590Sstevel@tonic-gate */
5600Sstevel@tonic-gate struct opts *
conf_opts(const char * lhs)5610Sstevel@tonic-gate conf_opts(const char *lhs)
5620Sstevel@tonic-gate {
5630Sstevel@tonic-gate struct confinfo *cp = lut_lookup(Conflut, lhs);
5640Sstevel@tonic-gate
565*12986SJohn.Zolnowsky@Sun.COM if (cp != NULL)
5660Sstevel@tonic-gate return (cp->cf_opts);
567*12986SJohn.Zolnowsky@Sun.COM return (opts_parse(NULL, NULL, OPTF_CONF));
5680Sstevel@tonic-gate }
5690Sstevel@tonic-gate
5700Sstevel@tonic-gate /*
5710Sstevel@tonic-gate * conf_replace -- replace an entry in the config file
5720Sstevel@tonic-gate */
5730Sstevel@tonic-gate void
conf_replace(const char * lhs,struct opts * newopts)5740Sstevel@tonic-gate conf_replace(const char *lhs, struct opts *newopts)
5750Sstevel@tonic-gate {
5760Sstevel@tonic-gate struct confinfo *cp = lut_lookup(Conflut, lhs);
5770Sstevel@tonic-gate
5780Sstevel@tonic-gate if (Conffd == -1)
5790Sstevel@tonic-gate return;
5800Sstevel@tonic-gate
5812397Sbasabi if (cp != NULL) {
5820Sstevel@tonic-gate cp->cf_opts = newopts;
583*12986SJohn.Zolnowsky@Sun.COM /* cp->cf_args = NULL; */
5840Sstevel@tonic-gate if (newopts == NULL)
5850Sstevel@tonic-gate cp->cf_flags |= CONFF_DELETED;
5860Sstevel@tonic-gate } else
587*12986SJohn.Zolnowsky@Sun.COM fillconflist(0, lhs, newopts, NULL, 0);
588*12986SJohn.Zolnowsky@Sun.COM
589*12986SJohn.Zolnowsky@Sun.COM Changed = CHG_BOTH;
5900Sstevel@tonic-gate }
5910Sstevel@tonic-gate
5920Sstevel@tonic-gate /*
5930Sstevel@tonic-gate * conf_set -- set options for an entry in the config file
5940Sstevel@tonic-gate */
5950Sstevel@tonic-gate void
conf_set(const char * entry,char * o,const char * optarg)5960Sstevel@tonic-gate conf_set(const char *entry, char *o, const char *optarg)
5970Sstevel@tonic-gate {
5980Sstevel@tonic-gate struct confinfo *cp = lut_lookup(Conflut, entry);
5990Sstevel@tonic-gate
6000Sstevel@tonic-gate if (Conffd == -1)
6010Sstevel@tonic-gate return;
6020Sstevel@tonic-gate
6032397Sbasabi if (cp != NULL) {
6040Sstevel@tonic-gate cp->cf_flags &= ~CONFF_DELETED;
6050Sstevel@tonic-gate } else {
606*12986SJohn.Zolnowsky@Sun.COM fillconflist(0, STRDUP(entry),
607*12986SJohn.Zolnowsky@Sun.COM opts_parse(NULL, NULL, OPTF_CONF), NULL, 0);
6080Sstevel@tonic-gate if ((cp = lut_lookup(Conflut, entry)) == NULL)
6090Sstevel@tonic-gate err(0, "conf_set internal error");
6100Sstevel@tonic-gate }
6110Sstevel@tonic-gate (void) opts_set(cp->cf_opts, o, optarg);
612*12986SJohn.Zolnowsky@Sun.COM if (strcmp(o, "P") == 0)
613*12986SJohn.Zolnowsky@Sun.COM Changed |= CHG_TIMES;
614*12986SJohn.Zolnowsky@Sun.COM else
615*12986SJohn.Zolnowsky@Sun.COM Changed = CHG_BOTH;
6160Sstevel@tonic-gate }
6170Sstevel@tonic-gate
6180Sstevel@tonic-gate /*
6190Sstevel@tonic-gate * conf_entries -- list all the entry names
6200Sstevel@tonic-gate */
6210Sstevel@tonic-gate struct fn_list *
conf_entries(void)6220Sstevel@tonic-gate conf_entries(void)
6230Sstevel@tonic-gate {
6240Sstevel@tonic-gate return (Confentries);
6250Sstevel@tonic-gate }
6260Sstevel@tonic-gate
6270Sstevel@tonic-gate /* print the config file */
6280Sstevel@tonic-gate static void
conf_print(FILE * cstream,FILE * tstream)629*12986SJohn.Zolnowsky@Sun.COM conf_print(FILE *cstream, FILE *tstream)
6300Sstevel@tonic-gate {
6310Sstevel@tonic-gate struct confinfo *cp;
632*12986SJohn.Zolnowsky@Sun.COM char *exclude_opts = "PFfhnrvVw";
633*12986SJohn.Zolnowsky@Sun.COM const char *timestamp;
6340Sstevel@tonic-gate
635*12986SJohn.Zolnowsky@Sun.COM if (tstream == NULL) {
636*12986SJohn.Zolnowsky@Sun.COM exclude_opts++; /* -P option goes to config file */
637*12986SJohn.Zolnowsky@Sun.COM } else {
638*12986SJohn.Zolnowsky@Sun.COM (void) fprintf(tstream, gettext(
639*12986SJohn.Zolnowsky@Sun.COM "# This file holds internal data for logadm(1M).\n"
640*12986SJohn.Zolnowsky@Sun.COM "# Do not edit.\n"));
641*12986SJohn.Zolnowsky@Sun.COM }
6420Sstevel@tonic-gate for (cp = Confinfo; cp; cp = cp->cf_next) {
6430Sstevel@tonic-gate if (cp->cf_flags & CONFF_DELETED)
6440Sstevel@tonic-gate continue;
6450Sstevel@tonic-gate if (cp->cf_entry) {
646*12986SJohn.Zolnowsky@Sun.COM opts_printword(cp->cf_entry, cstream);
647*12986SJohn.Zolnowsky@Sun.COM if (cp->cf_opts)
648*12986SJohn.Zolnowsky@Sun.COM opts_print(cp->cf_opts, cstream, exclude_opts);
649*12986SJohn.Zolnowsky@Sun.COM /* output timestamps to tstream */
650*12986SJohn.Zolnowsky@Sun.COM if (tstream != NULL && (timestamp =
651*12986SJohn.Zolnowsky@Sun.COM opts_optarg(cp->cf_opts, "P")) != NULL) {
652*12986SJohn.Zolnowsky@Sun.COM opts_printword(cp->cf_entry, tstream);
653*12986SJohn.Zolnowsky@Sun.COM (void) fprintf(tstream, " -P ");
654*12986SJohn.Zolnowsky@Sun.COM opts_printword(timestamp, tstream);
655*12986SJohn.Zolnowsky@Sun.COM (void) fprintf(tstream, "\n");
6560Sstevel@tonic-gate }
6570Sstevel@tonic-gate }
6580Sstevel@tonic-gate if (cp->cf_com) {
6590Sstevel@tonic-gate if (cp->cf_entry)
660*12986SJohn.Zolnowsky@Sun.COM (void) fprintf(cstream, " ");
661*12986SJohn.Zolnowsky@Sun.COM (void) fprintf(cstream, "#%s", cp->cf_com);
6620Sstevel@tonic-gate }
663*12986SJohn.Zolnowsky@Sun.COM (void) fprintf(cstream, "\n");
6640Sstevel@tonic-gate }
6650Sstevel@tonic-gate }
6660Sstevel@tonic-gate
6670Sstevel@tonic-gate #ifdef TESTMODULE
6680Sstevel@tonic-gate
6690Sstevel@tonic-gate /*
6700Sstevel@tonic-gate * test main for conf module, usage: a.out conffile
6710Sstevel@tonic-gate */
6722397Sbasabi int
main(int argc,char * argv[])6730Sstevel@tonic-gate main(int argc, char *argv[])
6740Sstevel@tonic-gate {
675*12986SJohn.Zolnowsky@Sun.COM struct opts *opts;
676*12986SJohn.Zolnowsky@Sun.COM
6770Sstevel@tonic-gate err_init(argv[0]);
6780Sstevel@tonic-gate setbuf(stdout, NULL);
679*12986SJohn.Zolnowsky@Sun.COM opts_init(Opttable, Opttable_cnt);
680*12986SJohn.Zolnowsky@Sun.COM
681*12986SJohn.Zolnowsky@Sun.COM opts = opts_parse(NULL, NULL, 0);
6820Sstevel@tonic-gate
6830Sstevel@tonic-gate if (argc != 2)
6840Sstevel@tonic-gate err(EF_RAW, "usage: %s conffile\n", argv[0]);
6850Sstevel@tonic-gate
686*12986SJohn.Zolnowsky@Sun.COM conf_open(argv[1], argv[1], opts);
6870Sstevel@tonic-gate
6880Sstevel@tonic-gate printf("conffile <%s>:\n", argv[1]);
689*12986SJohn.Zolnowsky@Sun.COM conf_print(stdout, NULL);
6900Sstevel@tonic-gate
691*12986SJohn.Zolnowsky@Sun.COM conf_close(opts);
6920Sstevel@tonic-gate
6930Sstevel@tonic-gate err_done(0);
6942397Sbasabi /* NOTREACHED */
6952397Sbasabi return (0);
6960Sstevel@tonic-gate }
6970Sstevel@tonic-gate
6980Sstevel@tonic-gate #endif /* TESTMODULE */
699