update-alternatives.pl 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. #!/usr/bin/perl --
  2. BEGIN { # Work-around for bug #479711 in perl
  3. $ENV{PERL_DL_NONLAZY} = 1;
  4. }
  5. use strict;
  6. use warnings;
  7. use POSIX qw(:errno_h);
  8. use Dpkg;
  9. use Dpkg::Gettext;
  10. textdomain("dpkg");
  11. # Global variables:
  12. my $altdir = '/etc/alternatives';
  13. # FIXME: this should not override the previous assignment.
  14. $admindir = $admindir . '/alternatives';
  15. my $verbosemode = 0;
  16. my $action = ''; # Action to perform (display / query / install / remove / auto / config)
  17. my $mode = 'auto'; # Update mode for alternative (manual / auto)
  18. my $skip = ''; # Skip alternatives properly configured in auto mode (for --config)
  19. my $state; # State of alternative:
  20. # expected: alternative with highest priority is the active alternative
  21. # expected-inprogress: busy selecting alternative with highest priority
  22. # unexpected: alternative another alternative is active / error during readlink
  23. # nonexistent: alternative-symlink does not exist
  24. my %versionnum; # Map from currently available versions into @versions and @priorities
  25. my @versions; # List of available versions for alternative
  26. my @priorities; # Map from @version-index to priority
  27. my $best;
  28. my $bestpri;
  29. my $bestnum;
  30. my $link; # Link we are working with
  31. my $linkname;
  32. my $alink; # Alternative we are managing (ie the symlink we're making/removing) (install only)
  33. my $name; # Name of the alternative (the symlink) we are processing
  34. my $apath; # Path of alternative we are offering
  35. my $apriority; # Priority of link (only when we are installing an alternative)
  36. my %aslavelink;
  37. my %aslavepath;
  38. my %aslavelinkcount;
  39. my $slink;
  40. my $sname;
  41. my $spath;
  42. my @slavenames; # List with names of slavelinks
  43. my %slavenum; # Map from name of slavelink to slave-index (into @slavelinks)
  44. my @slavelinks; # List of slavelinks (indexed by slave-index)
  45. my %slavepath; # Map from (@version-index,slavename) to slave-path
  46. my %slavelinkcount;
  47. sub version {
  48. printf _g("Debian %s version %s.\n"), $progname, $version;
  49. printf _g("
  50. Copyright (C) 1995 Ian Jackson.
  51. Copyright (C) 2000-2002 Wichert Akkerman.");
  52. printf _g("
  53. This is free software; see the GNU General Public Licence version 2 or
  54. later for copying conditions. There is NO warranty.
  55. ");
  56. }
  57. sub usage {
  58. printf _g(
  59. "Usage: %s [<option> ...] <command>
  60. Commands:
  61. --install <link> <name> <path> <priority>
  62. [--slave <link> <name> <path>] ...
  63. add a group of alternatives to the system.
  64. --remove <name> <path> remove <path> from the <name> group alternative.
  65. --remove-all <name> remove <name> group from the alternatives system.
  66. --auto <name> switch the master link <name> to automatic mode.
  67. --display <name> display information about the <name> group.
  68. --query <name> machine parseable version of --display <name>.
  69. --list <name> display all targets of the <name> group.
  70. --config <name> show alternatives for the <name> group and ask the
  71. user to select which one to use.
  72. --set <name> <path> set <path> as alternative for <name>.
  73. --all call --config on all alternatives.
  74. <link> is the symlink pointing to %s/<name>.
  75. (e.g. /usr/bin/pager)
  76. <name> is the master name for this link group.
  77. (e.g. pager)
  78. <path> is the location of one of the alternative target files.
  79. (e.g. /usr/bin/less)
  80. <priority> is an integer; options with higher numbers have higher priority in
  81. automatic mode.
  82. Options:
  83. --altdir <directory> change the alternatives directory.
  84. --admindir <directory> change the administrative directory.
  85. --skip-auto skip prompt for alternatives correctly configured
  86. in automatic mode (relevant for --config only)
  87. --verbose verbose operation, more output.
  88. --quiet quiet operation, minimal output.
  89. --help show this help message.
  90. --version show the version.
  91. "), $progname, $altdir;
  92. }
  93. sub quit
  94. {
  95. printf STDERR "%s: %s\n", $progname, "@_";
  96. exit(2);
  97. }
  98. sub badusage
  99. {
  100. printf STDERR "%s: %s\n\n", $progname, "@_";
  101. usage();
  102. exit(2);
  103. }
  104. sub read_link_group
  105. {
  106. return 1 if not -s "$admindir/$name"; # Ignore empty file
  107. if (open(AF, "$admindir/$name")) {
  108. $mode = gl("update_mode");
  109. $mode eq 'auto' || $mode eq 'manual' || badfmt(_g("invalid update mode"));
  110. $link = gl("link");
  111. while (($sname = gl("sname")) ne '') {
  112. push(@slavenames, $sname);
  113. defined($slavenum{$sname}) && badfmt(sprintf(_g("duplicate slave %s"), $sname));
  114. $slavenum{$sname} = $#slavenames;
  115. $slink = gl("slink");
  116. $slink eq $link && badfmt(sprintf(_g("slave link same as main link %s"), $link));
  117. $slavelinkcount{$slink}++ && badfmt(sprintf(_g("duplicate slave link %s"), $slink));
  118. push(@slavelinks, $slink);
  119. }
  120. while (($version = gl("version")) ne '') {
  121. defined($versionnum{$version}) && badfmt(sprintf(_g("duplicate path %s"), $version));
  122. if (-r $version) {
  123. push(@versions, $version);
  124. my $i;
  125. $versionnum{$version} = $i = $#versions;
  126. my $priority = gl("priority");
  127. $priority =~ m/^[-+]?\d+$/ || badfmt(sprintf(_g("priority %s %s"), $version, $priority));
  128. $priorities[$i] = $priority;
  129. for (my $j = 0; $j <= $#slavenames; $j++) {
  130. $slavepath{$i,$j} = gl("spath");
  131. }
  132. } else {
  133. # File not found - remove
  134. pr(sprintf(_g("Alternative for %s points to %s - which wasn't found. Removing from list of alternatives."), $name, $version))
  135. if $verbosemode > 0;
  136. gl("priority");
  137. for (my $j = 0; $j <= $#slavenames; $j++) {
  138. gl("spath");
  139. }
  140. }
  141. }
  142. close(AF);
  143. return 0;
  144. } elsif ($! != ENOENT) {
  145. quit(sprintf(_g("unable to open %s: %s"), "$admindir/$name", $!));
  146. } elsif ($! == ENOENT) {
  147. return 1;
  148. }
  149. }
  150. sub fill_missing_slavepaths()
  151. {
  152. for (my $j = 0; $j <= $#slavenames; $j++) {
  153. for (my $i = 0; $i <= $#versions; $i++) {
  154. $slavepath{$i,$j} ||= '';
  155. }
  156. }
  157. }
  158. sub find_best_version
  159. {
  160. $best = '';
  161. for (my $i = 0; $i <= $#versions; $i++) {
  162. if ($best eq '' || $priorities[$i] > $bestpri) {
  163. $best = $versions[$i];
  164. $bestpri = $priorities[$i];
  165. $bestnum = $i;
  166. }
  167. }
  168. }
  169. sub display_link_group
  170. {
  171. pr(sprintf("%s - %s", $name,
  172. ($mode eq "auto") ? _g("auto mode") : _g("manual mode")));
  173. $linkname = readlink("$altdir/$name");
  174. if (defined($linkname)) {
  175. pr(sprintf(_g(" link currently points to %s"), $linkname));
  176. } elsif ($! == ENOENT) {
  177. pr(_g(" link currently absent"));
  178. } else {
  179. pr(sprintf(_g(" link unreadable - %s"), $!));
  180. }
  181. for (my $i = 0; $i <= $#versions; $i++) {
  182. pr(sprintf(_g("%s - priority %s"), $versions[$i], $priorities[$i]));
  183. for (my $j = 0; $j <= $#slavenames; $j++) {
  184. my $tspath = $slavepath{$i, $j};
  185. next unless length($tspath);
  186. pr(sprintf(_g(" slave %s: %s"), $slavenames[$j], $tspath));
  187. }
  188. }
  189. if ($best eq '') {
  190. pr(_g("No versions available."));
  191. } else {
  192. pr(sprintf(_g("Current \`best' version is %s."), $best));
  193. }
  194. }
  195. sub query_link_group
  196. {
  197. pr(sprintf("Link: %s", $name));
  198. pr(sprintf("Status: %s", $mode));
  199. if ($best ne '') {
  200. pr(sprintf("Best: %s", $best));
  201. }
  202. $linkname = readlink("$altdir/$name");
  203. if (defined($linkname)) {
  204. pr(sprintf("Value: %s", $linkname));
  205. } elsif ($! == ENOENT) {
  206. pr("Value: none");
  207. } else {
  208. pr("Value: error");
  209. pr(sprintf("Error: %s", $!));
  210. }
  211. for (my $i = 0; $i <= $#versions; $i++) {
  212. pr("");
  213. pr(sprintf("Alternative: %s", $versions[$i]));
  214. pr(sprintf("Priority: %s", $priorities[$i]));
  215. next unless ($#slavenames >= 0);
  216. pr("Slaves:");
  217. for (my $j = 0; $j <= $#slavenames; $j++) {
  218. my $tspath = $slavepath{$i, $j};
  219. next unless length($tspath);
  220. pr(sprintf(" %s %s", $slavenames[$j], $tspath));
  221. }
  222. }
  223. }
  224. sub list_link_group
  225. {
  226. for (my $i = 0; $i <= $#versions; $i++) {
  227. pr("$versions[$i]");
  228. }
  229. }
  230. sub checked_alternative($$$)
  231. {
  232. my ($name, $link, $path) = @_;
  233. $linkname = readlink($link);
  234. if (!defined($linkname) && $! != ENOENT) {
  235. pr(sprintf(_g("warning: %s is supposed to be a symlink to %s, \n".
  236. "or nonexistent; however, readlink failed: %s"),
  237. $link, "$altdir/$name", $!))
  238. if $verbosemode > 0;
  239. } elsif (!defined($linkname) ||
  240. (defined($linkname) && $linkname ne "$altdir/$name")) {
  241. checked_rm("$link.dpkg-tmp");
  242. checked_symlink("$altdir/$name", "$link.dpkg-tmp");
  243. checked_mv("$link.dpkg-tmp", $link);
  244. }
  245. $linkname = readlink("$altdir/$name");
  246. if (defined($linkname) && $linkname eq $path) {
  247. pr(sprintf(_g("Leaving %s (%s) pointing to %s."), $name, $link, $path))
  248. if $verbosemode > 0;
  249. } else {
  250. pr(sprintf(_g("Updating %s (%s) to point to %s."), $name, $link, $path))
  251. if $verbosemode > 0;
  252. }
  253. }
  254. sub set_links($$$;$)
  255. {
  256. my ($spath, $link, $preferred, $quiet) = (@_);
  257. printf STDOUT _g("Using '%s' to provide '%s' in %s.") . "\n", $spath,
  258. $name, ($mode eq "auto" ? _g("auto mode") : _g("manual mode"))
  259. unless $quiet;
  260. checked_alternative($name, $link, $spath);
  261. checked_symlink("$spath","$altdir/$name.dpkg-tmp");
  262. checked_mv("$altdir/$name.dpkg-tmp", "$altdir/$name");
  263. # Link slaves...
  264. for (my $slnum = 0; $slnum < @slavenames; $slnum++) {
  265. my $slave = $slavenames[$slnum];
  266. if ($slavepath{$preferred, $slnum} ne '') {
  267. checked_alternative($slave, $slavelinks[$slnum],
  268. $slavepath{$preferred, $slnum});
  269. checked_symlink($slavepath{$preferred, $slnum},
  270. "$altdir/$slave.dpkg-tmp");
  271. checked_mv("$altdir/$slave.dpkg-tmp", "$altdir/$slave");
  272. } else {
  273. pr(sprintf(_g("Removing %s (%s), not appropriate with %s."), $slave,
  274. $slavelinks[$slnum], $versions[$preferred]))
  275. if $verbosemode > 0;
  276. checked_rm("$altdir/$slave");
  277. checked_rm($slavelinks[$slnum]);
  278. }
  279. }
  280. }
  281. sub check_many_actions()
  282. {
  283. return unless $action;
  284. badusage(sprintf(_g("two commands specified: %s and --%s"), $_, $action));
  285. }
  286. sub checked_rm($)
  287. {
  288. my ($f) = @_;
  289. unlink($f) || $! == ENOENT ||
  290. quit(sprintf(_g("unable to remove %s: %s"), $f, $!));
  291. }
  292. #
  293. # Main program
  294. #
  295. $| = 1;
  296. while (@ARGV) {
  297. $_= shift(@ARGV);
  298. last if m/^--$/;
  299. if (!m/^--/) {
  300. quit(sprintf(_g("unknown argument \`%s'"), $_));
  301. } elsif (m/^--help$/) {
  302. usage();
  303. exit(0);
  304. } elsif (m/^--version$/) {
  305. version();
  306. exit(0);
  307. } elsif (m/^--verbose$/) {
  308. $verbosemode= +1;
  309. } elsif (m/^--quiet$/) {
  310. $verbosemode= -1;
  311. } elsif (m/^--install$/) {
  312. check_many_actions();
  313. @ARGV >= 4 || badusage(_g("--install needs <link> <name> <path> <priority>"));
  314. ($alink,$name,$apath,$apriority,@ARGV) = @ARGV;
  315. $apriority =~ m/^[-+]?\d+/ || badusage(_g("priority must be an integer"));
  316. $action = 'install';
  317. } elsif (m/^--(remove|set)$/) {
  318. check_many_actions();
  319. @ARGV >= 2 || badusage(sprintf(_g("--%s needs <name> <path>"), $1));
  320. ($name,$apath,@ARGV) = @ARGV;
  321. $action = $1;
  322. } elsif (m/^--(display|query|auto|config|list|remove-all)$/) {
  323. check_many_actions();
  324. @ARGV || badusage(sprintf(_g("--%s needs <name>"), $1));
  325. $action = $1;
  326. $name= shift(@ARGV);
  327. } elsif (m/^--slave$/) {
  328. @ARGV >= 3 || badusage(_g("--slave needs <link> <name> <path>"));
  329. ($slink,$sname,$spath,@ARGV) = @ARGV;
  330. defined($aslavelink{$sname}) && badusage(sprintf(_g("slave name %s duplicated"), $sname));
  331. $aslavelinkcount{$slink}++ && badusage(sprintf(_g("slave link %s duplicated"), $slink));
  332. $aslavelink{$sname}= $slink;
  333. $aslavepath{$sname}= $spath;
  334. } elsif (m/^--altdir$/) {
  335. @ARGV || badusage(sprintf(_g("--%s needs a <directory> argument"), "altdir"));
  336. $altdir= shift(@ARGV);
  337. } elsif (m/^--admindir$/) {
  338. @ARGV || badusage(sprintf(_g("--%s needs a <directory> argument"), "admindir"));
  339. $admindir= shift(@ARGV);
  340. } elsif (m/^--skip-auto$/) {
  341. $skip = '--skip-auto';
  342. } elsif (m/^--all$/) {
  343. $action = 'all';
  344. } else {
  345. badusage(sprintf(_g("unknown option \`%s'"), $_));
  346. }
  347. }
  348. defined($name) && defined($aslavelink{$name}) &&
  349. badusage(sprintf(_g("name %s is both primary and slave"), $name));
  350. defined($alink) && $aslavelinkcount{$alink} &&
  351. badusage(sprintf(_g("link %s is both primary and slave"), $alink));
  352. $action ||
  353. badusage(_g("need --display, --query, --config, --set, --install, --remove, --all, --remove-all or --auto"));
  354. $action eq 'install' || !%aslavelink ||
  355. badusage(_g("--slave only allowed with --install"));
  356. if ($action eq 'all') {
  357. config_all();
  358. exit 0;
  359. }
  360. if (read_link_group()) {
  361. if ($action eq 'remove') {
  362. # FIXME: Be consistent for now with the case when we try to remove a
  363. # non-existing path from an existing link group file.
  364. exit 0;
  365. } elsif ($action ne 'install') {
  366. pr(sprintf(_g("No alternatives for %s."), $name));
  367. exit 1;
  368. }
  369. }
  370. if ($action eq 'display') {
  371. find_best_version();
  372. display_link_group();
  373. exit 0;
  374. }
  375. if ($action eq 'query') {
  376. find_best_version();
  377. query_link_group();
  378. exit 0;
  379. }
  380. if ($action eq 'list') {
  381. list_link_group();
  382. exit 0;
  383. }
  384. find_best_version();
  385. if ($action eq 'config') {
  386. config_alternatives($name);
  387. }
  388. if ($action eq 'set') {
  389. set_alternatives($name);
  390. }
  391. if (defined($linkname= readlink("$altdir/$name"))) {
  392. if ($linkname eq $best) {
  393. $state= 'expected';
  394. } elsif (defined(readlink("$altdir/$name.dpkg-tmp"))) {
  395. $state= 'expected-inprogress';
  396. } else {
  397. if (-e $linkname) {
  398. $state = 'unexpected';
  399. } else {
  400. $state = 'nonexistent';
  401. }
  402. }
  403. } elsif ($! == ENOENT) {
  404. $state= 'nonexistent';
  405. } else {
  406. $state= 'unexpected';
  407. }
  408. # Possible values for:
  409. # $mode manual, auto
  410. # $state expected, expected-inprogress, unexpected, nonexistent
  411. # $action auto, install, remove, remove-all
  412. # all independent
  413. if (($action eq 'auto') || ($state eq 'nonexistent')) {
  414. pr(sprintf(_g("Setting up automatic selection of %s."), $name))
  415. if $verbosemode > 0;
  416. checked_rm("$altdir/$name.dpkg-tmp");
  417. checked_rm("$altdir/$name");
  418. $state= 'nonexistent';
  419. $mode = 'auto';
  420. }
  421. # $mode manual, auto
  422. # $state expected, expected-inprogress, unexpected, nonexistent
  423. # $action auto, install, remove
  424. # action=auto <=> state=nonexistent
  425. if ($state eq 'unexpected' && $mode eq 'auto') {
  426. pr(sprintf(_g("%s has been changed (manually or by a script).\n" .
  427. "Switching to manual updates only."), "$altdir/$name"))
  428. if $verbosemode >= 0;
  429. $mode = 'manual';
  430. }
  431. # $mode manual, auto
  432. # $state expected, expected-inprogress, unexpected, nonexistent
  433. # $action auto, install, remove
  434. # action=auto <=> state=nonexistent
  435. # state=unexpected => mode=manual
  436. pr(sprintf(_g("Checking available versions of %s, updating links in %s ..."),
  437. $name, $altdir))
  438. if $verbosemode > 0;
  439. if ($action eq 'install') {
  440. if (defined($link) && $link ne $alink) {
  441. pr(sprintf(_g("Renaming %s link from %s to %s."), $name, $link, $alink))
  442. if $verbosemode > 0;
  443. rename_mv($link, $alink, 1) ||
  444. quit(sprintf(_g("unable to rename %s to %s: see error above"), $link, $alink));
  445. }
  446. $link= $alink;
  447. my $i;
  448. if (!defined($i= $versionnum{$apath})) {
  449. push(@versions,$apath);
  450. $versionnum{$apath}= $i= $#versions;
  451. }
  452. $priorities[$i]= $apriority;
  453. for $sname (keys %aslavelink) {
  454. my $j;
  455. if (!defined($j= $slavenum{$sname})) {
  456. push(@slavenames,$sname);
  457. $slavenum{$sname}= $j= $#slavenames;
  458. }
  459. my $oldslavelink = $slavelinks[$j];
  460. my $newslavelink = $aslavelink{$sname};
  461. $slavelinkcount{$oldslavelink}-- if defined($oldslavelink);
  462. $slavelinkcount{$newslavelink}++ &&
  463. quit(sprintf(_g("slave link name %s duplicated"), $newslavelink));
  464. if (defined($oldslavelink) && $newslavelink ne $oldslavelink) {
  465. pr(sprintf(_g("Renaming %s slave link from %s to %s."), $sname, $oldslavelink, $newslavelink))
  466. if $verbosemode > 0;
  467. rename_mv($oldslavelink, $newslavelink, 1) ||
  468. quit(sprintf(_g("unable to rename %s to %s: see error above"), $oldslavelink, $newslavelink));
  469. }
  470. $slavelinks[$j]= $newslavelink;
  471. }
  472. for (my $j = 0; $j <= $#slavenames; $j++) {
  473. $slavepath{$i,$j}= $aslavepath{$slavenames[$j]};
  474. }
  475. fill_missing_slavepaths();
  476. if (($mode eq "manual") and defined($linkname) and ($linkname eq $apath)) {
  477. # Recreate links to fix any links gone missing
  478. set_alternatives(1); # quiet
  479. }
  480. }
  481. if ($action eq 'remove') {
  482. my $hits = 0;
  483. if ($mode eq "manual" and $state ne "expected" and (map { $hits += $apath eq $_ } @versions) and $hits and $linkname eq $apath) {
  484. pr(_g("Removing manually selected alternative - switching to auto mode"));
  485. $mode = "auto";
  486. }
  487. if (defined(my $i = $versionnum{$apath})) {
  488. my $k = $#versions;
  489. $versionnum{$versions[$k]}= $i;
  490. delete $versionnum{$versions[$i]};
  491. $versions[$i]= $versions[$k]; $#versions--;
  492. $priorities[$i]= $priorities[$k]; $#priorities--;
  493. for (my $j = 0; $j <= $#slavenames; $j++) {
  494. $slavepath{$i,$j}= $slavepath{$k,$j};
  495. delete $slavepath{$k,$j};
  496. }
  497. } else {
  498. pr(sprintf(_g("Alternative %s for %s not registered, not removing."), $apath, $name))
  499. if $verbosemode > 0;
  500. }
  501. }
  502. if ($action eq 'remove-all') {
  503. $mode = "auto";
  504. my $k = $#versions;
  505. for (my $i = 0; $i <= $#versions; $i++) {
  506. $k--;
  507. delete $versionnum{$versions[$i]};
  508. $#priorities--;
  509. for (my $j = 0; $j <= $#slavenames; $j++) {
  510. $slavepath{$i,$j}= $slavepath{$k,$j};
  511. delete $slavepath{$k,$j};
  512. }
  513. }
  514. $#versions=$k;
  515. }
  516. for (my $j = 0; $j <= $#slavenames; $j++) {
  517. my $i;
  518. for ($i = 0; $i <= $#versions; $i++) {
  519. last if $slavepath{$i,$j} ne '';
  520. }
  521. if ($i > $#versions) {
  522. pr(sprintf(_g("Discarding obsolete slave link %s (%s)."), $slavenames[$j], $slavelinks[$j]))
  523. if $verbosemode > 0;
  524. checked_rm("$altdir/$slavenames[$j]");
  525. checked_rm($slavelinks[$j]);
  526. my $k = $#slavenames;
  527. $slavenum{$slavenames[$k]}= $j;
  528. delete $slavenum{$slavenames[$j]};
  529. $slavelinkcount{$slavelinks[$j]}--;
  530. $slavenames[$j]= $slavenames[$k]; $#slavenames--;
  531. $slavelinks[$j]= $slavelinks[$k]; $#slavelinks--;
  532. for (my $i = 0; $i <= $#versions; $i++) {
  533. $slavepath{$i,$j}= $slavepath{$i,$k};
  534. delete $slavepath{$i,$k};
  535. }
  536. $j--;
  537. }
  538. }
  539. if ($mode eq 'manual') {
  540. pr(sprintf(_g("Automatic updates of %s are disabled, leaving it alone."), "$altdir/$name"))
  541. if $verbosemode > 0;
  542. pr(sprintf(_g("To return to automatic updates use \`update-alternatives --auto %s'."), $name))
  543. if $verbosemode > 0;
  544. } else {
  545. if ($state eq 'expected-inprogress') {
  546. pr(sprintf(_g("Recovering from previous failed update of %s ..."), $name));
  547. checked_mv("$altdir/$name.dpkg-tmp", "$altdir/$name");
  548. $state= 'expected';
  549. }
  550. }
  551. # $mode manual, auto
  552. # $state expected, expected-inprogress, unexpected, nonexistent
  553. # $action auto, install, remove
  554. # action=auto <=> state=nonexistent
  555. # state=unexpected => mode=manual
  556. # mode=auto => state!=expected-inprogress && state!=unexpected
  557. open(AF,">$admindir/$name.dpkg-new") ||
  558. quit(sprintf(_g("unable to open %s for write: %s"), "$admindir/$name.dpkg-new", $!));
  559. paf($mode);
  560. paf($link);
  561. for (my $j = 0; $j <= $#slavenames; $j++) {
  562. paf($slavenames[$j]);
  563. paf($slavelinks[$j]);
  564. }
  565. find_best_version();
  566. paf('');
  567. for (my $i = 0; $i <= $#versions; $i++) {
  568. paf($versions[$i]);
  569. paf($priorities[$i]);
  570. for (my $j = 0; $j <= $#slavenames; $j++) {
  571. paf($slavepath{$i,$j});
  572. }
  573. }
  574. paf('');
  575. close(AF) || quit(sprintf(_g("unable to close %s: %s"), "$admindir/$name.dpkg-new", $!));
  576. # Purge alternative when nothing left
  577. if ($best eq '') {
  578. pr(sprintf(_g("Last package providing %s (%s) removed, deleting it."), $name, $link))
  579. if $verbosemode > 0;
  580. checked_rm("$altdir/$name");
  581. checked_rm("$link");
  582. checked_rm("$admindir/$name.dpkg-new");
  583. checked_rm("$admindir/$name");
  584. exit(0);
  585. }
  586. if ($mode eq 'auto') {
  587. checked_alternative($name, $link, $best);
  588. checked_rm("$altdir/$name.dpkg-tmp");
  589. checked_symlink($best, "$altdir/$name.dpkg-tmp");
  590. }
  591. checked_mv("$admindir/$name.dpkg-new", "$admindir/$name");
  592. if ($mode eq 'auto') {
  593. checked_mv("$altdir/$name.dpkg-tmp", "$altdir/$name");
  594. for (my $j = 0; $j <= $#slavenames; $j++) {
  595. $sname= $slavenames[$j];
  596. $slink= $slavelinks[$j];
  597. $spath= $slavepath{$bestnum,$j};
  598. checked_rm("$altdir/$sname.dpkg-tmp");
  599. if ($spath eq '') {
  600. pr(sprintf(_g("Removing %s (%s), not appropriate with %s."), $sname, $slink, $best))
  601. if $verbosemode > 0;
  602. checked_rm("$altdir/$sname");
  603. checked_rm("$slink");
  604. } else {
  605. checked_alternative($sname, $slink, $spath);
  606. checked_symlink("$spath", "$altdir/$sname.dpkg-tmp");
  607. checked_mv("$altdir/$sname.dpkg-tmp", "$altdir/$sname");
  608. }
  609. }
  610. }
  611. sub config_message {
  612. if ($#versions < 0) {
  613. print "\n";
  614. printf _g("There is no program which provides %s.\n".
  615. "Nothing to configure.\n"), $name;
  616. return -1;
  617. }
  618. if ($skip && $mode eq 'auto' && $best eq readlink("$altdir/$name")) {
  619. print "\n";
  620. display_link_group();
  621. return -1;
  622. }
  623. if ($#versions == 0 && $mode eq 'auto' && $best eq readlink("$altdir/$name")) {
  624. print "\n";
  625. printf _g("There is only 1 program which provides %s properly in auto mode\n".
  626. "(%s). Nothing to configure.\n"), $name, $versions[0];
  627. return -1;
  628. }
  629. print STDOUT "\n";
  630. printf(STDOUT _g("There are %s alternatives which provide \`%s'.\n\n".
  631. " Selection Alternative\n".
  632. "-----------------------------------------------\n"),
  633. $#versions+1, $name);
  634. printf STDOUT "%s 0 %s (%s)\n",
  635. ($mode eq "auto" && readlink("$altdir/$name") eq $best) ? '*' : ' ',
  636. $best, _g("auto mode");
  637. for (my $i = 0; $i <= $#versions; $i++) {
  638. printf STDOUT "%s %8s %s (%s) priority=%s\n",
  639. (readlink("$altdir/$name") eq $versions[$i] && $mode eq "manual") ? '*' : ' ',
  640. $i+1, $versions[$i], _g("manual mode"), $priorities[$i];
  641. }
  642. printf(STDOUT "\n"._g("Press enter to keep the default[*], or type selection number: "));
  643. return 0;
  644. }
  645. sub config_alternatives {
  646. my $preferred;
  647. do {
  648. return if config_message() < 0;
  649. $preferred=<STDIN>;
  650. chop($preferred);
  651. } until $preferred eq '' || $preferred>=0 && $preferred<=$#versions+1 &&
  652. ($preferred =~ m/[0-9]*/);
  653. if ($preferred ne '') {
  654. if ($preferred == 0) {
  655. $action = "auto";
  656. $mode = "auto";
  657. } else {
  658. $mode = "manual";
  659. $preferred--;
  660. my $spath = $versions[$preferred];
  661. set_links($spath, $link, $preferred);
  662. }
  663. }
  664. }
  665. sub set_alternatives {
  666. my ($quiet) = @_;
  667. $mode = "manual";
  668. # Get preferred number
  669. my $preferred = -1;
  670. for (my $i = 0; $i <= $#versions; $i++) {
  671. if($versions[$i] eq $apath) {
  672. $preferred = $i;
  673. last;
  674. }
  675. }
  676. if ($preferred == -1) {
  677. quit(sprintf(_g("Cannot find alternative `%s'."), $apath)."\n");
  678. }
  679. set_links($apath, $link, $preferred, $quiet);
  680. }
  681. sub pr
  682. {
  683. print(STDOUT "@_\n") ||
  684. quit(sprintf(_g("error writing stdout: %s"), $!));
  685. }
  686. sub paf {
  687. $_[0] =~ m/\n/ && quit(sprintf(_g("newlines prohibited in update-alternatives files (%s)"), $_[0]));
  688. print(AF "$_[0]\n") || quit(sprintf(_g("error writing stdout: %s"), $!));
  689. }
  690. sub gl {
  691. $!=0; $_= <AF>;
  692. defined($_) || quit(sprintf(_g("error or eof reading %s for %s (%s)"),
  693. "$admindir/$name", $_[0], $!));
  694. s/\n$// || badfmt(sprintf(_g("missing newline after %s"), $_[0]));
  695. $_;
  696. }
  697. sub badfmt {
  698. quit(sprintf(_g("internal error: %s corrupt: %s"), "$admindir/$name", $_[0]));
  699. }
  700. sub rename_mv {
  701. my ($source, $dest, $ignore_enoent) = @_;
  702. $ignore_enoent = 0 unless defined($ignore_enoent);
  703. return 1 if ($ignore_enoent and not -e $source);
  704. if (not rename($source, $dest)) {
  705. if (system("mv", $source, $dest) != 0) {
  706. return $ignore_enoent if $! == ENOENT;
  707. return 0;
  708. }
  709. }
  710. return 1;
  711. }
  712. sub checked_symlink {
  713. my ($filename, $linkname) = @_;
  714. symlink($filename, $linkname) ||
  715. quit(sprintf(_g("unable to make %s a symlink to %s: %s"), $linkname, $filename, $!));
  716. }
  717. sub checked_mv {
  718. my ($source, $dest) = @_;
  719. rename_mv($source, $dest) ||
  720. quit(sprintf(_g("unable to install %s as %s: %s"), $source, $dest, $!));
  721. }
  722. sub config_all {
  723. opendir ADMINDIR, $admindir or die sprintf(_g("Serious problem: %s"), $!);
  724. my @filenames = grep !/^\.\.?$/, readdir ADMINDIR;
  725. close ADMINDIR;
  726. foreach my $name (@filenames) {
  727. system "$0 $skip --config $name";
  728. exit $? if $?;
  729. }
  730. }
  731. exit(0);
  732. # vim: nowrap ts=8 sw=4