xref: /openbsd-src/usr.sbin/snmpd/snmpd.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: snmpd.c,v 1.8 2008/09/26 15:19:55 reyk Exp $	*/
2 
3 /*
4  * Copyright (c) 2007, 2008 Reyk Floeter <reyk@vantronix.net>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/socket.h>
22 #include <sys/param.h>
23 #include <sys/wait.h>
24 #include <sys/tree.h>
25 
26 #include <net/if.h>
27 
28 #include <string.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <getopt.h>
32 #include <err.h>
33 #include <errno.h>
34 #include <event.h>
35 #include <signal.h>
36 #include <unistd.h>
37 #include <pwd.h>
38 
39 #include "snmpd.h"
40 
41 __dead void	 usage(void);
42 
43 void		 snmpd_sig_handler(int, short, void *);
44 void		 snmpd_shutdown(struct snmpd *);
45 void		 snmpd_dispatch_snmpe(int, short, void *);
46 int		 check_child(pid_t, const char *);
47 
48 struct snmpd	*snmpd_env;
49 
50 int		 pipe_parent2snmpe[2];
51 struct imsgbuf	*ibuf_snmpe;
52 pid_t		 snmpe_pid = 0;
53 
54 void
55 snmpd_sig_handler(int sig, short event, void *arg)
56 {
57 	struct snmpd	*env = arg;
58 	int			 die = 0;
59 
60 	switch (sig) {
61 	case SIGTERM:
62 	case SIGINT:
63 		die = 1;
64 		/* FALLTHROUGH */
65 	case SIGCHLD:
66 		if (check_child(snmpe_pid, "snmp engine")) {
67 			snmpe_pid = 0;
68 			die  = 1;
69 		}
70 		if (die)
71 			snmpd_shutdown(env);
72 		break;
73 	case SIGHUP:
74 		/* reconfigure */
75 		break;
76 	default:
77 		fatalx("unexpected signal");
78 	}
79 }
80 
81 /* __dead is for lint */
82 __dead void
83 usage(void)
84 {
85 	extern char	*__progname;
86 
87 	fprintf(stderr, "usage: %s [-dNnv] [-D macro=value] "
88 	    "[-f file] [-r path]\n", __progname);
89 	exit(1);
90 }
91 
92 int
93 main(int argc, char *argv[])
94 {
95 	int			 c;
96 	struct snmpd		*env;
97 	struct event		 ev_sigint;
98 	struct event		 ev_sigterm;
99 	struct event		 ev_sigchld;
100 	struct event		 ev_sighup;
101 	int			 debug = 0;
102 	u_int			 flags = 0;
103 	int			 noaction = 0;
104 	const char		*conffile = CONF_FILE;
105 	const char		*rcsock = NULL;
106 
107 	smi_init();
108 
109 	log_init(1);	/* log to stderr until daemonized */
110 
111 	while ((c = getopt(argc, argv, "dD:nNf:r:v")) != -1) {
112 		switch (c) {
113 		case 'd':
114 			debug = 1;
115 			break;
116 		case 'D':
117 			if (cmdline_symset(optarg) < 0)
118 				log_warnx("could not parse macro definition %s",
119 				    optarg);
120 			break;
121 		case 'n':
122 			noaction++;
123 			break;
124 		case 'N':
125 			flags |= SNMPD_F_NONAMES;
126 			break;
127 		case 'f':
128 			conffile = optarg;
129 			break;
130 		case 'r':
131 			rcsock = optarg;
132 			break;
133 		case 'v':
134 			flags |= SNMPD_F_VERBOSE;
135 			break;
136 		default:
137 			usage();
138 		}
139 	}
140 
141 	argc -= optind;
142 	argv += optind;
143 	if (argc > 0)
144 		usage();
145 
146 	if ((env = parse_config(conffile, flags)) == NULL)
147 		exit(1);
148 	snmpd_env = env;
149 
150 	if (noaction) {
151 		fprintf(stderr, "configuration ok\n");
152 		exit(0);
153 	}
154 
155 	if (geteuid())
156 		errx(1, "need root privileges");
157 
158 	if (getpwnam(SNMPD_USER) == NULL)
159 		errx(1, "unknown user %s", SNMPD_USER);
160 
161 	/* Configure the control sockets */
162 	env->sc_csock.cs_name = SNMPD_SOCKET;
163 	env->sc_rcsock.cs_name = rcsock;
164 	env->sc_rcsock.cs_restricted = 1;
165 
166 	log_init(debug);
167 
168 	if (!debug) {
169 		if (daemon(1, 0) == -1)
170 			err(1, "failed to daemonize");
171 	}
172 
173 	gettimeofday(&env->sc_starttime, NULL);
174 
175 	log_info("startup");
176 
177 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
178 	    pipe_parent2snmpe) == -1)
179 		fatal("socketpair");
180 
181 	session_socket_blockmode(pipe_parent2snmpe[0], BM_NONBLOCK);
182 	session_socket_blockmode(pipe_parent2snmpe[1], BM_NONBLOCK);
183 
184 	snmpe_pid = snmpe(env, pipe_parent2snmpe);
185 	setproctitle("parent");
186 
187 	event_init();
188 
189 	signal_set(&ev_sigint, SIGINT, snmpd_sig_handler, env);
190 	signal_set(&ev_sigterm, SIGTERM, snmpd_sig_handler, env);
191 	signal_set(&ev_sigchld, SIGCHLD, snmpd_sig_handler, env);
192 	signal_set(&ev_sighup, SIGHUP, snmpd_sig_handler, env);
193 	signal_add(&ev_sigint, NULL);
194 	signal_add(&ev_sigterm, NULL);
195 	signal_add(&ev_sigchld, NULL);
196 	signal_add(&ev_sighup, NULL);
197 	signal(SIGPIPE, SIG_IGN);
198 
199 	close(pipe_parent2snmpe[1]);
200 
201 	if ((ibuf_snmpe = calloc(1, sizeof(struct imsgbuf))) == NULL)
202 		fatal(NULL);
203 
204 	imsg_init(ibuf_snmpe, pipe_parent2snmpe[0], snmpd_dispatch_snmpe);
205 
206 	ibuf_snmpe->events = EV_READ;
207 	event_set(&ibuf_snmpe->ev, ibuf_snmpe->fd, ibuf_snmpe->events,
208 	    ibuf_snmpe->handler, ibuf_snmpe);
209 	event_add(&ibuf_snmpe->ev, NULL);
210 
211 	event_dispatch();
212 
213 	return (0);
214 }
215 
216 void
217 snmpd_shutdown(struct snmpd *env)
218 {
219 	pid_t	pid;
220 
221 	if (snmpe_pid)
222 		kill(snmpe_pid, SIGTERM);
223 
224 	do {
225 		if ((pid = wait(NULL)) == -1 &&
226 		    errno != EINTR && errno != ECHILD)
227 			fatal("wait");
228 	} while (pid != -1 || (pid == -1 && errno == EINTR));
229 
230 	control_cleanup(&env->sc_csock);
231 	control_cleanup(&env->sc_rcsock);
232 	log_info("terminating");
233 	exit(0);
234 }
235 
236 int
237 check_child(pid_t pid, const char *pname)
238 {
239 	int	status;
240 
241 	if (waitpid(pid, &status, WNOHANG) > 0) {
242 		if (WIFEXITED(status)) {
243 			log_warnx("check_child: lost child: %s exited", pname);
244 			return (1);
245 		}
246 		if (WIFSIGNALED(status)) {
247 			log_warnx("check_child: lost child: %s terminated; "
248 			    "signal %d", pname, WTERMSIG(status));
249 			return (1);
250 		}
251 	}
252 
253 	return (0);
254 }
255 
256 void
257 imsg_event_add(struct imsgbuf *ibuf)
258 {
259 	ibuf->events = EV_READ;
260 	if (ibuf->w.queued)
261 		ibuf->events |= EV_WRITE;
262 
263 	event_del(&ibuf->ev);
264 	event_set(&ibuf->ev, ibuf->fd, ibuf->events, ibuf->handler, ibuf);
265 	event_add(&ibuf->ev, NULL);
266 }
267 
268 void
269 snmpd_dispatch_snmpe(int fd, short event, void * ptr)
270 {
271 	struct imsgbuf		*ibuf;
272 	struct imsg		 imsg;
273 	ssize_t			 n;
274 
275 	ibuf = ptr;
276 	switch (event) {
277 	case EV_READ:
278 		if ((n = imsg_read(ibuf)) == -1)
279 			fatal("imsg_read error");
280 		if (n == 0) {
281 			/* this pipe is dead, so remove the event handler */
282 			event_del(&ibuf->ev);
283 			event_loopexit(NULL);
284 			return;
285 		}
286 		break;
287 	case EV_WRITE:
288 		if (msgbuf_write(&ibuf->w) == -1)
289 			fatal("msgbuf_write");
290 		imsg_event_add(ibuf);
291 		return;
292 	default:
293 		fatalx("unknown event");
294 	}
295 
296 	for (;;) {
297 		if ((n = imsg_get(ibuf, &imsg)) == -1)
298 			fatal("snmpd_dispatch_relay: imsg_read error");
299 		if (n == 0)
300 			break;
301 
302 		switch (imsg.hdr.type) {
303 		default:
304 			log_debug("snmpd_dispatch_relay: unexpected imsg %d",
305 			    imsg.hdr.type);
306 			break;
307 		}
308 		imsg_free(&imsg);
309 	}
310 	imsg_event_add(ibuf);
311 }
312 
313 int
314 snmpd_socket_af(struct sockaddr_storage *ss, in_port_t port)
315 {
316 	int	 s;
317 
318 	switch (ss->ss_family) {
319 	case AF_INET:
320 		((struct sockaddr_in *)ss)->sin_port = port;
321 		((struct sockaddr_in *)ss)->sin_len =
322 		    sizeof(struct sockaddr_in);
323 		break;
324 	case AF_INET6:
325 		((struct sockaddr_in6 *)ss)->sin6_port = port;
326 		((struct sockaddr_in6 *)ss)->sin6_len =
327 		    sizeof(struct sockaddr_in6);
328 		break;
329 	default:
330 		return (-1);
331 	}
332 
333 	s = socket(ss->ss_family, SOCK_DGRAM, IPPROTO_UDP);
334 	return (s);
335 }
336 
337