python312Packages.aiohomeconnect: 0.10.0 -> 0.11.0 (#374011)
[NixPkgs.git] / nixos / modules / system / etc / setup-etc.pl
blobea0a383081721ae6b7a9e97b0cd679b4dc4f60a0
1 use strict;
2 use File::Find;
3 use File::Copy;
4 use File::Path;
5 use File::Basename;
6 use File::Slurp;
8 my $etc = $ARGV[0] or die;
9 my $static = "/etc/static";
11 sub atomicSymlink {
12 my ($source, $target) = @_;
13 my $tmp = "$target.tmp";
14 unlink $tmp;
15 symlink $source, $tmp or return 0;
16 if (rename $tmp, $target) {
17 return 1;
18 } else {
19 unlink $tmp;
20 return 0;
25 # Atomically update /etc/static to point at the etc files of the
26 # current configuration.
27 atomicSymlink $etc, $static or die;
29 # Returns 1 if the argument points to the files in /etc/static. That
30 # means either argument is a symlink to a file in /etc/static or a
31 # directory with all children being static.
32 sub isStatic {
33 my $path = shift;
35 if (-l $path) {
36 my $target = readlink $path;
37 return substr($target, 0, length "/etc/static/") eq "/etc/static/";
40 if (-d $path) {
41 opendir DIR, "$path" or return 0;
42 my @names = readdir DIR or die;
43 closedir DIR;
45 foreach my $name (@names) {
46 next if $name eq "." || $name eq "..";
47 unless (isStatic("$path/$name")) {
48 return 0;
51 return 1;
54 return 0;
57 # Remove dangling symlinks that point to /etc/static. These are
58 # configuration files that existed in a previous configuration but not
59 # in the current one. For efficiency, don't look under /etc/nixos
60 # (where all the NixOS sources live).
61 sub cleanup {
62 if ($File::Find::name eq "/etc/nixos") {
63 $File::Find::prune = 1;
64 return;
66 if (-l $_) {
67 my $target = readlink $_;
68 if (substr($target, 0, length $static) eq $static) {
69 my $x = "/etc/static/" . substr($File::Find::name, length "/etc/");
70 unless (-l $x) {
71 print STDERR "removing obsolete symlink ‘$File::Find::name’...\n";
72 unlink "$_";
78 find(\&cleanup, "/etc");
81 # Use /etc/.clean to keep track of copied files.
82 my @oldCopied = read_file("/etc/.clean", chomp => 1, err_mode => 'quiet');
83 open CLEAN, ">>/etc/.clean";
86 # For every file in the etc tree, create a corresponding symlink in
87 # /etc to /etc/static. The indirection through /etc/static is to make
88 # switching to a new configuration somewhat more atomic.
89 my %created;
90 my @copied;
92 sub link {
93 my $fn = substr $File::Find::name, length($etc) + 1 or next;
95 # nixos-enter sets up /etc/resolv.conf as a bind mount, so skip it.
96 if ($fn eq "resolv.conf" and $ENV{'IN_NIXOS_ENTER'}) {
97 return;
100 my $target = "/etc/$fn";
101 File::Path::make_path(dirname $target);
102 $created{$fn} = 1;
104 # Rename doesn't work if target is directory.
105 if (-l $_ && -d $target) {
106 if (isStatic $target) {
107 rmtree $target or warn;
108 } else {
109 warn "$target directory contains user files. Symlinking may fail.";
113 if (-e "$_.mode") {
114 my $mode = read_file("$_.mode"); chomp $mode;
115 if ($mode eq "direct-symlink") {
116 atomicSymlink readlink("$static/$fn"), $target or warn "could not create symlink $target";
117 } else {
118 my $uid = read_file("$_.uid"); chomp $uid;
119 my $gid = read_file("$_.gid"); chomp $gid;
120 copy "$static/$fn", "$target.tmp" or warn;
121 $uid = getpwnam $uid unless $uid =~ /^\+/;
122 $gid = getgrnam $gid unless $gid =~ /^\+/;
123 chown int($uid), int($gid), "$target.tmp" or warn;
124 chmod oct($mode), "$target.tmp" or warn;
125 unless (rename "$target.tmp", $target) {
126 warn "could not create target $target";
127 unlink "$target.tmp";
130 push @copied, $fn;
131 print CLEAN "$fn\n";
132 } elsif (-l "$_") {
133 atomicSymlink "$static/$fn", $target or warn "could not create symlink $target";
137 find(\&link, $etc);
140 # Delete files that were copied in a previous version but not in the
141 # current.
142 foreach my $fn (@oldCopied) {
143 if (!defined $created{$fn}) {
144 $fn = "/etc/$fn";
145 print STDERR "removing obsolete file ‘$fn’...\n";
146 unlink "$fn";
151 # Rewrite /etc/.clean.
152 close CLEAN;
153 write_file("/etc/.clean", map { "$_\n" } sort @copied);
155 # Create /etc/NIXOS tag if not exists.
156 # When /etc is not on a persistent filesystem, it will be wiped after reboot,
157 # so we need to check and re-create it during activation.
158 open TAG, ">>/etc/NIXOS";
159 close TAG;