home *** CD-ROM | disk | FTP | other *** search
/ PC World 2000 February / PCWorld_2000-02_cd.bin / live / usr / sbin / update-alternatives < prev    next >
Text File  |  1999-03-02  |  16KB  |  439 lines

  1. #! /usr/bin/perl --
  2.  
  3. #use POSIX; &ENOENT;
  4. sub ENOENT { 2; }
  5. # Sorry about this, but POSIX.pm isn't necessarily available
  6.  
  7. $version="1.4.0.34"; # This line modified by Makefile
  8. sub usageversion {
  9.     print(STDERR <<END)
  10. Debian GNU/Linux update-alternatives $version.  Copyright (C) 1995
  11. Ian Jackson.  This is free software; see the GNU General Public Licence
  12. version 2 or later for copying conditions.  There is NO warranty.
  13.  
  14. Usage: update-alternatives --install <link> <name> <path> <priority>
  15.                           [--slave <link> <name> <path>] ...
  16.        update-alternatives --remove <name> <path>
  17.        update-alternatives --auto <name>
  18.        update-alternatives --display <name>
  19. <name> is the name in /etc/alternatives.
  20. <path> is the name referred to.
  21. <link> is the link pointing to /etc/alternatives/<name>.
  22. <priority> is an integer; options with higher numbers are chosen.
  23.  
  24. Options:  --verbose|--quiet  --test  --help  --version
  25.           --altdir <directory>  --admindir <directory>
  26. END
  27.         || &quit("failed to write usage: $!");
  28. }
  29. sub quit { print STDERR "update-alternatives: @_\n"; exit(2); }
  30. sub badusage { print STDERR "update-alternatives: @_\n\n"; &usageversion; exit(2); }
  31.  
  32. $altdir= '/etc/alternatives';
  33. $admindir= '/var/lib/dpkg/alternatives';
  34. $testmode= 0;
  35. $verbosemode= 0;
  36. $mode='';
  37. $manual= 'auto';
  38. $|=1;
  39.  
  40. sub checkmanymodes {
  41.     return unless $mode;
  42.     &badusage("two modes specified: $_ and --$mode");
  43. }
  44.  
  45. while (@ARGV) {
  46.     $_= shift(@ARGV);
  47.     last if m/^--$/;
  48.     if (!m/^--/) {
  49.         &quit("unknown argument \`$_'");
  50.     } elsif (m/^--(help|version)$/) {
  51.         &usageversion; exit(0);
  52.     } elsif (m/^--test$/) {
  53.         $testmode= 1;
  54.     } elsif (m/^--verbose$/) {
  55.         $verbosemode= +1;
  56.     } elsif (m/^--quiet$/) {
  57.         $verbosemode= -1;
  58.     } elsif (m/^--install$/) {
  59.         &checkmanymodes;
  60.         @ARGV >= 4 || &badusage("--install needs <link> <name> <path> <priority>");
  61.         ($alink,$name,$apath,$apriority,@ARGV) = @ARGV;
  62.         $apriority =~ m/^[-+]?\d+/ || &badusage("priority must be an integer");
  63.         $mode= 'install';
  64.     } elsif (m/^--remove$/) {
  65.         &checkmanymodes;
  66.         @ARGV >= 2 || &badusage("--remove needs <name> <path>");
  67.         ($name,$apath,@ARGV) = @ARGV;
  68.         $mode= 'remove';
  69.     } elsif (m/^--(display|auto)$/) {
  70.         &checkmanymodes;
  71.         @ARGV || &badusage("--$1 needs <name>");
  72.         $mode= $1;
  73.         $name= shift(@ARGV);
  74.     } elsif (m/^--slave$/) {
  75.         @ARGV >= 3 || &badusage("--slave needs <link> <name> <path>");
  76.         ($slink,$sname,$spath,@ARGV) = @ARGV;
  77.         defined($aslavelink{$sname}) && &badusage("slave name $sname duplicated");
  78.         $aslavelinkcount{$slink}++ && &badusage("slave link $slink duplicated");
  79.         $aslavelink{$sname}= $slink;
  80.         $aslavepath{$sname}= $spath;
  81.     } elsif (m/^--altdir$/) {
  82.         @ARGV || &badusage("--altdir needs a <directory> argument");
  83.         $altdir= shift(@ARGV);
  84.     } elsif (m/^--admindir$/) {
  85.         @ARGV || &badusage("--admindir needs a <directory> argument");
  86.         $admindir= shift(@ARGV);
  87.     } else {
  88.         &badusage("unknown option \`$_'");
  89.     }
  90. }
  91.  
  92. defined($aslavelink{$name}) && &badusage("name $name is both primary and slave");
  93. $aslavelinkcount{$alink} && &badusage("link $link is both primary and slave");
  94.  
  95. $mode || &badusage("need --display, --install, --remove or --auto");
  96. $mode eq 'install' || !%slavelink || &badusage("--slave only allowed with --install");
  97.  
  98. if (open(AF,"$admindir/$name")) {
  99.     $manual= &gl("manflag");
  100.     $manual eq 'auto' || $manual eq 'manual' || &badfmt("manflag");
  101.     $link= &gl("link");
  102.     while (($sname= &gl("sname")) ne '') {
  103.         push(@slavenames,$sname);
  104.         defined($slavenum{$sname}) && &badfmt("duplicate slave $tsname");
  105.         $slavenum{$sname}= $#slavenames;
  106.         $slink= &gl("slink");
  107.         $slink eq $link && &badfmt("slave link same as main link $link");
  108.         $slavelinkcount{$slink}++ && &badfmt("duplicate slave link $slink");
  109.         push(@slavelinks,$slink);
  110.     }
  111.     while (($version= &gl("version")) ne '') {
  112.         defined($versionnum{$version}) && &badfmt("duplicate path $tver");
  113.        if ( -r $version ) {
  114.            push(@versions,$version);
  115.            $versionnum{$version}= $i= $#versions;
  116.            $priority= &gl("priority");
  117.            $priority =~ m/^[-+]?\d+$/ || &badfmt("priority $version $priority");
  118.            $priorities[$i]= $priority;
  119.            for ($j=0; $j<=$#slavenames; $j++) {
  120.                $slavepath{$i,$j}= &gl("spath");
  121.            }
  122.        } else {
  123.            # File not found - remove
  124.             &pr("Alternative for $name points to $version - which wasn't found.  Removing from list of alternatives.");
  125.            &gl("priority");
  126.            for ($j=0; $j<=$#slavenames; $j++) {
  127.                &gl("spath");
  128.            }
  129.        }
  130.     }
  131.     close(AF);
  132.     $dataread=1;
  133. } elsif ($! != &ENOENT) {
  134.     &quit("failed to open $admindir/$name: $!");
  135. }
  136.  
  137. if ($mode eq 'display') {
  138.     if (!$dataread) {
  139.         &pr("No alternatives for $name.");
  140.     } else {
  141.         &pr("$name - status is $manual.");
  142.         if (defined($linkname= readlink("$altdir/$name"))) {
  143.             &pr(" link currently points to $linkname");
  144.         } elsif ($! == &ENOENT) {
  145.             &pr(" link currently absent");
  146.         } else {
  147.             &pr(" link unreadable - $!");
  148.         }
  149.         $best= '';
  150.         for ($i=0; $i<=$#versions; $i++) {
  151.             if ($best eq '' || $priorities[$i] > $bestpri) {
  152.                 $best= $versions[$i]; $bestpri= $priorities[$i];
  153.             }
  154.             &pr("$versions[$i] - priority $priorities[$i]");
  155.             for ($j=0; $j<=$#slavenames; $j++) {
  156.                 next unless length($tspath= $slavepath{$i,$j});
  157.                 &pr(" slave $slavenames[$j]: $tspath");
  158.             }
  159.         }
  160.         if ($best eq '') {
  161.             &pr("No versions available.");
  162.         } else {
  163.             &pr("Current \`best' version is $best.");
  164.         }
  165.     }
  166.     exit 0;
  167. }
  168.  
  169. $best= '';
  170. for ($i=0; $i<=$#versions; $i++) {
  171.     if ($best eq '' || $priorities[$i] > $bestpri) {
  172.         $best= $versions[$i]; $bestpri= $priorities[$i];
  173.     }
  174. }
  175.  
  176. if (defined($linkname= readlink("$altdir/$name"))) {
  177.     if ($linkname eq $best) {
  178.         $state= 'expected';
  179.     } elsif (defined($linkname2= readlink("$altdir/$name.dpkg-tmp"))) {
  180.         $state= 'expected-inprogress';
  181.     } else {
  182.         $state= 'unexpected';
  183.     }
  184. } elsif ($! == &ENOENT) {
  185.     $state= 'nonexistent';
  186. } else {
  187.     $state= 'unexpected';
  188. }
  189.  
  190. # Possible values for:
  191. #   $manual      manual, auto
  192. #   $state       expected, expected-inprogress, unexpected, nonexistent
  193. #   $mode        auto, install, remove
  194. # all independent
  195.  
  196. if ($mode eq 'auto') {
  197.     &pr("Setting up automatic selection of $name.");
  198.     unlink("$altdir/$name.dpkg-tmp") || $! == &ENOENT ||
  199.         &quit("unable to remove $altdir/$name.dpkg-tmp: $!");
  200.     unlink("$altdir/$name") || $! == &ENOENT ||
  201.         &quit("unable to remove $altdir/$name.dpkg-tmp: $!");
  202.     $state= 'nonexistent';
  203.     $manual= 'auto';
  204. } elsif ($state eq 'nonexistent') {
  205.     if ($mode eq 'manual') {
  206.         &pr("$altdir/$name has been deleted, returning to automatic selection.");
  207.         $mode= 'auto';
  208.     }
  209. }
  210.  
  211. #   $manual      manual, auto
  212. #   $state       expected, expected-inprogress, unexpected, nonexistent
  213. #   $mode        auto, install, remove
  214. # mode=auto <=> state=nonexistent
  215.  
  216. if ($state eq 'unexpected' && $manual eq 'auto') {
  217.     &pr("$altdir/$name has been changed (manually or by a script).\n".
  218.         "Switching to manual updates only.");
  219.     $manual= 'manual';
  220. }
  221.  
  222. #   $manual      manual, auto
  223. #   $state       expected, expected-inprogress, unexpected, nonexistent
  224. #   $mode        auto, install, remove
  225. # mode=auto <=> state=nonexistent
  226. # state=unexpected => manual=manual
  227.  
  228. &pr("Checking available versions of $name, updating links in $altdir ...\n".
  229.     "(You may modify the symlinks there yourself if desired - see \`man ln'.)");
  230.  
  231. if ($mode eq 'install') {
  232.     if ($link ne $alink && $link ne '') {
  233.         &pr("Renaming $name link from $link to $alink.");
  234.         rename($link,$alink) || $! == &ENOENT ||
  235.             &quit("unable to rename $link to $alink: $!");
  236.     }
  237.     $link= $alink;
  238.     if (!defined($i= $versionnum{$apath})) {
  239.         push(@versions,$apath);
  240.         $versionnum{$apath}= $i= $#versions;
  241.     }
  242.     $priorities[$i]= $apriority;
  243.     for $sname (keys %aslavelink) {
  244.         if (!defined($j= $slavenum{$sname})) {
  245.             push(@slavenames,$sname);
  246.             $slavenum{$sname}= $j= $#slavenames;
  247.         }
  248.         $oldslavelink= $slavelinks[$j];
  249.         $newslavelink= $aslavelink{$sname};
  250.         $slavelinkcount{$oldslavelink}-- if $oldslavelink ne '';
  251.         $slavelinkcount{$newslavelink}++ &&
  252.             &quit("slave link name $newslavelink duplicated");
  253.         if ($newslavelink ne $oldslavelink && $oldslavelink ne '') {
  254.             &pr("Renaming $sname slave link from $oldslavelink to $newslavelink.");
  255.             rename($oldslavelink,$newslavelink) || $! == &ENOENT ||
  256.                 &quit("unable to rename $oldslavelink to $newslavelink: $!");
  257.         }
  258.         $slavelinks[$j]= $newslavelink;
  259.     }
  260.     for ($j=0; $j<=$#slavenames; $j++) {
  261.         $slavepath{$i,$j}= $aslavepath{$slavenames[$j]};
  262.     }
  263. }
  264.  
  265. if ($mode eq 'remove') {
  266.     if (defined($i= $versionnum{$apath})) {
  267.         $k= $#versions;
  268.         $versionnum{$versions[$k]}= $i;
  269.         delete $versionnum{$versions[$i]};
  270.         $versions[$i]= $versions[$k]; $#versions--;
  271.         $priorities[$i]= $priorities[$k]; $#priorities--;
  272.         for ($j=0; $j<=$#slavenames; $j++) {
  273.             $slavepath{$i,$j}= $slavepath{$k,$j};
  274.             delete $slavepath{$k,$j};
  275.         }
  276.     } else {
  277.         &pr("Alternative $apath for $name not registered, not removing.");
  278.     }
  279. }
  280.  
  281. for ($j=0; $j<=$#slavenames; $j++) {
  282.     for ($i=0; $i<=$#versions; $i++) {
  283.         last if $slavepath{$i,$j} ne '';
  284.     }
  285.     if ($i > $#versions) {
  286.         &pr("Discarding obsolete slave link $slavenames[$j] ($slavelinks[$j]).");
  287.         unlink("$altdir/$slavenames[$j]") || $! == &ENOENT ||
  288.             &quit("unable to remove $slavenames[$j]: $!");
  289.         unlink($slavelinks[$j]) || $! == &ENOENT ||
  290.             &quit("unable to remove $slavelinks[$j]: $!");
  291.         $k= $#slavenames;
  292.         $slavenum{$slavenames[$k]}= $j;
  293.         delete $slavenum{$slavenames[$j]};
  294.         $slavelinkcount{$slavelinks[$j]}--;
  295.         $slavenames[$j]= $slavenames[$k]; $#slavenames--;
  296.         $slavelinks[$j]= $slavelinks[$k]; $#slavelinks--;
  297.         for ($i=0; $i<=$#versions; $i++) {
  298.             $slavepath{$i,$j}= $slavepath{$i,$k};
  299.             delete $slavepath{$i,$k};
  300.         }
  301.         $j--;
  302.     }
  303. }
  304.         
  305. if ($manual eq 'manual') {
  306.     &pr("Automatic updates of $altdir/$name are disabled, leaving it alone.");
  307.     &pr("To return to automatic updates use \`update-alternatives --auto $name'.");
  308. } else {
  309.     if ($state eq 'expected-inprogress') {
  310.         &pr("Recovering from previous failed update of $name ...");
  311.         rename("$altdir/$name.dpkg-tmp","$altdir/$name") ||
  312.             &quit("unable to rename $altdir/$name.dpkg-tmp to $altdir/$name: $!");
  313.         $state= 'expected';
  314.     }
  315. }
  316.  
  317. #   $manual      manual, auto
  318. #   $state       expected, expected-inprogress, unexpected, nonexistent
  319. #   $mode        auto, install, remove
  320. # mode=auto <=> state=nonexistent
  321. # state=unexpected => manual=manual
  322. # manual=auto => state!=expected-inprogress && state!=unexpected
  323.  
  324. open(AF,">$admindir/$name.dpkg-new") ||
  325.     &quit("unable to open $admindir/$name.dpkg-new for write: $!");
  326. &paf($manual);
  327. &paf($link);
  328. for ($j=0; $j<=$#slavenames; $j++) {
  329.     &paf($slavenames[$j]);
  330.     &paf($slavelinks[$j]);
  331. }
  332. &paf('');
  333. $best= '';
  334. for ($i=0; $i<=$#versions; $i++) {
  335.     if ($best eq '' || $priorities[$i] > $bestpri) {
  336.         $best= $versions[$i]; $bestpri= $priorities[$i]; $bestnum= $i;
  337.     }
  338.     &paf($versions[$i]);
  339.     &paf($priorities[$i]);
  340.     for ($j=0; $j<=$#slavenames; $j++) {
  341.         &paf($slavepath{$i,$j});
  342.     }
  343. }
  344. &paf('');
  345. close(AF) || &quit("unable to close $admindir/$name.dpkg-new: $!");
  346.  
  347. if ($manual eq 'auto') {
  348.     if ($best eq '') {
  349.         &pr("Last package providing $name ($link) removed, deleting it.");
  350.         unlink("$altdir/$name") || $! == &ENOENT ||
  351.             &quit("unable to remove $altdir/$name: $!");
  352.         unlink("$link") || $! == &ENOENT ||
  353.             &quit("unable to remove $altdir/$name: $!");
  354.         unlink("$admindir/$name.dpkg-new") ||
  355.             &quit("unable to remove $admindir/$name.dpkg-new: $!");
  356.         unlink("$admindir/$name") || $! == &ENOENT ||
  357.             &quit("unable to remove $admindir/$name: $!");
  358.         exit(0);
  359.     } else {
  360.         if (!defined($linkname= readlink($link)) && $! != &ENOENT) {
  361.             &pr("warning: $link is supposed to be a symlink to $altdir/$name\n".
  362.                 " (or nonexistent); however, readlink failed: $!");
  363.         } elsif ($linkname ne "$altdir/$name") {
  364.             unlink("$link.dpkg-tmp") || $! == &ENOENT ||
  365.                 &quit("unable to ensure $link.dpkg-tmp nonexistent: $!");
  366.             symlink("$altdir/$name","$link.dpkg-tmp") ||
  367.                 &quit("unable to make $link.dpkg-tmp a symlink to $altdir/$name: $!");
  368.             rename("$link.dpkg-tmp",$link) ||
  369.                 &quit("unable to install $link.dpkg-tmp as $link: $!");
  370.         }
  371.         if (defined($linkname= readlink("$altdir/$name")) && $linkname eq $best) {
  372.             &pr("Leaving $name ($link) pointing to $best.");
  373.         } else {
  374.             &pr("Updating $name ($link) to point to $best.");
  375.         }
  376.         unlink("$altdir/$name.dpkg-tmp") || $! == &ENOENT ||
  377.             &quit("unable to ensure $altdir/$name.dpkg-tmp nonexistent: $!");
  378.         symlink($best,"$altdir/$name.dpkg-tmp");
  379.     }
  380. }
  381.  
  382. rename("$admindir/$name.dpkg-new","$admindir/$name") ||
  383.     &quit("unable to rename $admindir/$name.dpkg-new to $admindir/$name: $!");
  384.  
  385. if ($manual eq 'auto') {
  386.     rename("$altdir/$name.dpkg-tmp","$altdir/$name") ||
  387.         &quit("unable to install $altdir/$name.dpkg-tmp as $altdir/$name");
  388.     for ($j=0; $j<=$#slavenames; $j++) {
  389.         $sname= $slavenames[$j];
  390.         $slink= $slavelinks[$j];
  391.         if (!defined($linkname= readlink($slink)) && $! != &ENOENT) {
  392.             &pr("warning: $slink is supposed to be a slave symlink to\n".
  393.                 " $altdir/$sname, or nonexistent; however, readlink failed: $!");
  394.         } elsif ($linkname ne "$altdir/$sname") {
  395.             unlink("$slink.dpkg-tmp") || $! == &ENOENT ||
  396.                 &quit("unable to ensure $slink.dpkg-tmp nonexistent: $!");
  397.             symlink("$altdir/$sname","$slink.dpkg-tmp") ||
  398.                 &quit("unable to make $slink.dpkg-tmp a symlink to $altdir/$sname: $!");
  399.             rename("$slink.dpkg-tmp",$slink) ||
  400.                 &quit("unable to install $slink.dpkg-tmp as $slink: $!");
  401.         }
  402.         $spath= $slavepath{$bestnum,$j};
  403.         unlink("$altdir/$sname.dpkg-tmp") || $! == &ENOENT ||
  404.             &quit("unable to ensure $altdir/$sname.dpkg-tmp nonexistent: $!");
  405.         if ($spath eq '') {
  406.             &pr("Removing $sname ($slink), not appropriate with $best.");
  407.             unlink("$altdir/$sname") || $! == &ENOENT ||
  408.                 &quit("unable to remove $altdir/$sname: $!");
  409.         } else {
  410.             if (defined($linkname= readlink("$altdir/$sname")) && $linkname eq $spath) {
  411.                 &pr("Leaving $sname ($slink) pointing to $spath.");
  412.             } else {
  413.                 &pr("Updating $sname ($slink) to point to $spath.");
  414.             }
  415.             symlink("$spath","$altdir/$sname.dpkg-tmp") ||
  416.                 &quit("unable to make $altdir/$sname.dpkg-tmp a symlink to $spath: $!");
  417.             rename("$altdir/$sname.dpkg-tmp","$altdir/$sname") ||
  418.                 &quit("unable to install $altdir/$sname.dpkg-tmp as $altdir/$sname: $!");
  419.         }
  420.     }
  421. }
  422.  
  423. sub pr { print(STDOUT "@_\n") || &quit("error writing stdout: $!"); }
  424. sub paf {
  425.     $_[0] =~ m/\n/ && &quit("newlines prohibited in update-alternatives files ($_[0])");
  426.     print(AF "$_[0]\n") || &quit("error writing stdout: $!");
  427. }
  428. sub gl {
  429.     $!=0; $_= <AF>;
  430.     length($_) || &quit("error or eof reading $admindir/$name for $_[0] ($!)");
  431.     s/\n$// || &badfmt("missing newline after $_[0]");
  432.     $_;
  433. }
  434. sub badfmt {
  435.     &quit("internal error: $admindir/$name corrupt: $_[0]");
  436. }
  437.  
  438. exit(0);
  439.