Skip to content

Commit 24c2c56

Browse files
authored
Merge pull request systemd#8058 from keszybz/sysusers-inline
Extend sysusers for package installation scripts
2 parents 662b3e5 + fb959f1 commit 24c2c56

File tree

17 files changed

+480
-123
lines changed

17 files changed

+480
-123
lines changed

man/systemd-sysusers.xml

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,18 @@
6969
<citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
7070
</para>
7171

72-
<para>If invoked with no arguments, it applies all directives from
73-
all files found. If one or more filenames are passed on the
74-
command line, only the directives in these files are applied. If
75-
only the basename of a file is specified, all directories as
76-
specified in
77-
<citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
78-
are searched for a matching file. If the string
79-
<literal>-</literal> is specified instead of a filename, entries from the
80-
standard input of the process are read.</para>
72+
<para>If invoked with no arguments, it applies all directives from all files
73+
found in the directories specified by
74+
<citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
75+
When invoked with positional arguments, if option
76+
<option>--replace=<replaceable>PATH</replaceable></option> is specified, arguments
77+
specified on the command line are used instead of the configuration file
78+
<replaceable>PATH</replaceable>. Otherwise, just the configuration specified by
79+
the command line arguments is executed. The string <literal>-</literal> may be
80+
specified instead of a filename to instruct <command>systemd-sysusers</command>
81+
to read the configuration from standard input. If only the basename of a file is
82+
specified, all configuration directories are searched for a matching file and
83+
the file found that has the highest priority is executed.</para>
8184
</refsect1>
8285

8386
<refsect1>
@@ -94,6 +97,46 @@
9497
paths. </para></listitem>
9598
</varlistentry>
9699

100+
<varlistentry>
101+
<term><option>--replace=<replaceable>PATH</replaceable></option></term>
102+
<listitem><para>When this option is given, one ore more positional arguments
103+
must be specified. All configuration files found in the directories listed in
104+
<citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
105+
will be read, and the configuration given on the command line will be
106+
handled instead of and with the same priority as the configuration file
107+
<replaceable>PATH</replaceable>.</para>
108+
109+
<para>This option is intended to be used when package installation scripts
110+
are running and files belonging to that package are not yet available on
111+
disk, so their contents must be given on the command line, but the admin
112+
configuration might already exist and should be given higher priority.
113+
</para>
114+
115+
<example>
116+
<title>RPM installation script for radvd</title>
117+
118+
<programlisting>echo 'u radvd - "radvd daemon"' | \
119+
systemd-sysusers --replace=/usr/lib/sysusers.d/radvd.conf -</programlisting>
120+
121+
<para>This will create the radvd user as if
122+
<filename>/usr/lib/sysusers.d/radvd.conf</filename> was already on disk.
123+
An admin might override the configuration specified on the command line by
124+
placing <filename>/etc/sysusers.d/radvd.conf</filename> or even
125+
<filename>/etc/sysusers.d/00-overrides.conf</filename>.</para>
126+
127+
<para>Note that this is the expanded from, and when used in a package, this
128+
would be written using a macro with "radvd" and a file containing the
129+
configuration line as arguments.</para>
130+
</example>
131+
</listitem>
132+
</varlistentry>
133+
134+
<varlistentry>
135+
<term><option>--inline</option></term>
136+
<listitem><para>Treat each positional argument as a separate configuration
137+
line instead of a file name.</para></listitem>
138+
</varlistentry>
139+
97140
<xi:include href="standard-options.xml" xpointer="help" />
98141
<xi:include href="standard-options.xml" xpointer="version" />
99142
</variablelist>

man/sysusers.d.xml

Lines changed: 49 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,14 @@
5757
<refsect1>
5858
<title>Description</title>
5959

