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