diff --git a/man/systemctl.xml b/man/systemctl.xml
index c94dca55b9f..3fe455cb7ef 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -1177,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
@@ -2764,6 +2766,27 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
+
+
+
+
+ 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 dd77b0e20e3..2662b2bf1b3 100644
--- a/src/shared/edit-util.c
+++ b/src/shared/edit-util.c
@@ -178,7 +178,7 @@ static int populate_edit_temp_file(EditFile *e, FILE *f, const char *filename) {
return 0;
}
-static int create_edit_temp_file(EditFile *e) {
+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;
@@ -187,6 +187,7 @@ static int create_edit_temp_file(EditFile *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;
@@ -202,9 +203,15 @@ static int create_edit_temp_file(EditFile *e) {
if (fchmod(fileno(f), 0644) < 0)
return log_error_errno(errno, "Failed to change mode of temporary file '%s': %m", temp);
- r = populate_edit_temp_file(e, f, temp);
- if (r < 0)
- return r;
+ 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)
@@ -310,7 +317,7 @@ static int strip_edit_temp_file(EditFile *e) {
if (!tmp)
return log_oom();
- if (e->context->marker_start) {
+ if (e->context->marker_start && !e->context->stdin) {
/* Trim out the lines between the two markers */
char *contents_start, *contents_end;
@@ -349,6 +356,8 @@ static int strip_edit_temp_file(EditFile *e) {
}
int do_edit_files_and_install(EditFileContext *context) {
+ _cleanup_free_ char *data = NULL;
+ size_t data_size = 0;
int r;
assert(context);
@@ -356,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 83b3df86839..29ff81f4e74 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/systemctl/systemctl-edit.c b/src/systemctl/systemctl-edit.c
index 367afa20f7a..d967b1649ef 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 dd6f6c9873e..2a1d2d7967e 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;
@@ -335,6 +336,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,
@@ -461,6 +463,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[] = {
@@ -527,6 +530,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 },
{}
};
@@ -1017,6 +1021,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);
@@ -1067,7 +1075,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 e8ba8f76a07..9f63443fde9 100644
--- a/src/systemctl/systemctl.h
+++ b/src/systemctl/systemctl.h
@@ -83,6 +83,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;