From fb7fdb15e6073390c7f78240fdec92047924c240 Mon Sep 17 00:00:00 2001 From: MCApollo <34170230+MCApollo@users.noreply.github.com> Date: Tue, 23 Apr 2019 21:12:01 -0500 Subject: Slight Cleanup. TODO: - Reformat scripts. - Allow for 'mini-packages' inside 'main' packages to decrease /data clutter. --- util/dm.pl | 269 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100755 util/dm.pl (limited to 'util/dm.pl') diff --git a/util/dm.pl b/util/dm.pl new file mode 100755 index 000000000..5f743668d --- /dev/null +++ b/util/dm.pl @@ -0,0 +1,269 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use File::Find; +use File::Spec; +use Cwd; +use Getopt::Long; +use Pod::Usage; +use Archive::Tar; +use IPC::Open2; +use POSIX; + +package NIC::Archive::Tar::File; +use parent "Archive::Tar::File"; +sub new { + my $class = shift; + my $self = Archive::Tar::File->new(@_); + bless($self, $class); + return $self; +} + +sub full_path { + my $self = shift; + my $full_path = $self->SUPER::full_path(); $full_path = '' unless defined $full_path; + $full_path =~ s#^#./# if $full_path ne "" && $full_path ne "." && $full_path !~ m#^\./#; + return $full_path; +} +1; +package main; + +our $VERSION = '2.0'; + +our $_PROGNAME = "dm.pl"; + +my $ADMINARCHIVENAME = "control.tar.gz"; +my $DATAARCHIVENAME = "data.tar"; +my $ARCHIVEVERSION = "2.0"; + +our $compression = "gzip"; +our $compresslevel = -1; +Getopt::Long::Configure("bundling", "auto_version"); +GetOptions('compression|Z=s' => \$compression, + 'compress-level|z=i' => \$compresslevel, + 'build|b' => sub { }, + 'help|?' => sub { pod2usage(1); }, + 'man' => sub { pod2usage(-exitstatus => 0, -verbose => 2); }) + or pod2usage(2); + +pod2usage(1) if(@ARGV < 2); + +if($compresslevel < 0 || $compresslevel > 9) { + $compresslevel = 6; + $compresslevel = 9 if $compression eq "bzip2"; +} + +if(($compression eq "bzip2" || $compression eq "gzip") && $compresslevel eq 0) { + $compresslevel = 1; +} + +my $pwd = Cwd::cwd(); +my $indir = File::Spec->rel2abs($ARGV[0]); +my $outfile = $ARGV[1]; + +die "ERROR: '$indir' is not a directory or does not exist.\n" unless -d $indir; + +my $controldir = File::Spec->catpath("", $indir, "DEBIAN"); + +die "ERROR: control directory '$controldir' is not a directory or does not exist.\n" unless -d $controldir; +my $mode = (lstat($controldir))[2]; +die sprintf("ERROR: control directory has bad permissions %03lo (must be >=0755 and <=0775)\n", $mode & 07777) if(($mode & 07757) != 0755); + +my $controlfile = File::Spec->catfile($controldir, "control"); +die "ERROR: control file '$controlfile' is not a plain file\n" unless -f $controlfile; +my %control_data = read_control_file($controlfile); + +die "ERROR: control file '$controlfile' is missing a Package field" unless defined $control_data{"package"}; +die "ERROR: control file '$controlfile' is missing a Version field" unless defined $control_data{"version"}; +die "ERROR: control file '$controlfile' is missing an Architecture field" unless defined $control_data{"architecture"}; + +die "ERROR: package name has characters that aren't lowercase alphanums or '-+.'.\n" if($control_data{"package"} =~ m/[^a-z0-9+-._]/); +die "ERROR: package version ".$control_data{"version"}." doesn't contain any digits.\n" if($control_data{"version"} !~ m/[0-9]/); + +foreach my $m ("preinst", "postinst", "prerm", "postrm", "extrainst_") { + $_ = File::Spec->catfile($controldir, $m); + next unless -e $_; + die "ERROR: maintainer script '$m' is not a plain file or symlink\n" unless(-f $_ || -l $_); + $mode = (lstat)[2]; + die sprintf("ERROR: maintainer script '$m' has bad permissions %03lo (must be >=0555 and <=0775)\n", $mode & 07777) if(($mode & 07557) != 0555) +} + +if (-d "$outfile") { + $outfile = sprintf('%s/%s_%s_%s.deb', $outfile, $control_data{"package"}, $control_data{"version"}, $control_data{"architecture"}); +} + +print "$_PROGNAME: building package `".$control_data{"package"}.":".$control_data{"architecture"}."' in `$outfile'\n"; + +open(my $ar, '>', $outfile) or die $!; + +print $ar "!\n"; +print_ar_record($ar, "debian-binary", time, 0, 0, 0100644, 4); +print_ar_file($ar, "$ARCHIVEVERSION\n", 4); + +{ + my $tar = Archive::Tar->new(); + $tar->add_files(tar_filelist($controldir)); + my $comp; + my $zFd = IO::Compress::Gzip->new(\$comp, -Level => 9); + $tar->write($zFd); + $zFd->close(); + print_ar_record($ar, $ADMINARCHIVENAME, time, 0, 0, 0100644, length($comp)); + print_ar_file($ar, $comp, length($comp)); +} { + my $tar = Archive::Tar->new(); + $tar->add_files(tar_filelist($indir)); + my ($fh_out, $fh_in); + my $pid = open2($fh_out, $fh_in, compression_cmd()) or die "ERROR: open2 failed to create pipes for '$::compression'\n"; + fcntl($fh_out, F_SETFL, O_NONBLOCK); + fcntl($fh_in, F_SETFL, O_NONBLOCK); + my $tmp_data = $tar->write(); + my $tmp_size = length($tmp_data); + + my ($off_in, $off_out) = (0, 0); + my ($archivedata, $archivesize); + while($off_in < $tmp_size) { + my ($rin, $win) = ('', ''); + my ($rout, $wout); + vec($win, fileno($fh_in), 1) = 1; + vec($rin, fileno($fh_out), 1) = 1; + # Wait for space to be available to write or data to be available to read + select($rout=$rin, $wout=$win, undef, undef); + if (vec($wout, fileno($fh_in), 1)) { + # Write 8KB of data + my $wrote = syswrite $fh_in, $tmp_data, 8192, $off_in; + $off_in += $wrote if (defined $wrote); + } + if (vec($rin, fileno($fh_out), 1)) { + # Get the compressed result if possible + my $o = sysread $fh_out, $archivedata, 8192, $off_out; + $off_out += $o if (defined($o)); + } + } + $fh_in->close(); + + while (1) { + # Get the remaining data + my $o = sysread $fh_out, $archivedata, 8192, $off_out; + if (defined($o) && $o > 0) { + $off_out += $o; + } elsif ($! != EAGAIN) { + last; + } + } + $archivesize = $off_out; + $fh_out->close(); + waitpid($pid, 0); + print_ar_record($ar, compressed_filename($DATAARCHIVENAME), time, 0, 0, 0100644, $archivesize); + print_ar_file($ar, $archivedata, $archivesize); +} + +close $ar; + +sub print_ar_record { + my ($fh, $filename, $timestamp, $uid, $gid, $mode, $size) = @_; + printf $fh "%-16s%-12lu%-6lu%-6lu%-8lo%-10ld`\n", $filename, $timestamp, $uid, $gid, $mode, $size; + $fh->flush(); +} + +sub print_ar_file { + my ($fh, $data, $size) = @_; + syswrite $fh, $data; + print $fh "\n" if($size % 2 == 1); + $fh->flush(); +} + +sub tar_filelist { + my $dir = getcwd; + chdir(shift); + my @filelist; + my @symlinks; + + find({wanted => sub { + return if m#^./DEBIAN#; + my $tf = NIC::Archive::Tar::File->new(file=>$_); + my $mode = (lstat($_))[2] & 07777; + $tf->mode($mode); + $tf->chown("root", "wheel"); + push @symlinks, $tf if -l; + push @filelist, $tf if ! -l; + }, no_chdir => 1}, "."); + chdir($dir); + return (@filelist, @symlinks); +} + +sub read_control_file { + my $filename = shift; + open(my $fh, '<', $filename) or die "ERROR: can't open control file '$filename'\n"; + my %data; + while(<$fh>) { + die "ERROR: control file contains Windows/Macintosh line endings - please use a text editor or dos2unix to change to Unix line endings\n" if(m/\r/); + if(m/^(.*?): (.*)/) { + $data{lc($1)} = $2; + } + } + close $fh; + return %data; +} + +sub compression_cmd { + return "gzip -c".$compresslevel if $::compression eq "gzip"; + return "bzip2 -c".$compresslevel if $::compression eq "bzip2"; + return "lzma -c".$compresslevel if $::compression eq "lzma"; + return "xz -c".$compresslevel if $::compression eq "xz"; + if($::compression ne "cat") { + print "WARNING: compressor '$::compression' is unknown, falling back to cat.\n"; + } + return "cat"; +} + +sub compressed_filename { + my $fn = shift; + my $suffix = ""; + $suffix = ".gz" if $::compression eq "gzip"; + $suffix = ".bz2" if $::compression eq "bzip2"; + $suffix = ".lzma" if $::compression eq "lzma"; + $suffix = ".xz" if $::compression eq "xz"; + return $fn.$suffix; +} + +__END__ + +=head1 NAME + +dm.pl + +=head1 SYNOPSIS + +dm.pl [options] + +=head1 OPTIONS + +=over 8 + +=item B<-b> + +This option exists solely for compatibility with dpkg-deb. + +=item B<-ZEcompressionE> + +Specify the package compression type. Valid values are gzip (default), bzip2, lzma, xz and cat (no compression.) + +=item B<-zEcompress-levelE> + +Specify the package compression level. Valid values are between 0 and 9. Default is 9 for bzip2, 6 for others. 0 is identical to 1 when using bzip2 and gzip. Refer to B, B, B for explanations of what effect each compression level has. + +=item B<--help>, B<-?> + +Print a brief help message and exit. + +=item B<--man> + +Print a manual page and exit. + +=back + +=head1 DESCRIPTION + +B creates Debian software packages (.deb files) and is a drop-in replacement for dpkg-deb. + +=cut -- cgit v1.2.3