Test argument count early, avoiding the need to re-check over and over
[platform/upstream/rpm.git] / rpmbuild.c
1 #include "system.h"
2 const char *__progname;
3
4 #define _AUTOHELP
5
6 #include <sys/wait.h>
7 #if HAVE_MCHECK_H
8 #include <mcheck.h>
9 #endif
10
11 #include <rpm/rpmcli.h>
12 #include <rpm/rpmlib.h>                 /* RPMSIGTAG, rpmReadPackageFile .. */
13 #include <rpm/rpmbuild.h>
14 #include <rpm/rpmlog.h>
15 #include <rpm/rpmfileutil.h>
16 #include <rpm/rpmdb.h>
17 #include <rpm/rpmps.h>
18 #include <rpm/rpmts.h>
19 #include "lib/signature.h"
20 #include "build.h"
21
22 #include "debug.h"
23
24 enum modes {
25     MODE_BUILD          = (1 <<  4),
26     MODE_REBUILD        = (1 <<  5),
27     MODE_RECOMPILE      = (1 <<  8),
28     MODE_TARBUILD       = (1 << 11),
29 };
30
31 static int quiet;
32
33 /* the structure describing the options we take and the defaults */
34 static struct poptOption optionsTable[] = {
35
36  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmBuildPoptTable, 0,
37         N_("Build options with [ <specfile> | <tarball> | <source package> ]:"),
38         NULL },
39
40  { "quiet", '\0', 0, &quiet, 0,                 NULL, NULL},
41
42  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
43         N_("Common options for all rpm modes and executables:"),
44         NULL },
45
46    POPT_AUTOALIAS
47    POPT_AUTOHELP
48    POPT_TABLEEND
49 };
50
51 #ifdef __MINT__
52 /* MiNT cannot dynamically increase the stack.  */
53 long _stksize = 64 * 1024L;
54 #endif
55
56 RPM_GNUC_NORETURN
57 static void argerror(const char * desc)
58 {
59     fprintf(stderr, _("%s: %s\n"), __progname, desc);
60     exit(EXIT_FAILURE);
61 }
62
63 static void printVersion(FILE * fp)
64 {
65     fprintf(fp, _("RPM version %s\n"), rpmEVR);
66 }
67
68 static void printBanner(FILE * fp)
69 {
70     fprintf(fp, _("Copyright (C) 1998-2002 - Red Hat, Inc.\n"));
71     fprintf(fp, _("This program may be freely redistributed under the terms of the GNU GPL\n"));
72 }
73
74 static void printUsage(poptContext con, FILE * fp, int flags)
75 {
76     printVersion(fp);
77     printBanner(fp);
78     fprintf(fp, "\n");
79
80     if (rpmIsVerbose())
81         poptPrintHelp(con, fp, flags);
82     else
83         poptPrintUsage(con, fp, flags);
84 }
85
86 int main(int argc, char *argv[])
87 {
88     rpmts ts = NULL;
89     enum modes bigMode = MODE_BUILD;
90     BTA_t ba = &rpmBTArgs;
91     char * passPhrase = "";
92
93     int arg;
94     const char *optArg;
95     const char *poptCtx = "rpmbuild";
96     pid_t pipeChild = 0;
97     poptContext optCon;
98     int ec = 0;
99     int status;
100     int p[2];
101         
102 #if HAVE_MCHECK_H && HAVE_MTRACE
103     mtrace();   /* Trace malloc only if MALLOC_TRACE=mtrace-output-file. */
104 #endif
105     setprogname(argv[0]);       /* Retrofit glibc __progname */
106
107     /* XXX glibc churn sanity */
108     if (__progname == NULL) {
109         if ((__progname = strrchr(argv[0], '/')) != NULL) __progname++;
110         else __progname = argv[0];
111     }
112
113 #if defined(ENABLE_NLS)
114     /* set up the correct locale */
115     (void) setlocale(LC_ALL, "" );
116
117     bindtextdomain(PACKAGE, LOCALEDIR);
118     textdomain(PACKAGE);
119 #endif
120
121     rpmSetVerbosity(RPMLOG_NOTICE);     /* XXX silly use by showrc */
122
123     /* Make a first pass through the arguments, looking for --rcfile */
124     /* We need to handle that before dealing with the rest of the arguments. */
125     /* XXX popt argv definition should be fixed instead of casting... */
126     optCon = poptGetContext(poptCtx, argc, (const char **)argv, optionsTable, 0);
127     {
128         char *poptfile = rpmGenPath(rpmConfigDir(), LIBRPMALIAS_FILENAME, NULL);
129         (void) poptReadConfigFile(optCon, poptfile);
130         free(poptfile);
131     }
132     (void) poptReadDefaultConfig(optCon, 1);
133     poptSetExecPath(optCon, rpmConfigDir(), 1);
134
135     while ((arg = poptGetNextOpt(optCon)) > 0) {
136         optArg = poptGetOptArg(optCon);
137
138         switch (arg) {
139         default:
140             fprintf(stderr, _("Internal error in argument processing (%d) :-(\n"), arg);
141             exit(EXIT_FAILURE);
142         }
143     }
144
145     if (arg < -1) {
146         fprintf(stderr, "%s: %s\n", 
147                 poptBadOption(optCon, POPT_BADOPTION_NOALIAS), 
148                 poptStrerror(arg));
149         exit(EXIT_FAILURE);
150     }
151
152     if (argc <= 1 || poptPeekArg(optCon) == NULL) {
153         printUsage(optCon, stderr, 0);
154         exit(EXIT_FAILURE);
155     }
156
157     rpmcliConfigured();
158
159     switch (ba->buildMode) {
160     case 'b':   bigMode = MODE_BUILD;           break;
161     case 't':   bigMode = MODE_TARBUILD;        break;
162     case 'B':   bigMode = MODE_REBUILD;         break;
163     case 'C':   bigMode = MODE_RECOMPILE;       break;
164     }
165
166     if (rpmcliRootDir && rpmcliRootDir[0] != '/') {
167         argerror(_("arguments to --root (-r) must begin with a /"));
168     }
169
170     if (quiet)
171         rpmSetVerbosity(RPMLOG_WARNING);
172
173     if (ba->sign) {
174         if (bigMode == MODE_REBUILD || bigMode == MODE_BUILD ||
175             bigMode == MODE_TARBUILD)
176         {
177             if (poptPeekArg(optCon)) {
178                 int sigTag = rpmLookupSignatureType(RPMLOOKUPSIG_QUERY);
179                 switch (sigTag) {
180                   case 0:
181                     break;
182                   case RPMSIGTAG_PGP:
183                   case RPMSIGTAG_GPG:
184                   case RPMSIGTAG_DSA:
185                   case RPMSIGTAG_RSA:
186                     passPhrase = rpmGetPassPhrase(_("Enter pass phrase: "), sigTag);
187                     if (passPhrase == NULL) {
188                         fprintf(stderr, _("Pass phrase check failed\n"));
189                         ec = EXIT_FAILURE;
190                         goto exit;
191                     }
192                     fprintf(stderr, _("Pass phrase is good.\n"));
193                     passPhrase = xstrdup(passPhrase);
194                     break;
195                   default:
196                     fprintf(stderr,
197                             _("Invalid %%_signature spec in macro file.\n"));
198                     ec = EXIT_FAILURE;
199                     goto exit;
200                     break;
201                 }
202             }
203         } else {
204             argerror(_("--sign may only be used during package building"));
205         }
206     } else {
207         /* Make rpmLookupSignatureType() return 0 ("none") from now on */
208         (void) rpmLookupSignatureType(RPMLOOKUPSIG_DISABLE);
209     }
210
211     if (rpmcliPipeOutput) {
212         if (pipe(p) < 0) {
213             fprintf(stderr, _("creating a pipe for --pipe failed: %m\n"));
214             goto exit;
215         }
216
217         if (!(pipeChild = fork())) {
218             (void) signal(SIGPIPE, SIG_DFL);
219             (void) close(p[1]);
220             (void) dup2(p[0], STDIN_FILENO);
221             (void) close(p[0]);
222             (void) execl("/bin/sh", "/bin/sh", "-c", rpmcliPipeOutput, NULL);
223             fprintf(stderr, _("exec failed\n"));
224         }
225
226         (void) close(p[0]);
227         (void) dup2(p[1], STDOUT_FILENO);
228         (void) close(p[1]);
229     }
230         
231     ts = rpmtsCreate();
232     (void) rpmtsSetRootDir(ts, rpmcliRootDir);
233     switch (bigMode) {
234     case MODE_REBUILD:
235     case MODE_RECOMPILE:
236     {   const char * pkg;
237
238         while (!rpmIsVerbose())
239             rpmIncreaseVerbosity();
240
241         ba->buildAmount =
242             RPMBUILD_PREP | RPMBUILD_BUILD | RPMBUILD_INSTALL | RPMBUILD_CHECK;
243         if (bigMode == MODE_REBUILD) {
244             ba->buildAmount |= RPMBUILD_PACKAGEBINARY;
245             ba->buildAmount |= RPMBUILD_RMSOURCE;
246             ba->buildAmount |= RPMBUILD_RMSPEC;
247             ba->buildAmount |= RPMBUILD_CLEAN;
248             ba->buildAmount |= RPMBUILD_RMBUILD;
249         }
250
251         while ((pkg = poptGetArg(optCon))) {
252             char * specFile = NULL;
253
254             ba->cookie = NULL;
255             ec = rpmInstallSource(ts, pkg, &specFile, &ba->cookie);
256             if (ec == 0) {
257                 ba->rootdir = rpmcliRootDir;
258                 ba->passPhrase = passPhrase;
259                 ec = build(ts, specFile, ba, rpmcliRcfile);
260             }
261             ba->cookie = _free(ba->cookie);
262             specFile = _free(specFile);
263
264             if (ec)
265                 break;
266         }
267
268     }   break;
269
270     case MODE_BUILD:
271     case MODE_TARBUILD:
272     {   const char * pkg;
273         if (!quiet) while (!rpmIsVerbose())
274             rpmIncreaseVerbosity();
275        
276         switch (ba->buildChar) {
277         case 'a':
278             ba->buildAmount |= RPMBUILD_PACKAGESOURCE;
279         case 'b':
280             ba->buildAmount |= RPMBUILD_PACKAGEBINARY;
281             ba->buildAmount |= RPMBUILD_CLEAN;
282             if ((ba->buildChar == 'b') && ba->shortCircuit)
283                 break;
284         case 'i':
285             ba->buildAmount |= RPMBUILD_INSTALL;
286             ba->buildAmount |= RPMBUILD_CHECK;
287             if ((ba->buildChar == 'i') && ba->shortCircuit)
288                 break;
289         case 'c':
290             ba->buildAmount |= RPMBUILD_BUILD;
291             if ((ba->buildChar == 'c') && ba->shortCircuit)
292                 break;
293         case 'p':
294             ba->buildAmount |= RPMBUILD_PREP;
295             break;
296             
297         case 'l':
298             ba->buildAmount |= RPMBUILD_FILECHECK;
299             break;
300         case 's':
301             ba->buildAmount |= RPMBUILD_PACKAGESOURCE;
302             break;
303         }
304
305         while ((pkg = poptGetArg(optCon))) {
306             ba->rootdir = rpmcliRootDir;
307             ba->passPhrase = passPhrase;
308             ba->cookie = NULL;
309             ec = build(ts, pkg, ba, rpmcliRcfile);
310             if (ec)
311                 break;
312             rpmFreeMacros(NULL);
313             (void) rpmReadConfigFiles(rpmcliRcfile, NULL);
314         }
315     }   break;
316     }
317
318 exit:
319
320     ts = rpmtsFree(ts);
321
322     optCon = poptFreeContext(optCon);
323     rpmFreeMacros(NULL);
324         rpmFreeMacros(rpmCLIMacroContext);
325     rpmFreeRpmrc();
326
327     if (pipeChild) {
328         (void) fclose(stdout);
329         (void) waitpid(pipeChild, &status, 0);
330     }
331
332     /* keeps memory leak checkers quiet */
333     rpmlogClose();
334
335     freeNames();
336     ba->buildRootOverride = _free(ba->buildRootOverride);
337     ba->targets = _free(ba->targets);
338
339 #if HAVE_MCHECK_H && HAVE_MTRACE
340     muntrace();   /* Trace malloc only if MALLOC_TRACE=mtrace-output-file. */
341 #endif
342
343     /* XXX Avoid exit status overflow. Status 255 is special to xargs(1) */
344     if (ec > 254) ec = 254;
345
346     return ec;
347 }