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
zed_log_init(const char * identity)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
zed_log_fini(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
zed_log_pipe_open(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
zed_log_pipe_close_reads(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
zed_log_pipe_close_writes(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
zed_log_pipe_wait(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
zed_log_stderr_open(int priority)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
zed_log_stderr_close(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
zed_log_syslog_open(int facility)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
zed_log_syslog_close(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
_zed_log_aux(int priority,const char * fmt,va_list vargs)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
zed_log_msg(int priority,const char * fmt,...)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
zed_log_die(const char * fmt,...)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