Fix locking and cancellation cleanup handling.
[kopensolaris-gnu/glibc.git] / misc / syslog.c
1 /*
2  * Copyright (c) 1983, 1988, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #if defined(LIBC_SCCS) && !defined(lint)
31 static char sccsid[] = "@(#)syslog.c    8.4 (Berkeley) 3/18/94";
32 #endif /* LIBC_SCCS and not lint */
33
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/syslog.h>
37 #include <sys/uio.h>
38 #include <netdb.h>
39
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <paths.h>
43 #include <stdio.h>
44 #include <stdio_ext.h>
45 #include <string.h>
46 #include <time.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <bits/libc-lock.h>
50 #include <signal.h>
51
52 #if __STDC__
53 #include <stdarg.h>
54 #else
55 #include <varargs.h>
56 #endif
57
58 #ifdef USE_IN_LIBIO
59 # include <libio/iolibio.h>
60 # define ftell(s) INTUSE(_IO_ftell) (s)
61 #endif
62
63 static int      LogType = SOCK_DGRAM;   /* type of socket connection */
64 static int      LogFile = -1;           /* fd for log */
65 static int      connected;              /* have done connect */
66 static int      LogStat;                /* status bits, set by openlog() */
67 static const char *LogTag;              /* string to tag the entry with */
68 static int      LogFacility = LOG_USER; /* default facility code */
69 static int      LogMask = 0xff;         /* mask of priorities to be logged */
70 extern char     *__progname;            /* Program name, from crt0. */
71
72 /* Define the lock.  */
73 __libc_lock_define_initialized (static, syslog_lock)
74
75 static void openlog_internal(const char *, int, int) internal_function;
76 static void closelog_internal(void);
77 static void sigpipe_handler (int);
78
79
80 struct cleanup_arg
81 {
82   void *buf;
83   struct sigaction *oldaction;
84 };
85
86 static void
87 cancel_handler (void *ptr)
88 {
89   /* Restore the old signal handler.  */
90   struct cleanup_arg *clarg = (struct cleanup_arg *) ptr;
91
92   if (clarg != NULL && clarg->oldaction != NULL)
93     __sigaction (SIGPIPE, clarg->oldaction, NULL);
94
95   /* Free the lock.  */
96   __libc_lock_unlock (syslog_lock);
97 }
98
99
100 /*
101  * syslog, vsyslog --
102  *      print message on log file; output is intended for syslogd(8).
103  */
104 void
105 #if __STDC__
106 syslog(int pri, const char *fmt, ...)
107 #else
108 syslog(pri, fmt, va_alist)
109         int pri;
110         char *fmt;
111         va_dcl
112 #endif
113 {
114         va_list ap;
115
116 #if __STDC__
117         va_start(ap, fmt);
118 #else
119         va_start(ap);
120 #endif
121         vsyslog(pri, fmt, ap);
122         va_end(ap);
123 }
124 libc_hidden_def (syslog)
125
126 void
127 vsyslog(pri, fmt, ap)
128         int pri;
129         register const char *fmt;
130         va_list ap;
131 {
132         struct tm now_tm;
133         time_t now;
134         int fd;
135         FILE *f;
136         char *buf = 0;
137         size_t bufsize = 0;
138         size_t prioff, msgoff;
139         struct sigaction action, oldaction;
140         int sigpipe;
141         int saved_errno = errno;
142         char failbuf[3 * sizeof (pid_t) + sizeof "out of memory []"];
143
144 #define INTERNALLOG     LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
145         /* Check for invalid bits. */
146         if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
147                 syslog(INTERNALLOG,
148                     "syslog: unknown facility/priority: %x", pri);
149                 pri &= LOG_PRIMASK|LOG_FACMASK;
150         }
151
152         /* Check priority against setlogmask values. */
153         if ((LOG_MASK (LOG_PRI (pri)) & LogMask) == 0)
154                 return;
155
156         /* Set default facility if none specified. */
157         if ((pri & LOG_FACMASK) == 0)
158                 pri |= LogFacility;
159
160         /* Build the message in a memory-buffer stream.  */
161         f = open_memstream (&buf, &bufsize);
162         if (f == NULL)
163           {
164             /* We cannot get a stream.  There is not much we can do but
165                emitting an error messages.  */
166             char numbuf[3 * sizeof (pid_t)];
167             char *nump;
168             char *endp = __stpcpy (failbuf, "out of memory [");
169             pid_t pid = __getpid ();
170
171             nump = numbuf + sizeof (numbuf);
172             /* The PID can never be zero.  */
173             do
174               *--nump = '0' + pid % 10;
175             while ((pid /= 10) != 0);
176
177             endp = __mempcpy (endp, nump, (nump + sizeof (numbuf)) - nump);
178             *endp++ = ']';
179             *endp = '\0';
180             buf = failbuf;
181             bufsize = endp - failbuf;
182             msgoff = 0;
183           }
184         else
185           {
186             __fsetlocking (f, FSETLOCKING_BYCALLER);
187             prioff = fprintf (f, "<%d>", pri);
188             (void) time (&now);
189 #ifdef USE_IN_LIBIO
190             f->_IO_write_ptr += strftime (f->_IO_write_ptr,
191                                           f->_IO_write_end - f->_IO_write_ptr,
192                                           "%h %e %T ",
193                                           __localtime_r (&now, &now_tm));
194 #else
195             f->__bufp += strftime (f->__bufp, f->__put_limit - f->__bufp,
196                                    "%h %e %T ", __localtime_r (&now, &now_tm));
197 #endif
198             msgoff = ftell (f);
199             if (LogTag == NULL)
200               LogTag = __progname;
201             if (LogTag != NULL)
202               fputs_unlocked (LogTag, f);
203             if (LogStat & LOG_PID)
204               fprintf (f, "[%d]", (int) __getpid ());
205             if (LogTag != NULL)
206               {
207                 putc_unlocked (':', f);
208                 putc_unlocked (' ', f);
209               }
210
211             /* Restore errno for %m format.  */
212             __set_errno (saved_errno);
213
214             /* We have the header.  Print the user's format into the
215                buffer.  */
216             vfprintf (f, fmt, ap);
217
218             /* Close the memory stream; this will finalize the data
219                into a malloc'd buffer in BUF.  */
220             fclose (f);
221           }
222
223         /* Output to stderr if requested. */
224         if (LogStat & LOG_PERROR) {
225                 struct iovec iov[2];
226                 register struct iovec *v = iov;
227
228                 v->iov_base = buf + msgoff;
229                 v->iov_len = bufsize - msgoff;
230                 /* Append a newline if necessary.  */
231                 if (buf[bufsize - 1] != '\n')
232                   {
233                     ++v;
234                     v->iov_base = (char *) "\n";
235                     v->iov_len = 1;
236                   }
237
238                 __libc_cleanup_push (free, buf);
239
240                 /* writev is a cancellation point.  */
241                 (void)__writev(STDERR_FILENO, iov, v - iov + 1);
242
243                 __libc_cleanup_pop (0);
244         }
245
246         /* Prepare for multiple users.  We have to take care: open and
247            write are cancellation points.  */
248         struct cleanup_arg clarg;
249         clarg.buf = buf;
250         clarg.oldaction = NULL;
251         __libc_cleanup_push (cancel_handler, &clarg);
252         __libc_lock_lock (syslog_lock);
253
254         /* Prepare for a broken connection.  */
255         memset (&action, 0, sizeof (action));
256         action.sa_handler = sigpipe_handler;
257         sigemptyset (&action.sa_mask);
258         sigpipe = __sigaction (SIGPIPE, &action, &oldaction);
259         if (sigpipe == 0)
260           clarg.oldaction = &oldaction;
261
262         /* Get connected, output the message to the local logger. */
263         if (!connected)
264                 openlog_internal(LogTag, LogStat | LOG_NDELAY, 0);
265
266         /* If we have a SOCK_STREAM connection, also send ASCII NUL as
267            a record terminator.  */
268         if (LogType == SOCK_STREAM)
269           ++bufsize;
270
271         if (!connected || __send(LogFile, buf, bufsize, 0) < 0)
272           {
273             if (connected)
274               {
275                 /* Try to reopen the syslog connection.  Maybe it went
276                    down.  */
277                 closelog_internal ();
278                 openlog_internal(LogTag, LogStat | LOG_NDELAY, 0);
279               }
280
281             if (!connected || __send(LogFile, buf, bufsize, 0) < 0)
282               {
283                 closelog_internal ();   /* attempt re-open next time */
284                 /*
285                  * Output the message to the console; don't worry
286                  * about blocking, if console blocks everything will.
287                  * Make sure the error reported is the one from the
288                  * syslogd failure.
289                  */
290                 if (LogStat & LOG_CONS &&
291                     (fd = __open(_PATH_CONSOLE, O_WRONLY|O_NOCTTY, 0)) >= 0)
292                   {
293                     dprintf (fd, "%s\r\n", buf + msgoff);
294                     (void)__close(fd);
295                   }
296               }
297           }
298
299         if (sigpipe == 0)
300                 __sigaction (SIGPIPE, &oldaction, (struct sigaction *) NULL);
301
302         /* End of critical section.  */
303         __libc_cleanup_pop (0);
304         __libc_lock_unlock (syslog_lock);
305
306         free (buf);
307 }
308 libc_hidden_def (vsyslog)
309
310 static struct sockaddr SyslogAddr;      /* AF_UNIX address of local logger */
311
312
313 static void
314 internal_function
315 openlog_internal(const char *ident, int logstat, int logfac)
316 {
317         if (ident != NULL)
318                 LogTag = ident;
319         LogStat = logstat;
320         if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
321                 LogFacility = logfac;
322
323         while (1) {
324                 if (LogFile == -1) {
325                         SyslogAddr.sa_family = AF_UNIX;
326                         (void)strncpy(SyslogAddr.sa_data, _PATH_LOG,
327                                       sizeof(SyslogAddr.sa_data));
328                         if (LogStat & LOG_NDELAY) {
329                                 if ((LogFile = __socket(AF_UNIX, LogType, 0))
330                                     == -1)
331                                         return;
332                                 (void)__fcntl(LogFile, F_SETFD, 1);
333                         }
334                 }
335                 if (LogFile != -1 && !connected)
336                 {
337                         int old_errno = errno;
338                         if (__connect(LogFile, &SyslogAddr, sizeof(SyslogAddr))
339                             == -1)
340                         {
341                                 int saved_errno = errno;
342                                 int fd = LogFile;
343                                 LogFile = -1;
344                                 (void)__close(fd);
345                                 if (LogType == SOCK_DGRAM
346                                     && saved_errno == EPROTOTYPE)
347                                 {
348                                         /* retry with next SOCK_STREAM: */
349                                         LogType = SOCK_STREAM;
350                                         __set_errno (old_errno);
351                                         continue;
352                                 }
353                         } else
354                                 connected = 1;
355                 }
356                 break;
357         }
358 }
359
360 void
361 openlog (const char *ident, int logstat, int logfac)
362 {
363   /* Protect against multiple users and cancellation.  */
364   __libc_cleanup_push (cancel_handler, NULL);
365   __libc_lock_lock (syslog_lock);
366
367   openlog_internal (ident, logstat, logfac);
368
369   __libc_cleanup_pop (1);
370 }
371
372 static void
373 sigpipe_handler (int signo)
374 {
375   closelog_internal ();
376 }
377
378 static void
379 closelog_internal()
380 {
381   if (!connected)
382     return;
383
384   __close (LogFile);
385   LogFile = -1;
386   connected = 0;
387 }
388
389 void
390 closelog ()
391 {
392   /* Protect against multiple users and cancellation.  */
393   __libc_cleanup_push (cancel_handler, NULL);
394   __libc_lock_lock (syslog_lock);
395
396   closelog_internal ();
397   LogTag = NULL;
398   LogType = SOCK_DGRAM; /* this is the default */
399
400   /* Free the lock.  */
401   __libc_cleanup_pop (1);
402 }
403
404 /* setlogmask -- set the log mask level */
405 int
406 setlogmask(pmask)
407         int pmask;
408 {
409         int omask;
410
411         omask = LogMask;
412         if (pmask != 0)
413                 LogMask = pmask;
414         return (omask);
415 }