mirror of
https://github.com/systemd/systemd.git
synced 2025-02-03 17:47:28 +03:00
Merge pull request #30302 from keszybz/systemd-edit-stdin
systemctl edit --stdin
This commit is contained in:
commit
34f4fcb59f
@ -513,7 +513,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
<!-- Note that we don't document force-reload here, as that is just compatibility support, and we generally
|
||||
don't document that. -->
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v229"/>
|
||||
<xi:include href="version-info.xml" xpointer="v229"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
@ -1066,8 +1066,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v238"/>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v238"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -1171,7 +1170,6 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
<command>enable</command>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v217"/>
|
||||
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -1179,38 +1177,40 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
<term><command>edit <replaceable>UNIT</replaceable>…</command></term>
|
||||
|
||||
<listitem>
|
||||
<para>Edit a drop-in snippet or a whole replacement file if
|
||||
<option>--full</option> is specified, to extend or override the
|
||||
specified unit.</para>
|
||||
<para>Edit or replace a drop-in snippet or the main unit file, to extend or override the
|
||||
definition of the specified unit.</para>
|
||||
|
||||
<para>Depending on whether <option>--system</option> (the default),
|
||||
<option>--user</option>, or <option>--global</option> 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.</para>
|
||||
<para>Depending on whether <option>--system</option> (the default), <option>--user</option>, or
|
||||
<option>--global</option> 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.</para>
|
||||
|
||||
<para>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 <command>systemctl daemon-reload --system</command> or
|
||||
<command>systemctl daemon-reload --user</command>. For <command>edit --global</command>, 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).</para>
|
||||
|
||||
<para>If <option>--full</option> is specified, a replacement for the main unit file will be
|
||||
created or edited. Otherwise, a drop-in file will be created or edited.</para>
|
||||
|
||||
<para>If <option>--drop-in=</option> is specified, the given drop-in file name
|
||||
will be used instead of the default <filename>override.conf</filename>.</para>
|
||||
|
||||
<para>If <option>--full</option> is specified, this will copy the
|
||||
original units instead of creating drop-in files.</para>
|
||||
|
||||
<para>If <option>--force</option> is specified and any units do
|
||||
not already exist, new unit files will be opened for editing.</para>
|
||||
<para>The unit must exist, i.e. its main unit file must be present. If <option>--force</option>
|
||||
is specified, this requirement is ignored and a new unit may be created (with
|
||||
<option>--full</option>), or a drop-in for a nonexistent unit may be crated.</para>
|
||||
|
||||
<para>If <option>--runtime</option> is specified, the changes will
|
||||
be made temporarily in <filename>/run/</filename> and they will be
|
||||
lost on the next reboot.</para>
|
||||
|
||||
<para>If <option>--stdin</option> is specified, the new contents will be read from standard
|
||||
input. In this mode, the old contents of the file are discarded.</para>
|
||||
|
||||
<para>If the temporary file is empty upon exit, the modification of
|
||||
the related unit is canceled.</para>
|
||||
|
||||
<para>After the units have been edited, systemd configuration is
|
||||
reloaded (in a way that is equivalent to <command>daemon-reload</command>).
|
||||
</para>
|
||||
|
||||
<para>Note that this command cannot be used to remotely edit units
|
||||
and that you cannot temporarily edit units which are in
|
||||
<filename>/etc/</filename>, since they take precedence over
|
||||
@ -1565,7 +1565,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v215"/>
|
||||
<xi:include href="version-info.xml" xpointer="v215"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -1949,7 +1949,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
<option>-P</option> once will also affect all properties listed with
|
||||
<option>-p</option>/<option>--property=</option>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v246"/>
|
||||
<xi:include href="version-info.xml" xpointer="v246"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -2091,7 +2091,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
<para>When printing properties with <command>show</command>, only print the value, and skip the
|
||||
property name and <literal>=</literal>. Also see option <option>-P</option> above.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v230"/>
|
||||
<xi:include href="version-info.xml" xpointer="v230"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -2101,7 +2101,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
<listitem>
|
||||
<para>When showing sockets, show the type of the socket.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v202"/>
|
||||
<xi:include href="version-info.xml" xpointer="v202"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -2109,72 +2109,72 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
<term><option>--job-mode=</option></term>
|
||||
|
||||
<listitem>
|
||||
<para>When queuing a new job, this option controls how to deal with
|
||||
already queued jobs. It takes one of <literal>fail</literal>,
|
||||
<literal>replace</literal>,
|
||||
<literal>replace-irreversibly</literal>,
|
||||
<literal>isolate</literal>,
|
||||
<literal>ignore-dependencies</literal>,
|
||||
<literal>ignore-requirements</literal>,
|
||||
<literal>flush</literal>,
|
||||
<literal>triggering</literal>, or
|
||||
<literal>restart-dependencies</literal>. Defaults to
|
||||
<literal>replace</literal>, except when the
|
||||
<command>isolate</command> command is used which implies the
|
||||
<literal>isolate</literal> job mode.</para>
|
||||
<para>When queuing a new job, this option controls how to deal with
|
||||
already queued jobs. It takes one of <literal>fail</literal>,
|
||||
<literal>replace</literal>,
|
||||
<literal>replace-irreversibly</literal>,
|
||||
<literal>isolate</literal>,
|
||||
<literal>ignore-dependencies</literal>,
|
||||
<literal>ignore-requirements</literal>,
|
||||
<literal>flush</literal>,
|
||||
<literal>triggering</literal>, or
|
||||
<literal>restart-dependencies</literal>. Defaults to
|
||||
<literal>replace</literal>, except when the
|
||||
<command>isolate</command> command is used which implies the
|
||||
<literal>isolate</literal> job mode.</para>
|
||||
|
||||
<para>If <literal>fail</literal> 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.</para>
|
||||
<para>If <literal>fail</literal> 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.</para>
|
||||
|
||||
<para>If <literal>replace</literal> (the default) is
|
||||
specified, any conflicting pending job will be replaced, as
|
||||
necessary.</para>
|
||||
<para>If <literal>replace</literal> (the default) is
|
||||
specified, any conflicting pending job will be replaced, as
|
||||
necessary.</para>
|
||||
|
||||
<para>If <literal>replace-irreversibly</literal> is specified,
|
||||
operate like <literal>replace</literal>, 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 <command>cancel</command>
|
||||
command. This job mode should be used on any transaction which
|
||||
pulls in <filename>shutdown.target</filename>.</para>
|
||||
<para>If <literal>replace-irreversibly</literal> is specified,
|
||||
operate like <literal>replace</literal>, 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 <command>cancel</command>
|
||||
command. This job mode should be used on any transaction which
|
||||
pulls in <filename>shutdown.target</filename>.</para>
|
||||
|
||||
<para><literal>isolate</literal> 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
|
||||
<command>isolate</command> command is used.</para>
|
||||
<para><literal>isolate</literal> 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
|
||||
<command>isolate</command> command is used.</para>
|
||||
|
||||
<para><literal>flush</literal> will cause all queued jobs to
|
||||
be canceled when the new job is enqueued.</para>
|
||||
<para><literal>flush</literal> will cause all queued jobs to
|
||||
be canceled when the new job is enqueued.</para>
|
||||
|
||||
<para>If <literal>ignore-dependencies</literal> 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.</para>
|
||||
<para>If <literal>ignore-dependencies</literal> 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.</para>
|
||||
|
||||
<para><literal>ignore-requirements</literal> is similar to
|
||||
<literal>ignore-dependencies</literal>, but only causes the
|
||||
requirement dependencies to be ignored, the ordering
|
||||
dependencies will still be honored.</para>
|
||||
<para><literal>ignore-requirements</literal> is similar to
|
||||
<literal>ignore-dependencies</literal>, but only causes the
|
||||
requirement dependencies to be ignored, the ordering
|
||||
dependencies will still be honored.</para>
|
||||
|
||||
<para><literal>triggering</literal> may only be used with
|
||||
<command>systemctl stop</command>. In this mode, the specified
|
||||
unit and any active units that trigger it are stopped. See the
|
||||
discussion of
|
||||
<varname>Triggers=</varname> in <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for more information about triggering units.</para>
|
||||
<para><literal>triggering</literal> may only be used with
|
||||
<command>systemctl stop</command>. In this mode, the specified
|
||||
unit and any active units that trigger it are stopped. See the
|
||||
discussion of
|
||||
<varname>Triggers=</varname> in <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for more information about triggering units.</para>
|
||||
|
||||
<para><literal>restart-dependencies</literal> may only be used with
|
||||
<command>systemctl start</command>. In this mode, dependencies of
|
||||
the specified unit will receive restart propagation, as if a restart
|
||||
job had been enqueued for the unit.</para>
|
||||
<para><literal>restart-dependencies</literal> may only be used with
|
||||
<command>systemctl start</command>. In this mode, dependencies of
|
||||
the specified unit will receive restart propagation, as if a restart
|
||||
job had been enqueued for the unit.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v209"/>
|
||||
<xi:include href="version-info.xml" xpointer="v209"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -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.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v242"/>
|
||||
<xi:include href="version-info.xml" xpointer="v242"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -2237,7 +2237,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
<listitem>
|
||||
<para>Shortcut for <option>--check-inhibitors=no</option>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v198"/>
|
||||
<xi:include href="version-info.xml" xpointer="v198"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -2414,7 +2414,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
<filename>&UMOUNT_PATH;</filename>), but no main process is defined. If omitted, defaults to
|
||||
<option>all</option>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v252"/>
|
||||
<xi:include href="version-info.xml" xpointer="v252"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -2458,7 +2458,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
<varname>FileDescriptorStorePreserve=</varname> option is enabled, since the file descriptor store
|
||||
is otherwise cleaned automatically when the unit is stopped.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v243"/>
|
||||
<xi:include href="version-info.xml" xpointer="v243"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -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.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v225"/>
|
||||
<xi:include href="version-info.xml" xpointer="v225"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -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.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v220"/>
|
||||
<xi:include href="version-info.xml" xpointer="v220"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -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.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v215"/>
|
||||
<xi:include href="version-info.xml" xpointer="v215"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -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.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v220"/>
|
||||
<xi:include href="version-info.xml" xpointer="v220"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -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.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v242"/>
|
||||
<xi:include href="version-info.xml" xpointer="v242"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -2637,7 +2637,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
as argument, or <literal>help</literal> in order to list available entries. Note that not all boot
|
||||
loaders support this functionality.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v242"/>
|
||||
<xi:include href="version-info.xml" xpointer="v242"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -2645,11 +2645,12 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
<term><option>--reboot-argument=</option></term>
|
||||
|
||||
<listitem>
|
||||
<para>This switch is used with <command>reboot</command>. The value is architecture and firmware specific. As an example, <literal>recovery</literal>
|
||||
might be used to trigger system recovery, and <literal>fota</literal> might be used to trigger a
|
||||
<quote>firmware over the air</quote> update.</para>
|
||||
<para>This switch is used with <command>reboot</command>. The value is architecture and firmware
|
||||
specific. As an example, <literal>recovery</literal> might be used to trigger system recovery, and
|
||||
<literal>fota</literal> might be used to trigger a <quote>firmware over the air</quote>
|
||||
update.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v246"/>
|
||||
<xi:include href="version-info.xml" xpointer="v246"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -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.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v203"/>
|
||||
<xi:include href="version-info.xml" xpointer="v203"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -2720,7 +2721,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v247"/>
|
||||
<xi:include href="version-info.xml" xpointer="v247"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -2779,7 +2780,28 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
section "PARSING TIMESTAMPS". Specially, if <literal>show</literal> is given, the currently scheduled
|
||||
action will be shown, which can be canceled by passing an empty string or <literal>cancel</literal>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v254"/>
|
||||
<xi:include href="version-info.xml" xpointer="v254"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--stdin</option></term>
|
||||
|
||||
<listitem>
|
||||
<para>When used with <command>edit</command>, 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:</para>
|
||||
|
||||
<programlisting>$ systemctl edit --drop-in=limits.conf --stdin some-service.service <<EOF
|
||||
[Unit]
|
||||
AllowedCPUs=7,11
|
||||
EOF
|
||||
</programlisting>
|
||||
|
||||
<para>Multiple drop-ins may be "edited" in this mode; the same contents will be written to all of
|
||||
them.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v256"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 <<EOF
|
||||
[Unit]
|
||||
Description=spectacular
|
||||
# this comment should remain
|
||||
|
||||
EOF
|
||||
printf '%s\n' '[Unit]' 'Description=spectacular' '# this comment should remain' | \
|
||||
cmp - "/etc/systemd/system/$UNIT_NAME.d/override2.conf"
|
||||
|
||||
# Test simultaneous editing of two units and creation of drop-in for a nonexistent unit
|
||||
systemctl edit "$UNIT_NAME" "$UNIT_NAME2" --stdin --force --drop-in=override2.conf <<<'[X-Section]'
|
||||
printf '%s\n' '[X-Section]' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override2.conf"
|
||||
printf '%s\n' '[X-Section]' | cmp - "/etc/systemd/system/$UNIT_NAME2.d/override2.conf"
|
||||
|
||||
# Double free when editing a template unit (#26483)
|
||||
EDITOR='true' script -ec 'systemctl edit user@0' /dev/null
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user