1 /* $OpenBSD: syslog_r.c,v 1.4 2013/04/29 00:28:23 okan 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/syslog.h> 34 #include <sys/uio.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 47 extern char *__progname; /* Program name, from crt0. */ 48 49 static void disconnectlog_r(struct syslog_data *); /* disconnect from syslogd */ 50 static void connectlog_r(struct syslog_data *); /* (re)connect to syslogd */ 51 52 void __vsyslog_r(int pri, struct syslog_data *, size_t (*)(char *, size_t), 53 const char *, va_list); 54 55 /* Reentrant version of syslog, i.e. syslog_r() */ 56 57 /* PRINTFLIKE3 */ 58 void 59 syslog_r(int pri, struct syslog_data *data, const char *fmt, ...) 60 { 61 va_list ap; 62 63 va_start(ap, fmt); 64 vsyslog_r(pri, data, fmt, ap); 65 va_end(ap); 66 } 67 68 void 69 vsyslog_r(int pri, struct syslog_data *data, const char *fmt, va_list ap) 70 { 71 const char *ident; 72 73 __vsyslog_r(pri, data, NULL, fmt, ap); 74 75 /* close the socket without losing log_tag */ 76 ident = data->log_tag; 77 closelog_r(data); 78 data->log_tag = ident; 79 } 80 81 /* 82 * This is used by both syslog_r and syslog. The latter supplies 83 * a non-NULL gettime callback for filling in the date, but we also 84 * use the presence of that callback to decide whether it's safe 85 * to call strerror and what the name of the caller is 86 */ 87 void 88 __vsyslog_r(int pri, struct syslog_data *data, 89 size_t (*gettime)(char *, size_t), const char *fmt, va_list ap) 90 { 91 int cnt; 92 char ch, *p, *t; 93 int fd, saved_errno, error; 94 #define TBUF_LEN 2048 95 #define FMT_LEN 1024 96 char *stdp, tbuf[TBUF_LEN], fmt_cpy[FMT_LEN]; 97 int tbuf_left, fmt_left, prlen; 98 99 #define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID 100 /* Check for invalid bits. */ 101 if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) { 102 syslog_r(INTERNALLOG, data, 103 "syslog%s: unknown facility/priority: %x", 104 gettime != NULL ? "" : "_r", pri); 105 pri &= LOG_PRIMASK|LOG_FACMASK; 106 } 107 108 /* Check priority against setlogmask values. */ 109 if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask)) 110 return; 111 112 saved_errno = errno; 113 114 /* Set default facility if none specified. */ 115 if ((pri & LOG_FACMASK) == 0) 116 pri |= data->log_fac; 117 118 p = tbuf; 119 tbuf_left = TBUF_LEN; 120 121 #define DEC() \ 122 do { \ 123 if (prlen < 0) \ 124 prlen = 0; \ 125 if (prlen >= tbuf_left) \ 126 prlen = tbuf_left - 1; \ 127 p += prlen; \ 128 tbuf_left -= prlen; \ 129 } while (0) 130 131 prlen = snprintf(p, tbuf_left, "<%d>", pri); 132 DEC(); 133 134 /* 135 * syslogd will expand time automagically for reentrant case, and 136 * for normal case, invoke the callback to do it just do like before 137 */ 138 if (gettime != NULL) { 139 prlen = gettime(p, tbuf_left); 140 DEC(); 141 } 142 143 if (data->log_stat & LOG_PERROR) 144 stdp = p; 145 if (data->log_tag == NULL) 146 data->log_tag = __progname; 147 if (data->log_tag != NULL) { 148 prlen = snprintf(p, tbuf_left, "%s", data->log_tag); 149 DEC(); 150 } 151 if (data->log_stat & LOG_PID) { 152 prlen = snprintf(p, tbuf_left, "[%ld]", (long)getpid()); 153 DEC(); 154 } 155 if (data->log_tag != NULL) { 156 if (tbuf_left > 1) { 157 *p++ = ':'; 158 tbuf_left--; 159 } 160 if (tbuf_left > 1) { 161 *p++ = ' '; 162 tbuf_left--; 163 } 164 } 165 166 /* strerror() is not reentrant */ 167 168 for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt); ++fmt) { 169 if (ch == '%' && fmt[1] == 'm') { 170 ++fmt; 171 if (gettime != NULL) { 172 prlen = snprintf(t, fmt_left, "%s", 173 strerror(saved_errno)); 174 } else { 175 prlen = snprintf(t, fmt_left, "Error %d", 176 saved_errno); 177 } 178 if (prlen < 0) 179 prlen = 0; 180 if (prlen >= fmt_left) 181 prlen = fmt_left - 1; 182 t += prlen; 183 fmt_left -= prlen; 184 } else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) { 185 *t++ = '%'; 186 *t++ = '%'; 187 fmt++; 188 fmt_left -= 2; 189 } else { 190 if (fmt_left > 1) { 191 *t++ = ch; 192 fmt_left--; 193 } 194 } 195 } 196 *t = '\0'; 197 198 prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap); 199 DEC(); 200 cnt = p - tbuf; 201 202 /* Output to stderr if requested. */ 203 if (data->log_stat & LOG_PERROR) { 204 struct iovec iov[2]; 205 206 iov[0].iov_base = stdp; 207 iov[0].iov_len = cnt - (stdp - tbuf); 208 iov[1].iov_base = "\n"; 209 iov[1].iov_len = 1; 210 (void)writev(STDERR_FILENO, iov, 2); 211 } 212 213 /* Get connected, output the message to the local logger. */ 214 if (!data->opened) 215 openlog_r(data->log_tag, data->log_stat, 0, data); 216 connectlog_r(data); 217 218 /* 219 * If the send() failed, there are two likely scenarios: 220 * 1) syslogd was restarted 221 * 2) /dev/log is out of socket buffer space 222 * We attempt to reconnect to /dev/log to take care of 223 * case #1 and keep send()ing data to cover case #2 224 * to give syslogd a chance to empty its socket buffer. 225 */ 226 if ((error = send(data->log_file, tbuf, cnt, 0)) < 0) { 227 if (errno != ENOBUFS) { 228 disconnectlog_r(data); 229 connectlog_r(data); 230 } 231 do { 232 struct timespec rqt = { 0, 1000 }; 233 234 nanosleep(&rqt, NULL); 235 if ((error = send(data->log_file, tbuf, cnt, 0)) >= 0) 236 break; 237 } while (errno == ENOBUFS); 238 } 239 240 /* 241 * Output the message to the console; try not to block 242 * as a blocking console should not stop other processes. 243 * Make sure the error reported is the one from the syslogd failure. 244 */ 245 if (error == -1 && (data->log_stat & LOG_CONS) && 246 (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) { 247 struct iovec iov[2]; 248 249 p = strchr(tbuf, '>') + 1; 250 iov[0].iov_base = p; 251 iov[0].iov_len = cnt - (p - tbuf); 252 iov[1].iov_base = "\r\n"; 253 iov[1].iov_len = 2; 254 (void)writev(fd, iov, 2); 255 (void)close(fd); 256 } 257 } 258 259 static void 260 disconnectlog_r(struct syslog_data *data) 261 { 262 /* 263 * If the user closed the FD and opened another in the same slot, 264 * that's their problem. They should close it before calling on 265 * system services. 266 */ 267 if (data->log_file != -1) { 268 close(data->log_file); 269 data->log_file = -1; 270 } 271 data->connected = 0; /* retry connect */ 272 } 273 274 static void 275 connectlog_r(struct syslog_data *data) 276 { 277 struct sockaddr_un SyslogAddr; /* AF_UNIX address of local logger */ 278 279 if (data->log_file == -1) { 280 if ((data->log_file = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) 281 return; 282 (void)fcntl(data->log_file, F_SETFD, FD_CLOEXEC); 283 } 284 if (data->log_file != -1 && !data->connected) { 285 memset(&SyslogAddr, '\0', sizeof(SyslogAddr)); 286 SyslogAddr.sun_len = sizeof(SyslogAddr); 287 SyslogAddr.sun_family = AF_UNIX; 288 strlcpy(SyslogAddr.sun_path, _PATH_LOG, 289 sizeof(SyslogAddr.sun_path)); 290 if (connect(data->log_file, (struct sockaddr *)&SyslogAddr, 291 sizeof(SyslogAddr)) == -1) { 292 (void)close(data->log_file); 293 data->log_file = -1; 294 } else 295 data->connected = 1; 296 } 297 } 298 299 void 300 openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data) 301 { 302 if (ident != NULL) 303 data->log_tag = ident; 304 data->log_stat = logstat; 305 if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0) 306 data->log_fac = logfac; 307 308 if (data->log_stat & LOG_NDELAY) /* open immediately */ 309 connectlog_r(data); 310 311 data->opened = 1; /* ident and facility has been set */ 312 } 313 314 void 315 closelog_r(struct syslog_data *data) 316 { 317 (void)close(data->log_file); 318 data->log_file = -1; 319 data->connected = 0; 320 data->log_tag = NULL; 321 } 322 323