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