]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
daf71ef6 LP |
2 | |
3 | #include "bus-error.h" | |
4 | #include "copy.h" | |
85c5d313 | 5 | #include "fd-util.h" |
6 | #include "fileio.h" | |
daf71ef6 | 7 | #include "fs-util.h" |
35cd0ba5 | 8 | #include "mkdir-label.h" |
daf71ef6 LP |
9 | #include "pager.h" |
10 | #include "path-util.h" | |
11 | #include "pretty-print.h" | |
12 | #include "process-util.h" | |
13 | #include "selinux-util.h" | |
14 | #include "stat-util.h" | |
15 | #include "systemctl-daemon-reload.h" | |
16 | #include "systemctl-edit.h" | |
17 | #include "systemctl-util.h" | |
18 | #include "systemctl.h" | |
19 | #include "terminal-util.h" | |
20 | #include "tmpfile-util.h" | |
21 | ||
85c5d313 | 22 | #define EDIT_MARKER_START "### Anything between here and the comment below will become the new contents of the file" |
23 | #define EDIT_MARKER_END "### Lines below this comment will be discarded" | |
24 | ||
32baf64d | 25 | int verb_cat(int argc, char *argv[], void *userdata) { |
b4c527f4 | 26 | _cleanup_(hashmap_freep) Hashmap *cached_name_map = NULL, *cached_id_map = NULL; |
daf71ef6 LP |
27 | _cleanup_(lookup_paths_free) LookupPaths lp = {}; |
28 | _cleanup_strv_free_ char **names = NULL; | |
daf71ef6 LP |
29 | sd_bus *bus; |
30 | bool first = true; | |
31 | int r, rc = 0; | |
32 | ||
33 | /* Include all units by default — i.e. continue as if the --all option was used */ | |
34 | if (strv_isempty(arg_states)) | |
35 | arg_all = true; | |
36 | ||
37 | if (arg_transport != BUS_TRANSPORT_LOCAL) | |
38 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot remotely cat units."); | |
39 | ||
40 | r = lookup_paths_init(&lp, arg_scope, 0, arg_root); | |
41 | if (r < 0) | |
42 | return log_error_errno(r, "Failed to determine unit paths: %m"); | |
43 | ||
44 | r = acquire_bus(BUS_MANAGER, &bus); | |
45 | if (r < 0) | |
46 | return r; | |
47 | ||
48 | r = expand_unit_names(bus, strv_skip(argv, 1), NULL, &names, NULL); | |
49 | if (r < 0) | |
50 | return log_error_errno(r, "Failed to expand names: %m"); | |
51 | ||
52 | r = maybe_extend_with_unit_dependencies(bus, &names); | |
53 | if (r < 0) | |
54 | return r; | |
55 | ||
384c2c32 | 56 | pager_open(arg_pager_flags); |
daf71ef6 LP |
57 | |
58 | STRV_FOREACH(name, names) { | |
59 | _cleanup_free_ char *fragment_path = NULL; | |
60 | _cleanup_strv_free_ char **dropin_paths = NULL; | |
61 | ||
b4c527f4 | 62 | r = unit_find_paths(bus, *name, &lp, false, &cached_name_map, &cached_id_map, &fragment_path, &dropin_paths); |
daf71ef6 LP |
63 | if (r == -ERFKILL) { |
64 | printf("%s# Unit %s is masked%s.\n", | |
65 | ansi_highlight_magenta(), | |
66 | *name, | |
67 | ansi_normal()); | |
68 | continue; | |
69 | } | |
70 | if (r == -EKEYREJECTED) { | |
71 | printf("%s# Unit %s could not be loaded.%s\n", | |
72 | ansi_highlight_magenta(), | |
73 | *name, | |
74 | ansi_normal()); | |
75 | continue; | |
76 | } | |
77 | if (r < 0) | |
78 | return r; | |
79 | if (r == 0) { | |
80 | /* Skip units which have no on-disk counterpart, but propagate the error to the | |
81 | * user */ | |
82 | rc = -ENOENT; | |
83 | continue; | |
84 | } | |
85 | ||
86 | if (first) | |
87 | first = false; | |
88 | else | |
89 | puts(""); | |
90 | ||
91 | if (need_daemon_reload(bus, *name) > 0) /* ignore errors (<0), this is informational output */ | |
92 | fprintf(stderr, | |
93 | "%s# Warning: %s changed on disk, the version systemd has loaded is outdated.\n" | |
94 | "%s# This output shows the current version of the unit's original fragment and drop-in files.\n" | |
95 | "%s# If fragments or drop-ins were added or removed, they are not properly reflected in this output.\n" | |
96 | "%s# Run 'systemctl%s daemon-reload' to reload units.%s\n", | |
97 | ansi_highlight_red(), | |
98 | *name, | |
99 | ansi_highlight_red(), | |
100 | ansi_highlight_red(), | |
101 | ansi_highlight_red(), | |
102 | arg_scope == UNIT_FILE_SYSTEM ? "" : " --user", | |
103 | ansi_normal()); | |
104 | ||
105 | r = cat_files(fragment_path, dropin_paths, 0); | |
106 | if (r < 0) | |
107 | return r; | |
108 | } | |
109 | ||
110 | return rc; | |
111 | } | |
112 | ||
85c5d313 | 113 | static int create_edit_temp_file(const char *new_path, const char *original_path, char ** const original_unit_paths, char **ret_tmp_fn) { |
daf71ef6 LP |
114 | _cleanup_free_ char *t = NULL; |
115 | int r; | |
116 | ||
117 | assert(new_path); | |
daf71ef6 LP |
118 | assert(ret_tmp_fn); |
119 | ||
120 | r = tempfn_random(new_path, NULL, &t); | |
121 | if (r < 0) | |
122 | return log_error_errno(r, "Failed to determine temporary filename for \"%s\": %m", new_path); | |
123 | ||
124 | r = mkdir_parents_label(new_path, 0755); | |
125 | if (r < 0) | |
126 | return log_error_errno(r, "Failed to create directories for \"%s\": %m", new_path); | |
127 | ||
85c5d313 | 128 | if (original_path) { |
129 | r = mac_selinux_create_file_prepare(new_path, S_IFREG); | |
130 | if (r < 0) | |
131 | return r; | |
daf71ef6 | 132 | |
85c5d313 | 133 | r = copy_file(original_path, t, 0, 0644, 0, 0, COPY_REFLINK); |
134 | if (r == -ENOENT) { | |
135 | r = touch(t); | |
136 | mac_selinux_create_file_clear(); | |
137 | if (r < 0) | |
138 | return log_error_errno(r, "Failed to create temporary file \"%s\": %m", t); | |
139 | } else { | |
140 | mac_selinux_create_file_clear(); | |
141 | if (r < 0) | |
142 | return log_error_errno(r, "Failed to create temporary file for \"%s\": %m", new_path); | |
143 | } | |
144 | } else if (original_unit_paths) { | |
145 | _cleanup_free_ char *new_contents = NULL; | |
146 | _cleanup_fclose_ FILE *f = NULL; | |
daf71ef6 | 147 | |
85c5d313 | 148 | r = mac_selinux_create_file_prepare(new_path, S_IFREG); |
149 | if (r < 0) | |
150 | return r; | |
daf71ef6 | 151 | |
85c5d313 | 152 | f = fopen(t, "we"); |
daf71ef6 | 153 | mac_selinux_create_file_clear(); |
85c5d313 | 154 | if (!f) |
155 | return log_error_errno(errno, "Failed to open \"%s\": %m", t); | |
daf71ef6 | 156 | |
85c5d313 | 157 | r = fchmod(fileno(f), 0644); |
daf71ef6 | 158 | if (r < 0) |
85c5d313 | 159 | return log_error_errno(errno, "Failed to change mode of \"%s\": %m", t); |
160 | ||
be81e45c | 161 | r = read_full_file(new_path, &new_contents, NULL); |
85c5d313 | 162 | if (r < 0 && r != -ENOENT) |
163 | return log_error_errno(r, "Failed to read \"%s\": %m", new_path); | |
164 | ||
165 | fprintf(f, | |
166 | "### Editing %s\n" | |
167 | EDIT_MARKER_START | |
168 | "\n\n%s%s\n" | |
169 | EDIT_MARKER_END, | |
170 | new_path, | |
171 | strempty(new_contents), | |
172 | new_contents && endswith(new_contents, "\n") ? "" : "\n"); | |
173 | ||
174 | /* Add a comment with the contents of the original unit files */ | |
175 | STRV_FOREACH(path, original_unit_paths) { | |
176 | _cleanup_free_ char *contents = NULL; | |
177 | ||
178 | /* Skip the file that's being edited */ | |
179 | if (path_equal(*path, new_path)) | |
180 | continue; | |
181 | ||
be81e45c | 182 | r = read_full_file(*path, &contents, NULL); |
85c5d313 | 183 | if (r < 0) |
184 | return log_error_errno(r, "Failed to read \"%s\": %m", *path); | |
185 | ||
186 | fprintf(f, "\n\n### %s", *path); | |
187 | if (!isempty(contents)) { | |
8f3e1b9d DT |
188 | _cleanup_free_ char *commented_contents = NULL; |
189 | ||
190 | commented_contents = strreplace(strstrip(contents), "\n", "\n# "); | |
191 | if (!commented_contents) | |
85c5d313 | 192 | return log_oom(); |
8f3e1b9d | 193 | fprintf(f, "\n# %s", commented_contents); |
85c5d313 | 194 | } |
195 | } | |
daf71ef6 | 196 | |
85c5d313 | 197 | r = fflush_and_check(f); |
daf71ef6 | 198 | if (r < 0) |
85c5d313 | 199 | return log_error_errno(r, "Failed to create temporary file \"%s\": %m", t); |
daf71ef6 LP |
200 | } |
201 | ||
202 | *ret_tmp_fn = TAKE_PTR(t); | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
207 | static int get_file_to_edit( | |
208 | const LookupPaths *paths, | |
209 | const char *name, | |
210 | char **ret_path) { | |
211 | ||
212 | _cleanup_free_ char *path = NULL, *run = NULL; | |
213 | ||
214 | assert(name); | |
215 | assert(ret_path); | |
216 | ||
217 | path = path_join(paths->persistent_config, name); | |
218 | if (!path) | |
219 | return log_oom(); | |
220 | ||
221 | if (arg_runtime) { | |
222 | run = path_join(paths->runtime_config, name); | |
223 | if (!run) | |
224 | return log_oom(); | |
225 | } | |
226 | ||
227 | if (arg_runtime) { | |
228 | if (access(path, F_OK) >= 0) | |
229 | return log_error_errno(SYNTHETIC_ERRNO(EEXIST), | |
230 | "Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", | |
231 | run, path); | |
232 | ||
233 | *ret_path = TAKE_PTR(run); | |
234 | } else | |
235 | *ret_path = TAKE_PTR(path); | |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
240 | static int unit_file_create_new( | |
241 | const LookupPaths *paths, | |
242 | const char *unit_name, | |
243 | const char *suffix, | |
85c5d313 | 244 | char ** const original_unit_paths, |
daf71ef6 LP |
245 | char **ret_new_path, |
246 | char **ret_tmp_path) { | |
247 | ||
248 | _cleanup_free_ char *new_path = NULL, *tmp_path = NULL; | |
249 | const char *ending; | |
250 | int r; | |
251 | ||
252 | assert(unit_name); | |
253 | assert(ret_new_path); | |
254 | assert(ret_tmp_path); | |
255 | ||
256 | ending = strjoina(unit_name, suffix); | |
257 | r = get_file_to_edit(paths, ending, &new_path); | |
258 | if (r < 0) | |
259 | return r; | |
260 | ||
85c5d313 | 261 | r = create_edit_temp_file(new_path, NULL, original_unit_paths, &tmp_path); |
daf71ef6 LP |
262 | if (r < 0) |
263 | return r; | |
264 | ||
265 | *ret_new_path = TAKE_PTR(new_path); | |
266 | *ret_tmp_path = TAKE_PTR(tmp_path); | |
267 | ||
268 | return 0; | |
269 | } | |
270 | ||
271 | static int unit_file_create_copy( | |
272 | const LookupPaths *paths, | |
273 | const char *unit_name, | |
274 | const char *fragment_path, | |
275 | char **ret_new_path, | |
276 | char **ret_tmp_path) { | |
277 | ||
278 | _cleanup_free_ char *new_path = NULL, *tmp_path = NULL; | |
279 | int r; | |
280 | ||
281 | assert(fragment_path); | |
282 | assert(unit_name); | |
283 | assert(ret_new_path); | |
284 | assert(ret_tmp_path); | |
285 | ||
286 | r = get_file_to_edit(paths, unit_name, &new_path); | |
287 | if (r < 0) | |
288 | return r; | |
289 | ||
290 | if (!path_equal(fragment_path, new_path) && access(new_path, F_OK) >= 0) { | |
291 | char response; | |
292 | ||
293 | r = ask_char(&response, "yn", "\"%s\" already exists. Overwrite with \"%s\"? [(y)es, (n)o] ", new_path, fragment_path); | |
294 | if (r < 0) | |
295 | return r; | |
296 | if (response != 'y') | |
297 | return log_warning_errno(SYNTHETIC_ERRNO(EKEYREJECTED), "%s skipped.", unit_name); | |
298 | } | |
299 | ||
85c5d313 | 300 | r = create_edit_temp_file(new_path, fragment_path, NULL, &tmp_path); |
daf71ef6 LP |
301 | if (r < 0) |
302 | return r; | |
303 | ||
304 | *ret_new_path = TAKE_PTR(new_path); | |
305 | *ret_tmp_path = TAKE_PTR(tmp_path); | |
306 | ||
307 | return 0; | |
308 | } | |
309 | ||
310 | static int run_editor(char **paths) { | |
311 | int r; | |
312 | ||
313 | assert(paths); | |
314 | ||
315 | r = safe_fork("(editor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG|FORK_WAIT, NULL); | |
316 | if (r < 0) | |
317 | return r; | |
318 | if (r == 0) { | |
de010b0b | 319 | char **editor_args = NULL; |
daf71ef6 LP |
320 | size_t n_editor_args = 0, i = 1, argc; |
321 | const char **args, *editor, *p; | |
322 | ||
323 | argc = strv_length(paths)/2 + 1; | |
324 | ||
325 | /* SYSTEMD_EDITOR takes precedence over EDITOR which takes precedence over VISUAL. If | |
326 | * neither SYSTEMD_EDITOR nor EDITOR nor VISUAL are present, we try to execute well known | |
327 | * editors. */ | |
328 | editor = getenv("SYSTEMD_EDITOR"); | |
329 | if (!editor) | |
330 | editor = getenv("EDITOR"); | |
331 | if (!editor) | |
332 | editor = getenv("VISUAL"); | |
333 | ||
334 | if (!isempty(editor)) { | |
335 | editor_args = strv_split(editor, WHITESPACE); | |
336 | if (!editor_args) { | |
337 | (void) log_oom(); | |
338 | _exit(EXIT_FAILURE); | |
339 | } | |
340 | n_editor_args = strv_length(editor_args); | |
341 | argc += n_editor_args - 1; | |
342 | } | |
343 | ||
344 | args = newa(const char*, argc + 1); | |
345 | ||
346 | if (n_editor_args > 0) { | |
347 | args[0] = editor_args[0]; | |
348 | for (; i < n_editor_args; i++) | |
349 | args[i] = editor_args[i]; | |
350 | } | |
351 | ||
352 | STRV_FOREACH_PAIR(original_path, tmp_path, paths) | |
353 | args[i++] = *tmp_path; | |
354 | args[i] = NULL; | |
355 | ||
356 | if (n_editor_args > 0) | |
357 | execvp(args[0], (char* const*) args); | |
358 | ||
359 | FOREACH_STRING(p, "editor", "nano", "vim", "vi") { | |
360 | args[0] = p; | |
361 | execvp(p, (char* const*) args); | |
362 | /* We do not fail if the editor doesn't exist because we want to try each one of them | |
363 | * before failing. */ | |
364 | if (errno != ENOENT) { | |
365 | log_error_errno(errno, "Failed to execute %s: %m", editor); | |
366 | _exit(EXIT_FAILURE); | |
367 | } | |
368 | } | |
369 | ||
370 | log_error("Cannot edit unit(s), no editor available. Please set either $SYSTEMD_EDITOR, $EDITOR or $VISUAL."); | |
371 | _exit(EXIT_FAILURE); | |
372 | } | |
373 | ||
374 | return 0; | |
375 | } | |
376 | ||
377 | static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { | |
b4c527f4 | 378 | _cleanup_(hashmap_freep) Hashmap *cached_name_map = NULL, *cached_id_map = NULL; |
daf71ef6 | 379 | _cleanup_(lookup_paths_free) LookupPaths lp = {}; |
daf71ef6 LP |
380 | int r; |
381 | ||
382 | assert(names); | |
383 | assert(paths); | |
384 | ||
385 | r = lookup_paths_init(&lp, arg_scope, 0, arg_root); | |
386 | if (r < 0) | |
387 | return r; | |
388 | ||
389 | STRV_FOREACH(name, names) { | |
390 | _cleanup_free_ char *path = NULL, *new_path = NULL, *tmp_path = NULL, *tmp_name = NULL; | |
85c5d313 | 391 | _cleanup_strv_free_ char **unit_paths = NULL; |
daf71ef6 LP |
392 | const char *unit_name; |
393 | ||
85c5d313 | 394 | r = unit_find_paths(bus, *name, &lp, false, &cached_name_map, &cached_id_map, &path, &unit_paths); |
daf71ef6 LP |
395 | if (r == -EKEYREJECTED) { |
396 | /* If loading of the unit failed server side complete, then the server won't tell us | |
397 | * the unit file path. In that case, find the file client side. */ | |
398 | log_debug_errno(r, "Unit '%s' was not loaded correctly, retrying client-side.", *name); | |
e4d22a9f | 399 | r = unit_find_paths(bus, *name, &lp, true, &cached_name_map, &cached_id_map, &path, &unit_paths); |
daf71ef6 LP |
400 | } |
401 | if (r == -ERFKILL) | |
402 | return log_error_errno(r, "Unit '%s' masked, cannot edit.", *name); | |
403 | if (r < 0) | |
404 | return r; | |
405 | ||
98199724 | 406 | if (!path) { |
daf71ef6 LP |
407 | if (!arg_force) { |
408 | log_info("Run 'systemctl edit%s --force --full %s' to create a new unit.", | |
409 | arg_scope == UNIT_FILE_GLOBAL ? " --global" : | |
410 | arg_scope == UNIT_FILE_USER ? " --user" : "", | |
411 | *name); | |
412 | return -ENOENT; | |
413 | } | |
414 | ||
415 | /* Create a new unit from scratch */ | |
416 | unit_name = *name; | |
417 | r = unit_file_create_new(&lp, unit_name, | |
418 | arg_full ? NULL : ".d/override.conf", | |
85c5d313 | 419 | NULL, &new_path, &tmp_path); |
daf71ef6 | 420 | } else { |
daf71ef6 LP |
421 | unit_name = basename(path); |
422 | /* We follow unit aliases, but we need to propagate the instance */ | |
423 | if (unit_name_is_valid(*name, UNIT_NAME_INSTANCE) && | |
424 | unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) { | |
425 | _cleanup_free_ char *instance = NULL; | |
426 | ||
427 | r = unit_name_to_instance(*name, &instance); | |
428 | if (r < 0) | |
429 | return r; | |
430 | ||
431 | r = unit_name_replace_instance(unit_name, instance, &tmp_name); | |
432 | if (r < 0) | |
433 | return r; | |
434 | ||
435 | unit_name = tmp_name; | |
436 | } | |
437 | ||
438 | if (arg_full) | |
439 | r = unit_file_create_copy(&lp, unit_name, path, &new_path, &tmp_path); | |
85c5d313 | 440 | else { |
441 | r = strv_prepend(&unit_paths, path); | |
442 | if (r < 0) | |
443 | return log_oom(); | |
444 | ||
445 | r = unit_file_create_new(&lp, unit_name, ".d/override.conf", unit_paths, &new_path, &tmp_path); | |
446 | } | |
daf71ef6 LP |
447 | } |
448 | if (r < 0) | |
449 | return r; | |
450 | ||
451 | r = strv_push_pair(paths, new_path, tmp_path); | |
452 | if (r < 0) | |
453 | return log_oom(); | |
454 | ||
455 | new_path = tmp_path = NULL; | |
456 | } | |
457 | ||
458 | return 0; | |
459 | } | |
460 | ||
85c5d313 | 461 | static int trim_edit_markers(const char *path) { |
462 | _cleanup_free_ char *contents = NULL; | |
463 | char *contents_start = NULL; | |
464 | const char *contents_end = NULL; | |
465 | size_t size; | |
466 | int r; | |
467 | ||
468 | /* Trim out the lines between the two markers */ | |
be81e45c | 469 | r = read_full_file(path, &contents, NULL); |
85c5d313 | 470 | if (r < 0) |
471 | return log_error_errno(r, "Failed to read temporary file \"%s\": %m", path); | |
472 | ||
be81e45c LP |
473 | size = strlen(contents); |
474 | ||
85c5d313 | 475 | contents_start = strstr(contents, EDIT_MARKER_START); |
476 | if (contents_start) | |
477 | contents_start += strlen(EDIT_MARKER_START); | |
478 | else | |
479 | contents_start = contents; | |
480 | ||
481 | contents_end = strstr(contents_start, EDIT_MARKER_END); | |
482 | if (contents_end) | |
483 | strshorten(contents_start, contents_end - contents_start); | |
484 | ||
485 | contents_start = strstrip(contents_start); | |
486 | ||
487 | /* Write new contents if the trimming actually changed anything */ | |
488 | if (strlen(contents) != size) { | |
489 | r = write_string_file(path, contents_start, WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_TRUNCATE | WRITE_STRING_FILE_AVOID_NEWLINE); | |
490 | if (r < 0) | |
491 | return log_error_errno(r, "Failed to modify temporary file \"%s\": %m", path); | |
492 | } | |
493 | ||
494 | return 0; | |
495 | } | |
496 | ||
32baf64d | 497 | int verb_edit(int argc, char *argv[], void *userdata) { |
daf71ef6 LP |
498 | _cleanup_(lookup_paths_free) LookupPaths lp = {}; |
499 | _cleanup_strv_free_ char **names = NULL; | |
500 | _cleanup_strv_free_ char **paths = NULL; | |
daf71ef6 LP |
501 | sd_bus *bus; |
502 | int r; | |
503 | ||
504 | if (!on_tty()) | |
505 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit units if not on a tty."); | |
506 | ||
507 | if (arg_transport != BUS_TRANSPORT_LOCAL) | |
508 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit units remotely."); | |
509 | ||
510 | r = lookup_paths_init(&lp, arg_scope, 0, arg_root); | |
511 | if (r < 0) | |
512 | return log_error_errno(r, "Failed to determine unit paths: %m"); | |
513 | ||
514 | r = mac_selinux_init(); | |
515 | if (r < 0) | |
516 | return r; | |
517 | ||
518 | r = acquire_bus(BUS_MANAGER, &bus); | |
519 | if (r < 0) | |
520 | return r; | |
521 | ||
522 | r = expand_unit_names(bus, strv_skip(argv, 1), NULL, &names, NULL); | |
523 | if (r < 0) | |
524 | return log_error_errno(r, "Failed to expand names: %m"); | |
ffcd6838 ZJS |
525 | if (strv_isempty(names)) |
526 | return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No units matched the specified patterns."); | |
daf71ef6 LP |
527 | |
528 | STRV_FOREACH(tmp, names) { | |
529 | r = unit_is_masked(bus, &lp, *tmp); | |
530 | if (r < 0) | |
531 | return r; | |
532 | if (r > 0) | |
533 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit %s: unit is masked.", *tmp); | |
534 | } | |
535 | ||
536 | r = find_paths_to_edit(bus, names, &paths); | |
537 | if (r < 0) | |
538 | return r; | |
539 | ||
540 | if (strv_isempty(paths)) | |
541 | return -ENOENT; | |
542 | ||
543 | r = run_editor(paths); | |
544 | if (r < 0) | |
545 | goto end; | |
546 | ||
547 | STRV_FOREACH_PAIR(original, tmp, paths) { | |
548 | /* If the temporary file is empty we ignore it. This allows the user to cancel the | |
549 | * modification. */ | |
85c5d313 | 550 | r = trim_edit_markers(*tmp); |
551 | if (r < 0) | |
552 | continue; | |
553 | ||
daf71ef6 LP |
554 | if (null_or_empty_path(*tmp)) { |
555 | log_warning("Editing \"%s\" canceled: temporary file is empty.", *original); | |
556 | continue; | |
557 | } | |
558 | ||
559 | r = rename(*tmp, *original); | |
560 | if (r < 0) { | |
561 | r = log_error_errno(errno, "Failed to rename \"%s\" to \"%s\": %m", *tmp, *original); | |
562 | goto end; | |
563 | } | |
564 | } | |
565 | ||
566 | r = 0; | |
567 | ||
623461c1 LP |
568 | if (!arg_no_reload && !install_client_side()) { |
569 | r = daemon_reload(ACTION_RELOAD, /* graceful= */ false); | |
570 | if (r > 0) | |
571 | r = 0; | |
572 | } | |
daf71ef6 LP |
573 | |
574 | end: | |
575 | STRV_FOREACH_PAIR(original, tmp, paths) { | |
576 | (void) unlink(*tmp); | |
577 | ||
578 | /* Removing empty dropin dirs */ | |
579 | if (!arg_full) { | |
c2b2df60 | 580 | _cleanup_free_ char *dir = NULL; |
daf71ef6 LP |
581 | |
582 | dir = dirname_malloc(*original); | |
583 | if (!dir) | |
584 | return log_oom(); | |
585 | ||
586 | /* No need to check if the dir is empty, rmdir does nothing if it is not the case. */ | |
587 | (void) rmdir(dir); | |
588 | } | |
589 | } | |
590 | ||
591 | return r; | |
592 | } |