dpkg-divert.pl 9.5 KB

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