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, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at http://smartos.org/CDDL
10 *
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file.
16 *
17 * If applicable, add the following below this CDDL HEADER, with the
18 * fields enclosed by brackets "[]" replaced with your own identifying
19 * information: Portions Copyright [yyyy] [name of copyright owner]
20 *
21 * CDDL HEADER END
22 *
23 * Copyright 2020 Joyent, Inc.
24 *
25 */
26
27 #include <err.h>
28 #include <errno.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <sys/debug.h>
34 #include <sys/sysmacros.h>
35 #include <sys/types.h>
36 #include <libsysevent.h>
37 #include <sys/sysevent/eventdefs.h>
38
39 FILE *out;
40
41 static void
process_event(sysevent_t * ev)42 process_event(sysevent_t *ev)
43 {
44 char *class = NULL;
45 char *subclass = NULL;
46
47 /* get sysevent metadata and add to the nvlist */
48 class = sysevent_get_class_name(ev);
49 subclass = sysevent_get_subclass_name(ev);
50
51 if (class == NULL || subclass == NULL)
52 errx(EXIT_FAILURE, "failed to retrieve sysevent metadata");
53
54 VERIFY0(strcmp(class, EC_ZFS));
55
56 flockfile(out);
57 (void) fprintf(out, "Received %s.%s event\n", class, subclass);
58 (void) fflush(out);
59 funlockfile(out);
60 }
61
62 static void
child_fatal(int fd,const char * msg,...)63 child_fatal(int fd, const char *msg, ...)
64 {
65 va_list ap;
66 int fail = EXIT_FAILURE;
67
68 va_start(ap, msg);
69 (void) vfprintf(stderr, msg, ap);
70 va_end(ap);
71 (void) fputc('\n', stderr);
72
73 (void) write(fd, &fail, sizeof (fail));
74 (void) close(fd);
75 exit(EXIT_FAILURE);
76 }
77
78 static void
do_child(int fd,char * const subclasses[],size_t n)79 do_child(int fd, char * const subclasses[], size_t n)
80 {
81 sysevent_handle_t *handle;
82 int ret = 0;
83
84 if ((handle = sysevent_bind_handle(process_event)) == NULL) {
85 child_fatal(fd, "sysevent_bind_handle() failed: %s",
86 strerror(errno));
87 }
88
89 if (sysevent_subscribe_event(handle, EC_ZFS,
90 (const char **)subclasses, n) != 0) {
91 child_fatal(fd, "failed to subscribe to sysevents: %s",
92 strerror(errno));
93 }
94
95 (void) write(fd, &ret, sizeof (ret));
96 (void) close(fd);
97
98 /* leave stderr open so any errors get captured by test harness */
99 (void) fclose(stdin);
100 (void) fclose(stdout);
101
102 for (;;)
103 (void) pause();
104 }
105
106 static void
usage(const char * name)107 usage(const char *name)
108 {
109 (void) fprintf(stderr, "Usage: %s [-o outfile] zfs_event...\n", name);
110 exit(2);
111 }
112
113 int
main(int argc,char * const argv[])114 main(int argc, char * const argv[])
115 {
116 const char *outfile = NULL;
117 pid_t child;
118 int fds[2];
119 int ret = 0;
120 int c;
121
122 while ((c = getopt(argc, argv, "o:")) != -1) {
123 switch (c) {
124 case 'o':
125 outfile = optarg;
126 break;
127 case '?':
128 (void) fprintf(stderr, "Invalid option -%c\n", optopt);
129 usage(argv[0]);
130 }
131 }
132
133 if (outfile != NULL) {
134 if ((out = fopen(optarg, "w")) == NULL)
135 err(EXIT_FAILURE, "unable to open %s", optarg);
136 } else {
137 out = stdout;
138 }
139
140 VERIFY0(pipe(fds));
141
142 switch (child = fork()) {
143 case -1:
144 err(EXIT_FAILURE, "unable to fork");
145 case 0:
146 do_child(fds[1], argv + optind, (size_t)(argc - optind));
147 break;
148 default:
149 break;
150 }
151
152 (void) close(fds[1]);
153
154 if (read(fds[0], &ret, sizeof (ret)) < 0)
155 err(EXIT_FAILURE, "failure waiting on child");
156
157 if (ret != 0)
158 return (ret);
159
160 (void) close(fds[0]);
161 (void) printf("%d\n", child);
162 return (0);
163 }
164