Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * Copyright 2000, International Business Machines Corporation and others. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * This software has been released under the terms of the IBM Public | |
6 | * License. For details, see the LICENSE file in the top-level source | |
7 | * directory or online at http://www.openafs.org/dl/license10.html | |
8 | */ | |
9 | ||
10 | /* serverLog.c - Server logging */ | |
11 | /* */ | |
12 | /* Information Technology Center */ | |
13 | /* Date: 05/21/97 */ | |
14 | /* */ | |
15 | /* Function - These routines implement logging from the servers. */ | |
16 | /* */ | |
17 | /* ********************************************************************** */ | |
18 | ||
19 | #include <afsconfig.h> | |
20 | #include <afs/param.h> | |
21 | #include <afs/stds.h> | |
22 | ||
23 | #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */ | |
24 | ||
25 | #include <roken.h> /* Must come after procmgmt.h */ | |
26 | #ifdef AFS_PTHREAD_ENV | |
27 | #include <opr/softsig.h> | |
28 | #include <afs/procmgmt_softsig.h> /* Must come after softsig.h */ | |
29 | #endif | |
30 | #include <afs/opr.h> | |
31 | #include "afsutil.h" | |
32 | #include "fileutil.h" | |
33 | #include <lwp.h> | |
34 | ||
35 | #if defined(AFS_PTHREAD_ENV) | |
36 | #include <pthread.h> | |
37 | static pthread_once_t serverLogOnce = PTHREAD_ONCE_INIT; | |
38 | static pthread_mutex_t serverLogMutex; | |
39 | #define LOCK_SERVERLOG() opr_Verify(pthread_mutex_lock(&serverLogMutex) == 0) | |
40 | #define UNLOCK_SERVERLOG() opr_Verify(pthread_mutex_unlock(&serverLogMutex) == 0) | |
41 | ||
42 | #ifdef AFS_NT40_ENV | |
43 | #define NULLDEV "NUL" | |
44 | #else | |
45 | #define NULLDEV "/dev/null" | |
46 | #endif | |
47 | ||
48 | #else /* AFS_PTHREAD_ENV */ | |
49 | #define LOCK_SERVERLOG() | |
50 | #define UNLOCK_SERVERLOG() | |
51 | #endif /* AFS_PTHREAD_ENV */ | |
52 | ||
53 | #ifdef AFS_NT40_ENV | |
54 | #define F_OK 0 | |
55 | #define O_NONBLOCK 0 | |
56 | #endif | |
57 | ||
58 | /*! | |
59 | * Placeholder function to return dummy thread number. | |
60 | */ | |
61 | static int | |
62 | dummyThreadNum(void) | |
63 | { | |
64 | return -1; | |
65 | } | |
66 | static int (*threadNumProgram) (void) = dummyThreadNum; | |
67 | ||
68 | /* After single-threaded startup, accesses to serverlogFD and | |
69 | * serverLogSyslog* are protected by LOCK_SERVERLOG(). */ | |
70 | static int serverLogFD = -1; /*!< The log file descriptor. */ | |
71 | static struct logOptions serverLogOpts; /*!< logging options */ | |
72 | ||
73 | int LogLevel; /*!< The current logging level. */ | |
74 | static int threadIdLogs = 0; /*!< Include the thread id in log messages when true. */ | |
75 | static int resetSignals = 0; /*!< Reset signal handlers for the next signal when true. */ | |
76 | static char *ourName = NULL; /*!< The fully qualified log file path, saved for reopens. */ | |
77 | ||
78 | static int OpenLogFile(const char *fileName); | |
79 | static void RotateLogFile(void); | |
80 | ||
81 | /*! | |
82 | * Determine if the file is a named pipe. | |
83 | * | |
84 | * This check is performed to support named pipes as logs by not rotating them | |
85 | * and opening them with a non-blocking flags. | |
86 | * | |
87 | * \param[in] fileName log file name | |
88 | * | |
89 | * \returns non-zero if log file is a named pipe. | |
90 | */ | |
91 | static int | |
92 | IsFIFO(const char *fileName) | |
93 | { | |
94 | struct stat statbuf; | |
95 | return (lstat(fileName, &statbuf) == 0) && (S_ISFIFO(statbuf.st_mode)); | |
96 | } | |
97 | ||
98 | /*! | |
99 | * Return the current logging level. | |
100 | */ | |
101 | int | |
102 | GetLogLevel(void) | |
103 | { | |
104 | return LogLevel; | |
105 | } | |
106 | ||
107 | /*! | |
108 | * Return the log destination. | |
109 | */ | |
110 | enum logDest | |
111 | GetLogDest(void) | |
112 | { | |
113 | return serverLogOpts.lopt_dest; | |
114 | } | |
115 | ||
116 | /*! | |
117 | * Get the log filename for file based logging. | |
118 | * | |
119 | * An empty string is returned if the log destination is not | |
120 | * file based. The caller must make a copy of the string | |
121 | * if it is accessed after the CloseLog. | |
122 | */ | |
123 | const char * | |
124 | GetLogFilename(void) | |
125 | { | |
126 | return serverLogOpts.lopt_dest == logDest_file ? (const char*)ourName : ""; | |
127 | } | |
128 | ||
129 | /*! | |
130 | * Set the function to log thread numbers. | |
131 | */ | |
132 | void | |
133 | SetLogThreadNumProgram(int (*func) (void) ) | |
134 | { | |
135 | threadNumProgram = func; | |
136 | } | |
137 | ||
138 | /*! | |
139 | * Write a block of bytes to the log. | |
140 | * | |
141 | * Write a block of bytes directly to the log without formatting | |
142 | * or prepending a timestamp. | |
143 | * | |
144 | * \param[in] buf pointer to bytes to write | |
145 | * \param[in] len number of bytes to write | |
146 | */ | |
147 | void | |
148 | WriteLogBuffer(char *buf, afs_uint32 len) | |
149 | { | |
150 | LOCK_SERVERLOG(); | |
151 | if (serverLogFD >= 0) { | |
152 | if (write(serverLogFD, buf, len) < 0) { | |
153 | /* don't care */ | |
154 | } | |
155 | } | |
156 | UNLOCK_SERVERLOG(); | |
157 | } | |
158 | ||
159 | /*! | |
160 | * Get the current thread number. | |
161 | */ | |
162 | int | |
163 | LogThreadNum(void) | |
164 | { | |
165 | return (*threadNumProgram) (); | |
166 | } | |
167 | ||
168 | /*! | |
169 | * Write a message to the log. | |
170 | * | |
171 | * \param[in] format printf-style format string | |
172 | * \param[in] args variable list of arguments | |
173 | */ | |
174 | void | |
175 | vFSLog(const char *format, va_list args) | |
176 | { | |
177 | time_t currenttime; | |
178 | char tbuffer[1024]; | |
179 | char *info; | |
180 | size_t len; | |
181 | struct tm tm; | |
182 | int num; | |
183 | ||
184 | currenttime = time(NULL); | |
185 | len = strftime(tbuffer, sizeof(tbuffer), "%a %b %d %H:%M:%S %Y ", | |
186 | localtime_r(¤ttime, &tm)); | |
187 | info = &tbuffer[len]; | |
188 | ||
189 | if (threadIdLogs) { | |
190 | num = (*threadNumProgram) (); | |
191 | if (num > -1) { | |
192 | snprintf(info, (sizeof tbuffer) - strlen(tbuffer), "[%d] ", | |
193 | num); | |
194 | info += strlen(info); | |
195 | } | |
196 | } | |
197 | ||
198 | vsnprintf(info, (sizeof tbuffer) - strlen(tbuffer), format, args); | |
199 | ||
200 | len = strlen(tbuffer); | |
201 | LOCK_SERVERLOG(); | |
202 | #ifdef HAVE_SYSLOG | |
203 | if (serverLogOpts.dest == logDest_syslog) { | |
204 | syslog(LOG_INFO, "%s", info); | |
205 | } else | |
206 | #endif | |
207 | if (serverLogFD >= 0) { | |
208 | if (write(serverLogFD, tbuffer, len) < 0) { | |
209 | /* don't care */ | |
210 | } | |
211 | } | |
212 | UNLOCK_SERVERLOG(); | |
213 | ||
214 | #if !defined(AFS_PTHREAD_ENV) && !defined(AFS_NT40_ENV) | |
215 | if (serverLogOpts.dest == logDest_file) { | |
216 | fflush(stdout); | |
217 | fflush(stderr); /* in case they're sharing the same FD */ | |
218 | } | |
219 | #endif | |
220 | } /*vFSLog */ | |
221 | ||
222 | /*! | |
223 | * Write a message to the log. | |
224 | * | |
225 | * \param[in] format printf-style format specification | |
226 | * \param[in] ... arguments for format specification | |
227 | */ | |
228 | void | |
229 | FSLog(const char *format, ...) | |
230 | { | |
231 | va_list args; | |
232 | ||
233 | va_start(args, format); | |
234 | vFSLog(format, args); | |
235 | va_end(args); | |
236 | } /*FSLog */ | |
237 | ||
238 | /*! | |
239 | * Write the command-line invocation to the log. | |
240 | * | |
241 | * \param[in] argc argument count from main() | |
242 | * \param[in] argv argument vector from main() | |
243 | * \param[in] progname program name | |
244 | * \param[in] version program version | |
245 | * \param[in] logstring log message string | |
246 | * \param[in] log printf-style log function | |
247 | */ | |
248 | void | |
249 | LogCommandLine(int argc, char **argv, const char *progname, | |
250 | const char *version, const char *logstring, | |
251 | void (*log) (const char *format, ...)) | |
252 | { | |
253 | int i, l; | |
254 | char *commandLine, *cx; | |
255 | ||
256 | opr_Assert(argc > 0); | |
257 | ||
258 | for (l = i = 0; i < argc; i++) | |
259 | l += strlen(argv[i]) + 1; | |
260 | if ((commandLine = malloc(l))) { | |
261 | for (cx = commandLine, i = 0; i < argc; i++) { | |
262 | strcpy(cx, argv[i]); | |
263 | cx += strlen(cx); | |
264 | *(cx++) = ' '; | |
265 | } | |
266 | commandLine[l-1] = '\0'; | |
267 | (*log)("%s %s %s%s(%s)\n", logstring, progname, | |
268 | version, strlen(version)>0?" ":"", commandLine); | |
269 | free(commandLine); | |
270 | } else { | |
271 | /* What, we're out of memory already!? */ | |
272 | (*log)("%s %s%s%s\n", logstring, | |
273 | progname, strlen(version)>0?" ":"", version); | |
274 | } | |
275 | } | |
276 | ||
277 | /*! | |
278 | * Write the single-DES deprecation warning to the log. | |
279 | */ | |
280 | void | |
281 | LogDesWarning(void) | |
282 | { | |
283 | /* The blank newlines help this stand out a bit more in the log. */ | |
284 | ViceLog(0, ("\n")); | |
285 | ViceLog(0, ("WARNING: You are using single-DES keys in a KeyFile. Using single-DES\n")); | |
286 | ViceLog(0, ("WARNING: long-term keys is considered insecure, and it is strongly\n")); | |
287 | ViceLog(0, ("WARNING: recommended that you migrate to stronger encryption. See\n")); | |
288 | ViceLog(0, ("WARNING: OPENAFS-SA-2013-003 on http://www.openafs.org/security/\n")); | |
289 | ViceLog(0, ("WARNING: for details.\n")); | |
290 | ViceLog(0, ("\n")); | |
291 | } | |
292 | ||
293 | /*! | |
294 | * Move the current log file out of the way so a new one can be started. | |
295 | * | |
296 | * The format of the new name depends on the logging style. The traditional | |
297 | * Transarc style appends ".old" to the log file name. When MR-AFS style | |
298 | * logging is in effect, a time stamp is appended to the log file name instead | |
299 | * of ".old". | |
300 | * | |
301 | * \bug Unfortunately, no check is made to avoid overwriting | |
302 | * old logs in the traditional Transarc mode. | |
303 | * | |
304 | * \param fileName fully qualified log file path | |
305 | */ | |
306 | static void | |
307 | RenameLogFile(const char *fileName) | |
308 | { | |
309 | int code; | |
310 | char *nextName = NULL; | |
311 | int tries; | |
312 | time_t t; | |
313 | struct stat buf; | |
314 | struct tm *timeFields; | |
315 | ||
316 | switch (serverLogOpts.lopt_rotateStyle) { | |
317 | case logRotate_none: | |
318 | break; | |
319 | case logRotate_old: | |
320 | code = asprintf(&nextName, "%s.old", fileName); | |
321 | if (code < 0) { | |
322 | nextName = NULL; | |
323 | } | |
324 | break; | |
325 | case logRotate_timestamp: | |
326 | time(&t); | |
327 | for (tries = 0; nextName == NULL && tries < 100; t++, tries++) { | |
328 | timeFields = localtime(&t); | |
329 | code = asprintf(&nextName, "%s.%d%02d%02d%02d%02d%02d", | |
330 | fileName, timeFields->tm_year + 1900, | |
331 | timeFields->tm_mon + 1, timeFields->tm_mday, | |
332 | timeFields->tm_hour, timeFields->tm_min, | |
333 | timeFields->tm_sec); | |
334 | if (code < 0) { | |
335 | nextName = NULL; | |
336 | break; | |
337 | } | |
338 | if (lstat(nextName, &buf) == 0) { | |
339 | /* Avoid clobbering a log. */ | |
340 | free(nextName); | |
341 | nextName = NULL; | |
342 | } | |
343 | } | |
344 | break; | |
345 | default: | |
346 | opr_Assert(0); | |
347 | } | |
348 | if (nextName != NULL) { | |
349 | rk_rename(fileName, nextName); /* Don't check the error code. */ | |
350 | free(nextName); | |
351 | } | |
352 | } | |
353 | ||
354 | /*! | |
355 | * Write message to the log to indicate the log level. | |
356 | * | |
357 | * This helper function is called by the signal handlers when the log level is | |
358 | * changed, to write a message to the log to indicate the log level has been | |
359 | * changed. | |
360 | */ | |
361 | static void* | |
362 | DebugOn(void *param) | |
363 | { | |
364 | int loglevel = (intptr_t)param; | |
365 | if (loglevel == 0) { | |
366 | ViceLog(0, ("Reset Debug levels to 0\n")); | |
367 | } else { | |
368 | ViceLog(0, ("Set Debug On level = %d\n", loglevel)); | |
369 | } | |
370 | return 0; | |
371 | } /*DebugOn */ | |
372 | ||
373 | /*! | |
374 | * Signal handler to increase the logging level. | |
375 | * | |
376 | * Increase the current logging level to 1 if it in currently 0, | |
377 | * otherwise, increase the current logging level by a factor of 5 if it | |
378 | * is currently non-zero. | |
379 | * | |
380 | * Enables thread id logging when the log level is greater than 1. | |
381 | */ | |
382 | void | |
383 | SetDebug_Signal(int signo) | |
384 | { | |
385 | if (LogLevel > 0) { | |
386 | LogLevel *= 5; | |
387 | ||
388 | #if defined(AFS_PTHREAD_ENV) | |
389 | if (LogLevel > 1 && threadNumProgram != NULL && | |
390 | threadIdLogs == 0) { | |
391 | threadIdLogs = 1; | |
392 | } | |
393 | #endif | |
394 | } else { | |
395 | LogLevel = 1; | |
396 | ||
397 | #if defined(AFS_PTHREAD_ENV) | |
398 | if (threadIdLogs == 1) | |
399 | threadIdLogs = 0; | |
400 | #endif | |
401 | } | |
402 | #if defined(AFS_PTHREAD_ENV) | |
403 | DebugOn((void *)(intptr_t)LogLevel); | |
404 | #else /* AFS_PTHREAD_ENV */ | |
405 | IOMGR_SoftSig(DebugOn, (void *)(intptr_t)LogLevel); | |
406 | #endif /* AFS_PTHREAD_ENV */ | |
407 | ||
408 | if (resetSignals) { | |
409 | /* When pthreaded softsig handlers are not in use, some platforms | |
410 | * require this signal handler to be set again. */ | |
411 | (void)signal(signo, SetDebug_Signal); | |
412 | } | |
413 | } /*SetDebug_Signal */ | |
414 | ||
415 | /*! | |
416 | * Signal handler to reset the logging level. | |
417 | * | |
418 | * Reset the logging level and disable thread id logging. | |
419 | * | |
420 | * \note This handler has the side-effect of rotating and reopening | |
421 | * MR-AFS style logs. | |
422 | */ | |
423 | void | |
424 | ResetDebug_Signal(int signo) | |
425 | { | |
426 | LogLevel = 0; | |
427 | ||
428 | #if defined(AFS_PTHREAD_ENV) | |
429 | DebugOn((void *)(intptr_t)LogLevel); | |
430 | #else /* AFS_PTHREAD_ENV */ | |
431 | IOMGR_SoftSig(DebugOn, (void *)(intptr_t)LogLevel); | |
432 | #endif /* AFS_PTHREAD_ENV */ | |
433 | ||
434 | if (resetSignals) { | |
435 | /* When pthreaded softsig handlers are not in use, some platforms | |
436 | * require this signal handler to be set again. */ | |
437 | (void)signal(signo, ResetDebug_Signal); | |
438 | } | |
439 | #if defined(AFS_PTHREAD_ENV) | |
440 | if (threadIdLogs == 1) | |
441 | threadIdLogs = 0; | |
442 | #endif | |
443 | if (serverLogOpts.lopt_rotateOnReset) { | |
444 | RotateLogFile(); | |
445 | } | |
446 | } /*ResetDebug_Signal */ | |
447 | ||
448 | /*! | |
449 | * Handle requests to reopen the log. | |
450 | * | |
451 | * This signal handler will reopen the log file. A new, empty log file | |
452 | * will be created if the log file does not already exist. | |
453 | * | |
454 | * External log rotation programs may rotate a server log file by | |
455 | * renaming the existing server log file and then immediately sending a | |
456 | * signal to the corresponding server process. Server log messages will | |
457 | * continue to be appended to the renamed server log file until the | |
458 | * server log is reopened. After this signal handler completes, server | |
459 | * log messages will be written to the new log file. This allows | |
460 | * external log rotation programs to rotate log files without | |
461 | * messages being dropped. | |
462 | */ | |
463 | void | |
464 | ReOpenLog_Signal(int signo) | |
465 | { | |
466 | ReOpenLog(); | |
467 | if (resetSignals) { | |
468 | (void)signal(signo, ReOpenLog_Signal); | |
469 | } | |
470 | } | |
471 | ||
472 | #ifdef AFS_PTHREAD_ENV | |
473 | /*! | |
474 | * Register pthread-safe signal handlers for server log management. | |
475 | * | |
476 | * \note opr_softsig_Init() must be called before this function. | |
477 | */ | |
478 | void | |
479 | SetupLogSoftSignals(void) | |
480 | { | |
481 | opr_softsig_Register(SIGHUP, ResetDebug_Signal); | |
482 | opr_softsig_Register(SIGTSTP, SetDebug_Signal); | |
483 | opr_softsig_Register(SIGUSR1, ReOpenLog_Signal); | |
484 | #ifndef AFS_NT40_ENV | |
485 | (void)signal(SIGPIPE, SIG_IGN); | |
486 | #endif | |
487 | } | |
488 | #endif /* AFS_PTHREAD_ENV */ | |
489 | ||
490 | /*! | |
491 | * Register signal handlers for server log management. | |
492 | * | |
493 | * \note This function is deprecated and should not be used | |
494 | * in new code. This function should be removed when | |
495 | * all the servers have been converted to pthreads | |
496 | * and lwp has been removed. | |
497 | */ | |
498 | void | |
499 | SetupLogSignals(void) | |
500 | { | |
501 | resetSignals = 1; | |
502 | (void)signal(SIGHUP, ResetDebug_Signal); | |
503 | (void)signal(SIGTSTP, SetDebug_Signal); | |
504 | (void)signal(SIGUSR1, ReOpenLog_Signal); | |
505 | #ifndef AFS_NT40_ENV | |
506 | (void)signal(SIGPIPE, SIG_IGN); | |
507 | #endif | |
508 | } | |
509 | ||
510 | #if defined(AFS_PTHREAD_ENV) | |
511 | static void | |
512 | InitServerLogMutex(void) | |
513 | { | |
514 | opr_Verify(pthread_mutex_init(&serverLogMutex, NULL) == 0); | |
515 | } | |
516 | #endif /* AFS_PTHREAD_ENV */ | |
517 | ||
518 | /*! | |
519 | * Redirect stdout and stderr to the log file. | |
520 | * | |
521 | * \note Call directly after opening the log file. | |
522 | * | |
523 | * \param[in] fileName log file name | |
524 | */ | |
525 | static void | |
526 | RedirectStdStreams(const char *fileName) | |
527 | { | |
528 | if (freopen(fileName, "a", stdout) == NULL) { | |
529 | /* don't care */ | |
530 | } | |
531 | if (freopen(fileName, "a", stderr) != NULL) { | |
532 | #ifdef HAVE_SETVBUF | |
533 | setvbuf(stderr, NULL, _IONBF, 0); | |
534 | #else | |
535 | setbuf(stderr, NULL); | |
536 | #endif | |
537 | } | |
538 | } | |
539 | ||
540 | /*! | |
541 | * Open the log file. | |
542 | * | |
543 | * Open the log file using the options given in OpenLog(). | |
544 | * | |
545 | * \returns 0 on success | |
546 | */ | |
547 | static int | |
548 | OpenLogFile(const char *fileName) | |
549 | { | |
550 | /* | |
551 | * This function should allow various libraries that inconsistently | |
552 | * use stdout/stderr to all go to the same place | |
553 | */ | |
554 | int tempfd; | |
555 | int flags = O_WRONLY | O_CREAT | O_APPEND; | |
556 | ||
557 | opr_Assert(serverLogOpts.dest == logDest_file); | |
558 | ||
559 | opr_Assert(fileName != NULL); | |
560 | ||
561 | if (IsFIFO(fileName)) { | |
562 | /* Support named pipes as logs by not rotating them. */ | |
563 | flags |= O_NONBLOCK; | |
564 | } else if (serverLogOpts.lopt_rotateOnOpen) { | |
565 | /* Old style logging always started a new log file. */ | |
566 | flags |= O_TRUNC; | |
567 | RenameLogFile(fileName); | |
568 | } | |
569 | ||
570 | tempfd = open(fileName, flags, 0666); | |
571 | if (tempfd < 0) { | |
572 | printf("Unable to open log file %s\n", fileName); | |
573 | return -1; | |
574 | } | |
575 | RedirectStdStreams(fileName); | |
576 | ||
577 | /* Save our name for reopening. */ | |
578 | if (ourName != fileName) { | |
579 | /* Make a copy if needed */ | |
580 | free(ourName); | |
581 | ourName = strdup(fileName); | |
582 | opr_Assert(ourName != NULL); | |
583 | } | |
584 | ||
585 | serverLogFD = tempfd; | |
586 | ||
587 | return 0; | |
588 | } | |
589 | ||
590 | /*! | |
591 | * Open the log file descriptor or a connection to the system log. | |
592 | * | |
593 | * This function should be called once during program initialization and | |
594 | * must be called before calling FSLog() or WriteLogBuffer(). The | |
595 | * fields of the given argument specify the logging destination and | |
596 | * various optional features. | |
597 | * | |
598 | * The lopt_logLevel value specifies the initial logging level. | |
599 | * | |
600 | * The lopt_dest enum specifies the logging destination; either | |
601 | * file based (logDest_file) or the system log (logDest_syslog). | |
602 | * | |
603 | * File Based Logging | |
604 | * ------------------ | |
605 | * | |
606 | * A file will be opened for log messages when the lopt_dest enum is set | |
607 | * to logDest_file. The file specified by lopt_filename will be opened | |
608 | * for appending log messages. A new file will be created if the log | |
609 | * file does not exist. | |
610 | * | |
611 | * The lopt_rotateOnOpen flag specifies whether an existing log file is | |
612 | * to be renamed and a new log file created during the call to OpenLog. | |
613 | * The lopt_rotateOnOpen flag has no effect if the file given by | |
614 | * lopt_filename is a named pipe (fifo). | |
615 | * | |
616 | * The lopt_rotateOnReset flag specifies whether the log file is renamed | |
617 | * and then reopened when the reset signal (SIGHUP) is caught. | |
618 | * | |
619 | * The lopt_rotateStyle enum specifies how the new log file is renamed when | |
620 | * lopt_rotateOnOpen or lopt_rotateOnReset are set. The lopt_rotateStyle | |
621 | * may be the traditional Transarc style (logRotate_old) or the MR-AFS | |
622 | * style (logRotate_timestamp). | |
623 | * | |
624 | * When lopt_rotateStyle is set to logRotate_old, the suffix ".old" is | |
625 | * appended to the log file name. The existing ".old" log file is | |
626 | * removed. | |
627 | * | |
628 | * When lopt_rotateStyle is set to logRotate_timestamp, a timestamp | |
629 | * string is appended to the log file name and existing files are not | |
630 | * removed. | |
631 | * | |
632 | * \note Messages written to stdout and stderr are redirected to the log | |
633 | * file when file-based logging is in effect. | |
634 | * | |
635 | * System Logging | |
636 | * -------------- | |
637 | * | |
638 | * A connection to the system log (syslog) will be established for log | |
639 | * messages when the lopt_dest enum is set to logDest_syslog. | |
640 | * | |
641 | * The lopt_facility specifies the system log facility to be used when | |
642 | * writing messages to the system log. | |
643 | * | |
644 | * The lopt_tag string specifies the indentification string to be used | |
645 | * when writing messages to the system log. | |
646 | * | |
647 | * \param opts logging options. A copy of the logging | |
648 | * options will be made before returning to | |
649 | * the caller. | |
650 | * | |
651 | * \returns 0 on success | |
652 | */ | |
653 | int | |
654 | OpenLog(struct logOptions *opts) | |
655 | { | |
656 | int code; | |
657 | ||
658 | #if defined(AFS_PTHREAD_ENV) | |
659 | opr_Verify(pthread_once(&serverLogOnce, InitServerLogMutex) == 0); | |
660 | #endif /* AFS_PTHREAD_ENV */ | |
661 | ||
662 | LogLevel = serverLogOpts.logLevel = opts->logLevel; | |
663 | serverLogOpts.dest = opts->dest; | |
664 | switch (serverLogOpts.dest) { | |
665 | case logDest_file: | |
666 | serverLogOpts.lopt_rotateOnOpen = opts->lopt_rotateOnOpen; | |
667 | serverLogOpts.lopt_rotateOnReset = opts->lopt_rotateOnReset; | |
668 | serverLogOpts.lopt_rotateStyle = opts->lopt_rotateStyle; | |
669 | /* OpenLogFile() sets ourName; don't cache filename here. */ | |
670 | code = OpenLogFile(opts->lopt_filename); | |
671 | break; | |
672 | #ifdef HAVE_SYSLOG | |
673 | case logDest_syslog: | |
674 | serverLogOpts.lopt_rotateOnOpen = 0; | |
675 | serverLogOpts.lopt_rotateOnReset = 0; | |
676 | serverLogOpts.lopt_rotateStyle = logRotate_none; | |
677 | openlog(opts->lopt_tag, LOG_PID, opts->lopt_facility); | |
678 | code = 0; | |
679 | break; | |
680 | #endif | |
681 | default: | |
682 | opr_Assert(0); | |
683 | } | |
684 | return code; | |
685 | } /*OpenLog */ | |
686 | ||
687 | /*! | |
688 | * Reopen the log file descriptor. | |
689 | * | |
690 | * Reopen the log file descriptor in order to support rotation | |
691 | * of the log files. Has no effect when logging to the syslog. | |
692 | * | |
693 | * \returns 0 on success | |
694 | */ | |
695 | int | |
696 | ReOpenLog(void) | |
697 | { | |
698 | int flags = O_WRONLY | O_APPEND | O_CREAT; | |
699 | ||
700 | #ifdef HAVE_SYSLOG | |
701 | if (serverLogOpts.dest == logDest_syslog) { | |
702 | return 0; | |
703 | } | |
704 | #endif | |
705 | ||
706 | LOCK_SERVERLOG(); | |
707 | if (ourName == NULL) { | |
708 | UNLOCK_SERVERLOG(); | |
709 | return -1; | |
710 | } | |
711 | if (IsFIFO(ourName)) { | |
712 | flags |= O_NONBLOCK; | |
713 | } | |
714 | if (serverLogFD >= 0) | |
715 | close(serverLogFD); | |
716 | serverLogFD = open(ourName, flags, 0666); | |
717 | if (serverLogFD >= 0) { | |
718 | RedirectStdStreams(ourName); | |
719 | } | |
720 | UNLOCK_SERVERLOG(); | |
721 | return serverLogFD < 0 ? -1 : 0; | |
722 | } | |
723 | ||
724 | /*! | |
725 | * Rotate the log file by renaming then truncating. | |
726 | */ | |
727 | static void | |
728 | RotateLogFile(void) | |
729 | { | |
730 | LOCK_SERVERLOG(); | |
731 | if (ourName != NULL) { | |
732 | if (serverLogFD >= 0) { | |
733 | close(serverLogFD); | |
734 | serverLogFD = -1; | |
735 | } | |
736 | OpenLogFile(ourName); | |
737 | } | |
738 | UNLOCK_SERVERLOG(); | |
739 | } | |
740 | ||
741 | /*! | |
742 | * Close the server log file. | |
743 | * | |
744 | * \note Must be preceeded by OpenLog(). | |
745 | */ | |
746 | void | |
747 | CloseLog(void) | |
748 | { | |
749 | LOCK_SERVERLOG(); | |
750 | ||
751 | #ifdef HAVE_SYSLOG | |
752 | if (serverLogOpts.dest == logDest_syslog) { | |
753 | closelog(); | |
754 | } else | |
755 | #endif | |
756 | { | |
757 | if (serverLogFD >= 0) { | |
758 | close(serverLogFD); | |
759 | serverLogFD = -1; | |
760 | } | |
761 | free(ourName); | |
762 | ourName = NULL; | |
763 | } | |
764 | UNLOCK_SERVERLOG(); | |
765 | } |