Mon, 28 Jan 2013 17:37:18 +0100
Correct socket error reporting improvement with IPv6 portable code,
after helpful recommendation by Saúl Ibarra Corretgé on OSips devlist.
1 ##
2 ## makeproxy.pl -- OpenPKG Tool Chain
3 ## Copyright (c) 2000-2012 OpenPKG GmbH <http://openpkg.com/>
4 ##
5 ## This software is property of the OpenPKG GmbH, DE MUC HRB 160208.
6 ## All rights reserved. Licenses which grant limited permission to use,
7 ## copy, modify and distribute this software are available from the
8 ## OpenPKG GmbH.
9 ##
10 ## THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
11 ## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
12 ## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
13 ## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
14 ## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
15 ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
16 ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
17 ## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
18 ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
19 ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
20 ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
21 ## SUCH DAMAGE.
22 ##
24 require 5.003;
26 # OpenPKG instance prefix and RPM
27 my $my_prefix = $ENV{'OPENPKG_PREFIX'};
28 my $my_rpm = "$my_prefix/bin/openpkg rpm";
29 delete $ENV{'OPENPKG_PREFIX'};
31 # program identification
32 my $progname = "makeproxy";
33 my $progvers = "2.1.0";
35 # parameters (defaults)
36 my $help = 0;
37 my $version = 0;
38 my $verbose = 0;
39 my $debug = 0;
40 my $slave_prefix = $my_prefix;
41 my $master_prefix = '';
42 my $tmpdir = ($ENV{TMPDIR} || "/tmp");
43 my $output = '.';
44 my $input = '-';
46 # cleanup support
47 my @cleanup = ();
48 sub cleanup_remember {
49 my ($cmd) = @_;
50 push(@cleanup, $cmd);
51 }
52 sub cleanup_perform {
53 foreach my $cmd (reverse @cleanup) {
54 runcmd($cmd);
55 }
56 }
58 # exception handling support
59 $SIG{__DIE__} = sub {
60 my ($err) = @_;
61 $err =~ s|\s+at\s+.*||s if (not $verbose);
62 $err =~ s/\n+$//s;
63 print STDERR "openpkg:$progname:ERROR: $err\n";
64 cleanup_perform() if (not $verbose);
65 exit(1);
66 };
68 # verbose message printing
69 sub verbose {
70 my ($msg) = @_;
71 print STDERR "$msg\n" if ($verbose);
72 }
74 # execution of external commands
75 sub runcmd {
76 my ($cmd) = @_;
77 if ($cmd =~ m/^(.+)\|$/s) {
78 print STDERR "\$ $1\n" if ($debug);
79 return `$1`;
80 }
81 else {
82 print STDERR "\$ $cmd\n" if ($debug);
83 $cmd = "($cmd) >/dev/null 2>&1" if (not $debug);
84 return (system($cmd) == 0);
85 }
86 }
88 # expand into a full filesystem path
89 sub fullpath {
90 my ($prog) = @_;
91 my $fullprog = '';
92 foreach my $path (split(/:/, $ENV{PATH})) {
93 if (-x "$path/$prog") {
94 $fullprog = "$path/$prog";
95 last;
96 }
97 }
98 return $fullprog;
99 }
101 # convert a subdirectory (a/b/c/)
102 # into a corresponding reverse path (../../../)
103 sub sub2rev {
104 my ($sub) = @_;
105 my $rev = '';
106 $sub =~ s|^/+||s;
107 $sub =~ s|/+$||s;
108 while ($sub =~ s|/[^/]+||) {
109 $rev .= "../";
110 }
111 if ($sub ne '') {
112 $rev .= "../";
113 }
114 $rev =~ s|/$||s;
115 return $rev;
116 }
118 # create a directory (plus its missing parent dirs)
119 sub mkdirp {
120 my ($dir) = @_;
121 my $pdir = $dir;
122 $pdir =~ s|/[^/]*$||s;
123 if (not -d $pdir) {
124 mkdirp($pdir);
125 }
126 if (not -d $dir) {
127 runcmd("mkdir $dir");
128 }
129 }
131 # home-brewn getopt(3) style option parser
132 sub getopts ($) {
133 my ($opts) = @_;
134 my (%optf) = map { m/(\w)/; $1 => $_ } $opts =~ m/(\w:|\w)/g;
135 my (%opts, @argv, $optarg);
137 foreach (@ARGV) {
138 if (@argv) {
139 push @argv, $_;
140 } elsif (defined $optarg) {
141 if (exists $opts{$optarg}) {
142 $opts{$optarg} .= " $_";
143 } else {
144 $opts{$optarg} = $_;
145 }
146 $optarg = undef;
147 } elsif (!/^[-]/) {
148 push @argv, $_;
149 } else {
150 while (/^\-(\w)(.*)/) {
151 if (exists $optf{$1}) {
152 if (length($optf{$1}) > 1) {
153 if ($2 ne '') {
154 if (exists $opts{$1}) {
155 $opts{$1} .= " $2";
156 } else {
157 $opts{$1} = $2;
158 }
159 } else {
160 $optarg = $1;
161 }
162 last;
163 } else {
164 $opts{$1} = 1;
165 }
166 } else {
167 warn "openpkg:makeproxy:WARNING: unknown option $_\n";
168 }
169 $_ = "-$2";
170 }
171 }
172 }
173 if (defined $optarg) {
174 warn "openpkg:makeproxy:WARNING: option $optarg requires an argument\n";
175 }
176 foreach (keys %opts) {
177 eval '$opt_'.$_.' = "'.quotemeta($opts{$_}).'";';
178 }
179 @ARGV = @argv;
180 }
182 # command line parsing
183 getopts("Vhdvm:s:t:o:");
184 $version = $opt_V if (defined $opt_V);
185 $help = $opt_h if (defined $opt_h);
186 $debug = $opt_d if (defined $opt_d);
187 $verbose = $opt_v if (defined $opt_v);
188 $master_prefix = $opt_m if (defined $opt_m);
189 $slave_prefix = $opt_s if (defined $opt_s);
190 $tmpdir = $opt_t if (defined $opt_t);
191 $output = $opt_o if (defined $opt_o);
192 if ($help) {
193 print "Usage: openpkg $progname [options] master-package\n" .
194 " -h,--help print this usage page\n" .
195 " -V,--version print version\n" .
196 " -v,--verbose print verbose run-time information\n" .
197 " -d,--debug print debug information\n" .
198 " -m,--master=DIR filesystem path to master OpenPKG instance\n" .
199 " -s,--slave=DIR filesystem path to slave OpenPKG instance\n" .
200 " -t,--tmpdir=DIR filesystem path to temporary directory\n" .
201 " -o,--output=FILE filesystem path to output proxy OpenPKG RPM package\n";
202 exit(0);
203 }
204 if ($version) {
205 print "OpenPKG $progname $progvers\n";
206 exit(0);
207 }
208 if ($#ARGV == 0) {
209 $input = shift(@ARGV);
210 }
211 if ($#ARGV != -1) {
212 die "invalid number of command line arguments";
213 }
215 # prepare temporary location
216 verbose("++ prepare temporary directory");
217 $tmpdir = "$tmpdir/$progname.$$";
218 if (not -d $tmpdir) {
219 runcmd("umask 077 && mkdir $tmpdir")
220 or die "cannot create temporary directory \"$tmpdir\"";
221 cleanup_remember("rm -rf $tmpdir");
222 }
223 verbose("-- temporary directory: $tmpdir");
225 # determine input and output RPM
226 verbose("++ determining OpenPKG RPM package files");
227 verbose("-- input/master/regular RPM package: $input");
228 verbose("-- output/slave/proxy RPM package: $output");
230 # ensure input RPM package is available on the filesystem
231 if ($input eq '-') {
232 $input = "$tmpdir/input.rpm";
233 runcmd("cat >$input");
234 }
236 # determine master and slave OpenPKG instance and RPM commands
237 if ($master_prefix eq '') {
238 if (not -f $input) {
239 die "input/master/regular RPM package does not exist: \"$input\"";
240 }
241 $master_prefix = runcmd("($my_rpm -qp --qf '%{PREFIXES}' $input 2>/dev/null || true) |");
242 $master_prefix =~ s/\r?\n$//s;
243 if ($master_prefix eq '') {
244 die "unable to determine master OpenPKG instance";
245 }
246 }
247 if ($master_prefix eq $slave_prefix) {
248 die "master and slave OpenPKG instances have to be different";
249 }
250 my $slave_rpm = (-f "$slave_prefix/bin/openpkg" ? "$slave_prefix/bin/openpkg rpm" : $my_rpm);
251 my $master_rpm = (-f "$master_prefix/bin/openpkg" ? "$master_prefix/bin/openpkg rpm" : $my_rpm);
252 verbose("-- tool OpenPKG instance: $my_prefix");
253 verbose("-- tool OpenPKG RPM command: $my_rpm");
254 verbose("-- master OpenPKG instance: $master_prefix");
255 verbose("-- master OpenPKG RPM command: $master_rpm");
256 verbose("-- slave OpenPKG instance: $slave_prefix");
257 verbose("-- slave OpenPKG RPM command: $slave_rpm");
259 # helper function for parsing RPM query outputs
260 sub parseresponse {
261 my ($r, $o) = @_;
262 sub parseline {
263 my ($r, $t, $k, $v) = @_;
264 $v =~ s|^\s+||s;
265 $v =~ s|\s+$||s;
266 if ($t eq 'S') { # single-value results
267 $r->{$k} = $v;
268 }
269 elsif ($t eq 'M') { # multi-value results
270 $r->{$k} = [] if (not defined($r->{$k}));
271 push(@{$r->{$k}}, $v);
272 }
273 }
274 $o =~ s|([SM])-([^:]+):<(.*?)>\n|parseline($r, $1, $2, $3, '')|egs;
275 return $r;
276 }
278 # query master OpenPKG RPM package
279 verbose("++ query information from master OpenPKG RPM package");
280 my $q = '';
281 foreach my $t (qw(
282 NAME SUMMARY URL VENDOR PACKAGER DISTRIBUTION CLASS GROUP LICENSE VERSION RELEASE
283 DESCRIPTION
284 )) {
285 $q .= "S-$t:<%{$t}>\\n";
286 }
287 $q .= '[M-PREREQ:<%{REQUIRENAME} %|REQUIREFLAGS?{%{REQUIREFLAGS:depflags} %{REQUIREVERSION}}:{}|>\n]';
288 $q .= '[M-PROVIDES:<%{PROVIDENAME} %|PROVIDEFLAGS?{%{PROVIDEFLAGS:depflags} %{PROVIDEVERSION}}:{}|>\n]';
289 $q .= '[M-PREFIXES:<%{PREFIXES}>\n]';
290 my $cmd = sprintf("%s -q %s --qf '$q' %s", $master_rpm, (-f $input ? "-p" : ""), $input);
291 my $o = runcmd("$cmd|");
292 if ($o =~ m/package .* is not installed/s) {
293 die "master package \"$input\" not installed in master OpenPKG instance \"$master_prefix\"";
294 }
295 $o =~ s|M-PREREQ:<rpmlib\(.*?\).*?>\n||gs;
296 my $r = {};
297 $r = parseresponse($r, $o);
298 my $BD = '';
299 my $ID = '';
300 my $PR = '';
301 foreach my $d (@{$r->{PREREQ}}) {
302 if ($d =~ m|^OpenPKG|i) {
303 $BD .= ", " if ($BD ne '');
304 $BD .= $d;
305 }
306 $ID .= ", " if ($ID ne '');
307 $ID .= $d;
308 }
309 foreach my $d (@{$r->{PROVIDES}}) {
310 $PR .= ", " if ($PR ne '');
311 $PR .= $d;
312 }
314 # prepare build environment
315 verbose("++ establishing temporary OpenPKG RPM environment");
316 verbose("-- directory: $tmpdir");
317 runcmd("rm -rf $tmpdir/src; mkdir $tmpdir/src");
318 runcmd("rm -rf $tmpdir/bld; mkdir $tmpdir/bld");
319 cleanup_remember("rm -rf $tmpdir/src");
320 cleanup_remember("rm -rf $tmpdir/tmp");
321 cleanup_remember("rm -rf $tmpdir/bld");
322 cleanup_remember("rm -rf $tmpdir/pkg");
323 runcmd("mkdir $tmpdir/src/.openpkg");
324 open(MACRO, ">$tmpdir/src/.openpkg/rpmmacros")
325 or die "unable to write file \"$tmpdir/src/.openpkg/rpmmacros\": $!";
326 print MACRO "\%openpkg_layout macrosfile=\%{macrosfile} layout=local shared=no\n";
327 close(MACRO);
328 $ENV{HOME} = $tmpdir;
330 # generate OpenPKG RPM .spec file for proxy OpenPKG RPM package
331 verbose("++ generating OpenPKG RPM package specification for proxy package");
332 verbose("-- file: $tmpdir/src/".$r->{NAME}.".spec");
333 my $S = '';
334 $S .= "Name: ".$r->{NAME}."\n";
335 $S .= "Summary: ".$r->{SUMMARY}."\n";
336 $S .= "URL: ".$r->{URL}."\n";
337 $S .= "Vendor: ".$r->{VENDOR}."\n";
338 $S .= "Packager: ".$r->{PACKAGER}."\n";
339 $S .= "Distribution: ".$r->{DISTRIBUTION}."\n";
340 $S .= "Class: ".$r->{CLASS}."\n";
341 $S .= "Group: ".$r->{GROUP}."\n";
342 $S .= "License: ".$r->{LICENSE}."\n";
343 $S .= "Version: ".$r->{VERSION}."\n";
344 $S .= "Release: ".$r->{RELEASE}."+PROXY\n";
345 $S .= "\n";
346 $S .= "BuildPreReq: $BD\n" if ($BD ne '');
347 $S .= "PreReq: $ID\n" if ($ID ne '');
348 $S .= "Provides: $PR\n" if ($PR ne '');
349 $S .= "\n";
350 $S .= "\%description\n";
351 $S .= " ".$r->{DESCRIPTION}."\n";
352 $S .= "\n";
353 $S .= "\%install\n";
354 $S .= " \%{l_rpmtool} files -v -ofiles -r\$RPM_BUILD_ROOT \%{l_files_std}\n";
355 $S .= "\n";
356 $S .= "\%files -f files\n";
357 $S .= "\n";
358 open(SPEC, ">$tmpdir/src/".$r->{NAME}.".spec")
359 or die "unable to write file \"$tmpdir/src/".$r->{NAME}."\": $!";
360 print SPEC $S;
361 close(SPEC);
363 # creating shadow tree of original contents
364 verbose("++ creating shadow tree from original package contents");
365 $q = '[%{FILEMODES:perms} %{FILENAMES}\n]';
366 $cmd = sprintf("%s -q %s --qf '$q' '%s'", $master_rpm, (-f $input ? "-p" : ""), $input);
367 my @FL = runcmd("$cmd|");
368 my $FD = [];
369 my $FR = [];
370 foreach my $fl (@FL) {
371 $fl =~ s|\n$||s;
372 if ($fl =~ m|^(d\S+)\s+$master_prefix(.*)$|) {
373 mkdirp("$tmpdir/bld$slave_prefix$2");
374 verbose("-- | PHYS $1 $slave_prefix$2");
375 }
376 elsif ($fl =~ m|^(\S+)\s+$master_prefix(.*?)([^/\s]+)$|) {
377 my ($subdir, $file) = ($2, $3);
378 my $target = sub2rev($subdir)."/.prefix-".$r->{NAME}.$subdir.$file;
379 mkdirp("$tmpdir/bld$slave_prefix$subdir");
380 runcmd("ln -s $target $tmpdir/bld$slave_prefix$subdir$file");
381 verbose("-- | VIRT $1 $slave_prefix$subdir$file");
382 }
383 }
385 # create master-reference symbolic link
386 runcmd("ln -s $master_prefix $tmpdir/bld$slave_prefix/.prefix-".$r->{NAME});
388 # rolling output proxy RPM package
389 verbose("++ rolling output proxy RPM package");
390 runcmd(
391 "cd $tmpdir/src && " .
392 "$slave_rpm -bb " .
393 "--define 'buildroot $tmpdir/bld' " .
394 "--define '__spec_install_pre \%{___build_pre}' " .
395 "--nodeps ".$r->{NAME}.".spec"
396 );
398 # providing output
399 verbose("++ providing output: $output");
400 if ($output eq '-') {
401 runcmd("cat $tmpdir/src/*.rpm");
402 }
403 else {
404 runcmd("cp $tmpdir/src/*.rpm $output");
405 }
407 # die gracefully...
408 verbose("++ cleaning up environment");
409 cleanup_perform();
410 exit(0);