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