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