xref: /openbsd/usr.sbin/snmpd/snmpd.c (revision 7b36286a)
1 /*	$OpenBSD: snmpd.c,v 1.7 2008/05/12 19:15:02 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 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] [-f file]\n",
88 	    __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 
106 	smi_init();
107 
108 	log_init(1);	/* log to stderr until daemonized */
109 
110 	while ((c = getopt(argc, argv, "dD:nNf:v")) != -1) {
111 		switch (c) {
112 		case 'd':
113 			debug = 1;
114 			break;
115 		case 'D':
116 			if (cmdline_symset(optarg) < 0)
117 				log_warnx("could not parse macro definition %s",
118 				    optarg);
119 			break;
120 		case 'n':
121 			noaction++;
122 			break;
123 		case 'N':
124 			flags |= SNMPD_F_NONAMES;
125 			break;
126 		case 'f':
127 			conffile = optarg;
128 			break;
129 		case 'v':
130 			flags |= SNMPD_F_VERBOSE;
131 			break;
132 		default:
133 			usage();
134 		}
135 	}
136 
137 	argc -= optind;
138 	argv += optind;
139 	if (argc > 0)
140 		usage();
141 
142 	if ((env = parse_config(conffile, flags)) == NULL)
143 		exit(1);
144 	snmpd_env = env;
145 
146 	if (noaction) {
147 		fprintf(stderr, "configuration ok\n");
148 		exit(0);
149 	}
150 
151 	if (geteuid())
152 		errx(1, "need root privileges");
153 
154 	if (getpwnam(SNMPD_USER) == NULL)
155 		errx(1, "unknown user %s", SNMPD_USER);
156 
157 	log_init(debug);
158 
159 	if (!debug) {
160 		if (daemon(1, 0) == -1)
161 			err(1, "failed to daemonize");
162 	}
163 
164 	gettimeofday(&env->sc_starttime, NULL);
165 
166 	log_info("startup");
167 
168 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
169 	    pipe_parent2snmpe) == -1)
170 		fatal("socketpair");
171 
172 	session_socket_blockmode(pipe_parent2snmpe[0], BM_NONBLOCK);
173 	session_socket_blockmode(pipe_parent2snmpe[1], BM_NONBLOCK);
174 
175 	snmpe_pid = snmpe(env, pipe_parent2snmpe);
176 	setproctitle("parent");
177 
178 	event_init();
179 
180 	signal_set(&ev_sigint, SIGINT, snmpd_sig_handler, env);
181 	signal_set(&ev_sigterm, SIGTERM, snmpd_sig_handler, env);
182 	signal_set(&ev_sigchld, SIGCHLD, snmpd_sig_handler, env);
183 	signal_set(&ev_sighup, SIGHUP, snmpd_sig_handler, env);
184 	signal_add(&ev_sigint, NULL);
185 	signal_add(&ev_sigterm, NULL);
186 	signal_add(&ev_sigchld, NULL);
187 	signal_add(&ev_sighup, NULL);
188 	signal(SIGPIPE, SIG_IGN);
189 
190 	close(pipe_parent2snmpe[1]);
191 
192 	if ((ibuf_snmpe = calloc(1, sizeof(struct imsgbuf))) == NULL)
193 		fatal(NULL);
194 
195 	imsg_init(ibuf_snmpe, pipe_parent2snmpe[0], snmpd_dispatch_snmpe);
196 
197 	ibuf_snmpe->events = EV_READ;
198 	event_set(&ibuf_snmpe->ev, ibuf_snmpe->fd, ibuf_snmpe->events,
199 	    ibuf_snmpe->handler, ibuf_snmpe);
200 	event_add(&ibuf_snmpe->ev, NULL);
201 
202 	event_dispatch();
203 
204 	return (0);
205 }
206 
207 void
208 snmpd_shutdown(struct snmpd *env)
209 {
210 	pid_t	pid;
211 
212 	if (snmpe_pid)
213 		kill(snmpe_pid, SIGTERM);
214 
215 	do {
216 		if ((pid = wait(NULL)) == -1 &&
217 		    errno != EINTR && errno != ECHILD)
218 			fatal("wait");
219 	} while (pid != -1 || (pid == -1 && errno == EINTR));
220 
221 	control_cleanup();
222 	log_info("terminating");
223 	exit(0);
224 }
225 
226 int
227 check_child(pid_t pid, const char *pname)
228 {
229 	int	status;
230 
231 	if (waitpid(pid, &status, WNOHANG) > 0) {
232 		if (WIFEXITED(status)) {
233 			log_warnx("check_child: lost child: %s exited", pname);
234 			return (1);
235 		}
236 		if (WIFSIGNALED(status)) {
237 			log_warnx("check_child: lost child: %s terminated; "
238 			    "signal %d", pname, WTERMSIG(status));
239 			return (1);
240 		}
241 	}
242 
243 	return (0);
244 }
245 
246 void
247 imsg_event_add(struct imsgbuf *ibuf)
248 {
249 	ibuf->events = EV_READ;
250 	if (ibuf->w.queued)
251 		ibuf->events |= EV_WRITE;
252 
253 	event_del(&ibuf->ev);
254 	event_set(&ibuf->ev, ibuf->fd, ibuf->events, ibuf->handler, ibuf);
255 	event_add(&ibuf->ev, NULL);
256 }
257 
258 void
259 snmpd_dispatch_snmpe(int fd, short event, void * ptr)
260 {
261 	struct imsgbuf		*ibuf;
262 	struct imsg		 imsg;
263 	ssize_t			 n;
264 
265 	ibuf = ptr;
266 	switch (event) {
267 	case EV_READ:
268 		if ((n = imsg_read(ibuf)) == -1)
269 			fatal("imsg_read error");
270 		if (n == 0) {
271 			/* this pipe is dead, so remove the event handler */
272 			event_del(&ibuf->ev);
273 			event_loopexit(NULL);
274 			return;
275 		}
276 		break;
277 	case EV_WRITE:
278 		if (msgbuf_write(&ibuf->w) == -1)
279 			fatal("msgbuf_write");
280 		imsg_event_add(ibuf);
281 		return;
282 	default:
283 		fatalx("unknown event");
284 	}
285 
286 	for (;;) {
287 		if ((n = imsg_get(ibuf, &imsg)) == -1)
288 			fatal("snmpd_dispatch_relay: imsg_read error");
289 		if (n == 0)
290 			break;
291 
292 		switch (imsg.hdr.type) {
293 		default:
294 			log_debug("snmpd_dispatch_relay: unexpected imsg %d",
295 			    imsg.hdr.type);
296 			break;
297 		}
298 		imsg_free(&imsg);
299 	}
300 	imsg_event_add(ibuf);
301 }
302 
303 int
304 snmpd_socket_af(struct sockaddr_storage *ss, in_port_t port)
305 {
306 	int	 s;
307 
308 	switch (ss->ss_family) {
309 	case AF_INET:
310 		((struct sockaddr_in *)ss)->sin_port = port;
311 		((struct sockaddr_in *)ss)->sin_len =
312 		    sizeof(struct sockaddr_in);
313 		break;
314 	case AF_INET6:
315 		((struct sockaddr_in6 *)ss)->sin6_port = port;
316 		((struct sockaddr_in6 *)ss)->sin6_len =
317 		    sizeof(struct sockaddr_in6);
318 		break;
319 	default:
320 		return (-1);
321 	}
322 
323 	s = socket(ss->ss_family, SOCK_DGRAM, IPPROTO_UDP);
324 	return (s);
325 }
326 
327