1 /* The very final packaging steps */
9 #include <sys/resource.h>
11 #include <sys/types.h>
18 #include <sys/time.h> /* For 'select()' interfaces */
24 #include "signature.h"
28 #include "stringbuf.h"
35 static int writeMagic(int fd, char *name, unsigned short type);
36 static int cpio_gzip(int fd, char *tempdir, char *writePtr,
37 int *archiveSize, char *prefix);
38 static int generateRPM(char *name, /* name-version-release */
39 char *filename, /* output filename */
40 int type, /* source or binary */
41 Header header, /* the header */
42 char *stempdir, /* directory containing sources */
43 char *fileList, /* list of files for cpio */
44 char *passPhrase, /* PGP passphrase */
48 static int generateRPM(char *name, /* name-version-release */
49 char *filename, /* output filename */
50 int type, /* source or binary */
51 Header header, /* the header */
52 char *stempdir, /* directory containing sources */
53 char *fileList, /* list of files for cpio */
59 int fd, ifd, count, archiveSize;
60 unsigned char buffer[8192];
63 /* Add the a bogus archive size to the Header */
64 headerAddEntry(header, RPMTAG_ARCHIVESIZE, RPM_INT32_TYPE,
67 /* Write the header */
68 sigtarget = tempnam(rpmGetVar(RPMVAR_TMPPATH), "rpmbuild");
69 if ((fd = open(sigtarget, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
70 fprintf(stderr, "Could not open %s\n", sigtarget);
73 headerWrite(fd, header, HEADER_MAGIC_YES);
75 /* Write the archive and get the size */
76 if (cpio_gzip(fd, stempdir, fileList, &archiveSize, prefix)) {
82 /* Now set the real archive size in the Header */
83 headerModifyEntry(header, RPMTAG_ARCHIVESIZE,
84 RPM_INT32_TYPE, &archiveSize, 1);
86 /* Rewind and rewrite the Header */
87 lseek(fd, 0, SEEK_SET);
88 headerWrite(fd, header, HEADER_MAGIC_YES);
92 /* Now write the lead */
93 if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
94 fprintf(stderr, "Could not open %s\n", filename);
99 if (writeMagic(fd, name, type)) {
106 /* Generate the signature */
107 sigtype = rpmLookupSignatureType();
108 rpmMessage(RPMMESS_VERBOSE, "Generating signature: %d\n", sigtype);
110 sig = rpmNewSignature();
111 rpmAddSignature(sig, sigtarget, RPMSIGTAG_SIZE, passPhrase);
112 rpmAddSignature(sig, sigtarget, RPMSIGTAG_MD5, passPhrase);
114 rpmAddSignature(sig, sigtarget, sigtype, passPhrase);
116 if (rpmWriteSignature(fd, sig)) {
120 rpmFreeSignature(sig);
123 rpmFreeSignature(sig);
125 /* Append the header and archive */
126 ifd = open(sigtarget, O_RDONLY);
127 while ((count = read(ifd, buffer, sizeof(buffer))) > 0) {
129 perror("Couldn't read sigtarget");
136 if (write(fd, buffer, count) < 0) {
137 perror("Couldn't write package");
149 rpmMessage(RPMMESS_VERBOSE, "Wrote: %s\n", filename);
154 static int writeMagic(int fd, char *name,
159 /* There are the Major and Minor numbers */
163 lead.archnum = rpmGetArchNum();
164 lead.osnum = rpmGetOsNum();
165 lead.signature_type = RPMSIG_HEADERSIG; /* New-style signature */
166 strncpy(lead.name, name, sizeof(lead.name));
168 writeLead(fd, &lead);
173 static int cpio_gzip(int fd, char *tempdir, char *writePtr,
174 int *archiveSize, char *prefix)
176 int cpioPID, gzipPID;
177 int cpioDead, gzipDead;
184 int writeBytesLeft, bytesWritten;
187 unsigned char buf[8192];
188 fd_set read_fds, read_ok, write_fds, write_ok;
195 cpiobin = rpmGetVar(RPMVAR_CPIOBIN);
196 gzipbin = rpmGetVar(RPMVAR_GZIPBIN);
203 oldhandler = signal(SIGPIPE, SIG_IGN);
206 if (!(cpioPID = fork())) {
213 dup2(toCpio[0], 0); /* Make stdin the in pipe */
214 dup2(fromCpio[1], 1); /* Make stdout the out pipe */
218 } else if (rpmGetVar(RPMVAR_ROOT)) {
219 if (chdir(rpmGetVar(RPMVAR_ROOT))) {
220 rpmError(RPMERR_EXEC, "Couldn't chdir to %s",
221 rpmGetVar(RPMVAR_ROOT));
225 /* This is important! */
230 rpmError(RPMERR_EXEC, "Couldn't chdir to %s", prefix);
235 execlp(cpiobin, cpiobin,
236 (rpmIsVerbose()) ? "-ov" : "-o",
237 (tempdir) ? "-LH" : "-H",
239 rpmError(RPMERR_EXEC, "Couldn't exec cpio");
243 rpmError(RPMERR_FORK, "Couldn't fork cpio");
250 if (!(gzipPID = fork())) {
259 dup2(toGzip[0], 0); /* Make stdin the in pipe */
260 dup2(fd, 1); /* Make stdout the passed-in fd */
262 execlp(gzipbin, gzipbin, "-c9fn", NULL);
263 rpmError(RPMERR_EXEC, "Couldn't exec gzip");
267 rpmError(RPMERR_FORK, "Couldn't fork gzip");
275 /* It is OK to block writing to gzip. But it is not OK */
276 /* to block reading or writing from/to cpio. */
277 fcntl(fromCpio[0], F_SETFL, O_NONBLOCK);
278 fcntl(toCpio[1], F_SETFL, O_NONBLOCK);
279 writeBytesLeft = strlen(writePtr);
281 /* Set up to use 'select()' to multiplex this I/O stream */
283 FD_SET(fromCpio[0], &read_fds);
284 max_fd = fromCpio[0];
286 FD_SET(toCpio[1], &write_fds);
287 if (toCpio[1] > max_fd) max_fd = toCpio[1];
293 if (waitpid(cpioPID, &status, WNOHANG)) {
296 if (waitpid(gzipPID, &status, WNOHANG)) {
300 /* Pause here until we could perform some I/O */
302 write_ok = write_fds;
303 if ((num_fds = select(max_fd+1, &read_ok, &write_ok,
304 (fd_set *)NULL, (struct timeval *)NULL)) < 0) {
305 /* One or more file connections has broken */
306 if (fstat(fromCpio[0], &fd_info) < 0) {
307 FD_CLR(fromCpio[0], &read_fds);
309 if (fstat(toCpio[1], &fd_info) < 0) {
310 FD_CLR(toCpio[1], &write_fds);
315 /* Write some stuff to the cpio process if possible */
316 if (FD_ISSET(toCpio[1], &write_ok)) {
317 if (writeBytesLeft) {
319 write(toCpio[1], writePtr,
320 (1024<writeBytesLeft) ? 1024 : writeBytesLeft)) < 0) {
321 if (errno != EAGAIN) {
327 writeBytesLeft -= bytesWritten;
328 writePtr += bytesWritten;
331 FD_CLR(toCpio[1], &write_fds);
335 /* Read any data from cpio, write it to gzip */
336 bytes = 0; /* So end condition works OK */
337 if (FD_ISSET(fromCpio[0], &read_ok)) {
338 bytes = read(fromCpio[0], buf, sizeof(buf));
340 *archiveSize += bytes;
341 write(toGzip[1], buf, bytes);
342 bytes = read(fromCpio[0], buf, sizeof(buf));
346 /* while cpio is running, or we are writing to gzip */
347 /* terminate if gzip dies on us in the middle */
348 } while (((!cpioDead) || bytes) && (!gzipDead));
351 rpmError(RPMERR_GZIP, "gzip died");
355 close(toGzip[1]); /* Terminates the gzip process */
359 signal(SIGPIPE, oldhandler);
361 if (writeBytesLeft) {
362 rpmError(RPMERR_CPIO, "failed to write all data to cpio");
365 waitpid(cpioPID, &status, 0);
366 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
367 rpmError(RPMERR_CPIO, "cpio failed");
370 waitpid(gzipPID, &status, 0);
371 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
372 rpmError(RPMERR_GZIP, "gzip failed");
380 int packageBinaries(Spec s, char *passPhrase, int doPackage)
385 char sourcerpm[1024];
389 struct PackageRec *pr;
391 HeaderIterator headerIter;
399 char *packageVersion, *packageRelease;
400 char *prefix, *prefixSave;
403 StringBuf cpioFileList;
404 char **farray, *file;
407 if (!headerGetEntry(s->packages->header, RPMTAG_VERSION, NULL,
408 (void *) &version, NULL)) {
409 rpmError(RPMERR_BADSPEC, "No version field");
410 return RPMERR_BADSPEC;
412 if (!headerGetEntry(s->packages->header, RPMTAG_RELEASE, NULL,
413 (void *) &release, NULL)) {
414 rpmError(RPMERR_BADSPEC, "No release field");
415 return RPMERR_BADSPEC;
417 /* after the headerGetEntry() these are just pointers into the */
418 /* header structure, which can be moved around by headerAddEntry */
419 version = strdup(version);
420 release = strdup(release);
422 sprintf(sourcerpm, "%s-%s-%s.%ssrc.rpm", s->name, version, release,
423 (s->numNoPatch + s->numNoSource) ? "no" : "");
426 if (!headerIsEntry(s->packages->header, RPMTAG_VENDOR)) {
427 vendor = rpmGetVar(RPMVAR_VENDOR);
430 if (!headerIsEntry(s->packages->header, RPMTAG_DISTRIBUTION)) {
431 dist = rpmGetVar(RPMVAR_DISTRIBUTION);
434 if (!headerIsEntry(s->packages->header, RPMTAG_PACKAGER)) {
435 packager = rpmGetVar(RPMVAR_PACKAGER);
438 /* Look through for each package */
441 /* A file count of -1 means no package */
442 if (pr->files == -1) {
447 /* Handle subpackage version/release overrides */
448 if (!headerGetEntry(pr->header, RPMTAG_VERSION, NULL,
449 (void *) &packageVersion, NULL)) {
450 packageVersion = version;
452 if (!headerGetEntry(pr->header, RPMTAG_RELEASE, NULL,
453 (void *) &packageRelease, NULL)) {
454 packageRelease = release;
456 /* after the headerGetEntry() these are just pointers into the */
457 /* header structure, which can be moved around by headerAddEntry */
458 packageVersion = strdup(packageVersion);
459 packageRelease = strdup(packageRelease);
461 /* Figure out the name of this package */
462 if (!headerGetEntry(pr->header, RPMTAG_NAME, NULL, (void *)&nametmp, NULL)) {
463 rpmError(RPMERR_INTERNAL, "Package has no name!");
464 return RPMERR_INTERNAL;
466 sprintf(name, "%s-%s-%s", nametmp, packageVersion, packageRelease);
468 if (doPackage == PACK_PACKAGE) {
469 rpmMessage(RPMMESS_VERBOSE, "Binary Packaging: %s\n", name);
471 rpmMessage(RPMMESS_VERBOSE, "File List Check: %s\n", name);
474 /**** Generate the Header ****/
476 /* Here's the plan: copy the package's header, */
477 /* then add entries from the primary header */
478 /* that don't already exist. */
479 outHeader = headerCopy(pr->header);
480 headerIter = headerInitIterator(s->packages->header);
481 while (headerNextIterator(headerIter, &tag, &type, &ptr, &c)) {
482 /* Some tags we don't copy */
488 case RPMTAG_VERIFYSCRIPT:
490 break; /* Shouldn't need this */
492 if (! headerIsEntry(outHeader, tag)) {
493 headerAddEntry(outHeader, tag, type, ptr, c);
497 headerFreeIterator(headerIter);
499 /* Add some final entries to the header */
500 headerAddEntry(outHeader, RPMTAG_OS, RPM_STRING_TYPE, rpmGetOsName(), 1);
501 headerAddEntry(outHeader, RPMTAG_ARCH, RPM_STRING_TYPE, rpmGetArchName(), 1);
502 headerAddEntry(outHeader, RPMTAG_BUILDTIME, RPM_INT32_TYPE, getBuildTime(), 1);
503 headerAddEntry(outHeader, RPMTAG_BUILDHOST, RPM_STRING_TYPE, buildHost(), 1);
504 headerAddEntry(outHeader, RPMTAG_SOURCERPM, RPM_STRING_TYPE, sourcerpm, 1);
505 headerAddEntry(outHeader, RPMTAG_RPMVERSION, RPM_STRING_TYPE, VERSION, 1);
507 sprintf(filename, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), pr->icon);
508 stat(filename, &statbuf);
509 icon = malloc(statbuf.st_size);
510 iconFD = open(filename, O_RDONLY, 0644);
511 read(iconFD, icon, statbuf.st_size);
513 if (! strncmp(icon, "GIF", 3)) {
514 headerAddEntry(outHeader, RPMTAG_GIF, RPM_BIN_TYPE,
515 icon, statbuf.st_size);
516 } else if (! strncmp(icon, "/* XPM", 6)) {
517 headerAddEntry(outHeader, RPMTAG_XPM, RPM_BIN_TYPE,
518 icon, statbuf.st_size);
520 rpmError(RPMERR_BADSPEC, "Unknown icon type");
526 headerAddEntry(outHeader, RPMTAG_VENDOR, RPM_STRING_TYPE, vendor, 1);
529 headerAddEntry(outHeader, RPMTAG_DISTRIBUTION, RPM_STRING_TYPE, dist, 1);
532 headerAddEntry(outHeader, RPMTAG_PACKAGER, RPM_STRING_TYPE, packager, 1);
535 /**** Process the file list ****/
537 prefixSave = prefix = NULL;
539 if (headerGetEntry(outHeader, RPMTAG_DEFAULTPREFIX,
540 NULL, (void **)&prefix, NULL)) {
541 /* after the headerGetEntry() this is just pointers into the */
542 /* header structure, which can be moved around by headerAddEntry */
543 prefixSave = prefix = strdup(prefix);
544 while (*prefix && (*prefix == '/')) {
551 prefixLen = strlen(prefix);
552 rpmMessage(RPMMESS_VERBOSE, "Package Prefix = %s\n", prefix);
556 if (process_filelist(outHeader, pr, pr->filelist, &size, nametmp,
557 packageVersion, packageRelease, RPMLEAD_BINARY,
562 if (!headerGetEntry(outHeader, RPMTAG_FILENAMES, NULL, (void **) &farray,
564 /* count may already be 0, but this is safer */
568 cpioFileList = newStringBuf();
571 while (*file == '/') {
572 file++; /* Skip leading "/" */
575 if (strncmp(prefix, file, prefixLen)) {
576 rpmError(RPMERR_BADSPEC, "File doesn't match prefix (%s): %s",
580 file += prefixLen + 1; /* 1 for "/" */
582 appendLineStringBuf(cpioFileList, file);
585 /* Generate any automatic require/provide entries */
586 /* Then add the whole thing to the header */
587 if (s->autoReqProv) {
588 generateAutoReqProv(outHeader, pr);
590 processReqProv(outHeader, pr);
592 /* Generate the any trigger entries */
593 generateTriggerEntries(outHeader, pr);
595 /* And add the final Header entry */
596 headerAddEntry(outHeader, RPMTAG_SIZE, RPM_INT32_TYPE, &size, 1);
598 /**** Make the RPM ****/
600 /* Make the output RPM filename */
601 if (doPackage == PACK_PACKAGE) {
602 sprintf(filename, "%s/%s/%s.%s.rpm", rpmGetVar(RPMVAR_RPMDIR),
603 rpmGetArchName(), name, rpmGetArchName());
605 if (generateRPM(name, filename, RPMLEAD_BINARY, outHeader, NULL,
606 getStringBuf(cpioFileList), passPhrase, prefix)) {
612 freeStringBuf(cpioFileList);
613 headerFree(outHeader);
617 free(packageVersion);
618 free(packageRelease);
629 /**************** SOURCE PACKAGING ************************/
631 int packageSource(Spec s, char *passPhrase)
633 struct sources *source;
634 struct PackageRec *package;
636 char src[1024], dest[1024], fullname[1024], filename[1024], specFile[1024];
644 StringBuf cpioFileList;
652 /**** Create links for all the sources ****/
654 tempdir = tempnam(rpmGetVar(RPMVAR_TMPPATH), "rpmbuild");
655 mkdir(tempdir, 0700);
657 filelist = newStringBuf(); /* List in the header */
658 cpioFileList = newStringBuf(); /* List for cpio */
660 sources = malloc((s->numSources+1) * sizeof(char *));
661 patches = malloc((s->numPatches+1) * sizeof(char *));
663 /* Link in the spec file and all the sources */
664 p = strrchr(s->specfile, '/');
665 sprintf(dest, "%s%s", tempdir, p);
666 strcpy(specFile, dest);
667 symlink(s->specfile, dest);
668 appendLineStringBuf(filelist, dest);
669 appendLineStringBuf(cpioFileList, p+1);
674 if (source->ispatch) {
675 skipi = s->numNoPatch - 1;
678 skipi = s->numNoSource - 1;
682 if (skip[skipi] == source->num) {
687 sprintf(src, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), source->source);
688 sprintf(dest, "%s/%s", tempdir, source->source);
691 appendLineStringBuf(cpioFileList, source->source);
693 rpmMessage(RPMMESS_VERBOSE, "Skipping source/patch (%d): %s\n",
694 source->num, source->source);
696 appendLineStringBuf(filelist, src);
697 if (source->ispatch) {
698 patches[pcount++] = source->fullSource;
700 sources[scount++] = source->fullSource;
702 source = source->next;
705 package = s->packages;
708 sprintf(src, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), package->icon);
709 sprintf(dest, "%s/%s", tempdir, package->icon);
710 appendLineStringBuf(filelist, dest);
711 appendLineStringBuf(cpioFileList, package->icon);
714 package = package->next;
717 /**** Generate the Header ****/
719 if (!headerGetEntry(s->packages->header, RPMTAG_VERSION, NULL,
720 (void *) &version, NULL)) {
721 rpmError(RPMERR_BADSPEC, "No version field");
722 return RPMERR_BADSPEC;
724 if (!headerGetEntry(s->packages->header, RPMTAG_RELEASE, NULL,
725 (void *) &release, NULL)) {
726 rpmError(RPMERR_BADSPEC, "No release field");
727 return RPMERR_BADSPEC;
730 outHeader = headerCopy(s->packages->header);
731 headerAddEntry(outHeader, RPMTAG_OS, RPM_STRING_TYPE, rpmGetOsName(), 1);
732 headerAddEntry(outHeader, RPMTAG_ARCH, RPM_STRING_TYPE, rpmGetArchName(), 1);
733 headerAddEntry(outHeader, RPMTAG_BUILDTIME, RPM_INT32_TYPE, getBuildTime(), 1);
734 headerAddEntry(outHeader, RPMTAG_BUILDHOST, RPM_STRING_TYPE, buildHost(), 1);
735 headerAddEntry(outHeader, RPMTAG_RPMVERSION, RPM_STRING_TYPE, VERSION, 1);
737 headerAddEntry(outHeader, RPMTAG_SOURCE, RPM_STRING_ARRAY_TYPE, sources, scount);
739 headerAddEntry(outHeader, RPMTAG_PATCH, RPM_STRING_ARRAY_TYPE, patches, pcount);
740 if (s->numNoSource) {
741 headerAddEntry(outHeader, RPMTAG_NOSOURCE, RPM_INT32_TYPE, s->noSource,
745 headerAddEntry(outHeader, RPMTAG_NOPATCH, RPM_INT32_TYPE, s->noPatch,
748 if (!headerIsEntry(s->packages->header, RPMTAG_VENDOR)) {
749 if ((vendor = rpmGetVar(RPMVAR_VENDOR))) {
750 headerAddEntry(outHeader, RPMTAG_VENDOR, RPM_STRING_TYPE, vendor, 1);
753 if (!headerIsEntry(s->packages->header, RPMTAG_DISTRIBUTION)) {
754 if ((dist = rpmGetVar(RPMVAR_DISTRIBUTION))) {
755 headerAddEntry(outHeader, RPMTAG_DISTRIBUTION, RPM_STRING_TYPE, dist, 1);
759 /* Process the file list */
760 if (process_filelist(outHeader, NULL, filelist, &size,
761 s->name, version, release, RPMLEAD_SOURCE,
766 /* And add the final Header entry */
767 headerAddEntry(outHeader, RPMTAG_SIZE, RPM_INT32_TYPE, &size, 1);
769 /**** Make the RPM ****/
771 sprintf(fullname, "%s-%s-%s", s->name, version, release);
772 sprintf(filename, "%s/%s.%ssrc.rpm", rpmGetVar(RPMVAR_SRPMDIR), fullname,
773 (s->numNoPatch + s->numNoSource) ? "no" : "");
774 rpmMessage(RPMMESS_VERBOSE, "Source Packaging: %s\n", fullname);
776 if (generateRPM(fullname, filename, RPMLEAD_SOURCE, outHeader,
777 tempdir, getStringBuf(cpioFileList), passPhrase, NULL)) {
781 /**** Now clean up ****/
783 freeStringBuf(filelist);
784 freeStringBuf(cpioFileList);
788 sprintf(dest, "%s/%s", tempdir, source->source);
790 source = source->next;
792 package = s->packages;
795 sprintf(dest, "%s/%s", tempdir, package->icon);
798 package = package->next;
800 sprintf(dest, "%s%s", tempdir, strrchr(s->specfile, '/'));