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