1 /* 2 * This file is part of the ZFS Event Daemon (ZED). 3 * 4 * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). 5 * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. 6 * Refer to the OpenZFS git commit log for authoritative copyright attribution. 7 * 8 * The contents of this file are subject to the terms of the 9 * Common Development and Distribution License Version 1.0 (CDDL-1.0). 10 * You can obtain a copy of the license from the top-level file 11 * "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>. 12 * You may not use this file except in compliance with the license. 13 */ 14 15 #include <assert.h> 16 #include <errno.h> 17 #include <stdarg.h> 18 #include <stdio.h> 19 #include <stdlib.h> 20 #include <string.h> 21 #include <sys/types.h> 22 #include <syslog.h> 23 #include <unistd.h> 24 #include "zed_log.h" 25 26 #define ZED_LOG_MAX_LOG_LEN 1024 27 28 static struct { 29 unsigned do_stderr:1; 30 unsigned do_syslog:1; 31 const char *identity; 32 int priority; 33 int pipe_fd[2]; 34 } _ctx; 35 36 /* 37 * Initialize the logging subsystem. 38 */ 39 void 40 zed_log_init(const char *identity) 41 { 42 if (identity) { 43 const char *p = strrchr(identity, '/'); 44 _ctx.identity = (p != NULL) ? p + 1 : identity; 45 } else { 46 _ctx.identity = NULL; 47 } 48 _ctx.pipe_fd[0] = -1; 49 _ctx.pipe_fd[1] = -1; 50 } 51 52 /* 53 * Shutdown the logging subsystem. 54 */ 55 void 56 zed_log_fini(void) 57 { 58 zed_log_stderr_close(); 59 zed_log_syslog_close(); 60 } 61 62 /* 63 * Create pipe for communicating daemonization status between the parent and 64 * child processes across the double-fork(). 65 */ 66 void 67 zed_log_pipe_open(void) 68 { 69 if ((_ctx.pipe_fd[0] != -1) || (_ctx.pipe_fd[1] != -1)) 70 zed_log_die("Invalid use of zed_log_pipe_open in PID %d", 71 (int)getpid()); 72 73 if (pipe(_ctx.pipe_fd) < 0) 74 zed_log_die("Failed to create daemonize pipe in PID %d: %s", 75 (int)getpid(), strerror(errno)); 76 } 77 78 /* 79 * Close the read-half of the daemonize pipe. 80 * 81 * This should be called by the child after fork()ing from the parent since 82 * the child will never read from this pipe. 83 */ 84 void 85 zed_log_pipe_close_reads(void) 86 { 87 if (_ctx.pipe_fd[0] < 0) 88 zed_log_die( 89 "Invalid use of zed_log_pipe_close_reads in PID %d", 90 (int)getpid()); 91 92 if (close(_ctx.pipe_fd[0]) < 0) 93 zed_log_die( 94 "Failed to close reads on daemonize pipe in PID %d: %s", 95 (int)getpid(), strerror(errno)); 96 97 _ctx.pipe_fd[0] = -1; 98 } 99 100 /* 101 * Close the write-half of the daemonize pipe. 102 * 103 * This should be called by the parent after fork()ing its child since the 104 * parent will never write to this pipe. 105 * 106 * This should also be called by the child once initialization is complete 107 * in order to signal the parent that it can safely exit. 108 */ 109 void 110 zed_log_pipe_close_writes(void) 111 { 112 if (_ctx.pipe_fd[1] < 0) 113 zed_log_die( 114 "Invalid use of zed_log_pipe_close_writes in PID %d", 115 (int)getpid()); 116 117 if (close(_ctx.pipe_fd[1]) < 0) 118 zed_log_die( 119 "Failed to close writes on daemonize pipe in PID %d: %s", 120 (int)getpid(), strerror(errno)); 121 122 _ctx.pipe_fd[1] = -1; 123 } 124 125 /* 126 * Block on reading from the daemonize pipe until signaled by the child 127 * (via zed_log_pipe_close_writes()) that initialization is complete. 128 * 129 * This should only be called by the parent while waiting to exit after 130 * fork()ing the child. 131 */ 132 void 133 zed_log_pipe_wait(void) 134 { 135 ssize_t n; 136 char c; 137 138 if (_ctx.pipe_fd[0] < 0) 139 zed_log_die("Invalid use of zed_log_pipe_wait in PID %d", 140 (int)getpid()); 141 142 for (;;) { 143 n = read(_ctx.pipe_fd[0], &c, sizeof (c)); 144 if (n < 0) { 145 if (errno == EINTR) 146 continue; 147 zed_log_die( 148 "Failed to read from daemonize pipe in PID %d: %s", 149 (int)getpid(), strerror(errno)); 150 } 151 if (n == 0) { 152 break; 153 } 154 } 155 } 156 157 /* 158 * Start logging messages at the syslog [priority] level or higher to stderr. 159 * Refer to syslog(3) for valid priority values. 160 */ 161 void 162 zed_log_stderr_open(int priority) 163 { 164 _ctx.do_stderr = 1; 165 _ctx.priority = priority; 166 } 167 168 /* 169 * Stop logging messages to stderr. 170 */ 171 void 172 zed_log_stderr_close(void) 173 { 174 if (_ctx.do_stderr) 175 _ctx.do_stderr = 0; 176 } 177 178 /* 179 * Start logging messages to syslog. 180 * Refer to syslog(3) for valid option/facility values. 181 */ 182 void 183 zed_log_syslog_open(int facility) 184 { 185 _ctx.do_syslog = 1; 186 openlog(_ctx.identity, LOG_NDELAY | LOG_PID, facility); 187 } 188 189 /* 190 * Stop logging messages to syslog. 191 */ 192 void 193 zed_log_syslog_close(void) 194 { 195 if (_ctx.do_syslog) { 196 _ctx.do_syslog = 0; 197 closelog(); 198 } 199 } 200 201 /* 202 * Auxiliary function to log a message to syslog and/or stderr. 203 */ 204 static void 205 _zed_log_aux(int priority, const char *fmt, va_list vargs) 206 { 207 char buf[ZED_LOG_MAX_LOG_LEN]; 208 int n; 209 210 if (!fmt) 211 return; 212 213 n = vsnprintf(buf, sizeof (buf), fmt, vargs); 214 if ((n < 0) || (n >= sizeof (buf))) { 215 buf[sizeof (buf) - 2] = '+'; 216 buf[sizeof (buf) - 1] = '\0'; 217 } 218 219 if (_ctx.do_syslog) 220 syslog(priority, "%s", buf); 221 222 if (_ctx.do_stderr && (priority <= _ctx.priority)) 223 fprintf(stderr, "%s\n", buf); 224 } 225 226 /* 227 * Log a message at the given [priority] level specified by the printf-style 228 * format string [fmt]. 229 */ 230 void 231 zed_log_msg(int priority, const char *fmt, ...) 232 { 233 va_list vargs; 234 235 if (fmt) { 236 va_start(vargs, fmt); 237 _zed_log_aux(priority, fmt, vargs); 238 va_end(vargs); 239 } 240 } 241 242 /* 243 * Log a fatal error message specified by the printf-style format string [fmt]. 244 */ 245 void 246 zed_log_die(const char *fmt, ...) 247 { 248 va_list vargs; 249 250 if (fmt) { 251 va_start(vargs, fmt); 252 _zed_log_aux(LOG_ERR, fmt, vargs); 253 va_end(vargs); 254 } 255 exit(EXIT_FAILURE); 256 } 257