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