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