]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/machine-id-setup.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
11 #include "alloc-util.h"
13 #include "creds-util.h"
15 #include "id128-util.h"
16 #include "initrd-util.h"
19 #include "machine-id-setup.h"
22 #include "mount-util.h"
23 #include "mountpoint-util.h"
24 #include "namespace-util.h"
25 #include "path-util.h"
26 #include "process-util.h"
27 #include "stat-util.h"
28 #include "string-util.h"
29 #include "sync-util.h"
30 #include "umask-util.h"
33 static int acquire_machine_id_from_credential ( sd_id128_t
* ret
) {
34 _cleanup_free_
char * buf
= NULL
;
37 r
= read_credential_with_decryption ( "system.machine_id" , ( void **) & buf
, /* ret_size= */ NULL
);
39 return log_warning_errno ( r
, "Failed to read system.machine_id credential, ignoring: %m" );
40 if ( r
== 0 ) /* not found */
43 r
= sd_id128_from_string ( buf
, ret
);
45 return log_warning_errno ( r
, "Failed to parse system.machine_id credential, ignoring: %m" );
47 log_info ( "Initializing machine ID from credential." );
51 static int acquire_machine_id ( const char * root
, sd_id128_t
* ret
) {
52 _cleanup_close_
int fd
= - EBADF
;
57 /* First, try reading the machine ID from /run/machine-id, which may not be mounted on
58 * /etc/machine-id yet. This is important on switching root especially on soft-reboot, Otherwise,
59 * machine ID may be changed after the transition. */
60 if ( isempty ( root
) && running_in_chroot () <= 0 &&
61 id128_read ( "/run/machine-id" , ID128_FORMAT_PLAIN
, ret
) >= 0 ) {
62 log_info ( "Reusing machine ID stored in /run/machine-id." );
63 return 1 ; /* Indicate that the machine ID is reused. */
66 /* Then, try reading the D-Bus machine id, unless it is a symlink */
67 fd
= chase_and_open ( "/var/lib/dbus/machine-id" , root
, CHASE_PREFIX_ROOT
| CHASE_NOFOLLOW
, O_RDONLY
| O_CLOEXEC
| O_NOCTTY
, NULL
);
68 if ( fd
>= 0 && id128_read_fd ( fd
, ID128_FORMAT_PLAIN
| ID128_REFUSE_NULL
, ret
) >= 0 ) {
69 log_info ( "Initializing machine ID from D-Bus machine ID." );
73 if ( isempty ( root
) && running_in_chroot () <= 0 ) {
74 /* Let's use a system credential for the machine ID if we can */
75 if ( acquire_machine_id_from_credential ( ret
) >= 0 )
78 /* If that didn't work, see if we are running in a container,
79 * and a machine ID was passed in via $container_uuid the way
80 * libvirt/LXC does it */
82 if ( detect_container () > 0 ) {
83 _cleanup_free_
char * e
= NULL
;
85 if ( getenv_for_pid ( 1 , "container_uuid" , & e
) > 0 &&
86 sd_id128_from_string ( e
, ret
) >= 0 ) {
87 log_info ( "Initializing machine ID from container UUID." );
91 } else if ( IN_SET ( detect_vm (), VIRTUALIZATION_KVM
, VIRTUALIZATION_AMAZON
, VIRTUALIZATION_QEMU
, VIRTUALIZATION_XEN
)) {
93 /* If we are not running in a container, see if we are running in a VM that provides
94 * a system UUID via the SMBIOS/DMI interfaces. Such environments include QEMU/KVM
95 * with the -uuid on the qemu command line or the Amazon EC2 Nitro hypervisor. */
97 if ( id128_get_product ( ret
) >= 0 ) {
98 log_info ( "Initializing machine ID from VM UUID." );
104 /* If that didn't work, generate a random machine id */
105 r
= sd_id128_randomize ( ret
);
107 return log_error_errno ( r
, "Failed to generate randomized machine ID: %m" );
109 log_info ( "Initializing machine ID from random generator." );
113 int machine_id_setup ( const char * root
, bool force_transient
, sd_id128_t machine_id
, sd_id128_t
* ret
) {
114 const char * etc_machine_id
, * run_machine_id
;
115 _cleanup_close_
int fd
= - EBADF
;
116 bool writable
, write_run_machine_id
= true ;
119 etc_machine_id
= prefix_roota ( root
, "/etc/machine-id" );
122 /* We create this 0444, to indicate that this isn't really
123 * something you should ever modify. Of course, since the file
124 * will be owned by root it doesn't matter much, but maybe
127 ( void ) mkdir_parents ( etc_machine_id
, 0755 );
128 fd
= open ( etc_machine_id
, O_RDWR
| O_CREAT
| O_CLOEXEC
| O_NOCTTY
, 0444 );
130 int old_errno
= errno
;
132 fd
= open ( etc_machine_id
, O_RDONLY
| O_CLOEXEC
| O_NOCTTY
);
134 if ( old_errno
== EROFS
&& errno
== ENOENT
)
135 return log_error_errno ( errno
,
136 "System cannot boot: Missing /etc/machine-id and /etc is mounted read-only. \n "
137 "Booting up is supported only when: \n "
138 "1) /etc/machine-id exists and is populated. \n "
139 "2) /etc/machine-id exists and is empty. \n "
140 "3) /etc/machine-id is missing and /etc is writable. \n " );
142 return log_error_errno ( errno
, "Cannot open %s: %m" , etc_machine_id
);
150 /* A we got a valid machine ID argument, that's what counts */
151 if ( sd_id128_is_null ( machine_id
)) {
153 /* Try to read any existing machine ID */
154 if ( id128_read_fd ( fd
, ID128_FORMAT_PLAIN
, & machine_id
) >= 0 )
157 /* Hmm, so, the id currently stored is not useful, then let's acquire one. */
158 r
= acquire_machine_id ( root
, & machine_id
);
161 write_run_machine_id
= ! r
;
165 if ( lseek ( fd
, 0 , SEEK_SET
) < 0 )
166 return log_error_errno ( errno
, "Failed to seek %s: %m" , etc_machine_id
);
168 if ( ftruncate ( fd
, 0 ) < 0 )
169 return log_error_errno ( errno
, "Failed to truncate %s: %m" , etc_machine_id
);
171 /* If the caller requested a transient machine-id, write the string "uninitialized\n" to
172 * disk and overmount it with a transient file.
174 * Otherwise write the machine-id directly to disk. */
175 if ( force_transient
) {
176 r
= loop_write ( fd
, "uninitialized \n " , SIZE_MAX
);
178 return log_error_errno ( r
, "Failed to write uninitialized %s: %m" , etc_machine_id
);
182 return log_error_errno ( r
, "Failed to sync %s: %m" , etc_machine_id
);
184 r
= id128_write_fd ( fd
, ID128_FORMAT_PLAIN
| ID128_SYNC_ON_WRITE
, machine_id
);
186 return log_error_errno ( r
, "Failed to write %s: %m" , etc_machine_id
);
194 /* Hmm, we couldn't or shouldn't write the machine-id to /etc?
195 * So let's write it to /run/machine-id as a replacement */
197 run_machine_id
= prefix_roota ( root
, "/run/machine-id" );
199 if ( write_run_machine_id
) {
201 r
= id128_write ( run_machine_id
, ID128_FORMAT_PLAIN
, machine_id
);
203 ( void ) unlink ( run_machine_id
);
204 return log_error_errno ( r
, "Cannot write %s: %m" , run_machine_id
);
208 /* And now, let's mount it over */
209 r
= mount_follow_verbose ( LOG_ERR
, run_machine_id
, etc_machine_id
, NULL
, MS_BIND
, NULL
);
211 ( void ) unlink ( run_machine_id
);
215 log_full ( force_transient
? LOG_DEBUG
: LOG_INFO
, "Installed transient %s file." , etc_machine_id
);
217 /* Mark the mount read-only */
218 r
= mount_follow_verbose ( LOG_WARNING
, NULL
, etc_machine_id
, NULL
, MS_BIND
| MS_RDONLY
| MS_REMOUNT
, NULL
);
224 ( void ) sd_notifyf ( /* unset_environment= */ false , "X_SYSTEMD_MACHINE_ID=" SD_ID128_FORMAT_STR
, SD_ID128_FORMAT_VAL ( machine_id
));
232 int machine_id_commit ( const char * root
) {
233 _cleanup_close_
int fd
= - EBADF
, initial_mntns_fd
= - EBADF
;
234 const char * etc_machine_id
;
238 /* Before doing anything, sync everything to ensure any changes by first-boot units are persisted.
240 * First, explicitly sync the file systems we care about and check if it worked. */
241 FOREACH_STRING ( sync_path
, "/etc/" , "/var/" ) {
242 r
= syncfs_path ( AT_FDCWD
, sync_path
);
244 return log_error_errno ( r
, "Cannot sync %s: %m" , sync_path
);
247 /* Afterwards, sync() the rest too, but we can't check the return value for these. */
250 /* Replaces a tmpfs bind mount of /etc/machine-id by a proper file, atomically. For this, the umount is removed
251 * in a mount namespace, a new file is created at the right place. Afterwards the mount is also removed in the
252 * original mount namespace, thus revealing the file that was just created. */
254 etc_machine_id
= prefix_roota ( root
, "/etc/machine-id" );
256 r
= path_is_mount_point ( etc_machine_id
);
258 return log_error_errno ( r
, "Failed to determine whether %s is a mount point: %m" , etc_machine_id
);
260 log_debug ( "%s is not a mount point. Nothing to do." , etc_machine_id
);
264 /* Read existing machine-id */
265 fd
= open ( etc_machine_id
, O_RDONLY
| O_CLOEXEC
| O_NOCTTY
);
267 return log_error_errno ( errno
, "Cannot open %s: %m" , etc_machine_id
);
269 r
= fd_is_temporary_fs ( fd
);
271 return log_error_errno ( r
, "Failed to determine whether %s is on a temporary file system: %m" , etc_machine_id
);
273 return log_error_errno ( SYNTHETIC_ERRNO ( EROFS
),
274 "%s is not on a temporary file system." ,
277 r
= id128_read_fd ( fd
, ID128_FORMAT_PLAIN
, & id
);
279 return log_error_errno ( r
, "We didn't find a valid machine ID in %s: %m" , etc_machine_id
);
283 /* Store current mount namespace */
284 r
= namespace_open ( 0 ,
285 /* ret_pidns_fd = */ NULL
,
287 /* ret_netns_fd = */ NULL
,
288 /* ret_userns_fd = */ NULL
,
289 /* ret_root_fd = */ NULL
);
291 return log_error_errno ( r
, "Can't fetch current mount namespace: %m" );
293 /* Switch to a new mount namespace, isolate ourself and unmount etc_machine_id in our new namespace */
294 r
= detach_mount_namespace ();
296 return log_error_errno ( r
, "Failed to set up new mount namespace: %m" );
298 r
= umount_verbose ( LOG_ERR
, etc_machine_id
, 0 );
302 /* Update a persistent version of etc_machine_id */
303 r
= id128_write ( etc_machine_id
, ID128_FORMAT_PLAIN
| ID128_SYNC_ON_WRITE
, id
);
305 return log_error_errno ( r
, "Cannot write %s. This is mandatory to get a persistent machine ID: %m" , etc_machine_id
);
307 /* Return to initial namespace and proceed a lazy tmpfs unmount */
308 r
= namespace_enter ( /* pidns_fd = */ - EBADF
,
310 /* netns_fd = */ - EBADF
,
311 /* userns_fd = */ - EBADF
,
312 /* root_fd = */ - EBADF
);
314 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
);
316 if ( umount2 ( etc_machine_id
, MNT_DETACH
) < 0 )
317 return log_warning_errno ( errno
, "Failed to unmount transient %s file: %m. \n We keep that mount until next reboot." , etc_machine_id
);