]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
5de0409e | 2 | /*** |
96b2fb93 | 3 | Copyright © 2012 Zbigniew Jędrzejewski-Szmek |
5de0409e ZJS |
4 | ***/ |
5 | ||
3f6fd1ba LP |
6 | #include <fcntl.h> |
7 | #include <getopt.h> | |
a9cdc94f | 8 | #include <locale.h> |
5de0409e ZJS |
9 | #include <stdio.h> |
10 | #include <string.h> | |
ada45c78 | 11 | #include <unistd.h> |
5de0409e | 12 | |
012f2b7d | 13 | #include "sd-bus.h" |
2cf4172a | 14 | #include "sd-journal.h" |
2b044526 | 15 | #include "sd-messages.h" |
3f6fd1ba | 16 | |
b5efdb8a | 17 | #include "alloc-util.h" |
012f2b7d ZJS |
18 | #include "bus-error.h" |
19 | #include "bus-util.h" | |
3f6fd1ba | 20 | #include "compress.h" |
3ffd4af2 | 21 | #include "fd-util.h" |
0d39fa9c | 22 | #include "fileio.h" |
992e8f22 | 23 | #include "fs-util.h" |
3f6fd1ba | 24 | #include "journal-internal.h" |
b9aaa7f4 | 25 | #include "journal-util.h" |
5de0409e | 26 | #include "log.h" |
763c7aa2 | 27 | #include "macro.h" |
3f6fd1ba | 28 | #include "pager.h" |
6bedfcbb | 29 | #include "parse-util.h" |
3f6fd1ba | 30 | #include "path-util.h" |
0b452006 | 31 | #include "process-util.h" |
3f6fd1ba | 32 | #include "sigbus.h" |
24882e06 | 33 | #include "signal-util.h" |
07630cea | 34 | #include "string-util.h" |
5ab9ed07 | 35 | #include "strv.h" |
3f6fd1ba | 36 | #include "terminal-util.h" |
b1d4f8e1 | 37 | #include "user-util.h" |
6bedfcbb | 38 | #include "util.h" |
5ce97d33 | 39 | #include "verbs.h" |
5de0409e | 40 | |
501551e8 LP |
41 | #define SHORT_BUS_CALL_TIMEOUT_USEC (3 * USEC_PER_SEC) |
42 | ||
32485d09 | 43 | static usec_t arg_since = USEC_INFINITY, arg_until = USEC_INFINITY; |
e15758cc | 44 | static const char* arg_field = NULL; |
c5896b6a | 45 | static const char *arg_debugger = NULL; |
b73e9a02 | 46 | static const char *arg_directory = NULL; |
ea4b98e6 | 47 | static bool arg_no_pager = false; |
9a340880 | 48 | static int arg_no_legend = false; |
0c51aada | 49 | static int arg_one = false; |
3774cf57 | 50 | static FILE* arg_output = NULL; |
df65f77b | 51 | static bool arg_reverse = false; |
b9aaa7f4 | 52 | static bool arg_quiet = false; |
5de0409e | 53 | |
5ab9ed07 | 54 | static int add_match(sd_journal *j, const char *match) { |
7fd1b19b | 55 | _cleanup_free_ char *p = NULL; |
43bfe750 | 56 | const char* prefix, *pattern; |
0f474365 LP |
57 | pid_t pid; |
58 | int r; | |
5de0409e ZJS |
59 | |
60 | if (strchr(match, '=')) | |
61 | prefix = ""; | |
62 | else if (strchr(match, '/')) { | |
0f474365 LP |
63 | r = path_make_absolute_cwd(match, &p); |
64 | if (r < 0) | |
5ab9ed07 ZJS |
65 | return log_error_errno(r, "path_make_absolute_cwd(\"%s\"): %m", match); |
66 | ||
5de0409e ZJS |
67 | match = p; |
68 | prefix = "COREDUMP_EXE="; | |
0f474365 | 69 | } else if (parse_pid(match, &pid) >= 0) |
5de0409e ZJS |
70 | prefix = "COREDUMP_PID="; |
71 | else | |
72 | prefix = "COREDUMP_COMM="; | |
73 | ||
e8fb0238 | 74 | pattern = strjoina(prefix, match); |
5ab9ed07 ZJS |
75 | log_debug("Adding match: %s", pattern); |
76 | r = sd_journal_add_match(j, pattern, 0); | |
77 | if (r < 0) | |
78 | return log_error_errno(r, "Failed to add match \"%s\": %m", match); | |
43bfe750 | 79 | |
5ab9ed07 ZJS |
80 | return 0; |
81 | } | |
82 | ||
5ce97d33 | 83 | static int add_matches(sd_journal *j, char **matches) { |
5ab9ed07 ZJS |
84 | char **match; |
85 | int r; | |
5de0409e | 86 | |
2b044526 | 87 | r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR, 0); |
0f474365 | 88 | if (r < 0) |
2b044526 | 89 | return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR); |
5ab9ed07 | 90 | |
a7581ff9 ZJS |
91 | r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR, 0); |
92 | if (r < 0) | |
93 | return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR); | |
94 | ||
5ce97d33 | 95 | STRV_FOREACH(match, matches) { |
5ab9ed07 ZJS |
96 | r = add_match(j, *match); |
97 | if (r < 0) | |
98 | return r; | |
99 | } | |
5de0409e ZJS |
100 | |
101 | return 0; | |
5de0409e ZJS |
102 | } |
103 | ||
5ce97d33 YW |
104 | static int acquire_journal(sd_journal **ret, char **matches) { |
105 | _cleanup_(sd_journal_closep) sd_journal *j = NULL; | |
106 | int r; | |
107 | ||
108 | assert(ret); | |
109 | ||
110 | if (arg_directory) { | |
111 | r = sd_journal_open_directory(&j, arg_directory, 0); | |
112 | if (r < 0) | |
113 | return log_error_errno(r, "Failed to open journals in directory: %s: %m", arg_directory); | |
114 | } else { | |
115 | r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); | |
116 | if (r < 0) | |
117 | return log_error_errno(r, "Failed to open journal: %m"); | |
118 | } | |
119 | ||
120 | r = journal_access_check_and_warn(j, arg_quiet, true); | |
121 | if (r < 0) | |
122 | return r; | |
123 | ||
124 | r = add_matches(j, matches); | |
125 | if (r < 0) | |
126 | return r; | |
127 | ||
128 | if (DEBUG_LOGGING) { | |
129 | _cleanup_free_ char *filter; | |
130 | ||
131 | filter = journal_make_match_string(j); | |
132 | log_debug("Journal filter: %s", filter); | |
133 | } | |
134 | ||
1cc6c93a | 135 | *ret = TAKE_PTR(j); |
5ce97d33 YW |
136 | |
137 | return 0; | |
138 | } | |
139 | ||
140 | static int help(void) { | |
601185b4 ZJS |
141 | printf("%s [OPTIONS...]\n\n" |
142 | "List or retrieve coredumps from the journal.\n\n" | |
143 | "Flags:\n" | |
c5896b6a RG |
144 | " -h --help Show this help\n" |
145 | " --version Print version string\n" | |
146 | " --no-pager Do not pipe output into a pager\n" | |
147 | " --no-legend Do not print the column headers\n" | |
148 | " --debugger=DEBUGGER Use the given debugger\n" | |
149 | " -1 Show information about most recent entry only\n" | |
150 | " -S --since=DATE Only print coredumps since the date\n" | |
151 | " -U --until=DATE Only print coredumps until the date\n" | |
152 | " -r --reverse Show the newest entries first\n" | |
153 | " -F --field=FIELD List all values a certain field takes\n" | |
154 | " -o --output=FILE Write output to FILE\n" | |
155 | " -D --directory=DIR Use journal files from directory\n\n" | |
156 | " -q --quiet Do not show info messages and privilege warning\n" | |
601185b4 ZJS |
157 | "Commands:\n" |
158 | " list [MATCHES...] List available coredumps (default)\n" | |
159 | " info [MATCHES...] Show detailed information about one or more coredumps\n" | |
160 | " dump [MATCHES...] Print first matching coredump to stdout\n" | |
c5896b6a | 161 | " debug [MATCHES...] Start a debugger for the first matching coredump\n" |
601185b4 | 162 | , program_invocation_short_name); |
5ce97d33 YW |
163 | |
164 | return 0; | |
601185b4 ZJS |
165 | } |
166 | ||
5ab9ed07 | 167 | static int parse_argv(int argc, char *argv[]) { |
5de0409e ZJS |
168 | enum { |
169 | ARG_VERSION = 0x100, | |
170 | ARG_NO_PAGER, | |
9a340880 | 171 | ARG_NO_LEGEND, |
c5896b6a | 172 | ARG_DEBUGGER, |
5de0409e ZJS |
173 | }; |
174 | ||
32485d09 | 175 | int c, r; |
5de0409e ZJS |
176 | |
177 | static const struct option options[] = { | |
57ce4bd4 ZJS |
178 | { "help", no_argument, NULL, 'h' }, |
179 | { "version" , no_argument, NULL, ARG_VERSION }, | |
180 | { "no-pager", no_argument, NULL, ARG_NO_PAGER }, | |
9a340880 | 181 | { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, |
c5896b6a | 182 | { "debugger", required_argument, NULL, ARG_DEBUGGER }, |
57ce4bd4 | 183 | { "output", required_argument, NULL, 'o' }, |
4f76ae1b | 184 | { "field", required_argument, NULL, 'F' }, |
b73e9a02 | 185 | { "directory", required_argument, NULL, 'D' }, |
df65f77b | 186 | { "reverse", no_argument, NULL, 'r' }, |
32485d09 GS |
187 | { "since", required_argument, NULL, 'S' }, |
188 | { "until", required_argument, NULL, 'U' }, | |
b9aaa7f4 | 189 | { "quiet", no_argument, NULL, 'q' }, |
eb9da376 | 190 | {} |
5de0409e ZJS |
191 | }; |
192 | ||
193 | assert(argc >= 0); | |
194 | assert(argv); | |
195 | ||
b9aaa7f4 | 196 | while ((c = getopt_long(argc, argv, "ho:F:1D:rS:U:q", options, NULL)) >= 0) |
5de0409e ZJS |
197 | switch(c) { |
198 | case 'h': | |
5ce97d33 | 199 | return help(); |
5de0409e ZJS |
200 | |
201 | case ARG_VERSION: | |
3f6fd1ba | 202 | return version(); |
5de0409e ZJS |
203 | |
204 | case ARG_NO_PAGER: | |
205 | arg_no_pager = true; | |
206 | break; | |
207 | ||
9a340880 ZJS |
208 | case ARG_NO_LEGEND: |
209 | arg_no_legend = true; | |
210 | break; | |
211 | ||
c5896b6a RG |
212 | case ARG_DEBUGGER: |
213 | arg_debugger = optarg; | |
214 | break; | |
215 | ||
5de0409e | 216 | case 'o': |
3774cf57 | 217 | if (arg_output) { |
abdf29f5 | 218 | log_error("Cannot set output more than once."); |
5de0409e ZJS |
219 | return -EINVAL; |
220 | } | |
221 | ||
3774cf57 LP |
222 | arg_output = fopen(optarg, "we"); |
223 | if (!arg_output) | |
4a62c710 | 224 | return log_error_errno(errno, "writing to '%s': %m", optarg); |
5de0409e ZJS |
225 | |
226 | break; | |
57ce4bd4 | 227 | |
32485d09 GS |
228 | case 'S': |
229 | r = parse_timestamp(optarg, &arg_since); | |
230 | if (r < 0) | |
231 | return log_error_errno(r, "Failed to parse timestamp: %s", optarg); | |
232 | break; | |
233 | ||
234 | case 'U': | |
235 | r = parse_timestamp(optarg, &arg_until); | |
236 | if (r < 0) | |
237 | return log_error_errno(r, "Failed to parse timestamp: %s", optarg); | |
238 | break; | |
239 | ||
4f76ae1b | 240 | case 'F': |
a276ae74 | 241 | if (arg_field) { |
abdf29f5 | 242 | log_error("Cannot use --field/-F more than once."); |
4f76ae1b ZJS |
243 | return -EINVAL; |
244 | } | |
a276ae74 | 245 | arg_field = optarg; |
4f76ae1b ZJS |
246 | break; |
247 | ||
0c51aada LP |
248 | case '1': |
249 | arg_one = true; | |
250 | break; | |
251 | ||
b73e9a02 SW |
252 | case 'D': |
253 | arg_directory = optarg; | |
254 | break; | |
255 | ||
df65f77b NK |
256 | case 'r': |
257 | arg_reverse = true; | |
258 | break; | |
259 | ||
b9aaa7f4 ZJS |
260 | case 'q': |
261 | arg_quiet = true; | |
262 | break; | |
263 | ||
57ce4bd4 ZJS |
264 | case '?': |
265 | return -EINVAL; | |
266 | ||
5de0409e | 267 | default: |
eb9da376 | 268 | assert_not_reached("Unhandled option"); |
5de0409e ZJS |
269 | } |
270 | ||
32485d09 GS |
271 | if (arg_since != USEC_INFINITY && arg_until != USEC_INFINITY && |
272 | arg_since > arg_until) { | |
273 | log_error("--since= must be before --until=."); | |
274 | return -EINVAL; | |
275 | } | |
276 | ||
5ce97d33 | 277 | return 1; |
5de0409e ZJS |
278 | } |
279 | ||
8bc8ab83 LP |
280 | static int retrieve(const void *data, |
281 | size_t len, | |
282 | const char *name, | |
a276ae74 | 283 | char **var) { |
5de0409e | 284 | |
4f76ae1b | 285 | size_t ident; |
a276ae74 | 286 | char *v; |
8bc8ab83 | 287 | |
4f76ae1b | 288 | ident = strlen(name) + 1; /* name + "=" */ |
8bc8ab83 | 289 | |
4f76ae1b | 290 | if (len < ident) |
8bc8ab83 | 291 | return 0; |
5de0409e | 292 | |
4f76ae1b | 293 | if (memcmp(data, name, ident - 1) != 0) |
8bc8ab83 LP |
294 | return 0; |
295 | ||
4f76ae1b | 296 | if (((const char*) data)[ident - 1] != '=') |
8bc8ab83 | 297 | return 0; |
5de0409e | 298 | |
a276ae74 LP |
299 | v = strndup((const char*)data + ident, len - ident); |
300 | if (!v) | |
5de0409e ZJS |
301 | return log_oom(); |
302 | ||
a276ae74 LP |
303 | free(*var); |
304 | *var = v; | |
305 | ||
062b99e8 | 306 | return 1; |
5de0409e ZJS |
307 | } |
308 | ||
062b99e8 | 309 | static int print_field(FILE* file, sd_journal *j) { |
4f76ae1b ZJS |
310 | const void *d; |
311 | size_t l; | |
312 | ||
e15758cc LP |
313 | assert(file); |
314 | assert(j); | |
315 | ||
a276ae74 | 316 | assert(arg_field); |
4f76ae1b | 317 | |
062b99e8 ZJS |
318 | /* A (user-specified) field may appear more than once for a given entry. |
319 | * We will print all of the occurences. | |
320 | * This is different below for fields that systemd-coredump uses, | |
321 | * because they cannot meaningfully appear more than once. | |
322 | */ | |
323 | SD_JOURNAL_FOREACH_DATA(j, d, l) { | |
324 | _cleanup_free_ char *value = NULL; | |
325 | int r; | |
326 | ||
327 | r = retrieve(d, l, arg_field, &value); | |
328 | if (r < 0) | |
329 | return r; | |
330 | if (r > 0) | |
331 | fprintf(file, "%s\n", value); | |
332 | } | |
a276ae74 | 333 | |
062b99e8 | 334 | return 0; |
4f76ae1b ZJS |
335 | } |
336 | ||
062b99e8 ZJS |
337 | #define RETRIEVE(d, l, name, arg) \ |
338 | { \ | |
339 | int _r = retrieve(d, l, name, &arg); \ | |
340 | if (_r < 0) \ | |
341 | return _r; \ | |
342 | if (_r > 0) \ | |
343 | continue; \ | |
344 | } | |
345 | ||
e15758cc | 346 | static int print_list(FILE* file, sd_journal *j, int had_legend) { |
a276ae74 | 347 | _cleanup_free_ char |
a7581ff9 | 348 | *mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL, |
9fe13294 | 349 | *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL, |
cc4419ed | 350 | *filename = NULL, *truncated = NULL, *coredump = NULL; |
8bc8ab83 LP |
351 | const void *d; |
352 | size_t l; | |
684341b0 LP |
353 | usec_t t; |
354 | char buf[FORMAT_TIMESTAMP_MAX]; | |
355 | int r; | |
04de5879 | 356 | const char *present; |
a7581ff9 | 357 | bool normal_coredump; |
8bc8ab83 | 358 | |
e15758cc LP |
359 | assert(file); |
360 | assert(j); | |
361 | ||
8bc8ab83 | 362 | SD_JOURNAL_FOREACH_DATA(j, d, l) { |
a7581ff9 | 363 | RETRIEVE(d, l, "MESSAGE_ID", mid); |
062b99e8 ZJS |
364 | RETRIEVE(d, l, "COREDUMP_PID", pid); |
365 | RETRIEVE(d, l, "COREDUMP_UID", uid); | |
366 | RETRIEVE(d, l, "COREDUMP_GID", gid); | |
367 | RETRIEVE(d, l, "COREDUMP_SIGNAL", sgnl); | |
368 | RETRIEVE(d, l, "COREDUMP_EXE", exe); | |
369 | RETRIEVE(d, l, "COREDUMP_COMM", comm); | |
370 | RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline); | |
371 | RETRIEVE(d, l, "COREDUMP_FILENAME", filename); | |
cc4419ed | 372 | RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated); |
062b99e8 | 373 | RETRIEVE(d, l, "COREDUMP", coredump); |
8bc8ab83 | 374 | } |
5de0409e | 375 | |
9fe13294 | 376 | if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename) { |
684341b0 LP |
377 | log_warning("Empty coredump log entry"); |
378 | return -EINVAL; | |
379 | } | |
380 | ||
381 | r = sd_journal_get_realtime_usec(j, &t); | |
23bbb0de MS |
382 | if (r < 0) |
383 | return log_error_errno(r, "Failed to get realtime timestamp: %m"); | |
5de0409e | 384 | |
684341b0 LP |
385 | format_timestamp(buf, sizeof(buf), t); |
386 | ||
9a340880 | 387 | if (!had_legend && !arg_no_legend) |
cc4419ed | 388 | fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n", |
c3f84106 | 389 | FORMAT_TIMESTAMP_WIDTH, "TIME", |
5de0409e ZJS |
390 | 6, "PID", |
391 | 5, "UID", | |
392 | 5, "GID", | |
684341b0 | 393 | 3, "SIG", |
cc4419ed | 394 | 9, "COREFILE", |
684341b0 | 395 | "EXE"); |
5de0409e | 396 | |
a7581ff9 ZJS |
397 | normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR); |
398 | ||
04de5879 ZJS |
399 | if (filename) |
400 | if (access(filename, R_OK) == 0) | |
401 | present = "present"; | |
402 | else if (errno == ENOENT) | |
403 | present = "missing"; | |
404 | else | |
405 | present = "error"; | |
406 | else if (coredump) | |
407 | present = "journal"; | |
a7581ff9 | 408 | else if (normal_coredump) |
04de5879 | 409 | present = "none"; |
a7581ff9 ZJS |
410 | else |
411 | present = "-"; | |
04de5879 | 412 | |
32a1575f | 413 | if (STR_IN_SET(present, "present", "journal") && truncated && parse_boolean(truncated) > 0) |
cc4419ed ZJS |
414 | present = "truncated"; |
415 | ||
04de5879 | 416 | fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n", |
c3f84106 | 417 | FORMAT_TIMESTAMP_WIDTH, buf, |
a276ae74 LP |
418 | 6, strna(pid), |
419 | 5, strna(uid), | |
420 | 5, strna(gid), | |
a7581ff9 | 421 | 3, normal_coredump ? strna(sgnl) : "-", |
cc4419ed | 422 | 9, present, |
a276ae74 | 423 | strna(exe ?: (comm ?: cmdline))); |
684341b0 LP |
424 | |
425 | return 0; | |
5de0409e ZJS |
426 | } |
427 | ||
e15758cc LP |
428 | static int print_info(FILE *file, sd_journal *j, bool need_space) { |
429 | _cleanup_free_ char | |
a7581ff9 | 430 | *mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL, |
e15758cc LP |
431 | *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL, |
432 | *unit = NULL, *user_unit = NULL, *session = NULL, | |
a035f819 | 433 | *boot_id = NULL, *machine_id = NULL, *hostname = NULL, |
9fe13294 | 434 | *slice = NULL, *cgroup = NULL, *owner_uid = NULL, |
47f50642 | 435 | *message = NULL, *timestamp = NULL, *filename = NULL, |
cc4419ed | 436 | *truncated = NULL, *coredump = NULL; |
e15758cc LP |
437 | const void *d; |
438 | size_t l; | |
a7581ff9 | 439 | bool normal_coredump; |
4b8cbe9a | 440 | int r; |
e15758cc LP |
441 | |
442 | assert(file); | |
443 | assert(j); | |
444 | ||
445 | SD_JOURNAL_FOREACH_DATA(j, d, l) { | |
a7581ff9 | 446 | RETRIEVE(d, l, "MESSAGE_ID", mid); |
062b99e8 ZJS |
447 | RETRIEVE(d, l, "COREDUMP_PID", pid); |
448 | RETRIEVE(d, l, "COREDUMP_UID", uid); | |
449 | RETRIEVE(d, l, "COREDUMP_GID", gid); | |
450 | RETRIEVE(d, l, "COREDUMP_SIGNAL", sgnl); | |
451 | RETRIEVE(d, l, "COREDUMP_EXE", exe); | |
452 | RETRIEVE(d, l, "COREDUMP_COMM", comm); | |
453 | RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline); | |
454 | RETRIEVE(d, l, "COREDUMP_UNIT", unit); | |
455 | RETRIEVE(d, l, "COREDUMP_USER_UNIT", user_unit); | |
456 | RETRIEVE(d, l, "COREDUMP_SESSION", session); | |
457 | RETRIEVE(d, l, "COREDUMP_OWNER_UID", owner_uid); | |
458 | RETRIEVE(d, l, "COREDUMP_SLICE", slice); | |
459 | RETRIEVE(d, l, "COREDUMP_CGROUP", cgroup); | |
460 | RETRIEVE(d, l, "COREDUMP_TIMESTAMP", timestamp); | |
461 | RETRIEVE(d, l, "COREDUMP_FILENAME", filename); | |
cc4419ed | 462 | RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated); |
062b99e8 ZJS |
463 | RETRIEVE(d, l, "COREDUMP", coredump); |
464 | RETRIEVE(d, l, "_BOOT_ID", boot_id); | |
465 | RETRIEVE(d, l, "_MACHINE_ID", machine_id); | |
466 | RETRIEVE(d, l, "_HOSTNAME", hostname); | |
467 | RETRIEVE(d, l, "MESSAGE", message); | |
e15758cc LP |
468 | } |
469 | ||
470 | if (need_space) | |
471 | fputs("\n", file); | |
472 | ||
a7581ff9 ZJS |
473 | normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR); |
474 | ||
81cef14f LP |
475 | if (comm) |
476 | fprintf(file, | |
477 | " PID: %s%s%s (%s)\n", | |
1fc464f6 | 478 | ansi_highlight(), strna(pid), ansi_normal(), comm); |
81cef14f LP |
479 | else |
480 | fprintf(file, | |
481 | " PID: %s%s%s\n", | |
1fc464f6 | 482 | ansi_highlight(), strna(pid), ansi_normal()); |
a035f819 LP |
483 | |
484 | if (uid) { | |
485 | uid_t n; | |
486 | ||
487 | if (parse_uid(uid, &n) >= 0) { | |
488 | _cleanup_free_ char *u = NULL; | |
489 | ||
490 | u = uid_to_name(n); | |
491 | fprintf(file, | |
492 | " UID: %s (%s)\n", | |
493 | uid, u); | |
494 | } else { | |
495 | fprintf(file, | |
496 | " UID: %s\n", | |
497 | uid); | |
498 | } | |
499 | } | |
500 | ||
501 | if (gid) { | |
502 | gid_t n; | |
503 | ||
504 | if (parse_gid(gid, &n) >= 0) { | |
505 | _cleanup_free_ char *g = NULL; | |
506 | ||
507 | g = gid_to_name(n); | |
508 | fprintf(file, | |
509 | " GID: %s (%s)\n", | |
510 | gid, g); | |
511 | } else { | |
512 | fprintf(file, | |
513 | " GID: %s\n", | |
514 | gid); | |
515 | } | |
516 | } | |
e15758cc LP |
517 | |
518 | if (sgnl) { | |
519 | int sig; | |
a7581ff9 | 520 | const char *name = normal_coredump ? "Signal" : "Reason"; |
e15758cc | 521 | |
a7581ff9 ZJS |
522 | if (normal_coredump && safe_atoi(sgnl, &sig) >= 0) |
523 | fprintf(file, " %s: %s (%s)\n", name, sgnl, signal_to_string(sig)); | |
e15758cc | 524 | else |
a7581ff9 | 525 | fprintf(file, " %s: %s\n", name, sgnl); |
e15758cc LP |
526 | } |
527 | ||
4b8cbe9a LP |
528 | if (timestamp) { |
529 | usec_t u; | |
530 | ||
531 | r = safe_atou64(timestamp, &u); | |
532 | if (r >= 0) { | |
533 | char absolute[FORMAT_TIMESTAMP_MAX], relative[FORMAT_TIMESPAN_MAX]; | |
534 | ||
535 | fprintf(file, | |
536 | " Timestamp: %s (%s)\n", | |
537 | format_timestamp(absolute, sizeof(absolute), u), | |
538 | format_timestamp_relative(relative, sizeof(relative), u)); | |
539 | ||
540 | } else | |
541 | fprintf(file, " Timestamp: %s\n", timestamp); | |
542 | } | |
543 | ||
e15758cc LP |
544 | if (cmdline) |
545 | fprintf(file, " Command Line: %s\n", cmdline); | |
81cef14f | 546 | if (exe) |
1fc464f6 | 547 | fprintf(file, " Executable: %s%s%s\n", ansi_highlight(), exe, ansi_normal()); |
a035f819 LP |
548 | if (cgroup) |
549 | fprintf(file, " Control Group: %s\n", cgroup); | |
e15758cc LP |
550 | if (unit) |
551 | fprintf(file, " Unit: %s\n", unit); | |
552 | if (user_unit) | |
554ed50f | 553 | fprintf(file, " User Unit: %s\n", user_unit); |
a035f819 LP |
554 | if (slice) |
555 | fprintf(file, " Slice: %s\n", slice); | |
e15758cc LP |
556 | if (session) |
557 | fprintf(file, " Session: %s\n", session); | |
a035f819 LP |
558 | if (owner_uid) { |
559 | uid_t n; | |
560 | ||
561 | if (parse_uid(owner_uid, &n) >= 0) { | |
562 | _cleanup_free_ char *u = NULL; | |
563 | ||
564 | u = uid_to_name(n); | |
565 | fprintf(file, | |
566 | " Owner UID: %s (%s)\n", | |
567 | owner_uid, u); | |
568 | } else { | |
569 | fprintf(file, | |
570 | " Owner UID: %s\n", | |
571 | owner_uid); | |
572 | } | |
573 | } | |
e15758cc LP |
574 | if (boot_id) |
575 | fprintf(file, " Boot ID: %s\n", boot_id); | |
576 | if (machine_id) | |
577 | fprintf(file, " Machine ID: %s\n", machine_id); | |
578 | if (hostname) | |
579 | fprintf(file, " Hostname: %s\n", hostname); | |
580 | ||
cc4419ed | 581 | if (filename) { |
32a1575f LP |
582 | bool inacc, trunc; |
583 | ||
584 | inacc = access(filename, R_OK) < 0; | |
585 | trunc = truncated && parse_boolean(truncated) > 0; | |
cc4419ed ZJS |
586 | |
587 | if (inacc || trunc) | |
588 | fprintf(file, " Storage: %s%s (%s%s%s)%s\n", | |
589 | ansi_highlight_red(), | |
590 | filename, | |
591 | inacc ? "inaccessible" : "", | |
592 | inacc && trunc ? ", " : "", | |
593 | trunc ? "truncated" : "", | |
594 | ansi_normal()); | |
595 | else | |
596 | fprintf(file, " Storage: %s\n", filename); | |
597 | } | |
598 | ||
47f50642 ZJS |
599 | else if (coredump) |
600 | fprintf(file, " Storage: journal\n"); | |
601 | else | |
602 | fprintf(file, " Storage: none\n"); | |
e15758cc | 603 | |
8d4e028f LP |
604 | if (message) { |
605 | _cleanup_free_ char *m = NULL; | |
606 | ||
607 | m = strreplace(message, "\n", "\n "); | |
608 | ||
609 | fprintf(file, " Message: %s\n", strstrip(m ?: message)); | |
610 | } | |
611 | ||
e15758cc LP |
612 | return 0; |
613 | } | |
614 | ||
ada45c78 | 615 | static int focus(sd_journal *j) { |
5de0409e ZJS |
616 | int r; |
617 | ||
5de0409e ZJS |
618 | r = sd_journal_seek_tail(j); |
619 | if (r == 0) | |
620 | r = sd_journal_previous(j); | |
23bbb0de MS |
621 | if (r < 0) |
622 | return log_error_errno(r, "Failed to search journal: %m"); | |
8bc8ab83 | 623 | if (r == 0) { |
0c51aada | 624 | log_error("No match found."); |
8bc8ab83 LP |
625 | return -ESRCH; |
626 | } | |
ada45c78 LP |
627 | return r; |
628 | } | |
5de0409e | 629 | |
5ce97d33 | 630 | static int print_entry(sd_journal *j, unsigned n_found, bool verb_is_info) { |
0c51aada LP |
631 | assert(j); |
632 | ||
5ce97d33 | 633 | if (verb_is_info) |
062b99e8 | 634 | return print_info(stdout, j, n_found); |
0c51aada | 635 | else if (arg_field) |
062b99e8 | 636 | return print_field(stdout, j); |
0c51aada | 637 | else |
062b99e8 | 638 | return print_list(stdout, j, n_found); |
0c51aada LP |
639 | } |
640 | ||
5ce97d33 YW |
641 | static int dump_list(int argc, char **argv, void *userdata) { |
642 | _cleanup_(sd_journal_closep) sd_journal *j = NULL; | |
0c51aada | 643 | unsigned n_found = 0; |
5ce97d33 | 644 | bool verb_is_info; |
0c51aada LP |
645 | int r; |
646 | ||
5ce97d33 YW |
647 | verb_is_info = (argc >= 1 && streq(argv[0], "info")); |
648 | ||
649 | r = acquire_journal(&j, argv + 1); | |
650 | if (r < 0) | |
651 | return r; | |
652 | ||
ee5324aa | 653 | (void) pager_open(arg_no_pager, false); |
0c51aada LP |
654 | |
655 | /* The coredumps are likely to compressed, and for just | |
656 | * listing them we don't need to decompress them, so let's | |
657 | * pick a fairly low data threshold here */ | |
658 | sd_journal_set_data_threshold(j, 4096); | |
659 | ||
660 | if (arg_one) { | |
661 | r = focus(j); | |
662 | if (r < 0) | |
663 | return r; | |
664 | ||
5ce97d33 | 665 | return print_entry(j, 0, verb_is_info); |
0c51aada | 666 | } else { |
32485d09 GS |
667 | if (arg_since != USEC_INFINITY && !arg_reverse) |
668 | r = sd_journal_seek_realtime_usec(j, arg_since); | |
669 | else if (arg_until != USEC_INFINITY && arg_reverse) | |
670 | r = sd_journal_seek_realtime_usec(j, arg_until); | |
671 | else if (arg_reverse) | |
672 | r = sd_journal_seek_tail(j); | |
673 | else | |
674 | r = sd_journal_seek_head(j); | |
675 | if (r < 0) | |
676 | return log_error_errno(r, "Failed to seek to date: %m"); | |
677 | ||
678 | for (;;) { | |
679 | if (!arg_reverse) | |
680 | r = sd_journal_next(j); | |
681 | else | |
682 | r = sd_journal_previous(j); | |
683 | ||
684 | if (r < 0) | |
685 | return log_error_errno(r, "Failed to iterate through journal: %m"); | |
686 | ||
687 | if (r == 0) | |
688 | break; | |
689 | ||
690 | if (arg_until != USEC_INFINITY && !arg_reverse) { | |
691 | usec_t usec; | |
692 | ||
693 | r = sd_journal_get_realtime_usec(j, &usec); | |
df65f77b | 694 | if (r < 0) |
32485d09 GS |
695 | return log_error_errno(r, "Failed to determine timestamp: %m"); |
696 | if (usec > arg_until) | |
697 | continue; | |
df65f77b | 698 | } |
32485d09 GS |
699 | |
700 | if (arg_since != USEC_INFINITY && arg_reverse) { | |
701 | usec_t usec; | |
702 | ||
703 | r = sd_journal_get_realtime_usec(j, &usec); | |
df65f77b | 704 | if (r < 0) |
32485d09 GS |
705 | return log_error_errno(r, "Failed to determine timestamp: %m"); |
706 | if (usec < arg_since) | |
707 | continue; | |
df65f77b | 708 | } |
32485d09 | 709 | |
5ce97d33 | 710 | r = print_entry(j, n_found++, verb_is_info); |
32485d09 GS |
711 | if (r < 0) |
712 | return r; | |
062b99e8 | 713 | } |
0c51aada LP |
714 | |
715 | if (!arg_field && n_found <= 0) { | |
b9aaa7f4 ZJS |
716 | if (!arg_quiet) |
717 | log_notice("No coredumps found."); | |
0c51aada LP |
718 | return -ESRCH; |
719 | } | |
720 | } | |
721 | ||
722 | return 0; | |
723 | } | |
724 | ||
bb7c5bad | 725 | static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) { |
9fe13294 ZJS |
726 | const char *data; |
727 | _cleanup_free_ char *filename = NULL; | |
728 | size_t len; | |
bb7c5bad | 729 | int r, fd; |
fc6cec86 ZJS |
730 | _cleanup_close_ int fdt = -1; |
731 | char *temp = NULL; | |
ada45c78 | 732 | |
bb7c5bad ZJS |
733 | assert(!(file && path)); /* At most one can be specified */ |
734 | assert(!!path == !!unlink_temp); /* Those must be specified together */ | |
47f50642 | 735 | |
fc6cec86 | 736 | /* Look for a coredump on disk first. */ |
9fe13294 | 737 | r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len); |
bb7c5bad | 738 | if (r == 0) |
9fe13294 | 739 | retrieve(data, len, "COREDUMP_FILENAME", &filename); |
bb7c5bad ZJS |
740 | else { |
741 | if (r != -ENOENT) | |
742 | return log_error_errno(r, "Failed to retrieve COREDUMP_FILENAME field: %m"); | |
743 | /* Check that we can have a COREDUMP field. We still haven't set a high | |
744 | * data threshold, so we'll get a few kilobytes at most. | |
745 | */ | |
746 | ||
747 | r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len); | |
748 | if (r == -ENOENT) | |
749 | return log_error_errno(r, "Coredump entry has no core attached (neither internally in the journal nor externally on disk)."); | |
750 | if (r < 0) | |
751 | return log_error_errno(r, "Failed to retrieve COREDUMP field: %m"); | |
752 | } | |
93b73b06 | 753 | |
954d3a51 ZJS |
754 | if (filename) { |
755 | if (access(filename, R_OK) < 0) | |
756 | return log_error_errno(errno, "File \"%s\" is not readable: %m", filename); | |
5de0409e | 757 | |
954d3a51 | 758 | if (path && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) { |
ae2a15bc | 759 | *path = TAKE_PTR(filename); |
a276ae74 | 760 | |
954d3a51 ZJS |
761 | return 0; |
762 | } | |
fc6cec86 | 763 | } |
a276ae74 | 764 | |
bb7c5bad | 765 | if (path) { |
fc6cec86 | 766 | const char *vt; |
992e8f22 | 767 | |
fc6cec86 | 768 | /* Create a temporary file to write the uncompressed core to. */ |
992e8f22 | 769 | |
fc6cec86 ZJS |
770 | r = var_tmp_dir(&vt); |
771 | if (r < 0) | |
772 | return log_error_errno(r, "Failed to acquire temporary directory path: %m"); | |
9fe13294 | 773 | |
605405c6 | 774 | temp = strjoin(vt, "/coredump-XXXXXX"); |
fc6cec86 ZJS |
775 | if (!temp) |
776 | return log_oom(); | |
9fe13294 | 777 | |
fc6cec86 ZJS |
778 | fdt = mkostemp_safe(temp); |
779 | if (fdt < 0) | |
780 | return log_error_errno(fdt, "Failed to create temporary file: %m"); | |
781 | log_debug("Created temporary file %s", temp); | |
9fe13294 | 782 | |
fc6cec86 | 783 | fd = fdt; |
bb7c5bad ZJS |
784 | } else { |
785 | /* If neither path or file are specified, we will write to stdout. Let's now check | |
786 | * if stdout is connected to a tty. We checked that the file exists, or that the | |
787 | * core might be stored in the journal. In this second case, if we found the entry, | |
788 | * in all likelyhood we will be able to access the COREDUMP= field. In either case, | |
789 | * we stop before doing any "real" work, i.e. before starting decompression or | |
790 | * reading from the file or creating temporary files. | |
791 | */ | |
792 | if (!file) { | |
793 | if (on_tty()) | |
794 | return log_error_errno(ENOTTY, "Refusing to dump core to tty" | |
795 | " (use shell redirection or specify --output)."); | |
796 | file = stdout; | |
797 | } | |
798 | ||
799 | fd = fileno(file); | |
fc6cec86 ZJS |
800 | } |
801 | ||
802 | if (filename) { | |
349cc4a5 | 803 | #if HAVE_XZ || HAVE_LZ4 |
fc6cec86 | 804 | _cleanup_close_ int fdf; |
9fe13294 | 805 | |
fc6cec86 ZJS |
806 | fdf = open(filename, O_RDONLY | O_CLOEXEC); |
807 | if (fdf < 0) { | |
808 | r = log_error_errno(errno, "Failed to open %s: %m", filename); | |
2fb8159f | 809 | goto error; |
fc6cec86 ZJS |
810 | } |
811 | ||
812 | r = decompress_stream(filename, fdf, fd, -1); | |
813 | if (r < 0) { | |
814 | log_error_errno(r, "Failed to decompress %s: %m", filename); | |
9fe13294 ZJS |
815 | goto error; |
816 | } | |
fc6cec86 ZJS |
817 | #else |
818 | log_error("Cannot decompress file. Compiled without compression support."); | |
819 | r = -EOPNOTSUPP; | |
820 | goto error; | |
821 | #endif | |
822 | } else { | |
823 | ssize_t sz; | |
a276ae74 | 824 | |
bb7c5bad ZJS |
825 | /* We want full data, nothing truncated. */ |
826 | sd_journal_set_data_threshold(j, 0); | |
827 | ||
fc6cec86 ZJS |
828 | r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len); |
829 | if (r < 0) | |
bb7c5bad | 830 | return log_error_errno(r, "Failed to retrieve COREDUMP field: %m"); |
fc6cec86 ZJS |
831 | |
832 | assert(len >= 9); | |
833 | data += 9; | |
834 | len -= 9; | |
835 | ||
954d3a51 | 836 | sz = write(fd, data, len); |
fc6cec86 | 837 | if (sz < 0) { |
954d3a51 | 838 | r = log_error_errno(errno, "Failed to write output: %m"); |
fc6cec86 | 839 | goto error; |
a276ae74 | 840 | } |
fc6cec86 | 841 | if (sz != (ssize_t) len) { |
954d3a51 | 842 | log_error("Short write to output."); |
fc6cec86 ZJS |
843 | r = -EIO; |
844 | goto error; | |
845 | } | |
846 | } | |
a276ae74 | 847 | |
fc6cec86 ZJS |
848 | if (temp) { |
849 | *path = temp; | |
850 | *unlink_temp = true; | |
851 | } | |
852 | return 0; | |
9fe13294 ZJS |
853 | |
854 | error: | |
fc6cec86 ZJS |
855 | if (temp) { |
856 | unlink(temp); | |
857 | log_debug("Removed temporary file %s", temp); | |
9fe13294 | 858 | } |
fc6cec86 | 859 | return r; |
9fe13294 | 860 | } |
a276ae74 | 861 | |
5ce97d33 YW |
862 | static int dump_core(int argc, char **argv, void *userdata) { |
863 | _cleanup_(sd_journal_closep) sd_journal *j = NULL; | |
9fe13294 ZJS |
864 | int r; |
865 | ||
5ce97d33 YW |
866 | if (arg_field) { |
867 | log_error("Option --field/-F only makes sense with list"); | |
868 | return -EINVAL; | |
869 | } | |
870 | ||
871 | r = acquire_journal(&j, argv + 1); | |
872 | if (r < 0) | |
873 | return r; | |
9fe13294 ZJS |
874 | |
875 | r = focus(j); | |
876 | if (r < 0) | |
ada45c78 | 877 | return r; |
ada45c78 | 878 | |
3774cf57 | 879 | print_info(arg_output ? stdout : stderr, j, false); |
9fe13294 | 880 | |
bb7c5bad | 881 | r = save_core(j, arg_output, NULL, NULL); |
23bbb0de | 882 | if (r < 0) |
bb7c5bad | 883 | return r; |
5de0409e ZJS |
884 | |
885 | r = sd_journal_previous(j); | |
b9aaa7f4 ZJS |
886 | if (r > 0 && !arg_quiet) |
887 | log_notice("More than one entry matches, ignoring rest."); | |
5de0409e ZJS |
888 | |
889 | return 0; | |
890 | } | |
891 | ||
c5896b6a | 892 | static int run_debug(int argc, char **argv, void *userdata) { |
5ce97d33 | 893 | _cleanup_(sd_journal_closep) sd_journal *j = NULL; |
de8f6e54 | 894 | _cleanup_free_ char *exe = NULL, *path = NULL; |
9fe13294 | 895 | bool unlink_path = false; |
c5896b6a | 896 | const char *data, *fork_name; |
ada45c78 | 897 | size_t len; |
ada45c78 | 898 | pid_t pid; |
ada45c78 | 899 | int r; |
ada45c78 | 900 | |
c5896b6a RG |
901 | if (!arg_debugger) { |
902 | char *env_debugger; | |
903 | ||
904 | env_debugger = getenv("SYSTEMD_DEBUGGER"); | |
905 | if (env_debugger) | |
906 | arg_debugger = env_debugger; | |
907 | else | |
908 | arg_debugger = "gdb"; | |
909 | } | |
910 | ||
5ce97d33 YW |
911 | if (arg_field) { |
912 | log_error("Option --field/-F only makes sense with list"); | |
913 | return -EINVAL; | |
914 | } | |
915 | ||
916 | r = acquire_journal(&j, argv + 1); | |
917 | if (r < 0) | |
918 | return r; | |
ada45c78 LP |
919 | |
920 | r = focus(j); | |
921 | if (r < 0) | |
922 | return r; | |
923 | ||
e15758cc LP |
924 | print_info(stdout, j, false); |
925 | fputs("\n", stdout); | |
ada45c78 LP |
926 | |
927 | r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len); | |
23bbb0de MS |
928 | if (r < 0) |
929 | return log_error_errno(r, "Failed to retrieve COREDUMP_EXE field: %m"); | |
ada45c78 | 930 | |
fbd0b64f LP |
931 | assert(len > STRLEN("COREDUMP_EXE=")); |
932 | data += STRLEN("COREDUMP_EXE="); | |
933 | len -= STRLEN("COREDUMP_EXE="); | |
ada45c78 LP |
934 | |
935 | exe = strndup(data, len); | |
936 | if (!exe) | |
937 | return log_oom(); | |
938 | ||
939 | if (endswith(exe, " (deleted)")) { | |
940 | log_error("Binary already deleted."); | |
941 | return -ENOENT; | |
942 | } | |
943 | ||
a276ae74 LP |
944 | if (!path_is_absolute(exe)) { |
945 | log_error("Binary is not an absolute path."); | |
946 | return -ENOENT; | |
947 | } | |
948 | ||
bb7c5bad | 949 | r = save_core(j, NULL, &path, &unlink_path); |
23bbb0de | 950 | if (r < 0) |
bb7c5bad | 951 | return r; |
ada45c78 | 952 | |
3e7bc89b FB |
953 | /* Don't interfere with gdb and its handling of SIGINT. */ |
954 | (void) ignore_signals(SIGINT, -1); | |
955 | ||
c5896b6a RG |
956 | fork_name = strjoina("(", arg_debugger, ")"); |
957 | ||
958 | r = safe_fork(fork_name, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid); | |
b6e1fff1 | 959 | if (r < 0) |
ada45c78 | 960 | goto finish; |
4c253ed1 | 961 | if (r == 0) { |
c5896b6a | 962 | execlp(arg_debugger, arg_debugger, exe, "-c", path, NULL); |
0b1f3c76 | 963 | log_open(); |
c5896b6a | 964 | log_error_errno(errno, "Failed to invoke %s: %m", arg_debugger); |
a45d7127 | 965 | _exit(EXIT_FAILURE); |
ada45c78 LP |
966 | } |
967 | ||
c5896b6a | 968 | r = wait_for_terminate_and_check(arg_debugger, pid, WAIT_LOG_ABNORMAL); |
ada45c78 LP |
969 | |
970 | finish: | |
3e7bc89b FB |
971 | (void) default_signals(SIGINT, -1); |
972 | ||
9fe13294 ZJS |
973 | if (unlink_path) { |
974 | log_debug("Removed temporary file %s", path); | |
975 | unlink(path); | |
976 | } | |
a276ae74 | 977 | |
ada45c78 LP |
978 | return r; |
979 | } | |
980 | ||
012f2b7d ZJS |
981 | static int check_units_active(void) { |
982 | _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; | |
983 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; | |
984 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
985 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
986 | int c = 0, r; | |
7bbf2d84 | 987 | const char *id, *state, *substate; |
012f2b7d | 988 | |
b9aaa7f4 ZJS |
989 | if (arg_quiet) |
990 | return false; | |
991 | ||
012f2b7d ZJS |
992 | r = sd_bus_default_system(&bus); |
993 | if (r < 0) | |
994 | return log_error_errno(r, "Failed to acquire bus: %m"); | |
995 | ||
996 | r = sd_bus_message_new_method_call( | |
997 | bus, | |
998 | &m, | |
999 | "org.freedesktop.systemd1", | |
1000 | "/org/freedesktop/systemd1", | |
1001 | "org.freedesktop.systemd1.Manager", | |
1002 | "ListUnitsByPatterns"); | |
1003 | if (r < 0) | |
1004 | return bus_log_create_error(r); | |
1005 | ||
1006 | r = sd_bus_message_append_strv(m, NULL); | |
1007 | if (r < 0) | |
1008 | return bus_log_create_error(r); | |
1009 | ||
1010 | r = sd_bus_message_append_strv(m, STRV_MAKE("systemd-coredump@*.service")); | |
1011 | if (r < 0) | |
1012 | return bus_log_create_error(r); | |
1013 | ||
501551e8 | 1014 | r = sd_bus_call(bus, m, SHORT_BUS_CALL_TIMEOUT_USEC, &error, &reply); |
012f2b7d ZJS |
1015 | if (r < 0) |
1016 | return log_error_errno(r, "Failed to check if any systemd-coredump@.service units are running: %s", | |
1017 | bus_error_message(&error, r)); | |
1018 | ||
1019 | r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); | |
1020 | if (r < 0) | |
1021 | return bus_log_parse_error(r); | |
1022 | ||
1023 | while ((r = sd_bus_message_read( | |
1024 | reply, "(ssssssouso)", | |
7bbf2d84 ZJS |
1025 | &id, NULL, NULL, &state, &substate, |
1026 | NULL, NULL, NULL, NULL, NULL)) > 0) { | |
eb5877a0 | 1027 | bool found = !STR_IN_SET(state, "inactive", "dead", "failed"); |
7bbf2d84 ZJS |
1028 | log_debug("Unit %s is %s/%s, %scounting it.", id, state, substate, found ? "" : "not "); |
1029 | c += found; | |
1030 | } | |
012f2b7d ZJS |
1031 | if (r < 0) |
1032 | return bus_log_parse_error(r); | |
1033 | ||
1034 | r = sd_bus_message_exit_container(reply); | |
1035 | if (r < 0) | |
1036 | return bus_log_parse_error(r); | |
1037 | ||
1038 | return c; | |
1039 | } | |
1040 | ||
5ce97d33 YW |
1041 | static int coredumpctl_main(int argc, char *argv[]) { |
1042 | ||
1043 | static const Verb verbs[] = { | |
c5896b6a RG |
1044 | { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, dump_list }, |
1045 | { "info", VERB_ANY, VERB_ANY, 0, dump_list }, | |
1046 | { "dump", VERB_ANY, VERB_ANY, 0, dump_core }, | |
1047 | { "debug", VERB_ANY, VERB_ANY, 0, run_debug }, | |
1048 | { "gdb", VERB_ANY, VERB_ANY, 0, run_debug }, | |
5ce97d33 YW |
1049 | {} |
1050 | }; | |
1051 | ||
1052 | return dispatch_verb(argc, argv, verbs, NULL); | |
1053 | } | |
1054 | ||
5de0409e | 1055 | int main(int argc, char *argv[]) { |
5ce97d33 | 1056 | int r, units_active; |
5de0409e | 1057 | |
a9cdc94f | 1058 | setlocale(LC_ALL, ""); |
5de0409e ZJS |
1059 | log_parse_environment(); |
1060 | log_open(); | |
1061 | ||
5ab9ed07 | 1062 | r = parse_argv(argc, argv); |
5ce97d33 | 1063 | if (r <= 0) |
5de0409e ZJS |
1064 | goto end; |
1065 | ||
2cf4172a LP |
1066 | sigbus_install(); |
1067 | ||
012f2b7d ZJS |
1068 | units_active = check_units_active(); /* error is treated the same as 0 */ |
1069 | ||
5ce97d33 | 1070 | r = coredumpctl_main(argc, argv); |
5de0409e | 1071 | |
012f2b7d ZJS |
1072 | if (units_active > 0) |
1073 | printf("%s-- Notice: %d systemd-coredump@.service %s, output may be incomplete.%s\n", | |
1074 | ansi_highlight_red(), | |
1075 | units_active, units_active == 1 ? "unit is running" : "units are running", | |
1076 | ansi_normal()); | |
5de0409e | 1077 | end: |
5de0409e ZJS |
1078 | pager_close(); |
1079 | ||
f5c4b520 | 1080 | safe_fclose(arg_output); |
5de0409e | 1081 | |
ada45c78 | 1082 | return r >= 0 ? r : EXIT_FAILURE; |
5de0409e | 1083 | } |