chiark / gitweb /
2e236f0004cfc14d7998a0cb90cd649e9c340532
[elogind.git] / src / journal / cat.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 Lennart Poettering
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 <getopt.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <fcntl.h>
28
29 #include "systemd/sd-journal.h"
30
31 #include "util.h"
32 #include "build.h"
33
34 static char *arg_identifier = NULL;
35 static int arg_priority = LOG_INFO;
36 static bool arg_level_prefix = true;
37
38 static void help(void) {
39         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
40                "Execute process with stdout/stderr connected to the journal.\n\n"
41                "  -h --help               Show this help\n"
42                "     --version            Show package version\n"
43                "  -t --identifier=STRING  Set syslog identifier\n"
44                "  -p --priority=PRIORITY  Set priority value (0..7)\n"
45                "     --level-prefix=BOOL  Control whether level prefix shall be parsed\n"
46                , program_invocation_short_name);
47 }
48
49 static int parse_argv(int argc, char *argv[]) {
50
51         enum {
52                 ARG_VERSION = 0x100,
53                 ARG_LEVEL_PREFIX
54         };
55
56         static const struct option options[] = {
57                 { "help",         no_argument,       NULL, 'h'              },
58                 { "version",      no_argument,       NULL, ARG_VERSION      },
59                 { "identifier",   required_argument, NULL, 't'              },
60                 { "priority",     required_argument, NULL, 'p'              },
61                 { "level-prefix", required_argument, NULL, ARG_LEVEL_PREFIX },
62                 {}
63         };
64
65         int c;
66
67         assert(argc >= 0);
68         assert(argv);
69
70         while ((c = getopt_long(argc, argv, "+ht:p:", options, NULL)) >= 0)
71
72                 switch (c) {
73
74                 case 'h':
75                         help();
76                         return 0;
77
78                 case ARG_VERSION:
79                         puts(PACKAGE_STRING);
80                         puts(SYSTEMD_FEATURES);
81                         return 0;
82
83                 case 't':
84                         free(arg_identifier);
85                         if (isempty(optarg))
86                                 arg_identifier = NULL;
87                         else {
88                                 arg_identifier = strdup(optarg);
89                                 if (!arg_identifier)
90                                         return log_oom();
91                         }
92                         break;
93
94                 case 'p':
95                         arg_priority = log_level_from_string(optarg);
96                         if (arg_priority < 0) {
97                                 log_error("Failed to parse priority value.");
98                                 return arg_priority;
99                         }
100                         break;
101
102                 case ARG_LEVEL_PREFIX: {
103                         int k;
104
105                         k = parse_boolean(optarg);
106                         if (k < 0) {
107                                 log_error("Failed to parse level prefix value.");
108                                 return k;
109                         }
110                         arg_level_prefix = k;
111                         break;
112                 }
113
114                 case '?':
115                         return -EINVAL;
116
117                 default:
118                         assert_not_reached("Unhandled option");
119                 }
120
121         return 1;
122 }
123
124 int main(int argc, char *argv[]) {
125         int r, fd = -1, saved_stderr = -1;
126
127         log_parse_environment();
128         log_open();
129
130         r = parse_argv(argc, argv);
131         if (r <= 0)
132                 goto finish;
133
134         fd = sd_journal_stream_fd(arg_identifier, arg_priority, arg_level_prefix);
135         if (fd < 0) {
136                 log_error_errno(fd, "Failed to create stream fd: %m");
137                 r = fd;
138                 goto finish;
139         }
140
141         saved_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3);
142
143         if (dup3(fd, STDOUT_FILENO, 0) < 0 ||
144             dup3(fd, STDERR_FILENO, 0) < 0) {
145                 log_error_errno(errno, "Failed to duplicate fd: %m");
146                 r = -errno;
147                 goto finish;
148         }
149
150         if (fd >= 3)
151                 safe_close(fd);
152
153         fd = -1;
154
155         if (argc <= optind)
156                 execl("/bin/cat", "/bin/cat", NULL);
157         else
158                 execvp(argv[optind], argv + optind);
159
160         r = -errno;
161
162         /* Let's try to restore a working stderr, so we can print the error message */
163         if (saved_stderr >= 0)
164                 dup3(saved_stderr, STDERR_FILENO, 0);
165
166         log_error_errno(r, "Failed to execute process: %m");
167
168 finish:
169         safe_close(fd);
170         safe_close(saved_stderr);
171
172         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
173 }