xref: /openbsd-src/usr.sbin/snmpd/snmpd.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: snmpd.c,v 1.11 2012/05/28 20:55:40 joel 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 imsgev	*iev_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 	pf_init();
178 
179 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
180 	    pipe_parent2snmpe) == -1)
181 		fatal("socketpair");
182 
183 	session_socket_blockmode(pipe_parent2snmpe[0], BM_NONBLOCK);
184 	session_socket_blockmode(pipe_parent2snmpe[1], BM_NONBLOCK);
185 
186 	snmpe_pid = snmpe(env, pipe_parent2snmpe);
187 	setproctitle("parent");
188 
189 	event_init();
190 
191 	signal_set(&ev_sigint, SIGINT, snmpd_sig_handler, env);
192 	signal_set(&ev_sigterm, SIGTERM, snmpd_sig_handler, env);
193 	signal_set(&ev_sigchld, SIGCHLD, snmpd_sig_handler, env);
194 	signal_set(&ev_sighup, SIGHUP, snmpd_sig_handler, env);
195 	signal_add(&ev_sigint, NULL);
196 	signal_add(&ev_sigterm, NULL);
197 	signal_add(&ev_sigchld, NULL);
198 	signal_add(&ev_sighup, NULL);
199 	signal(SIGPIPE, SIG_IGN);
200 
201 	close(pipe_parent2snmpe[1]);
202 
203 	if ((iev_snmpe = calloc(1, sizeof(struct imsgev))) == NULL)
204 		fatal(NULL);
205 
206 	imsg_init(&iev_snmpe->ibuf, pipe_parent2snmpe[0]);
207 	iev_snmpe->handler = snmpd_dispatch_snmpe;
208 
209 	iev_snmpe->events = EV_READ;
210 	event_set(&iev_snmpe->ev, iev_snmpe->ibuf.fd, iev_snmpe->events,
211 	    iev_snmpe->handler, iev_snmpe);
212 	event_add(&iev_snmpe->ev, NULL);
213 
214 	event_dispatch();
215 
216 	return (0);
217 }
218 
219 void
220 snmpd_shutdown(struct snmpd *env)
221 {
222 	pid_t	pid;
223 
224 	if (snmpe_pid)
225 		kill(snmpe_pid, SIGTERM);
226 
227 	do {
228 		if ((pid = wait(NULL)) == -1 &&
229 		    errno != EINTR && errno != ECHILD)
230 			fatal("wait");
231 	} while (pid != -1 || (pid == -1 && errno == EINTR));
232 
233 	control_cleanup(&env->sc_csock);
234 	control_cleanup(&env->sc_rcsock);
235 	log_info("terminating");
236 	exit(0);
237 }
238 
239 int
240 check_child(pid_t pid, const char *pname)
241 {
242 	int	status;
243 
244 	if (waitpid(pid, &status, WNOHANG) > 0) {
245 		if (WIFEXITED(status)) {
246 			log_warnx("check_child: lost child: %s exited", pname);
247 			return (1);
248 		}
249 		if (WIFSIGNALED(status)) {
250 			log_warnx("check_child: lost child: %s terminated; "
251 			    "signal %d", pname, WTERMSIG(status));
252 			return (1);
253 		}
254 	}
255 
256 	return (0);
257 }
258 
259 void
260 imsg_event_add(struct imsgev *iev)
261 {
262 	iev->events = EV_READ;
263 	if (iev->ibuf.w.queued)
264 		iev->events |= EV_WRITE;
265 
266 	event_del(&iev->ev);
267 	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
268 	event_add(&iev->ev, NULL);
269 }
270 
271 void
272 snmpd_dispatch_snmpe(int fd, short event, void * ptr)
273 {
274 	struct imsgev		*iev;
275 	struct imsgbuf		*ibuf;
276 	struct imsg		 imsg;
277 	ssize_t			 n;
278 
279 	iev = ptr;
280 	ibuf = &iev->ibuf;
281 	switch (event) {
282 	case EV_READ:
283 		if ((n = imsg_read(ibuf)) == -1)
284 			fatal("imsg_read error");
285 		if (n == 0) {
286 			/* this pipe is dead, so remove the event handler */
287 			event_del(&iev->ev);
288 			event_loopexit(NULL);
289 			return;
290 		}
291 		break;
292 	case EV_WRITE:
293 		if (msgbuf_write(&ibuf->w) == -1)
294 			fatal("msgbuf_write");
295 		imsg_event_add(iev);
296 		return;
297 	default:
298 		fatalx("unknown event");
299 	}
300 
301 	for (;;) {
302 		if ((n = imsg_get(ibuf, &imsg)) == -1)
303 			fatal("snmpd_dispatch_relay: imsg_read error");
304 		if (n == 0)
305 			break;
306 
307 		switch (imsg.hdr.type) {
308 		default:
309 			log_debug("snmpd_dispatch_relay: unexpected imsg %d",
310 			    imsg.hdr.type);
311 			break;
312 		}
313 		imsg_free(&imsg);
314 	}
315 	imsg_event_add(iev);
316 }
317 
318 int
319 snmpd_socket_af(struct sockaddr_storage *ss, in_port_t port)
320 {
321 	int	 s;
322 
323 	switch (ss->ss_family) {
324 	case AF_INET:
325 		((struct sockaddr_in *)ss)->sin_port = port;
326 		((struct sockaddr_in *)ss)->sin_len =
327 		    sizeof(struct sockaddr_in);
328 		break;
329 	case AF_INET6:
330 		((struct sockaddr_in6 *)ss)->sin6_port = port;
331 		((struct sockaddr_in6 *)ss)->sin6_len =
332 		    sizeof(struct sockaddr_in6);
333 		break;
334 	default:
335 		return (-1);
336 	}
337 
338 	s = socket(ss->ss_family, SOCK_DGRAM, IPPROTO_UDP);
339 	return (s);
340 }
341 
342