Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <errno.h>
4 : #include <fcntl.h>
5 : #include <getopt.h>
6 : #include <stdio.h>
7 : #include <stdlib.h>
8 : #include <unistd.h>
9 :
10 : #include "sd-journal.h"
11 :
12 : #include "alloc-util.h"
13 : #include "fd-util.h"
14 : #include "main-func.h"
15 : #include "parse-util.h"
16 : #include "pretty-print.h"
17 : #include "string-util.h"
18 : #include "syslog-util.h"
19 : #include "util.h"
20 :
21 : static const char *arg_identifier = NULL;
22 : static int arg_priority = LOG_INFO;
23 : static int arg_stderr_priority = -1;
24 : static bool arg_level_prefix = true;
25 :
26 3 : static int help(void) {
27 3 : _cleanup_free_ char *link = NULL;
28 : int r;
29 :
30 3 : r = terminal_urlify_man("systemd-cat", "1", &link);
31 3 : if (r < 0)
32 0 : return log_oom();
33 :
34 3 : printf("%s [OPTIONS...] {COMMAND} ...\n\n"
35 : "Execute process with stdout/stderr connected to the journal.\n\n"
36 : " -h --help Show this help\n"
37 : " --version Show package version\n"
38 : " -t --identifier=STRING Set syslog identifier\n"
39 : " -p --priority=PRIORITY Set priority value (0..7)\n"
40 : " --stderr-priority=PRIORITY Set priority value (0..7) used for stderr\n"
41 : " --level-prefix=BOOL Control whether level prefix shall be parsed\n"
42 : "\nSee the %s for details.\n"
43 : , program_invocation_short_name
44 : , link
45 : );
46 :
47 3 : return 0;
48 : }
49 :
50 4 : static int parse_argv(int argc, char *argv[]) {
51 :
52 : enum {
53 : ARG_VERSION = 0x100,
54 : ARG_STDERR_PRIORITY,
55 : ARG_LEVEL_PREFIX
56 : };
57 :
58 : static const struct option options[] = {
59 : { "help", no_argument, NULL, 'h' },
60 : { "version", no_argument, NULL, ARG_VERSION },
61 : { "identifier", required_argument, NULL, 't' },
62 : { "priority", required_argument, NULL, 'p' },
63 : { "stderr-priority", required_argument, NULL, ARG_STDERR_PRIORITY },
64 : { "level-prefix", required_argument, NULL, ARG_LEVEL_PREFIX },
65 : {}
66 : };
67 :
68 : int c;
69 :
70 4 : assert(argc >= 0);
71 4 : assert(argv);
72 :
73 4 : while ((c = getopt_long(argc, argv, "+ht:p:", options, NULL)) >= 0)
74 :
75 4 : switch (c) {
76 :
77 3 : case 'h':
78 3 : help();
79 3 : return 0;
80 :
81 0 : case ARG_VERSION:
82 0 : return version();
83 :
84 0 : case 't':
85 0 : if (isempty(optarg))
86 0 : arg_identifier = NULL;
87 : else
88 0 : arg_identifier = optarg;
89 0 : break;
90 :
91 0 : case 'p':
92 0 : arg_priority = log_level_from_string(optarg);
93 0 : if (arg_priority < 0)
94 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
95 : "Failed to parse priority value.");
96 0 : break;
97 :
98 0 : case ARG_STDERR_PRIORITY:
99 0 : arg_stderr_priority = log_level_from_string(optarg);
100 0 : if (arg_stderr_priority < 0)
101 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
102 : "Failed to parse stderr priority value.");
103 0 : break;
104 :
105 0 : case ARG_LEVEL_PREFIX: {
106 : int k;
107 :
108 0 : k = parse_boolean(optarg);
109 0 : if (k < 0)
110 0 : return log_error_errno(k, "Failed to parse level prefix value.");
111 :
112 0 : arg_level_prefix = k;
113 0 : break;
114 : }
115 :
116 1 : case '?':
117 1 : return -EINVAL;
118 :
119 0 : default:
120 0 : assert_not_reached("Unhandled option");
121 : }
122 :
123 0 : return 1;
124 : }
125 :
126 4 : static int run(int argc, char *argv[]) {
127 4 : _cleanup_close_ int outfd = -1, errfd = -1, saved_stderr = -1;
128 : int r;
129 :
130 4 : log_show_color(true);
131 4 : log_parse_environment();
132 4 : log_open();
133 :
134 4 : r = parse_argv(argc, argv);
135 4 : if (r <= 0)
136 4 : return r;
137 :
138 0 : outfd = sd_journal_stream_fd(arg_identifier, arg_priority, arg_level_prefix);
139 0 : if (outfd < 0)
140 0 : return log_error_errno(outfd, "Failed to create stream fd: %m");
141 :
142 0 : if (arg_stderr_priority >= 0 && arg_stderr_priority != arg_priority) {
143 0 : errfd = sd_journal_stream_fd(arg_identifier, arg_stderr_priority, arg_level_prefix);
144 0 : if (errfd < 0)
145 0 : return log_error_errno(errfd, "Failed to create stream fd: %m");
146 : }
147 :
148 0 : saved_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3);
149 :
150 0 : r = rearrange_stdio(STDIN_FILENO, outfd, errfd < 0 ? outfd : errfd); /* Invalidates fd on success + error! */
151 0 : TAKE_FD(outfd);
152 0 : TAKE_FD(errfd);
153 0 : if (r < 0)
154 0 : return log_error_errno(r, "Failed to rearrange stdout/stderr: %m");
155 :
156 0 : if (argc <= optind)
157 0 : (void) execl("/bin/cat", "/bin/cat", NULL);
158 : else
159 0 : (void) execvp(argv[optind], argv + optind);
160 0 : r = -errno;
161 :
162 : /* Let's try to restore a working stderr, so we can print the error message */
163 0 : if (saved_stderr >= 0)
164 0 : (void) dup3(saved_stderr, STDERR_FILENO, 0);
165 :
166 0 : return log_error_errno(r, "Failed to execute process: %m");
167 : }
168 :
169 4 : DEFINE_MAIN_FUNCTION(run);
|