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