static int check_completion(child_process *cp, int flags) { int result, status; if (!cp || !cp->ei->pid) { return 0; } /* * we mustn't let EINTR interrupt us, since it could well * be a SIGCHLD from the properly exiting process doing it */ do { errno = 0; result = wait4(cp->ei->pid, &status, flags, &cp->ei->rusage); } while (result < 0 && errno == EINTR); if (result == cp->ei->pid || (result < 0 && errno == ECHILD)) { cp->ret = status; finish_job(cp, 0); destroy_job(cp); return 0; } if (!result) return -1; return -errno; }
void sigchld_handler(int sig, siginfo_t* info, void* u) { JOB* job; job = job_list_find_by_pid(info->si_pid); if (job == NULL) return; switch(info->si_code) { case CLD_KILLED: case CLD_EXITED: if (!job->blocking) { waitpid(info->si_pid, NULL, 0); if (--job->run_count == 0) { job_list_erase(job); destroy_job(&job); } } break; case CLD_STOPPED: job->blocking = 0; break; default: break; } }
void fg_wait(JOB* job) { int i; tcsetpgrp(STDIN_FILENO, job->pgid); for (i = 0; i < job->ncmd && job->blocking; i++) { if (job->pid[i] > 0) { while (waitpid(job->pid[i], NULL, 0) < 0 && job->blocking) { if (errno == ECHILD) /* A suspended job stops being blocking (sigchld_handler sets job->blocking to 0) */ break; /* The while loop is needed because if the child is killed then the wait will be canceled */ } /* (returning -1) by the call to sigchld_handler. It must be called again, otherwise zombie */ /* processes would remain: sigchld_handler doesn't wait for blocking jobs */ } } tcsetpgrp(STDIN_FILENO, getpgid(0)); if (job->blocking) /* Blocking job has terminated */ { job_list_erase(job); destroy_job(&job); } }
JOB_LIST* job_list(int cmd) { static JOB_LIST* list = NULL; int i; switch(cmd) { default: case JL_GET: if (list != NULL) return list; list = (JOB_LIST*) malloc(sizeof(JOB_LIST)); error(list == NULL, NULL); list->jobcount = 0; list->capacity = INITIAL_JOB_LIST_CAPACITY; list->last = -1; list->v = (JOB**) malloc(sizeof(JOB*)*(list->capacity)); error(list->v == NULL, (free(list), NULL)); break; case JL_DESTROY: if (list == NULL) return NULL; for (i = 0; i <= list->last; i++) destroy_job(&(list->v[i])); free(list->v); free(list); list = NULL; break; } return list; }
static int remove_queued_job(int id) { struct queue *q = NULL; int err = -ENXIO; if (id < 0) return -EINVAL; mutex_lock(&qmutex); INFO("removing job[%d]", id); if (qlen > 0) { q = remove_job(id); if (q) { qlen--; destroy_job(q->job); kfree(q); err = 0; INFO("job [%d] removed", id); wake_up_all(&pwq); } } mutex_unlock(&qmutex); return err; }
static void destroy_global(void) { struct queue *q; int i; INFO("destorying..."); mutex_lock(&qmutex); should_stop = true; wake_up_all(&pwq); /* wake up all producers */ /* wake up and stop all consumers */ for (i = 0; i < num_consumer; i++) kthread_stop(cthreads[i]); while (head) { q = remove_first_job(); qlen--; destroy_job(q->job); kfree(q); } kfree(cthreads); mutex_unlock(&qmutex); }
/* * Handles adding the command and macros to the kvvec, * as well as shipping the command off to a designated * worker */ static int wproc_run_job(struct wproc_job *job, nagios_macros *mac) { static struct kvvec kvv = KVVEC_INITIALIZER; struct kvvec_buf *kvvb; struct kvvec *env_kvvp = NULL; struct kvvec_buf *env_kvvb = NULL; struct wproc_worker *wp; int ret, result = OK; if (!job || !job->wp) return ERROR; wp = job->wp; if (!kvvec_init(&kvv, 4)) /* job_id, type, command and timeout */ return ERROR; kvvec_addkv(&kvv, "job_id", (char *)mkstr("%d", job->id)); kvvec_addkv(&kvv, "type", (char *)mkstr("%d", job->type)); kvvec_addkv(&kvv, "command", job->command); kvvec_addkv(&kvv, "timeout", (char *)mkstr("%u", job->timeout)); /* Add the macro environment variables */ if(mac) { env_kvvp = macros_to_kvv(mac); if(NULL != env_kvvp) { env_kvvb = kvvec2buf(env_kvvp, '=', '\n', 0); if(NULL == env_kvvb) { kvvec_destroy(env_kvvp, KVVEC_FREE_KEYS); } else { kvvec_addkv_wlen(&kvv, "env", strlen("env"), env_kvvb->buf, env_kvvb->buflen); } } } kvvb = build_kvvec_buf(&kvv); ret = write(wp->sd, kvvb->buf, kvvb->bufsize); if (ret != (int)kvvb->bufsize) { logit(NSLOG_RUNTIME_ERROR, TRUE, "wproc: '%s' seems to be choked. ret = %d; bufsize = %lu: errno = %d (%s)\n", wp->name, ret, kvvb->bufsize, errno, strerror(errno)); destroy_job(job); result = ERROR; } else { wp->jobs_running++; wp->jobs_started++; loadctl.jobs_running++; } if(NULL != env_kvvp) kvvec_destroy(env_kvvp, KVVEC_FREE_KEYS); if(NULL != env_kvvb) { free(env_kvvb->buf); free(env_kvvb); } free(kvvb->buf); free(kvvb); return result; }
JOB* create_job(const char* command) { JOB* job = NULL; char* cleancmd, ***iter; if (command == NULL) return NULL; job = (JOB*) malloc(sizeof(JOB)); error(job == NULL, NULL); job->name = (char*) malloc(sizeof(char)*(strlen(command)+1)); error(job->name == NULL, (free(job), NULL)); strcpy(job->name, command); job->inputfd = get_io_redir_file(command, SLSH_INPUT); job->outputfd = get_io_redir_file(command, SLSH_OUTPUT); job->blocking = is_blocking(command); cleancmd = clean_command(command); error(cleancmd == NULL, (destroy_job(&job), NULL)); job->cmd = make_cmd_array(cleancmd); free(cleancmd); error(job->cmd == NULL, (destroy_job(&job), NULL)); job->ncmd = 0; iter = job->cmd; while (*iter != NULL) { job->ncmd++; iter++; } job->run_count = 0; job->pgid = 0; job->pid = (pid_t*) calloc(job->ncmd, sizeof(pid_t)); error(job->pid == NULL, (destroy_job(&job), NULL)); job->lastmodified = -1; return job; }
int wproc_destroy(worker_process *wp, int flags) { int i = 0, destroyed = 0, force = 0, sd, self; if (!wp) return 0; force = !!(flags & WPROC_FORCE); self = getpid(); /* master retains workers through restarts */ if (self == nagios_pid && !force) return 0; /* free all memory when either forcing or a worker called us */ iocache_destroy(wp->ioc); wp->ioc = NULL; if (wp->jobs) { for (i = 0; i < wp->max_jobs; i++) { if (!wp->jobs[i]) continue; destroy_job(wp, wp->jobs[i]); /* we can (often) break out early */ if (++destroyed >= wp->jobs_running) break; } /* this triggers a double-free() for some reason */ /* free(wp->jobs); */ wp->jobs = NULL; } sd = wp->sd; free(wp); /* workers must never control other workers, so they return early */ if (self != nagios_pid) return 0; /* kill(0, SIGKILL) equals suicide, so we avoid it */ if (wp->pid) { kill(wp->pid, SIGKILL); } iobroker_close(nagios_iobs, sd); /* reap our possibly lost children */ while (waitpid(-1, &i, WNOHANG) > 0) ; /* do nothing */ return 0; }
static int process_job(struct job *job, int cid) { int err = 0; job->state = STATE_PROCESSING; INFO("%s", job->infile); err = __process_job(job); if (err) INFO("__process_job return %d", err); job->state = err ? STATE_FAILED : STATE_SUCCESS; notify_user(job, err, cid); destroy_job(job); return 0; }
/* * Handles adding the command and macros to the kvvec, * as well as shipping the command off to a designated * worker */ static int wproc_run_job(struct wproc_job *job, nagios_macros *mac) { static struct kvvec kvv = KVVEC_INITIALIZER; struct kvvec_buf *kvvb; struct wproc_worker *wp; int ret, result = OK; if (!job || !job->wp) return ERROR; wp = job->wp; /* * XXX FIXME: add environment macros as * kvvec_addkv(kvv, "env", "NAGIOS_LALAMACRO=VALUE"); * kvvec_addkv(kvv, "env", "NAGIOS_LALAMACRO2=VALUE"); * so workers know to add them to environment. For now, * we don't support that though. */ if (!kvvec_init(&kvv, 4)) /* job_id, type, command and timeout */ return ERROR; kvvec_addkv(&kvv, "job_id", (char *)mkstr("%d", job->id)); kvvec_addkv(&kvv, "type", (char *)mkstr("%d", job->type)); kvvec_addkv(&kvv, "command", job->command); kvvec_addkv(&kvv, "timeout", (char *)mkstr("%u", job->timeout)); kvvb = build_kvvec_buf(&kvv); ret = write(wp->sd, kvvb->buf, kvvb->bufsize); if (ret != (int)kvvb->bufsize) { logit(NSLOG_RUNTIME_ERROR, TRUE, "wproc: '%s' seems to be choked. ret = %d; bufsize = %lu: errno = %d (%s)\n", wp->name, ret, kvvb->bufsize, errno, strerror(errno)); destroy_job(job); result = ERROR; } else { wp->jobs_running++; wp->jobs_started++; loadctl.jobs_running++; } free(kvvb->buf); free(kvvb); return result; }
static int remove_queued_jobs(void) { struct queue *q; mutex_lock(&qmutex); INFO("removing all..."); while (head) { q = remove_first_job(); qlen--; destroy_job(q->job); kfree(q); } wake_up_all(&pwq); mutex_unlock(&qmutex); return 0; }
static int handle_worker_result(int sd, int events, void *arg) { wproc_object_job *oj = NULL; char *buf, *error_reason = NULL; unsigned long size; int ret; static struct kvvec kvv = KVVEC_INITIALIZER; struct wproc_worker *wp = (struct wproc_worker *)arg; if(iocache_capacity(wp->ioc) == 0) { logit(NSLOG_RUNTIME_WARNING, TRUE, "wproc: iocache_capacity() is 0 for worker %s.\n", wp->name); } ret = iocache_read(wp->ioc, wp->sd); if (ret < 0) { logit(NSLOG_RUNTIME_WARNING, TRUE, "wproc: iocache_read() from %s returned %d: %s\n", wp->name, ret, strerror(errno)); return 0; } else if (ret == 0) { logit(NSLOG_INFO_MESSAGE, TRUE, "wproc: Socket to worker %s broken, removing", wp->name); wproc_num_workers_online--; iobroker_unregister(nagios_iobs, sd); if (workers.len <= 0) { /* there aren't global workers left, we can't run any more checks * we should try respawning a few of the standard ones */ logit(NSLOG_RUNTIME_ERROR, TRUE, "wproc: All our workers are dead, we can't do anything!"); } remove_worker(wp); fanout_destroy(wp->jobs, fo_reassign_wproc_job); wp->jobs = NULL; wproc_destroy(wp, 0); return 0; } while ((buf = worker_ioc2msg(wp->ioc, &size, 0))) { struct wproc_job *job; wproc_result wpres; /* log messages are handled first */ if (size > 5 && !memcmp(buf, "log=", 4)) { logit(NSLOG_INFO_MESSAGE, TRUE, "wproc: %s: %s\n", wp->name, buf + 4); continue; } /* for everything else we need to actually parse */ if (buf2kvvec_prealloc(&kvv, buf, size, '=', '\0', KVVEC_ASSIGN) <= 0) { logit(NSLOG_RUNTIME_ERROR, TRUE, "wproc: Failed to parse key/value vector from worker response with len %lu. First kv=%s", size, buf ? buf : "(NULL)"); continue; } memset(&wpres, 0, sizeof(wpres)); wpres.job_id = -1; wpres.type = -1; wpres.response = &kvv; parse_worker_result(&wpres, &kvv); job = get_job(wp, wpres.job_id); if (!job) { logit(NSLOG_RUNTIME_WARNING, TRUE, "wproc: Job with id '%d' doesn't exist on %s.\n", wpres.job_id, wp->name); continue; } if (wpres.type != job->type) { logit(NSLOG_RUNTIME_WARNING, TRUE, "wproc: %s claims job %d is type %d, but we think it's type %d\n", wp->name, job->id, wpres.type, job->type); break; } oj = (wproc_object_job *)job->arg; /* * ETIME ("Timer expired") doesn't really happen * on any modern systems, so we reuse it to mean * "program timed out" */ if (wpres.error_code == ETIME) { wpres.early_timeout = TRUE; } if (wpres.early_timeout) { asprintf(&error_reason, "timed out after %.2fs", tv_delta_f(&wpres.start, &wpres.stop)); } else if (WIFSIGNALED(wpres.wait_status)) { asprintf(&error_reason, "died by signal %d%s after %.2f seconds", WTERMSIG(wpres.wait_status), WCOREDUMP(wpres.wait_status) ? " (core dumped)" : "", tv_delta_f(&wpres.start, &wpres.stop)); } else if (job->type != WPJOB_CHECK && WEXITSTATUS(wpres.wait_status) != 0) { asprintf(&error_reason, "is a non-check helper but exited with return code %d", WEXITSTATUS(wpres.wait_status)); } if (error_reason) { logit(NSLOG_RUNTIME_ERROR, TRUE, "wproc: %s job %d from worker %s %s", wpjob_type_name(job->type), job->id, wp->name, error_reason); logit(NSLOG_RUNTIME_ERROR, TRUE, "wproc: command: %s\n", job->command); if (job->type != WPJOB_CHECK && oj) { logit(NSLOG_RUNTIME_ERROR, TRUE, "wproc: host=%s; service=%s; contact=%s\n", oj->host_name ? oj->host_name : "(none)", oj->service_description ? oj->service_description : "(none)", oj->contact_name ? oj->contact_name : "(none)"); } else if (oj) { struct check_result *cr = (struct check_result *)job->arg; logit(NSLOG_RUNTIME_ERROR, TRUE, "wproc: host=%s; service=%s;\n", cr->host_name, cr->service_description); } logit(NSLOG_RUNTIME_ERROR, TRUE, "wproc: early_timeout=%d; exited_ok=%d; wait_status=%d; error_code=%d;\n", wpres.early_timeout, wpres.exited_ok, wpres.wait_status, wpres.error_code); wproc_logdump_buffer(NSLOG_RUNTIME_ERROR, TRUE, "wproc: stderr", wpres.outerr); wproc_logdump_buffer(NSLOG_RUNTIME_ERROR, TRUE, "wproc: stdout", wpres.outstd); } my_free(error_reason); switch (job->type) { case WPJOB_CHECK: ret = handle_worker_check(&wpres, wp, job); break; case WPJOB_NOTIFY: if (wpres.early_timeout) { if (oj->service_description) { logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Notifying contact '%s' of service '%s' on host '%s' by command '%s' timed out after %.2f seconds\n", oj->contact_name, oj->service_description, oj->host_name, job->command, tv2float(&wpres.runtime)); } else { logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Notifying contact '%s' of host '%s' by command '%s' timed out after %.2f seconds\n", oj->contact_name, oj->host_name, job->command, tv2float(&wpres.runtime)); } } break; case WPJOB_OCSP: if (wpres.early_timeout) { logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: OCSP command '%s' for service '%s' on host '%s' timed out after %.2f seconds\n", job->command, oj->service_description, oj->host_name, tv2float(&wpres.runtime)); } break; case WPJOB_OCHP: if (wpres.early_timeout) { logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: OCHP command '%s' for host '%s' timed out after %.2f seconds\n", job->command, oj->host_name, tv2float(&wpres.runtime)); } break; case WPJOB_GLOBAL_SVC_EVTHANDLER: if (wpres.early_timeout) { logit(NSLOG_EVENT_HANDLER | NSLOG_RUNTIME_WARNING, TRUE, "Warning: Global service event handler command '%s' timed out after %.2f seconds\n", job->command, tv2float(&wpres.runtime)); } break; case WPJOB_SVC_EVTHANDLER: if (wpres.early_timeout) { logit(NSLOG_EVENT_HANDLER | NSLOG_RUNTIME_WARNING, TRUE, "Warning: Service event handler command '%s' timed out after %.2f seconds\n", job->command, tv2float(&wpres.runtime)); } break; case WPJOB_GLOBAL_HOST_EVTHANDLER: if (wpres.early_timeout) { logit(NSLOG_EVENT_HANDLER | NSLOG_RUNTIME_WARNING, TRUE, "Warning: Global host event handler command '%s' timed out after %.2f seconds\n", job->command, tv2float(&wpres.runtime)); } break; case WPJOB_HOST_EVTHANDLER: if (wpres.early_timeout) { logit(NSLOG_EVENT_HANDLER | NSLOG_RUNTIME_WARNING, TRUE, "Warning: Host event handler command '%s' timed out after %.2f seconds\n", job->command, tv2float(&wpres.runtime)); } break; case WPJOB_CALLBACK: run_job_callback(job, &wpres, 0); break; default: logit(NSLOG_RUNTIME_WARNING, TRUE, "Worker %d: Unknown jobtype: %d\n", wp->pid, job->type); break; } destroy_job(job); } return 0; }
static void fo_destroy_job(void *job) { destroy_job((struct wproc_job *)job); }
/* * "What can the harvest hope for, if not for the care * of the Reaper Man?" * -- Terry Pratchett, Reaper Man * * We end up here no matter if the job is stale (ie, the child is * stuck in uninterruptable sleep) or if it's the first time we try * to kill it. * A job is considered reaped once we reap our direct child, in * which case init will become parent of our grandchildren. * It's also considered fully reaped if kill() results in ESRCH or * EPERM, or if wait()ing for the process group results in ECHILD. */ static void kill_job(child_process *cp, int reason) { int ret, status, reaped = 0; int pid = cp ? cp->ei->pid : 0; /* * first attempt at reaping, so see if we just failed to * notice that things were going wrong her */ if (reason == ETIME && !check_completion(cp, WNOHANG)) { timeouts++; wlog("job %d with pid %d reaped at timeout. timeouts=%u; started=%u", cp->id, pid, timeouts, started); return; } /* brutal but efficient */ if (kill(-cp->ei->pid, SIGKILL) < 0) { if (errno == ESRCH) { reaped = 1; } else { wlog("kill(-%d, SIGKILL) failed: %s\n", cp->ei->pid, strerror(errno)); } } /* * we must iterate at least once, in case kill() returns * ESRCH when there's zombies */ do { ret = waitpid(cp->ei->pid, &status, WNOHANG); if (ret < 0 && errno == EINTR) continue; if (ret == cp->ei->pid || (ret < 0 && errno == ECHILD)) { reaped = 1; break; } if (!ret) { struct timeval tv; gettimeofday(&tv, NULL); /* * stale process (signal may not have been delivered, or * the child can be stuck in uninterruptible sleep). We * can't hang around forever, so just reschedule a new * reap attempt later. */ if (reason == ESTALE) { tv.tv_sec += 5; wlog("Failed to reap child with pid %d. Next attempt @ %lu.%lu", cp->ei->pid, tv.tv_sec, tv.tv_usec); } else { tv.tv_usec = 250000; if (tv.tv_usec > 1000000) { tv.tv_usec -= 1000000; tv.tv_sec += 1; } cp->ei->state = ESTALE; finish_job(cp, reason); } squeue_remove(sq, cp->ei->sq_event); cp->ei->sq_event = squeue_add_tv(sq, &tv, cp); return; } } while (!reaped); if (cp->ei->state != ESTALE) finish_job(cp, reason); else wlog("job %d (pid=%d): Dormant child reaped", cp->id, cp->ei->pid); destroy_job(cp); }
static int handle_worker_result(int sd, int events, void *arg) { worker_process *wp = (worker_process *)arg; wproc_object_job *oj; char *buf; unsigned long size; int ret; static struct kvvec kvv = KVVEC_INITIALIZER; ret = iocache_read(wp->ioc, wp->sd); if (ret < 0) { logit(NSLOG_RUNTIME_WARNING, TRUE, "iocache_read() from worker %d returned %d: %s\n", wp->pid, ret, strerror(errno)); return 0; } else if (ret == 0) { /* * XXX FIXME worker exited. spawn a new on to replace it * and distribute all unfinished jobs from this one to others */ return 0; } while ((buf = iocache_use_delim(wp->ioc, MSG_DELIM, MSG_DELIM_LEN, &size))) { int job_id = -1; worker_job *job; wproc_result wpres; /* log messages are handled first */ if (size > 5 && !memcmp(buf, "log=", 4)) { logit(NSLOG_INFO_MESSAGE, TRUE, "worker %d: %s\n", wp->pid, buf + 4); continue; } /* for everything else we need to actually parse */ if (buf2kvvec_prealloc(&kvv, buf, size, '=', '\0', KVVEC_ASSIGN) <= 0) { /* XXX FIXME log an error */ continue; } memset(&wpres, 0, sizeof(wpres)); wpres.job_id = -1; wpres.type = -1; wpres.response = &kvv; parse_worker_result(&wpres, &kvv); job = get_job(wp, wpres.job_id); if (!job) { logit(NSLOG_RUNTIME_WARNING, TRUE, "Worker job with id '%d' doesn't exist on worker %d.\n", job_id, wp->pid); continue; } if (wpres.type != job->type) { logit(NSLOG_RUNTIME_WARNING, TRUE, "Worker %d claims job %d is type %d, but we think it's type %d\n", wp->pid, job->id, wpres.type, job->type); break; } oj = (wproc_object_job *)job->arg; /* * ETIME ("Timer expired") doesn't really happen * on any modern systems, so we reuse it to mean * "program timed out" */ if (wpres.error_code == ETIME) { wpres.early_timeout = TRUE; } switch (job->type) { case WPJOB_CHECK: ret = handle_worker_check(&wpres, wp, job); break; case WPJOB_NOTIFY: if (wpres.early_timeout) { if (oj->service_description) { logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Notifying contact '%s' of service '%s' on host '%s' by command '%s' timed out after %.2f seconds\n", oj->contact_name, oj->service_description, oj->host_name, job->command, tv2float(&wpres.runtime)); } else { logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Notifying contact '%s' of host '%s' by command '%s' timed out after %.2f seconds\n", oj->contact_name, oj->host_name, job->command, tv2float(&wpres.runtime)); } } break; case WPJOB_OCSP: if (wpres.early_timeout) { logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: OCSP command '%s' for service '%s' on host '%s' timed out after %.2f seconds\n", job->command, oj->service_description, oj->host_name, tv2float(&wpres.runtime)); } break; case WPJOB_OCHP: if (wpres.early_timeout) { logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: OCHP command '%s' for host '%s' timed out after %.2f seconds\n", job->command, oj->host_name, tv2float(&wpres.runtime)); } break; case WPJOB_GLOBAL_SVC_EVTHANDLER: if (wpres.early_timeout) { logit(NSLOG_EVENT_HANDLER | NSLOG_RUNTIME_WARNING, TRUE, "Warning: Global service event handler command '%s' timed out after %.2f seconds\n", job->command, tv2float(&wpres.runtime)); } break; case WPJOB_SVC_EVTHANDLER: if (wpres.early_timeout) { logit(NSLOG_EVENT_HANDLER | NSLOG_RUNTIME_WARNING, TRUE, "Warning: Service event handler command '%s' timed out after %.2f seconds\n", job->command, tv2float(&wpres.runtime)); } break; case WPJOB_GLOBAL_HOST_EVTHANDLER: if (wpres.early_timeout) { logit(NSLOG_EVENT_HANDLER | NSLOG_RUNTIME_WARNING, TRUE, "Warning: Global host event handler command '%s' timed out after %.2f seconds\n", job->command, tv2float(&wpres.runtime)); } break; case WPJOB_HOST_EVTHANDLER: if (wpres.early_timeout) { logit(NSLOG_EVENT_HANDLER | NSLOG_RUNTIME_WARNING, TRUE, "Warning: Host event handler command '%s' timed out after %.2f seconds\n", job->command, tv2float(&wpres.runtime)); } break; default: logit(NSLOG_RUNTIME_WARNING, TRUE, "Worker %d: Unknown jobtype: %d\n", wp->pid, job->type); break; } destroy_job(wp, job); } return 0; }
asmlinkage static long xjob(__user void *args, int argslen) { struct xargs *xarg = NULL; struct job *job = NULL; int err = 0; /* check and copy user argument */ if (!args) return -EINVAL; /* unless modifing syscall prototype to two arguments * if (sizeof(struct xargs) != argslen) * return -EINVAL; */ argslen = sizeof(struct xargs); xarg = kmalloc(argslen, GFP_KERNEL); if (!xarg) return -ENOMEM; if (copy_from_user(xarg, args, argslen)) { INFO("invalid user address passed"); err = -EFAULT; goto out; } if (xarg->action == ACTION_SETUP) { err = new_job_id(); goto out; } else if (xarg->action == ACTION_REMOVE_ONE) { err = remove_queued_job(xarg->id); goto out; } else if (xarg->action == ACTION_REMOVE_ALL) { err = remove_queued_jobs(); goto out; } else if (xarg->action == ACTION_LIST) { err = list_queued_jobs(xarg->list_buf, xarg->list_len); goto out; } else if (xarg->action >= ACTION_LAST) { err = -EINVAL; goto out; } /* init job */ job = kmalloc(sizeof(struct job), GFP_KERNEL); if (!job) { err = -ENOMEM; goto out; } err = init_job(job, xarg); if (err) goto out_err; err = produce(job); if (err) goto out_err; err = job->id; /* submit successfully, do not free job */ goto out; out_err: destroy_job(job); out: kfree(xarg); return err; }