xref: /freebsd/sys/contrib/openzfs/cmd/zed/zed_log.c (revision 10ff414c)
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