···77 freebsd = ["i686-freebsd" "x86_64-freebsd"];
88 openbsd = ["i686-openbsd" "x86_64-openbsd"];
99 netbsd = ["i686-netbsd" "x86_64-netbsd"];
1010- cygwin = ["i686-cygwin"];
1010+ cygwin = ["i686-cygwin" "x86_64-cygwin"];
1111 unix = linux ++ darwin ++ freebsd ++ openbsd;
1212 all = linux ++ darwin ++ cygwin ++ freebsd ++ openbsd;
1313 none = [];
+1-7
nixos/doc/manual/configuration/user-mgmt.xml
···13131414<programlisting>
1515users.extraUsers.alice =
1616- { createHome = true;
1616+ { isNormalUser = true;
1717 home = "/home/alice";
1818 description = "Alice Foobar";
1919 extraGroups = [ "wheel" "networkmanager" ];
2020- useDefaultShell = true;
2120 openssh.authorizedKeys.keys = [ "ssh-dss AAAAB3Nza... alice@foobar" ];
2221 };
2322</programlisting>
···57565857As with users, the group ID (gid) is optional and will be assigned
5958automatically if it’s missing.</para>
6060-6161-<warning><para>Currently declarative user management is not perfect:
6262-<command>nixos-rebuild</command> does not know how to realise certain
6363-configuration changes. This includes removing a user or group, and
6464-removing group membership from a user.</para></warning>
65596660<para>In the imperative style, users and groups are managed by
6761commands such as <command>useradd</command>,
+239
nixos/modules/config/update-users-groups.pl
···11+use strict;
22+use File::Path qw(make_path);
33+use File::Slurp;
44+use JSON;
55+66+make_path("/var/lib/nixos", { mode => 0755 });
77+88+99+# Functions for allocating free GIDs/UIDs. FIXME: respect ID ranges in
1010+# /etc/login.defs.
1111+sub allocId {
1212+ my ($used, $idMin, $idMax, $up, $getid) = @_;
1313+ my $id = $up ? $idMin : $idMax;
1414+ while ($id >= $idMin && $id <= $idMax) {
1515+ if (!$used->{$id} && !defined &$getid($id)) {
1616+ $used->{$id} = 1;
1717+ return $id;
1818+ }
1919+ $used->{$id} = 1;
2020+ if ($up) { $id++; } else { $id--; }
2121+ }
2222+ die "$0: out of free UIDs or GIDs\n";
2323+}
2424+2525+my (%gidsUsed, %uidsUsed);
2626+2727+sub allocGid {
2828+ return allocId(\%gidsUsed, 400, 499, 0, sub { my ($gid) = @_; getgrgid($gid) });
2929+}
3030+3131+sub allocUid {
3232+ my ($isSystemUser) = @_;
3333+ my ($min, $max, $up) = $isSystemUser ? (400, 499, 0) : (1000, 29999, 1);
3434+ return allocId(\%uidsUsed, $min, $max, $up, sub { my ($uid) = @_; getpwuid($uid) });
3535+}
3636+3737+3838+# Read the declared users/groups.
3939+my $spec = decode_json(read_file($ARGV[0]));
4040+4141+# Don't allocate UIDs/GIDs that are already in use.
4242+foreach my $g (@{$spec->{groups}}) {
4343+ $gidsUsed{$g->{gid}} = 1 if defined $g->{gid};
4444+}
4545+4646+foreach my $u (@{$spec->{groups}}) {
4747+ $uidsUsed{$u->{u}} = 1 if defined $u->{uid};
4848+}
4949+5050+# Read the current /etc/group.
5151+sub parseGroup {
5252+ chomp;
5353+ my @f = split(':', $_, -4);
5454+ my $gid = $f[2] eq "" ? undef : int($f[2]);
5555+ $gidsUsed{$gid} = 1 if defined $gid;
5656+ return ($f[0], { name => $f[0], password => $f[1], gid => $gid, members => $f[3] });
5757+}
5858+5959+my %groupsCur = -f "/etc/group" ? map { parseGroup } read_file("/etc/group") : ();
6060+6161+# Read the current /etc/passwd.
6262+sub parseUser {
6363+ chomp;
6464+ my @f = split(':', $_, -7);
6565+ my $uid = $f[2] eq "" ? undef : int($f[2]);
6666+ $uidsUsed{$uid} = 1 if defined $uid;
6767+ return ($f[0], { name => $f[0], fakePassword => $f[1], uid => $uid,
6868+ gid => $f[3], description => $f[4], home => $f[5], shell => $f[6] });
6969+}
7070+7171+my %usersCur = -f "/etc/passwd" ? map { parseUser } read_file("/etc/passwd") : ();
7272+7373+# Read the groups that were created declaratively (i.e. not by groups)
7474+# in the past. These must be removed if they are no longer in the
7575+# current spec.
7676+my $declGroupsFile = "/var/lib/nixos/declarative-groups";
7777+my %declGroups;
7878+$declGroups{$_} = 1 foreach split / /, -e $declGroupsFile ? read_file($declGroupsFile) : "";
7979+8080+# Idem for the users.
8181+my $declUsersFile = "/var/lib/nixos/declarative-users";
8282+my %declUsers;
8383+$declUsers{$_} = 1 foreach split / /, -e $declUsersFile ? read_file($declUsersFile) : "";
8484+8585+8686+# Generate a new /etc/group containing the declared groups.
8787+my %groupsOut;
8888+foreach my $g (@{$spec->{groups}}) {
8989+ my $name = $g->{name};
9090+ my $existing = $groupsCur{$name};
9191+9292+ my %members = map { ($_, 1) } @{$g->{members}};
9393+9494+ if (defined $existing) {
9595+ $g->{gid} = $existing->{gid} if !defined $g->{gid};
9696+ if ($g->{gid} != $existing->{gid}) {
9797+ warn "warning: not applying GID change of group ‘$name’\n";
9898+ $g->{gid} = $existing->{gid};
9999+ }
100100+ $g->{password} = $existing->{password}; # do we want this?
101101+ if ($spec->{mutableUsers}) {
102102+ # Merge in non-declarative group members.
103103+ foreach my $uname (split /,/, $existing->{members} // "") {
104104+ $members{$uname} = 1 if !defined $declUsers{$uname};
105105+ }
106106+ }
107107+ } else {
108108+ $g->{gid} = allocGid if !defined $g->{gid};
109109+ $g->{password} = "x";
110110+ }
111111+112112+ $g->{members} = join ",", sort(keys(%members));
113113+ $groupsOut{$name} = $g;
114114+}
115115+116116+# Update the persistent list of declarative groups.
117117+write_file($declGroupsFile, join(" ", sort(keys %groupsOut)));
118118+119119+# Merge in the existing /etc/group.
120120+foreach my $name (keys %groupsCur) {
121121+ my $g = $groupsCur{$name};
122122+ next if defined $groupsOut{$name};
123123+ if (!$spec->{mutableUsers} || defined $declGroups{$name}) {
124124+ print STDERR "removing group ‘$name’\n";
125125+ } else {
126126+ $groupsOut{$name} = $g;
127127+ }
128128+}
129129+130130+131131+# Rewrite /etc/group. FIXME: acquire lock.
132132+my @lines = map { join(":", $_->{name}, $_->{password}, $_->{gid}, $_->{members}) . "\n" }
133133+ (sort { $a->{gid} <=> $b->{gid} } values(%groupsOut));
134134+write_file("/etc/group.tmp", @lines);
135135+rename("/etc/group.tmp", "/etc/group") or die;
136136+system("nscd --invalidate group");
137137+138138+# Generate a new /etc/passwd containing the declared users.
139139+my %usersOut;
140140+foreach my $u (@{$spec->{users}}) {
141141+ my $name = $u->{name};
142142+143143+ # Resolve the gid of the user.
144144+ if ($u->{group} =~ /^[0-9]$/) {
145145+ $u->{gid} = $u->{group};
146146+ } elsif (defined $groupsOut{$u->{group}}) {
147147+ $u->{gid} = $groupsOut{$u->{group}}->{gid} // die;
148148+ } else {
149149+ warn "warning: user ‘$name’ has unknown group ‘$u->{group}’\n";
150150+ $u->{gid} = 65534;
151151+ }
152152+153153+ my $existing = $usersCur{$name};
154154+ if (defined $existing) {
155155+ $u->{uid} = $existing->{uid} if !defined $u->{uid};
156156+ if ($u->{uid} != $existing->{uid}) {
157157+ warn "warning: not applying UID change of user ‘$name’\n";
158158+ $u->{uid} = $existing->{uid};
159159+ }
160160+ } else {
161161+ $u->{uid} = allocUid($u->{isSystemUser}) if !defined $u->{uid};
162162+163163+ # Create a home directory.
164164+ if ($u->{createHome}) {
165165+ make_path($u->{home}, { mode => 0700 }) if ! -e $u->{home};
166166+ chown $u->{uid}, $u->{gid}, $u->{home};
167167+ }
168168+ }
169169+170170+ if (defined $u->{passwordFile}) {
171171+ if (-e $u->{passwordFile}) {
172172+ $u->{hashedPassword} = read_file($u->{passwordFile});
173173+ chomp $u->{hashedPassword};
174174+ } else {
175175+ warn "warning: password file ‘$u->{passwordFile}’ does not exist\n";
176176+ }
177177+ }
178178+179179+ $u->{fakePassword} = $existing->{fakePassword} // "x";
180180+ $usersOut{$name} = $u;
181181+}
182182+183183+# Update the persistent list of declarative users.
184184+write_file($declUsersFile, join(" ", sort(keys %usersOut)));
185185+186186+# Merge in the existing /etc/passwd.
187187+foreach my $name (keys %usersCur) {
188188+ my $u = $usersCur{$name};
189189+ next if defined $usersOut{$name};
190190+ if (!$spec->{mutableUsers} || defined $declUsers{$name}) {
191191+ print STDERR "removing user ‘$name’\n";
192192+ } else {
193193+ $usersOut{$name} = $u;
194194+ }
195195+}
196196+197197+# Rewrite /etc/passwd. FIXME: acquire lock.
198198+@lines = map { join(":", $_->{name}, $_->{fakePassword}, $_->{uid}, $_->{gid}, $_->{description}, $_->{home}, $_->{shell}) . "\n" }
199199+ (sort { $a->{uid} <=> $b->{uid} } (values %usersOut));
200200+write_file("/etc/passwd.tmp", @lines);
201201+rename("/etc/passwd.tmp", "/etc/passwd") or die;
202202+system("nscd --invalidate passwd");
203203+204204+205205+# Rewrite /etc/shadow to add new accounts or remove dead ones.
206206+my @shadowNew;
207207+my %shadowSeen;
208208+209209+foreach my $line (-f "/etc/shadow" ? read_file("/etc/shadow") : ()) {
210210+ chomp $line;
211211+ my ($name, $password, @rest) = split(':', $line, -9);
212212+ my $u = $usersOut{$name};;
213213+ next if !defined $u;
214214+ $password = $u->{hashedPassword} if defined $u->{hashedPassword} && !$spec->{mutableUsers}; # FIXME
215215+ push @shadowNew, join(":", $name, $password, @rest) . "\n";
216216+ $shadowSeen{$name} = 1;
217217+}
218218+219219+foreach my $u (values %usersOut) {
220220+ next if defined $shadowSeen{$u->{name}};
221221+ my $password = "!";
222222+ $password = $u->{hashedPassword} if defined $u->{hashedPassword};
223223+ # FIXME: set correct value for sp_lstchg.
224224+ push @shadowNew, join(":", $u->{name}, $password, "1::::::") . "\n";
225225+}
226226+227227+write_file("/etc/shadow.tmp", { perms => 0600 }, @shadowNew);
228228+rename("/etc/shadow.tmp", "/etc/shadow") or die;
229229+230230+231231+# Call chpasswd to apply password. FIXME: generate the hashes directly
232232+# and merge into the /etc/shadow updating above.
233233+foreach my $u (@{$spec->{users}}) {
234234+ if (defined $u->{password}) {
235235+ my $pid = open(PW, "| chpasswd") or die;
236236+ print PW "$u->{name}:$u->{password}\n";
237237+ close PW or die "unable to change password of user ‘$u->{name}’: $?\n";
238238+ }
239239+}
+57-178
nixos/modules/config/users-groups.nix
···77 ids = config.ids;
88 cfg = config.users;
991010- nonUidUsers = filterAttrs (n: u: u.createUser && u.uid == null) cfg.extraUsers;
1111- nonGidGroups = filterAttrs (n: g: g.gid == null) cfg.extraGroups;
1212-1310 passwordDescription = ''
1411 The options <literal>hashedPassword</literal>,
1512 <literal>password</literal> and <literal>passwordFile</literal>
···5552 type = with types; nullOr int;
5653 default = null;
5754 description = ''
5858- The account UID. If the <option>mutableUsers</option> option
5959- is false, the UID cannot be null. Otherwise, the UID might be
6060- null, in which case a free UID is picked on activation (by the
6161- useradd command).
5555+ The account UID. If the UID is null, a free UID is picked on
5656+ activation.
6257 '';
6358 };
6459···6762 default = false;
6863 description = ''
6964 Indicates if the user is a system user or not. This option
7070- only has an effect if <option>mutableUsers</option> is
7171- <literal>true</literal> and <option>uid</option> is
6565+ only has an effect if <option>uid</option> is
7266 <option>null</option>, in which case it determines whether
7367 the user's UID is allocated in the range for system users
7468 (below 500) or in the range for normal users (starting at
7569 1000).
7070+ '';
7171+ };
7272+7373+ isNormalUser = mkOption {
7474+ type = types.bool;
7575+ default = false;
7676+ description = ''
7777+ Indicates whether this is an account for a “real” user. This
7878+ automatically sets <option>group</option> to
7979+ <literal>users</literal>, <option>createHome</option> to
8080+ <literal>true</literal>, <option>home</option> to
8181+ <filename>/home/<replaceable>username</replaceable></filename>,
8282+ <option>useDefaultShell</option> to <literal>true</literal>,
8383+ and <option>isSystemUser</option> to
8484+ <literal>false</literal>.
7685 '';
7786 };
7887···182191 ${passwordDescription}
183192 '';
184193 };
185185-186186- createUser = mkOption {
187187- type = types.bool;
188188- default = true;
189189- description = ''
190190- Indicates if the user should be created automatically as a local user.
191191- Set this to false if the user for instance is an LDAP user. NixOS will
192192- then not modify any of the basic properties for the user account.
193193- '';
194194- };
195194 };
196195197197- config = {
198198- name = mkDefault name;
199199- shell = mkIf config.useDefaultShell (mkDefault cfg.defaultUserShell);
200200- };
196196+ config = mkMerge
197197+ [ { name = mkDefault name;
198198+ shell = mkIf config.useDefaultShell (mkDefault cfg.defaultUserShell);
199199+ }
200200+ (mkIf config.isNormalUser {
201201+ group = mkDefault "users";
202202+ createHome = mkDefault true;
203203+ home = mkDefault "/home/${name}";
204204+ useDefaultShell = mkDefault true;
205205+ isSystemUser = mkDefault false;
206206+ })
207207+ ];
201208202209 };
203210···217224 type = with types; nullOr int;
218225 default = null;
219226 description = ''
220220- The group GID. If the <literal>mutableUsers</literal> option
221221- is false, the GID cannot be null. Otherwise, the GID might be
222222- null, in which case a free GID is picked on activation (by the
223223- groupadd command).
227227+ The group GID. If the GID is null, a free GID is picked on
228228+ activation.
224229 '';
225230 };
226231···271276 };
272277 };
273278274274- getGroup = gname:
275275- let
276276- groups = mapAttrsToList (n: g: g) (
277277- filterAttrs (n: g: g.name == gname) cfg.extraGroups
278278- );
279279- in
280280- if length groups == 1 then head groups
281281- else if groups == [] then throw "Group ${gname} not defined"
282282- else throw "Group ${gname} has multiple definitions";
283283-284284- getUser = uname:
285285- let
286286- users = mapAttrsToList (n: u: u) (
287287- filterAttrs (n: u: u.name == uname) cfg.extraUsers
288288- );
289289- in
290290- if length users == 1 then head users
291291- else if users == [] then throw "User ${uname} not defined"
292292- else throw "User ${uname} has multiple definitions";
293293-294294- mkGroupEntry = gname:
295295- let
296296- g = getGroup gname;
297297- users = mapAttrsToList (n: u: u.name) (
298298- filterAttrs (n: u: elem g.name u.extraGroups) cfg.extraUsers
299299- );
300300- in concatStringsSep ":" [
301301- g.name "x" (toString g.gid)
302302- (concatStringsSep "," (users ++ (filter (u: !(elem u users)) g.members)))
303303- ];
304304-305305- mkPasswdEntry = uname: let u = getUser uname; in
306306- concatStringsSep ":" [
307307- u.name "x" (toString u.uid)
308308- (toString (getGroup u.group).gid)
309309- u.description u.home u.shell
310310- ];
311311-312312- filterNull = a: filter (x: hasAttr a x && getAttr a x != null);
313313-314314- sortOn = a: sort (as1: as2: lessThan (getAttr a as1) (getAttr a as2));
315315-316316- groupFile = pkgs.writeText "group" (
317317- concatStringsSep "\n" (map (g: mkGroupEntry g.name) (
318318- sortOn "gid" (filterNull "gid" (attrValues cfg.extraGroups))
319319- ))
320320- );
321321-322322- passwdFile = pkgs.writeText "passwd" (
323323- concatStringsSep "\n" (map (u: mkPasswdEntry u.name) (
324324- sortOn "uid" (filterNull "uid" (attrValues cfg.extraUsers))
325325- ))
326326- );
327327-328279 mkSubuidEntry = user: concatStrings (
329280 map (range: "${user.name}:${toString range.startUid}:${toString range.count}\n")
330330- user.subUidRanges);
281281+ user.subUidRanges);
331282332332- subuidFile = concatStrings (map mkSubuidEntry (
333333- sortOn "uid" (filterNull "uid" (attrValues cfg.extraUsers))));
283283+ subuidFile = concatStrings (map mkSubuidEntry (attrValues cfg.extraUsers));
334284335285 mkSubgidEntry = user: concatStrings (
336286 map (range: "${user.name}:${toString range.startGid}:${toString range.count}\n")
337287 user.subGidRanges);
338288339339- subgidFile = concatStrings (map mkSubgidEntry (
340340- sortOn "uid" (filterNull "uid" (attrValues cfg.extraUsers))));
341341-342342- # If mutableUsers is true, this script adds all users/groups defined in
343343- # users.extra{Users,Groups} to /etc/{passwd,group} iff there isn't any
344344- # existing user/group with the same name in those files.
345345- # If mutableUsers is false, the /etc/{passwd,group} files will simply be
346346- # replaced with the users/groups defined in the NixOS configuration.
347347- # The merging procedure could certainly be improved, and instead of just
348348- # keeping the lines as-is from /etc/{passwd,group} they could be combined
349349- # in some way with the generated content from the NixOS configuration.
350350- merger = src: pkgs.writeScript "merger" ''
351351- #!${pkgs.bash}/bin/bash
352352-353353- PATH=${pkgs.gawk}/bin:${pkgs.gnugrep}/bin:$PATH
354354-355355- ${if !cfg.mutableUsers
356356- then ''cp ${src} $1.tmp''
357357- else ''awk -F: '{ print "^"$1":.*" }' $1 | egrep -vf - ${src} | cat $1 - > $1.tmp''
358358- }
359359-360360- # set mtime to +1, otherwise change might go unnoticed (vipw/vigr only looks at mtime)
361361- touch -m -t $(date -d @$(($(stat -c %Y $1)+1)) +%Y%m%d%H%M.%S) $1.tmp
362362-363363- mv -f $1.tmp $1
364364- '';
289289+ subgidFile = concatStrings (map mkSubgidEntry (attrValues cfg.extraUsers));
365290366291 idsAreUnique = set: idAttr: !(fold (name: args@{ dup, acc }:
367292 let
···375300376301 uidsAreUnique = idsAreUnique (filterAttrs (n: u: u.uid != null) cfg.extraUsers) "uid";
377302 gidsAreUnique = idsAreUnique (filterAttrs (n: g: g.gid != null) cfg.extraGroups) "gid";
303303+304304+ spec = builtins.toFile "users-groups.json" (builtins.toJSON {
305305+ inherit (cfg) mutableUsers;
306306+ users = mapAttrsToList (n: u:
307307+ { inherit (u)
308308+ name uid group description home shell createHome isSystemUser
309309+ password passwordFile hashedPassword;
310310+ }) cfg.extraUsers;
311311+ groups = mapAttrsToList (n: g:
312312+ { inherit (g) name gid;
313313+ members = mapAttrsToList (n: u: u.name) (
314314+ filterAttrs (n: u: elem g.name u.extraGroups) cfg.extraUsers
315315+ );
316316+ }) cfg.extraGroups;
317317+ });
378318379319in {
380320···512452 grsecurity.gid = ids.gids.grsecurity;
513453 };
514454515515- system.activationScripts.users =
516516- let
517517- mkhomeUsers = filterAttrs (n: u: u.createHome) cfg.extraUsers;
518518- setpwUsers = filterAttrs (n: u: u.createUser) cfg.extraUsers;
519519- pwFile = u: if !(isNull u.hashedPassword)
520520- then pkgs.writeTextFile { name = "password-file"; text = u.hashedPassword; }
521521- else if !(isNull u.password)
522522- then pkgs.runCommand "password-file" { pw = u.password; } ''
523523- echo -n "$pw" | ${pkgs.mkpasswd}/bin/mkpasswd -s > $out
524524- '' else u.passwordFile;
525525- setpw = n: u: ''
526526- setpw=yes
527527- ${optionalString cfg.mutableUsers ''
528528- test "$(getent shadow '${u.name}' | cut -d: -f2)" != "x" && setpw=no
529529- ''}
530530- if [ "$setpw" == "yes" ]; then
531531- ${if !(isNull (pwFile u))
532532- then ''
533533- echo -n "${u.name}:" | cat - "${pwFile u}" | \
534534- ${pkgs.shadow}/sbin/chpasswd -e
535535- ''
536536- else "passwd -l '${u.name}' &>/dev/null"
537537- }
538538- fi
539539- '';
540540- mkhome = n: u: ''
541541- uid="$(id -u ${u.name})"
542542- gid="$(id -g ${u.name})"
543543- h="${u.home}"
544544- test -a "$h" || mkdir -p "$h" || true
545545- test "$(stat -c %u "$h")" = $uid || chown $uid "$h" || true
546546- test "$(stat -c %g "$h")" = $gid || chgrp $gid "$h" || true
547547- '';
548548- groupadd = n: g: ''
549549- if [ -z "$(getent group "${g.name}")" ]; then
550550- ${pkgs.shadow}/sbin/groupadd "${g.name}"
551551- fi
552552- '';
553553- useradd = n: u: ''
554554- if ! id "${u.name}" &>/dev/null; then
555555- ${pkgs.shadow}/sbin/useradd \
556556- -g "${u.group}" \
557557- -G "${concatStringsSep "," u.extraGroups}" \
558558- -s "${u.shell}" \
559559- -d "${u.home}" \
560560- ${optionalString u.isSystemUser "--system"} \
561561- "${u.name}"
562562- echo "${u.name}:x" | ${pkgs.shadow}/sbin/chpasswd -e
563563- fi
564564- '';
565565- in stringAfter [ "etc" ] ''
566566- touch /etc/group
567567- touch /etc/passwd
568568- VISUAL=${merger groupFile} ${pkgs.shadow}/sbin/vigr &>/dev/null
569569- VISUAL=${merger passwdFile} ${pkgs.shadow}/sbin/vipw &>/dev/null
570570- ${pkgs.shadow}/sbin/grpconv
571571- ${pkgs.shadow}/sbin/pwconv
572572- ${concatStrings (mapAttrsToList groupadd nonGidGroups)}
573573- ${concatStrings (mapAttrsToList useradd nonUidUsers)}
574574- ${concatStrings (mapAttrsToList mkhome mkhomeUsers)}
575575- ${concatStrings (mapAttrsToList setpw setpwUsers)}
455455+ system.activationScripts.users = stringAfter [ "etc" ]
456456+ ''
457457+ ${pkgs.perl}/bin/perl -w \
458458+ -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl \
459459+ -I${pkgs.perlPackages.JSON}/lib/perl5/site_perl \
460460+ ${./update-users-groups.pl} ${spec}
576461 '';
577462578463 # for backwards compatibility
···589474590475 assertions = [
591476 { assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique);
592592- message = "uids and gids must be unique!";
593593- }
594594- { assertion = cfg.mutableUsers || (nonUidUsers == {});
595595- message = "When mutableUsers is false, no uid can be null: ${toString (attrNames nonUidUsers)}";
596596- }
597597- { assertion = cfg.mutableUsers || (nonGidGroups == {});
598598- message = "When mutableUsers is false, no gid can be null";
477477+ message = "UIDs and GIDs must be unique!";
599478 }
600479 ];
601480
···177177 if [ "$PRIVATE_NETWORK" = 1 ]; then
178178 ip link del dev "ve-$INSTANCE" 2> /dev/null || true
179179 fi
180180+181181+182182+ if [ "$PRIVATE_NETWORK" = 1 ]; then
183183+ ip link del dev "ve-$INSTANCE" 2> /dev/null || true
184184+ fi
180185 '';
181186182187 script =
···240245 ip route add $LOCAL_ADDRESS dev $ifaceHost
241246 fi
242247 fi
248248+249249+ # This blocks until the container-startup-done service
250250+ # writes something to this pipe. FIXME: it also hangs
251251+ # until the start timeout expires if systemd-nspawn exits.
252252+ read x < $root/var/lib/startup-done
253253+ rm -f $root/var/lib/startup-done
243254 '';
244255245256 preStop =
···11+--- ./giscanner/utils.py.orig 2014-08-14 22:05:05.055334080 +0200
22++++ ./giscanner/utils.py 2014-08-14 22:05:24.687497334 +0200
33+@@ -110,17 +110,11 @@
44+ if dlname is None:
55+ return None
66+77+- # Darwin uses absolute paths where possible; since the libtool files never
88+- # contain absolute paths, use the libdir field
99+- if platform.system() == 'Darwin':
1010+- dlbasename = os.path.basename(dlname)
1111+- libdir = _extract_libdir_field(la_file)
1212+- if libdir is None:
1313+- return dlbasename
1414+- return libdir + '/' + dlbasename
1515+- # From the comments in extract_libtool(), older libtools had
1616+- # a path rather than the raw dlname
1717+- return os.path.basename(dlname)
1818++ dlbasename = os.path.basename(dlname)
1919++ libdir = _extract_libdir_field(la_file)
2020++ if libdir is None:
2121++ return dlbasename
2222++ return libdir + '/' + dlbasename
2323+2424+2525+ def extract_libtool(la_file):
···29293030 setupHook = ./setup-hook.sh;
31313232+ patches = [ ./absolute_shlib_path.patch ];
3333+3234 meta = with stdenv.lib; {
3335 description = "A middleware layer between C libraries and language bindings";
3436 homepage = http://live.gnome.org/GObjectIntrospection;
···55with stdenv.lib;
6677stdenv.mkDerivation rec {
88- name = "pcre-8.34";
88+ name = "pcre-8.35";
991010 src = fetchurl {
1111 url = "ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/${name}.tar.bz2";
1212- sha256 = "0gsqmsp0q0n3q0ba32gkjvgcsdy6nwidqa7sbxkbw817zzhkl15n";
1212+ sha256 = "0nw66r92dr24vy9k4lw17bkv8x5nlzn6wx9hq4y2dvzgig3w2qd9";
1313 };
14141515 # The compiler on Darwin crashes with an internal error while building the
···154154 || system == "x86_64-kfreebsd-gnu";
155155 isSunOS = system == "i686-solaris"
156156 || system == "x86_64-solaris";
157157- isCygwin = system == "i686-cygwin";
157157+ isCygwin = system == "i686-cygwin"
158158+ || system == "x86_64-cygwin";
158159 isFreeBSD = system == "i686-freebsd"
159160 || system == "x86_64-freebsd";
160161 isOpenBSD = system == "i686-openbsd"
+135-153
pkgs/stdenv/linux/default.nix
···3535 # The bootstrap process proceeds in several steps.
363637373838- # 1) Create a standard environment by downloading pre-built binaries
3939- # of coreutils, GCC, etc.
3838+ # Create a standard environment by downloading pre-built binaries of
3939+ # coreutils, GCC, etc.
404041414242 # Download and unpack the bootstrap tools (coreutils, GCC, Glibc, ...).
···4646 builder = bootstrapFiles.sh;
47474848 args =
4949- if system == "armv5tel-linux" || system == "armv6l-linux"
4949+ if system == "armv5tel-linux" || system == "armv6l-linux"
5050 || system == "armv7l-linux"
5151 then [ ./scripts/unpack-bootstrap-tools-arm.sh ]
5252 else [ ./scripts/unpack-bootstrap-tools.sh ];
···6666 };
676768686969- # This function builds the various standard environments used during
7070- # the bootstrap.
7171- stdenvBootFun =
7272- {gcc, extraAttrs ? {}, overrides ? (pkgs: {}), extraPath ? [], fetchurl}:
6969+ # A helper function to call gcc-wrapper.
7070+ wrapGCC =
7171+ { gcc, libc, binutils, coreutils, name }:
73727474- import ../generic {
7575- inherit system config;
7676- name = "stdenv-linux-boot";
7777- preHook =
7878- ''
7979- # Don't patch #!/interpreter because it leads to retained
8080- # dependencies on the bootstrapTools in the final stdenv.
8181- dontPatchShebangs=1
8282- ${commonPreHook}
8383- '';
8484- shell = "${bootstrapTools}/bin/sh";
8585- initialPath = [bootstrapTools] ++ extraPath;
8686- fetchurlBoot = fetchurl;
8787- inherit gcc;
8888- # Having the proper 'platform' in all the stdenvs allows getting proper
8989- # linuxHeaders for example.
9090- extraAttrs = extraAttrs // { inherit platform; };
9191- overrides = pkgs: (overrides pkgs) // {
9292- inherit fetchurl;
9393- };
7373+ lib.makeOverridable (import ../../build-support/gcc-wrapper) {
7474+ nativeTools = false;
7575+ nativeLibc = false;
7676+ inherit gcc binutils coreutils libc name;
7777+ stdenv = stage0.stdenv;
9478 };
95799696- # Build a dummy stdenv with no GCC or working fetchurl. This is
9797- # because we need a stdenv to build the GCC wrapper and fetchurl.
9898- stdenvLinuxBoot0 = stdenvBootFun {
9999- gcc = "/no-such-path";
100100- fetchurl = null;
101101- };
102808181+ # This function builds the various standard environments used during
8282+ # the bootstrap. In all stages, we build an stdenv and the package
8383+ # set that can be built with that stdenv.
8484+ stageFun =
8585+ {gcc, extraAttrs ? {}, overrides ? (pkgs: {}), extraPath ? []}:
10386104104- fetchurl = import ../../build-support/fetchurl {
105105- stdenv = stdenvLinuxBoot0;
106106- curl = bootstrapTools;
107107- };
8787+ let
108888989+ thisStdenv = import ../generic {
9090+ inherit system config;
9191+ name = "stdenv-linux-boot";
9292+ preHook =
9393+ ''
9494+ # Don't patch #!/interpreter because it leads to retained
9595+ # dependencies on the bootstrapTools in the final stdenv.
9696+ dontPatchShebangs=1
9797+ ${commonPreHook}
9898+ '';
9999+ shell = "${bootstrapTools}/bin/sh";
100100+ initialPath = [bootstrapTools] ++ extraPath;
101101+ fetchurlBoot = import ../../build-support/fetchurl {
102102+ stdenv = stage0.stdenv;
103103+ curl = bootstrapTools;
104104+ };
105105+ inherit gcc;
106106+ # Having the proper 'platform' in all the stdenvs allows getting proper
107107+ # linuxHeaders for example.
108108+ extraAttrs = extraAttrs // { inherit platform; };
109109+ overrides = pkgs: (overrides pkgs) // { fetchurl = thisStdenv.fetchurlBoot; };
110110+ };
109111110110- # The Glibc include directory cannot have the same prefix as the GCC
111111- # include directory, since GCC gets confused otherwise (it will
112112- # search the Glibc headers before the GCC headers). So create a
113113- # dummy Glibc.
114114- bootstrapGlibc = stdenvLinuxBoot0.mkDerivation {
115115- name = "bootstrap-glibc";
116116- buildCommand = ''
117117- mkdir -p $out
118118- ln -s ${bootstrapTools}/lib $out/lib
119119- ln -s ${bootstrapTools}/include-glibc $out/include
120120- '';
121121- };
112112+ thisPkgs = allPackages {
113113+ inherit system platform;
114114+ bootStdenv = thisStdenv;
115115+ };
122116117117+ in { stdenv = thisStdenv; pkgs = thisPkgs; };
123118124124- # A helper function to call gcc-wrapper.
125125- wrapGCC =
126126- { gcc ? bootstrapTools, libc, binutils, coreutils, shell ? "", name ? "bootstrap-gcc-wrapper" }:
127119128128- lib.makeOverridable (import ../../build-support/gcc-wrapper) {
129129- nativeTools = false;
130130- nativeLibc = false;
131131- inherit gcc binutils coreutils libc shell name;
132132- stdenv = stdenvLinuxBoot0;
120120+ # Build a dummy stdenv with no GCC or working fetchurl. This is
121121+ # because we need a stdenv to build the GCC wrapper and fetchurl.
122122+ stage0 = stageFun {
123123+ gcc = "/no-such-path";
124124+125125+ overrides = pkgs: {
126126+ # The Glibc include directory cannot have the same prefix as the
127127+ # GCC include directory, since GCC gets confused otherwise (it
128128+ # will search the Glibc headers before the GCC headers). So
129129+ # create a dummy Glibc here, which will be used in the stdenv of
130130+ # stage1.
131131+ glibc = stage0.stdenv.mkDerivation {
132132+ name = "bootstrap-glibc";
133133+ buildCommand = ''
134134+ mkdir -p $out
135135+ ln -s ${bootstrapTools}/lib $out/lib
136136+ ln -s ${bootstrapTools}/include-glibc $out/include
137137+ '';
138138+ };
133139 };
140140+ };
134141135142136143 # Create the first "real" standard environment. This one consists
137144 # of bootstrap tools only, and a minimal Glibc to keep the GCC
138145 # configure script happy.
139139- stdenvLinuxBoot1 = stdenvBootFun {
146146+ #
147147+ # For clarity, we only use the previous stage when specifying these
148148+ # stages. So stageN should only ever have references for stage{N-1}.
149149+ #
150150+ # If we ever need to use a package from more than one stage back, we
151151+ # simply re-export those packages in the middle stage(s) using the
152152+ # overrides attribute and the inherit syntax.
153153+ stage1 = stageFun {
140154 gcc = wrapGCC {
141141- libc = bootstrapGlibc;
155155+ gcc = bootstrapTools;
156156+ libc = stage0.pkgs.glibc;
142157 binutils = bootstrapTools;
143158 coreutils = bootstrapTools;
159159+ name = "bootstrap-gcc-wrapper";
144160 };
145145- inherit fetchurl;
146146- };
147147-148148-149149- # 2) These are the packages that we can build with the first
150150- # stdenv. We only need binutils, because recent Glibcs
151151- # require recent Binutils, and those in bootstrap-tools may
152152- # be too old.
153153- stdenvLinuxBoot1Pkgs = allPackages {
154154- inherit system platform;
155155- bootStdenv = stdenvLinuxBoot1;
161161+ # Rebuild binutils to use from stage2 onwards.
162162+ overrides = pkgs: {
163163+ binutils = pkgs.binutils.override { gold = false; };
164164+ inherit (stage0.pkgs) glibc;
165165+ };
156166 };
157167158158- binutils1 = stdenvLinuxBoot1Pkgs.binutils.override { gold = false; };
159168160160-161161- # 3) 2nd stdenv that we will use to build only Glibc.
162162- stdenvLinuxBoot2 = stdenvBootFun {
169169+ # 2nd stdenv that contains our own rebuilt binutils and is used for
170170+ # compiling our own Glibc.
171171+ stage2 = stageFun {
163172 gcc = wrapGCC {
164164- libc = bootstrapGlibc;
165165- binutils = binutils1;
173173+ gcc = bootstrapTools;
174174+ libc = stage1.pkgs.glibc;
175175+ binutils = stage1.pkgs.binutils;
166176 coreutils = bootstrapTools;
177177+ name = "bootstrap-gcc-wrapper";
167178 };
168179 overrides = pkgs: {
169169- inherit (stdenvLinuxBoot1Pkgs) perl;
180180+ inherit (stage1.pkgs) perl binutils paxctl;
181181+ # This also contains the full, dynamically linked, final Glibc.
170182 };
171171- inherit fetchurl;
172183 };
173184174185175175- # 4) These are the packages that we can build with the 2nd
176176- # stdenv.
177177- stdenvLinuxBoot2Pkgs = allPackages {
178178- inherit system platform;
179179- bootStdenv = stdenvLinuxBoot2;
180180- };
181181-182182-183183- # 5) Build Glibc with the bootstrap tools. The result is the full,
184184- # dynamically linked, final Glibc.
185185- stdenvLinuxGlibc = stdenvLinuxBoot2Pkgs.glibc;
186186-187187-188188- # 6) Construct a third stdenv identical to the 2nd, except that this
189189- # one uses the Glibc built in step 5. It still uses the recent
190190- # binutils and rest of the bootstrap tools, including GCC.
191191- stdenvLinuxBoot3 = stdenvBootFun {
186186+ # Construct a third stdenv identical to the 2nd, except that this
187187+ # one uses the rebuilt Glibc from stage2. It still uses the recent
188188+ # binutils and rest of the bootstrap tools, including GCC.
189189+ stage3 = stageFun {
192190 gcc = wrapGCC {
193193- binutils = binutils1;
191191+ gcc = bootstrapTools;
192192+ libc = stage2.pkgs.glibc;
193193+ binutils = stage2.pkgs.binutils;
194194 coreutils = bootstrapTools;
195195- libc = stdenvLinuxGlibc;
195195+ name = "bootstrap-gcc-wrapper";
196196 };
197197 overrides = pkgs: {
198198- glibc = stdenvLinuxGlibc;
199199- inherit (stdenvLinuxBoot1Pkgs) perl;
198198+ inherit (stage2.pkgs) binutils glibc perl;
200199 # Link GCC statically against GMP etc. This makes sense because
201200 # these builds of the libraries are only used by GCC, so it
202201 # reduces the size of the stdenv closure.
···208207 ppl = pkgs.ppl.override { stdenv = pkgs.makeStaticLibraries pkgs.stdenv; };
209208 };
210209 extraAttrs = {
211211- glibc = stdenvLinuxGlibc; # Required by gcc47 build
210210+ glibc = stage2.pkgs.glibc; # Required by gcc47 build
212211 };
213213- extraPath = [ stdenvLinuxBoot1Pkgs.paxctl ];
214214- inherit fetchurl;
215215- };
216216-217217-218218- # 7) The packages that can be built using the third stdenv.
219219- stdenvLinuxBoot3Pkgs = allPackages {
220220- inherit system platform;
221221- bootStdenv = stdenvLinuxBoot3;
212212+ extraPath = [ stage2.pkgs.paxctl ];
222213 };
223214224215225225- # 8) Construct a fourth stdenv identical to the second, except that
226226- # this one uses the new GCC from step 7. The other tools
227227- # (e.g. coreutils) are still from the bootstrap tools.
228228- stdenvLinuxBoot4 = stdenvBootFun {
229229- gcc = wrapGCC rec {
230230- binutils = binutils1;
216216+ # Construct a fourth stdenv that uses the new GCC. But coreutils is
217217+ # still from the bootstrap tools.
218218+ stage4 = stageFun {
219219+ gcc = wrapGCC {
220220+ gcc = stage3.pkgs.gcc.gcc;
221221+ libc = stage3.pkgs.glibc;
222222+ binutils = stage3.pkgs.binutils;
231223 coreutils = bootstrapTools;
232232- libc = stdenvLinuxGlibc;
233233- gcc = stdenvLinuxBoot3Pkgs.gcc.gcc;
234224 name = "";
235225 };
236236- extraPath = [ stdenvLinuxBoot3Pkgs.xz ];
226226+ extraPath = [ stage3.pkgs.xz ];
237227 overrides = pkgs: {
238238- inherit (stdenvLinuxBoot1Pkgs) perl;
239239- inherit (stdenvLinuxBoot3Pkgs) gettext gnum4 gmp;
228228+ # Zlib has to be inherited and not rebuilt in this stage,
229229+ # because gcc (since JAR support) already depends on zlib, and
230230+ # then if we already have a zlib we want to use that for the
231231+ # other purposes (binutils and top-level pkgs) too.
232232+ inherit (stage3.pkgs) gettext gnum4 gmp perl glibc zlib;
240233 };
241241- inherit fetchurl;
242234 };
243235244236245245- # 9) The packages that can be built using the fourth stdenv.
246246- stdenvLinuxBoot4Pkgs = allPackages {
247247- inherit system platform;
248248- bootStdenv = stdenvLinuxBoot4;
249249- };
250250-251251-252252- # 10) Construct the final stdenv. It uses the Glibc and GCC, and
253253- # adds in a new binutils that doesn't depend on bootstrap-tools,
254254- # as well as dynamically linked versions of all other tools.
237237+ # Construct the final stdenv. It uses the Glibc and GCC, and adds
238238+ # in a new binutils that doesn't depend on bootstrap-tools, as well
239239+ # as dynamically linked versions of all other tools.
255240 #
256256- # When updating stdenvLinux, make sure that the result has no
257257- # dependency (`nix-store -qR') on bootstrapTools or the
258258- # first binutils built.
241241+ # When updating stdenvLinux, make sure that the result has no
242242+ # dependency (`nix-store -qR') on bootstrapTools or the first
243243+ # binutils built.
259244 stdenvLinux = import ../generic rec {
260245 inherit system config;
261246···268253 '';
269254270255 initialPath =
271271- ((import ../common-path.nix) {pkgs = stdenvLinuxBoot4Pkgs;})
272272- ++ [stdenvLinuxBoot4Pkgs.patchelf stdenvLinuxBoot4Pkgs.paxctl ];
256256+ ((import ../common-path.nix) {pkgs = stage4.pkgs;})
257257+ ++ [stage4.pkgs.patchelf stage4.pkgs.paxctl ];
273258274274- gcc = wrapGCC rec {
275275- inherit (stdenvLinuxBoot4Pkgs) binutils coreutils;
276276- libc = stdenvLinuxGlibc;
277277- gcc = stdenvLinuxBoot4.gcc.gcc;
278278- shell = stdenvLinuxBoot4Pkgs.bash + "/bin/bash";
279279- name = "";
280280- };
259259+ shell = stage4.pkgs.bash + "/bin/bash";
281260282282- shell = stdenvLinuxBoot4Pkgs.bash + "/bin/bash";
261261+ gcc = (wrapGCC rec {
262262+ gcc = stage4.stdenv.gcc.gcc;
263263+ libc = stage4.pkgs.glibc;
264264+ inherit (stage4.pkgs) binutils coreutils;
265265+ name = "";
266266+ }).override { inherit shell; };
283267284284- fetchurlBoot = fetchurl;
268268+ inherit (stage4.stdenv) fetchurlBoot;
285269286270 extraAttrs = {
287287- inherit (stdenvLinuxBoot3Pkgs) glibc;
271271+ inherit (stage4.pkgs) glibc;
288272 inherit platform bootstrapTools;
289289- shellPackage = stdenvLinuxBoot4Pkgs.bash;
273273+ shellPackage = stage4.pkgs.bash;
290274 };
291275292276 overrides = pkgs: {
293277 inherit gcc;
294294- inherit (stdenvLinuxBoot3Pkgs) glibc;
295295- inherit (stdenvLinuxBoot4Pkgs) binutils;
296296- inherit (stdenvLinuxBoot4Pkgs)
297297- gzip bzip2 xz bash coreutils diffutils findutils gawk
298298- gnumake gnused gnutar gnugrep gnupatch patchelf
299299- attr acl paxctl;
278278+ inherit (stage4.pkgs)
279279+ gzip bzip2 xz bash binutils coreutils diffutils findutils gawk
280280+ glibc gnumake gnused gnutar gnugrep gnupatch patchelf
281281+ attr acl paxctl zlib;
300282 };
301283 };
302284
+3
pkgs/tools/compression/xz/default.nix
···10101111 doCheck = true;
12121313+ # In stdenv-linux, prevent a dependency on bootstrap-tools.
1414+ preHook = "unset CONFIG_SHELL";
1515+1316 meta = {
1417 homepage = http://tukaani.org/xz/;
1518 description = "XZ, general-purpose data compression software, successor of LZMA";