1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
3*0Sstevel@tonic-gate  * Use is subject to license terms.
4*0Sstevel@tonic-gate  */
5*0Sstevel@tonic-gate 
6*0Sstevel@tonic-gate /*
7*0Sstevel@tonic-gate  * Copyright (c) 1996-1999 by Internet Software Consortium.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * Permission to use, copy, modify, and distribute this software for any
10*0Sstevel@tonic-gate  * purpose with or without fee is hereby granted, provided that the above
11*0Sstevel@tonic-gate  * copyright notice and this permission notice appear in all copies.
12*0Sstevel@tonic-gate  *
13*0Sstevel@tonic-gate  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
14*0Sstevel@tonic-gate  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
15*0Sstevel@tonic-gate  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
16*0Sstevel@tonic-gate  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
17*0Sstevel@tonic-gate  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18*0Sstevel@tonic-gate  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19*0Sstevel@tonic-gate  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20*0Sstevel@tonic-gate  * SOFTWARE.
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate 
23*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
24*0Sstevel@tonic-gate 
25*0Sstevel@tonic-gate #if !defined(LINT) && !defined(CODECENTER)
26*0Sstevel@tonic-gate static const char rcsid[] = "$Id: logging.c,v 8.32 2003/01/02 00:35:42 marka Exp $";
27*0Sstevel@tonic-gate #endif /* not lint */
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include "port_before.h"
30*0Sstevel@tonic-gate 
31*0Sstevel@tonic-gate #include <sys/types.h>
32*0Sstevel@tonic-gate #include <sys/time.h>
33*0Sstevel@tonic-gate #include <sys/stat.h>
34*0Sstevel@tonic-gate 
35*0Sstevel@tonic-gate #include <fcntl.h>
36*0Sstevel@tonic-gate #include <limits.h>
37*0Sstevel@tonic-gate #include <stdio.h>
38*0Sstevel@tonic-gate #include <stdlib.h>
39*0Sstevel@tonic-gate #include <string.h>
40*0Sstevel@tonic-gate #include <stdarg.h>
41*0Sstevel@tonic-gate #include <syslog.h>
42*0Sstevel@tonic-gate #include <errno.h>
43*0Sstevel@tonic-gate #include <time.h>
44*0Sstevel@tonic-gate #include <unistd.h>
45*0Sstevel@tonic-gate 
46*0Sstevel@tonic-gate #include <isc/assertions.h>
47*0Sstevel@tonic-gate #include <isc/logging.h>
48*0Sstevel@tonic-gate #include <isc/memcluster.h>
49*0Sstevel@tonic-gate #include <isc/misc.h>
50*0Sstevel@tonic-gate 
51*0Sstevel@tonic-gate #include "port_after.h"
52*0Sstevel@tonic-gate 
53*0Sstevel@tonic-gate #ifdef VSPRINTF_CHAR
54*0Sstevel@tonic-gate # define VSPRINTF(x) strlen(vsprintf/**/x)
55*0Sstevel@tonic-gate #else
56*0Sstevel@tonic-gate # define VSPRINTF(x) ((size_t)vsprintf x)
57*0Sstevel@tonic-gate #endif
58*0Sstevel@tonic-gate 
59*0Sstevel@tonic-gate #include "logging_p.h"
60*0Sstevel@tonic-gate 
61*0Sstevel@tonic-gate static const int syslog_priority[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE,
62*0Sstevel@tonic-gate 				       LOG_WARNING, LOG_ERR, LOG_CRIT };
63*0Sstevel@tonic-gate 
64*0Sstevel@tonic-gate static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
65*0Sstevel@tonic-gate 				"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
66*0Sstevel@tonic-gate 
67*0Sstevel@tonic-gate static const char *level_text[] = {
68*0Sstevel@tonic-gate 	"info: ", "notice: ", "warning: ", "error: ", "critical: "
69*0Sstevel@tonic-gate };
70*0Sstevel@tonic-gate 
71*0Sstevel@tonic-gate static void
72*0Sstevel@tonic-gate version_rename(log_channel chan) {
73*0Sstevel@tonic-gate 	unsigned int ver;
74*0Sstevel@tonic-gate 	char old_name[PATH_MAX+1];
75*0Sstevel@tonic-gate 	char new_name[PATH_MAX+1];
76*0Sstevel@tonic-gate 
77*0Sstevel@tonic-gate 	ver = chan->out.file.versions;
78*0Sstevel@tonic-gate 	if (ver < 1)
79*0Sstevel@tonic-gate 		return;
80*0Sstevel@tonic-gate 	if (ver > LOG_MAX_VERSIONS)
81*0Sstevel@tonic-gate 		ver = LOG_MAX_VERSIONS;
82*0Sstevel@tonic-gate 	/*
83*0Sstevel@tonic-gate 	 * Need to have room for '.nn' (XXX assumes LOG_MAX_VERSIONS < 100)
84*0Sstevel@tonic-gate 	 */
85*0Sstevel@tonic-gate 	if (strlen(chan->out.file.name) > (PATH_MAX-3))
86*0Sstevel@tonic-gate 		return;
87*0Sstevel@tonic-gate 	for (ver--; ver > 0; ver--) {
88*0Sstevel@tonic-gate 		sprintf(old_name, "%s.%d", chan->out.file.name, ver-1);
89*0Sstevel@tonic-gate 		sprintf(new_name, "%s.%d", chan->out.file.name, ver);
90*0Sstevel@tonic-gate 		(void)rename(old_name, new_name);
91*0Sstevel@tonic-gate 	}
92*0Sstevel@tonic-gate 	sprintf(new_name, "%s.0", chan->out.file.name);
93*0Sstevel@tonic-gate 	(void)rename(chan->out.file.name, new_name);
94*0Sstevel@tonic-gate }
95*0Sstevel@tonic-gate 
96*0Sstevel@tonic-gate FILE *
97*0Sstevel@tonic-gate log_open_stream(log_channel chan) {
98*0Sstevel@tonic-gate 	FILE *stream;
99*0Sstevel@tonic-gate 	int fd, flags;
100*0Sstevel@tonic-gate 	struct stat sb;
101*0Sstevel@tonic-gate 	int regular;
102*0Sstevel@tonic-gate 
103*0Sstevel@tonic-gate 	if (chan == NULL || chan->type != log_file) {
104*0Sstevel@tonic-gate 		errno = EINVAL;
105*0Sstevel@tonic-gate 		return (NULL);
106*0Sstevel@tonic-gate 	}
107*0Sstevel@tonic-gate 
108*0Sstevel@tonic-gate 	/*
109*0Sstevel@tonic-gate 	 * Don't open already open streams
110*0Sstevel@tonic-gate 	 */
111*0Sstevel@tonic-gate 	if (chan->out.file.stream != NULL)
112*0Sstevel@tonic-gate 		return (chan->out.file.stream);
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate 	if (stat(chan->out.file.name, &sb) < 0) {
115*0Sstevel@tonic-gate 		if (errno != ENOENT) {
116*0Sstevel@tonic-gate 			syslog(LOG_ERR,
117*0Sstevel@tonic-gate 			       "log_open_stream: stat of %s failed: %s",
118*0Sstevel@tonic-gate 			       chan->out.file.name, strerror(errno));
119*0Sstevel@tonic-gate 			chan->flags |= LOG_CHANNEL_BROKEN;
120*0Sstevel@tonic-gate 			return (NULL);
121*0Sstevel@tonic-gate 		}
122*0Sstevel@tonic-gate 		regular = 1;
123*0Sstevel@tonic-gate 	} else
124*0Sstevel@tonic-gate 		regular = (sb.st_mode & S_IFREG);
125*0Sstevel@tonic-gate 
126*0Sstevel@tonic-gate 	if (chan->out.file.versions) {
127*0Sstevel@tonic-gate 		if (!regular) {
128*0Sstevel@tonic-gate 			syslog(LOG_ERR,
129*0Sstevel@tonic-gate        "log_open_stream: want versions but %s isn't a regular file",
130*0Sstevel@tonic-gate 			       chan->out.file.name);
131*0Sstevel@tonic-gate 			chan->flags |= LOG_CHANNEL_BROKEN;
132*0Sstevel@tonic-gate 			errno = EINVAL;
133*0Sstevel@tonic-gate 			return (NULL);
134*0Sstevel@tonic-gate 		}
135*0Sstevel@tonic-gate 	}
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate 	flags = O_WRONLY|O_CREAT|O_APPEND;
138*0Sstevel@tonic-gate 
139*0Sstevel@tonic-gate 	if ((chan->flags & LOG_TRUNCATE) != 0) {
140*0Sstevel@tonic-gate 		if (regular) {
141*0Sstevel@tonic-gate 			(void)unlink(chan->out.file.name);
142*0Sstevel@tonic-gate 			flags |= O_EXCL;
143*0Sstevel@tonic-gate 		} else {
144*0Sstevel@tonic-gate 			syslog(LOG_ERR,
145*0Sstevel@tonic-gate        "log_open_stream: want truncation but %s isn't a regular file",
146*0Sstevel@tonic-gate 			       chan->out.file.name);
147*0Sstevel@tonic-gate 			chan->flags |= LOG_CHANNEL_BROKEN;
148*0Sstevel@tonic-gate 			errno = EINVAL;
149*0Sstevel@tonic-gate 			return (NULL);
150*0Sstevel@tonic-gate 		}
151*0Sstevel@tonic-gate 	}
152*0Sstevel@tonic-gate 
153*0Sstevel@tonic-gate 	fd = open(chan->out.file.name, flags,
154*0Sstevel@tonic-gate 		  S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
155*0Sstevel@tonic-gate 	if (fd < 0) {
156*0Sstevel@tonic-gate 		syslog(LOG_ERR, "log_open_stream: open(%s) failed: %s",
157*0Sstevel@tonic-gate 		       chan->out.file.name, strerror(errno));
158*0Sstevel@tonic-gate 		chan->flags |= LOG_CHANNEL_BROKEN;
159*0Sstevel@tonic-gate 		return (NULL);
160*0Sstevel@tonic-gate 	}
161*0Sstevel@tonic-gate 	stream = fdopen(fd, "a");
162*0Sstevel@tonic-gate 	if (stream == NULL) {
163*0Sstevel@tonic-gate 		syslog(LOG_ERR, "log_open_stream: fdopen() failed");
164*0Sstevel@tonic-gate 		chan->flags |= LOG_CHANNEL_BROKEN;
165*0Sstevel@tonic-gate 		return (NULL);
166*0Sstevel@tonic-gate 	}
167*0Sstevel@tonic-gate 	(void) fchown(fd, chan->out.file.owner, chan->out.file.group);
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate 	chan->out.file.stream = stream;
170*0Sstevel@tonic-gate 	return (stream);
171*0Sstevel@tonic-gate }
172*0Sstevel@tonic-gate 
173*0Sstevel@tonic-gate int
174*0Sstevel@tonic-gate log_close_stream(log_channel chan) {
175*0Sstevel@tonic-gate 	FILE *stream;
176*0Sstevel@tonic-gate 
177*0Sstevel@tonic-gate 	if (chan == NULL || chan->type != log_file) {
178*0Sstevel@tonic-gate 		errno = EINVAL;
179*0Sstevel@tonic-gate 		return (0);
180*0Sstevel@tonic-gate 	}
181*0Sstevel@tonic-gate 	stream = chan->out.file.stream;
182*0Sstevel@tonic-gate 	chan->out.file.stream = NULL;
183*0Sstevel@tonic-gate 	if (stream != NULL && fclose(stream) == EOF)
184*0Sstevel@tonic-gate 		return (-1);
185*0Sstevel@tonic-gate 	return (0);
186*0Sstevel@tonic-gate }
187*0Sstevel@tonic-gate 
188*0Sstevel@tonic-gate void
189*0Sstevel@tonic-gate log_close_debug_channels(log_context lc) {
190*0Sstevel@tonic-gate 	log_channel_list lcl;
191*0Sstevel@tonic-gate 	int i;
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate 	for (i = 0; i < lc->num_categories; i++)
194*0Sstevel@tonic-gate 		for (lcl = lc->categories[i]; lcl != NULL; lcl = lcl->next)
195*0Sstevel@tonic-gate 			if (lcl->channel->type == log_file &&
196*0Sstevel@tonic-gate 			    lcl->channel->out.file.stream != NULL &&
197*0Sstevel@tonic-gate 			    lcl->channel->flags & LOG_REQUIRE_DEBUG)
198*0Sstevel@tonic-gate 				(void)log_close_stream(lcl->channel);
199*0Sstevel@tonic-gate }
200*0Sstevel@tonic-gate 
201*0Sstevel@tonic-gate FILE *
202*0Sstevel@tonic-gate log_get_stream(log_channel chan) {
203*0Sstevel@tonic-gate 	if (chan == NULL || chan->type != log_file) {
204*0Sstevel@tonic-gate 		errno = EINVAL;
205*0Sstevel@tonic-gate 		return (NULL);
206*0Sstevel@tonic-gate 	}
207*0Sstevel@tonic-gate 	return (chan->out.file.stream);
208*0Sstevel@tonic-gate }
209*0Sstevel@tonic-gate 
210*0Sstevel@tonic-gate char *
211*0Sstevel@tonic-gate log_get_filename(log_channel chan) {
212*0Sstevel@tonic-gate 	if (chan == NULL || chan->type != log_file) {
213*0Sstevel@tonic-gate 		errno = EINVAL;
214*0Sstevel@tonic-gate 		return (NULL);
215*0Sstevel@tonic-gate 	}
216*0Sstevel@tonic-gate 	return (chan->out.file.name);
217*0Sstevel@tonic-gate }
218*0Sstevel@tonic-gate 
219*0Sstevel@tonic-gate int
220*0Sstevel@tonic-gate log_check_channel(log_context lc, int level, log_channel chan) {
221*0Sstevel@tonic-gate 	int debugging, chan_level;
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate 	REQUIRE(lc != NULL);
224*0Sstevel@tonic-gate 
225*0Sstevel@tonic-gate 	debugging = ((lc->flags & LOG_OPTION_DEBUG) != 0);
226*0Sstevel@tonic-gate 
227*0Sstevel@tonic-gate 	/*
228*0Sstevel@tonic-gate 	 * If not debugging, short circuit debugging messages very early.
229*0Sstevel@tonic-gate 	 */
230*0Sstevel@tonic-gate 	if (level > 0 && !debugging)
231*0Sstevel@tonic-gate 		return (0);
232*0Sstevel@tonic-gate 
233*0Sstevel@tonic-gate 	if ((chan->flags & (LOG_CHANNEL_BROKEN|LOG_CHANNEL_OFF)) != 0)
234*0Sstevel@tonic-gate 		return (0);
235*0Sstevel@tonic-gate 
236*0Sstevel@tonic-gate 	/* Some channels only log when debugging is on. */
237*0Sstevel@tonic-gate 	if ((chan->flags & LOG_REQUIRE_DEBUG) && !debugging)
238*0Sstevel@tonic-gate 		return (0);
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate 	/* Some channels use the global level. */
241*0Sstevel@tonic-gate 	if ((chan->flags & LOG_USE_CONTEXT_LEVEL) != 0) {
242*0Sstevel@tonic-gate 		chan_level = lc->level;
243*0Sstevel@tonic-gate 	} else
244*0Sstevel@tonic-gate 		chan_level = chan->level;
245*0Sstevel@tonic-gate 
246*0Sstevel@tonic-gate 	if (level > chan_level)
247*0Sstevel@tonic-gate 		return (0);
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate 	return (1);
250*0Sstevel@tonic-gate }
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate int
253*0Sstevel@tonic-gate log_check(log_context lc, int category, int level) {
254*0Sstevel@tonic-gate 	log_channel_list lcl;
255*0Sstevel@tonic-gate 	int debugging;
256*0Sstevel@tonic-gate 
257*0Sstevel@tonic-gate 	REQUIRE(lc != NULL);
258*0Sstevel@tonic-gate 
259*0Sstevel@tonic-gate 	debugging = ((lc->flags & LOG_OPTION_DEBUG) != 0);
260*0Sstevel@tonic-gate 
261*0Sstevel@tonic-gate 	/*
262*0Sstevel@tonic-gate 	 * If not debugging, short circuit debugging messages very early.
263*0Sstevel@tonic-gate 	 */
264*0Sstevel@tonic-gate 	if (level > 0 && !debugging)
265*0Sstevel@tonic-gate 		return (0);
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate 	if (category < 0 || category > lc->num_categories)
268*0Sstevel@tonic-gate 		category = 0;		/* use default */
269*0Sstevel@tonic-gate 	lcl = lc->categories[category];
270*0Sstevel@tonic-gate 	if (lcl == NULL) {
271*0Sstevel@tonic-gate 		category = 0;
272*0Sstevel@tonic-gate 		lcl = lc->categories[0];
273*0Sstevel@tonic-gate 	}
274*0Sstevel@tonic-gate 
275*0Sstevel@tonic-gate 	for ( /* nothing */; lcl != NULL; lcl = lcl->next) {
276*0Sstevel@tonic-gate 		if (log_check_channel(lc, level, lcl->channel))
277*0Sstevel@tonic-gate 			return (1);
278*0Sstevel@tonic-gate 	}
279*0Sstevel@tonic-gate 	return (0);
280*0Sstevel@tonic-gate }
281*0Sstevel@tonic-gate 
282*0Sstevel@tonic-gate void
283*0Sstevel@tonic-gate log_vwrite(log_context lc, int category, int level, const char *format,
284*0Sstevel@tonic-gate 	   va_list args) {
285*0Sstevel@tonic-gate 	log_channel_list lcl;
286*0Sstevel@tonic-gate 	int pri, debugging, did_vsprintf = 0;
287*0Sstevel@tonic-gate 	int original_category;
288*0Sstevel@tonic-gate 	FILE *stream;
289*0Sstevel@tonic-gate 	log_channel chan;
290*0Sstevel@tonic-gate 	struct timeval tv;
291*0Sstevel@tonic-gate 	struct tm *local_tm;
292*0Sstevel@tonic-gate #ifdef HAVE_TIME_R
293*0Sstevel@tonic-gate 	struct tm tm_tmp;
294*0Sstevel@tonic-gate #endif
295*0Sstevel@tonic-gate 	time_t tt;
296*0Sstevel@tonic-gate 	const char *category_name;
297*0Sstevel@tonic-gate 	const char *level_str;
298*0Sstevel@tonic-gate 	char time_buf[256];
299*0Sstevel@tonic-gate 	char level_buf[256];
300*0Sstevel@tonic-gate 
301*0Sstevel@tonic-gate 	REQUIRE(lc != NULL);
302*0Sstevel@tonic-gate 
303*0Sstevel@tonic-gate 	debugging = (lc->flags & LOG_OPTION_DEBUG);
304*0Sstevel@tonic-gate 
305*0Sstevel@tonic-gate 	/*
306*0Sstevel@tonic-gate 	 * If not debugging, short circuit debugging messages very early.
307*0Sstevel@tonic-gate 	 */
308*0Sstevel@tonic-gate 	if (level > 0 && !debugging)
309*0Sstevel@tonic-gate 		return;
310*0Sstevel@tonic-gate 
311*0Sstevel@tonic-gate 	if (category < 0 || category > lc->num_categories)
312*0Sstevel@tonic-gate 		category = 0;		/* use default */
313*0Sstevel@tonic-gate 	original_category = category;
314*0Sstevel@tonic-gate 	lcl = lc->categories[category];
315*0Sstevel@tonic-gate 	if (lcl == NULL) {
316*0Sstevel@tonic-gate 		category = 0;
317*0Sstevel@tonic-gate 		lcl = lc->categories[0];
318*0Sstevel@tonic-gate 	}
319*0Sstevel@tonic-gate 
320*0Sstevel@tonic-gate 	/*
321*0Sstevel@tonic-gate 	 * Get the current time and format it.
322*0Sstevel@tonic-gate 	 */
323*0Sstevel@tonic-gate 	time_buf[0]='\0';
324*0Sstevel@tonic-gate 	if (gettimeofday(&tv, NULL) < 0) {
325*0Sstevel@tonic-gate 		syslog(LOG_INFO, "gettimeofday failed in log_vwrite()");
326*0Sstevel@tonic-gate 	} else {
327*0Sstevel@tonic-gate 		tt = tv.tv_sec;
328*0Sstevel@tonic-gate #ifdef HAVE_TIME_R
329*0Sstevel@tonic-gate 		local_tm = localtime_r(&tt, &tm_tmp);
330*0Sstevel@tonic-gate #else
331*0Sstevel@tonic-gate 		local_tm = localtime(&tt);
332*0Sstevel@tonic-gate #endif
333*0Sstevel@tonic-gate 		if (local_tm != NULL) {
334*0Sstevel@tonic-gate 			sprintf(time_buf, "%02d-%s-%4d %02d:%02d:%02d.%03ld ",
335*0Sstevel@tonic-gate 				local_tm->tm_mday, months[local_tm->tm_mon],
336*0Sstevel@tonic-gate 				local_tm->tm_year+1900, local_tm->tm_hour,
337*0Sstevel@tonic-gate 				local_tm->tm_min, local_tm->tm_sec,
338*0Sstevel@tonic-gate 				(long)tv.tv_usec/1000);
339*0Sstevel@tonic-gate 		}
340*0Sstevel@tonic-gate 	}
341*0Sstevel@tonic-gate 
342*0Sstevel@tonic-gate 	/*
343*0Sstevel@tonic-gate 	 * Make a string representation of the current category and level
344*0Sstevel@tonic-gate 	 */
345*0Sstevel@tonic-gate 
346*0Sstevel@tonic-gate 	if (lc->category_names != NULL &&
347*0Sstevel@tonic-gate 	    lc->category_names[original_category] != NULL)
348*0Sstevel@tonic-gate 		category_name = lc->category_names[original_category];
349*0Sstevel@tonic-gate 	else
350*0Sstevel@tonic-gate 		category_name = "";
351*0Sstevel@tonic-gate 
352*0Sstevel@tonic-gate 	if (level >= log_critical) {
353*0Sstevel@tonic-gate 		if (level >= 0) {
354*0Sstevel@tonic-gate 			sprintf(level_buf, "debug %d: ", level);
355*0Sstevel@tonic-gate 			level_str = level_buf;
356*0Sstevel@tonic-gate 		} else
357*0Sstevel@tonic-gate 			level_str = level_text[-level-1];
358*0Sstevel@tonic-gate 	} else {
359*0Sstevel@tonic-gate 		sprintf(level_buf, "level %d: ", level);
360*0Sstevel@tonic-gate 		level_str = level_buf;
361*0Sstevel@tonic-gate 	}
362*0Sstevel@tonic-gate 
363*0Sstevel@tonic-gate 	/*
364*0Sstevel@tonic-gate 	 * Write the message to channels.
365*0Sstevel@tonic-gate 	 */
366*0Sstevel@tonic-gate 	for ( /* nothing */; lcl != NULL; lcl = lcl->next) {
367*0Sstevel@tonic-gate 		chan = lcl->channel;
368*0Sstevel@tonic-gate 
369*0Sstevel@tonic-gate 		if (!log_check_channel(lc, level, chan))
370*0Sstevel@tonic-gate 			continue;
371*0Sstevel@tonic-gate 
372*0Sstevel@tonic-gate 		if (!did_vsprintf) {
373*0Sstevel@tonic-gate 			if (VSPRINTF((lc->buffer, format, args)) >
374*0Sstevel@tonic-gate 			    LOG_BUFFER_SIZE) {
375*0Sstevel@tonic-gate 				syslog(LOG_CRIT,
376*0Sstevel@tonic-gate 				       "memory overrun in log_vwrite()");
377*0Sstevel@tonic-gate 				exit(1);
378*0Sstevel@tonic-gate 			}
379*0Sstevel@tonic-gate 			did_vsprintf = 1;
380*0Sstevel@tonic-gate 		}
381*0Sstevel@tonic-gate 
382*0Sstevel@tonic-gate 		switch (chan->type) {
383*0Sstevel@tonic-gate 		case log_syslog:
384*0Sstevel@tonic-gate 			if (level >= log_critical)
385*0Sstevel@tonic-gate 				pri = (level >= 0) ? 0 : -level;
386*0Sstevel@tonic-gate 			else
387*0Sstevel@tonic-gate 				pri = -log_critical;
388*0Sstevel@tonic-gate 			syslog(chan->out.facility|syslog_priority[pri],
389*0Sstevel@tonic-gate 			       "%s%s%s%s",
390*0Sstevel@tonic-gate 			       (chan->flags & LOG_TIMESTAMP) ?	time_buf : "",
391*0Sstevel@tonic-gate 			       (chan->flags & LOG_PRINT_CATEGORY) ?
392*0Sstevel@tonic-gate 			       category_name : "",
393*0Sstevel@tonic-gate 			       (chan->flags & LOG_PRINT_LEVEL) ?
394*0Sstevel@tonic-gate 			       level_str : "",
395*0Sstevel@tonic-gate 			       lc->buffer);
396*0Sstevel@tonic-gate 			break;
397*0Sstevel@tonic-gate 		case log_file:
398*0Sstevel@tonic-gate 			stream = chan->out.file.stream;
399*0Sstevel@tonic-gate 			if (stream == NULL) {
400*0Sstevel@tonic-gate 				stream = log_open_stream(chan);
401*0Sstevel@tonic-gate 				if (stream == NULL)
402*0Sstevel@tonic-gate 					break;
403*0Sstevel@tonic-gate 			}
404*0Sstevel@tonic-gate 			if (chan->out.file.max_size != ULONG_MAX) {
405*0Sstevel@tonic-gate 				long pos;
406*0Sstevel@tonic-gate 
407*0Sstevel@tonic-gate 				pos = ftell(stream);
408*0Sstevel@tonic-gate 				if (pos >= 0 &&
409*0Sstevel@tonic-gate 				    (unsigned long)pos >
410*0Sstevel@tonic-gate 				    chan->out.file.max_size) {
411*0Sstevel@tonic-gate 					/*
412*0Sstevel@tonic-gate 					 * try to roll over the log files,
413*0Sstevel@tonic-gate 					 * ignoring all all return codes
414*0Sstevel@tonic-gate 					 * except the open (we don't want
415*0Sstevel@tonic-gate 					 * to write any more anyway)
416*0Sstevel@tonic-gate 					 */
417*0Sstevel@tonic-gate 					log_close_stream(chan);
418*0Sstevel@tonic-gate 					version_rename(chan);
419*0Sstevel@tonic-gate 					stream = log_open_stream(chan);
420*0Sstevel@tonic-gate 					if (stream == NULL)
421*0Sstevel@tonic-gate 						break;
422*0Sstevel@tonic-gate 				}
423*0Sstevel@tonic-gate 			}
424*0Sstevel@tonic-gate 			fprintf(stream, "%s%s%s%s\n",
425*0Sstevel@tonic-gate 				(chan->flags & LOG_TIMESTAMP) ?	time_buf : "",
426*0Sstevel@tonic-gate 				(chan->flags & LOG_PRINT_CATEGORY) ?
427*0Sstevel@tonic-gate 				category_name : "",
428*0Sstevel@tonic-gate 				(chan->flags & LOG_PRINT_LEVEL) ?
429*0Sstevel@tonic-gate 				level_str : "",
430*0Sstevel@tonic-gate 				lc->buffer);
431*0Sstevel@tonic-gate 			fflush(stream);
432*0Sstevel@tonic-gate 			break;
433*0Sstevel@tonic-gate 		case log_null:
434*0Sstevel@tonic-gate 			break;
435*0Sstevel@tonic-gate 		default:
436*0Sstevel@tonic-gate 			syslog(LOG_ERR,
437*0Sstevel@tonic-gate 			       "unknown channel type in log_vwrite()");
438*0Sstevel@tonic-gate 		}
439*0Sstevel@tonic-gate 	}
440*0Sstevel@tonic-gate }
441*0Sstevel@tonic-gate 
442*0Sstevel@tonic-gate void
443*0Sstevel@tonic-gate log_write(log_context lc, int category, int level, const char *format, ...) {
444*0Sstevel@tonic-gate 	va_list args;
445*0Sstevel@tonic-gate 
446*0Sstevel@tonic-gate 	va_start(args, format);
447*0Sstevel@tonic-gate 	log_vwrite(lc, category, level, format, args);
448*0Sstevel@tonic-gate 	va_end(args);
449*0Sstevel@tonic-gate }
450*0Sstevel@tonic-gate 
451*0Sstevel@tonic-gate /*
452*0Sstevel@tonic-gate  * Functions to create, set, or destroy contexts
453*0Sstevel@tonic-gate  */
454*0Sstevel@tonic-gate 
455*0Sstevel@tonic-gate int
456*0Sstevel@tonic-gate log_new_context(int num_categories, char **category_names, log_context *lc) {
457*0Sstevel@tonic-gate 	log_context nlc;
458*0Sstevel@tonic-gate 
459*0Sstevel@tonic-gate 	nlc = memget(sizeof (struct log_context));
460*0Sstevel@tonic-gate 	if (nlc == NULL) {
461*0Sstevel@tonic-gate 		errno = ENOMEM;
462*0Sstevel@tonic-gate 		return (-1);
463*0Sstevel@tonic-gate 	}
464*0Sstevel@tonic-gate 	nlc->num_categories = num_categories;
465*0Sstevel@tonic-gate 	nlc->category_names = category_names;
466*0Sstevel@tonic-gate 	nlc->categories = memget(num_categories * sizeof (log_channel_list));
467*0Sstevel@tonic-gate 	if (nlc->categories == NULL) {
468*0Sstevel@tonic-gate 		memput(nlc, sizeof (struct log_context));
469*0Sstevel@tonic-gate 		errno = ENOMEM;
470*0Sstevel@tonic-gate 		return (-1);
471*0Sstevel@tonic-gate 	}
472*0Sstevel@tonic-gate 	memset(nlc->categories, '\0',
473*0Sstevel@tonic-gate 	       num_categories * sizeof (log_channel_list));
474*0Sstevel@tonic-gate 	nlc->flags = 0U;
475*0Sstevel@tonic-gate 	nlc->level = 0;
476*0Sstevel@tonic-gate 	*lc = nlc;
477*0Sstevel@tonic-gate 	return (0);
478*0Sstevel@tonic-gate }
479*0Sstevel@tonic-gate 
480*0Sstevel@tonic-gate void
481*0Sstevel@tonic-gate log_free_context(log_context lc) {
482*0Sstevel@tonic-gate 	log_channel_list lcl, lcl_next;
483*0Sstevel@tonic-gate 	log_channel chan;
484*0Sstevel@tonic-gate 	int i;
485*0Sstevel@tonic-gate 
486*0Sstevel@tonic-gate 	REQUIRE(lc != NULL);
487*0Sstevel@tonic-gate 
488*0Sstevel@tonic-gate 	for (i = 0; i < lc->num_categories; i++)
489*0Sstevel@tonic-gate 		for (lcl = lc->categories[i]; lcl != NULL; lcl = lcl_next) {
490*0Sstevel@tonic-gate 			lcl_next = lcl->next;
491*0Sstevel@tonic-gate 			chan = lcl->channel;
492*0Sstevel@tonic-gate 			(void)log_free_channel(chan);
493*0Sstevel@tonic-gate 			memput(lcl, sizeof (struct log_channel_list));
494*0Sstevel@tonic-gate 		}
495*0Sstevel@tonic-gate 	memput(lc->categories,
496*0Sstevel@tonic-gate 	       lc->num_categories * sizeof (log_channel_list));
497*0Sstevel@tonic-gate 	memput(lc, sizeof (struct log_context));
498*0Sstevel@tonic-gate }
499*0Sstevel@tonic-gate 
500*0Sstevel@tonic-gate int
501*0Sstevel@tonic-gate log_add_channel(log_context lc, int category, log_channel chan) {
502*0Sstevel@tonic-gate 	log_channel_list lcl;
503*0Sstevel@tonic-gate 
504*0Sstevel@tonic-gate 	if (lc == NULL || category < 0 || category >= lc->num_categories) {
505*0Sstevel@tonic-gate 		errno = EINVAL;
506*0Sstevel@tonic-gate 		return (-1);
507*0Sstevel@tonic-gate 	}
508*0Sstevel@tonic-gate 
509*0Sstevel@tonic-gate 	lcl = memget(sizeof (struct log_channel_list));
510*0Sstevel@tonic-gate 	if (lcl == NULL) {
511*0Sstevel@tonic-gate 		errno = ENOMEM;
512*0Sstevel@tonic-gate 		return(-1);
513*0Sstevel@tonic-gate 	}
514*0Sstevel@tonic-gate 	lcl->channel = chan;
515*0Sstevel@tonic-gate 	lcl->next = lc->categories[category];
516*0Sstevel@tonic-gate 	lc->categories[category] = lcl;
517*0Sstevel@tonic-gate 	chan->references++;
518*0Sstevel@tonic-gate 	return (0);
519*0Sstevel@tonic-gate }
520*0Sstevel@tonic-gate 
521*0Sstevel@tonic-gate int
522*0Sstevel@tonic-gate log_remove_channel(log_context lc, int category, log_channel chan) {
523*0Sstevel@tonic-gate 	log_channel_list lcl, prev_lcl, next_lcl;
524*0Sstevel@tonic-gate 	int found = 0;
525*0Sstevel@tonic-gate 
526*0Sstevel@tonic-gate 	if (lc == NULL || category < 0 || category >= lc->num_categories) {
527*0Sstevel@tonic-gate 		errno = EINVAL;
528*0Sstevel@tonic-gate 		return (-1);
529*0Sstevel@tonic-gate 	}
530*0Sstevel@tonic-gate 
531*0Sstevel@tonic-gate 	for (prev_lcl = NULL, lcl = lc->categories[category];
532*0Sstevel@tonic-gate 	     lcl != NULL;
533*0Sstevel@tonic-gate 	     lcl = next_lcl) {
534*0Sstevel@tonic-gate 		next_lcl = lcl->next;
535*0Sstevel@tonic-gate 		if (lcl->channel == chan) {
536*0Sstevel@tonic-gate 			log_free_channel(chan);
537*0Sstevel@tonic-gate 			if (prev_lcl != NULL)
538*0Sstevel@tonic-gate 				prev_lcl->next = next_lcl;
539*0Sstevel@tonic-gate 			else
540*0Sstevel@tonic-gate 				lc->categories[category] = next_lcl;
541*0Sstevel@tonic-gate 			memput(lcl, sizeof (struct log_channel_list));
542*0Sstevel@tonic-gate 			/*
543*0Sstevel@tonic-gate 			 * We just set found instead of returning because
544*0Sstevel@tonic-gate 			 * the channel might be on the list more than once.
545*0Sstevel@tonic-gate 			 */
546*0Sstevel@tonic-gate 			found = 1;
547*0Sstevel@tonic-gate 		} else
548*0Sstevel@tonic-gate 			prev_lcl = lcl;
549*0Sstevel@tonic-gate 	}
550*0Sstevel@tonic-gate 	if (!found) {
551*0Sstevel@tonic-gate 		errno = ENOENT;
552*0Sstevel@tonic-gate 		return (-1);
553*0Sstevel@tonic-gate 	}
554*0Sstevel@tonic-gate 	return (0);
555*0Sstevel@tonic-gate }
556*0Sstevel@tonic-gate 
557*0Sstevel@tonic-gate int
558*0Sstevel@tonic-gate log_option(log_context lc, int option, int value) {
559*0Sstevel@tonic-gate 	if (lc == NULL) {
560*0Sstevel@tonic-gate 		errno = EINVAL;
561*0Sstevel@tonic-gate 		return (-1);
562*0Sstevel@tonic-gate 	}
563*0Sstevel@tonic-gate 	switch (option) {
564*0Sstevel@tonic-gate 	case LOG_OPTION_DEBUG:
565*0Sstevel@tonic-gate 		if (value)
566*0Sstevel@tonic-gate 			lc->flags |= option;
567*0Sstevel@tonic-gate 		else
568*0Sstevel@tonic-gate 			lc->flags &= ~option;
569*0Sstevel@tonic-gate 		break;
570*0Sstevel@tonic-gate 	case LOG_OPTION_LEVEL:
571*0Sstevel@tonic-gate 		lc->level = value;
572*0Sstevel@tonic-gate 		break;
573*0Sstevel@tonic-gate 	default:
574*0Sstevel@tonic-gate 		errno = EINVAL;
575*0Sstevel@tonic-gate 		return (-1);
576*0Sstevel@tonic-gate 	}
577*0Sstevel@tonic-gate 	return (0);
578*0Sstevel@tonic-gate }
579*0Sstevel@tonic-gate 
580*0Sstevel@tonic-gate int
581*0Sstevel@tonic-gate log_category_is_active(log_context lc, int category) {
582*0Sstevel@tonic-gate 	if (lc == NULL) {
583*0Sstevel@tonic-gate 		errno = EINVAL;
584*0Sstevel@tonic-gate 		return (-1);
585*0Sstevel@tonic-gate 	}
586*0Sstevel@tonic-gate 	if (category >= 0 && category < lc->num_categories &&
587*0Sstevel@tonic-gate 	    lc->categories[category] != NULL)
588*0Sstevel@tonic-gate 		return (1);
589*0Sstevel@tonic-gate 	return (0);
590*0Sstevel@tonic-gate }
591*0Sstevel@tonic-gate 
592*0Sstevel@tonic-gate log_channel
593*0Sstevel@tonic-gate log_new_syslog_channel(unsigned int flags, int level, int facility) {
594*0Sstevel@tonic-gate 	log_channel chan;
595*0Sstevel@tonic-gate 
596*0Sstevel@tonic-gate 	chan = memget(sizeof (struct log_channel));
597*0Sstevel@tonic-gate 	if (chan == NULL) {
598*0Sstevel@tonic-gate 		errno = ENOMEM;
599*0Sstevel@tonic-gate 		return (NULL);
600*0Sstevel@tonic-gate 	}
601*0Sstevel@tonic-gate 	chan->type = log_syslog;
602*0Sstevel@tonic-gate 	chan->flags = flags;
603*0Sstevel@tonic-gate 	chan->level = level;
604*0Sstevel@tonic-gate 	chan->out.facility = facility;
605*0Sstevel@tonic-gate 	chan->references = 0;
606*0Sstevel@tonic-gate 	return (chan);
607*0Sstevel@tonic-gate }
608*0Sstevel@tonic-gate 
609*0Sstevel@tonic-gate log_channel
610*0Sstevel@tonic-gate log_new_file_channel(unsigned int flags, int level,
611*0Sstevel@tonic-gate 		     const char *name, FILE *stream, unsigned int versions,
612*0Sstevel@tonic-gate 		     unsigned long max_size) {
613*0Sstevel@tonic-gate 	log_channel chan;
614*0Sstevel@tonic-gate 
615*0Sstevel@tonic-gate 	chan = memget(sizeof (struct log_channel));
616*0Sstevel@tonic-gate 	if (chan == NULL) {
617*0Sstevel@tonic-gate 		errno = ENOMEM;
618*0Sstevel@tonic-gate 		return (NULL);
619*0Sstevel@tonic-gate 	}
620*0Sstevel@tonic-gate 	chan->type = log_file;
621*0Sstevel@tonic-gate 	chan->flags = flags;
622*0Sstevel@tonic-gate 	chan->level = level;
623*0Sstevel@tonic-gate 	if (name != NULL) {
624*0Sstevel@tonic-gate 		size_t len;
625*0Sstevel@tonic-gate 
626*0Sstevel@tonic-gate 		len = strlen(name);
627*0Sstevel@tonic-gate 		/*
628*0Sstevel@tonic-gate 		 * Quantize length to a multiple of 256.  There's space for the
629*0Sstevel@tonic-gate 		 * NUL, since if len is a multiple of 256, the size chosen will
630*0Sstevel@tonic-gate 		 * be the next multiple.
631*0Sstevel@tonic-gate 		 */
632*0Sstevel@tonic-gate 		chan->out.file.name_size = ((len / 256) + 1) * 256;
633*0Sstevel@tonic-gate 		chan->out.file.name = memget(chan->out.file.name_size);
634*0Sstevel@tonic-gate 		if (chan->out.file.name == NULL) {
635*0Sstevel@tonic-gate 			memput(chan, sizeof (struct log_channel));
636*0Sstevel@tonic-gate 			errno = ENOMEM;
637*0Sstevel@tonic-gate 			return (NULL);
638*0Sstevel@tonic-gate 		}
639*0Sstevel@tonic-gate 		/* This is safe. */
640*0Sstevel@tonic-gate 		strcpy(chan->out.file.name, name);
641*0Sstevel@tonic-gate 	} else {
642*0Sstevel@tonic-gate 		chan->out.file.name_size = 0;
643*0Sstevel@tonic-gate 		chan->out.file.name = NULL;
644*0Sstevel@tonic-gate 	}
645*0Sstevel@tonic-gate 	chan->out.file.stream = stream;
646*0Sstevel@tonic-gate 	chan->out.file.versions = versions;
647*0Sstevel@tonic-gate 	chan->out.file.max_size = max_size;
648*0Sstevel@tonic-gate 	chan->out.file.owner = getuid();
649*0Sstevel@tonic-gate 	chan->out.file.group = getgid();
650*0Sstevel@tonic-gate 	chan->references = 0;
651*0Sstevel@tonic-gate 	return (chan);
652*0Sstevel@tonic-gate }
653*0Sstevel@tonic-gate 
654*0Sstevel@tonic-gate int
655*0Sstevel@tonic-gate log_set_file_owner(log_channel chan, uid_t owner, gid_t group) {
656*0Sstevel@tonic-gate 	if (chan->type != log_file) {
657*0Sstevel@tonic-gate 		errno = EBADF;
658*0Sstevel@tonic-gate 		return (-1);
659*0Sstevel@tonic-gate 	}
660*0Sstevel@tonic-gate 	chan->out.file.owner = owner;
661*0Sstevel@tonic-gate 	chan->out.file.group = group;
662*0Sstevel@tonic-gate 	return (0);
663*0Sstevel@tonic-gate }
664*0Sstevel@tonic-gate 
665*0Sstevel@tonic-gate log_channel
666*0Sstevel@tonic-gate log_new_null_channel() {
667*0Sstevel@tonic-gate 	log_channel chan;
668*0Sstevel@tonic-gate 
669*0Sstevel@tonic-gate 	chan = memget(sizeof (struct log_channel));
670*0Sstevel@tonic-gate 	if (chan == NULL) {
671*0Sstevel@tonic-gate 		errno = ENOMEM;
672*0Sstevel@tonic-gate 		return (NULL);
673*0Sstevel@tonic-gate 	}
674*0Sstevel@tonic-gate 	chan->type = log_null;
675*0Sstevel@tonic-gate 	chan->flags = LOG_CHANNEL_OFF;
676*0Sstevel@tonic-gate 	chan->level = log_info;
677*0Sstevel@tonic-gate 	chan->references = 0;
678*0Sstevel@tonic-gate 	return (chan);
679*0Sstevel@tonic-gate }
680*0Sstevel@tonic-gate 
681*0Sstevel@tonic-gate int
682*0Sstevel@tonic-gate log_inc_references(log_channel chan) {
683*0Sstevel@tonic-gate 	if (chan == NULL) {
684*0Sstevel@tonic-gate 		errno = EINVAL;
685*0Sstevel@tonic-gate 		return (-1);
686*0Sstevel@tonic-gate 	}
687*0Sstevel@tonic-gate 	chan->references++;
688*0Sstevel@tonic-gate 	return (0);
689*0Sstevel@tonic-gate }
690*0Sstevel@tonic-gate 
691*0Sstevel@tonic-gate int
692*0Sstevel@tonic-gate log_dec_references(log_channel chan) {
693*0Sstevel@tonic-gate 	if (chan == NULL || chan->references <= 0) {
694*0Sstevel@tonic-gate 		errno = EINVAL;
695*0Sstevel@tonic-gate 		return (-1);
696*0Sstevel@tonic-gate 	}
697*0Sstevel@tonic-gate 	chan->references--;
698*0Sstevel@tonic-gate 	return (0);
699*0Sstevel@tonic-gate }
700*0Sstevel@tonic-gate 
701*0Sstevel@tonic-gate log_channel_type
702*0Sstevel@tonic-gate log_get_channel_type(log_channel chan) {
703*0Sstevel@tonic-gate 	REQUIRE(chan != NULL);
704*0Sstevel@tonic-gate 
705*0Sstevel@tonic-gate 	return (chan->type);
706*0Sstevel@tonic-gate }
707*0Sstevel@tonic-gate 
708*0Sstevel@tonic-gate int
709*0Sstevel@tonic-gate log_free_channel(log_channel chan) {
710*0Sstevel@tonic-gate 	if (chan == NULL || chan->references <= 0) {
711*0Sstevel@tonic-gate 		errno = EINVAL;
712*0Sstevel@tonic-gate 		return (-1);
713*0Sstevel@tonic-gate 	}
714*0Sstevel@tonic-gate 	chan->references--;
715*0Sstevel@tonic-gate 	if (chan->references == 0) {
716*0Sstevel@tonic-gate 		if (chan->type == log_file) {
717*0Sstevel@tonic-gate 			if ((chan->flags & LOG_CLOSE_STREAM) &&
718*0Sstevel@tonic-gate 			    chan->out.file.stream != NULL)
719*0Sstevel@tonic-gate 				(void)fclose(chan->out.file.stream);
720*0Sstevel@tonic-gate 			if (chan->out.file.name != NULL)
721*0Sstevel@tonic-gate 				memput(chan->out.file.name,
722*0Sstevel@tonic-gate 				       chan->out.file.name_size);
723*0Sstevel@tonic-gate 		}
724*0Sstevel@tonic-gate 		memput(chan, sizeof (struct log_channel));
725*0Sstevel@tonic-gate 	}
726*0Sstevel@tonic-gate 	return (0);
727*0Sstevel@tonic-gate }
728