int main(int argc, char **argv) { simple_worker *wp; int i; #ifdef HAVE_SIGACTION struct sigaction sig_action; sig_action.sa_sigaction = NULL; sigfillset(&sig_action.sa_mask); sig_action.sa_flags=SA_NOCLDSTOP; sig_action.sa_handler = child_exited; sigaction(SIGCHLD, &sig_action, NULL); sig_action.sa_flags = SA_NODEFER|SA_RESTART; sig_action.sa_handler = sighandler; sigfillset(&sig_action.sa_mask); sigaction(SIGINT, &sig_action, NULL); sigaction(SIGPIPE, &sig_action, NULL); #else /* HAVE_SIGACTION */ signal(SIGINT, sighandler); signal(SIGPIPE, sighandler); signal(SIGCHLD, child_exited); #endif /* HAVE_SIGACTION */ iobs = iobroker_create(); if (!iobs) die("Failed to create io broker set"); for (i = 0; i < NWPS; i++) { wp = spawn_worker(print_some_crap, "lalala"); if (!wp) { die("Failed to spawn worker(s)\n"); } wps[i] = wp; printf("Registering worker sd %d with io broker\n", wp->sd); iobroker_register(iobs, wp->sd, wp, print_input); } iobroker_register(iobs, fileno(stdin), NULL, send_command); /* get to work */ while (!sigreceived && iobroker_get_num_fds(iobs)) { iobroker_poll(iobs, -1); } for (i = 0; i < NWPS; i++) { kill(wps[i]->pid, SIGKILL); } return 0; }
/* * This gets called when a connect() attempt has become writable. * It's entirely possible that the node we're trying to connect * to has connected to us while we were waiting for them, in * which case we need to figure out which of the two connections * we're supposed to use. */ static int conn_writable(int sd, int events, void *node_) { merlin_node *node = (merlin_node *)node_; int result; int sel_sd; /* unregister so we don't peg one cpu at 100% */ ldebug("CONN: In conn_writable(): node=%s; sd=%d; node->conn_sock=%d", node->name, sd, node->conn_sock); iobroker_unregister(nagios_iobs, sd); if (node->sock < 0) { /* no inbound connection accept()'ed yet */ node->sock = sd; node->conn_sock = -1; if (!net_is_connected(node)) { node_disconnect(node, "Connection attempt failed: %s", strerror(errno)); close(sd); return 0; } iobroker_register(nagios_iobs, sd, node, net_input); node_set_state(node, STATE_NEGOTIATING, "Connect completed successfully. Negotiating protocol"); return 0; } sel_sd = net_negotiate_socket(node, node->conn_sock, node->sock); if (sel_sd < 0) { node_disconnect(node, "Failed to negotiate socket"); return 0; } if (sel_sd == node->conn_sock) { iobroker_close(nagios_iobs, node->sock); } else if (sel_sd == node->sock) { iobroker_close(nagios_iobs, node->conn_sock); } node->sock = sel_sd; node->conn_sock = -1; node_set_state(node, STATE_NEGOTIATING, "polled for writability"); /* now re-register for input */ ldebug("IOB: registering %s(%d) for input events", node->name, node->sock); result = iobroker_register(nagios_iobs, node->sock, node, net_input); if (result < 0) { lerr("IOB: Failed to register %s(%d) for input events: %s", node->name, node->sock, iobroker_strerror(result)); } return 0; }
static int fd_start_cmd(child_process *cp) { int pfd[2], pfderr[2]; cp->outstd.fd = runcmd_open(cp->cmd, pfd, pfderr, NULL); if (cp->outstd.fd == -1) { return -1; } gettimeofday(&cp->start, NULL); cp->outerr.fd = pfderr[0]; cp->pid = runcmd_pid(cp->outstd.fd); iobroker_register(iobs, cp->outstd.fd, cp, stdout_handler); iobroker_register(iobs, cp->outerr.fd, cp, stderr_handler); return 0; }
int init_workers(int desired_workers) { worker_process **wps; int i; if (desired_workers <= 0) { desired_workers = 4; } if (workers_alive() == desired_workers) return 0; /* can't shrink the number of workers (yet) */ if (desired_workers < num_workers) return -1; wps = calloc(desired_workers, sizeof(worker_process *)); if (!wps) return -1; if (workers) { if (num_workers < desired_workers) { for (i = 0; i < num_workers; i++) { wps[i] = workers[i]; } } free(workers); } workers = wps; for (i = 0; i < desired_workers; i++) { int ret; worker_process *wp; if (wps[i]) continue; wp = spawn_worker(worker_init_func, (void *)get_global_macros()); if (!wp) { logit(NSLOG_RUNTIME_WARNING, TRUE, "Failed to spawn worker: %s\n", strerror(errno)); free_worker_memory(0); return ERROR; } set_socket_options(wp->sd, 256 * 1024); wps[i] = wp; ret = iobroker_register(nagios_iobs, wp->sd, wp, handle_worker_result); if (ret < 0) { printf("Failed to register worker socket with iobroker %p\n", nagios_iobs); exit(1); } } num_workers = desired_workers; logit(NSLOG_INFO_MESSAGE, TRUE, "Workers spawned: %d\n", num_workers); return 0; }
/* * Accept an inbound connection from a remote host * Returns 0 on success and -1 on errors */ static int net_accept_one(int sd, int events, void *discard) { int sock, result; merlin_node *node; struct sockaddr_in sain; socklen_t slen = sizeof(struct sockaddr_in); sock = accept(sd, (struct sockaddr *)&sain, &slen); if (sock < 0) { lerr("accept() failed: %s", strerror(errno)); return -1; } node = find_node(&sain); linfo("NODESTATE: %s connected from %s:%d. Current state is %s", node ? node->name : "An unregistered node", inet_ntoa(sain.sin_addr), ntohs(sain.sin_port), node ? node_state_name(node->state) : "unknown"); if (!node) { close(sock); return 0; } switch (node->state) { case STATE_NEGOTIATING: case STATE_CONNECTED: case STATE_PENDING: /* if node->sock >= 0, we must negotiate which one to use */ if (node->sock >= 0) { int sel_sd = net_negotiate_socket(node, node->sock, sock); if (sel_sd != sock) { close(sock); } } break; case STATE_NONE: /* * we must close it unconditionally or we'll leak fd's * for reconnecting nodes that were previously connected */ node_disconnect(node, "fd leak prevention for connecting nodes"); node->sock = sock; break; default: lerr("%s %s has an unknown status", node_type(node), node->name); break; } node_set_state(node, STATE_NEGOTIATING, "Inbound connection accepted. Negotiating protocol version"); result = iobroker_register(nagios_iobs, node->sock, node, net_input); if (result < 0) { lerr("IOB: Failed to register %d for %s node %s for input events: %s", node->sock, node_type(node), node->name, iobroker_strerror(result)); } return sock; }
int main(int argc, char **argv) { int listen_fd, flags, sockopt = 1; struct sockaddr_in sain; int error; const char *err_msg; t_set_colors(0); t_start("iobroker ipc test"); error = iobroker_get_max_fds(NULL); ok_int(error, IOBROKER_ENOSET, "test errors when passing null"); err_msg = iobroker_strerror(error); test(err_msg && !strcmp(err_msg, iobroker_errors[(~error) + 1].string), "iobroker_strerror() returns the right string"); iobs = iobroker_create(); error = iobroker_get_max_fds(iobs); test(iobs && error >= 0, "max fd's for real iobroker set must be > 0"); listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); flags = fcntl(listen_fd, F_GETFD); flags |= FD_CLOEXEC; fcntl(listen_fd, F_SETFD, flags); (void)setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); memset(&sain, 0, sizeof(sain)); sain.sin_port = ntohs(9123); sain.sin_family = AF_INET; bind(listen_fd, (struct sockaddr *)&sain, sizeof(sain)); listen(listen_fd, 128); iobroker_register(iobs, listen_fd, iobs, listen_handler); if (argc == 1) conn_spam(&sain); for (;;) { iobroker_poll(iobs, -1); if (iobroker_get_num_fds(iobs) <= 1) { break; } } iobroker_close(iobs, listen_fd); iobroker_destroy(iobs, 0); t_end(); return 0; }
int qh_init(const char *path) { int result, old_umask; if (qh_listen_sock >= 0) iobroker_close(nagios_iobs, qh_listen_sock); if (!path) { nm_log(NSLOG_RUNTIME_ERROR, "qh: query_socket is NULL. What voodoo is this?\n"); return ERROR; } old_umask = umask(0117); errno = 0; qh_listen_sock = nsock_unix(path, NSOCK_TCP | NSOCK_UNLINK); umask(old_umask); if (qh_listen_sock < 0) { nm_log(NSLOG_RUNTIME_ERROR, "qh: Failed to init socket '%s'. %s: %s\n", path, nsock_strerror(qh_listen_sock), strerror(errno)); return ERROR; } /* plugins shouldn't have this socket */ (void)fcntl(qh_listen_sock, F_SETFD, FD_CLOEXEC); /* most likely overkill, but it's small, so... */ qh_table = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify) qh_remove); errno = 0; result = iobroker_register(nagios_iobs, qh_listen_sock, NULL, qh_registration_input); if (result < 0) { g_hash_table_destroy(qh_table); close(qh_listen_sock); nm_log(NSLOG_RUNTIME_ERROR, "qh: Failed to register socket with io broker: %s\n", iobroker_strerror(result)); return ERROR; } nm_log(NSLOG_INFO_MESSAGE, "qh: Socket '%s' successfully initialized\n", path); /* now register our the in-core handlers */ qh_register_handler("command", "Naemon external commands interface", 0, qh_command); qh_register_handler("echo", "The Echo Service - What You Put Is What You Get", 0, qh_echo); qh_register_handler("help", "Help for the query handler", 0, qh_help); return 0; }
static int qh_registration_input(int sd, int events, void *bq_) { nm_bufferqueue *bq = (nm_bufferqueue *)bq_; struct sockaddr sa; socklen_t slen = 0; int nsd, result; memset(&sa, 0, sizeof(sa)); /* shut valgrind up */ nsd = accept(sd, &sa, &slen); if (qh_max_running && qh_running >= qh_max_running) { nsock_printf(nsd, "503: Server full"); close(nsd); return 0; } if (!(bq = nm_bufferqueue_create())) { nm_log(NSLOG_RUNTIME_ERROR, "qh: Failed to create iocache for inbound request\n"); nsock_printf(nsd, "500: Internal server error"); close(nsd); return 0; } /* * @todo: Stash the iocache and the socket in some * addressable list so we can release them on deinit */ result = iobroker_register(nagios_iobs, nsd, bq, qh_input); if (result < 0) { nm_log(NSLOG_RUNTIME_ERROR, "qh: Failed to register input socket %d with I/O broker: %s; errno=%d (%s)\n", nsd, iobroker_strerror(result), errno, strerror(errno)); nm_bufferqueue_destroy(bq); close(nsd); return 0; } /* make it non-blocking, but leave kernel buffers unchanged */ worker_set_sockopts(nsd, 0); qh_running++; return 0; }
/* * set up the listening socket (if applicable) */ int net_init(void) { int result, sockopt = 1; struct sockaddr_in sain, inbound; struct sockaddr *sa = (struct sockaddr *)&sain; socklen_t addrlen = sizeof(inbound); if (!num_nodes) return 0; sain.sin_addr.s_addr = default_addr; sain.sin_port = htons(default_port); sain.sin_family = AF_INET; net_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (net_sock < 0) return -1; merlin_set_socket_options(net_sock, 0); /* if this fails we can do nothing but try anyway */ (void)setsockopt(net_sock, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(int)); result = bind(net_sock, sa, addrlen); if (result < 0) return -1; result = listen(net_sock, SOMAXCONN); if (result < 0) return -1; result = iobroker_register(nagios_iobs, net_sock, NULL, net_accept_one); if (result < 0) { lerr("IOB: Failed to register network socket with I/O broker: %s", iobroker_strerror(result)); return -1; } return 0; }
static int conn_spam(struct sockaddr_in *sain) { int i; signal(SIGALRM, (void (*)(int))sighandler); signal(SIGINT, (void (*)(int))sighandler); signal(SIGPIPE, SIG_IGN); alarm(20); for (i = 0; i < NUM_PROCS; i++) { int fd, sockopt = 1; fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); proc_counter[i] = 0; iobroker_register(iobs, fd, (void *)&proc_counter[i], connected_handler); if (connect(fd, (struct sockaddr *)sain, sizeof(*sain))) { perror("connect"); } iobroker_poll(iobs, -1); } return 0; }
static int listen_handler(int fd, int events, void *arg) { int sock; struct sockaddr_in sain; socklen_t addrlen; if (!arg || arg != iobs) { printf("Argument passing seems to fail spectacularly\n"); } addrlen = sizeof(sain); //printf("listen_handler(%d, %d, %p) called\n", fd, events, arg); sock = accept(fd, (struct sockaddr *)&sain, &addrlen); if (sock < 0) { perror("accept"); return -1; } if (write(sock, msg[0], strlen(msg[0])) < 0) t_fail("write returned failure: %s", strerror(errno)); iobroker_register(iobs, sock, iobs, echo_service); ok_int(iobroker_is_registered(iobs, sock), 1, "is_registered must be true"); return 0; }
/* a service for registering workers */ static int register_worker(int sd, char *buf, unsigned int len) { int i, is_global = 1; struct kvvec *info; struct wproc_worker *worker; logit(NSLOG_INFO_MESSAGE, TRUE, "wproc: Registry request: %s\n", buf); if (!(worker = calloc(1, sizeof(*worker)))) { logit(NSLOG_RUNTIME_ERROR, TRUE, "wproc: Failed to allocate worker: %s\n", strerror(errno)); return 500; } info = buf2kvvec(buf, len, '=', ';', 0); if (info == NULL) { free(worker); logit(NSLOG_RUNTIME_ERROR, TRUE, "wproc: Failed to parse registration request\n"); return 500; } worker->sd = sd; worker->ioc = iocache_create(1 * 1024 * 1024); iobroker_unregister(nagios_iobs, sd); iobroker_register(nagios_iobs, sd, worker, handle_worker_result); for(i = 0; i < info->kv_pairs; i++) { struct key_value *kv = &info->kv[i]; if (!strcmp(kv->key, "name")) { worker->name = strdup(kv->value); } else if (!strcmp(kv->key, "pid")) { worker->pid = atoi(kv->value); } else if (!strcmp(kv->key, "max_jobs")) { worker->max_jobs = atoi(kv->value); } else if (!strcmp(kv->key, "plugin")) { struct wproc_list *command_handlers; is_global = 0; if (!(command_handlers = dkhash_get(specialized_workers, kv->value, NULL))) { command_handlers = calloc(1, sizeof(struct wproc_list)); command_handlers->wps = calloc(1, sizeof(struct wproc_worker**)); command_handlers->len = 1; command_handlers->wps[0] = worker; dkhash_insert(specialized_workers, strdup(kv->value), NULL, command_handlers); } else { command_handlers->len++; command_handlers->wps = realloc(command_handlers->wps, command_handlers->len * sizeof(struct wproc_worker**)); command_handlers->wps[command_handlers->len - 1] = worker; } worker->wp_list = command_handlers; } } if (!worker->max_jobs) { /* * each worker uses two filedescriptors per job, one to * connect to the master and about 13 to handle libraries * and memory allocation, so this guesstimate shouldn't * be too far off (for local workers, at least). */ worker->max_jobs = (iobroker_max_usable_fds() / 2) - 50; } worker->jobs = fanout_create(worker->max_jobs); if (is_global) { workers.len++; workers.wps = realloc(workers.wps, workers.len * sizeof(struct wproc_worker *)); workers.wps[workers.len - 1] = worker; worker->wp_list = &workers; } wproc_num_workers_online++; kvvec_destroy(info, 0); nsock_printf_nul(sd, "OK"); /* signal query handler to release its iocache for this one */ return QH_TAKEOVER; }
static void enter_worker(int sd) { /* created with socketpair(), usually */ master_sd = sd; parent_pid = getppid(); (void)chdir("/tmp"); (void)chdir("nagios-workers"); if (setpgid(0, 0)) { /* XXX: handle error somehow, or maybe just ignore it */ } /* we need to catch child signals the default way */ signal(SIGCHLD, SIG_DFL); fcntl(fileno(stdout), F_SETFD, FD_CLOEXEC); fcntl(fileno(stderr), F_SETFD, FD_CLOEXEC); fcntl(master_sd, F_SETFD, FD_CLOEXEC); iobs = iobroker_create(); if (!iobs) { /* XXX: handle this a bit better */ worker_die("Worker failed to create io broker socket set"); } /* * Create a modest scheduling queue that will be * more than enough for our needs */ sq = squeue_create(1024); set_socket_options(master_sd, 256 * 1024); iobroker_register(iobs, master_sd, NULL, receive_command); while (iobroker_get_num_fds(iobs) > 0) { int poll_time = -1; /* check for timed out jobs */ for (;;) { child_process *cp; struct timeval now, tmo; /* stop when scheduling queue is empty */ cp = (child_process *)squeue_peek(sq); if (!cp) break; tmo.tv_usec = cp->start.tv_usec; tmo.tv_sec = cp->start.tv_sec + cp->timeout; gettimeofday(&now, NULL); poll_time = tv_delta_msec(&now, &tmo); /* * A little extra takes care of rounding errors and * ensures we never kill a job before it times out. * 5 milliseconds is enough to take care of that. */ poll_time += 5; if (poll_time > 0) break; /* this job timed out, so kill it */ wlog("job with pid %d timed out. Killing it", cp->pid); kill_job(cp, ETIME); } iobroker_poll(iobs, poll_time); /* * if our parent goes away we can't really do anything * sensible at all, so let's just break out and exit */ if (kill(parent_pid, 0) < 0 && errno == ESRCH) { break; } } /* we exit when the master shuts us down */ exit(EXIT_SUCCESS); }