Imported Upstream version 4.14.1
[platform/upstream/rpm.git] / lib / rpmscript.c
1 #include "system.h"
2
3 #include <sys/types.h>
4 #include <sys/wait.h>
5 #include <errno.h>
6 #include <unistd.h>
7
8 #include <rpm/rpmfileutil.h>
9 #include <rpm/rpmmacro.h>
10 #include <rpm/rpmio.h>
11 #include <rpm/rpmlog.h>
12 #include <rpm/header.h>
13 #include <rpm/rpmds.h>
14
15 #include "rpmio/rpmlua.h"
16 #include "lib/rpmscript.h"
17
18 #include "lib/rpmplugins.h"     /* rpm plugins hooks */
19
20 #include "debug.h"
21
22 struct scriptNextFileFunc_s {
23     char *(*func)(void *);      /* function producing input for script */
24     void *param;                /* parameter for func */
25 };
26
27 typedef struct scriptNextFileFunc_s *scriptNextFileFunc;
28
29 struct rpmScript_s {
30     rpmscriptTypes type;        /* script type */
31     rpmTagVal tag;              /* script tag */
32     char **args;                /* scriptlet call arguments */
33     char *body;                 /* script body */
34     char *descr;                /* description for logging */
35     rpmscriptFlags flags;       /* flags to control operation */
36     struct scriptNextFileFunc_s nextFileFunc;  /* input function */
37 };
38
39 struct scriptInfo_s {
40     rpmscriptTypes type;
41     const char *desc;
42     rpmsenseFlags sense;
43     rpmTagVal tag;
44     rpmTagVal progtag;
45     rpmTagVal flagtag;
46 };
47
48 static const struct scriptInfo_s scriptInfo[] = {
49     { RPMSCRIPT_PREIN, "%prein", 0,
50         RPMTAG_PREIN, RPMTAG_PREINPROG, RPMTAG_PREINFLAGS },
51     { RPMSCRIPT_PREUN, "%preun", 0,
52         RPMTAG_PREUN, RPMTAG_PREUNPROG, RPMTAG_PREUNFLAGS },
53     { RPMSCRIPT_POSTIN, "%post", 0,
54         RPMTAG_POSTIN, RPMTAG_POSTINPROG, RPMTAG_POSTINFLAGS },
55     { RPMSCRIPT_POSTUN, "%postun", 0,
56         RPMTAG_POSTUN, RPMTAG_POSTUNPROG, RPMTAG_POSTUNFLAGS },
57     { RPMSCRIPT_PRETRANS, "%pretrans", 0,
58         RPMTAG_PRETRANS, RPMTAG_PRETRANSPROG, RPMTAG_PRETRANSFLAGS },
59     { RPMSCRIPT_POSTTRANS, "%posttrans", 0,
60         RPMTAG_POSTTRANS, RPMTAG_POSTTRANSPROG, RPMTAG_POSTTRANSFLAGS },
61     { RPMSCRIPT_TRIGGERPREIN, "%triggerprein", RPMSENSE_TRIGGERPREIN,
62         RPMTAG_TRIGGERPREIN, 0, 0 },
63     { RPMSCRIPT_TRIGGERUN, "%triggerun", RPMSENSE_TRIGGERUN,
64         RPMTAG_TRIGGERUN, 0, 0 },
65     { RPMSCRIPT_TRIGGERIN, "%triggerin", RPMSENSE_TRIGGERIN,
66         RPMTAG_TRIGGERIN, 0, 0 },
67     { RPMSCRIPT_TRIGGERPOSTUN, "%triggerpostun", RPMSENSE_TRIGGERPOSTUN,
68         RPMTAG_TRIGGERPOSTUN, 0, 0 },
69     { RPMSCRIPT_VERIFY, "%verify", 0,
70         RPMTAG_VERIFYSCRIPT, RPMTAG_VERIFYSCRIPTPROG, RPMTAG_VERIFYSCRIPTFLAGS},
71     { 0, "unknown", 0,
72         RPMTAG_NOT_FOUND, RPMTAG_NOT_FOUND, RPMTAG_NOT_FOUND }
73 };
74
75 static const struct scriptInfo_s * findTag(rpmTagVal tag)
76 {
77     const struct scriptInfo_s * si = scriptInfo;
78     while (si->type && si->tag != tag)
79         si++;
80     return si;
81 }
82 /**
83  * Run internal Lua script.
84  */
85 static rpmRC runLuaScript(rpmPlugins plugins, ARGV_const_t prefixes,
86                    const char *sname, rpmlogLvl lvl, FD_t scriptFd,
87                    ARGV_t * argvp, const char *script, int arg1, int arg2,
88                    scriptNextFileFunc nextFileFunc)
89 {
90     rpmRC rc = RPMRC_FAIL;
91 #ifdef WITH_LUA
92     ARGV_t argv = argvp ? *argvp : NULL;
93     rpmlua lua = NULL; /* Global state. */
94     rpmluav var = rpmluavNew();
95     int cwd = -1;
96
97     rpmlog(RPMLOG_DEBUG, "%s: running <lua> scriptlet.\n", sname);
98
99     /* Create arg variable */
100     rpmluaPushTable(lua, "arg");
101     rpmluavSetListMode(var, 1);
102     rpmluaSetNextFileFunc(nextFileFunc->func, nextFileFunc->param);
103     if (argv) {
104         char **p;
105         for (p = argv; *p; p++) {
106             rpmluavSetValue(var, RPMLUAV_STRING, *p);
107             rpmluaSetVar(lua, var);
108         }
109     }
110     if (arg1 >= 0) {
111         rpmluavSetValueNum(var, arg1);
112         rpmluaSetVar(lua, var);
113     }
114     if (arg2 >= 0) {
115         rpmluavSetValueNum(var, arg2);
116         rpmluaSetVar(lua, var);
117     }
118     rpmluaPop(lua);
119
120     /* Lua scripts can change our cwd and umask, save and restore */
121     /* XXX TODO: use cwd from chroot state to save unnecessary open here */
122     cwd = open(".", O_RDONLY);
123     if (cwd != -1) {
124         mode_t oldmask = umask(0);
125         umask(oldmask);
126         pid_t pid = getpid();
127
128         if (chdir("/") == 0 && rpmluaRunScript(lua, script, sname) == 0) {
129             rc = RPMRC_OK;
130         }
131         if (pid != getpid()) {
132             /* Terminate child process forked in lua scriptlet */
133             rpmlog(RPMLOG_ERR, _("No exec() called after fork() in lua scriptlet\n"));
134             _exit(EXIT_FAILURE);
135         }
136         /* This failing would be fatal, return something different for it... */
137         if (fchdir(cwd)) {
138             rpmlog(RPMLOG_ERR, _("Unable to restore current directory: %m"));
139             rc = RPMRC_NOTFOUND;
140         }
141         close(cwd);
142         umask(oldmask);
143     }
144
145     rpmluaDelVar(lua, "arg");
146     rpmluavFree(var);
147
148 #else
149     rpmlog(lvl, _("<lua> scriptlet support not built in\n"));
150 #endif
151
152     return rc;
153 }
154
155 static const char * const SCRIPT_PATH = "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin";
156
157 static void doScriptExec(ARGV_const_t argv, ARGV_const_t prefixes,
158                         FD_t scriptFd, FD_t out)
159 {
160     int flag;
161     int fdno;
162     int xx;
163     int open_max;
164
165     /* SIGPIPE is ignored in rpm, reset to default for the scriptlet */
166     (void) signal(SIGPIPE, SIG_DFL);
167
168     /* XXX Force FD_CLOEXEC on all inherited fdno's. */
169     open_max = sysconf(_SC_OPEN_MAX);
170     if (open_max == -1) {
171         open_max = 1024;
172     }
173     for (fdno = 3; fdno < open_max; fdno++) {
174         flag = fcntl(fdno, F_GETFD);
175         if (flag == -1 || (flag & FD_CLOEXEC))
176             continue;
177         xx = fcntl(fdno, F_SETFD, FD_CLOEXEC);
178         /* XXX W2DO? debug msg for inheirited fdno w/o FD_CLOEXEC */
179     }
180
181     if (scriptFd != NULL) {
182         int sfdno = Fileno(scriptFd);
183         int ofdno = Fileno(out);
184         if (sfdno != STDERR_FILENO)
185             xx = dup2(sfdno, STDERR_FILENO);
186         if (ofdno != STDOUT_FILENO)
187             xx = dup2(ofdno, STDOUT_FILENO);
188         /* make sure we don't close stdin/stderr/stdout by mistake! */
189         if (ofdno > STDERR_FILENO && ofdno != sfdno)
190             xx = Fclose (out);
191         if (sfdno > STDERR_FILENO && ofdno != sfdno)
192             xx = Fclose (scriptFd);
193     }
194
195     {   char *ipath = rpmExpand("%{_install_script_path}", NULL);
196         const char *path = SCRIPT_PATH;
197
198         if (ipath && ipath[5] != '%')
199             path = ipath;
200
201         xx = setenv("PATH", path, 1);
202         free(ipath);
203     }
204
205     for (ARGV_const_t pf = prefixes; pf && *pf; pf++) {
206         char *name = NULL;
207         int num = (pf - prefixes);
208
209         rasprintf(&name, "RPM_INSTALL_PREFIX%d", num);
210         setenv(name, *pf, 1);
211         free(name);
212
213         /* scripts might still be using the old style prefix */
214         if (num == 0) {
215             setenv("RPM_INSTALL_PREFIX", *pf, 1);
216         }
217     }
218         
219     if (chdir("/") == 0) {
220         /* XXX Don't mtrace into children. */
221         unsetenv("MALLOC_CHECK_");
222
223         if (xx == 0) {
224             xx = execv(argv[0], argv);
225         }
226     }
227     _exit(127); /* exit 127 for compatibility with bash(1) */
228 }
229
230 static char * writeScript(const char *cmd, const char *script)
231 {
232     char *fn = NULL;
233     size_t slen = strlen(script);
234     int ok = 0;
235     FD_t fd = rpmMkTempFile("/", &fn);
236
237     if (Ferror(fd))
238         goto exit;
239
240     if (rpmIsDebug() && (rstreq(cmd, "/bin/sh") || rstreq(cmd, "/bin/bash"))) {
241         static const char set_x[] = "set -x\n";
242         /* Assume failures will be caught by the write below */
243         Fwrite(set_x, sizeof(set_x[0]), sizeof(set_x)-1, fd);
244     }
245
246     ok = (Fwrite(script, sizeof(script[0]), slen, fd) == slen);
247
248 exit:
249     if (!ok) fn = _free(fn);
250     Fclose(fd);
251     return fn;
252 }
253
254 /**
255  * Run an external script.
256  */
257 static rpmRC runExtScript(rpmPlugins plugins, ARGV_const_t prefixes,
258                    const char *sname, rpmlogLvl lvl, FD_t scriptFd,
259                    ARGV_t * argvp, const char *script, int arg1, int arg2,
260                    scriptNextFileFunc nextFileFunc)
261 {
262     FD_t out = NULL;
263     char * fn = NULL;
264     pid_t pid, reaped;
265     int status;
266     int inpipe[2];
267     FILE *in = NULL;
268     const char *line;
269     char *mline = NULL;
270     rpmRC rc = RPMRC_FAIL;
271
272     rpmlog(RPMLOG_DEBUG, "%s: scriptlet start\n", sname);
273
274     if (script) {
275         fn = writeScript(*argvp[0], script);
276         if (fn == NULL) {
277             rpmlog(RPMLOG_ERR,
278                    _("Couldn't create temporary file for %s: %s\n"),
279                    sname, strerror(errno));
280             goto exit;
281         }
282
283         argvAdd(argvp, fn);
284         if (arg1 >= 0) {
285             argvAddNum(argvp, arg1);
286         }
287         if (arg2 >= 0) {
288             argvAddNum(argvp, arg2);
289         }
290     }
291
292     if (pipe(inpipe) < 0) {
293         rpmlog(RPMLOG_ERR,
294                 ("Couldn't create pipe: %s\n"), strerror(errno));
295         goto exit;
296     }
297     in = fdopen(inpipe[1], "w");
298     inpipe[1] = 0;
299
300     if (scriptFd != NULL) {
301         if (rpmIsVerbose()) {
302             out = fdDup(Fileno(scriptFd));
303         } else {
304             out = Fopen("/dev/null", "w.fdio");
305             if (Ferror(out)) {
306                 out = fdDup(Fileno(scriptFd));
307             }
308         }
309     } else {
310         out = fdDup(STDOUT_FILENO);
311     }
312     if (out == NULL) { 
313         rpmlog(RPMLOG_ERR, _("Couldn't duplicate file descriptor: %s: %s\n"),
314                sname, strerror(errno));
315         goto exit;
316     }
317
318     pid = fork();
319     if (pid == (pid_t) -1) {
320         rpmlog(RPMLOG_ERR, _("Couldn't fork %s: %s\n"),
321                 sname, strerror(errno));
322         goto exit;
323     } else if (pid == 0) {/* Child */
324         rpmlog(RPMLOG_DEBUG, "%s: execv(%s) pid %d\n",
325                sname, *argvp[0], (unsigned)getpid());
326
327         fclose(in);
328         dup2(inpipe[0], STDIN_FILENO);
329
330         /* Run scriptlet post fork hook for all plugins */
331         if (rpmpluginsCallScriptletForkPost(plugins, *argvp[0], RPMSCRIPTLET_FORK | RPMSCRIPTLET_EXEC) != RPMRC_FAIL) {
332             doScriptExec(*argvp, prefixes, scriptFd, out);
333         } else {
334             _exit(126); /* exit 126 for compatibility with bash(1) */
335         }
336     }
337     close(inpipe[0]);
338     inpipe[0] = 0;
339
340     if (nextFileFunc->func) {
341         while ((line = nextFileFunc->func(nextFileFunc->param)) != NULL) {
342             size_t size = strlen(line);
343             size_t ret_size;
344             mline = xstrdup(line);
345             mline[size] = '\n';
346
347             ret_size = fwrite(mline, size + 1, 1, in);
348             mline = _free(mline);
349             if (ret_size != 1) {
350                 if (errno == EPIPE) {
351                     break;
352                 } else {
353                     rpmlog(RPMLOG_ERR, _("Fwrite failed: %s"), strerror(errno));
354                     rc = RPMRC_FAIL;
355                     goto exit;
356                 }
357             }
358         }
359     }
360     fclose(in);
361     in = NULL;
362
363     do {
364         reaped = waitpid(pid, &status, 0);
365     } while (reaped == -1 && errno == EINTR);
366
367     rpmlog(RPMLOG_DEBUG, "%s: waitpid(%d) rc %d status %x\n",
368            sname, (unsigned)pid, (unsigned)reaped, status);
369
370     if (reaped < 0) {
371         rpmlog(lvl, _("%s scriptlet failed, waitpid(%d) rc %d: %s\n"),
372                  sname, pid, reaped, strerror(errno));
373     } else if (!WIFEXITED(status) || WEXITSTATUS(status)) {
374         if (WIFSIGNALED(status)) {
375             rpmlog(lvl, _("%s scriptlet failed, signal %d\n"),
376                    sname, WTERMSIG(status));
377         } else {
378             rpmlog(lvl, _("%s scriptlet failed, exit status %d\n"),
379                    sname, WEXITSTATUS(status));
380         }
381     } else {
382         /* if we get this far we're clear */
383         rc = RPMRC_OK;
384     }
385
386 exit:
387     if (in)
388         fclose(in);
389
390     if (inpipe[0])
391         close(inpipe[0]);
392
393     if (out)
394         Fclose(out);    /* XXX dup'd STDOUT_FILENO */
395
396     if (fn) {
397         if (!rpmIsDebug())
398             unlink(fn);
399         free(fn);
400     }
401     free(mline);
402
403     return rc;
404 }
405
406 rpmRC rpmScriptRun(rpmScript script, int arg1, int arg2, FD_t scriptFd,
407                    ARGV_const_t prefixes, int warn_only, rpmPlugins plugins)
408 {
409     ARGV_t args = NULL;
410     rpmlogLvl lvl = warn_only ? RPMLOG_WARNING : RPMLOG_ERR;
411     rpmRC rc;
412     int script_type = RPMSCRIPTLET_FORK | RPMSCRIPTLET_EXEC;
413
414     if (script == NULL) return RPMRC_OK;
415
416     /* construct a new argv as we can't modify the one from header */
417     if (script->args) {
418         argvAppend(&args, script->args);
419     } else {
420         argvAdd(&args, "/bin/sh");
421     }
422     
423     if (rstreq(args[0], "<lua>"))
424         script_type = RPMSCRIPTLET_NONE;
425
426     /* Run scriptlet pre hook for all plugins */
427     rc = rpmpluginsCallScriptletPre(plugins, script->descr, script_type);
428
429     if (rc != RPMRC_FAIL) {
430         if (script_type & RPMSCRIPTLET_EXEC) {
431             rc = runExtScript(plugins, prefixes, script->descr, lvl, scriptFd, &args, script->body, arg1, arg2, &script->nextFileFunc);
432         } else {
433             rc = runLuaScript(plugins, prefixes, script->descr, lvl, scriptFd, &args, script->body, arg1, arg2, &script->nextFileFunc);
434         }
435     }
436
437     /* Run scriptlet post hook for all plugins */
438     rpmpluginsCallScriptletPost(plugins, script->descr, script_type, rc);
439
440     argvFree(args);
441
442     return rc;
443 }
444
445 static rpmscriptTypes getScriptType(rpmTagVal scriptTag)
446 {
447     return findTag(scriptTag)->type;
448 }
449
450 static rpmTagVal getProgTag(rpmTagVal scriptTag)
451 {
452     return findTag(scriptTag)->progtag;
453 }
454
455 static rpmTagVal getFlagTag(rpmTagVal scriptTag)
456 {
457     return findTag(scriptTag)->flagtag;
458 }
459
460 static const char * tag2sln(rpmTagVal tag)
461 {
462     return findTag(tag)->desc;
463 }
464
465 static rpmScript rpmScriptNew(Header h, rpmTagVal tag, const char *body,
466                               rpmscriptFlags flags)
467 {
468     char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
469     rpmScript script = xcalloc(1, sizeof(*script));
470     script->tag = tag;
471     script->type = getScriptType(tag);
472     script->flags = flags;
473     script->body = (body != NULL) ? xstrdup(body) : NULL;
474     rasprintf(&script->descr, "%s(%s)", tag2sln(tag), nevra);
475
476     /* macros need to be expanded before possible queryformat */
477     if (script->body && (script->flags & RPMSCRIPT_FLAG_EXPAND)) {
478         char *body = rpmExpand(script->body, NULL);
479         free(script->body);
480         script->body = body;
481     }
482     if (script->body && (script->flags & RPMSCRIPT_FLAG_QFORMAT)) {
483         /* XXX TODO: handle queryformat errors */
484         char *body = headerFormat(h, script->body, NULL);
485         free(script->body);
486         script->body = body;
487     }
488
489     script->nextFileFunc.func = NULL;
490     script->nextFileFunc.param = NULL;
491
492     free(nevra);
493     return script;
494 }
495
496 void rpmScriptSetNextFileFunc(rpmScript script, char *(*func)(void *),
497                             void *param)
498 {
499     script->nextFileFunc.func = func;
500     script->nextFileFunc.param = param;
501 }
502
503 rpmTagVal triggerDsTag(rpmscriptTriggerModes tm)
504 {
505     rpmTagVal tag = RPMTAG_NOT_FOUND;
506     switch (tm) {
507     case RPMSCRIPT_NORMALTRIGGER:
508         tag = RPMTAG_TRIGGERNAME;
509         break;
510     case RPMSCRIPT_FILETRIGGER:
511         tag = RPMTAG_FILETRIGGERNAME;
512         break;
513     case RPMSCRIPT_TRANSFILETRIGGER:
514         tag = RPMTAG_TRANSFILETRIGGERNAME;
515         break;
516     }
517     return tag;
518 }
519
520 rpmscriptTriggerModes triggerMode(rpmTagVal tag)
521 {
522     rpmscriptTriggerModes tm = 0;
523     switch (tag) {
524     case RPMTAG_TRIGGERNAME:
525         tm = RPMSCRIPT_NORMALTRIGGER;
526         break;
527     case RPMTAG_FILETRIGGERNAME:
528         tm = RPMSCRIPT_FILETRIGGER;
529         break;
530     case RPMTAG_TRANSFILETRIGGERNAME:
531         tm = RPMSCRIPT_TRANSFILETRIGGER;
532         break;
533     }
534     return tm;
535 }
536
537 rpmTagVal triggertag(rpmsenseFlags sense)
538 {
539     rpmTagVal tag = RPMTAG_NOT_FOUND;
540     switch (sense) {
541     case RPMSENSE_TRIGGERIN:
542         tag = RPMTAG_TRIGGERIN;
543         break;
544     case RPMSENSE_TRIGGERUN:
545         tag = RPMTAG_TRIGGERUN;
546         break;
547     case RPMSENSE_TRIGGERPOSTUN:
548         tag = RPMTAG_TRIGGERPOSTUN;
549         break;
550     case RPMSENSE_TRIGGERPREIN:
551         tag = RPMTAG_TRIGGERPREIN;
552         break;
553     default:
554         break;
555     }
556     return tag;
557 }
558
559 rpmScript rpmScriptFromTriggerTag(Header h, rpmTagVal triggerTag,
560                             rpmscriptTriggerModes tm, uint32_t ix)
561 {
562     rpmScript script = NULL;
563     struct rpmtd_s tscripts, tprogs, tflags;
564     headerGetFlags hgflags = HEADERGET_MINMEM;
565
566     switch (tm) {
567         case RPMSCRIPT_NORMALTRIGGER:
568             headerGet(h, RPMTAG_TRIGGERSCRIPTS, &tscripts, hgflags);
569             headerGet(h, RPMTAG_TRIGGERSCRIPTPROG, &tprogs, hgflags);
570             headerGet(h, RPMTAG_TRIGGERSCRIPTFLAGS, &tflags, hgflags);
571             break;
572         case RPMSCRIPT_FILETRIGGER:
573             headerGet(h, RPMTAG_FILETRIGGERSCRIPTS, &tscripts, hgflags);
574             headerGet(h, RPMTAG_FILETRIGGERSCRIPTPROG, &tprogs, hgflags);
575             headerGet(h, RPMTAG_FILETRIGGERSCRIPTFLAGS, &tflags, hgflags);
576             break;
577         case RPMSCRIPT_TRANSFILETRIGGER:
578             headerGet(h, RPMTAG_TRANSFILETRIGGERSCRIPTS, &tscripts, hgflags);
579             headerGet(h, RPMTAG_TRANSFILETRIGGERSCRIPTPROG, &tprogs, hgflags);
580             headerGet(h, RPMTAG_TRANSFILETRIGGERSCRIPTFLAGS, &tflags, hgflags);
581             break;
582     }
583
584     if (rpmtdSetIndex(&tscripts, ix) >= 0 && rpmtdSetIndex(&tprogs, ix) >= 0) {
585         rpmscriptFlags sflags = 0;
586         const char *prog = rpmtdGetString(&tprogs);
587
588         if (rpmtdSetIndex(&tflags, ix) >= 0)
589             sflags = rpmtdGetNumber(&tflags);
590
591         script = rpmScriptNew(h, triggerTag, rpmtdGetString(&tscripts), sflags);
592
593         /* hack up a hge-style NULL-terminated array */
594         script->args = xmalloc(2 * sizeof(*script->args) + strlen(prog) + 1);
595         script->args[0] = (char *)(script->args + 2);
596         script->args[1] = NULL;
597         strcpy(script->args[0], prog);
598     }
599
600     rpmtdFreeData(&tscripts);
601     rpmtdFreeData(&tprogs);
602     rpmtdFreeData(&tflags);
603
604     return script;
605 }
606
607 rpmScript rpmScriptFromTag(Header h, rpmTagVal scriptTag)
608 {
609     rpmScript script = NULL;
610     rpmTagVal progTag = getProgTag(scriptTag);
611
612     if (headerIsEntry(h, scriptTag) || headerIsEntry(h, progTag)) {
613         struct rpmtd_s prog;
614
615         script = rpmScriptNew(h, scriptTag,
616                               headerGetString(h, scriptTag),
617                               headerGetNumber(h, getFlagTag(scriptTag)));
618
619         if (headerGet(h, progTag, &prog, (HEADERGET_ALLOC|HEADERGET_ARGV))) {
620             script->args = prog.data;
621         }
622     }
623     return script;
624 }
625
626 rpmScript rpmScriptFree(rpmScript script)
627 {
628     if (script) {
629         free(script->args);
630         free(script->body);
631         free(script->descr);
632         free(script);
633     }
634     return NULL;
635 }
636
637 rpmTagVal rpmScriptTag(rpmScript script)
638 {
639     return (script != NULL) ? script->tag : RPMTAG_NOT_FOUND;
640 }
641
642 rpmscriptTypes rpmScriptType(rpmScript script)
643 {
644     return (script != NULL) ? script->type : 0;
645 }