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