11
11
import warnings
12
12
import ctypes
13
13
libc = ctypes .CDLL ("libc.so.6" )
14
+ import re
14
15
15
16
def copy_if_not_exists (source , dest ):
16
17
if not os .path .exists (dest ):
17
18
shutil .copyfile (source , dest )
18
19
19
- def system_dir (generation ):
20
- return "/nix/var/nix/profiles/system-%d-link" % (generation )
20
+ def system_dir (profile , generation ):
21
+ if profile :
22
+ return "/nix/var/nix/profiles/system-profiles/%s-%d-link" % (profile , generation )
23
+ else :
24
+ return "/nix/var/nix/profiles/system-%d-link" % (generation )
21
25
22
- BOOT_ENTRY = """title NixOS
26
+ BOOT_ENTRY = """title NixOS{profile}
23
27
version Generation {generation}
24
28
linux {kernel}
25
29
initrd {initrd}
26
30
options {kernel_params}
27
31
"""
28
32
29
- def write_loader_conf (generation ):
33
+ def write_loader_conf (profile , generation ):
30
34
with open ("@efiSysMountPoint@/loader/loader.conf.tmp" , 'w' ) as f :
31
35
if "@timeout@" != "" :
32
36
f .write ("timeout @timeout@\n " )
33
- f .write ("default nixos-generation-%d\n " % generation )
37
+ if profile :
38
+ f .write ("default nixos-%s-generation-%d\n " % (profile , generation ))
39
+ else :
40
+ f .write ("default nixos-generation-%d\n " % (generation ))
34
41
if not @editor @:
35
42
f .write ("editor 0" );
36
43
os .rename ("@efiSysMountPoint@/loader/loader.conf.tmp" , "@efiSysMountPoint@/loader/loader.conf" )
37
44
38
- def profile_path (generation , name ):
39
- return os .readlink ("%s/%s" % (system_dir (generation ), name ))
45
+ def profile_path (profile , generation , name ):
46
+ return os .readlink ("%s/%s" % (system_dir (profile , generation ), name ))
40
47
41
- def copy_from_profile (generation , name , dry_run = False ):
42
- store_file_path = profile_path (generation , name )
48
+ def copy_from_profile (profile , generation , name , dry_run = False ):
49
+ store_file_path = profile_path (profile , generation , name )
43
50
suffix = os .path .basename (store_file_path )
44
51
store_dir = os .path .basename (os .path .dirname (store_file_path ))
45
52
efi_file_path = "/efi/nixos/%s-%s.efi" % (store_dir , suffix )
46
53
if not dry_run :
47
54
copy_if_not_exists (store_file_path , "@efiSysMountPoint@%s" % (efi_file_path ))
48
55
return efi_file_path
49
56
50
- def write_entry (generation , machine_id ):
51
- kernel = copy_from_profile (generation , "kernel" )
52
- initrd = copy_from_profile (generation , "initrd" )
57
+ def write_entry (profile , generation , machine_id ):
58
+ kernel = copy_from_profile (profile , generation , "kernel" )
59
+ initrd = copy_from_profile (profile , generation , "initrd" )
53
60
try :
54
- append_initrd_secrets = profile_path (generation , "append-initrd-secrets" )
61
+ append_initrd_secrets = profile_path (profile , generation , "append-initrd-secrets" )
55
62
subprocess .check_call ([append_initrd_secrets , "@efiSysMountPoint@%s" % (initrd )])
56
63
except FileNotFoundError :
57
64
pass
58
- entry_file = "@efiSysMountPoint@/loader/entries/nixos-generation-%d.conf" % (generation )
59
- generation_dir = os .readlink (system_dir (generation ))
65
+ if profile :
66
+ entry_file = "@efiSysMountPoint@/loader/entries/nixos-%s-generation-%d.conf" % (profile , generation )
67
+ else :
68
+ entry_file = "@efiSysMountPoint@/loader/entries/nixos-generation-%d.conf" % (generation )
69
+ generation_dir = os .readlink (system_dir (profile , generation ))
60
70
tmp_path = "%s.tmp" % (entry_file )
61
71
kernel_params = "systemConfig=%s init=%s/init " % (generation_dir , generation_dir )
62
72
with open ("%s/kernel-params" % (generation_dir )) as params_file :
63
73
kernel_params = kernel_params + params_file .read ()
64
74
with open (tmp_path , 'w' ) as f :
65
- f .write (BOOT_ENTRY .format (generation = generation ,
75
+ f .write (BOOT_ENTRY .format (profile = " [" + profile + "]" if profile else "" ,
76
+ generation = generation ,
66
77
kernel = kernel ,
67
78
initrd = initrd ,
68
79
kernel_params = kernel_params ))
@@ -77,36 +88,48 @@ def mkdir_p(path):
77
88
if e .errno != errno .EEXIST or not os .path .isdir (path ):
78
89
raise
79
90
80
- def get_generations (profile ):
91
+ def get_generations (profile = None ):
81
92
gen_list = subprocess .check_output ([
82
93
"@nix@/bin/nix-env" ,
83
94
"--list-generations" ,
84
95
"-p" ,
85
- "/nix/var/nix/profiles/%s" % (profile ),
96
+ "/nix/var/nix/profiles/%s" % ("system-profiles/" + profile if profile else "system" ),
86
97
"--option" , "build-users-group" , "" ],
87
98
universal_newlines = True )
88
99
gen_lines = gen_list .split ('\n ' )
89
100
gen_lines .pop ()
90
- return [ int (line .split ()[0 ]) for line in gen_lines ]
101
+ return [ ( profile , int (line .split ()[0 ]) ) for line in gen_lines ]
91
102
92
103
def remove_old_entries (gens ):
93
- slice_start = len ( " @efiSysMountPoint@/loader/entries/nixos-generation-" )
94
- slice_end = - 1 * len ( ". conf" )
104
+ rex_profile = re . compile ( "^ @efiSysMountPoint@/loader/entries/nixos-(.*)- generation-.*\.conf$ " )
105
+ rex_generation = re . compile ( "^@efiSysMountPoint@/loader/entries/nixos.*-generation-(.*)\. conf$ " )
95
106
known_paths = []
96
107
for gen in gens :
97
- known_paths .append (copy_from_profile (gen , "kernel" , True ))
98
- known_paths .append (copy_from_profile (gen , "initrd" , True ))
99
- for path in glob .iglob ("@efiSysMountPoint@/loader/entries/nixos-generation-[1-9]*.conf" ):
108
+ known_paths .append (copy_from_profile (* gen , "kernel" , True ))
109
+ known_paths .append (copy_from_profile (* gen , "initrd" , True ))
110
+ for path in glob .iglob ("@efiSysMountPoint@/loader/entries/nixos* -generation-[1-9]*.conf" ):
100
111
try :
101
- gen = int (path [slice_start :slice_end ])
102
- if not gen in gens :
112
+ if rex_profile .match (path ):
113
+ prof = rex_profile .sub (r"\1" , path )
114
+ else :
115
+ prof = "system"
116
+ gen = int (rex_generation .sub (r"\1" , path ))
117
+ if not (prof , gen ) in gens :
103
118
os .unlink (path )
104
119
except ValueError :
105
120
pass
106
121
for path in glob .iglob ("@efiSysMountPoint@/efi/nixos/*" ):
107
122
if not path in known_paths :
108
123
os .unlink (path )
109
124
125
+ def get_profiles ():
126
+ if os .path .isdir ("/nix/var/nix/profiles/system-profiles/" ):
127
+ return [x
128
+ for x in os .listdir ("/nix/var/nix/profiles/system-profiles/" )
129
+ if not x .endswith ("-link" )]
130
+ else :
131
+ return []
132
+
110
133
def main ():
111
134
parser = argparse .ArgumentParser (description = 'Update NixOS-related systemd-boot files' )
112
135
parser .add_argument ('default_config' , metavar = 'DEFAULT-CONFIG' , help = 'The default NixOS config to boot' )
@@ -141,12 +164,14 @@ def main():
141
164
mkdir_p ("@efiSysMountPoint@/efi/nixos" )
142
165
mkdir_p ("@efiSysMountPoint@/loader/entries" )
143
166
144
- gens = get_generations ("system" )
167
+ gens = get_generations ()
168
+ for profile in get_profiles ():
169
+ gens += get_generations (profile )
145
170
remove_old_entries (gens )
146
171
for gen in gens :
147
- write_entry (gen , machine_id )
148
- if os .readlink (system_dir (gen )) == args .default_config :
149
- write_loader_conf (gen )
172
+ write_entry (* gen , machine_id )
173
+ if os .readlink (system_dir (* gen )) == args .default_config :
174
+ write_loader_conf (* gen )
150
175
151
176
# Since fat32 provides little recovery facilities after a crash,
152
177
# it can leave the system in an unbootable state, when a crash/outage
0 commit comments