chiark-utils-4.4.2build1/0000775000000000000000000000000012423201731012104 5ustar chiark-utils-4.4.2build1/sync-accounts/0000775000000000000000000000000012423200672014700 5ustar chiark-utils-4.4.2build1/sync-accounts/sync-accounts0000775000000000000000000004526512423200672017433 0ustar #!/usr/bin/perl # This is part of sync-accounts, a tool for synchronising UN*X password data. # # sync-accounts is # Copyright 1999-2000,2002 Ian Jackson # Copyright 2000-2001 nCipher Corporation Ltd # # sync-accounts is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 3, or (at # your option) any later version. # # sync-accounts is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should already have a copy of the GNU General Public License. # If not, consult the Free Software Foundation's website at # www.fsf.org, or the GNU Project website at www.gnu.org. # # $Id: sync-accounts,v 1.25 2007-09-21 21:21:15 ianmdlvl Exp $ use POSIX; $configfile= '/etc/sync-accounts'; $def_createuser= 'sync-accounts-createuser'; $ch_homebase= '/home'; $ch_defaultshell= '/bin/sh'; $defaultgid= -1; # -1 => usergroups; -2 => nousergroups @groupglobs= [ '.*', 0 ]; regroupglobs(); $file{'passwd','std'}= 'passwd'; $file{'shadow','std'}= 'shadow'; $file{'group','std'}= 'group'; $file{'passwd','bsd'}= 'master.passwd'; $file{'shadow','bsd'}= 'shadow-non-existent'; $file{'group','bsd'}= 'group'; @fields_pw_std= qw(USER PW UID GID COMMENT HOME SHELL); @fields_pw_bsd= qw(USER PW UID GID CLASS CHANGE EXPIRE COMMENT HOME SHELL); fields_fmt('PW','std'); fields('GR',qw(GROUP PW GID USERS)); fields('SP',qw(USER PW DAYSCHGD DAYSFIX DAYSEXP DAYSWARN DAYSEXPDIS DAYSDISD RESERVED)); # The name field had better always be field 0 ! END { foreach $x (@unlocks) { ($fn, $style, $arg) = @$x; &{ "unlock_$style" } ( $fn,$arg ); } } sub fields { my ($pfx,@l) = @_; my ($i, $v, $vn); foreach $v (@l) { $vn= "${pfx}_$v"; $$vn = $i++; } $vn= "${pfx}_fields"; $$vn= $i; } sub fields_fmt ($$) { my ($pfx,$fmt) = @_; my ($vn); $vn= "fields_pw_$fmt"; die "unknown format $fmt\n" unless defined @$vn; fields($pfx,@$vn); $vn= "${pfx}_format"; $$vn= $fmt; } sub newentry { my ($pfx,$name,@field_val_list) = @_; my (@rv, $vn, $fn, $v, $i); @rv= (); $vn= "${pfx}_fields"; for ($i=0; $i<$$vn; $i++) { $rv[$i]= ''; } die "@field_val_list ?" if @field_val_list % 2; $rv[0] = $name; while (@field_val_list) { ($fn,$v,@field_val_list) = @field_val_list; $vn= "${pfx}_$fn"; #print STDERR ">$fn|$v|$vn|$$vn<\n"; $rv[$$vn]= $v; } return @rv; } $|=1; $cdays= int(time/86400); @envs_createuser= qw(USER UID GID COMMENT HOME SHELL); if (@ARGV == 1 && length $ENV{'SYNC_ACCOUNTS_RUNVIA_INFO'}) { @na= map { s/\%([0-9a-f][0-9a-f])/ pack("C", hex $1) /ge; $_; } split(/\:/, $ENV{'SYNC_ACCOUNTS_RUNVIA_INFO'}); delete $ENV{'SYNC_ACCOUNTS_RUNVIA_INFO'}; $ta= shift @na; $ENV{"SYNC_ACCOUNTS_RUNVIA_LOCKEDGOT_$ta"} = $ARGV[0]; @ARGV= @na; } @orgargv= @ARGV; while ($ARGV[0] =~ m/^-/) { $_= shift @ARGV; last if m/^--$/; if (m/^-C/) { $configfile= $'; } elsif (m/^-n$/) { $no_act= 1; $display= 0; } elsif (m/^-q$/) { $no_act= 1; $display= 1; } else { die "unknown option $_\n"; } } die "hosts must not be specified with -q\n" if @ARGV && $display; for $h (@ARGV) { $wanthost{$h}= 1; } open CF,"< $configfile" or die "$configfile: $!"; sub fetchfile (\%$) { my ($ary_ref,$get_str) = @_; undef %$ary_ref; open G,"$get_str" or die "$get_str: $!"; while () { chomp; m/^([^:]+)\:/ or die "$ch_name: $get_str:$.: $_ ?\n"; $ary_ref->{$1}= [ split(/\:/,$_,-1) ]; } close G; $? and die "$ch_name: $get_str: exit code $?\n"; } sub lockstyle_ ($$) { my ($fn,$lock) = @_; die "$configfile:$.: locking mechanism for $fn not". " defined (use lockpasswd/lockgroup)\n"; } sub abslock (@) { my ($fn,$lock) = @_; $fn= "/etc/$fn"; $lock= "$fn$lock" unless $lock =~ m,^/,; return ($fn,$lock); } sub lock_none ($$) { return (abslock(@_))[0]; } sub unlock_none ($$) { } sub lock_link ($$) { my ($fn,$lock) = abslock(@_); link $fn,$lock or die "cannot lock $fn by creating $lock: $!\n"; return $fn; } sub unlock_link ($$) { my ($fn,$lock) = abslock(@_); unlink $lock or warn "unable to unlock by removing $lock: $!\n"; } sub lock_runvia ($$) { my ($fn,$lock) = @_; my ($evn); $evn= "SYNC_ACCOUNTS_RUNVIA_LOCKEDGOT_$fn"; return $ENV{$evn} if exists $ENV{$evn}; @na= map { s/\W/ sprintf("%%%02x", unpack("C", $&)) /ge; $_; } ($fn,@orgargv); $ENV{'SYNC_ACCOUNTS_RUNVIA_INFO'}= join(":", @na); $ENV{'EDITOR'}= $0; delete $ENV{'VISUAL'}; exec $lock; die "cannot lock $fn by executing $lock: $!\n"; } sub unlock_runvia ($$) { } sub fetchownfile (\@$$$$) { my ($ary_ref,$fn_str,$nfields,$style,$lock_arg) = @_; my ($fn_use, $record, $fn_emsg); $fn_emsg= $fn_str; if (!$no_act) { $fn_use= &{ "lock_$style" } ($fn_str, $lock_arg); push @unlocks, [ $fn_str, $style, $lock_arg ]; $savebackto{$fn_str}= $fn_use; } else { $fn_use= $fn_emsg= "/etc/".$file{$fn_str,$PW_format}; } open O,"$fn_use" or die "$fn_use ($fn_str): $!"; while () { chomp; if (m/^\#/ || !m/\S/) { $record= $_; } else { $record= [ split(/\:/,$_,-1) ]; die "$fn_emsg:$.:wrong number of fields:\`$_'\n" unless @$record == $nfields; } push @$ary_ref, $record; } close O or die "$fn_use ($fn_str): $!"; } sub diag ($) { print "$diagstr: $_[0]\n" or die $!; } sub regroupglobs () { $nogroups= (@groupglobs == 1 && $groupglobs[0]->[0] eq '.*' && !$groupglobs[0]->[1]); $ggfunc= "sub wantsyncgroup {\n \$_= \$_[0];\n return\n"; for $g (@groupglobs) { $ggfunc.= " m/^$g->[0]\$/ ? $g->[1] :\n"; } $ggfunc.= " die;\n};\n1;\n"; #print STDERR "$ggfunc\n"; must_eval($ggfunc); } sub fetchown () { if (!$own_fetchedpasswd) { #print STDERR ">$PW_fields<\n"; fetchownfile(@ownpasswd,'passwd', $PW_fields, $ch_lockstyle_passwd, $ch_lock_passwd); $shadowfile= $file{'shadow',$PW_format}; if (stat("/etc/$shadowfile")) { $own_haveshadow= 1; $own_fetchedshadow= 1; fetchownfile(@ownshadow,'shadow',$SP_fields,'none',''); } elsif ($! == &ENOENT) { $own_haveshadow= 0; } else { die "unable to check for /etc/$shadowfile: $!\n"; } $own_fetchedpasswd= 1; } if (!$own_fetchedgroup) { fetchownfile(@owngroup,'group',$GR_fields, $ch_lockstyle_group, $ch_lock_group); $own_fetchedgroup= 1; } #print STDERR "fetchown() -> $#ownpasswd $#owngroup\n"; } sub checkuid ($$) { my ($useuid,$foruser) = @_; for $e (@ownpasswd) { next unless ref $e; if ($e->[$PW_USER] ne $foruser && $e->[$PW_UID] == $useuid) { diag("uid clash with $e->[$PW_USER] (uid $e->[$PW_UID])"); return 0; } } return 1; } sub must_eval ($) { eval $_[0] or die "$_[0] // $@"; } sub copyfield ($$$$) { my ($file,$entry,$field,$value) = @_; must_eval("\$ary_ref= \\\@own$file; 1;"); #print STDERR "copyfield($file,$entry,$field,$value)\n"; for $e (@$ary_ref) { #print STDERR "copyfield($file,$entry,$field,$value) $e->[0] $e->[field] ".join(':',@$e)."\n"; next unless $e->[0] eq $entry; next if $e->[$field] eq $value; $e->[$field]= $value; must_eval("\$modified$file= 1; 1;"); } } sub fetchpasswd () { return if $ch_fetchedpasswd; die "$configfile:$.: getpasswd not specified for host $ch_name\n" unless length $ch_getpasswd; undef %remshadow; fetchfile(%rempasswd,"$ch_getpasswd |"); if (length $ch_getshadow) { fetchfile(%remshadow,"$ch_getshadow |"); for $k (keys %rempasswd) { $rempasswd{$k}->[$REM_PW]= 'xx' unless length $rempasswd{$k}->[$REM_PW]; } for $k (keys %remshadow) { next unless exists $rempasswd{$k}; $rempasswd{$k}->[$REM_PW]= $remshadow{$k}->[$SP_PW]; } } $ch_fetchedpasswd= 1; } sub fetchgroup () { return if $ch_fetchedgroup; die "$configfile:$.: getgroup not specified for host $ch_name\n" unless length $ch_getgroup; fetchfile(%remgroup,"$ch_getgroup |"); $ch_fetchedgroup= 1; } sub syncusergroup ($$) { my ($lu,$luid) = @_; return 1 if $defaultgid != -1; #print STDERR "syncusergroup($lu,$luid)\n"; $ugfound=0; for $e (@owngroup) { next unless ref $e; $samename= $e->[$GR_GROUP] eq $lu; $sameid= $e->[$GR_GID] eq $luid; next unless $samename || $sameid; if (!$samename || !$sameid) { diag("local group $e->[$GR_GROUP] ($e->[$GR_GID]) mismatch vs.". " local user $lu ($luid)"); return 0; } if ($ugfound) { diag("per-user group $lu ($luid) duplicated"); return 0; } $ugfound=1; } return 1 if $ugfound; if (!length $opt_createuser) { diag("account creation not enabled, not creating per-user group"); return 0; } push @owngroup, [ newentry('GR', $lu, 'PW', 'x', 'GID', $luid) ]; $modifiedgroup= 1; return 1; } sub hosthead ($) { my ($th) = @_; return if $hostheaddone eq $th; print "\n\n" or die $! if length $hostheaddone; print "==== $th ====\n" or die $!; $hostheaddone= $th; } sub syncuser ($$) { my ($lu,$ru) = @_; my ($vn); #print STDERR "syncuser($lu,$ru)\n"; return if $doneuser{$lu}++; next unless $ch_doinghost; return if !length $ru; fetchown(); if ($display) { for $e (@ownpasswd) { next unless ref $e && $e->[$PW_USER] eq $lu; hosthead("from $ch_name"); print ($lu eq $ru ? " $lu" : " $lu($ru)") or die $!; print "" if $displaydone{$lu}++; } return; } $diagstr= "user $lu from $ch_name!$ru"; #print STDERR "syncuser($lu,$ru) doing\n"; fetchpasswd(); if (!$rempasswd{$ru}) { diag("no remote entry"); return; } if (length $ch_getshadow && exists $remshadow{$ru} && length $remshadow{$ru}->[$SP_DAYSDISD]) { diag("remote account disabled in shadow"); return; } if (!grep(ref $_ && $_->[$PW_USER] eq $lu, @ownpasswd)) { if (!length $opt_createuser) { diag("account creation not enabled"); return; } if ($no_act) { diag("-n specified; not creating account"); return; } if ($opt_sameuid) { $useuid= $rempasswd{$ru}->[$REM_UID]; $usegid= $rempasswd{$ru}->[$REM_GID]; } else { die "nousergroups specified, cannot create users\n" if $defaultgid==-2; length $ch_uidmin or die "no uidmin specified, cannot create users\n"; length $ch_uidmax or die "no uidmax specified, cannot create users\n"; $ch_uidmin<$ch_uidmax or die "uidmin>=uidmax, cannot create users\n"; $useuid= $ch_uidmin; for $e ($defaultgid==-1 ? (@ownpasswd, @owngroup) : (@ownpasswd)) { next unless ref $e; $tuid= $e->[$PW_UID]; next if $tuid<$useuid || $tuid>$ch_uidmax; if ($tuid==$ch_uidmax) { diag("uid (or gid?) $ch_uidmax used, cannot create users"); return; } $useuid= $tuid+1; } $usegid= $defaultgid==-1 ? $useuid : $defaultgid; } @newpwent= newentry('PW', $lu, 'PW', 'x', 'UID', $useuid, 'GID', $usegid, 'COMMENT', $rempasswd{$ru}->[$REM_COMMENT], 'HOME', "$ch_homebase/$lu", 'SHELL', $ch_defaultshell); defined($c= open CU,"-|") or die $!; if (!$c) { @unlocks= (); defined($c2= open STDIN,"-|") or die $!; if (!$c2) { print STDOUT join(':',@newpwent),"\n" or die $!; exit 0; } for ($i=0; $i<@envs_createuser; $i++) { $vn= "PW_$envs_createuser[$i]"; #print STDERR ">$i|$vn|$$vn|$newpwent[$$vn]<\n"; $ENV{"SYNCUSER_CREATE_$envs_createuser[$i]"}= $newpwent[$$vn]; } exec $opt_createuser; die "$configfile:$.: ($lu): $opt_createuser: $!\n"; } $newpwent= ; close CU; $? and die "$configfile:$.: ($lu): $opt_createuser: code $?\n"; chomp $newpwent; if (length $newpwent) { if ($newpwent !~ m/\:/) { diag("creation script demurred"); return; } @newpwent= split(/\:/,$newpwent,-1); } die "$opt_createuser: bad result: \`".join(':',@newpwent)."\'\n" if @newpwent != $PW_fields or $newpwent[$PW_USER] ne $lu; checkuid($newpwent[$PW_UID],$lu) or return; if ($own_haveshadow) { push @ownshadow, [ newentry('SP', $lu, 'PW', 'x', 'DAYSCHGD', $cdays, 'DAYSFIX', 0, 'DAYSEXP', 99999, 'DAYSEXPDIS', 7) ]; $modifiedshadow= 1; } syncusergroup($lu,$newpwent[$PW_UID]) or return; push @ownpasswd,[ @newpwent ]; $modifiedpasswd= 1; } for $e (@ownpasswd) { next unless ref $e && $e->[$PW_USER] eq $lu; syncusergroup($lu,$e->[$PW_UID]) or return; } $ruid= $rempasswd{$ru}->[$REM_UID]; $rgid= $rempasswd{$ru}->[$REM_GID]; if ($opt_sameuid && checkuid($ruid,$lu)) { for $e (@ownpasswd) { next unless ref $e && $e->[$PW_USER] eq $lu; $luid= $e->[$PW_UID]; $lgid= $e->[$PW_GID]; die "$diagstr: local uid $luid, remote uid $ruid\n" if $luid ne $ruid; die "$diagstr: local gid $lgid, remote gid $rgid\n" if $lgid ne $rgid; } } #print STDERR "syncuser($lu,$ru) exists $own_haveshadow\n"; if ($own_haveshadow && grep(ref $_ && $_->[$PW_USER] eq $lu, @ownshadow)) { #print STDERR "syncuser($lu,$ru) shadow $rempasswd{$ru}->[$REM_PW]\n"; copyfield('shadow',$lu,$SP_PW, $rempasswd{$ru}->[$REM_PW]); } else { #print STDERR "syncuser($lu,$ru) passwd $rempasswd{$ru}->[$REM_PW]\n"; copyfield('passwd',$lu,$PW_PW, $rempasswd{$ru}->[$REM_PW]); } copyfield('passwd',$lu,$PW_COMMENT, $rempasswd{$ru}->[$REM_COMMENT]); $newsh= $rempasswd{$ru}->[$REM_SHELL]; $oksh= $checkedshell{$newsh}; if (!length $oksh) { $checkedshell{$newsh}= $oksh= (-x $newsh) ? 1 : 0; } copyfield('passwd',$lu,$PW_SHELL, $newsh) if $oksh; if (!$nogroups) { for $e (@owngroup) { next unless ref $e; $tgroup= $e->[$GR_GROUP]; #print STDERR "syncuser($lu,$ru) group $tgroup\n"; next unless &wantsyncgroup($tgroup); #print STDERR "syncuser($lu,$ru) group $tgroup yes\n"; fetchgroup(); if (!exists $remgroup{$tgroup}) { diag("group $tgroup: not on remote host"); next; } $inremote= grep($_ eq $ru, split(/\,/,$remgroup{$tgroup}->[$GR_USERS])); $cusers= $e->[$GR_USERS]; $inlocal= grep($_ eq $lu, split(/\,/,$cusers)); if ($inremote && !$inlocal) { $cusers.= ',' if length $cusers; $cusers.= $lu; } elsif ($inlocal && !$inremote) { $cusers= join(',', grep($_ ne $lu, split(/\,/, $cusers))); } else { next; } $e->[$GR_USERS]= $cusers; $modifiedgroup= 1; } } } sub banner () { return if $bannerdone; print "\n" or die $!; system 'date'; $? and die $?; $bannerdone= 1; } sub finish () { my ($record); for $h (keys %wanthost) { die "host $h not in config file\n" if $wanthost{$h}; } if ($display) { #print STDERR "\n\nfinish display=$display pw=$pw\n\n"; for $e (@ownpasswd) { next unless ref $e; $tu= $e->[$PW_USER]; $tuid= $e->[$PW_UID]; next if $displaydone{$tu}; $tpw= $e->[$PW_PW]; #print STDERR ">$tu|$tpw<\n"; for $e2 (@ownshadow) { next unless ref $e2 && $e2->[$SP_USER] eq $tu; $tpw= $e2->[$SP_PW]; last; } $tpw= length($tpw)>=13 ? 1 : length($tpw) ? -1 : 0; $head= ($tpw == 1 ? "unsynched" : $tpw == 0 ? "unsynched, passwordless" : "unsynched, no-logins"). ($tuid < 100 ? " system account" : " normal user"); $unsynch_type{$head} .= " $e->[$PW_USER]"; } foreach $head (sort keys %unsynch_type) { hosthead($head); print $unsynch_type{$head} or die $!; } print "\n\n" or die $!; exit 0; } umask 077; for $file (qw(passwd shadow group)) { $realfile= $file{$file,$PW_format}; must_eval("\$modified= \$modified$file; \$data_ref= \\\@own$file;". " \$fetched= \$own_fetched$file; 1;"); next if !$modified; die $file unless $fetched; banner(); if ($no_act) { $newfileinst= "/etc/$realfile"; $newfile= "$realfile.new"; } else { $newfileinst= $savebackto{$file}; $newfile= "$newfileinst.new"; } open NF,"> $newfile" or die "$newfile: $!"; for $e (@$data_ref) { $record= ref $e ? join(':',@$e) : $e; print NF $record,"\n" or die $!; } close NF or die $!; system 'diff','-U0','--label',$realfile,$newfileinst, '--label',"$realfile.new",$newfile; $?==256 or die $?; if (!$no_act) { (@stats= stat $newfileinst) or die "$file: $!"; chown $stats[4],$stats[5], $newfile or die $!; chmod $stats[2] & 07777, $newfile or die $!; rename $newfile, $newfileinst or die $!; } } exit 0; } while () { chomp; s/^\s*//; s/\s*$//; next if m/^\#/ || !m/\S/; finish() if m/^end$/; if (m/^host\s+(\S+)$/) { $ch_name= $1; $ch_getpasswd= $ch_getgroup= $ch_getshadow= ''; $ch_fetchedpasswd= $ch_fetchedgroup; if (!@ARGV) { $ch_doinghost= 1; } elsif (exists $wanthost{$ch_name}) { $wanthost{$ch_name}= 0; $ch_doinghost= 1; } else { $ch_doinghost= 0; } fields_fmt('REM','std'); } elsif (m/^(getpasswd|getshadow|getgroup)\s+(.*\S)$/) { must_eval("\$ch_$1= \$2; 1;"); } elsif (m/^(local|remote)format\s+(\w+)$/) { fields_fmt($1 eq 'local' ? 'PW' : $1 eq 'remote' ? 'REM' : die, $2); } elsif (m/^lock(passwd|group)\s+(runvia|link)\s+(\S+)$/) { must_eval("\$ch_lock_$1= \$3; \$ch_lockstyle_$1= \$2; 1;"); } elsif (m/^lock(passwd|group)\s+(none)$/) { must_eval("\$ch_lockstyle_$1= \$2; 1;"); } elsif (m,^(homebase|defaultshell)\s+(/\S+)$,) { must_eval("\$ch_$1= \$2; 1;"); } elsif (m/^(uidmin|uidmax)\s+(\d+)$/ && $2>0) { must_eval("\$ch_$1= \$2; 1;"); } elsif (m/^createuser$/) { $opt_createuser= $def_createuser; } elsif (m/^nocreateuser$/) { $opt_createuser= ''; } elsif (m/^createuser\s+(\S+)$/) { $opt_createuser= $1; } elsif (m/^logfile\s+(.*\S)$/) { if (!$no_act) { open STDOUT,">> $1" or die "$1: $!"; $|=1; $!=0; system 'date'; $? and die "date: $! $?\n"; } elsif (!$display) { print "would log to $1\n" or die $!; } } elsif (m/^(no|)(sameuid)$/) { must_eval("\$opt_$2= ".($1 eq 'no' ? 0 : 1)."; 1;"); } elsif (m/^usergroups$/) { $defaultgid= -1; } elsif (m/^nousergroups$/) { $defaultgid= -2; } elsif (m/^defaultgid\s+(\d+)$/) { $defaultgid= $1; } elsif (m/^(no|)group\s+([-+.0-9a-zA-Z*?]+)$/) { $yes= $1 eq 'no' ? 0 : 1; $_= $2; @groupglobs=() if $_ eq '*'; s/[-+._]/\\$&/g; s/\*/\.\*/g; s/\?/\./g; unshift @groupglobs, [ $_, $yes ]; regroupglobs(); } elsif (m/^user\s+(\S+)$/) { syncuser($1,$1); } elsif (m/^user\s+(\S+)\s+remote\=(\S+)$/) { syncuser($1,$2); } elsif (m/^nouser\s+(\S+)$/) { syncuser($1,''); } elsif (m/^users\s+(\d+)\-(\d+)$/) { $tmin= $1; $tmax= $2; $except= $3; fetchpasswd(); for $k (keys %rempasswd) { $tuid= $rempasswd{$k}->[2]; next if $tuid<$1 or $tuid>$2; syncuser($k,$k); } } elsif (m/^addhere$/) { } else { die "$configfile:$.: unknown directive\n"; } } die "$configfile:$.: missing \`end', or read error\n"; chiark-utils-4.4.2build1/sync-accounts/Makefile0000664000000000000000000000401712423200672016342 0ustar # Makefile # This file is part of chiark-utils, a collection of useful programs # used on chiark.greenend.org.uk. # # This file is: # Copyright 2001-2002 Ian Jackson # # This is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3, or (at your option) any later version. # # This is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. SYSTEM_GROUP= root INSTALL= install -c INSTALL_SHARE= $(INSTALL) -m 644 -o root -g $(SYSTEM_GROUP) INSTALL_SCRIPT= $(INSTALL) -m 755 -o root -g $(SYSTEM_GROUP) INSTALL_DIRECTORY= $(INSTALL) -m 2755 -o root -g $(SYSTEM_GROUP) -d prefix=/usr/local bindir=$(prefix)/bin mandir=${prefix}/man man5dir=${mandir}/man5 man8dir=${mandir}/man8 txtdocdir=$(prefix)/share/doc/sync-accounts exampledir=$(txtdocdir)/examples SCRIPTS= sync-accounts sync-accounts-createuser grab-account MANPAGES5= sync-accounts MANPAGES8= sync-accounts sync-accounts-createuser grab-account EXAMPLES= linux bsd all: install: $(INSTALL_DIRECTORY) $(bindir) set -e; for f in $(SCRIPTS); do \ $(INSTALL_SCRIPT) $$f $(bindir)/$$f; done install-docs: $(INSTALL_DIRECTORY) $(man5dir) $(man8dir) set -e; for f in $(MANPAGES5); do \ $(INSTALL_SHARE) $$f.5 $(man5dir)/$$f.5; done set -e; for f in $(MANPAGES8); do \ $(INSTALL_SHARE) $$f.8 $(man8dir)/$$f.8; done install-examples: $(INSTALL_DIRECTORY) $(exampledir) set -e; for e in $(EXAMPLES); do \ $(INSTALL_SHARE) sync-accounts.example-$$e \ $(exampledir)/sync-accounts.$$e; \ done clean: rm -f *~ ./#*# distclean realclean: clean chiark-utils-4.4.2build1/sync-accounts/sync-accounts.example-bsd0000664000000000000000000000102612423200672021613 0ustar # This is an example /etc/sync-accounts for BSD. lockpasswd runvia vipw lockgroup none localformat bsd logfile /var/log/sync-accounts uidmin 500 uidmax 29999 homebase /home defaultshell /usr/local/bin/bash host fowey getpasswd rsh fowey -l syncacct cat /etc/passwd getshadow rsh fowey -l syncacct cat /etc/shadow getgroup rsh fowey -l syncacct cat /etc/group createuser sync-accounts-createuser sameuid group devel nousergroups user devel usergroups users 500-29999 nogroup * nocreateuser nousergroups nosameuid #user root end chiark-utils-4.4.2build1/sync-accounts/sync-accounts.50000664000000000000000000003103612423200672017562 0ustar .\" Hey, Emacs! This is an -*- nroff -*- source file. .TH SYNC\-ACCOUNTS 5 "15th July 2002" "Greenend" "chiark utilities" .SH NAME /etc/sync\-accounts \- configuration file for sync\-accounts .SH DESCRIPTION .B /etc/sync\-accounts contains the default configuration of the .BR sync-accounts (8) account synchronisation tool. The configuration file specifies how to access and update the local password and group databases, where sync-accounts should log. It also specifies the list of (remote) sources for account information, and which accounts and details should be copied from each source to the local system. .SH OVERALL SYNTAX AND SEMANTICS The configuration file is parsed as a series of lines. First, leading and trailing whitespace on each line is removed, and then empty lines, or lines starting with .BR # , are removed. Each line is parsed as a directive. The order of directives is significant; some directives set up information which later directives rely on. The configuration file must contain an .B end directive; anything after that point is ignored. .SH GLOBAL DIRECTIVES These directives may appear only at the start of the file (before any other directives), and each directive must appear only once; otherwise, sync-accounts my behave oddly. .TP .BR lockpasswd | lockgroup " \fImethod\fP [\fIdetails \fP...]" Specifies how the passwd and group files should be read and/or locked. See .B LOCKING METHOD DIRECTIVES below. .TP .BI "logfile " filename Append log messages to .I filename instead of stdout. Errors still go to stderr. .TP .BR localformat " " bsd | std Specifies the local password file is in the relevant format: .B std is the standard V7 password file (with a SysV-style /etc/shadow if /etc/shadow exists). .B bsd is the BSD4.4 master.passwd format, and should be used only with .BR "lockpasswd runvia vipw" . The default is .BR std . .SH LOCKING METHOD DIRECTIVES One .B lockgroup and one .B lockpasswd directive must be present, in the global directives at the start of the config file. The choice of the appropriate directives can be difficult without special knowledge of the local system. In general, it is best to use .B lockpasswd runvia vipw where this is available, as if this works avoids having to know the names of the lockfiles. GNU systems (including GNU/Linux and Debian GNU/BSD) typically lock the group file separately and supply .BR vigr , in which case you should use .BR "lockgroup vigr" . Most systems other than GNU do not lock the group file at all (or assume that all programs which modify the group file will lock the passwd file), in which case .B lockgroup none is appropriate. If vigr or vipw is not available or is known to be broken (eg, because it does not lock properly), then use .BR link . .TP .BR lockpasswd | lockgroup " " runvia " \fIprogram\fP sync-accounts will reinvoke itself using .IR program , which must behave like .B vipw or .BR vigr . sync-accounts will set the .B EDITOR environment variable to the path it was invoked with (Perl's .BR $0 ) and put some information for its own use into .B SYNC_ACCOUNTS_* environment variables (which will also allow sync-accounts to tell that it has already been reinvoked via .I program and should not do so again). If both .BI "lockpasswd runvia " vipw and .BI "lockgroup runvia " vigr are specified, then it must be possible and safe for the EDITOR run by .I vipw to invoke .IR vigr , as this is what sync-accounts will do. .TP .BR lockpasswd | lockgroup " " link " \fIsuffix\fP|\fIfilename\fP sync-accounts will attempt to lock the passwd or group file by making a hardlink from the real file to the specified filename. If .IR suffix | filename starts with a .B / it is interpreted as a filename; otherwise it is interpreted as a suffix, to be appended to the real database filename. .TP .BR lockpasswd | lockgroup " " none sync-accounts will not attempt to lock the passwd or group files at all. .B lockgroup none is appropriate on systems where there is no separate locking for the group file (either because there is no proper support for automatic editing of the group file, or because you're expected to lock the password file), although in the absence of .B vigr it's inevitable that simultaneous changes to the group file made by both the human sysadmin and by sync-accounts will cause problems. .B lockpasswd none is very dangerous and should not normally be used. It will cause data loss if any other tool for changing password data is used - eg, .BR passwd (1). .SH PER-SOURCE DIRECTIVES Within each source's section, all of the per-source directives must appear before any account-selection directives; otherwise sync-accounts may behave oddly. If a per-source directive is repeated, the last setting takes effect. .TP .BI "host " source Starts a source's section. Usually each source will correspond exactly to one host which is acting as a source of account data. The .B host directive resets the per-source parameters to the defaults. .I source need not be the source host's official name in any sense and is used only for identification. Any .I source must be named in only one .B host directive, or sync-accounts may behave oddly. .TP .BR getpasswd | getgroup | getshadow " \fIcommand\fP..." sync-accounts always fetches account data from sources by running specified commands on the local host; it does not contain any network protocols, itself. .I command is fed to .BR "sh -c" and might typically contain something like .br .B " ssh syncacct@remote.host cat /etc/passwd" .br where the user syncacct on remote.host is in group shadow, or .br .B " cat /var/local/sync-accounts/remote.host/passwd" where the file named is copied across using cron. .B getpasswd must be specified if user data is to be transferred; .B getgroup must be specified if group data is to be transferred. .B getshadow should be specified iff getpasswd is specified but the data from getpasswd does not contain actual password information, and should emit data in Sys-V shadow password format. .TP .BR remoteformat " " std | bsd Specifies the format of the output of getpasswd. .B std is standard V7 passwd file format (optionally augmented by the use of a shadow file fetched with getshadow). .B bsd is the BSD4.4 master.passwd format (and getshadow should not normally be used with .BR "remoteformat bsd" ). The default is .BR std . .SH SYNCHRONISATION SETTINGS The following directives affect the way that account data is copied. They may be freely mixed with other directives, and repeated. The setting in effect is the one set by the last relevant settings directive before any particular account-selection directive. .TP .BR uidmin | uidmax " \fivalue\fP" When an account is to be created locally, a uid/gid will be chosen which is one higher than the highest currently in use, except that ids below uidmin or above uidmax are ignored and will never be used. There is no default. .TP .BI "homebase " homebase When an account is to be created locally, its home directory will be .IB homebase / username where .I username is the name of the account. The default is .BR /home . .TP .RB [ no ] sameuid Specifies whether uids are supposed to match. With .BR sameuid , it is an error for the uid or gid of a synchronised local account not to match the corresponding remote account, and new local accounts will get the remote accounts' ids. The default is .BR nosameuid . .TP .BR usergroups " | " nousergroups " | " defaultgid " \fIgid\fP" Specifies whether local accounts are supposed to have corresponding groups, or all be part of a particular group. The default is .BR usergroups . With .BR usergroups , when a new account is created, the corresponding per-user group will be created as well, and per-user groups are created for existing accounts if necessary (if account creation is enabled). If the gid or group name for a per-user group is already taken for a different group name or gid this will be logged, and processing of that account will be inhibited, but it is not a fatal error. With .BR defaultgid , newly-created accounts will be made a part of that group, and the groups of existing accounts will be left alone. With .BR nousergroups , no new accounts can be created, and existing accounts' groups will be left alone. .TP .BR createuser " [\fIcommand\fP] | " nocreateuser Specifies whether accounts found on the remote host should be created if necessary, and what command to run to do the the rest of the account setup (eg, creation of home directory, etc.). The default is .BR nocreateuser . If .B createuser is specified without a command then .B sync-accounts-createuser is used; the supplied sync-accounts-createuser program is a reasonable minimal implementation. With .BR createuser , either sameuid, or both uidmin and uidmax, must be specified, if accounts are actually to be created. The command is passed to .BR "sh -c" . See .BR sync-accounts-createuser (8) for details of .IR command 's environment and functionality. .TP .BR group | nogroup " \fIglob-pattern\fP" .B group specifies that the membership of the local groups specified should be adjusted adjusted whenever account data for any user is copied, so that the account will be a member of the affected group locally iff the source account it is a member of the same group on the source host. The most recently-encountered glob-pattern for a particular group takes effect. The default is .BR "nogroups *" . The glob patterns may contain only alphanumerics, the two glob metacharacters .BR "* ?" and four punctuation characters .BR "- + . _" ; \fB\\\fP-quoting and character sets and ranges are not supported. .TP .BI "defaultshell " pathname Local accounts' shells will, when an account is synchronised, be set to the remote account's shell if the same file exists locally and is executable. Otherwise, this value will be used. The default is .BR /bin/sh . .SH ACCOUNT SELECTION These directives specify that the selected accounts are to be synchronised: that is, the local account data will be unconditionally overwritten (according to the synchronisation settings) with data from the current source (according to the most recent .B host directive). Any particular local username will only be synchronised once; the source and settings for first account selection directive which selects that local username will be used. When an account is synchronised, the account password, comment field, and shell will be copied unconditionally. If .B sameuid is in effect specified the uid will be checked (or copied, for new accounts). .TP .BR user " \fIusername\fP [" remote "=\fIremoteusername\fP]" Specifies that account data should be copied for local user .I username from the remote account .I remoteusername (or .I username if .I remoteusername is not specified). .TP .RI "\fBusers\fP " ruidmin - ruidmax Specifies that all remote users whose remote uid is in the given range are to be synchronised to corresponding user accounts. (Note that the remote uid will only be copied if .B sameuid is in effect.) .TP .BI "nouser " username Specifies that data for .I username is not to be copied, even if subsequent user or users directives suggest that it should be. .TP .B addhere This directive has no effect on sync-accounts. However, it is used as a placeholder by grab-account: new accounts for creation are inserted just before addhere. See .BR grab-account (8). .SH FINAL DIRECTIVE .TP .B end must appear in the configuration file, usually at the end of the file. Nothing after it will be read. .SH BUGS The advice about the correct .B lockpasswd and .B lockgroup directives is probably out of date or flatly wrong. .SH AUTHOR .B sync-accounts and this manpage are part of the .B sync-accounts package which was written by Ian Jackson . They are Copyright 1999-2000,2002 Ian Jackson , and Copyright 2000-2001 nCipher Corporation Ltd. The sync-accounts package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, consult the Free Software Foundation's website at www.fsf.org, or the GNU Project website at www.gnu.org. .SH SEE ALSO .BR sync-accounts "(8), " .BR grab-account "(8), " .BR sync-accounts-createuser "(8), " .BR passwd "(5), " .BR group "(5), " .BR shadow "(5), " .BR master.passwd "(5), " .BR vipw "(8), " .BR vigr "(8)" chiark-utils-4.4.2build1/sync-accounts/sync-accounts-createuser.80000664000000000000000000000664112423200672021731 0ustar .\" Hey, Emacs! This is an -*- nroff -*- source file. .TH SYNC\-ACCOUNTS\-CREATEUSER 8 "14th July 2002" "Greenend" "chiark utilities" .SH NAME sync\-accounts\-createuser \- helper/hook program for sync\-accounts .SH SYNOPSIS .BI SYNCUSER_CREATE_ var = "value\fP... \fI" sync\-accounts\-createuser .SH DESCRIPTION .B sync-accounts-createuser is invoked by .B sync-accounts when sync-accounts is creating a local account. It must perform all of the tasks involved with local account creation except for the actual changes to the password, shadow and group databases. At the very minimum, it must create the new account's home directory (with appropriate permissions). The supplied sync-accounts-createuser script does exactly that. It may also suggest to sync-accounts modifications to the new account's passwd entry. .SH INVOCATION When sync-accounts-createuser is invoked, the passwd and group entries will not yet have been set up, so it may not rely on them. sync-accounts-createuser will not be supplied with any arguments. However, the following environment variables will be set, giving details about the account to be created: .br .B " " SYNCUSER_CREATE_USER .br .B " " SYNCUSER_CREATE_UID .br .B " " SYNCUSER_CREATE_GID .br .B " " SYNCUSER_CREATE_COMMENT .br .B " " SYNCUSER_CREATE_HOME .br .B " " SYNCUSER_CREATE_SHELL .SH RESULTS sync-accounts-createuser should usually produce no output. It can inhibit the creation of the user by outputting a single line not containing a colon; in this case, a diagnostic message will be written to sync-accounts's logfile, and the user will be skipped. Alternatively, it may write out an alternative password file entry, in which case sync-accounts will use the supplied data for the local passwd file instead of that from the remote host. The line should be in Sys-V passwd file format (regardless of .B localformat or .B remoteformat settings). The username field should be taken from .BR SYNCUSER_CREATE_USER , and the password field should be .BR x . .SH EXIT STATUS .TP .B 0 All went well, or we wrote a line without a colon to say that the account should not be created. .TP any other There were serious problems and sync-accounts should bomb out immediately. .SH FILES None. .SH ENVIRONMENT See above. .SH BUGS The supplied sync-accounts-createuser does not check that it was not supplied with any arguments; nor does it check that the .B SYNCUSER_CREATE_* variables are set, or have sensible values. .SH AUTHOR .B sync-accounts-createuser and this manpage were written by Ian Jackson . They are Copyright 1999-2002 Ian Jackson . This manpage forms part of the sync-accounts package. sync-accounts-createuser and the sync-accounts package are free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, consult the Free Software Foundation's website at www.fsf.org, or the GNU Project website at www.gnu.org. .SH SEE ALSO .BR sync-accounts "(8), " .BR sync-accounts "(5), " .BR passwd "(5)" chiark-utils-4.4.2build1/sync-accounts/grab-account.80000664000000000000000000000463412423200672017345 0ustar .\" Hey, Emacs! This is an -*- nroff -*- source file. .TH GRAB\-ACCOUNT 8 "14th July 2002" "Greenend" "chiark utilities" .SH NAME grab\-account \- add new account synchronised to remote system .SH SYNOPSIS .BI "grab\-account " local\-user " " source " [" remote\-user ] .SH DESCRIPTION .B grab-account reconfigures sync-accounts to start synchronising a specified local user (which may not yet exist) from a specified remote system, and then invokes sync-accounts once to synchronise from that source. .B /etc/sync-accounts/createuser should contain a .B addhere line in the appropriate source section (ie, after .BR host " \fIsource\fP)." grab-account adds a .br .BR " user" " \fIlocal\-user\fP [" remote= "\fIremote\-user\fP]" .br directive just before .B addhere and runs .BR sync-accounts " \fIsource\fP." .SH EXIT STATUS .TP .B 0 All went well. .TP any other There were problems. .SH FILES .BR /etc/sync-accounts ; See also .BR sync-accounts (8). .SH ENVIRONMENT See .BR sync-accounts (8). .SH BUGS There is no locking of .B /etc/sync-accounts so do not invoke grab-account from a script, or more than once at a time by hand. Do not edit /etc/sync-accounts by hand and also simultaneously run grab-account. The mechanism involving .B addhere is suboptimal. This should be done with an include feature in sync-accounts, so that grab-account does not have to edit a configuration file that really belongs to the sysadmin. .SH AUTHOR .B grab-account and this manpage are part of the .B sync-accounts package which was written by Ian Jackson . They are Copyright 1999-2000,2002 Ian Jackson , and Copyright 2000-2001 nCipher Corporation Ltd. The sync-accounts package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, consult the Free Software Foundation's website at www.fsf.org, or the GNU Project website at www.gnu.org. .SH SEE ALSO .BR sync-accounts "(8), " .BR sync-accounts "(5), " .BR passwd "(5)" chiark-utils-4.4.2build1/sync-accounts/sync-accounts.example-linux0000664000000000000000000000225312423200672022205 0ustar # This is an example /etc/sync-accounts for Linux. lockpasswd runvia vipw lockgroup runvia vigr logfile /var/log/sync-accounts uidmin 1000 uidmax 29999 homebase /u2 defaultshell /bin/bash host sfere getpasswd ssh sfere -l ian cat /etc/passwd createuser user richardk remote=richard addhere #host snoopy #getpasswd ssh snoopy -l ian cat /etc/passwd #getshadow ssh snoopy -l ian really cat /etc/shadow #createuser #user christi #addhere host khem getpasswd rsh khem -l ian cat /etc/passwd createuser user andrewm addhere host chiark getpasswd cat /var/lib/sync-accounts/chiark/passwd getshadow cat /var/lib/sync-accounts/chiark/shadow getgroup cat /var/lib/sync-accounts/chiark/group createuser sync-accounts-createuser-chiark2davenant sameuid user richard user ian user ijackson user owend user damerell user jonr user duncanm user siona user dans user marisal user vivienh user davidr user fanf user wednsday user emmaburt user eleanorb user stevee user diziet user matthewv user mattheww user sgtatham user jdamery user twomack user aldabra user janetmck user johns user andreww user sbleas user clareb user chrisj user beckyc addhere nogroup * nocreateuser nousergroups user root end chiark-utils-4.4.2build1/sync-accounts/sync-accounts.80000664000000000000000000001120212423200672017556 0ustar .\" Hey, Emacs! This is an -*- nroff -*- source file. .TH SYNC\-ACCOUNTS 8 "14th July 2002" "Greenend" "chiark utilities" .SH NAME sync\-accounts \- synchronise accounts and passwords .SH SYNOPSIS .BR sync\-accounts " [\fIoptions\fP] [\fIsource\fP ...]" .SH DESCRIPTION .B sync-accounts is a tool for copying account information into the local system's password and group databases, or equivalent, from other systems. It can be used to slave individual accounts, whole systems, or various partial combinations. By default, when invoked, sync-accounts reads is configuration file and updates all of the local details it is configured to synchronise, from all relevant sources. If one or more \fIsource\fPs are named as command-line arguments, only information from those sources is installed locally. See .BR sync-accounts(5) for detailed information about sync-accounts's behaviour and configuration. .SH OPTIONS .TP .BI \-C config\-file Reads .I config-file instead of .BR /etc/sync-accounts . .TP .BR \-q Instead of updating local information, sync-accounts displays a summary of which accounts are synchronised or not, and from where. .TP .BR \-n Causes sync-accounts not to actually install the new information in the local password and group databases. Instead, updated versions are written to the files .B passwd and .B group in the current directory. With .B \-n new accounts are not created at all. The system databases are not locked. .SH SECURITY sync-accounts is not resistant to malicious data in the local password and group databases, or its configuration file or command line arguments. Malicious data in source information will not be able to take control of sync-accounts, but will be copied to the local databases if sync-accounts is configured to do so. To update the local databases, sync-accounts must be run as root. For \-q and \-n sync-accounts still needs to be able to successfuly invoke the commands specified in the configuration for getpasswd and getgroup. .SH EXIT STATUS .TP .B 0 All went well and there were no warnings. .TP any other There were problems. The local databases may or may not have been updated. .SH FILES .TP .B /etc/sync-accounts Default configuration file. (Override with .BR -C .) .TP .B sync-accounts-createuser Default command invoked by sync-accounts to create local users. .TP .B /home Default location for created users' home directories. .TP .B /bin/sh Default shell for created users. .TP .BR /etc/passwd ", " /etc/group ", " /etc/shadow ", " /etc/master.passwd Local account databases, depending on configuration. .TP .BR /etc/shadow-non-existent Must not exist. .SH ENVIRONMENT .TP .BR EDITOR ", " VISUAL Manipulated by sync-\accounts when it is reinvoking itself via vipw or vigr, according to .B lockpasswd runvia or .BR "lockgroup runvia" . .TP .BR SYNC_ACCOUNTS_* Used by sync-accounts for its own purposes. Do not set these variables. .LP Setting variables used by .BR vipw (8) and .BR vigr (8), apart from .BR EDITOR " and/or" VISUAL will affect the operation of sync-accounts. Avoid messing with these if possible. .LP .B PATH is used to find subprograms such as .BR sync-accounts-createuser " and " vipw / vigr . .SH BUGS Using sync-accounts does not give particularly prompt propagation of changed account information. There is no simple mechanism for automatically getting the right configuration details for accessing the local system's password and group databases. All the systems sharing account information using sync-accounts need to be using compatible encrypted-password schemes. .SH AUTHOR .B sync-accounts and this manpage are part of the .B sync-accounts package which was written by Ian Jackson . They are Copyright 1999-2000,2002 Ian Jackson , and Copyright 2000-2001 nCipher Corporation Ltd. The sync-accounts package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, consult the Free Software Foundation's website at www.fsf.org, or the GNU Project website at www.gnu.org. .SH SEE ALSO .BR sync-accounts "(5), " .BR grab-account "(8), " .BR sync-accounts-createuser "(8), " .BR passwd "(5), " .BR group "(5), " .BR shadow "(5), " .BR master.passwd "(5), " .BR vipw "(8), " .BR vigr "(8)" chiark-utils-4.4.2build1/sync-accounts/sync-accounts-createuser0000775000000000000000000000173212423200672021562 0ustar #!/bin/sh # $Id: sync-accounts-createuser,v 1.6 2007-09-21 21:21:15 ianmdlvl Exp $ # # Copyright 1999-2002 Ian Jackson # # This is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free # Software Foundation; either version 3, or (at your option) any later # version. # # This is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should already have a copy of the GNU General Public License. # If not, consult the Free Software Foundation's website at # www.fsf.org, or the GNU Project website at www.gnu.org. set -e un=$SYNCUSER_CREATE_USER ui=$SYNCUSER_CREATE_UID gi=$SYNCUSER_CREATE_GID ho=$SYNCUSER_CREATE_HOME test -d $ho || mkdir $ho chgrp $gi $ho chown $ui $ho chmod 2755 $ho chiark-utils-4.4.2build1/sync-accounts/grab-account0000775000000000000000000000335312423200672017177 0ustar #!/bin/sh # This is part of sync-accounts, a tool for synchronising UN*X password data. # # sync-accounts is # Copyright 1999-2000,2002 Ian Jackson # Copyright 2000-2001 nCipher Corporation Ltd # # sync-accounts is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 3, or (at # your option) any later version. # # sync-accounts is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should already have a copy of the GNU General Public License. # If not, consult the Free Software Foundation's website at # www.fsf.org, or the GNU Project website at www.gnu.org. # # $Id: grab-account,v 1.5 2007-09-21 21:21:15 ianmdlvl Exp $ set -e if [ $# -lt 2 -o $# -gt 3 ] then echo >&2 \ 'usage: grab-account [] creates an entry in /etc/sync-accounts, and runs sync-accounts $Id: grab-account,v 1.5 2007-09-21 21:21:15 ianmdlvl Exp $' exit 1 fi lu="$1" sh="$2" if [ $# -gt 2 ] then ru="$3" else ru="$1" fi cf=/etc/sync-accounts if perl -ne 'exit 1 if m/^\s*user\s+'$lu'\s/;' <$cf then perl -pe ' next unless m/^\s*host\s+'$sh'\s*$/...m/^host|^end/; next unless m/^\s*addhere\s*$/; next if $done++; print "user '$lu'".("'$lu'" eq "'$ru'" ? "" : " remote='$ru'")."\n" or die $!; END { print(STDERR "\`addhere'\'' line not found\n"), $?=1 if !$? && !$done; } ' $cf >$cf.new mv -f $cf.new $cf else echo "entry already exists in $cf, leaving alone" fi sync-accounts $sh chiark-utils-4.4.2build1/backup/0000775000000000000000000000000012423200672013354 5ustar chiark-utils-4.4.2build1/backup/full0000775000000000000000000002401212423200672014243 0ustar #!/usr/bin/perl # full # Main backup script - does a full dump or execs increm. Do NOT run directly! # # This file is part of chiark backup, a system for backing up GNU/Linux and # other UN*X-compatible machines, as used on chiark.greenend.org.uk. # # chiark backup is: # Copyright (C) 1997-1998,2000-2001,2007 # Ian Jackson # Copyright (C) 1999 Peter Maydell # # This is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3, or (at your option) any later version. # # This is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. BEGIN { $etc= '/etc/chiark-backup'; require "$etc/settings.pl"; require 'backuplib.pl'; } $|=1; while (@ARGV) { $_= shift @ARGV; if (m/^\-\-no\-reten$/) { $noreten=1; } elsif (m/^\-\-no\-config\-check$/) { $nocheck=1; } else { die "unknown option/argument \`$_'\n"; } } # Check to see whether the tape.nn and fsys.nn files are sane. # checkallused checks that all the filesystems mounted are in fact # dumped in both full and incremental dumps. openlog(); if (!$nocheck) { setstatus "FAILED configuration check"; print "Configuration check ...\n" or die $!; system 'backup-checkallused'; $? and die $?; } else { setstatus "FAILED rewinding"; rewind_raw(); } printdate(); setstatus "FAILED reading TAPEID"; # Try to read the tape ID from the tape into the file TAPEID readtapeid_raw(); setstatus "FAILED during startup"; # We need some ID; if the tape has one already that takes precedence; # otherwise the user might have set a tape ID that this should be # by creating really-TAPEID. if (open T, "TAPEID") { unlink 'really-TAPEID'; } elsif (open T, "really-TAPEID") { } else { die "No TAPEID.\n"; } # read the ID; it had better be a non-empty string of alphanumeric chars. chomp($tapeid= ); $tapeid =~ m/[^0-9a-zA-Z]/ and die "Bad TAPEID ($&).\n"; $tapeid =~ m/[0-9a-zA-Z]/ or die "Empty TAPEID.\n"; close T; setstatus "FAILED at tape identity check"; # We don't let the user overwrite the tape used for the last backup. if (open L, "last-tape") { chomp($lasttape= ); close L; } else { undef $lasttape; } die "Tape $tapeid same as last time.\n" if $tapeid eq $lasttape; # $tapeid identifies the individual tape; $tapedesc is its current # identity and function, for printing in messages. You can make these # namespaces the same if you like, or you can make the tape. # files be links to tape. files. if (defined($tapedesc= readlink "$etc/tape.$tapeid")) { $tapedesc =~ s/^.*\.//; $tapedesc .= "($tapeid)"; } else { $tapedesc = $tapeid; } # Parse the appropriate tape.nn file. # Format is: empty lines and lines starting '#' are ignored. Trailing # whitespace is ignored. File must end with 'end' on a line by itself. # Either there should be a line 'incremental' to indicate that this is # a tape for incremental backups, or a pair of lines 'filesystems fsg' # and 'next tapeid', indicating that this tape is part of a full # backup, containing the filesystem group fsg. undef $fsys; open D, "$etc/tape.$tapeid" or die "Unknown tape $tapeid ($!).\n"; for (;;) { $_= or die; chomp; s/\s+$//; last if m/^end$/; next unless m/\S/; next if m/^\#/; if (m/^filesystems (\w+)$/) { $fsys= $1; } elsif (m/^next (\w+)$/) { $next= $1; } elsif (m/^incremental$/) { $incremental= 1; } else { die "unknown entry in tape $tapeid at line $.: $_\n"; } } close D or die $!; # Incremental backups are handled by increm, not us. if ($incremental) { die "incremental tape $tapeid has next or filesystems\n" if defined($next) || defined($fsys); print STDERR "Incremental tape $tapeid.\n\n"; setstatus "FAILED during incremental startup"; exec "increm",$tapeid,$tapedesc; die $!; } # Read the filesystem group definition (file fsys.nnn) readfsys("$fsys"); $doing= "dump of $fsys to tape $tapedesc in drive $tape"; print LOG "$doing:\n" or die $!; if (!$noreten) { setstatus "FAILED retensioning"; runsystem("mt -f $tape reten"); } setstatus "FAILED writing tape ID"; # First write the tape ID to this tape. writetapeid($tapeid,$tapedesc); unlink 'this-md5sums'; print "Doing $doing ...\n" or die $!; unlink 'p'; system 'mknod -m600 p p'; $? and die $?; setstatus "FAILED during dump"; sub closepipes () { close(DUMPOR); close(TEEOR); close(BUFOR); close(FINDOR); close(DUMPOW); close(TEEOW); close(BUFOW); close(FINDOW); close(GZOR); close(GZOW); close(DDERRR); close(DDERRW); } # work out a find option string that will exclude the required files # Note that dump pays no attention to exclude options. $exclopt = ''; foreach $exc (@excldir) { $exclopt .= "-regex $exc -prune -o "; } foreach $exc (@excl) { $exclopt .= "-regex $exc -o "; } # For each filesystem to be put on this tape: for $tf (@fsys) { printdate(); parsefsys(); prepfsys(); pipe(FINDOR,FINDOW) or die $!; pipe(DUMPOR,DUMPOW) or die $!; pipe(TEEOR,TEEOW) or die $!; pipe(TEEOR,TEEOW) or die $!; pipe(BUFOR,BUFOW) or die $!; pipe(DDERRR,DDERRW) or die $!; $bufir='TEEOR'; $ddcmd= "dd ibs=$softblocksizebytes obs=$blocksizebytes of=$ntape 2>&1"; if ($gz) { $bufir='GZOR'; pipe(GZOR,GZOW) or die $!; $ddcmd .= " conv=sync"; } nexttapefile("full $prefix:$atf_print"); # We can back up via dump or cpio or zafio $dumpin= '&FINDOW',$rstr."find $atf -xdev -noleaf -print0"; $dumpcmd= "cpio -Hustar -o0C$softblocksizebytes"; $dumpin= '<&FINDOR'; } elsif ($tm eq 'zafio') { # compress-each-file-then-archive using afio startprocess '&FINDOW',$rstr."find $atf -xdev -noleaf $exclopt -print"; # don't use verbose flag as this generates 2MB report emails :-> $dumpcmd = "afio -b $softblocksizebytes -Zo -"; $dumpin = '<&FINDOR'; } elsif ($tm eq 'ntfsimage') { $dumpcmd= "ntfsimage -svvf --dirty $dev"; } elsif ($tm eq 'gtar') { execute("$rstr touch $fsidfile+new"); $dumpcmd= "tar Ccfl $atf - ."; } else { die "unknown method $tm for $prefix:$atf_print\n"; } # This is a funky way of doing a pipeline which pays attention # to the exit status of all the commands in the pipeline. # It is roughly equivalent to: # md5sum

