/* The very final packaging steps */ #include "config.h" #include "miscfn.h" #if HAVE_ALLOCA_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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; }