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 <libio/libioP.h> |
35 | #include <paths.h> |
36 | #include <stdarg.h> |
37 | #include <stdlib.h> |
38 | #include <stdio.h> |
39 | #include <stdio_ext.h> |
40 | #include <sys/socket.h> |
41 | #include <sys/uio.h> |
42 | #include <sys/un.h> |
43 | #include <syslog.h> |
44 | #include <limits.h> |
45 | |
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() */ |
50 | static const char *LogTag; /* string to tag the entry with */ |
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. */ |
54 | |
55 | /* Define the lock. */ |
56 | __libc_lock_define_initialized (static, syslog_lock) |
57 | static void openlog_internal (const char *, int, int); |
58 | static void closelog_internal (void); |
59 | |
60 | struct cleanup_arg |
61 | { |
62 | void *buf; |
63 | struct sigaction *oldaction; |
64 | }; |
65 | |
66 | static void |
67 | cancel_handler (void *ptr) |
68 | { |
69 | /* Restore the old signal handler. */ |
70 | struct cleanup_arg *clarg = (struct cleanup_arg *) ptr; |
71 | |
72 | if (clarg != NULL) |
73 | /* Free the memstream buffer, */ |
74 | free (ptr: clarg->buf); |
75 | |
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 |
86 | __syslog (int pri, const char *fmt, ...) |
87 | { |
88 | va_list ap; |
89 | |
90 | va_start (ap, fmt); |
91 | __vsyslog_internal (pri, fmt, ap, mode_flags: 0); |
92 | va_end (ap); |
93 | } |
94 | ldbl_hidden_def (__syslog, syslog) |
95 | ldbl_strong_alias (__syslog, syslog) |
96 | |
97 | void |
98 | __vsyslog (int pri, const char *fmt, va_list ap) |
99 | { |
100 | __vsyslog_internal (pri, fmt, ap, mode_flags: 0); |
101 | } |
102 | ldbl_weak_alias (__vsyslog, vsyslog) |
103 | |
104 | void |
105 | ___syslog_chk (int pri, int flag, const char *fmt, ...) |
106 | { |
107 | va_list ap; |
108 | |
109 | va_start (ap, fmt); |
110 | __vsyslog_internal (pri, fmt, ap, mode_flags: (flag > 0) ? PRINTF_FORTIFY : 0); |
111 | va_end (ap); |
112 | } |
113 | ldbl_hidden_def (___syslog_chk, __syslog_chk) |
114 | ldbl_strong_alias (___syslog_chk, __syslog_chk) |
115 | |
116 | void |
117 | __vsyslog_chk (int pri, int flag, const char *fmt, va_list ap) |
118 | { |
119 | __vsyslog_internal (pri, fmt, ap, mode_flags: (flag > 0) ? PRINTF_FORTIFY : 0); |
120 | } |
121 | |
122 | void |
123 | __vsyslog_internal (int pri, const char *fmt, va_list ap, |
124 | unsigned int mode_flags) |
125 | { |
126 | /* Try to use a static buffer as an optimization. */ |
127 | char bufs[1024]; |
128 | char *buf = bufs; |
129 | size_t bufsize; |
130 | |
131 | int msgoff; |
132 | int saved_errno = errno; |
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. */ |
144 | struct cleanup_arg clarg = { NULL, NULL }; |
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 | |
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 " ]; |
160 | __time64_t now = time64_now (); |
161 | struct tm now_tm; |
162 | struct tm *now_tmp = __localtime64_r (timer: &now, tp: &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); |
172 | |
173 | #define (__pri, __timestamp, __msgoff, pid) \ |
174 | "<%d>%s%n%s%s%.0d%s: ", \ |
175 | __pri, __timestamp, __msgoff, \ |
176 | LogTag == NULL ? __progname : LogTag, \ |
177 | "[" + (pid == 0), pid, "]" + (pid == 0) |
178 | |
179 | #define (__pri, __msgoff) \ |
180 | "<%d>: %n", __pri, __msgoff |
181 | |
182 | int l, vl; |
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)); |
189 | if (l < 0) |
190 | goto out; |
191 | |
192 | char *pos; |
193 | size_t len; |
194 | |
195 | if (l < sizeof bufs) |
196 | { |
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 | } |
212 | |
213 | { |
214 | va_list apc; |
215 | va_copy (apc, ap); |
216 | |
217 | /* Restore errno for %m format. */ |
218 | __set_errno (saved_errno); |
219 | |
220 | vl = __vsnprintf_internal (string: pos, maxlen: len, format: fmt, ap: apc, mode_flags); |
221 | va_end (apc); |
222 | |
223 | if (vl < 0 || vl >= INT_MAX - l) |
224 | goto out; |
225 | |
226 | if (vl >= len) |
227 | buf = NULL; |
228 | |
229 | bufsize = l + vl; |
230 | } |
231 | |
232 | if (buf == NULL) |
233 | { |
234 | buf = malloc (size: (bufsize + 1) * sizeof (char)); |
235 | if (buf != NULL) |
236 | { |
237 | /* Tell the cancellation handler to free this buffer. */ |
238 | clarg.buf = buf; |
239 | |
240 | int cl; |
241 | if (has_ts) |
242 | cl = __snprintf (buf, l + 1, |
243 | SYSLOG_HEADER (pri, timestamp, &msgoff, pid)); |
244 | else |
245 | cl = __snprintf (buf, l + 1, |
246 | SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff)); |
247 | if (cl != l) |
248 | goto out; |
249 | |
250 | va_list apc; |
251 | va_copy (apc, ap); |
252 | cl = __vsnprintf_internal (string: buf + l, maxlen: bufsize - l + 1, format: fmt, ap: apc, |
253 | mode_flags); |
254 | va_end (apc); |
255 | |
256 | if (cl != vl) |
257 | goto out; |
258 | } |
259 | else |
260 | { |
261 | int bl; |
262 | /* Nothing much to do but emit an error message. */ |
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; |
269 | buf = bufs; |
270 | msgoff = 0; |
271 | } |
272 | } |
273 | |
274 | /* Output to stderr if requested. */ |
275 | if (LogStat & LOG_PERROR) |
276 | __dprintf (STDERR_FILENO, "%s%s" , buf + msgoff, |
277 | "\n" + (buf[bufsize - 1] == '\n')); |
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); |
295 | } |
296 | |
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 | */ |
306 | int fd; |
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 | |
322 | if (buf != bufs) |
323 | free (ptr: buf); |
324 | } |
325 | |
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 | }; |
332 | |
333 | static void |
334 | openlog_internal (const char *ident, int logstat, int logfac) |
335 | { |
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) |
365 | { |
366 | /* retry with the other type: */ |
367 | LogType = LogType == SOCK_DGRAM ? SOCK_STREAM : SOCK_DGRAM; |
368 | ++retry; |
369 | continue; |
370 | } |
371 | } |
372 | else |
373 | connected = true; |
374 | } |
375 | break; |
376 | } |
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 | |
391 | static void |
392 | closelog_internal (void) |
393 | { |
394 | if (!connected) |
395 | return; |
396 | |
397 | __close (LogFile); |
398 | LogFile = -1; |
399 | connected = false; |
400 | } |
401 | |
402 | void |
403 | closelog (void) |
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 |
419 | setlogmask (int pmask) |
420 | { |
421 | int omask; |
422 | |
423 | /* Protect against multiple users. */ |
424 | __libc_lock_lock (syslog_lock); |
425 | |
426 | omask = LogMask; |
427 | if (pmask != 0) |
428 | LogMask = pmask; |
429 | |
430 | __libc_lock_unlock (syslog_lock); |
431 | |
432 | return (omask); |
433 | } |
434 | |