>this-md5sums # dump <$dumpin | tee p [| gzip] | writebuffer | dd >/dev/null startprocess '>this-md5sums',"$nice md5sum"; startprocess $dumpin,'>&DUMPOW',"$nice ".$rstr.$dumpcmd; startprocess '<&DUMPOR','>&TEEOW',"$nice tee p"; if ($gz) { startprocess '<&TEEOR','>&GZOW',"$nice gzip -v$gz"; } startprocess "<&$bufir",'>&BUFOW',"$nasty writebuffer"; startprocess '<&DDERRR','>/dev/null',"$nice tee dderr >&2"; startprocess '<&BUFOR','>&DDERRW',"$nasty $ddcmd"; closepipes(); endprocesses(); open DDERR, "dderr" or die $!; defined(read DDERR,$_,1023) or die $!; close DDERR; m/\n(\d+)\+0 records out\n/ or die ">$dderr< ?"; push @tapefilesizes, [ $1, $currenttapefilename ]; $totalrecords += $1; pboth("total blocks written so far: $totalrecords\n"); if ($tm eq 'gtar') { execute("$rstr mv -f $fsidfile+new $fsidfile"); } finfsys(); } # The backup should now be complete; verify it setstatus "FAILED during check"; # Rewind the tape and skip the TAPEID record runsystem("mt -f $tape rewind"); runsystem("mt -f $ntape fsf 1"); # Check the md5sums match for each filesystem on the tape open S,"this-md5sums" or die $!; for $tf (@fsys) { printdate(); parsefsys(); chomp($orgsum= ); $orgsum =~ s/\ +\-?$//; $orgsum =~ m/^[0-9a-fA-F]{32}$/i or die "orgsum \`$orgsum' ?"; $cmd= "$nasty dd if=$ntape ibs=$blocksizebytes"; $cmd .= " | $nasty readbuffer"; $cmd .= " | $nice gzip -vd" if $gz; $cmd .= " | $nice md5sum"; pboth(" $cmd\n"); chomp($csum= `$cmd`); $csum =~ s/\ +\-?$//; $orgsum eq $csum or die "MISMATCH $tf $csum $orgsum\n"; print "checksum ok $csum\t$tf\n" or die $!; print LOG "checksum ok $csum\t$tf\n" or die $!; } printdate(); runsystem("mt -f $tape rewind"); setstatus "FAILED during cleanup"; $summary= ''; foreach $tfs (@tapefilesizes) { $summary .= sprintf " %10d blocks for %s\n", $tfs->[0], $tfs->[1] } $summary .= sprintf " %10d blocks total (of %d bytes) plus TAPEID and headers\n", $totalrecords, $blocksizebytes; pboth("size-summary:\n"); pboth($summary); open SS, ">size-summary..new" or die $!; print SS $summary or die $!; close SS or die $!; rename 'size-summary..new',"size-summary.$fsys" or die $!; # Write to some status files to indicate what the backup system # ought to do when next invoked. # reset incremental backup count to 1. open IAN,">increm-advance.new" or die $!; print IAN "1\n" or die $!; close IAN or die $!; # Next full backup is whatever the next link in the tape description # file says it ought to be. open TN,">next-full.new" or die $!; print TN "$next\n" or die $!; close TN or die $!; unlink 'last-tape','next-full'; # We are the last tape to have been backed up rename 'TAPEID','last-tape' or die $!; rename 'this-md5sums',"md5sums.$fsys" or die $!; rename 'log',"log.$fsys" or die $!; rename 'next-full.new',"next-full" or die $!; rename 'increm-advance.new',"increm-advance" or die $!; print "$doing completed.\nNext dump tape is $next.\n" or die $!; setstatus "Successful: $tapedesc $fsys, next $next"; exit 0; chiark-utils-4.4.2build1/backup/Makefile0000664000000000000000000000431212423200672015014 0ustar # Makefile # simple make settings # # This file is part of chiark backup, a system for backing up GNU/Linux and # other UN*X-compatible machines, as used on chiark.greenend.org.uk. # # chiark backup is: # Copyright (C) 1997-1998,2000-2001,2007 # Ian Jackson # Copyright (C) 1999 Peter Maydell # # This is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3, or (at your option) any later version. # # This is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. us= chiark-backup include ../settings.make BINSCRIPTS= checkallused loaded driver takedown whatsthis labeltape \ snaprsync SHARESCRIPTS= bringup full increm snap-drop SHAREFILES= backuplib.pl snap-common SNAPKINDS= lvm remount remountrocp nosnap EXAMPLES= relativity chiark all: install: all $(INSTALL_DIRECTORY) $(confdir) $(confdir)/snap $(bindir) \ $(sharedir) $(vardir) $(man1dir) set -e; for s in $(BINSCRIPTS); do \ $(INSTALL_SCRIPT) $$s $(bindir)/backup-$$s; done $(INSTALL_SHARE) $(SHAREFILES) $(sharedir) $(INSTALL_SCRIPT) $(SHARESCRIPTS) $(sharedir) set -e; for s in $(SNAPKINDS); do \ d=$(confdir)/snap/$$s; \ test ! -f $$d || d=$$d.dist; \ $(INSTALL_SCRIPT) $$s $$d; done install-docs: $(INSTALL_DIRECTORY) $(txtdocdir) $(INSTALL_SHARE) iwjbackup.txt $(txtdocdir)/README install-examples: set -e; for e in $(EXAMPLES); do \ cd examples/$$e; \ $(INSTALL_DIRECTORY) $(exampledir)/$$e; \ $(INSTALL_SHARE) [!A-Z]*[!~] $(exampledir)/$$e; \ if test -f SYMLINKS.tar; then \ exec &2 <<'END' usage: .../remount snap VARDIR DEV MOUNT .../remount drop VARDIR END exit 1 ;; esac chiark-utils-4.4.2build1/backup/examples/0000775000000000000000000000000012423200672015172 5ustar chiark-utils-4.4.2build1/backup/examples/relativity/0000775000000000000000000000000012423200672017366 5ustar chiark-utils-4.4.2build1/backup/examples/relativity/tape.v10000664000000000000000000000003412423200672020564 0ustar filesystems pt1 next v2 end chiark-utils-4.4.2build1/backup/examples/relativity/fsys.all0000664000000000000000000000012112423200672021036 0ustar include ifsys.prefixes include ifsys.pt0 include ifsys.pt1 include ifsys.pt2 end chiark-utils-4.4.2build1/backup/examples/relativity/bringup-hook0000775000000000000000000000003112423200672021712 0ustar #!/bin/sh set -e chvt 11 chiark-utils-4.4.2build1/backup/examples/relativity/tape.u10000664000000000000000000000003412423200672020563 0ustar filesystems pt1 next u2 end chiark-utils-4.4.2build1/backup/examples/relativity/tape.v00000664000000000000000000000003412423200672020563 0ustar filesystems pt0 next v1 end chiark-utils-4.4.2build1/backup/examples/relativity/tape.u00000664000000000000000000000003412423200672020562 0ustar filesystems pt0 next u1 end chiark-utils-4.4.2build1/backup/examples/relativity/fsys.pt10000664000000000000000000000005512423200672021000 0ustar include ifsys.prefixes include ifsys.pt1 end chiark-utils-4.4.2build1/backup/examples/relativity/tape.u20000664000000000000000000000003412423200672020564 0ustar filesystems pt2 next v0 end chiark-utils-4.4.2build1/backup/examples/relativity/tape.s00000664000000000000000000000003412423200672020560 0ustar filesystems pt0 next s1 end chiark-utils-4.4.2build1/backup/examples/relativity/warnings.soon0000775000000000000000000000005012423200672022114 0ustar T 45 "in 1 minute" T 15 "in 15 seconds" chiark-utils-4.4.2build1/backup/examples/relativity/tape.s20000664000000000000000000000003412423200672020562 0ustar filesystems pt2 next t0 end chiark-utils-4.4.2build1/backup/examples/relativity/tape.t10000664000000000000000000000003412423200672020562 0ustar filesystems pt1 next t2 end chiark-utils-4.4.2build1/backup/examples/relativity/fsys.pt20000664000000000000000000000005512423200672021001 0ustar include ifsys.prefixes include ifsys.pt2 end chiark-utils-4.4.2build1/backup/examples/relativity/tape.v20000664000000000000000000000003412423200672020565 0ustar filesystems pt2 next s0 end chiark-utils-4.4.2build1/backup/examples/relativity/tape.kt0000664000000000000000000000002012423200672020647 0ustar incremental end chiark-utils-4.4.2build1/backup/examples/relativity/tape.t20000664000000000000000000000003412423200672020563 0ustar filesystems pt2 next u0 end chiark-utils-4.4.2build1/backup/examples/relativity/warnings.now0000775000000000000000000000000012423200672021734 0ustar chiark-utils-4.4.2build1/backup/examples/relativity/tape.s10000664000000000000000000000003412423200672020561 0ustar filesystems pt1 next s2 end chiark-utils-4.4.2build1/backup/examples/relativity/expected-diffs0000664000000000000000000000021112423200672022175 0ustar #!sfere:/opt/bigdisc !davenant:/export/mirror davenant:/export/mirror/work !davenant:/export/mp3 !kadath:/tmp #!elmyra:/mnt/fat/data end chiark-utils-4.4.2build1/backup/examples/relativity/ifsys.pt20000664000000000000000000000007712423200672021156 0ustar / dump xenophobe / dump /dos/c cpio /dos/d cpio end chiark-utils-4.4.2build1/backup/examples/relativity/ifsys.pt10000664000000000000000000000022412423200672021147 0ustar / dump kadath /usr dump kadath /var dump kadath /home dump kadath /var dump khem /home dump khem / dump khem /usr dump khem end chiark-utils-4.4.2build1/backup/examples/relativity/fsys.pt00000664000000000000000000000005512423200672020777 0ustar include ifsys.prefixes include ifsys.pt0 end chiark-utils-4.4.2build1/backup/examples/relativity/ifsys.prefixes0000664000000000000000000000122712423200672022274 0ustar prefix davenant ssh -o 'BatchMode yes' -c blowfish -o 'Compression yes' davenant 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/usr/sbin:$PATH' prefix xenophobe ssh -o 'BatchMode yes' -c blowfish -o 'Compression no' xenophobe 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/usr/sbin:$PATH' prefix khem ssh -o 'BatchMode yes' -c blowfish -o 'Compression yes' -o 'CompressionLevel 1' khem 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/usr/sbin:$PATH' prefix kadath ssh -o 'BatchMode yes' -c blowfish -o 'Compression yes' -o 'CompressionLevel 1' kadath -l ian 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/usr/sbin:$PATH really' prefix-df kadath /bin/df -t noprocfs,nfs end chiark-utils-4.4.2build1/backup/examples/relativity/tape.t00000664000000000000000000000003412423200672020561 0ustar filesystems pt0 next t1 end chiark-utils-4.4.2build1/backup/examples/relativity/ifsys.pt00000664000000000000000000000025012423200672021145 0ustar /usr/src dump davenant /var dump davenant /u dump davenant / dump davenant /export/mirror/work cpio davenant /usr dump davenant /boot dump davenant end chiark-utils-4.4.2build1/backup/examples/relativity/settings.pl0000664000000000000000000000123312423200672021562 0ustar # configuration, for putting in /etc/chiark-backup chdir '/var/lib/chiark-backup' or die $!; push(@INC,'/usr/share/chiark-backup'); $ENV{'PATH'} =~ s,^/usr/share/chiark-backup,,; $ENV{'PATH'}= '/usr/share/chiark-backup:'.$ENV{'PATH'}; # This sets both blocksizes to 512b. Note that both must be the # same if using the zftape floppy tape driver, since that requires # blocks to be the right size, but dd with the obs=10k option # doesn't pad the final block to the blocksize... $blocksize= 1; $blocksizebytes= 512*$blocksize; $softblocksizekb= 1; $softblocksizebytes= 1024*$softblocksizekb; $tapename= 'st0'; $tape= "/dev/$tapename"; $ntape= "/dev/n$tapename"; 1; chiark-utils-4.4.2build1/backup/examples/relativity/tape.ks0000664000000000000000000000002012423200672020646 0ustar incremental end chiark-utils-4.4.2build1/backup/examples/chiark/0000775000000000000000000000000012423200672016433 5ustar chiark-utils-4.4.2build1/backup/examples/chiark/tape.b10000664000000000000000000000003412423200672017605 0ustar filesystems pt1 next c0 end chiark-utils-4.4.2build1/backup/examples/chiark/SYMLINKS.tar0000664000000000000000000002400012423200672020410 0ustar tape.5060120777000014400001440000000000007360164221011507 2tape.a0ustar ianiantape.5070120777000014400001440000000000007360164221011511 2tape.b0ustar ianiantape.5080120777000014400001440000000000007360164221011513 2tape.c0ustar ianiantape.5090120777000014400001440000000000007360164221011515 2tape.d0ustar ianiantape.5100120777000014400001440000000000007360164221011575 2tape.kaustar ianiantape.5120120777000014400001440000000000007360164221011600 2tape.kbustar ianiantape.5150120777000014400001440000000000007360164221011510 2tape.b0ustar ianiantape.5170120777000014400001440000000000007360164221011605 2tape.kbustar ianiantape.5190120777000014400001440000000000007360164221011514 2tape.b0ustar ianianchiark-utils-4.4.2build1/backup/examples/chiark/fsys.all0000664000000000000000000000004612423200672020111 0ustar include fsys.pt0 include fsys.pt1 end chiark-utils-4.4.2build1/backup/examples/chiark/tape.d00000664000000000000000000000003412423200672017606 0ustar filesystems all next a0 end chiark-utils-4.4.2build1/backup/examples/chiark/fsys.pt10000664000000000000000000000006712423200672020050 0ustar /u2 dump /usr dump /var/spool/news/chiark cpio end chiark-utils-4.4.2build1/backup/examples/chiark/warnings.soon0000775000000000000000000000005012423200672021161 0ustar T 45 "in 1 minute" T 15 "in 15 seconds" chiark-utils-4.4.2build1/backup/examples/chiark/tape.d10000664000000000000000000000003412423200672017607 0ustar filesystems pt1 next a0 end chiark-utils-4.4.2build1/backup/examples/chiark/tape.c00000664000000000000000000000003412423200672017605 0ustar filesystems all next d0 end chiark-utils-4.4.2build1/backup/examples/chiark/warnings.now0000775000000000000000000000000012423200672021001 0ustar chiark-utils-4.4.2build1/backup/examples/chiark/tape.b00000664000000000000000000000003412423200672017604 0ustar filesystems all next c0 end chiark-utils-4.4.2build1/backup/examples/chiark/expected-diffs0000664000000000000000000000006212423200672021246 0ustar /var/spool/news/chiark !/var/spool/news !/tmp end chiark-utils-4.4.2build1/backup/examples/chiark/fsys.pt00000664000000000000000000000004312423200672020041 0ustar / dump /var dump /u dump end chiark-utils-4.4.2build1/backup/examples/chiark/settings.sh0000775000000000000000000000053112423200672020631 0ustar # shell script fragment setting options # defaults for currently implemented parameters are #lvm_vg= #lvm_lv=chiark_backup #lvm_lvsize_opts= # -l (lvm) # -l (remountrocp) #lvm_lvtools_opts='-A n' #lvm_lvcreate_opts= #lvm_lvcreate_args= chiark-utils-4.4.2build1/backup/examples/chiark/tape.c10000664000000000000000000000003412423200672017606 0ustar filesystems pt1 next d0 end chiark-utils-4.4.2build1/backup/examples/chiark/tape.ka0000664000000000000000000000002012423200672017671 0ustar incremental end chiark-utils-4.4.2build1/backup/examples/chiark/tape.a00000664000000000000000000000003412423200672017603 0ustar filesystems all next b0 end chiark-utils-4.4.2build1/backup/examples/chiark/settings.pl0000664000000000000000000000056512423200672020636 0ustar # chdir '/var/lib/chiark-backup' or die $!; push(@INC,'/usr/share/chiark-backup'); $ENV{'PATH'} =~ s,^/usr/share/chiark-backup:,,; $ENV{'PATH'}= '/usr/share/chiark-backup:'.$ENV{'PATH'}; $blocksize= 1; $blocksizebytes= 512*$blocksize; $softblocksizekb= 1; $softblocksizebytes= 1024*$softblocksizekb; $tapename= 'st0'; $tape= "/dev/$tapename"; $ntape= "/dev/n$tapename"; 1; chiark-utils-4.4.2build1/backup/examples/chiark/tape.kb0000664000000000000000000000002012423200672017672 0ustar incremental end chiark-utils-4.4.2build1/backup/snap-common0000664000000000000000000000322112423200672015524 0ustar # sourced by snap/lvm and snap/remountrocp #---------- common arg parsing nargs=$# opmode="$1" vardir="$2" device="$3" mountpoint="$4" lvm_lv=chiark-backup lvm_lvtools_opts='-A n' lvm_lvcreate_opts= lvm_lvcreate_args= test ! -f /etc/chiark-backup/settings.sh || . /etc/chiark-backup/settings.sh case "$nargs.$opmode" in 4.snap|2.drop) ;; *) cat >&2 </ case "$device" in /dev/mapper/*) device="`printf '%s' "$device" | perl -pe ' s,^/dev/mapper/,,; die if m,/,; s,\-\-,!,g; s,\-,/,g; s,\!,-,g; s,^,/dev/,; '`" ;; esac } lvmdevice2vgroup () { vgroup="${device#/dev/}" vgroup="${vgroup%/*}" } lvmdropcore () { snmnt="$vardir/snap-mount" umount -v "$snmnt" || true test ! -d "$snmnt" || rmdir -- "$snmnt" || rm -f "$snmnt" set +e old_lv_dev="$(readlink $vardir/snap-device)" rc=$? set -e if [ $rc = 0 ]; then set +e lvchange $lvm_lvtools_opts -a n $old_lv_dev lvremove -f $lvm_lvtools_opts $old_lv_dev set -e rm $vardir/snap-device fi } lvmextentscore1 () { # vgroup must be set vgdisplay_out="$(really vgdisplay -c "$vgroup")" extents="$(printf "%s" "$vgdisplay_out" | awk -F: '{print $16}')" extsize="$(printf "%s" "$vgdisplay_out" | awk -F: '{print $13}')" } lvmextentscore2 () { if [ $extents2 -lt $extents ]; then extents=$extents2; fi lvm_lvsize_opts="-l $extents" } lvmcreatecore1 () { # vgroup must be set lvpath="/dev/$vgroup/$lvm_lv" ln -s -- "$lvpath" "$vardir"/snap-device sync } chiark-utils-4.4.2build1/backup/whatsthis0000775000000000000000000001033212423200672015317 0ustar #!/usr/bin/perl # whatsthis # read an id off the tape and display it to the user # This file is part of chiark backup, a system for backing up GNU/Linux and # other UN*X-compatible machines, as used on chiark.greenend.org.uk. # # chiark backup is: # Copyright (C) 1997-1998,2000-2001,2007 # Ian Jackson # Copyright (C) 1999 Peter Maydell # # This is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3, or (at your option) any later version. # # This is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. # First rough hack; mostly just code nabbed from full. # --list assumes the dump type was 'zafio', which is a bit bogus. # whatsthis : no args => just print tapeid # whatsthis --list [n] : print tapeid then list archive n (if n omitted, # 0 is assumed.) Note that archives are numbered from zero! sub rewind(); sub stopandsay(@); $etc='/etc/chiark-backup'; require "$etc/settings.pl"; require 'backuplib.pl'; $| = 1; # This isn't intended to be run automatically, so don't bother # with setting status. # If we are run with the argument --list then list the backup to # stdout. Otherwise just print the tape ID. $listing = 0; # default : don't list $listing = 1 if ($ARGV[0] eq '--list'); $listarchive = 0; $listarchive = $ARGV[1] if defined $ARGV[1]; print "Trying to read tape ID from currently inserted tape...\n"; unlink 'TAPEID'; system "mt -f $ntape setblk $blocksizebytes"; $? and die $?; system "dd if=$ntape bs=${blocksize}b count=10 | tar -b$blocksize -vvxf - TAPEID"; $? and stopandsay "Failed to read TAPEID.\n"; if (!open(T, "TAPEID")) { stopandsay "Tape has no ID label.\n"; } # OK, there's a TAPEID file, read the ID and check for sanity. chomp($tapeid= ); if ($tapeid =~ m/[^0-9a-zA-Z]/) { stopandsay "Tape has a bad (non-alphanumeric) TAPEID ($&).\n"; } elsif (! $tapeid =~ m/[0-9a-zA-Z]/) { stopandsay "Empty TAPEID.\n"; } print "TAPEID is $tapeid.\n"; close T; # If we aren't listing the tape contents, we can just rewind the tape # and exit. if (!$listing) { rewind(); exit; } # List the contents of archive $listarchive on the tape. # We are already at the right place for the first archive # (after the TAPEID). # For any other archive, we skip forwards to the start of that archive. if ($listarchive) { system "mt -f $ntape fsf $listarchive"; $? and stopandsay "Couldn't skip forward to archive $listarchive."; } # Use file to figure out what the archive type is # This doesn't seem to work too well, so I'm disabling it -- PMM #$ftype = `dd if=$ntape ibs=$blocksizebytes | file -`; #$? and stopandsay "couldn't determine file type: $?"; $ftype = 'POSIX tar'; # What we want to do here is roughly: # dd if=$ntape ibs=$blocksizebytes | readbuffer | afio ??? # # where the afio options are such as to list an archive created with # afio -b $softblocksizebytes -Zvo if ($ftype =~ /POSIX tar/) { # POSIX tar archive; we read it with cpio $reader = "cpio -it -C$softblocksizebytes -Hustar"; } elsif ($ftype =~ /ASCII cpio/) { $reader = "afio -b $softblocksizebytes -Zt -"; } elsif ($ftype =~ /dump file/) { stopandsay "sorry: can't list dump files yet"; } else { stopandsay "listing failed: unknown archive type"; } # Now back up so we can read the file again properly #system "mt -f $ntape bsf 1"; $? and stopandsay "couldn't backspace tape: $?"; system "dd if=$ntape ibs=$blocksizebytes | readbuffer | $reader"; $? and stopandsay "listing failed: $?"; # All's well, stop here. print "Listing complete.\n"; rewind(); exit; # Rewind the tape. sub rewind () { system "mt -f $tape rewind"; $? and die $?; } # Print the given message, rewind the tape and exit failure. sub stopandsay(@) { print @_; rewind(); exit(1); } chiark-utils-4.4.2build1/backup/snaprsync0000775000000000000000000001053712423200672015330 0ustar #!/bin/bash # # usage: snaprsync ... # is --= # are assigned to unused mandatory values in order # mandatory: # rhost device mountpoint localarea # optional: localprevious= snapkind=lvm rsharedir=/usr/share/chiark-backup retcdir=/etc/chiark-backup rvardir=/var/lib/chiark-backup bwlimit= subdir=. rsyncopts= rsynccompress=z sshopts= summer=summer # snaprsync # # This file is part of chiark backup, a system for backing up GNU/Linux and # other UN*X-compatible machines, as used on chiark.greenend.org.uk. # # chiark backup is: # Copyright (C) 1997-1998,2000-2001,2007 # Ian Jackson # Copyright (C) 1999 Peter Maydell # # This is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3, or (at your option) any later version. # # This is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. set -e badusage () { echo >&2 "snaprsync: bad usage: $1"; exit 12; } nb_echo () { (echo "$@"); } # See Debian #382798 x () { nb_echo "+ $@"; "$@"; } xspawned () { eval "${1}pid=$!; nb_echo \"+[$!] ($1) &\";"; } xwait () { eval "nb_echo \"+[\$${1}pid] ($1)...\"; wait \$${1}pid;"; } while true; do case "$1" in --?*=*) name=${1#--}; name=${name%%=*} value=${1#--*=} case "$name" in rhost|device|mountpoint|localarea);; localprevious|snapkind|rsharedir|retcdir|rvardir|bwlimit);; subdir|rsyncopts|rsynccompress|sshopts|summer);; *) badusage "unknown setting $name";; esac eval "$name=\$value" ;; --) shift; break ;; -*) badusage "unknown option $1" ;; *) break ;; esac shift done for name in rhost device mountpoint localarea; do eval "value=\$$name" if [ "x$value" != x ]; then continue; fi if [ $# = 0 ]; then badusage "no value for setting $name"; fi eval "$name=$1" shift done datefmt='%Y-%m-%d %H:%M:%S Z' rsync="rsync ${bwlimit:+--bwlimit} $bwlimit" export RSYNC_RSH="ssh -o compression=no $sshopts" sshpfx='PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin; export PATH; ' ssh $sshopts $rhost "$sshpfx date -u '+$rhost $datefmt start'" ssh $sshopts $rhost "$sshpfx id" ssh $sshopts $rhost "$sshpfx ls -d $rsharedir" ssh $sshopts $rhost "$sshpfx ls -d $rvardir" test -d $localarea || x mkdir $localarea ournode=`uname -n` rsumsfile=for-$ournode.sums summer="$summer -ACDBtqfx" td=/dev/enoent rc=12 trap 'rm -rf $td; exit $rc' 0 td=`mktemp -td` mkfifo -m 600 $td/sentinel exec 4<>$td/sentinel x ssh $sshopts $rhost "$sshpfx $rsharedir/snap-drop $rvardir" x ssh $sshopts $rhost " $sshpfx set -e cd $rvardir echo '$retcdir/snap/$snapkind drop $rvardir' >snap-drop.new mv snap-drop.new snap-drop " x ssh $sshopts $rhost "$sshpfx $retcdir/snap/$snapkind snap $rvardir $device $mountpoint" ssh $sshopts $rhost <$td/sentinel 4<&- " $sshpfx set -e date -u '+$rhost $datefmt main' exec 3<&0 0$rsumsfile cd snap-mount $summer . >&3 date -u '+$rhost $datefmt sumsdone' cd .. " & xspawned rsum x $rsync -aHSx$rsynccompress --numeric-ids --delete $rsyncopts \ ${localprevious:+--link-dest} $localprevious \ $rhost:$rvardir/snap-mount/$subdir $localarea/. date -u "+ $datefmt rsyncdone" exec 3>$localarea,lsums (cd $localarea && \ $summer . >&3) & xspawned lsum exec 3>&- xwait rsum exec 4<&- date -u "+ $datefmt sumsdone" x ssh $sshopts $rhost "$sshpfx $rsharedir/snap-drop $rvardir" if [ "x${localprevious}" != x ] && test -f "$localprevious,rsums"; then cp "$localprevious,rsums" "$localarea,rsums" fi x $rsync -pI \ $rhost:$rvardir/$rsumsfile \ "$localarea,rsums" xwait $lsum date -u "+ $datefmt checking" set +e diff -u <(sed -e 's/^mountpoint/dir /' "$localarea,rsums") \ "$localarea,lsums" >"$localarea,sumsdiff" diffrc=$? set -e test $diffrc = 0 || test $diffrc = 1 date -u "+ $datefmt checked $diffrc" rc=$diffrc chiark-utils-4.4.2build1/backup/nosnap0000775000000000000000000000072612423200672014605 0ustar #!/bin/sh set -e removes () { if test -L "$vardir/snap-mount"; then rm -f -- "$vardir/snap-mount" elif test -d "$vardir/snap-mount"; then rmdir -- "$vardir/snap-mount" fi rm -f -- "$vardir/snap-device" } vardir="$2" case "$#.$1" in 2.drop) removes ;; 4.snap) removes ln -s -- "$3" "$vardir/snap-device" ln -s -- "$4" "$vardir/snap-mount" ;; *) cat >&2 <<'END' usage: .../nosnap snap VARDIR DEV MOUNT .../nosnap drop VARDIR END exit 1 ;; esac chiark-utils-4.4.2build1/backup/increm0000775000000000000000000001101112423200672014551 0ustar #!/usr/bin/perl # increm # Do an incremental backup; ONLY for invocation by full ! # # This file is part of chiark backup, a system for backing up GNU/Linux and # other UN*X-compatible machines, as used on chiark.greenend.org.uk. # # chiark backup is: # Copyright (C) 1997-1998,2000-2001,2007 # Ian Jackson # Copyright (C) 1999 Peter Maydell # # This is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3, or (at your option) any later version. # # This is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. # We are invoked by full if the tape description file says that it is # for an incremental backup. We expect two commandline argument which # is the ID string of the tape to use, and the description (which # includes ID and function). BEGIN { $etc= '/etc/chiark-backup'; require "$etc/settings.pl"; require 'backuplib.pl'; } $|=1; @ARGV==2 or die; ($tapeid,$tapedesc)= @ARGV; print "Running incremental onto $tapedesc ...\n" or die $!; # Just check that we weren't passed a bogus ID (the tape description # file for incrementals is just 'incremental\nend\n') open T,"$etc/tape.$tapeid" or die "Tape $tapeid not found: $!\n"; close T; # This is only used for the 'next FULL backup is X' message at the end. open NF,"next-full" or die $!; chomp($next= ); close NF or die $!; setstatus "FAILED during incremental"; # Read the number of the incremental involved # (ie, (how many files are already on the tape) - 1) open A,"increm-advance" or die $!; chomp($advance= ); close A or die $!; # better be a decimal number $advance =~ m/^\d+$/ or die "$advance ?"; # Get a list of all filesystems readfsys('all'); openlog(); # Rewind the tape and if we are the first incremental on the tape then # write the TAPEID record, otherwise skip forward to the correct point. # (full will already have checked that this is the right tape before # it invoked us, so no need to read the existing TAPEID record first.) runsystem("mt -f $ntape rewind"); if ($advance == 1) { writetapeid($tapeid,$tapedesc); } else { runsystem("mt -f $ntape fsf $advance"); $currenttapefilenumber= $advance; } sub closepipes () { close(DUMPOR); close(BUFOR); close(DUMPOW); close(BUFOW); close(GZOR); close(GZOW); } setstatus "PROBLEMS during incremental dump"; for $tf (@fsys) { parsefsys(); prepfsys(); $bufir='DUMPOR'; $ddcmd= "$nasty dd ibs=$softblocksizebytes obs=$blocksizebytes of=$ntape"; pipe(DUMPOR,DUMPOW) or die $!; pipe(BUFOR,BUFOW) or die $!; $gz= $gzi if length $gzi; if ($gz) { $bufir='GZOR'; pipe(GZOR,GZOW) or die $!; $ddcmd .= " conv=sync"; } if ($dopt{'noinc'}) { pboth("Incrementals of $atf_print ($prefix) suppressed in config.\n"); } if ($tm eq 'dump') { $dumplabel= $pcstr.$atf_print.'$'; $dumpcmd= "dump 1Lbfu $dumplabel $softblocksizekb - $atf"; } elsif ($tm eq 'gtar') { $dumpcmd= "tar NCcfl $fsidfile $atf - ."; } else { pboth("Not dumping $atf_print ($prefix) - not supported.\n"); next; } nexttapefile("inc $prefix:$atf_print"); # Same trick as full uses to do a pipeline whilst keeping track # of all exit statuses: # dump /dev/null startprocess '&DUMPOW',"$nice ".$rstr.$dumpcmd; if ($gz) { startprocess '<&DUMPOR','>&GZOW',"$nice gzip -v$gz"; } startprocess "<&$bufir",'>&BUFOW',"$nasty writebuffer"; startprocess '<&BUFOR','>/dev/null',"$nasty $ddcmd"; closepipes(); endprocesses(); # advance is a file counter, so it needs to be updated for each # dump we do to tape. $advance++; finfsys(); } # Rewind the tape, and increment the counter of incremental backups. runsystem("mt -f $tape rewind"); open IAN,">increm-advance.new" or die $!; print IAN "$advance\n" or die $!; close IAN or die $!; rename 'increm-advance.new','increm-advance' or die $!; pboth("Next FULL dump tape is $next\n"); setstatus "INCREMENTAL successful: $tapedesc, next full is $next"; exit 0; chiark-utils-4.4.2build1/backup/remountrocp0000775000000000000000000000347112423200672015664 0ustar #!/bin/bash # invoked by backup scripts as # remountrocp snap $vardir $device $mountpoint # remounts $mountpoint readonly # copies data to $vardir/snap-mount # remounts $mountpoint readwrite # remountrocp drop $vardir # deletes $vardir/snap-mount set -e snapkind=remountrocp : ${lvm_vg:=} remountrocp_fs=ext2 . ${CHIARK_BACKUP_SHAREDIR:-/usr/share/chiark-backup}/snap-common #---------- clean up anything vgroup=$lvm_vg lvmdropcore lastsettings="$vardir/remountrocp-settings" test ! -f $lastsettings || . $lastsettings if test "$opmode" = drop; then test -z "$last_mountpoint" || mount -o remount,rw $last_mountpoint rm -f $lastsettings echo 'remountrocp snap dropped' exit 0 fi #---------- create snapshot if [ -z "$lvm_lvsize_opts" ]; then lvmextentscore1 df_out="$(really df -P --block-size=$extsize $mountpoint)" extents2="$(printf "%s" "$df_out" | awk '/^\// {print $3}')" extents2=$(( ($extents2*150+102399)/102400 + 4 )) lvmextentscore2 fi lvmcreatecore1 cat >$lastsettings.new <&2 'cannot remount readonly' exit 1 fi sleep 1 done trap "set +e; mount -o remount,rw $mountpoint; exit 12" 0 echo ' source remounted readonly, copying...' cp -ax -- "$mountpoint/." "$snmnt/." echo ' finalising...' mount -o remount,rw "$mountpoint" trap '' 0 mount -o remount,ro "$lvpath" echo 'remountrocp snap activated' chiark-utils-4.4.2build1/backup/labeltape0000775000000000000000000000151312423200672015233 0ustar #!/usr/bin/perl use POSIX; $etc= '/etc/chiark-backup'; require "$etc/settings.pl"; require 'backuplib.pl'; while ($ARGV[0] =~ m/^-/) { $_= shift @ARGV; last if m/^\-$/; s/^\-//; while (length) { if (s/^f//) { $force=1; } else { die "$0: unknown option -$_\n"; } } } @ARGV==1 or die "$0: need 1 arg, new TAPEID\n"; ($newid)= @ARGV; open LOG, ">/dev/null" or die $!; readtapeid_raw(); if (!open T,'TAPEID') { $!==&ENOENT or die $!; } else { chomp($oldid= ); close T or die $!; print "Tape is currently labelled \`$oldid'\n" or die $!; die "$0: use -f to force relabelling\n" unless $force; } open T,'>TAPEID' or die $!; print T "$newid\n" or die $!; close T or die $!; writetapeid($newid,'tapeid set manually'); rewind_raw(); print "Labelled tape \`$newid'\n" or die $!; exit 0; chiark-utils-4.4.2build1/backup/loaded0000775000000000000000000000256312423200672014540 0ustar #!/bin/sh # loaded # Entry point for sysadmin to state that we've loaded a tape # # This file is part of chiark backup, a system for backing up GNU/Linux and # other UN*X-compatible machines, as used on chiark.greenend.org.uk. # # chiark backup is: # Copyright (C) 1997-1998,2000-2001,2007 # Ian Jackson # Copyright (C) 1999 Peter Maydell # # This is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3, or (at your option) any later version. # # This is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. set -e cd /var/lib/chiark-backup if test -f "/etc/chiark-backup/tape.$1" then echo "$1" >really-TAPEID echo "Will assume tape is $1 unless I discover otherwise." else if [ "x$1" != x ]; then echo "Tape $1 not found."; fi echo "Will only use tape if it has a TAPEID." rm -f really-TAPEID fi chiark-utils-4.4.2build1/backup/driver0000775000000000000000000000372412423200672014603 0ustar #!/bin/sh # driver # entry point for inittab (or perhaps cron) to run the backups. # # This file is part of chiark backup, a system for backing up GNU/Linux and # other UN*X-compatible machines, as used on chiark.greenend.org.uk. # # chiark backup is: # Copyright (C) 1997-1998,2000-2001,2007 # Ian Jackson # Copyright (C) 1999 Peter Maydell # # This is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3, or (at your option) any later version. # # This is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. cd /var/lib/chiark-backup PATH=/usr/share/chiark-backup:$PATH export PATH if [ "x$1" != test ]; then stty sane stty -isig fi rm -f this-status p p2 echo 'FAILED to start dump script' >this-status # Here we go : run 'full', which (name notwithstanding) handles # both full and incremental backups, according to the ID of the # tape in the drive. (full; snap-drop) 2>&1 | tee this-log status=`cat this-status 2>/dev/null` # Mail a report to somewhere appropriate; -odq removed (means just # queue message, don't try to deliver) because it just delays the # message (you might want that if mail was one of the services turned # off for the duration of the backup, though). cat < # Copyright (C) 1999 Peter Maydell # # This is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3, or (at your option) any later version. # # This is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. # All filesystems must either be backed up in both full and # incremental dumps or listed as exceptions. BEGIN { $etc= '/etc/chiark-backup'; require "$etc/settings.pl"; require 'backuplib.pl'; } $|=1; open X,'last-tape' or die $!; chomp($tape= ); close X or die $!; while (!defined $tapedone{$tape}) { open X,"$etc/tape.$tape" or die "$tape $!"; $fsg=''; $next=''; for (;;) { $_= or die $1; chomp; s/\s*$//; last if m/^end$/; next unless m/\S/; next if m/^\#/; if (m/^filesystems (\w+)$/) { $fsg= $1; } elsif (m/^next (\w+)$/) { $next=$1; } else { die "$tape $_ ?"; } } length $fsg or die "$tape $!"; length $next or die "$tape $!"; push @{$fsgdone{$fsg}}, $tape; $tapedone{$tape}=1; $tape= $next; } sub checkdevspec ($$$) { my ($atf,$devspec,$why) = @_; push @{ $devspec{$atf}{$devspec} }, $why; } for $fsg (sort keys %fsgdone) { print "filesystem group $fsg: ".join(' ',@{$fsgdone{$fsg}}).":\n "; @fsys= (); readfsys($fsg); for $tf (@fsys) { parsefsys(); $pstr= "$pcstr$atf"; &e("dumped twice ($backed{$pstr}, $fsg): $pstr") if defined $backed{$pstr}; $backed{$pstr}= $fsg; checkdevspec($pstr,"$pcstr$dev","filesystem group $fsg") if length $dev; print " $pstr"; } print "\n"; } print "incremental group:\n "; @fsys= (); readfsys('all'); for $tf (@fsys) { parsefsys(); $pstr= "$pcstr$atf"; $incrd{$pstr}= $fsg; checkdevspec($pstr,"$pcstr$dev","incremental group") if length $dev; print " $pstr"; } print "\n"; for $pfx ('', sort keys %prefix) { $rstr= length($pfx) ? $prefix{$pfx}.' ' : ''; $dfstr= exists($prefixdf{$pfx}) ? $prefixdf{$pfx} : 'df -P --no-sync -xiso9660 -xnfs -xproc -xtmpfs'; $cmd= "$rstr $dfstr"; open X, "$cmd |" or die $!; $_= ; m/^Filesystem/ or die "$cmd => $_ ?"; $prefix= length($pfx) ? $pfx : ''; $pcstr= length($pfx) ? "$pfx:" : ''; print "mount points: $prefix:"; while () { chomp; next if m,^procfs\s,; m,^/dev/(\S+)\s.*\s(/\S*)\s*$, or die "$_ ?"; ($dev,$mp) = ($1,$2); checkdevspec("$pcstr$mp","$pcstr/dev/$dev","df"); $mounted{"$pcstr$mp"}="$pcstr$dev"; print " $1-$2"; if (defined($backto= $backed{"$pcstr$mp"})) { if (m,^/dev/\S+\s+\d+\s+(\d+)\s,) { $usedkb{$backto} += $1; $countedkb{"$pcstr$mp"}++; } } } print "\n"; $!=0; close(X); $? and die "$? $!"; } foreach $fsg (keys %usedkb) { print "filesystem group $fsg: $usedkb{$fsg} 1K-blocks raw accounted\n"; } foreach $fsg (keys %backed) { next if $countedkb{$fsg}; print "unaccounted filesystem: $fsg\n"; } foreach $dsk (keys %devspec) { if (keys %{ $devspec{$dsk} } != 1) { foreach $devspec (keys %{ $devspec{$dsk} }) { &e("inconsistent devices for $dsk: $devspec (". join(', ', @{ $devspec{$dsk}{$devspec} }).")"); } } } # We check that all mounted filesystems are dumped and all # filesystems to be dumped are mounted. The expected-diffs # config file allows us to make exceptions. # eg: # #expect disk2 to be mounted but not dumped # !/disk2 # # CD may or may not be mounted but should not be dumped in either case # ?/cdrom open Z,"$etc/expected-diffs" or die $!; for (;;) { $_= or die; chomp; s/\s*$//; last if m/^end$/; next unless m/^\S/; next if m/^\#/; if (s/^\?//) { print "non-permanent filesystem expected not to be dumped: $_\n"; if (defined($mounted{$_})) { delete $mounted{$_}; } } elsif (s/^\!//) { &e("expected not to be dumped, but not a mount point: $_") unless defined($mounted{$_}); print "filesystem expected not to be dumped: $_\n"; delete $mounted{$_}; } else { &e("non-filesystem expected to be dumped is mounted: $_ on $mounted{$_}") if defined($mounted{$_}); $mounted{$_}= 'expected-diffs'; print "non-filesystem expected to be dumped: $_\n"; } } for $fs (sort keys %backed) { length($mounted{$fs}) || &e("dumped ($backed{$fs}), not a mount point: $fs"); } for $fs (sort keys %incrd) { length($mounted{$fs}) || &e("increm'd ($incrd{$fs}), not a mount point: $fs"); } for $fs (sort keys %mounted) { length($backed{$fs}) || &e("mount point ($mounted{$fs}), not dumped: $fs"); } for $fs (sort keys %mounted) { length($incrd{$fs}) || &e("mount point ($mounted{$fs}), not increm'd: $fs"); } $emsg.= "configuration ok\n" unless $e; print STDERR $emsg; exit($e); sub e { $emsg.="** @_\n"; $e=1; } chiark-utils-4.4.2build1/backup/lvm0000775000000000000000000000175512423200672014110 0ustar #!/bin/bash # invoked by backup scripts as # lvm snap $vardir $device $mountpoint # creates and mounts on $vardir/snap-mount # creates $vardir/snap-device -> device # lvm drop $vardir set -e snapkind=lvm . /usr/share/chiark-backup/snap-common #---------- clean up anything lvmdropcore if test "$opmode" = drop; then echo 'lvm snap dropped' exit 0 fi #---------- create snapshot fstype="$(mount | sed -n \ "s,^$device on $mountpoint type \([a-z0-9][a-z0-9]*\) .*,-t \1 ,p")" lvmunmapperdevice lvmdevice2vgroup if [ -z "$lvm_lvsize_opts" ]; then lvmextentscore1 lvdisplay_out="$(really lvdisplay -c "$device")" extents2="$(printf "%s" "$lvdisplay_out" | awk -F: '{print $8}')" extents2=$(( $extents2 + ($extents2+9)/10 - 1 )) lvmextentscore2 fi lvmcreatecore1 lvcreate -s \ $lvm_lvtools_opts \ $lvm_lvsize_opts \ -n $lvm_lv \ $lvm_lvcreate_opts "$device" $lvm_lvcreate_args mkdir -- "$snmnt" mount -v -r $fstype $lvm_mount_opts "$lvpath" "$snmnt" echo 'lvm snap activated' chiark-utils-4.4.2build1/backup/backuplib.pl0000664000000000000000000001650112423200672015650 0ustar # backuplib.pl # core common routines # # This file is part of chiark backup, a system for backing up GNU/Linux and # other UN*X-compatible machines, as used on chiark.greenend.org.uk. # # chiark backup is: # Copyright (C) 1997-1998,2000-2001,2007 # Ian Jackson # Copyright (C) 1999 Peter Maydell # # This is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3, or (at your option) any later version. # # This is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. require IO::File; $nice='nice ' if !defined $nice; sub printdate () { print scalar(localtime),"\n"; } # Set status info -- we write the current status to a file # so if we hang or crash the last thing written to the file # will tell us where we were when things went pear-shaped. sub setstatus ($) { open S, ">this-status.new" or die $!; print S $_[0],"\n" or die $!; close S or die $!; rename "this-status.new","this-status" or die $!; } # startprocess, endprocesses, killprocesses are # used to implement the funky pipeline stuff. sub startprocess ($$$) { my ($i,$o,$c) = @_; pboth(" $c\n"); defined($p= fork) or die $!; if ($p) { $processes{$p}= $c; return; } open STDIN,"$i" or die "$c stdin $i: $!"; open STDOUT,"$o" or die "$c stdout $o: $!"; &closepipes; exec $c; die "$c: $!"; } sub rewind_raw () { runsystem("mt -f $tape rewind"); } sub readtapeid_raw () { open T, ">>TAPEID" or die $!; close T; unlink 'TAPEID' or die $!; rewind_raw(); system "mt -f $tape setblk $blocksizebytes"; $? and die $?; system "dd if=$tape bs=${blocksize}b count=10 ". "| tar -b$blocksize -vvxf - TAPEID"; } sub runsystem ($) { pboth(" $_[0]\n"); system $_[0]; $? and die $?; } sub pboth ($) { my ($str) = @_; print LOG $str or die $!; print $str or die $!; } sub nexttapefile ($) { my ($what) = @_; $currenttapefilenumber++; $currenttapefilename= $what; pboth(sprintf "writing tape file #%d (mt fsf %d): %s\n", $currenttapefilenumber, $currenttapefilenumber-1, $what); } sub writetapeid ($$) { open T, ">TAPEID" or die $!; print T "$_[0]\n$_[1]\n" or die $!; close T or die $!; $currenttapefilenumber= 0; nexttapefile('TAPEID'); system "tar -b$blocksize -vvcf TAPEID.tar TAPEID"; $? and die $?; system "dd if=TAPEID.tar of=$ntape bs=${blocksize}b count=10"; $? and die $?; } sub endprocesses () { while (keys %processes) { $p= waitpid(-1,0) or die "wait: $!"; if (!exists $processes{$p}) { warn "unknown pid exited: $p, code $?\n"; next; } $c= $processes{$p}; delete $processes{$p}; $? && die "error: command gave code $?: $c\n"; } pboth(" ok\n"); } sub killprocesses { for $p (keys %processes) { kill 15,$p or warn "kill process $p: $!"; } undef %processes; } # Read a fsys.foo filesystem group definition file. # Syntax is: empty lines and those beginning with '#' are ignored. # Trailing whitespace is ignored. Lines of the form 'prefix foo bar' # are handled specially, as arex lines 'exclude regexp'; otherwise # we just shove the line into @fsys and let parsefsys deal with it. sub readfsysfile ($) { my ($fn) = @_; my ($fh,$sfn); $fh= new IO::File "$fn", "r" or die "cannot open fsys file $fn ($!).\n"; for (;;) { $!=0; $_= <$fh> or die "unexpected EOF in $fn ($!)\n"; chomp; s/\s*$//; last if m/^end$/; next unless m/\S/; next if m/^\#/; if (m/^prefix\s+(\w+)\s+(\S.*\S)$/) { $prefix{$1}= $2; } elsif (m/^prefix\-df\s+(\w+)\s+(\S.*\S)$/) { $prefixdf{$1}= $2; } elsif (m/^snap(?:\=(\w+))?\s+(\w+)\s+(\w+)$/) { push @excldir,$1; } elsif (m/^excludedir\s+(\S.*\S)$/) { push @excldir,$1; } elsif (m/^exclude\s+(\S.*\S)$/) { push @excl,$1; } elsif (m/^include\s+(\S.*\S)$/) { $sfn = $1; $sfn =~ s/^\./fsys./; $sfn = "$etc/$sfn" unless $sfn =~ m,^/,; readfsysfile($sfn); } else { push @fsys,$_; } } close $fh or die $!; } sub readfsys ($) { my ($fsnm) = @_; my ($fsf); $fsf= "$etc/fsys.$fsnm"; stat $fsf or die "Filesystems $fsnm unknown ($!).\n"; readfsysfile($fsf); } # Parse a line from a filesystem definition file. We expect the line # to be in $tf. sub parsefsys () { my ($dopts,$dopt); if ($tf =~ m#^(/\S*)\s+(\w+)([,=0-9a-z]*)$#) { # Line of form '[/device:]/file/system dumptype[,options]' $atf= $1; $tm= $2; $dopts= $3; $prefix= ''; $pcstr= ''; $rstr= ''; } elsif ($tf =~ m#^(/\S*)\s+(\w+)([,=0-9a-z]*)\s+(\w+)$#) { # Line of form '[/device:]/file/system dumptype[,options] prefix' # (used for remote backups) $atf= $1; $tm= $2; $dopts= $3; $prefix= $4; $pcstr= "$prefix:"; defined($prefix{$prefix}) or die "prefix $prefix in $tf ?\n"; $rstr= $prefix{$prefix}.' '; } else { die "fsys $tf ?"; } $fsidstr= $pcstr.$atf; $fsidstr =~ s/[,+]/+$&/g; $fsidstr =~ s#/#,#g; $fsidfile= "/var/lib/chiark-backup/incstamp,$fsidstr"; $dev = $atf =~ s,^(.*)\:,, ? $1 : ''; if (!length $pcstr) { stat $atf or die "stat $atf: $!"; -d _ or die "not a dir: $atf"; } undef %dopt; foreach $dopt (split /\,/,$dopts) { if (grep { $dopt eq $_ } qw(gz noinc)) { $dopt{$dopt}= 'y'; } elsif (grep { $dopt eq $_ } qw(snap)) { $dopt{$dopt}= $dopt; } elsif ($dopt =~ m/\=/ && grep { $` eq $_ } qw(gz snap)) { $dopt{$`}= $'; } elsif (length $dopt) { die "unknown option $dopt (in $dopts $tf)"; } } my ($gzo); foreach $gzo (qw(gz gzi)) { if ($dopt{$gzo} eq 'y') { $$gzo= '1'; } elsif ($dopt{$gzo} =~ m/^\d$/) { $$gzo= $dopt{$gzo}; } elsif (defined $dopt{$gzo}) { die "$tf bad $gzo"; } else { $$gzo= ''; } } if (length $dopt{'snap'}) { length $dev or die "$pcstr:$atf no device but needed for snap"; } } sub execute ($) { pboth(" $_[0]\n"); system $_[0]; $? and die "$_[0] $?"; } sub prepfsys () { $dev_print= $dev; $atf_print= $atf; if (length $dopt{'snap'}) { system('snap-drop'); $? and die $?; $snapscripts= '/etc/chiark-backup/snap'; $snapbase= "$rstr $snapscripts/$dopt{'snap'}"; $snapargs= "/var/lib/chiark-backup"; $snapsnap= "$snapbase snap $snapargs $dev $atf"; $snapdrop= "$snapbase drop $snapargs"; open SD, ">snap-drop.new" or die $!; print SD $snapdrop,"\n" or die $!; close SD or die $!; rename "snap-drop.new","snap-drop" or die $!; execute($snapsnap); $dev_nosnap= $dev; $atf_nosnap= $atf; $dev= "/var/lib/chiark-backup/snap-device"; $atf= "/var/lib/chiark-backup/snap-mount"; } } sub finfsys () { if (length $dopt{'snap'}) { system('snap-drop'); $? and die $?; } } sub openlog () { unlink 'log'; $u= umask(007); open LOG, ">log" or die $!; umask $u; select(LOG); $|=1; select(STDOUT); } $SIG{'__DIE__'}= 'killprocesses'; 1; chiark-utils-4.4.2build1/backup/man/0000775000000000000000000000000012423200672014127 5ustar chiark-utils-4.4.2build1/backup/man/takedown.10000664000000000000000000000233612423200672016031 0ustar .TH BACKUP-TAKEDOWN "1" "July 2003" "Debian" "Chiark-backup" .SH NAME backup-takedown \- Bring some system services down for backups .SH SYNOPSIS .B backup-takedown .I [freq] .br .SH DESCRIPTION `backup-takedown' is for running a reduced level of system services during backups. To use this command you need to configure init by setting up runlevel 5 to provide the level of services you want, and to run backup-driver automatically on entering runlevel 5. .SH OPTIONS .TP .BR freq `freq' may be `now', `soon' or nothing depending on the number of warning messages desired - these correspond to warnings.* files. .SH FILES .TP .I /etc/chiark-backup/warnings.* Files specifying what number and frequency of warnings will be produced. .TP .I /etc/chiark-backup/settings.pl Configuration file for the whole of chiark-backup .P .SH AUTHOR This Manual page was written by Matthew Vernon but may be used by anyone. .SH COPYRIGHT Copyright (C) 1997-1998,2000-2001 Ian Jackson .br Copyright (C) 1999 Peter Maydell .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. chiark-utils-4.4.2build1/backup/man/driver.10000664000000000000000000000177012423200672015511 0ustar .TH BACKUP-DRIVER "1" "July 2003" "Debian" "Chiark-backup" .SH NAME backup-driver \- entry point for cron or inittab to start backups .SH SYNOPSIS .B backup-driver .I [-test] .br .SH DESCRIPTION `backup-driver' is the script to actually run to do a backup. It assumes that it is being run from cron or inittab unless passed the `test' option .SH OPTIONS .TP .BR -test Do not attempt to use backup-bringup to change runlevel. Use this argument when running backup-driver from the command line. .SH FILES .TP .I /etc/chiark-backup/settings.pl Configuration file for the whole of chiark-backup .P .SH AUTHOR This Manual page was written by Matthew Vernon but may be used by anyone. .SH COPYRIGHT Copyright (C) 1997-1998,2000-2001 Ian Jackson .br Copyright (C) 1999 Peter Maydell .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. chiark-utils-4.4.2build1/backup/man/whatsthis.10000664000000000000000000000233712423200672016234 0ustar .TH BACKUP-WHATSTHIS "1" "July 2003" "Debian" "Chiark-backup" .SH NAME backup-whatsthis \- read an id off a tape and display it .SH SYNOPSIS .B backup-whatsthis .RB [\| --list .RI [\| n \|]] .br .SH DESCRIPTION `backup-whatsthis' is a simple script to display the TAPEID of the current tape and optionally list its contents. This script is a bit of a hack and may not be fully reliable. .SH OPTIONS .TP .B \--list .RI [\| n \|] Print TAPEID then list archive n (default 0). Note that archives are numbered from zero. .SH FILES .TP .I /etc/chiark-backup/settings.pl Configuration file for the whole of chiark-backup .P .SH BUGS `backup-whatsthis' is currently hardwired to assume `cpio' type backups when listing; it could be trivially hardwired to assume `zafio' or with slightly more effort it could be done properly :->. .SH AUTHOR This Manual page was written by Matthew Vernon but may be used by anyone. .SH COPYRIGHT Copyright (C) 1997-1998,2000-2001 Ian Jackson .br Copyright (C) 1999 Peter Maydell .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. chiark-utils-4.4.2build1/backup/man/loaded.10000664000000000000000000000236212423200672015444 0ustar .TH BACKUP-LOADED "1" "July 2003" "Debian" "Chiark-backup" .SH NAME backup-loaded \- tell the chiark-backup system what to do with a new tape .SH SYNOPSIS .B backup-loaded .I [TAPEID] .br .SH DESCRIPTION backup-loaded: this tells the scripts that a currently unlabelled tape should be treated as tape X: eg: .br \fBbackup-loaded b3\fP .br will cause it to treat it as tape `b3'. NB: this won't override the TAPEID label written on the tape; it's just for use with previously unused tapes. This applies only to the next time the backup scripts are invoked. You can say just .br \fBbackup-loaded\fP .br to go back to the default behaviour, which is to fail if the tape has no TAPEID. .SH OPTIONS .TP .BR TAPEID Treat the tape as label TAPEID .SH FILES .TP .I /etc/chiark-backup/settings.pl Configuration file for the whole of chiark-backup .P .SH AUTHOR This Manual page was written by Matthew Vernon but may be used by anyone. .SH COPYRIGHT Copyright (C) 1997-1998,2000-2001 Ian Jackson .br Copyright (C) 1999 Peter Maydell .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. chiark-utils-4.4.2build1/backup/man/checkallused.10000664000000000000000000000222212423200672016636 0ustar .TH BACKUP-CHECKALLUSED "1" "July 2003" "Debian" "Chiark-backup" .SH NAME backup-checkallused \- check chiark-backup configuration .SH SYNOPSIS .B backup-checkallused .br .SH DESCRIPTION `backup-checkallused' does a check of the configuration files. It should give a cryptic summary of the configuration and print 'configuration ok'. If not, fix your config files :-> You have to create the file .br /var/lib/chiark-backup/last-tape containing the id of a tape; this helps backup-checkallused know where to start iterating over tapes. Any tapeid will do. (But don't make it the same as the one you want to back up to first.) .SH OPTIONS None .SH FILES .TP .I /etc/chiark-backup/settings.pl Configuration file for the whole of chiark-backup .P .SH AUTHOR This Manual page was written by Matthew Vernon but may be used by anyone. .SH COPYRIGHT Copyright (C) 1997-1998,2000-2001 Ian Jackson .br Copyright (C) 1999 Peter Maydell .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. chiark-utils-4.4.2build1/backup/man/labeltape.10000664000000000000000000000177212423200672016151 0ustar .TH BACKUP-LABELTAPE "1" "July 2003" "Debian" "Chiark-backup" .SH NAME backup-labeltape \- display or change tape label .SH SYNOPSIS .B backup-labeltape .I [-force] TAPEID .br .SH DESCRIPTION `backup-labeltape' will display the tape label; it will also label the tape with TAPEID unless the tape already has a label (the -force option will over-write the current label) `test' option .SH OPTIONS .TP .BR -force Over-write an existing tape label .TP .BR TAPEID The new label to write to the tape .SH FILES .TP .I /etc/chiark-backup/settings.pl Configuration file for the whole of chiark-backup .P .SH AUTHOR This Manual page was written by Matthew Vernon but may be used by anyone. .SH COPYRIGHT Copyright (C) 1997-1998,2000-2001 Ian Jackson .br Copyright (C) 1999 Peter Maydell .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. chiark-utils-4.4.2build1/backup/bringup0000775000000000000000000000242712423200672014755 0ustar #!/bin/sh # bringup # script for going back to defaultrunlevel # # This file is part of chiark backup, a system for backing up GNU/Linux and # other UN*X-compatible machines, as used on chiark.greenend.org.uk. # # chiark backup is: # Copyright (C) 1997-1998,2000-2001,2007 # Ian Jackson # Copyright (C) 1999 Peter Maydell # # This is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3, or (at your option) any later version. # # This is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. # Very simple: extract the default runlevel from /etc/inittab # and change to it with telinit. runlevel=`sed -ne '/^id:/ s/.*:\([0-9]\):.*/\1/p' /etc/inittab` telinit $runlevel test ! -f /etc/chiark-backup/bringup-hook chiark-utils-4.4.2build1/backup/snap-drop0000775000000000000000000000024212423200672015203 0ustar #!/bin/sh set -e vd=/var/lib/chiark-backup if [ "x$1" != "x" ]; then vd="$1"; shift fi cd "$vd" test -f snap-drop || exit 0 sh -x snap-drop || true rm snap-drop chiark-utils-4.4.2build1/backup/takedown0000775000000000000000000000414212423200672015117 0ustar #!/bin/sh # takedown # Entry point for cron to take the system down for backups # # This file is part of chiark backup, a system for backing up GNU/Linux and # other UN*X-compatible machines, as used on chiark.greenend.org.uk. # # chiark backup is: # Copyright (C) 1997-1998,2000-2001,2007 # Ian Jackson # Copyright (C) 1999 Peter Maydell # # This is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3, or (at your option) any later version. # # This is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. # Expects a single (possibly empty) argument X which is used to select # a file /etc/chiark-backup/warnings.X. This file will contain lines like: # T 300 "in 10 minutes" # T 240 "in 5 minutes" # T 45 "in 1 minute" # T 15 "in 15 seconds" # configuring the frequency of warning messages. If you call the # files 'warnings.soon', 'warnings.now' and 'warnings.' then # you can invoke this as: # takedown lots of warnings # takedown soon not so many warnings # takedown now no warning at all set -e cd /etc/chiark-backup host="`hostname`" || true T () { ( exec wall < To run, the contents of /etc/chiark-backup should be: warnings.*: files defining how many warnings you get as the system is brought down to do backups. The defaults are fine. settings.pl: generic config file: in particular, the name of the tape device is set here. settings.sh: generic config file for shell scripts. Currently only contains some options for the lvm snapshotter. tape.*: conventionally, each tape you're going to use in the backup cycle has a tape number, a name and a config file. The tape numbers in use at Relativity are digit strings like `512'. The name is a combination of rotation set and volume number; rotation sets are typically a single letter (`s', `t', `u', `v') at Relativity and volumes a single digit (`0', `1', `2') at Relativity. You need at least two tapes as the system won't write a backup on the same tape it wrote the last one to. There are also conventionally incremental tapes whose names are a fixed letter (`k' in the current scheme) followed by a rotation letter. At Relativity we have two of these, `ks' and `kt'. Syntax of the tape.* files for full dump tapes: filesystems X next N end where N is the name of the next tape in the *full dump* sequence (which should be circular; eg v0->v1->v2->s0->s1->s1->t0->t1->t2->u0->u1->u2->v0->... and X is a filesystem group name (typically the same as the volume number). Each defined filesystem group has a name and a config file fsys.. These files define what is backed up and how. The filesystem `all' must also exist; it's used for incremental backups (and it must exist even if you don't do incrementals). In the fsys.* files: Empty lines and lines starting '#' are comments and ignored. Lines starting `excludedir' given regexps of things to exclude (temp dirs, Netscape's cache, etc). Lines starting `include' say to include another file when reading this one. Lines starting `prefix' give a command prefix necessary to run things on a remote machine: prefix Other lines should be of the form [:] [,] for local backups, or [:] [,] for remote backups. The file (including any included files) must end with the word 'end' on a line of its own. Valid values for are cpio uses cpio to produce tar-format backups dump uses dump to dump entire filesystems should be a mount-point gtar uses GNU tar to produce GNU tar format backups and -N-based semi-incrementals (not --incremental or --listed-incremental) zafio uses afio to compress each file as it is backed up ntfsimage for NTFS volumes, requires device name Only `dump' and `gtar' type backups perform any kind of incremental backups. is a comma-separated list of

