]> pd.if.org Git - zpackage/blob - zpm-script.c
add option to set a log message in rmpackage
[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 (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;
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 = "/var/tmp/zpm-script";
141         char *output = "/var/tmp/zpm-script.out";
142         char *phase = 0;
143         char *envvar = 0;
144         int quiet = 0;
145         int scriptishash = 0;
146         int mode = RUN;
147
148         if (getenv("ZPMDB")) {
149                 db = getenv("ZPMDB");
150         }
151         /* ZPM_PACKAGE_FILE ? */
152
153         rootdir = getenv("ZPM_ROOT_DIR");
154
155         envvar = getenv("ZPM_SCRIPT_FILE");
156         if (envvar) {
157                 script = envvar;
158         }
159
160         envvar = getenv("ZPM_SCRIPT_OUTPUT");
161         if (envvar) {
162                 output = envvar;
163         }
164
165         /* run, set, show, hash */
166         /* set -S, if -H set the hash, output hash, unless quiet
167          * show: -o, or stdout, 
168          * All modes: [-f pkgfile] default to zpmdb
169          * All modes: [-p phase], default to configure
170          * All modes: [-R rootdir], chdir, attempt to chroot
171          * All modes: [-N], required, error if no such script
172          *
173          * run: zpm-script -r -p foo <pkgid args...>
174          * set: zpm-script -s <pkgid [script]>
175          * set: zpm-script -s -h <pkgid [hash]>
176          * show: zpm-script -l [-a] [-o outfile] <pkgid>
177          * show hash: zpm-script -lh <pkgid>
178          */
179         while ((opt = getopt(ac, av, "f:p:rslRFho:S:q")) != -1) {
180                 switch (opt) {
181                         case 'f': db = optarg; break;
182                         case 'p': phase = optarg; break;
183                         case 'r': mode = RUN; break;
184                         case 's': mode = SET; break;
185                         case 'l': mode = LIST; break;
186                         case 'R': rootdir = optarg; break;
187                         case 'F': required = 1; break;
188
189                         case 'h': scriptishash = 1; break;
190                         case 'o': output = optarg; break;
191                         case 'S': script = optarg; break;
192                         case 'q': quiet = 1; break;
193
194                         default:
195                                   usage();
196                                   exit(EXIT_FAILURE);
197                                   break;
198                 }
199         }
200         int argn = optind;
201
202         if (argn >= ac) {
203                 usage();
204                 exit(EXIT_FAILURE);
205         }
206
207         if (!zpm_open(&zpm, db)) {
208                 fprintf(stderr, "unable to open zpm database: %s\n", db);
209                 if (zpm.errmsg) {
210                         fprintf(stderr, "error detail: %s\n", zpm.errmsg);
211                 }
212                 exit(EXIT_FAILURE);
213         }
214
215         /* first non option arg is always a package id */
216         pkgstr = av[argn];
217         pkgid = zpm_findpkg(&zpm, pkgstr, NULL);
218
219         if (!pkgid) {
220                 fprintf(stderr, "no package for %s\n", pkgstr);
221                 zpm_close(&zpm);
222                 exit(EXIT_FAILURE);
223         }
224
225         argn++;
226
227         if (mode == SET) {
228                 char *sethash = 0;
229                 char *setscript = 0;
230                 int rv = 0;
231
232                 if (!phase) {
233                         phase = "configure";
234                 }
235
236                 if (argn < ac) {
237                         if (scriptishash) {
238                                 sethash = av[argn];
239                         } else {
240                                 setscript = av[argn];
241                         }
242                 }
243
244                 if (setscript) {
245                         rv = zpm_import(&zpm,setscript,0,hash);
246                         if (!rv) {
247                                 fprintf(stderr, "unable to import %s\n",
248                                                 setscript);
249                                 fail = HARD;
250                         } else {
251                                 sethash = hash;
252                         }
253                 } 
254
255                 if (!fail && !zpm_script_set(&zpm,pkgid,phase,sethash)) {
256                         fprintf(stderr, "unable to set %s script hash %s\n",
257                                         pkgid,
258                                         sethash ? sethash : "null");
259                         fprintf(stderr, "zpm error: %s\n", zpm.errmsg);
260                         fail = HARD;
261                 }
262         } else if (mode == LIST) {
263                 if (!phase) {
264                         zpm_foreach_script(&zpm, pkgid, phase, 0, list_scripts);
265                 } else if (!zpm_script_hash(&zpm, pkgid, phase, hash)) {
266                         fail = SOFT;
267                 } else if (scriptishash) {
268                         if (!quiet) {
269                                 printf("%s\n", hash);
270                         }
271                 } else {
272                         if (!output) {
273                                 output = "-";
274                         }
275                         if (!zpm_extract(&zpm, hash, output, 0700)) {
276                                 fail = HARD;
277                         }
278                 }
279         } else {
280                 /* run a script */
281                 if (!phase) {
282                         phase = "configure";
283                 }
284
285                 if (!output) {
286                         output = "/var/tmp/zpm-script.out";
287                 }
288
289                 if (!script) {
290                         script = "/var/tmp/zpm-script";
291                 }
292
293                 /* since the script file name doesn't really
294                  * mean anything, pass in the phase as arg 0
295                  */
296                 /* TODO sanitize environment ? */
297                 args[0] = phase;
298                 args[1] = pkgid;
299                 args[2] = 0;
300                 args[3] = 0;
301
302                 if (argn <= ac) {
303                         args[2] = av[argn];
304                 }
305
306                 if (!zpm_script_hash(&zpm, pkgid, phase, hash)) {
307                         fail = SOFT;
308                         goto cleanup;
309                 }
310
311                 if (!setdir(rootdir)) {
312                         fail = HARD;
313                         goto cleanup;
314                 }
315
316                 if (!zpm_extract(&zpm, hash, script, 0700)) {
317                         fprintf(stderr, "unable to extract script");
318                         fail = HARD;
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                 if (rv) {
325                         fprintf(stderr, "package %s script failed with code %d\n",
326                                         pkgid, rv);
327                         
328                         zpm_import(&zpm, output, 0, hash);
329                         zpm_note_add(&zpm, pkgid, NULL, hash, "package %s script failed with code %d", phase, rv);
330
331                         fail = HARD;
332                 }
333
334                 /* TODO log output */
335                 if (script) {
336                         unlink(script);
337                 }
338                 if (output) {
339                         unlink(output);
340                 }
341         }
342
343 cleanup:
344         free(pkgid);
345         zpm_close(&zpm);
346
347         return (fail == HARD || (required && fail)) ? EXIT_FAILURE : 0;
348 }