dpkg-divert.pl 10 KB

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