chroot (but *not* chdir - danger!)\n", stderr) == EOF) { perror("write usage"); exit(-1); } } static const char *opt_user, *opt_useronly, *opt_chroot; static int opt_groupsclear= 0, opt_ngids= 0, opt_uidonly= -1; static int opt_gids[512]; static void af_uidonly(const struct cmdinfo *cip, const char *value) { unsigned long ul; char *ep; ul= strtoul(value,&ep,10); if (*ep) { fprintf(stderr,"bad uid `%s'\n",value); exit(-1); } opt_uidonly= ul; } static void af_group(const struct cmdinfo *cip, const char *value) { struct group *gr; if (opt_ngids >= sizeof(opt_gids)/sizeof(opt_gids[0])) badusage("too many groups specified"); gr= getgrnam(value); if (!gr) { fprintf(stderr,"unknown group `%s'\n",value); exit(-1); } opt_gids[opt_ngids++]= gr->gr_gid; } static void af_gid(const struct cmdinfo *cip, const char *value) { char *ep; unsigned long ul; if (opt_ngids >= sizeof(opt_gids)/sizeof(opt_gids[0])) badusage("too many gids specified"); ul= strtoul(value,&ep,0); if ((*ep) || (uid_t)ul != ul || ul>INT_MAX) badusage("bad gid `%s'",value); opt_gids[opt_ngids++]= ul; } static void af_help(const struct cmdinfo *cip, const char *value) { usagemessage(); exit(0); } static const struct cmdinfo cmdinfos[]= { { "user", 'u', 1, 0, &opt_user, 0, }, { "useronly", 'i', 1, 0, &opt_useronly, 0 }, { "uidonly", 'I', 1, 0, 0, af_uidonly }, { "groupsclear", 'z', 0, &opt_groupsclear, 0, 0, 1 }, { "group", 'g', 1, 0, 0, af_group }, { "gid", 'G', 1, 0, 0, af_gid }, { "chroot", 'R', 1, 0, &opt_chroot, 0 }, { "help", 'h', 0, 0, 0, af_help }, { 0, 0 } }; #ifdef REALLY_CHECK_FILE static int checkroot(void) { int r; r= access(REALLY_CHECK_FILE,W_OK); if (r) return -1; return 0; } #endif #ifdef REALLY_CHECK_GID static int checkroot(void) { gid_t groups[512]; int r, i; r= getgid(); if (r==REALLY_CHECK_GID) return 0; if (r<0) { perror("getgid check"); exit(-1); } r= getgroups(sizeof(groups)/sizeof(groups[0]),groups); if (r<0) { perror("getgroups check"); exit(-1); } for (i=0; ipw_uid; } if (opt_chroot) { if (chroot(opt_chroot)) { perror("chroot failed"); exit(-1); } } orgmaingid= getgid(); if (orgmaingid<0) { perror("getgid failed"); exit(-1); } if (opt_user) { r= initgroups(opt_user,pw->pw_gid); if (r) { perror("initgroups failed"); exit(-1); } maingid= pw->pw_gid; } else { maingid= -1; } if (opt_groupsclear) { ngroups= 0; if (opt_ngids > sizeof(groups)/sizeof(groups[0])) { fputs("too many groups to set\n",stderr); exit(-1); } } else { ngroups= getgroups(0,0); if (ngroups<0) { perror("getgroups(0,0) failed"); exit(-1); } if (ngroups+opt_ngids > sizeof(groups)/sizeof(groups[0])) { fputs("too many groups already set for total to fit\n",stderr); exit(-1); } ngroups= getgroups(ngroups,groups); if (ngroups<0) { perror("getgroups failed"); exit(-1); } } if (opt_ngids) { maingid= opt_gids[0]; } if (opt_ngids || opt_groupsclear) { ngroups_in= ngroups; ngroups= 0; for (i=0; i=0) { fputs("could seteuid 0",stderr); exit(-1); } if (errno != EPERM) { perror("unexpected failure mode for seteuid 0"); exit(-1); } } r= getuid(); if (r<0) { perror("getuid failed"); exit(-1); } if (r != mainuid) { fputs("getuid mismatch",stderr); exit(-1); } r= geteuid(); if (r<0) { perror("geteuid failed"); exit(-1); } if (r != mainuid) { fputs("geteuid mismatch",stderr); exit(-1); } if (maingid != -1) { for (i=0; i=ngroups && maingid != orgmaingid) { r= setgid(orgmaingid); if (r>=0) { fputs("could setgid back",stderr); exit(-1); } if (errno != EPERM) { perror("unexpected failure mode for setgid back"); exit(-1); } } r= getgid(); if (r<0) { perror("getgid failed"); exit(-1); } if (r != maingid) { fputs("getgid mismatch",stderr); exit(-1); } r= getegid(); if (r<0) { perror("getegid failed"); exit(-1); } if (r != maingid) { fputs("getegid mismatch",stderr); exit(-1); } } if (!*argv) { cp= getenv("SHELL"); if (!cp) cp= "sh"; execlp(cp,cp,"-i",(const char*)0); } else { execvp(argv[0],(char**)argv); } perror("exec failed"); exit(-1); } chiark-utils-4.4.2build1/cprogs/dlist.h0000664000000000000000000000366712423200672014710 0ustar /* * dlist.h * - macros for handling doubly linked lists */ /* * This file is * Copyright (C) 1997-1999 Ian Jackson * * It is part of adns, which is * Copyright (C) 1997-2000 Ian Jackson * Copyright (C) 1999 Tony Finch * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, consult the Free Software Foundation, * Inc., website at www.fsf.org, or the GNU Project website at www.gnu.org. */ #ifndef ADNS_DLIST_H_INCLUDED #define ADNS_DLIST_H_INCLUDED #define LIST_INIT(list) ((list).head= (list).tail= 0) #define LINK_INIT(link) ((link).next= (link).back= 0) #define LIST_UNLINK_PART(list,node,part) \ do { \ if ((node)->part back) (node)->part back->part next= (node)->part next; \ else (list).head= (node)->part next; \ if ((node)->part next) (node)->part next->part back= (node)->part back; \ else (list).tail= (node)->part back; \ } while(0) #define LIST_LINK_TAIL_PART(list,node,part) \ do { \ (node)->part next= 0; \ (node)->part back= (list).tail; \ if ((list).tail) (list).tail->part next= (node); else (list).head= (node); \ (list).tail= (node); \ } while(0) #define LIST_UNLINK(list,node) LIST_UNLINK_PART(list,node,) #define LIST_LINK_TAIL(list,node) LIST_LINK_TAIL_PART(list,node,) #endif chiark-utils-4.4.2build1/cprogs/really.testcases0000775000000000000000000001017712423200672016625 0ustar #!/usr/bin/perl $testuser= 'testac'; $testgroup= 'testac'; $testuid= 1000; $testgid= 1000; @testxgids= qw(1000); $numgid= 50008; $othergroup= 'daemon'; $othergid= 1; sub parseid ($) { my ($id) = @_; my $orgid= $id; my $out= ''; my $part; chomp($id); $id =~ s/^uid=// or return $id =~ m/\n/ ? "ERROR: $`" : "ERROR: $id"; $id =~ s/^(\d+)// or return $orgid; $out= $1; $id =~ s/^\([^\)]+\)//; $id =~ s/^\s+// or return $orgid; $id =~ s/^gid=(\d+)// or return $orgid; $out.= " $1"; $id =~ s/^\([^\)]+\)//; $id =~ s/^\s+// or return $orgid; $id =~ s/^groups=// or return $orgid; for $part (split(/,/,$id)) { $part =~ s/^(\d+)// or return $orgid; $out.= " $1"; $part =~ s/^\([^\)]+\)//; $part eq '' or return $orgid; } return $out; } $org= `id`; $org =~ m/^uid=\d+\(([^\)]+)\) gid=\d+\(([^\)]+)\) / or die "$org ?"; $orguser= $1; $orggroup= $2; $org= parseid($org); $org =~ m/^\d+ \d+ / or die $org; ($orguid,$orggid,@orgxgids)= split(/ /,$org); $tests= <&1`; $newout= parseid($out); print("OK $tests[$i] ($tests[$i+2])\n"), next if $newout eq $tests[$i+1]; die "$newout != $tests[$i+1] ($tests[$i]) $i"; } chiark-utils-4.4.2build1/cprogs/trivsoundd-start0000775000000000000000000000033012423200672016662 0ustar #!/bin/bash set -e pf=`tempfile` rm -f $pf mknod -m600 $pf p exec 3<>$pf exec >$pf 4<$pf rm -f $pf exec 3>&- (exec logger -p user.info -t trivsoundd <&4 >/dev/null 2>&1) & exec 4<&- 2>&1 ... ] .SH DESCRIPTION .B acctdump reads process accounting data from .B file or, by default, from the system process account file .I /var/log/account/pacct and outputs it as a table on stdout. .SH OPTIONS .TP .B -f|--forwards Output data with the oldest entry first rather than last. .TP .B -q|--no-banner Don\'t output a header in the first line. .TP .B -p|--stdin Read accounting data from stdin. .TP .B -r|--raw Don\'t convert date, uid, gid, tty, and exit status to human readable forms. .TP .B -u|--resource Additionally display resource usage data. .SH "SEE ALSO" .BR ac (1) .BR accton (8) .BR dump-acct (8) .BR dump-utmp (8) .BR lastcomm (1) .BR sa (8) .SH AUTHOR .B acctdump is .BR Copyright (C) 1998, 2001, 2006 Ian Jackson This manpage was written by Jonathan Amery. It is .BR Copyright (C) 2014 Jonathan Amery This is free software, distributed under the GNU General Public Licence, version 3 or (at your option) any later version; see /usr/share/doc/chiark-utils-bin/copyright or /usr/share/common-licenses/GPL-3 for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. chiark-utils-4.4.2build1/cprogs/xduplic-copier.10000664000000000000000000000612412423200672016420 0ustar .TH WITH-LOCK-EX "1" "July 2003" "Debian" "Chiark-utils-bin" .SH NAME xduplic-copier \- type into multiple X windows at once .SH SYNOPSIS .B xduplic-copier .SH DESCRIPTION xduplic-copier lets you type into multiple X windows at once. It has a very basic user interface for selecting which windows to type into. .SH OPTIONS xduplic-copier ignores its command-line arguments. .SH MODES xduplic-copier puts up a small window with some text in it. It has two modes: .TP Idle: "\fBi 0\fR" (red); Typing "\fBT\fR \fIcount\fR" (green) If you type keystrokes into xduplic-copier, they will be replicated to all the selected windows (if there are any). (You can also type into the selected windows individually in the normal way.) count is the number of selected windows. The starting mode is Idle; you should left-click to start selecting. .TP Selecting: "\fBS\fR \fIcount\fR" (white) In this mode xduplic-copier has grabbed the mouse pointer and you can indicate which windows you are going to want to type into. .SH MOUSE ACTIONS .TP Left-click in xduplic-copier Switch between typing and selecting modes. .TP Right-click in xduplic-copier Quit. .TP Left-click in another window while selecting Select this window. (Beeps if already selected.) .TP Right-click in another window while selecting Deselect this window. (Beeps if not selected.) .TP Right-click in root window (ie, desktop background) while selecting Deselect all windows. .TP Q key while selecting Quits. .SH XTERM AND ALLOW SENDEVENTS xduplic-copier does its work by generating synthetic events for the selected windows, using XSendEvent. Unfortunately the xterm authors think that allowing XSendEvent is a security problem; they are wrong: any untrusted person can already take over your xterms anyway. But the xterm authors have configured xterm to discard synthetic events by default. You can solve this at runtime by bringing up the ctrl-leftbutton menu in each xterm, and ticking the option "Allow SendEvents". Or you can set the allowSendEvents in your xterm X resources. Neither of these significantly reduce your security. Indeed, xterm itself allows these properties to be set via the X toolkit system's remote widget property setting arrangements - so it would be possible for xduplic-copier to set this property itself on any xterms it encountered. Unfortunately doing so would be a lot of tedious programming. .SH BUGS If one of the windows you have selected is closed, and you try typing, xduplic-copier will crash due to an unhandled X11 error. If you notice that you have got into this state, you cannot retain your set of selected windows because there is no way to click on the now-destroyed window to deselect it. You can keep xduplic-copier from crashing by right-clicking on the background in selecting mode, and then reselecting all your windows, which may be marginally more convenient than restarting it. The UI is perhaps excessively sparse. .SH AUTHOR AND COPYRIGHT Ian Jackson wrote xduplic-copier some time in 2002, and updated it in 2013. The manpage is from 2013. xduplic-copier is govered by the GNU GPL, v3 or later. chiark-utils-4.4.2build1/cprogs/with-lock-ex.10000664000000000000000000001034612423200672016005 0ustar .TH WITH-LOCK-EX "1" "July 2003" "Debian" "Chiark-utils-bin" .SH NAME with-lock-ex \- file locker .SH SYNOPSIS .B with-lock-ex .BR \-w \||\| \-q \||\| \-f .I lockfile command .IR args \ \|.\|.\|. .br .SH DESCRIPTION with-lock-ex will open and lock the lockfile for writing and then feed the remainder of its arguments to .BR exec (2); when that process terminates the fd will be closed and the file unlocked automatically by the kernel. .PP If the file does not exist it is created, with permissions .B rw for each user class for which the umask has .BR w . .SH OPTIONS .TP .B \-w Wait for the lock to be available. .TP .B \-f Fail (printing a message to stderr and exiting 255) if the lock cannot be acquired immediately because another process has it. .TP .B \-q Silently do nothing (ie, exit 0 instead of executing the specified process) if the lock cannot be acquired immediately because another process has it. .SH STALE LOCKS The locking protocol used does not suffer from stale locks. If the lock cannot be acquired, one or more running processes must currently hold the lock; if the lock needs to be freed those processes should be killed. .PP Under no circumstances should `stale lock cleaner' cron jobs, or the like, be instituted. In systems where a great many locks may exist, old lockfiles may be removed from cron but only if each lock is acquired before the lockfile is removed, for example with .IP .B with-lock-ex -q .I lockfile .B rm .I lockfile .SH DEADLOCKS There is no deadlock detection. In a system with several locks, a lock hierarchy should be established, such that for every pair of locks .I A and .I B which a process might lock simultaneously, either .IR A > B or .IR B > A where the relation > is transitive and noncyclic. .PP Then, for any two locks .I X and .I Y with .IR X > Y it is forbidden to acquire .I X while holding .IR Y . Instead, acquire .I X first, or release .I Y before (re)acquiring .I X and .I Y in that order. .PP (There are more complicated ways of avoiding deadlocks, but a lock hierarchy is simple to understand and implement. If it does not meet your needs, consult the literature.) .SH LOCKING PROTOCOL The locking protocol used by .B with-lock-ex is as follows: .PP The lock is held by a process (or group of processes) which holds an fcntl exclusive lock on the first byte of the plain file which has the specified name. A holder of the lock (and only a holder of the lock) may delete the file or change the inode to which the name refers, and as soon as it does so it ceases to hold the lock. .PP Any process may create the file if it does not exist. There is no need for the file to contain any actual data. Indeed, actually using the file for data storage is strongly disrecommended, as this will foreclose most strategies for reliable update. Use a separate lockfile instead. .PP Ability to obtain the lock corresponds to write permission on the file (and of course permission to create the file, if it does not already exist). However, processes with only read permission on the file can prevent the lock being acquired at all; therefore lockfiles should not usually be world-readable. .PP When a (group of) processes wishes to acquire the lock, it should open the file (with .BR O_CREAT ) and lock it with .BR fcntl (2) .BR F_RWLCK , operation .B F_SETLK or .BR F_SETLKW . If this succeeds it should fstat the file descriptor it has, and the file by its path. If the device and inode match then the lock has been acquired and remains acquired until that group of processes changes which file the name refers to, deletes the file, or releases the fcntl lock. If they do not then another process acquired the lock and deleted the file in the meantime; you must now close your filedescriptor and start again. .B with-lock-ex follows this specification. .PP Note that .BR flock (2) is a different kind of lock to .BR fcntl (2). .B with-lock-ex uses .BR fcntl . .SH AUTHOR This Manual page was written by Matthew Vernon and enhanced by Ian Jackson , in 2003, but may be used by anyone. .SH COPYRIGHT with-lock-ex was written by Ian Jackson in 1993, 1994, 1995, 1996, 1998, 1999. He has placed it in the public domain. .SH "SEE ALSO" .BR fcntl (2), .BR flock (2), .BR chmod (2) chiark-utils-4.4.2build1/cprogs/rwbuffer.c0000664000000000000000000000663512423200672015404 0ustar /* * rwbuffer.c * common definitions for readbuffer/writebuffer * * readbuffer and writebuffer are: * Copyright (C) 1997-1998,2000-2001 Ian Jackson * * readbuffer is part of chiark backup, a system for backing up GNU/Linux and * other UN*X-compatible machines, as used on chiark.greenend.org.uk. * chiark backup is: * Copyright (C) 1997-1998,2000-2001 Ian Jackson * Copyright (C) 1999 Peter Maydell * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 3, * or (at your option) any later version. * * This is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this file; if not, consult the Free Software * Foundation's website at www.fsf.org, or the GNU Project website at * www.gnu.org. * */ #include "rwbuffer.h" #ifndef RWBUFFER_SIZE_MB_DEF #define RWBUFFER_SIZE_MB_DEF 16 #endif #ifndef RWBUFFER_SIZE_MB_MAX #define RWBUFFER_SIZE_MB_MAX 512 #endif unsigned char *buf, *wp, *rp; int used, seeneof, maxselfd; size_t buffersize= RWBUFFER_SIZE_MB_DEF*1024*1024; fd_set readfds; fd_set writefds; static int opt_mlock=0; int min(int a, int b) { return a<=b ? a : b; } static void usage(FILE *f) { if (fprintf(f,"usage: %s [--mlock] []\n",progname) < 0) { perror("print usage"); exit(16); } } static void usageerr(const char *what) { fprintf(stderr,"%s: bad usage: %s\n",progname,what); usage(stderr); exit(12); } void nonblock(int fd, int yesno) { int r; r= fcntl(fd,F_GETFL,0); if (r == -1) { perror("fcntl getfl"); exit(8); } if (yesno) r |= O_NDELAY; else r &= ~O_NDELAY; if (fcntl(fd,F_SETFL,r) == -1) { perror("fcntl setfl"); exit(8); } } static void unnonblock(void) { nonblock(0,0); nonblock(1,0); } void startupcore(void) { buf= xmalloc(buffersize); if (opt_mlock) { if (mlock(buf,buffersize)) { perror("mlock"); exit(2); } } used=0; wp=rp=buf; seeneof=0; if (atexit(unnonblock)) { perror("atexit"); exit(16); } } void startup(const char *const *argv) { const char *arg; char *ep; int shift=-1; assert(argv[0]); while ((arg= *++argv)) { if (!strcmp(arg,"--mlock")) { opt_mlock= 1; } else if (isdigit((unsigned char)arg[0])) { buffersize= strtoul(arg,&ep,0); if (ep[0] && ep[1]) usageerr("buffer size spec. invalid"); switch (ep[0]) { case 0: case 'm': shift= 20; break; case 'k': shift= 10; break; case 'b': shift= 0; break; default: usageerr("buffer size unit unknown"); } if (buffersize > ((RWBUFFER_SIZE_MB_MAX << 20) >> shift)) usageerr("buffer size too big"); buffersize <<= shift; } else { usageerr("invalid option"); } } startupcore(); nonblock(0,1); nonblock(1,1); } void *xmalloc(size_t sz) { void *r= malloc(sz); if (!r) { perror("malloc"); exit(6); }; return r; } void callselect(void) { int r; for (;;) { r= select(maxselfd,&readfds,&writefds,0,0); if (r != -1) return; if (errno != EINTR) { perror("select"); exit(4); } } } chiark-utils-4.4.2build1/cprogs/myopt.h0000664000000000000000000000251312423200706014724 0ustar /* * myopt.h - declarations for my very own option parsing * * Copyright (C) 1994,1995 Ian Jackson * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 3, * or (at your option) any later version. * * This is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this file; if not, consult the Free Software * Foundation's website at www.fsf.org, or the GNU Project website at * www.gnu.org. */ #ifndef MYOPT_H #define MYOPT_H extern void usagemessage(void); /* supply this */ typedef void (*voidfnp)(void); struct cmdinfo { const char *olong; char oshort; int takesvalue; /* 0 = normal 1 = standard value 2 = option string cont */ int *iassignto; const char **sassignto; void (*call)(const struct cmdinfo*, const char *value); int arg; void *parg; voidfnp farg; }; void myopt(const char *const **argvp, const struct cmdinfo *cmdinfos); void badusage(const char *fmt, ...); #endif /* MYOPT_H */ chiark-utils-4.4.2build1/cprogs/readbuffer.10000664000000000000000000000144412423200672015576 0ustar .TH readbuffer 1 2001-10-21 chiark-backup .SH NAME readbuffer \- read input from devices which don't like constant stopping and starting .SH SYNOPSIS .B readbuffer .RB [ --mlock ] .RI [ size ] .SH DESCRIPTION .B readbuffer reads data on standard input and writes it to standard output. It will internally buffer up to \fIsize\fR megabytes of data, and will only read more data when the buffer is at least 75% empty. .PP \fIsize\fR may also be suffixed with .BR m ", " k ", or " b to indicate that it is in megabytes (2^20), kilobytes (2^10) or bytes. .PP It is intended for use in situations where many small reads are undesirable for performance reasons, e.g. tape drives. .SH OPTIONS .TP .B --mlock Calls .BR mlock (2) to lock the buffer into memory. .SH "SEE ALSO" .BR writebuffer (1), .BR mlock (2) chiark-utils-4.4.2build1/cprogs/wrbufcore.c0000664000000000000000000000522012423200672015545 0ustar /* * wrbufcore.c * * Core of algorithm for writing output to devices which don't like * constant stopping and starting, such as tape drives. This is: * Copyright (C) 1997-1998,2000-2001 Ian Jackson * * writebuffer is part of chiark backup, a system for backing up GNU/Linux * and other UN*X-compatible machines, as used on chiark.greenend.org.uk. * chiark backup is: * Copyright (C) 1997-1998,2000-2001 Ian Jackson * Copyright (C) 1999 Peter Maydell * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 3, * or (at your option) any later version. * * This is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this file; if not, consult the Free Software * Foundation's website at www.fsf.org, or the GNU Project website at * www.gnu.org. * */ #include "rwbuffer.h" static size_t waitfill; int writing; void wrbufcore_startup(void) { waitfill= (buffersize*3)/4; writing=0; maxselfd=0; } void fdsetset(int fd, fd_set *set) { FD_SET(fd,set); if (fd >= maxselfd) maxselfd= fd+1; } void wrbufcore_prepselect(int rdfd, int wrfd) { FD_ZERO(&readfds); if (rdfd>=0 && !seeneof && used+1=0 && FD_ISSET(rdfd,&readfds)) && !used) { wrbuf_report("stopping"); writing= 0; FD_CLR(wrfd,&writefds); } if (rdfd>=0 && FD_ISSET(rdfd,&readfds)) { r= read(rdfd,rp,min(buffersize-1-used,buf+buffersize-rp)); if (!r) { seeneof=1; writing=1; wrbuf_report("seeneof"); } else if (r<0) { if (!(errno == EAGAIN || errno == EINTR)) { perror("read"); exit(1); } } else { used+= r; rp+= r; if (rp == buf+buffersize) rp=buf; } if (used > waitfill) { if (!writing) wrbuf_report("starting"); writing=1; } } if (FD_ISSET(wrfd,&writefds) && used) { r= write(wrfd,wp,min(used,buf+buffersize-wp)); if (r<=0) { if (!(errno == EAGAIN || errno == EINTR)) { perror("write"); exit(1); } } else { used-= r; wp+= r; if (wp == buf+buffersize) wp=buf; } } } chiark-utils-4.4.2build1/cprogs/watershed.c0000664000000000000000000004014512423200672015542 0ustar /* * watershed - an auxiliary verb for optimising away * unnecessary runs of idempotent commands * * watershed is Copyright 2007 Canonical Ltd * written by Ian Jackson * and this version now maintained as part of chiark-utils * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this file; if not, consult the Free Software * Foundation's website at www.fsf.org, or the GNU Project website at * www.gnu.org. * */ /* * NB a different fork of this code exists in Ubuntu's udev. */ /* * * usage: watershed [] [...] * * options: * -d|--state-dir * default is /var/run/watershed for uid 0 * $HOME/.watershed for others * -i|--command-id * * files used: * /.lock lockfile * /.cohort cohort * * default is * hex(sha256(argv[0]+'\0' + argv[1]+'\0' ... argv[argc-1]+'\0') * '=' * mangled argv[0] (all chars [^-+_0-9A-Za-z] replaced with ? * and max 32 chars) * * exit status: * 127 - something went wrong, or process died with some other signal * SIGPIPE - process died with SIGPIPE * x - process called _exit(x) * * stdin/stdout/stderr: * * If watershed exits 127 due to some unexpected problem, a message * is printed to stderr explaining why (obviously). * * If a watershed invocation ends up running the process, the process * simply inherits stdin/out/err. Otherwise stdin/stdout are not used. * * If the process run for us by another invocation of watershed exits * zero, or watershed die with the same signal as the process * (currently just SIGPIPE), nothing is printed to stderr. Otherwise * (ie, failure of the actual process, in another invocation), * watershed prints a description of the wait status to stderr, much * as the shell might. * */ /* * gcc -Wall -Wwrite-strings -Wmissing-prototypes watershed.c -o watershed /usr/lib/libnettle.a */ /* * * Theory: * * We consider only invocations with a specific command id (and state * directory), since other invocations are completely independent by * virtue of having different state file pathnames and thus different * state files. Normally, a command id corresponds to invocations * with a particular set of command line arguments and a state * directory corresponds to a particular euid; environment variable * settings and other inherited process properties are disregarded. * * A `cohort' is a set of invocations which can be coalesced into one * run of the command. For each cohort there is a file, the cohort * file (which may not yet exist, may exist and have a name, or may * be unliked). * * An `invocation' is an invocation of the `watershed' program. A * `process' is an invocation of the requested command. * * There is always one current cohort, in one of the following * two states: * * * Empty * No invocations are in this cohort yet. * The cohort filename is ENOENT. * This is the initial state for a cohort, and the legal next * state is Accumulating. * * * Accumulating * The process for this run has not yet started, so that new * invocations arriving would be satisfied if this cohort were to * run. * The cohort filename refers to this cohort's file. * The legal next state for the cohort is Ready. * * Additionally, there may be older cohorts in the following states: * * * Ready * The command for this cohort has not yet been run. * The cohort file has no name and is empty. * Only one cohort, the lockholder's, may be in this state. * The next legal states are Running, or exceptionally Forgotten * (if the lockholder crashes and is the only invocation in the * cohort). * * * Running * The lockholder is running the command for this cohort. * This state is identical to Ready from the point of view * of all invocations except the lockholder. * The legal next states are Done (the usual case), or (if the * lockholder crashes) Ready or Forgotten. * * * Done * The main process for this run has finished. * The cohort file has no name and contains sizeof(int) * bytes, the `status' value from waitpid. * The legal next state is Forgotten. * * * Forgotten * All invocations have finished and the cohort file no longer * exists. This is the final state. * * Only the lockholder may move a cohort between states, except that * any invocation may make the current Empty cohort become * Accumulating, and that the kernel will automatically move a cohort * from Running to Ready or from Done to Forgotten, when appropriate. * * * Algorithm: * * 1. Open the cohort file (O_CREAT|O_RDWR) so our cohort is * Accumulating/Ready/ * Running/Done * * 2. Acquire lock (see below) so lockholder's cohort is * Accumulating/Ready/Done * 3. fstat the open cohort file * If it is nonempty: Done * Read status from it and exit. * Otherwise, if nonzero link count: Accumulating * Unlink the cohort filename * Otherwise: Ready * * 4. Fork and run the command Running * and wait for it * * 5. Write the wait status to the cohort file Done * * * 6. Release the lock so we are no longer lockholder * but our cohort is still * Done * * 8. Exit Done/Forgotten * * If an invocation crashes (ie, if watershed itself fails, rather * than if the command does) then that invocation's caller will be * informed of the error. * * If the lockholder crashes with the cohort in: * * Accumulating: * The cohort remains in Accumulating and another invocation can * become the lockholder. If there are never any other * invocations then the lockfile and cohort file will not be * cleaned up (see below). * * Running/Ready: * The cohort goes from Running back to Ready (see above) and * another invocation in the same cohort will become the * lockholder and run it. If there is no other invocation in * the cohort the cohort goes to Forgotten although the lockfile * will not be cleaned up - see below. * * Done: * If there are no more invocations, the cohort is Forgotten but * the lockfile is not cleaned up. * * Lockfile: * * There is one lock for all cohorts. The lockholder is the * invocation which holds the fcntl lock on the file whose name is * the lockfile. The lockholder (and no-one else) may unlink the * lockfile. * * To acquire the lock: * * 1. Open the lockfile (O_CREAT|O_RDWR) * 2. Acquire fcntl lock (F_SETLKW) * 3. fstat the open lockfile and stat the lockfile filenmae * If inode numbers disagree, close lockfile and start * again from the beginning. * * To release the lock, unlink the lockfile and then either close it * or exit. Crashing will also release the lock but leave the * lockfile lying around (which is slightly untidy but not * incorrect); if this is a problem a cleanup task could periodically * acquire and release the lock for each lockfile found: * * Cleanup: * * As described above and below, stale cohort files and lockfiles can * result from invocations which crashed if the same command is never * run again. Such cohorts are always in Empty or Accumulating. * * If it became necessary to clean up stale cohort files and * lockfiles resulting from crashes, the following algorithm should * be executed for each lockfile found, as a cleanup task: * * 1. Acquire the lock. * This makes us the lockholder. and the current cohort is in * Empty/Accumulating * * so now that cohort is * 2. Unlink the cohort file, ignoring ENOENT. Ready/Forgotten * 3. Release the lock. Ready/Forgotten * 4. Exit. Ready/Forgotten * * This consists only of legal transitions, so if current cohort * wasn't stale, it will have been moved to Ready and some other * invocation in this cohort will become the lockholder and as normal * from step 4 of the main algorithm. If the cohort was stale it * will go to Forgotten straight away. * * A suitable cleanup script, on a system with with-lock-ex, is: */ // #!/bin/sh // set -e // if [ $# != 1 ]; echo >&2 'usage: cleanup '; exit 1; fi // cd "$1" // for f in ./*.lock; do // with-lock-ex -w rm -f "${f%.lock}.cohort" // done /* */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const struct option os[]= { { "--state-dir", 1,0,'d' }, { "--command-id",1,0,'i' }, { "--help", 0,0,'h' }, { 0 } }; static const char *state_dir, *command_id, *command; static const char *lock_path, *cohort_path; static int cohort_fd, lock_fd; #define _(x) gettext(x) #define NOEINTR_TYPED(type,assign) do{ \ while ((assign)==(type)-1 && errno==EINTR) {} \ }while(0) #define NOEINTR(assign) \ NOEINTR_TYPED(int,(assign)) #define CHECKED(value,what) do{ \ NOEINTR(r= (value)); \ if (r<0) diee((what)); \ }while(0) static void printusage(FILE *f) { fputs(_("usage: watershed [] ...\n" "options:\n" " -d|--state-dir \n" " -i|--command-id \n" " -h|--help\n" "see /usr/share/doc/chiark-utils-bin/watershed.txt\n"), f); } static void badusage(void) { printusage(stderr); exit(127); } static void die(const char *m) { fprintf(stderr,_("watershed: error: %s\n"), m); exit(127); } static void diee(const char *m) { fprintf(stderr,_("watershed: error: %s failed: %s\n"), m, strerror(errno)); exit(127); } static void dieep(const char *action, const char *path) { fprintf(stderr,_("watershed: error: could not %s `%s': %s\n"), action, path, strerror(errno)); exit(127); } static char *m_vasprintf(const char *fmt, va_list al) { char *s; int r; r= vasprintf(&s,fmt,al); if (r==-1) diee("vasprintf"); return s; } static char *m_asprintf(const char *fmt, ...) { char *s; va_list al; va_start(al,fmt); s= m_vasprintf(fmt,al); va_end(al); return s; } static void parse_args(int argc, char *const *argv) { int o; for (;;) { o= getopt_long(argc, argv, "+d:i:h", os,0); if (o==-1) break; switch (o) { case 'd': state_dir= optarg; break; case 'i': command_id= optarg; break; case 'h': printusage(stdout); exit(0); break; default: badusage(); } } command= argv[optind]; if (!command) badusage(); if (!state_dir) state_dir= getenv("WATERSHED_STATEDIR"); if (!state_dir) { uid_t u= geteuid(); if (u==(uid_t)-1) diee("getuid"); if (u) { const char *home= getenv("HOME"); if (!home) die(_("HOME not set, no --state-dir option" " supplied, not root")); state_dir= m_asprintf("%s/.watershed", home); } else { state_dir= "/var/run/watershed"; } } if (!command_id) { char *const *ap; struct sha256_ctx sc; unsigned char dbuf[SHA256_DIGEST_SIZE], *p; char *construct, *q; int i, c; sha256_init(&sc); for (ap= argv+optind; *ap; ap++) sha256_update(&sc,strlen(*ap)+1,*ap); sha256_digest(&sc,sizeof(dbuf),dbuf); construct= m_asprintf("%*s#%.32s", (int)sizeof(dbuf)*2,"", command); for (i=sizeof(dbuf), p=dbuf, q=construct; i; i--,p++,q+=2) sprintf(q,"%02x",*p); *q++= '='; while ((c=*q++)) { if (!(c=='-' || c=='+' || c=='_' || isalnum((unsigned char)c))) q[-1]= '?'; } command_id= construct; } lock_path= m_asprintf("%s/%s.lock", state_dir, command_id); cohort_path= m_asprintf("%s/%s.cohort", state_dir, command_id); } static void acquire_lock(void) { struct stat current_stab, our_stab; struct flock fl; int r; for (;;) { NOEINTR( lock_fd= open(lock_path, O_CREAT|O_RDWR, 0600) ); if (lock_fd<0) diee("open lock"); memset(&fl,0,sizeof(fl)); fl.l_type= F_WRLCK; fl.l_whence= SEEK_SET; CHECKED( fcntl(lock_fd, F_SETLKW, &fl), "acquire lock" ); CHECKED( fstat(lock_fd, &our_stab), "fstat our lock"); NOEINTR( r= stat(lock_path, ¤t_stab) ); if (!r && our_stab.st_ino == current_stab.st_ino && our_stab.st_dev == current_stab.st_dev) break; if (r && errno!=ENOENT) diee("fstat current lock"); close(lock_fd); } } static void release_lock(void) { int r; CHECKED( unlink(lock_path), "unlink lock"); } static void report(int status) { int v; if (WIFEXITED(status)) { v= WEXITSTATUS(status); if (v) fprintf(stderr,_("watershed: `%s' failed with error exit status %d" " (in another invocation)\n"), command, v); exit(status); } if (WIFSIGNALED(status)) { v= WTERMSIG(status); assert(v); if (v == SIGPIPE) raise(v); fprintf(stderr, WCOREDUMP(status) ? _("watershed: `%s' died due to fatal signal %s (core dumped)\n") : _("watershed: `%s' died due to fatal signal %s\n"), command, strsignal(v)); } else { fprintf(stderr, _("watershed: `%s' failed with" " crazy wait status 0x%x\n"), command, status); } exit(127); } int main(int argc, char *const *argv) { int status, r, dir_created=0, l; unsigned char *p; struct stat cohort_stab; pid_t c, c2; setlocale(LC_MESSAGES,""); /* not LC_ALL, see use of isalnum below */ parse_args(argc,argv); for (;;) { NOEINTR( cohort_fd= open(cohort_path, O_CREAT|O_RDWR, 0644) ); if (cohort_fd>=0) break; if (errno!=ENOENT) dieep(_("open/create cohort state file"), cohort_path); if (dir_created++) die("open cohort state file still ENOENT after mkdir"); NOEINTR( r= mkdir(state_dir,0700) ); if (r && errno!=EEXIST) dieep(_("create state directory"), state_dir); } acquire_lock(); CHECKED( fstat(cohort_fd, &cohort_stab), "fstat our cohort"); if (cohort_stab.st_size) { if (cohort_stab.st_size < sizeof(status)) die(_("cohort status file too short (disk full?)")); else if (cohort_stab.st_size != sizeof(status)) die("cohort status file too long"); NOEINTR( r= read(cohort_fd,&status,sizeof(status)) ); if (r==-1) diee("read cohort"); if (r!=sizeof(status)) die("cohort file read wrong length"); release_lock(); report(status); } if (cohort_stab.st_nlink) CHECKED( unlink(cohort_path), "unlink our cohort"); NOEINTR_TYPED(pid_t, c= fork() ); if (c==(pid_t)-1) diee("fork"); if (!c) { close(cohort_fd); close(lock_fd); execvp(command, argv+optind); fprintf(stderr,_("watershed: failed to execute `%s': %s\n"), command, strerror(errno)); exit(127); } NOEINTR( c2= waitpid(c, &status, 0) ); if (c2==(pid_t)-1) diee("waitpid"); if (c2!=c) die("waitpid gave wrong pid"); for (l=sizeof(status), p=(void*)&status; l>0; l-=r, p+=r) CHECKED( write(cohort_fd,p,l), _("write result status")); release_lock(); if (!WIFEXITED(status)) report(status); exit(WEXITSTATUS(status)); } chiark-utils-4.4.2build1/.gitignore0000664000000000000000000000047712423201477014113 0ustar *~ *.o build cprogs/readbuffer cprogs/writebuffer cprogs/trivsoundd cprogs/really cprogs/with-lock-ex cprogs/xbatmon-simple cprogs/xduplic-copier cprogs/mcastsoundd cprogs/summer cprogs/watershed cprogs/watershed.txt cprogs/rcopy-repeatedly cprogs/acctdump debian/tmp debian/sv-* debian/files debian/*.debhelper.log chiark-utils-4.4.2build1/scripts/0000775000000000000000000000000012445561556013615 5ustar chiark-utils-4.4.2build1/scripts/nntpid0000775000000000000000000000502112445561415015027 0ustar #!/usr/bin/perl # Originally by Simon Tatham # Modified by Richard Kettlewell, Colin Watson, Ian Jackson # # Copyright -2011 Simon Tatham # Copyright 2011 Richard Kettlewell # Copyright 2011 Colin Watson # Copyright 2011 Ian Jackson # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # SOFTWARE IN THE PUBLIC INTEREST, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. use ChiarkNNTP; our $verbose; ($verbose='STDERR', shift @ARGV) if $ARGV[0] eq "-v"; my $c = cnntp_connect($verbose); $c->banner_reader(); our $code; # some servers require a GROUP before an ARTICLE command $c->docmd("GROUP misc.misc"); if(@ARGV == 0) { while(<>) { s/(^\s+|\s+$)//gs; lookup($_); } } else { while (@ARGV) { my $item = shift @ARGV; if($item !~ /[\@:]/ and not defined $group) { # maybe a bare group followed by an article number die unless @ARGV; my $number = shift @ARGV; $item = "$item $number"; } lookup($item); } } $c->docmd("QUIT"); close S; sub lookup { my $mid = shift; if($mid !~ /\@/ and $mid =~ /^(.*)[: ](\d+)$/) { my ($g, $n) = ($1, $2); $c->docmd("GROUP $g"); $c->docmd("ARTICLE $n"); } else { $mid =~ s/.*\.*//; $c->docmd("ARTICLE <$mid>"); } my $fh= 'STDOUT'; if (-t $fh) { my $lesscmd= $ENV{'NNTPID_PAGER'}; $lesscmd= 'less' unless defined $lesscmd; open LESS, "|-", 'sh','-c',$lesscmd or die $!; $fh= 'LESS'; } while (1) { ($code,$_) = $c->getline(); s/[\r\n]//g; last if /^\.$/; s/^\.//; print $fh "$_\n"; } if ($fh ne 'STDOUT') { close $fh or die "$? $!"; } } chiark-utils-4.4.2build1/scripts/Makefile0000664000000000000000000000332012423200672015234 0ustar # Makefile # This file is part of chiark-utils, a collection of useful programs # used on chiark.greenend.org.uk. # # This file is: # Copyright (C) 2001 Ian Jackson # # This is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3, or (at your option) any later version. # # This is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. include ../settings.make SCRIPTS= palm-datebook-reminders random-word expire-iso8601 \ genspic2gnuplot gnucap2genspic ngspice2genspic \ cvs-repomove cvs-adjustroot remountresizereiserfs \ hexterm summarise-mailbox-preserving-privacy \ git-cache-proxy MANPAGES1= palm-datebook-reminders CSCRIPTS= named-conf CMANPAGES8= named-conf all: install: $(INSTALL_DIRECTORY) $(bindir) set -e; for f in $(SCRIPTS); do \ $(INSTALL_SCRIPT) $$f $(bindir)/$$f; done set -e; for f in $(CSCRIPTS); do \ $(INSTALL_SCRIPT) $$f $(bindir)/chiark-$$f; done install-docs: $(INSTALL_DIRECTORY) $(man1dir) $(man8dir) set -e; for f in $(MANPAGES1); do \ $(INSTALL_SHARE) $$f.1 $(man1dir)/$$f.1; done set -e; for f in $(CMANPAGES8); do \ $(INSTALL_SHARE) $$f.8 $(man8dir)/chiark-$$f.8; done install-examples: clean: rm -f *~ ./#*# distclean realclean: clean chiark-utils-4.4.2build1/scripts/random-word0000775000000000000000000000531112423200672015755 0ustar #!/usr/bin/perl -w # Copyright 2004 Ian Jackson # # This script and its documentation (if any) are free software; you # can redistribute it and/or modify them under the terms of the GNU # General Public License as published by the Free Software Foundation; # either version 3, or (at your option) any later version. # # chiark-named-conf and its manpage are distributed in the hope that # it will be useful, but WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. use strict; use IO::Handle; use IO::File; use POSIX; our $want= 1; our $filename= "/usr/share/dict/words"; our @randfile= ("/dev/urandom", "/dev/random"); our $filemaxlen; sub fail ($) { die "random-word: $_[0]\n"; } open D, ">/dev/null" or fail("open /dev/null: $!"); while (@ARGV && $ARGV[0] =~ m/^\-/) { $_= shift @ARGV; if (m/^\-\-?$/) { last; } elsif (m/^\-n(\d+)$/) { $want= $1; } elsif (m/^\-f/ && length > 2) { $filename= $'; #'; } elsif (m/^\-F(\d+)$/) { $filemaxlen= $1; } elsif (m/^\-r/ && length > 2) { @randfile= ($'); #'); } elsif (m/^\-D$/) { open D, ">&STDERR" or fail("dup stderr for debug: $!"); } else { fail("unknown option \`$_'"); } } sub debug ($) { print D "random-word: debug: $_[0]\n" or fail("write debug: $!"); } our $randfile; our $r; for $randfile (@randfile) { $r= new IO::File "$randfile", 'r'; debug("open $randfile ".($r ? "ok" : "failed $!")); last if $r; $!==&ENOENT or fail("cannot open $randfile: $!"); } $r or fail("could not open any random device: $!\n (tried @randfile)"); $r->autoflush(0); our $w= new IO::File $filename, 'r'; $w or fail("cannot open $filename: $!"); debug("open $filename ok"); our @words; if (defined $filemaxlen) { while (@words < $filemaxlen) { my $l = <$w>; last unless defined $l; push @words, $l; } } else { @words= <$w>; } $w->error and fail("cannot read $filename: $!"); debug("read $filename ok"); our @out; while (@out < $want) { my $rbytes; $!=0; read $r,$rbytes,4; length $rbytes==4 or fail("cannot read $randfile: $!"); my $wordno= unpack 'L',$rbytes; $wordno &= ~0x80000000; $wordno %= @words; $_= $words[$wordno]; chomp; debug("picked $wordno \`$_'"); next unless m/^([a-z][-a-z]+)$/; push @out, $1; debug("good, now ".scalar @out); } debug("enough"); print join(' ',@out), "\n" or fail("cannot write output: $!"); chiark-utils-4.4.2build1/scripts/cvs-adjustroot0000775000000000000000000000245612423200672016522 0ustar #!/bin/bash set -e usage () { echo >&2 'usage: cvs-adjustroot OLD NEW'; exit 1; } case "$#.$1" in 4.--reinvoke) reinvoke=true; shift ;; *.-*) usage ;; 2.*) reinvoke=false ;; 1.*) usage ;; 0.*) usage ;; *) usage ;; esac # Copyright 2004 Ian Jackson # # This script and its documentation (if any) are free software; you # can redistribute it and/or modify them under the terms of the GNU # General Public License as published by the Free Software Foundation; # either version 3, or (at your option) any later version. # # chiark-named-conf and its manpage are distributed in the hope that # it will be useful, but WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. old="$1"; shift new="$1"; shift if $reinvoke; then filename="$1"; cmp -- "$filename" <(printf "%s\n" "$old") printf "%s\n" "$new" >"$filename".new mv -f -- "$filename".new "$filename" exit 0 fi find -path '*/CVS/Root' -exec cvs-adjustroot --reinvoke "$old" "$new" '{}' ';' chiark-utils-4.4.2build1/scripts/named-conf0000775000000000000000000010542012423200672015535 0ustar #!/usr/bin/perl -w # This is chiark-named-conf, which is Copyright 2002 Ian Jackson. # # chiark-named-conf and its manpage are free software; you can # redistribute it and/or modify them under the terms of the GNU # General Public License as published by the Free Software Foundation; # either version 3, or (at your option) any later version. # # chiark-named-conf and its manpage are distributed in the hope that # it will be useful, but WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. use strict; use IO::File; use Data::Dumper; use POSIX; use Fcntl qw(:DEFAULT :flock); # bastard Perl wants me to do this now ! sub loarg(); sub soarg(); sub usageerr ($); sub cfg_fail ($); sub read_config ($); sub qualify ($); sub bad_modifiers ($); sub zone_conf ($$$$$@); sub set_output($); sub progress ($$); sub verbose ($); sub process_zones (@); sub zone_warning ($$); sub zone_warnmore ($); sub zone_check_full (); sub zone_reset(); sub zone_investigate(); sub zone_check_nsrrset ($$$$); sub zone_ns_name ($$); sub zone_server_queue ($$$$$); sub zone_server_addr ($$$$$); sub zone_check_soa ($$$$); sub zone_consistency(); sub zone_servers_ok (); sub zone_consistency_set ($%); sub zone_check_local (); sub zone_servers_simplefind (); sub zone_server_simple ($$$); sub zone_style ($$); sub mail_zone_before (); sub mail_zone_after (); sub pmail ($); sub ptime ($); sub mail_zone_mail (); sub zone_output (); sub output_files (); sub debug_dump ($); sub debug_trace ($); sub has_suffix_of ($$); sub lookup ($$$$); sub dig (&$$$); sub domain_canon ($$); use vars qw($quis $stdout_fh $stderr_fh $mode $doall $domail $etcfile $where $debug $needglue $localonly $repeat $verbosity $admin $mail_state_dir $mail_max_warnfreq $progress_fh $warn_fh $modifiers %group2modcmd %group2used); $|=1; $quis= $0; $quis =~ s,.*/,,; $mode= ''; $doall= 0; $etcfile= "/etc/bind/chiark-conf-gen.zones"; $where= ''; $debug= 0; $needglue= 1; $localonly= 0; $verbosity= 2; $admin=''; $mail_state_dir=''; $mail_max_warnfreq= 50; $repeat= 0; $domail= ''; $modifiers= ''; $group2modcmd{'foreign'}= '$!*@?'; $group2used{'foreign'}= 1; ($progress_fh= $stdout_fh= new_from_fd IO::Handle(1,'w') and $warn_fh= $stderr_fh = new_from_fd IO::Handle(2,'w')) or die "$quis: setup standard filehandles: $!\n"; use vars qw($dig_owner $dig_type $dig_rdata); while (@ARGV && $ARGV[0] =~ m/^\-/) { $_= shift @ARGV; if (s/^\-\-//) { last if m/^$/; if (m/^(yes|no|force)$/) { m/^./; $mode= $&; $domail=''; } elsif (m/^nothing$/) { $mode= 'x'; $domail=''; } elsif (m/^mail\-(first|middle|final|final\-test)$/) { $mode='n'; $domail=$1; } elsif (m/^all$/) { $doall=1; } elsif (m/^config$/) { $etcfile= loarg(); $where= '--config option'; } elsif (m/^glueless$/) { $needglue=0; } elsif (m/^localonly$/) { $localonly=1; } elsif (m/^quiet$/) { $verbosity--; } elsif (m/^repeat$/) { $repeat=1; } elsif (m/^verbose$/) { $verbosity++; } else { usageerr("unknown option --$_"); } } else { s/^\-//; last if m/^$/; while (m/^./) { if (s/^[ynf]//) { $mode=$&; $domail=''; } elsif (s/^A//) { $doall=1; } elsif (s/^C//) { $etcfile= soarg(); $where= '-C option'; } elsif (s/^D//) { $debug++; } elsif (s/^g//) { $needglue=0; } elsif (s/^l//) { $localonly=1; } elsif (s/^m(\w+)(\W+)$//) { my ($g,$m) = ($1,$2); $group2modcmd{$g}=$m; usageerr("modifiers $m for group $g: $@") if bad_modifiers($m); } elsif (s/^q//) { $verbosity--; } elsif (s/^r//) { $repeat=1; } elsif (s/^v//) { $verbosity++; } else { usageerr("unknown option -$&"); } } } } sub loarg() { usageerr("missing option value") if !@ARGV; return shift @ARGV; } sub soarg() { my ($rv); $rv=$_; $_=''; return length $rv ? $rv : loarg(); } usageerr("-q may be specified at most twice") if $verbosity<0; usageerr("-v may be specified at most once") if $verbosity>3; usageerr("-D may be specified at most twice") if $debug>2; usageerr("must specify either -f|-y|-n or zones (and not both)") if !!$mode == !!@ARGV && !$domail; sub usageerr ($) { die <... operation modes: -f --force install without checking -y --yes check and install -n --no check only (configured zones) --nothing list zones only --mail-* send mail about broken zones (see manpage) ... check only (specified zones, even unconfigured ones) additional options: -A --all report on zones marked ? (ones we know are broken) -D debug $quis (does not help debug your DNS config) -g --glueless do not warn about any glueless referrals (not recommended) -l --localonly full checks only on zones which we primary -q --quiet no output for OK zones -r --repeat repeat warnings for all sources of imperfect data -v --verbose extra verbose info about each zone -C|--config close or die "$quis: write messages to stdout: $!\n"; $stderr_fh->close or die "$quis: write messages to stderr: $!\n"; exit 0; #-------------------- configuration reading sub cfg_fail ($) { die "$quis: $where:\n $_[0]\n"; } sub read_config ($) { my ($if) = @_; my ($fh,$z,@self,$before,$group, $mod,$dir,$prefix,$suffix,$subfile,$lprefix,$lsuffix,$zf); local ($_); $fh= new IO::File $if,'r'; unless ($fh) { return if $! == &ENOENT; cfg_fail("open $if:\n $!"); } $before= ''; for (;;) { if (!defined($_= <$fh>)) { cfg_fail("read config file $if:\n $!") if $fh->error(); last; } chomp; s/\s+$//; if (s/\\$//) { $before.= $_; next; } $_= $before.$_; $before= ''; s/^\s+//; $where= "$if:$."; next if m/^\#/; last if m/^end$/; next unless m/\S/; if (m/^self(\-ns|\-soa|)\s+(\S.*\S)/) { @self= split /\s+/, $2; @self_ns= @self if $1 ne '-soa'; @self_soa= @self if $1 ne '-ns'; } elsif (m/^serverless\-glueless\s+(\S.*\S)/) { @conv_glueless= split /\s+/, $1; } elsif (m/^allow\-indirect\-glue\s+(\S.*\S)/) { @indirect_glue= split /\s+/, $1; } elsif (m/^self\-addr\s+([0-9. \t]+)/) { @self_addr= split /\s+/, $1; } elsif (m/^forbid\-addr(?:\s+([0-9. \t]+))?/) { @forbid_addr= defined $1 ? split /\s+/, $1 : (); } elsif (m/^forbid\-slave(?:\s+([0-9. \t]+))?/) { @forbid_slave= defined $1 ? split /\s+/, $1 : (); } elsif (m,^ primary\-dir (\W*) \s+ (\S+)/([^/ \t]*) (?: \s+ ([^/ \t]*) (?: (/.+) )? )? $,x) { ($mod, $dir, $prefix, $suffix, $subfile) = ($1,qualify($2),$3,$4,$5); cfg_fail("modifiers $mod for directory $dir: $@") if bad_modifiers($mod); $suffix= '' if !defined $suffix; $subfile= '' if !defined $subfile; $suffix= '_db' if !length $suffix && !length $subfile; if (-d "$dir/$prefix") { $dir.='/'; $dir.=$prefix; $prefix=''; } opendir D, $dir or cfg_fail("open primary-dir $dir:\n $!"); $lprefix= length $prefix; $lsuffix= length $suffix; while (defined($_= readdir D)) { next if m/^\./ && !$lprefix; next unless length > $lprefix+$lsuffix; next unless substr($_,0,$lprefix) eq $prefix; next unless substr($_,length($_)-$lsuffix) eq $suffix; $z= substr($_,$lprefix,length($_)-($lprefix+$lsuffix)); $zf= $dir.'/'.$prefix.$z.$suffix.$subfile; if (!stat $zf) { next if length $subfile && $! == &ENOENT; cfg_fail("cannot stat zonefile $zf:\n $!"); } -f _ or cfg_fail("zonefile $zf is not a plain file"); zone_conf($z,'primary','p',$mod,$zf); } closedir D or cfg_fail("close primary-dir $dir:\n $!"); } elsif (m/^primary(\W*)\s+(\S+)\s+(\S+)$/) { zone_conf($2,'primary','p',$1,qualify($3)); } elsif (m/^published(\W*)\s+(\S+)\s+([0-9.\t]+)$/) { zone_conf($2,'published','s',$1,'',$3); } elsif (m/^stealth(\W*)\s+(\S+)\s+([0-9. \t]+)$/) { zone_conf($2,'stealth','u',$1,'',split /\s+/, $3); } elsif (m/^modifiers\s+(\W+)(?:\s+(\w+))$/) { ($mod,$group) = ($1,$2); cfg_fail("modifiers $mod for group $group: $@") if bad_modifiers($mod); if (exists $group2modcmd{$group}) { $mod= $group2modcmd{$group}; $group2used{$group}++; } $modifiers= $mod; } elsif (m/^slave\-dir\s+(\S+)(?:(?:\s+(\S+))\s+(\S+))?$/) { ($slave_dir, $slave_prefix, $slave_suffix) = (qualify($1),$2,$3); $slave_prefix='' if !defined $slave_prefix; $slave_suffix='' if !defined $slave_suffix; } elsif (m/^output\s+bind8\+(\S+)$/) { cfg_fail("default output may not apply to only some zones") if @zone_cfg_list && length $default_output; set_output(qualify($1)); } elsif (m/^include\s+(\S+)$/) { read_config($1); } elsif (m/^admin\s+(\S+)$/) { $admin=$1; } elsif (m/^mail\-state\-dir\s+(\S+)$/) { $mail_state_dir= $1; } elsif (m/^mail\-max\-warnfreq\s+(\d{1,3}(?:\.\d{0,5})?)$/) { cfg_fail("mail-max-warnfreq must be <=100") if $1>100; $mail_max_warnfreq= $1; } else { cfg_fail("unknown configuration directive". " or incorrect syntax or arguments:\n". " \`$_'"); } } foreach $group (keys %group2modcmd) { next if exists $group2used{$group}; cfg_fail("command line specifies modifier group $group". " but missing in configuration file"); } $fh->close or cfg_fail("close config file $if:\n $!"); } sub qualify ($) { my ($i) = @_; $i= "$default_dir$i" unless $i =~ m,^/,; return $i; } sub bad_modifiers ($) { local ($_) = @_; if (!eval { die "bad modifier $&" if m/[^!*\$\@~?]/; die "repeated modifier $1" if m/(.).*\1/; 1; }) { $@ =~ s/\n//; return 1; } return 0; } sub zone_conf_settings ($$) { my ($cfg,$zone) = @_; my ($sfx,$aref); foreach $sfx (qw(self_soa self_ns self_addr forbid_addr forbid_slave conv_glueless indirect_glue)) { { no strict 'refs'; $aref= [ @$sfx ]; } @$aref or cfg_fail("failed to specify $sfx before zone") if $sfx =~ m/^self/; $cfg->{$sfx}= $aref; } foreach $sfx (qw(self_soa self_ns)) { map { s/\*$/$zone/ } @{ $zone_cfg{$zone}{$sfx} }; } } sub zone_conf ($$$$$@) { my ($zone,$style,$sabbr,$mod,$file,@servers) = @_; $file= qualify("$slave_dir/$slave_prefix".$zone.$slave_suffix) unless length $file; if (!length $output) { $default_output= qualify('chiark-conf-gen.bind8') unless length $default_output; set_output($default_output); } cfg_fail("redefined zone $zone\n". " earlier definition $zone_cfg{$zone}{'where'}") if exists $zone_cfg{$zone}; $zone_cfg{$zone}{'where'}= $where; $zone_cfg{$zone}{'file'}= $file; $zone_cfg{$zone}{'style_p'}= $style.$mod; $zone_cfg{$zone}{'s'}= "$sabbr $mod $modifiers"; # p)rimary s)econdary u)npub f)oreign # followed by modifiers, first per-zone, then default $zone_cfg{$zone}{'servers'}= [ @servers ]; if ($domail) { length $admin && length $mail_state_dir or cfg_fail("mailing but failed to specify admin". " or mail-state-dir before zone"); $zone_cfg{$zone}{'admin'}= $admin; $zone_cfg{$zone}{'maildir'}= qualify($mail_state_dir); $zone_cfg{$zone}{'mailmwarn'}= $mail_max_warnfreq; } zone_conf_settings($zone_cfg{$zone}, $zone); $zone_cfg{$zone}{'output'}= $output; push @zone_cfg_list, $zone; } sub set_output($) { my ($newout) = @_; $output= $newout; $output_contents{$output}= ''; } #-------------------- checking use vars qw($zone $cfg $warnings %zone_warnings); $warnings= 0; sub progress ($$) { my ($minv,$m) = @_; return if $verbosity < $minv; print $progress_fh "$m\n" or die "$quis: $zone: write log: $!\n"; } sub verbose ($) { progress(3, ' ' . $_[0]); } sub process_zones (@) { my (@zones) = @_; local ($zone,$cfg); foreach $zone (@zones) { if ($zone =~ m/\.$/) { zone_warning("zone specified with trailing dot -". " will not work", ''); } $cfg= $zone_cfg{$zone}; if (!$cfg) { $cfg= { 'style_p' => 'foreign', 's' => "f $group2modcmd{'foreign'}", 'servers' => [ ], }; zone_conf_settings($cfg, $zone); } mail_zone_before() or next if $domail; zone_reset(); progress(1, sprintf "%-20s %s", $zone, $$cfg{'style_p'}); if ($check && ($doall || !zone_style('?',0))) { eval { if ($localonly && $cfg->{'s'} =~ m/f/) { zone_warning("foreign zone specified with -l",''); } elsif ($localonly && $cfg->{'s'} !~ m/p/) { zone_check_local(); } else { zone_check_full(); } }; zone_warning("checks failed: $@",'') if length $@; } $output_contents{$$cfg{'output'}} .= zone_output() if $install; mail_zone_after() if $domail; } if ($domail) { } elsif ($warnings) { printf STDERR ("%s: %d warning(s) in %d zone(s);". " %d zone(s) checked ok.\n", $quis, $warnings, scalar(keys %zone_warnings), scalar(@zones - keys %zone_warnings)); } else { progress(1, sprintf "%d zone(s) checked ok", scalar @zones); } } use vars qw(%delgs); # $delgs{$nameserver_list} = [ $whosaidandwhy ] use vars qw(%auths); # $auths{$nameserver_list} = [ $whosaidandwhy ] use vars qw(%glue); # $glue{$name}{$addr_list} = [ $whosaidandwhy ] use vars qw(%soas); # $soa{"$serial $mname"} = [ $whosaidandwhy ] use vars qw(%addr_is_ok %warned); use vars qw($delg_to_us); use vars qw(@to_check); # ($addr,$whyask,$name_if_auth,$glueless_ok, ...) use vars qw(@to_check_soa); # ($addr,$whyask,$name,$is_ns, ...) sub zone_warning ($$) { my ($w,$o) = @_; my ($wk); return 0 if !$repeat && $warned{$w}++; $w =~ s/\n$//; $w =~ s,\n, // ,g; $w .= " ($o)" if length $o; print $warn_fh "$zone: warning: $w\n" or die $!; $warnings++; $zone_warnings{$zone}++; return 1; } sub zone_warnmore ($) { print $warn_fh "$zone: $_[0]\n" or die $!; } sub zone_check_full () { zone_investigate(); zone_consistency(); zone_servers_ok(); } sub zone_reset() { %delgs= %auths= %glue= %soas= %warned= %addr_is_ok= (); $delg_to_us= 0; @to_check= @to_check_soa= (); } sub zone_investigate() { my ($super_zone, @start_nsnames, $start_ww, $start_ns, @start_ns_addrs, $s, $wa, $name_if_auth, %nsrrset_checked, %soa_checked, $addr, $glueless_ok, $rcode, $name, $is_ns); if (!zone_style('*',0)) { $super_zone= $zone; for (;;) { debug_trace("zone $zone superzone $super_zone"); $super_zone eq '.' and die "no superzone ? ($super_zone)\n"; $super_zone =~ s/^[^.]+\.// or $super_zone= '.'; ($rcode,@start_nsnames)= lookup($super_zone,'ns-','06',"superzone search"); last if !$rcode; } $start_ww= "server for $super_zone"; } else { ($rcode,@start_nsnames)= lookup($zone,'ns-','0',"initial nameserver search"); $start_ww= "nameserver for $zone"; } for $start_ns (@start_nsnames) { $start_ns= lc $start_ns; ($rcode,@start_ns_addrs)= lookup($start_ns,'a','0',"$start_ww"); foreach $addr (@start_ns_addrs) { push @to_check, $addr, "$start_ns, $start_ww", undef, 0; } } for (;;) { # We do these in order so that we always do NS RRset checks on # nameservers that came from other NS RRsets first; otherwise # we might set nsrrset_checked due to a glueless_ok check, # and then not check for gluefulness later. debug_dump('@to_check @to_check_soa'); if (($addr,$wa,$name_if_auth,$glueless_ok,@to_check) = @to_check) { push @to_check_soa, $addr, $wa, $name_if_auth, 1, if defined $name_if_auth; next if $nsrrset_checked{$addr}++; zone_check_nsrrset($addr, $wa, $name_if_auth, $glueless_ok); } elsif (($addr,$wa,$name,$is_ns,@to_check_soa) = @to_check_soa) { next if $soa_checked{$addr}++; zone_check_soa($addr,$wa,$name,$is_ns); } else { last; } } } sub zone_check_nsrrset ($$$$) { my ($uaddr,$wa, $name_if_auth, $glueless_ok) = @_; my (@s, $s, $a, %s2g, @glue, $glue, $delgs_or_auths, $wwn, $ww, $cg); my ($rcode); $ww= "[$uaddr] $wa"; verbose("checking delegation by $ww"); dig(sub { if ($dig_type eq 'ns' && $dig_owner eq $zone) { $s2g{lc $dig_rdata} = [ ]; } elsif ($dig_type eq 'a' && exists $s2g{$dig_owner}) { $wwn= "in glue from $ww"; zone_server_queue($dig_rdata,$dig_owner,$wwn,"NS [$uaddr]",0); push @{ $s2g{$dig_owner} }, $dig_rdata; } }, $zone,'ns',$uaddr); if (!%s2g) { zone_warning("unable to find NS RRset at [$uaddr]", $wa); return; } elsif (keys %s2g == 1) { zone_warning("only one nameserver ". (join '', keys %s2g), $ww); } @s= sort keys %s2g; foreach $s (@s) { zone_ns_name($s,$ww); @glue= @{ $s2g{$s} }; if (!@glue) { zone_warning("glueless NS $s", $ww) unless $glueless_ok or zone_style('~',!$needglue) or grep { has_suffix_of($zone,".$_"); } @{ $cfg->{'conv_glueless'} } or ((grep { has_suffix_of($s,".$_"); } @{ $cfg->{'indirect_glue'} }) and !(grep { has_suffix_of($zone,".$_"); } @{ $cfg->{'indirect_glue'} })); foreach $cg (@{ $cfg->{'conv_glueless'} }) { zone_warning("nameserver $s (glueless) in". " serverless-glueless namespace area $cg", $ww) if has_suffix_of(".$s",".$cg"); } ($rcode,@glue)= lookup($s,'a','0',"glueless NS from $ww"); foreach $a (@glue) { $wwn= "glueless NS from $ww"; zone_server_queue($a,$s,$wwn,"NS [$uaddr]",0); } } $glue= join ' ', sort @glue; push @{ $glue{$s}{$glue} }, $ww; } $s= join ' ', @s; $delgs_or_auths= defined($name_if_auth) ? \%auths : \%delgs; push @{ $delgs_or_auths->{$s} }, $ww; } sub zone_ns_name ($$) { my ($name,$ww) = @_; $delg_to_us=1 if grep { $name eq $_ } @{ $cfg->{'self_ns'} }; zone_warning("published server, as $name, but configured as stealth", $ww) if $cfg->{'s'} =~ m/u/ && grep { $_ eq $name } @{ $cfg->{'self_ns'} }, @{ $cfg->{'self_soa'} }; } sub zone_server_queue ($$$$$) { my ($addr,$name,$wwn,$wwq,$is_soa) = @_; zone_server_addr($addr,$name,$wwn,$wwq,$is_soa); push @to_check, $addr, "$name, $wwn", $name, $is_soa; } sub zone_server_addr ($$$$$) { my ($addr,$name,$ww,$wwq,$is_soa) = @_; debug_trace("zone_server_addr ".join('|',@_)); $addr_is_ok{$addr}= "$name ($wwq)" if $is_soa || $cfg->{'s'} =~ m/u/; zone_warning("forbidden nameserver address [$addr] $name",$ww) if grep { $_ eq $addr } @{ $cfg->{'forbid_addr'} }; zone_warning("forbidden server address for our slave [$addr] $name",$ww) if $cfg->{'s'} =~ m/p/ and grep { $_ eq $addr } @{ $cfg->{'forbid_slave'} }; my ($name_is_self, $addr_is_self); $name_is_self= grep { $_ eq $name } @{ $cfg->{$is_soa ? 'self_soa' : 'self_ns'} }; $addr_is_self= grep { $_ eq $addr } @{ $cfg->{'self_addr'} }; if ($name_is_self && !$addr_is_self) { zone_warning("our $name supplied with wrong address [$addr]", $ww); } if (!$name_is_self && $addr_is_self) { zone_warning("we [$addr] are named in ". ($is_soa ? "SOA" : "NS"). " by wrong name $name", $ww); } if (!$name_is_self && !$addr_is_self && $is_soa && $cfg->{'s'} =~ m/p/) { zone_warning("SOA MNAME $name is not us (". (join ' ', @{ $cfg->{'self_soa'} }).")", $ww); } $delg_to_us=1 if $addr_is_self && !$is_soa; } sub zone_check_soa ($$$$) { my ($uaddr,$wa,$name,$is_ns) = @_; my ($lame,$serial,$mname,$got,$rcode,@soa_addrs,$soa_addr,$ww,$wwn); verbose("checking service at [$uaddr] $name"); $lame= 'dead or lame'; $ww= "[$uaddr] $wa"; dig(sub { if ($dig_type eq 'flags:') { $lame= $dig_rdata =~ m/ aa / ? '' : 'lame'; } elsif ($dig_type eq 'soa' && $dig_owner eq $zone && !$lame) { die "several SOAs ? $ww" if defined $mname; $got= $dig_rdata; $got =~ m/^(\d+) (\S+)$/ or die "$got ?"; ($serial,$mname) = ($1,$2); } }, $zone,'soa',$uaddr); $lame= 'broken' if !$lame && !defined $mname; if ($lame) { zone_warning("$lame server [$uaddr]",$wa); return; } progress(2, sprintf "%-16s %46s has %s%s", $zone, "$name [$uaddr]", $serial, $is_ns ? '' : '*'); push @{ $soas{$got} }, $ww; ($rcode,@soa_addrs)= lookup($mname,'a','0',"SOA MNAME"); $wwn= "SOA MNAME from $ww"; foreach $soa_addr (@soa_addrs) { zone_server_queue($soa_addr,$mname,$wwn,"SOA [$uaddr]",1); } } sub zone_consistency() { my ($d, $org_ser, $mname, $a, $h, $self_soa, $wa); zone_consistency_set('delegations',\%delgs); foreach $d (keys %delgs) { delete $auths{$d}; } zone_consistency_set('zone nameserver rrset',\%auths); foreach $h (keys %glue) { zone_consistency_set("glue for $h", $glue{$h}); } zone_consistency_set("serial number and/or SOA MNAME",\%soas); $self_soa= $cfg->{'self_soa'}; } sub zone_servers_ok () { my ($showok,%fs); if (%addr_is_ok) { $showok= 0; foreach $a (@{ $cfg->{'servers'} }) { next if exists $addr_is_ok{$a}; zone_warning("we slave from [$a]",'') and $showok=1; } if ($showok) { foreach $a (keys %addr_is_ok) { zone_warnmore("permitted master [$a] $addr_is_ok{$a}"); } } } if ($cfg->{'s'} =~ m/s/ && !$delg_to_us) { zone_warning("we are supposedly published secondary,". " but not listed as a nameserver",''); map { $fs{$_}=1 } keys(%delgs), keys(%auths); zone_warnmore("servers are: ". join ' ', sort keys %fs); } } sub zone_consistency_set ($%) { my ($msg,$set) = @_; my ($d,$o); if (keys(%$set) > 1) { zone_warning("inconsistent $msg:",''); foreach $d (keys %$set) { foreach $o (@{ $set->{$d} }) { zone_warnmore(" $d from $o"); } } } } sub zone_check_local () { zone_servers_simplefind(); zone_servers_ok(); } sub zone_servers_simplefind () { my ($rcode,@nsnames,$ns,@soas,$mname); ($rcode,@nsnames)= lookup($zone,'ns-','0',"zone's servers"); foreach $ns (@nsnames) { zone_ns_name($ns,"NS"); zone_server_simple($ns,'NS',0); } $delgs{join ' ', sort @nsnames} = [ "zone's servers" ]; ($rcode,@soas)= lookup($zone,'soa','0',"SOA MNAME"); die "multiple SOA RRs in set! @soas ?" if @soas!=1; $soas[0] =~ m/^(\S+)\s/ or die "SOA ? $_"; zone_server_simple(domain_canon($1,"lookup $zone SOA"),'SOA',1); } sub zone_server_simple ($$$) { my ($name,$ww,$is_soa) = @_; my ($rcode,@addrs,$addr); ($rcode,@addrs)= lookup($name,'a','0', "server - ". ($is_soa ? "SOA MNAME" : "NS")); foreach $addr (@addrs) { zone_server_addr($addr,$name,$ww,$ww,$is_soa); } } sub zone_style ($$) { my ($stylechar, $default) = @_; local ($_) = $$cfg{'s'}; $stylechar =~ s/\W/\\$&/ or die; return m/(\S*)$stylechar/ ? $1 !~ m/\!/ : $default; } #-------------------- mailing use vars qw($m_base $m_lastok @m_ok @m_fail $m_info $m_time $m_lock $m_m); sub mail_zone_before () { my (@s1,@s2); $m_base= $$cfg{'maildir'}.'/'.$zone; $m_lastok= '-'; @m_ok= (); @m_fail= (); for (;;) { $m_lock= new IO::File "${m_base}_lock", O_RDWR|O_CREAT, 0600 or die "$quis: create lockfile ${m_base}_lock: $!\n"; if (!flock($m_lock, LOCK_EX|LOCK_NB)) { <$m_lock> =~ m/^\d+ /; die "$quis: $zone: concurrrency? - flock $&$!\n"; $m_lock->close; return 0; } (@s1= $m_lock->stat) or die "$quis: fstat ${m_base}_lock: $!\n"; (@s2= stat "${m_base}_lock") or die "$quis: stat ${m_base}_lock: $!\n"; last if ($s1[0] eq $s2[0] && $s1[1] eq $s2[1]); $m_lock->close; } (print $m_lock "$$ \n" and $m_lock->flush) or die "$quis: write pid to ${m_base}_lock: $!\n"; if ($m_info= new IO::File "${m_base}_info", 'r') { $!=0; $_= <$m_info>; $_ =~ m/\n/ or die "$quis: read ${m_base}_info: $!\n"; m/^\d+ (\d+|-) ([0-9:]*) ([0-9:]*) / or die "$quis: ${m_base}_info malformed\n"; $m_lastok= $1; if ($domail ne 'first') { if ($2 eq ':' && $3 eq ':') { warn "$quis: $zone: mid/last run, but last". "run already done, ignoring zone\n"; return 0; } @m_ok= split /\:/, $2; @m_fail= split /\:/, $3; } } elsif ($! != &ENOENT) { die "$quis: open ${m_base}_info: $!\n"; } elsif ($domail ne 'first') { warn "$quis: $zone: first run, but not --mail-first,". " ignoring zone\n"; return 0; } if ($domail eq 'first') { remove "${m_base}_history" or $!==&ENOENT or die "$quis: remove ${m_base}_history: $!\n"; } $progress_fh= $warn_fh= new IO::File "${m_base}_history",'a',0666 or die "$quis: open ${m_base}_history: $!\n"; $m_info= new IO::File "${m_base}_info.tmp",'w',0666 or die "$quis: open ${m_base}_info.tmp: $!\n"; $m_time= time; progress(-1, "\n".('-'x70)."\n".ptime($m_time)."\n"); return 1; } sub mail_zone_after () { if ($zone_warnings{$zone}) { push @m_fail, $m_time; progress(-1,"failed - $zone_warnings{$zone} warnings"); } else { push @m_ok, $m_time; $m_lastok= $m_time; progress(-1,"everything is fine"); } close $progress_fh or die "$quis: close ${m_base}_history: $!\n"; $progress_fh= $warn_fh= $stderr_fh; if ($domail =~ m/^final/) { if (100*@m_fail <= $$cfg{'mailmwarn'}*(@m_fail + @m_ok)) { printf " %-40s ok\n", $zone or die "$quis: mail ok report: $!\n"; } elsif (zone_style('@',0)) { printf " %-40s mail suppressed\n", $zone or die "$quis: mail suppress report: $!\n"; } else { mail_zone_mail(); } } else { printf " %-40s %d warns. OK %s Fail %s\n", $zone, defined $zone_warnings{$zone} ? $zone_warnings{$zone} : 0, join(',', map { $_ - $m_time } @m_ok), join(',', map { $_ - $m_time } @m_fail) or die "$quis: checking progress report: $!\n"; } @m_fail= @m_ok= ('','') if $domail =~ m/^final/; printf $m_info "%s %s %s %s \n", $m_time, $m_lastok, join(':',@m_ok), join(':',@m_fail) or die "$quis: write new ${m_base}_info: $!\n"; $m_info->close or die "$quis: close new ${m_base}_info: $!\n"; rename "${m_base}_info.tmp", "${m_base}_info" or die "$quis: install new ${m_base}_info: $!\n"; $m_lock->close; } sub pmail ($) { print $m_m $_[0] or die "$quis: write ${m_base}_mail: $!\n"; } sub ptime ($) { my ($time) = @_; return gmtime($time)." GMT ($time)"; } sub mail_zone_mail () { my ($log, $zone_to, $zterr, $c, $r, $t, @soas); $m_m= new IO::File "${m_base}_mail", 'w', 0666 or die "$quis: create ${m_base}_mail: $!\n"; $zone_to=''; $zterr=''; if (!zone_style("\$", $$cfg{'s'} !~ m/[ps]/)) { eval { ($r,@soas)= lookup($zone,'soa','0','problem-addr'); @soas==1 or die "multiple soas\n"; $soas[0] =~ m/^\S+ (\S.*\@\S+) [0-9 ]+$/ or die "bad soa \`$_'\n"; $zone_to= $1; }; $zterr= $@; $zterr =~ s/\n$//; } pmail < Subject: $zone - configuration problems report END ; pmail("To: "); pmail("(testing!) ") if $domail ne 'final'; pmail("SOA MNAME for $zone <$zone_to>\nCC: ") if length($zone_to); pmail($$cfg{'admin'}."\n\n"); pmail <getline); $/= "\n"; (!$log->error and $log->close) or die "$quis: reread or close ${m_base}_log: $!\n"; $log->eof or die; $m_m->close or die "$quis: close ${m_base}_mail: $!\n"; $m_m= new IO::File "${m_base}_mail" or die "$quis: reopen ${m_base}_mail: $!"; defined($c= fork) or die "$quis: fork for mail: $!\n"; if (!$c) { defined dup2($m_m->fileno, 0) or die "$quis - sendmail: dup for stdin: $!\n"; exec (qw(/usr/sbin/sendmail -odb -oee -oi), ($domail eq 'final' ? '-t' : $$cfg{'admin'})); die "$quis - sendmail: exec: $!\n"; } $m_m->close; $!=0; $r= waitpid $c,0; $r == $c or die "$quis: waitpid sendmail ($c): $r $!"; $? and warn "$quis: sendmail failed: $?\n"; printf " %-40s %s\n", $zone, length $zone_to ? $zone_to : 'reporting to admin' or die "$quis: write mailing report: $!\n"; } #-------------------- outputting sub zone_output () { my ($o,$m); $o= "zone \"$zone\" {\n"; if ($$cfg{'s'} =~ m/p/) { $o.= " type master;\n"; } else { $o.= " type slave;\n". " masters {\n"; foreach $m (@{ $$cfg{'servers'} }) { $o.= " $m;\n"; } $o.= " };\n"; } $o.= " file \"$$cfg{'file'}\";\n"; $o.= "};\n"; return $o; } sub output_files () { my ($fn,$ofn,$mfn,$l,$dir, $maxmode,$h,@to_install); foreach $ofn (keys %output_contents) { $fn= $ofn; $mfn= "output file $fn"; for (;;) { if (!lstat $fn) { $! == &ENOENT or die "$quis: stat $mfn:\n $!\n"; $maxmode= 0666; last; } elsif (-f _) { $maxmode= (stat _)[2]; last; } elsif (-l _) { defined($l= readlink $fn) or die "$quis: readlink $mfn:\n $!\n"; $dir= $fn =~ m,^.*/, ? $& : './'; $fn= "$dir$l" unless $l =~ m,^/,; $mfn= "output file $fn (symlink target of $ofn)"; } else { die "$quis: output file $mfn exists but is not a file". " (or symlink to one)"; } } unlink "$fn.new" or $! == &ENOENT or die "$quis: cannot clear out old .new version of $mfn:\n $!"; $h= new IO::File "$fn.new",'w',$maxmode or die("$quis: create .new version of $mfn:\n $!"); print $h "# generated by $quis, do not edit\n", $output_contents{$ofn} or die "$quis: write data to .new version of $mfn:\n $!"; $h->close or die "$quis: close .new version of $mfn:\n $!"; push @to_install, $fn,$mfn; } while (($fn,$mfn, @to_install) = @to_install) { rename "$fn.new",$fn or die "$quis: install new version of $mfn:\n $!"; } } #-------------------- general utilities sub debug_dump ($) { my ($vn); return unless $debug>1; local $Data::Dumper::Terse=1; foreach $vn (split /\s+/, $_[0]) { print "$vn := ", eval "Dumper(\\$vn)"; } } sub debug_trace ($) { return unless $debug; print "D $_[0]\n"; } sub has_suffix_of ($$) { my ($whole,$suffix) = @_; debug_trace("has_suffix_of $whole $suffix"); return 0 if length $whole < length $suffix; return 0 if substr($whole, length($whole) - length($suffix)) ne $suffix; debug_trace("has_suffix_of $whole $suffix YES"); return 1; } sub lookup ($$$$) { my ($domain,$type,$okrcodes,$w) = @_; my ($c,$h,@result); debug_trace("lookup ==> (->$okrcodes) $domain $type"); $h= new IO::Handle; defined($c= open $h, "-|") or die "$quis: fork adnshost:\n $!\n"; if (!$c) { exec 'adnshost','-Fi','+Do','+Dt','+Dc','-Cf',"-t$type", '-',"$domain"; die "$quis: exec adnshost:\n $!\n"; } @result= $h->getlines(); $h->error and die "$quis: read from adnshost:\n $!\n"; chomp @result; $!=0; $h->close; die "$quis: lookup -t$type $domain $okrcodes ($w) failed $? $! @result\n" if $! or $?&255 or $?>1536 or index($okrcodes,$?>>8)<0; debug_trace("lookup <== $? @result"); return ($?,@result); } sub dig (&$$$) { my ($eachrr, $qowner,$qtype,$qaddr) = @_; # also pseudo-rr with type `flags:' my ($h,$inmid,$irdata,$c,$digwhy); local ($_); debug_trace("dig ==> \@$qaddr $qowner $qtype"); $h= new IO::Handle; defined($c= open $h, "-|") or die "$quis: fork dig:\n $!\n"; if (!$c) { open STDERR, ">&STDOUT" or die $!; exec ('dig', '+nodef','+nosea','+norecurse', "\@$qaddr",'-t',$qtype,$qowner); die "$quis: exec dig:\n $!\n"; } $inmid=''; for (;;) { if (!defined($_= $h->getline())) { $h->error() and die "$quis: read from dig:\n $!\n"; last; } chomp; if (length $inmid) { s/^\s+/ / or die "$inmid // $_ ?"; s/\;.*$//; $_= $inmid.$_; $inmid=''; s/$/ \(/ unless s/\s*\)\s*$//; } if (s/\s*\(\s*$//) { $inmid= $_; next; } $digwhy= "dig $qowner $qtype $qaddr \`$_'"; if (m/^\;\; flags\:( [-0-9a-z ]+)\;/) { $dig_owner=''; $dig_type='flags:'; $dig_rdata= "$1 "; debug_trace("dig f: $dig_rdata"); &$eachrr; } elsif (m/^\;/) { } elsif (!m/\S/) { } elsif (m/^([-.0-9a-z]+)\s+\d\w+\s+in\s+([a-z]+)\s+(\S.*)/i) { $dig_owner=domain_canon($1,$digwhy); $dig_type=lc $2; $irdata=$3; if ($dig_type eq 'a') { $irdata =~ m/^[.0-9]+$/ or die "$irdata ?"; $dig_rdata= $&; } elsif ($dig_type eq 'ns') { $irdata =~ m/^[-.0-9a-z]+$/i or die "bad nameserver $irdata ?"; $dig_rdata= domain_canon($irdata,$digwhy); } elsif ($dig_type eq 'soa') { $irdata =~ m/^([-.0-9a-z]+)\s+.*\s+(\d+)(?:\s+\d\w+){4}$/i or die "bad SOA $irdata ?"; $dig_rdata= $2.' '.domain_canon($1,$digwhy); } else { debug_trace("ignoring uknown RR type $dig_type"); next; } debug_trace("dig $dig_owner $dig_type $dig_rdata"); &$eachrr; } else { debug_trace("ignoring unknown dig output $_"); } } $h->close; debug_trace("dig <== gave $?"); } sub domain_canon ($$) { my ($i,$w) = @_; $i =~ s/(.)\.$/$1/; return '.' if $i eq '.'; die "domain $i ($w) ?" unless $i =~ m/^[0-9a-z]/i; return lc $i; } chiark-utils-4.4.2build1/scripts/summarise-mailbox-preserving-privacy0000775000000000000000000001030612423200672023017 0ustar #!/usr/bin/perl # usage: # summarise-mailbox-preserving-privacy \ # [] [--] # our options: # -S default: "summary of messages on " # -F default: $HOME/.summarise-mailbox/last # -f passed to from(1) must use -F if it contains / or | # -s passed to from(1) must use -F if it contains / or | # -q throw away stderr and always exit 0 # -- end of our options # Copyright 2006 Ian Jackson # # This script and its documentation (if any) are free software; you # can redistribute it and/or modify them under the terms of the GNU # General Public License as published by the Free Software Foundation; # either version 3, or (at your option) any later version. # # chiark-named-conf and its manpage are distributed in the hope that # it will be useful, but WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. use strict (qw(refs)); use POSIX; use IO::Handle; use Fcntl (qw(:flock)); @from_options=(); $sendmail= '/usr/sbin/sendmail -odi -oee -oi -t'; umask 077; while (@ARGV && $ARGV[0] =~ m/\-/) { $_= shift; if (m/^\-[fs]/) { push @from_options, $_; } elsif (s/^\-S//) { $subject= $_; } elsif (s/^\-F//) { $statefile= $_; } elsif (s/^\-q$//) { $quiet= 1; open STDERR, ">/dev/null"; eval('END { $?=0; }'); } elsif (m/^\-\-$/) { last; } else { die "$0: unknown option \`$_'\n"; } } die unless @ARGV==1; $emailto= shift @ARGV; unless (defined $subject) { my ($our_hostname); open M, "/etc/mailname" or die $!; defined($our_hostname= ) or die $!; chomp($our_hostname); close M; $subject= "summary of messages on $our_hostname"; } unless (defined $statefile) { my ($dir); die "$0: -F needed with that -f or -s\n" if grep m:[|/]:, @from_options; die "$0: no HOME in environment\n" unless defined $ENV{'HOME'}; $dir= $ENV{'HOME'}.'/.summarise-mailbox'; mkdir $dir, 02700 or $!==&EEXIST or die "$dir: $!"; $statefile= $dir.'/last'. join('|',@from_options); } $statefile= "./$statefile" unless $statefile =~ m,^/,; $lockfile= $statefile.'.lock'; $errfile= $statefile.'.err'; open L, "+> $lockfile" or die "$lockfile: $!"; flock L, LOCK_EX or die "$lockfile: $!"; if ($quiet) { open STDERR, "> $errfile"; } sub parse($) { my ($incr, $lasttime) = @_; $lasttime= ''; while (defined($_= )) { print N or die "$statefile.new: $!" if $incr>0; $have{$_} += $incr; $have += $incr; m/^From .* (\w+ \w+ \d+ [0-9:]+ \d+)$/ or die "$_ ?"; $lasttime= $1; } die $! if F->error; return $lasttime; } if (open F, "< $statefile\0") { $old_lasttime= parse(-1); close F or die $!; } elsif ($! != &ENOENT) { die "$statefile $!"; } open N, "> $statefile.new" or die "$statefile.new: $!"; $child= open F, "-|"; defined $child or die $!; if (!$child) { exec "from",@from_options; die "$!"; } $did_have= $have; $new_lasttime= parse(+1); $?=0; close F or die "$? $!"; close N or die "$statefile.new: $!"; if ($new_lasttime ne $old_lasttime and $new_lasttime ne '') { push @reasons, "Timestamp of last message in mailbox changed."; } map { $total_more+=$_ if $_>0 } values %have; if ($total_more) { push @reasons, "$total_more message(s)". " which were not previously present."; } elsif ($have>0) { push @reasons, "More messages than previously reported."; } exit 0 unless @reasons; $new_have= $have - $did_have; $msg= < # # This script and its documentation (if any) are free software; you # can redistribute it and/or modify them under the terms of the GNU # General Public License as published by the Free Software Foundation; # either version 3, or (at your option) any later version. # # chiark-named-conf and its manpage are distributed in the hope that # it will be useful, but WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. if {[llength $argv] != 1} { error "need serial port arg" } set port [lindex $argv 0] set count 0 set lit 0 ;# 1 means literal (ASCII) entry mode set echo 1 proc p {s} { puts -nonewline $s } proc tput {args} { global tput if {[catch { set s $tput($args) }]} { set s [eval exec tput $args] set tput($args) $s } p $s } proc csr_pos {lit bytenum} { set x [expr { (!$lit ? (3*$bytenum) : 53+$bytenum) + ($bytenum>>2) - (2-$lit)*($bytenum==16) + 5 }] tput hpa $x } proc csr_this {} { global lit x; csr_pos $lit $x } proc csr_other {} { global lit x; csr_pos [expr {!$lit}] $x } proc csrs_erase {} { csr_this; p " "; csr_other; p " " } proc csr_this_show {} { global h1 csr_this; if {[info exists h1]} { p $h1; p "\b" } } proc csrs_show {} { csr_other; p _ csr_this_show } proc echop {} { global echo return [expr {$echo ? "|" : "'"}] } proc newline {} { global x echo count if {[info exists x]} { csrs_erase; p "\r\n" } set x 0 p [format "%3x%s%*s|%*s|" $count [echop] 52 "" 21 ""] csrs_show } proc p_ch_spaces {} { global x lit if {$x==15} return if {$lit} { p " " } if {($x & 3) != 3} return p " " } proc p_rmso {smso} { if {[string length $smso]} { tput sgr0 } } proc ch {d smso} { global lit x count if {$x == 16} newline if {[string length $smso]} { tput $smso } set h [format %02x [expr {$d & 0xff}]] set c [format %c [expr {($d > 33 && $d < 127 && $d != 95) ? $d : 46}]] if {$lit} { p $c; csr_other; p $h p_ch_spaces p_rmso $smso p _ } else { p $h; csr_other; p $c p_ch_spaces p_rmso $smso p _ } incr x set count [expr {($count+1) & 0xfff}] csr_this_show } proc onreadp {} { global p while 1 { set c [read $p 1] binary scan $c c* d if {![llength $d]} { if {[eof $p]} { error "eof on device" } return } ch $d {} } } proc transmit {d} { global p echo puts -nonewline $p [format %c $d] if {$echo} { ch $d bold } } proc k_echo {} { global echo set echo [expr {!$echo}] tput hpa 3 p [echop] csr_this } proc k_newline {} { global count x if {$x} { newline } else { set count 0 p "\r" p [format %3x $count] csr_this } } proc k_switch {} { global lit h1 csrs_erase catch { unset h1 } set lit [expr {!$lit}] csrs_show } proc k_stop {} { restore exit 0 } proc k_suspend {} { restore exec kill -TSTP [info pid] setup } proc k_noparthex {} { global h1 csrs_erase catch { unset h1 } csrs_show } proc k_hexdigit {c} { global h1 echo if {![info exists h1]} { set h1 $c; p $c; p "\b"; return } set d [expr 0x${h1}${c}] unset h1 transmit $d if {!$echo} { p " \b" } } proc onreadk {} { global lit while 1 { set c [read stdin 1] binary scan $c c* d if {![llength $d]} { if {[eof stdin]} { error "eof on stdin" } return } switch -exact $d { 9 { k_switch; continue } 3 - 4 { k_stop; continue } 26 { k_suspend; continue } } if {$lit} { transmit $d; continue } switch -exact $d { 13 { k_newline; continue } 32 { transmit 0; continue } 39 { k_echo; continue } 127 { k_noparthex; continue } } if {$d >= 48 && $d <= 57} { k_hexdigit $c; continue } set kl [expr {$d | 32}] if {$d >= 97 && $d <= 102} { k_hexdigit $c; continue } p "\a" } } proc try {script} { if {[catch { uplevel 1 $script } emsg]} { catch { puts stderr "(warning: $emsg)" } } } proc tryv {variable script} { upvar #0 $variable var if {![info exists var]} return uplevel 1 " global $variable $script " unset var } proc restore {} { tryv x { puts "\r\n" } try { fconfigure stdin -blocking true } try { fconfigure stdout -blocking true } tryv term_stty { exec stty $term_stty } tryv p { close $p } } proc setup {} { global term_stty port p set term_stty [exec stty -g] set p [open $port {RDWR NONBLOCK} 0] exec stty min 1 time 0 -istrip -ocrnl -onlcr -onocr -opost \ -ctlecho -echo -echoe -echok -echonl -iexten -isig \ -icanon -icrnl exec stty -F $port min 1 time 0 -istrip -ocrnl -onlcr -onocr -opost \ -ctlecho -echo -echoe -echok -echonl -iexten -isig \ -icanon -icrnl \ 9600 clocal cread -crtscts -hup -parenb cs8 -cstopb \ -ixoff bs0 cr0 ff0 nl0 -ofill -olcuc fconfigure $p -blocking false -buffering none -encoding binary \ -translation binary fconfigure stdin -blocking false -buffering none -translation binary fconfigure stdout -blocking false -buffering none -translation binary newline fileevent stdin readable onreadk fileevent $p readable onreadp } proc bgerror {m} { try { restore global errorInfo errorCode puts stderr "$m\n$errorCode\n$errorInfo" } exit 127 } if {[catch setup emsg]} { restore error $emsg $errorInfo $errorCode } vwait quit chiark-utils-4.4.2build1/scripts/named-conf.80000664000000000000000000005337212423200672015710 0ustar .\" Hey, Emacs! This is an -*- nroff -*- source file. .TH CHIARK\-NAMED\-CONF 8 "12th January 2002" "Greenend" "chiark utilities" .SH NAME chiark\-named\-conf \- check and generate nameserver configuration .SH SYNOPSIS .BR chiark\-named\-conf " [\fIoptions\fP] " \-n | \-y | \-f .br \fBchiark\-named\-conf\fP [\fIoptions\fP] \fIzone ...\fP .SH DESCRIPTION .B chiark\-named\-conf is a tool for managing nameserver configurations and checking for suspected DNS problems. Its main functions are to check that delegations are appropriate and working, that secondary zones are slaved from the right places, and to generate a configuration for .BR BIND , from its own input file. By default, for each zone, in addition to any warnings, the output lists the zone's configuration type. If the zone is checked, the serial number at each of the nameservers is shown, with any unpublished primary having .B * after the serial number. .SH OPTIONS .SS MODE OPTIONS If one of the options .BR -n ", " -y ", or " -f is supplied then chiark-named-conf will read its main configuration file for the list of relevant zones. It will then check the configuration and delegation for each zone and/or generate and install a new configuration file for the nameserver: .TP .BR \-y | \-\-yes Generate and install new nameserver config, as well as checking configuration, for all listed zones. .TP .BR \-n | \-\-no Check configuration, for all listed zones, but do not generate new nameserver config. .TP .BR \-f | \-\-force Generate and install new nameserver config, without doing any configuration cross-checking. (Syntax errors in our input configuration will still abort this operation.) .TP .BR \-\-nothing Do nothing: do no checks, and don't write a new config. This can be used to get a list of the zones being processed. .TP .BR \-\-mail\-first " | " \-\-mail\-middle " | " \-\-mail\-final Send mails to zone SOA MNAMEs reporting zones with problems. You must call chiark\-named\-conf at least twice, once with \-\-mail\-first, and later with \-\-mail\-final, and preferably with one or more calls to \-\-mail\-middle in between. All three options carry out a check and store the results; \-\-mail\-final also sends a mail to the zone SOA MNAME or local administrator, if too many of the calls had errors or warnings (calls before the most recent \-\-mail\-first being ignored). .TP .B \-mail\-final\-test just like \-\-mail\-final except that it always sends mail to the local server admin and never to remote zone contacts, adding .B (testing!) to the start of the To: field. .LP Alternatively, one or more zone names may be supplied as arguments, in which case their delegations will be checked, and compared with the data for that zone in the main configuration (if any). In this case no new configuration file for the nameserver will be made. .SS ADDITIONAL OPTIONS .TP .BR \-A | \-\-all Checks even zones known to be broken. Ie, ignores the .B ? zone style modifier in the configuration. .TP .BR \-C | \-\-config " \fIconfig\-file\fP" Use .I config\-file instead of .BR /etc/bind/chiark-conf-gen.zones . Also changes the default directory. .TP .BR \-D Enables debugging. Useful for debugging chiark\-named\-conf, but probably not useful for debugging your DNS configuration. Repeat to increase the debugging level. (Maximum is .BR -DD .) .TP .BR \-g | \-\-glueless Do not warn about glueless referrals (strictly, makes the zone style modifier .B ~ the default). Not recommended - see the section GLUELESSNESS, below. .TP .BR \-l | \-\-local Only checks for mistakes which are the responsibility of the local administrator (to fix or get fixed). This means that for published and stealth zones we only check that we're slaving from the right place and that any names and addresses for ourself are right. For primary zones all checks are still done. It is a mistake to specify .B \-l with foreign zones (zones supplied explictly on the command line but not relevant to the local server); doing so produces a warning. .TP .BI \-m group !*$@~? Overrides a .B modifiers directive in the configuration file. The modifiers specified in the directive are completely replaced by those specified in this command line option. (Note that modifiers specified in per-zone directives still override these per-group settings.) If more than one .B modifiers directive specifies the same group, they are all affected. .B modifiers directives which don't specify a group cannot be affected. It is an error if the group does not appear in the config file. See ZONE STYLE MODIFIERS, below. .PP The special group .B foreign is used for zones which don't appear in the configuration file. .TP .BR \-q | \-\-quiet Suppress the usual report of the list of nameservers for each zone and the serial number from each. When specified twice, do not print any information except warnings. .TP .BR \-r | \-\-repeat When a problem is detected, warn for all sources of the same imperfect data, rather than only the first we come across .TP .BR \-v | \-\-verbose Print additional information about what is being checked, as we go along. .SH USAGE The file .B /etc/bind/chiark-conf-gen.zones (or other file specified with the .B \-C option) contains a sequence of directives, one per line. Blank lines are permitted. Leading and trailing whitespace on each line is ignored. Comments are lines starting with .BR # . Ending a line with a .BR \\ joins it to the next line, so that long directives can be split across several physical lines. .SS GENERAL DIRECTIVES These directives specify general configuration details. They should appear before directives specifying zones, as each will affect only later zone directives. Foreign zones (zones explicitly specified on the command line but not mentioned in the configuration) use the configuration settings prevailing at the end of the config file. .TP \fBadmin\fP \fIemail\-address\fP Specifies the email address of the local administrator. This is used in the From: line of mails sent out, and will also receive copies of the reports. There is no default. .TP \fBdefault\-dir\fP \fIdirectory\fP Makes .I directory be the default directory (which affects the interpretation of relative filenames). The default is the directory containing the main configuration file, ie .BR /etc/bind if no .B -C option is specified. .TP \fBforbid\-addr\fP [\fIip-address ...\fP] Specifies the list of addresses that are forbidden as any nameserver for any zone. The default is no such addresses. .TP \fBforbid\-addr\fP [\fIip-address ...\fP] Specifies the list of addresses that are forbidden as a nameserver for a zone for which we are the primary - ie, the list of our old or to-be-obsoleted slaves. The default is no such addresses. .TP \fBserverless\-glueless\fP \fIdomain ...\fP Specifies a list of domains under which we do not expect to find any nameservers without glue; for these zones it is OK to find glueless referrals. Each domain listed names a complete subtree of the DNS, starting at the named point. The default is .BR "in\-addr.arpa ip6.arpa ip6.int" . To avoid indefinitely long or even circularly glueless referrals (which delay or prevent lookups) it is necessary for all sites to effectively implement similar conventions; currently the author believes that only the reverse lookup namespaces are conventionally devoid of (glueless) nameservers, and therefore fine to provide glueless referrals for. See GLUELESSNESS below. .TP \fBallow-\-indirect\-glue\fP \fInameserver-superdomain ...\fP Specifies a list of domains under which we expect to find glueless nameservers, with up to one layer of indirection. For nameservers under these domains it is OK to to find glueless referrals, but only when listed as a nameserver for a zone which is not itself a subdomain of an \fBallow-indirect-glue\fR \fInameserver-superdomain\fR. This supports to common configuration style where DNS operator(s) set up all of their nameservers with names within a small subsection of the DNS (the portions under \fInameserver-superdomain\fRs), and provide glueless referrals naming these nameservers for all other zones. This provides at most one level of missing glue. Note that if the DNS administrators collectively able to influence the service for some zone (including the admins for its superzones, the zones containing its nameservers, and their superzones and so forth) are not in sufficiently close communication do not all agree on the proper set of \fInameserver-superdomain\fR then they might still set up circular glue and \fBchiark-named-conf\fR would not necessarily be able to detect this even if it was run on every relevant nameserver. .TP \fBmail\-state\-dir\fP \fIdirectory\fP Uses .I directory for storing information about recent failures for mailing to zone admins. See \-\-mail\-first et al. Old files in here should be cleaned up periodically out of cron. There is no default. .TP \fBmail\-max\-warnfreq\fP \fIpercentage\fP When \-\-mail\-final is used, a mail will be sent to all zones which had warnings or errors more than .IR percentage % of the times \-\-mail\-* was used (since the last \-\-mail\-first). The default is 50%. .TP .BR modifiers " " !*$@~? "] [\fIgroup\fP]" Applies the specified zone style modifiers (see below) to subsequently declared zones (until the next .B modifiers directive), as if the modifiers specified were written out for each zone. You must specify at least one character for the modifiers; if you want to reset everything to the default, just say .BR ! . If style modifiers specified in the zone directive conflict with the .B modifiers directive, those specified in the zone directive take effect. .I group may contain alphanumerics and underscores, and is used for the .B -m command-line option. .TP \fBself\-addr\fP \fIip-address ...\fP Specifies the list of addresses that this server may be known by in A records. There is no default. .TP \fBoutput\fP \fIformat\fP \fIfilename\fP [\fIformat\fP \fIfilename ...\fP] Arranges that each .I filename will be overwritten when .BR -y " or " -f are used; its new contents will be configuration directives for the zones which follow for the nameserver in question. Currently the only .I format supported is .B bind8 which indicates new-style BIND 8. If no zones follow, then each file will still be overwritten, by an effectively empty file. Default: if there is no .B output directive in the configuration then the default is to use .BR bind8 " " chiark-conf-gen.bind8 ; otherwise it is an error for there to be any zones in the configuration before the first .B output directive. .TP \fBself\-ns\fP \fIfqdn ...\fP Specifies the list of names that this server may be known by in NS records. There is no default. Any trailing * is replaced by the name of the zone being checked, so for example .B self\-ns isp.ns.* before the zone example.com would mean to expect us to be listed as isp.ns.example.com in the NS RRset. .TP \fBself\-soa\fP \fIfqdn ...\fP Specifies the list of names that this server may be known by in the ORIGIN field of SOA records. There is no default. Any trailing * is replaced by the name of the zone, as for .BR self\-ns . .TP .BI self " fqdn ..." Equivalent to both .B self\-ns " and " self\-soa with the same set of names. .TP \fBslave\-dir\fP \fIdirectory\fP [[\fIprefix\fP] \fIsuffix\fP] Specifies the directory in which slave (published and stealth) zonefiles should be placed. The default .I directory is .BR /var/cache/bind/chiark-slave . The default .IR suffix " and " prefix are empty; they also will be reset to these defaults by a .B slave\-dir directive which does not specify them. .SS ZONE DIRECTIVES These directives specify one or more zones. .TP .BR primary [ !*$@~? "] \fIzone filename\fP" Specifies that this server is supposed to be the primary nameserver for .I zone and that the zone data is to be found in .IR filename . .TP .BR primary\-dir [ !*$@~? "] \fIdirectory\fP[" / "\fIprefix\fP] [\fIsuffix\fP[" / \fIsubfile\fP]] Search .I directory for files whose names start with .I prefix and end with .IR suffix . Each such file is taken to represent a zone file for which this server is supposed to be the primary; the part of the filename between .IR prefix " and " suffix is the name of the zone. If .BI / subfile is specified, then instead of looking for files, we search for directories containing .IR subfile ; directories which do not contain the subfile are simply skipped. If .IR directory [\fB/\fP prefix ] exists as specified and is a directory then it is interpreted as .I directory with an empty prefix; otherwise the final path component is assumed to be the prefix. If no .IB suffix / subfile is specified then the default is .BR _db . .TP .BR published [ !*$@~? "] \fIzone origin\-addr\fP" Specifies that this server is supposed to be a published slave nameserver for the zone in question. .TP .BR stealth [ !*$@~? "] \fIzone server\-addr ...\fP" Specifies that this server is supposed to be an unpublished secondary (aka stealth secondary) for the zone in question. .SS ZONE STYLE MODIFIERS Each of the zone directives may optionally be followed by one or more of the following characters (each at most once): .TP .B ! Reverses the meaning of all style modifiers after the .BR ! . Only one .BR ! must appear in the modifier list. In this list, other modifiers which default to `enabled' are described by describing the effect of their inverse - see the description for .B !@ below. .TP .B * Indicates that the zone is unofficial, ie that it is not delegated as part of the global Internet DNS and that no attempt should be made to find the superzone and check delegations. Note that unofficial, local zones should be created with caution. They should be in parts of the namespace which are reserved for private use, or belong to the actual zone maintainer. .TP .B $ Indicates that any mails should be sent about the zone to the nameserver admin rather than to the zone SOA MNAME. This is the default unless we are supposedly a published server for the zone. .TP .B !@ Indicates that no mails should be sent about the zone to anyone. .TP .B ~ Indicates that the zone's delegation is known to be glueless, and that lack of glue should not be flagged. Not recommended - see the section GLUELESSNESS, below. .TP .B ? Indicates that the zone is known to be broken and no checks should be carried out on it, unless the .B \-A option is specified. .SS OTHER DIRECTIVES .TP \fBinclude\fP \fIfile\fP Reads .I file as if it were included here. .TP \fBend\fP Ends processing of this file; any data beyond this point is ignored. .SH CHECKS chiark\-named\-conf makes the following checks: Delegations: Each delegation from a server for the superzone should contain the same set of nameservers. None of the delegations should lack glue. The glue addresses should be the same in each delegation, and agree with the local default nameserver. Delegated servers: Each server mentioned in the delegation should have the same SOA record (and obviously, should be authoritative). All published nameservers - including delegated servers and servers named in the zone's nameserver set: All nameservers for the zone should supply the same list of nameservers for the zone, and none of this authority information should be glueless. All the glue should always give the same addresses. Origin server's data: The set of nameservers in the origin server's version of the zone should be a superset of those in the delegations. Our zone configuration: For primary zones, the SOA origin should be one of the names specified with .BR self\-soa " (or " self ). For published zones, the address should be that of the SOA origin. For stealth zones, the address should be that of the SOA origin or one of the published nameservers. .SH GLUELESSNESS Glue is the name given for the addresses of nameservers which are often supplied in a referral. In fact, it turns out that it is important for the reliability and performance of the DNS that referrals, in general, always come with glue. Firstly, glueless referrals usually cause extra delays looking up names. BIND 8, when it receives a completely glueless referral and does not have the nameservers' addresses in its cache, will start queries for the nameserver addresses; but it will throw the original client's question away, so that when these queries arrive, it won't restart the query from where it left off. This means that the client won't get its answer until it retries, typically at least 1 second later - longer if you have more than one nameserver listed. Worse, if the nameserver to which the glueless referral points is itself under another glueless referral, another retry will be required. Even for better resolvers than BIND 8, long chains of glueless referrals can cause performance and reliability problems, turning a simple two or three query exchange into something needing more than a dozen queries. Even worse, one might accidentally create a set of circularly glueless referrals such as .br .B example.com NS ns0.example.net.uk .br .B example.com NS ns1.example.net.uk .br .B example.net.uk NS ns0.example.com .br .B example.net.uk NS ns1.example.com .br Here it is impossible to look up anything in either example.com or example.net.uk. There are, as far as the author is aware, no generally agreed conventions or standards for avoiding unreasonably long glueless chains, or even circular glueless situations. The only way to guarantee that things will work properly is therefore to always supply glue. However, the situation is further complicated by the fact that many implementations (including BIND 8.2.3, and many registry systems), will refuse to accept glue RRs for delegations in a parent zonefile unless they are under the parent's zone apex. In these cases it can be necessary to create names for the child's nameservers which are underneath the child's apex, so that the glue records are both in the parent's bailiwick and obviously necessary. In the past, the `shared registry system' managing .com, .net and .org did not allow a single IPv4 address to be used for more than one nameserver name. However, at the time of writing (October 2002) this problem seems to have been fixed, and the workaround I previously recommended (creating a single name for your nameserver somewhere in .com, .net or .org, and using that for all the delegations from .com, .net and .org) should now be avoided. Finally, a note about `reverse' zones, such as those in in-addr.arpa: It does not seem at all common practice to create nameservers in in-addr.arpa zones (ie, no NS RRs seem to point into in-addr.arpa, even those for in-addr.arpa zones). Current practice seems to be to always use nameservers for in-addr.arpa which are in the normal, forward, address space. If everyone sticks to the rule of always publishing nameservers names in the `main' part of the namespace, and publishing glue for them, there is no chance of anything longer than a 1-step glueless chain might occur for a in-addr.arpa zone. It is probably best to maintain this as the status quo, despite the performance problem this implies for BIND 8 caches. This is what the serverless\-glueless directive is for. Dan Bernstein has some information and examples about this at .UR http://cr.yp.to/djbdns/notes.html#gluelessness http://cr.yp.to/djbdns/notes.html#gluelessness .UE but be warned that it is rather opinionated. .SS GLUELESSNESS SUMMARY I recommend that every nameserver should have its own name in every forward zone that it serves. For example: .br .B zone.example.com NS servus.ns.example.com .br .B servus.ns.example.com A 127.0.0.2 .br .B 2.0.0.127.in-addr.arpa PTR servus.example.net .br .B servus.example.net A 127.0.0.2 .LP Domain names in .B in-addr.arpa should not be used in the right hand side of NS records. .SH SECURITY chiark\-named\-conf is supposed to be resistant to malicious data in the DNS. It is not resistant to malicious data in its own options, configuration file or environment. It is not supposed to read its stdin, but is not guaranteed to be safe if stdin is dangerous. .LP Killing chiark-named-conf suddenly should be safe, even with .BR -y " or " -f (though of course it may not complete its task if killed), provided that only one invocation is made at once. .LP Slow remote nameservers will cause chiark-named-conf to take excessively long. .SH EXIT STATUS .TP .B 0 All went well and there were no warnings. .TP any other There were warnings or errors. .SH FILES .TP .B /etc/bind/chiark-conf-gen.zones Default input configuration file. (Override with .BR -C .) .TP .B /etc/bind Default directory. (Override with .BR -C " or " default\-dir .) .TP .IB dir /chiark-conf-gen.bind8 Default output file. .TP .B /var/cache/bind/chiark-slave Default location for slave zones. .SH ENVIRONMENT .LP Setting variables used by .BR dig (1) and .BR adnshost (1) will affect the operation of chiark\-named\-conf. Avoid messing with these if possible. .LP .B PATH is used to find subprograms such as .BR dig " and " adnshost . .SH BUGS The determination of the parent zone for each zone to be checked, and its nameservers, is done simply using the system default nameserver. The processing of output from .B dig is not very reliable or robust, but this is mainly the fault of dig. This can lead to somewhat unhelpful error reporting for lookup failures. .SH AUTHOR .B chiark\-named\-conf and this manpage were written by Ian Jackson . They are Copyright 2002 Ian Jackson. chiark\-named\-conf and this manpage are free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, consult the Free Software Foundation's website at www.fsf.org, or the GNU Project website at www.gnu.org. chiark-utils-4.4.2build1/scripts/remountresizereiserfs0000775000000000000000000000207412423200672020205 0ustar #!/bin/bash # usage: # remountresizereiserfs /mountpoint set -e fail () { echo >&2 "$*"; exit 1; } case "$#.$1" in 1.[^-]*);; *) fail 'bad usage';; esac mp=$1 df=`df -P $mp` dfl2=`printf "%s" "$df" | sed 1d` case "$dfl2" in /dev/*" "[0-9]*" "[0-9]*" "[0-9]*" "[0-9]*"% "/*) dev=${dfl2%% *} mp2=${dfl2##* } if [ "x$mp2" != "x$mp" ]; then fail "mountpoint is $mp2 not $mp"; fi ;; *) fail "could not parse df output" ;; esac dm=/dev/mapper case "$dev" in $dm/*/*) fail "too many path segments in mapper device \`$dev'" ;; $dm/*) lv=${dev#$dm/} lv=${lv//--//} case "$lv" in *-*) ;; *) fail "no single hyphen in mapper device \`$lv'";; esac vg=${lv%%-*} lv=${lv#*-} vg=${vg//\//-} lv=${lv//\//-} devu=/dev/$vg/$lv ;; *) devu=$dev esac lvi=$(lvdisplay -c $devu) vg=${lvi#*:} vg=${vg%%:*} vgsz_kb=${lvi#*:*:*:*:*:*:} vgsz_kb=${vgsz_kb%%:*} dbrfs=$(debugreiserfs $dev) blksz_by=$(printf "%s" "$dbrfs" | egrep '^Blocksize: ' || fail "blocksize?") blksz_by=${blksz_by#*: } vgsz_blk=$(dc -e "$vgsz_kb 1024* $blksz_by /p") echo mount -o remount,resize=$vgsz_blk $mp chiark-utils-4.4.2build1/scripts/ChiarkNNTP.pm0000664000000000000000000001017712445561415016054 0ustar #!/usr/bin/perl # Originally by Simon Tatham # Modified by Richard Kettlewell, Colin Watson, Ian Jackson # # Copyright -2011 Simon Tatham # Copyright 2011 Richard Kettlewell # Copyright 2011 Colin Watson # Copyright 2011 Ian Jackson # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # SOFTWARE IN THE PUBLIC INTEREST, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. use strict qw(subs); use warnings; require 5.002; use Socket; use FileHandle; BEGIN { use Exporter (); our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); # set the version for version checking $VERSION = 1.00; @ISA = qw(Exporter); @EXPORT = qw(cnntp_connect); %EXPORT_TAGS = ( ); # eg: TAG => [ qw!name1 name2! ], @EXPORT_OK = qw(); } our @EXPORT_OK; sub cnntp_connect ($) { my ($verbose) = @_; my $ns=$ENV{'NNTPSERVER'}; if (!defined $ns or !length $ns) { $ns = `cat /etc/nntpserver`; chomp($ns); } my $port = (getservbyname("nntp", "tcp"))[2]; $ns = inet_aton($ns); my $proto = getprotobyname("tcp"); my $paddr = sockaddr_in($port, $ns); my $sock = new IO::Handle; socket($sock,PF_INET,SOCK_STREAM,$proto) or die "socket: $!"; connect($sock,$paddr) or die "connect: $!"; $sock->autoflush(1); return bless { S => $sock, V => $verbose }; } sub banner_reader ($) { my ($c) = @_; my ($code,$l) = $c->getline(); $code =~ /^2\d\d/ or die "no initial greeting from server\n"; $c->docmd("MODE READER"); } sub disconnect ($) { my ($c) = @_; close $c->{S}; } sub putline ($$) { my ($c, $line) = @_; my $s = $c->{S}; my $v = $c->{V}; print $v ">>> $line\n" if $v; print $s "$line\r\n"; } sub getline_raw ($) { my ($c) = @_; my $s = $c->{S}; my $l = <$s>; return $l; } sub getline ($) { my ($c) = @_; my $v = $c->{V}; my $l = $c->getline_raw(); $l =~ s/[\r\n]*$//s; my $code = substr($l,0,3); print $v "<<< $l\n" if $v; return ($code,$l); } sub docmd ($$;$) { my ($c,$cmd,$nocheck) = @_; my ($code,$l); for my $n (0,1) { $c->putline($cmd); ($code,$l) = $c->getline(); if ($code eq "480") { $c->auth(); } else { last; } } if (!$nocheck) { $code =~ /^2\d\d/ or die "failed on `$cmd':\n$l\n"; } return ($code,$l); } sub auth ($) { my ($c) = @_; # Authentication. return if $c->{Authed}++; my $auth = $ENV{"NNTPAUTH"}; if (defined $auth) { $c->putline("AUTHINFO GENERIC $auth"); pipe AUTHSTDIN, TOAUTH or die "unable to create pipes"; pipe FROMAUTH, AUTHSTDOUT or die "unable to create pipes"; flush STDOUT; my $pid = fork; if (!defined $pid) { die "unable to fork for authentication helper"; } elsif ($pid == 0) { # we are child $c->{V} = undef if $c->{V} eq 'STDOUT'; $ENV{"NNTP_AUTH_FDS"} = "0.1"; open STDIN, "<&AUTHSTDIN"; open STDOUT, ">&AUTHSTDOUT"; close $c->{S}; exec $auth; die $!; } # we are parent close AUTHSTDIN; close AUTHSTDOUT; autoflush TOAUTH 1; my ($code,$l) = $c->getline(); print TOAUTH "$l\n"; while () { s/[\r\n]*$//s; $c->putline($_); ($code,$l) = $c->getline(); print TOAUTH "$l\n"; } die "failed authentication\n" unless $? == 0; } } 1; chiark-utils-4.4.2build1/scripts/ngspice2genspic0000775000000000000000000000572112423200672016614 0ustar #!/usr/bin/perl # ngspice2genspic - Copyright 2004 Ian Jackson - see below # # Reads the output from ngspice and outputs a `genspic' file # for use with genspic2gnuplot. Takes no arguments or options. # # Limitations # # Only frequency (.AC) and transient plots have been tested. # # Displaying voltages and currents on the same .TRAN graph won't work # well because they currently have to have the same Y scale. # # This whole scheme is very clumsy. There should be a driver program # with command-line option parsing. # This program and its documentation are free software; you can # redistribute them and/or modify them under the terms of the GNU # General Public License as published by the Free Software Foundation; # either version 3, or (at your option) any later version. # # This program and its documentation are distributed in the hope that # they will be useful, but WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. die if @ARGV; while () { if (m/^Total CPU/) { die if $mode ne ''; $seentcpu=1; } elsif (m/^Current dynamic memory/ || m/^Dynamic memory limit/ || m/^Level not specified/ || m/^Doing analysis/ || m/^CPU time since/ || m/^Circuit\:/) { die if length $mode; } elsif (m/\f/) { die if m/\S/; } elsif (!m/\S/ && $mode eq InitTransSkip) { $mode= ''; } elsif (m/^Initial Transient/ && $mode eq '') { $mode= InitTrans; } elsif (m/^Node/ && $mode eq InitTrans) { $mode= InitTransSkip; } elsif (m/\w/ && $mode eq InitTransSkip) { } elsif (m/^\-\-/ && length $mode) { } elsif (m/^\s\s+(\w+) Analysis /) { if ($mode eq '') { $mode= Table; $ctable= $1; $dokey= 1; } else { $dokey= 0; } } elsif (m/^\s\s+/ && ($mode eq Table or !length $mode)) { } elsif (m/^Index / && $mode eq Table) { s/\s+$//; ($index,$key,@nowheadings) = split /\s+/, $_; $logxy= 0+($key eq 'frequency'); } elsif (m/^\d+\s/ && $mode eq Table) { ($index,$key,@nowcolumns) = split /\s+/, $_; shift @nowcolumns if $key =~ s/\,$//; $coldata[$index] .= " $key" if $dokey; $coldata[$index] .= " @nowcolumns"; if (!$index) { foreach $c (@nowheadings) { $h= 'y'; $h= 'y2' if $c =~ m/^ph\(/; push @columns, "$h:$c"; } } } elsif (!m/\S/ && $mode eq Table) { print "S $ctable $logxy @columns\n" or die $!; foreach $l (@coldata) { printf "D%s\n", $l or die $!; } print "T\n" or die $!; $mode= ''; @columns= (); @coldata= (); } elsif (!m/\S/) { $mode= '' if $mode eq InitTransSkip; } else { die "$mode: $_ ?"; } } die "no plots" unless defined $ctable; print "F\n" or die $!; # $Id: ngspice2genspic,v 1.3 2007-09-21 21:21:15 ianmdlvl Exp $ chiark-utils-4.4.2build1/scripts/genspic2gnuplot0000775000000000000000000000722112423200672016651 0ustar #!/usr/bin/perl # genspic2gnuplot - Copyright 2004 Ian Jackson - see below # # Reads a `genspic' file on stdin. Takes exactly one arg, . # # Produces various output files: # .gnuplots.sh run this to display results # ,.gnuplot-cmd gnuplot script for displaying: # ,-.gnuplot-data gnuplot-format input data # ,gnuplot-fifo working fifo for .gnuplots.sh # where # is Freq or Time (according to the type of analysis) # is the count, starting at 0, of which report this is from gnucap # is the individual column of Y data # # Limitations # # There's no easy way to mess with the gnuplot settings. # # This whole scheme is very clumsy. There should be a driver program # with command-line option parsing. # This program and its documentation are free software; you can # redistribute them and/or modify them under the terms of the GNU # General Public License as published by the Free Software Foundation; # either version 3, or (at your option) any later version. # # This program and its documentation are distributed in the hope that # they will be useful, but WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. die unless @ARGV==1; die if $ARGV[0] =~ m/^\-/; ($ofb)= @ARGV; $sof= "$ofb.gnuplots.sh"; open A, "> $sof" or die $!; system 'chmod','+x',"$sof"; $? and die $?; print A <; if ($c eq 'S') { ($cplot,$logxy,@columns) = @s; $cplot .= $counter{$cplot}++; unshift @columns, 'x:'; @mmm= map { s/^(\w+)\:// or die; $1; } @columns; open S, "> $ofb,$cplot.gnuplot-cmd" or die $!; print S < $ofb,$cplot-$yn.gnuplot-data" or die $!; } } elsif ($c eq 'T') { die unless @mmm; die if @s; foreach $mmm (keys %min) { print S "set ${mmm}range [$min{$mmm}:$max{$mmm}]\n" or die $!; } $sep= "plot "; for ($yn=1; $yn<=$#columns; $yn++) { close "O$yn" or die $!; $mmm[$yn] =~ m/^y2?$/ or die "$mmm[$yn]"; $axes= $mmm[$yn]; $axes =~ s/^y$/y1/; $yoff= 1-$yn; print S "$sep\\\n". " '$ofb,$cplot-$yn.gnuplot-data'". " axes x1$axes title '$columns[$yn]'" or die $!; $sep= ','; } print S "\n\npause -1\n" or die $!; close S or die $!; print A " gnuplot $ofb,$cplot.gnuplot-cmd <\$fi &\n" or die $!; @mmm=@columns=(); } elsif ($c eq 'D') { die unless @mmm; @numbers= @s; die unless @numbers == @columns; for ($yn=0; $yn<=$#columns; $yn++) { $_= $numbers[$yn]; $mmm= $mmm[$yn]; $min{$mmm}= $_ unless exists($min{$mmm}) && $min{$mmm} <= $_; $max{$mmm}= $_ unless exists($max{$mmm}) && $max{$mmm} >= $_; if ($yn) { printf {"O$yn"} "%s %s\n", $numbers[0], $_ or die $!; } } } elsif ($c eq 'F') { last; } else { die; } } print A <\$fi printf 'hit return to quit: ' read exec 3>&- END or die $!; close A or die $!; print ": generated ; $sof\n" or die $!; # $Id: genspic2gnuplot,v 1.6 2007-09-21 21:21:15 ianmdlvl Exp $ chiark-utils-4.4.2build1/scripts/cvsweb-list0000775000000000000000000000443512423200672015774 0ustar #!/usr/bin/perl # cvsweb-list # This little program produces a web page listing the cvs repositories # available by ucgi cvsweb. It doesn't really separate code and # configuration, so it's not installed by default with chiark-utils. # This file is part of chiark-utils, a collection of useful programs # used on chiark.greenend.org.uk. # # This file is: # Copyright 2001 Ian Jackson # # This is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3, or (at your option) any later version. # # This is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. print < chiark public CVS

