]> pd.if.org Git - zpackage/blob - zpm-script.c
8018849b36c450b1f00db39657557e658877b8ea
[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         int quiet = 0;
147         int scriptishash = 0;
148         int mode = RUN;
149         int all = 0;
150
151         if (getenv("ZPMDB")) {
152                 db = getenv("ZPMDB");
153         }
154         /* ZPM_PACKAGE_FILE ? */
155
156         rootdir = getenv("ZPM_ROOT_DIR");
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:q")) != -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': all = 1; break;
182
183                         case 'h': scriptishash = 1; break;
184                         case 'o': output = optarg; break;
185                         case 'S': script = optarg; break;
186                         case 'q': quiet = 1; break;
187
188                         default:
189                                   usage();
190                                   exit(EXIT_FAILURE);
191                                   break;
192                 }
193         }
194         int argn = optind;
195
196         if (argn >= ac) {
197                 usage();
198                 exit(EXIT_FAILURE);
199         }
200
201         if (!zpm_open(&zpm, db)) {
202                 fprintf(stderr, "unable to open zpm database: %s\n", db);
203                 if (zpm.errmsg) {
204                         fprintf(stderr, "error detail: %s\n", zpm.errmsg);
205                 }
206                 exit(EXIT_FAILURE);
207         }
208
209         /* first non option arg is always a package id */
210         pkgstr = av[argn];
211         pkgid = zpm_findpkg(&zpm, pkgstr, NULL);
212
213         if (!pkgid) {
214                 fprintf(stderr, "no package for %s\n", pkgstr);
215                 zpm_close(&zpm);
216                 exit(EXIT_FAILURE);
217         }
218
219         argn++;
220
221         if (mode == SET) {
222                 char *sethash = 0;
223                 char *setscript = 0;
224                 int rv = 0;
225
226                 if (!phase) {
227                         phase = "configure";
228                 }
229
230                 if (argn < ac) {
231                         if (scriptishash) {
232                                 sethash = av[argn];
233                         } else {
234                                 setscript = av[argn];
235                         }
236                 }
237
238                 if (setscript) {
239                         rv = zpm_import(&zpm,setscript,0,hash);
240                         if (!rv) {
241                                 fprintf(stderr, "unable to import %s\n",
242                                                 setscript);
243                                 fail = HARD;
244                         } else {
245                                 sethash = hash;
246                         }
247                 } 
248
249                 if (!fail && !zpm_script_set(&zpm,pkgid,phase,sethash)) {
250                         fprintf(stderr, "unable to set %s script hash %s\n",
251                                         pkgid,
252                                         sethash ? sethash : "null");
253                         fprintf(stderr, "zpm error: %s\n", zpm.errmsg);
254                         fail = HARD;
255                 }
256         } else if (mode == LIST) {
257                 if (!phase) {
258                         zpm_foreach_script(&zpm, pkgid, phase, 0, list_scripts);
259                 } else if (!zpm_script_hash(&zpm, pkgid, phase, hash)) {
260                         fail = SOFT;
261                 } else if (scriptishash) {
262                         if (!quiet) {
263                                 printf("%s\n", hash);
264                         }
265                 } else {
266                         if (!output) {
267                                 output = "-";
268                         }
269                         if (!zpm_extract(&zpm, hash, output, 0700)) {
270                                 fail = HARD;
271                         }
272                 }
273         } else {
274                 /* run a script */
275                 if (!phase) {
276                         phase = "configure";
277                 }
278
279                 if (!output) {
280                         output = "/var/tmp/zpm-script.out";
281                 }
282
283                 if (!script) {
284                         script = "/var/tmp/zpm-script";
285                 }
286
287                 /* since the script file name doesn't really
288                  * mean anything, pass in the phase as arg 0
289                  */
290                 /* TODO sanitize environment ? */
291                 args[0] = phase;
292                 args[1] = pkgid;
293                 args[2] = 0;
294                 args[3] = 0;
295
296                 if (argn <= ac) {
297                         args[2] = av[argn];
298                 }
299
300                 if (!zpm_script_hash(&zpm, pkgid, phase, hash)) {
301                         fail = SOFT;
302                         goto cleanup;
303                 }
304
305                 if (!setdir(rootdir)) {
306                         fail = HARD;
307                         goto cleanup;
308                 }
309
310                 if (!zpm_extract(&zpm, hash, script, 0700)) {
311                         fprintf(stderr, "unable to extract script");
312                         fail = HARD;
313                         goto cleanup;
314                 }
315
316                 rv = run(script, args, output, &status);
317                 if (rv) {
318                         fprintf(stderr, "package %s script failed with code %d\n",
319                                         pkgid, rv);
320                         fail = HARD;
321                 }
322
323                 /* TODO log output */
324                 if (script) {
325                         unlink(script);
326                 }
327                 if (output) {
328                         unlink(output);
329                 }
330         }
331
332 cleanup:
333         free(pkgid);
334         zpm_close(&zpm);
335
336         return (fail == HARD || (required && fail)) ? EXIT_FAILURE : 0;
337
338 }