Add comment about serverlog locking
[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 routiens 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 static int
59 dummyThreadNum(void)
60 {
61     return -1;
62 }
63 static int (*threadNumProgram) (void) = dummyThreadNum;
64
65 /* After single-threaded startup, accesses to serverlogFD and
66  * serverLogSyslog* are protected by LOCK_SERVERLOG(). */
67 static int serverLogFD = -1;
68
69 #ifndef AFS_NT40_ENV
70 int serverLogSyslog = 0;
71 int serverLogSyslogFacility = LOG_DAEMON;
72 char *serverLogSyslogTag = 0;
73 #endif
74
75 int LogLevel;
76 int mrafsStyleLogs = 0;
77 static int threadIdLogs = 0;
78 static int resetSignals = 0;
79 static char *ourName = NULL;
80
81 static void RotateLogFile(void);
82
83 /*!
84  * Determine if the file is a named pipe.
85  *
86  * This check is performed to support named pipes as logs by not rotating them
87  * and opening them with a non-blocking flags.
88  *
89  * \param[in] fileName  log file name
90  *
91  * \returns non-zero if log file is a named pipe.
92  */
93 static int
94 IsFIFO(const char *fileName)
95 {
96     struct stat statbuf;
97     return (lstat(fileName, &statbuf) == 0) && (S_ISFIFO(statbuf.st_mode));
98 }
99
100 void
101 SetLogThreadNumProgram(int (*func) (void) )
102 {
103     threadNumProgram = func;
104 }
105
106 void
107 WriteLogBuffer(char *buf, afs_uint32 len)
108 {
109     LOCK_SERVERLOG();
110     if (serverLogFD >= 0) {
111         if (write(serverLogFD, buf, len) < 0)
112             ; /* don't care */
113     }
114     UNLOCK_SERVERLOG();
115 }
116
117 int
118 LogThreadNum(void)
119 {
120   return (*threadNumProgram) ();
121 }
122
123 void
124 vFSLog(const char *format, va_list args)
125 {
126     time_t currenttime;
127     char tbuffer[1024];
128     char *info;
129     size_t len;
130     struct tm tm;
131     int num;
132
133     currenttime = time(NULL);
134     len = strftime(tbuffer, sizeof(tbuffer), "%a %b %d %H:%M:%S %Y ",
135                    localtime_r(&currenttime, &tm));
136     info = &tbuffer[len];
137
138     if (threadIdLogs) {
139         num = (*threadNumProgram) ();
140         if (num > -1) {
141             snprintf(info, (sizeof tbuffer) - strlen(tbuffer), "[%d] ",
142                      num);
143             info += strlen(info);
144         }
145     }
146
147     vsnprintf(info, (sizeof tbuffer) - strlen(tbuffer), format, args);
148
149     len = strlen(tbuffer);
150     LOCK_SERVERLOG();
151 #ifndef AFS_NT40_ENV
152     if (serverLogSyslog) {
153         syslog(LOG_INFO, "%s", info);
154     } else
155 #endif
156     if (serverLogFD >= 0) {
157         if (write(serverLogFD, tbuffer, len) < 0)
158             ; /* don't care */
159     }
160     UNLOCK_SERVERLOG();
161
162 #if !defined(AFS_PTHREAD_ENV) && !defined(AFS_NT40_ENV)
163     if (!serverLogSyslog) {
164         fflush(stdout);
165         fflush(stderr);         /* in case they're sharing the same FD */
166     }
167 #endif
168 }                               /*vFSLog */
169
170 /* VARARGS1 */
171 /*@printflike@*/
172 void
173 FSLog(const char *format, ...)
174 {
175     va_list args;
176
177     va_start(args, format);
178     vFSLog(format, args);
179     va_end(args);
180 }                               /*FSLog */
181
182 void
183 LogCommandLine(int argc, char **argv, const char *progname,
184                const char *version, const char *logstring,
185                void (*log) (const char *format, ...))
186 {
187     int i, l;
188     char *commandLine, *cx;
189
190     opr_Assert(argc > 0);
191
192     for (l = i = 0; i < argc; i++)
193         l += strlen(argv[i]) + 1;
194     if ((commandLine = malloc(l))) {
195         for (cx = commandLine, i = 0; i < argc; i++) {
196             strcpy(cx, argv[i]);
197             cx += strlen(cx);
198             *(cx++) = ' ';
199         }
200         commandLine[l-1] = '\0';
201         (*log)("%s %s %s%s(%s)\n", logstring, progname,
202                     version, strlen(version)>0?" ":"", commandLine);
203         free(commandLine);
204     } else {
205         /* What, we're out of memory already!? */
206         (*log)("%s %s%s%s\n", logstring,
207               progname, strlen(version)>0?" ":"", version);
208     }
209 }
210
211 void
212 LogDesWarning(void)
213 {
214     /* The blank newlines help this stand out a bit more in the log. */
215     ViceLog(0, ("\n"));
216     ViceLog(0, ("WARNING: You are using single-DES keys in a KeyFile. Using single-DES\n"));
217     ViceLog(0, ("WARNING: long-term keys is considered insecure, and it is strongly\n"));
218     ViceLog(0, ("WARNING: recommended that you migrate to stronger encryption. See\n"));
219     ViceLog(0, ("WARNING: OPENAFS-SA-2013-003 on http://www.openafs.org/security/\n"));
220     ViceLog(0, ("WARNING: for details.\n"));
221     ViceLog(0, ("\n"));
222 }
223
224 /*!
225  * Move the current log file out of the way so a new one can be started.
226  *
227  * The format of the new name depends on the logging style.  The traditional
228  * Transarc style appends ".old" to the log file name.  When MR-AFS style
229  * logging is in effect, a time stamp is appended to the log file name instead
230  * of ".old".
231  *
232  * \bug  Unfortunately, no check is made to avoid overwriting
233  *       old logs in the traditional Transarc mode.
234  *
235  * \param fileName  fully qualified log file path
236  */
237 static void
238 RenameLogFile(const char *fileName)
239 {
240     int code;
241     char *nextName = NULL;
242
243     if (mrafsStyleLogs) {
244         int tries;
245         time_t t;
246         struct stat buf;
247         struct tm *timeFields;
248
249         time(&t);
250         for (tries = 0; nextName == NULL && tries < 100; t++, tries++) {
251             timeFields = localtime(&t);
252             code = asprintf(&nextName, "%s.%d%02d%02d%02d%02d%02d",
253                             fileName, timeFields->tm_year + 1900,
254                             timeFields->tm_mon + 1, timeFields->tm_mday,
255                             timeFields->tm_hour, timeFields->tm_min,
256                             timeFields->tm_sec);
257             if (code < 0) {
258                 nextName = NULL;
259                 break;
260             }
261             if (lstat(nextName, &buf) == 0) {
262                 /* Avoid clobbering a log. */
263                 free(nextName);
264                 nextName = NULL;
265             }
266         }
267     } else {
268         code = asprintf(&nextName, "%s.old", fileName);
269         if (code < 0) {
270             nextName = NULL;
271         }
272     }
273     if (nextName != NULL) {
274         rk_rename(fileName, nextName);  /* Don't check the error code. */
275         free(nextName);
276     }
277 }
278
279 static void*
280 DebugOn(void *param)
281 {
282     int loglevel = (intptr_t)param;
283     if (loglevel == 0) {
284         ViceLog(0, ("Reset Debug levels to 0\n"));
285     } else {
286         ViceLog(0, ("Set Debug On level = %d\n", loglevel));
287     }
288     return 0;
289 }                               /*DebugOn */
290
291
292
293 void
294 SetDebug_Signal(int signo)
295 {
296     if (LogLevel > 0) {
297         LogLevel *= 5;
298
299 #if defined(AFS_PTHREAD_ENV)
300         if (LogLevel > 1 && threadNumProgram != NULL &&
301             threadIdLogs == 0) {
302             threadIdLogs = 1;
303         }
304 #endif
305     } else {
306         LogLevel = 1;
307
308 #if defined(AFS_PTHREAD_ENV)
309         if (threadIdLogs == 1)
310             threadIdLogs = 0;
311 #endif
312     }
313 #if defined(AFS_PTHREAD_ENV)
314     DebugOn((void *)(intptr_t)LogLevel);
315 #else /* AFS_PTHREAD_ENV */
316     IOMGR_SoftSig(DebugOn, (void *)(intptr_t)LogLevel);
317 #endif /* AFS_PTHREAD_ENV */
318
319     if (resetSignals) {
320         /* When pthreaded softsig handlers are not in use, some platforms
321          * require this signal handler to be set again. */
322         (void)signal(signo, SetDebug_Signal);
323     }
324 }                               /*SetDebug_Signal */
325
326 void
327 ResetDebug_Signal(int signo)
328 {
329     LogLevel = 0;
330
331 #if defined(AFS_PTHREAD_ENV)
332     DebugOn((void *)(intptr_t)LogLevel);
333 #else /* AFS_PTHREAD_ENV */
334     IOMGR_SoftSig(DebugOn, (void *)(intptr_t)LogLevel);
335 #endif /* AFS_PTHREAD_ENV */
336
337     if (resetSignals) {
338         /* When pthreaded softsig handlers are not in use, some platforms
339          * require this signal handler to be set again. */
340         (void)signal(signo, ResetDebug_Signal);
341     }
342 #if defined(AFS_PTHREAD_ENV)
343     if (threadIdLogs == 1)
344         threadIdLogs = 0;
345 #endif
346     if (mrafsStyleLogs) {
347         RotateLogFile();
348     }
349 }                               /*ResetDebug_Signal */
350
351
352 #ifdef AFS_PTHREAD_ENV
353 /*!
354  * Register pthread-safe signal handlers for server log management.
355  *
356  * \note opr_softsig_Init() must be called before this function.
357  */
358 void
359 SetupLogSoftSignals(void)
360 {
361     opr_softsig_Register(SIGHUP, ResetDebug_Signal);
362     opr_softsig_Register(SIGTSTP, SetDebug_Signal);
363 #ifndef AFS_NT40_ENV
364     (void)signal(SIGPIPE, SIG_IGN);
365 #endif
366 }
367 #endif /* AFS_PTHREAD_ENV */
368
369 /*!
370  * Register signal handlers for server log management.
371  *
372  * \note This function is deprecated and should not be used
373  *       in new code. This function should be removed when
374  *       all the servers have been converted to pthreads
375  *       and lwp has been removed.
376  */
377 void
378 SetupLogSignals(void)
379 {
380     resetSignals = 1;
381     (void)signal(SIGHUP, ResetDebug_Signal);
382     /* Note that we cannot use SIGUSR1 -- Linux stole it for pthreads! */
383     (void)signal(SIGTSTP, SetDebug_Signal);
384 #ifndef AFS_NT40_ENV
385     (void)signal(SIGPIPE, SIG_IGN);
386 #endif
387 }
388
389 #if defined(AFS_PTHREAD_ENV)
390 static void
391 InitServerLogMutex(void)
392 {
393     opr_Verify(pthread_mutex_init(&serverLogMutex, NULL) == 0);
394 }
395 #endif /* AFS_PTHREAD_ENV */
396
397 /*!
398  * Redirect stdout and stderr to the log file.
399  *
400  * \note Call directly after opening the log file.
401  *
402  * \param[in] fileName  log file name
403  */
404 static void
405 RedirectStdStreams(const char *fileName)
406 {
407     if (freopen(fileName, "a", stdout) == NULL)
408         ; /* don't care */
409     if (freopen(fileName, "a", stderr) != NULL) {
410 #ifdef HAVE_SETVBUF
411         setvbuf(stderr, NULL, _IONBF, 0);
412 #else
413         setbuf(stderr, NULL);
414 #endif
415     }
416 }
417
418 int
419 OpenLog(const char *fileName)
420 {
421     /*
422      * This function should allow various libraries that inconsistently
423      * use stdout/stderr to all go to the same place
424      */
425     int tempfd;
426     int flags = O_WRONLY | O_TRUNC | O_CREAT | O_APPEND;
427
428 #if defined(AFS_PTHREAD_ENV)
429     opr_Verify(pthread_once(&serverLogOnce, InitServerLogMutex) == 0);
430 #endif /* AFS_PTHREAD_ENV */
431
432 #ifndef AFS_NT40_ENV
433     if (serverLogSyslog) {
434         openlog(serverLogSyslogTag, LOG_PID, serverLogSyslogFacility);
435         return (0);
436     }
437 #endif
438
439     opr_Assert(fileName != NULL);
440
441     if (IsFIFO(fileName)) {
442         /* Support named pipes as logs by not rotating them. */
443         flags |= O_NONBLOCK;
444     } else {
445         RenameLogFile(fileName);
446     }
447
448     tempfd = open(fileName, flags, 0666);
449     if (tempfd < 0) {
450         printf("Unable to open log file %s\n", fileName);
451         return -1;
452     }
453     RedirectStdStreams(fileName);
454
455     /* Save our name for reopening. */
456     free(ourName);
457     ourName = strdup(fileName);
458     opr_Assert(ourName != NULL);
459
460     serverLogFD = tempfd;
461
462     return 0;
463 }                               /*OpenLog */
464
465 /*!
466  * Reopen the log file descriptor.
467  *
468  * Reopen the log file descriptor in order to support rotation
469  * of the log files.  Has no effect when logging to the syslog.
470  *
471  * \returns  0 on success
472  */
473 int
474 ReOpenLog(void)
475 {
476     int flags = O_WRONLY | O_APPEND | O_CREAT;
477
478 #if !defined(AFS_NT40_ENV)
479     if (serverLogSyslog) {
480         return 0;
481     }
482 #endif
483
484     LOCK_SERVERLOG();
485     if (ourName == NULL) {
486         UNLOCK_SERVERLOG();
487         return -1;
488     }
489     if (IsFIFO(ourName)) {
490         flags |= O_NONBLOCK;
491     }
492     if (serverLogFD >= 0)
493         close(serverLogFD);
494     serverLogFD = open(ourName, flags, 0666);
495     if (serverLogFD >= 0) {
496         RedirectStdStreams(ourName);
497     }
498     UNLOCK_SERVERLOG();
499     return serverLogFD < 0 ? -1 : 0;
500 }
501
502 /*!
503  * Rotate the log file by renaming then truncating.
504  */
505 static void
506 RotateLogFile(void)
507 {
508     LOCK_SERVERLOG();
509     if (ourName != NULL) {
510         if (serverLogFD >= 0) {
511             close(serverLogFD);
512             serverLogFD = -1;
513         }
514         OpenLog(ourName);
515     }
516     UNLOCK_SERVERLOG();
517 }
518
519 /*!
520  * Close the server log file.
521  *
522  * \note Must be preceeded by OpenLog().
523  */
524 void
525 CloseLog(void)
526 {
527     LOCK_SERVERLOG();
528 #ifndef AFS_NT40_ENV
529     if (serverLogSyslog) {
530         closelog();
531     } else
532 #endif
533     {
534         if (serverLogFD >= 0) {
535             close(serverLogFD);
536             serverLogFD = -1;
537         }
538         free(ourName);
539         ourName = NULL;
540     }
541     UNLOCK_SERVERLOG();
542 }