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