Home | History | Annotate | Download | only in tools
      1 #!/usr/perl5/bin/perl
      2 #
      3 # CDDL HEADER START
      4 #
      5 # The contents of this file are subject to the terms of the
      6 # Common Development and Distribution License (the "License").
      7 # You may not use this file except in compliance with the License.
      8 #
      9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10 # or http://www.opensolaris.org/os/licensing.
     11 # See the License for the specific language governing permissions
     12 # and limitations under the License.
     13 #
     14 # When distributing Covered Code, include this CDDL HEADER in each
     15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16 # If applicable, add the following below this CDDL HEADER, with the
     17 # fields enclosed by brackets "[]" replaced with your own identifying
     18 # information: Portions Copyright [yyyy] [name of copyright owner]
     19 #
     20 # CDDL HEADER END
     21 #
     22 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23 # Use is subject to license terms.
     24 # 
     25 #ident	"@(#)pkgtemplate.pl	1.1	08/03/26 SMI"
     26 #
     27 
     28 use Getopt::Long;
     29 use Cwd;
     30 use File::Find;
     31 use Fcntl ':mode';
     32 
     33 *name  = *File::Find::name;
     34 
     35 my ($pkg_pkg, $pkg_name, $pkg_description, $pkginfo, $prototype, $basedir) = (
     36 	'SUNWxxx', 'package name goes here', 'description goes here',
     37 	'pkginfo', 'prototype', '/');
     38 my (@proto_paths, %contents, $ignore_contents_file) = ();
     39 
     40 my $proto = $ENV{'ROOT'};
     41 
     42 $| = 1;
     43 
     44 sub load_contents_file {
     45 	my ($lineno, $line) = ();
     46 
     47 	open(CFD, "</var/sadm/install/contents");
     48 
     49 	print("loading contents file");
     50 
     51 	while ($line = <CFD>) {
     52 		my ($entry) = ();
     53 
     54 		(($lineno++ % 5000) == 0) && print(".");
     55 
     56 		if ($line =~ /^#/) {
     57 			next;
     58 		}
     59 
     60 		chop($line);
     61 
     62 		if ($line =~ m{/([^=]+)=([^\s]+) ([ls]) ([^\s]+) (.*)}) {
     63 			$entry->{path} = $1;
     64 			$entry->{rpath} = $2;
     65 			$entry->{type} = $3;
     66 			$entry->{class} = $4;
     67 			$entry->{pkgs} = split(/ /, $5);
     68 		} elsif ($line =~ m{/([^\s]+) ([dx]) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) (.*)}) {
     69 			$entry->{path} = $1;
     70 			$entry->{type} = $2;
     71 			$entry->{class} = $3;
     72 			$entry->{mode} = oct($4);
     73 			$entry->{owner} = $5;
     74 			$entry->{group} = $6;
     75 			$entry->{pkgs} = split(/ /, $7);
     76 		} elsif ($line =~ m{/([^\s]+) ([efv]) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) (.*)}) {
     77 			$entry->{path} = $1;
     78 			$entry->{type} = $2;
     79 			$entry->{class} = $3;
     80 			$entry->{mode} = oct($4);
     81 			$entry->{owner} = $5;
     82 			$entry->{group} = $6;
     83 			$entry->{size} = $7;
     84 			$entry->{sum} = $8;
     85 			$entry->{modtime} = $9;
     86 			$entry->{pkgs} = split(/ /, $10);
     87 		} elsif ($line =~ m{/([^\s]+) ([bc]) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) (.*)}) {
     88 			$entry->{path} = $1;
     89 			$entry->{type} = $2;
     90 			$entry->{class} = $3;
     91 			$entry->{major} = $4;
     92 			$entry->{minor} = $5;
     93 			$entry->{mode} = oct($6);
     94 			$entry->{owner} = $7;
     95 			$entry->{group} = $8;
     96 			$entry->{pkgs} = split(/ /, $9);
     97 		}  else {
     98 			print("unrecognized: $line\n");
     99 			next;
    100 		}
    101 
    102 		$contents{$entry->{path}} = $entry;
    103 	}
    104 	print("loaded\n");
    105 }
    106 
    107 sub find_proto_paths {
    108     ($name eq '.') && next;
    109     ($name =~ /\.\/(.*)/) && ($name = $1);
    110 	push(@proto_paths, $name);
    111 }
    112 
    113 sub file_header {
    114 	local ($FH) = @_;
    115 
    116 	my $year = (localtime)[5] + 1900;
    117 	print $FH
    118 "#
    119 # CDDL HEADER START
    120 #
    121 # The contents of this file are subject to the terms of the
    122 # Common Development and Distribution License (the \"License\").
    123 # You may not use this file except in compliance with the License.
    124 #
    125 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
    126 # or http://www.opensolaris.org/os/licensing.
    127 # See the License for the specific language governing permissions
    128 # and limitations under the License.
    129 #
    130 # When distributing Covered Code, include this CDDL HEADER in each
    131 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
    132 # If applicable, add the following below this CDDL HEADER, with the
    133 # fields enclosed by brackets \"[]\" replaced with your own identifying
    134 # information: Portions Copyright [yyyy] [name of copyright owner]
    135 #
    136 # CDDL HEADER END
    137 #
    138 
    139 #
    140 # Copyright $year Sun Microsystems, Inc.  All rights reserved.
    141 # Use is subject to license terms.
    142 #
    143 # ident	\"@(#)pkgtemplate.pl	1.1	08/03/26 SMI\"
    144 #
    145 
    146 " ;
    147 
    148 }
    149 sub pkginfo_file {
    150 	local ($FH, $pkg, $name, $description, $type) = @_;
    151 
    152 	file_header($FH);
    153 	my @classes = keys %classes;
    154 	print $FH
    155 "
    156 PKG=\"$pkg\"
    157 NAME=\"$name\"
    158 ARCH=\"ISA\"
    159 VERSION=\"SFWVERS,REV=0,0,0\"
    160 SUNW_PRODNAME=\"$name\"
    161 SUNW_PRODVERS=\"RELEASE/VERSION\"
    162 SUNW_PKGTYPE=\"$type\"
    163 MAXINST=\"1000\"
    164 CATEGORY=\"system\"
    165 DESC=\"$description\"
    166 VENDOR=\"SunMicrosystems, Inc.\"
    167 HOTLINE=\"Please contact your local service provider\"
    168 EMAIL=\"\"
    169 CLASSES=\"@classes\"
    170 BASEDIR=$basedir
    171 SUNW_PKGVERS=\"1.0\"
    172 SUNW_PKG_ALLZONES=\"false\"
    173 SUNW_PKG_HOLLOW=\"false\"
    174 SUNW_PKG_THISZONE=\"false\"
    175 " ;
    176 }
    177 
    178 sub file_type {
    179         local ($path) = @_;
    180 
    181         open(FH, "/bin/file $path|");
    182         my $line = <FH>;
    183         close(FH);
    184 	chop $line;
    185 
    186         # if the path matches the pattern, it's a man page.
    187         ($path =~ /.*\/man\/man.+\/.*\.[1-9].*/) && ($line = 'roff');
    188         if ($line =~ /XML document/) {
    189         	open(FH, "<$path");
    190 		my $tmp;
    191 
    192 		while(defined($tmp = <FH>)) {
    193         		($tmp =~ /DOCTYPE (.+) SYSTEM/) && ($line = $1);
    194 		}
    195         	close(FH);
    196 	}
    197 
    198         return($line);
    199 }
    200 
    201 sub pathinfo_to_class_type_mode {
    202 	my ($path, $imode) = @_;
    203 	my ($class, $type, $mode) = ('none', 'f', $imode);
    204 
    205 	if (-f $path) {
    206 	    $mode = $imode & 0555;
    207 	    
    208 		if (($path =~ m{^etc/}) && (-f $path)) {
    209 			($class, $type, $mode) = ('preserve', 'e', oct('0644'));
    210 		} elsif (defined($ftype = file_type($path))) {
    211 			if ($ftype =~ 'executable') {	# strip write bits from executables
    212 				$mode = $imode & 07555;
    213 			} elsif ($ftype eq 'service_bundle') {
    214 				($class, $type, $mode) = ('manifest', 'f', oct('0444'));
    215 			}
    216 		} elsif ($path =~ m{^var/.*$}) {
    217 			($type, $mode) = ('v', oct('0644'));
    218 		}
    219 	}
    220 
    221 	return ($class, $type, $mode);
    222 }
    223 
    224 sub filesystem_to_entry {
    225 	local ($path, %entry) = @_;
    226 
    227 	my ($dev, $inode, $mode, $nlink, $uid, $gid) = lstat($path);
    228 
    229 	if (defined($mode) && defined($uid) && defined($gid)) {
    230 		$entry->{path} = $path;
    231 		$entry->{class} = 'none';
    232 		$entry->{owner} = 'root'; # getpwuid($uid);
    233 		$entry->{group} = 'bin'; # getgrgid($gid);
    234 
    235 		if (S_ISDIR($mode)) {
    236 			$entry->{type} = 'd';
    237 			$entry->{mode} = S_IMODE($mode) #& 07555;
    238 		} elsif (S_ISREG($mode)) {
    239 			$entry->{type} = 'f';
    240 			($entry->{class}, $entry->{type}, $entry->{mode}) =
    241 					 pathinfo_to_class_type_mode($path, S_IMODE($mode));
    242 		} elsif (S_ISLNK($mode)) {
    243 			$entry->{type} = 's';
    244 			$entry->{rpath} = readlink($path);
    245 		} else {
    246 			printf("failed: $path: %8.8o\n", $mode);
    247 			$entry = ();
    248 		}
    249 	} else {
    250 		printf("%s: %s\n", $path, $!);
    251 	}
    252 
    253 	return $entry;
    254 }
    255 
    256 sub prototype_entry {
    257 	local ($FH, $base, $entry) = @_;
    258 	my $path = $entry->{path};
    259 
    260 	(length($base) > 0) && ($path =~ m{$base/(.*)}) && ($path = $1);
    261 	($path eq $base) && return;
    262 
    263 	(($path =~ /.*\.a$/) || ($path =~ /.*\.la$/)) &&
    264 		print($FH "# CHECKME: we don't normally ship this type of file\n");
    265 	(($path =~ /etc\/init\.d/) || ($path =~ /etc\/rc.*\.d/)) &&
    266 		print($FH "# CHECKME: ship the SMF bits instead\n");
    267 
    268 	(($path =~ /\/i86pc/)  || ($path =~ /\/amd64/) ||
    269 	 ($path =~ /\/sparc\//)  || ($path =~ /\/sun4\//)) &&
    270 		print($FH "# CHECKME: replace with entries in prototype_{arch}");
    271 	if ($entry->{type} =~ /[defvx]/) {
    272 		printf($FH "%s %s %s %4.4o %s %s\n", $entry->{type},
    273 			$entry->{class}, $path, $entry->{mode},
    274 			$entry->{owner}, $entry->{group});
    275 	} elsif ($entry->{type} =~ /[ls]/) {
    276 		printf($FH "%s %s %s=%s\n", $entry->{type}, $entry->{class},
    277 			$path, $entry->{rpath});
    278 	}
    279 }
    280 
    281 sub prototype_header {
    282 	local ($FH) = @_;
    283 
    284 	print $FH "i pkginfo\n";
    285 	print $FH "i copyright\n";
    286 	print $FH "i depend\n\n";
    287 }
    288 
    289 sub prototype_file {
    290 	local ($FH, $base, @paths) = @_;
    291 
    292 	my $lineno = 0;
    293 
    294 	for $path (@paths) {
    295 		($path eq '.') && next;	# skip .
    296 
    297 		(($lineno++ % 100) == 0) && print(".");
    298 
    299 		# use the contents file data first.
    300 		my ($centry, $fentry, $entry) =
    301 			($contents{$path}, filesystem_to_entry($path));
    302 		if (defined($centry) && defined($fentry)) {
    303 			if ($centry->{type} ne $fentry->{type}) {
    304 				print $FH "# CHECK ME: package db/filesystem conflict\n";
    305 				$entry = $fentry;
    306 			} else {
    307 				$entry = $centry;
    308 			}
    309 		} elsif (defined($centry)) {
    310 			$entry = $centry;
    311 		} elsif (defined($fentry)) {
    312 			$entry = $fentry;
    313 		}
    314 
    315 		if (defined($entry)) {
    316 			prototype_entry($FH, $base, $entry);
    317 			$classes{$entry->{class}} = $entry->{class};
    318 		}
    319 	}
    320 }
    321 
    322 sub prototype_com_file {
    323 	local ($FH, @paths) = @_;
    324 	my $base = $basedir;
    325 
    326 	($base =~ m{/(.*)}) && ($base = $1);
    327 
    328 	file_header($FH);
    329 	print $FH "i pkginfo\n";
    330 	print $FH "i copyright\n";
    331 	print $FH "i depend\n\n";
    332         prototype_file($FH, $base, @paths);
    333 }
    334 
    335 sub prototype_arch_file {
    336 	local ($FH, @paths) = @_;
    337 	my $base = $basedir;
    338 
    339 	($base =~ m{/(.*)}) && ($base = $1);
    340 
    341 	file_header($FH);
    342 	print $FH "!include prototype_com\n\n";
    343         prototype_file($FH, $base, @paths);
    344 }
    345 
    346 sub generate_package {
    347     local ($pkg, $name, $description, $type, @paths) = @_;
    348     my $pkgdir = "$ENV{'SRC'}/pkgdefs/$pkg";
    349 
    350     %classes = ();    
    351     print("Generating $pkg ($pkgdir)...");
    352 
    353     mkdir($pkgdir, 0777);
    354     (-f $license_file) &&
    355         copy($license_file, "$pkgdir/copyright");
    356  
    357     # this should split the paths into architecture specific and independent
    358     # file sets.   
    359     open($FH, ">$pkgdir/prototype_com");
    360     prototype_com_file($FH, sort @paths);
    361     close($FH);
    362 
    363     open($FH, ">$pkgdir/prototype_i386");
    364     prototype_arch_file($FH); #, sort @paths);
    365     close($FH);
    366 
    367     open($FH, ">$pkgdir/prototype_sparc");
    368     prototype_arch_file($FH); #, sort @paths);
    369     close($FH);
    370 
    371     #print("\n"); return;
    372     
    373     open($FH, ">$pkgdir/pkginfo.tmpl");
    374     pkginfo_file($FH, $pkg, $name, $description, $type);
    375     close($FH);
    376     
    377     print("\n");
    378 }
    379 
    380 sub root_usr_opt_split {
    381     local($root, $usr, $opt, @paths) = @_;
    382     my $path;
    383     
    384     for $path (@paths) {
    385         if ($path =~ /^opt/) {
    386             push(@{$opt}, $path);
    387         } elsif ($path =~ /^usr/) {
    388             push(@{$usr}, $path);
    389         } else {
    390             push(@{$root}, $path);
    391         }
    392     }
    393 }
    394 
    395 #
    396 # Execution begins here
    397 #
    398 
    399 GetOptions('pkg=s' => \$pkg_pkg, 'name=s' => \$pkg_name,
    400 	   'description=s' => \$description, 'basedir=s' => \$basedir,
    401 	   'ignore-contents-file', \$ignore_contents_file,
    402            'pkginfo=s' => \$pkginfo, 'prototype=s' => \$prototype,
    403            'proto=s' => \$proto);
    404 
    405 # load the contents file
    406 (!defined($ignore_contents_file)) && load_contents_file();
    407 
    408 print("Enumerating files in $proto...");
    409 @proto_paths = ();
    410 chdir($proto);
    411 File::Find::find({wanted => \&find_proto_paths}, '.');
    412 @paths = @proto_paths;
    413 print("done\n");
    414 
    415 my (@root, @usr, @opt) = ();
    416 root_usr_opt_split(\@root, \@usr, \@opt, @paths);
    417 
    418 if (($#root >= 0) && ($#usr >= 0)) {
    419     generate_package($pkg_pkg.'r', $pkg_name.' (root)',
    420                      $pkg_description.' (root)', 'root', @root);
    421     generate_package($pkg_pkg.'u', $pkg_name.' (usr)',
    422                      $pkg_description.' (usr)', 'usr', @usr);
    423 } elsif ($#opt >= 0) {
    424     # assume that "optional" software delivers all files in /opt
    425     generate_package($pkg_pkg, $pkg_name, $pkg_description, 'optional', @opt);
    426 } else {
    427     generate_package($pkg_pkg, $pkg_name, $pkg_description, 'usr', @paths);
    428 }
    429 
    430 exit 0;
    431