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