dpkg-divert.pl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. #!/usr/bin/perl
  2. #
  3. # dpkg-divert
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. BEGIN { # Work-around for bug #479711 in perl
  18. $ENV{PERL_DL_NONLAZY} = 1;
  19. }
  20. use strict;
  21. use warnings;
  22. use POSIX qw(:errno_h);
  23. use Dpkg;
  24. use Dpkg::Gettext;
  25. textdomain("dpkg");
  26. sub version {
  27. printf _g("Debian %s version %s.\n"), $progname, $version;
  28. printf _g("
  29. Copyright (C) 1995 Ian Jackson.
  30. Copyright (C) 2000,2001 Wichert Akkerman.");
  31. printf _g("
  32. This is free software; see the GNU General Public Licence version 2 or
  33. later for copying conditions. There is NO warranty.
  34. ");
  35. }
  36. sub usage {
  37. printf(_g(
  38. "Usage: %s [<option> ...] <command>
  39. Commands:
  40. [--add] <file> add a diversion.
  41. --remove <file> remove the diversion.
  42. --list [<glob-pattern>] show file diversions.
  43. --listpackage <file> show what package diverts the file.
  44. --truename <file> return the diverted file.
  45. Options:
  46. --package <package> name of the package whose copy of <file> will not
  47. be diverted.
  48. --local all packages' versions are diverted.
  49. --divert <divert-to> the name used by other packages' versions.
  50. --rename actually move the file aside (or back).
  51. --admindir <directory> set the directory with the diversions file.
  52. --test don't do anything, just demonstrate.
  53. --quiet quiet operation, minimal output.
  54. --help show this help message.
  55. --version show the version.
  56. When adding, default is --local and --divert <original>.distrib.
  57. When removing, --package or --local and --divert must match if specified.
  58. Package preinst/postrm scripts should always specify --package and --divert.
  59. "), $progname);
  60. }
  61. my $testmode = 0;
  62. my $dorename = 0;
  63. my $verbose = 1;
  64. my $mode = '';
  65. my $package = undef;
  66. my $divertto = undef;
  67. my @contest;
  68. my @altname;
  69. my @package;
  70. my $file;
  71. $|=1;
  72. # FIXME: those should be local.
  73. my ($rsrc, $rdest);
  74. my (@ssrc, @sdest);
  75. sub checkmanymodes {
  76. return unless $mode;
  77. badusage(sprintf(_g("two commands specified: %s and --%s"), $_, $mode));
  78. }
  79. while (@ARGV) {
  80. $_= shift(@ARGV);
  81. last if m/^--$/;
  82. if (!m/^-/) {
  83. unshift(@ARGV,$_); last;
  84. } elsif (m/^--help$/) {
  85. usage();
  86. exit(0);
  87. } elsif (m/^--version$/) {
  88. version();
  89. exit(0);
  90. } elsif (m/^--test$/) {
  91. $testmode= 1;
  92. } elsif (m/^--rename$/) {
  93. $dorename= 1;
  94. } elsif (m/^--quiet$/) {
  95. $verbose= 0;
  96. } elsif (m/^--local$/) {
  97. $package= ':';
  98. } elsif (m/^--add$/) {
  99. checkmanymodes();
  100. $mode= 'add';
  101. } elsif (m/^--remove$/) {
  102. checkmanymodes();
  103. $mode= 'remove';
  104. } elsif (m/^--list$/) {
  105. checkmanymodes();
  106. $mode= 'list';
  107. } elsif (m/^--listpackage$/) {
  108. checkmanymodes();
  109. $mode= 'listpackage';
  110. } elsif (m/^--truename$/) {
  111. checkmanymodes();
  112. $mode= 'truename';
  113. } elsif (m/^--divert$/) {
  114. @ARGV || badusage(sprintf(_g("--%s needs a divert-to argument"), "divert"));
  115. $divertto= shift(@ARGV);
  116. $divertto =~ m/\n/ && badusage(_g("divert-to may not contain newlines"));
  117. } elsif (m/^--package$/) {
  118. @ARGV || badusage(sprintf(_g("--%s needs a <package> argument"), "package"));
  119. $package= shift(@ARGV);
  120. $package =~ m/\n/ && badusage(_g("package may not contain newlines"));
  121. } elsif (m/^--admindir$/) {
  122. @ARGV || badusage(sprintf(_g("--%s needs a <directory> argument"), "admindir"));
  123. $admindir= shift(@ARGV);
  124. } else {
  125. badusage(sprintf(_g("unknown option \`%s'"), $_));
  126. }
  127. }
  128. $mode='add' unless $mode;
  129. open(O, "$admindir/diversions") || quit(sprintf(_g("cannot open diversions: %s"), $!));
  130. while(<O>) {
  131. s/\n$//; push(@contest,$_);
  132. $_ = <O>;
  133. s/\n$// || badfmt(_g("missing altname"));
  134. push(@altname,$_);
  135. $_ = <O>;
  136. s/\n$// || badfmt(_g("missing package"));
  137. push(@package,$_);
  138. }
  139. close(O);
  140. if ($mode eq 'add') {
  141. @ARGV == 1 || badusage(sprintf(_g("--%s needs a single argument"), "add"));
  142. $file= $ARGV[0];
  143. $file =~ m#^/# || badusage(sprintf(_g("filename \"%s\" is not absolute"), $file));
  144. $file =~ m/\n/ && badusage(_g("file may not contain newlines"));
  145. -d $file && badusage(_g("Cannot divert directories"));
  146. $divertto= "$file.distrib" unless defined($divertto);
  147. $divertto =~ m#^/# || badusage(sprintf(_g("filename \"%s\" is not absolute"), $divertto));
  148. $file ne $divertto || badusage(sprintf(_g("cannot divert file '%s' to itself"), $file));
  149. $package= ':' unless defined($package);
  150. for (my $i = 0; $i <= $#contest; $i++) {
  151. if ($contest[$i] eq $file || $altname[$i] eq $file ||
  152. $contest[$i] eq $divertto || $altname[$i] eq $divertto) {
  153. if ($contest[$i] eq $file && $altname[$i] eq $divertto &&
  154. $package[$i] eq $package) {
  155. printf(_g("Leaving \`%s'")."\n", infon($i)) if $verbose > 0;
  156. exit(0);
  157. }
  158. quit(sprintf(_g("\`%s' clashes with \`%s'"), infoa(), infon($i)));
  159. }
  160. }
  161. push(@contest,$file);
  162. push(@altname,$divertto);
  163. push(@package,$package);
  164. printf(_g("Adding \`%s'")."\n", infon($#contest)) if $verbose > 0;
  165. checkrename($file, $divertto);
  166. save();
  167. dorename($file, $divertto);
  168. exit(0);
  169. } elsif ($mode eq 'remove') {
  170. @ARGV == 1 || badusage(sprintf(_g("--%s needs a single argument"), "remove"));
  171. $file= $ARGV[0];
  172. for (my $i = 0; $i <= $#contest; $i++) {
  173. next unless $file eq $contest[$i];
  174. quit(sprintf(_g("mismatch on divert-to\n when removing \`%s'\n found \`%s'"), infoa(), infon($i)))
  175. if defined($divertto) && $altname[$i] ne $divertto;
  176. quit(sprintf(_g("mismatch on package\n when removing \`%s'\n found \`%s'"), infoa(), infon($i)))
  177. if defined($package) && $package[$i] ne $package;
  178. printf(_g("Removing \`%s'")."\n", infon($i)) if $verbose > 0;
  179. my $orgfile = $contest[$i];
  180. my $orgdivertto = $altname[$i];
  181. @contest= (($i > 0 ? @contest[0..$i-1] : ()),
  182. ($i < $#contest ? @contest[$i+1..$#contest] : ()));
  183. @altname= (($i > 0 ? @altname[0..$i-1] : ()),
  184. ($i < $#altname ? @altname[$i+1..$#altname] : ()));
  185. @package= (($i > 0 ? @package[0..$i-1] : ()),
  186. ($i < $#package ? @package[$i+1..$#package] : ()));
  187. checkrename($orgdivertto, $orgfile);
  188. dorename($orgdivertto, $orgfile);
  189. save();
  190. exit(0);
  191. }
  192. printf(_g("No diversion \`%s', none removed")."\n", infoa())
  193. if $verbose > 0;
  194. exit(0);
  195. } elsif ($mode eq 'list') {
  196. my @list;
  197. my @ilist = @ARGV ? @ARGV : ('*');
  198. while (defined($_=shift(@ilist))) {
  199. s/\W/\\$&/g;
  200. s/\\\?/./g;
  201. s/\\\*/.*/g;
  202. push(@list,"^$_\$");
  203. }
  204. my $pat = join('|', @list);
  205. for (my $i = 0; $i <= $#contest; $i++) {
  206. next unless ($contest[$i] =~ m/$pat/o ||
  207. $altname[$i] =~ m/$pat/o ||
  208. $package[$i] =~ m/$pat/o);
  209. print infon($i), "\n";
  210. }
  211. exit(0);
  212. } elsif ($mode eq 'truename') {
  213. @ARGV == 1 || badusage(sprintf(_g("--%s needs a single argument"), "truename"));
  214. $file= $ARGV[0];
  215. for (my $i = 0; $i <= $#contest; $i++) {
  216. next unless $file eq $contest[$i];
  217. print $altname[$i], "\n";
  218. exit(0);
  219. }
  220. print $file, "\n";
  221. exit(0);
  222. } elsif ($mode eq 'listpackage') {
  223. @ARGV == 1 || badusage(sprintf(_g("--%s needs a single argument"), $mode));
  224. $file= $ARGV[0];
  225. for (my $i = 0; $i <= $#contest; $i++) {
  226. next unless $file eq $contest[$i];
  227. if ($package[$i] eq ':') {
  228. # indicate package is local using something not in package namespace
  229. print "LOCAL\n";
  230. } else {
  231. print $package[$i], "\n";
  232. }
  233. exit(0);
  234. }
  235. # print nothing if file is not diverted
  236. exit(0);
  237. } else {
  238. quit(sprintf(_g("internal error - bad mode \`%s'"), $mode));
  239. }
  240. sub infol {
  241. return ((defined($_[2]) ? ($_[2] eq ':' ? "local " : "") : "any ").
  242. "diversion of $_[0]".
  243. (defined($_[1]) ? " to $_[1]" : "").
  244. (defined($_[2]) && $_[2] ne ':' ? " by $_[2]" : ""));
  245. }
  246. sub checkrename {
  247. return unless $dorename;
  248. ($rsrc,$rdest) = @_;
  249. (@ssrc = lstat($rsrc)) || $! == ENOENT ||
  250. quit(sprintf(_g("cannot stat old name \`%s': %s"), $rsrc, $!));
  251. (@sdest = lstat($rdest)) || $! == ENOENT ||
  252. quit(sprintf(_g("cannot stat new name \`%s': %s"), $rdest, $!));
  253. # Unfortunately we have to check for write access in both
  254. # places, just having +w is not enough, since people do
  255. # mount things RO, and we need to fail before we start
  256. # mucking around with things. So we open a file with the
  257. # same name as the diversions but with an extension that
  258. # (hopefully) wont overwrite anything. If it succeeds, we
  259. # assume a writable filesystem.
  260. if (open (TMP, ">>", "${rsrc}.dpkg-devert.tmp")) {
  261. close TMP;
  262. unlink ("${rsrc}.dpkg-devert.tmp");
  263. } elsif ($! == ENOENT) {
  264. $dorename = !$dorename;
  265. # If the source file is not present and we are not going to do the
  266. # rename anyway there's no point in checking the target.
  267. return;
  268. } else {
  269. quit(sprintf(_g("error checking \`%s': %s"), $rsrc, $!));
  270. }
  271. if (open (TMP, ">>", "${rdest}.dpkg-devert.tmp")) {
  272. close TMP;
  273. unlink ("${rdest}.dpkg-devert.tmp");
  274. } else {
  275. quit(sprintf(_g("error checking \`%s': %s"), $rdest, $!));
  276. }
  277. if (@ssrc && @sdest &&
  278. !($ssrc[0] == $sdest[0] && $ssrc[1] == $sdest[1])) {
  279. quit(sprintf(_g("rename involves overwriting \`%s' with\n".
  280. " different file \`%s', not allowed"), $rdest, $rsrc));
  281. }
  282. }
  283. sub rename_mv($$)
  284. {
  285. return (rename($_[0], $_[1]) || (system(("mv", $_[0], $_[1])) == 0));
  286. }
  287. sub dorename {
  288. return unless $dorename;
  289. return if $testmode;
  290. if (@ssrc) {
  291. if (@sdest) {
  292. unlink($rsrc) || quit(sprintf(_g("rename: remove duplicate old link \`%s': %s"), $rsrc, $!));
  293. } else {
  294. rename_mv($rsrc, $rdest) ||
  295. quit(sprintf(_g("rename: rename \`%s' to \`%s': %s"), $rsrc, $rdest, $!));
  296. }
  297. }
  298. }
  299. sub save {
  300. return if $testmode;
  301. open(N, "> $admindir/diversions-new") || quit(sprintf(_g("create diversions-new: %s"), $!));
  302. chmod 0644, "$admindir/diversions-new";
  303. for (my $i = 0; $i <= $#contest; $i++) {
  304. print(N "$contest[$i]\n$altname[$i]\n$package[$i]\n")
  305. || quit(sprintf(_g("write diversions-new: %s"), $!));
  306. }
  307. close(N) || quit(sprintf(_g("close diversions-new: %s"), $!));
  308. unlink("$admindir/diversions-old") ||
  309. $! == ENOENT || quit(sprintf(_g("remove old diversions-old: %s"), $!));
  310. link("$admindir/diversions","$admindir/diversions-old") ||
  311. $! == ENOENT || quit(sprintf(_g("create new diversions-old: %s"), $!));
  312. rename("$admindir/diversions-new","$admindir/diversions")
  313. || quit(sprintf(_g("install new diversions: %s"), $!));
  314. }
  315. sub infoa
  316. {
  317. infol($file, $divertto, $package);
  318. }
  319. sub infon
  320. {
  321. my $i = shift;
  322. infol($contest[$i], $altname[$i], $package[$i]);
  323. }
  324. sub quit
  325. {
  326. printf STDERR "%s: %s\n", $progname, "@_";
  327. exit(2);
  328. }
  329. sub badusage
  330. {
  331. printf STDERR "%s: %s\n\n", $progname, "@_";
  332. usage();
  333. exit(2);
  334. }
  335. sub badfmt
  336. {
  337. quit(sprintf(_g("internal error: %s corrupt: %s"), "$admindir/diversions", $_[0]));
  338. }