static void wlog(const char *fmt, ...) { va_list ap; static char lmsg[8192] = "log="; int len = 4, to_send; va_start(ap, fmt); len = vsnprintf(&lmsg[len], sizeof(lmsg) - 7, fmt, ap); va_end(ap); if (len < 0 || len >= (int)sizeof(lmsg)) return; len += 4; /* log= */ /* add delimiter and send it. 1 extra as kv pair separator */ to_send = len + MSG_DELIM_LEN + 1; lmsg[len] = 0; memcpy(&lmsg[len + 1], MSG_DELIM, MSG_DELIM_LEN); if (write(master_sd, lmsg, to_send) < 0) { if (errno == EPIPE) { /* master has died or abandoned us, so exit */ exit_worker(1, "Failed to write() to master"); } } }
static void handle_alarm(int signo) { if (global_ws) exit_worker(global_ws); exit(1); }
/* * blocking_thread - thread functions have WINAPI calling convention */ void * blocking_thread( void * ThreadArg ) { blocking_child *c; c = ThreadArg; exit_worker(blocking_child_common(c)); /* NOTREACHED */ return 0; }
static void job_error(child_process *cp, struct kvvec *kvv, const char *fmt, ...) { char msg[4096]; int len; va_list ap; int ret; va_start(ap, fmt); len = vsnprintf(msg, sizeof(msg) - 1, fmt, ap); va_end(ap); if (cp) { kvvec_addkv(kvv, "job_id", (char *)mkstr("%d", cp->id)); } kvvec_addkv_wlen(kvv, "error_msg", 9, msg, len); ret = send_kvvec(master_sd, kvv); if (ret < 0 && errno == EPIPE) exit_worker(); kvvec_destroy(kvv, 0); }
static int receive_command(int sd, int events, void *discard) { int ioc_ret; char *buf; unsigned long size; if (!ioc) { ioc = iocache_create(512 * 1024); } ioc_ret = iocache_read(ioc, sd); /* master closed the connection, so we exit */ if (ioc_ret == 0) { iobroker_close(iobs, sd); exit_worker(); } if (ioc_ret < 0) { /* XXX: handle this somehow */ } #if 0 /* debug-volley */ buf = iocache_use_size(ioc, ioc_ret); write(master_sd, buf, ioc_ret); return 0; #endif /* * now loop over all inbound messages in the iocache. * Since KV_TERMINATOR is a nul-byte, they're separated by 3 nuls */ while ((buf = iocache_use_delim(ioc, MSG_DELIM, MSG_DELIM_LEN_RECV, &size))) { struct kvvec *kvv; /* we must copy vars here, as we preserve them for the response */ kvv = buf2kvvec(buf, (unsigned int)size, KV_SEP, PAIR_SEP, KVVEC_COPY); if (kvv) spawn_job(kvv); } return 0; }
static void fork_blocking_child( blocking_child * c ) { static int atexit_installed; static int blocking_pipes[4] = { -1, -1, -1, -1 }; int rc; int was_pipe; int is_pipe; int saved_errno = 0; int childpid; int keep_fd; int fd; /* * parent and child communicate via a pair of pipes. * * 0 child read request * 1 parent write request * 2 parent read response * 3 child write response */ if (-1 == c->req_write_pipe) { rc = pipe_socketpair(&blocking_pipes[0], &was_pipe); if (0 != rc) { saved_errno = errno; } else { rc = pipe_socketpair(&blocking_pipes[2], &is_pipe); if (0 != rc) { saved_errno = errno; close(blocking_pipes[0]); close(blocking_pipes[1]); } else { INSIST(was_pipe == is_pipe); } } if (0 != rc) { errno = saved_errno; msyslog(LOG_ERR, "unable to create worker pipes: %m"); exit(1); } /* * Move the descriptors the parent will keep open out of the * low descriptors preferred by C runtime buffered FILE *. */ c->req_write_pipe = move_fd(blocking_pipes[1]); c->resp_read_pipe = move_fd(blocking_pipes[2]); /* * wake any worker child on orderly shutdown of the * daemon so that it can notice the broken pipes and * go away promptly. */ if (!atexit_installed) { atexit(&send_worker_home_atexit); atexit_installed = TRUE; } } #ifdef HAVE_DROPROOT /* defer the fork until after root is dropped */ if (droproot && !root_dropped) return; #endif if (syslog_file != NULL) fflush(syslog_file); fflush(stdout); fflush(stderr); signal_no_reset(SIGCHLD, SIG_IGN); childpid = fork(); if (-1 == childpid) { msyslog(LOG_ERR, "unable to fork worker: %m"); exit(1); } if (childpid) { /* this is the parent */ TRACE(1, ("forked worker child (pid %d)\n", childpid)); c->pid = childpid; c->ispipe = is_pipe; /* close the child's pipe descriptors. */ close(blocking_pipes[0]); close(blocking_pipes[3]); memset(blocking_pipes, -1, sizeof(blocking_pipes)); /* wire into I/O loop */ (*addremove_io_fd)(c->resp_read_pipe, is_pipe, FALSE); return; /* parent returns */ } /* * The parent gets the child pid as the return value of fork(). * The child must work for it. */ c->pid = getpid(); worker_process = TRUE; /* * In the child, close all files except stdin, stdout, stderr, * and the two child ends of the pipes. */ DEBUG_INSIST(-1 == c->req_read_pipe); DEBUG_INSIST(-1 == c->resp_write_pipe); c->req_read_pipe = blocking_pipes[0]; c->resp_write_pipe = blocking_pipes[3]; kill_asyncio(0); closelog(); if (syslog_file != NULL) { fclose(syslog_file); syslog_file = NULL; syslogit = TRUE; } keep_fd = max(c->req_read_pipe, c->resp_write_pipe); for (fd = 3; fd < keep_fd; fd++) if (fd != c->req_read_pipe && fd != c->resp_write_pipe) close(fd); close_all_beyond(keep_fd); /* * We get signals from refclock serial I/O on NetBSD in the * worker if we do not reset SIGIO's handler to the default. * It is not conditionalized for NetBSD alone because on * systems where it is not needed, it is harmless, and that * allows us to handle unknown others with NetBSD behavior. * [Bug 1386] */ #if defined(USE_SIGIO) signal_no_reset(SIGIO, SIG_DFL); #elif defined(USE_SIGPOLL) signal_no_reset(SIGPOLL, SIG_DFL); #endif signal_no_reset(SIGHUP, worker_sighup); init_logging("ntp_intres", 0, FALSE); setup_logfile(NULL); /* * And now back to the portable code */ exit_worker(blocking_child_common(c)); }
static int finish_job(child_process *cp, int reason) { static struct kvvec resp = KVVEC_INITIALIZER; struct rusage *ru = &cp->rusage; int i, ret; /* how many key/value pairs do we need? */ if (kvvec_init(&resp, 12 + cp->request->kv_pairs) == NULL) { /* what the hell do we do now? */ exit_worker(); } gettimeofday(&cp->stop, NULL); if (running_jobs != squeue_size(sq)) { wlog("running_jobs(%d) != squeue_size(sq) (%d)\n", running_jobs, squeue_size(sq)); wlog("started: %d; running: %d; finished: %d\n", started, running_jobs, started - running_jobs); } /* * we must remove the job's timeout ticker, * or we'll end up accessing an already free()'d * pointer, or the pointer to a different child. */ squeue_remove(sq, cp->sq_event); /* get rid of still open filedescriptors */ if (cp->outstd.fd != -1) iobroker_close(iobs, cp->outstd.fd); if (cp->outerr.fd != -1) iobroker_close(iobs, cp->outerr.fd); cp->runtime = tv_delta_f(&cp->start, &cp->stop); /* * Now build the return message. * First comes the request, minus environment variables */ for (i = 0; i < cp->request->kv_pairs; i++) { struct key_value *kv = &cp->request->kv[i]; /* skip environment macros */ if (kv->key_len == 3 && !strcmp(kv->key, "env")) { continue; } kvvec_addkv_wlen(&resp, kv->key, kv->key_len, kv->value, kv->value_len); } kvvec_addkv(&resp, "wait_status", (char *)mkstr("%d", cp->ret)); kvvec_addkv_wlen(&resp, "outstd", 6, cp->outstd.buf, cp->outstd.len); kvvec_addkv_wlen(&resp, "outerr", 6, cp->outerr.buf, cp->outerr.len); kvvec_add_tv(&resp, "start", cp->start); kvvec_add_tv(&resp, "stop", cp->stop); kvvec_addkv(&resp, "runtime", (char *)mkstr("%f", cp->runtime)); if (!reason) { /* child exited nicely */ kvvec_addkv(&resp, "exited_ok", "1"); kvvec_add_tv(&resp, "ru_utime", ru->ru_utime); kvvec_add_tv(&resp, "ru_stime", ru->ru_stime); kvvec_add_long(&resp, "ru_minflt", ru->ru_minflt); kvvec_add_long(&resp, "ru_majflt", ru->ru_majflt); kvvec_add_long(&resp, "ru_nswap", ru->ru_nswap); kvvec_add_long(&resp, "ru_inblock", ru->ru_inblock); kvvec_add_long(&resp, "ru_oublock", ru->ru_oublock); kvvec_add_long(&resp, "ru_nsignals", ru->ru_nsignals); } else { /* some error happened */ kvvec_addkv(&resp, "exited_ok", "0"); kvvec_addkv(&resp, "error_code", (char *)mkstr("%d", reason)); } ret = send_kvvec(master_sd, &resp); if (ret < 0 && errno == EPIPE) exit_worker(); running_jobs--; if (cp->outstd.buf) { free(cp->outstd.buf); cp->outstd.buf = NULL; } if (cp->outerr.buf) { free(cp->outerr.buf); cp->outerr.buf = NULL; } kvvec_destroy(cp->request, KVVEC_FREE_ALL); free(cp->cmd); free(cp); return 0; }
int finish_job(child_process *cp, int reason) { static struct kvvec resp = KVVEC_INITIALIZER; struct rusage *ru = &cp->ei->rusage; int i, ret; /* get rid of still open filedescriptors */ if (cp->outstd.fd != -1) { gather_output(cp, &cp->outstd, 1); iobroker_close(iobs, cp->outstd.fd); } if (cp->outerr.fd != -1) { gather_output(cp, &cp->outerr, 1); iobroker_close(iobs, cp->outerr.fd); } /* Make sure network-supplied data doesn't contain nul bytes */ strip_nul_bytes(cp->outstd); strip_nul_bytes(cp->outerr); /* how many key/value pairs do we need? */ if (kvvec_init(&resp, 12 + cp->request->kv_pairs) == NULL) { /* what the hell do we do now? */ exit_worker(1, "Failed to init response key/value vector"); } gettimeofday(&cp->ei->stop, NULL); if (running_jobs != squeue_size(sq)) { wlog("running_jobs(%d) != squeue_size(sq) (%d)\n", running_jobs, squeue_size(sq)); wlog("started: %d; running: %d; finished: %d\n", started, running_jobs, started - running_jobs); } cp->ei->runtime = tv_delta_f(&cp->ei->start, &cp->ei->stop); /* * Now build the return message. * First comes the request, minus environment variables */ for (i = 0; i < cp->request->kv_pairs; i++) { struct key_value *kv = &cp->request->kv[i]; /* skip environment macros */ if (kv->key_len == 3 && !strcmp(kv->key, "env")) { continue; } kvvec_addkv_wlen(&resp, kv->key, kv->key_len, kv->value, kv->value_len); } kvvec_addkv(&resp, "wait_status", mkstr("%d", cp->ret)); kvvec_add_tv(&resp, "start", cp->ei->start); kvvec_add_tv(&resp, "stop", cp->ei->stop); kvvec_addkv(&resp, "runtime", mkstr("%f", cp->ei->runtime)); if (!reason) { /* child exited nicely (or with a signal, so check wait_status) */ kvvec_addkv(&resp, "exited_ok", "1"); kvvec_add_tv(&resp, "ru_utime", ru->ru_utime); kvvec_add_tv(&resp, "ru_stime", ru->ru_stime); kvvec_add_long(&resp, "ru_minflt", ru->ru_minflt); kvvec_add_long(&resp, "ru_majflt", ru->ru_majflt); kvvec_add_long(&resp, "ru_inblock", ru->ru_inblock); kvvec_add_long(&resp, "ru_oublock", ru->ru_oublock); } else { /* some error happened */ kvvec_addkv(&resp, "exited_ok", "0"); kvvec_addkv(&resp, "error_code", mkstr("%d", reason)); } kvvec_addkv_wlen(&resp, "outerr", 6, cp->outerr.buf, cp->outerr.len); kvvec_addkv_wlen(&resp, "outstd", 6, cp->outstd.buf, cp->outstd.len); ret = worker_send_kvvec(master_sd, &resp); if (ret < 0 && errno == EPIPE) exit_worker(1, "Failed to send kvvec struct to master"); return 0; }
int post_auth_handler(worker_st * ws, unsigned http_ver) { int ret = -1, sd = -1; struct http_req_st *req = &ws->req; const char *reason = "Authentication failed"; char *username = NULL; char *password = NULL; char *groupname = NULL; char *msg = NULL; unsigned def_group = 0; if (req->body_length > 0) { oclog(ws, LOG_HTTP_DEBUG, "POST body: '%.*s'", (int)req->body_length, req->body); } if (ws->sid_set && ws->auth_state == S_AUTH_INACTIVE) ws->auth_state = S_AUTH_INIT; if (ws->auth_state == S_AUTH_INACTIVE) { SecAuthInitMsg ireq = SEC_AUTH_INIT_MSG__INIT; ret = parse_reply(ws, req->body, req->body_length, GROUPNAME_FIELD, sizeof(GROUPNAME_FIELD)-1, GROUPNAME_FIELD_XML, sizeof(GROUPNAME_FIELD_XML)-1, &groupname); if (ret < 0) { ret = parse_reply(ws, req->body, req->body_length, GROUPNAME_FIELD2, sizeof(GROUPNAME_FIELD2)-1, GROUPNAME_FIELD_XML, sizeof(GROUPNAME_FIELD_XML)-1, &groupname); } if (ret < 0) { oclog(ws, LOG_HTTP_DEBUG, "failed reading groupname"); } else { if (ws->config->default_select_group != NULL && strcmp(groupname, ws->config->default_select_group) == 0) { def_group = 1; } else { strlcpy(ws->groupname, groupname, sizeof(ws->groupname)); ireq.group_name = ws->groupname; } } talloc_free(groupname); if (ws->selected_auth->type & AUTH_TYPE_GSSAPI) { if (req->authorization == NULL || req->authorization_size == 0) return basic_auth_handler(ws, http_ver, NULL); if (req->authorization_size > 10) { ireq.user_name = req->authorization + 10; ireq.auth_type |= AUTH_TYPE_GSSAPI; } else { oclog(ws, LOG_HTTP_DEBUG, "Invalid authorization data: %.*s", req->authorization_size, req->authorization); goto auth_fail; } } if (ws->selected_auth->type & AUTH_TYPE_USERNAME_PASS) { ret = parse_reply(ws, req->body, req->body_length, USERNAME_FIELD, sizeof(USERNAME_FIELD)-1, NULL, 0, &username); if (ret < 0) { oclog(ws, LOG_HTTP_DEBUG, "failed reading username"); goto ask_auth; } strlcpy(ws->username, username, sizeof(ws->username)); talloc_free(username); ireq.user_name = ws->username; ireq.auth_type |= AUTH_TYPE_USERNAME_PASS; } if (ws->selected_auth->type & AUTH_TYPE_CERTIFICATE) { if (ws->cert_auth_ok == 0) { reason = MSG_NO_CERT_ERROR; oclog(ws, LOG_INFO, "no certificate provided for authentication"); goto auth_fail; } else { ret = get_cert_info(ws); if (ret < 0) { reason = MSG_CERT_READ_ERROR; oclog(ws, LOG_ERR, "failed reading certificate info"); goto auth_fail; } } if (def_group == 0 && ws->cert_groups_size > 0 && ws->groupname[0] == 0) { oclog(ws, LOG_HTTP_DEBUG, "user has not selected a group"); return get_auth_handler2(ws, http_ver, "Please select your group"); } ireq.tls_auth_ok = ws->cert_auth_ok; ireq.cert_user_name = ws->cert_username; ireq.cert_group_names = ws->cert_groups; ireq.n_cert_group_names = ws->cert_groups_size; ireq.auth_type |= AUTH_TYPE_CERTIFICATE; } ireq.hostname = req->hostname; ireq.ip = ws->remote_ip_str; sd = connect_to_secmod(ws); if (sd == -1) { reason = MSG_INTERNAL_ERROR; oclog(ws, LOG_ERR, "failed connecting to sec mod"); goto auth_fail; } ret = send_msg_to_secmod(ws, sd, SM_CMD_AUTH_INIT, &ireq, (pack_size_func) sec_auth_init_msg__get_packed_size, (pack_func) sec_auth_init_msg__pack); if (ret < 0) { reason = MSG_INTERNAL_ERROR; oclog(ws, LOG_ERR, "failed sending auth init message to sec mod"); goto auth_fail; } ws->auth_state = S_AUTH_INIT; } else if (ws->auth_state == S_AUTH_INIT || ws->auth_state == S_AUTH_REQ) { SecAuthContMsg areq = SEC_AUTH_CONT_MSG__INIT; areq.ip = ws->remote_ip_str; if (ws->selected_auth->type & AUTH_TYPE_GSSAPI) { if (req->authorization == NULL || req->authorization_size <= 10) { if (req->authorization != NULL) oclog(ws, LOG_HTTP_DEBUG, "Invalid authorization data: %.*s", req->authorization_size, req->authorization); goto auth_fail; } areq.password = req->authorization + 10; } if (areq.password == NULL && ws->selected_auth->type & AUTH_TYPE_USERNAME_PASS) { ret = parse_reply(ws, req->body, req->body_length, PASSWORD_FIELD, sizeof(PASSWORD_FIELD)-1, NULL, 0, &password); if (ret < 0) { reason = MSG_NO_PASSWORD_ERROR; oclog(ws, LOG_ERR, "failed reading password"); goto auth_fail; } areq.password = password; } if (areq.password != NULL) { if (ws->sid_set != 0) { areq.sid.data = ws->sid; areq.sid.len = sizeof(ws->sid); } sd = connect_to_secmod(ws); if (sd == -1) { reason = MSG_INTERNAL_ERROR; oclog(ws, LOG_ERR, "failed connecting to sec mod"); goto auth_fail; } ret = send_msg_to_secmod(ws, sd, SM_CMD_AUTH_CONT, &areq, (pack_size_func) sec_auth_cont_msg__get_packed_size, (pack_func) sec_auth_cont_msg__pack); talloc_free(password); if (ret < 0) { reason = MSG_INTERNAL_ERROR; oclog(ws, LOG_ERR, "failed sending auth req message to main"); goto auth_fail; } ws->auth_state = S_AUTH_REQ; } else goto auth_fail; } else { oclog(ws, LOG_ERR, "unexpected POST request in auth state %u", (unsigned)ws->auth_state); goto auth_fail; } ret = recv_auth_reply(ws, sd, &msg); if (sd != -1) { close(sd); sd = -1; } if (ret == ERR_AUTH_CONTINUE) { oclog(ws, LOG_DEBUG, "continuing authentication for '%s'", ws->username); ws->auth_state = S_AUTH_REQ; if (ws->selected_auth->type & AUTH_TYPE_GSSAPI) { ret = basic_auth_handler(ws, http_ver, msg); } else { ret = get_auth_handler2(ws, http_ver, msg); } goto cleanup; } else if (ret < 0) { if (ws->selected_auth->type & AUTH_TYPE_GSSAPI) { /* Fallback from GSSAPI to USERNAME-PASSWORD */ ws_disable_auth(ws, AUTH_TYPE_GSSAPI); oclog(ws, LOG_ERR, "failed gssapi authentication"); if (ws_switch_auth_to(ws, AUTH_TYPE_USERNAME_PASS) == 0) goto auth_fail; ws->auth_state = S_AUTH_INACTIVE; ws->sid_set = 0; goto ask_auth; } else { oclog(ws, LOG_ERR, "failed authentication for '%s'", ws->username); goto auth_fail; } } oclog(ws, LOG_HTTP_DEBUG, "user '%s' obtained cookie", ws->username); ws->auth_state = S_AUTH_COOKIE; ret = post_common_handler(ws, http_ver, msg); goto cleanup; ask_auth: return get_auth_handler(ws, http_ver); auth_fail: if (sd != -1) close(sd); oclog(ws, LOG_HTTP_DEBUG, "HTTP sending: 401 Unauthorized"); cstp_printf(ws, "HTTP/1.%d 401 Unauthorized\r\nContent-Length: 0\r\nX-Reason: %s\r\n\r\n", http_ver, reason); cstp_fatal_close(ws, GNUTLS_A_ACCESS_DENIED); talloc_free(msg); exit_worker(ws); cleanup: talloc_free(msg); return ret; }