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
_nscd_set_lf(char * lf)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
_nscd_logit(char * funcname,char * format,...)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
debug_to_log_level(int level)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
_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)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
_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)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
_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)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
_nscd_set_log_file(char * name)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
_nscd_set_debug_level(int level)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
_nscd_get_log_info(char * level,int llen,char * file,int flen)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