void proc_destroy() { job_list_t &jobs = parser_t::principal_parser().job_list(); while (!jobs.empty()) { job_t *job = jobs.front().get(); debug(2, L"freeing leaked job %ls", job->command_wcstr()); job_remove(job); } }
void cron_update(void) { TimeVal now; double dt; List *iter, *next; timeval_now(&now); dt = timeval_elapsed(&cron_info.now, &now); for(iter = cron_info.oneshot; iter != NULL; iter = next) { Job *job = list_data(iter); next = list_next(iter); if(timeval_passed(&job->when.oneshot, &now)) { if(job->handler == (int (*)(void *)) printf) printf("%s\n", (const char *) job->data); else job->handler(job->data); job_remove(&cron_info.oneshot, iter); } } for(iter = cron_info.periodic; iter != NULL; iter = next) { Job *job = list_data(iter); next = list_next(iter); job->when.periodic.bucket += dt; if(job->when.periodic.bucket >= job->when.periodic.period) { if(job->handler == (int (*)(void *)) printf) /* Clever? */ printf("%s\n", (const char *) job->data); else { if(!job->handler(job->data)) job_remove(&cron_info.periodic, iter); else job->when.periodic.bucket = 0.0; } } } cron_info.now = now; }
/** Find out whether the user's application for the job is accepted and show the appropriate popups. @return TRUE if accepted, FALSE otherwise. */ gboolean misc2_callback_evaluate_job_application(Job *job, User *user) { #ifdef DEBUG printf("misc2_callback_evaluate_job_application\n"); #endif if(!query_job_application_successful(job, user)) { game_gui_show_warning( _("The owners of %s politely reject your application. You're not successful enough in their eyes."), job_get_team(job)->name); return FALSE; } if(job->type != JOB_TYPE_NATIONAL) { game_gui_show_warning( /* A lame duck is someone who will quit his job soon and thus doesn't have a lot of influence/impact anymore, e.g. an American president during the last 2 years of his second presidency. */ _("The owners of %s accept your application. Since %s don't want to get stuck with a lame duck, you get fired instantly and spend the rest of the current season tending your garden."), job_get_team(job)->name, user->tm->name); job_change_country(job); } else game_gui_show_warning( _("The owners of %s accept your application."), job_get_team(job)->name); user_change_team(user, team_of_id(job->team_id)); if(job->type == JOB_TYPE_NATIONAL) job_remove(job, TRUE); else free_jobs(TRUE); return TRUE; }
/* Remove job from the job list and free all memory associated with it. */ void job_free( job_t * j ) { job_remove( j ); delete j; }
int cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx) { struct cmd_target_data *data = self->data; const struct set_option_entry *table; struct session *s; struct winlink *wl; struct client *c; struct options *oo; const struct set_option_entry *entry, *opt; struct jobs *jobs; struct job *job, *nextjob; u_int i; int try_again; if (cmd_check_flag(data->chflags, 's')) { oo = &global_options; table = set_option_table; } else if (cmd_check_flag(data->chflags, 'w')) { table = set_window_option_table; if (cmd_check_flag(data->chflags, 'g')) oo = &global_w_options; else { wl = cmd_find_window(ctx, data->target, NULL); if (wl == NULL) return (-1); oo = &wl->window->options; } } else { table = set_session_option_table; if (cmd_check_flag(data->chflags, 'g')) oo = &global_s_options; else { s = cmd_find_session(ctx, data->target); if (s == NULL) return (-1); oo = &s->options; } } if (*data->arg == '\0') { ctx->error(ctx, "invalid option"); return (-1); } entry = NULL; for (opt = table; opt->name != NULL; opt++) { if (strncmp(opt->name, data->arg, strlen(data->arg)) != 0) continue; if (entry != NULL) { ctx->error(ctx, "ambiguous option: %s", data->arg); return (-1); } entry = opt; /* Bail now if an exact match. */ if (strcmp(entry->name, data->arg) == 0) break; } if (entry == NULL) { ctx->error(ctx, "unknown option: %s", data->arg); return (-1); } if (cmd_check_flag(data->chflags, 'u')) { if (cmd_check_flag(data->chflags, 'g')) { ctx->error(ctx, "can't unset global option: %s", entry->name); return (-1); } if (data->arg2 != NULL) { ctx->error(ctx, "value passed to unset option: %s", entry->name); return (-1); } options_remove(oo, entry->name); ctx->info(ctx, "unset option: %s", entry->name); } else { switch (entry->type) { case SET_OPTION_STRING: cmd_set_option_string(ctx, oo, entry, data->arg2, cmd_check_flag(data->chflags, 'a')); break; case SET_OPTION_NUMBER: cmd_set_option_number(ctx, oo, entry, data->arg2); break; case SET_OPTION_KEYS: cmd_set_option_keys(ctx, oo, entry, data->arg2); break; case SET_OPTION_COLOUR: cmd_set_option_colour(ctx, oo, entry, data->arg2); break; case SET_OPTION_ATTRIBUTES: cmd_set_option_attributes(ctx, oo, entry, data->arg2); break; case SET_OPTION_FLAG: cmd_set_option_flag(ctx, oo, entry, data->arg2); break; case SET_OPTION_CHOICE: cmd_set_option_choice(ctx, oo, entry, data->arg2); break; } } recalculate_sizes(); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c != NULL && c->session != NULL) server_redraw_client(c); } /* * Special-case: kill all persistent jobs if status-left, status-right * or set-titles-string have changed. Persistent jobs are only used by * the status line at the moment so this works XXX. */ if (strcmp(entry->name, "status-left") == 0 || strcmp(entry->name, "status-right") == 0 || strcmp(entry->name, "status") == 0 || strcmp(entry->name, "set-titles-string") == 0 || strcmp(entry->name, "window-status-format") == 0) { for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL) continue; jobs = &c->status_jobs; do { try_again = 0; job = RB_ROOT(jobs); while (job != NULL) { nextjob = RB_NEXT(jobs, jobs, job); if (job->flags & JOB_PERSIST) { job_remove(jobs, job); try_again = 1; break; } job = nextjob; } } while (try_again); server_redraw_client(c); } } return (0); }
static int process_clean_after_marking(bool allow_interactive) { ASSERT_IS_MAIN_THREAD(); job_t *jnext; int found = 0; // this function may fire an event handler, we do not want to call ourselves recursively (to avoid // infinite recursion). static bool locked = false; if (locked) { return 0; } locked = true; // this may be invoked in an exit handler, after the TERM has been torn down // don't try to print in that case (#3222) const bool interactive = allow_interactive && cur_term != NULL; job_iterator_t jobs; const size_t job_count = jobs.count(); jnext = jobs.next(); while (jnext) { job_t *j = jnext; jnext = jobs.next(); // If we are reaping only jobs who do not need status messages sent to the console, do not // consider reaping jobs that need status messages. if ((!j->get_flag(JOB_SKIP_NOTIFICATION)) && (!interactive) && (!j->get_flag(JOB_FOREGROUND))) { continue; } for (const process_ptr_t &p : j->processes) { int s; if (!p->completed) continue; if (!p->pid) continue; s = p->status; // TODO: The generic process-exit event is useless and unused. // Remove this in future. proc_fire_event(L"PROCESS_EXIT", EVENT_EXIT, p->pid, (WIFSIGNALED(s) ? -1 : WEXITSTATUS(s))); // Ignore signal SIGPIPE.We issue it ourselves to the pipe writer when the pipe reader // dies. if (!WIFSIGNALED(s) || WTERMSIG(s) == SIGPIPE) { continue; } // Handle signals other than SIGPIPE. int proc_is_job = (p->is_first_in_job && p->is_last_in_job); if (proc_is_job) j->set_flag(JOB_NOTIFIED, true); if (j->get_flag(JOB_SKIP_NOTIFICATION)) { continue; } // Print nothing if we get SIGINT in the foreground process group, to avoid spamming // obvious stuff on the console (#1119). If we get SIGINT for the foreground // process, assume the user typed ^C and can see it working. It's possible they // didn't, and the signal was delivered via pkill, etc., but the SIGINT/SIGTERM // distinction is precisely to allow INT to be from a UI // and TERM to be programmatic, so this assumption is keeping with the design of // signals. If echoctl is on, then the terminal will have written ^C to the console. // If off, it won't have. We don't echo ^C either way, so as to respect the user's // preference. if (WTERMSIG(p->status) != SIGINT || !j->get_flag(JOB_FOREGROUND)) { if (proc_is_job) { // We want to report the job number, unless it's the only job, in which case // we don't need to. const wcstring job_number_desc = (job_count == 1) ? wcstring() : format_string(_(L"Job %d, "), j->job_id); fwprintf(stdout, _(L"%ls: %ls\'%ls\' terminated by signal %ls (%ls)"), program_name, job_number_desc.c_str(), truncate_command(j->command()).c_str(), sig2wcs(WTERMSIG(p->status)), signal_get_desc(WTERMSIG(p->status))); } else { const wcstring job_number_desc = (job_count == 1) ? wcstring() : format_string(L"from job %d, ", j->job_id); const wchar_t *fmt = _(L"%ls: Process %d, \'%ls\' %ls\'%ls\' terminated by signal %ls (%ls)"); fwprintf(stdout, fmt, program_name, p->pid, p->argv0(), job_number_desc.c_str(), truncate_command(j->command()).c_str(), sig2wcs(WTERMSIG(p->status)), signal_get_desc(WTERMSIG(p->status))); } if (cur_term != NULL) { tputs(clr_eol, 1, &writeb); } else { fwprintf(stdout, L"\e[K"); // no term set up - do clr_eol manually } fwprintf(stdout, L"\n"); } found = 1; p->status = 0; // clear status so it is not reported more than once } // If all processes have completed, tell the user the job has completed and delete it from // the active job list. if (job_is_completed(j)) { if (!j->get_flag(JOB_FOREGROUND) && !j->get_flag(JOB_NOTIFIED) && !j->get_flag(JOB_SKIP_NOTIFICATION)) { format_job_info(j, JOB_ENDED); found = 1; } // TODO: The generic process-exit event is useless and unused. // Remove this in future. // Don't fire the exit-event for jobs with pgid -2. // That's our "sentinel" pgid, for jobs that don't (yet) have a pgid, // or jobs that consist entirely of builtins (and hence don't have a process). // This causes issues if fish is PID 2, which is quite common on WSL. See #4582. if (j->pgid != -2) { proc_fire_event(L"JOB_EXIT", EVENT_EXIT, -j->pgid, 0); } proc_fire_event(L"JOB_EXIT", EVENT_JOB_ID, j->job_id, 0); job_remove(j); } else if (job_is_stopped(j) && !j->get_flag(JOB_NOTIFIED)) { // Notify the user about newly stopped jobs. if (!j->get_flag(JOB_SKIP_NOTIFICATION)) { format_job_info(j, JOB_STOPPED); found = 1; } j->set_flag(JOB_NOTIFIED, true); } } if (found) fflush(stdout); locked = false; return found; }
// Readrec5 is like readrec, but it reads a record in "version 5" // of the log format. static int readrec5(File *f, job l, int *err) { int r, sz = 0; size_t namelen; Jobrec5 jr; job j; tube t; char tubename[MAX_TUBE_NAME_LEN]; r = read(f->fd, &namelen, sizeof(namelen)); if (r == -1) { twarn("read"); warnpos(f, 0, "error"); *err = 1; return 0; } if (r != sizeof(namelen)) { return 0; } sz += r; if (namelen >= MAX_TUBE_NAME_LEN) { warnpos(f, -r, "namelen %zu exceeds maximum of %d", namelen, MAX_TUBE_NAME_LEN - 1); *err = 1; return 0; } if (namelen) { r = readfull(f, tubename, namelen, err, "v5 tube name"); if (!r) { return 0; } sz += r; } tubename[namelen] = '\0'; r = readfull(f, &jr, Jobrec5size, err, "v5 job struct"); if (!r) { return 0; } sz += r; // are we reading trailing zeroes? if (!jr.id) return 0; j = job_find(jr.id); if (!(j || namelen)) { // We read a short record without having seen a // full record for this job, so the full record // was in an eariler file that has been deleted. // Therefore the job itself has either been // deleted or migrated; either way, this record // should be ignored. return 1; } switch (jr.state) { case Reserved: jr.state = Ready; case Ready: case Buried: case Delayed: if (!j) { if (jr.body_size > job_data_size_limit) { warnpos(f, -r, "job %"PRIu64" is too big (%"PRId32" > %zu)", jr.id, jr.body_size, job_data_size_limit); goto Error; } t = tube_find_or_make(tubename); j = make_job_with_id(jr.pri, jr.delay, jr.ttr, jr.body_size, t, jr.id); j->next = j->prev = j; j->r.created_at = jr.created_at; } j->r.id = jr.id; j->r.pri = jr.pri; j->r.delay = jr.delay * 1000; // us => ns j->r.ttr = jr.ttr * 1000; // us => ns j->r.body_size = jr.body_size; j->r.created_at = jr.created_at * 1000; // us => ns j->r.deadline_at = jr.deadline_at * 1000; // us => ns j->r.reserve_ct = jr.reserve_ct; j->r.timeout_ct = jr.timeout_ct; j->r.release_ct = jr.release_ct; j->r.bury_ct = jr.bury_ct; j->r.kick_ct = jr.kick_ct; j->r.state = jr.state; job_insert(l, j); // full record; read the job body if (namelen) { if (jr.body_size != j->r.body_size) { warnpos(f, -r, "job %"PRIu64" size changed", j->r.id); warnpos(f, -r, "was %"PRId32", now %"PRId32, j->r.body_size, jr.body_size); goto Error; } r = readfull(f, j->body, j->r.body_size, err, "v5 job body"); if (!r) { goto Error; } sz += r; // since this is a full record, we can move // the file pointer and decref the old // file, if any filermjob(j->file, j); fileaddjob(f, j); } j->walused += sz; f->w->alive += sz; return 1; case Invalid: if (j) { job_remove(j); filermjob(j->file, j); job_free(j); } return 1; } Error: *err = 1; if (j) { job_remove(j); filermjob(j->file, j); job_free(j); } return 0; }
int cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx) { struct cmd_target_data *data = self->data; const struct options_table_entry *table, *oe, *oe_loop; struct session *s; struct winlink *wl; struct client *c; struct options *oo; struct jobs *jobs; struct job *job, *nextjob; u_int i; int try_again; /* Work out the options tree and table to use. */ if (cmd_check_flag(data->chflags, 's')) { oo = &global_options; table = server_options_table; } else if (cmd_check_flag(data->chflags, 'w')) { table = window_options_table; if (cmd_check_flag(data->chflags, 'g')) oo = &global_w_options; else { wl = cmd_find_window(ctx, data->target, NULL); if (wl == NULL) return (-1); oo = &wl->window->options; } } else { table = session_options_table; if (cmd_check_flag(data->chflags, 'g')) oo = &global_s_options; else { s = cmd_find_session(ctx, data->target); if (s == NULL) return (-1); oo = &s->options; } } /* Find the option table entry. */ oe = NULL; for (oe_loop = table; oe_loop->name != NULL; oe_loop++) { if (strncmp(oe_loop->name, data->arg, strlen(data->arg)) != 0) continue; if (oe != NULL) { ctx->error(ctx, "ambiguous option: %s", data->arg); return (-1); } oe = oe_loop; /* Bail now if an exact match. */ if (strcmp(oe->name, data->arg) == 0) break; } if (oe == NULL) { ctx->error(ctx, "unknown option: %s", data->arg); return (-1); } /* Unset or set the option. */ if (cmd_check_flag(data->chflags, 'u')) { if (cmd_set_option_unset(self, ctx, oe, oo) != 0) return (-1); } else { if (cmd_set_option_set(self, ctx, oe, oo) != 0) return (-1); } /* Update sizes and redraw. May not need it but meh. */ recalculate_sizes(); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c != NULL && c->session != NULL) server_redraw_client(c); } /* * Special-case: kill all persistent jobs if status-left, status-right * or set-titles-string have changed. Persistent jobs are only used by * the status line at the moment so this works XXX. */ if (strcmp(oe->name, "status-left") == 0 || strcmp(oe->name, "status-right") == 0 || strcmp(oe->name, "status") == 0 || strcmp(oe->name, "set-titles-string") == 0 || strcmp(oe->name, "window-status-format") == 0) { for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL) continue; jobs = &c->status_jobs; do { try_again = 0; job = RB_ROOT(jobs); while (job != NULL) { nextjob = RB_NEXT(jobs, jobs, job); if (job->flags & JOB_PERSIST) { job_remove(jobs, job); try_again = 1; break; } job = nextjob; } } while (try_again); server_redraw_client(c); } } return (0); }