60-
<para><command>systemd-sysusers</command> uses the files from <filename>sysusers.d</filename> directory to create
61-
system users and groups at package installation or boot time. This tool may be used to allocate system users and
62-
groups only, it is not useful for creating non-system (i.e. regular, "human") users and groups, as it accesses
63-
<filename>/etc/passwd</filename> and <filename>/etc/group</filename> directly, bypassing any more complex user
64-
databases, for example any database involving NIS or LDAP.</para>
60+
<para><command>systemd-sysusers</command> uses the files from
61+
<filename>sysusers.d</filename> directory to create system users and groups and
62+
to add users to groups, at package installation or boot time. This tool may be
63+
used to allocate system users and groups only, it is not useful for creating
64+
non-system (i.e. regular, "human") users and groups, as it accesses
65+
<filename>/etc/passwd</filename> and <filename>/etc/group</filename> directly,
66+
bypassing any more complex user databases, for example any database involving NIS
67+
or LDAP.</para>
6568
</refsect1>
6669

6770
<refsect1>
@@ -100,15 +103,16 @@
100103
<refsect1>
101104
<title>Configuration File Format</title>
102105

103-
<para>The file format is one line per user or group containing
104-
name, ID, GECOS field description and home directory:</para>
106+
<para>The file format is one line per user or group containing name, ID, GECOS
107+
field description, home directory, and login shell:</para>
105108

106-
<programlisting>#Type Name ID GECOS Home directory
107-
u httpd 440 "HTTP User"
108-
u authd /usr/bin/authd "Authorization user"
109-
g input - -
110-
m authd input
111-
u root 0 "Superuser" /root</programlisting>
109+
<programlisting>#Type Name ID GECOS Home directory Shell
110+
u httpd 404 "HTTP User"
111+
u authd /usr/bin/authd "Authorization user"
112+
u postgres - "Postgresql Database" /var/lib/pgsql /usr/libexec/postgresdb
113+
g input - -
114+
m authd input
115+
u root 0 "Superuser" /root /bin/zsh</programlisting>
112116

113117
<para>Empty lines and lines beginning with the <literal>#</literal> character are ignored, and may be used for
114118
commenting.</para>
@@ -122,14 +126,10 @@ u root 0 "Superuser" /root</programlisting>
122126
<variablelist>
123127
<varlistentry>
124128
<term><varname>u</varname></term>
125-
<listitem><para>Create a system user and group of the
126-
specified name should they not exist yet. The user's primary
127-
group will be set to the group bearing the same name. The
128-
user's shell will be set to
129-
<filename>/sbin/nologin</filename>, the home directory to
130-
the specified home directory, or <filename>/</filename> if
131-
none is given. The account will be created disabled, so that
132-
logins are not allowed.</para></listitem>
129+
<listitem><para>Create a system user and group of the specified name should
130+
they not exist yet. The user's primary group will be set to the group
131+
bearing the same name. The account will be created disabled, so that logins
132+
are not allowed.</para></listitem>
133133
</varlistentry>
134134

