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