]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/escape/escape.c
strv: make iterator in STRV_FOREACH() declaread in the loop
[thirdparty/systemd.git] / src / escape / escape.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
b1a5a998 2
3f6fd1ba 3#include <getopt.h>
b1a5a998
MB
4#include <stdio.h>
5#include <stdlib.h>
6
b5efdb8a 7#include "alloc-util.h"
b1a5a998 8#include "log.h"
5e332028 9#include "main-func.h"
99db797b 10#include "path-util.h"
294bf0c3 11#include "pretty-print.h"
07630cea 12#include "string-util.h"
a1948c7b 13#include "strv.h"
3f6fd1ba 14#include "unit-name.h"
b1a5a998 15
a1948c7b
LP
16static enum {
17 ACTION_ESCAPE,
18 ACTION_UNESCAPE,
19 ACTION_MANGLE
20} arg_action = ACTION_ESCAPE;
21static const char *arg_suffix = NULL;
22static const char *arg_template = NULL;
23static bool arg_path = false;
d936cddc 24static bool arg_instance = false;
a1948c7b 25
37ec0fdd
LP
26static int help(void) {
27 _cleanup_free_ char *link = NULL;
28 int r;
29
30 r = terminal_urlify_man("systemd-escape", "1", &link);
31 if (r < 0)
32 return log_oom();
33
a1948c7b 34 printf("%s [OPTIONS...] [NAME...]\n\n"
ab7e3ef5 35 "Escape strings for usage in systemd unit names.\n\n"
a1948c7b
LP
36 " -h --help Show this help\n"
37 " --version Show package version\n"
38 " --suffix=SUFFIX Unit suffix to append to escaped strings\n"
39 " --template=TEMPLATE Insert strings as instance into template\n"
d936cddc 40 " --instance With --unescape, show just the instance part\n"
a1948c7b
LP
41 " -u --unescape Unescape strings\n"
42 " -m --mangle Mangle strings\n"
601185b4 43 " -p --path When escaping/unescaping assume the string is a path\n"
bc556335
DDM
44 "\nSee the %s for details.\n",
45 program_invocation_short_name,
46 link);
37ec0fdd
LP
47
48 return 0;
a1948c7b
LP
49}
50
51static int parse_argv(int argc, char *argv[]) {
52
53 enum {
54 ARG_VERSION = 0x100,
55 ARG_SUFFIX,
56 ARG_TEMPLATE
57 };
58
59 static const struct option options[] = {
60 { "help", no_argument, NULL, 'h' },
61 { "version", no_argument, NULL, ARG_VERSION },
62 { "suffix", required_argument, NULL, ARG_SUFFIX },
63 { "template", required_argument, NULL, ARG_TEMPLATE },
64 { "unescape", no_argument, NULL, 'u' },
65 { "mangle", no_argument, NULL, 'm' },
66 { "path", no_argument, NULL, 'p' },
d936cddc 67 { "instance", no_argument, NULL, 'i' },
a1948c7b
LP
68 {}
69 };
70
71 int c;
72
73 assert(argc >= 0);
74 assert(argv);
75
601185b4 76 while ((c = getopt_long(argc, argv, "hump", options, NULL)) >= 0)
a1948c7b
LP
77
78 switch (c) {
79
80 case 'h':
37ec0fdd 81 return help();
a1948c7b
LP
82
83 case ARG_VERSION:
3f6fd1ba 84 return version();
a1948c7b 85
7211c853
ZJS
86 case ARG_SUFFIX: {
87 UnitType t = unit_type_from_string(optarg);
88 if (t < 0)
89 return log_error_errno(t, "Invalid unit suffix type \"%s\".", optarg);
a1948c7b
LP
90
91 arg_suffix = optarg;
92 break;
7211c853 93 }
a1948c7b
LP
94
95 case ARG_TEMPLATE:
baaa35ad
ZJS
96 if (!unit_name_is_valid(optarg, UNIT_NAME_TEMPLATE))
97 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
98 "Template name %s is not valid.", optarg);
a1948c7b
LP
99
100 arg_template = optarg;
101 break;
102
103 case 'u':
104 arg_action = ACTION_UNESCAPE;
105 break;
106
107 case 'm':
108 arg_action = ACTION_MANGLE;
109 break;
110
111 case 'p':
112 arg_path = true;
113 break;
114
d936cddc
LW
115 case 'i':
116 arg_instance = true;
117 break;
118
a1948c7b
LP
119 case '?':
120 return -EINVAL;
121
122 default:
04499a70 123 assert_not_reached();
a1948c7b 124 }
b1a5a998 125
baaa35ad
ZJS
126 if (optind >= argc)
127 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
128 "Not enough arguments.");
129
130 if (arg_template && arg_suffix)
131 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
132 "--suffix= and --template= may not be combined.");
133
134 if ((arg_template || arg_suffix) && arg_action == ACTION_MANGLE)
135 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
136 "--suffix= and --template= are not compatible with --mangle.");
137
138 if (arg_suffix && arg_action == ACTION_UNESCAPE)
139 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
140 "--suffix is not compatible with --unescape.");
141
142 if (arg_path && !IN_SET(arg_action, ACTION_ESCAPE, ACTION_UNESCAPE))
143 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
144 "--path may not be combined with --mangle.");
145
146 if (arg_instance && arg_action != ACTION_UNESCAPE)
147 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
148 "--instance must be used in conjunction with --unescape.");
149
150 if (arg_instance && arg_template)
151 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
152 "--instance may not be combined with --template.");
d936cddc 153
a1948c7b
LP
154 return 1;
155}
156
5f200747 157static int run(int argc, char *argv[]) {
a1948c7b
LP
158 int r;
159
d2acb93d 160 log_setup();
a1948c7b
LP
161
162 r = parse_argv(argc, argv);
163 if (r <= 0)
5f200747 164 return r;
a1948c7b
LP
165
166 STRV_FOREACH(i, argv + optind) {
167 _cleanup_free_ char *e = NULL;
168
169 switch (arg_action) {
170
171 case ACTION_ESCAPE:
7410616c
LP
172 if (arg_path) {
173 r = unit_name_path_escape(*i, &e);
99db797b
LP
174 if (r < 0) {
175 if (r == -EINVAL) {
176 /* If escaping failed because the string was invalid, let's print a
177 * friendly message about it. Catch these specific error cases
178 * explicitly. */
179
180 if (!path_is_valid(*i))
181 return log_error_errno(r, "Input '%s' is not a valid file system path, failed to escape.", *i);
182 if (!path_is_absolute(*i))
183 return log_error_errno(r, "Input '%s' is not an absolute file system path, failed to escape.", *i);
184 if (!path_is_normalized(*i))
185 return log_error_errno(r, "Input '%s' is not a normalized file system path, failed to escape.", *i);
186 }
187
188 /* All other error cases. */
5f200747 189 return log_error_errno(r, "Failed to escape string: %m");
99db797b
LP
190 }
191
192 /* If the escaping worked, then still warn if the path is not like we'd like
193 * it. Because that means escaping is not necessarily reversible. */
194
195 if (!path_is_valid(*i))
196 log_warning("Input '%s' is not a valid file system path, escaping is likely not going be reversible.", *i);
197 else if (!path_is_absolute(*i))
198 log_warning("Input '%s' is not an absolute file system path, escaping is likely not going to be reversible.", *i);
199
200 /* Note that we don't complain about paths not being normalized here, because
201 * some forms of non-normalization is actually OK, such as a series // and
202 * unit_name_path_escape() will clean those up silently, and the reversal is
203 * "close enough" to be OK. */
7410616c 204 } else {
a1948c7b 205 e = unit_name_escape(*i);
5f200747
ZJS
206 if (!e)
207 return log_oom();
a1948c7b
LP
208 }
209
210 if (arg_template) {
211 char *x;
212
7410616c 213 r = unit_name_replace_instance(arg_template, e, &x);
5f200747
ZJS
214 if (r < 0)
215 return log_error_errno(r, "Failed to replace instance: %m");
a1948c7b 216
c88f45e7 217 free_and_replace(e, x);
a1948c7b 218 } else if (arg_suffix) {
f8abe13f 219 if (!strextend(&e, ".", arg_suffix))
5f200747 220 return log_oom();
a1948c7b
LP
221 }
222
223 break;
224
e563e253
LW
225 case ACTION_UNESCAPE: {
226 _cleanup_free_ char *name = NULL;
227
d936cddc 228 if (arg_template || arg_instance) {
e563e253
LW
229 _cleanup_free_ char *template = NULL;
230
231 r = unit_name_to_instance(*i, &name);
5f200747
ZJS
232 if (r < 0)
233 return log_error_errno(r, "Failed to extract instance: %m");
234 if (isempty(name))
c413bb28
ZJS
235 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
236 "Unit %s is missing the instance name.", *i);
5f200747 237
e563e253 238 r = unit_name_template(*i, &template);
5f200747
ZJS
239 if (r < 0)
240 return log_error_errno(r, "Failed to extract template: %m");
241 if (arg_template && !streq(arg_template, template))
c413bb28
ZJS
242 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
243 "Unit %s template %s does not match specified template %s.",
244 *i, template, arg_template);
e563e253
LW
245 } else {
246 name = strdup(*i);
5f200747
ZJS
247 if (!name)
248 return log_oom();
e563e253
LW
249 }
250
a1948c7b 251 if (arg_path)
e563e253 252 r = unit_name_path_unescape(name, &e);
a1948c7b 253 else
e563e253 254 r = unit_name_unescape(name, &e);
5f200747
ZJS
255 if (r < 0)
256 return log_error_errno(r, "Failed to unescape string: %m");
a1948c7b 257
a1948c7b 258 break;
e563e253 259 }
a1948c7b
LP
260
261 case ACTION_MANGLE:
37cbc1d5 262 r = unit_name_mangle(*i, 0, &e);
5f200747
ZJS
263 if (r < 0)
264 return log_error_errno(r, "Failed to mangle name: %m");
265
a1948c7b
LP
266 break;
267 }
268
c88f45e7 269 if (i != argv + optind)
a1948c7b 270 fputc(' ', stdout);
b1a5a998 271
a1948c7b 272 fputs(e, stdout);
b1a5a998
MB
273 }
274
a1948c7b 275 fputc('\n', stdout);
b1a5a998 276
5f200747 277 return 0;
b1a5a998 278}
5f200747
ZJS
279
280DEFINE_MAIN_FUNCTION(run);