]>
Commit | Line | Data |
---|---|---|
790aa1f2 UD |
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 | ||
0cc15f45 | 34 | #include <libio/libioP.h> |
790aa1f2 | 35 | #include <paths.h> |
0cc15f45 AZ |
36 | #include <stdarg.h> |
37 | #include <stdlib.h> | |
790aa1f2 UD |
38 | #include <stdio.h> |
39 | #include <stdio_ext.h> | |
0cc15f45 AZ |
40 | #include <sys/socket.h> |
41 | #include <sys/uio.h> | |
42 | #include <sys/un.h> | |
43 | #include <syslog.h> | |
ddf542da | 44 | #include <limits.h> |
37233df9 | 45 | |
0cc15f45 AZ |
46 | static int LogType = SOCK_DGRAM; /* type of socket connection */ |
47 | static int LogFile = -1; /* fd for log */ | |
48 | static bool connected; /* have done connect */ | |
49 | static int LogStat; /* status bits, set by openlog() */ | |
790aa1f2 | 50 | static const char *LogTag; /* string to tag the entry with */ |
0cc15f45 AZ |
51 | static int LogFacility = LOG_USER; /* default facility code */ |
52 | static int LogMask = 0xff; /* mask of priorities to be logged */ | |
53 | extern char *__progname; /* Program name, from crt0. */ | |
790aa1f2 UD |
54 | |
55 | /* Define the lock. */ | |
56 | __libc_lock_define_initialized (static, syslog_lock) | |
0cc15f45 AZ |
57 | static void openlog_internal (const char *, int, int); |
58 | static void closelog_internal (void); | |
790aa1f2 UD |
59 | |
60 | struct cleanup_arg | |
61 | { | |
62 | void *buf; | |
63 | struct sigaction *oldaction; | |
64 | }; | |
65 | ||
66 | static void | |
67 | cancel_handler (void *ptr) | |
68 | { | |
790aa1f2 UD |
69 | /* Restore the old signal handler. */ |
70 | struct cleanup_arg *clarg = (struct cleanup_arg *) ptr; | |
71 | ||
c4e4b2e1 | 72 | if (clarg != NULL) |
ded3cef3 AZ |
73 | /* Free the memstream buffer, */ |
74 | free (clarg->buf); | |
c4e4b2e1 | 75 | |
790aa1f2 UD |
76 | /* Free the lock. */ |
77 | __libc_lock_unlock (syslog_lock); | |
78 | } | |
79 | ||
80 | ||
81 | /* | |
82 | * syslog, vsyslog -- | |
83 | * print message on log file; output is intended for syslogd(8). | |
84 | */ | |
85 | void | |
0cc15f45 | 86 | __syslog (int pri, const char *fmt, ...) |
790aa1f2 | 87 | { |
0cc15f45 | 88 | va_list ap; |
790aa1f2 | 89 | |
0cc15f45 AZ |
90 | va_start (ap, fmt); |
91 | __vsyslog_internal (pri, fmt, ap, 0); | |
92 | va_end (ap); | |
790aa1f2 | 93 | } |
c6251f03 RM |
94 | ldbl_hidden_def (__syslog, syslog) |
95 | ldbl_strong_alias (__syslog, syslog) | |
790aa1f2 | 96 | |
124fc732 | 97 | void |
0cc15f45 | 98 | __vsyslog (int pri, const char *fmt, va_list ap) |
124fc732 | 99 | { |
0cc15f45 | 100 | __vsyslog_internal (pri, fmt, ap, 0); |
124fc732 ZW |
101 | } |
102 | ldbl_weak_alias (__vsyslog, vsyslog) | |
103 | ||
790aa1f2 | 104 | void |
ba745eff | 105 | ___syslog_chk (int pri, int flag, const char *fmt, ...) |
29c21e49 | 106 | { |
0cc15f45 | 107 | va_list ap; |
29c21e49 | 108 | |
0cc15f45 AZ |
109 | va_start (ap, fmt); |
110 | __vsyslog_internal (pri, fmt, ap, (flag > 0) ? PRINTF_FORTIFY : 0); | |
111 | va_end (ap); | |
29c21e49 | 112 | } |
ba745eff FB |
113 | ldbl_hidden_def (___syslog_chk, __syslog_chk) |
114 | ldbl_strong_alias (___syslog_chk, __syslog_chk) | |
29c21e49 UD |
115 | |
116 | void | |
0cc15f45 | 117 | __vsyslog_chk (int pri, int flag, const char *fmt, va_list ap) |
124fc732 | 118 | { |
0cc15f45 | 119 | __vsyslog_internal (pri, fmt, ap, (flag > 0) ? PRINTF_FORTIFY : 0); |
124fc732 ZW |
120 | } |
121 | ||
122 | void | |
0cc15f45 AZ |
123 | __vsyslog_internal (int pri, const char *fmt, va_list ap, |
124 | unsigned int mode_flags) | |
790aa1f2 | 125 | { |
a583b6ad AZ |
126 | /* Try to use a static buffer as an optimization. */ |
127 | char bufs[1024]; | |
6bd0e4ef AS |
128 | char *buf = bufs; |
129 | size_t bufsize; | |
130 | ||
f9f5c70e | 131 | int msgoff; |
0cc15f45 | 132 | int saved_errno = errno; |
0cc15f45 AZ |
133 | |
134 | #define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID | |
135 | /* Check for invalid bits. */ | |
136 | if (pri & ~(LOG_PRIMASK | LOG_FACMASK)) | |
137 | { | |
138 | syslog (INTERNALLOG, "syslog: unknown facility/priority: %x", pri); | |
139 | pri &= LOG_PRIMASK | LOG_FACMASK; | |
140 | } | |
141 | ||
142 | /* Prepare for multiple users. We have to take care: most syscalls we are | |
143 | using are cancellation points. */ | |
f9f5c70e | 144 | struct cleanup_arg clarg = { NULL, NULL }; |
0cc15f45 AZ |
145 | __libc_cleanup_push (cancel_handler, &clarg); |
146 | __libc_lock_lock (syslog_lock); | |
147 | ||
148 | /* Check priority against setlogmask values. */ | |
149 | if ((LOG_MASK (LOG_PRI (pri)) & LogMask) == 0) | |
150 | goto out; | |
151 | ||
152 | /* Set default facility if none specified. */ | |
153 | if ((pri & LOG_FACMASK) == 0) | |
154 | pri |= LogFacility; | |
155 | ||
a583b6ad AZ |
156 | pid_t pid = LogStat & LOG_PID ? __getpid () : 0; |
157 | ||
158 | /* "%b %e %H:%M:%S " */ | |
159 | char timestamp[sizeof "MMM DD hh:mm:ss "]; | |
ac0d208b | 160 | __time64_t now = time64_now (); |
a583b6ad | 161 | struct tm now_tm; |
ac0d208b AZ |
162 | struct tm *now_tmp = __localtime64_r (&now, &now_tm); |
163 | bool has_ts = now_tmp != NULL; | |
164 | ||
165 | /* In the unlikely case of localtime_r failure (tm_year out of int range) | |
166 | skip the hostname so the message is handled as valid PRI but without | |
167 | TIMESTAMP or invalid TIMESTAMP (which should force the relay to add the | |
168 | timestamp itself). */ | |
169 | if (has_ts) | |
170 | __strftime_l (timestamp, sizeof timestamp, "%h %e %T ", now_tmp, | |
171 | _nl_C_locobj_ptr); | |
a583b6ad AZ |
172 | |
173 | #define SYSLOG_HEADER(__pri, __timestamp, __msgoff, pid) \ | |
45459476 | 174 | "<%d>%s%n%s%s%.0d%s: ", \ |
a583b6ad AZ |
175 | __pri, __timestamp, __msgoff, \ |
176 | LogTag == NULL ? __progname : LogTag, \ | |
177 | "[" + (pid == 0), pid, "]" + (pid == 0) | |
178 | ||
ac0d208b AZ |
179 | #define SYSLOG_HEADER_WITHOUT_TS(__pri, __msgoff) \ |
180 | "<%d>: %n", __pri, __msgoff | |
181 | ||
6bd0e4ef | 182 | int l, vl; |
ac0d208b AZ |
183 | if (has_ts) |
184 | l = __snprintf (bufs, sizeof bufs, | |
185 | SYSLOG_HEADER (pri, timestamp, &msgoff, pid)); | |
186 | else | |
187 | l = __snprintf (bufs, sizeof bufs, | |
188 | SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff)); | |
7e5a0c28 AS |
189 | if (l < 0) |
190 | goto out; | |
6bd0e4ef AS |
191 | |
192 | char *pos; | |
193 | size_t len; | |
194 | ||
7e5a0c28 | 195 | if (l < sizeof bufs) |
0cc15f45 | 196 | { |
6bd0e4ef AS |
197 | /* At this point, there is still a chance that we can print the |
198 | remaining part of the log into bufs and use that. */ | |
199 | pos = bufs + l; | |
200 | len = sizeof (bufs) - l; | |
201 | } | |
202 | else | |
203 | { | |
204 | buf = NULL; | |
205 | /* We already know that bufs is too small to use for this log message. | |
206 | The next vsnprintf into bufs is used only to calculate the total | |
207 | required buffer length. We will discard bufs contents and allocate | |
208 | an appropriately sized buffer later instead. */ | |
209 | pos = bufs; | |
210 | len = sizeof (bufs); | |
211 | } | |
a583b6ad | 212 | |
6bd0e4ef AS |
213 | { |
214 | va_list apc; | |
215 | va_copy (apc, ap); | |
0cc15f45 | 216 | |
6bd0e4ef AS |
217 | /* Restore errno for %m format. */ |
218 | __set_errno (saved_errno); | |
0cc15f45 | 219 | |
6bd0e4ef | 220 | vl = __vsnprintf_internal (pos, len, fmt, apc, mode_flags); |
7e5a0c28 AS |
221 | va_end (apc); |
222 | ||
ddf542da | 223 | if (vl < 0 || vl >= INT_MAX - l) |
7e5a0c28 | 224 | goto out; |
6bd0e4ef | 225 | |
7e5a0c28 | 226 | if (vl >= len) |
6bd0e4ef AS |
227 | buf = NULL; |
228 | ||
229 | bufsize = l + vl; | |
6bd0e4ef | 230 | } |
a583b6ad AZ |
231 | |
232 | if (buf == NULL) | |
f9f5c70e | 233 | { |
52a5be0d | 234 | buf = malloc ((bufsize + 1) * sizeof (char)); |
a583b6ad AZ |
235 | if (buf != NULL) |
236 | { | |
237 | /* Tell the cancellation handler to free this buffer. */ | |
238 | clarg.buf = buf; | |
239 | ||
7e5a0c28 | 240 | int cl; |
ac0d208b | 241 | if (has_ts) |
7e5a0c28 AS |
242 | cl = __snprintf (buf, l + 1, |
243 | SYSLOG_HEADER (pri, timestamp, &msgoff, pid)); | |
ac0d208b | 244 | else |
7e5a0c28 AS |
245 | cl = __snprintf (buf, l + 1, |
246 | SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff)); | |
247 | if (cl != l) | |
248 | goto out; | |
52a5be0d AZ |
249 | |
250 | va_list apc; | |
251 | va_copy (apc, ap); | |
7e5a0c28 AS |
252 | cl = __vsnprintf_internal (buf + l, bufsize - l + 1, fmt, apc, |
253 | mode_flags); | |
52a5be0d | 254 | va_end (apc); |
7e5a0c28 AS |
255 | |
256 | if (cl != vl) | |
257 | goto out; | |
a583b6ad AZ |
258 | } |
259 | else | |
260 | { | |
7e5a0c28 | 261 | int bl; |
a583b6ad | 262 | /* Nothing much to do but emit an error message. */ |
7e5a0c28 AS |
263 | bl = __snprintf (bufs, sizeof bufs, |
264 | "out of memory[%d]", __getpid ()); | |
265 | if (bl < 0 || bl >= sizeof bufs) | |
266 | goto out; | |
267 | ||
268 | bufsize = bl; | |
a583b6ad | 269 | buf = bufs; |
7e5a0c28 | 270 | msgoff = 0; |
a583b6ad | 271 | } |
f9f5c70e | 272 | } |
0cc15f45 AZ |
273 | |
274 | /* Output to stderr if requested. */ | |
275 | if (LogStat & LOG_PERROR) | |
f9f5c70e AZ |
276 | __dprintf (STDERR_FILENO, "%s%s", buf + msgoff, |
277 | "\n" + (buf[bufsize - 1] == '\n')); | |
0cc15f45 AZ |
278 | |
279 | /* Get connected, output the message to the local logger. */ | |
280 | if (!connected) | |
281 | openlog_internal (NULL, LogStat | LOG_NDELAY, LogFacility); | |
282 | ||
283 | /* If we have a SOCK_STREAM connection, also send ASCII NUL as a record | |
284 | terminator. */ | |
285 | if (LogType == SOCK_STREAM) | |
286 | ++bufsize; | |
287 | ||
288 | if (!connected || __send (LogFile, buf, bufsize, MSG_NOSIGNAL) < 0) | |
289 | { | |
290 | if (connected) | |
291 | { | |
292 | /* Try to reopen the syslog connection. Maybe it went down. */ | |
293 | closelog_internal (); | |
294 | openlog_internal (NULL, LogStat | LOG_NDELAY, LogFacility); | |
790aa1f2 UD |
295 | } |
296 | ||
0cc15f45 AZ |
297 | if (!connected || __send (LogFile, buf, bufsize, MSG_NOSIGNAL) < 0) |
298 | { | |
299 | closelog_internal (); /* attempt re-open next time */ | |
300 | /* | |
301 | * Output the message to the console; don't worry | |
302 | * about blocking, if console blocks everything will. | |
303 | * Make sure the error reported is the one from the | |
304 | * syslogd failure. | |
305 | */ | |
f9f5c70e | 306 | int fd; |
0cc15f45 AZ |
307 | if (LogStat & LOG_CONS && |
308 | (fd = __open (_PATH_CONSOLE, O_WRONLY | O_NOCTTY | |
309 | | O_CLOEXEC, 0)) >= 0) | |
310 | { | |
311 | __dprintf (fd, "%s\r\n", buf + msgoff); | |
312 | __close (fd); | |
313 | } | |
314 | } | |
315 | } | |
316 | ||
317 | out: | |
318 | /* End of critical section. */ | |
319 | __libc_cleanup_pop (0); | |
320 | __libc_lock_unlock (syslog_lock); | |
321 | ||
a583b6ad | 322 | if (buf != bufs) |
0cc15f45 | 323 | free (buf); |
790aa1f2 | 324 | } |
790aa1f2 | 325 | |
5ad1a81c AZ |
326 | /* AF_UNIX address of local logger */ |
327 | static const struct sockaddr_un SyslogAddr = | |
328 | { | |
329 | .sun_family = AF_UNIX, | |
330 | .sun_path = _PATH_LOG | |
331 | }; | |
790aa1f2 UD |
332 | |
333 | static void | |
0cc15f45 | 334 | openlog_internal (const char *ident, int logstat, int logfac) |
790aa1f2 | 335 | { |
0cc15f45 AZ |
336 | if (ident != NULL) |
337 | LogTag = ident; | |
338 | LogStat = logstat; | |
339 | if ((logfac & ~LOG_FACMASK) == 0) | |
340 | LogFacility = logfac; | |
341 | ||
342 | int retry = 0; | |
343 | while (retry < 2) | |
344 | { | |
345 | if (LogFile == -1) | |
346 | { | |
347 | if (LogStat & LOG_NDELAY) | |
348 | { | |
349 | LogFile = __socket (AF_UNIX, LogType | SOCK_CLOEXEC, 0); | |
350 | if (LogFile == -1) | |
351 | return; | |
352 | } | |
353 | } | |
354 | if (LogFile != -1 && !connected) | |
355 | { | |
356 | int old_errno = errno; | |
357 | if (__connect (LogFile, &SyslogAddr, sizeof (SyslogAddr)) == -1) | |
358 | { | |
359 | int saved_errno = errno; | |
360 | int fd = LogFile; | |
361 | LogFile = -1; | |
362 | __close (fd); | |
363 | __set_errno (old_errno); | |
364 | if (saved_errno == EPROTOTYPE) | |
790aa1f2 | 365 | { |
0cc15f45 AZ |
366 | /* retry with the other type: */ |
367 | LogType = LogType == SOCK_DGRAM ? SOCK_STREAM : SOCK_DGRAM; | |
368 | ++retry; | |
369 | continue; | |
790aa1f2 | 370 | } |
0cc15f45 AZ |
371 | } |
372 | else | |
373 | connected = true; | |
790aa1f2 | 374 | } |
0cc15f45 AZ |
375 | break; |
376 | } | |
790aa1f2 UD |
377 | } |
378 | ||
379 | void | |
380 | openlog (const char *ident, int logstat, int logfac) | |
381 | { | |
382 | /* Protect against multiple users and cancellation. */ | |
383 | __libc_cleanup_push (cancel_handler, NULL); | |
384 | __libc_lock_lock (syslog_lock); | |
385 | ||
386 | openlog_internal (ident, logstat, logfac); | |
387 | ||
388 | __libc_cleanup_pop (1); | |
389 | } | |
390 | ||
790aa1f2 | 391 | static void |
60d2f8f3 | 392 | closelog_internal (void) |
790aa1f2 UD |
393 | { |
394 | if (!connected) | |
395 | return; | |
396 | ||
397 | __close (LogFile); | |
398 | LogFile = -1; | |
7cb10381 | 399 | connected = false; |
790aa1f2 UD |
400 | } |
401 | ||
402 | void | |
60d2f8f3 | 403 | closelog (void) |
790aa1f2 UD |
404 | { |
405 | /* Protect against multiple users and cancellation. */ | |
406 | __libc_cleanup_push (cancel_handler, NULL); | |
407 | __libc_lock_lock (syslog_lock); | |
408 | ||
409 | closelog_internal (); | |
410 | LogTag = NULL; | |
411 | LogType = SOCK_DGRAM; /* this is the default */ | |
412 | ||
413 | /* Free the lock. */ | |
414 | __libc_cleanup_pop (1); | |
415 | } | |
416 | ||
417 | /* setlogmask -- set the log mask level */ | |
418 | int | |
9d46370c | 419 | setlogmask (int pmask) |
790aa1f2 | 420 | { |
0cc15f45 | 421 | int omask; |
790aa1f2 | 422 | |
0cc15f45 AZ |
423 | /* Protect against multiple users. */ |
424 | __libc_lock_lock (syslog_lock); | |
c4e4b2e1 | 425 | |
0cc15f45 AZ |
426 | omask = LogMask; |
427 | if (pmask != 0) | |
428 | LogMask = pmask; | |
c4e4b2e1 | 429 | |
0cc15f45 | 430 | __libc_lock_unlock (syslog_lock); |
c4e4b2e1 | 431 | |
0cc15f45 | 432 | return (omask); |
790aa1f2 | 433 | } |