1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "alloc-util.h"
10 #include "path-util.h"
11 #include "pretty-print.h"
12 #include "string-util.h"
14 #include "unit-name.h"
20 } arg_action
= ACTION_ESCAPE
;
21 static const char *arg_suffix
= NULL
;
22 static const char *arg_template
= NULL
;
23 static bool arg_path
= false;
24 static bool arg_instance
= false;
26 static int help(void) {
27 _cleanup_free_
char *link
= NULL
;
30 r
= terminal_urlify_man("systemd-escape", "1", &link
);
34 printf("%s [OPTIONS...] [NAME...]\n\n"
35 "Escape strings for usage in systemd unit names.\n\n"
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"
40 " --instance With --unescape, show just the instance part\n"
41 " -u --unescape Unescape strings\n"
42 " -m --mangle Mangle strings\n"
43 " -p --path When escaping/unescaping assume the string is a path\n"
44 "\nSee the %s for details.\n",
45 program_invocation_short_name
,
51 static int parse_argv(int argc
, char *argv
[]) {
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' },
67 { "instance", no_argument
, NULL
, 'i' },
76 while ((c
= getopt_long(argc
, argv
, "hump", options
, NULL
)) >= 0)
87 UnitType t
= unit_type_from_string(optarg
);
89 return log_error_errno(t
, "Invalid unit suffix type \"%s\".", optarg
);
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
);
100 arg_template
= optarg
;
104 arg_action
= ACTION_UNESCAPE
;
108 arg_action
= ACTION_MANGLE
;
123 assert_not_reached();
127 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
128 "Not enough arguments.");
130 if (arg_template
&& arg_suffix
)
131 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
132 "--suffix= and --template= may not be combined.");
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.");
138 if (arg_suffix
&& arg_action
== ACTION_UNESCAPE
)
139 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
140 "--suffix is not compatible with --unescape.");
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.");
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.");
150 if (arg_instance
&& arg_template
)
151 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
152 "--instance may not be combined with --template.");
157 static int run(int argc
, char *argv
[]) {
163 r
= parse_argv(argc
, argv
);
167 STRV_FOREACH(i
, argv
+ optind
) {
168 _cleanup_free_
char *e
= NULL
;
170 switch (arg_action
) {
174 r
= unit_name_path_escape(*i
, &e
);
177 /* If escaping failed because the string was invalid, let's print a
178 * friendly message about it. Catch these specific error cases
181 if (!path_is_valid(*i
))
182 return log_error_errno(r
, "Input '%s' is not a valid file system path, failed to escape.", *i
);
183 if (!path_is_absolute(*i
))
184 return log_error_errno(r
, "Input '%s' is not an absolute file system path, failed to escape.", *i
);
185 if (!path_is_normalized(*i
))
186 return log_error_errno(r
, "Input '%s' is not a normalized file system path, failed to escape.", *i
);
189 /* All other error cases. */
190 return log_error_errno(r
, "Failed to escape string: %m");
193 /* If the escaping worked, then still warn if the path is not like we'd like
194 * it. Because that means escaping is not necessarily reversible. */
196 if (!path_is_valid(*i
))
197 log_warning("Input '%s' is not a valid file system path, escaping is likely not going be reversible.", *i
);
198 else if (!path_is_absolute(*i
))
199 log_warning("Input '%s' is not an absolute file system path, escaping is likely not going to be reversible.", *i
);
201 /* Note that we don't complain about paths not being normalized here, because
202 * some forms of non-normalization is actually OK, such as a series // and
203 * unit_name_path_escape() will clean those up silently, and the reversal is
204 * "close enough" to be OK. */
206 e
= unit_name_escape(*i
);
214 r
= unit_name_replace_instance(arg_template
, e
, &x
);
216 return log_error_errno(r
, "Failed to replace instance: %m");
218 free_and_replace(e
, x
);
219 } else if (arg_suffix
) {
220 if (!strextend(&e
, ".", arg_suffix
))
226 case ACTION_UNESCAPE
: {
227 _cleanup_free_
char *name
= NULL
;
229 if (arg_template
|| arg_instance
) {
230 _cleanup_free_
char *template = NULL
;
232 r
= unit_name_to_instance(*i
, &name
);
234 return log_error_errno(r
, "Failed to extract instance: %m");
236 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
237 "Unit %s is missing the instance name.", *i
);
239 r
= unit_name_template(*i
, &template);
241 return log_error_errno(r
, "Failed to extract template: %m");
242 if (arg_template
&& !streq(arg_template
, template))
243 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
244 "Unit %s template %s does not match specified template %s.",
245 *i
, template, arg_template
);
253 r
= unit_name_path_unescape(name
, &e
);
255 r
= unit_name_unescape(name
, &e
);
257 return log_error_errno(r
, "Failed to unescape string: %m");
263 r
= unit_name_mangle(*i
, 0, &e
);
265 return log_error_errno(r
, "Failed to mangle name: %m");
270 if (i
!= argv
+ optind
)
281 DEFINE_MAIN_FUNCTION(run
);