]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/coredumpctl.c
coredumpctl: initialize global vars
[thirdparty/systemd.git] / src / journal / coredumpctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2012 Zbigniew Jędrzejewski-Szmek
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <getopt.h>
25
26 #include <systemd/sd-journal.h>
27
28 #include "build.h"
29 #include "set.h"
30 #include "util.h"
31 #include "log.h"
32 #include "path-util.h"
33 #include "pager.h"
34
35 static enum {
36 ACTION_NONE,
37 ACTION_LIST,
38 ACTION_DUMP,
39 } arg_action = ACTION_LIST;
40
41 static Set *matches = NULL;
42 static FILE* output = NULL;
43
44 static int arg_no_pager = false;
45
46 static Set *new_matches(void) {
47 Set *set;
48 char *tmp;
49 int r;
50
51 set = set_new(trivial_hash_func, trivial_compare_func);
52 if (!set) {
53 log_oom();
54 return NULL;
55 }
56
57 tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
58 if (!tmp) {
59 log_oom();
60 set_clear_free(set);
61 return NULL;
62 }
63
64 r = set_put(set, tmp);
65 if (r < 0) {
66 log_error("failed to add to set: %s", strerror(-r));
67 free(tmp);
68 set_clear_free(set);
69 return NULL;
70 }
71
72 return set;
73 }
74
75 static int help(void) {
76 printf("%s [OPTIONS...] [MATCHES...]\n\n"
77 "List or retrieve coredumps from the journal.\n\n"
78 "Flags:\n"
79 " -o --output=FILE Write output to FILE\n"
80 " --no-pager Do not pipe output into a pager\n"
81
82 "Commands:\n"
83 " -h --help Show this help\n"
84 " --version Print version string\n"
85 " list List available coredumps\n"
86 " dump PID Print coredump to stdout\n"
87 " dump PATH Print coredump to stdout\n"
88 , program_invocation_short_name);
89
90 return 0;
91 }
92
93 static int add_match(Set *set, const char *match) {
94 int r = -ENOMEM;
95 unsigned pid;
96 const char* prefix;
97 char *pattern = NULL;
98 char _cleanup_free_ *p = NULL;
99
100 if (strchr(match, '='))
101 prefix = "";
102 else if (strchr(match, '/')) {
103 p = path_make_absolute_cwd(match);
104 if (!p)
105 goto fail;
106
107 match = p;
108 prefix = "COREDUMP_EXE=";
109 }
110 else if (safe_atou(match, &pid) == 0)
111 prefix = "COREDUMP_PID=";
112 else
113 prefix = "COREDUMP_COMM=";
114
115 pattern = strjoin(prefix, match, NULL);
116 if (!pattern)
117 goto fail;
118
119 r = set_put(set, pattern);
120 if (r < 0) {
121 log_error("failed to add pattern '%s': %s",
122 pattern, strerror(-r));
123 goto fail;
124 }
125 log_debug("Added pattern: %s", pattern);
126
127 return 0;
128 fail:
129 free(pattern);
130 log_error("failed to add match: %s", strerror(-r));
131 return r;
132 }
133
134 static int parse_argv(int argc, char *argv[]) {
135 enum {
136 ARG_VERSION = 0x100,
137 ARG_NO_PAGER,
138 };
139
140 int r, c;
141
142 static const struct option options[] = {
143 { "help", no_argument, NULL, 'h' },
144 { "version" , no_argument, NULL, ARG_VERSION },
145 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
146 { "output", required_argument, NULL, 'o' },
147 };
148
149 assert(argc >= 0);
150 assert(argv);
151
152 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
153 switch(c) {
154 case 'h':
155 help();
156 arg_action = ACTION_NONE;
157 return 0;
158
159 case ARG_VERSION:
160 puts(PACKAGE_STRING);
161 puts(DISTRIBUTION);
162 puts(SYSTEMD_FEATURES);
163 arg_action = ACTION_NONE;
164 return 0;
165
166 case ARG_NO_PAGER:
167 arg_no_pager = true;
168 break;
169
170 case 'o':
171 if (output) {
172 log_error("cannot set output more than once");
173 return -EINVAL;
174 }
175
176 output = fopen(optarg, "we");
177 if (!output) {
178 log_error("writing to '%s': %m", optarg);
179 return -errno;
180 }
181
182 break;
183 default:
184 log_error("Unknown option code %c", c);
185 return -EINVAL;
186 }
187
188 if (optind < argc) {
189 const char *cmd = argv[optind++];
190 if(streq(cmd, "list"))
191 arg_action = ACTION_LIST;
192 else if (streq(cmd, "dump"))
193 arg_action = ACTION_DUMP;
194 else {
195 log_error("Unknown action '%s'", cmd);
196 return -EINVAL;
197 }
198 }
199
200 while (optind < argc) {
201 r = add_match(matches, argv[optind]);
202 if (r != 0)
203 return r;
204 optind++;
205 }
206
207 return 0;
208 }
209
210 static int retrieve(sd_journal *j, const char *name, const char **var) {
211 const void *data;
212 size_t len, field;
213 int r;
214
215 r = sd_journal_get_data(j, name, &data, &len);
216 if (r < 0) {
217 log_warning("Failed to retrieve %s", name);
218 return r;
219 }
220
221 field = strlen(name) + 1; // name + "="
222 assert(len >= field);
223
224 *var = strndup((const char*)data + field, len - field);
225 if (!var)
226 return log_oom();
227
228 return 0;
229 }
230
231 static void print_entry(FILE* file, sd_journal *j, int had_header) {
232 const char _cleanup_free_
233 *pid = NULL, *uid = NULL, *gid = NULL,
234 *sgnl = NULL, *exe = NULL;
235
236 retrieve(j, "COREDUMP_PID", &pid);
237 retrieve(j, "COREDUMP_UID", &uid);
238 retrieve(j, "COREDUMP_GID", &gid);
239 retrieve(j, "COREDUMP_SIGNAL", &sgnl);
240 retrieve(j, "COREDUMP_EXE", &exe);
241 if (!exe)
242 retrieve(j, "COREDUMP_COMM", &exe);
243 if (!exe)
244 retrieve(j, "COREDUMP_CMDLINE", &exe);
245
246 if (!pid && !uid && !gid && !sgnl && !exe) {
247 log_warning("empty coredump log entry");
248 return;
249 }
250
251 if (!had_header)
252 fprintf(file, "%*s %*s %*s %*s %s\n",
253 6, "PID",
254 5, "UID",
255 5, "GID",
256 3, "sig",
257 "exe");
258
259 fprintf(file, "%*s %*s %*s %*s %s\n",
260 6, pid,
261 5, uid,
262 5, gid,
263 3, sgnl,
264 exe);
265 }
266
267 static int dump_list(sd_journal *j) {
268 int found = 0;
269
270 assert(j);
271
272 SD_JOURNAL_FOREACH(j)
273 print_entry(stdout, j, found++);
274
275 if (!found) {
276 log_error("no coredumps found");
277 return -ESRCH;
278 }
279
280 return 0;
281 }
282
283 static int dump_core(sd_journal* j) {
284 const char *data;
285 size_t len, ret;
286 int r;
287
288 assert(j);
289
290 r = sd_journal_seek_tail(j);
291 if (r == 0)
292 r = sd_journal_previous(j);
293 if (r < 0) {
294 log_error("Failed to search journal: %s", strerror(-r));
295 return r;
296 }
297
298 if (r == 0) {
299 log_error("No match found");
300 return -ESRCH;
301 }
302
303 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
304 if (r != 0) {
305 log_error("retrieve COREDUMP field: %s", strerror(-r));
306 return r;
307 }
308
309 print_entry(output ? stdout : stderr, j, false);
310
311 if (on_tty() && !output) {
312 log_error("Refusing to dump core to tty");
313 return -ENOTTY;
314 }
315
316 assert(len >= 9);
317
318 ret = fwrite(data+9, len-9, 1, output ? output : stdout);
319 if (ret != 1) {
320 log_error("dumping coredump: %m (%zu)", ret);
321 return -errno;
322 }
323
324 r = sd_journal_previous(j);
325 if (r >= 0)
326 log_warning("More than one entry matches, ignoring rest.\n");
327
328 return 0;
329 }
330
331 int main(int argc, char *argv[]) {
332 sd_journal *j = NULL;
333 const char* match;
334 Iterator it;
335 int r = 0;
336
337 log_parse_environment();
338 log_open();
339
340 matches = new_matches();
341 if (!matches)
342 goto end;
343
344 if (parse_argv(argc, argv))
345 goto end;
346
347 if (arg_action == ACTION_NONE)
348 goto end;
349
350 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
351 if (r < 0) {
352 log_error("Failed to open journal: %s", strerror(-r));
353 goto end;
354 }
355
356 SET_FOREACH(match, matches, it) {
357 log_info("Matching: %s", match);
358
359 r = sd_journal_add_match(j, match, strlen(match));
360 if (r != 0) {
361 log_error("Failed to add match '%s': %s",
362 match, strerror(-r));
363 goto end;
364 }
365 }
366
367 switch(arg_action) {
368 case ACTION_LIST:
369 if (!arg_no_pager)
370 pager_open();
371
372 r = dump_list(j);
373 break;
374 case ACTION_DUMP:
375 r = dump_core(j);
376 break;
377 case ACTION_NONE:
378 assert_not_reached("Shouldn't be here");
379 }
380
381 end:
382 if (j)
383 sd_journal_close(j);
384
385 set_free_free(matches);
386
387 pager_close();
388
389 if (output)
390 fclose(output);
391
392 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
393 }