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];
390 struct PackageRec *pr;
392 HeaderIterator headerIter;
400 char *packageVersion, *packageRelease;
401 char *prefix, *prefixSave;
402 char * binformat, * errorString;
405 StringBuf cpioFileList;
406 char **farray, *file;
409 if (!headerGetEntry(s->packages->header, RPMTAG_VERSION, NULL,
410 (void *) &version, NULL)) {
411 rpmError(RPMERR_BADSPEC, "No version field");
412 return RPMERR_BADSPEC;
414 if (!headerGetEntry(s->packages->header, RPMTAG_RELEASE, NULL,
415 (void *) &release, NULL)) {
416 rpmError(RPMERR_BADSPEC, "No release field");
417 return RPMERR_BADSPEC;
419 /* after the headerGetEntry() these are just pointers into the */
420 /* header structure, which can be moved around by headerAddEntry */
421 version = strdup(version);
422 release = strdup(release);
424 sprintf(sourcerpm, "%s-%s-%s.%sprc.rpm", s->name, version, release,
425 (s->numNoPatch + s->numNoSource) ? "no" : "");
428 if (!headerIsEntry(s->packages->header, RPMTAG_VENDOR)) {
429 vendor = rpmGetVar(RPMVAR_VENDOR);
432 if (!headerIsEntry(s->packages->header, RPMTAG_DISTRIBUTION)) {
433 dist = rpmGetVar(RPMVAR_DISTRIBUTION);
436 if (!headerIsEntry(s->packages->header, RPMTAG_PACKAGER)) {
437 packager = rpmGetVar(RPMVAR_PACKAGER);
440 /* Look through for each package */
443 /* A file count of -1 means no package */
444 if (pr->files == -1) {
449 /* Handle subpackage version/release overrides */
450 if (!headerGetEntry(pr->header, RPMTAG_VERSION, NULL,
451 (void *) &packageVersion, NULL)) {
452 packageVersion = version;
454 if (!headerGetEntry(pr->header, RPMTAG_RELEASE, NULL,
455 (void *) &packageRelease, NULL)) {
456 packageRelease = release;
458 /* after the headerGetEntry() these are just pointers into the */
459 /* header structure, which can be moved around by headerAddEntry */
460 packageVersion = strdup(packageVersion);
461 packageRelease = strdup(packageRelease);
463 /* Figure out the name of this package */
464 if (!headerGetEntry(pr->header, RPMTAG_NAME, NULL, (void *)&nametmp, NULL)) {
465 rpmError(RPMERR_INTERNAL, "Package has no name!");
466 return RPMERR_INTERNAL;
468 sprintf(name, "%s-%s-%s", nametmp, packageVersion, packageRelease);
470 if (doPackage == PACK_PACKAGE) {
471 rpmMessage(RPMMESS_VERBOSE, "Binary Packaging: %s\n", name);
473 rpmMessage(RPMMESS_VERBOSE, "File List Check: %s\n", name);
476 /**** Generate the Header ****/
478 /* Here's the plan: copy the package's header, */
479 /* then add entries from the primary header */
480 /* that don't already exist. */
481 outHeader = headerCopy(pr->header);
482 headerIter = headerInitIterator(s->packages->header);
483 while (headerNextIterator(headerIter, &tag, &type, &ptr, &c)) {
484 /* Some tags we don't copy */
490 case RPMTAG_VERIFYSCRIPT:
492 break; /* Shouldn't need this */
494 if (! headerIsEntry(outHeader, tag)) {
495 headerAddEntry(outHeader, tag, type, ptr, c);
499 headerFreeIterator(headerIter);
501 /* Add some final entries to the header */
502 headerAddEntry(outHeader, RPMTAG_OS, RPM_STRING_TYPE, rpmGetOsName(), 1);
503 headerAddEntry(outHeader, RPMTAG_ARCH, RPM_STRING_TYPE, rpmGetArchName(), 1);
504 headerAddEntry(outHeader, RPMTAG_BUILDTIME, RPM_INT32_TYPE, getBuildTime(), 1);
505 headerAddEntry(outHeader, RPMTAG_BUILDHOST, RPM_STRING_TYPE, buildHost(), 1);
506 headerAddEntry(outHeader, RPMTAG_SOURCERPM, RPM_STRING_TYPE, sourcerpm, 1);
507 headerAddEntry(outHeader, RPMTAG_RPMVERSION, RPM_STRING_TYPE, VERSION, 1);
509 sprintf(filename, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), pr->icon);
510 stat(filename, &statbuf);
511 icon = malloc(statbuf.st_size);
512 iconFD = open(filename, O_RDONLY, 0644);
513 read(iconFD, icon, statbuf.st_size);
515 if (! strncmp(icon, "GIF", 3)) {
516 headerAddEntry(outHeader, RPMTAG_GIF, RPM_BIN_TYPE,
517 icon, statbuf.st_size);
518 } else if (! strncmp(icon, "/* XPM", 6)) {
519 headerAddEntry(outHeader, RPMTAG_XPM, RPM_BIN_TYPE,
520 icon, statbuf.st_size);
522 rpmError(RPMERR_BADSPEC, "Unknown icon type");
528 headerAddEntry(outHeader, RPMTAG_VENDOR, RPM_STRING_TYPE, vendor, 1);
531 headerAddEntry(outHeader, RPMTAG_DISTRIBUTION, RPM_STRING_TYPE, dist, 1);
534 headerAddEntry(outHeader, RPMTAG_PACKAGER, RPM_STRING_TYPE, packager, 1);
537 /**** Process the file list ****/
539 prefixSave = prefix = NULL;
541 if (headerGetEntry(outHeader, RPMTAG_DEFAULTPREFIX,
542 NULL, (void **)&prefix, NULL)) {
543 /* after the headerGetEntry() this is just pointers into the */
544 /* header structure, which can be moved around by headerAddEntry */
545 prefixSave = prefix = strdup(prefix);
546 while (*prefix && (*prefix == '/')) {
553 prefixLen = strlen(prefix);
554 rpmMessage(RPMMESS_VERBOSE, "Package Prefix = %s\n", prefix);
558 if (process_filelist(outHeader, pr, pr->filelist, &size, nametmp,
559 packageVersion, packageRelease, RPMLEAD_BINARY,
564 if (!headerGetEntry(outHeader, RPMTAG_FILENAMES, NULL, (void **) &farray,
566 /* count may already be 0, but this is safer */
570 cpioFileList = newStringBuf();
573 while (*file == '/') {
574 file++; /* Skip leading "/" */
577 if (strncmp(prefix, file, prefixLen)) {
578 rpmError(RPMERR_BADSPEC, "File doesn't match prefix (%s): %s",
582 file += prefixLen + 1; /* 1 for "/" */
584 appendLineStringBuf(cpioFileList, file);
587 /* Generate any automatic require/provide entries */
588 /* Then add the whole thing to the header */
589 if (s->autoReqProv) {
590 generateAutoReqProv(outHeader, pr);
592 processReqProv(outHeader, pr);
594 /* Generate the any trigger entries */
595 generateTriggerEntries(outHeader, pr);
597 /* And add the final Header entry */
598 headerAddEntry(outHeader, RPMTAG_SIZE, RPM_INT32_TYPE, &size, 1);
600 /**** Make the RPM ****/
602 /* Make the output RPM filename */
603 if (doPackage == PACK_PACKAGE) {
604 binformat = rpmGetVar(RPMVAR_RPMFILENAME);
605 binrpm = headerSprintf(outHeader, binformat, rpmTagTable,
606 rpmHeaderFormats, &errorString);
609 rpmError(RPMERR_BADFILENAME, "could not generate output "
610 "filename for package %s: %s\n", name, binrpm);
613 sprintf(filename, "%s/%s", rpmGetVar(RPMVAR_RPMDIR), binrpm);
616 if (generateRPM(name, filename, RPMLEAD_BINARY, outHeader, NULL,
617 getStringBuf(cpioFileList), passPhrase, prefix)) {
623 freeStringBuf(cpioFileList);
624 headerFree(outHeader);
628 free(packageVersion);
629 free(packageRelease);
640 /**************** SOURCE PACKAGING ************************/
642 int packageSource(Spec s, char *passPhrase)
644 struct sources *source;
645 struct PackageRec *package;
647 char src[1024], dest[1024], fullname[1024], filename[1024], specFile[1024];
655 StringBuf cpioFileList;
663 /**** Create links for all the sources ****/
665 tempdir = tempnam(rpmGetVar(RPMVAR_TMPPATH), "rpmbuild");
666 mkdir(tempdir, 0700);
668 filelist = newStringBuf(); /* List in the header */
669 cpioFileList = newStringBuf(); /* List for cpio */
671 sources = malloc((s->numSources+1) * sizeof(char *));
672 patches = malloc((s->numPatches+1) * sizeof(char *));
674 /* Link in the spec file and all the sources */
675 p = strrchr(s->specfile, '/');
676 sprintf(dest, "%s%s", tempdir, p);
677 strcpy(specFile, dest);
678 symlink(s->specfile, dest);
679 appendLineStringBuf(filelist, dest);
680 appendLineStringBuf(cpioFileList, p+1);
685 if (source->ispatch) {
686 skipi = s->numNoPatch - 1;
689 skipi = s->numNoSource - 1;
693 if (skip[skipi] == source->num) {
698 sprintf(src, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), source->source);
699 sprintf(dest, "%s/%s", tempdir, source->source);
702 appendLineStringBuf(cpioFileList, source->source);
704 rpmMessage(RPMMESS_VERBOSE, "Skipping source/patch (%d): %s\n",
705 source->num, source->source);
707 appendLineStringBuf(filelist, src);
708 if (source->ispatch) {
709 patches[pcount++] = source->fullSource;
711 sources[scount++] = source->fullSource;
713 source = source->next;
716 package = s->packages;
719 sprintf(src, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), package->icon);
720 sprintf(dest, "%s/%s", tempdir, package->icon);
721 appendLineStringBuf(filelist, dest);
722 appendLineStringBuf(cpioFileList, package->icon);
725 package = package->next;
728 /**** Generate the Header ****/
730 if (!headerGetEntry(s->packages->header, RPMTAG_VERSION, NULL,
731 (void *) &version, NULL)) {
732 rpmError(RPMERR_BADSPEC, "No version field");
733 return RPMERR_BADSPEC;
735 if (!headerGetEntry(s->packages->header, RPMTAG_RELEASE, NULL,
736 (void *) &release, NULL)) {
737 rpmError(RPMERR_BADSPEC, "No release field");
738 return RPMERR_BADSPEC;
741 outHeader = headerCopy(s->packages->header);
742 headerAddEntry(outHeader, RPMTAG_OS, RPM_STRING_TYPE, rpmGetOsName(), 1);
743 headerAddEntry(outHeader, RPMTAG_ARCH, RPM_STRING_TYPE, rpmGetArchName(), 1);
744 headerAddEntry(outHeader, RPMTAG_BUILDTIME, RPM_INT32_TYPE, getBuildTime(), 1);
745 headerAddEntry(outHeader, RPMTAG_BUILDHOST, RPM_STRING_TYPE, buildHost(), 1);
746 headerAddEntry(outHeader, RPMTAG_RPMVERSION, RPM_STRING_TYPE, VERSION, 1);
748 headerAddEntry(outHeader, RPMTAG_SOURCE, RPM_STRING_ARRAY_TYPE, sources, scount);
750 headerAddEntry(outHeader, RPMTAG_PATCH, RPM_STRING_ARRAY_TYPE, patches, pcount);
751 if (s->numNoSource) {
752 headerAddEntry(outHeader, RPMTAG_NOSOURCE, RPM_INT32_TYPE, s->noSource,
756 headerAddEntry(outHeader, RPMTAG_NOPATCH, RPM_INT32_TYPE, s->noPatch,
759 if (!headerIsEntry(s->packages->header, RPMTAG_VENDOR)) {
760 if ((vendor = rpmGetVar(RPMVAR_VENDOR))) {
761 headerAddEntry(outHeader, RPMTAG_VENDOR, RPM_STRING_TYPE, vendor, 1);
764 if (!headerIsEntry(s->packages->header, RPMTAG_DISTRIBUTION)) {
765 if ((dist = rpmGetVar(RPMVAR_DISTRIBUTION))) {
766 headerAddEntry(outHeader, RPMTAG_DISTRIBUTION, RPM_STRING_TYPE, dist, 1);
770 /* Process the file list */
771 if (process_filelist(outHeader, NULL, filelist, &size,
772 s->name, version, release, RPMLEAD_SOURCE,
777 /* And add the final Header entry */
778 headerAddEntry(outHeader, RPMTAG_SIZE, RPM_INT32_TYPE, &size, 1);
780 /**** Make the RPM ****/
782 sprintf(fullname, "%s-%s-%s", s->name, version, release);
783 sprintf(filename, "%s/%s.%ssrc.rpm", rpmGetVar(RPMVAR_SRPMDIR), fullname,
784 (s->numNoPatch + s->numNoSource) ? "no" : "");
785 rpmMessage(RPMMESS_VERBOSE, "Source Packaging: %s\n", fullname);
787 if (generateRPM(fullname, filename, RPMLEAD_SOURCE, outHeader,
788 tempdir, getStringBuf(cpioFileList), passPhrase, NULL)) {
792 /**** Now clean up ****/
794 freeStringBuf(filelist);
795 freeStringBuf(cpioFileList);
799 sprintf(dest, "%s/%s", tempdir, source->source);
801 source = source->next;
803 package = s->packages;
806 sprintf(dest, "%s/%s", tempdir, package->icon);
809 package = package->next;
811 sprintf(dest, "%s%s", tempdir, strrchr(s->specfile, '/'));