Monday, April 14, 2008

Converting Solaris Pkg's Into RPM's on Linux

Greetings,

Today, we're going to wrap up our series of posts on rpm/pkg mixing and matching, as this is about the last thing that's, technically, really worthwhile doing (I think). So far we've created the following scripts based on Linux RPM and/or Solaris Unix pkg dissection, translation and derivation:

How to unpack Solaris datastream packages without using Sun's pkg tools

How to build pkg's using spec files, like in Linux, with pkg-build

How to create RPM's from already installed RPM packages

How to create Solaris datastream pkg's from already installed pkg's

How to convert Linux RPM's into Solaris datastream pkg's

How to build Linux RPM's part 1- Initial points

How to build Linux RPM's part 2 - Starting your spec file

How to build Linux RPM's part 3 - Finalizing the spec file and building the RPM

How to build your own Solaris datastream pkg's quickly

How to build your own Solaris datastream pkg's (more theory than practice)


And, now, whether or not it will ever be practical, we've put together a script to convert Solaris pkg's to RPM's on a Linux system. If nothing else, it rounds everything out :)

The script is easy to run, and only requires one argument of the pkg file that you'll be converting. For instance, to convert the Solaris 9 m4 pkg from SunFreeware.com, you'd just type this (after unzipping the package with gunzip):

host # ./pkg2rpm m4-1.4.10-sol9-sparc-local

One special note about this Perl script: When the pkg gets converted to an RPM, it's done so with the files (listed under the "%files" specification in the "spec file") saved in the RPM with the full path to the temporary build root. This is done because "%files" won't accept relative pathnames and also won't allow you to add files that don't exist (if you, say, populate "%files" with %{prefix}/bin/whatever, etc). Instead, the "Prefix:" value is set to the "BASEDIR" value in the pkg file (e.g. /usr/local), but the "%files" listed are in their actual location during the build (otherwise we'd be forced to overwrite good Linux binaries with potentially unexecutable Solaris binaries just to build the RPM). Therefore, you'll need to install the newly created RPM with the "--relocate" switch.

So, in our example, assuming we ran the above command from "/home/user" (with the pkg file being in that directory), we would want to install the resulting RPM file by first determining where it's supposed to be relocated and then running the "rpm" command, like so:

host # ls
m4-1.4.10-sol9-sparc-local m4-1.4.10-1.x86_64.rpm
host # rpm -qif m4-1.4.10-1.x86_64.rpm|grep -i reloc
Name : m4-1.4.10-1 Relocations: /usr/local
<--- The "Relocations:" specification is where we'll be relocating "to."
host # rpm -qlf m4-1.4.10-1|head -1 <--- The trunk of this output shows us where we will be relocating "from." In this case "/home/user/SMCm4"
/home/user/SMCm4/bin/m4

Now we know where we're relocating "from" and "to," so we're ready to install (Note that you can install this RPM in your home directory, or the build directory, if you want to. The only downside is the inconvenience ;)

host # rpm --force --nodeps --relocate=/home/user/SMCm4=/usr/local -ivh m4-1.4.10-1.x86_64.rpm
Preparing... ########################################### [100%]
1:m4 ########################################### [100%]


Now, all we have to do is a quick verify and we should see that we're all set:

host # rpm -q m4-1.4.10-1
m4-1.4.10-1
host # rpm -ql m4-1.4.10-1.x86_64.rpm|head -5
/usr/local/bin/m4
/usr/local/doc/m4/AUTHORS
/usr/local/doc/m4/BACKLOG
/usr/local/doc/m4/COPYING
/usr/local/doc/m4/ChangeLog


Here's hoping we've covered every useful angle of Solaris Unix datastream pkg to Linux RPM conversion and manipulation that we possibly can. At least for the here and now ;)


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

Cheers,

#!/usr/bin/perl

#
# pkg2rpm - convert Solaris datastream pkg files to RPM's for Linux
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

if ( $#ARGV != 0 ) {
print "Usage: $0 pkg_file\n";
exit(1);
}

$pkg_name = $ARGV[0];

chomp($simple_header_check=`grep -i "package datastream" $pkg_name >/dev/null 2>&1;echo \$?`);

if ( $simple_header_check != 0 ) {
print "This doesn't appear to be a valid SVR4 DataStream pkg file. Exiting...\n";
exit;
}

open(PKG, "<$pkg_name");
@pkg_file = <PKG>;
close(PKG);

$header_end = 0;
$cpio_start = 0;
open (P_HEADERS, ">$pkg_name.headers");
foreach $line (@pkg_file) {
if ( $header_end == 0 && $line =~ /TRAILER/ && $line =~ /pkginfo/ ) {
print P_HEADERS $line;
$header_end = 1;
} elsif ( $header_end == 1 && $line =~ /pkginfo/ ) {
print P_HEADERS $line;
close(P_HEADERS);
$cpio_start = 1;
open(P_CPIO, ">$pkg_name.cpio");
} elsif ( $cpio_start == 0 ) {
print P_HEADERS $line;
} elsif ( $cpio_start == 1 ) {
print P_CPIO $line;
} else {
print STDERR "WTF?\n";
}
}
close(P_CPIO);

open(P_HEADERS, "<$pkg_name.headers");
@p_headers = <P_HEADERS>;
close(P_HEADERS);

