chiark / gitweb /
coredumpctl: initialize global vars
[elogind.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 }