/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "bus-error.h" #include "bus-locator.h" #include "locale-util.h" #include "systemctl-list-jobs.h" #include "systemctl-util.h" #include "systemctl.h" #include "terminal-util.h" static int output_waiting_jobs(sd_bus *bus, Table *table, uint32_t id, const char *method, const char *prefix) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; const char *name, *type; uint32_t other_id; int r; assert(bus); r = bus_call_method(bus, bus_systemd_mgr, method, &error, &reply, "u", id); if (r < 0) return log_debug_errno(r, "Failed to get waiting jobs for job %" PRIu32, id); r = sd_bus_message_enter_container(reply, 'a', "(usssoo)"); if (r < 0) return bus_log_parse_error(r); while ((r = sd_bus_message_read(reply, "(usssoo)", &other_id, &name, &type, NULL, NULL, NULL)) > 0) { _cleanup_free_ char *row = NULL; int rc; if (asprintf(&row, "%s %u (%s/%s)", prefix, other_id, name, type) < 0) return log_oom(); rc = table_add_many(table, TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_RIGHT), TABLE_STRING, row, TABLE_EMPTY, TABLE_EMPTY); if (rc < 0) return table_log_add_error(r); } if (r < 0) return bus_log_parse_error(r); r = sd_bus_message_exit_container(reply); if (r < 0) return bus_log_parse_error(r); return 0; } struct job_info { uint32_t id; const char *name, *type, *state; }; static int output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned n, bool skipped) { _cleanup_(table_unrefp) Table *table = NULL; const char *on, *off; int r; assert(n == 0 || jobs); if (n == 0) { if (arg_legend != 0) { on = ansi_highlight_green(); off = ansi_normal(); printf("%sNo jobs %s.%s\n", on, skipped ? "listed" : "running", off); } return 0; } pager_open(arg_pager_flags); table = table_new("job", "unit", "type", "state"); if (!table) return log_oom(); table_set_header(table, arg_legend != 0); if (arg_full) table_set_width(table, 0); table_set_ersatz_string(table, TABLE_ERSATZ_DASH); for (const struct job_info *j = jobs; j < jobs + n; j++) { if (streq(j->state, "running")) on = ansi_highlight(); else on = ""; r = table_add_many(table, TABLE_UINT, j->id, TABLE_STRING, j->name, TABLE_SET_COLOR, on, TABLE_STRING, j->type, TABLE_STRING, j->state, TABLE_SET_COLOR, on); if (r < 0) return table_log_add_error(r); if (arg_jobs_after) output_waiting_jobs(bus, table, j->id, "GetJobAfter", "\twaiting for job"); if (arg_jobs_before) output_waiting_jobs(bus, table, j->id, "GetJobBefore", "\tblocking job"); } r = table_print(table, NULL); if (r < 0) return log_error_errno(r, "Failed to print the table: %m"); if (arg_legend != 0) { on = ansi_highlight(); off = ansi_normal(); printf("\n%s%u jobs listed%s.\n", on, n, off); } return 0; } static bool output_show_job(struct job_info *job, char **patterns) { return strv_fnmatch_or_empty(patterns, job->name, FNM_NOESCAPE); } int verb_list_jobs(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ struct job_info *jobs = NULL; const char *name, *type, *state; bool skipped = false; unsigned c = 0; sd_bus *bus; uint32_t id; int r; r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) return r; r = bus_call_method(bus, bus_systemd_mgr, "ListJobs", &error, &reply, NULL); if (r < 0) return log_error_errno(r, "Failed to list jobs: %s", bus_error_message(&error, r)); r = sd_bus_message_enter_container(reply, 'a', "(usssoo)"); if (r < 0) return bus_log_parse_error(r); while ((r = sd_bus_message_read(reply, "(usssoo)", &id, &name, &type, &state, NULL, NULL)) > 0) { struct job_info job = { id, name, type, state }; if (!output_show_job(&job, strv_skip(argv, 1))) { skipped = true; continue; } if (!GREEDY_REALLOC(jobs, c + 1)) return log_oom(); jobs[c++] = job; } if (r < 0) return bus_log_parse_error(r); r = sd_bus_message_exit_container(reply); if (r < 0) return bus_log_parse_error(r); pager_open(arg_pager_flags); return output_jobs_list(bus, jobs, c, skipped); }