update-alternatives.pl 16 KB

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