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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdlib.h> 27 #include <locale.h> 28 #include <limits.h> 29 #include <fcntl.h> 30 #include <sys/stat.h> 31 #include <sys/varargs.h> 32 #include <synch.h> 33 #include <thread.h> 34 #include <string.h> 35 #include <unistd.h> 36 #include "nscd_log.h" 37 #include "nscd_config.h" 38 #include "nscd_switch.h" 39 #include "cache.h" 40 41 /* 42 * old nscd debug levels 43 */ 44 #define DBG_OFF 0 45 #define DBG_CANT_FIND 2 46 #define DBG_NETLOOKUPS 4 47 #define DBG_ALL 6 48 49 /* max. chars in a nscd log entry */ 50 #define LOGBUFLEN 1024 51 52 /* configuration for the nscd log component */ 53 int _nscd_log_comp = 0x0; 54 int _nscd_log_level = 0x0; 55 static char _nscd_logfile[PATH_MAX] = { 0 }; 56 57 #define NSCD_DEBUG_NONE '0' 58 #define NSCD_DEBUG_OPEN '1' 59 #define NSCD_DEBUG_CLOSE '2' 60 61 static char _nscd_debug = NSCD_DEBUG_NONE; 62 static char _nscd_logfile_d[PATH_MAX] = { 0 }; 63 static char _nscd_logfile_s[PATH_MAX] = { 0 }; 64 65 /* statistics data */ 66 static nscd_cfg_stat_global_log_t logstats = { 67 NSCD_CFG_STAT_GROUP_INFO_GLOBAL_LOG, 0 }; 68 69 /* if no log file specified, log entry goes to stderr */ 70 int _logfd = 2; 71 72 73 /* close old log file and open a new one */ 74 static nscd_rc_t 75 _nscd_set_lf( 76 char *lf) 77 { 78 int newlogfd; 79 char *me = "_nscd_set_lf"; 80 81 /* 82 * don't try and open the log file /dev/null 83 */ 84 if (lf == NULL || *lf == 0) { 85 /* ignore empty log file specs */ 86 return (NSCD_SUCCESS); 87 } else if (strcmp(lf, "/dev/null") == 0) { 88 (void) strlcpy(_nscd_logfile, lf, PATH_MAX); 89 if (_logfd >= 0) 90 (void) close(_logfd); 91 _logfd = -1; 92 return (NSCD_SUCCESS); 93 } else if (strcmp(lf, "stderr") == 0) { 94 (void) strlcpy(_nscd_logfile, lf, PATH_MAX); 95 if (_logfd != -1 && _logfd != 2) 96 (void) close(_logfd); 97 _logfd = 2; 98 return (NSCD_SUCCESS); 99 } else { 100 101 /* 102 * In order to open this file securely, we'll try a few tricks 103 */ 104 105 if ((newlogfd = open(lf, O_EXCL|O_WRONLY|O_CREAT, 0644)) < 0) { 106 /* 107 * File already exists... now we need to get cute 108 * since opening a file in a world-writeable directory 109 * safely is hard = it could be a hard link or a 110 * symbolic link to a system file. 111 */ 112 struct stat before; 113 114 if (lstat(lf, &before) < 0) { 115 if (_nscd_debug == NSCD_DEBUG_NONE) 116 _nscd_logit(me, "Cannot open new " 117 "logfile \"%s\": %sn", 118 lf, strerror(errno)); 119 return (NSCD_CFG_FILE_OPEN_ERROR); 120 } 121 122 if (S_ISREG(before.st_mode) && /* no symbolic links */ 123 (before.st_nlink == 1) && /* no hard links */ 124 (before.st_uid == 0)) { /* owned by root */ 125 if ((newlogfd = 126 open(lf, O_APPEND|O_WRONLY, 0644)) < 0) { 127 if (_nscd_debug == NSCD_DEBUG_NONE) 128 _nscd_logit(me, 129 "Cannot open new "\ 130 "logfile \"%s\": %s\n", lf, 131 strerror(errno)); 132 return (NSCD_CFG_FILE_OPEN_ERROR); 133 } 134 } else { 135 if (_nscd_debug == NSCD_DEBUG_NONE) 136 _nscd_logit(me, "Cannot use specified " 137 "logfile \"%s\": "\ 138 "file is/has links or isn't " 139 "owned by root\n", lf); 140 return (NSCD_CFG_FILE_OPEN_ERROR); 141 } 142 } 143 144 (void) close(_logfd); 145 (void) strlcpy(_nscd_logfile, lf, PATH_MAX); 146 _logfd = newlogfd; 147 if (_nscd_debug == NSCD_DEBUG_NONE) 148 _nscd_logit(me, "Start of new logfile %s\n", lf); 149 } 150 return (NSCD_SUCCESS); 151 } 152 153 154 /* log an entry to the configured nscd log file */ 155 void 156 _nscd_logit( 157 char *funcname, 158 char *format, 159 ...) 160 { 161 static mutex_t loglock = DEFAULTMUTEX; 162 struct timeval tv; 163 char tid_buf[32]; 164 char pid_buf[32]; 165 char buffer[LOGBUFLEN]; 166 int safechars, offset; 167 va_list ap; 168 169 if (_logfd < 0) 170 return; 171 172 if (_nscd_debug == NSCD_DEBUG_OPEN) { 173 (void) mutex_lock(&loglock); 174 if (_nscd_debug == NSCD_DEBUG_OPEN && 175 *_nscd_logfile_d != '\0' && 176 (strcmp(_nscd_logfile, "/dev/null") == 0 || 177 strcmp(_nscd_logfile, "stderr") == 0)) { 178 (void) strlcpy(_nscd_logfile_s, 179 _nscd_logfile, PATH_MAX); 180 (void) _nscd_set_lf(_nscd_logfile_d); 181 } 182 _nscd_debug = NSCD_DEBUG_NONE; 183 (void) mutex_unlock(&loglock); 184 } else if (_nscd_debug == NSCD_DEBUG_CLOSE) { 185 (void) mutex_lock(&loglock); 186 if (_nscd_debug == NSCD_DEBUG_CLOSE) 187 (void) _nscd_set_lf(_nscd_logfile_s); 188 _nscd_debug = NSCD_DEBUG_NONE; 189 (void) mutex_unlock(&loglock); 190 } 191 192 va_start(ap, format); 193 194 if (gettimeofday(&tv, NULL) != 0 || 195 ctime_r(&tv.tv_sec, buffer, LOGBUFLEN) == NULL) { 196 (void) snprintf(buffer, LOGBUFLEN, 197 "<time conversion failed>\t"); 198 } else { 199 (void) sprintf(tid_buf, "--%d", thr_self()); 200 (void) sprintf(pid_buf, "--%ld", getpid()); 201 /* 202 * ctime_r() includes some stuff we don't want; 203 * adjust length to overwrite " YYYY\n" and 204 * include tid string length. 205 */ 206 offset = strlen(buffer) - 6; 207 safechars = LOGBUFLEN - (offset - 1); 208 (void) snprintf(buffer + offset, 209 safechars, ".%.4ld%s%s\t%s:\n\t\t", 210 tv.tv_usec/100, tid_buf, pid_buf, 211 funcname); 212 } 213 offset = strlen(buffer); 214 safechars = LOGBUFLEN - (offset - 1); 215 /*LINTED: E_SEC_PRINTF_VAR_FMT*/ 216 if (vsnprintf(buffer + offset, safechars, format, ap) > 217 safechars) { 218 (void) strncat(buffer, "...\n", LOGBUFLEN); 219 } 220 221 (void) mutex_lock(&loglock); 222 (void) write(_logfd, buffer, strlen(buffer)); 223 logstats.entries_logged++; 224 (void) mutex_unlock(&loglock); 225 226 va_end(ap); 227 } 228 229 /* 230 * Map old nscd debug level (0 -10) to log level: 231 * -- >= 6: DBG_ALL --> NSCD_LOG_LEVEL_ALL 232 * -- >= 4: DBG_DBG_NETLOOKUPS --> NSCD_LOG_LEVEL_CANT_FIND 233 * -- >= 2: DBG_CANT_FIND --> NSCD_LOG_LEVEL_CANT_FIND 234 * -- >= 0: DBG_OFF --> NSCD_LOG_LEVEL_NONE 235 */ 236 static int 237 debug_to_log_level( 238 int level) 239 { 240 if (level >= 0 && level <= 10) { 241 if (level >= DBG_ALL) 242 return (NSCD_LOG_LEVEL_ALL); 243 else if (level >= DBG_NETLOOKUPS) 244 return (NSCD_LOG_LEVEL_CANT_FIND); 245 else if (level >= DBG_CANT_FIND) 246 return (NSCD_LOG_LEVEL_CANT_FIND); 247 else if (level >= DBG_OFF) 248 return (NSCD_LOG_LEVEL_NONE); 249 } 250 return (level); 251 } 252 253 /* ARGSUSED */ 254 nscd_rc_t 255 _nscd_cfg_log_notify( 256 void *data, 257 struct nscd_cfg_param_desc *pdesc, 258 nscd_cfg_id_t *nswdb, 259 nscd_cfg_flag_t dflag, 260 nscd_cfg_error_t **errorp, 261 void *cookie) 262 { 263 264 nscd_cfg_global_log_t *logcfg; 265 int off; 266 267 /* 268 * At init time, the whole group of config params are received. 269 * At update time, group or individual parameter value could 270 * be received. 271 */ 272 273 if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) { 274 275 logcfg = (nscd_cfg_global_log_t *)data; 276 277 _nscd_log_comp = logcfg->debug_comp; 278 _nscd_log_level = logcfg->debug_level; 279 280 /* 281 * logcfg->logfile should have been opened 282 * by _nscd_cfg_log_verify() 283 */ 284 285 return (NSCD_SUCCESS); 286 } 287 288 /* 289 * individual config parameter 290 */ 291 off = offsetof(nscd_cfg_global_log_t, debug_comp); 292 if (pdesc->p_offset == off) { 293 _nscd_log_comp = *(nscd_cfg_bitmap_t *)data; 294 return (NSCD_SUCCESS); 295 } 296 297 off = offsetof(nscd_cfg_global_log_t, debug_level); 298 if (pdesc->p_offset == off) 299 _nscd_log_level = *(nscd_cfg_bitmap_t *)data; 300 301 /* 302 * logcfg->logfile should have been opened 303 * by _nscd_cfg_log_verify() 304 */ 305 306 return (NSCD_SUCCESS); 307 } 308 309 /* ARGSUSED */ 310 nscd_rc_t 311 _nscd_cfg_log_verify( 312 void *data, 313 struct nscd_cfg_param_desc *pdesc, 314 nscd_cfg_id_t *nswdb, 315 nscd_cfg_flag_t dflag, 316 nscd_cfg_error_t **errorp, 317 void **cookie) 318 { 319 nscd_cfg_global_log_t *logcfg; 320 nscd_cfg_bitmap_t bt; 321 int off; 322 323 /* 324 * There is no switch db specific config params 325 * for the nscd log component. It is a bug if 326 * the input param description is global. 327 */ 328 if (_nscd_cfg_flag_is_not_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL)) 329 return (NSCD_CFG_PARAM_DESC_ERROR); 330 331 /* 332 * At init time, the whole group of config params are received. 333 * At update time, group or individual parameter value could 334 * be received. 335 */ 336 337 if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) { 338 339 logcfg = (nscd_cfg_global_log_t *)data; 340 341 if (_nscd_cfg_bitmap_valid(logcfg->debug_comp, 342 NSCD_LOG_ALL) == 0) 343 return (NSCD_CFG_SYNTAX_ERROR); 344 345 if (_nscd_cfg_bitmap_valid(logcfg->debug_level, 346 NSCD_LOG_LEVEL_ALL) == 0) 347 return (NSCD_CFG_SYNTAX_ERROR); 348 349 if (logcfg->logfile != NULL) 350 return (_nscd_set_lf(logcfg->logfile)); 351 352 return (NSCD_SUCCESS); 353 } 354 355 /* 356 * individual config parameter 357 */ 358 359 off = offsetof(nscd_cfg_global_log_t, debug_comp); 360 if (pdesc->p_offset == off) { 361 362 bt = *(nscd_cfg_bitmap_t *)data; 363 if (_nscd_cfg_bitmap_valid(bt, NSCD_LOG_ALL) == 0) 364 return (NSCD_CFG_SYNTAX_ERROR); 365 366 return (NSCD_SUCCESS); 367 } 368 369 off = offsetof(nscd_cfg_global_log_t, debug_level); 370 if (pdesc->p_offset == off) { 371 372 bt = *(nscd_cfg_bitmap_t *)data; 373 if (_nscd_cfg_bitmap_valid(bt, NSCD_LOG_LEVEL_ALL) == 0) 374 return (NSCD_CFG_SYNTAX_ERROR); 375 376 return (NSCD_SUCCESS); 377 } 378 379 off = offsetof(nscd_cfg_global_log_t, logfile); 380 if (pdesc->p_offset == off) { 381 if (data != NULL) 382 return (_nscd_set_lf((char *)data)); 383 else 384 return (NSCD_SUCCESS); 385 } 386 387 return (NSCD_CFG_PARAM_DESC_ERROR); 388 } 389 390 /* ARGSUSED */ 391 nscd_rc_t 392 _nscd_cfg_log_get_stat( 393 void **stat, 394 struct nscd_cfg_stat_desc *sdesc, 395 nscd_cfg_id_t *nswdb, 396 nscd_cfg_flag_t *dflag, 397 void (**free_stat)(void *stat), 398 nscd_cfg_error_t **errorp) 399 { 400 401 *(nscd_cfg_stat_global_log_t **)stat = &logstats; 402 403 /* indicate the statistics are static, i.e., do not free */ 404 *dflag = _nscd_cfg_flag_set(*dflag, NSCD_CFG_DFLAG_STATIC_DATA); 405 406 return (NSCD_SUCCESS); 407 } 408 409 /* 410 * set the name of the current log file and make it current. 411 */ 412 nscd_rc_t 413 _nscd_set_log_file( 414 char *name) 415 { 416 nscd_rc_t rc; 417 nscd_cfg_handle_t *h; 418 419 rc = _nscd_cfg_get_handle("logfile", NULL, &h, NULL); 420 if (rc != NSCD_SUCCESS) 421 return (rc); 422 423 rc = _nscd_cfg_set(h, name, NULL); 424 _nscd_cfg_free_handle(h); 425 if (rc != NSCD_SUCCESS) 426 exit(rc); 427 428 return (NSCD_SUCCESS); 429 } 430 431 /* Set debug level to the new one and make it current */ 432 nscd_rc_t 433 _nscd_set_debug_level( 434 int level) 435 { 436 nscd_rc_t rc; 437 nscd_cfg_handle_t *h; 438 int l = 0; 439 int c = -1; 440 441 /* old nscd debug level is 1 to 10, map it to log_level and log_comp */ 442 if (level >= 0 && level <= 10) { 443 l = debug_to_log_level(level); 444 c = NSCD_LOG_CACHE; 445 } else 446 l = level; 447 448 if (level < 0) 449 c = -1 * level / 1000000; 450 451 if (c != -1) { 452 rc = _nscd_cfg_get_handle("debug-components", NULL, &h, NULL); 453 if (rc != NSCD_SUCCESS) 454 return (rc); 455 456 rc = _nscd_cfg_set(h, &c, NULL); 457 _nscd_cfg_free_handle(h); 458 if (rc != NSCD_SUCCESS) 459 exit(rc); 460 } 461 462 rc = _nscd_cfg_get_handle("debug-level", NULL, &h, NULL); 463 if (rc != NSCD_SUCCESS) 464 return (rc); 465 466 if (level < 0) 467 l = -1 * level % 1000000; 468 469 rc = _nscd_cfg_set(h, &l, NULL); 470 _nscd_cfg_free_handle(h); 471 if (rc != NSCD_SUCCESS) 472 exit(rc); 473 474 return (NSCD_SUCCESS); 475 } 476 477 void 478 _nscd_get_log_info( 479 char *level, 480 int llen, 481 char *file, 482 int flen) 483 { 484 if (_nscd_log_level != 0) 485 (void) snprintf(level, llen, "%d", _nscd_log_level); 486 if (*_nscd_logfile != '\0') 487 (void) strlcpy(file, _nscd_logfile, flen); 488 } 489