]>
Commit | Line | Data |
---|---|---|
9e246ac3 | 1 | /* |
492b1d2e CD |
2 | * This file is part of the ZFS Event Daemon (ZED) |
3 | * for ZFS on Linux (ZoL) <http://zfsonlinux.org/>. | |
9e246ac3 CD |
4 | * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). |
5 | * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. | |
492b1d2e CD |
6 | * Refer to the ZoL 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. | |
9e246ac3 CD |
13 | */ |
14 | ||
15 | #include <errno.h> | |
16 | #include <fcntl.h> | |
17 | #include <signal.h> | |
18 | #include <stdio.h> | |
19 | #include <stdlib.h> | |
20 | #include <string.h> | |
21 | #include <sys/mman.h> | |
22 | #include <sys/stat.h> | |
23 | #include <unistd.h> | |
24 | #include "zed.h" | |
25 | #include "zed_conf.h" | |
26 | #include "zed_event.h" | |
27 | #include "zed_file.h" | |
28 | #include "zed_log.h" | |
29 | ||
30 | static volatile sig_atomic_t _got_exit = 0; | |
31 | static volatile sig_atomic_t _got_hup = 0; | |
32 | ||
33 | /* | |
34 | * Signal handler for SIGINT & SIGTERM. | |
35 | */ | |
36 | static void | |
37 | _exit_handler(int signum) | |
38 | { | |
39 | _got_exit = 1; | |
40 | } | |
41 | ||
42 | /* | |
43 | * Signal handler for SIGHUP. | |
44 | */ | |
45 | static void | |
46 | _hup_handler(int signum) | |
47 | { | |
48 | _got_hup = 1; | |
49 | } | |
50 | ||
51 | /* | |
52 | * Register signal handlers. | |
53 | */ | |
54 | static void | |
55 | _setup_sig_handlers(void) | |
56 | { | |
57 | struct sigaction sa; | |
58 | ||
59 | if (sigemptyset(&sa.sa_mask) < 0) | |
60 | zed_log_die("Failed to initialize sigset"); | |
61 | ||
62 | sa.sa_flags = SA_RESTART; | |
63 | sa.sa_handler = SIG_IGN; | |
64 | ||
65 | if (sigaction(SIGPIPE, &sa, NULL) < 0) | |
66 | zed_log_die("Failed to ignore SIGPIPE"); | |
67 | ||
68 | sa.sa_handler = _exit_handler; | |
69 | if (sigaction(SIGINT, &sa, NULL) < 0) | |
70 | zed_log_die("Failed to register SIGINT handler"); | |
71 | ||
72 | if (sigaction(SIGTERM, &sa, NULL) < 0) | |
73 | zed_log_die("Failed to register SIGTERM handler"); | |
74 | ||
75 | sa.sa_handler = _hup_handler; | |
76 | if (sigaction(SIGHUP, &sa, NULL) < 0) | |
77 | zed_log_die("Failed to register SIGHUP handler"); | |
78 | } | |
79 | ||
80 | /* | |
81 | * Lock all current and future pages in the virtual memory address space. | |
5043deaa CD |
82 | * Access to locked pages will never be delayed by a page fault. |
83 | * | |
9e246ac3 | 84 | * EAGAIN is tested up to max_tries in case this is a transient error. |
5043deaa | 85 | * |
5a8855b7 | 86 | * Note that memory locks are not inherited by a child created via fork() |
5043deaa CD |
87 | * and are automatically removed during an execve(). As such, this must |
88 | * be called after the daemon fork()s (when running in the background). | |
9e246ac3 CD |
89 | */ |
90 | static void | |
91 | _lock_memory(void) | |
92 | { | |
518eba14 | 93 | #if HAVE_MLOCKALL |
9e246ac3 CD |
94 | int i = 0; |
95 | const int max_tries = 10; | |
96 | ||
97 | for (i = 0; i < max_tries; i++) { | |
98 | if (mlockall(MCL_CURRENT | MCL_FUTURE) == 0) { | |
99 | zed_log_msg(LOG_INFO, "Locked all pages in memory"); | |
100 | return; | |
101 | } | |
102 | if (errno != EAGAIN) | |
103 | break; | |
104 | } | |
105 | zed_log_die("Failed to lock memory pages: %s", strerror(errno)); | |
106 | ||
518eba14 CD |
107 | #else /* HAVE_MLOCKALL */ |
108 | zed_log_die("Failed to lock memory pages: mlockall() not supported"); | |
109 | #endif /* HAVE_MLOCKALL */ | |
9e246ac3 CD |
110 | } |
111 | ||
112 | /* | |
5a8855b7 | 113 | * Start daemonization of the process including the double fork(). |
5043deaa | 114 | * |
5a8855b7 | 115 | * The parent process will block here until _finish_daemonize() is called |
5043deaa CD |
116 | * (in the grandchild process), at which point the parent process will exit. |
117 | * This prevents the parent process from exiting until initialization is | |
118 | * complete. | |
9e246ac3 CD |
119 | */ |
120 | static void | |
5a8855b7 | 121 | _start_daemonize(void) |
9e246ac3 CD |
122 | { |
123 | pid_t pid; | |
5a8855b7 CD |
124 | struct sigaction sa; |
125 | ||
126 | /* Create pipe for communicating with child during daemonization. */ | |
127 | zed_log_pipe_open(); | |
9e246ac3 | 128 | |
5a8855b7 | 129 | /* Background process and ensure child is not process group leader. */ |
9e246ac3 CD |
130 | pid = fork(); |
131 | if (pid < 0) { | |
132 | zed_log_die("Failed to create child process: %s", | |
133 | strerror(errno)); | |
134 | } else if (pid > 0) { | |
5a8855b7 CD |
135 | |
136 | /* Close writes since parent will only read from pipe. */ | |
137 | zed_log_pipe_close_writes(); | |
138 | ||
139 | /* Wait for notification that daemonization is complete. */ | |
140 | zed_log_pipe_wait(); | |
141 | ||
142 | zed_log_pipe_close_reads(); | |
9e246ac3 CD |
143 | _exit(EXIT_SUCCESS); |
144 | } | |
5a8855b7 CD |
145 | |
146 | /* Close reads since child will only write to pipe. */ | |
147 | zed_log_pipe_close_reads(); | |
148 | ||
149 | /* Create independent session and detach from terminal. */ | |
9e246ac3 CD |
150 | if (setsid() < 0) |
151 | zed_log_die("Failed to create new session: %s", | |
152 | strerror(errno)); | |
153 | ||
5a8855b7 CD |
154 | /* Prevent child from terminating on HUP when session leader exits. */ |
155 | if (sigemptyset(&sa.sa_mask) < 0) | |
156 | zed_log_die("Failed to initialize sigset"); | |
157 | ||
158 | sa.sa_flags = 0; | |
159 | sa.sa_handler = SIG_IGN; | |
160 | ||
161 | if (sigaction(SIGHUP, &sa, NULL) < 0) | |
162 | zed_log_die("Failed to ignore SIGHUP"); | |
163 | ||
164 | /* Ensure process cannot re-acquire terminal. */ | |
9e246ac3 CD |
165 | pid = fork(); |
166 | if (pid < 0) { | |
167 | zed_log_die("Failed to create grandchild process: %s", | |
168 | strerror(errno)); | |
169 | } else if (pid > 0) { | |
170 | _exit(EXIT_SUCCESS); | |
171 | } | |
5a8855b7 CD |
172 | } |
173 | ||
174 | /* | |
175 | * Finish daemonization of the process by closing stdin/stdout/stderr. | |
5043deaa | 176 | * |
5a8855b7 | 177 | * This must be called at the end of initialization after all external |
5043deaa | 178 | * communication channels are established and accessible. |
5a8855b7 CD |
179 | */ |
180 | static void | |
181 | _finish_daemonize(void) | |
182 | { | |
183 | int devnull; | |
9e246ac3 | 184 | |
5a8855b7 CD |
185 | /* Preserve fd 0/1/2, but discard data to/from stdin/stdout/stderr. */ |
186 | devnull = open("/dev/null", O_RDWR); | |
187 | if (devnull < 0) | |
9e246ac3 CD |
188 | zed_log_die("Failed to open /dev/null: %s", strerror(errno)); |
189 | ||
5a8855b7 | 190 | if (dup2(devnull, STDIN_FILENO) < 0) |
9e246ac3 CD |
191 | zed_log_die("Failed to dup /dev/null onto stdin: %s", |
192 | strerror(errno)); | |
193 | ||
5a8855b7 | 194 | if (dup2(devnull, STDOUT_FILENO) < 0) |
9e246ac3 CD |
195 | zed_log_die("Failed to dup /dev/null onto stdout: %s", |
196 | strerror(errno)); | |
197 | ||
5a8855b7 | 198 | if (dup2(devnull, STDERR_FILENO) < 0) |
9e246ac3 CD |
199 | zed_log_die("Failed to dup /dev/null onto stderr: %s", |
200 | strerror(errno)); | |
201 | ||
048bb5bd | 202 | if ((devnull > STDERR_FILENO) && (close(devnull) < 0)) |
9e246ac3 | 203 | zed_log_die("Failed to close /dev/null: %s", strerror(errno)); |
5a8855b7 CD |
204 | |
205 | /* Notify parent that daemonization is complete. */ | |
206 | zed_log_pipe_close_writes(); | |
9e246ac3 CD |
207 | } |
208 | ||
209 | /* | |
210 | * ZFS Event Daemon (ZED). | |
211 | */ | |
212 | int | |
213 | main(int argc, char *argv[]) | |
214 | { | |
215 | struct zed_conf *zcp; | |
216 | uint64_t saved_eid; | |
217 | int64_t saved_etime[2]; | |
218 | ||
219 | zed_log_init(argv[0]); | |
220 | zed_log_stderr_open(LOG_NOTICE); | |
221 | zcp = zed_conf_create(); | |
222 | zed_conf_parse_opts(zcp, argc, argv); | |
223 | if (zcp->do_verbose) | |
224 | zed_log_stderr_open(LOG_INFO); | |
225 | ||
226 | if (geteuid() != 0) | |
227 | zed_log_die("Must be run as root"); | |
228 | ||
9e246ac3 CD |
229 | zed_conf_parse_file(zcp); |
230 | ||
231 | zed_file_close_from(STDERR_FILENO + 1); | |
232 | ||
5a8855b7 CD |
233 | (void) umask(0); |
234 | ||
9e246ac3 CD |
235 | if (chdir("/") < 0) |
236 | zed_log_die("Failed to change to root directory"); | |
237 | ||
238 | if (zed_conf_scan_dir(zcp) < 0) | |
239 | exit(EXIT_FAILURE); | |
240 | ||
9e246ac3 | 241 | if (!zcp->do_foreground) { |
5a8855b7 | 242 | _start_daemonize(); |
9e246ac3 | 243 | zed_log_syslog_open(LOG_DAEMON); |
9e246ac3 | 244 | } |
5a8855b7 CD |
245 | _setup_sig_handlers(); |
246 | ||
247 | if (zcp->do_memlock) | |
248 | _lock_memory(); | |
9e246ac3 | 249 | |
56697c42 CD |
250 | if ((zed_conf_write_pid(zcp) < 0) && (!zcp->do_force)) |
251 | exit(EXIT_FAILURE); | |
9e246ac3 | 252 | |
5a8855b7 CD |
253 | if (!zcp->do_foreground) |
254 | _finish_daemonize(); | |
255 | ||
256 | zed_log_msg(LOG_NOTICE, | |
257 | "ZFS Event Daemon %s-%s (PID %d)", | |
258 | ZFS_META_VERSION, ZFS_META_RELEASE, (int) getpid()); | |
259 | ||
9e246ac3 CD |
260 | if (zed_conf_open_state(zcp) < 0) |
261 | exit(EXIT_FAILURE); | |
262 | ||
263 | if (zed_conf_read_state(zcp, &saved_eid, saved_etime) < 0) | |
264 | exit(EXIT_FAILURE); | |
265 | ||
266 | zed_event_init(zcp); | |
267 | zed_event_seek(zcp, saved_eid, saved_etime); | |
268 | ||
269 | while (!_got_exit) { | |
270 | if (_got_hup) { | |
271 | _got_hup = 0; | |
272 | (void) zed_conf_scan_dir(zcp); | |
273 | } | |
274 | zed_event_service(zcp); | |
275 | } | |
276 | zed_log_msg(LOG_NOTICE, "Exiting"); | |
277 | zed_event_fini(zcp); | |
278 | zed_conf_destroy(zcp); | |
279 | zed_log_fini(); | |
280 | exit(EXIT_SUCCESS); | |
281 | } |