proot 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111
  1. #! /usr/bin/perl
  2. # ex:ts=8 sw=4:
  3. # $OpenBSD: proot,v 1.57 2017/04/30 20:56:19 espie Exp $
  4. #
  5. # Copyright (c) 2016 Marc Espie <espie@openbsd.org>
  6. #
  7. # Permission to use, copy, modify, and distribute this software for any
  8. # purpose with or without fee is hereby granted, provided that the above
  9. # copyright notice and this permission notice appear in all copies.
  10. #
  11. # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  12. # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  13. # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  14. # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  15. # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  16. # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  17. # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  18. use strict;
  19. use warnings;
  20. my $ports1;
  21. use FindBin;
  22. BEGIN {
  23. $ports1 = $ENV{PORTSDIR} || '/usr/ports';
  24. }
  25. use lib ("$ports1/infrastructure/lib", "$FindBin::Bin/../lib");
  26. use OpenBSD::Getopt;
  27. use OpenBSD::Paths;
  28. use OpenBSD::State;
  29. use OpenBSD::ProgressMeter;
  30. use DPB::User;
  31. package MyUser;
  32. our @ISA = qw(DPB::User);
  33. use File::Basename;
  34. use File::Find;
  35. sub new
  36. {
  37. my $class = shift;
  38. return $class->SUPER::new(@_)->enforce_local;
  39. }
  40. sub mkpath
  41. {
  42. my ($self, $dir) = @_;
  43. my $p = dirname($dir);
  44. if (! -d $p) {
  45. $self->mkpath($p);
  46. }
  47. $self->mkdir($dir);
  48. }
  49. sub mkRpath
  50. {
  51. my ($self, $dir, @except) = @_;
  52. my %dont = map {($_, 1)} @except;
  53. if (-d $dir) {
  54. find(sub {
  55. if ($dont{$_}) {
  56. $File::Find::prune = 1;
  57. return;
  58. }
  59. $self->own($_);
  60. }, $dir);
  61. } else {
  62. $self->mkpath($dir);
  63. }
  64. }
  65. sub own
  66. {
  67. my ($self, $path) = @_;
  68. chown $self->{uid}, $self->{gid}, $path;
  69. }
  70. sub mkdir
  71. {
  72. my ($self, $dir) = @_;
  73. mkdir($dir);
  74. $self->own($dir);
  75. }
  76. package MyState;
  77. our @ISA = (qw(OpenBSD::State));
  78. use File::Copy;
  79. use File::Spec;
  80. use File::Basename;
  81. use File::Find;
  82. use File::Path qw(remove_tree);
  83. sub set_chroot
  84. {
  85. my $state = shift;
  86. for my $item (keys %{$state->{dontrm}}) {
  87. $state->add_chroot_preserved($item);
  88. }
  89. }
  90. sub fatal_error
  91. {
  92. my $self = shift;
  93. $self->errsay(@_);
  94. $self->{error} = 1;
  95. }
  96. sub add_chroot_preserved
  97. {
  98. my ($state, $item) = @_;
  99. $state->{chroot_dont}{$state->chroot($item)} = 1;
  100. }
  101. sub add_preserved
  102. {
  103. my ($state, $item) = @_;
  104. $state->{dontrm}{$item} = 1;
  105. if (exists $state->{chroot_dont}) {
  106. $state->add_chroot_preserved($item);
  107. }
  108. }
  109. sub canonical_dir
  110. {
  111. my ($state, $path) = @_;
  112. $path = File::Spec->canonpath($path);
  113. $path =~ s,/+$,,;
  114. return $path;
  115. }
  116. sub do_parm
  117. {
  118. my ($state, $k, $v) = @_;
  119. my $opts = {
  120. chroot => sub {
  121. $state->{chroot} = File::Spec->canonpath($v);
  122. }, srcroot => sub {
  123. $state->{srcroot} = File::Spec->canonpath($v);
  124. }, PORT_USER => sub {
  125. $state->{portuser} = MyUser->new($v);
  126. }, LOG_USER => sub {
  127. $state->{loguser} = MyUser->new($v);
  128. }, FETCH_USER => sub {
  129. $state->{fetchuser} = MyUser->new($v);
  130. }, BUILD_USER => sub {
  131. $state->{builduser} = MyUser->new($v);
  132. }, PORTSDIR => sub {
  133. $state->{PORTSDIR} = File::Spec->canonpath($v);
  134. }, DISTDIR => sub {
  135. $state->{DISTDIR} = File::Spec->canonpath($v);
  136. }, PACKAGE_REPOSITORY => sub {
  137. $state->{PACKAGE_REPOSITORY} = File::Spec->canonpath($v);
  138. }, PLIST_REPOSITORY => sub {
  139. $state->{PLIST_REPOSITORY} = File::Spec->canonpath($v);
  140. }, NFSDIR => sub {
  141. $state->{DISTDIR} = File::Spec->canonpath("$v/distfiles");
  142. $state->{PACKAGE_REPOSITORY} =
  143. File::Spec->canonpath("$v/packages");
  144. $state->{PLIST_REPOSITORY} =
  145. File::Spec->canonpath("$v/plist");
  146. }, LOCALDIR => sub {
  147. $state->{WRKOBJDIR} = File::Spec->canonpath("$v/pobj");
  148. $state->{LOCKDIR} = File::Spec->canonpath("$v/locks");
  149. }, preserve => sub {
  150. $state->add_preserved($v);
  151. }, portscvs => sub {
  152. $state->{portscvs} = $v;
  153. }, ignorecvs => sub {
  154. $state->{ignorecvs} = 1;
  155. }, chown_all => sub {
  156. $state->{chown_all} = $v;
  157. }, WRKOBJDIR => sub {
  158. $state->{WRKOBJDIR} = File::Spec->canonpath($v);
  159. }, DISTDIR => sub {
  160. $state->{DISTDIR} = File::Spec->canonpath($v);
  161. }, LOCKDIR => sub {
  162. $state->{LOCKDIR} = File::Spec->canonpath($v);
  163. }, snapshot => sub {
  164. $state->{snapshot} = $v;
  165. }, actions => sub {
  166. if ($v eq 'none') {
  167. delete $state->{actions};
  168. }
  169. if ($v =~ m/^-(.*)/) {
  170. $state->{actions}{$1} = 0;
  171. } else {
  172. $state->{actions}{$v} = 1;
  173. }
  174. }, sets => sub {
  175. if ($v =~ m/^-(.*)/) {
  176. $state->{sets}{$1} = 0;
  177. } else {
  178. $state->{sets}{$v} = 1;
  179. }
  180. }, extra => sub {
  181. $state->{known}{$v} = 1;
  182. }, mkconf_tail => sub {
  183. $state->{mkconf_tail} = $v;
  184. }, mkconf_lines => sub {
  185. push(@{$state->{mkconf_lines}}, $v);
  186. }
  187. };
  188. if (defined $opts->{$k}) {
  189. &{$opts->{$k}};
  190. } else {
  191. $state->fatal_error("Unknown options #1=#2", $k, $v);
  192. }
  193. }
  194. sub read_configfile
  195. {
  196. my ($state, $name) = @_;
  197. open(my $f, '<', $name) or die "Can't open config $name: $!";
  198. my ($k, $v);
  199. while (<$f>) {
  200. chomp;
  201. my $line = $_;
  202. s/\s*\#.*//;
  203. next if /^$/;
  204. if (m/^(.*?)\s*\=\s*(.*)$/) {
  205. ($k, $v) = ($1, $2);
  206. } elsif (m/^\s+(.*)$/) {
  207. $v = $1;
  208. } else {
  209. $state->fatal_error("Error in config file #1: #2",
  210. $., $line);
  211. next;
  212. }
  213. $state->do_parm($k, $v);
  214. }
  215. close($f);
  216. }
  217. sub handle_options
  218. {
  219. my $state = shift;
  220. # default shit
  221. $state->{actions} = {
  222. check_mount => 1,
  223. devs => 1,
  224. ldconfig => 1,
  225. ports_subdirs => 1,
  226. unpopulate_light => 1,
  227. resolv => 1,
  228. write_mk => 1,
  229. };
  230. $state->{sets} = {
  231. base => 1,
  232. comp => 1,
  233. etc => 1,
  234. xetc => 1,
  235. xbase => 1,
  236. xfont => 1,
  237. xshare => 1,
  238. };
  239. $state->{error} = 0;
  240. $state->{no_exports} = 1;
  241. $state->{opt} = {
  242. B => sub { $state->{fchroot} = File::Spec->canonpath(shift); },
  243. S => sub { $state->{fsrcroot} = File::Spec->canonpath(shift); },
  244. c => sub { $state->read_configfile(shift); },
  245. };
  246. $state->SUPER::handle_options("B:S:c:mx",
  247. "[-B chroot] [-c configfile] [-S chroot] [arg=value...]");
  248. # command-line trump configuration file
  249. my ($k, $v);
  250. for my $arg (@ARGV) {
  251. if ($arg =~ m/^(.*?)\s*\=\s*(.*)$/) {
  252. ($k, $v) = ($1, $2);
  253. } else {
  254. $v = $arg;
  255. }
  256. $state->do_parm($k, $v);
  257. }
  258. if (defined $state->{fchroot}) {
  259. $state->{chroot} = $state->{fchroot};
  260. }
  261. $state->set_chroot;
  262. if (defined $state->{fsrcroot}) {
  263. $state->{srcroot} = $state->{fsrcroot};
  264. }
  265. # more basic option defaults
  266. if (!defined $state->{actions}{snapshot} &&
  267. !defined $state->{actions}{locate} &&
  268. !defined $state->{actions}{none}) {
  269. if ($state->{snapshot}) {
  270. $state->{actions}{snapshot} = 1;
  271. } else {
  272. $state->{actions}{locate} = 1;
  273. }
  274. }
  275. if (defined $state->{portscvs} &&
  276. !defined $state->{actions}{checkout_ports}) {
  277. $state->{actions}{checkout_ports} = 1;
  278. }
  279. if ($state->{actions}{unpopulate}) {
  280. delete $state->{actions}{unpopulate_light};
  281. }
  282. if (!defined $state->{chroot}) {
  283. $state->usage("need a chroot base");
  284. }
  285. if (defined $state->{actions}{checkout_ports} &&
  286. !defined $state->{portuser}) {
  287. $state->usage("can't do action checkout_ports without a PORT_USER");
  288. }
  289. if (defined $state->{snapshot} &&
  290. $state->{snapshot} =~ m/^\Q$state->{chroot}\E(.*)/) {
  291. $state->add_preserved($1);
  292. }
  293. $state->{progressmeter} = OpenBSD::ProgressMeter->new;
  294. $state->{progressmeter}->setup($state->opt('x'), $state->opt('m'),
  295. $state);
  296. if ($< != 0) {
  297. $state->fatal("Must be root");
  298. }
  299. for my $i (qw(PORTSDIR DISTDIR WRKOBJDIR PACKAGE_REPOSITORY PLIST_REPOSITORY LOCKDIR LOGDIR)) {
  300. if (defined $state->{$i}) {
  301. $state->{write}{$i} = 1;
  302. }
  303. }
  304. $state->{PORTSDIR} //= "/usr/ports";
  305. $state->{DISTDIR} //= join('/', $state->{PORTSDIR}, 'distfiles');
  306. $state->{WRKOBJDIR} //= join('/', $state->{PORTSDIR}, 'pobj');
  307. $state->{LOCKDIR} //= join('/', $state->{WRKOBJDIR}, 'locks');
  308. $state->{LOGDIR} //= join('/', $state->{PORTSDIR}, 'logs');
  309. $state->{fetchuser} //= MyUser->new('_pfetch');
  310. $state->{builduser} //= MyUser->new('_pbuild');
  311. $state->{loguser} //= $state->{builduser};
  312. $state->{PACKAGE_REPOSITORY} //= join('/', $state->{PORTSDIR}, 'packages');
  313. $state->{PLIST_REPOSITORY} //= join('/', $state->{PORTSDIR}, 'plist');
  314. $state->{sysdir} //= '/usr/src/sys';
  315. for my $dir (qw(DISTDIR WRKOBJDIR LOGDIR PACKAGE_REPOSITORY PLIST_REPOSITORY LOCKDIR)) {
  316. $state->{$dir} = $state->canonical_dir($state->{$dir});
  317. $state->add_preserved($state->{$dir});
  318. }
  319. $state->{PORTSDIR} = $state->canonical_dir($state->{PORTSDIR});
  320. if (!$state->{actions}{copy_ports}) {
  321. $state->add_preserved($state->{PORTSDIR});
  322. }
  323. for my $i (qw(portuser loguser fetchuser builduser)) {
  324. if (defined $state->{$i}) {
  325. $state->say("#1: #2", $i, $state->{$i}{user});
  326. }
  327. }
  328. for my $i (qw(PORTSDIR DISTDIR WRKOBJDIR LOCKDIR LOGDIR
  329. PACKAGE_REPOSITORY PLIST_REPOSITORY)) {
  330. $state->say("#1=#2", $i, $state->{$i});
  331. }
  332. }
  333. sub chroot
  334. {
  335. my $state = shift;
  336. if (defined $state->{chroot}) {
  337. unshift @_, $state->{chroot};
  338. }
  339. return File::Spec->canonpath(join('/', @_));
  340. }
  341. sub srcroot
  342. {
  343. my $state = shift;
  344. if (defined $state->{srcroot}) {
  345. unshift @_, $state->{srcroot};
  346. }
  347. return File::Spec->canonpath(join('/', @_));
  348. }
  349. sub chdir
  350. {
  351. my ($state, $dir) = @_;
  352. CORE::chdir($dir) or $state->fatal("Can't change to #1: #2", $dir, $!);
  353. }
  354. sub banner
  355. {
  356. my ($self, $text, $sub) = @_;
  357. $self->{progressmeter}->set_header($text);
  358. &$sub;
  359. $self->{progressmeter}->next;
  360. }
  361. sub run_chroot
  362. {
  363. my ($self, $text, $sub) = @_;
  364. $self->{progressmeter}->set_header($text);
  365. my $r = fork();
  366. if (!defined $r) {
  367. $self->fatal("Couldn't fork");
  368. }
  369. if ($r == 0) {
  370. CORE::chroot($self->chroot) or
  371. $self->fatal("Can't chroot into #1: #2",
  372. $self->chroot, $!);
  373. CORE::chdir('/') or
  374. $self->fatal("Can't chdir after chroot: #1", $!);
  375. &$sub;
  376. exit(0);
  377. }
  378. waitpid($r, 0);
  379. if ($? != 0) {
  380. $self->fatal("Running task failed");
  381. }
  382. # XXX fork'd so we don't know what we showed
  383. eval {
  384. $self->{progressmeter}->forked;
  385. };
  386. $self->{progressmeter}->next;
  387. }
  388. sub sync_display
  389. {
  390. my $self = shift;
  391. $self->{progressmeter}->clear if defined $self->{progressmeter};
  392. }
  393. sub check_mountpoint
  394. {
  395. my $state = shift;
  396. open(my $cmd, "-|", OpenBSD::Paths->mount);
  397. my ($dev, $nosuid, $wx);
  398. while (<$cmd>) {
  399. chomp;
  400. if (m/^(\S+)\s+on\s+(\S+)\s+type\s+(\S+)\s+\((.+)\)$/) {
  401. my ($dir, $type, $options) = ($2, $3, $4);
  402. my %opts = map {($_, 1)} split(/,\s+/, $options);
  403. if ($opts{nodev}) {
  404. $dev->{$dir} = 0;
  405. } else {
  406. $dev->{$dir} = 1;
  407. }
  408. if ($opts{nosuid}) {
  409. $nosuid->{$dir} = 1;
  410. } else {
  411. $nosuid->{$dir} = 0;
  412. }
  413. if ($opts{wxallowed}) {
  414. $wx->{$dir} = 1;
  415. } else {
  416. $wx->{$dir} = 0;
  417. }
  418. my $devno = (stat $dir)[0];
  419. if ($type eq 'nfs') {
  420. $state->{nfs}{$devno} = $dir;
  421. }
  422. }
  423. }
  424. close($cmd);
  425. my $root = $state->{chroot};
  426. if (-l $root) {
  427. $root = readlink $root;
  428. }
  429. for my $dir (keys %$dev) {
  430. if ($dir =~ m/^\Q$root\E(\/.*)/) {
  431. $state->say("Preserve #1", $1);
  432. $state->add_preserved($1);
  433. my $devno = (stat $dir)[0];
  434. $state->{dontrm_dev}{$devno} = 1;
  435. }
  436. }
  437. my $mnt = $root;
  438. do {{
  439. if (!defined $dev->{$mnt}) {
  440. $mnt = dirname($mnt);
  441. next;
  442. }
  443. $state->errsay("#1 is under #2 which is nodev", $root, $mnt)
  444. if $dev->{$mnt} == 0;
  445. $state->errsay("#1 is under #2 which does not have nosuid",
  446. $root, $mnt) if $nosuid->{$mnt} == 0;
  447. $state->errsay("#1 is under #2 which does not have wxallowed",
  448. $root, $mnt) if $wx->{$mnt} == 0;
  449. return;
  450. }} while ($mnt ne dirname($mnt));
  451. $state->fatal_error("Couldn't find mountpoint for #1 ???", $root);
  452. }
  453. sub special_data
  454. {
  455. my $state = shift;
  456. $state->{known}{'/etc/resolv.conf'} = 1;
  457. $state->{known}{'/etc/hosts'} = 1;
  458. }
  459. sub read_locates
  460. {
  461. my $state = shift;
  462. $state->banner("Running locate",
  463. sub {
  464. open(my $cmd, '-|', 'locate',
  465. '-d', $state->srcroot(OpenBSD::Paths->srclocatedb),
  466. '-d', $state->srcroot(OpenBSD::Paths->xlocatedb), ':');
  467. while (<$cmd>) {
  468. chomp;
  469. my ($set, $path) = split(':', $_, 2);
  470. $set =~ s/\d+//;
  471. # next if $path =~ m/\.ph$/;
  472. if ($state->{sets}{$set}) {
  473. $state->{known}{$path} = 1;
  474. }
  475. $state->{progressmeter}->working(1000);
  476. }
  477. close($cmd);
  478. });
  479. }
  480. # THIS IS THE WORK HORSE
  481. sub simple_copy
  482. {
  483. my ($state, $path, $cpath, $user) = @_;
  484. $state->{accounted}{$path} = 1;
  485. if (-l $path) {
  486. return if $state->{chroot_dont}{$cpath};
  487. remove_tree($cpath);
  488. my $target = readlink $path;
  489. if (!defined $target) {
  490. $state->fatal_error("Can't read link to #1: #2",
  491. $path, $!);
  492. } else {
  493. symlink($target, $cpath) or
  494. $state->fatal_error("Can't symlink #1 -> #2",
  495. $target, $cpath);
  496. }
  497. return;
  498. } else {
  499. my ($dev, $ino, $mode, $uid, $gid, $sz, $atime, $mtime) =
  500. (stat $path)[0, 1, 2, 4, 5, 7, 8, 9];
  501. if (-d $path) {
  502. if (!-d $cpath && -e _) {
  503. return if $state->{chroot_dont}{$cpath};
  504. remove_tree($cpath);
  505. }
  506. mkdir $cpath, $mode;
  507. if (!-d $cpath) {
  508. $state->fatal_error("Can't mkdir #1", $cpath);
  509. }
  510. if (defined $user) {
  511. $user->own($cpath);
  512. } else {
  513. chown $uid, $gid, $cpath;
  514. }
  515. chmod $mode, $cpath;
  516. utime $atime, $mtime, $cpath;
  517. return;
  518. } elsif (-f $path) {
  519. my ($dev2, $ino2, $sz2, $mtime2) =
  520. (stat $cpath)[0, 1, 7, 9];
  521. if (defined $dev2 && $dev2 == $dev && $ino2 == $ino) {
  522. return;
  523. }
  524. my $key = "$dev/$ino";
  525. if (exists $state->{copied}{$key}) {
  526. return if $state->{chroot_dont}{$cpath};
  527. remove_tree($cpath);
  528. link($state->{copied}{$key}, $cpath);
  529. return;
  530. }
  531. # avoid the copy if same filesystem
  532. if (link($path, $cpath)) {
  533. return;
  534. }
  535. my $okay = 0;
  536. if (defined $sz2 && $sz2 == $sz && $mtime2 >= $mtime) {
  537. $okay = 1;
  538. } else {
  539. return if $state->{chroot_dont}{$cpath};
  540. remove_tree($cpath);
  541. $okay = copy($path, $cpath);
  542. }
  543. if ($okay) {
  544. if (defined $user) {
  545. $user->own($cpath);
  546. } else {
  547. chown $uid, $gid, $cpath;
  548. }
  549. chmod $mode, $cpath;
  550. utime $atime, $mtime, $cpath;
  551. $state->{copied}{$key} = $cpath;
  552. return;
  553. }
  554. }
  555. }
  556. $state->fatal_error("Can't copy #1: #2", $path, $!);
  557. }
  558. sub recursive_copy
  559. {
  560. my ($state, $path) = @_;
  561. my $d = dirname($path);
  562. if ($d ne $path) {
  563. if (!-d $state->chroot($d)) {
  564. $state->recursive_copy($d);
  565. }
  566. }
  567. my $spath = $state->srcroot($path);
  568. if (!-e $spath) {
  569. $state->errsay("#1 does not exist", $spath);
  570. return;
  571. }
  572. $state->simple_copy($spath, $state->chroot($path));
  573. }
  574. sub copy_sync
  575. {
  576. my $state = shift;
  577. # farting all over the place
  578. $state->banner("Copying stuff over",
  579. sub {
  580. my $old = umask;
  581. umask 0;
  582. for my $path (sort keys %{$state->{known}}) {
  583. $state->recursive_copy($path);
  584. $state->{progressmeter}->message($path);
  585. }
  586. umask $old;
  587. });
  588. }
  589. sub best_user
  590. {
  591. my $state = shift;
  592. local $_ = shift;
  593. if (m/^\Q$state->{LOGDIR}\E/) {
  594. return $state->{loguser};
  595. }
  596. if (m/^\Q$state->{DISTDIR}\E\/build-stats/) {
  597. return $state->{loguser};
  598. }
  599. if (m/^\Q$state->{PLIST_REPOSITORY}\E/) {
  600. return $state->{builduser};
  601. }
  602. if (m/^\Q$state->{PACKAGE_REPOSITORY}\E/) {
  603. return $state->{builduser};
  604. }
  605. if (m/^\Q$state->{DISTDIR}\E/) {
  606. return $state->{fetchuser};
  607. }
  608. if (m/^\Q$state->{DISTDIR}\E/) {
  609. return $state->{fetchuser};
  610. }
  611. return $state->{portuser};
  612. }
  613. sub grab_file
  614. {
  615. my ($state, $snapdir, $file) = @_;
  616. my $baseurl = $state->{snapshot};
  617. unless (-f $file) {
  618. if ($state->system('/usr/bin/ftp' , "-C", "-o",
  619. "$snapdir/$file.part", "$baseurl/$file") == 0) {
  620. rename("$snapdir/$file.part", "$snapdir/$file");
  621. } else {
  622. $state->fatal("fetch #1 failed", $file);
  623. }
  624. }
  625. }
  626. sub extract_archive
  627. {
  628. my ($state, $dir, $archive) = @_;
  629. require IO::Uncompress::AnyUncompress;
  630. my $in = IO::Uncompress::AnyUncompress->new("$dir/$archive",
  631. MultiStream => 1);
  632. if (!$in) {
  633. $state->fatal("Couldn't open #1", "$dir/$archive");
  634. }
  635. require OpenBSD::Ustar;
  636. my $arc = OpenBSD::Ustar->new($in, $state, $state->chroot);
  637. while (my $o = $arc->next) {
  638. $o->{name} =~ s,^\./,/,;
  639. if (defined $o->{linkname}) {
  640. $o->{linkname} =~ s,^\./,/,;
  641. }
  642. unlink($state->chroot($o->{name}));
  643. # $state->say("$o->{name}");
  644. $state->{progressmeter}->message("$archive: $o->{name}");
  645. $state->{accounted}{$o->{name}} = 1;
  646. $o->create;
  647. }
  648. $arc->close;
  649. $in->close;
  650. }
  651. sub get_snapshot
  652. {
  653. my $state = shift;
  654. $state->banner("Grabbing snapshot",
  655. sub {
  656. my ($snapdir, $grab);
  657. if ($state->{snapshot} =~ m/^(https?|ftp):/) {
  658. $snapdir = $state->chroot('/tmp');
  659. File::Path::make_path($snapdir);
  660. $grab = sub { $state->grab_file($snapdir, shift); }
  661. } else {
  662. $snapdir = $state->{snapshot};
  663. $grab = sub {};
  664. }
  665. &$grab("SHA256.sig");
  666. open my $f, '<', "$snapdir/SHA256.sig"
  667. or $state->fatal("no SHA256.sig");
  668. my $line = <$f>;
  669. if ($line !~ m/openbsd\s+(\d+)\.(\d+)\s+base/) {
  670. $state->fatal("Unrecognized snapshot");
  671. }
  672. my $v = "$1$2";
  673. my (@files, @later);
  674. for my $set (sort keys %{$state->{sets}}) {
  675. if ($set =~ m/etc/) {
  676. push(@later, "$set.tgz");
  677. } else {
  678. my $file = "$set$v.tgz";
  679. &$grab($file);
  680. push(@files, $file);
  681. }
  682. }
  683. if ($state->system(
  684. sub {
  685. $state->chdir($snapdir); },
  686. '/usr/bin/signify', '-C', '-p',
  687. "/etc/signify/openbsd-$v-base.pub", '-x', 'SHA256.sig',
  688. @files) != 0) {
  689. $state->fatal("Checksum error");
  690. }
  691. for my $archive (@files) {
  692. $state->extract_archive($snapdir, $archive);
  693. }
  694. for my $archive (@later) {
  695. $state->extract_archive($state->chroot("var/sysmerge"),
  696. $archive);
  697. }
  698. if ($state->{snapshot} =~ m/^(https?|ftp):/) {
  699. for my $file (@files) {
  700. unlink("$snapdir/$file");
  701. }
  702. }
  703. });
  704. }
  705. sub copy_ports
  706. {
  707. my $state = shift;
  708. $state->banner("Copying ports",
  709. sub {
  710. my $old = umask;
  711. umask 0;
  712. my $portsdir = $state->{PORTSDIR};
  713. my $srcports = $state->srcroot($portsdir);
  714. if (-l $srcports) {
  715. $srcports = readlink $srcports;
  716. }
  717. my $destports = $state->chroot($portsdir);
  718. find(sub {
  719. if ($state->{ignorecvs} && $_ eq 'CVS' && -d $_) {
  720. $File::Find::prune = 1;
  721. return;
  722. }
  723. my $realname = $File::Find::name;
  724. $realname =~ s,^\Q$srcports\E\b,,;
  725. my $r2 = $realname;
  726. $r2 =~ s,^,$portsdir,;
  727. if ($r2 eq $state->{WRKOBJDIR}) {
  728. $File::Find::prune = 1;
  729. return;
  730. }
  731. $realname =~ s,^/,,;
  732. $state->{progressmeter}->message($realname);
  733. $state->simple_copy($File::Find::name,
  734. $state->chroot($r2), $state->best_user($r2));
  735. }, $srcports);
  736. umask $old;
  737. });
  738. }
  739. sub copy_sys
  740. {
  741. my $state = shift;
  742. $state->banner("Copying sys includes",
  743. sub {
  744. my $old = umask;
  745. umask 0;
  746. my $srcsys = $state->srcroot($state->{sysdir});
  747. my $destsys = $state->chroot($state->{sysdir});
  748. find(sub {
  749. # XXX don't bother copying stuff that's NOT .h
  750. return if -f $_ && !m/\.h$/ && $_ ne 'Makefile';
  751. if (-d _ && $_ eq 'obj' || $_ eq 'CVS') {
  752. $File::Find::prune = 1;
  753. return;
  754. }
  755. my $destname = $File::Find::name;
  756. $destname =~ s,^\Q$srcsys\E\b,$destsys,;
  757. $state->{progressmeter}->message($File::Find::name);
  758. $state->simple_copy($File::Find::name, $destname);
  759. }, $srcsys);
  760. umask $old;
  761. });
  762. }
  763. sub regen_devs
  764. {
  765. my $state = shift;
  766. $state->banner("Generating devices",
  767. sub {
  768. $state->system(
  769. sub {
  770. $state->chdir($state->chroot("/dev"));
  771. }, '/bin/sh', './MAKEDEV', 'all');
  772. $state->add_preserved('/dev');
  773. });
  774. $state->run_chroot("Generating devices db",
  775. sub {
  776. $state->system("/usr/sbin/dev_mkdb");
  777. });
  778. }
  779. sub run_ldconfig
  780. {
  781. my $state = shift;
  782. $state->banner("Running ldconfig",
  783. sub {
  784. $state->system(
  785. sub {
  786. CORE::chroot($state->chroot) or exit 1;
  787. },
  788. 'ldconfig', '/usr/lib', '/usr/X11R6/lib', '/usr/local/lib');
  789. $state->{accounted}{'/var/run/ld.so.hints'} = 1;
  790. });
  791. }
  792. sub checkout_ports
  793. {
  794. my $state = shift;
  795. $state->banner("Checking out ports tree",
  796. sub {
  797. my @cvs = ("/usr/bin/cvs", "-z3", "-d", $state->{portscvs});
  798. my $dir = $state->chroot($state->{PORTSDIR});
  799. if (-d "$dir/CVS") {
  800. $state->{portuser}->run_as(sub {
  801. $state->system(
  802. sub { $state->chdir($dir); },
  803. @cvs, "update", "-dP", "-A");
  804. });
  805. } else {
  806. # okay, so stupid cvs creates the dir, work-around that
  807. $state->{portuser}->mkpath("$dir.tmp");
  808. $state->{portuser}->run_as(sub {
  809. $state->system(
  810. sub { $state->chdir("$dir.tmp"); },
  811. @cvs, "co", "-P", "-A", "ports");
  812. });
  813. rename("$dir.tmp/ports", "$dir");
  814. rmdir("$dir.tmp");
  815. }
  816. });
  817. }
  818. sub unpopulate_chroot
  819. {
  820. my $state = shift;
  821. $state->run_chroot("Cleaning up files and directories",
  822. sub {
  823. my @dirs;
  824. find(
  825. sub {
  826. my $devno = (lstat $_)[0];
  827. if ($state->{dontrm_dev}{$devno} ||
  828. $state->{dontrm}{$File::Find::name}) {
  829. $File::Find::prune = 1 if -d _;
  830. return;
  831. }
  832. if ($state->{accounted}{$File::Find::name}) {
  833. return;
  834. }
  835. $state->{progressmeter}->message($File::Find::name);
  836. if (-l _ || ! -d _) {
  837. unlink($_);
  838. } else {
  839. push(@dirs, $File::Find::name);
  840. }
  841. }, '/');
  842. for my $dir (reverse @dirs) {
  843. #$state->say("rmdir #1", $dir);
  844. rmdir $dir;
  845. }
  846. });
  847. }
  848. sub verbose_shit
  849. {
  850. my $state = shift;
  851. $state->run_chroot("Showing up stragglers",
  852. sub {
  853. find(
  854. sub {
  855. if (-d $_ && $state->{dontrm}{$File::Find::name}) {
  856. $File::Find::prune = 1;
  857. return;
  858. }
  859. if ($state->{dontrm}{$File::Find::name}) {
  860. return;
  861. }
  862. if ($state->{accounted}{$File::Find::name} ||
  863. $File::Find::name eq '/') {
  864. return;
  865. }
  866. $state->say("#1", $File::Find::name);
  867. }, '/');
  868. });
  869. }
  870. sub show_absolute_symlinks
  871. {
  872. my $state = shift;
  873. my $wrapper = '/usr/sbin/mailwrapper';
  874. my $expected = {
  875. '/etc/localtime' => undef,
  876. '/etc/rmt' => '/usr/sbin/rmt',
  877. '/etc/termcap' => '/usr/share/misc/termcap',
  878. '/usr/bin/hoststat' => $wrapper,
  879. '/usr/bin/mailq' => $wrapper,
  880. '/usr/bin/newaliases' => $wrapper,
  881. '/usr/bin/purgestat' => $wrapper,
  882. '/usr/bin/rcs2log' => '/usr/libexec/cvs/contrib/rcs2log',
  883. '/usr/local/lib/X11/app-defaults' => '/etc/X11/app-defaults',
  884. '/usr/sbin/makemap' => $wrapper,
  885. '/usr/sbin/sendmail' => $wrapper,
  886. };
  887. $state->run_chroot("Showing absolute symlinks",
  888. sub {
  889. find(
  890. sub {
  891. return unless -l $_;
  892. my $dest = readlink $_;
  893. return unless $dest =~ m/^\//;
  894. my $ok = 0;
  895. my $n = $File::Find::name;
  896. if (exists $expected->{$n}) {
  897. $ok = 1;
  898. if (defined $expected->{$n}
  899. and $expected->{$n} ne $dest) {
  900. $ok = 0;
  901. }
  902. }
  903. if (!$ok) {
  904. $state->errsay("#1 points to #2", $n, $dest);
  905. }
  906. }, '/');
  907. });
  908. }
  909. sub is_under_nfs
  910. {
  911. my ($state, $dir) = @_;
  912. my $devno = (stat $dir)[0];
  913. if (defined $devno && exists $state->{nfs}{$devno}) {
  914. return 1;
  915. } else {
  916. return 0;
  917. }
  918. }
  919. use constant { WHINE => 1, MKPATH => 2 };
  920. sub build_dir
  921. {
  922. my ($state, $opts, $username, $dirname, @r) = @_;
  923. $state->{progressmeter}->message($dirname);
  924. my $dir = join('/', $state->{$dirname}, @r);
  925. $state->{$username}->mkpath($dir);
  926. my $nfs = $state->is_under_nfs($dir);
  927. if (($opts & WHINE) && $nfs) {
  928. $state->errsay("#1 (#2) is under nfs", $dirname, $dir);
  929. }
  930. if ($state->{chown_all} && !$nfs && !($opts & MKPATH)) {
  931. $state->{$username}->mkRpath($dir);
  932. }
  933. }
  934. sub make_ports_subdirs
  935. {
  936. my $state = shift;
  937. $state->run_chroot("Adjusting ports directories",
  938. sub {
  939. $state->build_dir(0, "fetchuser", "DISTDIR");
  940. if (defined $state->{loguser}) {
  941. $state->build_dir(0, "loguser", "LOGDIR");
  942. $state->build_dir(0, "loguser", "DISTDIR", "build-stats");
  943. }
  944. $state->build_dir(WHINE|MKPATH , "builduser", "WRKOBJDIR");
  945. $state->build_dir(0, "builduser", "PACKAGE_REPOSITORY");
  946. $state->build_dir(0, "builduser", "PLIST_REPOSITORY");
  947. $state->build_dir(WHINE, "builduser", "LOCKDIR");
  948. });
  949. }
  950. sub write_mk_conf
  951. {
  952. my $state = shift;
  953. $state->banner("Writing mk.conf",
  954. sub {
  955. open(my $f, ">", $state->chroot("/etc/mk.conf"));
  956. print $f "# Automatically generated by $state->{cmd}\n\n";
  957. for my $i (sort keys %{$state->{write}}) {
  958. print $f "$i = $state->{$i}\n";
  959. }
  960. if (exists $state->{mkconf_lines}) {
  961. print $f "\n";
  962. for my $l (@{$state->{mkconf_lines}}) {
  963. print $f $l, "\n";
  964. }
  965. }
  966. if (exists $state->{mkconf_tail}) {
  967. open(my $g, "<", $state->{mkconf_tail}) or
  968. $state->fatal_error("Couldn't read tail #1: #2",
  969. $state->{mkconf_tail}, $!);
  970. print $f "# Copied from $state->{mkconf_tail}\n";
  971. while(<$g>) {
  972. print $f $_;
  973. }
  974. close $g;
  975. }
  976. close($f);
  977. });
  978. }
  979. package main;
  980. my $state = MyState->new("proot");
  981. $state->handle_options;
  982. if ($state->{actions}{check_mount}) {
  983. $state->check_mountpoint;
  984. }
  985. if ($state->{actions}{unpopulate}) {
  986. $state->unpopulate_chroot;
  987. }
  988. if ($state->{actions}{locate}) {
  989. $state->read_locates;
  990. }
  991. if ($state->{actions}{snapshot}) {
  992. $state->get_snapshot;
  993. }
  994. if ($state->{actions}{resolv}) {
  995. $state->special_data;
  996. }
  997. if (defined $state->{known}) {
  998. $state->copy_sync;
  999. }
  1000. if ($state->{actions}{copy_ports}) {
  1001. $state->copy_ports;
  1002. }
  1003. if ($state->{actions}{copy_sys}) {
  1004. $state->copy_sys;
  1005. }
  1006. if ($state->{actions}{unpopulate_light}) {
  1007. $state->unpopulate_chroot;
  1008. }
  1009. if ($state->{actions}{ldconfig}) {
  1010. $state->run_ldconfig;
  1011. }
  1012. if ($state->{actions}{devs}) {
  1013. $state->regen_devs;
  1014. }
  1015. if ($state->{actions}{checkout_ports}) {
  1016. $state->checkout_ports;
  1017. }
  1018. if ($state->{actions}{ports_subdirs}) {
  1019. $state->make_ports_subdirs;
  1020. }
  1021. if ($state->{actions}{stragglers}) {
  1022. $state->verbose_shit;
  1023. }
  1024. if ($state->{actions}{write_mk} &&
  1025. exists $state->{write} || exists $state->{mkconf_tail}) {
  1026. $state->write_mk_conf;
  1027. }
  1028. if ($state->{actions}{check_symlinks}) {
  1029. $state->show_absolute_symlinks;
  1030. }
  1031. exit($state->{error});