···
use File::Path qw(make_path);
6
-
make_path("/var/lib/nixos", { mode => 0755 });
# Keep track of deleted uids and gids.
my $uidMapFile = "/var/lib/nixos/uid-map";
···
my $gidMapFile = "/var/lib/nixos/gid-map";
my $gidMap = -e $gidMapFile ? decode_json(read_file($gidMapFile)) : {};
15
+
my $is_dry = ($ENV{'NIXOS_ACTION'} // "") eq "dry-activate";
16
+
GetOptions("dry-activate" => \$is_dry);
17
+
make_path("/var/lib/nixos", { mode => 0755 }) unless $is_dry;
my ($path, $contents, $perms) = @_;
write_file($path, { atomic => 1, binmode => ':utf8', perms => $perms // 0644 }, $contents) or die;
25
+
sub nscdInvalidate {
26
+
system("nscd", "--invalidate", $_[0]) unless $is_dry;
···
my @chars = ('.', '/', 0..9, 'A'..'Z', 'a'..'z');
$salt .= $chars[rand 64] for (1..8);
return crypt($password, '$6$' . $salt . '$');
39
+
print STDERR ("$_[1] $_[2]\n")
41
+
print STDERR ("$_[0] $_[2]\n")
···
my $prevGid = $gidMap->{$name};
if (defined $prevGid && !defined $gidsUsed{$prevGid}) {
54
-
print STDERR "reviving group '$name' with GID $prevGid\n";
68
+
dry_print("reviving", "would revive", "group '$name' with GID $prevGid");
···
my ($min, $max, $up) = $isSystemUser ? (400, 999, 0) : (1000, 29999, 1);
my $prevUid = $uidMap->{$name};
if (defined $prevUid && $prevUid >= $min && $prevUid <= $max && !defined $uidsUsed{$prevUid}) {
66
-
print STDERR "reviving user '$name' with UID $prevUid\n";
80
+
dry_print("reviving", "would revive", "user '$name' with UID $prevUid");
return allocId(\%uidsUsed, \%uidsPrevUsed, $min, $max, $up, sub { my ($uid) = @_; getpwuid($uid) });
74
-
# Read the declared users/groups.
87
+
# Read the declared users/groups
my $spec = decode_json(read_file($ARGV[0]));
# Don't allocate UIDs/GIDs that are manually assigned.
···
$g->{gid} = $existing->{gid} if !defined $g->{gid};
if ($g->{gid} != $existing->{gid}) {
137
-
warn "warning: not applying GID change of group ‘$name’ ($existing->{gid} -> $g->{gid})\n";
150
+
dry_print("warning: not applying", "warning: would not apply", "GID change of group ‘$name’ ($existing->{gid} -> $g->{gid})");
$g->{gid} = $existing->{gid};
$g->{password} = $existing->{password}; # do we want this?
···
my $g = $groupsCur{$name};
next if defined $groupsOut{$name};
if (!$spec->{mutableUsers} || defined $declGroups{$name}) {
166
-
print STDERR "removing group ‘$name’\n";
179
+
dry_print("removing group", "would remove group", "‘$name’");
···
(sort { $a->{gid} <=> $b->{gid} } values(%groupsOut));
updateFile($gidMapFile, to_json($gidMap));
updateFile("/etc/group", \@lines);
178
-
system("nscd --invalidate group");
191
+
nscdInvalidate("group");
# Generate a new /etc/passwd containing the declared users.
···
$u->{uid} = $existing->{uid} if !defined $u->{uid};
if ($u->{uid} != $existing->{uid}) {
199
-
warn "warning: not applying UID change of user ‘$name’ ($existing->{uid} -> $u->{uid})\n";
212
+
dry_print("warning: not applying", "warning: would not apply", "UID change of user ‘$name’ ($existing->{uid} -> $u->{uid})");
$u->{uid} = $existing->{uid};
···
# Ensure home directory incl. ownership and permissions.
214
-
make_path($u->{home}, { mode => 0700 }) if ! -e $u->{home};
227
+
make_path($u->{home}, { mode => 0700 }) if ! -e $u->{home} and ! $is_dry;
chown $u->{uid}, $u->{gid}, $u->{home};
···
my $u = $usersCur{$name};
next if defined $usersOut{$name};
if (!$spec->{mutableUsers} || defined $declUsers{$name}) {
253
-
print STDERR "removing user ‘$name’\n";
266
+
dry_print("removing user", "would remove user", "‘$name’");
···
(sort { $a->{uid} <=> $b->{uid} } (values %usersOut));
updateFile($uidMapFile, to_json($uidMap));
updateFile("/etc/passwd", \@lines);
264
-
system("nscd --invalidate passwd");
277
+
nscdInvalidate("passwd");
# Rewrite /etc/shadow to add new accounts or remove dead ones.
···
my $uid = getpwnam "root";
my $gid = getgrnam "shadow";
my $path = "/etc/shadow";
296
-
chown($uid, $gid, $path) || die "Failed to change ownership of $path: $!";
309
+
(chown($uid, $gid, $path) || die "Failed to change ownership of $path: $!") unless $is_dry;
# Rewrite /etc/subuid & /etc/subgid to include default container mappings