Add braces to empty conditional blocks
[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  #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(&currenttime, &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     free(ourName);
579     ourName = strdup(fileName);
580     opr_Assert(ourName != NULL);
581
582     serverLogFD = tempfd;
583
584     return 0;
585 }
586
587 /*!
588  * Open the log file descriptor or a connection to the system log.
589  *
590  * This function should be called once during program initialization and
591  * must be called before calling FSLog() or WriteLogBuffer().  The
592  * fields of the given argument specify the logging destination and
593  * various optional features.
594  *
595  * The lopt_logLevel value specifies the initial logging level.
596  *
597  * The lopt_dest enum specifies the logging destination; either
598  * file based (logDest_file) or the system log (logDest_syslog).
599  *
600  * File Based Logging
601  * ------------------
602  *
603  * A file will be opened for log messages when the lopt_dest enum is set
604  * to logDest_file.  The file specified by lopt_filename will be opened
605  * for appending log messages.  A new file will be created if the log
606  * file does not exist.
607  *
608  * The lopt_rotateOnOpen flag specifies whether an existing log file is
609  * to be renamed and a new log file created during the call to OpenLog.
610  * The lopt_rotateOnOpen flag has no effect if the file given by
611  * lopt_filename is a named pipe (fifo).
612  *
613  * The lopt_rotateOnReset flag specifies whether the log file is renamed
614  * and then reopened when the reset signal (SIGHUP) is caught.
615  *
616  * The lopt_rotateStyle enum specifies how the new log file is renamed when
617  * lopt_rotateOnOpen or lopt_rotateOnReset are set. The lopt_rotateStyle
618  * may be the traditional Transarc style (logRotate_old) or the MR-AFS
619  * style (logRotate_timestamp).
620  *
621  * When lopt_rotateStyle is set to logRotate_old, the suffix ".old" is
622  * appended to the log file name. The existing ".old" log file is
623  * removed.
624  *
625  * When lopt_rotateStyle is set to logRotate_timestamp, a timestamp
626  * string is appended to the log file name and existing files are not
627  * removed.
628  *
629  * \note  Messages written to stdout and stderr are redirected to the log
630  *        file when file-based logging is in effect.
631  *
632  * System Logging
633  * --------------
634  *
635  * A connection to the system log (syslog) will be established for log
636  * messages when the lopt_dest enum is set to logDest_syslog.
637  *
638  * The lopt_facility specifies the system log facility to be used when
639  * writing messages to the system log.
640  *
641  * The lopt_tag string specifies the indentification string to be used
642  * when writing messages to the system log.
643  *
644  * \param opts  logging options. A copy of the logging
645  *              options will be made before returning to
646  *              the caller.
647  *
648  * \returns 0 on success
649  */
650 int
651 OpenLog(struct logOptions *opts)
652 {
653     int code;
654
655 #if defined(AFS_PTHREAD_ENV)
656     opr_Verify(pthread_once(&serverLogOnce, InitServerLogMutex) == 0);
657 #endif /* AFS_PTHREAD_ENV */
658
659     LogLevel = serverLogOpts.logLevel = opts->logLevel;
660     serverLogOpts.dest = opts->dest;
661     switch (serverLogOpts.dest) {
662     case logDest_file:
663         serverLogOpts.lopt_rotateOnOpen = opts->lopt_rotateOnOpen;
664         serverLogOpts.lopt_rotateOnReset = opts->lopt_rotateOnReset;
665         serverLogOpts.lopt_rotateStyle = opts->lopt_rotateStyle;
666         /* OpenLogFile() sets ourName; don't cache filename here. */
667         code = OpenLogFile(opts->lopt_filename);
668         break;
669 #ifdef HAVE_SYSLOG
670     case logDest_syslog:
671         serverLogOpts.lopt_rotateOnOpen = 0;
672         serverLogOpts.lopt_rotateOnReset = 0;
673         serverLogOpts.lopt_rotateStyle = logRotate_none;
674         openlog(opts->lopt_tag, LOG_PID, opts->lopt_facility);
675         code = 0;
676         break;
677 #endif
678     default:
679         opr_Assert(0);
680     }
681     return code;
682 }                               /*OpenLog */
683
684 /*!
685  * Reopen the log file descriptor.
686  *
687  * Reopen the log file descriptor in order to support rotation
688  * of the log files.  Has no effect when logging to the syslog.
689  *
690  * \returns  0 on success
691  */
692 int
693 ReOpenLog(void)
694 {
695     int flags = O_WRONLY | O_APPEND | O_CREAT;
696
697 #ifdef HAVE_SYSLOG
698     if (serverLogOpts.dest == logDest_syslog) {
699         return 0;
700     }
701 #endif
702
703     LOCK_SERVERLOG();
704     if (ourName == NULL) {
705         UNLOCK_SERVERLOG();
706         return -1;
707     }
708     if (IsFIFO(ourName)) {
709         flags |= O_NONBLOCK;
710     }
711     if (serverLogFD >= 0)
712         close(serverLogFD);
713     serverLogFD = open(ourName, flags, 0666);
714     if (serverLogFD >= 0) {
715         RedirectStdStreams(ourName);
716     }
717     UNLOCK_SERVERLOG();
718     return serverLogFD < 0 ? -1 : 0;
719 }
720
721 /*!
722  * Rotate the log file by renaming then truncating.
723  */
724 static void
725 RotateLogFile(void)
726 {
727     LOCK_SERVERLOG();
728     if (ourName != NULL) {
729         if (serverLogFD >= 0) {
730             close(serverLogFD);
731             serverLogFD = -1;
732         }
733         OpenLogFile(ourName);
734     }
735     UNLOCK_SERVERLOG();
736 }
737
738 /*!
739  * Close the server log file.
740  *
741  * \note Must be preceeded by OpenLog().
742  */
743 void
744 CloseLog(void)
745 {
746     LOCK_SERVERLOG();
747
748 #ifdef HAVE_SYSLOG
749     if (serverLogOpts.dest == logDest_syslog) {
750         closelog();
751     } else
752 #endif
753     {
754         if (serverLogFD >= 0) {
755             close(serverLogFD);
756             serverLogFD = -1;
757         }
758         free(ourName);
759         ourName = NULL;
760     }
761     UNLOCK_SERVERLOG();
762 }