]>
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/>.
22 #include <sys/mount.h>
27 #include "alloc-util.h"
30 #include "id128-util.h"
32 #include "machine-id-setup.h"
35 #include "mount-util.h"
36 #include "path-util.h"
37 #include "process-util.h"
38 #include "stat-util.h"
39 #include "string-util.h"
40 #include "umask-util.h"
44 static int generate_machine_id ( const char * root
, sd_id128_t
* ret
) {
45 const char * dbus_machine_id
;
46 _cleanup_close_
int fd
= - 1 ;
51 /* First, try reading the D-Bus machine id, unless it is a symlink */
52 dbus_machine_id
= prefix_roota ( root
, "/var/lib/dbus/machine-id" );
53 fd
= open ( dbus_machine_id
, O_RDONLY
| O_CLOEXEC
| O_NOCTTY
| O_NOFOLLOW
);
55 if ( id128_read_fd ( fd
, ID128_PLAIN
, ret
) >= 0 ) {
56 log_info ( "Initializing machine ID from D-Bus machine ID." );
64 /* If that didn't work, see if we are running in a container,
65 * and a machine ID was passed in via $container_uuid the way
66 * libvirt/LXC does it */
68 if ( detect_container () > 0 ) {
69 _cleanup_free_
char * e
= NULL
;
71 if ( getenv_for_pid ( 1 , "container_uuid" , & e
) > 0 &&
72 sd_id128_from_string ( e
, ret
) >= 0 ) {
73 log_info ( "Initializing machine ID from container UUID." );
77 } else if ( detect_vm () == VIRTUALIZATION_KVM
) {
79 /* If we are not running in a container, see if we are
80 * running in qemu/kvm and a machine ID was passed in
81 * via -uuid on the qemu/kvm command line */
83 if ( id128_read ( "/sys/class/dmi/id/product_uuid" , ID128_UUID
, ret
) >= 0 ) {
84 log_info ( "Initializing machine ID from KVM UUID." );
90 /* If that didn't work, generate a random machine id */
91 r
= sd_id128_randomize ( ret
);
93 return log_error_errno ( r
, "Failed to generate randomized : %m" );
95 log_info ( "Initializing machine ID from random generator." );
99 int machine_id_setup ( const char * root
, sd_id128_t machine_id
, sd_id128_t
* ret
) {
100 const char * etc_machine_id
, * run_machine_id
;
101 _cleanup_close_
int fd
= - 1 ;
105 etc_machine_id
= prefix_roota ( root
, "/etc/machine-id" );
107 RUN_WITH_UMASK ( 0000 ) {
108 /* We create this 0444, to indicate that this isn't really
109 * something you should ever modify. Of course, since the file
110 * will be owned by root it doesn't matter much, but maybe
113 ( void ) mkdir_parents ( etc_machine_id
, 0755 );
114 fd
= open ( etc_machine_id
, O_RDWR
| O_CREAT
| O_CLOEXEC
| O_NOCTTY
, 0444 );
116 int old_errno
= errno
;
118 fd
= open ( etc_machine_id
, O_RDONLY
| O_CLOEXEC
| O_NOCTTY
);
120 if ( old_errno
== EROFS
&& errno
== ENOENT
)
121 log_error_errno ( errno
,
122 "System cannot boot: Missing /etc/machine-id and /etc is mounted read-only. \n "
123 "Booting up is supported only when: \n "
124 "1) /etc/machine-id exists and is populated. \n "
125 "2) /etc/machine-id exists and is empty. \n "
126 "3) /etc/machine-id is missing and /etc is writable. \n " );
128 log_error_errno ( errno
, "Cannot open %s: %m" , etc_machine_id
);
138 /* A we got a valid machine ID argument, that's what counts */
139 if ( sd_id128_is_null ( machine_id
)) {
141 /* Try to read any existing machine ID */
142 if ( id128_read_fd ( fd
, ID128_PLAIN
, ret
) >= 0 )
145 /* Hmm, so, the id currently stored is not useful, then let's generate one */
146 r
= generate_machine_id ( root
, & machine_id
);
150 if ( lseek ( fd
, 0 , SEEK_SET
) == ( off_t
) - 1 )
151 return log_error_errno ( errno
, "Failed to seek: %m" );
155 if ( id128_write_fd ( fd
, ID128_PLAIN
, machine_id
, true ) >= 0 )
160 /* Hmm, we couldn't write it? So let's write it to /run/machine-id as a replacement */
162 run_machine_id
= prefix_roota ( root
, "/run/machine-id" );
165 r
= id128_write ( run_machine_id
, ID128_PLAIN
, machine_id
, false );
167 ( void ) unlink ( run_machine_id
);
168 return log_error_errno ( r
, "Cannot write %s: %m" , run_machine_id
);
171 /* And now, let's mount it over */
172 if ( mount ( run_machine_id
, etc_machine_id
, NULL
, MS_BIND
, NULL
) < 0 ) {
173 ( void ) unlink_noerrno ( run_machine_id
);
174 return log_error_errno ( errno
, "Failed to mount %s: %m" , etc_machine_id
);
177 log_info ( "Installed transient %s file." , etc_machine_id
);
179 /* Mark the mount read-only */
180 if ( mount ( NULL
, etc_machine_id
, NULL
, MS_BIND
| MS_RDONLY
| MS_REMOUNT
, NULL
) < 0 )
181 log_warning_errno ( errno
, "Failed to make transient %s read-only, ignoring: %m" , etc_machine_id
);
190 int machine_id_commit ( const char * root
) {
191 _cleanup_close_
int fd
= - 1 , initial_mntns_fd
= - 1 ;
192 const char * etc_machine_id
;
196 /* Replaces a tmpfs bind mount of /etc/machine-id by a proper file, atomically. For this, the umount is removed
197 * in a mount namespace, a new file is created at the right place. Afterwards the mount is also removed in the
198 * original mount namespace, thus revealing the file that was just created. */
200 etc_machine_id
= prefix_roota ( root
, "/etc/machine-id" );
202 r
= path_is_mount_point ( etc_machine_id
, NULL
, 0 );
204 return log_error_errno ( r
, "Failed to determine whether %s is a mount point: %m" , etc_machine_id
);
206 log_debug ( "%s is not a mount point. Nothing to do." , etc_machine_id
);
210 /* Read existing machine-id */
211 fd
= open ( etc_machine_id
, O_RDONLY
| O_CLOEXEC
| O_NOCTTY
);
213 return log_error_errno ( errno
, "Cannot open %s: %m" , etc_machine_id
);
215 r
= fd_is_temporary_fs ( fd
);
217 return log_error_errno ( r
, "Failed to determine whether %s is on a temporary file system: %m" , etc_machine_id
);
219 log_error ( "%s is not on a temporary file system." , etc_machine_id
);
223 r
= id128_read_fd ( fd
, ID128_PLAIN
, & id
);
225 return log_error_errno ( r
, "We didn't find a valid machine ID in %s." , etc_machine_id
);
229 /* Store current mount namespace */
230 r
= namespace_open ( 0 , NULL
, & initial_mntns_fd
, NULL
, NULL
, NULL
);
232 return log_error_errno ( r
, "Can't fetch current mount namespace: %m" );
234 /* Switch to a new mount namespace, isolate ourself and unmount etc_machine_id in our new namespace */
235 if ( unshare ( CLONE_NEWNS
) < 0 )
236 return log_error_errno ( errno
, "Failed to enter new namespace: %m" );
238 if ( mount ( NULL
, "/" , NULL
, MS_SLAVE
| MS_REC
, NULL
) < 0 )
239 return log_error_errno ( errno
, "Couldn't make-rslave / mountpoint in our private namespace: %m" );
241 if ( umount ( etc_machine_id
) < 0 )
242 return log_error_errno ( errno
, "Failed to unmount transient %s file in our private namespace: %m" , etc_machine_id
);
244 /* Update a persistent version of etc_machine_id */
245 r
= id128_write ( etc_machine_id
, ID128_PLAIN
, id
, true );
247 return log_error_errno ( r
, "Cannot write %s. This is mandatory to get a persistent machine ID: %m" , etc_machine_id
);
249 /* Return to initial namespace and proceed a lazy tmpfs unmount */
250 r
= namespace_enter (- 1 , initial_mntns_fd
, - 1 , - 1 , - 1 );
252 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
);
254 if ( umount2 ( etc_machine_id
, MNT_DETACH
) < 0 )
255 return log_warning_errno ( errno
, "Failed to unmount transient %s file: %m. \n We keep that mount until next reboot." , etc_machine_id
);