]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/machine-id-setup.c
2 This file is part of systemd.
4 Copyright 2010 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include <sys/mount.h>
30 #include "alloc-util.h"
34 #include "hexdecoct.h"
37 #include "machine-id-setup.h"
40 #include "mount-util.h"
41 #include "path-util.h"
42 #include "process-util.h"
43 #include "stat-util.h"
44 #include "string-util.h"
45 #include "umask-util.h"
49 static int shorten_uuid ( char destination
[ 34 ], const char source
[ 36 ]) {
55 /* Converts a UUID into a machine ID, by lowercasing it and
56 * removing dashes. Validates everything. */
58 for ( i
= 0 , j
= 0 ; i
< 36 && j
< 32 ; i
++) {
61 t
= unhexchar ( source
[ i
]);
65 destination
[ j
++] = hexchar ( t
);
68 if ( i
!= 36 || j
!= 32 )
71 destination
[ 32 ] = ' \n ' ;
76 static int read_machine_id ( int fd
, char id
[ 34 ]) {
77 char id_to_validate
[ 34 ];
83 /* Reads a machine ID from a file, validates it, and returns
84 * it. The returned ID ends in a newline. */
86 r
= loop_read_exact ( fd
, id_to_validate
, 33 , false );
90 if ( id_to_validate
[ 32 ] != ' \n ' )
93 id_to_validate
[ 32 ] = 0 ;
95 if (! id128_is_valid ( id_to_validate
))
98 memcpy ( id
, id_to_validate
, 32 );
104 static int write_machine_id ( int fd
, char id
[ 34 ]) {
108 if ( lseek ( fd
, 0 , SEEK_SET
) < 0 )
111 return loop_write ( fd
, id
, 33 , false );
114 static int generate_machine_id ( char id
[ 34 ], const char * root
) {
119 const char * dbus_machine_id
;
124 dbus_machine_id
= "/var/lib/dbus/machine-id" ;
126 dbus_machine_id
= strjoina ( root
, "/var/lib/dbus/machine-id" );
128 /* First, try reading the D-Bus machine id, unless it is a symlink */
129 fd
= open ( dbus_machine_id
, O_RDONLY
| O_CLOEXEC
| O_NOCTTY
| O_NOFOLLOW
);
131 r
= read_machine_id ( fd
, id
);
135 log_info ( "Initializing machine ID from D-Bus machine ID." );
141 /* If that didn't work, see if we are running in a container,
142 * and a machine ID was passed in via $container_uuid the way
143 * libvirt/LXC does it */
145 if ( detect_container () > 0 ) {
146 _cleanup_free_
char * e
= NULL
;
148 r
= getenv_for_pid ( 1 , "container_uuid" , & e
);
150 r
= shorten_uuid ( id
, e
);
152 log_info ( "Initializing machine ID from container UUID." );
157 } else if ( detect_vm () == VIRTUALIZATION_KVM
) {
159 /* If we are not running in a container, see if we are
160 * running in qemu/kvm and a machine ID was passed in
161 * via -uuid on the qemu/kvm command line */
165 fd
= open ( "/sys/class/dmi/id/product_uuid" , O_RDONLY
| O_CLOEXEC
| O_NOCTTY
| O_NOFOLLOW
);
167 r
= loop_read_exact ( fd
, uuid
, 36 , false );
171 r
= shorten_uuid ( id
, uuid
);
173 log_info ( "Initializing machine ID from KVM UUID." );
181 /* If that didn't work, generate a random machine id */
182 r
= sd_id128_randomize (& buf
);
184 return log_error_errno ( r
, "Failed to open /dev/urandom: %m" );
186 for ( p
= buf
. bytes
, q
= id
; p
< buf
. bytes
+ sizeof ( buf
); p
++, q
+= 2 ) {
187 q
[ 0 ] = hexchar (* p
>> 4 );
188 q
[ 1 ] = hexchar (* p
& 15 );
194 log_info ( "Initializing machine ID from random generator." );
199 int machine_id_setup ( const char * root
, sd_id128_t machine_id
) {
200 const char * etc_machine_id
, * run_machine_id
;
201 _cleanup_close_
int fd
= - 1 ;
202 bool writable
= true ;
203 char id
[ 34 ]; /* 32 + \n + \0 */
207 etc_machine_id
= "/etc/machine-id" ;
208 run_machine_id
= "/run/machine-id" ;
212 x
= strjoina ( root
, "/etc/machine-id" );
213 etc_machine_id
= path_kill_slashes ( x
);
215 x
= strjoina ( root
, "/run/machine-id" );
216 run_machine_id
= path_kill_slashes ( x
);
219 RUN_WITH_UMASK ( 0000 ) {
220 /* We create this 0444, to indicate that this isn't really
221 * something you should ever modify. Of course, since the file
222 * will be owned by root it doesn't matter much, but maybe
225 mkdir_parents ( etc_machine_id
, 0755 );
226 fd
= open ( etc_machine_id
, O_RDWR
| O_CREAT
| O_CLOEXEC
| O_NOCTTY
, 0444 );
228 int old_errno
= errno
;
230 fd
= open ( etc_machine_id
, O_RDONLY
| O_CLOEXEC
| O_NOCTTY
);
232 if ( old_errno
== EROFS
&& errno
== ENOENT
)
233 log_error_errno ( errno
,
234 "System cannot boot: Missing /etc/machine-id and /etc is mounted read-only. \n "
235 "Booting up is supported only when: \n "
236 "1) /etc/machine-id exists and is populated. \n "
237 "2) /etc/machine-id exists and is empty. \n "
238 "3) /etc/machine-id is missing and /etc is writable. \n " );
240 log_error_errno ( errno
, "Cannot open %s: %m" , etc_machine_id
);
249 /* A machine id argument overrides all other machined-ids */
250 if (! sd_id128_is_null ( machine_id
)) {
251 sd_id128_to_string ( machine_id
, id
);
255 if ( read_machine_id ( fd
, id
) >= 0 )
258 /* Hmm, so, the id currently stored is not useful, then let's
261 r
= generate_machine_id ( id
, root
);
267 if ( write_machine_id ( fd
, id
) >= 0 )
272 /* Hmm, we couldn't write it? So let's write it to
273 * /run/machine-id as a replacement */
275 RUN_WITH_UMASK ( 0022 ) {
276 r
= write_string_file ( run_machine_id
, id
, WRITE_STRING_FILE_CREATE
);
279 ( void ) unlink ( run_machine_id
);
280 return log_error_errno ( r
, "Cannot write %s: %m" , run_machine_id
);
283 /* And now, let's mount it over */
284 if ( mount ( run_machine_id
, etc_machine_id
, NULL
, MS_BIND
, NULL
) < 0 ) {
285 ( void ) unlink_noerrno ( run_machine_id
);
286 return log_error_errno ( errno
, "Failed to mount %s: %m" , etc_machine_id
);
289 log_info ( "Installed transient %s file." , etc_machine_id
);
291 /* Mark the mount read-only */
292 if ( mount ( NULL
, etc_machine_id
, NULL
, MS_BIND
| MS_RDONLY
| MS_REMOUNT
, NULL
) < 0 )
293 log_warning_errno ( errno
, "Failed to make transient %s read-only: %m" , etc_machine_id
);
298 int machine_id_commit ( const char * root
) {
299 _cleanup_close_
int fd
= - 1 , initial_mntns_fd
= - 1 ;
300 const char * etc_machine_id
;
301 char id
[ 34 ]; /* 32 + \n + \0 */
305 etc_machine_id
= "/etc/machine-id" ;
309 x
= strjoina ( root
, "/etc/machine-id" );
310 etc_machine_id
= path_kill_slashes ( x
);
313 r
= path_is_mount_point ( etc_machine_id
, 0 );
315 return log_error_errno ( r
, "Failed to determine whether %s is a mount point: %m" , etc_machine_id
);
317 log_debug ( "%s is is not a mount point. Nothing to do." , etc_machine_id
);
321 /* Read existing machine-id */
322 fd
= open ( etc_machine_id
, O_RDONLY
| O_CLOEXEC
| O_NOCTTY
);
324 return log_error_errno ( errno
, "Cannot open %s: %m" , etc_machine_id
);
326 r
= read_machine_id ( fd
, id
);
328 return log_error_errno ( r
, "We didn't find a valid machine ID in %s." , etc_machine_id
);
330 r
= fd_is_temporary_fs ( fd
);
332 return log_error_errno ( r
, "Failed to determine whether %s is on a temporary file system: %m" , etc_machine_id
);
334 log_error ( "%s is not on a temporary file system." , etc_machine_id
);
340 /* Store current mount namespace */
341 r
= namespace_open ( 0 , NULL
, & initial_mntns_fd
, NULL
, NULL
, NULL
);
343 return log_error_errno ( r
, "Can't fetch current mount namespace: %m" );
345 /* Switch to a new mount namespace, isolate ourself and unmount etc_machine_id in our new namespace */
346 if ( unshare ( CLONE_NEWNS
) < 0 )
347 return log_error_errno ( errno
, "Failed to enter new namespace: %m" );
349 if ( mount ( NULL
, "/" , NULL
, MS_SLAVE
| MS_REC
, NULL
) < 0 )
350 return log_error_errno ( errno
, "Couldn't make-rslave / mountpoint in our private namespace: %m" );
352 if ( umount ( etc_machine_id
) < 0 )
353 return log_error_errno ( errno
, "Failed to unmount transient %s file in our private namespace: %m" , etc_machine_id
);
355 /* Update a persistent version of etc_machine_id */
356 fd
= open ( etc_machine_id
, O_RDWR
| O_CREAT
| O_CLOEXEC
| O_NOCTTY
, 0444 );
358 return log_error_errno ( errno
, "Cannot open for writing %s. This is mandatory to get a persistent machine-id: %m" , etc_machine_id
);
360 r
= write_machine_id ( fd
, id
);
362 return log_error_errno ( r
, "Cannot write %s: %m" , etc_machine_id
);
366 /* Return to initial namespace and proceed a lazy tmpfs unmount */
367 r
= namespace_enter (- 1 , initial_mntns_fd
, - 1 , - 1 , - 1 );
369 return log_warning_errno ( r
, "Failed to switch back to initial mount namespace: %m. \n We'll keep transient %s file until next reboot." , etc_machine_id
);
371 if ( umount2 ( etc_machine_id
, MNT_DETACH
) < 0 )
372 return log_warning_errno ( errno
, "Failed to unmount transient %s file: %m. \n We keep that mount until next reboot." , etc_machine_id
);