]> pd.if.org Git - zpackage/blob - src/script.c
remove stray debug fprintf
[zpackage] / src / script.c
1 #define _POSIX_C_SOURCE 200809L
2
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <sys/types.h>
7 #include <sys/wait.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <errno.h>
12 #include <unistd.h>
13
14 #include <sys/mman.h>
15
16 #include "zpm.h"
17
18 /*
19  * -f package file, otherwise localdb
20  * -p phase, defaults to 'configure'
21  * -s script name, defaults to /var/tmp/zpm-script
22  * -r chroot to directory
23  * -o script output, /var/tmp/zpm-script.out, '-' for stdout
24  *
25  * arg is package id
26  */
27
28 void usage(void) {
29         fprintf(stderr, "usage: zpm script ...\n");
30 }
31
32 int setdir(char *rootdir) {
33         if (chdir(rootdir ? rootdir : "/") == -1) {
34                 perror("can not chdir to rootdir");
35                 return 0;
36         }
37 #if 0
38         if (rootdir && strcmp(rootdir, "/")) {
39                 if (geteuid() == 0) {
40                         /* chroot is deprecated, and not in posix.  need to use
41                          * OS/kernel specific code.
42                          */
43                         fprintf(stderr, "support for chroot equivalent not supported on this OS\n");
44                 } else {
45                         fprintf(stderr, "unable to chroot as non root user\n");
46                 }
47         }
48 #endif
49         return 1;
50 }
51
52
53 int run(char *program, char **args, char *output, int *status) {
54         /* if stdout is a terminal, leave stdout where it goes,
55          * if it's not a terminal, redirect stdout to output.
56          * in either case, send stderr to output, unless null
57          * if output is null or "-", just run the program
58          */
59         int interactive = 0;
60         pid_t pid;
61         int rv;
62
63         errno = 0;
64
65         interactive = isatty(1);
66
67         pid = fork();
68
69         if (pid == -1) {
70                 *status = 1;
71                 return -1;
72         }
73
74         if (pid == 0) {
75                 /* child */
76                 if (output && strcmp(output, "-") != 0) {
77                         close(2);
78                         rv = open(output, O_NOFOLLOW|O_TRUNC|O_CREAT|O_WRONLY,
79                                         0600);
80                         if (rv == -1) {
81                                 perror("cannot open output file");
82                         }
83                         if (!interactive) {
84                                 rv = dup2(2,1);
85                                 if (rv == -1) {
86                                         perror("unable to redirect stdout");
87                                         exit(EXIT_FAILURE);
88                                 }
89                         }
90                 }
91
92                 rv = execvp(program, args);
93                 if (rv == -1) {
94                         perror("cannot exec");
95                         exit(EXIT_FAILURE);
96                 }
97         }
98
99         pid = wait(status);
100         if (pid == -1) {
101                 perror("error waiting for child");
102                 return -2;
103         }
104
105         if (WIFEXITED(*status)) {
106                 return WEXITSTATUS(*status);
107         }
108
109         return -3;
110 }
111
112 #define RUN 1
113 #define SET 2
114 #define LIST 3
115
116 #define SOFT 1
117 #define HARD 2
118
119 static int list_scripts(void *ud, const char *pkg, const char *stage,
120                 const char *hash) {
121         printf("%s %s %.8s\n", pkg, stage, hash);
122         return 0;
123 }
124
125 int main(int ac, char **av){
126         struct zpm zpm;
127         int rv;
128         int status = 0;
129         int required = 0;
130         int fail = 0;
131         char *pkgstr;
132         int opt;
133
134         char hash[ZPM_HASH_STRLEN+1];
135         char *args[4];
136         char *pkgid;
137
138         char *rootdir = 0;
139         char *db = "/var/lib/zpm/local.db";
140         char *script = 0;
141         char *output = 0;
142         char *phase = 0;
143         int quiet = 0, verbose = 0;
144         int allowempty = 0, notealways = 0, makenote = 0, showoutput = 0;
145         int scriptishash = 0, notenever = 0, noteifwritable = 0;
146         size_t outsize;
147         int mode = RUN;
148
149         if (getenv("ZPMDB")) {
150                 db = getenv("ZPMDB");
151         }
152         /* ZPM_PACKAGE_FILE ? */
153
154         rootdir = getenv("ZPM_ROOT_DIR");
155         script = getenv("ZPM_SCRIPT_FILE");
156         output = getenv("ZPM_SCRIPT_OUTPUT");
157
158         /* run, set, show, hash */
159         /* set -S, if -H set the hash, output hash, unless quiet
160          * show: -o, or stdout, 
161          * All modes: [-f pkgfile] default to zpmdb
162          * All modes: [-p phase], default to configure
163          * All modes: [-R rootdir], chdir, attempt to chroot
164          * All modes: [-N], required, error if no such script
165          *
166          * run: zpm-script -r -p foo <pkgid args...>
167          * set: zpm-script -s <pkgid [script]>
168          * set: zpm-script -s -h <pkgid [hash]>
169          * show: zpm-script -l [-a] [-o outfile] <pkgid>
170          * show hash: zpm-script -lh <pkgid>
171          */
172         while ((opt = getopt(ac, av, "f:p:rslRFho:S:qv")) != -1) {
173                 switch (opt) {
174                         case 'f': db = optarg; break;
175                         case 'p': phase = optarg; break;
176                         case 'r': mode = RUN; break;
177                         case 's': mode = SET; break;
178                         case 'l': mode = LIST; break;
179                         case 'R': rootdir = optarg; break;
180                         case 'F': required = 1; break;
181                         case 'a': notealways = 1; break;
182                         case 'n': notenever = 1; break;
183                         case 'N': noteifwritable = 1; break;
184                         case 'e': allowempty = 1; break;
185                         case 'h': scriptishash = 1; break;
186                         case 'o': output = optarg; break;
187                         case 'S': script = optarg; break;
188                         case 'q': quiet = 1; break;
189                         case 'v': verbose = 1; break;
190
191                         default:
192                                   usage();
193                                   exit(EXIT_FAILURE);
194                                   break;
195                 }
196         }
197         int argn = optind;
198
199         if (argn >= ac) {
200                 usage();
201                 exit(EXIT_FAILURE);
202         }
203
204         if (!zpm_open(&zpm, db)) {
205                 fprintf(stderr, "unable to open zpm database: %s\n", db);
206                 if (zpm.errmsg) {
207                         fprintf(stderr, "error detail: %s\n", zpm.errmsg);
208                 }
209                 exit(EXIT_FAILURE);
210         }
211
212         /* first non option arg is always a package id */
213         pkgstr = av[argn];
214         pkgid = zpm_findpkg(&zpm, pkgstr, NULL);
215
216         if (!pkgid) {
217                 fprintf(stderr, "no package for %s\n", pkgstr);
218                 zpm_close(&zpm);
219                 exit(EXIT_FAILURE);
220         }
221
222         argn++;
223
224         if (mode == SET) {
225                 char *sethash = 0;
226                 char *setscript = 0;
227                 int rv = 0;
228
229                 if (!phase) {
230                         phase = "configure";
231                 }
232
233                 if (argn < ac) {
234                         if (scriptishash) {
235                                 sethash = av[argn];
236                         } else {
237                                 setscript = av[argn];
238                         }
239                 }
240
241                 if (setscript) {
242                         rv = zpm_import(&zpm,setscript,0,hash);
243                         if (!rv) {
244                                 fprintf(stderr, "unable to import %s\n",
245                                                 setscript);
246                                 fail = HARD;
247                         } else {
248                                 sethash = hash;
249                         }
250                 } 
251
252                 if (!fail && !zpm_script_set(&zpm,pkgid,phase,sethash)) {
253                         fprintf(stderr, "unable to set %s script hash %s\n",
254                                         pkgid,
255                                         sethash ? sethash : "null");
256                         fprintf(stderr, "zpm error: %s\n", zpm.errmsg);
257                         fail = HARD;
258                 }
259         } else if (mode == LIST) {
260                 if (!phase) {
261                         zpm_foreach_script(&zpm, pkgid, phase, 0, list_scripts);
262                 } else if (!zpm_script_hash(&zpm, pkgid, phase, hash)) {
263                         fail = SOFT;
264                 } else if (scriptishash) {
265                         if (!quiet) {
266                                 printf("%s\n", hash);
267                         }
268                 } else {
269                         if (!output) {
270                                 output = "-";
271                         }
272                         if (!zpm_extract(&zpm, hash, output, 0700)) {
273                                 fail = HARD;
274                         }
275                 }
276         } else {
277                 /* run a script */
278                 if (!phase) {
279                         phase = "configure";
280                 }
281
282                 if (!output) {
283                         output = "/var/tmp/zpm-script.out";
284                 }
285
286                 if (!script) {
287                         script = "/var/tmp/zpm-script";
288                 }
289
290                 /* since the script file name doesn't really
291                  * mean anything, pass in the phase as arg 0
292                  */
293                 /* TODO sanitize environment ? */
294                 args[0] = phase;
295                 args[1] = pkgid;
296                 args[2] = 0;
297                 args[3] = 0;
298
299                 if (argn <= ac) {
300                         args[2] = av[argn];
301                 }
302
303                 if (!zpm_script_hash(&zpm, pkgid, phase, hash)) {
304                         fail = SOFT;
305                         status = EXIT_FAILURE;
306                         goto cleanup;
307                 }
308
309                 if (!setdir(rootdir)) {
310                         fail = HARD;
311                         status = EXIT_FAILURE;
312                         goto cleanup;
313                 }
314
315                 if (!zpm_extract(&zpm, hash, script, 0700)) {
316                         fprintf(stderr, "unable to extract script");
317                         fail = HARD;
318                         status = EXIT_FAILURE;
319                         zpm_note_add(&zpm, pkgid, NULL, hash, "unable to extract %s script", phase);
320                         goto cleanup;
321                 }
322
323                 rv = run(script, args, output, &status);
324
325                 status = WEXITSTATUS(status);
326                 if (status != 0) {
327                         fail = HARD;
328                 }
329
330                 if (zpm_readonly(&zpm) && noteifwritable) {
331                         notenever = 1;
332                 }
333
334                 if (output && !notenever) {
335                         struct stat outstat;
336                         stat(output, &outstat);
337                         outsize = outstat.st_size;
338                         if (notealways || (outsize && status && (outsize || allowempty))) {
339                                 makenote = 1;
340                         }
341                 }
342                 
343                 if (makenote) {
344                         char note[1024];
345
346                         zpm_import(&zpm, output, 0, hash);
347
348                         if (status) {
349                                 sprintf(note, "%.64s script failed with code %d",
350                                                 phase, status);
351
352                                 fail = HARD;
353                         } else {
354                                 sprintf(note, "%.64s script succeeded", phase);
355                         }
356                         zpm_note_add(&zpm, pkgid, NULL, hash, note);
357                 }
358
359                 if (output && showoutput) {
360                         char buf[4096];
361                         int out = open(output, O_RDONLY);
362                         ssize_t bytes;
363                         if (out == -1) {
364                                 perror("can't open output file");
365                         } else {
366                                 while ((bytes = read(out, buf, sizeof buf)) > 0) {
367                                         write(1, buf, bytes);
368                                 }
369                                 if (bytes == -1) {
370                                         perror("output file read error");
371                                 }
372                         }
373                         if (out != -1) {
374                                 close(out);
375                         }
376                 }
377
378                 if (script) {
379                         unlink(script);
380                 }
381                 if (output) {
382                         unlink(output);
383                 }
384         }
385
386 cleanup:
387         free(pkgid);
388         zpm_close(&zpm);
389
390         return (fail == HARD || (required && fail)) ? status : 0;
391 }