135135
<varlistentry>
@@ -187,7 +187,8 @@ u root 0 "Superuser" /root</programlisting>
187187
numeric 32-bit UID or GID of the user/group. Do not use IDs 65535
188188
or 4294967295, as they have special placeholder meanings.
189189
Specify <literal>-</literal> for automatic UID/GID allocation
190-
for the user or group. Alternatively, specify an absolute path
190+
for the user or group (this is strongly recommended unless it is strictly
191+
necessary to use a specific UID or GID). Alternatively, specify an absolute path
191192
in the file system. In this case, the UID/GID is read from the
192193
path's owner/group. This is useful to create users whose UID/GID
193194
match the owners of pre-existing files (such as SUID or SGID
@@ -209,37 +210,45 @@ u root 0 "Superuser" /root</programlisting>
209210
<refsect2>
210211
<title>GECOS</title>
211212

212-
<para>A short, descriptive string for users to be created,
213-
enclosed in quotation marks. Note that this field may not
214-
contain colons.</para>
213+
<para>A short, descriptive string for users to be created, enclosed in
214+
quotation marks. Note that this field may not contain colons.</para>
215215

216-
<para>Only applies to lines of type <varname>u</varname> and
217-
should otherwise be left unset, or be set to
218-
<literal>-</literal>.</para>
216+
<para>Only applies to lines of type <varname>u</varname> and should otherwise
217+
be left unset (or <literal>-</literal>).</para>
219218
</refsect2>
220219

221220
<refsect2>
222221
<title>Home Directory</title>
223222

224-
<para>The home directory for a new system user. If omitted,
225-
defaults to the root directory. It is recommended to not
226-
unnecessarily specify home directories for system users, unless
227-
software strictly requires one to be set.</para>
223+
<para>The home directory for a new system user. If omitted, defaults to the
224+
root directory.</para>
228225

229-
<para>Only applies to lines of type <varname>u</varname> and
230-
should otherwise be left unset, or be set to
231-
<literal>-</literal>.</para>
226+
<para>Only applies to lines of type <varname>u</varname> and should otherwise
227+
be left unset (or <literal>-</literal>). It is recommended to omit this, unless
228+
software strictly requires a home directory to be set.</para>
229+
</refsect2>
230+
231+
<refsect2>
232+
<title>Shell</title>
233+
234+
<para>The login shell of the user. If not specified, this will be set to
235+
<filename>/sbin/nologin</filename>, except if the UID of the user is 0, in
236+
which case <filename>/bin/sh</filename> will be used.</para>
237+
238+
<para>Only applies to lines of type <varname>u</varname> and should otherwise
239+
be left unset (or <literal>-</literal>). It is recommended to omit this, unless
240+
a shell different <filename>/sbin/nologin</filename> must be used.</para>
232241
</refsect2>
233242
</refsect1>
234243

235244
<refsect1>
236245
<title>Idempotence</title>
237246

238-
<para>Note that <command>systemd-sysusers</command> will do
239-
nothing if the specified users or groups already exist, so
240-
normally, there is no reason to override
241-
<filename>sysusers.d</filename> vendor configuration, except to
242-
block certain users or groups from being created.</para>
247+
<para>Note that <command>systemd-sysusers</command> will do nothing if the
248+
specified users or groups already exist or the users are members of specified
249+
groups, so normally there is no reason to override
250+
<filename>sysusers.d</filename> vendor configuration, except to block certain
251+
users or groups from being created.</para>
243252
</refsect1>
244253

245254
<refsect1>

src/basic/conf-files.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,70 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const
154154
return 0;
155155
}
156156

