diff --git a/man/systemctl.xml b/man/systemctl.xml
index 6029434ae6..73c9ea143b 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -513,7 +513,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
-
+
@@ -1066,8 +1066,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
-
-
+
@@ -1171,7 +1170,6 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
enable.
-
@@ -1179,38 +1177,40 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
edit UNIT…
- Edit a drop-in snippet or a whole replacement file if
- is specified, to extend or override the
- specified unit.
+ Edit or replace a drop-in snippet or the main unit file, to extend or override the
+ definition of the specified unit.
- Depending on whether (the default),
- , or is specified,
- this command creates a drop-in file for each unit either for the system,
- for the calling user, or for all futures logins of all users. Then,
- the editor (see the "Environment" section below) is invoked on
- temporary files which will be written to the real location if the
- editor exits successfully.
+ Depending on whether (the default), , or
+ is specified, this command will operate on the system unit files, unit
+ files for the calling user, or the unit files shared between all users.
+
+ The editor (see the "Environment" section below) is invoked on temporary files which will
+ be written to the real location if the editor exits successfully. After the editing is finished,
+ configuration is reloaded, equivalent to systemctl daemon-reload --system or
+ systemctl daemon-reload --user. For edit --global, the
+ reload is not performed and the edits will take effect only for subsequent logins (or after a
+ reload is requested in a different way).
+
+ If is specified, a replacement for the main unit file will be
+ created or edited. Otherwise, a drop-in file will be created or edited.If is specified, the given drop-in file name
will be used instead of the default override.conf.
- If is specified, this will copy the
- original units instead of creating drop-in files.
-
- If is specified and any units do
- not already exist, new unit files will be opened for editing.
+ The unit must exist, i.e. its main unit file must be present. If
+ is specified, this requirement is ignored and a new unit may be created (with
+ ), or a drop-in for a nonexistent unit may be crated.If is specified, the changes will
be made temporarily in /run/ and they will be
lost on the next reboot.
+ If is specified, the new contents will be read from standard
+ input. In this mode, the old contents of the file are discarded.
+
If the temporary file is empty upon exit, the modification of
the related unit is canceled.
- After the units have been edited, systemd configuration is
- reloaded (in a way that is equivalent to daemon-reload).
-
-
Note that this command cannot be used to remotely edit units
and that you cannot temporarily edit units which are in
/etc/, since they take precedence over
@@ -1565,7 +1565,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
-
+
@@ -1949,7 +1949,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
once will also affect all properties listed with
/.
-
+
@@ -2091,7 +2091,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
When printing properties with show, only print the value, and skip the
property name and =. Also see option above.
-
+
@@ -2101,7 +2101,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
When showing sockets, show the type of the socket.
-
+
@@ -2109,72 +2109,72 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
- When queuing a new job, this option controls how to deal with
- already queued jobs. It takes one of fail,
- replace,
- replace-irreversibly,
- isolate,
- ignore-dependencies,
- ignore-requirements,
- flush,
- triggering, or
- restart-dependencies. Defaults to
- replace, except when the
- isolate command is used which implies the
- isolate job mode.
+ When queuing a new job, this option controls how to deal with
+ already queued jobs. It takes one of fail,
+ replace,
+ replace-irreversibly,
+ isolate,
+ ignore-dependencies,
+ ignore-requirements,
+ flush,
+ triggering, or
+ restart-dependencies. Defaults to
+ replace, except when the
+ isolate command is used which implies the
+ isolate job mode.
- If fail is specified and a requested
- operation conflicts with a pending job (more specifically:
- causes an already pending start job to be reversed into a stop
- job or vice versa), cause the operation to fail.
+ If fail is specified and a requested
+ operation conflicts with a pending job (more specifically:
+ causes an already pending start job to be reversed into a stop
+ job or vice versa), cause the operation to fail.
- If replace (the default) is
- specified, any conflicting pending job will be replaced, as
- necessary.
+ If replace (the default) is
+ specified, any conflicting pending job will be replaced, as
+ necessary.
- If replace-irreversibly is specified,
- operate like replace, but also mark the new
- jobs as irreversible. This prevents future conflicting
- transactions from replacing these jobs (or even being enqueued
- while the irreversible jobs are still pending). Irreversible
- jobs can still be cancelled using the cancel
- command. This job mode should be used on any transaction which
- pulls in shutdown.target.
+ If replace-irreversibly is specified,
+ operate like replace, but also mark the new
+ jobs as irreversible. This prevents future conflicting
+ transactions from replacing these jobs (or even being enqueued
+ while the irreversible jobs are still pending). Irreversible
+ jobs can still be cancelled using the cancel
+ command. This job mode should be used on any transaction which
+ pulls in shutdown.target.
- isolate is only valid for start
- operations and causes all other units to be stopped when the
- specified unit is started. This mode is always used when the
- isolate command is used.
+ isolate is only valid for start
+ operations and causes all other units to be stopped when the
+ specified unit is started. This mode is always used when the
+ isolate command is used.
- flush will cause all queued jobs to
- be canceled when the new job is enqueued.
+ flush will cause all queued jobs to
+ be canceled when the new job is enqueued.
- If ignore-dependencies is specified,
- then all unit dependencies are ignored for this new job and
- the operation is executed immediately. If passed, no required
- units of the unit passed will be pulled in, and no ordering
- dependencies will be honored. This is mostly a debugging and
- rescue tool for the administrator and should not be used by
- applications.
+ If ignore-dependencies is specified,
+ then all unit dependencies are ignored for this new job and
+ the operation is executed immediately. If passed, no required
+ units of the unit passed will be pulled in, and no ordering
+ dependencies will be honored. This is mostly a debugging and
+ rescue tool for the administrator and should not be used by
+ applications.
- ignore-requirements is similar to
- ignore-dependencies, but only causes the
- requirement dependencies to be ignored, the ordering
- dependencies will still be honored.
+ ignore-requirements is similar to
+ ignore-dependencies, but only causes the
+ requirement dependencies to be ignored, the ordering
+ dependencies will still be honored.
- triggering may only be used with
- systemctl stop. In this mode, the specified
- unit and any active units that trigger it are stopped. See the
- discussion of
- Triggers= in systemd.unit5
- for more information about triggering units.
+ triggering may only be used with
+ systemctl stop. In this mode, the specified
+ unit and any active units that trigger it are stopped. See the
+ discussion of
+ Triggers= in systemd.unit5
+ for more information about triggering units.
- restart-dependencies may only be used with
- systemctl start. In this mode, dependencies of
- the specified unit will receive restart propagation, as if a restart
- job had been enqueued for the unit.
+ restart-dependencies may only be used with
+ systemctl start. In this mode, dependencies of
+ the specified unit will receive restart propagation, as if a restart
+ job had been enqueued for the unit.
-
+
@@ -2190,7 +2190,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
run as effect of the enqueued jobs might request further jobs to be pulled in. This means that
completion of the listed jobs might ultimately entail more jobs than the listed ones.
-
+
@@ -2237,7 +2237,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
Shortcut for .
-
+
@@ -2414,7 +2414,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
&UMOUNT_PATH;), but no main process is defined. If omitted, defaults to
.
-
+
@@ -2458,7 +2458,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
FileDescriptorStorePreserve= option is enabled, since the file descriptor store
is otherwise cleaned automatically when the unit is stopped.
-
+
@@ -2494,7 +2494,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
short message explaining the reason for the operation. The message will be logged together with the default
shutdown message.
-
+
@@ -2508,7 +2508,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
or stop operation is only carried out when the respective enable or
disable operation has been successful.
-
+
@@ -2575,7 +2575,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
enabled according to the preset rules, or only enabled, or
only disabled.
-
+
@@ -2611,7 +2611,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
reboot into the firmware setup interface. Note that this functionality is not available on all
systems.
-
+
@@ -2624,7 +2624,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
menu timeout. Pass zero in order to disable the menu timeout. Note that not all boot loaders
support this functionality.
-
+
@@ -2637,7 +2637,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
as argument, or help in order to list available entries. Note that not all boot
loaders support this functionality.
-
+
@@ -2645,11 +2645,12 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
- This switch is used with reboot. The value is architecture and firmware specific. As an example, recovery
- might be used to trigger system recovery, and fota might be used to trigger a
- firmware over the air update.
+ This switch is used with reboot. The value is architecture and firmware
+ specific. As an example, recovery might be used to trigger system recovery, and
+ fota might be used to trigger a firmware over the air
+ update.
-
+
@@ -2662,7 +2663,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
the output is printed as a list instead of a tree, and the bullet
circles are omitted.
-
+
@@ -2720,7 +2721,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
-
+
@@ -2779,7 +2780,28 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
section "PARSING TIMESTAMPS". Specially, if show is given, the currently scheduled
action will be shown, which can be canceled by passing an empty string or cancel.
-
+
+
+
+
+
+
+
+
+ When used with edit, the contents of the file will be read from standard
+ input and the editor will not be launched. In this mode, the old contents of the file are
+ completely replaced. This is useful to "edit" unit files from scripts:
+
+ $ systemctl edit --drop-in=limits.conf --stdin some-service.service <<EOF
+[Unit]
+AllowedCPUs=7,11
+EOF
+
+
+ Multiple drop-ins may be "edited" in this mode; the same contents will be written to all of
+ them.
+
+
diff --git a/src/shared/edit-util.c b/src/shared/edit-util.c
index 045839b663..2662b2bf1b 100644
--- a/src/shared/edit-util.c
+++ b/src/shared/edit-util.c
@@ -93,41 +93,22 @@ int edit_files_add(
.path = TAKE_PTR(new_path),
.original_path = TAKE_PTR(new_original_path),
.comment_paths = TAKE_PTR(new_comment_paths),
+ .line = 1,
};
context->n_files++;
return 1;
}
-static int create_edit_temp_file(EditFile *e) {
- _cleanup_(unlink_and_freep) char *temp = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- const char *source;
- bool has_original, has_target;
- unsigned line = 1;
- int r;
-
+static int populate_edit_temp_file(EditFile *e, FILE *f, const char *filename) {
assert(e);
- assert(e->context);
- assert(e->path);
- assert(!e->comment_paths || (e->context->marker_start && e->context->marker_end));
+ assert(f);
+ assert(filename);
- if (e->temp)
- return 0;
-
- r = mkdir_parents_label(e->path, 0755);
- if (r < 0)
- return log_error_errno(r, "Failed to create parent directories for '%s': %m", e->path);
-
- r = fopen_temporary_label(e->path, e->path, &f, &temp);
- if (r < 0)
- return log_error_errno(r, "Failed to create temporary file for '%s': %m", e->path);
-
- if (fchmod(fileno(f), 0644) < 0)
- return log_error_errno(errno, "Failed to change mode of temporary file '%s': %m", temp);
-
- has_original = e->original_path && access(e->original_path, F_OK) >= 0;
- has_target = access(e->path, F_OK) >= 0;
+ bool has_original = e->original_path && access(e->original_path, F_OK) >= 0;
+ bool has_target = access(e->path, F_OK) >= 0;
+ const char *source;
+ int r;
if (has_original && (!has_target || e->context->overwrite_with_origin))
/* We are asked to overwrite target with original_path or target doesn't exist. */
@@ -160,7 +141,7 @@ static int create_edit_temp_file(EditFile *e) {
source_contents && endswith(source_contents, "\n") ? "" : "\n",
e->context->marker_end);
- line = 4; /* Start editing at the contents area */
+ e->line = 4; /* Start editing at the contents area */
STRV_FOREACH(path, e->comment_paths) {
_cleanup_free_ char *comment = NULL;
@@ -189,16 +170,54 @@ static int create_edit_temp_file(EditFile *e) {
r = copy_file_fd(source, fileno(f), COPY_REFLINK);
if (r < 0) {
assert(r != -ENOENT);
- return log_error_errno(r, "Failed to copy file '%s' to temporary file '%s': %m", source, temp);
+ return log_error_errno(r, "Failed to copy file '%s' to temporary file '%s': %m",
+ source, filename);
}
}
+ return 0;
+}
+
+static int create_edit_temp_file(EditFile *e, const char *contents, size_t contents_size) {
+ _cleanup_(unlink_and_freep) char *temp = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ assert(e);
+ assert(e->context);
+ assert(e->path);
+ assert(!e->comment_paths || (e->context->marker_start && e->context->marker_end));
+ assert(contents || contents_size == 0);
+
+ if (e->temp)
+ return 0;
+
+ r = mkdir_parents_label(e->path, 0755);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create parent directories for '%s': %m", e->path);
+
+ r = fopen_temporary_label(e->path, e->path, &f, &temp);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create temporary file for '%s': %m", e->path);
+
+ if (fchmod(fileno(f), 0644) < 0)
+ return log_error_errno(errno, "Failed to change mode of temporary file '%s': %m", temp);
+
+ if (e->context->stdin) {
+ if (fwrite(contents, 1, contents_size, f) != contents_size)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to copy input to temporary file '%s': %m", temp);
+ } else {
+ r = populate_edit_temp_file(e, f, temp);
+ if (r < 0)
+ return r;
+ }
+
r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to write to temporary file '%s': %m", temp);
e->temp = TAKE_PTR(temp);
- e->line = line;
return 0;
}
@@ -282,7 +301,7 @@ static int run_editor(const EditFileContext *context) {
}
static int strip_edit_temp_file(EditFile *e) {
- _cleanup_free_ char *old_contents = NULL, *new_contents = NULL;
+ _cleanup_free_ char *old_contents = NULL, *tmp = NULL, *new_contents = NULL;
const char *stripped;
int r;
@@ -294,15 +313,17 @@ static int strip_edit_temp_file(EditFile *e) {
if (r < 0)
return log_error_errno(r, "Failed to read temporary file '%s': %m", e->temp);
- if (e->context->marker_start) {
+ tmp = strdup(old_contents);
+ if (!tmp)
+ return log_oom();
+
+ if (e->context->marker_start && !e->context->stdin) {
/* Trim out the lines between the two markers */
char *contents_start, *contents_end;
assert(e->context->marker_end);
- contents_start = strstrafter(old_contents, e->context->marker_start);
- if (!contents_start)
- contents_start = old_contents;
+ contents_start = strstrafter(tmp, e->context->marker_start) ?: tmp;
contents_end = strstr(contents_start, e->context->marker_end);
if (contents_end)
@@ -310,9 +331,13 @@ static int strip_edit_temp_file(EditFile *e) {
stripped = strstrip(contents_start);
} else
- stripped = strstrip(old_contents);
- if (isempty(stripped))
- return 0; /* File is empty (has no real changes) */
+ stripped = strstrip(tmp);
+
+ if (isempty(stripped)) {
+ /* File is empty (has no real changes) */
+ log_notice("%s: after editing, new contents are empty, not writing file.", e->path);
+ return 0;
+ }
/* Trim prefix and suffix, but ensure suffixed by single newline */
new_contents = strjoin(stripped, "\n");
@@ -320,16 +345,19 @@ static int strip_edit_temp_file(EditFile *e) {
return log_oom();
if (streq(old_contents, new_contents)) /* Don't touch the file if the above didn't change a thing */
- return 1; /* Contents unchanged after stripping but has changes */
+ return 1; /* Contents have real changes */
- r = write_string_file(e->temp, new_contents, WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_TRUNCATE | WRITE_STRING_FILE_AVOID_NEWLINE);
+ r = write_string_file(e->temp, new_contents,
+ WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_TRUNCATE | WRITE_STRING_FILE_AVOID_NEWLINE);
if (r < 0)
return log_error_errno(r, "Failed to strip temporary file '%s': %m", e->temp);
- return 1; /* Contents have real changes and are changed after stripping */
+ return 1; /* Contents have real changes */
}
int do_edit_files_and_install(EditFileContext *context) {
+ _cleanup_free_ char *data = NULL;
+ size_t data_size = 0;
int r;
assert(context);
@@ -337,33 +365,41 @@ int do_edit_files_and_install(EditFileContext *context) {
if (context->n_files == 0)
return log_debug_errno(SYNTHETIC_ERRNO(ENOENT), "Got no files to edit.");
- FOREACH_ARRAY(i, context->files, context->n_files) {
- r = create_edit_temp_file(i);
+ if (context->stdin) {
+ r = read_full_stream(stdin, &data, &data_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read stdin: %m");
+ }
+
+ FOREACH_ARRAY(editfile, context->files, context->n_files) {
+ r = create_edit_temp_file(editfile, data, data_size);
if (r < 0)
return r;
}
- r = run_editor(context);
- if (r < 0)
- return r;
+ if (!context->stdin) {
+ r = run_editor(context);
+ if (r < 0)
+ return r;
+ }
- FOREACH_ARRAY(i, context->files, context->n_files) {
+ FOREACH_ARRAY(editfile, context->files, context->n_files) {
/* Always call strip_edit_temp_file which will tell if the temp file has actual changes */
- r = strip_edit_temp_file(i);
+ r = strip_edit_temp_file(editfile);
if (r < 0)
return r;
if (r == 0) /* temp file doesn't carry actual changes, ignoring */
continue;
- r = RET_NERRNO(rename(i->temp, i->path));
+ r = RET_NERRNO(rename(editfile->temp, editfile->path));
if (r < 0)
return log_error_errno(r,
"Failed to rename temporary file '%s' to target file '%s': %m",
- i->temp,
- i->path);
- i->temp = mfree(i->temp);
+ editfile->temp,
+ editfile->path);
+ editfile->temp = mfree(editfile->temp);
- log_info("Successfully installed edited file '%s'.", i->path);
+ log_info("Successfully installed edited file '%s'.", editfile->path);
}
return 0;
diff --git a/src/shared/edit-util.h b/src/shared/edit-util.h
index 83b3df8683..29ff81f4e7 100644
--- a/src/shared/edit-util.h
+++ b/src/shared/edit-util.h
@@ -24,7 +24,8 @@ struct EditFileContext {
const char *marker_start;
const char *marker_end;
bool remove_parent;
- bool overwrite_with_origin; /* whether to always overwrite target with original file */
+ bool overwrite_with_origin; /* Always overwrite target with original file. */
+ bool stdin; /* Read contents from stdin instead of launching an editor. */
};
void edit_file_context_done(EditFileContext *context);
diff --git a/src/shared/verbs.c b/src/shared/verbs.c
index a01095288c..035337760e 100644
--- a/src/shared/verbs.c
+++ b/src/shared/verbs.c
@@ -168,4 +168,4 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
return verb->dispatch(1, STRV_MAKE(verb->verb), userdata);
return verb->dispatch(left, argv, userdata);
- }
+}
diff --git a/src/systemctl/systemctl-edit.c b/src/systemctl/systemctl-edit.c
index 367afa20f7..d967b1649e 100644
--- a/src/systemctl/systemctl-edit.c
+++ b/src/systemctl/systemctl-edit.c
@@ -317,12 +317,13 @@ int verb_edit(int argc, char *argv[], void *userdata) {
.marker_end = DROPIN_MARKER_END,
.remove_parent = !arg_full,
.overwrite_with_origin = true,
+ .stdin = arg_stdin,
};
_cleanup_strv_free_ char **names = NULL;
sd_bus *bus;
int r;
- if (!on_tty())
+ if (!on_tty() && !arg_stdin)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit units if not on a tty.");
if (arg_transport != BUS_TRANSPORT_LOCAL)
@@ -342,6 +343,10 @@ int verb_edit(int argc, char *argv[], void *userdata) {
if (strv_isempty(names))
return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No units matched the specified patterns.");
+ if (arg_stdin && arg_full && strv_length(names) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "With 'edit --stdin --full', exactly one unit for editing must be specified.");
+
STRV_FOREACH(tmp, names) {
r = unit_is_masked(bus, *tmp);
if (r < 0)
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 0fa672eb90..d278d74789 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -103,6 +103,7 @@ bool arg_kill_value_set = false;
char *arg_root = NULL;
char *arg_image = NULL;
usec_t arg_when = 0;
+bool arg_stdin = false;
const char *arg_reboot_argument = NULL;
enum action arg_action = ACTION_SYSTEMCTL;
BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
@@ -337,6 +338,7 @@ static int systemctl_help(void) {
" --drop-in=NAME Edit unit files using the specified drop-in file name\n"
" --when=TIME Schedule halt/power-off/reboot/kexec action after\n"
" a certain timestamp\n"
+ " --stdin Read contents of edited file from stdin\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
@@ -463,6 +465,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
ARG_NO_WARN,
ARG_DROP_IN,
ARG_WHEN,
+ ARG_STDIN,
};
static const struct option options[] = {
@@ -529,6 +532,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "marked", no_argument, NULL, ARG_MARKED },
{ "drop-in", required_argument, NULL, ARG_DROP_IN },
{ "when", required_argument, NULL, ARG_WHEN },
+ { "stdin", no_argument, NULL, ARG_STDIN },
{}
};
@@ -1019,6 +1023,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
break;
+ case ARG_STDIN:
+ arg_stdin = true;
+ break;
+
case '.':
/* Output an error mimicking getopt, and print a hint afterwards */
log_error("%s: invalid option -- '.'", program_invocation_name);
@@ -1069,7 +1077,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
}
if (arg_image && arg_root)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Please specify either --root= or --image=, the combination of both is not supported.");
return 1;
}
diff --git a/src/systemctl/systemctl.h b/src/systemctl/systemctl.h
index a42fc36a2e..17dcfb4048 100644
--- a/src/systemctl/systemctl.h
+++ b/src/systemctl/systemctl.h
@@ -84,6 +84,7 @@ extern int arg_kill_value;
extern bool arg_kill_value_set;
extern char *arg_root;
extern usec_t arg_when;
+extern bool arg_stdin;
extern const char *arg_reboot_argument;
extern enum action arg_action;
extern BusTransport arg_transport;
diff --git a/test/units/testsuite-26.sh b/test/units/testsuite-26.sh
index 1e11c420e1..5aaac099f4 100755
--- a/test/units/testsuite-26.sh
+++ b/test/units/testsuite-26.sh
@@ -22,6 +22,8 @@ trap at_exit EXIT
# Note: the service file is created under /usr on purpose to test
# the 'revert' verb as well
export UNIT_NAME="systemctl-test-$RANDOM.service"
+export UNIT_NAME2="systemctl-test-$RANDOM.service"
+
cat >"/usr/lib/systemd/system/$UNIT_NAME" <<\EOF
[Unit]
Description=systemctl test
@@ -56,6 +58,20 @@ printf '%b' '[Service]\n' 'ExecStart=\n' 'ExecStart=sleep 10d' >"+4"
EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf"
+systemctl edit "$UNIT_NAME" --stdin --drop-in=override2.conf <