]>
Commit | Line | Data |
---|---|---|
9e246ac3 CD |
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 <ctype.h> | |
29 | #include <dirent.h> | |
30 | #include <errno.h> | |
31 | #include <fcntl.h> | |
6ac770b1 | 32 | #include <libgen.h> |
9e246ac3 CD |
33 | #include <limits.h> |
34 | #include <stdio.h> | |
35 | #include <stdlib.h> | |
36 | #include <string.h> | |
37 | #include <sys/stat.h> | |
38 | #include <sys/uio.h> | |
39 | #include <unistd.h> | |
40 | #include "zed.h" | |
41 | #include "zed_conf.h" | |
42 | #include "zed_file.h" | |
43 | #include "zed_log.h" | |
44 | #include "zed_strings.h" | |
45 | ||
46 | /* | |
47 | * Return a new configuration with default values. | |
48 | */ | |
49 | struct zed_conf * | |
50 | zed_conf_create(void) | |
51 | { | |
52 | struct zed_conf *zcp; | |
53 | ||
54 | zcp = malloc(sizeof (*zcp)); | |
55 | if (!zcp) | |
56 | goto nomem; | |
57 | ||
58 | memset(zcp, 0, sizeof (*zcp)); | |
59 | ||
60 | zcp->syslog_facility = LOG_DAEMON; | |
61 | zcp->min_events = ZED_MIN_EVENTS; | |
62 | zcp->max_events = ZED_MAX_EVENTS; | |
63 | zcp->scripts = NULL; /* created via zed_conf_scan_dir() */ | |
64 | zcp->state_fd = -1; /* opened via zed_conf_open_state() */ | |
65 | zcp->zfs_hdl = NULL; /* opened via zed_event_init() */ | |
66 | zcp->zevent_fd = -1; /* opened via zed_event_init() */ | |
67 | ||
68 | if (!(zcp->conf_file = strdup(ZED_CONF_FILE))) | |
69 | goto nomem; | |
70 | ||
71 | if (!(zcp->pid_file = strdup(ZED_PID_FILE))) | |
72 | goto nomem; | |
73 | ||
74 | if (!(zcp->script_dir = strdup(ZED_SCRIPT_DIR))) | |
75 | goto nomem; | |
76 | ||
77 | if (!(zcp->state_file = strdup(ZED_STATE_FILE))) | |
78 | goto nomem; | |
79 | ||
80 | return (zcp); | |
81 | ||
82 | nomem: | |
83 | zed_log_die("Failed to create conf: %s", strerror(errno)); | |
84 | return (NULL); | |
85 | } | |
86 | ||
87 | /* | |
88 | * Destroy the configuration [zcp]. | |
5043deaa | 89 | * |
9e246ac3 CD |
90 | * Note: zfs_hdl & zevent_fd are destroyed via zed_event_fini(). |
91 | */ | |
92 | void | |
93 | zed_conf_destroy(struct zed_conf *zcp) | |
94 | { | |
95 | if (!zcp) | |
96 | return; | |
97 | ||
98 | if (zcp->state_fd >= 0) { | |
99 | if (close(zcp->state_fd) < 0) | |
100 | zed_log_msg(LOG_WARNING, | |
101 | "Failed to close state file \"%s\": %s", | |
102 | zcp->state_file, strerror(errno)); | |
103 | } | |
104 | if (zcp->pid_file) { | |
105 | if ((unlink(zcp->pid_file) < 0) && (errno != ENOENT)) | |
106 | zed_log_msg(LOG_WARNING, | |
8125fb71 | 107 | "Failed to remove PID file \"%s\": %s", |
9e246ac3 CD |
108 | zcp->pid_file, strerror(errno)); |
109 | } | |
110 | if (zcp->conf_file) | |
111 | free(zcp->conf_file); | |
112 | ||
113 | if (zcp->pid_file) | |
114 | free(zcp->pid_file); | |
115 | ||
116 | if (zcp->script_dir) | |
117 | free(zcp->script_dir); | |
118 | ||
119 | if (zcp->state_file) | |
120 | free(zcp->state_file); | |
121 | ||
122 | if (zcp->scripts) | |
123 | zed_strings_destroy(zcp->scripts); | |
124 | ||
125 | free(zcp); | |
126 | } | |
127 | ||
128 | /* | |
129 | * Display command-line help and exit. | |
5043deaa | 130 | * |
9e246ac3 | 131 | * If [got_err] is 0, output to stdout and exit normally; |
5043deaa | 132 | * otherwise, output to stderr and exit with a failure status. |
9e246ac3 CD |
133 | */ |
134 | static void | |
135 | _zed_conf_display_help(const char *prog, int got_err) | |
136 | { | |
137 | FILE *fp = got_err ? stderr : stdout; | |
138 | int w1 = 4; /* width of leading whitespace */ | |
139 | int w2 = 8; /* width of L-justified option field */ | |
140 | ||
141 | fprintf(fp, "Usage: %s [OPTION]...\n", (prog ? prog : "zed")); | |
142 | fprintf(fp, "\n"); | |
143 | fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-h", | |
144 | "Display help."); | |
145 | fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-L", | |
146 | "Display license information."); | |
147 | fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-V", | |
148 | "Display version information."); | |
149 | fprintf(fp, "\n"); | |
150 | fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-v", | |
151 | "Be verbose."); | |
152 | fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-f", | |
153 | "Force daemon to run."); | |
154 | fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-F", | |
155 | "Run daemon in the foreground."); | |
156 | fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-M", | |
157 | "Lock all pages in memory."); | |
158 | fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-Z", | |
159 | "Zero state file."); | |
160 | fprintf(fp, "\n"); | |
161 | #if 0 | |
162 | fprintf(fp, "%*c%*s %s [%s]\n", w1, 0x20, -w2, "-c FILE", | |
163 | "Read configuration from FILE.", ZED_CONF_FILE); | |
164 | #endif | |
165 | fprintf(fp, "%*c%*s %s [%s]\n", w1, 0x20, -w2, "-d DIR", | |
166 | "Read enabled scripts from DIR.", ZED_SCRIPT_DIR); | |
167 | fprintf(fp, "%*c%*s %s [%s]\n", w1, 0x20, -w2, "-p FILE", | |
168 | "Write daemon's PID to FILE.", ZED_PID_FILE); | |
169 | fprintf(fp, "%*c%*s %s [%s]\n", w1, 0x20, -w2, "-s FILE", | |
170 | "Write daemon's state to FILE.", ZED_STATE_FILE); | |
171 | fprintf(fp, "\n"); | |
172 | ||
173 | exit(got_err ? EXIT_FAILURE : EXIT_SUCCESS); | |
174 | } | |
175 | ||
176 | /* | |
177 | * Display license information to stdout and exit. | |
178 | */ | |
179 | static void | |
180 | _zed_conf_display_license(void) | |
181 | { | |
182 | const char **pp; | |
183 | const char *text[] = { | |
184 | "The ZFS Event Daemon (ZED) is distributed under the terms of the", | |
185 | " Common Development and Distribution License (CDDL-1.0)", | |
186 | " <http://opensource.org/licenses/CDDL-1.0>.", | |
187 | "Developed at Lawrence Livermore National Laboratory" | |
188 | " (LLNL-CODE-403049).", | |
189 | "Copyright (C) 2013-2014" | |
190 | " Lawrence Livermore National Security, LLC.", | |
191 | "", | |
192 | NULL | |
193 | }; | |
194 | ||
195 | for (pp = text; *pp; pp++) | |
196 | printf("%s\n", *pp); | |
197 | ||
198 | exit(EXIT_SUCCESS); | |
199 | } | |
200 | ||
201 | /* | |
202 | * Display version information to stdout and exit. | |
203 | */ | |
204 | static void | |
205 | _zed_conf_display_version(void) | |
206 | { | |
207 | printf("%s-%s-%s\n", | |
208 | ZFS_META_NAME, ZFS_META_VERSION, ZFS_META_RELEASE); | |
209 | ||
210 | exit(EXIT_SUCCESS); | |
211 | } | |
212 | ||
213 | /* | |
214 | * Copy the [path] string to the [resultp] ptr. | |
215 | * If [path] is not an absolute path, prefix it with the current working dir. | |
216 | * If [resultp] is non-null, free its existing string before assignment. | |
217 | */ | |
218 | static void | |
219 | _zed_conf_parse_path(char **resultp, const char *path) | |
220 | { | |
221 | char buf[PATH_MAX]; | |
222 | ||
223 | assert(resultp != NULL); | |
224 | assert(path != NULL); | |
225 | ||
226 | if (*resultp) | |
227 | free(*resultp); | |
228 | ||
229 | if (path[0] == '/') { | |
230 | *resultp = strdup(path); | |
231 | } else if (!getcwd(buf, sizeof (buf))) { | |
232 | zed_log_die("Failed to get current working dir: %s", | |
233 | strerror(errno)); | |
234 | } else if (strlcat(buf, "/", sizeof (buf)) >= sizeof (buf)) { | |
235 | zed_log_die("Failed to copy path: %s", strerror(ENAMETOOLONG)); | |
236 | } else if (strlcat(buf, path, sizeof (buf)) >= sizeof (buf)) { | |
237 | zed_log_die("Failed to copy path: %s", strerror(ENAMETOOLONG)); | |
238 | } else { | |
239 | *resultp = strdup(buf); | |
240 | } | |
241 | if (!*resultp) | |
242 | zed_log_die("Failed to copy path: %s", strerror(ENOMEM)); | |
243 | } | |
244 | ||
245 | /* | |
246 | * Parse the command-line options into the configuration [zcp]. | |
247 | */ | |
248 | void | |
249 | zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv) | |
250 | { | |
251 | const char * const opts = ":hLVc:d:p:s:vfFMZ"; | |
252 | int opt; | |
253 | ||
254 | if (!zcp || !argv || !argv[0]) | |
255 | zed_log_die("Failed to parse options: Internal error"); | |
256 | ||
257 | opterr = 0; /* suppress default getopt err msgs */ | |
258 | ||
259 | while ((opt = getopt(argc, argv, opts)) != -1) { | |
260 | switch (opt) { | |
261 | case 'h': | |
262 | _zed_conf_display_help(argv[0], EXIT_SUCCESS); | |
263 | break; | |
264 | case 'L': | |
265 | _zed_conf_display_license(); | |
266 | break; | |
267 | case 'V': | |
268 | _zed_conf_display_version(); | |
269 | break; | |
270 | case 'c': | |
271 | _zed_conf_parse_path(&zcp->conf_file, optarg); | |
272 | break; | |
273 | case 'd': | |
274 | _zed_conf_parse_path(&zcp->script_dir, optarg); | |
275 | break; | |
276 | case 'p': | |
277 | _zed_conf_parse_path(&zcp->pid_file, optarg); | |
278 | break; | |
279 | case 's': | |
280 | _zed_conf_parse_path(&zcp->state_file, optarg); | |
281 | break; | |
282 | case 'v': | |
283 | zcp->do_verbose = 1; | |
284 | break; | |
285 | case 'f': | |
286 | zcp->do_force = 1; | |
287 | break; | |
288 | case 'F': | |
289 | zcp->do_foreground = 1; | |
290 | break; | |
291 | case 'M': | |
292 | zcp->do_memlock = 1; | |
293 | break; | |
294 | case 'Z': | |
295 | zcp->do_zero = 1; | |
296 | break; | |
297 | case '?': | |
298 | default: | |
299 | if (optopt == '?') | |
300 | _zed_conf_display_help(argv[0], EXIT_SUCCESS); | |
301 | ||
302 | fprintf(stderr, "%s: %s '-%c'\n\n", argv[0], | |
303 | "Invalid option", optopt); | |
304 | _zed_conf_display_help(argv[0], EXIT_FAILURE); | |
305 | break; | |
306 | } | |
307 | } | |
308 | } | |
309 | ||
310 | /* | |
311 | * Parse the configuration file into the configuration [zcp]. | |
5043deaa | 312 | * |
9e246ac3 CD |
313 | * FIXME: Not yet implemented. |
314 | */ | |
315 | void | |
316 | zed_conf_parse_file(struct zed_conf *zcp) | |
317 | { | |
318 | if (!zcp) | |
319 | zed_log_die("Failed to parse config: %s", strerror(EINVAL)); | |
320 | } | |
321 | ||
322 | /* | |
323 | * Scan the [zcp] script_dir for files to exec based on the event class. | |
5043deaa CD |
324 | * Files must be executable by user, but not writable by group or other. |
325 | * Dotfiles are ignored. | |
326 | * | |
9e246ac3 | 327 | * Return 0 on success with an updated set of scripts, |
5043deaa CD |
328 | * or -1 on error with errno set. |
329 | * | |
9e246ac3 CD |
330 | * FIXME: Check if script_dir and all parent dirs are secure. |
331 | */ | |
332 | int | |
333 | zed_conf_scan_dir(struct zed_conf *zcp) | |
334 | { | |
335 | zed_strings_t *scripts; | |
336 | DIR *dirp; | |
337 | struct dirent *direntp; | |
338 | char pathname[PATH_MAX]; | |
339 | struct stat st; | |
340 | int n; | |
341 | ||
342 | if (!zcp) { | |
343 | errno = EINVAL; | |
344 | zed_log_msg(LOG_ERR, "Failed to scan script dir: %s", | |
345 | strerror(errno)); | |
346 | return (-1); | |
347 | } | |
348 | scripts = zed_strings_create(); | |
349 | if (!scripts) { | |
350 | errno = ENOMEM; | |
351 | zed_log_msg(LOG_WARNING, "Failed to scan dir \"%s\": %s", | |
352 | zcp->script_dir, strerror(errno)); | |
353 | return (-1); | |
354 | } | |
355 | dirp = opendir(zcp->script_dir); | |
356 | if (!dirp) { | |
357 | int errno_bak = errno; | |
358 | zed_log_msg(LOG_WARNING, "Failed to open dir \"%s\": %s", | |
359 | zcp->script_dir, strerror(errno)); | |
360 | zed_strings_destroy(scripts); | |
361 | errno = errno_bak; | |
362 | return (-1); | |
363 | } | |
364 | while ((direntp = readdir(dirp))) { | |
365 | if (direntp->d_name[0] == '.') | |
366 | continue; | |
367 | ||
368 | n = snprintf(pathname, sizeof (pathname), | |
369 | "%s/%s", zcp->script_dir, direntp->d_name); | |
370 | if ((n < 0) || (n >= sizeof (pathname))) { | |
371 | zed_log_msg(LOG_WARNING, "Failed to stat \"%s\": %s", | |
372 | direntp->d_name, strerror(ENAMETOOLONG)); | |
373 | continue; | |
374 | } | |
375 | if (stat(pathname, &st) < 0) { | |
376 | zed_log_msg(LOG_WARNING, "Failed to stat \"%s\": %s", | |
377 | pathname, strerror(errno)); | |
378 | continue; | |
379 | } | |
380 | if (!S_ISREG(st.st_mode)) { | |
381 | zed_log_msg(LOG_INFO, | |
382 | "Ignoring \"%s\": not a regular file", | |
383 | direntp->d_name); | |
384 | continue; | |
385 | } | |
386 | if ((st.st_uid != 0) && !zcp->do_force) { | |
387 | zed_log_msg(LOG_NOTICE, | |
388 | "Ignoring \"%s\": not owned by root", | |
389 | direntp->d_name); | |
390 | continue; | |
391 | } | |
392 | if (!(st.st_mode & S_IXUSR)) { | |
393 | zed_log_msg(LOG_INFO, | |
394 | "Ignoring \"%s\": not executable by user", | |
395 | direntp->d_name); | |
396 | continue; | |
397 | } | |
398 | if ((st.st_mode & S_IWGRP) & !zcp->do_force) { | |
399 | zed_log_msg(LOG_NOTICE, | |
400 | "Ignoring \"%s\": writable by group", | |
401 | direntp->d_name); | |
402 | continue; | |
403 | } | |
404 | if ((st.st_mode & S_IWOTH) & !zcp->do_force) { | |
405 | zed_log_msg(LOG_NOTICE, | |
406 | "Ignoring \"%s\": writable by other", | |
407 | direntp->d_name); | |
408 | continue; | |
409 | } | |
410 | if (zed_strings_add(scripts, direntp->d_name) < 0) { | |
411 | zed_log_msg(LOG_WARNING, | |
412 | "Failed to register \"%s\": %s", | |
413 | direntp->d_name, strerror(errno)); | |
414 | continue; | |
415 | } | |
416 | if (zcp->do_verbose) | |
417 | zed_log_msg(LOG_INFO, | |
418 | "Registered script \"%s\"", direntp->d_name); | |
419 | } | |
420 | if (closedir(dirp) < 0) { | |
421 | int errno_bak = errno; | |
422 | zed_log_msg(LOG_WARNING, "Failed to close dir \"%s\": %s", | |
423 | zcp->script_dir, strerror(errno)); | |
424 | zed_strings_destroy(scripts); | |
425 | errno = errno_bak; | |
426 | return (-1); | |
427 | } | |
428 | if (zcp->scripts) | |
429 | zed_strings_destroy(zcp->scripts); | |
430 | ||
431 | zcp->scripts = scripts; | |
432 | return (0); | |
433 | } | |
434 | ||
435 | /* | |
436 | * Write the PID file specified in [zcp]. | |
437 | * Return 0 on success, -1 on error. | |
5043deaa | 438 | * |
5a8855b7 | 439 | * This must be called after fork()ing to become a daemon (so the correct PID |
5043deaa CD |
440 | * is recorded), but before daemonization is complete and the parent process |
441 | * exits (for synchronization with systemd). | |
442 | * | |
5a8855b7 | 443 | * FIXME: Only update the PID file after verifying the PID previously stored |
5043deaa CD |
444 | * in the PID file no longer exists or belongs to a foreign process |
445 | * in order to ensure the daemon cannot be started more than once. | |
446 | * (This check is currently done by zed_conf_open_state().) | |
9e246ac3 CD |
447 | */ |
448 | int | |
449 | zed_conf_write_pid(struct zed_conf *zcp) | |
450 | { | |
451 | char dirbuf[PATH_MAX]; | |
6ac770b1 | 452 | mode_t dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; |
9e246ac3 CD |
453 | int n; |
454 | char *p; | |
455 | mode_t mask; | |
456 | FILE *fp; | |
457 | ||
458 | if (!zcp || !zcp->pid_file) { | |
459 | errno = EINVAL; | |
8125fb71 | 460 | zed_log_msg(LOG_ERR, "Failed to write PID file: %s", |
9e246ac3 CD |
461 | strerror(errno)); |
462 | return (-1); | |
463 | } | |
464 | n = strlcpy(dirbuf, zcp->pid_file, sizeof (dirbuf)); | |
465 | if (n >= sizeof (dirbuf)) { | |
466 | errno = ENAMETOOLONG; | |
8125fb71 | 467 | zed_log_msg(LOG_WARNING, "Failed to write PID file: %s", |
9e246ac3 CD |
468 | strerror(errno)); |
469 | return (-1); | |
470 | } | |
471 | p = strrchr(dirbuf, '/'); | |
472 | if (p) | |
473 | *p = '\0'; | |
474 | ||
6ac770b1 CD |
475 | if ((mkdirp(dirbuf, dirmode) < 0) && (errno != EEXIST)) { |
476 | zed_log_msg(LOG_WARNING, | |
477 | "Failed to create directory \"%s\": %s", | |
478 | dirbuf, strerror(errno)); | |
9e246ac3 | 479 | return (-1); |
6ac770b1 | 480 | } |
9e246ac3 CD |
481 | (void) unlink(zcp->pid_file); |
482 | ||
483 | mask = umask(0); | |
484 | umask(mask | 022); | |
485 | fp = fopen(zcp->pid_file, "w"); | |
486 | umask(mask); | |
487 | ||
488 | if (!fp) { | |
8125fb71 | 489 | zed_log_msg(LOG_WARNING, "Failed to open PID file \"%s\": %s", |
9e246ac3 CD |
490 | zcp->pid_file, strerror(errno)); |
491 | } else if (fprintf(fp, "%d\n", (int) getpid()) == EOF) { | |
8125fb71 | 492 | zed_log_msg(LOG_WARNING, "Failed to write PID file \"%s\": %s", |
9e246ac3 CD |
493 | zcp->pid_file, strerror(errno)); |
494 | } else if (fclose(fp) == EOF) { | |
8125fb71 | 495 | zed_log_msg(LOG_WARNING, "Failed to close PID file \"%s\": %s", |
9e246ac3 CD |
496 | zcp->pid_file, strerror(errno)); |
497 | } else { | |
498 | return (0); | |
499 | } | |
500 | (void) unlink(zcp->pid_file); | |
501 | return (-1); | |
502 | } | |
503 | ||
504 | /* | |
505 | * Open and lock the [zcp] state_file. | |
506 | * Return 0 on success, -1 on error. | |
5043deaa | 507 | * |
9e246ac3 CD |
508 | * FIXME: If state_file exists, verify ownership & permissions. |
509 | * FIXME: Move lock to pid_file instead. | |
510 | */ | |
511 | int | |
512 | zed_conf_open_state(struct zed_conf *zcp) | |
513 | { | |
514 | char dirbuf[PATH_MAX]; | |
6ac770b1 | 515 | mode_t dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; |
9e246ac3 CD |
516 | int n; |
517 | char *p; | |
518 | int rv; | |
519 | ||
520 | if (!zcp || !zcp->state_file) { | |
521 | errno = EINVAL; | |
522 | zed_log_msg(LOG_ERR, "Failed to open state file: %s", | |
523 | strerror(errno)); | |
524 | return (-1); | |
525 | } | |
526 | n = strlcpy(dirbuf, zcp->state_file, sizeof (dirbuf)); | |
527 | if (n >= sizeof (dirbuf)) { | |
528 | errno = ENAMETOOLONG; | |
529 | zed_log_msg(LOG_WARNING, "Failed to open state file: %s", | |
530 | strerror(errno)); | |
531 | return (-1); | |
532 | } | |
533 | p = strrchr(dirbuf, '/'); | |
534 | if (p) | |
535 | *p = '\0'; | |
536 | ||
6ac770b1 CD |
537 | if ((mkdirp(dirbuf, dirmode) < 0) && (errno != EEXIST)) { |
538 | zed_log_msg(LOG_WARNING, | |
539 | "Failed to create directory \"%s\": %s", | |
540 | dirbuf, strerror(errno)); | |
9e246ac3 | 541 | return (-1); |
6ac770b1 | 542 | } |
9e246ac3 CD |
543 | if (zcp->state_fd >= 0) { |
544 | if (close(zcp->state_fd) < 0) { | |
545 | zed_log_msg(LOG_WARNING, | |
546 | "Failed to close state file \"%s\": %s", | |
547 | zcp->state_file, strerror(errno)); | |
548 | return (-1); | |
549 | } | |
550 | } | |
551 | if (zcp->do_zero) | |
552 | (void) unlink(zcp->state_file); | |
553 | ||
554 | zcp->state_fd = open(zcp->state_file, | |
555 | (O_RDWR | O_CREAT), (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); | |
556 | if (zcp->state_fd < 0) { | |
557 | zed_log_msg(LOG_WARNING, "Failed to open state file \"%s\": %s", | |
558 | zcp->state_file, strerror(errno)); | |
559 | return (-1); | |
560 | } | |
561 | rv = zed_file_lock(zcp->state_fd); | |
562 | if (rv < 0) { | |
563 | zed_log_msg(LOG_WARNING, "Failed to lock state file \"%s\": %s", | |
564 | zcp->state_file, strerror(errno)); | |
565 | return (-1); | |
566 | } | |
567 | if (rv > 0) { | |
568 | pid_t pid = zed_file_is_locked(zcp->state_fd); | |
569 | if (pid < 0) { | |
570 | zed_log_msg(LOG_WARNING, | |
571 | "Failed to test lock on state file \"%s\"", | |
572 | zcp->state_file); | |
573 | } else if (pid > 0) { | |
574 | zed_log_msg(LOG_WARNING, | |
8125fb71 | 575 | "Found PID %d bound to state file \"%s\"", |
9e246ac3 CD |
576 | pid, zcp->state_file); |
577 | } else { | |
578 | zed_log_msg(LOG_WARNING, | |
579 | "Inconsistent lock state on state file \"%s\"", | |
580 | zcp->state_file); | |
581 | } | |
582 | return (-1); | |
583 | } | |
584 | return (0); | |
585 | } | |
586 | ||
587 | /* | |
5043deaa CD |
588 | * Read the opened [zcp] state_file to obtain the eid & etime of the last event |
589 | * processed. Write the state from the last event to the [eidp] & [etime] args | |
590 | * passed by reference. Note that etime[] is an array of size 2. | |
9e246ac3 CD |
591 | * Return 0 on success, -1 on error. |
592 | */ | |
593 | int | |
594 | zed_conf_read_state(struct zed_conf *zcp, uint64_t *eidp, int64_t etime[]) | |
595 | { | |
596 | ssize_t len; | |
597 | struct iovec iov[3]; | |
598 | ssize_t n; | |
599 | ||
600 | if (!zcp || !eidp || !etime) { | |
601 | errno = EINVAL; | |
602 | zed_log_msg(LOG_ERR, | |
603 | "Failed to read state file: %s", strerror(errno)); | |
604 | return (-1); | |
605 | } | |
606 | if (lseek(zcp->state_fd, 0, SEEK_SET) == (off_t) -1) { | |
607 | zed_log_msg(LOG_WARNING, | |
608 | "Failed to reposition state file offset: %s", | |
609 | strerror(errno)); | |
610 | return (-1); | |
611 | } | |
612 | len = 0; | |
613 | iov[0].iov_base = eidp; | |
614 | len += iov[0].iov_len = sizeof (*eidp); | |
615 | iov[1].iov_base = &etime[0]; | |
616 | len += iov[1].iov_len = sizeof (etime[0]); | |
617 | iov[2].iov_base = &etime[1]; | |
618 | len += iov[2].iov_len = sizeof (etime[1]); | |
619 | ||
620 | n = readv(zcp->state_fd, iov, 3); | |
621 | if (n == 0) { | |
622 | *eidp = 0; | |
623 | } else if (n < 0) { | |
624 | zed_log_msg(LOG_WARNING, | |
625 | "Failed to read state file \"%s\": %s", | |
626 | zcp->state_file, strerror(errno)); | |
627 | return (-1); | |
628 | } else if (n != len) { | |
629 | errno = EIO; | |
630 | zed_log_msg(LOG_WARNING, | |
631 | "Failed to read state file \"%s\": Read %d of %d bytes", | |
632 | zcp->state_file, n, len); | |
633 | return (-1); | |
634 | } | |
635 | return (0); | |
636 | } | |
637 | ||
638 | /* | |
639 | * Write the [eid] & [etime] of the last processed event to the opened | |
5043deaa | 640 | * [zcp] state_file. Note that etime[] is an array of size 2. |
9e246ac3 CD |
641 | * Return 0 on success, -1 on error. |
642 | */ | |
643 | int | |
644 | zed_conf_write_state(struct zed_conf *zcp, uint64_t eid, int64_t etime[]) | |
645 | { | |
646 | ssize_t len; | |
647 | struct iovec iov[3]; | |
648 | ssize_t n; | |
649 | ||
650 | if (!zcp) { | |
651 | errno = EINVAL; | |
652 | zed_log_msg(LOG_ERR, | |
653 | "Failed to write state file: %s", strerror(errno)); | |
654 | return (-1); | |
655 | } | |
656 | if (lseek(zcp->state_fd, 0, SEEK_SET) == (off_t) -1) { | |
657 | zed_log_msg(LOG_WARNING, | |
658 | "Failed to reposition state file offset: %s", | |
659 | strerror(errno)); | |
660 | return (-1); | |
661 | } | |
662 | len = 0; | |
663 | iov[0].iov_base = &eid; | |
664 | len += iov[0].iov_len = sizeof (eid); | |
665 | iov[1].iov_base = &etime[0]; | |
666 | len += iov[1].iov_len = sizeof (etime[0]); | |
667 | iov[2].iov_base = &etime[1]; | |
668 | len += iov[2].iov_len = sizeof (etime[1]); | |
669 | ||
670 | n = writev(zcp->state_fd, iov, 3); | |
671 | if (n < 0) { | |
672 | zed_log_msg(LOG_WARNING, | |
673 | "Failed to write state file \"%s\": %s", | |
674 | zcp->state_file, strerror(errno)); | |
675 | return (-1); | |
676 | } | |
677 | if (n != len) { | |
678 | errno = EIO; | |
679 | zed_log_msg(LOG_WARNING, | |
680 | "Failed to write state file \"%s\": Wrote %d of %d bytes", | |
681 | zcp->state_file, n, len); | |
682 | return (-1); | |
683 | } | |
684 | if (fdatasync(zcp->state_fd) < 0) { | |
685 | zed_log_msg(LOG_WARNING, | |
686 | "Failed to sync state file \"%s\": %s", | |
687 | zcp->state_file, strerror(errno)); | |
688 | return (-1); | |
689 | } | |
690 | return (0); | |
691 | } |