home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 1998 February
/
CHIP_2_98.iso
/
misc
/
src
/
rpm
/
build
/
pack.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-09-17
|
21KB
|
757 lines
/* The very final packaging steps */
#include "config.h"
#include "miscfn.h"
#if HAVE_ALLOCA_H
# include <alloca.h>
#endif
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h> /* For 'select()' interfaces */
#include "cpio.h"
#include "pack.h"
#include "header.h"
#include "spec.h"
#include "specP.h"
#include "signature.h"
#include "rpmlead.h"
#include "rpmlib.h"
#include "misc.h"
#include "stringbuf.h"
#include "names.h"
#include "files.h"
#include "reqprov.h"
#include "trigger.h"
#include "messages.h"
static int writeMagic(int fd, char *name, unsigned short type);
static int cpio_gzip(int fd, char *tempdir, char *writePtr,
int *archiveSize, char *prefix);
static int generateRPM(char *name, /* name-version-release */
char *filename, /* output filename */
int type, /* source or binary */
Header header, /* the header */
char *stempdir, /* directory containing sources */
char *fileList, /* list of files for cpio */
char *passPhrase, /* PGP passphrase */
char *prefix);
static int generateRPM(char *name, /* name-version-release */
char *filename, /* output filename */
int type, /* source or binary */
Header header, /* the header */
char *stempdir, /* directory containing sources */
char *fileList, /* list of files for cpio */
char *passPhrase,
char *prefix)
{
int_32 sigtype;
char *sigtarget;
int fd, ifd, count, archiveSize;
unsigned char buffer[8192];
Header sig;
/* Add the a bogus archive size to the Header */
headerAddEntry(header, RPMTAG_ARCHIVESIZE, RPM_INT32_TYPE,
&archiveSize, 1);
/* Write the header */
if (makeTempFile(NULL, &sigtarget, &fd))
return 1;
headerWrite(fd, header, HEADER_MAGIC_YES);
/* Write the archive and get the size */
if (cpio_gzip(fd, stempdir, fileList, &archiveSize, prefix)) {
close(fd);
unlink(sigtarget);
free(sigtarget);
return 1;
}
/* Now set the real archive size in the Header */
headerModifyEntry(header, RPMTAG_ARCHIVESIZE,
RPM_INT32_TYPE, &archiveSize, 1);
/* Rewind and rewrite the Header */
lseek(fd, 0, SEEK_SET);
headerWrite(fd, header, HEADER_MAGIC_YES);
close(fd);
/* Now write the lead */
if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
fprintf(stderr, "Could not open %s\n", filename);
unlink(sigtarget);
free(sigtarget);
unlink(filename);
return 1;
}
if (writeMagic(fd, name, type)) {
close(fd);
unlink(sigtarget);
free(sigtarget);
unlink(filename);
return 1;
}
/* Generate the signature */
sigtype = rpmLookupSignatureType();
rpmMessage(RPMMESS_VERBOSE, "Generating signature: %d\n", sigtype);
fflush(stdout);
sig = rpmNewSignature();
rpmAddSignature(sig, sigtarget, RPMSIGTAG_SIZE, passPhrase);
rpmAddSignature(sig, sigtarget, RPMSIGTAG_MD5, passPhrase);
if (sigtype>0) {
rpmAddSignature(sig, sigtarget, sigtype, passPhrase);
}
if (rpmWriteSignature(fd, sig)) {
close(fd);
unlink(sigtarget);
free(sigtarget);
unlink(filename);
rpmFreeSignature(sig);
return 1;
}
rpmFreeSignature(sig);
/* Append the header and archive */
ifd = open(sigtarget, O_RDONLY);
while ((count = read(ifd, buffer, sizeof(buffer))) > 0) {
if (count == -1) {
perror("Couldn't read sigtarget");
close(ifd);
close(fd);
unlink(sigtarget);
free(sigtarget);
unlink(filename);
return 1;
}
if (write(fd, buffer, count) < 0) {
perror("Couldn't write package");
close(ifd);
close(fd);
unlink(sigtarget);
free(sigtarget);
unlink(filename);
return 1;
}
}
close(ifd);
close(fd);
unlink(sigtarget);
free(sigtarget);
rpmMessage(RPMMESS_VERBOSE, "Wrote: %s\n", filename);
return 0;
}
static int writeMagic(int fd, char *name,
unsigned short type)
{
struct rpmlead lead;
int arch, os;
rpmGetArchInfo(NULL, &arch);
rpmGetOsInfo(NULL, &os);
/* There are the Major and Minor numbers */
lead.major = 3;
lead.minor = 0;
lead.type = type;
lead.archnum = arch;
lead.osnum = os;
lead.signature_type = RPMSIG_HEADERSIG; /* New-style signature */
strncpy(lead.name, name, sizeof(lead.name));
writeLead(fd, &lead);
return 0;
}
static int cpio_gzip(int fd, char *tempdir, char *writePtr,
int *archiveSize, char *prefix)
{
int gzipPID;
int toGzip[2];
int rc;
char * gzipbin;
char * writebuf;
char * chptr;
int numMappings;
struct cpioFileMapping * cpioList;
int status;
void *oldhandler;
char savecwd[1024];
char * failedFile;
gzipbin = rpmGetVar(RPMVAR_GZIPBIN);
numMappings = 0;
chptr = writePtr;
while (chptr && *chptr) {
numMappings++;
chptr = strchr(chptr, '\n');
if (chptr) *chptr++ = '\n';
}
writebuf = alloca(strlen(writePtr) + 1);
strcpy(writebuf, writePtr);
cpioList = alloca(sizeof(*cpioList) * numMappings);
chptr = writebuf;
numMappings = 0;
while (chptr && *chptr) {
cpioList[numMappings].fsPath = chptr;
cpioList[numMappings].mapFlags = tempdir ? CPIO_FOLLOW_SYMLINKS : 0;
chptr = strchr(chptr, '\n');
if (chptr) *chptr++ = '\0';
/* hack */
if (!strlen(cpioList[numMappings].fsPath)) {
cpioList[numMappings].fsPath = ".";
}
numMappings++;
}
oldhandler = signal(SIGPIPE, SIG_IGN);
pipe(toGzip);
/* GZIP */
if (!(gzipPID = fork())) {
close(toGzip[1]);
dup2(toGzip[0], 0); /* Make stdin the in pipe */
dup2(fd, 1); /* Make stdout the passed-in fd */
execlp(gzipbin, gzipbin, "-c9fn", NULL);
rpmError(RPMERR_EXEC, "Couldn't exec gzip");
_exit(RPMERR_EXEC);
}
if (gzipPID < 0) {
rpmError(RPMERR_FORK, "Couldn't fork gzip");
return RPMERR_FORK;
}
close(toGzip[0]);
getcwd(savecwd, 1024);
if (tempdir) {
chdir(tempdir);
} else if (rpmGetVar(RPMVAR_ROOT)) {
if (chdir(rpmGetVar(RPMVAR_ROOT))) {
rpmError(RPMERR_EXEC, "Couldn't chdir to %s",
rpmGetVar(RPMVAR_ROOT));
exit(RPMERR_EXEC);
}
} else {
/* This is important! */
chdir("/");
}
if (prefix) {
if (chdir(prefix)) {
rpmError(RPMERR_EXEC, "Couldn't chdir to %s", prefix);
_exit(RPMERR_EXEC);
}
}
rc = cpioBuildArchive(toGzip[1], cpioList, numMappings, NULL, NULL,
archiveSize, &failedFile);
chdir(savecwd);
close(toGzip[1]); /* Terminates the gzip process */
signal(SIGPIPE, oldhandler);
waitpid(gzipPID, &status, 0);
if (!WIFEXITED(status) || WEXITSTATUS(status)) {
rpmError(RPMERR_GZIP, "gzip failed");
return 1;
}
if (rc) {
if (rc & CPIO_CHECK_ERRNO)
rpmError(RPMERR_CPIO, "cpio failed on file %s: %d: %s", failedFile,
rc, strerror(errno));
else
rpmError(RPMERR_CPIO, "cpio failed on file %s: %d", failedFile, rc);
return 1;
}
return 0;
}
int packageBinaries(Spec s, char *passPhrase, int doPackage)
{
char name[1024];
char *nametmp;
char filename[1024];
char sourcerpm[1024];
char * binrpm;
char *icon;
int iconFD;
struct stat statbuf;
struct PackageRec *pr;
Header outHeader;
HeaderIterator headerIter;
int_32 tag, type, c;
void *ptr;
char *version;
char *release;
char *vendor;
char *dist;
char *packager;
char *packageVersion, *packageRelease;
char *prefix, *prefixSave;
char * arch, * os;
char * binformat, * errorString;
int prefixLen;
int size;
StringBuf cpioFileList;
char **farray, *file;
int count;
if (!headerGetEntry(s->packages->header, RPMTAG_VERSION, NULL,
(void *) &version, NULL)) {
rpmError(RPMERR_BADSPEC, "No version field");
return RPMERR_BADSPEC;
}
if (!headerGetEntry(s->packages->header, RPMTAG_RELEASE, NULL,
(void *) &release, NULL)) {
rpmError(RPMERR_BADSPEC, "No release field");
return RPMERR_BADSPEC;
}
/* after the headerGetEntry() these are just pointers into the */
/* header structure, which can be moved around by headerAddEntry */
version = strdup(version);
release = strdup(release);
sprintf(sourcerpm, "%s-%s-%s.%ssrc.rpm", s->name, version, release,
(s->numNoPatch + s->numNoSource) ? "no" : "");
vendor = NULL;
if (!headerIsEntry(s->packages->header, RPMTAG_VENDOR)) {
vendor = rpmGetVar(RPMVAR_VENDOR);
}
dist = NULL;
if (!headerIsEntry(s->packages->header, RPMTAG_DISTRIBUTION)) {
dist = rpmGetVar(RPMVAR_DISTRIBUTION);
}
packager = NULL;
if (!headerIsEntry(s->packages->header, RPMTAG_PACKAGER)) {
packager = rpmGetVar(RPMVAR_PACKAGER);
}
/* Look through for each package */
pr = s->packages;
while (pr) {
/* A file count of -1 means no package */
if (pr->files == -1) {
pr = pr->next;
continue;
}
/* Handle subpackage version/release overrides */
if (!headerGetEntry(pr->header, RPMTAG_VERSION, NULL,
(void *) &packageVersion, NULL)) {
packageVersion = version;
}
if (!headerGetEntry(pr->header, RPMTAG_RELEASE, NULL,
(void *) &packageRelease, NULL)) {
packageRelease = release;
}
/* after the headerGetEntry() these are just pointers into the */
/* header structure, which can be moved around by headerAddEntry */
packageVersion = strdup(packageVersion);
packageRelease = strdup(packageRelease);
/* Figure out the name of this package */
if (!headerGetEntry(pr->header, RPMTAG_NAME, NULL, (void *)&nametmp, NULL)) {
rpmError(RPMERR_INTERNAL, "Package has no name!");
return RPMERR_INTERNAL;
}
sprintf(name, "%s-%s-%s", nametmp, packageVersion, packageRelease);
if (doPackage == PACK_PACKAGE) {
rpmMessage(RPMMESS_VERBOSE, "Binary Packaging: %s\n", name);
} else {
rpmMessage(RPMMESS_VERBOSE, "File List Check: %s\n", name);
}
/**** Generate the Header ****/
/* Here's the plan: copy the package's header, */
/* then add entries from the primary header */
/* that don't already exist. */
outHeader = headerCopy(pr->header);
headerIter = headerInitIterator(s->packages->header);
while (headerNextIterator(headerIter, &tag, &type, &ptr, &c)) {
/* Some tags we don't copy */
switch (tag) {
case RPMTAG_PREIN:
case RPMTAG_POSTIN:
case RPMTAG_PREUN:
case RPMTAG_POSTUN:
case RPMTAG_PREINPROG:
case RPMTAG_POSTINPROG:
case RPMTAG_PREUNPROG:
case RPMTAG_POSTUNPROG:
case RPMTAG_VERIFYSCRIPT:
continue;
break; /* Shouldn't need this */
default:
if (! headerIsEntry(outHeader, tag)) {
headerAddEntry(outHeader, tag, type, ptr, c);
}
}
}
headerFreeIterator(headerIter);
headerRemoveEntry(outHeader, RPMTAG_BUILDARCHS);
rpmGetArchInfo(&arch, NULL);
rpmGetOsInfo(&os, NULL);
/* Add some final entries to the header */
headerAddEntry(outHeader, RPMTAG_OS, RPM_STRING_TYPE, os, 1);
headerAddEntry(outHeader, RPMTAG_ARCH, RPM_STRING_TYPE, arch, 1);
headerAddEntry(outHeader, RPMTAG_BUILDTIME, RPM_INT32_TYPE, getBuildTime(), 1);
headerAddEntry(outHeader, RPMTAG_BUILDHOST, RPM_STRING_TYPE, buildHost(), 1);
headerAddEntry(outHeader, RPMTAG_SOURCERPM, RPM_STRING_TYPE, sourcerpm, 1);
headerAddEntry(outHeader, RPMTAG_RPMVERSION, RPM_STRING_TYPE, VERSION, 1);
if (pr->icon) {
sprintf(filename, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), pr->icon);
stat(filename, &statbuf);
icon = malloc(statbuf.st_size);
iconFD = open(filename, O_RDONLY, 0644);
read(iconFD, icon, statbuf.st_size);
close(iconFD);
if (! strncmp(icon, "GIF", 3)) {
headerAddEntry(outHeader, RPMTAG_GIF, RPM_BIN_TYPE,
icon, statbuf.st_size);
} else if (! strncmp(icon, "/* XPM", 6)) {
headerAddEntry(outHeader, RPMTAG_XPM, RPM_BIN_TYPE,
icon, statbuf.st_size);
} else {
rpmError(RPMERR_BADSPEC, "Unknown icon type");
return 1;
}
free(icon);
}
if (vendor) {
headerAddEntry(outHeader, RPMTAG_VENDOR, RPM_STRING_TYPE, vendor, 1);
}
if (dist) {
headerAddEntry(outHeader, RPMTAG_DISTRIBUTION, RPM_STRING_TYPE, dist, 1);
}
if (packager) {
headerAddEntry(outHeader, RPMTAG_PACKAGER, RPM_STRING_TYPE, packager, 1);
}
/**** Process the file list ****/
prefixSave = prefix = NULL;
prefixLen = 0;
if (headerGetEntry(outHeader, RPMTAG_DEFAULTPREFIX,
NULL, (void **)&prefix, NULL)) {
/* after the headerGetEntry() this is just pointers into the */
/* header structure, which can be moved around by headerAddEntry */
prefixSave = prefix = strdup(prefix);
while (*prefix && (*prefix == '/')) {
prefix++;
}
if (! *prefix) {
prefix = NULL;
prefixLen = 0;
} else {
prefixLen = strlen(prefix);
rpmMessage(RPMMESS_VERBOSE, "Package Prefix = %s\n", prefix);
}
}
if (process_filelist(outHeader, pr, pr->filelist, &size, nametmp,
packageVersion, packageRelease, RPMLEAD_BINARY,
prefix, NULL)) {
return 1;
}
if (!headerGetEntry(outHeader, RPMTAG_FILENAMES, NULL, (void **) &farray,
&count)) {
/* count may already be 0, but this is safer */
count = 0;
}
cpioFileList = newStringBuf();
while (count--) {
file = *farray++;
while (*file == '/') {
file++; /* Skip leading "/" */
}
if (prefix) {
if (strncmp(prefix, file, prefixLen)) {
rpmError(RPMERR_BADSPEC, "File doesn't match prefix (%s): %s",
prefix, file);
return 1;
}
file += prefixLen + 1; /* 1 for "/" */
}
appendLineStringBuf(cpioFileList, file);
}
/* Generate any automatic require/provide entries */
/* Then add the whole thing to the header */
if (s->autoReqProv) {
generateAutoReqProv(outHeader, pr);
}
processReqProv(outHeader, pr);
/* Generate the any trigger entries */
generateTriggerEntries(outHeader, pr);
/* And add the final Header entry */
headerAddEntry(outHeader, RPMTAG_SIZE, RPM_INT32_TYPE, &size, 1);
/**** Make the RPM ****/
/* Make the output RPM filename */
if (doPackage == PACK_PACKAGE) {
binformat = rpmGetVar(RPMVAR_RPMFILENAME);
binrpm = headerSprintf(outHeader, binformat, rpmTagTable,
rpmHeaderFormats, &errorString);
if (!binrpm) {
rpmError(RPMERR_BADFILENAME, "could not generate output "
"filename for package %s: %s\n", name, binrpm);
}
sprintf(filename, "%s/%s", rpmGetVar(RPMVAR_RPMDIR), binrpm);
free(binrpm);
if (generateRPM(name, filename, RPMLEAD_BINARY, outHeader, NULL,
getStringBuf(cpioFileList), passPhrase, prefix)) {
/* Build failed */
return 1;
}
}
freeStringBuf(cpioFileList);
headerFree(outHeader);
if (prefixSave)
free(prefixSave);
free(packageVersion);
free(packageRelease);
pr = pr->next;
}
free(version);
free(release);
return 0;
}
/**************** SOURCE PACKAGING ************************/
int packageSource(Spec s, char *passPhrase)
{
struct sources *source;
struct PackageRec *package;
char *tempdir;
char src[1024], dest[1024], fullname[1024], filename[1024], specFile[1024];
char *version;
char *release;
char *vendor;
char *dist;
char *p;
Header outHeader;
StringBuf filelist;
StringBuf cpioFileList;
int size;
char **sources;
char **patches;
char * arch, * os;
int scount, pcount;
int skipi;
int_32 *skip;
/**** Create links for all the sources ****/
tempdir = tempnam(rpmGetVar(RPMVAR_TMPPATH), "rpmbuild");
mkdir(tempdir, 0700);
filelist = newStringBuf(); /* List in the header */
cpioFileList = newStringBuf(); /* List for cpio */
sources = malloc((s->numSources+1) * sizeof(char *));
patches = malloc((s->numPatches+1) * sizeof(char *));
/* Link in the spec file and all the sources */
p = strrchr(s->specfile, '/');
sprintf(dest, "%s%s", tempdir, p);
strcpy(specFile, dest);
symlink(s->specfile, dest);
appendLineStringBuf(filelist, dest);
appendLineStringBuf(cpioFileList, p+1);
source = s->sources;
scount = 0;
pcount = 0;
while (source) {
if (source->ispatch) {
skipi = s->numNoPatch - 1;
skip = s->noPatch;
} else {
skipi = s->numNoSource - 1;
skip = s->noSource;
}
while (skipi >= 0) {
if (skip[skipi] == source->num) {
break;
}
skipi--;
}
sprintf(src, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), source->source);
sprintf(dest, "%s/%s", tempdir, source->source);
if (skipi < 0) {
symlink(src, dest);
appendLineStringBuf(cpioFileList, source->source);
} else {
rpmMessage(RPMMESS_VERBOSE, "Skipping source/patch (%d): %s\n",
source->num, source->source);
}
appendLineStringBuf(filelist, src);
if (source->ispatch) {
patches[pcount++] = source->fullSource;
} else {
sources[scount++] = source->fullSource;
}
source = source->next;
}
/* ... and icons */
package = s->packages;
while (package) {
if (package->icon) {
sprintf(src, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), package->icon);
sprintf(dest, "%s/%s", tempdir, package->icon);
appendLineStringBuf(filelist, dest);
appendLineStringBuf(cpioFileList, package->icon);
symlink(src, dest);
}
package = package->next;
}
/**** Generate the Header ****/
if (!headerGetEntry(s->packages->header, RPMTAG_VERSION, NULL,
(void *) &version, NULL)) {
rpmError(RPMERR_BADSPEC, "No version field");
return RPMERR_BADSPEC;
}
if (!headerGetEntry(s->packages->header, RPMTAG_RELEASE, NULL,
(void *) &release, NULL)) {
rpmError(RPMERR_BADSPEC, "No release field");
return RPMERR_BADSPEC;
}
rpmGetArchInfo(&arch, NULL);
rpmGetOsInfo(&os, NULL);
outHeader = headerCopy(s->packages->header);
headerAddEntry(outHeader, RPMTAG_OS, RPM_STRING_TYPE, os, 1);
headerAddEntry(outHeader, RPMTAG_ARCH, RPM_STRING_TYPE, arch, 1);
headerAddEntry(outHeader, RPMTAG_BUILDTIME, RPM_INT32_TYPE, getBuildTime(), 1);
headerAddEntry(outHeader, RPMTAG_BUILDHOST, RPM_STRING_TYPE, buildHost(), 1);
headerAddEntry(outHeader, RPMTAG_RPMVERSION, RPM_STRING_TYPE, VERSION, 1);
if (scount)
headerAddEntry(outHeader, RPMTAG_SOURCE, RPM_STRING_ARRAY_TYPE, sources, scount);
if (pcount)
headerAddEntry(outHeader, RPMTAG_PATCH, RPM_STRING_ARRAY_TYPE, patches, pcount);
if (s->numNoSource) {
headerAddEntry(outHeader, RPMTAG_NOSOURCE, RPM_INT32_TYPE, s->noSource,
s->numNoSource);
}
if (s->numNoPatch) {
headerAddEntry(outHeader, RPMTAG_NOPATCH, RPM_INT32_TYPE, s->noPatch,
s->numNoPatch);
}
if (!headerIsEntry(s->packages->header, RPMTAG_VENDOR)) {
if ((vendor = rpmGetVar(RPMVAR_VENDOR))) {
headerAddEntry(outHeader, RPMTAG_VENDOR, RPM_STRING_TYPE, vendor, 1);
}
}
if (!headerIsEntry(s->packages->header, RPMTAG_DISTRIBUTION)) {
if ((dist = rpmGetVar(RPMVAR_DISTRIBUTION))) {
headerAddEntry(outHeader, RPMTAG_DISTRIBUTION, RPM_STRING_TYPE, dist, 1);
}
}
/* Process the file list */
if (process_filelist(outHeader, NULL, filelist, &size,
s->name, version, release, RPMLEAD_SOURCE,
NULL, specFile)) {
return 1;
}
/* And add the final Header entry */
headerAddEntry(outHeader, RPMTAG_SIZE, RPM_INT32_TYPE, &size, 1);
/**** Make the RPM ****/
sprintf(fullname, "%s-%s-%s", s->name, version, release);
sprintf(filename, "%s/%s.%ssrc.rpm", rpmGetVar(RPMVAR_SRPMDIR), fullname,
(s->numNoPatch + s->numNoSource) ? "no" : "");
rpmMessage(RPMMESS_VERBOSE, "Source Packaging: %s\n", fullname);
if (generateRPM(fullname, filename, RPMLEAD_SOURCE, outHeader,
tempdir, getStringBuf(cpioFileList), passPhrase, NULL)) {
return 1;
}
/**** Now clean up ****/
freeStringBuf(filelist);
freeStringBuf(cpioFileList);
source = s->sources;
while (source) {
sprintf(dest, "%s/%s", tempdir, source->source);
unlink(dest);
source = source->next;
}
package = s->packages;
while (package) {
if (package->icon) {
sprintf(dest, "%s/%s", tempdir, package->icon);
unlink(dest);
}
package = package->next;
}
sprintf(dest, "%s%s", tempdir, strrchr(s->specfile, '/'));
unlink(dest);
rmdir(tempdir);
return 0;
}