1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 3*0Sstevel@tonic-gate * Use is subject to license terms. 4*0Sstevel@tonic-gate */ 5*0Sstevel@tonic-gate 6*0Sstevel@tonic-gate /* 7*0Sstevel@tonic-gate * Copyright (c) 1996-1999 by Internet Software Consortium. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * Permission to use, copy, modify, and distribute this software for any 10*0Sstevel@tonic-gate * purpose with or without fee is hereby granted, provided that the above 11*0Sstevel@tonic-gate * copyright notice and this permission notice appear in all copies. 12*0Sstevel@tonic-gate * 13*0Sstevel@tonic-gate * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 14*0Sstevel@tonic-gate * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 15*0Sstevel@tonic-gate * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 16*0Sstevel@tonic-gate * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 17*0Sstevel@tonic-gate * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 18*0Sstevel@tonic-gate * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 19*0Sstevel@tonic-gate * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20*0Sstevel@tonic-gate * SOFTWARE. 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate 23*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 24*0Sstevel@tonic-gate 25*0Sstevel@tonic-gate #if !defined(LINT) && !defined(CODECENTER) 26*0Sstevel@tonic-gate static const char rcsid[] = "$Id: logging.c,v 8.32 2003/01/02 00:35:42 marka Exp $"; 27*0Sstevel@tonic-gate #endif /* not lint */ 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include "port_before.h" 30*0Sstevel@tonic-gate 31*0Sstevel@tonic-gate #include <sys/types.h> 32*0Sstevel@tonic-gate #include <sys/time.h> 33*0Sstevel@tonic-gate #include <sys/stat.h> 34*0Sstevel@tonic-gate 35*0Sstevel@tonic-gate #include <fcntl.h> 36*0Sstevel@tonic-gate #include <limits.h> 37*0Sstevel@tonic-gate #include <stdio.h> 38*0Sstevel@tonic-gate #include <stdlib.h> 39*0Sstevel@tonic-gate #include <string.h> 40*0Sstevel@tonic-gate #include <stdarg.h> 41*0Sstevel@tonic-gate #include <syslog.h> 42*0Sstevel@tonic-gate #include <errno.h> 43*0Sstevel@tonic-gate #include <time.h> 44*0Sstevel@tonic-gate #include <unistd.h> 45*0Sstevel@tonic-gate 46*0Sstevel@tonic-gate #include <isc/assertions.h> 47*0Sstevel@tonic-gate #include <isc/logging.h> 48*0Sstevel@tonic-gate #include <isc/memcluster.h> 49*0Sstevel@tonic-gate #include <isc/misc.h> 50*0Sstevel@tonic-gate 51*0Sstevel@tonic-gate #include "port_after.h" 52*0Sstevel@tonic-gate 53*0Sstevel@tonic-gate #ifdef VSPRINTF_CHAR 54*0Sstevel@tonic-gate # define VSPRINTF(x) strlen(vsprintf/**/x) 55*0Sstevel@tonic-gate #else 56*0Sstevel@tonic-gate # define VSPRINTF(x) ((size_t)vsprintf x) 57*0Sstevel@tonic-gate #endif 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gate #include "logging_p.h" 60*0Sstevel@tonic-gate 61*0Sstevel@tonic-gate static const int syslog_priority[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, 62*0Sstevel@tonic-gate LOG_WARNING, LOG_ERR, LOG_CRIT }; 63*0Sstevel@tonic-gate 64*0Sstevel@tonic-gate static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", 65*0Sstevel@tonic-gate "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; 66*0Sstevel@tonic-gate 67*0Sstevel@tonic-gate static const char *level_text[] = { 68*0Sstevel@tonic-gate "info: ", "notice: ", "warning: ", "error: ", "critical: " 69*0Sstevel@tonic-gate }; 70*0Sstevel@tonic-gate 71*0Sstevel@tonic-gate static void 72*0Sstevel@tonic-gate version_rename(log_channel chan) { 73*0Sstevel@tonic-gate unsigned int ver; 74*0Sstevel@tonic-gate char old_name[PATH_MAX+1]; 75*0Sstevel@tonic-gate char new_name[PATH_MAX+1]; 76*0Sstevel@tonic-gate 77*0Sstevel@tonic-gate ver = chan->out.file.versions; 78*0Sstevel@tonic-gate if (ver < 1) 79*0Sstevel@tonic-gate return; 80*0Sstevel@tonic-gate if (ver > LOG_MAX_VERSIONS) 81*0Sstevel@tonic-gate ver = LOG_MAX_VERSIONS; 82*0Sstevel@tonic-gate /* 83*0Sstevel@tonic-gate * Need to have room for '.nn' (XXX assumes LOG_MAX_VERSIONS < 100) 84*0Sstevel@tonic-gate */ 85*0Sstevel@tonic-gate if (strlen(chan->out.file.name) > (PATH_MAX-3)) 86*0Sstevel@tonic-gate return; 87*0Sstevel@tonic-gate for (ver--; ver > 0; ver--) { 88*0Sstevel@tonic-gate sprintf(old_name, "%s.%d", chan->out.file.name, ver-1); 89*0Sstevel@tonic-gate sprintf(new_name, "%s.%d", chan->out.file.name, ver); 90*0Sstevel@tonic-gate (void)rename(old_name, new_name); 91*0Sstevel@tonic-gate } 92*0Sstevel@tonic-gate sprintf(new_name, "%s.0", chan->out.file.name); 93*0Sstevel@tonic-gate (void)rename(chan->out.file.name, new_name); 94*0Sstevel@tonic-gate } 95*0Sstevel@tonic-gate 96*0Sstevel@tonic-gate FILE * 97*0Sstevel@tonic-gate log_open_stream(log_channel chan) { 98*0Sstevel@tonic-gate FILE *stream; 99*0Sstevel@tonic-gate int fd, flags; 100*0Sstevel@tonic-gate struct stat sb; 101*0Sstevel@tonic-gate int regular; 102*0Sstevel@tonic-gate 103*0Sstevel@tonic-gate if (chan == NULL || chan->type != log_file) { 104*0Sstevel@tonic-gate errno = EINVAL; 105*0Sstevel@tonic-gate return (NULL); 106*0Sstevel@tonic-gate } 107*0Sstevel@tonic-gate 108*0Sstevel@tonic-gate /* 109*0Sstevel@tonic-gate * Don't open already open streams 110*0Sstevel@tonic-gate */ 111*0Sstevel@tonic-gate if (chan->out.file.stream != NULL) 112*0Sstevel@tonic-gate return (chan->out.file.stream); 113*0Sstevel@tonic-gate 114*0Sstevel@tonic-gate if (stat(chan->out.file.name, &sb) < 0) { 115*0Sstevel@tonic-gate if (errno != ENOENT) { 116*0Sstevel@tonic-gate syslog(LOG_ERR, 117*0Sstevel@tonic-gate "log_open_stream: stat of %s failed: %s", 118*0Sstevel@tonic-gate chan->out.file.name, strerror(errno)); 119*0Sstevel@tonic-gate chan->flags |= LOG_CHANNEL_BROKEN; 120*0Sstevel@tonic-gate return (NULL); 121*0Sstevel@tonic-gate } 122*0Sstevel@tonic-gate regular = 1; 123*0Sstevel@tonic-gate } else 124*0Sstevel@tonic-gate regular = (sb.st_mode & S_IFREG); 125*0Sstevel@tonic-gate 126*0Sstevel@tonic-gate if (chan->out.file.versions) { 127*0Sstevel@tonic-gate if (!regular) { 128*0Sstevel@tonic-gate syslog(LOG_ERR, 129*0Sstevel@tonic-gate "log_open_stream: want versions but %s isn't a regular file", 130*0Sstevel@tonic-gate chan->out.file.name); 131*0Sstevel@tonic-gate chan->flags |= LOG_CHANNEL_BROKEN; 132*0Sstevel@tonic-gate errno = EINVAL; 133*0Sstevel@tonic-gate return (NULL); 134*0Sstevel@tonic-gate } 135*0Sstevel@tonic-gate } 136*0Sstevel@tonic-gate 137*0Sstevel@tonic-gate flags = O_WRONLY|O_CREAT|O_APPEND; 138*0Sstevel@tonic-gate 139*0Sstevel@tonic-gate if ((chan->flags & LOG_TRUNCATE) != 0) { 140*0Sstevel@tonic-gate if (regular) { 141*0Sstevel@tonic-gate (void)unlink(chan->out.file.name); 142*0Sstevel@tonic-gate flags |= O_EXCL; 143*0Sstevel@tonic-gate } else { 144*0Sstevel@tonic-gate syslog(LOG_ERR, 145*0Sstevel@tonic-gate "log_open_stream: want truncation but %s isn't a regular file", 146*0Sstevel@tonic-gate chan->out.file.name); 147*0Sstevel@tonic-gate chan->flags |= LOG_CHANNEL_BROKEN; 148*0Sstevel@tonic-gate errno = EINVAL; 149*0Sstevel@tonic-gate return (NULL); 150*0Sstevel@tonic-gate } 151*0Sstevel@tonic-gate } 152*0Sstevel@tonic-gate 153*0Sstevel@tonic-gate fd = open(chan->out.file.name, flags, 154*0Sstevel@tonic-gate S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); 155*0Sstevel@tonic-gate if (fd < 0) { 156*0Sstevel@tonic-gate syslog(LOG_ERR, "log_open_stream: open(%s) failed: %s", 157*0Sstevel@tonic-gate chan->out.file.name, strerror(errno)); 158*0Sstevel@tonic-gate chan->flags |= LOG_CHANNEL_BROKEN; 159*0Sstevel@tonic-gate return (NULL); 160*0Sstevel@tonic-gate } 161*0Sstevel@tonic-gate stream = fdopen(fd, "a"); 162*0Sstevel@tonic-gate if (stream == NULL) { 163*0Sstevel@tonic-gate syslog(LOG_ERR, "log_open_stream: fdopen() failed"); 164*0Sstevel@tonic-gate chan->flags |= LOG_CHANNEL_BROKEN; 165*0Sstevel@tonic-gate return (NULL); 166*0Sstevel@tonic-gate } 167*0Sstevel@tonic-gate (void) fchown(fd, chan->out.file.owner, chan->out.file.group); 168*0Sstevel@tonic-gate 169*0Sstevel@tonic-gate chan->out.file.stream = stream; 170*0Sstevel@tonic-gate return (stream); 171*0Sstevel@tonic-gate } 172*0Sstevel@tonic-gate 173*0Sstevel@tonic-gate int 174*0Sstevel@tonic-gate log_close_stream(log_channel chan) { 175*0Sstevel@tonic-gate FILE *stream; 176*0Sstevel@tonic-gate 177*0Sstevel@tonic-gate if (chan == NULL || chan->type != log_file) { 178*0Sstevel@tonic-gate errno = EINVAL; 179*0Sstevel@tonic-gate return (0); 180*0Sstevel@tonic-gate } 181*0Sstevel@tonic-gate stream = chan->out.file.stream; 182*0Sstevel@tonic-gate chan->out.file.stream = NULL; 183*0Sstevel@tonic-gate if (stream != NULL && fclose(stream) == EOF) 184*0Sstevel@tonic-gate return (-1); 185*0Sstevel@tonic-gate return (0); 186*0Sstevel@tonic-gate } 187*0Sstevel@tonic-gate 188*0Sstevel@tonic-gate void 189*0Sstevel@tonic-gate log_close_debug_channels(log_context lc) { 190*0Sstevel@tonic-gate log_channel_list lcl; 191*0Sstevel@tonic-gate int i; 192*0Sstevel@tonic-gate 193*0Sstevel@tonic-gate for (i = 0; i < lc->num_categories; i++) 194*0Sstevel@tonic-gate for (lcl = lc->categories[i]; lcl != NULL; lcl = lcl->next) 195*0Sstevel@tonic-gate if (lcl->channel->type == log_file && 196*0Sstevel@tonic-gate lcl->channel->out.file.stream != NULL && 197*0Sstevel@tonic-gate lcl->channel->flags & LOG_REQUIRE_DEBUG) 198*0Sstevel@tonic-gate (void)log_close_stream(lcl->channel); 199*0Sstevel@tonic-gate } 200*0Sstevel@tonic-gate 201*0Sstevel@tonic-gate FILE * 202*0Sstevel@tonic-gate log_get_stream(log_channel chan) { 203*0Sstevel@tonic-gate if (chan == NULL || chan->type != log_file) { 204*0Sstevel@tonic-gate errno = EINVAL; 205*0Sstevel@tonic-gate return (NULL); 206*0Sstevel@tonic-gate } 207*0Sstevel@tonic-gate return (chan->out.file.stream); 208*0Sstevel@tonic-gate } 209*0Sstevel@tonic-gate 210*0Sstevel@tonic-gate char * 211*0Sstevel@tonic-gate log_get_filename(log_channel chan) { 212*0Sstevel@tonic-gate if (chan == NULL || chan->type != log_file) { 213*0Sstevel@tonic-gate errno = EINVAL; 214*0Sstevel@tonic-gate return (NULL); 215*0Sstevel@tonic-gate } 216*0Sstevel@tonic-gate return (chan->out.file.name); 217*0Sstevel@tonic-gate } 218*0Sstevel@tonic-gate 219*0Sstevel@tonic-gate int 220*0Sstevel@tonic-gate log_check_channel(log_context lc, int level, log_channel chan) { 221*0Sstevel@tonic-gate int debugging, chan_level; 222*0Sstevel@tonic-gate 223*0Sstevel@tonic-gate REQUIRE(lc != NULL); 224*0Sstevel@tonic-gate 225*0Sstevel@tonic-gate debugging = ((lc->flags & LOG_OPTION_DEBUG) != 0); 226*0Sstevel@tonic-gate 227*0Sstevel@tonic-gate /* 228*0Sstevel@tonic-gate * If not debugging, short circuit debugging messages very early. 229*0Sstevel@tonic-gate */ 230*0Sstevel@tonic-gate if (level > 0 && !debugging) 231*0Sstevel@tonic-gate return (0); 232*0Sstevel@tonic-gate 233*0Sstevel@tonic-gate if ((chan->flags & (LOG_CHANNEL_BROKEN|LOG_CHANNEL_OFF)) != 0) 234*0Sstevel@tonic-gate return (0); 235*0Sstevel@tonic-gate 236*0Sstevel@tonic-gate /* Some channels only log when debugging is on. */ 237*0Sstevel@tonic-gate if ((chan->flags & LOG_REQUIRE_DEBUG) && !debugging) 238*0Sstevel@tonic-gate return (0); 239*0Sstevel@tonic-gate 240*0Sstevel@tonic-gate /* Some channels use the global level. */ 241*0Sstevel@tonic-gate if ((chan->flags & LOG_USE_CONTEXT_LEVEL) != 0) { 242*0Sstevel@tonic-gate chan_level = lc->level; 243*0Sstevel@tonic-gate } else 244*0Sstevel@tonic-gate chan_level = chan->level; 245*0Sstevel@tonic-gate 246*0Sstevel@tonic-gate if (level > chan_level) 247*0Sstevel@tonic-gate return (0); 248*0Sstevel@tonic-gate 249*0Sstevel@tonic-gate return (1); 250*0Sstevel@tonic-gate } 251*0Sstevel@tonic-gate 252*0Sstevel@tonic-gate int 253*0Sstevel@tonic-gate log_check(log_context lc, int category, int level) { 254*0Sstevel@tonic-gate log_channel_list lcl; 255*0Sstevel@tonic-gate int debugging; 256*0Sstevel@tonic-gate 257*0Sstevel@tonic-gate REQUIRE(lc != NULL); 258*0Sstevel@tonic-gate 259*0Sstevel@tonic-gate debugging = ((lc->flags & LOG_OPTION_DEBUG) != 0); 260*0Sstevel@tonic-gate 261*0Sstevel@tonic-gate /* 262*0Sstevel@tonic-gate * If not debugging, short circuit debugging messages very early. 263*0Sstevel@tonic-gate */ 264*0Sstevel@tonic-gate if (level > 0 && !debugging) 265*0Sstevel@tonic-gate return (0); 266*0Sstevel@tonic-gate 267*0Sstevel@tonic-gate if (category < 0 || category > lc->num_categories) 268*0Sstevel@tonic-gate category = 0; /* use default */ 269*0Sstevel@tonic-gate lcl = lc->categories[category]; 270*0Sstevel@tonic-gate if (lcl == NULL) { 271*0Sstevel@tonic-gate category = 0; 272*0Sstevel@tonic-gate lcl = lc->categories[0]; 273*0Sstevel@tonic-gate } 274*0Sstevel@tonic-gate 275*0Sstevel@tonic-gate for ( /* nothing */; lcl != NULL; lcl = lcl->next) { 276*0Sstevel@tonic-gate if (log_check_channel(lc, level, lcl->channel)) 277*0Sstevel@tonic-gate return (1); 278*0Sstevel@tonic-gate } 279*0Sstevel@tonic-gate return (0); 280*0Sstevel@tonic-gate } 281*0Sstevel@tonic-gate 282*0Sstevel@tonic-gate void 283*0Sstevel@tonic-gate log_vwrite(log_context lc, int category, int level, const char *format, 284*0Sstevel@tonic-gate va_list args) { 285*0Sstevel@tonic-gate log_channel_list lcl; 286*0Sstevel@tonic-gate int pri, debugging, did_vsprintf = 0; 287*0Sstevel@tonic-gate int original_category; 288*0Sstevel@tonic-gate FILE *stream; 289*0Sstevel@tonic-gate log_channel chan; 290*0Sstevel@tonic-gate struct timeval tv; 291*0Sstevel@tonic-gate struct tm *local_tm; 292*0Sstevel@tonic-gate #ifdef HAVE_TIME_R 293*0Sstevel@tonic-gate struct tm tm_tmp; 294*0Sstevel@tonic-gate #endif 295*0Sstevel@tonic-gate time_t tt; 296*0Sstevel@tonic-gate const char *category_name; 297*0Sstevel@tonic-gate const char *level_str; 298*0Sstevel@tonic-gate char time_buf[256]; 299*0Sstevel@tonic-gate char level_buf[256]; 300*0Sstevel@tonic-gate 301*0Sstevel@tonic-gate REQUIRE(lc != NULL); 302*0Sstevel@tonic-gate 303*0Sstevel@tonic-gate debugging = (lc->flags & LOG_OPTION_DEBUG); 304*0Sstevel@tonic-gate 305*0Sstevel@tonic-gate /* 306*0Sstevel@tonic-gate * If not debugging, short circuit debugging messages very early. 307*0Sstevel@tonic-gate */ 308*0Sstevel@tonic-gate if (level > 0 && !debugging) 309*0Sstevel@tonic-gate return; 310*0Sstevel@tonic-gate 311*0Sstevel@tonic-gate if (category < 0 || category > lc->num_categories) 312*0Sstevel@tonic-gate category = 0; /* use default */ 313*0Sstevel@tonic-gate original_category = category; 314*0Sstevel@tonic-gate lcl = lc->categories[category]; 315*0Sstevel@tonic-gate if (lcl == NULL) { 316*0Sstevel@tonic-gate category = 0; 317*0Sstevel@tonic-gate lcl = lc->categories[0]; 318*0Sstevel@tonic-gate } 319*0Sstevel@tonic-gate 320*0Sstevel@tonic-gate /* 321*0Sstevel@tonic-gate * Get the current time and format it. 322*0Sstevel@tonic-gate */ 323*0Sstevel@tonic-gate time_buf[0]='\0'; 324*0Sstevel@tonic-gate if (gettimeofday(&tv, NULL) < 0) { 325*0Sstevel@tonic-gate syslog(LOG_INFO, "gettimeofday failed in log_vwrite()"); 326*0Sstevel@tonic-gate } else { 327*0Sstevel@tonic-gate tt = tv.tv_sec; 328*0Sstevel@tonic-gate #ifdef HAVE_TIME_R 329*0Sstevel@tonic-gate local_tm = localtime_r(&tt, &tm_tmp); 330*0Sstevel@tonic-gate #else 331*0Sstevel@tonic-gate local_tm = localtime(&tt); 332*0Sstevel@tonic-gate #endif 333*0Sstevel@tonic-gate if (local_tm != NULL) { 334*0Sstevel@tonic-gate sprintf(time_buf, "%02d-%s-%4d %02d:%02d:%02d.%03ld ", 335*0Sstevel@tonic-gate local_tm->tm_mday, months[local_tm->tm_mon], 336*0Sstevel@tonic-gate local_tm->tm_year+1900, local_tm->tm_hour, 337*0Sstevel@tonic-gate local_tm->tm_min, local_tm->tm_sec, 338*0Sstevel@tonic-gate (long)tv.tv_usec/1000); 339*0Sstevel@tonic-gate } 340*0Sstevel@tonic-gate } 341*0Sstevel@tonic-gate 342*0Sstevel@tonic-gate /* 343*0Sstevel@tonic-gate * Make a string representation of the current category and level 344*0Sstevel@tonic-gate */ 345*0Sstevel@tonic-gate 346*0Sstevel@tonic-gate if (lc->category_names != NULL && 347*0Sstevel@tonic-gate lc->category_names[original_category] != NULL) 348*0Sstevel@tonic-gate category_name = lc->category_names[original_category]; 349*0Sstevel@tonic-gate else 350*0Sstevel@tonic-gate category_name = ""; 351*0Sstevel@tonic-gate 352*0Sstevel@tonic-gate if (level >= log_critical) { 353*0Sstevel@tonic-gate if (level >= 0) { 354*0Sstevel@tonic-gate sprintf(level_buf, "debug %d: ", level); 355*0Sstevel@tonic-gate level_str = level_buf; 356*0Sstevel@tonic-gate } else 357*0Sstevel@tonic-gate level_str = level_text[-level-1]; 358*0Sstevel@tonic-gate } else { 359*0Sstevel@tonic-gate sprintf(level_buf, "level %d: ", level); 360*0Sstevel@tonic-gate level_str = level_buf; 361*0Sstevel@tonic-gate } 362*0Sstevel@tonic-gate 363*0Sstevel@tonic-gate /* 364*0Sstevel@tonic-gate * Write the message to channels. 365*0Sstevel@tonic-gate */ 366*0Sstevel@tonic-gate for ( /* nothing */; lcl != NULL; lcl = lcl->next) { 367*0Sstevel@tonic-gate chan = lcl->channel; 368*0Sstevel@tonic-gate 369*0Sstevel@tonic-gate if (!log_check_channel(lc, level, chan)) 370*0Sstevel@tonic-gate continue; 371*0Sstevel@tonic-gate 372*0Sstevel@tonic-gate if (!did_vsprintf) { 373*0Sstevel@tonic-gate if (VSPRINTF((lc->buffer, format, args)) > 374*0Sstevel@tonic-gate LOG_BUFFER_SIZE) { 375*0Sstevel@tonic-gate syslog(LOG_CRIT, 376*0Sstevel@tonic-gate "memory overrun in log_vwrite()"); 377*0Sstevel@tonic-gate exit(1); 378*0Sstevel@tonic-gate } 379*0Sstevel@tonic-gate did_vsprintf = 1; 380*0Sstevel@tonic-gate } 381*0Sstevel@tonic-gate 382*0Sstevel@tonic-gate switch (chan->type) { 383*0Sstevel@tonic-gate case log_syslog: 384*0Sstevel@tonic-gate if (level >= log_critical) 385*0Sstevel@tonic-gate pri = (level >= 0) ? 0 : -level; 386*0Sstevel@tonic-gate else 387*0Sstevel@tonic-gate pri = -log_critical; 388*0Sstevel@tonic-gate syslog(chan->out.facility|syslog_priority[pri], 389*0Sstevel@tonic-gate "%s%s%s%s", 390*0Sstevel@tonic-gate (chan->flags & LOG_TIMESTAMP) ? time_buf : "", 391*0Sstevel@tonic-gate (chan->flags & LOG_PRINT_CATEGORY) ? 392*0Sstevel@tonic-gate category_name : "", 393*0Sstevel@tonic-gate (chan->flags & LOG_PRINT_LEVEL) ? 394*0Sstevel@tonic-gate level_str : "", 395*0Sstevel@tonic-gate lc->buffer); 396*0Sstevel@tonic-gate break; 397*0Sstevel@tonic-gate case log_file: 398*0Sstevel@tonic-gate stream = chan->out.file.stream; 399*0Sstevel@tonic-gate if (stream == NULL) { 400*0Sstevel@tonic-gate stream = log_open_stream(chan); 401*0Sstevel@tonic-gate if (stream == NULL) 402*0Sstevel@tonic-gate break; 403*0Sstevel@tonic-gate } 404*0Sstevel@tonic-gate if (chan->out.file.max_size != ULONG_MAX) { 405*0Sstevel@tonic-gate long pos; 406*0Sstevel@tonic-gate 407*0Sstevel@tonic-gate pos = ftell(stream); 408*0Sstevel@tonic-gate if (pos >= 0 && 409*0Sstevel@tonic-gate (unsigned long)pos > 410*0Sstevel@tonic-gate chan->out.file.max_size) { 411*0Sstevel@tonic-gate /* 412*0Sstevel@tonic-gate * try to roll over the log files, 413*0Sstevel@tonic-gate * ignoring all all return codes 414*0Sstevel@tonic-gate * except the open (we don't want 415*0Sstevel@tonic-gate * to write any more anyway) 416*0Sstevel@tonic-gate */ 417*0Sstevel@tonic-gate log_close_stream(chan); 418*0Sstevel@tonic-gate version_rename(chan); 419*0Sstevel@tonic-gate stream = log_open_stream(chan); 420*0Sstevel@tonic-gate if (stream == NULL) 421*0Sstevel@tonic-gate break; 422*0Sstevel@tonic-gate } 423*0Sstevel@tonic-gate } 424*0Sstevel@tonic-gate fprintf(stream, "%s%s%s%s\n", 425*0Sstevel@tonic-gate (chan->flags & LOG_TIMESTAMP) ? time_buf : "", 426*0Sstevel@tonic-gate (chan->flags & LOG_PRINT_CATEGORY) ? 427*0Sstevel@tonic-gate category_name : "", 428*0Sstevel@tonic-gate (chan->flags & LOG_PRINT_LEVEL) ? 429*0Sstevel@tonic-gate level_str : "", 430*0Sstevel@tonic-gate lc->buffer); 431*0Sstevel@tonic-gate fflush(stream); 432*0Sstevel@tonic-gate break; 433*0Sstevel@tonic-gate case log_null: 434*0Sstevel@tonic-gate break; 435*0Sstevel@tonic-gate default: 436*0Sstevel@tonic-gate syslog(LOG_ERR, 437*0Sstevel@tonic-gate "unknown channel type in log_vwrite()"); 438*0Sstevel@tonic-gate } 439*0Sstevel@tonic-gate } 440*0Sstevel@tonic-gate } 441*0Sstevel@tonic-gate 442*0Sstevel@tonic-gate void 443*0Sstevel@tonic-gate log_write(log_context lc, int category, int level, const char *format, ...) { 444*0Sstevel@tonic-gate va_list args; 445*0Sstevel@tonic-gate 446*0Sstevel@tonic-gate va_start(args, format); 447*0Sstevel@tonic-gate log_vwrite(lc, category, level, format, args); 448*0Sstevel@tonic-gate va_end(args); 449*0Sstevel@tonic-gate } 450*0Sstevel@tonic-gate 451*0Sstevel@tonic-gate /* 452*0Sstevel@tonic-gate * Functions to create, set, or destroy contexts 453*0Sstevel@tonic-gate */ 454*0Sstevel@tonic-gate 455*0Sstevel@tonic-gate int 456*0Sstevel@tonic-gate log_new_context(int num_categories, char **category_names, log_context *lc) { 457*0Sstevel@tonic-gate log_context nlc; 458*0Sstevel@tonic-gate 459*0Sstevel@tonic-gate nlc = memget(sizeof (struct log_context)); 460*0Sstevel@tonic-gate if (nlc == NULL) { 461*0Sstevel@tonic-gate errno = ENOMEM; 462*0Sstevel@tonic-gate return (-1); 463*0Sstevel@tonic-gate } 464*0Sstevel@tonic-gate nlc->num_categories = num_categories; 465*0Sstevel@tonic-gate nlc->category_names = category_names; 466*0Sstevel@tonic-gate nlc->categories = memget(num_categories * sizeof (log_channel_list)); 467*0Sstevel@tonic-gate if (nlc->categories == NULL) { 468*0Sstevel@tonic-gate memput(nlc, sizeof (struct log_context)); 469*0Sstevel@tonic-gate errno = ENOMEM; 470*0Sstevel@tonic-gate return (-1); 471*0Sstevel@tonic-gate } 472*0Sstevel@tonic-gate memset(nlc->categories, '\0', 473*0Sstevel@tonic-gate num_categories * sizeof (log_channel_list)); 474*0Sstevel@tonic-gate nlc->flags = 0U; 475*0Sstevel@tonic-gate nlc->level = 0; 476*0Sstevel@tonic-gate *lc = nlc; 477*0Sstevel@tonic-gate return (0); 478*0Sstevel@tonic-gate } 479*0Sstevel@tonic-gate 480*0Sstevel@tonic-gate void 481*0Sstevel@tonic-gate log_free_context(log_context lc) { 482*0Sstevel@tonic-gate log_channel_list lcl, lcl_next; 483*0Sstevel@tonic-gate log_channel chan; 484*0Sstevel@tonic-gate int i; 485*0Sstevel@tonic-gate 486*0Sstevel@tonic-gate REQUIRE(lc != NULL); 487*0Sstevel@tonic-gate 488*0Sstevel@tonic-gate for (i = 0; i < lc->num_categories; i++) 489*0Sstevel@tonic-gate for (lcl = lc->categories[i]; lcl != NULL; lcl = lcl_next) { 490*0Sstevel@tonic-gate lcl_next = lcl->next; 491*0Sstevel@tonic-gate chan = lcl->channel; 492*0Sstevel@tonic-gate (void)log_free_channel(chan); 493*0Sstevel@tonic-gate memput(lcl, sizeof (struct log_channel_list)); 494*0Sstevel@tonic-gate } 495*0Sstevel@tonic-gate memput(lc->categories, 496*0Sstevel@tonic-gate lc->num_categories * sizeof (log_channel_list)); 497*0Sstevel@tonic-gate memput(lc, sizeof (struct log_context)); 498*0Sstevel@tonic-gate } 499*0Sstevel@tonic-gate 500*0Sstevel@tonic-gate int 501*0Sstevel@tonic-gate log_add_channel(log_context lc, int category, log_channel chan) { 502*0Sstevel@tonic-gate log_channel_list lcl; 503*0Sstevel@tonic-gate 504*0Sstevel@tonic-gate if (lc == NULL || category < 0 || category >= lc->num_categories) { 505*0Sstevel@tonic-gate errno = EINVAL; 506*0Sstevel@tonic-gate return (-1); 507*0Sstevel@tonic-gate } 508*0Sstevel@tonic-gate 509*0Sstevel@tonic-gate lcl = memget(sizeof (struct log_channel_list)); 510*0Sstevel@tonic-gate if (lcl == NULL) { 511*0Sstevel@tonic-gate errno = ENOMEM; 512*0Sstevel@tonic-gate return(-1); 513*0Sstevel@tonic-gate } 514*0Sstevel@tonic-gate lcl->channel = chan; 515*0Sstevel@tonic-gate lcl->next = lc->categories[category]; 516*0Sstevel@tonic-gate lc->categories[category] = lcl; 517*0Sstevel@tonic-gate chan->references++; 518*0Sstevel@tonic-gate return (0); 519*0Sstevel@tonic-gate } 520*0Sstevel@tonic-gate 521*0Sstevel@tonic-gate int 522*0Sstevel@tonic-gate log_remove_channel(log_context lc, int category, log_channel chan) { 523*0Sstevel@tonic-gate log_channel_list lcl, prev_lcl, next_lcl; 524*0Sstevel@tonic-gate int found = 0; 525*0Sstevel@tonic-gate 526*0Sstevel@tonic-gate if (lc == NULL || category < 0 || category >= lc->num_categories) { 527*0Sstevel@tonic-gate errno = EINVAL; 528*0Sstevel@tonic-gate return (-1); 529*0Sstevel@tonic-gate } 530*0Sstevel@tonic-gate 531*0Sstevel@tonic-gate for (prev_lcl = NULL, lcl = lc->categories[category]; 532*0Sstevel@tonic-gate lcl != NULL; 533*0Sstevel@tonic-gate lcl = next_lcl) { 534*0Sstevel@tonic-gate next_lcl = lcl->next; 535*0Sstevel@tonic-gate if (lcl->channel == chan) { 536*0Sstevel@tonic-gate log_free_channel(chan); 537*0Sstevel@tonic-gate if (prev_lcl != NULL) 538*0Sstevel@tonic-gate prev_lcl->next = next_lcl; 539*0Sstevel@tonic-gate else 540*0Sstevel@tonic-gate lc->categories[category] = next_lcl; 541*0Sstevel@tonic-gate memput(lcl, sizeof (struct log_channel_list)); 542*0Sstevel@tonic-gate /* 543*0Sstevel@tonic-gate * We just set found instead of returning because 544*0Sstevel@tonic-gate * the channel might be on the list more than once. 545*0Sstevel@tonic-gate */ 546*0Sstevel@tonic-gate found = 1; 547*0Sstevel@tonic-gate } else 548*0Sstevel@tonic-gate prev_lcl = lcl; 549*0Sstevel@tonic-gate } 550*0Sstevel@tonic-gate if (!found) { 551*0Sstevel@tonic-gate errno = ENOENT; 552*0Sstevel@tonic-gate return (-1); 553*0Sstevel@tonic-gate } 554*0Sstevel@tonic-gate return (0); 555*0Sstevel@tonic-gate } 556*0Sstevel@tonic-gate 557*0Sstevel@tonic-gate int 558*0Sstevel@tonic-gate log_option(log_context lc, int option, int value) { 559*0Sstevel@tonic-gate if (lc == NULL) { 560*0Sstevel@tonic-gate errno = EINVAL; 561*0Sstevel@tonic-gate return (-1); 562*0Sstevel@tonic-gate } 563*0Sstevel@tonic-gate switch (option) { 564*0Sstevel@tonic-gate case LOG_OPTION_DEBUG: 565*0Sstevel@tonic-gate if (value) 566*0Sstevel@tonic-gate lc->flags |= option; 567*0Sstevel@tonic-gate else 568*0Sstevel@tonic-gate lc->flags &= ~option; 569*0Sstevel@tonic-gate break; 570*0Sstevel@tonic-gate case LOG_OPTION_LEVEL: 571*0Sstevel@tonic-gate lc->level = value; 572*0Sstevel@tonic-gate break; 573*0Sstevel@tonic-gate default: 574*0Sstevel@tonic-gate errno = EINVAL; 575*0Sstevel@tonic-gate return (-1); 576*0Sstevel@tonic-gate } 577*0Sstevel@tonic-gate return (0); 578*0Sstevel@tonic-gate } 579*0Sstevel@tonic-gate 580*0Sstevel@tonic-gate int 581*0Sstevel@tonic-gate log_category_is_active(log_context lc, int category) { 582*0Sstevel@tonic-gate if (lc == NULL) { 583*0Sstevel@tonic-gate errno = EINVAL; 584*0Sstevel@tonic-gate return (-1); 585*0Sstevel@tonic-gate } 586*0Sstevel@tonic-gate if (category >= 0 && category < lc->num_categories && 587*0Sstevel@tonic-gate lc->categories[category] != NULL) 588*0Sstevel@tonic-gate return (1); 589*0Sstevel@tonic-gate return (0); 590*0Sstevel@tonic-gate } 591*0Sstevel@tonic-gate 592*0Sstevel@tonic-gate log_channel 593*0Sstevel@tonic-gate log_new_syslog_channel(unsigned int flags, int level, int facility) { 594*0Sstevel@tonic-gate log_channel chan; 595*0Sstevel@tonic-gate 596*0Sstevel@tonic-gate chan = memget(sizeof (struct log_channel)); 597*0Sstevel@tonic-gate if (chan == NULL) { 598*0Sstevel@tonic-gate errno = ENOMEM; 599*0Sstevel@tonic-gate return (NULL); 600*0Sstevel@tonic-gate } 601*0Sstevel@tonic-gate chan->type = log_syslog; 602*0Sstevel@tonic-gate chan->flags = flags; 603*0Sstevel@tonic-gate chan->level = level; 604*0Sstevel@tonic-gate chan->out.facility = facility; 605*0Sstevel@tonic-gate chan->references = 0; 606*0Sstevel@tonic-gate return (chan); 607*0Sstevel@tonic-gate } 608*0Sstevel@tonic-gate 609*0Sstevel@tonic-gate log_channel 610*0Sstevel@tonic-gate log_new_file_channel(unsigned int flags, int level, 611*0Sstevel@tonic-gate const char *name, FILE *stream, unsigned int versions, 612*0Sstevel@tonic-gate unsigned long max_size) { 613*0Sstevel@tonic-gate log_channel chan; 614*0Sstevel@tonic-gate 615*0Sstevel@tonic-gate chan = memget(sizeof (struct log_channel)); 616*0Sstevel@tonic-gate if (chan == NULL) { 617*0Sstevel@tonic-gate errno = ENOMEM; 618*0Sstevel@tonic-gate return (NULL); 619*0Sstevel@tonic-gate } 620*0Sstevel@tonic-gate chan->type = log_file; 621*0Sstevel@tonic-gate chan->flags = flags; 622*0Sstevel@tonic-gate chan->level = level; 623*0Sstevel@tonic-gate if (name != NULL) { 624*0Sstevel@tonic-gate size_t len; 625*0Sstevel@tonic-gate 626*0Sstevel@tonic-gate len = strlen(name); 627*0Sstevel@tonic-gate /* 628*0Sstevel@tonic-gate * Quantize length to a multiple of 256. There's space for the 629*0Sstevel@tonic-gate * NUL, since if len is a multiple of 256, the size chosen will 630*0Sstevel@tonic-gate * be the next multiple. 631*0Sstevel@tonic-gate */ 632*0Sstevel@tonic-gate chan->out.file.name_size = ((len / 256) + 1) * 256; 633*0Sstevel@tonic-gate chan->out.file.name = memget(chan->out.file.name_size); 634*0Sstevel@tonic-gate if (chan->out.file.name == NULL) { 635*0Sstevel@tonic-gate memput(chan, sizeof (struct log_channel)); 636*0Sstevel@tonic-gate errno = ENOMEM; 637*0Sstevel@tonic-gate return (NULL); 638*0Sstevel@tonic-gate } 639*0Sstevel@tonic-gate /* This is safe. */ 640*0Sstevel@tonic-gate strcpy(chan->out.file.name, name); 641*0Sstevel@tonic-gate } else { 642*0Sstevel@tonic-gate chan->out.file.name_size = 0; 643*0Sstevel@tonic-gate chan->out.file.name = NULL; 644*0Sstevel@tonic-gate } 645*0Sstevel@tonic-gate chan->out.file.stream = stream; 646*0Sstevel@tonic-gate chan->out.file.versions = versions; 647*0Sstevel@tonic-gate chan->out.file.max_size = max_size; 648*0Sstevel@tonic-gate chan->out.file.owner = getuid(); 649*0Sstevel@tonic-gate chan->out.file.group = getgid(); 650*0Sstevel@tonic-gate chan->references = 0; 651*0Sstevel@tonic-gate return (chan); 652*0Sstevel@tonic-gate } 653*0Sstevel@tonic-gate 654*0Sstevel@tonic-gate int 655*0Sstevel@tonic-gate log_set_file_owner(log_channel chan, uid_t owner, gid_t group) { 656*0Sstevel@tonic-gate if (chan->type != log_file) { 657*0Sstevel@tonic-gate errno = EBADF; 658*0Sstevel@tonic-gate return (-1); 659*0Sstevel@tonic-gate } 660*0Sstevel@tonic-gate chan->out.file.owner = owner; 661*0Sstevel@tonic-gate chan->out.file.group = group; 662*0Sstevel@tonic-gate return (0); 663*0Sstevel@tonic-gate } 664*0Sstevel@tonic-gate 665*0Sstevel@tonic-gate log_channel 666*0Sstevel@tonic-gate log_new_null_channel() { 667*0Sstevel@tonic-gate log_channel chan; 668*0Sstevel@tonic-gate 669*0Sstevel@tonic-gate chan = memget(sizeof (struct log_channel)); 670*0Sstevel@tonic-gate if (chan == NULL) { 671*0Sstevel@tonic-gate errno = ENOMEM; 672*0Sstevel@tonic-gate return (NULL); 673*0Sstevel@tonic-gate } 674*0Sstevel@tonic-gate chan->type = log_null; 675*0Sstevel@tonic-gate chan->flags = LOG_CHANNEL_OFF; 676*0Sstevel@tonic-gate chan->level = log_info; 677*0Sstevel@tonic-gate chan->references = 0; 678*0Sstevel@tonic-gate return (chan); 679*0Sstevel@tonic-gate } 680*0Sstevel@tonic-gate 681*0Sstevel@tonic-gate int 682*0Sstevel@tonic-gate log_inc_references(log_channel chan) { 683*0Sstevel@tonic-gate if (chan == NULL) { 684*0Sstevel@tonic-gate errno = EINVAL; 685*0Sstevel@tonic-gate return (-1); 686*0Sstevel@tonic-gate } 687*0Sstevel@tonic-gate chan->references++; 688*0Sstevel@tonic-gate return (0); 689*0Sstevel@tonic-gate } 690*0Sstevel@tonic-gate 691*0Sstevel@tonic-gate int 692*0Sstevel@tonic-gate log_dec_references(log_channel chan) { 693*0Sstevel@tonic-gate if (chan == NULL || chan->references <= 0) { 694*0Sstevel@tonic-gate errno = EINVAL; 695*0Sstevel@tonic-gate return (-1); 696*0Sstevel@tonic-gate } 697*0Sstevel@tonic-gate chan->references--; 698*0Sstevel@tonic-gate return (0); 699*0Sstevel@tonic-gate } 700*0Sstevel@tonic-gate 701*0Sstevel@tonic-gate log_channel_type 702*0Sstevel@tonic-gate log_get_channel_type(log_channel chan) { 703*0Sstevel@tonic-gate REQUIRE(chan != NULL); 704*0Sstevel@tonic-gate 705*0Sstevel@tonic-gate return (chan->type); 706*0Sstevel@tonic-gate } 707*0Sstevel@tonic-gate 708*0Sstevel@tonic-gate int 709*0Sstevel@tonic-gate log_free_channel(log_channel chan) { 710*0Sstevel@tonic-gate if (chan == NULL || chan->references <= 0) { 711*0Sstevel@tonic-gate errno = EINVAL; 712*0Sstevel@tonic-gate return (-1); 713*0Sstevel@tonic-gate } 714*0Sstevel@tonic-gate chan->references--; 715*0Sstevel@tonic-gate if (chan->references == 0) { 716*0Sstevel@tonic-gate if (chan->type == log_file) { 717*0Sstevel@tonic-gate if ((chan->flags & LOG_CLOSE_STREAM) && 718*0Sstevel@tonic-gate chan->out.file.stream != NULL) 719*0Sstevel@tonic-gate (void)fclose(chan->out.file.stream); 720*0Sstevel@tonic-gate if (chan->out.file.name != NULL) 721*0Sstevel@tonic-gate memput(chan->out.file.name, 722*0Sstevel@tonic-gate chan->out.file.name_size); 723*0Sstevel@tonic-gate } 724*0Sstevel@tonic-gate memput(chan, sizeof (struct log_channel)); 725*0Sstevel@tonic-gate } 726*0Sstevel@tonic-gate return (0); 727*0Sstevel@tonic-gate } 728