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