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