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 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 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 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 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 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 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 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 263 logstrerror(const char *emsg) 264 { 265 logerr("%s: %s\n", emsg, mystrerror(errno)); 266 } 267 268 void 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 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 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 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