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