chiark users' public CVS

    END open UL, "/etc/userlist" or die $!; while (
      ) { next if m/^\#/ or !m/\S/; chomp($user= $_); next unless readlink "/home/$user/public-cgi/cvsweb" eq '/usr/local/lib/cvsweb'; $hd= 0; $pc= "/home/$user/public-CVS/"; next unless opendir D, $pc; while (defined($mod= readdir D)) { next unless -d "$pc/$mod"; next if $mod =~ m/^\./; if (!$hd) { print "
    • $user" or die $!; print " (homepage)" or die $! if -d "/home/$user/public-html"; print ":" or die $!; $hd= 1; } else { print "," or die $!; } print " $mod" or die $!; } next unless $hd; print "
    • \n" or die $!; } close UL or die $!; print <
      maintained by $ENV{SERVER_ADMIN}; chiark home page
      END exit 0 chiark-utils-4.4.2build1/scripts/palm-datebook-reminders.10000664000000000000000000000424012423200672020365 0ustar .\" Hey, Emacs! This is an -*- nroff -*- source file. .TH PALM\-DATEBOOK\-REMINDERS 1 "12th January 2002" "Greenend" "chiark utilities" .SH NAME palm\-datebook\-reminders \- generate simple report from Palm Datebook DB .SH SYNOPSIS .BR PALM\-DATEBOOK\-REMINDERS " [\fIoptions\fP] .SH DESCRIPTION .B palm\-datebook\-reminders reads a Palm Datebook database and prints out a list of events in the near future in a reasonably easy to read format. .PP Immiment events (ones in approximately the next 48 hours by default) are listed with complete item text. Forthcoming events (approximately the next 4 weeks by default) are listed in summary form. .SH OPTIONS .TP .IR imminent \fB/\fR forthcoming " | " \fB-t\fR imminent \fB/\fR forthcoming Specifies that imminent and forthcoming events are ones within the next .I immiment and .I forthcoming seconds, respectively. .TP .IR filename " | \fB-f\fR" filename Reads the file .I filename instead of .BR ./DatebookDB.pdb . If the bare form is used, .I filename must start with a .B . or .B / (dot or slash). .SH BUGS The program is not configurable enough. The option parsing is very grotty. Proper character set conversion is not performed. .SH FILES .TP .BR DatebookDB.pdb Default input file. .SH AUTHOR .B PALM\-DATEBOOK\-REMINDERS and this manpage were written by Ian Jackson . They are Copyright 2003 Ian Jackson. palm\-datebook\-reminders and this manpage are free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, consult the Free Software Foundation's website at www.fsf.org, or the GNU Project website at www.gnu.org. Palm Datebook is a piece of proprietary software supplied with Palm organisers. Palm and Datebook are probably trademarks. chiark-utils-4.4.2build1/scripts/git-cache-proxy0000775000000000000000000003135712445561556016557 0ustar #!/usr/bin/perl -w # # git caching proxy # Suitable only for exposing to semi-trusted clients: clients are not # supposed to be able to take over the server. However, clients can # probably deny service to each other because the current # implementation is not very good at handling various out-of-course # situations (notably, clients which are too slow). # usage: run it on some port, and then clone or fetch # "git://:/[ ]" # where is http:///... or git:///... # and is zero or more (whitespace-separated) of # [] will be ignored if not recognised # {} error if not recognised # options currently known: # fetch=must fail if the fetch/clone from upstream fails # fetch=no just use what is in the cache # fetch=try use what is in the cache if the fetch/clone fails # timeout= length of time to allow for fetch/clone # example inetd.conf line: # 9419 stream tcp nowait git-cache /usr/bin/git-cache-proxy git-cache-proxy # you'll need to # adduser git-cache # mkdir /var/cache/git-cache-proxy # chown git-cache /var/cache/git-cache-proxy # git-cache-proxy # Copyright 2010 Tony Finch # Copyright 2013,2014 Ian Jackson # # git-cache-proxy is free software; you can redistribute it and/or # modify them under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 3, or (at # your option) any later version. # # git-cache-proxy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. # # (Some code taken from userv-utils's git-daemon.in and git-service.in # which were written by Tony Finch and subsequently # heavily modified by Ian Jackson # and were released under CC0 1.0. The whole program is now GPLv3+.) use strict; use warnings; use POSIX; use Socket; use Sys::Syslog; use Fcntl qw(:flock SEEK_SET); use File::Path qw(remove_tree); our $us = 'git-cache-proxy'; our $debug = 0; our $housekeepingeverydays = 1; our $treeexpiredays = 21; our $fetchtimeout = 1800; our $maxfetchtimeout = 3600; our $cachedir = '/var/cache/git-cache-proxy'; our $housekeepingonly = 0; #---------- error handling and logging ---------- # This is a bit fiddly, because we want to catch errors sent to stderr # and dump them to syslog if we can, but only if we are running as an # inetd service. our $log; # filehandle (ref), or "1" meaning syslog sub ntoa { my $sockaddr = shift; return ('(local)') unless defined $sockaddr; my ($port,$addr) = sockaddr_in $sockaddr; $addr = inet_ntoa $addr; return ("[$addr]:$port",$addr,$port); } our ($client) = ntoa getpeername STDIN; our ($server) = ntoa getsockname STDIN; sub ensurelog () { return if $log; openlog $us, qw(pid), 'daemon'; $log = 1; } sub logm ($$) { my ($pri, $msg) = @_; return if $pri eq 'debug' && !$debug; if ($client eq '(local)') { print STDERR "$us: $pri: $msg\n" or die $!; return; } ensurelog(); my $mainmsg = sprintf "%s-%s: %s", $server, $client, $msg; if (ref $log) { my $wholemsg = sprintf("%s [%d] %s: %s\n", strftime("%Y-%m-%d %H:%M:%S Z", gmtime), $$, $pri eq 'err' ? 'error' : $pri, $mainmsg); print $log $wholemsg; } else { syslog $pri, "%s", "$pri $mainmsg"; } } if ($client ne '(local)') { open STDERR, ">/dev/null" or exit 255; open TEMPERR, "+>", undef or exit 255; open STDERR, ">&TEMPERR" or exit 255; } END { if ($client ne '(local)') { if ($?) { logm 'crit', "crashing ($?)"; } seek TEMPERR, 0, SEEK_SET; while () { chomp; logm 'crit', $_; } } exit $?; } sub fail ($) { my ($msg) = @_; logm 'err', $msg; exit 0; } sub gitfail ($) { my ($msg) = @_; close LOCK; alarm 60; logm 'notice', $msg; my $gitmsg = "ERR $us: $msg"; $gitmsg = substr($gitmsg,0,65535); # just in case printf "%04x%s", length($gitmsg)+4, $gitmsg; flush STDOUT; exit 0; } #---------- argument parsing ---------- for (;;) { last unless @ARGV; last unless $ARGV[0] =~ m/^-/; $_ = shift @ARGV; for (;;) { last unless m/^-./; if (s/^-H/-/) { $housekeepingonly++; } elsif (s/^-D/-/) { $debug++; } elsif (s/^-L(.*)$//) { my $logfile = $_; open STDERR, ">>", $logfile or fail "open $logfile: $!"; $log = \*STDERR; } elsif (s/^-d(.*)$//) { $cachedir = $1; } elsif (s/^--( max-fetch-timeout | fetch-timeout | tree-expire-days | housekeeping-interval-days )=(\d+)$//x) { my $vn = $1; $vn =~ y/-//d; die $vn unless defined ${ $::{$vn} }; ${ $::{$vn} } = $2; } else { fail "bad usage: unknown option `$_'"; } } } !@ARGV or fail "bad usage: no non-option arguments permitted"; #---------- utility functions ---------- sub lockfile ($$$) { my ($fh, $fn, $flockmode) = @_; my $what = $fn.(($flockmode & ~LOCK_NB) == LOCK_SH ? " (shared)" : ""); for (;;) { close $fh; open $fh, '+>', $fn or fail "open/create $fn for lock: $!"; logm 'debug', "lock $what: acquiring"; if (!flock $fh, $flockmode) { if ($flockmode & LOCK_NB && $! == EWOULDBLOCK) { return 0; # ok then } fail "lock $what: $!"; } stat $fh or fail "stat opened $fn: $!"; my $fh_ino = ((stat _)[1]); if (!stat $fn) { $! == ENOENT or fail "stat $fn: $!"; next; } my $fn_ino = ((stat _)[1]); if ($fn_ino == $fh_ino) { logm 'debug', "lock $what: acquired"; return 1; } logm 'debug', "lock $what: deleted, need to loop again"; # oh dear } } sub xread { my $length = shift; my $buffer = ""; while ($length > length $buffer) { my $ret = sysread STDIN, $buffer, $length, length $buffer; fail "expected $length bytes, got ".length $buffer if defined $ret and $ret == 0; fail "read: $!" if not defined $ret and $! != EINTR and $! != EAGAIN; } return $buffer; } #---------- main program ---------- chdir $cachedir or fail "chdir $cachedir: $!"; our ($service,$specpath,$spechost,$subdir); our ($tmpd,$gitd,$lock); our ($fetch,$url); sub servinfo ($) { my ($msg) = @_; logm 'info', "service `$specpath': $msg"; } sub readcommand () { $SIG{ALRM} = sub { fail "timeout" }; alarm 30; my $hex_len = xread 4; fail "Bad hex in packet length" unless $hex_len =~ m|^[0-9a-fA-F]{4}$|; my $line = xread -4 + hex $hex_len; unless (($service,$specpath,$spechost) = $line =~ m|^(git-[a-z-]+) /*([!-~ ]+)\0host=([!-~]+)\0$|) { $line =~ s|[^ -~]+| |g; gitfail "unknown/unsupported instruction `$line'" } alarm 0; $service eq 'git-upload-pack' or gitfail "unknown/unsupported service `$service'"; $fetch = 2; # 0:don't; 1:try; 2:force $url = $specpath; while ($url =~ s#\s+(\[)([^][{}]+)\]$## || $url =~ s#\s+(\{)([^][{}]+)\}$##) { $_ = $2; my $must = $1 eq '{'; if (m/^fetch=try$/) { $fetch = 1; } elsif (m/^fetch=no$/) { $fetch = 0; } elsif (m/^fetch=must$/) { $fetch = 2; # the default } elsif (m/^timeout=(\d+)$/ && $1 >= 1) { $fetchtimeout = $1 <= $maxfetchtimeout ? $1 : $maxfetchtimeout; } elsif ($must) { gitfail "unknown/unsupported option `$_'"; } } $url =~ m{^(?:https?|git)://[-.0-9a-z]+/} or gitfail "unknown/unsupported url scheme or format `$url'"; $subdir = $url; $subdir =~ s|\\|\\\\|g; $subdir =~ s|,|\\,|g; $subdir =~ s|/|,|g; $tmpd= "$subdir\\.tmp"; $gitd= "$subdir\\.git"; $lock = "$subdir\\.lock"; servinfo "locking"; } sub clonefetch () { lockfile \*LOCK, $lock, LOCK_EX; my $exists = lstat $gitd; $exists or $!==ENOENT or fail "lstat $gitd: $!"; our $fetchfail = ''; if ($fetch) { our @cmd; if (!$exists) { system qw(rm -rf --), $tmpd; @cmd = (qw(git clone -q --mirror), $url, $tmpd); servinfo "cloning"; } else { @cmd = (qw(git remote update --prune)); servinfo "fetching"; } my $cmd = "@cmd[0..1]"; my $child = open FETCHERR, "-|"; defined $child or fail "fork: $!"; if (!$child) { if ($exists) { chdir $gitd or fail "chdir $gitd: $!"; } setpgrp or fail "setpgrp: $!"; open STDERR, ">&STDOUT" or fail "redirect stderr: $!"; exec @cmd or fail "exec $cmd[0]: $!"; } my $fetcherr = ''; my $timedout = 0; { local $SIG{ALRM} = sub { servinfo "fetch/clone timeout"; $timedout=1; kill 9, -$child; }; alarm($fetchtimeout); $!=0; { local $/=undef; $fetcherr = ; } !FETCHERR->error or fail "read pipe from fetch/clone: $!"; alarm(10); } kill -9, $child or fail "kill fetch/clone: $!"; $!=0; $?=0; if (!close FETCHERR) { fail "reap fetch/clone: $!" if $!; my $fetchfail = !($? & 255) ? "$cmd died with error exit code ".($? >> 8) : $? != 9 ? "$cmd died due to fatal signa, status $?" : $timedout ? "$cmd timed out (${fetchtimeout}s)" : "$cmd died due to unexpected SIGKILL"; if (length $fetcherr) { $fetchfail .= "\n$fetcherr"; $fetchfail =~ s/\n$//; $fetchfail =~ s{\n}{ // }g; } if ($fetch >= 2) { gitfail $fetchfail; } else { servinfo "fetch/clone failed: $fetchfail"; } } if (!$exists) { rename $tmpd, $gitd or fail "rename fresh $tmpd to $gitd: $!"; $exists = 1; } } else { $fetchfail = 'not attempted'; } if (!$exists) { gitfail "no cached data, and not cloned: $fetchfail"; } servinfo "sharing"; lockfile \*LOCK, $lock, LOCK_SH; # NB releases and relocks if (stat $gitd) { return 1; } $!==ENOENT or fail "stat $gitd: $!"; # Well, err, someone must have taken the lock in between # and garbage collected it. How annoying. return 0; } sub hkfail ($) { my ($msg) = @_; fail "housekeeping: $msg"; } sub housekeeping () { logm 'info', "housekeeping started"; foreach $lock (<[a-z]*\\.lock>) { my $subdir = $lock; $subdir =~ s/\\.lock$//; if (!lstat $lock) { $! == ENOENT or hkfail "$lock: lstat: $!"; next; } if (-M _ <= $treeexpiredays) { logm 'debug', "housekeeping: subdirs $subdir: touched recently"; next; } if (!lockfile \*LOCK, $lock, LOCK_EX|LOCK_NB) { logm 'info', "housekeeping: subdirs $subdir: lock busy, skipping"; next; } logm 'info', "housekeeping: subdirs $subdir: cleaning"; eval { foreach my $suffix (qw(tmp git)) { my $dir = "${subdir}\\.$suffix"; my $tdir = "${subdir}\\.tmp"; if ($dir ne $tdir) { if (!rename $dir,$tdir) { next if $! == ENOENT; die "$dir: cannot rename to $tdir: $!\n"; } } system qw(rm -rf --), $tdir; if (stat $tdir) { die "$dir: problem deleting file(s), rm exited $?\n"; } elsif ($! != ENOENT) { die "$tdir: cannot stat after deletion: $!\n"; } } }; if (length $@) { chomp $@; logm 'warning', "housekeeping: $subdir: cleanup prevented: $@"; } else { unlink $lock or hkfail "remove $lock: $!"; } } open HS, ">", "Housekeeping.stamp" or hkfail "touch Housekeeping.stamp: $!"; close HS or hkfail "close Housekeeping.stamp: $!"; logm 'info', "housekeeping finished"; } sub housekeepingcheck ($$) { my ($dofork, $force) = @_; if (!$force) { if (!lockfile \*HLOCK, "Housekeeping.lock", LOCK_EX|LOCK_NB) { logm 'debug', "housekeeping lock taken, not running"; close HLOCK; return 0; } } if ($force) { logm 'info', "housekeeping forced"; } elsif (!lstat "Housekeeping.stamp") { $! == ENOENT or fail "lstat Housekeeping.stamp: $!"; logm 'info', "housekeeping not done yet, will run"; } elsif (-M _ <= $housekeepingeverydays) { logm 'debug', "housekeeping done recently"; close HLOCK; return 0; } if ($dofork) { my $child = fork; defined $child or fail "fork: $!"; if (!$child) { open STDERR, "|logger -p daemon.warning -t '$us(housekeeping)'" or die "fork: logger $!"; housekeeping(); exit 0; } } else { housekeeping(); } close HLOCK; return 1; } sub runcommand () { servinfo "serving"; chdir $gitd or fail "chdir $gitd: $!"; exec qw(git-upload-pack --strict --timeout=1000 .) or fail "exec git-upload-pack: $!"; } sub daemonservice () { readcommand(); while (!clonefetch()) { } housekeepingcheck(1,0); runcommand(); } if ($housekeepingonly) { housekeepingcheck(0, $housekeepingonly>=2); } else { daemonservice(); } chiark-utils-4.4.2build1/scripts/palm-datebook-reminders0000775000000000000000000000770012423200672020235 0ustar #!/usr/bin/perl # Copyright 2003 Ian Jackson # # This script and its documentation (if any) are free software; you # can redistribute it and/or modify them under the terms of the GNU # General Public License as published by the Free Software Foundation; # either version 3, or (at your option) any later version. # # chiark-named-conf and its manpage are distributed in the hope that # it will be useful, but WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. use Palm::PDB; use Palm::Datebook; use Time::Local; use POSIX; $us= $0 =~ m,[^/]+$, ? $& : $0; $detail= 86400*2; $lookforward= 86400*(7*4+1); # run at 15:00 daily sub setfilename($) { die "$us: only one filename at a time please\n" if defined $filename; $filename= $_[0]; } while (@ARGV) { $_= shift @ARGV; if (m,^(?:\-t)?(\d+)/(\d+)$,) { ($detail,$lookforward)=($1+0,$2+0); } elsif (m,^[./],) { setfilename($_); } elsif (m/^\-f/) { setfilename($'); } else { die "$us: unknown argument/option \`$_'\n"; } } $filename= "DatebookDB.pdb" if !defined $filename; defined($now= time) or die $!; stat $filename or die "$us: $filename: $!\n"; $backuptime= (stat _)[10]; $pdb = new Palm::PDB or die $!; $pdb->Load($filename) or die $!; foreach $record (@{ $pdb->{records} }) { if ($record->{start_hour} != 255) { $timestr= sprintf("%02d:%02d-%02d:%02d", $record->{start_hour}, $record->{start_minute}, $record->{end_hour}, $record->{end_minute}); @lt=(0, $record->{start_minute}, $record->{start_hour}); } else { @lt=(0, 0, 9); $timestr= " "; } if ($record->{repeat}{type}) { $timestr= "onwards "; } push @lt, $record->{day}, $record->{month}-1, $record->{year}-1900; defined($ettt= timelocal @lt) or die $!; next if ($ettt < $now - 86400 || $ettt > $now + $lookforward); @lt2= localtime($ettt) or die $!; defined($dowstr= strftime "%a", @lt2) or die $!; $datestr= sprintf("%04d-%02d-%02d", $record->{year}, $record->{month}, $record->{day}); $timesortkey= "$datestr $timestr"; $evhead= "$datestr $dowstr $timestr"; $desc= $record->{description}; $desc =~ s/\x93/\~/g; $desc =~ s/\xa3/L/g; $desc =~ s/[^\n -\176]/ sprintf "\\x%02x", ord $& /eg; $desc =~ s/^ +//; $desc =~ s/\n+$//; $desc .= "\n"; if ($desc =~ m/^(.{0,51})\n/) { $descsumm= $1; } elsif ($desc =~ m/^.{48}/) { $descsumm= $&.'...'; } else { $descsumm= " --- no description ?! ---"; } $kind= $ettt < $now + $detail ? 'detail' : 'forward'; push @{ $events{$kind} }, { TSK => $timesortkey, Headline => sprintf("%s %s", $evhead, $descsumm), Desc => $desc }; } sub sectline_detail(){ return "Imminent events"; } sub sectline_forward(){ return "Forthcoming events"; } sub headline(){ printf("%s\n", $ev->{Headline}) or die $!; } sub print_forward(){ headline(); } sub print_detail(){ print "\n" or die $!; headline(); $_= $ev->{Desc}; s/^/ /gm; print $_ or die $!; } foreach $kind (qw(detail forward)) { $sectline= &{"sectline_$kind"}; printf("%s\n%s\n", $sectline, '-'x(length $sectline)) or die $!; if (!@{ $events{$kind} }) { printf("None scheduled.\n") or die $!; } foreach $ev (sort { $a->{TSK} cmp $b->{TSK} } @{ $events{$kind} }) { &{"print_$kind"}; } print "\n" or die $!; } @lt2= localtime $backuptime or die $!; defined ($syncstr= strftime "%Y-%m-%d %a %H:%M", @lt2) or die $!; print("Date of last synch: $syncstr\n". "Events entered on PDA after this date are omitted.\n") or die $!; close STDOUT or die $!; chiark-utils-4.4.2build1/scripts/cvs-repomove0000775000000000000000000001540612423200672016157 0ustar #!/bin/bash set -e print_usage () { cat <<'END' usage: to move repository directory, cd to anywhere and cvs-repomove --move local-repo module hostname remote-repo cvs-repomove --move src-hostname src-repo module dst-hostname dst-repo to adjust CVS/Root afterwards, in each working directory cvs-repomove automatically update **/CVS/Root END } # Copyright 2004-2006 Ian Jackson # # This script and its documentation (if any) are free software; you # can redistribute it and/or modify them under the terms of the GNU # General Public License as published by the Free Software Foundation; # either version 3, or (at your option) any later version. # # chiark-named-conf and its manpage are distributed in the hope that # it will be useful, but WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. # We do things in the following order: # src dst # 0. Check things @/..moving-to none/..moved-to # 1. Rename src repo to prevent commits etc. ..moving-to none/..moved-to # 2. Make temporary copy at destination ..moving-to ..tmp # 3. Install temporary copy at destination ..moving-to @ # 4. Move aside src repo ..moved-to @ fail () { echo >&2 "error: $1"; exit 8; } bad_usage () { echo >&2 "bad usage: $1"; print_usage >&2; exit 12; } move=false while [ $# -gt 0 ]; do case "$1" in --move) move=true ;; --help) print_usage; exit 0 ;; --) ;; -*) bad_usage "unknown option $1" ;; *) break ;; esac shift done mn () { echo " $1"; } check_module () { case "$module" in *..*) fail \ "moving a module with \`..' in its name is not supported" ;; */*) fail \ "moving a subdirectory is not supported" ;; -*) fail \ "moving a module whose name starts with \`-' is not supported" ;; esac } check_hostname () { case "$1" in /*|.*|-*) fail "bad hostname $dsthost" ;; esac } check_remote_path () { case "$1" in *[^0-9a-zA-Z/._+,-]*) fail \ "pathname may not contain metacharacters, sorry" ;; esac } do_move () { check_module check_hostname "$srchost" check_hostname "$dsthost" check_remote_path "$srcrepo/$module" check_remote_path "$dstrepo/$module" case "$dstrepo" in /*) ;; *) bad_usage "destination repo path must be absolute" ;; esac printf "moving module %s from %s:%s to %s:%s\n" \ "$module" "$srchost" "$srcrepo" "$dsthost" "$dstrepo" mn "checking existing repository" "$CVS_RSH" "$srchost" bash -ec "' ls -d -- $srcrepo/CVSROOT >/dev/null '" dstrepotrans="$(printf '%s\n' "$dstrepo" | tr / :)" movingto="moving-to-$dsthost:$dstrepotrans" resume="$("$CVS_RSH" "$srchost" bash -ec "' if test -d $srcrepo/$module..$movingto; then echo >&2 \" resuming previous attempt at a move\" resume=true if test -d $srcrepo/$module; then echo >&2 \"but $srcrepo/$module exists too\" exit 1 fi else resume=false ls -d -- $srcrepo/$module >/dev/null fi set +e previously=\"$(ls -d -- $srcrepo/$module..moved-to-* 2>/dev/null)\" set -e if [ \"x\$previously\" != x ]; then echo >&2 \" btw, module was once before moved away from here\" mv -- \"\$previously\" \ \"\${previously/..moved-to-/..previously-\$(date +%s)-moved-to-}\" fi echo \$resume '")" mn "checking dst repository" "$CVS_RSH" "$dsthost" bash -ec "' cd $dstrepo ls -d CVSROOT >/dev/null if test -d $dstrepo/$module; then echo >&2 module already exists in destination repo exit 1 fi for f in $module..*; do case \"\$f\" in *..moved-to-*) echo \" btw, module was previously at destn repo\" mv -- \"\$f\" \ \"\${f/..moved-to-/..previously-\$(date +%s)-moved-to-}\" ;; *..previously-*) ;; *..tmp-*) echo \" nb: possibly-stale temp/partial copy \$f\" ;; *..\*) ;; *) echo >&2 \"error: found unexpected subdir \$f\" exit 8 ;; esac done '" if ! $resume; then "$CVS_RSH" "$srchost" bash -ec "' mv -- $srcrepo/$module $srcrepo/$module..$movingto '" fi mn "transferring repo data" tmpid="tmp-$(uname -n).$(date +%s)" "$CVS_RSH" "$srchost" bash -ec "' tar -c -C $srcrepo/$module..$movingto -f - . '" | "$CVS_RSH" "$dsthost" bash -ec "' cd $dstrepo mkdir $module..$tmpid cd $module..$tmpid tar --no-same-owner -xf - '" test "${PIPESTATUS[*]}" = "0 0" mn "confirming move at destination repo" "$CVS_RSH" "$dsthost" bash -ec "' cd $dstrepo mv $module..$tmpid $module '" mn "confirming move at source repo" "$CVS_RSH" "$srchost" bash -ec "' mv -- $srcrepo/$module..$movingto \ $srcrepo/$module..moved-to-$dsthost:$dstrepotrans '" echo "module moved successfully" } compute_fqdn_data () { fqdn_data="$(adnshost -s - "$1" 2>/dev/null || true)" } do_furtle () { module="$(cat CVS/Repository)" oldroot="$(cat CVS/Root)" goose="$oldroot" check_module printf "checking/updating repo for %s\n" "$module" compute_fqdn_data "$(uname -n)" our_fqdn_data="$fqdn_data" searching=true while $searching; do mn "chasing geese at $goose" case "$goose" in *[0-9a-zA-Z]:/*) remotehost="${goose%%:*}" path="${goose#*:}" check_hostname "$remotehost" check_remote_path "$path/$module" isremote=true compute_fqdn_data "$remotehost" if [ "x$fqdn_data" = "x$our_fqdn_data" -a \ "x$fqdn_data" != x ]; then isremote=false goose="$path" fi ;; *:*) fail "unknown remote repository string $goose" ;; /*) isremote=false path="$goose" ;; *) fail "unknown repository string $goose" ;; esac check=" cd $path if test -d $module; then echo good; exit 0; fi if ls -d $module..moved-to-* 2>/dev/null; then exit 0; fi echo bad " if $isremote; then new_goose_info="$("$CVS_RSH" "$remotehost" \ bash -ec "'$check'")" else new_goose_info="$( bash -ec "$check")" fi case "$new_goose_info" in good) searching=false ;; bad) echo >&2 "trail went cold at $goose" exit 4 ;; *..moved-to-*) goose="$(printf '%s\n' \ "${new_goose_info#*..moved-to-}" | \ tr : / | sed -e 's,/,:,')" ;; esac done if [ "x$goose" = "x$oldroot" ]; then echo 'repo has not moved - nothing to do' exit 0 fi mn "found new repo, adjusting working tree" cvs-adjustroot "$oldroot" "$goose" echo 'working tree adjustment completed' } if $move; then if [ $# = 4 ]; then srchost="$(hostname -f)"; srcrepo="$1" module="$2"; dsthost="$3"; dstrepo="$4" elif [ $# = 5 ]; then srchost="$1"; srcrepo="$2" module="$3"; dsthost="$4"; dstrepo="$5" else bad_usage "--move needs hostname(s) and paths" fi do_move else [ "$#" = 0 ] || bad_usage "without --move, give no arguments" do_furtle fi chiark-utils-4.4.2build1/scripts/gnucap2genspic0000775000000000000000000000562212423200672016441 0ustar #!/usr/bin/perl # gnucap2genspic - Copyright 2004 Ian Jackson - see below # # Reads the output from gnucap and outputs a `genspic' file # for use with genspic2gnuplot. Takes no arguments or options. # # Limitations # # Only Freq (.AC) and Time (.TRAN) plots have been tested. If # other types go wrong they can probably be fixed by adding code for # them to startplot(). # # Displaying voltages and currents on the same .TRAN graph won't work # well because they currently have to have the same Y scale. This # could be fixed by assigning carefully to $mmm in startplot(). # # This whole scheme is very clumsy. There should be a driver program # with command-line option parsing. # This program and its documentation are free software; you can # redistribute them and/or modify them under the terms of the GNU # General Public License as published by the Free Software Foundation; # either version 3, or (at your option) any later version. # # This program and its documentation are distributed in the hope that # they will be useful, but WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. die if @ARGV; %facttimes= qw(f 1e-15 p 1e-12 n 1e-9 u 1e-6 m 1e-3 K 1e3 Meg 1e6 G 1e9 T 1e12); sub startplot () { $logxy= 0; for ($yn=1; $yn<=$#columns; $yn++) { $mmm[$yn]= 'y'; } if ($kind eq 'Freq') { $logxy= 1; for ($yn=1; $yn<=$#columns; $yn++) { die unless $columns[$yn] =~ m/.*([MP])\(\d+\)$/i; $mmm[$yn]= 'y2' if uc $1 eq 'P'; } } printf "S %s %d", $cplot, $logxy or die $!; for ($yn=1; $yn<=$#columns; $yn++) { printf " %s:%s", $mmm[$yn], $columns[$yn] or die $!; } print "\n" or die $!; } sub endplot () { return unless defined $kind; print "T\n" or die $!; } $readahead= ; for (;;) { $linesofar= $readahead; for (;;) { $readahead= ; last unless $readahead =~ s/^\+//; die unless length $linesofar; $linesofar =~ s/\n$//; $linesofar .= $readahead; } $_= $linesofar; last unless length; s/\s+$//; if (m/^\#(\w+)/) { endplot(); $kind= $1; @columns= split /\s+/; $cplot= $kind; startplot(); next; } elsif (!defined $kind) { next; } elsif (s/^\s+//) { @numbers= split /\s+/; map { if (m/^(\-?\d+\.\d*)([A-Za-z]+)$/) { die "factor $2" unless exists $facttimes{$2}; $_= $1*$facttimes{$2}; } } @numbers; print "D @numbers\n" or die $!; } else { die "$_ ?"; } } die "no plots" unless defined $kind; endplot(); print "F\n" or die $!; # $Id: gnucap2genspic,v 1.4 2007-09-21 21:21:15 ianmdlvl Exp $ chiark-utils-4.4.2build1/scripts/expire-iso86010000775000000000000000000001324112423200672016130 0ustar #!/bin/bash set -e usage () { cat <] x [x ...] options: -u is measured in units of seconds (default is 86400, so is in days) -s allow kept items to be seconds shorter apart than specified; default is 10% of -n do not really delete -r recursive removal (rm -r) example: /home/ian/junk/expire-iso8601 14x1 4x7 uses units of 86400s (1 day) with a slop of 8640 it keeps 14 daily items (that is 14 items, dated no less than 86400-8640 apart) and 4 weekly items (that is 4 items, dated no less than 7*86400-8640 apart) the 14 daily and 7 weekly items may be the same, or not There is no need to sort the list of x pairs. exit status: 0 ok 4 rm failed 8 bad usage 16 catastrophic failure END } # Copyright 2006 Ian Jackson # # This script and its documentation (if any) are free software; you # can redistribute it and/or modify them under the terms of the GNU # General Public License as published by the Free Software Foundation; # either version 3, or (at your option) any later version. # # chiark-named-conf and its manpage are distributed in the hope that # it will be useful, but WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. trap 'exit 16' 0 badusage () { echo >&2 "bad usage: $*"; usage >&2; trap '' 0; exit 8; } #-------------------- argument parsing -------------------- alldigits () { [ "x${2##*[^0-9]}" = "x$2" ] || \ badusage "bad $1 \`$2'; must be all digits" [ "$2" ] || badusage "bad $2; must be nonempty" eval $1='$2' } rm=rm recurse='' unit=86400 slop='' while [ $# -ge 1 ]; do arg=$1; shift case "$arg" in --|-) break ;; --help) usage; exit 0 ;; --*) badusage "unknown option $arg" ;; -*) val=${arg#-?} case "$arg" in -n*) rm=: ;; -r*) recurse=-r ;; -u*) alldigits unit "$val"; arg='' ;; -s*) alldigits slop "$val"; arg='' ;; *) badusage "unknown option ${1:0:2}" ;; esac arg=-${arg#-?} if test "x$arg" != x-; then set -- "$arg" "$@"; fi ;; *) set "$arg" "$@"; break ;; esac done [ $# -ge 1 ] || badusage 'too few arguments' [ "$slop" ] || slop=$(( $unit / 10 )) for ni in "$@"; do case "$ni" in *x*);; *) badusage "bad x $ni";; esac alldigits number "${ni%%x*}" alldigits interval "${ni#*x}" done #-------------------- scanning the directory ---------- # We build in $l a list of the relevant filenames and the time_t's # they represent. # # Each entry in $l is $time_t/$filename, and the list is # newline-separated for the benefit of sort(1). ls=0 for cn in [0-9]*; do case "$cn" in ????-??-??) conv="$cn";; ????-??-??T[0-2][0-9]+[0-9][0-9][0-9][0-9]|\ ????-??-??T[0-2][0-9]:[0-6][0-9]+[0-9][0-9][0-9][0-9]|\ ????-??-??T[0-2][0-9]:[0-6][0-9]:[0-6][0-9]+[0-9][0-9][0-9][0-9]) conv="${cn%T*} ${cn#*T}";; *) echo >&2 "ignoring $cn" continue;; esac cs=$(date -d "$conv" +%s) l="$cs/$cn $l" done #-------------------- main computation -------------------- # We process each minimum/extent pair, to have it select a bunch of # versions to keep. We annotate entries in $l: if we are keeping # an entry we prepend a colon; temporarily, if we are keeping an entry # because of this particular minimum/extent, we prepend a comma. # For each minimum/extent pair we look at the list from most recent # to least recent, # ie in order of increasing age # ie in order of decreasing time_t # and each time we're more than min older than the last item we kept, # we mark the item to keep, until we have as many as we want. # # We build the new list (space-separated) in lnew. l=$(sort -nr < # # This is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3, or (at your option) any later version. # # This is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with this program; if not, consult the Free Software Foundation's # website at www.fsf.org, or the GNU Project website at www.gnu.org. CONFIG_CPPFLAGS= -DRWBUFFER_SIZE_MB=$(RWBUFFER_SIZE_MB) \ -DREALLY_CHECK_FILE='"/etc/inittab"' CC= gcc CFLAGS= $(WARNINGS) $(OPTIMISE) $(DEBUG) $(CMDLINE_CFLAGS) CPPFLAGS= $(CONFIG_CPPFLAGS) $(CMDLINE_CPPFLAGS) LDFLAGS= $(CMDLINE_LDFLAGS) WARNINGS= -Wall -Wwrite-strings -Wmissing-prototypes \ -Wstrict-prototypes -Wpointer-arith -Wno-pointer-sign OPTIMISE= -O2 DEBUG= -g SYSTEM_GROUP= root prefix=/usr/local etcdir=/etc varlib=/var/lib confdir=$(etcdir)/$(us) bindir=$(prefix)/bin sbindir=$(prefix)/sbin sharedir=$(prefix)/share/$(us) txtdocdir=$(prefix)/share/doc/$(us) exampledir=$(txtdocdir)/examples vardir=$(varlib)/$(us) mandir=${prefix}/man man1dir=${mandir}/man1 man8dir=${mandir}/man8 # INSTALL_PROGRAM_STRIP_OPT=-s INSTALL= install -c INSTALL_SHARE= $(INSTALL) -m 644 -o root -g $(SYSTEM_GROUP) INSTALL_SCRIPT= $(INSTALL) -m 755 -o root -g $(SYSTEM_GROUP) INSTALL_PROGRAM= $(INSTALL_SCRIPT) $(INSTALL_PROGRAM_STRIP_OPT) INSTALL_DIRECTORY= $(INSTALL) -m 2755 -o root -g $(SYSTEM_GROUP) -d