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