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