/* * This is the back end to do_processes, saves some repeated code */ static void handle_filedesc (Process *proc, int *fd, int hook_nonl, int hook_nl) { char exec_buffer[IO_BUFFER_SIZE + 1]; ssize_t len; int ofs; const char *callback = NULL; int hook = -1; int l; char logical_name[1024]; const char *utf8_text; char *extra = NULL; /* No buffering! */ switch ((len = dgets(*fd, exec_buffer, IO_BUFFER_SIZE, 0))) { case -1: /* Something died */ { *fd = new_close(*fd); if (proc->p_stdout == -1 && proc->p_stderr == -1) proc->dumb = 1; return; /* PUNT! */ } case 0: /* We didnt get a full line */ { /* * XXX This is a hack. dgets() can return 0 for a line * containing solely a newline, as well as a line that didn't * have a newline. So we have to check to see if the line * contains only a newline! */ if (exec_buffer[0] != '\n') { if (hook_nl == EXEC_LIST) { if (proc->stdoutpc && *proc->stdoutpc) callback = proc->stdoutpc; } else if (hook_nl == EXEC_ERRORS_LIST) { if (proc->stderrpc && *proc->stderrpc) callback = proc->stderrpc; } hook = hook_nonl; break; } /* XXX HACK -- Line contains only a newline. */ *exec_buffer = 0; /* FALLTHROUGH */ } default: /* We got a full line */ { if (hook_nl == EXEC_LIST) { if (proc->stdoutc && *proc->stdoutc) callback = proc->stdoutc; } else if (hook_nl == EXEC_ERRORS_LIST) { if (proc->stderrc && *proc->stderrc) callback = proc->stderrc; } hook = hook_nl; break; } } ofs = from_server; from_server = proc->server; if (proc->refnum) l = message_setall(proc->refnum, NULL, LEVEL_OTHER); else l = message_from(NULL, LEVEL_OTHER); proc->counter++; while (len > 0 && (exec_buffer[len - 1] == '\n' || exec_buffer[len - 1] == '\r')) exec_buffer[--len] = 0; index_to_target(proc->index, logical_name, sizeof(logical_name)); utf8_text = inbound_recode(logical_name, proc->server, empty_string, exec_buffer, &extra); if (proc->redirect) redirect_text(proc->server, proc->who, utf8_text, proc->redirect, 1); if (callback) call_lambda_command("EXEC", callback, utf8_text); else if (proc->logical) { if ((do_hook(hook, "%s %s", proc->logical, utf8_text))) if (!proc->redirect) put_it("%s", utf8_text); } else { if ((do_hook(hook, "%d %s", proc->index, utf8_text))) if (!proc->redirect) put_it("%s", utf8_text); } new_free(&extra); pop_message_from(l); from_server = ofs; }
/* * This function is called by the three places that can effect a change * on the state of a running process: * 1) get_child_exit, which can mark a process as exited * 2) do_processes, which can mark a child as being done with I/O * 3) execcmd, which can mark a child as being done with I/O * * Any processes that are found to have both exited and having completed * their I/O will be summarily destroyed. */ static void cleanup_dead_processes (void) { int i; List *cmd, *next; Process *deadproc, *proc; char *exit_info; int old_from_server, l; if (!process_list) return; /* Nothing to do */ old_from_server = from_server; for (i = 0; i < process_list_size; i++) { if (!(proc = process_list[i])) continue; /* * We do not parse the process if it has not * both exited and finished its io, UNLESS * it has been disowned. */ if ((!proc->exited || !proc->dumb) && !proc->disowned) continue; /* Not really dead yet */ deadproc = process_list[i]; process_list[i] = NULL; /* * First thing to do is fill out the exit information */ if (deadproc->logical) { size_t len = strlen(deadproc->logical) + 25; exit_info = alloca(len); snprintf(exit_info, len, "%s %d %d", deadproc->logical, deadproc->termsig, deadproc->retcode); } else { exit_info = alloca(40); snprintf(exit_info, 32, "%d %d %d", deadproc->index, deadproc->termsig, deadproc->retcode); } from_server = deadproc->server; l = message_from(NULL, LEVEL_OTHER); /* * First thing we do is run any /wait %proc -cmd commands */ next = deadproc->waitcmds; deadproc->waitcmds = NULL; while ((cmd = next)) { next = cmd->next; call_lambda_command("WAITPROC", cmd->name, exit_info); new_free(&cmd->name); new_free((char **)&cmd); } /* * Throw /on exec_exit */ if (do_hook(EXEC_EXIT_LIST, "%s", exit_info)) { if (get_int_var(NOTIFY_ON_TERMINATION_VAR)) { if (deadproc->termsig > 0 && deadproc->termsig < NSIG) { say("Process %d (%s) terminated " "with signal %s (%d)", deadproc->index, deadproc->name, sys_siglist[deadproc->termsig], deadproc->termsig); } else if (deadproc->disowned) { say("Process %d (%s) disowned", deadproc->index, deadproc->name); } else { say("Process %d (%s) terminated " "with return code %d", deadproc->index, deadproc->name, deadproc->retcode); } } } pop_message_from(l); deadproc->p_stdin = new_close(deadproc->p_stdin); deadproc->p_stdout = new_close(deadproc->p_stdout); deadproc->p_stderr = new_close(deadproc->p_stderr); new_free(&deadproc->name); new_free(&deadproc->logical); new_free(&deadproc->who); new_free(&deadproc->redirect); new_free(&deadproc->stdoutc); new_free(&deadproc->stdoutpc); new_free(&deadproc->stderrc); new_free(&deadproc->stderrpc); new_free((char **)&deadproc); } /* * Resize away any dead processes at the end */ for (i = process_list_size - 1; i >= 0; i--) { if (process_list[i]) break; } if (process_list_size != i + 1) { process_list_size = i + 1; RESIZE(process_list, Process, process_list_size); } from_server = old_from_server; }
/* * Restore the value of a previously cloned biv. This does not create * or remove a biv from the bucket! */ void unclone_biv (const char *name, IrcVariable *clone) { IrcVariable *var; int i; for (i = 0; i < var_bucket->numitems; i++) { if (!strcmp(name, var_bucket->list[i].name)) { var = (IrcVariable *)var_bucket->list[i].stuff; var->type = clone->type; if (clone->script) { malloc_strcpy(&var->script, clone->script); new_free(&clone->script); } else new_free(&var->script); var->flags = clone->flags; /* * XXX This should be unified with set_variable() somehow. */ switch (clone->type) { case BOOL_VAR: case CHAR_VAR: case INT_VAR: var->data->integer = clone->data->integer; break; case STR_VAR: if (clone->data->string) malloc_strcpy(&var->data->string, clone->data->string); else new_free(&var->data->string); new_free(&clone->data->string); break; } /* * XXX I copied this from set_variable(), but this * should be refactored and shared with that function. */ if ((var->func || var->script) && !(var->flags & VIF_PENDING)) { var->flags |= VIF_PENDING; if (var->func) (var->func)(var->data); if (var->script) { char *s; int owd = window_display; s = make_string_var_bydata(var->type, (void *)var->data); window_display = 0; call_lambda_command("SET", var->script, s); window_display = owd; new_free(&s); } var->flags &= ~VIF_PENDING; } new_free(&clone->data); new_free(&clone); return; } } }
/* * set_var_value: Given the variable structure and the string representation * of the value, this sets the value in the most verbose and error checking * of manors. It displays the results of the set and executes the function * defined in the var structure */ int set_variable (const char *name, IrcVariable *var, const char *orig_value, int noisy) { char *rest; int changed = 0; unsigned char *value; int retval = 0; if (orig_value) value = LOCAL_COPY(orig_value); else value = NULL; switch (var->type) { case BOOL_VAR: { if (value && *value && (value = next_arg(value, &rest))) { if (do_boolean(value, &(var->data->integer))) { say("Value must be either ON, OFF, or TOGGLE"); retval = -1; } else changed = 1; } break; } case CHAR_VAR: { int codepoint; if (!value || !*value) { var->data->integer = ' '; changed = 1; break; } if ((codepoint = next_code_point((const unsigned char **)&value, 0)) == -1) { say("New value of %s could not be determined", name); retval = -1; break; } if (codepoint_numcolumns(codepoint) != 1) { say("New value of %s must be exactly 1 column wide", name); retval = -1; break; } var->data->integer = codepoint; changed = 1; break; } case INT_VAR: { if (value && *value && (value = next_arg(value, &rest))) { int val; if (!is_number(value)) { say("Value of %s must be numeric!", name); retval = -1; } else if ((val = my_atol(value)) < 0) { say("Value of %s must be a non-negative number", name); retval = -1; } else { var->data->integer = val; changed = 1; } } break; } case STR_VAR: { if (!value) { new_free(&(var->data->string)); changed = 1; } else if (*value) { malloc_strcpy(&(var->data->string), value); changed = 1; } } } if (changed) { if ((var->func || var->script) && !(var->flags & VIF_PENDING)) { var->flags |= VIF_PENDING; if (var->func) (var->func)(var->data); if (var->script) { char *s; int owd = window_display; s = make_string_var_bydata(var->type, (void *)var->data); window_display = 0; call_lambda_command("SET", var->script, s); window_display = owd; new_free(&s); } var->flags &= ~VIF_PENDING; } } if (noisy) show_var_value(name, var, changed); return retval; }
/* * ExecuteTimers: checks to see if any currently pending timers have * gone off, and if so, execute them, delete them, etc, setting the * current_exec_timer, so that we can't remove the timer while its * still executing. * * changed the behavior: timers will not hook while we are waiting. */ void ExecuteTimers (void) { Timeval right_now; Timer * current, *next; int old_from_server = from_server; get_time(&right_now); while (PendingTimers && time_diff(right_now, PendingTimers->time) < 0) { int old_refnum; old_refnum = current_window->refnum; current = PendingTimers; unlink_timer(current); /* Reschedule the timer if necessary */ if (current->events < 0 || (current->events != 1)) { next = clone_timer(current); if (next->events != -1) next->events--; next->time = time_add(next->time, next->interval); schedule_timer(next); } if (current->domain == SERVER_TIMER) { if (!is_server_valid(current->domref)) { if (current->cancelable) goto advance; /* Otherwise, pretend you were a "GENERAL" type */ } else { from_server = current->domref; make_window_current_by_refnum( get_winref_by_servref(from_server)); } } else if (current->domain == WINDOW_TIMER) { if (!get_window_by_refnum(current->domref)) { if (current->cancelable) goto advance; /* Otherwise, pretend you were a "GENERAL" type */ } else { make_window_current_by_refnum(current->domref); from_server = current_window->server; } } else { /* General timers focus on the current window. */ if (current_window) { if (current_window->server != from_server) from_server = current_window->server; } else { if (from_server != NOSERV) make_window_current_by_refnum( get_winref_by_servref(from_server)); } } /* * If a callback function was registered, then * we use it. If no callback function was registered, * then we call the lambda function. */ get_time(&right_now); now = right_now; if (current->callback) (*current->callback)(current->callback_data); else call_lambda_command("TIMER", current->command, current->subargs); from_server = old_from_server; make_window_current_by_refnum(old_refnum); advance: delete_timer(current); } }