X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/c20c4066311341e332dc425023f856b6414de9ae..11550f95937c51d49dced7dba17b940be2500386:/ikiwiki-mass-rebuild

diff --git a/ikiwiki-mass-rebuild b/ikiwiki-mass-rebuild
index dac54cb8e..f13033e7f 100755
--- a/ikiwiki-mass-rebuild
+++ b/ikiwiki-mass-rebuild
@@ -1,32 +1,115 @@
-#!/bin/sh
-set -e
+#!/usr/bin/perl
+use warnings;
+use strict;
 
-action=""
-if [ -n "$1" ]; then
-	action="$1"
-fi
+sub supplemental_groups {
+	my $user=shift;
 
-wikilist=/etc/ikiwiki/wikilist
+	my @list;
+	while (my @fields=getgrent()) {
+		if (grep { $_ eq $user } split(' ', $fields[3])) {
+			push @list, $fields[2];
+		}
+	}
 
-processline () {
-	user="$1"
-	setup="$2"
-	
-	if [ -z "$user" ] || [ -z "$setup" ]; then
-		echo "parse failure in /etc/ikiwiki/wikilist, line: '$user $setup'" >&2
-		exit 1
-	fi
+	return @list;
+}
+
+sub samelists {
+	my %a=map { $_ => 1 } split(' ', shift());
+	my %b=map { $_ => 1 } split(' ', shift());
+
+	foreach my $i (keys %b) {
+		if (! exists $a{$i}) {
+			return 0;
+		}
+	}
+	foreach my $i (keys %a) {
+		if (! exists $b{$i}) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+sub processline {
+	my $user=shift;
+	my $setup=shift;
 	
-	if [ ! -f "$setup" ]; then
-		echo "warning: $setup specified in /etc/ikiwiki/wikilist does not exist, skipping" >&2
-	else
-		echo "Processing $setup as user $user ..."
-		su "$user" -c "ikiwiki -setup $setup $action"
-	fi
+	if (! getpwnam("$user")) {
+		print STDERR "warning: user $user does not exist\n";
+		return
+	}
+	if (! -f "$setup") {
+		print STDERR "warning: $setup does not exist, skipping\n";
+		return;
+	}
+	print "Processing $setup as user $user ...\n";
+	# su is not used because it passes arguments through the shell,
+	# which is not safe for untrusted setup file names.
+	defined(my $pid = fork) or die "Can’t fork: $!";
+	if (! $pid) {
+		my ($uuid, $ugid) = (getpwnam($user))[2, 3];
+		my $grouplist=join(" ", $ugid, sort {$a <=> $b} $ugid, supplemental_groups($user));
+		if (! samelists(($)=$grouplist), $grouplist)) {
+			die "failed to set egid $grouplist (got back $))";
+		}
+		$(=$ugid;
+		$<=$uuid;
+		$>=$uuid;
+		if ($< != $uuid || $> != $uuid || $( != $ugid) {
+			die "failed to drop permissions to $user";
+		}
+		%ENV=(
+			PATH => $ENV{PATH},
+			HOME => (getpwnam($user))[7],
+		);
+		exec("ikiwiki", "-setup", $setup, @ARGV);
+		die "failed to run ikiwiki: $!";
+	}
+	waitpid($pid,0);
+	if ($?) {
+		print STDERR "Processing $setup as user $user failed with code $?\n";
+	}
+}
+
+sub processlist {
+	my $file=shift;
+	my $forceuser=shift;
+
+	my $list;
+	open ($list, "<$file") || die "$file: $!";
+	while (<$list>) {
+		chomp;
+		s/^\s+//;
+		s/\s+$//;
+		next if /^#/ || ! length;
+
+		if (/^([^\s]+)\s+([^\s]+)$/) {
+			my $user=$1;
+			my $setup=$2;
+			if (defined $forceuser && $forceuser ne $user) {
+				print STDERR "warning: in $file line $., attempt to set user to $user, but user forced to $forceuser. Skipping\n";
+			}
+			processline($user, $setup);
+		}
+		elsif (/^([^\s]+)$/) {
+			my $user=$1;
+			my $home=(getpwnam($user))[7];
+			if (defined $home && -d $home) {
+				my $dotfile="$home/.ikiwiki/wikilist";
+				if (-e $dotfile) {
+					processlist($dotfile, $user);
+				}
+			}
+		}
+	}
+	close $list;
+}
+
+my $wikilist="/etc/ikiwiki/wikilist";
+
+if (-e $wikilist) {
+	processlist($wikilist);
 }
 
-if [ -e "$wikilist" ]; then
-	grep -v '^#' $wikilist | grep -v '^$' | while read line; do 
-		processline $line
-	done
-fi