OSDN Git Service

794c0c17fe6638943088dcfc82457fc62c831bfa
[uclinux-h8/uClibc.git] / libc / misc / syslog / 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  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 /*
35  * SYSLOG -- print message on log file
36  *
37  * This routine looks a lot like printf, except that it outputs to the
38  * log file instead of the standard output.  Also:
39  *      adds a timestamp,
40  *      prints the module name in front of the message,
41  *      has some other formatting types (or will sometime),
42  *      adds a newline on the end of the message.
43  *
44  * The output of this routine is intended to be read by syslogd(8).
45  *
46  * Author: Eric Allman
47  * Modified to use UNIX domain IPC by Ralph Campbell
48  * Patched March 12, 1996 by A. Ian Vogelesang <vogelesang@hdshq.com>
49  *  - to correct the handling of message & format string truncation,
50  *  - to visibly tag truncated records to facilitate
51  *    investigation of such Bad Things with grep, and,
52  *  - to correct the handling of case where "write"
53  *    returns after writing only part of the message.
54  * Rewritten by Martin Mares <mj@atrey.karlin.mff.cuni.cz> on May 14, 1997
55  *  - better buffer overrun checks.
56  *  - special handling of "%m" removed as we use GNU sprintf which handles
57  *    it automatically.
58  *  - Major code cleanup.
59  */
60
61 #define __FORCE_GLIBC
62 #include <features.h>
63 #include <sys/types.h>
64 #include <sys/socket.h>
65 #include <sys/file.h>
66 #include <sys/signal.h>
67 #include <sys/syslog.h>
68
69 #include <sys/uio.h>
70 #include <sys/wait.h>
71 #include <netdb.h>
72 #include <string.h>
73 #include <time.h>
74 #include <unistd.h>
75 #include <errno.h>
76 #include <stdarg.h>
77 #include <paths.h>
78 #include <stdio.h>
79 #include <ctype.h>
80 #include <signal.h>
81
82
83
84 #include <bits/uClibc_mutex.h>
85 __UCLIBC_MUTEX_STATIC(mylock, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP);
86
87
88 static int       LogFile = -1;          /* fd for log */
89 static smalluint connected;             /* have done connect */
90 /* all bits in option argument for openlog() fit in 8 bits */
91 static smalluint LogStat = 0;           /* status bits, set by openlog() */
92 static const char *LogTag = "syslog";   /* string to tag the entry with */
93 /* this fits in 8 bits too (LOG_LOCAL7 = 23<<3 = 184),
94  * but NB: LOG_FACMASK is bigger (= 0x03f8 = 127<<3) for some strange reason.
95  * Oh well. */
96 static int       LogFacility = LOG_USER;/* default facility code */
97 /* bits mask of priorities (eight prios - 8 bits is enough) */
98 static smalluint LogMask = 0xff;        /* mask of priorities to be logged */
99 /* AF_UNIX address of local logger (we use struct sockaddr
100  * instead of struct sockaddr_un since "/dev/log" is small enough) */
101 static const struct sockaddr SyslogAddr = {
102         .sa_family = AF_UNIX, /* sa_family_t (usually a short) */
103         .sa_data = _PATH_LOG  /* char [14] */
104 };
105
106 static void
107 closelog_intern(int sig)
108 {
109         /* mylock must be held by the caller */
110         if (LogFile != -1) {
111                 (void) close(LogFile);
112         }
113         LogFile = -1;
114         connected = 0;
115         if (sig == 0) { /* called from closelog()? - reset to defaults */
116                 LogStat = 0;
117                 LogTag = "syslog";
118                 LogFacility = LOG_USER;
119                 LogMask = 0xff;
120         }
121 }
122
123 /*
124  * OPENLOG -- open system log
125  */
126 void
127 openlog(const char *ident, int logstat, int logfac)
128 {
129         int logType = SOCK_DGRAM;
130
131         __UCLIBC_MUTEX_LOCK(mylock);
132
133         if (ident != NULL)
134                 LogTag = ident;
135         LogStat = logstat;
136         if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
137                 LogFacility = logfac;
138         if (LogFile == -1) {
139 retry:
140                 if (LogStat & LOG_NDELAY) {
141                         if ((LogFile = socket(AF_UNIX, logType, 0)) == -1) {
142                                 goto DONE;
143                         }
144                         fcntl(LogFile, F_SETFD, FD_CLOEXEC);
145                         /* We don't want to block if e.g. syslogd is SIGSTOPed */
146                         fcntl(LogFile, F_SETFL, O_NONBLOCK | fcntl(LogFile, F_GETFL));
147                 }
148         }
149
150         if (LogFile != -1 && !connected) {
151                 if (connect(LogFile, &SyslogAddr, sizeof(SyslogAddr)) != -1) {
152                         connected = 1;
153                 } else {
154                         if (LogFile != -1) {
155                                 close(LogFile);
156                                 LogFile = -1;
157                         }
158                         if (logType == SOCK_DGRAM) {
159                                 logType = SOCK_STREAM;
160                                 goto retry;
161                         }
162                 }
163         }
164
165 DONE:
166         __UCLIBC_MUTEX_UNLOCK(mylock);
167 }
168 libc_hidden_def(openlog)
169
170 /*
171  * syslog, vsyslog --
172  *     print message on log file; output is intended for syslogd(8).
173  */
174 void
175 vsyslog(int pri, const char *fmt, va_list ap)
176 {
177         register char *p;
178         char *last_chr, *head_end, *end, *stdp;
179         time_t now;
180         int fd, saved_errno;
181         int rc;
182         char tbuf[1024]; /* syslogd is unable to handle longer messages */
183         struct sigaction action, oldaction;
184
185         memset(&action, 0, sizeof(action));
186         action.sa_handler = closelog_intern;
187         sigaction(SIGPIPE, &action, &oldaction);
188
189         saved_errno = errno;
190
191         __UCLIBC_MUTEX_LOCK(mylock);
192
193         /* See if we should just throw out this message. */
194         if (!(LogMask & LOG_MASK(LOG_PRI(pri))) || (pri &~ (LOG_PRIMASK|LOG_FACMASK)))
195                 goto getout;
196         if (LogFile < 0 || !connected)
197                 openlog(LogTag, LogStat | LOG_NDELAY, 0);
198
199         /* Set default facility if none specified. */
200         if ((pri & LOG_FACMASK) == 0)
201                 pri |= LogFacility;
202
203         /* Build the message. We know the starting part of the message can take
204          * no longer than 64 characters plus length of the LogTag. So it's
205          * safe to test only LogTag and use normal sprintf everywhere else.
206          */
207         (void)time(&now);
208         stdp = p = tbuf + sprintf(tbuf, "<%d>%.15s ", pri, ctime(&now) + 4);
209         if (LogTag) {
210                 if (strlen(LogTag) < sizeof(tbuf) - 64)
211                         p += sprintf(p, "%s", LogTag);
212                 else
213                         p += sprintf(p, "<BUFFER OVERRUN ATTEMPT>");
214         }
215         if (LogStat & LOG_PID)
216                 p += sprintf(p, "[%d]", getpid());
217         if (LogTag) {
218                 *p++ = ':';
219                 *p++ = ' ';
220         }
221         head_end = p;
222
223         /* We format the rest of the message. If the buffer becomes full, we mark
224          * the message as truncated. Note that we require at least 2 free bytes
225          * in the buffer as we might want to add "\r\n" there.
226          */
227
228         end = tbuf + sizeof(tbuf) - 1;
229         __set_errno(saved_errno);
230         p += vsnprintf(p, end - p, fmt, ap);
231         if (p >= end || p < head_end) { /* Returned -1 in case of error... */
232                 static const char truncate_msg[12] = "[truncated] "; /* no NUL! */
233                 memmove(head_end + sizeof(truncate_msg), head_end,
234                                 end - head_end - sizeof(truncate_msg));
235                 memcpy(head_end, truncate_msg, sizeof(truncate_msg));
236                 if (p < head_end) {
237                         while (p < end && *p) {
238                                 p++;
239                         }
240                 }
241                 else {
242                         p = end - 1;
243                 }
244
245         }
246         last_chr = p;
247
248         /* Output to stderr if requested. */
249         if (LogStat & LOG_PERROR) {
250                 *last_chr = '\n';
251                 (void)write(STDERR_FILENO, stdp, last_chr - stdp + 1);
252         }
253
254         /* Output the message to the local logger using NUL as a message delimiter. */
255         p = tbuf;
256         *last_chr = 0;
257         if (LogFile >= 0) {
258                 do {
259                         rc = write(LogFile, p, last_chr + 1 - p);
260                         if (rc < 0) {
261                                 /* I don't think looping forever on EAGAIN is a good idea.
262                                  * Imagine that syslogd is SIGSTOPed... */
263                                 if (/* (errno != EAGAIN) && */ (errno != EINTR)) {
264                                         closelog_intern(1); /* 1: do not reset LogXXX globals to default */
265                                         goto write_err;
266                                 }
267                                 rc = 0;
268                         }
269                         p += rc;
270                 } while (p <= last_chr);
271                 goto getout;
272         }
273
274  write_err:
275         /*
276          * Output the message to the console; don't worry about blocking,
277          * if console blocks everything will.  Make sure the error reported
278          * is the one from the syslogd failure.
279          */
280         /* should mode be O_WRONLY | O_NOCTTY? -- Uli */
281         /* yes, but in Linux "/dev/console" never becomes ctty anyway -- vda */
282         if ((LogStat & LOG_CONS) &&
283             (fd = open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY)) >= 0) {
284                 p = strchr(tbuf, '>') + 1;
285                 last_chr[0] = '\r';
286                 last_chr[1] = '\n';
287                 (void)write(fd, p, last_chr - p + 2);
288                 (void)close(fd);
289         }
290
291 getout:
292         __UCLIBC_MUTEX_UNLOCK(mylock);
293         sigaction(SIGPIPE, &oldaction, NULL);
294 }
295 libc_hidden_def(vsyslog)
296
297 void
298 syslog(int pri, const char *fmt, ...)
299 {
300         va_list ap;
301
302         va_start(ap, fmt);
303         vsyslog(pri, fmt, ap);
304         va_end(ap);
305 }
306 libc_hidden_def(syslog)
307
308 /*
309  * CLOSELOG -- close the system log
310  */
311 void
312 closelog(void)
313 {
314         __UCLIBC_MUTEX_LOCK(mylock);
315         closelog_intern(0); /* 0: reset LogXXX globals to default */
316         __UCLIBC_MUTEX_UNLOCK(mylock);
317 }
318 libc_hidden_def(closelog)
319
320 /* setlogmask -- set the log mask level */
321 int setlogmask(int pmask)
322 {
323         int omask;
324
325         omask = LogMask;
326         if (pmask != 0) {
327                 __UCLIBC_MUTEX_LOCK(mylock);
328                 LogMask = pmask;
329                 __UCLIBC_MUTEX_UNLOCK(mylock);
330         }
331         return omask;
332 }