update-alternatives.pl 18 KB

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