Wait for popen() to finish before passing to isSpecFile()
[platform/upstream/rpm.git] / build.c
1 /** \ingroup rpmcli
2  * Parse spec file and build package.
3  */
4
5 #include "system.h"
6
7 #include <libgen.h>
8
9 #include <rpm/rpmcli.h>
10 #include <rpm/rpmtag.h>
11 #include <rpm/rpmlib.h>         /* rpmrc, MACHTABLE .. */
12 #include <rpm/rpmbuild.h>
13
14 #include <rpm/rpmps.h>
15 #include <rpm/rpmte.h>
16 #include <rpm/rpmts.h>
17 #include <rpm/rpmfileutil.h>
18 #include <rpm/rpmlog.h>
19
20 #include "build.h"
21 #include "debug.h"
22
23 /**
24  */
25 static int checkSpec(rpmts ts, Header h)
26 {
27     rpmps ps;
28     int rc;
29
30     if (!headerIsEntry(h, RPMTAG_REQUIRENAME)
31      && !headerIsEntry(h, RPMTAG_CONFLICTNAME))
32         return 0;
33
34     rc = rpmtsAddInstallElement(ts, h, NULL, 0, NULL);
35
36     rc = rpmtsCheck(ts);
37
38     ps = rpmtsProblems(ts);
39     if (rc == 0 && rpmpsNumProblems(ps) > 0) {
40         rpmlog(RPMLOG_ERR, _("Failed build dependencies:\n"));
41         rpmpsPrint(NULL, ps);
42         rc = 1;
43     }
44     ps = rpmpsFree(ps);
45
46     /* XXX nuke the added package. */
47     rpmtsClean(ts);
48
49     return rc;
50 }
51
52 /**
53  */
54 static int isSpecFile(const char * specfile)
55 {
56     char buf[256];
57     const char * s;
58     FILE * f;
59     int count;
60     int checking;
61
62     f = fopen(specfile, "r");
63     if (f == NULL || ferror(f)) {
64         rpmlog(RPMLOG_ERR, _("Unable to open spec file %s: %s\n"),
65                 specfile, strerror(errno));
66         return 0;
67     }
68     count = fread(buf, sizeof(buf[0]), sizeof(buf), f);
69     (void) fclose(f);
70
71     if (count == 0)
72         return 0;
73
74     checking = 1;
75     for (s = buf; count--; s++) {
76         switch (*s) {
77         case '\r':
78         case '\n':
79             checking = 1;
80             break;
81         case ':':
82             checking = 0;
83             break;
84         default:
85 #if 0
86             if (checking && !(isprint(*s) || isspace(*s))) return 0;
87             break;
88 #else
89             if (checking && !(isprint(*s) || isspace(*s)) && *(unsigned char *)s < 32) return 0;
90             break;
91 #endif
92         }
93     }
94     return 1;
95 }
96
97 /* 
98  * Try to find a spec from a tarball pointed to by arg. 
99  * Return absolute path to spec name on success, otherwise NULL.
100  */
101 static char * getTarSpec(const char *arg)
102 {
103     char *specFile = NULL;
104     char *specDir;
105     char *specBase;
106     char *tmpSpecFile;
107     const char **try;
108     char *tar;
109     char tarbuf[BUFSIZ];
110     int gotspec = 0;
111     rpmCompressedMagic res = COMPRESSED_OTHER;
112
113     /* FIX: static zcmds heartburn */
114     static const char *zcmds[] = { "cat", "gunzip", "bunzip2", "cat" };
115     static const char *tryspec[] = { "Specfile", "\\*.spec", NULL };
116
117     specDir = rpmGetPath("%{_specdir}", NULL);
118     tmpSpecFile = rpmGetPath("%{_specdir}/", "rpm-spec.XXXXXX", NULL);
119     tar = rpmGetPath("%{__tar}", NULL);
120
121 #if defined(HAVE_MKSTEMP)
122     (void) close(mkstemp(tmpSpecFile));
123 #else
124     (void) mktemp(tmpSpecFile);
125 #endif
126
127     (void) rpmFileIsCompressed(arg, &res);
128
129     for (try = tryspec; *try != NULL; try++) {
130         FILE *fp;
131         char *cmd;
132
133         rasprintf(&cmd, "%s < '%s' | %s xOvf - --wildcards %s 2>&1 > '%s'",
134              zcmds[res & 0x3], arg, tar, *try, tmpSpecFile);
135
136         if (!(fp = popen(cmd, "r"))) {
137             rpmlog(RPMLOG_ERR, _("Failed to open tar pipe: %m\n"));
138         } else {
139             char *fok = fgets(tarbuf, sizeof(tarbuf) - 1, fp);
140             pclose(fp);
141             gotspec = (fok != NULL) && isSpecFile(tmpSpecFile);
142         }
143
144         if (!gotspec) 
145             unlink(tmpSpecFile);
146         free(cmd);
147     }
148
149     if (!gotspec) {
150         rpmlog(RPMLOG_ERR, _("Failed to read spec file from %s\n"), arg);
151         goto exit;
152     }
153
154     specBase = basename(tarbuf);
155     /* remove trailing \n */
156     specBase[strlen(specBase)-1] = '\0';
157
158     rasprintf(&specFile, "%s/%s", specDir, specBase);
159     res = rename(tmpSpecFile, specFile);
160
161     if (res) {
162         rpmlog(RPMLOG_ERR, _("Failed to rename %s to %s: %m\n"),
163                 tmpSpecFile, specFile);
164         free(specFile);
165         specFile = NULL;
166     }
167
168 exit:
169     (void) unlink(tmpSpecFile);
170     free(tmpSpecFile);
171     free(specDir);
172     free(tar);
173     return specFile;
174 }
175
176 /**
177  */
178 static int buildForTarget(rpmts ts, const char * arg, BTA_t ba)
179 {
180     const char * passPhrase = ba->passPhrase;
181     const char * cookie = ba->cookie;
182     int buildAmount = ba->buildAmount;
183     char * buildRootURL = NULL;
184     const char * specFile;
185     char * specURL = NULL;
186     int specut;
187     rpmSpec spec = NULL;
188     int rc = 1; /* assume failure */
189
190 #ifndef DYING
191     rpmSetTables(RPM_MACHTABLE_BUILDARCH, RPM_MACHTABLE_BUILDOS);
192 #endif
193
194     if (ba->buildRootOverride)
195         buildRootURL = rpmGenPath(NULL, ba->buildRootOverride, NULL);
196
197     if (ba->buildMode == 't') {
198         char *srcdir = NULL, *dir;
199
200         specURL = getTarSpec(arg);
201         if (!specURL)
202             goto exit;
203
204         /* Make the directory of the tarball %_sourcedir for this run */
205         /* dirname() may modify contents so extra hoops needed. */
206         if (*arg != '/') {
207             srcdir = dir = rpmGetCwd();
208         } else {
209             dir = xstrdup(arg);
210             srcdir = dirname(dir);
211         }
212         addMacro(NULL, "_sourcedir", NULL, srcdir, RMIL_TARBALL);
213         free(dir);
214     } else {
215         specURL = xstrdup(arg);
216     }
217
218     specut = urlPath(specURL, &specFile);
219     if (*specFile != '/') {
220         char *s = alloca(BUFSIZ);
221         if (!getcwd(s, BUFSIZ)) {
222             rpmlog(RPMLOG_ERR, _("getcwd failed: %m\n"));
223             goto exit;
224         }
225         strcat(s, "/");
226         strcat(s, arg);
227         specURL = s;
228     }
229
230     if (specut != URL_IS_DASH) {
231         struct stat st;
232         if (stat(specURL, &st) < 0) {
233             rpmlog(RPMLOG_ERR, _("failed to stat %s: %m\n"), specURL);
234             goto exit;
235         }
236         if (! S_ISREG(st.st_mode)) {
237             rpmlog(RPMLOG_ERR, _("File %s is not a regular file.\n"),
238                 specURL);
239             goto exit;
240         }
241
242         /* Try to verify that the file is actually a specfile */
243         if (!isSpecFile(specURL)) {
244             rpmlog(RPMLOG_ERR,
245                 _("File %s does not appear to be a specfile.\n"), specURL);
246             goto exit;
247         }
248     }
249     
250     /* Parse the spec file */
251 #define _anyarch(_f)    \
252 (((_f)&(RPMBUILD_PREP|RPMBUILD_BUILD|RPMBUILD_INSTALL|RPMBUILD_PACKAGEBINARY)) == 0)
253     if (parseSpec(ts, specURL, ba->rootdir, buildRootURL, 0, passPhrase,
254                 cookie, _anyarch(buildAmount), ba->force))
255     {
256         goto exit;
257     }
258 #undef  _anyarch
259     if ((spec = rpmtsSetSpec(ts, NULL)) == NULL) {
260         goto exit;
261     }
262
263     /* Assemble source header from parsed components */
264     initSourceHeader(spec);
265
266     /* Check build prerequisites */
267     if (!ba->noDeps && checkSpec(ts, spec->sourceHeader)) {
268         goto exit;
269     }
270
271     if (buildSpec(ts, spec, buildAmount, ba->noBuild)) {
272         goto exit;
273     }
274     
275     if (ba->buildMode == 't')
276         (void) unlink(specURL);
277     rc = 0;
278
279 exit:
280     free(specURL);
281     freeSpec(spec);
282     free(buildRootURL);
283     return rc;
284 }
285
286 int build(rpmts ts, const char * arg, BTA_t ba, const char * rcfile)
287 {
288     char *t, *te;
289     int rc = 0;
290     char * targets = ba->targets;
291 #define buildCleanMask  (RPMBUILD_RMSOURCE|RPMBUILD_RMSPEC)
292     int cleanFlags = ba->buildAmount & buildCleanMask;
293     rpmVSFlags vsflags, ovsflags;
294
295     vsflags = rpmExpandNumeric("%{_vsflags_build}");
296     if (ba->qva_flags & VERIFY_DIGEST)
297         vsflags |= _RPMVSF_NODIGESTS;
298     if (ba->qva_flags & VERIFY_SIGNATURE)
299         vsflags |= _RPMVSF_NOSIGNATURES;
300     if (ba->qva_flags & VERIFY_HDRCHK)
301         vsflags |= RPMVSF_NOHDRCHK;
302     ovsflags = rpmtsSetVSFlags(ts, vsflags);
303
304     if (targets == NULL) {
305         rc =  buildForTarget(ts, arg, ba);
306         goto exit;
307     }
308
309     /* parse up the build operators */
310
311     printf(_("Building target platforms: %s\n"), targets);
312
313     ba->buildAmount &= ~buildCleanMask;
314     for (t = targets; *t != '\0'; t = te) {
315         char *target;
316         if ((te = strchr(t, ',')) == NULL)
317             te = t + strlen(t);
318         target = alloca(te-t+1);
319         strncpy(target, t, (te-t));
320         target[te-t] = '\0';
321         if (*te != '\0')
322             te++;
323         else    /* XXX Perform clean-up after last target build. */
324             ba->buildAmount |= cleanFlags;
325
326         printf(_("Building for target %s\n"), target);
327
328         /* Read in configuration for target. */
329         rpmFreeMacros(NULL);
330         rpmFreeRpmrc();
331         (void) rpmReadConfigFiles(rcfile, target);
332         rc = buildForTarget(ts, arg, ba);
333         if (rc)
334             break;
335     }
336
337 exit:
338     vsflags = rpmtsSetVSFlags(ts, ovsflags);
339     /* Restore original configuration. */
340     rpmFreeMacros(NULL);
341     rpmFreeRpmrc();
342     (void) rpmReadConfigFiles(rcfile, NULL);
343
344     return rc;
345 }