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
_nscd_set_lf(char * lf)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
_nscd_logit(char * funcname,char * format,...)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
debug_to_log_level(int level)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
_nscd_cfg_log_notify(void * data,struct nscd_cfg_param_desc * pdesc,nscd_cfg_id_t * nswdb,nscd_cfg_flag_t dflag,nscd_cfg_error_t ** errorp,void * cookie)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
_nscd_cfg_log_verify(void * data,struct nscd_cfg_param_desc * pdesc,nscd_cfg_id_t * nswdb,nscd_cfg_flag_t dflag,nscd_cfg_error_t ** errorp,void ** cookie)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
_nscd_cfg_log_get_stat(void ** stat,struct nscd_cfg_stat_desc * sdesc,nscd_cfg_id_t * nswdb,nscd_cfg_flag_t * dflag,void (** free_stat)(void * stat),nscd_cfg_error_t ** errorp)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
_nscd_set_log_file(char * name)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
_nscd_set_debug_level(int level)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
_nscd_get_log_info(char * level,int llen,char * file,int flen)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