update-alternatives.pl 17 KB

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