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