blob: 339df680adcda200035ec30a326502de4c8df335 [file] [log] [blame]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001/* $OpenBSD: syslog.c,v 1.28 2005/08/08 08:05:34 espie Exp $ */
2/*
3 * Copyright (c) 1983, 1988, 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <sys/types.h>
32#include <sys/socket.h>
33#include <sys/uio.h>
34#include <syslog.h>
35#include <sys/un.h>
36#include <netdb.h>
37
38#include <errno.h>
39#include <fcntl.h>
40#include <paths.h>
41#include <stdio.h>
42#include <string.h>
43#include <time.h>
44#include <unistd.h>
45#include <stdarg.h>
46
Elliott Hughesa382a792014-06-09 17:16:19 -070047struct syslog_data {
48 int log_file;
49 int connected;
50 int opened;
51 int log_stat;
52 const char* log_tag;
53 int log_fac;
54 int log_mask;
55};
56
57#define SYSLOG_DATA_INIT {-1, 0, 0, 0, (const char *)0, LOG_USER, 0xff}
58
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080059static struct syslog_data sdata = SYSLOG_DATA_INIT;
60
Elliott Hughese4ccf5a2013-02-07 12:06:44 -080061extern const char *__progname; /* Program name, from crt0. */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080062
63static void disconnectlog_r(struct syslog_data *); /* disconnect from syslogd */
64static void connectlog_r(struct syslog_data *); /* (re)connect to syslogd */
65
Elliott Hughesa382a792014-06-09 17:16:19 -070066#if defined(__LP64__)
67#define SYSLOG_R_VISIBILITY static
68#else
69#define SYSLOG_R_VISIBILITY extern
70#endif
71
72SYSLOG_R_VISIBILITY void closelog_r(struct syslog_data*);
73SYSLOG_R_VISIBILITY void openlog_r(const char*, int, int, struct syslog_data*);
74SYSLOG_R_VISIBILITY int setlogmask_r(int, struct syslog_data*);
75SYSLOG_R_VISIBILITY void syslog_r(int, struct syslog_data*, const char*, ...) __printflike(3, 4);
76SYSLOG_R_VISIBILITY void vsyslog_r(int, struct syslog_data*, const char*, va_list) __printflike(3, 0);
77
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080078/*
79 * syslog, vsyslog --
80 * print message on log file; output is intended for syslogd(8).
81 */
82void
83syslog(int pri, const char *fmt, ...)
84{
85 va_list ap;
86
87 va_start(ap, fmt);
88 vsyslog(pri, fmt, ap);
89 va_end(ap);
90}
91
92void
93vsyslog(int pri, const char *fmt, va_list ap)
94{
95 vsyslog_r(pri, &sdata, fmt, ap);
96}
97
98void
99openlog(const char *ident, int logstat, int logfac)
100{
101 openlog_r(ident, logstat, logfac, &sdata);
102}
103
104void
105closelog(void)
106{
107 closelog_r(&sdata);
108}
109
110/* setlogmask -- set the log mask level */
111int
112setlogmask(int pmask)
113{
114 return setlogmask_r(pmask, &sdata);
115}
116
117/* Reentrant version of syslog, i.e. syslog_r() */
118
119void
120syslog_r(int pri, struct syslog_data *data, const char *fmt, ...)
121{
122 va_list ap;
123
124 va_start(ap, fmt);
125 vsyslog_r(pri, data, fmt, ap);
126 va_end(ap);
127}
128
129void
130vsyslog_r(int pri, struct syslog_data *data, const char *fmt, va_list ap)
131{
132 int cnt;
133 char ch, *p, *t;
134 time_t now;
135 int fd, saved_errno, error;
136#define TBUF_LEN 2048
137#define FMT_LEN 1024
138 char *stdp = NULL, tbuf[TBUF_LEN], fmt_cpy[FMT_LEN];
139 int tbuf_left, fmt_left, prlen;
140
141#define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
142 /* Check for invalid bits. */
143 if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
144 if (data == &sdata) {
145 syslog(INTERNALLOG,
146 "syslog: unknown facility/priority: %x", pri);
147 } else {
148 syslog_r(INTERNALLOG, data,
149 "syslog_r: unknown facility/priority: %x", pri);
150 }
151 pri &= LOG_PRIMASK|LOG_FACMASK;
152 }
153
154 /* Check priority against setlogmask values. */
155 if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask))
156 return;
157
158 saved_errno = errno;
159
160 /* Set default facility if none specified. */
161 if ((pri & LOG_FACMASK) == 0)
162 pri |= data->log_fac;
163
164 /* If we have been called through syslog(), no need for reentrancy. */
165 if (data == &sdata)
166 (void)time(&now);
167
168 p = tbuf;
169 tbuf_left = TBUF_LEN;
170
171#define DEC() \
172 do { \
173 if (prlen < 0) \
174 prlen = 0; \
175 if (prlen >= tbuf_left) \
176 prlen = tbuf_left - 1; \
177 p += prlen; \
178 tbuf_left -= prlen; \
179 } while (0)
180
181 prlen = snprintf(p, tbuf_left, "<%d>", pri);
182 DEC();
183
Elliott Hughesa382a792014-06-09 17:16:19 -0700184 /*
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800185 * syslogd will expand time automagically for reentrant case, and
186 * for normal case, just do like before
187 */
188 if (data == &sdata) {
189 prlen = strftime(p, tbuf_left, "%h %e %T ", localtime(&now));
190 DEC();
191 }
192
193 if (data->log_stat & LOG_PERROR)
194 stdp = p;
195 if (data->log_tag == NULL)
196 data->log_tag = __progname;
197 if (data->log_tag != NULL) {
198 prlen = snprintf(p, tbuf_left, "%s", data->log_tag);
199 DEC();
200 }
201 if (data->log_stat & LOG_PID) {
202 prlen = snprintf(p, tbuf_left, "[%ld]", (long)getpid());
203 DEC();
204 }
205 if (data->log_tag != NULL) {
206 if (tbuf_left > 1) {
207 *p++ = ':';
208 tbuf_left--;
209 }
210 if (tbuf_left > 1) {
211 *p++ = ' ';
212 tbuf_left--;
213 }
214 }
215
216 /* strerror() is not reentrant */
217
218 for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt); ++fmt) {
219 if (ch == '%' && fmt[1] == 'm') {
220 ++fmt;
221 if (data == &sdata) {
222 prlen = snprintf(t, fmt_left, "%s",
Elliott Hughesa382a792014-06-09 17:16:19 -0700223 strerror(saved_errno));
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800224 } else {
225 prlen = snprintf(t, fmt_left, "Error %d",
Elliott Hughesa382a792014-06-09 17:16:19 -0700226 saved_errno);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800227 }
228 if (prlen < 0)
229 prlen = 0;
230 if (prlen >= fmt_left)
231 prlen = fmt_left - 1;
232 t += prlen;
233 fmt_left -= prlen;
234 } else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
235 *t++ = '%';
236 *t++ = '%';
237 fmt++;
238 fmt_left -= 2;
239 } else {
240 if (fmt_left > 1) {
241 *t++ = ch;
242 fmt_left--;
243 }
244 }
245 }
246 *t = '\0';
247
248 prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
249 DEC();
250 cnt = p - tbuf;
251
252 /* Output to stderr if requested. */
253 if (data->log_stat & LOG_PERROR) {
254 struct iovec iov[2];
255
256 iov[0].iov_base = stdp;
257 iov[0].iov_len = cnt - (stdp - tbuf);
258 iov[1].iov_base = "\n";
259 iov[1].iov_len = 1;
260 (void)writev(STDERR_FILENO, iov, 2);
261 }
262
263 /* Get connected, output the message to the local logger. */
264 if (!data->opened)
265 openlog_r(data->log_tag, data->log_stat, 0, data);
266 connectlog_r(data);
267
268 /*
269 * If the send() failed, there are two likely scenarios:
270 * 1) syslogd was restarted
271 * 2) /dev/log is out of socket buffer space
272 * We attempt to reconnect to /dev/log to take care of
273 * case #1 and keep send()ing data to cover case #2
274 * to give syslogd a chance to empty its socket buffer.
275 */
276 if ((error = send(data->log_file, tbuf, cnt, 0)) < 0) {
277 if (errno != ENOBUFS) {
278 disconnectlog_r(data);
279 connectlog_r(data);
280 }
281 do {
282 usleep(1);
283 if ((error = send(data->log_file, tbuf, cnt, 0)) >= 0)
284 break;
285 } while (errno == ENOBUFS);
286 }
287
288 /*
289 * Output the message to the console; try not to block
290 * as a blocking console should not stop other processes.
291 * Make sure the error reported is the one from the syslogd failure.
292 */
293 if (error == -1 && (data->log_stat & LOG_CONS) &&
294 (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) {
295 struct iovec iov[2];
Elliott Hughesa382a792014-06-09 17:16:19 -0700296
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800297 p = strchr(tbuf, '>') + 1;
298 iov[0].iov_base = p;
299 iov[0].iov_len = cnt - (p - tbuf);
300 iov[1].iov_base = "\r\n";
301 iov[1].iov_len = 2;
302 (void)writev(fd, iov, 2);
303 (void)close(fd);
304 }
305
306 if (data != &sdata)
307 closelog_r(data);
308}
309
310static void
311disconnectlog_r(struct syslog_data *data)
312{
313 /*
314 * If the user closed the FD and opened another in the same slot,
315 * that's their problem. They should close it before calling on
316 * system services.
317 */
318 if (data->log_file != -1) {
319 close(data->log_file);
320 data->log_file = -1;
321 }
322 data->connected = 0; /* retry connect */
323}
324
325static void
326connectlog_r(struct syslog_data *data)
327{
328 union {
329 struct sockaddr syslogAddr;
330 struct sockaddr_un syslogAddrUn;
331 } u;
332
333#define SyslogAddr u.syslogAddrUn
334
335 if (data->log_file == -1) {
336 if ((data->log_file = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
337 return;
338 (void)fcntl(data->log_file, F_SETFD, 1);
339 }
340 if (data->log_file != -1 && !data->connected) {
341 memset(&SyslogAddr, '\0', sizeof(SyslogAddr));
342#if 0
343 /* BIONIC: no sun_len field to fill on Linux */
344 SyslogAddr.sun_len = sizeof(SyslogAddr);
345#endif
346 SyslogAddr.sun_family = AF_UNIX;
347 strlcpy(SyslogAddr.sun_path, _PATH_LOG,
348 sizeof(SyslogAddr.sun_path));
349 if (connect(data->log_file, &u.syslogAddr,
350 sizeof(SyslogAddr)) == -1) {
351 (void)close(data->log_file);
352 data->log_file = -1;
353 } else
354 data->connected = 1;
355 }
356}
357
358void
359openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data)
360{
361 if (ident != NULL)
362 data->log_tag = ident;
363 data->log_stat = logstat;
364 if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
365 data->log_fac = logfac;
366
367 if (data->log_stat & LOG_NDELAY) /* open immediately */
368 connectlog_r(data);
369
370 data->opened = 1; /* ident and facility has been set */
371}
372
373void
374closelog_r(struct syslog_data *data)
375{
376 (void)close(data->log_file);
377 data->log_file = -1;
378 data->connected = 0;
379 data->log_tag = NULL;
380}
381
382/* setlogmask -- set the log mask level */
383int
384setlogmask_r(int pmask, struct syslog_data *data)
385{
386 int omask;
387
388 omask = data->log_mask;
389 if (pmask != 0)
390 data->log_mask = pmask;
391 return (omask);
392}