source: devel/pb/bin/pb @ 1037

Revision 1037, 96.5 KB checked in by bruno, 3 years ago (diff)

Update pres for HP tech Forum

  • Property svn:executable set to *
Line 
1#!/usr/bin/perl -w
2#
3# Project Builder main application
4#
5# $Id$
6#
7# Copyright B. Cornec 2007
8# Provided under the GPL v2
9
10# Syntax: see at end
11
12use strict 'vars';
13use Getopt::Long qw(:config auto_abbrev no_ignore_case);
14use Data::Dumper;
15use English;
16use File::Basename;
17use File::Copy;
18use File::stat;
19use File::Temp qw(tempdir);
20use File::Find;
21use Time::localtime qw(localtime);
22use POSIX qw(strftime);
23use lib qw (lib);
24use ProjectBuilder::Version;
25use ProjectBuilder::Base;
26use ProjectBuilder::Display;
27use ProjectBuilder::Conf;
28use ProjectBuilder::Distribution;
29use ProjectBuilder::CMS;
30use ProjectBuilder::Env;
31use ProjectBuilder::Filter;
32use ProjectBuilder::Changelog;
33use Mail::Sendmail;
34
35# Global variables
36my %opts;                                       # CLI Options
37my $action;                                     # action to realize
38my $test = "FALSE";                     # Not used
39my $pbforce = 0;                        # Force VE/VM rebuild
40my $pbsnap = 0;                         # Do not use snapshot mode for VM/VE by default
41my $option = "";                        # Not used
42my @pkgs;                                       # list of packages
43my $pbtag;                                      # Global Tag variable
44my $pbver;                                      # Global Version variable
45my $pbscript;                           # Name of the script
46my %pbver;                                      # per package
47my %pbtag;                                      # per package
48my $pbrev;                                      # Global REVISION variable
49my $pbaccount;                          # Login to use to connect to the VM
50my $pbport;                                     # Port to use to connect to the VM
51my $newver;                                     # New version to create
52my $iso = undef;                        # ISO image for the VM to create
53
54my @date = pb_get_date();
55my $pbdate = strftime("%Y-%m-%d", @date);
56
57=pod
58
59=head1 NAME
60
61pb, aka project-builder.org - builds packages for your projects
62
63=head1 DESCRIPTION
64
65pb helps you build various packages directly from your project sources.
66Those sources could be handled by a CMS (Configuration Management System)
67such as Subversion, CVS, Git, Mercurial... or being a simple reference to a compressed tar file.
68It's based on a set of configuration files, a set of provided macros to help
69you keeping build files as generic as possible. For example, a single .spec
70file should be required to generate for all rpm based distributions, even
71if you could also have multiple .spec files if required.
72
73=head1 SYNOPSIS
74
75pb [-vhSq][-r pbroot][-p project][[-s script -a account -P port][-m mach-1[,...]]][-i iso] <action> [<pkg1> ...]
76
77pb [--verbose][--help][--man][--quiet][--snapshot][--revision pbroot][--project project][[--script script --account account --port port][--machine mach-1[,...]]][--iso iso] <action> [<pkg1> ...]
78
79=head1 OPTIONS
80
81=over 4
82
83=item B<-v|--verbose>
84
85Print a brief help message and exits.
86
87=item B<-q|--quiet>
88
89Do not print any output.
90
91=item B<-h|--help>
92
93Print a brief help message and exits.
94
95=item B<-S|--snapshot>
96
97Use the snapshot mode of VMs or VEs
98
99=item B<--man>
100
101Prints the manual page and exits.
102
103=item B<-m|--machine machine1[,machine2,...]>
104
105Name of the Virtual Machines (VM) or Virtual Environments (VE) you want to build on (coma separated).
106All if none precised (or use the env variable PBV).
107
108=item B<-s|--script script>
109
110Name of the script you want to execute on the related VMs or VEs.
111
112=item B<-i|--iso iso_image>
113
114Name of the ISO image of the distribution you want to install on the related VMs.
115
116=item B<-a|--account account>
117
118Name of the account to use to connect on the related VMs.
119
120=item B<-P|--port port_number>
121
122Port number to use to connect on the related VMs.\n";
123
124=item B<-p|--project project_name>
125
126Name of the project you're working on (or use the env variable PBPROJ)
127
128=item B<-r|--revision revision>
129
130Path Name of the project revision under the CMS (or use the env variable PBROOT)
131
132=item B<-V|--version new_version>
133
134New version of the project to create based on the current one.
135
136=back
137
138=head1 ARGUMENTS
139
140<action> can be:
141
142=over 4
143
144=item B<cms2build>
145
146Create tar files for the project under your CMS.
147CMS supported are SVN, CVS and Mercurial
148parameters are packages to build
149if not using default list
150
151=item B<build2pkg>
152
153Create packages for your running distribution
154
155=item B<cms2pkg>
156
157cms2build + build2pkg
158
159=item B<build2ssh>
160
161Send the tar files to a SSH host
162
163=item B<cms2ssh>
164
165cms2build + build2ssh
166
167=item B<pkg2ssh>
168
169Send the packages built to a SSH host
170
171=item B<build2vm>
172
173Create packages in VMs, launching them if needed
174and send those packages to a SSH host once built
175VM type supported are QEMU
176
177=item B<build2ve>
178
179Create packages in VEs, creating it if needed
180and send those packages to a SSH host once built
181
182=item B<cms2vm>
183
184cms2build + build2vm
185
186=item B<cms2ve>
187
188cms2build + build2ve
189
190=item B<launchvm>
191
192Launch one virtual machine
193
194=item B<launchve>
195
196Launch one virtual environment
197
198=item B<script2vm>
199
200Launch one virtual machine if needed
201and executes a script on it
202
203=item B<script2ve>
204
205Execute a script in a virtual environment
206
207=item B<newvm>
208
209Create a new virtual machine
210
211=item B<newve>
212
213Create a new virtual environment
214
215=item B<setupvm>
216
217Setup a virtual machine for pb usage
218
219=item B<setupve>
220
221Setup a virtual environment for pb usage
222
223=item B<snapvm>
224
225Snapshot a virtual machine for pb usage
226
227=item B<snapve>
228
229Snapshot a virtual environment for pb usage
230
231=item B<test2pkg>
232
233Test a package locally
234
235=item B<test2vm>
236
237Test a package in a virtual machine
238
239=item B<test2ve>
240
241Test a package in a virtual environment
242
243=item B<newver>
244
245Create a new version of the project derived
246from the current one
247
248=item B<newproj>
249
250Create a new project and a template set of
251configuration files under pbconf
252
253=item B<announce>
254
255Announce the availability of the project through various means
256
257=item B<web2ssh>
258
259Deliver the Web site content to the target server using ssh.
260
261=item B<clean>
262
263Purge the build and delivery directories related to the current project
264
265=back
266
267<pkgs> can be a list of packages, the keyword 'all' or nothing, in which case the default list of packages is taken (corresponding to the defpkgdir list of arguments in the configuration file).
268
269=head1 WEB SITES
270
271The main Web site of the project is available at L<http://www.project-builder.org/>. Bug reports should be filled using the trac instance of the project at L<http://trac.project-builder.org/>.
272
273=head1 USER MAILING LIST
274
275None exists for the moment.
276
277=head1 CONFIGURATION FILES
278
279Each pb user may have a configuration in F<$HOME/.pbrc>. The values in this file may overwrite any other configuration file value.
280
281Here is an example of such a configuration file:
282
283 #
284 # Define for each project the URL of its pbconf repository
285 # No default option allowed here as they need to be all different
286 #
287 # URL of the pbconf content
288 # This is the format of a classical URL with the extension of additional schema such as
289 # svn+ssh, cvs+ssh, ...
290 #
291 pbconfurl linuxcoe = cvs+ssh://:ext:bcornec@linuxcoe.cvs.sourceforge.net:/cvsroot/linuxcoe/pbconf
292
293 # This is normaly defined in the project's configuration file
294 # Url of the project
295 #
296 pburl linuxcoe = cvs+ssh://:ext:bcornec@linuxcoe.cvs.sourceforge.net:/cvsroot/linuxcoe
297 
298 # All these URLs needs to be defined here as the are the entry point
299 # for how to build packages for the project
300 #
301 pbconfurl pb = svn+ssh://svn.project-builder.org/mondo/svn/pb/pbconf
302 pbconfurl mondorescue = svn+ssh://svn.project-builder.org/mondo/svn/project-builder/mondorescue/pbconf
303 pbconfurl collectl = svn+ssh://bruno@svn.mondorescue.org/mondo/svn/project-builder/collectl/pbconf
304 pbconfurl netperf = svn+ssh://svn.mondorescue.org/mondo/svn/project-builder/netperf/pbconf
305 
306 # Under that dir will take place everything related to pb
307 # If you want to use VMs/chroot/..., then use $ENV{'HOME'} to make it portable
308 # to your VMs/chroot/...
309 # if not defined then /var/cache
310 pbdefdir default = $ENV{'HOME'}/project-builder
311 pbdefdir pb = $ENV{'HOME'}
312 pbdefdir linuxcoe = $ENV{'HOME'}/LinuxCOE/cvs
313 pbdefdir mondorescue = $ENV{'HOME'}/mondo/svn
314 
315 # pbconfdir points to the directory where the CMS content of the pbconfurl is checked out
316 # If not defined, pbconfdir is under pbdefdir/pbproj/pbconf
317 pbconfdir linuxcoe = $ENV{'HOME'}/LinuxCOE/cvs/pbconf
318 pbconfdir mondorescue = $ENV{'HOME'}/mondo/svn/pbconf
319 
320 # pbdir points to the directory where the CMS content of the pburl is checked out
321 # If not defined, pbdir is under pbdefdir/pbproj
322 # Only defined if we have access to the dev of the project
323 pbdir linuxcoe = $ENV{'HOME'}/LinuxCOE/cvs
324 pbdir mondorescue = $ENV{'HOME'}/mondo/svn
325 
326 # -daemonize doesn't work with qemu 0.8.2
327 vmopt default = -m 384
328
329=head1 AUTHORS
330
331The Project-Builder.org team L<http://trac.project-builder.org/> lead by Bruno Cornec L<mailto:bruno@project-builder.org>.
332
333=head1 COPYRIGHT
334
335Project-Builder.org is distributed under the GPL v2.0 license
336described in the file C<COPYING> included with the distribution.
337
338=cut
339
340# ---------------------------------------------------------------------------
341
342my ($projectbuilderver,$projectbuilderrev) = pb_version_init();
343my $appname = "pb";
344
345# Initialize the syntax string
346
347pb_syntax_init("$appname (aka project-builder.org) Version $projectbuilderver-$projectbuilderrev\n");
348
349GetOptions("help|?|h" => \$opts{'h'}, 
350                "man" => \$opts{'man'},
351                "verbose|v+" => \$opts{'v'},
352                "snapshot|S" => \$opts{'S'},
353                "quiet|q" => \$opts{'q'},
354                "log-files|l=s" => \$opts{'l'},
355                "force|f" => \$opts{'f'},
356                "account|a=s" => \$opts{'a'},
357                "revision|r=s" => \$opts{'r'},
358                "script|s=s" => \$opts{'s'},
359                "machines|mock|m=s" => \$opts{'m'},
360                "port|P=i" => \$opts{'P'},
361                "project|p=s" => \$opts{'p'},
362                "iso|i=s" => \$opts{'i'},
363                "version|V=s" => \$opts{'V'},
364) || pb_syntax(-1,0);
365
366if (defined $opts{'h'}) {
367        pb_syntax(0,1);
368}
369if (defined $opts{'man'}) {
370        pb_syntax(0,2);
371}
372if (defined $opts{'v'}) {
373        $pbdebug = $opts{'v'};
374}
375if (defined $opts{'f'}) {
376        $pbforce=1;
377}
378if (defined $opts{'q'}) {
379        $pbdebug=-1;
380}
381if (defined $opts{'S'}) {
382        $pbsnap=1;
383}
384if (defined $opts{'l'}) {
385        open(pbLOG,"> $opts{'l'}") || die "Unable to log to $opts{'l'}: $!";
386        $pbLOG = \*pbLOG;
387        $pbdebug = 0  if ($pbdebug == -1);
388        }
389pb_log_init($pbdebug, $pbLOG);
390pb_display_init("text","");
391
392# Handle root of the project if defined
393if (defined $opts{'r'}) {
394        $ENV{'PBROOTDIR'} = $opts{'r'};
395}
396# Handle virtual machines if any
397if (defined $opts{'m'}) {
398        $ENV{'PBV'} = $opts{'m'};
399}
400if (defined $opts{'s'}) {
401        $pbscript = $opts{'s'};
402}
403if (defined $opts{'a'}) {
404        $pbaccount = $opts{'a'};
405        die "option -a requires a -s script option" if (not defined $pbscript);
406}
407if (defined $opts{'P'}) {
408        $pbport = $opts{'P'};
409}
410if (defined $opts{'V'}) {
411        $newver = $opts{'V'};
412}
413if (defined $opts{'i'}) {
414        $iso = $opts{'i'};
415}
416
417# Get Action
418$action = shift @ARGV;
419die pb_syntax(-1,1) if (not defined $action);
420
421my ($filteredfiles, $supfiles, $defpkgdir, $extpkgdir);
422my $pbinit = undef;
423$pbinit = 1 if ($action =~ /^newproj$/);
424
425# Handles project name if any
426# And get global params
427($filteredfiles, $supfiles, $defpkgdir, $extpkgdir) = pb_env_init($opts{'p'},$pbinit,$action);
428
429#
430# Check for command requirements
431#
432my ($req,$opt) = pb_conf_get_if("oscmd","oscmdopt");
433my ($req2,$opt2) = (undef,undef);
434$req2 = $req->{$appname} if (defined $req);
435$opt2 = $opt->{$appname} if (defined $opt);
436pb_check_requirements($req2,$opt2);
437
438pb_log(0,"Project: $ENV{'PBPROJ'}\n");
439pb_log(0,"Action: $action\n");
440
441# Act depending on action
442if ($action =~ /^cms2build$/) {
443        pb_cms2build();
444} elsif ($action =~ /^build2pkg$/) {
445        pb_build2pkg();
446} elsif ($action =~ /^cms2pkg$/) {
447        pb_cms2build();
448        pb_build2pkg();
449} elsif ($action =~ /^build2ssh$/) {
450        pb_build2ssh();
451} elsif ($action =~ /^cms2ssh$/) {
452        pb_cms2build();
453        pb_build2ssh();
454} elsif ($action =~ /^pkg2ssh$/) {
455        pb_pkg2ssh();
456} elsif ($action =~ /^build2ve$/) {
457        pb_build2v("ve","build");
458} elsif ($action =~ /^build2vm$/) {
459        pb_build2v("vm","build");
460} elsif ($action =~ /^cms2ve$/) {
461        pb_cms2build();
462        pb_build2v("ve","build");
463} elsif ($action =~ /^cms2vm$/) {
464        pb_cms2build();
465        pb_build2v("vm","build");
466} elsif ($action =~ /^launchvm$/) {
467        pb_launchv("vm",$ENV{'PBV'},0);
468} elsif ($action =~ /^launchve$/) {
469        pb_launchv("ve",$ENV{'PBV'},0);
470} elsif ($action =~ /^script2vm$/) {
471        pb_script2v($pbscript,"vm");
472} elsif ($action =~ /^script2ve$/) {
473        pb_script2v($pbscript,"ve");
474} elsif ($action =~ /^newver$/) {
475        pb_newver();
476} elsif ($action =~ /^newve$/) {
477        pb_launchv("ve",$ENV{'PBV'},1);
478} elsif ($action =~ /^newvm$/) {
479        pb_launchv("vm",$ENV{'PBV'},1);
480        pb_log(0, "Please ensure that sshd is running in your VM by default\n");
481        pb_log(0, "and that it allows remote root login (PermitRootLogin yes in /etc/ssh/sshd_config)\n");
482} elsif ($action =~ /^setupve$/) {
483        pb_setup2v("ve");
484} elsif ($action =~ /^setupvm$/) {
485        pb_setup2v("vm");
486} elsif ($action =~ /^snapve$/) {
487        pb_snap2v("ve");
488} elsif ($action =~ /^snapvm$/) {
489        pb_snap2v("vm");
490} elsif ($action =~ /^test2pkg$/) {
491        pb_test2pkg();
492} elsif ($action =~ /^test2ve$/) {
493        pb_build2v("ve","test");
494} elsif ($action =~ /^test2vm$/) {
495        pb_build2v("vm","test");
496} elsif ($action =~ /^newproj$/) {
497        # Nothing to do - already done in pb_env_init
498} elsif ($action =~ /^clean$/) {
499        pb_clean();
500} elsif ($action =~ /^announce$/) {
501        # For announce only. Require avoids the systematic load of these modules
502        require DBI;
503        require DBD::SQLite;
504
505        pb_announce();
506} elsif ($action =~ /^web2ssh$/) {
507        require DBI;
508        require DBD::SQLite;
509
510        pb_cms2build("Web");
511        pb_send2target("Web");
512} else {
513        pb_log(0,"\'$action\' is not available\n");
514        pb_syntax(-2,1);
515}
516
517sub pb_cms2build {
518
519        my $param = shift || undef;
520
521        my $pkg;
522        my @pkgs;
523        my $webdir;
524
525        my %pkgs;
526        my %pb;                         # Structure to store conf info
527
528        # If Website, then pkg is only the website
529        if ((defined $param) && ($param eq "Web")) {
530                ($webdir) = pb_conf_get("webdir");
531                pb_log(2,"webdir: ".Dumper($webdir)."\n");
532                $pkgs[0] = $webdir->{$ENV{'PBPROJ'}};
533                $extpkgdir = $webdir;
534                pb_log(0,"Package: $pkgs[0]\n");
535        } else {
536                $pkg = pb_cms_get_pkg($defpkgdir,$extpkgdir);
537                @pkgs = @$pkg;
538        }
539
540        my ($scheme, $uri) = pb_cms_init($pbinit);
541
542        # We need 2 lines here
543        my ($pkgv, $pkgt, $testver) = pb_conf_get_if("pkgver","pkgtag","testver");
544        my @pt = pb_conf_get_if("vmlist","velist");
545
546        # declare packager and repo for filtering
547        # TODO: Is pbrepo needed so early in the process ?
548        my ($tmp1, $tmp2) = pb_conf_get("pbpackager","pbrepo");
549        $ENV{'PBPACKAGER'} = $tmp1->{$ENV{'PBPROJ'}};
550        $ENV{'PBREPO'} = $tmp2->{$ENV{'PBPROJ'}};
551
552        foreach my $pbpkg (@pkgs) {
553                $ENV{'PBPKG'} = $pbpkg;
554
555                if ((defined $pkgv) && (defined $pkgv->{$pbpkg})) {
556                        $pbver = $pkgv->{$pbpkg};
557                } else {
558                        $pbver = $ENV{'PBPROJVER'};
559                }
560                # If it's a test version, then tag == 0.date
561                if ((defined $testver) && (defined $testver->{$ENV{'PBPROJ'}}) && ($testver->{$ENV{'PBPROJ'}} =~ /true/i)) {
562                        $pbtag = "0.".strftime("%Y%m%d%H%M%S", @date);
563                        $ENV{'PBPROJTAG'} = $pbtag;
564                } elsif ((defined $pkgt) && (defined $pkgt->{$pbpkg})) {
565                        $pbtag = $pkgt->{$pbpkg};
566                } else {
567                        $pbtag = $ENV{'PBPROJTAG'};
568                }
569
570                $pbrev = $ENV{'PBREVISION'};
571                pb_log(0,"\n");
572                pb_log(0,"Management of $pbpkg $pbver-$pbtag (rev $pbrev)\n");
573                die "Unable to get env var PBDESTDIR" if (not defined $ENV{'PBDESTDIR'});
574
575                # Clean up dest if necessary. The export will recreate it
576                my $dest = "$ENV{'PBDESTDIR'}/$pbpkg-$pbver";
577                pb_rm_rf($dest) if (-d $dest);
578
579                # Export CMS tree for the concerned package to dest
580                # And generate some additional files
581                $OUTPUT_AUTOFLUSH=1;
582
583                # computes in which dir we have to work
584                my $dir = $defpkgdir->{$pbpkg};
585                $dir = $extpkgdir->{$pbpkg} if (not defined $dir);
586                $dir = $webdir->{$ENV{'PBPROJ'}} if ((defined $param) && ($param eq "Web"));
587                pb_log(2,"def:".Dumper($defpkgdir)." ext: ".Dumper($extpkgdir)." \n");
588
589                # Exporting content from CMS
590                my $preserve = pb_cms_export($uri,"$ENV{'PBDIR'}/$dir",$dest);
591
592                # Generated fake content for test versions to speed up stuff
593                my $chglog;
594
595                # Get project info on authors and log file
596                $chglog = "$ENV{'PBROOTDIR'}/$pbpkg/pbcl";
597                $chglog = "$ENV{'PBROOTDIR'}/pbcl" if (! -f $chglog);
598                $chglog = undef if (! -f $chglog);
599
600                my $authors = "$ENV{'PBROOTDIR'}/$pbpkg/pbauthors";
601                $authors = "$ENV{'PBROOTDIR'}/pbauthors" if (! -f $authors);
602                $authors = "/dev/null" if (! -f $authors);
603
604                # Extract cms log history and store it
605                if ((defined $chglog) && (! -f "$dest/NEWS")) {
606                        pb_log(2,"Generating NEWS file from $chglog\n");
607                        copy($chglog,"$dest/NEWS") || die "Unable to create $dest/NEWS";
608                }
609                pb_cms_log($scheme,"$ENV{'PBDIR'}/$dir",$dest,$chglog,$authors,$testver);
610
611                my %build;
612                # We want to at least build for the underlying distro
613                my ($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd, $arch) = pb_distro_init();
614                my $tmpl = "$ddir-$dver-$arch,";
615                my %patches;
616
617                # Get list of distributions for which I need to generate build files
618                if (defined $pt[0]->{$ENV{'PBPROJ'}}) {
619                        $tmpl .= $pt[0]->{$ENV{'PBPROJ'}};
620                }
621                if (defined $pt[1]->{$ENV{'PBPROJ'}}) {
622                        # the 2 lists needs to be grouped with a ',' separating them
623                        if ($tmpl ne "") {
624                                $tmpl .= ",";
625                        }
626                        $tmpl .= $pt[1]->{$ENV{'PBPROJ'}} 
627                }
628       
629                # Setup %pb structure to allow filtering later on, on files using that structure
630                $pb{'tag'} = $pbtag;
631                $pb{'rev'} = $pbrev;
632                $pb{'ver'} = $pbver;
633                $pb{'pkg'} = $pbpkg;
634                $pb{'date'} = $pbdate;
635                $pb{'defpkgdir'} = $defpkgdir;
636                $pb{'extpkgdir'} = $extpkgdir;
637                $pb{'chglog'} = $chglog;
638                $pb{'packager'} = $ENV{'PBPACKAGER'};
639                $pb{'proj'} = $ENV{'PBPROJ'};
640                $pb{'repo'} = $ENV{'PBREPO'};
641                $pb{'patches'} = \%patches;
642                pb_log(2,"DEBUG: pb: ".Dumper(%pb)."\n");
643       
644                # Do not do that for website
645                if ((not defined $param) || ($param ne "Web")) {
646                        pb_log(0,"Build files are being generated for ...\n");
647                        my %virt;
648                        # De-duplicate similar VM and VE
649                        foreach my $d (split(/,/,$tmpl)) {
650                                # skip ill-formatted vms (name-ver-arch)
651                                next if ($d !~ /-/);
652                                $virt{$d} = $d;
653                        }
654
655                        foreach my $d (keys %virt) {
656                                my ($name,$ver,$arch) = split(/-/,$d);
657                                pb_log(0,"Bad format for $d") if ((not defined $name) || (not defined $ver) || (not defined $arch)) ;
658                                chomp($arch);
659                                my ($ddir, $dver, $dfam);
660                                ($ddir, $dver, $dfam, $pb{'dtype'}, $pb{'suf'}, $pb{'upd'}, $pb{'arch'}) = pb_distro_init($name,$ver,$arch);
661                                pb_log(2,"DEBUG: distro tuple: ".Dumper($ddir, $dver, $dfam, $pb{'dtype'}, $pb{'suf'})."\n");
662                                pb_log(2,"DEBUG Filtering PBDATE => $pbdate, PBTAG => $pbtag, PBVER => $pbver\n");
663       
664                                # We need to compute the real name of the package
665                                my $pbrealpkg = pb_cms_get_real_pkg($pbpkg,$pb{'dtype'});
666                                $pb{'realpkg'} = $pbrealpkg;
667                                pb_log(1,"Virtual package $pbpkg has a real package name of $pbrealpkg on $ddir-$dver\n") if ($pbrealpkg ne $pbpkg);
668       
669                                # Filter build files from the less precise up to the most with overloading
670                                # Filter all files found, keeping the name, and generating in dest
671       
672                                # Find all build files first relatively to PBROOTDIR
673                                # Find also all specific files referenced in the .pb conf file
674                                my %bfiles = ();
675                                my %pkgfiles = ();
676                                $build{"$ddir-$dver-$arch"} = "yes";
677       
678                                if (-d "$ENV{'PBROOTDIR'}/$pbpkg/$pb{'dtype'}") {
679                                        pb_list_bfiles("$ENV{'PBROOTDIR'}/$pbpkg/$pb{'dtype'}",$pbpkg,\%bfiles,\%pkgfiles,$supfiles);
680                                } elsif (-d "$ENV{'PBROOTDIR'}/$pbpkg/$dfam") {
681                                        pb_list_bfiles("$ENV{'PBROOTDIR'}/$pbpkg/$dfam",$pbpkg,\%bfiles,\%pkgfiles,$supfiles);
682                                } elsif (-d "$ENV{'PBROOTDIR'}/$pbpkg/$ddir") {
683                                        pb_list_bfiles("$ENV{'PBROOTDIR'}/$pbpkg/$ddir",$pbpkg,\%bfiles,\%pkgfiles,$supfiles);
684                                } elsif (-d "$ENV{'PBROOTDIR'}/$pbpkg/$ddir-$dver") {
685                                        pb_list_bfiles("$ENV{'PBROOTDIR'}/$pbpkg/$ddir-$dver",$pbpkg,\%bfiles,\%pkgfiles,$supfiles);
686                                } elsif (-d "$ENV{'PBROOTDIR'}/$pbpkg/$ddir-$dver-$arch") {
687                                        pb_list_bfiles("$ENV{'PBROOTDIR'}/$pbpkg/$ddir-$dver-$arch",$pbpkg,\%bfiles,\%pkgfiles,$supfiles);
688                                } else {
689                                        $build{"$ddir-$dver-$arch"} = "no";
690                                        next;
691                                }
692                                pb_log(2,"DEBUG bfiles: ".Dumper(\%bfiles)."\n");
693       
694                                # Get all filters to apply
695                                my $ptr = pb_get_filters($pbpkg, $pb{'dtype'}, $dfam, $ddir, $dver);
696       
697                                # Prepare local patches for this distro - They are always applied first - May be a problem one day
698                                foreach my $p (sort(<$ENV{'PBROOTDIR'}/$pbpkg/pbpatch/*>)) {
699                                        $patches{"$ddir-$dver-$arch"} .= "," if ((defined $patches{"$ddir-$dver-$arch"}) and ($p =~ /\.all$/));
700                                        $patches{"$ddir-$dver-$arch"} .= "file://$p" if ($p =~ /\.all$/);
701                                        $patches{"$ddir-$dver-$arch"} .= "," if ((defined $patches{"$ddir-$dver-$arch"}) and ($p =~ /\.$pb{'dtype'}$/));
702                                        $patches{"$ddir-$dver-$arch"} .= "file://$p" if ($p =~ /\.$pb{'dtype'}$/);
703                                        $patches{"$ddir-$dver-$arch"} .= "," if ((defined $patches{"$ddir-$dver-$arch"}) and ($p =~ /\.$dfam$/));
704                                        $patches{"$ddir-$dver-$arch"} .= "file://$p" if ($p =~ /\.$dfam$/);
705                                        $patches{"$ddir-$dver-$arch"} .= "," if ((defined $patches{"$ddir-$dver-$arch"}) and ($p =~ /\.$ddir$/));
706                                        $patches{"$ddir-$dver-$arch"} .= "file://$p" if ($p =~ /\.$ddir$/);
707                                        $patches{"$ddir-$dver-$arch"} .= "," if ((defined $patches{"$ddir-$dver-$arch"}) and ($p =~ /\.$ddir-$dver$/));
708                                        $patches{"$ddir-$dver-$arch"} .= "file://$p" if ($p =~ /\.$ddir-$dver$/);
709                                        $patches{"$ddir-$dver-$arch"} .= "," if ((defined $patches{"$ddir-$dver-$arch"}) and ($p =~ /\.$ddir-$dver-$arch$/));
710                                        $patches{"$ddir-$dver-$arch"} .= "file://$p" if ($p =~ /\.$ddir-$dver-$arch$/);
711                                }
712       
713                                # Prepare also remote patches to be included - Applied after the local ones
714                                foreach my $p ("all","$pb{'dtype'}","$dfam","$ddir","$ddir-$dver","$ddir-$dver-$arch") {
715                                        my $f = "$ENV{'PBROOTDIR'}/$pbpkg/pbextpatch.$p";
716                                        next if (not -f $f);
717                                        if (not open(PATCH,$f)) {
718                                                pb_display("Unable to open existing external patch file content $f\n");
719                                                next;
720                                        }
721                                        while (<PATCH>) {
722                                                chomp();
723                                                $patches{"$ddir-$dver-$arch"} .= "," if (defined $patches{"$ddir-$dver-$arch"});
724                                                $patches{"$ddir-$dver-$arch"} .= "$_";
725                                        }
726                                        close(PATCH);
727                                }
728                                pb_log(2,"DEBUG: pb->patches: ".Dumper($pb{'patches'})."\n");
729       
730                                # Apply now all the filters on all the files concerned
731                                # destination dir depends on the type of file
732                                if (defined $ptr) {
733                                        # For patch support
734                                        $pb{'tuple'} = "$ddir-$dver-$arch";
735                                        foreach my $f (values %bfiles,values %pkgfiles) {
736                                                pb_filter_file("$ENV{'PBROOTDIR'}/$f",$ptr,"$dest/pbconf/$ddir-$dver-$arch/".basename($f),\%pb);
737                                        }
738                                }
739                        }
740                        my @found;
741                        my @notfound;
742                        foreach my $b (keys %build) {
743                                push @found,$b if ($build{$b} =~ /yes/);
744                                push @notfound,$b if ($build{$b} =~ /no/);
745                        }
746                        pb_log(0," ... ".join(',',sort(@found))."\n");
747                        pb_log(0,"No Build files found for ".join(',',sort(@notfound))."\n") if (@notfound);
748                        pb_log(2,"DEBUG: patches: ".Dumper(%patches)."\n");
749                }
750
751                # Get the generic filter (all.pbf) and
752                # apply those to the non-build files including those
753                # generated by pbinit if applicable
754
755                # Get only all.pbf filter
756                my $ptr = pb_get_filters($pbpkg);
757
758                my $liste ="";
759                if (defined $filteredfiles->{$pbpkg}) {
760                        foreach my $f (split(/,/,$filteredfiles->{$pbpkg})) {
761                                pb_filter_file_inplace($ptr,"$dest/$f",\%pb);
762                                $liste = "$f $liste";
763                        }
764                }
765                pb_log(2,"Files ".$liste."have been filtered\n");
766
767                # Do not do that for website
768                if ((not defined $param) || ($param ne "Web")) {
769                        my %tmp;
770                        # Filter potential patches (local + remote)
771                        pb_log(0,"Delivering and compressing patches ");
772                        foreach my $v (keys %patches) {
773                                pb_mkdir_p("$dest/pbconf/$v/pbpatch");
774                                foreach my $pf (split(/,/,$patches{$v})) {
775                                        my $pp = basename($pf);
776                                        pb_cms_export($pf,undef,"$dest/pbconf/$v/pbpatch");
777                                        pb_filter_file_inplace($ptr,"$dest/pbconf/$v/pbpatch/$pp",\%pb);
778                                        pb_system("gzip -9f $dest/pbconf/$v/pbpatch/$pp","","quiet");
779                                        $tmp{$pf} = "";
780                                }
781                        }
782                        foreach my $v (keys %tmp) {
783                                pb_log(0,"$v ");
784                        }
785                        pb_log(0,"\n");
786                } else {
787                        # Instead call News generation
788                        pb_web_news2html($dest);
789                        # And create an empty pbconf
790                        pb_mkdir_p("$dest/pbconf");
791                        # And prepare the pbscript to execute remotely
792                        open(SCRIPT,"> $ENV{'PBDESTDIR'}/pbscript") || die "Unable to create $ENV{'PBDESTDIR'}/pbscript";
793                        print SCRIPT "#!/bin/bash\n";
794                        print SCRIPT "#set -x\n";
795                        print SCRIPT "echo ... Extracting Website content\n";
796                        print SCRIPT "find . -type f | grep -Ev '^./$pbpkg-$pbver.tar.gz|^./pbscript' | xargs rm -f non-existent\n";
797                        print SCRIPT "find * -type d -depth | xargs rmdir 2> /dev/null \n";
798                        print SCRIPT "tar xfz $pbpkg-$pbver.tar.gz\n";
799                        print SCRIPT "mv $pbpkg-$pbver/* .\n";
800                        print SCRIPT "rm -f $pbpkg-$pbver.tar.gz\n";
801                        print SCRIPT "rmdir $pbpkg-$pbver\n";
802                        close(SCRIPT);
803                }
804
805                # Prepare the dest directory for archive
806                if (-x "$ENV{'PBROOTDIR'}/$pbpkg/pbinit") {
807                        pb_filter_file("$ENV{'PBROOTDIR'}/$pbpkg/pbinit",$ptr,"$ENV{'PBTMP'}/pbinit",\%pb);
808                        chmod 0755,"$ENV{'PBTMP'}/pbinit";
809                        pb_system("cd $dest ; $ENV{'PBTMP'}/pbinit","Executing init script from $ENV{'PBROOTDIR'}/$pbpkg/pbinit","verbose");
810                }
811
812                # Do we have additional script to run to prepare the environement for the project ?
813                # Then include it in the pbconf delivery
814                foreach my $pbvf (<$ENV{'PBROOTDIR'}/pbv*.pre>,<$ENV{'PBROOTDIR'}/pbv*.post>, <$ENV{'PBROOTDIR'}/pbtest*>) {
815                        if (-x "$pbvf") {
816                                my $target = "$ENV{'PBDESTDIR'}/".basename($pbvf);
817                                pb_filter_file("$pbvf",$ptr,$target,\%pb);
818                                chmod 0755,"$target";
819                        }
820                }
821
822                # Archive dest dir
823                chdir "$ENV{'PBDESTDIR'}" || die "Unable to change dir to $ENV{'PBDESTDIR'}";
824                if (defined $preserve) {
825                        # In that case we want to preserve the original tar file for checksum purposes
826                        # The one created is btw equivalent in that case to this one
827                        # Maybe check basename of both to be sure they are the same ?
828                        pb_log(0,"Preserving original tar file ");
829                        move("$preserve","$pbpkg-$pbver.tar.gz");
830                } else {
831                        # Possibility to look at PBSRC to guess more the filename
832                        pb_system("tar cfz $pbpkg-$pbver.tar.gz --exclude=$pbpkg-$pbver/pbconf $pbpkg-$pbver","Creating $pbpkg tar files compressed");
833                }
834                pb_log(0,"Under $ENV{'PBDESTDIR'}/$pbpkg-$pbver.tar.gz\n");
835                pb_system("tar cfz $pbpkg-$pbver.pbconf.tar.gz $pbpkg-$pbver/pbconf","Creating pbconf tar files compressed");
836                pb_log(0,"Under $ENV{'PBDESTDIR'}/$pbpkg-$pbver.pbconf.tar.gz\n");
837
838                # Keep track of version-tag per pkg
839                $pkgs{$pbpkg} = "$pbver-$pbtag";
840
841                # Final cleanup
842                pb_rm_rf($dest) if (-d $dest);
843        }
844
845        # Keep track of per package version
846        pb_log(2,"DEBUG pkgs: ".Dumper(%pkgs)."\n");
847        open(PKG,"> $ENV{'PBDESTDIR'}/$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}.pb") || die "Unable to create $ENV{'PBDESTDIR'}/$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}.pb";
848        foreach my $pbpkg (keys %pkgs) {
849                print PKG "pbpkg $pbpkg = $pkgs{$pbpkg}\n";
850        }
851        close(PKG);
852
853        # Keep track of what is generated by default
854        # We need to store the dir and info on version-tag
855        # Base our content on the existing .pb file
856        copy("$ENV{'PBROOTDIR'}/$ENV{'PBPROJ'}.pb","$ENV{'PBDESTDIR'}/pbrc");
857        open(LAST,">> $ENV{'PBDESTDIR'}/pbrc") || die "Unable to create $ENV{'PBDESTDIR'}/pbrc";
858        print LAST "pbroot $ENV{'PBPROJ'} = $ENV{'PBROOTDIR'}\n";
859        print LAST "projver $ENV{'PBPROJ'} = $ENV{'PBPROJVER'}\n";
860        print LAST "projtag $ENV{'PBPROJ'} = $ENV{'PBPROJTAG'}\n";
861        print LAST "pbpackager $ENV{'PBPROJ'} = $ENV{'PBPACKAGER'}\n";
862        close(LAST);
863}
864
865sub pb_test2pkg {
866        # Get the running distro to test on
867        my ($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd, $arch) = pb_distro_init();
868        pb_log(2,"DEBUG: distro tuple: ".join(',',($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd, $arch))."\n");
869
870        # Get list of packages to test
871        # Get content saved in cms2build
872        my $ptr = pb_get_pkg();
873        @pkgs = @$ptr;
874
875        # Additional potential repo
876        pb_distro_setuprepo($ddir,$dver,$arch,$dtype);
877        foreach my $pbpkg (@pkgs) {
878                # We need to install the package to test, and deps brought with it
879                pb_distro_installdeps(undef,$dtype,$pbupd,$pbpkg);
880                pb_system("$ENV{'PBDESTDIR'}/pbtest","Launching test for $pbpkg","verbose");
881        }
882}
883
884sub pb_build2pkg {
885
886        # Get the running distro to build on
887        my ($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd, $arch) = pb_distro_init();
888        pb_log(2,"DEBUG: distro tuple: ".join(',',($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd, $arch))."\n");
889
890        # Get list of packages to build
891        my $ptr = pb_get_pkg();
892        @pkgs = @$ptr;
893
894        # Get content saved in cms2build
895        my ($pkg) = pb_conf_read("$ENV{'PBDESTDIR'}/$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}.pb","pbpkg");
896        $pkg = { } if (not defined $pkg);
897
898        chdir "$ENV{'PBBUILDDIR'}";
899        my $made = ""; # pkgs made during build
900        foreach my $pbpkg (@pkgs) {
901                my $vertag = $pkg->{$pbpkg};
902                # get the version of the current package - maybe different
903                ($pbver,$pbtag) = split(/-/,$vertag);
904
905                my $src="$ENV{'PBDESTDIR'}/$pbpkg-$pbver.tar.gz";
906                my $src2="$ENV{'PBDESTDIR'}/$pbpkg-$pbver.pbconf.tar.gz";
907                pb_log(2,"Source file: $src\n");
908                pb_log(2,"Pbconf file: $src2\n");
909
910                pb_log(2,"Working directory: $ENV{'PBBUILDDIR'}\n");
911                if ($dtype eq "rpm") {
912                        foreach my $d ('RPMS','SRPMS','SPECS','SOURCES','BUILD') {
913                                if (! -d "$ENV{'PBBUILDDIR'}/$d") {
914                                pb_mkdir_p("$ENV{'PBBUILDDIR'}/$d") || die "Please ensure that you can write into $ENV{'PBBUILDDIR'} to create $d\nchown the $ENV{'PBBUILDDIR'} directory to your uid";
915                                }
916                        }
917
918                        # Remove in case a previous link/file was there
919                        unlink "$ENV{'PBBUILDDIR'}/SOURCES/".basename($src);
920                        symlink "$src","$ENV{'PBBUILDDIR'}/SOURCES/".basename($src) || die "Unable to symlink $src in $ENV{'PBBUILDDIR'}/SOURCES";
921                        # We need to first extract the spec file
922                        my @specfile = pb_extract_build_files($src2,"$pbpkg-$pbver/pbconf/$ddir-$dver-$arch/","$ENV{'PBBUILDDIR'}/SPECS","spec");
923
924                        # We need to handle potential patches to upstream sources
925                        pb_extract_build_files($src2,"$pbpkg-$pbver/pbconf/$ddir-$dver-$arch/pbpatch/","$ENV{'PBBUILDDIR'}/SOURCES","patch");
926
927                        pb_log(2,"specfile: ".Dumper(\@specfile)."\n");
928                        # set LANGUAGE to check for correct log messages
929                        $ENV{'LANGUAGE'}="C";
930                        # Older Redhat use _target_platform in %configure incorrectly
931                        my $specialdef = "";
932                        if (($ddir eq "redhat") || (($ddir eq "rhel") && ($dver eq "2.1"))) {
933                                $specialdef = "--define \'_target_platform \"\"\'";
934                        }
935
936                        # If needed we may add repository to the build env
937                        pb_distro_setuprepo($ddir,$dver,$arch,$dtype);
938                        foreach my $f (@specfile) {
939                                if ($f =~ /\.spec$/) {
940                                        pb_distro_installdeps($f,$dtype,$pbupd);
941                                        pb_system("rpmbuild $specialdef --define \'packager $ENV{'PBPACKAGER'}\' --define \"_topdir $ENV{'PBBUILDDIR'}\" -ba $f","Building package with $f under $ENV{'PBBUILDDIR'}","verbose");
942                                        last;
943                                }
944                        }
945                        # Get the name of the generated packages
946                        open(LOG,"$ENV{'PBTMP'}/system.log") || die "Unable to open $ENV{'PBTMP'}/system.log";
947                        while (<LOG>) {
948                                chomp($_);
949                                next if ($_ !~ /^Wrote:/);
950                                s|.*/([S]*RPMS.*)|$1|;
951                                $made .=" $_";
952                        }
953                        close(LOG);
954
955                } elsif ($dtype eq "deb") {
956                        chdir "$ENV{'PBBUILDDIR'}" || die "Unable to chdir to $ENV{'PBBUILDDIR'}";
957                        pb_system("tar xfz $src","Extracting sources");
958                        pb_system("tar xfz $src2","Extracting pbconf");
959
960                        chdir "$pbpkg-$pbver" || die "Unable to chdir to $pbpkg-$pbver";
961                        pb_rm_rf("debian");
962                        symlink "pbconf/$ddir-$dver-$arch","debian" || die "Unable to symlink to pbconf/$ddir-$dver-$arch";
963                        chmod 0755,"debian/rules";
964
965                        pb_distro_setuprepo($ddir,$dver,$arch,$dtype);
966                        pb_distro_installdeps("debian/control",$dtype,$pbupd);
967                        pb_system("dpkg-buildpackage -us -uc -rfakeroot","Building package","verbose");
968                        # Get the name of the generated packages
969                        open(LOG,"$ENV{'PBTMP'}/system.log") || die "Unable to open $ENV{'PBTMP'}/system.log";
970                        while (<LOG>) {
971                                chomp();
972                                my $tmp = $_;
973                                next if ($tmp !~ /^dpkg-deb.*:/);
974                                $tmp =~ s|.*../(.*)_(.*).deb.*|$1|;
975                                $made="$made $tmp.dsc $tmp.tar.gz $tmp"."_*.deb $tmp"."_*.changes";
976                        }
977                        close(LOG);
978                } elsif ($dtype eq "ebuild") {
979                        my @ebuildfile;
980                        # For gentoo we need to take pb as subsystem name
981                        # We put every apps here under sys-apps. hope it's correct
982                        # We use pb's home dir in order to have a single OVERLAY line
983                        my $tmpd = "$ENV{'HOME'}/portage/pb/sys-apps/$pbpkg";
984                        pb_mkdir_p($tmpd) if (! -d "$tmpd");
985                        pb_mkdir_p("$ENV{'HOME'}/portage/distfiles") if (! -d "$ENV{'HOME'}/portage/distfiles");
986
987                        # We need to first extract the ebuild file
988                        @ebuildfile = pb_extract_build_files($src2,"$pbpkg-$pbver/pbconf/$ddir-$dver-$arch/","$tmpd","ebuild");
989
990                        # Prepare the build env for gentoo
991                        my $found = 0;
992                        my $pbbd = $ENV{'HOME'};
993                        $pbbd =~ s|/|\\/|g;
994                        if (-r "/etc/make.conf") {
995                                open(MAKE,"/etc/make.conf");
996                                while (<MAKE>) {
997                                        $found = 1 if (/$pbbd\/portage/);
998                                }
999                                close(MAKE);
1000                        }
1001                        if ($found == 0) {
1002                                pb_system("sudo sh -c 'echo PORTDIR_OVERLAY=\"$ENV{'HOME'}/portage\" >> /etc/make.conf'");
1003                        }
1004                        #$found = 0;
1005                        #if (-r "/etc/portage/package.keywords") {
1006                        #open(KEYW,"/etc/portage/package.keywords");
1007                        #while (<KEYW>) {
1008                        #$found = 1 if (/portage\/pb/);
1009                        #}
1010                        #close(KEYW);
1011                        #}
1012                        #if ($found == 0) {
1013                        #pb_system("sudo sh -c \"echo portage/pb >> /etc/portage/package.keywords\"");
1014                        #}
1015
1016                        # Build
1017                        foreach my $f (@ebuildfile) {
1018                                if ($f =~ /\.ebuild$/) {
1019                                        pb_distro_installdeps($f,$dtype,$pbupd);
1020                                        move($f,"$tmpd/$pbpkg-$pbver.ebuild");
1021                                        pb_system("cd $tmpd ; ebuild $pbpkg-$pbver.ebuild clean ; ebuild $pbpkg-$pbver.ebuild digest ; ebuild $pbpkg-$pbver.ebuild package","verbose");
1022                                        # Now move it where pb expects it
1023                                        pb_mkdir_p("$ENV{'PBBUILDDIR'}/portage/pb/sys-apps/$pbpkg");
1024                                        move("$tmpd/$pbpkg-$pbver.ebuild","$ENV{'PBBUILDDIR'}/portage/pb/sys-apps/$pbpkg/$pbpkg-$pbver-$pbtag.ebuild");
1025                                }
1026                        }
1027
1028                        $made="$made portage/pb/sys-apps/$pbpkg/$pbpkg-$pbver-$pbtag.ebuild";
1029                } elsif ($dtype eq "tgz") {
1030                        # Slackware family
1031                        $made="$made $pbpkg/$pbpkg-$pbver-*-$pbtag.tgz";
1032
1033                        chdir "$ENV{'PBBUILDDIR'}" || die "Unable to chdir to $ENV{'PBBUILDDIR'}";
1034                        pb_system("tar xfz $src","Extracting sources");
1035                        pb_system("tar xfz $src2","Extracting pbconf");
1036                        chdir "$pbpkg-$pbver" || die "Unable to chdir to $pbpkg-$pbver";
1037                        symlink "pbconf/$ddir-$dver-$arch","install" || die "Unable to symlink to pbconf/$ddir-$dver-$arch";
1038                        if (-x "install/pbslack") {
1039                                pb_distro_installdeps("./install/pbslack",$dtype,$pbupd);
1040                                pb_system("./install/pbslack","Building software");
1041                                pb_system("sudo /sbin/makepkg -p -l y -c y $pbpkg","Packaging $pbpkg","verbose");
1042                        }
1043                } elsif ($dtype eq "pkg") {
1044                        # Solaris
1045                        $made="$made $pbpkg-$pbver-$pbtag.pkg.gz";
1046                        my $pkgdestdir="$ENV{'PBBUILDDIR'}/install";
1047
1048                        chdir "$ENV{'PBBUILDDIR'}" || die "Unable to chdir to $ENV{'PBBUILDDIR'}";
1049                        # Will host resulting packages
1050                        pb_mkdir_p("$dtype");
1051                        pb_mkdir_p("$pkgdestdir/delivery");
1052                        pb_system("tar xfz $src","Extracting sources under $ENV{'PBBUILDDIR'}");
1053                        pb_system("tar xfz $src2","Extracting pbconf under $ENV{'PBBUILDDIR'}");
1054                        chdir "$pbpkg-$pbver" || die "Unable to chdir to $pbpkg-$pbver";
1055                        if (-f "pbconf/$ddir-$dver-$arch/pbbuild") {
1056                                chmod 0755,"pbconf/$ddir-$dver-$arch/pbbuild";
1057                                # pkginfo file is mandatory
1058                                die "Unable to find pkginfo file in pbconf/$ddir-$dver-$arch" if (! -f "pbconf/$ddir-$dver-$arch/pkginfo");
1059                                # Build
1060                                pb_system("pbconf/$ddir-$dver-$arch/pbbuild $pkgdestdir/delivery","Building software and installing under $pkgdestdir/delivery");
1061                                # Copy complementary files
1062                                if (-f "pbconf/$ddir-$dver-$arch/prototype") {
1063                                        copy("pbconf/$ddir-$dver-$arch/prototype", $pkgdestdir) 
1064                                } else {
1065                                        # No prototype provided, calculating it
1066                                        open(PROTO,"> $pkgdestdir/prototype") || die "Unable to create prototype file";
1067                                        print PROTO "i pkginfo\n";
1068                                        print PROTO "i depend\n" if (-f "pbconf/$ddir-$dver-$arch/depend");
1069                                        $ENV{'PBSOLDESTDIR'} = "$pkgdestdir/delivery";
1070                                        find(\&create_solaris_prototype, "$pkgdestdir/delivery");
1071                                }
1072                                copy("pbconf/$ddir-$dver-$arch/depend", $pkgdestdir) if (-f "pbconf/$ddir-$dver-$arch/depend");
1073                                copy("pbconf/$ddir-$dver-$arch/pkginfo", $pkgdestdir);
1074                                pb_system("cd $pkgdestdir/delivery ; pkgmk -o -f ../prototype -r $pkgdestdir/delivery -d $ENV{'PBBUILDDIR'}/$dtype","Packaging $pbpkg","verbose");
1075                                pb_system("cd $ENV{'PBBUILDDIR'}/$dtype ;  echo \"\" | pkgtrans -o -n -s $ENV{'PBBUILDDIR'}/$dtype $ENV{'PBBUILDDIR'}/$pbpkg-$pbver-$pbtag.pkg all","Transforming $pbpkg","verbose");
1076                                pb_system("cd $ENV{'PBBUILDDIR'} ;  gzip -9f $pbpkg-$pbver-$pbtag.pkg","Compressing $pbpkg-$pbver-$pbtag.pkg","verbose");
1077                        } else {
1078                                pb_log(0,"No pbconf/$ddir-$dver-$arch/pbbuild file found for $pbpkg-$pbver in \n");
1079                        }
1080                        chdir ".." || die "Unable to chdir to parent dir";
1081                        pb_system("rm -rf $pbpkg-$pbver $ENV{'PBBUILDDIR'}/$dtype $pkgdestdir", "Cleanup");
1082                } else {
1083                        die "Unknown dtype format $dtype";
1084                }
1085        }
1086        # Packages check if needed
1087        if ($dtype eq "rpm") {
1088                if (-f "/usr/bin/rpmlint") {
1089                        pb_system("rpmlint $made","Checking validity of rpms with rpmlint","verbose");
1090                } else {
1091                        pb_log(0,"rpm packages generated: $made\n");
1092                }
1093        } elsif ($dtype eq "deb") {
1094                my $made2 = "";
1095                foreach my $f (split(/ /,$made)) {
1096                        $made2 .= "../$f " if ($f =~ /\.changes$/);
1097                }
1098                if (-f "/usr/bin/lintian") {
1099                        pb_system("lintian $made2","Checking validity of debs with lintian","verbose");
1100                } else {
1101                        pb_log(0,"deb packages generated: $made2\n");
1102                }
1103        } else {
1104                pb_log(0,"No check done for $dtype yet\n");
1105                pb_log(0,"Packages generated: $made\n");
1106        }
1107
1108        # Keep track of what is generated so that we can get them back from VMs
1109        open(KEEP,"> $ENV{'PBBUILDDIR'}/pbgen-$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}") || die "Unable to create $ENV{'PBBUILDDIR'}/pbgen-$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}";
1110        print KEEP "$made\n";
1111        close(KEEP);
1112}
1113
1114sub create_solaris_prototype {
1115
1116        my $uidgid = "bin bin";
1117        my $pkgdestdir = $ENV{'PBSOLDESTDIR'};
1118
1119        return if ($_ =~ /^$pkgdestdir$/);
1120        if (-d $_) {
1121                my $n = $File::Find::name;
1122                $n =~ s~$pkgdestdir/~~;
1123                print PROTO "d none $n 0755 $uidgid\n";
1124        } elsif (-x $_) {
1125                my $n = $File::Find::name;
1126                $n =~ s~$pkgdestdir/~~;
1127                print PROTO "f none $n 0755 $uidgid\n";
1128        } elsif (-f $_) {
1129                my $n = $File::Find::name;
1130                $n =~ s~$pkgdestdir/~~;
1131                print PROTO "f none $n 0644 $uidgid\n";
1132        }
1133}
1134
1135sub pb_build2ssh {
1136        pb_send2target("Sources");
1137}
1138
1139sub pb_pkg2ssh {
1140        pb_send2target("Packages");
1141}
1142
1143# By default deliver to the the public site hosting the
1144# ftp structure (or whatever) or a VM/VE
1145sub pb_send2target {
1146
1147        my $cmt = shift;
1148        my $v = shift || undef;
1149        my $vmexist = shift || 0;                       # 0 is FALSE
1150        my $vmpid = shift || 0;                         # 0 is FALSE
1151        my $snapme = shift || 0;                        # 0 is FALSE
1152
1153        pb_log(2,"DEBUG: pb_send2target($cmt,".Dumper($v).",$vmexist,$vmpid)\n");
1154        my $host = "sshhost";
1155        my $login = "sshlogin";
1156        my $dir = "sshdir";
1157        my $port = "sshport";
1158        my $conf = "sshconf";
1159        my $tmout = undef;
1160        my $path = undef;
1161        if ($cmt =~ /^VM/) {
1162                $login = "vmlogin";
1163                $dir = "pbdefdir";
1164                # Specific VM
1165                $tmout = "vmtmout";
1166                $path = "vmpath";
1167                $host = "vmhost";
1168                $port = "vmport";
1169        } elsif ($cmt =~ /^VE/) {
1170                $login = "velogin";
1171                $dir = "pbdefdir";
1172                # Specific VE
1173                $path = "vepath";
1174                $conf = "rbsconf";
1175        } elsif ($cmt eq "Web") {
1176                $host = "websshhost";
1177                $login = "websshlogin";
1178                $dir = "websshdir";
1179                $port = "websshport";
1180        }
1181        my $cmd = "";
1182        my $src = "";
1183        my ($odir,$over,$oarch) = (undef, undef, undef);
1184        my ($ddir, $dver, $dfam, $dtype, $pbsuf);
1185
1186        if ($cmt ne "Announce") {
1187                # Get list of packages to build
1188                my $ptr = pb_get_pkg();
1189                @pkgs = @$ptr;
1190
1191                # Get the running distro to consider
1192                if (defined $v) {
1193                        ($odir,$over,$oarch) = split(/-/,$v);
1194                }
1195                ($ddir, $dver, $dfam, $dtype, $pbsuf) = pb_distro_init($odir,$over,$oarch);
1196                pb_log(2,"DEBUG: distro tuple: ".join(',',($ddir, $dver, $dfam, $dtype, $pbsuf))."\n");
1197
1198                # Get list of packages to build
1199                # Get content saved in cms2build
1200                my ($pkg) = pb_conf_read("$ENV{'PBDESTDIR'}/$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}.pb","pbpkg");
1201                $pkg = { } if (not defined $pkg);
1202
1203                chdir "$ENV{'PBBUILDDIR'}";
1204                foreach my $pbpkg (@pkgs) {
1205                        my $vertag = $pkg->{$pbpkg};
1206                        # get the version of the current package - maybe different
1207                        ($pbver,$pbtag) = split(/-/,$vertag);
1208
1209                        if (($cmt eq "Sources") || ($cmt =~ /V[EM]build/)) {
1210                                $src = "$src $ENV{'PBDESTDIR'}/$pbpkg-$pbver.tar.gz $ENV{'PBDESTDIR'}/$pbpkg-$pbver.pbconf.tar.gz";
1211                                if ($cmd eq "") {
1212                                        $cmd = "ln -sf $pbpkg-$pbver.tar.gz $pbpkg-latest.tar.gz";
1213                                } else {
1214                                        $cmd = "$cmd ; ln -sf $pbpkg-$pbver.tar.gz $pbpkg-latest.tar.gz";
1215                                }
1216                        } elsif ($cmt eq "Web") {
1217                                $src = "$src $ENV{'PBDESTDIR'}/$pbpkg-$pbver.tar.gz"
1218                        }
1219                }
1220                # Adds conf file for availability of conf elements
1221                pb_conf_add("$ENV{'PBROOTDIR'}/$ENV{'PBPROJ'}.pb");
1222        }
1223
1224        if ($cmt =~ /V[EM]build/) {
1225                $src="$src $ENV{'PBROOTDIR'}/$ENV{'PBPROJ'}.pb $ENV{'PBDESTDIR'}/$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}.pb $ENV{'PBETC'} $ENV{'PBDESTDIR'}/pbrc $ENV{'PBDESTDIR'}/pbscript";
1226        } elsif (($cmt =~ /V[EM]Script/) || ($cmt eq "Web")) {
1227                $src="$src $ENV{'PBDESTDIR'}/pbscript";
1228        } elsif ($cmt =~ /V[EM]test/) {
1229                $src="$src $ENV{'PBROOTDIR'}/$ENV{'PBPROJ'}.pb $ENV{'PBDESTDIR'}/$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}.pb $ENV{'PBETC'} $ENV{'PBDESTDIR'}/pbrc $ENV{'PBDESTDIR'}/pbscript $ENV{'PBDESTDIR'}/pbtest";
1230        } elsif ($cmt eq "Announce") {
1231                $src="$src $ENV{'PBTMP'}/pbscript";
1232        } elsif ($cmt eq "Packages") {
1233                # Get package list from file made during build2pkg
1234                open(KEEP,"$ENV{'PBBUILDDIR'}/pbgen-$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}") || die "Unable to read $ENV{'PBBUILDDIR'}/pbgen-$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}";
1235                $src = <KEEP>;
1236                chomp($src);
1237                close(KEEP);
1238                $src="$src $ENV{'PBBUILDDIR'}/pbscript";
1239        }
1240        # Remove potential leading spaces (cause problem with basename)
1241        $src =~ s/^ *//;
1242        my $basesrc = "";
1243        foreach my $i (split(/ +/,$src)) {
1244                $basesrc .= " ".basename($i);
1245        }
1246
1247        pb_log(0,"Sources handled ($cmt): $src\n");
1248        pb_log(2,"values: ".Dumper(($host,$login,$dir,$port,$tmout,$path,$conf))."\n");
1249        my ($sshhost,$sshlogin,$sshdir,$sshport) = pb_conf_get($host,$login,$dir,$port);
1250        # Not mandatory...
1251        my ($rbsconf,$testver) = pb_conf_get_if($conf,"testver");
1252        my ($vtmout,$vepath);
1253        # ...Except those in virtual context
1254        if ($cmt =~ /^VE/) {
1255                ($vepath) = pb_conf_get($path);
1256        }
1257        if ($cmt =~ /^VM/) {
1258                ($vtmout) = pb_conf_get($tmout);
1259        }
1260        pb_log(2,"ssh: ".Dumper(($sshhost,$sshlogin,$sshdir,$sshport,$vtmout,$vepath,$rbsconf))."\n");
1261
1262        my $mac;
1263        if ($cmt !~ /^VE/) {
1264                $mac = "$sshlogin->{$ENV{'PBPROJ'}}\@$sshhost->{$ENV{'PBPROJ'}}";
1265                # Overwrite account value if passed as parameter
1266                $mac = "$pbaccount\@$sshhost->{$ENV{'PBPROJ'}}" if (defined $pbaccount);
1267                pb_log(2, "DEBUG: pbaccount: $pbaccount => mac: $mac\n") if (defined $pbaccount);
1268        } else {
1269                # VE
1270                # Overwrite account value if passed as parameter (typically for setup2ve)
1271                $mac = $sshlogin->{$ENV{'PBPROJ'}};
1272                $mac = $pbaccount if (defined $pbaccount);
1273        }
1274
1275        my $tdir;
1276        my $bdir;
1277        if (($cmt eq "Sources") || ($cmt =~ /V[EM]Script/)) {
1278                $tdir = $sshdir->{$ENV{'PBPROJ'}}."/src";
1279                if ((defined $testver) && (defined $testver->{$ENV{'PBPROJ'}}) && ($testver->{$ENV{'PBPROJ'}} =~ /true/i)) {
1280                        # This is a test pkg => target dir is under test
1281                        $tdir = $sshdir->{$ENV{'PBPROJ'}}."/test/src";
1282                }
1283        } elsif (($cmt =~ /V[EM]build/)  || ($cmt =~ /V[EM]test/)) {
1284                $tdir = $sshdir->{$ENV{'PBPROJ'}}."/$ENV{'PBPROJ'}/delivery";
1285                $bdir = $sshdir->{$ENV{'PBPROJ'}}."/$ENV{'PBPROJ'}/build";
1286                # Remove a potential $ENV{'HOME'} as bdir should be relative to pb's home
1287                $bdir =~ s|\$ENV.+\}/||;
1288        } elsif ($cmt eq "Announce") {
1289                $tdir = "$sshdir->{$ENV{'PBPROJ'}}";
1290                if ((defined $testver) && (defined $testver->{$ENV{'PBPROJ'}}) && ($testver->{$ENV{'PBPROJ'}} =~ /true/i)) {
1291                        # This is a test pkg => target dir is under test
1292                        $tdir = $sshdir->{$ENV{'PBPROJ'}}."/test";
1293                }
1294        } elsif ($cmt eq "Web") {
1295                $tdir = "$sshdir->{$ENV{'PBPROJ'}}";
1296                if ((defined $testver) && (defined $testver->{$ENV{'PBPROJ'}}) && ($testver->{$ENV{'PBPROJ'}} =~ /true/i)) {
1297                        # This is a test website => target dir is under test
1298                        $tdir = $sshdir->{$ENV{'PBPROJ'}}."/../test";
1299                }
1300        } elsif ($cmt eq "Packages") {
1301                $tdir = $sshdir->{$ENV{'PBPROJ'}}."/$ddir/$dver";
1302
1303                if ((defined $testver) && (defined $testver->{$ENV{'PBPROJ'}}) && ($testver->{$ENV{'PBPROJ'}} =~ /true/i)) {
1304                        # This is a test pkg => target dir is under test
1305                        $tdir = $sshdir->{$ENV{'PBPROJ'}}."/test/$ddir/$dver";
1306                }
1307
1308                my $repodir = $tdir;
1309                $repodir =~ s|^$sshdir->{$ENV{'PBPROJ'}}/||;
1310
1311                my ($pbrepo) = pb_conf_get("pbrepo");
1312
1313                # Repository management
1314                open(PBS,"> $ENV{'PBBUILDDIR'}/pbscript") || die "Unable to create $ENV{'PBBUILDDIR'}/pbscript";
1315                if ($dtype eq "rpm") {
1316                        # Also make a pbscript to generate yum/urpmi bases
1317                        print PBS << "EOF";
1318#!/bin/bash
1319# Prepare a script to ease yum setup
1320cat > $ENV{'PBPROJ'}.repo << EOT
1321[$ENV{'PBPROJ'}]
1322name=$ddir $dver - $ENV{'PBPROJ'} Vanilla Packages
1323baseurl=$pbrepo->{$ENV{'PBPROJ'}}/$repodir
1324enabled=1
1325gpgcheck=0
1326EOT
1327chmod 644 $ENV{'PBPROJ'}.repo
1328
1329# Clean up old repo content
1330rm -rf headers/ repodata/
1331# Create yum repo
1332yum-arch .
1333# Create repodata
1334createrepo .
1335EOF
1336                        if ($dfam eq "md") {
1337                                # For Mandriva add urpmi management
1338                                print PBS << "EOF";
1339# Prepare a script to ease urpmi setup
1340cat > $ENV{'PBPROJ'}.addmedia << EOT
1341urpmi.addmedia $ENV{'PBPROJ'} $pbrepo->{$ENV{'PBPROJ'}}/$repodir with media_info/hdlist.cz
1342EOT
1343chmod 755 $ENV{'PBPROJ'}.addmedia
1344
1345# Clean up old repo content
1346rm -f hdlist.cz synthesis.hdlist.cz
1347# Create urpmi repo
1348genhdlist2 --clean .
1349if [ \$\? -ne 0 ]; then
1350        genhdlist .
1351fi
1352EOF
1353                        }
1354                        if ($ddir eq "fedora") {
1355                                # Extract the spec file to please Fedora maintainers :-(
1356                                print PBS << "EOF";
1357for p in $basesrc; do
1358        echo \$p | grep -q 'src.rpm'
1359        if [ \$\? -eq 0 ]; then
1360                rpm2cpio \$p | cpio -ivdum --quiet '*.spec'
1361        fi
1362done
1363EOF
1364                        }
1365                } elsif ($dtype eq "deb") {
1366                        # Also make a pbscript to generate apt bases
1367                        # Cf: http://www.debian.org/doc/manuals/repository-howto/repository-howto.fr.html
1368                        my $rpd = dirname("$pbrepo->{$ENV{'PBPROJ'}}/$repodir");
1369                        print PBS << "EOF";
1370#!/bin/bash
1371# Prepare a script to ease apt setup
1372cat > $ENV{'PBPROJ'}.sources.list << EOT
1373deb $rpd $dver contrib
1374deb-src $rpd $dver contrib
1375EOT
1376chmod 644 $ENV{'PBPROJ'}.sources.list
1377
1378# Prepare a script to create apt info file
1379(cd .. ; for a in i386 amd64 ia64; do mkdir -p dists/$dver/contrib/binary-\$a; dpkg-scanpackages -a\$a $dver /dev/null | gzip -c9 > dists/$dver/contrib/binary-\$a/Packages.gz; done; mkdir -p dists/$dver/contrib/source; dpkg-scansources $dver /dev/null | gzip -c9 > dists/$dver/contrib/source/Sources.gz)
1380#(cd .. ; rm -f dists/$dver/Release ; apt-ftparchive release dists/$dver > dists/$dver/Release; gpg --sign -ba -o dists/$dver/Release.gpg dists/$dver/Release)
1381EOF
1382                } elsif ($dtype eq "ebuild") {
1383                        # make a pbscript to generate links to latest version
1384                        print PBS << "EOF";
1385#!/bin/bash
1386# Prepare a script to create correct links
1387for p in $src; do
1388        echo \$p | grep -q '.ebuild'
1389        if [ \$\? -eq 0 ]; then
1390                j=`basename \$p`
1391                pp=`echo \$j | cut -d'-' -f1`
1392                ln -sf \$j \$pp.ebuild
1393        fi
1394done
1395EOF
1396                }
1397                close(PBS);
1398                chmod 0755,"$ENV{'PBBUILDDIR'}/pbscript";
1399        } else {
1400                return;
1401        }
1402
1403        # Useless for VE
1404        my $nport;
1405        if ($cmt !~ /^VE/) {
1406                $nport = $sshport->{$ENV{'PBPROJ'}};
1407                $nport = "$pbport" if (defined $pbport);
1408        }
1409
1410        # Remove a potential $ENV{'HOME'} as tdir should be relative to pb's home
1411        $tdir =~ s|\$ENV.+\}/||;
1412
1413        my $tm = undef;
1414        if ($cmt =~ /^VM/) {
1415                $tm = $vtmout->{$ENV{'PBPROJ'}};
1416        }
1417
1418        # ssh communication if not VE
1419        # should use a hash instead...
1420        my ($shcmd,$cpcmd,$cptarget,$cp2target);
1421        if ($cmt !~ /^VE/) {
1422                my $keyfile = pb_ssh_get(0);
1423                $shcmd = "ssh -i $keyfile -q -o UserKnownHostsFile=/dev/null -p $nport $mac";
1424                $cpcmd = "scp -i $keyfile -p -o UserKnownHostsFile=/dev/null -P $nport";
1425                $cptarget = "$mac:$tdir";
1426                if ($cmt =~ /^VMbuild/) {
1427                        $cp2target = "$mac:$bdir";
1428                }
1429        } else {
1430                my $tp = $vepath->{$ENV{'PBPROJ'}};
1431                ($odir,$over,$oarch) = split(/-/,$v);
1432                my $tpdir = "$tp/$odir/$over/$oarch";
1433                my ($ptr) = pb_conf_get("vetype");
1434                my $vetype = $ptr->{$ENV{'PBPROJ'}};
1435                if ($vetype eq "chroot") {
1436                        $shcmd = "sudo chroot $tpdir /bin/su - $mac -c ";
1437                } elsif ($vetype eq "schroot") {
1438                        $shcmd = "schroot $tp -u $mac -- ";
1439                }
1440                $cpcmd = "sudo cp -r ";
1441                # We need to get the home dir of the target account to deliver in the right place
1442                open(PASS,"$tpdir/etc/passwd") || die "Unable to open $tpdir/etc/passwd";
1443                my $homedir = "";
1444                while (<PASS>) {
1445                        my ($c1,$c2,$c3,$c4,$c5,$c6,$c7) = split(/:/);
1446                        $homedir = $c6 if ($c1 =~ /^$mac$/);
1447                        pb_log(3,"Homedir: $homedir - account: $c6\n");
1448                }
1449                close(PASS);
1450                $cptarget = "$tpdir/$homedir/$tdir";
1451                if ($cmt eq "VEbuild") {
1452                        $cp2target = "$tpdir/$homedir/$bdir";
1453                }
1454                pb_log(2,"On VE using $cptarget as target dir to copy to\n");
1455        }
1456
1457        my $logres = "";
1458        # Do not touch when just announcing
1459        if ($cmt ne "Announce") {
1460                pb_system("$shcmd \"mkdir -p $tdir ; cd $tdir ; echo \'for i in $basesrc; do if [ -f \$i ]; then rm -f \$i; fi; done\ ; $cmd' | bash\"","Preparing $tdir on $cptarget");
1461        } else {
1462                $logres = "> ";
1463        }
1464        pb_system("cd $ENV{'PBBUILDDIR'} ; $cpcmd $src $cptarget 2> /dev/null","$cmt delivery in $cptarget");
1465
1466        # For VE we need to change the owner manually
1467        if ($cmt =~ /^VE/) {
1468                pb_system("$shcmd \"sudo chown -R $mac $tdir\"","Adapt owner in $tdir to $mac");
1469        }
1470
1471        pb_system("$shcmd \"echo \'cd $tdir ; if [ -x pbscript ]; then ./pbscript; fi ; rm -f ./pbscript\' | bash\"","Executing pbscript on $cptarget if needed","verbose");
1472        if ($cmt =~ /^V[EM]build/) {
1473                # Get back info on pkg produced, compute their name and get them from the VM
1474                pb_system("$cpcmd $cp2target/pbgen-$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'} $ENV{'PBBUILDDIR'} 2> /dev/null","Get package names in $cp2target");
1475                # For VE we need to change the owner manually
1476                if ($cmt eq "VEbuild") {
1477                        pb_system("sudo chown $UID $ENV{'PBBUILDDIR'}/pbgen-$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}","Adapt owner in $tdir to $UID");
1478                }
1479                # return here to avoid breaking the load on VMs/VEs in case of a bad one
1480                if (not -f "$ENV{'PBBUILDDIR'}/pbgen-$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}") {
1481                        pb_log(0,"Problem with VM $v on $ENV{'PBBUILDDIR'}/pbgen-$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}");
1482                        return;
1483                }
1484                open(KEEP,"$ENV{'PBBUILDDIR'}/pbgen-$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}") || die "Unable to read $ENV{'PBBUILDDIR'}/pbgen-$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}";
1485                my $src = <KEEP>;
1486                chomp($src);
1487                close(KEEP);
1488                $src =~ s/^ *//;
1489                pb_mkdir_p("$ENV{'PBBUILDDIR'}/$odir/$over");
1490                # Change pgben to make the next send2target happy
1491                my $made = "";
1492                open(KEEP,"> $ENV{'PBBUILDDIR'}/pbgen-$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}") || die "Unable to write $ENV{'PBBUILDDIR'}/pbgen-$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}";
1493                foreach my $p (split(/ +/,$src)) {
1494                        my $j = basename($p);
1495                        # For VM we don't want shell expansion to hapen locally but remotely
1496                        my $delim = '\'';
1497                        if ($cmt =~ /^VEbuild/) {
1498                                # For VE we need to support shell expansion locally
1499                                $delim = "";
1500                        }       
1501                        pb_system("$cpcmd $cp2target/$delim$p$delim $ENV{'PBBUILDDIR'}/$odir/$over 2> /dev/null","Recovery of package $j in $ENV{'PBBUILDDIR'}/$odir/$over");
1502                        $made="$made $odir/$over/$j"; # if (($dtype ne "rpm") || ($j !~ /.src.rpm$/));
1503                }
1504                print KEEP "$made\n";
1505                close(KEEP);
1506                pb_system("$shcmd \"rm -rf $tdir $bdir\"","$cmt cleanup");
1507
1508                # Sign packages locally
1509                if ($dtype eq "rpm") {
1510                                #pb_system("rpm --addsign --define \'_signature gpg\' --define \'__gpg_sign_cmd /usr/bin/gpg --batch --no-verbose --no-armor --no-secmem-warning -u \"$ENV{'PBPACKAGER'}\" -sbo %{__signature_filename} %{__plaintext_filename} --use-agent\' $made","Signing RPM packages packages");
1511                } elsif ($dtype eq "deb") {
1512                                #pb_system("debsign $made","Signing DEB packages");
1513                } else {
1514                        pb_log(0,"I don't know yet how to sign packages for type $dtype.\nPlease give feedback to dev team\n");
1515                }
1516
1517                # We want to send them to the ssh account so overwrite what has been done before
1518                undef $pbaccount;
1519                pb_log(2,"Before sending pkgs, vmexist: $vmexist, vmpid: $vmpid\n");
1520                pb_send2target("Packages",$odir."-".$over."-".$oarch,$vmexist,$vmpid);
1521                pb_rm_rf("$ENV{'PBBUILDDIR'}/$odir");
1522        }
1523        pb_log(2,"Before halt, vmexist: $vmexist, vmpid: $vmpid\n");
1524        if ((! $vmexist) && ($cmt =~ /^VM/)) {
1525                # If in setupvm then takes a snapshot just before halting
1526                if ($snapme != 0) {
1527                        my ($vmmonport,$vmtype) = pb_conf_get("vmmonport","vmtype");
1528                        # For monitoring control
1529                        if ((($vmtype->{$ENV{'PBPROJ'}}) eq "kvm") || (($vmtype->{$ENV{'PBPROJ'}}) eq "qemu")) {
1530                                require Net::Telnet;
1531                                my $t = new Net::Telnet (Timeout => 120, Host => "localhost", Port => $vmmonport->{$ENV{'PBPROJ'}}) || die "Unable to dialog on the monitor";
1532                                # move to monitor mode
1533                                my @lines = $t->cmd("c");
1534                                # Create a snapshot named pb
1535                                @lines = $t->cmd("savevm pb");
1536                                # Write the new status in the VM
1537                                @lines = $t->cmd("commit all");
1538                                # End
1539                                @lines = $t->cmd("quit");
1540                        }
1541                }
1542                my $hoption = "-p";
1543                # Solaris doesn't support -h
1544                $hoption = "" if ($dtype eq "pkg");
1545                pb_system("$shcmd \"sudo /sbin/halt $hoption \"; sleep $tm ; echo \'if [ -d /proc/$vmpid ]; then kill -9 $vmpid; fi \' | bash ; sleep 10","VM $v halt (pid $vmpid)");
1546        }
1547        if (($cmt =~ /^VE/) && ($snapme != 0)) {
1548                ($odir,$over,$oarch) = split(/-/,$v);
1549                my $tpdir = "$vepath->{$ENV{'PBPROJ'}}/$odir/$over/$oarch";
1550                pb_system("sudo tar cz -f $vepath->{$ENV{'PBPROJ'}}/$odir-$over-$oarch.tar.gz -C $tpdir .","Creating a snapshot of $tpdir");
1551        }
1552}
1553
1554sub pb_script2v {
1555        my $pbscript=shift;
1556        my $vtype=shift;
1557        my $pbforce=shift || 0; # Force stop of VM. Default not
1558        my $vm1=shift || undef; # Only that VM to treat
1559        my $snapme=shift || 0;  # Do we have to create a snapshot
1560        my $vm;
1561        my $all;
1562
1563        pb_log(2,"DEBUG: pb_script2v($pbscript,$vtype,$pbforce,".Dumper($vm1).",$snapme)\n");
1564        # Prepare the script to be executed on the VM
1565        # in $ENV{'PBDESTDIR'}/pbscript
1566        if ((defined $pbscript ) && ($pbscript ne "$ENV{'PBDESTDIR'}/pbscript")) {
1567                copy($pbscript,"$ENV{'PBDESTDIR'}/pbscript") || die "Unable to create $ENV{'PBDESTDIR'}/pbscript";
1568                chmod 0755,"$ENV{'PBDESTDIR'}/pbscript";
1569        }
1570
1571        if (not defined $vm1) {
1572                ($vm,$all) = pb_get2v($vtype);
1573        } else {
1574                @$vm = ($vm1);
1575        }
1576        my ($vmexist,$vmpid) = (undef,undef);
1577
1578        foreach my $v (@$vm) {
1579                # Launch VM/VE
1580                ($vmexist,$vmpid) = pb_launchv($vtype,$v,0,$snapme,$pbsnap);
1581
1582                if ($vtype eq "vm") {
1583                        pb_log(2,"DEBUG: After pb_launchv, vmexist: $vmexist, vmpid: $vmpid\n");
1584
1585                        # Skip that VM if something went wrong
1586                        next if (($vmpid == 0) && ($vmexist == 0));
1587
1588                        # If force stopping the VM then reset vmexist
1589                        if ($pbforce == 1) {
1590                                $vmpid = $vmexist;
1591                                $vmexist = 0;
1592                        }
1593                } else {
1594                        #VE
1595                        $vmexist = 0;
1596                        $vmpid = 0;
1597                }
1598
1599                # Gather all required files to send them to the VM
1600                # and launch the build through pbscript
1601                pb_log(2,"DEBUG: Before send2target, vmexist: $vmexist, vmpid: $vmpid\n");
1602                pb_send2target(uc($vtype)."Script","$v",$vmexist,$vmpid,$snapme);
1603
1604        }
1605}
1606
1607sub pb_launchv {
1608        my $vtype = shift;
1609        my $v = shift;
1610        my $create = shift || 0;                # By default do not create a VM/VE
1611        my $snapme = shift || 0;                # By default do not snap a VM/VE
1612        my $usesnap = shift || 1;               # By default study the usage of the snapshot feature of VM/VE
1613
1614        # If creation or snapshot creation mode, no snapshot usable
1615        if (($create == 1) || ($snapme == 1)) {
1616                $usesnap = 0;
1617        }
1618
1619        pb_log(2,"DEBUG: pb_launchv($vtype,$v,$create,$snapme,$usesnap)\n");
1620        die "No VM/VE defined, unable to launch" if (not defined $v);
1621        # Keep only the first VM in case many were given
1622        $v =~ s/,.*//;
1623
1624        my $arch = pb_get_arch();
1625
1626        # Launch the VMs/VEs
1627        if ($vtype eq "vm") {
1628                die "-i iso parameter needed" if (((not defined $iso) || ($iso eq "")) && ($create != 0));
1629
1630                # TODO: vmmonport should be optional
1631                my ($ptr,$vmpath,$vmport,$vmsize,$vmmonport) = pb_conf_get("vmtype","vmpath","vmport","vmsize","vmmonport");
1632                my ($vmopt,$vmtmout,$vmsnap) = pb_conf_get_if("vmopt","vmtmout","vmsnap");
1633
1634                my $vmtype = $ptr->{$ENV{'PBPROJ'}};
1635                if (not defined $ENV{'PBVMOPT'}) {
1636                        $ENV{'PBVMOPT'} = "";
1637                }
1638                # Save the current status for later restoration
1639                $ENV{'PBOLDVMOPT'} = $ENV{'PBVMOPT'};
1640                # Set a default timeout of 2 minutes
1641                if (not defined $ENV{'PBVMTMOUT'}) {
1642                        $ENV{'PBVMTMOUT'} = "120";
1643                }
1644                if (defined $vmopt->{$v}) {
1645                        $ENV{'PBVMOPT'} .= " $vmopt->{$v}" if ($ENV{'PBVMOPT'} !~ / $vmopt->{$v}/);
1646                } elsif (defined $vmopt->{$ENV{'PBPROJ'}}) {
1647                        $ENV{'PBVMOPT'} .= " $vmopt->{$ENV{'PBPROJ'}}" if ($ENV{'PBVMOPT'} !~ / $vmopt->{$ENV{'PBPROJ'}}/);
1648                }
1649
1650                # Are we allowed to use snapshot feature
1651                if ($usesnap == 1) {
1652                        if ((defined $vmsnap->{$v}) && ($vmsnap->{$v} =~ /true/i)) {
1653                                $ENV{'PBVMOPT'} .= "-snapshot ";
1654                        } elsif ((defined $vmsnap->{$ENV{'PBPROJ'}}) && ($vmsnap->{$ENV{'PBPROJ'}} =~ /true/i)) {
1655                                $ENV{'PBVMOPT'} .= "-snapshot ";
1656                        } elsif ($pbsnap eq 1) {
1657                                $ENV{'PBVMOPT'} .= "-snapshot ";
1658                        }
1659                } 
1660                if ($snapme != 0) {
1661                        if (($vmtype eq "kvm") || ($vmtype eq "qemu")) {
1662                                # Configure the monitoring to automize the creation of the 'pb' snapshot
1663                                $ENV{'PBVMOPT'} .= "-serial mon:telnet::$vmmonport->{$ENV{'PBPROJ'}},server,nowait ";
1664                                # In that case no snapshot call needed
1665                                $ENV{'PBVMOPT'} =~ s/-snapshot //;
1666                        }
1667                }
1668                if (defined $vmtmout->{$v}) {
1669                        $ENV{'PBVMTMOUT'} = $vmtmout->{$v};
1670                } elsif (defined $vmtmout->{$ENV{'PBPROJ'}}) {
1671                        $ENV{'PBVMTMOUT'} = $vmtmout->{$ENV{'PBPROJ'}};
1672                }
1673                my $nport = $vmport->{$ENV{'PBPROJ'}};
1674                $nport = "$pbport" if (defined $pbport);
1675       
1676                my $cmd;
1677                my $vmcmd;              # has to be used for pb_check_ps
1678                my $vmm;                # has to be used for pb_check_ps
1679                if (($vmtype eq "qemu") || ($vmtype eq "kvm")) {
1680                        my $qemucmd32;
1681                        my $qemucmd64;
1682                        if ($arch eq "x86_64") {
1683                                $qemucmd32 = "/usr/bin/qemu-system-i386";
1684                                $qemucmd64 = "/usr/bin/qemu";
1685                        } else {
1686                                $qemucmd32 = "/usr/bin/qemu";
1687                                $qemucmd64 = "/usr/bin/qemu-system-x86_64";
1688                        }
1689                        if ($v =~ /x86_64/) {
1690                                        $vmcmd = "$qemucmd64";
1691                                } else {
1692                                        $vmcmd = "$qemucmd32";
1693                                }
1694                        if ($vmtype eq "kvm") {
1695                                $vmcmd = "/usr/bin/kvm";
1696                                }
1697                        $vmm = "$vmpath->{$ENV{'PBPROJ'}}/$v.qemu";
1698                        if (($create != 0) || (defined $iso)) {
1699                                $ENV{'PBVMOPT'} .= " -cdrom $iso -boot d";
1700                        }
1701                        # Always redirect the network and always try to use a 'pb' snapshot
1702                        $cmd = "$vmcmd $ENV{'PBVMOPT'} -redir tcp:$nport:10.0.2.15:22 -loadvm pb $vmm"
1703                } elsif ($vmtype eq "xen") {
1704                } elsif ($vmtype eq "vmware") {
1705                } else {
1706                        die "VM of type $vmtype not supported. Report to the dev team";
1707                }
1708                # Restore the ENV VAR Value
1709                $ENV{'PBVMOPT'} = $ENV{'PBOLDVMOPT'};
1710
1711                my ($tmpcmd,$void) = split(/ +/,$cmd);
1712                my $vmexist = pb_check_ps($tmpcmd,$vmm);
1713                my $vmpid = 0;
1714                if (! $vmexist) {
1715                        if ($create != 0) {
1716                                die("Found an existing Virtual machine $vmm. Won't overwrite") if (-r $vmm);
1717                                if (($vmtype eq "qemu") || ($vmtype eq "xen") || ($vmtype eq "kvm")) {
1718                                        pb_system("/usr/bin/qemu-img create -f qcow2 $vmm $vmsize->{$ENV{'PBPROJ'}}","Creating the QEMU VM");
1719                                } elsif ($vmtype eq "vmware") {
1720                                } else {
1721                                }
1722                        }
1723                        if (! -f "$vmm") {
1724                                pb_log(0,"Unable to find VM $vmm\n");
1725                        } else {
1726                                pb_system("$cmd &","Launching the VM $vmm");
1727                                pb_system("sleep $ENV{'PBVMTMOUT'}","Waiting $ENV{'PBVMTMOUT'} s for VM $v to come up");
1728                                $vmpid = pb_check_ps($tmpcmd,$vmm);
1729                                pb_log(0,"VM $vmm launched (pid $vmpid)\n");
1730                        }
1731                } else {
1732                        pb_log(0,"Found an existing VM $vmm (pid $vmexist)\n");
1733                }
1734                pb_log(2,"DEBUG: pb_launchv returns ($vmexist,$vmpid)\n");
1735                return($vmexist,$vmpid);
1736        # VE here
1737        } else {
1738                # Get distro context
1739                my ($name,$ver,$darch) = split(/-/,$v);
1740                chomp($darch);
1741                my ($ddir, $dver, $dfam, $dtype, $pbsuf) = pb_distro_init($name,$ver,$darch);
1742
1743                # Get VE context
1744                my ($ptr,$vepath) = pb_conf_get("vetype","vepath");
1745                my $vetype = $ptr->{$ENV{'PBPROJ'}};
1746
1747                # We can probably only get those params now we have the distro context
1748                my ($rbsb4pi,$rbspi,$vesnap,$oscodename,$osmindep,$verebuild,$rbsmirrorsrv) = pb_conf_get_if("rbsb4pi","rbspi","vesnap","oscodename","osmindep","verebuild","rbsmirrorsrv");
1749
1750                # We need to avoid umask propagation to the VE
1751                umask 0022;
1752
1753                if (($vetype eq "chroot") || ($vetype eq "schroot")) {
1754                        # Architecture consistency
1755                        if ($arch ne $darch) {
1756                                die "Unable to launch a VE of architecture $darch on a $arch platform" if (($darch eq "x86_64") && ($arch =~ /i?86/));
1757                        }
1758
1759                        my ($verpmtype,$vedebtype) = pb_conf_get("verpmtype","vedebtype");
1760                        if (($create != 0) || ((defined $verebuild) && ($verebuild->{$ENV{'PBPROJ'}} =~ /true/i)) || ($pbforce == 1)) {
1761                                my ($rbsopt1) = pb_conf_get_if("rbsopt");
1762
1763                                # We have to rebuild the chroot
1764                                if ($dtype eq "rpm") {
1765
1766                                        # Which tool is used
1767                                        my $verpmstyle = $verpmtype->{$ENV{'PBPROJ'}};
1768
1769                                        # Get potential rbs option
1770                                        my $rbsopt = "";
1771                                        if (defined $rbsopt1) {
1772                                                if (defined $rbsopt1->{$verpmstyle}) {
1773                                                        $rbsopt = $rbsopt1->{$verpmstyle};
1774                                                } elsif (defined $rbsopt1->{$ENV{'PBPROJ'}}) {
1775                                                        $rbsopt = $rbsopt1->{$ENV{'PBPROJ'}};
1776                                                } else {
1777                                                        $rbsopt = "";
1778                                                }
1779                                        }
1780
1781                                        my $postinstall = pb_get_postinstall($ddir,$dver,$darch,$rbspi,$verpmstyle);
1782                                        if ($verpmstyle eq "rinse") {
1783                                                # Need to reshape the mirrors generated with local before-post-install script
1784                                                my $b4post = "--before-post-install ";
1785                                                my $postparam = pb_distro_get_param($ddir,$dver,$darch,$rbsb4pi);
1786                                                if ($postparam eq "") {
1787                                                        $b4post = "";
1788                                                } else {
1789                                                        $b4post .= $postparam;
1790                                                }
1791
1792                                                # Need to reshape the package list for pb
1793                                                my $addpkgs;
1794                                                $postparam = "";
1795                                                $postparam .= pb_distro_get_param($ddir,$dver,$darch,$osmindep);
1796                                                if ($postparam eq "") {
1797                                                        $addpkgs = "";
1798                                                } else {
1799                                                        my $pkgfile = "$ENV{'PBTMP'}/addpkgs.lis";
1800                                                        open(PKG,"> $pkgfile") || die "Unable to create $pkgfile";
1801                                                        foreach my $p (split(/,/,$postparam)) {
1802                                                                print PKG "$p\n";
1803                                                        }
1804                                                        close(PKG);
1805                                                        $addpkgs = "--add-pkg-list $pkgfile";
1806                                                }
1807
1808                                                my $rinseverb = "";
1809                                                $rinseverb = "--verbose" if ($pbdebug gt 0);
1810                                                my ($rbsconf) = pb_conf_get("rbsconf");
1811
1812                                                pb_system("sudo /usr/sbin/rinse --directory \"$vepath->{$ENV{'PBPROJ'}}/$ddir/$dver/$darch\" --arch \"$darch\" --distribution \"$ddir-$dver\" --config \"$rbsconf->{$ENV{'PBPROJ'}}\" $b4post $postinstall $rbsopt $addpkgs $rinseverb","Creating the rinse VE for $ddir-$dver ($darch)", "verbose");
1813                                        } elsif ($verpmstyle eq "rpmbootstrap") {
1814                                                my $rbsverb = "";
1815                                                foreach my $i (1..$pbdebug) {
1816                                                        $rbsverb .= " -v";
1817                                                }
1818                                                my $addpkgs = "";
1819                                                my $postparam = "";
1820                                                $postparam .= pb_distro_get_param($ddir,$dver,$darch,$osmindep);
1821                                                if ($postparam eq "") {
1822                                                        $addpkgs = "";
1823                                                } else {
1824                                                        $addpkgs = "-a $postparam";
1825                                                }
1826                                                pb_system("sudo /usr/bin/rpmbootstrap $rbsopt $postinstall $addpkgs $ddir-$dver-$darch $rbsverb","Creating the rpmbootstrap VE for $ddir-$dver ($darch)", "verbose");
1827                                        } elsif ($verpmstyle eq "mock") {
1828                                                my ($rbsconf) = pb_conf_get("rbsconf");
1829                                                pb_system("sudo /usr/sbin/mock --init --resultdir=\"/tmp\" --configdir=\"$rbsconf->{$ENV{'PBPROJ'}}\" -r $v $rbsopt","Creating the mock VE for $ddir-$dver ($darch)");
1830                                                # Once setup we need to install some packages, the pb account, ...
1831                                                pb_system("sudo /usr/sbin/mock --install --configdir=\"$rbsconf->{$ENV{'PBPROJ'}}\" -r $v su","Configuring the mock VE");
1832                                        } else {
1833                                                die "Unknown verpmtype type $verpmstyle. Report to dev team";
1834                                        }
1835                                } elsif ($dtype eq "deb") {
1836                                        my $vedebstyle = $vedebtype->{$ENV{'PBPROJ'}};
1837
1838                                        my $codename = pb_distro_get_param($ddir,$dver,$darch,$oscodename);
1839                                        my $postparam = "";
1840                                        my $addpkgs;
1841                                        $postparam .= pb_distro_get_param($ddir,$dver,$darch,$osmindep);
1842                                        if ($postparam eq "") {
1843                                                $addpkgs = "";
1844                                        } else {
1845                                                $addpkgs = "--include $postparam";
1846                                        }
1847                                        my $debmir = "";
1848                                        $debmir .= pb_distro_get_param($ddir,$dver,$darch,$rbsmirrorsrv);
1849
1850                                        # Get potential rbs option
1851                                        my $rbsopt = "";
1852                                        if (defined $rbsopt1) {
1853                                                if (defined $rbsopt1->{$vedebstyle}) {
1854                                                        $rbsopt = $rbsopt1->{$vedebstyle};
1855                                                } elsif (defined $rbsopt1->{$ENV{'PBPROJ'}}) {
1856                                                        $rbsopt = $rbsopt1->{$ENV{'PBPROJ'}};
1857                                                } else {
1858                                                        $rbsopt = "";
1859                                                }
1860                                        }
1861       
1862                                        # debootstrap works with amd64 not x86_64
1863                                        my $debarch = $darch;
1864                                        $debarch = "amd64" if ($darch eq "x86_64");
1865                                        if ($vedebstyle eq "debootstrap") {
1866                                                my $dbsverb = "";
1867                                                $dbsverb = "--verbose" if ($pbdebug gt 0);
1868
1869                                                # Some perl modules are in Universe on Ubuntu
1870                                                $rbsopt .= " --components=main,universe" if ($ddir eq "ubuntu");
1871
1872                                                pb_system("sudo /usr/sbin/debootstrap $dbsverb $rbsopt --arch=$debarch $addpkgs $codename \"$vepath->{$ENV{'PBPROJ'}}/$ddir/$dver/$darch\" $debmir","Creating the debootstrap VE for $ddir-$dver ($darch)", "verbose");
1873                                                # debootstrap doesn't create an /etc/hosts file
1874                                                if (! -f "$vepath->{$ENV{'PBPROJ'}}/$ddir/$dver/$darch/etc/hosts" ) {
1875                                                        pb_system("sudo cp /etc/hosts $vepath->{$ENV{'PBPROJ'}}/$ddir/$dver/$darch/etc/hosts");
1876                                                }
1877                                        } else {
1878                                                die "Unknown vedebtype type $vedebstyle. Report to dev team";
1879                                        }
1880                                } elsif ($dtype eq "ebuild") {
1881                                        die "Please teach the dev team how to build gentoo chroot";
1882                                } else {
1883                                        die "Unknown distribution type $dtype. Report to dev team";
1884                                }
1885                        }
1886                        # Fix modes to allow access to the VE for pb user
1887                        pb_system("sudo chmod 755 $vepath->{$ENV{'PBPROJ'}}/$ddir $vepath->{$ENV{'PBPROJ'}}/$ddir/$dver $vepath->{$ENV{'PBPROJ'}}/$ddir/$dver/$darch","Fixing permissions");
1888
1889                        # Test if an existing snapshot exists and use it if appropriate
1890                        # And also use it of no local extracted VE is present
1891                        if ((-f "$vepath->{$ENV{'PBPROJ'}}/$ddir-$dver-$darch.tar.gz") &&
1892                                (((defined $vesnap->{$v}) && ($vesnap->{$v} =~ /true/i)) ||
1893                                        ((defined $vesnap->{$ENV{'PBPROJ'}}) && ($vesnap->{$ENV{'PBPROJ'}} =~ /true/i)) ||
1894                                        ($pbsnap eq 1) ||
1895                                        (! -d "$vepath->{$ENV{'PBPROJ'}}/$ddir/$dver/$darch"))) {
1896                                                pb_system("sudo rm -rf $vepath->{$ENV{'PBPROJ'}}/$ddir/$dver/$darch ; sudo mkdir -p $vepath->{$ENV{'PBPROJ'}}/$ddir/$dver/$darch ; sudo tar xz  -C $vepath->{$ENV{'PBPROJ'}}/$ddir/$dver/$darch -f $vepath->{$ENV{'PBPROJ'}}/$ddir-$dver-$darch.tar.gz","Extracting snapshot of $ddir-$dver-$darch.tar.gz under $vepath->{$ENV{'PBPROJ'}}/$ddir/$dver/$darch");
1897                        }
1898                        # Nothing more to do for VE. No real launch
1899                } else {
1900                        die "VE of type $vetype not supported. Report to the dev team";
1901                }
1902        }
1903}
1904
1905# Return string for date synchro
1906sub pb_date2v {
1907
1908my $vtype = shift;
1909my $v = shift;
1910
1911my ($ntp) = pb_conf_get_if($vtype."ntp");
1912my $vntp = $ntp->{$ENV{'PBPROJ'}} if (defined $ntp);
1913my $ntpline;
1914
1915if (defined $vntp) {
1916        my ($ntpcmd) = pb_conf_get($vtype."ntpcmd");
1917        my $vntpcmd;
1918        if (defined $ntpcmd->{$v}) {
1919                $vntpcmd = $ntpcmd->{$v};
1920        } elsif (defined $ntpcmd->{$ENV{'PBPROJ'}}) {
1921                $vntpcmd = $ntpcmd->{$ENV{'PBPROJ'}};
1922        } else {
1923                $vntpcmd = "/bin/true";
1924        }
1925        $ntpline = "sudo $vntpcmd $vntp";
1926} else {
1927        $ntpline = undef;
1928}
1929# Force new date to be in the future compared to the date
1930# of the host by adding 1 minute
1931my @date=pb_get_date();
1932$date[1]++;
1933my $upddate = strftime("%m%d%H%M%Y", @date);
1934my $dateline = "sudo date $upddate";
1935return($ntpline,$dateline);
1936}
1937
1938sub pb_build2v {
1939
1940my $vtype = shift;
1941my $action = shift || "build";
1942
1943my ($v,$all) = pb_get2v($vtype);
1944
1945# Send tar files when we do a global generation
1946pb_build2ssh() if (($all == 1) && ($action eq "build"));
1947
1948my ($vmexist,$vmpid) = (undef,undef);
1949
1950foreach my $v (@$v) {
1951        # Prepare the script to be executed on the VM/VE
1952        # in $ENV{'PBDESTDIR'}/pbscript
1953        open(SCRIPT,"> $ENV{'PBDESTDIR'}/pbscript") || die "Unable to create $ENV{'PBDESTDIR'}/pbscript";
1954        print SCRIPT "#!/bin/bash\n";
1955
1956        # Transmit the verbosity level to the virtual env/mach.
1957        my $verbose = "";
1958        my $i = 0;                                                      # minimal debug level
1959        while ($i lt $pbdebug) {
1960                $verbose .= "-v ";
1961                $i++;
1962        }
1963        # Activate script verbosity if at least 2 for pbdebug
1964        print SCRIPT "set -x\n" if ($i gt 1);
1965        # Quiet if asked to be so on the original system
1966        $verbose = "-q" if ($pbdebug eq -1);
1967
1968        print SCRIPT "echo ... Execution needed\n";
1969        print SCRIPT "# This is in directory delivery\n";
1970        print SCRIPT "# Setup the variables required for building\n";
1971        print SCRIPT "export PBPROJ=$ENV{'PBPROJ'}\n";
1972
1973        if ($action eq "build") {
1974                print SCRIPT "# Preparation for pb\n";
1975                print SCRIPT "mv .pbrc \$HOME\n";
1976                print SCRIPT "cd ..\n";
1977        }
1978
1979        # VE needs a good /proc
1980        if ($vtype eq "ve") {
1981                print SCRIPT "sudo mount -t proc /proc /proc\n";
1982        }
1983
1984        my ($ntpline,$dateline) = pb_date2v($vtype,$v);
1985        print SCRIPT "# Time sync\n";
1986        print SCRIPT "echo 'setting up date with '";
1987        if (defined $ntpline) {
1988                print SCRIPT "echo $ntpline\n";
1989                print SCRIPT "$ntpline\n";
1990        } else {
1991                print SCRIPT "echo $dateline\n";
1992                print SCRIPT "$dateline\n";
1993        }
1994        # Use potential local proxy declaration in case we need it to download repo, pkgs, ...
1995        if (defined $ENV{'http_proxy'}) {
1996                print SCRIPT "export http_proxy=\"$ENV{'http_proxy'}\"\n";
1997        }
1998
1999        if (defined $ENV{'ftp_proxy'}) {
2000                print SCRIPT "export ftp_proxy=\"$ENV{'ftp_proxy'}\"\n";
2001        }
2002
2003        # Get list of packages to build/test and get some ENV vars as well
2004        my $ptr = pb_get_pkg();
2005        @pkgs = @$ptr;
2006        my $p = join(' ',@pkgs) if (@pkgs);
2007        print SCRIPT "export PBPROJVER=$ENV{'PBPROJVER'}\n";
2008        print SCRIPT "export PBPROJTAG=$ENV{'PBPROJTAG'}\n";
2009        print SCRIPT "export PBPACKAGER=\"$ENV{'PBPACKAGER'}\"\n";
2010
2011        # We may need to do some other tasks before building. Read a script here to finish setup
2012        if (-x "$ENV{'PBDESTDIR'}/pb$vtype".".pre") {
2013                print SCRIPT "# Special pre-instructions to be launched\n";
2014                print SCRIPT pb_get_content("$ENV{'PBDESTDIR'}/pb$vtype".".pre");
2015        }
2016
2017        if (-x "$ENV{'PBDESTDIR'}/pb$vtype"."$action.pre") {
2018                print SCRIPT "# Special pre-$action instructions to be launched\n";
2019                print SCRIPT pb_get_content("$ENV{'PBDESTDIR'}/pb$vtype"."$action.pre");
2020        }
2021
2022        print SCRIPT "# $action\n";
2023        print SCRIPT "echo $action"."ing packages on $vtype...\n";
2024
2025        if (($action eq "test") && (! -x "$ENV{'PBDESTDIR'}/pbtest")) {
2026                        die "No test script ($ENV{'PBDESTDIR'}/pbtest) found when in test mode. Aborting ...";
2027        }
2028        print SCRIPT "pb $verbose -p $ENV{'PBPROJ'} $action"."2pkg $p\n";
2029
2030        if ($vtype eq "ve") {
2031                print SCRIPT "sudo umount /proc\n";
2032        }
2033
2034        # We may need to do some other tasks after building. Read a script here to exit properly
2035        if (-x "$ENV{'PBDESTDIR'}/pb$vtype"."$action.post") {
2036                print SCRIPT "# Special post-$action instructions to be launched\n";
2037                print SCRIPT pb_get_content("$ENV{'PBDESTDIR'}/pb$vtype"."$action.post");
2038        }
2039
2040        if (-x "$ENV{'PBDESTDIR'}/pb$vtype".".post") {
2041                print SCRIPT "# Special post-instructions to be launched\n";
2042                print SCRIPT pb_get_content("$ENV{'PBDESTDIR'}/pb$vtype".".post");
2043        }
2044
2045        close(SCRIPT);
2046        chmod 0755,"$ENV{'PBDESTDIR'}/pbscript";
2047       
2048        # Launch the VM/VE
2049        ($vmexist,$vmpid) = pb_launchv($vtype,$v,0);
2050
2051        if ($vtype eq "vm") {
2052                # Skip that VM if it something went wrong
2053                next if (($vmpid == 0) && ($vmexist == 0));
2054        } else {
2055                # VE
2056                $vmexist = 0;
2057                $vmpid = 0;
2058        }
2059        # Gather all required files to send them to the VM/VE
2060        # and launch the build through pbscript
2061        pb_log(2,"Calling send2target $vtype,$v,$vmexist,$vmpid\n");
2062        pb_send2target(uc($vtype).$action,"$v",$vmexist,$vmpid);
2063}
2064}
2065
2066
2067sub pb_clean {
2068
2069        my $sleep=10;
2070        die "Unable to get env var PBDESTDIR" if (not defined $ENV{'PBDESTDIR'});
2071        die "Unable to get env var PBBUILDDIR" if (not defined $ENV{'PBBUILDDIR'});
2072        pb_log(0,"We will now wait $sleep s before removing both directories\n$ENV{'PBDESTDIR'} and $ENV{'PBBUILDDIR'}.\nPlease break me if this is wrong\n");
2073        sleep $sleep;
2074        pb_rm_rf($ENV{'PBDESTDIR'});
2075        pb_rm_rf($ENV{'PBBUILDDIR'});
2076}
2077
2078sub pb_newver {
2079
2080        die "-V Version parameter needed" if ((not defined $newver) || ($newver eq ""));
2081
2082        # Need this call for PBDIR
2083        my ($scheme2,$uri) = pb_cms_init($pbinit);
2084
2085        my ($pbconf,$pburl) = pb_conf_get("pbconfurl","pburl");
2086        $uri = $pbconf->{$ENV{'PBPROJ'}};
2087        my ($scheme, $account, $host, $port, $path) = pb_get_uri($uri);
2088
2089        # Checking CMS repositories status
2090        ($scheme2, $account, $host, $port, $path) = pb_get_uri($pburl->{$ENV{'PBPROJ'}});
2091
2092        if ($scheme !~ /^svn/) {
2093                die "Only SVN is supported at the moment";
2094        }
2095
2096        my $res = pb_cms_isdiff($scheme,$ENV{'PBROOTDIR'});
2097        die "ERROR: No differences accepted in CMS for $ENV{'PBROOTDIR'} before creating a new version" if ($res != 0);
2098
2099        $res = pb_cms_isdiff($scheme2,$ENV{'PBDIR'});
2100        die "ERROR: No differences accepted in CMS for $ENV{'PBDIR'} before creating a new version" if ($res != 0);
2101
2102        # Tree identical between PBCONFDIR and PBROOTDIR. The delta is what
2103        # we want to get for the root of the new URL
2104
2105        my $tmp = $ENV{'PBROOTDIR'};
2106        $tmp =~ s|^$ENV{'PBCONFDIR'}||;
2107
2108        my $newurl = "$uri/".dirname($tmp)."/$newver";
2109        # Should probably use projver in the old file
2110        my $oldver= basename($tmp);
2111
2112        # Duplicate and extract project-builder part
2113        pb_log(2,"Copying $uri/$tmp to $newurl\n");
2114        pb_cms_copy($scheme,"$uri/$tmp",$newurl);
2115        pb_log(2,"Checkout $newurl to $ENV{'PBROOTDIR'}/../$newver\n");
2116        pb_cms_up($scheme,"$ENV{'PBCONFDIR'}/..");
2117
2118        # Duplicate and extract project
2119        my $newurl2 = "$pburl->{$ENV{'PBPROJ'}}/".dirname($tmp)."/$newver";
2120
2121        pb_log(2,"Copying $pburl->{$ENV{'PBPROJ'}}/$tmp to $newurl2\n");
2122        pb_cms_copy($scheme2,"$pburl->{$ENV{'PBPROJ'}}/$tmp",$newurl2);
2123        pb_log(2,"Checkout $newurl2 to $ENV{'PBDIR'}/../$newver\n");
2124        pb_cms_up($scheme2,"$ENV{'PBDIR'}/..");
2125
2126        # Update the .pb file
2127        open(FILE,"$ENV{'PBROOTDIR'}/../$newver/$ENV{'PBPROJ'}.pb") || die "Unable to open $ENV{'PBROOTDIR'}/../$newver/$ENV{'PBPROJ'}.pb";
2128        open(OUT,"> $ENV{'PBROOTDIR'}/../$newver/$ENV{'PBPROJ'}.pb.new") || die "Unable to write to $ENV{'PBROOTDIR'}/../$newver/$ENV{'PBPROJ'}.pb.new";
2129        while(<FILE>) {
2130                s/^projver\s+$ENV{'PBPROJ'}\s*=\s*$oldver/projver $ENV{'PBPROJ'} = $newver/;
2131                pb_log(0,"Changing projver from $oldver to $newver in $ENV{'PBROOTDIR'}/../$newver/$ENV{'PBPROJ'}.pb\n") if (/^projver\s+$ENV{'PBPROJ'}\s*=\s*$oldver/);
2132                pb_log(0,"Commenting testver in $ENV{'PBROOTDIR'}/../$newver/$ENV{'PBPROJ'}.pb\n") if (/^testver/);
2133                s/^testver/#testver/;
2134                print OUT $_;
2135        }
2136        close(FILE);
2137        close(OUT);
2138        rename("$ENV{'PBROOTDIR'}/../$newver/$ENV{'PBPROJ'}.pb.new","$ENV{'PBROOTDIR'}/../$newver/$ENV{'PBPROJ'}.pb");
2139
2140        # Checking pbcl files
2141        foreach my $f (<$ENV{'PBROOTDIR'}/*/pbcl>) {
2142                # Compute new pbcl file
2143                my $f2 = $f;
2144                $f2 =~ s|$ENV{'PBROOTDIR'}|$ENV{'PBROOTDIR'}/../$newver/|;
2145                open(PBCL,$f) || die "Unable to open $f";
2146                my $foundnew = 0;
2147                while (<PBCL>) {
2148                        $foundnew = 1 if (/^$newver \(/);
2149                }
2150                close(PBCL);
2151                open(OUT,"> $f2") || die "Unable to write to $f2: $!";
2152                open(PBCL,$f) || die "Unable to open $f";
2153                while (<PBCL>) {
2154                        print OUT "$_" if (not /^$oldver \(/);
2155                        if ((/^$oldver \(/) && ($foundnew == 0)) {
2156                                print OUT "$newver ($pbdate)\n";
2157                                print OUT "- TBD\n";
2158                                print OUT "\n";
2159                                pb_log(0,"WARNING: version $newver not found in $f so added to $f2...\n") if ($foundnew == 0);
2160                        }
2161                }
2162                close(OUT);
2163                close(PBCL);
2164        }
2165
2166        pb_log(2,"Checkin $ENV{'PBROOTDIR'}/../$newver\n");
2167        pb_cms_checkin($scheme,"$ENV{'PBROOTDIR'}/../$newver",undef);
2168}
2169
2170#
2171# Return the list of VMs/VEs we are working on
2172# $all is a flag to know if we return all of them
2173# or only some (if all we publish also tar files in addition to pkgs
2174#
2175sub pb_get2v {
2176
2177my $vtype = shift;
2178my @v;
2179my $all = 0;
2180my $vlist;
2181my $pbv = 'PBV';
2182
2183if ($vtype eq "vm") {
2184        $vlist = "vmlist";
2185} elsif ($vtype eq "ve") {
2186        $vlist = "velist";
2187}
2188# Get VM/VE list
2189if ((not defined $ENV{$pbv}) || ($ENV{$pbv} =~ /^all$/)) {
2190        my ($ptr) = pb_conf_get($vlist);
2191        $ENV{$pbv} = $ptr->{$ENV{'PBPROJ'}};
2192        $all = 1;
2193}
2194pb_log(2,"$vtype: $ENV{$pbv}\n");
2195@v = split(/,/,$ENV{$pbv});
2196return(\@v,$all);
2197}
2198
2199# Function to create a potentialy missing pb account on the VM/VE, and adds it to sudo
2200# Needs to use root account to connect to the VM/VE
2201# pb will take your local public SSH key to access
2202# the pb account in the VM later on if needed
2203sub pb_setup2v {
2204
2205my $vtype = shift;
2206
2207my ($vm,$all) = pb_get2v($vtype);
2208
2209# Script generated
2210my $pbscript = "$ENV{'PBDESTDIR'}/setupv";
2211
2212foreach my $v (@$vm) {
2213        # Deal with date sync.
2214        my ($ntpline,$dateline) = pb_date2v($vtype,$v);
2215
2216        # Get distro context
2217        my ($name,$ver,$darch) = split(/-/,$v);
2218        chomp($darch);
2219        my ($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd) = pb_distro_init($name,$ver,$darch);
2220       
2221        # Name of the account to deal with for VM/VE
2222        # Do not use the one passed potentially with -a
2223        my ($pbac) = pb_conf_get($vtype."login");
2224        my ($key,$zero0,$zero1,$zero2);
2225        my ($vmexist,$vmpid);
2226
2227        # Prepare the script to be executed on the VM/VE
2228        # in $ENV{'PBDESTDIR'}/setupv
2229        open(SCRIPT,"> $pbscript") || die "Unable to create $pbscript";
2230       
2231        print SCRIPT << 'EOF';
2232#!/usr/bin/perl -w
2233
2234use strict;
2235use File::Copy;
2236
2237# We should not need in this script more functions than what is provided
2238# by Base and Distribution to avoid problems at exec time.
2239# They are appended at the end.
2240
2241our $pbdebug;
2242our $pbLOG;
2243our $pbsynmsg = "pbscript";
2244our $pbdisplaytype = "text";
2245our $pblocale = "";
2246pb_log_init($pbdebug, $pbLOG);
2247pb_temp_init();
2248
2249EOF
2250
2251        # Launch the VM/VE - Usage of snapshot disabled
2252        ($vmexist,$vmpid) = pb_launchv($vtype,$v,0,0,0);
2253
2254        if ($vtype eq "vm") {
2255                # Prepare the key to be used and transfered remotely
2256                my $keyfile = pb_ssh_get(1);
2257               
2258                my ($vmhost,$vmport,$vmntp) = pb_conf_get("vmhost","vmport","vmntp");
2259                my $nport = $vmport->{$ENV{'PBPROJ'}};
2260                $nport = "$pbport" if (defined $pbport);
2261       
2262                # Skip that VM if something went wrong
2263                next if (($vmpid == 0) && ($vmexist == 0));
2264       
2265                # Store the pub key part in a variable
2266                open(FILE,"$keyfile.pub") || die "Unable to open $keyfile.pub";
2267                ($zero0,$zero1,$zero2) = split(/ /,<FILE>);
2268                close(FILE);
2269
2270                $key = "\Q$zero1";
2271
2272                pb_system("cat $keyfile.pub | ssh -q -o UserKnownHostsFile=/dev/null -p $nport -i $keyfile root\@$vmhost->{$ENV{'PBPROJ'}} \"mkdir -p .ssh ; chmod 700 .ssh ; cat >> .ssh/authorized_keys ; chmod 600 .ssh/authorized_keys ; if [ -x /usr/bin/chcon ]; then /usr/bin/chcon -Rt home_ssh_t .ssh 2> /dev/null; fi\"","Copying local keys to $vtype. This may require the root password");
2273                # once this is done, we can do what we want on the VM remotely
2274        } elsif ($vtype eq "ve") {
2275                print SCRIPT << "EOF";
2276# For VE we need a good null dev
2277pb_system("rm -f /dev/null; mknod /dev/null c 1 3; chmod 777 /dev/null");
2278EOF
2279                print SCRIPT << "EOF";
2280# For VE we first need to mount some FS
2281pb_system("mount -t proc /proc /proc");
2282
2283EOF
2284        }
2285
2286if ($vtype eq "vm") {
2287                print SCRIPT << 'EOF';
2288# Removes duplicate in .ssh/authorized_keys of our key if needed
2289#
2290my $file1="$ENV{'HOME'}/.ssh/authorized_keys";
2291open(PBFILE,$file1) || die "Unable to open $file1";
2292open(PBOUT,"> $file1.new") || die "Unable to open $file1.new";
2293my $count = 0;
2294while (<PBFILE>) {
2295
2296EOF
2297                print SCRIPT << "EOF";
2298        if (/ $key /) {
2299                \$count++;
2300        }
2301print PBOUT \$_ if ((\$count <= 1) || (\$_ !~ / $key /));
2302}
2303close(PBFILE);
2304close(PBOUT);
2305rename("\$file1.new",\$file1);
2306chmod 0600,\$file1;
2307
2308# Sync date
2309EOF
2310                if (defined $ntpline) {
2311                        print SCRIPT "pb_system(\"$ntpline\");\n";
2312                } else {
2313                        print SCRIPT "pb_system(\"$dateline\");\n";
2314                }
2315        }
2316        print SCRIPT << 'EOF';
2317
2318# Adds $pbac->{$ENV{'PBPROJ'}} as an account if needed
2319#
2320my $file="/etc/passwd";
2321open(PBFILE,$file) || die "Unable to open $file";
2322my $found = 0;
2323while (<PBFILE>) {
2324EOF
2325        print SCRIPT << "EOF";
2326        \$found = 1 if (/^$pbac->{$ENV{'PBPROJ'}}:/);
2327EOF
2328
2329my $home = "/home";
2330# Solaris doesn't like that we use /home
2331$home = "/export/home" if ($dtype eq "pkg");
2332
2333        print SCRIPT << "EOF";
2334}
2335close(PBFILE);
2336
2337if ( \$found == 0 ) {
2338        if ( ! -d "$home" ) {
2339                pb_mkdir_p("$home");
2340        }
2341EOF
2342        print SCRIPT << "EOF";
2343pb_system("groupadd $pbac->{$ENV{'PBPROJ'}}","Adding group $pbac->{$ENV{'PBPROJ'}}");
2344pb_system("useradd -g $pbac->{$ENV{'PBPROJ'}} -m -d $home/$pbac->{$ENV{'PBPROJ'}} $pbac->{$ENV{'PBPROJ'}}","Adding user $pbac->{$ENV{'PBPROJ'}} (group $pbac->{$ENV{'PBPROJ'}} - home $home/$pbac->{$ENV{'PBPROJ'}})");
2345}
2346EOF
2347
2348        # Copy the content of our local conf file to the VM/VE
2349        my $content = pb_get_content(pb_distro_conffile());
2350        print SCRIPT << "EOF";
2351        #
2352        # Create a temporary local conf file for distribution support
2353        # This is created here before its use later. Its place is hardcoded, so no choice for the path
2354        #
2355        my \$tempconf = pb_distro_conffile();
2356        pb_mkdir_p(dirname(\$tempconf));
2357        open(CONF,"> \$tempconf") || die "Unable to create \$tempconf";
2358        print CONF q{$content};
2359        close(CONF);
2360EOF
2361
2362        if ($vtype eq "vm") {
2363                print SCRIPT << "EOF";
2364# allow ssh entry to build
2365#
2366mkdir "$home/$pbac->{$ENV{'PBPROJ'}}/.ssh",0700;
2367# Allow those accessing root to access the build account
2368copy("\$ENV{'HOME'}/.ssh/authorized_keys","$home/$pbac->{$ENV{'PBPROJ'}}/.ssh/authorized_keys");
2369chmod 0600,".ssh/authorized_keys";
2370pb_system("chown -R $pbac->{$ENV{'PBPROJ'}}:$pbac->{$ENV{'PBPROJ'}} $home/$pbac->{$ENV{'PBPROJ'}}","Finish setting up the account env for $pbac->{$ENV{'PBPROJ'}}");
2371
2372EOF
2373}
2374        print SCRIPT << 'EOF';
2375# No passwd for build account only keys
2376$file="/etc/shadow";
2377if (-f $file) {
2378        open(PBFILE,$file) || die "Unable to open $file";
2379        open(PBOUT,"> $file.new") || die "Unable to open $file.new";
2380        while (<PBFILE>) {
2381EOF
2382        print SCRIPT << "EOF";
2383                s/^$pbac->{$ENV{'PBPROJ'}}:\!\!:/$pbac->{$ENV{'PBPROJ'}}:*:/;
2384                s/^$pbac->{$ENV{'PBPROJ'}}:\!:/$pbac->{$ENV{'PBPROJ'}}:*:/;     #SLES 9 e.g.
2385                s/^$pbac->{$ENV{'PBPROJ'}}:\\*LK\\*:/$pbac->{$ENV{'PBPROJ'}}:NP:/;      #Solaris e.g.
2386EOF
2387                print SCRIPT << 'EOF';
2388                print PBOUT $_;
2389        }
2390        close(PBFILE);
2391        close(PBOUT);
2392        rename("$file.new",$file);
2393        chmod 0640,$file;
2394        }
2395
2396# Keep the VM in text mode
2397$file="/etc/inittab";
2398if (-f $file) {
2399        open(PBFILE,$file) || die "Unable to open $file";
2400        open(PBOUT,"> $file.new") || die "Unable to open $file.new";
2401        while (<PBFILE>) {
2402                s/^(..):5:initdefault:$/$1:3:initdefault:/;
2403                print PBOUT $_;
2404        }
2405        close(PBFILE);
2406        close(PBOUT);
2407        rename("$file.new",$file);
2408        chmod 0640,$file;
2409}
2410
2411# pb has to be added to portage group on gentoo
2412
2413# We need to have that pb_distro_init function
2414# Get it from Project-Builder::Distribution
2415# And we now need the conf file required for this to work created above
2416
2417my ($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd, $darch) = pb_distro_init(); 
2418print "distro tuple: ".join(',',($ddir, $dver, $dfam, $dtype, $pbsuf, $darch))."\n";
2419
2420# Adapt sudoers
2421# sudo is not default on Solaris and needs to be installed first
2422# from http://www.sunfreeware.com/programlistsparc10.html#sudo
2423if ($dtype eq "pkg") {
2424        $file="/usr/local/etc/sudoers";
2425} else {
2426        $file="/etc/sudoers";
2427}
2428open(PBFILE,$file) || die "Unable to open $file";
2429open(PBOUT,"> $file.new") || die "Unable to open $file.new";
2430while (<PBFILE>) {
2431EOF
2432        print SCRIPT << "EOF";
2433        next if (/^$pbac->{$ENV{'PBPROJ'}}   /);
2434EOF
2435        print SCRIPT << 'EOF';
2436        s/Defaults[ \t]+requiretty//;
2437        print PBOUT $_;
2438}
2439close(PBFILE);
2440EOF
2441        print SCRIPT << "EOF";
2442# Some distro force requiretty at compile time, so disable here
2443print PBOUT "Defaults:$pbac->{$ENV{'PBPROJ'}} !requiretty\n";
2444print PBOUT "Defaults:root !requiretty\n";
2445# This is needed in order to be able to halt the machine from the $pbac->{$ENV{'PBPROJ'}} account at least
2446print PBOUT "Defaults:$pbac->{$ENV{'PBPROJ'}} env_keep += \\\"http_proxy ftp_proxy\\\"\n";
2447print PBOUT "$pbac->{$ENV{'PBPROJ'}}   ALL=(ALL) NOPASSWD:ALL\n";
2448EOF
2449        print SCRIPT << 'EOF';
2450close(PBOUT);
2451rename("$file.new",$file);
2452chmod 0440,$file;
2453
2454EOF
2455
2456        # We may need a proxy configuration. Get it from the local env
2457
2458        if (defined $ENV{'http_proxy'}) {
2459                print SCRIPT "\$ENV\{'http_proxy'\}=\"$ENV{'http_proxy'}\";\n";
2460        }
2461
2462        if (defined $ENV{'ftp_proxy'}) {
2463                print SCRIPT "\$ENV\{'ftp_proxy'\}=\"$ENV{'ftp_proxy'}\";\n";
2464        }
2465
2466        print SCRIPT << 'EOF';
2467       
2468        my ($ospkgdep,$osperldep,$osperlver) = pb_conf_get_if("ospkgdep","osperldep","osperlver");
2469       
2470        # First install all required packages
2471        pb_system("yum clean all","Cleaning yum env") if (($ddir eq "fedora") || ($ddir eq "asianux") || ($ddir eq "rhel"));
2472        my $pkgdep = pb_distro_get_param($ddir,$dver,$darch,$ospkgdep,$dfam,$dtype);
2473        pb_distro_installdeps(undef,$dtype,$pbupd,pb_distro_only_deps_needed($dtype,join(' ',split(/,/,$pkgdep))));
2474
2475        # Then install manually the missing perl modules
2476        my $perldep = pb_distro_get_param($ddir,$dver,$darch,$osperldep,$dfam,$dtype);
2477        foreach my $m (split(/,/,$perldep)) {
2478                # Skip empty deps
2479                next if ($m =~ /^\s*$/);
2480                my $dir = $m;
2481                $dir =~ s/-.*//;
2482                pb_system("echo \"rm -rf $m* ; wget http://search.cpan.org/CPAN/modules/by-module/$dir/$m-$osperlver->{$m}.tar.gz ; gzip -cd $m-$osperlver->{$m}.tar.gz | tar xf - ; cd $m* ; if [ -f Build.PL ]; then perl Build.PL; ./Build ; ./Build install ; else perl Makefile.PL; make ; make install ; fi; cd .. ; rm -rf $m*\" | bash" ,"Installing perl module $m-$osperlver->{$m}");
2483        }
2484
2485# Suse wants sudoers as 640
2486if ((($ddir eq "sles") && (($dver =~ /10/) || ($dver =~ /9/))) || (($ddir eq "opensuse") && ($dver =~ /10.[012]/))) {
2487        chmod 0640,$file;
2488}
2489
2490pb_system("rm -rf ProjectBuilder-* ; wget --passive-ftp ftp://ftp.mondorescue.org/src/ProjectBuilder-latest.tar.gz ; gzip -cd ProjectBuilder-latest.tar.gz | tar xf - ; cd ProjectBuilder-* ; perl Makefile.PL ; make ; make install ; cd .. ; rm -rf ProjectBuilder-* ; rm -rf project-builder-* ; wget --passive-ftp ftp://ftp.mondorescue.org/src/project-builder-latest.tar.gz ; gzip -cd project-builder-latest.tar.gz | tar xf - ; cd project-builder-* ; perl Makefile.PL ; make ; make install ; cd .. ; rm -rf project-builder-* ;","Building Project-Builder");
2491pb_system("pb 2>&1 | head -5",undef,"verbose");
2492EOF
2493        if ($vtype eq "ve") {
2494                        print SCRIPT << 'EOF';
2495# For VE we need to umount some FS at the end
2496
2497pb_system("umount /proc");
2498
2499# Create a basic network file if not already there
2500
2501my $nf="/etc/sysconfig/network";
2502if ((! -f $nf) && ($dtype eq "rpm")) {
2503        open(NF,"> $nf") || die "Unable to create $nf";
2504        print NF "NETWORKING=yes\n";
2505        print NF "HOSTNAME=localhost\n";
2506        close(NF);
2507}
2508chmod 0755,$nf;
2509EOF
2510        }
2511
2512        # Adds pb_distro_init and all functions needed from ProjectBuilder::Distribution and Base
2513        foreach my $d (@INC) {
2514                my @f = ("$d/ProjectBuilder/Base.pm","$d/ProjectBuilder/Distribution.pm","$d/ProjectBuilder/Conf.pm");
2515                foreach my $f (@f) {
2516                        if (-f "$f") {
2517                                open(PBD,"$f") || die "Unable to open $f";
2518                                while (<PBD>) {
2519                                        next if (/^package/);
2520                                        next if (/^use Exporter/);
2521                                        next if (/^use ProjectBuilder::/);
2522                                        next if (/^our /);
2523                                        print SCRIPT $_;
2524                                }
2525                                close(PBD);
2526                        }
2527                }
2528        }
2529        close(SCRIPT);
2530        chmod 0755,"$pbscript";
2531
2532        # That build script needs to be run as root and force stop of VM at end
2533        $pbaccount = "root";
2534
2535        # Force shutdown of VM exept if it was already launched
2536        my $pbforce = 0;
2537        if ((! $vmexist) && ($vtype eq "vm")) {
2538                $pbforce = 1;
2539        }
2540       
2541        pb_script2v($pbscript,$vtype,$pbforce,$v);
2542}
2543return;
2544}
2545
2546# Function to create a snapshot named 'pb' for VMs and a compressed tar for VEs
2547sub pb_snap2v {
2548
2549my $vtype = shift;
2550
2551my ($vm,$all) = pb_get2v($vtype);
2552
2553# Script generated
2554my $pbscript = "$ENV{'PBDESTDIR'}/snapv";
2555
2556my ($pbac) = pb_conf_get($vtype."login");
2557
2558foreach my $v (@$vm) {
2559        # Get distro context
2560        my ($name,$ver,$darch) = split(/-/,$v);
2561        chomp($darch);
2562        my ($ddir, $dver, $dfam, $dtype, $pbsuf) = pb_distro_init($name,$ver,$darch);
2563        my ($vepath) = pb_conf_get("vepath");
2564
2565        # Test if an existing snapshot exists and remove it if there is a VE
2566        if ((-f "$vepath->{$ENV{'PBPROJ'}}/$ddir-$dver-$darch.tar.gz") &&
2567                (! -d "$vepath->{$ENV{'PBPROJ'}}/$ddir/$dver/$darch")) {
2568                        pb_system("sudo rm -f $vepath->{$ENV{'PBPROJ'}}/$ddir-$dver-$darch.tar.gz","Removing previous snapshot $ddir-$dver-$darch.tar.gz");
2569        }
2570
2571        # Name of the account to deal with for VM/VE
2572        # Do not use the one passed potentially with -a
2573        my ($vmexist,$vmpid);
2574
2575        # Prepare the script to be executed on the VM/VE
2576        # in $ENV{'PBDESTDIR'}/setupv
2577        open(SCRIPT,"> $pbscript") || die "Unable to create $pbscript";
2578       
2579        print SCRIPT << 'EOF';
2580        #!/bin/bash
2581        sleep 2
2582EOF
2583        close(SCRIPT);
2584        chmod 0755,"$pbscript";
2585
2586        # Force shutdown of VM/VE
2587        # Force snapshot of VM/VE
2588        pb_script2v($pbscript,$vtype,1,$v,1);
2589}
2590return;
2591}
2592
2593sub pb_announce {
2594
2595        # Get all required parameters
2596        my ($pbpackager,$pbrepo,$pbml,$pbsmtp) = pb_conf_get("pbpackager","pbrepo","pbml","pbsmtp");
2597        my ($pkgv, $pkgt, $testver) = pb_conf_get_if("pkgver","pkgtag","testver");
2598        my $pkg = pb_cms_get_pkg($defpkgdir,$extpkgdir);
2599        my @pkgs = @$pkg;
2600        my %pkgs;
2601        my $first = 0;
2602
2603        # Command to find packages on repo
2604        my $findstr = "find . ";
2605        # Generated announce files
2606        my @files;
2607
2608        foreach my $pbpkg (@pkgs) {
2609                if ($first != 0) {
2610                        $findstr .= "-o ";
2611                }
2612                $first++;
2613                if ((defined $pkgv) && (defined $pkgv->{$pbpkg})) {
2614                        $pbver = $pkgv->{$pbpkg};
2615                } else {
2616                        $pbver = $ENV{'PBPROJVER'};
2617                }
2618                if ((defined $pkgt) && (defined $pkgt->{$pbpkg})) {
2619                        $pbtag = $pkgt->{$pbpkg};
2620                } else {
2621                        $pbtag = $ENV{'PBPROJTAG'};
2622                }
2623
2624                # TODO: use virtual/real names here now
2625                $findstr .= "-name \'$pbpkg-$pbver-$pbtag\.*.rpm\' -o -name \'$pbpkg"."_$pbver*\.deb\' -o -name \'$pbpkg-$pbver\.ebuild\' ";
2626
2627                my $chglog;
2628
2629                # Get project info on log file and generate tmp files used later on
2630                pb_cms_init($pbinit);
2631                $chglog = "$ENV{'PBROOTDIR'}/$pbpkg/pbcl";
2632                $chglog = "$ENV{'PBROOTDIR'}/pbcl" if (! -f $chglog);
2633                $chglog = undef if (! -f $chglog);
2634
2635                open(OUT,"> $ENV{'PBTMP'}/$pbpkg.ann") || die "Unable to create $ENV{'PBTMP'}/$pbpkg.ann: $!";
2636                my %pb;
2637                $pb{'dtype'} = "announce";
2638                $pb{'realpkg'} = $pbpkg;
2639                $pb{'ver'} = $pbver;
2640                $pb{'tag'} = $pbtag;
2641                $pb{'suf'} = "N/A";             # Should not be empty even if unused
2642                $pb{'date'} = $pbdate;
2643                $pb{'chglog'} = $chglog;
2644                $pb{'packager'} = $pbpackager;
2645                $pb{'proj'} = $ENV{'PBPROJ'};
2646                $pb{'repo'} = $pbrepo;
2647                pb_changelog(\%pb,\*OUT,"yes");
2648                close(OUT);
2649                push(@files,"$ENV{'PBTMP'}/$pbpkg.ann");
2650        }
2651        $findstr .= " | grep -Ev \'src.rpm\'";
2652        if ((not defined $testver) || (not defined $testver->{$ENV{'PBPROJ'}}) || ($testver->{$ENV{'PBPROJ'}} !~ /true/i)) {
2653                $findstr .= " | grep -v ./test/";
2654        }
2655
2656        # Prepare the command to run and execute it
2657        open(PBS,"> $ENV{'PBTMP'}/pbscript") || die "Unable to create $ENV{'PBTMP'}/pbscript";
2658        print PBS "$findstr\n";
2659        close(PBS);
2660        chmod 0755,"$ENV{'PBTMP'}/pbscript";
2661        pb_send2target("Announce");
2662
2663        # Get subject line
2664        my $sl = "Project $ENV{'PBPROJ'} version $ENV{'PBPROJVER'} is now available";
2665        pb_log(0,"Please enter the title of your announce\n");
2666        pb_log(0,"(By default: $sl)\n");
2667        my $sl2 = <STDIN>;
2668        $sl = $sl2 if ($sl2 !~ /^$/);
2669
2670        # Prepare a template of announce
2671        open(ANN,"> $ENV{'PBTMP'}/announce.html") || die "Unable to create $ENV{'PBTMP'}/announce.html: $!";
2672        print ANN << "EOF";
2673$sl</p>
2674
2675<p>The project team is happy to announce the availability of a newest version of $ENV{'PBPROJ'} $ENV{'PBPROJVER'}. Enjoy it as usual!</p>
2676<p>
2677Now available at <a href="$pbrepo->{$ENV{'PBPROJ'}}">$pbrepo->{$ENV{'PBPROJ'}}</a>
2678</p>
2679<p>
2680EOF
2681        open(LOG,"$ENV{'PBTMP'}/system.log") || die "Unable to read $ENV{'PBTMP'}/system.log: $!";
2682        my $col = 2;
2683        my $i = 1;
2684        print ANN << 'EOF';
2685<TABLE WIDTH="700" CELLPADDING="0" CELLSPACING="0" BORDER="0">
2686<TR>
2687EOF
2688        while (<LOG>) {
2689                print ANN "<TD><A HREF=\"$pbrepo->{$ENV{'PBPROJ'}}/$_\">$_</A></TD>";
2690                $i++;
2691                if ($i > $col) {
2692                        print ANN "</TR>\n<TR>";
2693                        $i = 1;
2694                }
2695        }
2696        close(LOG);
2697        print ANN << "EOF";
2698</TR>
2699</TABLE>
2700</p>
2701
2702<p>As usual source packages are also available in the same directory.</p>
2703
2704<p>
2705Changes are :
2706</p>
2707<p>
2708EOF
2709        # Get each package changelog content
2710        foreach my $f (sort(@files)) {
2711                open(IN,"$f") || die "Unable to read $f:$!";
2712                while (<IN>) {
2713                        print ANN $_;
2714                }
2715                close(IN);
2716                print ANN "</p><p>\n";
2717        }
2718        print ANN "</p>\n";
2719        close(ANN);
2720
2721        # Allow for modification
2722        my $editor = "vi";
2723        $editor = $ENV{'EDITOR'} if (defined $ENV{'EDITOR'});
2724        pb_system("$editor $ENV{'PBTMP'}/announce.html","Allowing modification of the announce","noredir");
2725
2726        # Store it in DB for external usage (Web pages generation)
2727        my $db = "$ENV{'PBCONFDIR'}/announces3.sql";
2728
2729        my $precmd = "";
2730        if (! -f $db) {
2731                $precmd = "CREATE TABLE announces (id INTEGER PRIMARY KEY AUTOINCREMENT, date DATE, announce VARCHAR[65535])";
2732        }
2733
2734        my $dbh = DBI->connect("dbi:SQLite:dbname=$db","","",
2735                        { RaiseError => 1, AutoCommit => 1 })
2736                        || die "Unable to connect to $db";
2737
2738        if ($precmd ne "") {
2739                my $sth = $dbh->prepare(qq{$precmd})
2740                        || die "Unable to create table into $db";
2741                $sth->execute();
2742        }
2743
2744        # To read whole file
2745        local $/;
2746        open(ANN,"$ENV{'PBTMP'}/announce.html") || die "Unable to read $ENV{'PBTMP'}/announce.html: $!";
2747        my $announce = <ANN>;
2748        close(ANN);
2749       
2750        pb_log(2,"INSERT INTO announces VALUES (NULL, $pbdate, $announce)");
2751        my $sth = $dbh->prepare(qq{INSERT INTO announces VALUES (NULL,?,?)})
2752                        || die "Unable to insert into $db";
2753        $sth->execute($pbdate, $announce);
2754        $sth->finish();
2755        $dbh->disconnect;
2756
2757        # Then deliver it on the Web
2758        # $TOOLHOME/livwww www
2759
2760        # Mail it to project's ML
2761        open(ML,"| w3m -dump -T text/html > $ENV{'PBTMP'}/announce.txt") || die "Unable to create $ENV{'PBTMP'}/announce.txt: $!";
2762        print ML << 'EOF';
2763<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/x html1/DTD/xhtml1-strict.dtd">
2764
2765<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" xml:lang="en" lang="en">
2766  <head>
2767  </head>
2768  <body>
2769  <p>
2770EOF
2771        open(ANN,"$ENV{'PBTMP'}/announce.html") || die "Unable to read $ENV{'PBTMP'}/announce.html: $!";
2772        while(<ANN>) {
2773                print ML $_;
2774        }
2775        print ML << 'EOF';
2776</body>
2777</html>
2778EOF
2779        close(ML);
2780
2781        # To read whole file
2782        local $/;
2783        open(ANN,"$ENV{'PBTMP'}/announce.txt") || die "Unable to read $ENV{'PBTMP'}/announce.txt: $!";
2784        my $msg = <ANN>;
2785        close(ANN);
2786       
2787        # Preparation of headers
2788
2789        my %mail = (   
2790                        To                      =>      $pbml->{$ENV{'PBPROJ'}},
2791                        From            =>      $pbpackager->{$ENV{'PBPROJ'}},
2792                        Smtp            =>      $pbsmtp->{$ENV{'PBPROJ'}},
2793                        Body            =>      $msg,
2794                        Subject         =>      "[ANNOUNCE] $sl",
2795                );
2796                       
2797        # Send mail
2798        sendmail(%mail) or die "Unable to send mail ($Mail::Sendmail::error): $Mail::Sendmail::log";
2799}
2800
2801#
2802# Creates a set of HTML file containing the news for the project
2803# based on what has been generated by the pb_announce function
2804#
2805sub pb_web_news2html {
2806
2807        my $dest = shift || $ENV{'PBTMP'};
2808
2809        # Get all required parameters
2810        my ($pkgv, $pkgt, $testver) = pb_conf_get_if("pkgver","pkgtag","testver");
2811
2812        # DB of announces for external usage (Web pages generation)
2813        my $db = "$ENV{'PBCONFDIR'}/announces3.sql";
2814
2815        my $dbh = DBI->connect("dbi:SQLite:dbname=$db","","",
2816                        { RaiseError => 1, AutoCommit => 1 })
2817                        || die "Unable to connect to $db";
2818        # For date handling
2819        $ENV{LANGUAGE}="C";
2820        my $firstjan = strftime("%Y-%m-%d", 0, 0, 0, 1, 0, localtime->year(), 0, 0, -1);
2821        my $oldfirst = strftime("%Y-%m-%d", 0, 0, 0, 1, 0, localtime->year()-1, 0, 0, -1);
2822        pb_log(2,"firstjan: $firstjan, oldfirst: $oldfirst, pbdate:$pbdate\n");
2823        my $all = $dbh->selectall_arrayref("SELECT id,date,announce FROM announces ORDER BY date DESC");
2824        my %news;
2825        $news{"cy"} = "";       # current year's news
2826        $news{"ly"} = "";       # last year news
2827        $news{"py"} = "";       # previous years news
2828        $news{"fp"} = "";       # first page news
2829        my $cpt = 4;            # how many news for first page
2830        # Extract info from DB
2831        foreach my $row (@$all) {
2832                my ($id, $date, $announce) = @$row;
2833                $news{"cy"} = $news{"cy"}."<p><B>$date</B> $announce\n" if ((($date cmp $pbdate) le 0) && (($firstjan cmp $date) le 0));
2834                $news{"ly"} = $news{"ly"}."<p><B>$date</B> $announce\n" if ((($date cmp $firstjan) le 0) && (($oldfirst cmp $date) le 0));
2835                $news{"py"} = $news{"py"}."<p><B>$date</B> $announce\n" if (($date cmp $oldfirst) le 0);
2836                $news{"fp"} = $news{"fp"}."<p><B>$date</B> $announce\n" if ($cpt > 0);
2837                $cpt--;
2838        }
2839        pb_log(1,"news{fp}: ".$news{"fp"}."\n");
2840        $dbh->disconnect;
2841
2842        # Generate the HTML content
2843        foreach my $pref (keys %news) {
2844                open(NEWS,"> $dest/pb_web_$pref"."news.html") || die "Unable to create $dest/pb_web_$pref"."news.html: $!";
2845                print NEWS "$news{$pref}";
2846                close(NEWS);
2847        }
2848}
2849
2850
2851# Return the SSH key file to use
2852# Potentially create it if needed
2853
2854sub pb_ssh_get {
2855
2856my $create = shift || 0;        # Do not create keys by default
2857
2858# Check the SSH environment
2859my $keyfile = undef;
2860
2861# We have specific keys by default
2862$keyfile = "$ENV{'HOME'}/.ssh/pb_dsa";
2863if (!(-e $keyfile) && ($create eq 1)) {
2864        pb_system("ssh-keygen -q -b 1024 -N '' -f $keyfile -t dsa","Generating SSH keys for pb");
2865}
2866
2867$keyfile = "$ENV{'HOME'}/.ssh/id_rsa" if (-s "$ENV{'HOME'}/.ssh/id_rsa");
2868$keyfile = "$ENV{'HOME'}/.ssh/id_dsa" if (-s "$ENV{'HOME'}/.ssh/id_dsa");
2869$keyfile = "$ENV{'HOME'}/.ssh/pb_dsa" if (-s "$ENV{'HOME'}/.ssh/pb_dsa");
2870die "Unable to find your public ssh key under $keyfile" if (not defined $keyfile);
2871return($keyfile);
2872}
2873
2874
2875# Returns the pid of a running VM command using a specific VM file
2876sub pb_check_ps {
2877        my $vmcmd = shift;
2878        my $vmm = shift;
2879        my $vmexist = 0;                # FALSE by default
2880
2881        open(PS, "ps auxhww|") || die "Unable to call ps";
2882        while (<PS>) {
2883                next if (! /$vmcmd/);
2884                next if (! /$vmm/);
2885                my ($void1, $void2);
2886                ($void1, $vmexist, $void2) = split(/ +/);
2887                last;
2888        }
2889        return($vmexist);
2890}
2891
2892
2893sub pb_extract_build_files {
2894
2895my $src=shift;
2896my $dir=shift;
2897my $ddir=shift;
2898my $mandatory=shift || "spec";
2899my @files;
2900
2901my $flag = "mayfail" if ($mandatory eq "patch");
2902my $res;
2903
2904if ($src =~ /tar\.gz$/) {
2905        $res = pb_system("tar xfpz $src $dir","Extracting $mandatory files from $src",$flag);
2906} elsif ($src =~ /tar\.bz2$/) {
2907        $res = pb_system("tar xfpj $src $dir","Extracting $mandatory files from $src",$flag);
2908} else {
2909        die "Unknown compression algorithm for $src";
2910}
2911# If not mandatory return now
2912return() if (($res != 0) and ($mandatory eq "patch"));
2913opendir(DIR,"$dir") || die "Unable to open directory $dir";
2914foreach my $f (readdir(DIR)) {
2915        next if ($f =~ /^\./);
2916        # Skip potential patch dir
2917        next if ($f =~ /^pbpatch/);
2918        move("$dir/$f","$ddir") || die "Unable to move $dir/$f to $ddir";
2919        pb_log(2,"mv $dir/$f $ddir\n");
2920        push @files,"$ddir/$f";
2921}
2922closedir(DIR);
2923# Not enough but still a first cleanup
2924pb_rm_rf("$dir");
2925return(@files);
2926}
2927
2928sub pb_list_bfiles {
2929
2930my $dir = shift;
2931my $pbpkg = shift;
2932my $bfiles = shift;
2933my $pkgfiles = shift;
2934my $supfiles = shift;
2935
2936opendir(BDIR,"$dir") || die "Unable to open dir $dir: $!";
2937foreach my $f (readdir(BDIR)) {
2938        next if ($f =~ /^\./);
2939        $bfiles->{$f} = "$dir/$f";
2940        $bfiles->{$f} =~ s~$ENV{'PBROOTDIR'}~~;
2941        if (defined $supfiles->{$pbpkg}) {
2942                $pkgfiles->{$f} = "$dir/$f" if ($f =~ /$supfiles->{$pbpkg}/);
2943        }
2944}
2945closedir(BDIR);
2946}
2947
2948
2949#
2950# Return the list of packages we are working on in a non CMS action
2951#
2952sub pb_get_pkg {
2953
2954my @pkgs = ();
2955
2956my ($var) = pb_conf_read("$ENV{'PBDESTDIR'}/$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}.pb","pbpkg");
2957@pkgs = keys %$var;
2958
2959pb_log(0,"Packages: ".join(',',@pkgs)."\n");
2960return(\@pkgs);
2961}
2962
2963#
2964# Return the postinstall line if needed
2965#
2966
2967sub pb_get_postinstall {
2968
2969my $ddir = shift;
2970my $dver = shift;
2971my $darch = shift;
2972my $rbspi = shift;
2973my $vestyle = shift;
2974my $post = "";
2975
2976# Do we have a local post-install script
2977if ($vestyle eq "rinse") {
2978        $post = "--post-install ";
2979} elsif ($vestyle eq "rpmbootstrap") {
2980        $post = "-s ";
2981}
2982
2983my $postparam = pb_distro_get_param($ddir,$dver,$darch,$rbspi);
2984if ($postparam eq "") {
2985        $post = "";
2986} else {
2987        $post .= $postparam;
2988}
2989return($post);
2990}
2991
29921;
Note: See TracBrowser for help on using the repository browser.