dpkg-divert.pl 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. #!/usr/bin/perl --
  2. use strict;
  3. use warnings;
  4. my $version = '1.0.11'; # This line modified by Makefile
  5. my $admindir = "/var/lib/dpkg"; # This line modified by Makefile
  6. my $dpkglibdir = "../utils"; # This line modified by Makefile
  7. ($0) = $0 =~ m:.*/(.+):;
  8. push (@INC, $dpkglibdir);
  9. require 'dpkg-gettext.pl';
  10. textdomain("dpkg");
  11. my $enoent = `$dpkglibdir/enoent` || die sprintf(_g("Cannot get ENOENT value from %s: %s"), "$dpkglibdir/enoent", $!);
  12. sub ENOENT { $enoent; }
  13. sub version {
  14. printf _g("Debian %s version %s.\n"), $0, $version;
  15. printf _g("
  16. Copyright (C) 1995 Ian Jackson.
  17. Copyright (C) 2000,2001 Wichert Akkerman.");
  18. printf _g("
  19. This is free software; see the GNU General Public Licence version 2 or
  20. later for copying conditions. There is NO warranty.
  21. ");
  22. }
  23. sub usage {
  24. printf(_g(
  25. "Usage: %s [<option> ...] <command>
  26. Commands:
  27. [--add] <file> add a diversion.
  28. --remove <file> remove the diversion.
  29. --list [<glob-pattern>] show file diversions.
  30. --truename <file> return the diverted file.
  31. Options:
  32. --package <package> name of the package whose copy of <file> will not
  33. be diverted.
  34. --local all packages' versions are diverted.
  35. --divert <divert-to> the name used by other packages' versions.
  36. --rename actually move the file aside (or back).
  37. --admindir <directory> set the directory with the diversions file.
  38. --test don't do anything, just demonstrate.
  39. --quiet quiet operation, minimal output.
  40. --help show this help message.
  41. --version show the version.
  42. When adding, default is --local and --divert <original>.distrib.
  43. When removing, --package or --local and --divert must match if specified.
  44. Package preinst/postrm scripts should always specify --package and --divert.
  45. "), $0);
  46. }
  47. my $testmode = 0;
  48. my $dorename = 0;
  49. my $verbose = 1;
  50. my $mode = '';
  51. my $package = undef;
  52. my $divertto = undef;
  53. my @contest;
  54. my @altname;
  55. my @package;
  56. my $file;
  57. $|=1;
  58. # FIXME: those should be local.
  59. my ($rsrc, $rdest);
  60. my (@ssrc, @sdest);
  61. sub checkmanymodes {
  62. return unless $mode;
  63. badusage(sprintf(_g("two commands specified: %s and --%s"), $_, $mode));
  64. }
  65. while (@ARGV) {
  66. $_= shift(@ARGV);
  67. last if m/^--$/;
  68. if (!m/^-/) {
  69. unshift(@ARGV,$_); last;
  70. } elsif (m/^--help$/) {
  71. &usage; exit(0);
  72. } elsif (m/^--version$/) {
  73. &version; exit(0);
  74. } elsif (m/^--test$/) {
  75. $testmode= 1;
  76. } elsif (m/^--rename$/) {
  77. $dorename= 1;
  78. } elsif (m/^--quiet$/) {
  79. $verbose= 0;
  80. } elsif (m/^--local$/) {
  81. $package= ':';
  82. } elsif (m/^--add$/) {
  83. &checkmanymodes;
  84. $mode= 'add';
  85. } elsif (m/^--remove$/) {
  86. &checkmanymodes;
  87. $mode= 'remove';
  88. } elsif (m/^--list$/) {
  89. &checkmanymodes;
  90. $mode= 'list';
  91. } elsif (m/^--truename$/) {
  92. &checkmanymodes;
  93. $mode= 'truename';
  94. } elsif (m/^--divert$/) {
  95. @ARGV || &badusage(sprintf(_g("--%s needs a divert-to argument"), "divert"));
  96. $divertto= shift(@ARGV);
  97. $divertto =~ m/\n/ && &badusage(_g("divert-to may not contain newlines"));
  98. } elsif (m/^--package$/) {
  99. @ARGV || &badusage(sprintf(_g("--%s needs a <package> argument"), "package"));
  100. $package= shift(@ARGV);
  101. $package =~ m/\n/ && &badusage(_g("package may not contain newlines"));
  102. } elsif (m/^--admindir$/) {
  103. @ARGV || &badusage(sprintf(_g("--%s needs a <directory> argument"), "admindir"));
  104. $admindir= shift(@ARGV);
  105. } else {
  106. &badusage(sprintf(_g("unknown option \`%s'"), $_));
  107. }
  108. }
  109. $mode='add' unless $mode;
  110. open(O,"$admindir/diversions") || &quit(sprintf(_g("cannot open diversions: %s"), $!));
  111. while(<O>) {
  112. s/\n$//; push(@contest,$_);
  113. $_=<O>; s/\n$// || &badfmt(_g("missing altname"));
  114. push(@altname,$_);
  115. $_=<O>; s/\n$// || &badfmt(_g("missing package"));
  116. push(@package,$_);
  117. }
  118. close(O);
  119. if ($mode eq 'add') {
  120. @ARGV == 1 || &badusage(sprintf(_g("--%s needs a single argument"), "add"));
  121. $file= $ARGV[0];
  122. $file =~ m#^/# || &badusage(sprintf(_g("filename \"%s\" is not absolute"), $file));
  123. $file =~ m/\n/ && &badusage(_g("file may not contain newlines"));
  124. -d $file && &badusage(_g("Cannot divert directories"));
  125. $divertto= "$file.distrib" unless defined($divertto);
  126. $divertto =~ m#^/# || &badusage(sprintf(_g("filename \"%s\" is not absolute"), $divertto));
  127. $package= ':' unless defined($package);
  128. for (my $i = 0; $i <= $#contest; $i++) {
  129. if ($contest[$i] eq $file || $altname[$i] eq $file ||
  130. $contest[$i] eq $divertto || $altname[$i] eq $divertto) {
  131. if ($contest[$i] eq $file && $altname[$i] eq $divertto &&
  132. $package[$i] eq $package) {
  133. printf(_g("Leaving \`%s'")."\n", &infon($i)) if $verbose > 0;
  134. exit(0);
  135. }
  136. &quit(sprintf(_g("\`%s' clashes with \`%s'"), &infoa, &infon($i)));
  137. }
  138. }
  139. push(@contest,$file);
  140. push(@altname,$divertto);
  141. push(@package,$package);
  142. printf(_g("Adding \`%s'")."\n", &infon($#contest)) if $verbose > 0;
  143. &checkrename($file,$divertto);
  144. &save;
  145. &dorename($file,$divertto);
  146. exit(0);
  147. } elsif ($mode eq 'remove') {
  148. @ARGV == 1 || &badusage(sprintf(_g("--%s needs a single argument"), "remove"));
  149. $file= $ARGV[0];
  150. for (my $i = 0; $i <= $#contest; $i++) {
  151. next unless $file eq $contest[$i];
  152. &quit(sprintf(_g("mismatch on divert-to\n when removing \`%s'\n found \`%s'"), &infoa, &infon($i)))
  153. if defined($divertto) && $altname[$i] ne $divertto;
  154. &quit(sprintf(_g("mismatch on package\n when removing \`%s'\n found \`%s'"), &infoa, &infon($i)))
  155. if defined($package) && $package[$i] ne $package;
  156. printf(_g("Removing \`%s'")."\n", &infon($i)) if $verbose > 0;
  157. my $orgfile = $contest[$i];
  158. my $orgdivertto = $altname[$i];
  159. @contest= (($i > 0 ? @contest[0..$i-1] : ()),
  160. ($i < $#contest ? @contest[$i+1..$#contest] : ()));
  161. @altname= (($i > 0 ? @altname[0..$i-1] : ()),
  162. ($i < $#altname ? @altname[$i+1..$#altname] : ()));
  163. @package= (($i > 0 ? @package[0..$i-1] : ()),
  164. ($i < $#package ? @package[$i+1..$#package] : ()));
  165. $dorename = 1;
  166. &checkrename($orgdivertto,$orgfile);
  167. &dorename($orgdivertto,$orgfile);
  168. &save;
  169. exit(0);
  170. }
  171. printf(_g("No diversion \`%s', none removed")."\n", &infoa) if $verbose > 0;
  172. exit(0);
  173. } elsif ($mode eq 'list') {
  174. my @list;
  175. my @ilist = @ARGV ? @ARGV : ('*');
  176. while (defined($_=shift(@ilist))) {
  177. s/\W/\\$&/g;
  178. s/\\\?/./g;
  179. s/\\\*/.*/g;
  180. push(@list,"^$_\$");
  181. }
  182. my $pat = join('|', @list);
  183. for (my $i = 0; $i <= $#contest; $i++) {
  184. next unless ($contest[$i] =~ m/$pat/o ||
  185. $altname[$i] =~ m/$pat/o ||
  186. $package[$i] =~ m/$pat/o);
  187. print &infon($i),"\n";
  188. }
  189. exit(0);
  190. } elsif ($mode eq 'truename') {
  191. @ARGV == 1 || &badusage(sprintf(_g("--%s needs a single argument"), "truename"));
  192. $file= $ARGV[0];
  193. for (my $i = 0; $i <= $#contest; $i++) {
  194. next unless $file eq $contest[$i];
  195. print $altname[$i], "\n";
  196. exit(0);
  197. }
  198. print $file, "\n";
  199. exit(0);
  200. } else {
  201. &quit(sprintf(_g("internal error - bad mode \`%s'"), $mode));
  202. }
  203. sub infol {
  204. return (($_[2] eq ':' ? "local " : length($_[2]) ? "" : "any ").
  205. "diversion of $_[0]".
  206. (length($_[1]) ? " to $_[1]" : "").
  207. (length($_[2]) && $_[2] ne ':' ? " by $_[2]" : ""));
  208. }
  209. sub checkrename {
  210. return unless $dorename;
  211. ($rsrc,$rdest) = @_;
  212. (@ssrc= lstat($rsrc)) || $! == &ENOENT ||
  213. &quit(sprintf(_g("cannot stat old name \`%s': %s"), $rsrc, $!));
  214. (@sdest= lstat($rdest)) || $! == &ENOENT ||
  215. &quit(sprintf(_g("cannot stat new name \`%s': %s"), $rdest, $!));
  216. # Unfortunately we have to check for write access in both
  217. # places, just having +w is not enough, since people do
  218. # mount things RO, and we need to fail before we start
  219. # mucking around with things. So we open a file with the
  220. # same name as the diversions but with an extension that
  221. # (hopefully) wont overwrite anything. If it succeeds, we
  222. # assume a writable filesystem.
  223. foreach my $file ($rsrc, $rdest) {
  224. if (open (TMP, ">> ${file}.dpkg-devert.tmp")) {
  225. close TMP;
  226. unlink ("${file}.dpkg-devert.tmp");
  227. } elsif ($! == ENOENT) {
  228. $dorename = !$dorename;
  229. } else {
  230. &quit(sprintf(_g("error checking \`%s': %s"), $file, $!));
  231. }
  232. }
  233. if (@ssrc && @sdest &&
  234. !($ssrc[0] == $sdest[0] && $ssrc[1] == $sdest[1])) {
  235. &quit(sprintf(_g("rename involves overwriting \`%s' with\n".
  236. " different file \`%s', not allowed"), $rdest, $rsrc));
  237. }
  238. }
  239. sub dorename {
  240. return unless $dorename;
  241. return if $testmode;
  242. if (@ssrc) {
  243. if (@sdest) {
  244. unlink($rsrc) || &quit(sprintf(_g("rename: remove duplicate old link \`%s': %s"), $rsrc, $!));
  245. } else {
  246. rename($rsrc,$rdest) || &quit(sprintf(_g("rename: rename \`%s' to \`%s': %s"), $rsrc, $rdest, $!));
  247. }
  248. }
  249. }
  250. sub save {
  251. return if $testmode;
  252. open(N,"> $admindir/diversions-new") || &quit(sprintf(_g("create diversions-new: %s"), $!));
  253. chmod 0644, "$admindir/diversions-new";
  254. for (my $i = 0; $i <= $#contest; $i++) {
  255. print(N "$contest[$i]\n$altname[$i]\n$package[$i]\n")
  256. || &quit(sprintf(_g("write diversions-new: %s"), $!));
  257. }
  258. close(N) || &quit(sprintf(_g("close diversions-new: %s"), $!));
  259. unlink("$admindir/diversions-old") ||
  260. $! == &ENOENT || &quit(sprintf(_g("remove old diversions-old: %s"), $!));
  261. link("$admindir/diversions","$admindir/diversions-old") ||
  262. $! == &ENOENT || &quit(sprintf(_g("create new diversions-old: %s"), $!));
  263. rename("$admindir/diversions-new","$admindir/diversions")
  264. || &quit(sprintf(_g("install new diversions: %s"), $!));
  265. }
  266. sub infoa { &infol($file,$divertto,$package); }
  267. sub infon
  268. {
  269. my $i = shift;
  270. &infol($contest[$i], $altname[$i], $package[$i]);
  271. }
  272. sub quit
  273. {
  274. printf STDERR "%s: %s\n", $0, "@_";
  275. exit(2);
  276. }
  277. sub badusage
  278. {
  279. printf STDERR "%s: %s\n\n", $0, "@_";
  280. &usage;
  281. exit(2);
  282. }
  283. sub badfmt { &quit(sprintf(_g("internal error: %s corrupt: %s"), "$admindir/diversions", $_[0])); }