$output_dir = 0;
$pkginfo_start = 0;
$pkginfo_seek = 0;
$pkgmap_start = 0;
foreach $p_head (@p_headers) {
if ( $output_dir eq 1 && $pkginfo_seek == 0 ) {
$output_dir = $p_head;
$output_dir =~ s/^(\w+)\W*.*$/$1/;
chomp($output_dir);
$pkginfo_seek = 1;
} elsif ( $output_dir eq 0 && $p_head =~ /datastream/i ) {
$output_dir = 1;
} elsif ( $output_dir eq 0 && $pkginfo_start == 0 ) {
next;
} elsif ( $output_dir ne 0 && $p_head =~ /pkgmap/ ) {
$pkgmap_start = 1;
$pkginfo_start = 0;
} elsif ( $pkgmap_start == 1 && $p_head =~ /pkginfo/ ) {
chomp($p_head);
push(@pkgmap, $p_head);
last;
} elsif ( $output_dir ne 0 && $p_head =~ /pkginfo.*PKG=/ ) {
$pkginfo_start = 1;
} elsif ( $output_dir ne 0 && $pkgmap_start == 1 ) {
chomp($p_head);
push(@pkgmap, $p_head);
} elsif ( $output_dir ne 0 && $pkginfo_start == 1 ) {
chomp($p_head);
push(@pkginfo, $p_head);
}
}

mkdir("$output_dir");
chdir("$output_dir");
open(PKGINFO_OUT, ">>pkginfo");
foreach $info (@pkginfo) {
print PKGINFO_OUT "$info\n";
}
close(PKGINFO_OUT);
open(PKGMAP_OUT, ">>pkgmap");
foreach $map (@pkgmap) {
print PKGMAP_OUT "$map\n";
}
close(PKGMAP_OUT);
open(RPM_SPEC_FILES, ">>${pkg_name}.specfiles");
foreach $specfile (@pkgmap) {
@specfile = split(" ", $specfile);
if ( $specfile[1] !~ /^d$/ && $specfile[2] =~ /^none$/ ) {
print RPM_SPEC_FILES "$specfile[3]\n";
}
}
close(RPM_SPEC_FILES);
system("cpio -iv <../$pkg_name.cpio >/dev/null 2>&1");
chdir("reloc");
system("tar cpf - *|(cd ../;tar xpf -)");
chdir("../");

$rpm_name=$pkg_name;
chomp($starting_point=`pwd`);
$temp_var=$$;

system("mkdir ${starting_point}/BUILD");
system("mkdir ${starting_point}/SOURCES");
system("mkdir ${starting_point}/SPECS");
system("mkdir ${starting_point}/SRPMS");
system("mkdir ${starting_point}/RPMS");
system("mkdir ${starting_point}/RPMS/noarch");
system("mkdir ${starting_point}/RPMS/`uname -i`");
system("mkdir $temp_var");
system("cd $temp_var");
open(PKGINFO_RPM, "<${starting_point}/pkginfo");
@pkginfo_rpm = <PKGINFO_RPM>;
close(PKGINFO_RPM);
($pkgrpm_name,$pkgrpm_arch,$pkgrpm_version,$pkgrpm_category,$pkgrpm_vendor,$pkgrpm_email,$pkgrpm_pstamp,$pkgrpm_basedir,$pkgrpm_classes) = @pkginfo_rpm;
$pkgrpm_name =~ s/.*=(.*)\n$/$1/;
$pkgrpm_arch =~ s/.*=(.*)\n$/$1/;
$pkgrpm_version =~ s/.*=(.*)\n$/$1/;
$pkgrpm_category =~ s/.*=(.*)\n$/$1/;
$pkgrpm_vendor =~ s/.*=(.*)\n$/$1/;
$pkgrpm_email =~ s/.*=(.*)\n$/$1/;
$pkgrpm_pstamp =~ s/.*=(.*)\n$/$1/;
$pkgrpm_basedir =~ s/.*=(.*)\n$/$1/;
$pkgrpm_classes =~ s/.*=(.*)\n$/$1/;
open(RPM_SPECIFICS, ">>$rpm_name.spec");
print RPM_SPECIFICS "%define _topdir $starting_point\n";
print RPM_SPECIFICS "Summary: $pkgrpm_name $pkgrpm_version\n";
print RPM_SPECIFICS "Name: $pkgrpm_name\n";
print RPM_SPECIFICS "Version: $pkgrpm_version\n";
print RPM_SPECIFICS "Release: 1\n";
print RPM_SPECIFICS "Copyright: $pkgrpm_pstamp\n";
print RPM_SPECIFICS "Group: $pkgrpm_category\n";
print RPM_SPECIFICS "Prefix: $pkgrpm_basedir\n";
print RPM_SPECIFICS "%description\n";
print RPM_SPECIFICS "Vendor = $pkgrpm_vendor - Email = $pkgrpm_email - Arch = $pkgrpm_arch - Classes = $pkgrpm_classes\n";
print RPM_SPECIFICS "%files\n";
open(RPM_SPEC_FILES, "<${starting_point}/$pkg_name.specfiles");
@specks = <RPM_SPEC_FILES>;
close(SPECKS);
foreach $speck (@specks) {
print RPM_SPECIFICS "${starting_point}/$speck\n";
}
close(RPM_SPECIFICS);

system("rpmbuild -bb $rpm_name.spec 2>&1|grep -v twice");
chdir("../");
system("mv -f ${starting_point}/RPMS/*/* .");
system("rm -rf $output_dir");
unlink "$pkg_name.headers";
unlink "$pkg_name.cpio";


, Mike