1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * PPPoE Server-mode daemon log file support.
24  *
25  * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
26  * All rights reserved.
27  */
28 
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <stdarg.h>
32 #include <alloca.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <syslog.h>
37 #include <assert.h>
38 #include <sys/types.h>
39 
40 #include "common.h"
41 #include "logging.h"
42 
43 /* Not all functions are used by all applications.  Let lint know this. */
44 /*LINTLIBRARY*/
45 
46 const char *prog_name = "none";	/* Subsystem name for syslog */
47 int log_level;			/* Higher number for more detail. */
48 
49 static int curlogfd = -1;	/* Current log file */
50 static const char *curfname;	/* Name of current log file */
51 static const char *stderr_name = "stderr";
52 
53 #define	SMALLSTR	254	/* Don't allocate for most strings. */
54 
55 /*
56  * Returns -1 on error (with errno set), 0 on blocked write (file
57  * system full), or N (buffer length) on success.
58  */
59 static int
dowrite(int fd,const void * buf,int len)60 dowrite(int fd, const void *buf, int len)
61 {
62 	int retv;
63 	const uint8_t *bp = (uint8_t *)buf;
64 
65 	while (len > 0) {
66 		retv = write(fd, bp, len);
67 		if (retv == 0) {
68 			break;
69 		}
70 		if (retv == -1) {
71 			if (errno != EINTR)
72 				break;
73 		} else {
74 			bp += retv;
75 			len -= retv;
76 		}
77 	}
78 	if (len <= 0)
79 		return (bp - (uint8_t *)buf);
80 	return (retv);
81 }
82 
83 /* A close that avoids closing stderr */
84 static int
doclose(void)85 doclose(void)
86 {
87 	int	retval = 0;
88 
89 	if (curlogfd == -1)
90 		return (0);
91 	if ((curlogfd != STDERR_FILENO) || (curfname != stderr_name))
92 		retval = close(curlogfd);
93 	curlogfd = -1;
94 	return (retval);
95 }
96 
97 /*
98  * Log levels are 0 for no messages, 1 for errors, 2 for warnings, 3
99  * for informational messages, and 4 for debugging messages.
100  */
101 static void
vlogat(int loglev,const char * fmt,va_list args)102 vlogat(int loglev, const char *fmt, va_list args)
103 {
104 	char timbuf[64];
105 	char regbuf[SMALLSTR+2];
106 	char *ostr;
107 	int timlen;
108 	int slen;
109 	char *nstr;
110 	int err1, err2;
111 	int sloglev;
112 	int retv;
113 	va_list args2;
114 	static int xlate_loglev[] = {
115 		LOG_ERR, LOG_WARNING, LOG_INFO, LOG_DEBUG
116 	};
117 
118 	if (loglev >= log_level)
119 		return;
120 
121 	timbuf[0] = '\0';
122 	timlen = 0;
123 	if (curlogfd >= 0) {
124 		time_t now = time(NULL);
125 
126 		/*
127 		 * Form a time/date string for file (non-syslog) logging.
128 		 * Caution: string broken in two so that SCCS doesn't mangle
129 		 * the %-T-% sequence.
130 		 */
131 		timlen = strftime(timbuf, sizeof (timbuf), "%Y/%m/%d %T"
132 		    "%Z: ", localtime(&now));
133 	}
134 
135 	/* Try formatting once into the small buffer. */
136 	va_copy(args2, args);
137 	slen = vsnprintf(regbuf, SMALLSTR, fmt, args);
138 	if (slen < SMALLSTR) {
139 		ostr = regbuf;
140 	} else {
141 		/*
142 		 * Length returned by vsnprintf doesn't include null,
143 		 * and may also be missing a terminating \n.
144 		 */
145 		ostr = alloca(slen + 2);
146 		slen = vsnprintf(ostr, slen + 1, fmt, args2);
147 	}
148 
149 	/* Don't bother logging empty lines. */
150 	if (slen <= 0)
151 		return;
152 
153 	/* Tack on a \n if needed. */
154 	if (ostr[slen - 1] != '\n') {
155 		ostr[slen++] = '\n';
156 		ostr[slen] = '\0';
157 	}
158 
159 	/* Translate our log levels into syslog standard values */
160 	assert(loglev >= 0 && loglev < Dim(xlate_loglev));
161 	sloglev = xlate_loglev[loglev];
162 
163 	/* Log each line separately */
164 	for (; *ostr != '\0'; ostr = nstr + 1) {
165 		nstr = strchr(ostr, '\n');
166 
167 		/* Ignore zero-length lines. */
168 		if (nstr == ostr)
169 			continue;
170 
171 		slen = nstr - ostr + 1;
172 
173 		/*
174 		 * If we're supposed to be logging to a file, then try
175 		 * that first.  Ditch the file and revert to syslog if
176 		 * any errors occur.
177 		 */
178 		if (curlogfd >= 0) {
179 			if ((retv = dowrite(curlogfd, timbuf, timlen)) > 0)
180 				retv = dowrite(curlogfd, ostr, slen);
181 
182 			/*
183 			 * If we've successfully logged this line,
184 			 * then go do the next one.
185 			 */
186 			if (retv > 0)
187 				continue;
188 
189 			/* Save errno (if any) and close log file */
190 			err1 = errno;
191 			if (doclose() == -1)
192 				err2 = errno;
193 			else
194 				err2 = 0;
195 
196 			/*
197 			 * Recursion is safe here because we cleared
198 			 * out curlogfd above.
199 			 */
200 			if (retv == -1)
201 				logerr("write log %s: %s", curfname,
202 				    mystrerror(err1));
203 			else
204 				logerr("cannot write %s", curfname);
205 			if (err2 == 0)
206 				logdbg("closed log %s", curfname);
207 			else
208 				logerr("closing log %s: %s", curfname,
209 				    mystrerror(err2));
210 		}
211 		syslog(sloglev, "%.*s", slen, ostr);
212 	}
213 }
214 
215 /* Log at debug level */
216 void
logdbg(const char * fmt,...)217 logdbg(const char *fmt, ...)
218 {
219 	va_list args;
220 
221 	va_start(args, fmt);
222 	vlogat(LOGLVL_DBG, fmt, args);
223 	va_end(args);
224 }
225 
226 /* Log informational messages */
227 void
loginfo(const char * fmt,...)228 loginfo(const char *fmt, ...)
229 {
230 	va_list args;
231 
232 	va_start(args, fmt);
233 	vlogat(LOGLVL_INFO, fmt, args);
234 	va_end(args);
235 }
236 
237 /* Log warning messages */
238 void
logwarn(const char * fmt,...)239 logwarn(const char *fmt, ...)
240 {
241 	va_list args;
242 
243 	va_start(args, fmt);
244 	vlogat(LOGLVL_WARN, fmt, args);
245 	va_end(args);
246 }
247 
248 /* Log error messages */
249 void
logerr(const char * fmt,...)250 logerr(const char *fmt, ...)
251 {
252 	va_list args;
253 
254 	va_start(args, fmt);
255 	vlogat(LOGLVL_ERR, fmt, args);
256 	va_end(args);
257 }
258 
259 /* Log a strerror message */
260 void
logstrerror(const char * emsg)261 logstrerror(const char *emsg)
262 {
263 	logerr("%s: %s\n", emsg, mystrerror(errno));
264 }
265 
266 void
log_to_stderr(int dbglvl)267 log_to_stderr(int dbglvl)
268 {
269 	log_level = dbglvl;
270 	if (curlogfd >= 0)
271 		close_log_files();
272 	curlogfd = STDERR_FILENO;
273 	curfname = stderr_name;
274 }
275 
276 /*
277  * Set indicated log file and debug level.
278  */
279 void
log_for_service(const char * fname,int dbglvl)280 log_for_service(const char *fname, int dbglvl)
281 {
282 	int err1, err2;
283 	boolean_t closed;
284 
285 	log_level = dbglvl;
286 	if (fname != NULL &&
287 	    (*fname == '\0' || strcasecmp(fname, "syslog") == 0))
288 		fname = NULL;
289 	if (fname == NULL && curfname == NULL)
290 		return;
291 	err1 = err2 = 0;
292 	closed = B_FALSE;
293 	if (curlogfd >= 0) {
294 		if (fname == curfname ||
295 		    (fname != NULL && strcmp(fname, curfname) == 0)) {
296 			curfname = fname;
297 			return;
298 		}
299 		if (doclose() == -1)
300 			err1 = errno;
301 		closed = B_TRUE;
302 	}
303 	if (fname != NULL) {
304 		curlogfd = open(fname, O_WRONLY|O_APPEND|O_CREAT, 0600);
305 		if (curlogfd == -1)
306 			err2 = errno;
307 	}
308 	if (closed) {
309 		if (err1 == 0)
310 			logdbg("closed log %s", curfname);
311 		else
312 			logerr("closing log %s: %s", curfname,
313 			    mystrerror(err1));
314 	}
315 	if (fname != NULL) {
316 		if (err2 == 0)
317 			logdbg("opened log %s", fname);
318 		else
319 			logerr("opening log %s: %s", fname, mystrerror(err2));
320 	}
321 	curfname = fname;
322 }
323 
324 /*
325  * Close any open log file.  This is used for SIGHUP (to support log
326  * file rotation) and when execing.
327  */
328 void
close_log_files(void)329 close_log_files(void)
330 {
331 	int err = 0;
332 
333 	if (curlogfd >= 0) {
334 		if (doclose() == -1)
335 			err = errno;
336 		if (err == 0)
337 			logdbg("closed log %s", curfname);
338 		else
339 			logerr("closing log %s: %s", curfname,
340 			    mystrerror(err));
341 	}
342 }
343 
344 /*
345  * Reopen syslog connection; in case it was closed.
346  */
347 void
reopen_log(void)348 reopen_log(void)
349 {
350 	openlog(prog_name, LOG_PID | LOG_NDELAY | LOG_NOWAIT, LOG_DAEMON);
351 	/* I control the log level */
352 	(void) setlogmask(LOG_UPTO(LOG_DEBUG));
353 }
354