4cde9bb84381ab18809f840cdda97115bf4dd1f0
[platform/upstream/rpm.git] / lib / rpmscript.c
1 #include "system.h"
2
3 #include <errno.h>
4 #include <unistd.h>
5
6 #define _RPMSQ_INTERNAL
7 #include <rpm/rpmsq.h>
8 #include <rpm/rpmts.h>
9 #include <rpm/rpmfileutil.h>
10 #include <rpm/rpmmacro.h>
11 #include <rpm/rpmio.h>
12 #include <rpm/rpmlog.h>
13 #include <rpm/rpmsw.h>
14 #include <rpm/header.h>
15
16 #include "rpmio/rpmlua.h"
17 #include "lib/rpmscript.h"
18
19 #include "debug.h"
20
21 /**
22  * Run internal Lua script.
23  */
24 static rpmRC runLuaScript(rpmts ts, ARGV_const_t prefixes,
25                    const char *sname, rpmlogLvl lvl,
26                    ARGV_t * argvp, const char *script, int arg1, int arg2)
27 {
28     rpmRC rc = RPMRC_FAIL;
29 #ifdef WITH_LUA
30     int rootFd = -1;
31     int xx;
32     ARGV_t argv = argvp ? *argvp : NULL;
33     rpmlua lua = NULL; /* Global state. */
34     rpmluav var;
35
36     rpmlog(RPMLOG_DEBUG, "%s: running <lua> scriptlet.\n", sname);
37     if (!rpmtsChrootDone(ts)) {
38         const char *rootDir = rpmtsRootDir(ts);
39         xx = chdir("/");
40         rootFd = open(".", O_RDONLY, 0);
41         if (rootFd >= 0) {
42             if (rootDir != NULL && !rstreq(rootDir, "/") && *rootDir == '/')
43                 xx = chroot(rootDir);
44             xx = rpmtsSetChrootDone(ts, 1);
45         }
46     }
47
48     /* Create arg variable */
49     rpmluaPushTable(lua, "arg");
50     var = rpmluavNew();
51     rpmluavSetListMode(var, 1);
52     if (argv) {
53         char **p;
54         for (p = argv; *p; p++) {
55             rpmluavSetValue(var, RPMLUAV_STRING, *p);
56             rpmluaSetVar(lua, var);
57         }
58     }
59     if (arg1 >= 0) {
60         rpmluavSetValueNum(var, arg1);
61         rpmluaSetVar(lua, var);
62     }
63     if (arg2 >= 0) {
64         rpmluavSetValueNum(var, arg2);
65         rpmluaSetVar(lua, var);
66     }
67     var = rpmluavFree(var);
68     rpmluaPop(lua);
69
70     if (rpmluaRunScript(lua, script, sname) == 0) {
71         rc = RPMRC_OK;
72     }
73
74     rpmluaDelVar(lua, "arg");
75
76     if (rootFd >= 0) {
77         const char *rootDir = rpmtsRootDir(ts);
78         xx = fchdir(rootFd);
79         xx = close(rootFd);
80         if (rootDir != NULL && !rstreq(rootDir, "/") && *rootDir == '/')
81             xx = chroot(".");
82         xx = rpmtsSetChrootDone(ts, 0);
83     }
84 #else
85     rpmlog(lvl, _("<lua> scriptlet support not built in\n"));
86 #endif
87
88     return rc;
89 }
90
91 static const char * const SCRIPT_PATH = "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin";
92
93 static void doScriptExec(rpmts ts, ARGV_const_t argv, ARGV_const_t prefixes,
94                         FD_t scriptFd, FD_t out)
95 {
96     const char * rootDir;
97     int pipes[2];
98     int flag;
99     int fdno;
100     int xx;
101     int open_max;
102
103     (void) signal(SIGPIPE, SIG_DFL);
104     pipes[0] = pipes[1] = 0;
105     /* make stdin inaccessible */
106     xx = pipe(pipes);
107     xx = close(pipes[1]);
108     xx = dup2(pipes[0], STDIN_FILENO);
109     xx = close(pipes[0]);
110
111     /* XXX Force FD_CLOEXEC on all inherited fdno's. */
112     open_max = sysconf(_SC_OPEN_MAX);
113     if (open_max == -1) {
114         open_max = 1024;
115     }
116     for (fdno = 3; fdno < open_max; fdno++) {
117         flag = fcntl(fdno, F_GETFD);
118         if (flag == -1 || (flag & FD_CLOEXEC))
119             continue;
120         xx = fcntl(fdno, F_SETFD, FD_CLOEXEC);
121         /* XXX W2DO? debug msg for inheirited fdno w/o FD_CLOEXEC */
122     }
123
124     if (scriptFd != NULL) {
125         int sfdno = Fileno(scriptFd);
126         int ofdno = Fileno(out);
127         if (sfdno != STDERR_FILENO)
128             xx = dup2(sfdno, STDERR_FILENO);
129         if (ofdno != STDOUT_FILENO)
130             xx = dup2(ofdno, STDOUT_FILENO);
131         /* make sure we don't close stdin/stderr/stdout by mistake! */
132         if (ofdno > STDERR_FILENO && ofdno != sfdno)
133             xx = Fclose (out);
134         if (sfdno > STDERR_FILENO && ofdno != sfdno)
135             xx = Fclose (scriptFd);
136     }
137
138     {   char *ipath = rpmExpand("%{_install_script_path}", NULL);
139         const char *path = SCRIPT_PATH;
140
141         if (ipath && ipath[5] != '%')
142             path = ipath;
143
144         xx = setenv("PATH", path, 1);
145         ipath = _free(ipath);
146     }
147
148     for (ARGV_const_t pf = prefixes; pf && *pf; pf++) {
149         char *name = NULL;
150         int num = (pf - prefixes);
151
152         rasprintf(&name, "RPM_INSTALL_PREFIX%d", num);
153         setenv(name, *pf, 1);
154         free(name);
155
156         /* scripts might still be using the old style prefix */
157         if (num == 0) {
158             setenv("RPM_INSTALL_PREFIX", *pf, 1);
159         }
160     }
161         
162     rootDir = rpmtsRootDir(ts);
163     if (rootDir  != NULL) {     /* XXX can't happen */
164         if (!rpmtsChrootDone(ts) &&
165             !(rootDir[0] == '/' && rootDir[1] == '\0'))
166         {
167             xx = chroot(rootDir);
168         }
169         xx = chdir("/");
170
171         /* XXX Don't mtrace into children. */
172         unsetenv("MALLOC_CHECK_");
173
174         /* Permit libselinux to do the scriptlet exec. */
175         if (rpmtsSELinuxEnabled(ts) == 1) {     
176             xx = rpm_execcon(0, argv[0], argv, environ);
177         }
178
179         if (xx == 0) {
180             xx = execv(argv[0], argv);
181         }
182     }
183     _exit(127); /* exit 127 for compatibility with bash(1) */
184 }
185
186 /**
187  * Run an external script.
188  */
189 static rpmRC runExtScript(rpmts ts, ARGV_const_t prefixes,
190                    const char *sname, rpmlogLvl lvl,
191                    ARGV_t * argvp, const char *script, int arg1, int arg2)
192 {
193     FD_t scriptFd;
194     FD_t out = NULL;
195     char * fn = NULL;
196     int xx;
197     rpmRC rc = RPMRC_FAIL;
198     struct rpmsqElem sq;
199
200     memset(&sq, 0, sizeof(sq));
201     sq.reaper = 1;
202
203     rpmlog(RPMLOG_DEBUG, "%s: scriptlet start\n", sname);
204
205     if (script) {
206         const char * rootDir = rpmtsRootDir(ts);
207         FD_t fd;
208
209         fd = rpmMkTempFile((!rpmtsChrootDone(ts) ? rootDir : "/"), &fn);
210         if (fd == NULL || Ferror(fd)) {
211             rpmlog(RPMLOG_ERR, _("Couldn't create temporary file for %s: %s\n"),
212                    sname, strerror(errno));
213             goto exit;
214         }
215
216         if (rpmIsDebug() &&
217             (rstreq(*argvp[0], "/bin/sh") || rstreq(*argvp[0], "/bin/bash")))
218         {
219             static const char set_x[] = "set -x\n";
220             xx = Fwrite(set_x, sizeof(set_x[0]), sizeof(set_x)-1, fd);
221         }
222
223         xx = Fwrite(script, sizeof(script[0]), strlen(script), fd);
224         xx = Fclose(fd);
225
226         {   const char * sn = fn;
227             if (!rpmtsChrootDone(ts) && rootDir != NULL &&
228                 !(rootDir[0] == '/' && rootDir[1] == '\0'))
229             {
230                 sn += strlen(rootDir)-1;
231             }
232             argvAdd(argvp, sn);
233         }
234
235         if (arg1 >= 0) {
236             argvAddNum(argvp, arg1);
237         }
238         if (arg2 >= 0) {
239             argvAddNum(argvp, arg2);
240         }
241     }
242
243     scriptFd = rpmtsScriptFd(ts);
244     if (scriptFd != NULL) {
245         if (rpmIsVerbose()) {
246             out = fdDup(Fileno(scriptFd));
247         } else {
248             out = Fopen("/dev/null", "w.fdio");
249             if (Ferror(out)) {
250                 out = fdDup(Fileno(scriptFd));
251             }
252         }
253     } else {
254         out = fdDup(STDOUT_FILENO);
255     }
256     if (out == NULL) { 
257         rpmlog(RPMLOG_ERR, _("Couldn't duplicate file descriptor: %s: %s\n"),
258                sname, strerror(errno));
259         goto exit;
260     }
261
262     xx = rpmsqFork(&sq);
263     if (sq.child == 0) {
264         rpmlog(RPMLOG_DEBUG, "%s: execv(%s) pid %d\n",
265                sname, *argvp[0], (unsigned)getpid());
266         doScriptExec(ts, *argvp, prefixes, scriptFd, out);
267     }
268
269     if (sq.child == (pid_t)-1) {
270         rpmlog(RPMLOG_ERR, _("Couldn't fork %s: %s\n"), sname, strerror(errno));
271         goto exit;
272     }
273
274     rpmsqWait(&sq);
275
276     rpmlog(RPMLOG_DEBUG, "%s: waitpid(%d) rc %d status %x\n",
277            sname, (unsigned)sq.child, (unsigned)sq.reaped, sq.status);
278
279     if (sq.reaped < 0) {
280         rpmlog(lvl, _("%s scriptlet failed, waitpid(%d) rc %d: %s\n"),
281                  sname, sq.child, sq.reaped, strerror(errno));
282     } else if (!WIFEXITED(sq.status) || WEXITSTATUS(sq.status)) {
283         if (WIFSIGNALED(sq.status)) {
284             rpmlog(lvl, _("%s scriptlet failed, signal %d\n"),
285                    sname, WTERMSIG(sq.status));
286         } else {
287             rpmlog(lvl, _("%s scriptlet failed, exit status %d\n"),
288                    sname, WEXITSTATUS(sq.status));
289         }
290     } else {
291         /* if we get this far we're clear */
292         rc = RPMRC_OK;
293     }
294
295 exit:
296     if (out)
297         xx = Fclose(out);       /* XXX dup'd STDOUT_FILENO */
298
299     if (script) {
300         if (!rpmIsDebug())
301             xx = unlink(fn);
302         fn = _free(fn);
303     }
304     return rc;
305 }
306
307 rpmRC rpmScriptRun(rpmScript script, int arg1, int arg2,
308                    rpmts ts, ARGV_const_t prefixes, int warn_only)
309 {
310     ARGV_t args = NULL;
311     rpmlogLvl lvl = warn_only ? RPMLOG_WARNING : RPMLOG_ERR;
312     rpmRC rc;
313
314     if (script == NULL) return RPMRC_OK;
315
316     /* construct a new argv as we can't modify the one from header */
317     if (script->args) {
318         argvAppend(&args, script->args);
319     } else {
320         argvAdd(&args, "/bin/sh");
321     }
322
323     rpmswEnter(rpmtsOp(ts, RPMTS_OP_SCRIPTLETS), 0);
324     if (rstreq(args[0], "<lua>")) {
325         rc = runLuaScript(ts, prefixes, script->descr, lvl, &args, script->body, arg1, arg2);
326     } else {
327         rc = runExtScript(ts, prefixes, script->descr, lvl, &args, script->body, arg1, arg2);
328     }
329     rpmswExit(rpmtsOp(ts, RPMTS_OP_SCRIPTLETS), 0);
330     argvFree(args);
331
332     return rc;
333 }
334
335 static rpmTag getProgTag(rpmTag scriptTag)
336 {
337     switch (scriptTag) {
338     case RPMTAG_PREIN:          return RPMTAG_PREINPROG;
339     case RPMTAG_POSTIN:         return RPMTAG_POSTINPROG;
340     case RPMTAG_PREUN:          return RPMTAG_PREUNPROG;
341     case RPMTAG_POSTUN:         return RPMTAG_POSTUNPROG;
342     case RPMTAG_PRETRANS:       return RPMTAG_PRETRANSPROG;
343     case RPMTAG_POSTTRANS:      return RPMTAG_POSTTRANSPROG;
344     case RPMTAG_VERIFYSCRIPT:   return RPMTAG_VERIFYSCRIPTPROG;
345     default:                    return RPMTAG_NOT_FOUND;
346     }
347 }
348
349 static const char * tag2sln(rpmTag tag)
350 {
351     switch ((rpm_tag_t) tag) {
352     case RPMTAG_PRETRANS:       return "%pretrans";
353     case RPMTAG_TRIGGERPREIN:   return "%triggerprein";
354     case RPMTAG_PREIN:          return "%pre";
355     case RPMTAG_POSTIN:         return "%post";
356     case RPMTAG_TRIGGERIN:      return "%triggerin";
357     case RPMTAG_TRIGGERUN:      return "%triggerun";
358     case RPMTAG_PREUN:          return "%preun";
359     case RPMTAG_POSTUN:         return "%postun";
360     case RPMTAG_POSTTRANS:      return "%posttrans";
361     case RPMTAG_TRIGGERPOSTUN:  return "%triggerpostun";
362     case RPMTAG_VERIFYSCRIPT:   return "%verify";
363     default: break;
364     }
365     return "%unknownscript";
366 }
367
368 rpmScript rpmScriptFromTag(Header h, rpmTag scriptTag)
369 {
370     rpmScript script = NULL;
371     rpmTag progTag = getProgTag(scriptTag);
372
373     if (headerIsEntry(h, scriptTag) || headerIsEntry(h, progTag)) {
374         struct rpmtd_s prog;
375         char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
376
377         script = xcalloc(1, sizeof(*script));
378         script->tag = scriptTag;
379         script->body = headerGetAsString(h, scriptTag);
380         rasprintf(&script->descr, "%s(%s)", tag2sln(scriptTag), nevra);
381
382         if (headerGet(h, progTag, &prog, (HEADERGET_ALLOC|HEADERGET_ARGV))) {
383             script->args = prog.data;
384         }
385         free(nevra);
386     }
387     return script;
388 }
389
390 rpmScript rpmScriptFree(rpmScript script)
391 {
392     if (script) {
393         free(script->args);
394         free(script->body);
395         free(script->descr);
396         free(script);
397     }
398     return NULL;
399 }