157+
int conf_files_insert(char ***strv, const char *root, const char *dirs, const char *path) {
158+
/* Insert a path into strv, at the place honouring the usual sorting rules:
159+
* - we first compare by the basename
160+
* - and then we compare by dirname, allowing just one file with the given
161+
* basename.
162+
* This means that we will
163+
* - add a new entry if basename(path) was not on the list,
164+
* - do nothing if an entry with higher priority was already present,
165+
* - do nothing if our new entry matches the existing entry,
166+
* - replace the existing entry if our new entry has higher priority.
167+
*/
168+
char *t;
169+
unsigned i;
170+
int r;
171+
172+
for (i = 0; i < strv_length(*strv); i++) {
173+
int c;
174+
175+
c = base_cmp(*strv + i, &path);
176+
if (c == 0) {
177+
const char *dir;
178+
179+
/* Oh, we found our spot and it already contains something. */
180+
NULSTR_FOREACH(dir, dirs) {
181+
char *p1, *p2;
182+
183+
p1 = path_startswith((*strv)[i], root);
184+
if (p1)
185+
/* Skip "/" in dir, because p1 is without "/" too */
186+
p1 = path_startswith(p1, dir + 1);
187+
if (p1)
188+
/* Existing entry with higher priority
189+
* or same priority, no need to do anything. */
190+
return 0;
191+
192+
p2 = path_startswith(path, dir);
193+
if (p2) {
194+
/* Our new entry has higher priority */
195+
t = path_join(root, path, NULL);
196+
if (!t)
197+
return log_oom();
198+
199+
return free_and_replace((*strv)[i], t);
200+
}
201+
}
202+
203+
} else if (c > 0)
204+
/* Following files have lower priority, let's go insert our
205+
* new entry. */
206+
break;
207+
208+
/* … we are not there yet, let's continue */
209+
}
210+
211+
t = path_join(root, path, NULL);
212+
if (!t)
213+
return log_oom();
214+
215+
r = strv_insert(strv, i, t);
216+
if (r < 0)
217+
free(t);
218+
return r;
219+
}
220+
157221
int conf_files_list_strv(char ***strv, const char *suffix, const char *root, unsigned flags, const char* const* dirs) {
158222
_cleanup_strv_free_ char **copy = NULL;
159223

src/basic/conf-files.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ enum {
2828
int conf_files_list(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dir, ...);
2929
int conf_files_list_strv(char ***ret, const char *suffix, const char *root, unsigned flags, const char* const* dirs);
3030
int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dirs);
31+
int conf_files_insert(char ***strv, const char *root, const char *dirs, const char *path);

src/basic/strv.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -446,14 +446,15 @@ int strv_push_pair(char ***l, char *a, char *b) {
446446
return 0;
447447
}
448448

449-
int strv_push_prepend(char ***l, char *value) {
449+
int strv_insert(char ***l, unsigned position, char *value) {
450450
char **c;
451451
unsigned n, m, i;
452452

453453
if (!value)
454454
return 0;
455455

456456
n = strv_length(*l);
457+
position = MIN(position, n);
457458

458459
/* increase and check for overflow */
459460
m = n + 2;
@@ -464,10 +465,12 @@ int strv_push_prepend(char ***l, char *value) {
464465
if (!c)
465466
return -ENOMEM;
466467

467-
for (i = 0; i < n; i++)
468+
for (i = 0; i < position; i++)
469+
c[i] = (*l)[i];
470+
c[position] = value;
471+
for (i = position; i < n; i++)
468472
c[i+1] = (*l)[i];
469473

470-
c[0] = value;
471474
c[n+1] = NULL;
472475

473476
free(*l);

src/basic/strv.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,12 @@ int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
5454
int strv_extend_front(char ***l, const char *value);
5555
int strv_push(char ***l, char *value);
5656
int strv_push_pair(char ***l, char *a, char *b);
57-
int strv_push_prepend(char ***l, char *value);
57+
int strv_insert(char ***l, unsigned position, char *value);
58+
59+
static inline int strv_push_prepend(char ***l, char *value) {
60+
return strv_insert(l, 0, value);
61+
}
62+
5863
int strv_consume(char ***l, char *value);
5964
int strv_consume_pair(char ***l, char *a, char *b);
6065
int strv_consume_prepend(char ***l, char *value);

src/basic/user-util.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -553,18 +553,18 @@ int take_etc_passwd_lock(const char *root) {
553553
* awfully racy, and thus we just won't do them. */
554554

555555
if (root)
556-
path = prefix_roota(root, "/etc/.pwd.lock");
556+
path = prefix_roota(root, ETC_PASSWD_LOCK_PATH);
557557
else
558-
path = "/etc/.pwd.lock";
558+
path = ETC_PASSWD_LOCK_PATH;
559559

560560
fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
561561
if (fd < 0)
562-
return -errno;
562+
return log_debug_errno(errno, "Cannot open %s: %m", path);
563563

564564
r = fcntl(fd, F_SETLKW, &flock);
565565
if (r < 0) {
566566
safe_close(fd);
567-
return -errno;
567+
return log_debug_errno(errno, "Locking %s failed: %m", path);
568568
}
569569

570570
return fd;
@@ -645,6 +645,8 @@ bool valid_gecos(const char *d) {
645645
}
646646

647647
bool valid_home(const char *p) {
648+
/* Note that this function is also called by valid_shell(), any
649+
* changes must account for that. */
648650

649651
if (isempty(p))
650652
return false;

src/basic/user-util.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ int take_etc_passwd_lock(const char *root);
6363
#define UID_NOBODY ((uid_t) 65534U)
6464
#define GID_NOBODY ((gid_t) 65534U)
6565

66+
#define ETC_PASSWD_LOCK_PATH "/etc/.pwd.lock"
67+
6668
static inline bool uid_is_dynamic(uid_t uid) {
6769
return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX;
6870
}
@@ -96,6 +98,15 @@ bool valid_user_group_name_or_id(const char *u);
9698
bool valid_gecos(const char *d);
9799
bool valid_home(const char *p);
98100

101+
static inline bool valid_shell(const char *p) {
102+
/* We have the same requirements, so just piggy-back on the home check.
103+
*
104+
* Let's ignore /etc/shells because this is only applicable to real and
105+
* not system users. It is also incompatible with the idea of empty /etc.
106+
*/
107+
return valid_home(p);
108+
}
109+
99110
int maybe_setgroups(size_t size, const gid_t *list);
100111

101112
bool synthesize_nobody(void);

0 commit comments

Comments
 (0)