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