rpm  5.4.15
filetriggers.c
Go to the documentation of this file.
1 #include "system.h"
2 
3 #if defined(RPM_VENDOR_MANDRIVA)
4 #include <rpmio_internal.h> /* XXX for fdGetFILE() */
5 #include <argv.h>
6 #include <rpmlog.h>
7 #define _MIRE_INTERNAL
8 #include <mire.h>
9 #include <rpmmacro.h> /* XXX for rpmExpand */
10 #include <rpmtag.h>
11 #include <rpmtypes.h>
12 #include <rpmfi.h>
13 
14 #include "filetriggers.h" /* mayAddToFilesAwaitingFiletriggers rpmRunFileTriggers */
15 
16 #include "debug.h"
17 
18 #define _FILES_AWAITING_FILETRIGGERS "/var/lib/rpm/files-awaiting-filetriggers"
19 
20 /*@unchecked@*/ /*@observer@*/
21 static const char * files_awaiting_filetriggers = _FILES_AWAITING_FILETRIGGERS;
22 
23 #define FILTER_EXTENSION ".filter"
24 
25 /*@unchecked@*/ /*@shared@*/ /*@null@*/
26 static const char * _filetriggers_dir;
27 
28 /*@null@*/ /*@observer@*/
29 static const char * filetriggers_dir(void)
30  /*@globals _filetriggers_dir, rpmGlobalMacroContext, h_errno, internalState @*/
31  /*@modifies _filetriggers_dir, rpmGlobalMacroContext, internalState @*/
32 {
33  if (_filetriggers_dir == NULL)
34  _filetriggers_dir = rpmExpand("%{?_filetriggers_dir}", NULL);
35  return (_filetriggers_dir != NULL && _filetriggers_dir[0] != '\0')
36  ? _filetriggers_dir : NULL;
37 }
38 
39 /*@only@*/ /*@null@*/
40 static char * get_filter_name(/*@returned@*/ const char * fn)
41  /*@*/
42 {
43  char * p = strrchr(fn, '/');
44  if (p == NULL)
45  return NULL;
46 #if defined(HAVE_STRNDUP) && !defined(__LCLINT__)
47  p = strndup(p+1, strlen(p+1) - strlen(FILTER_EXTENSION));
48 #else
49  { char * p_src = p;
50  size_t p_len = strlen(p_src+1) - strlen(FILTER_EXTENSION);
51  p = (char *) xmalloc(p_len+1);
52  strncpy(p, p_src+1, p_len);
53  }
54 #endif
55  return p;
56 }
57 
58 int mayAddToFilesAwaitingFiletriggers(const char * rootDir, rpmfi fi,
59  int install_or_erase)
60 {
61  const char * fn;
62  FILE * fp;
63  int rc = RPMRC_FAIL;
64  int xx;
65 
66  if (filetriggers_dir() == NULL)
67  return RPMRC_OK;
68 
69  fn = rpmGetPath(rootDir ? rootDir : "/", files_awaiting_filetriggers, NULL);
70 
71  fp = fopen(fn, "a");
72  if (fp == NULL) {
73  rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), fn, strerror(errno));
74  goto exit;
75  }
76 
77  fi = rpmfiInit(fi, 0);
78  if (fi != NULL)
79  while (rpmfiNext(fi) >= 0) {
80  xx = fputc(install_or_erase ? '+' : '-', fp);
81  xx = fputs(rpmfiFN(fi), fp);
82  xx = fputc('\n', fp);
83  }
84  xx = fclose(fp);
85  rc = RPMRC_OK;
86 
87 exit:
88  fn = _free(fn);
89  return rc;
90 }
91 
92 struct filetrigger_raw {
93  char * regexp;
94 /*@relnull@*/
95  char * name;
96 };
97 
98 struct filetrigger {
99  miRE mire;
100  char * name;
101  int command_pipe;
102  pid_t command_pid;
103 };
104 
105 static int getFiletriggers_raw(const char * rootDir, int * nftp,
106  struct filetrigger_raw ** list_raw)
107  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
108  /*@modifies *nftp, *list_raw, rpmGlobalMacroContext, fileSystem, internalState @*/
109 {
110  const char * dn = filetriggers_dir();
111  const char * globstr = NULL;
112  ARGV_t av = NULL;
113  int ac = 0;
114  int xx;
115  int i;
116 
117  if (dn == NULL)
118  goto exit;
119 
120  globstr = rpmGetPath(rootDir ? rootDir : "/", dn, "/*" FILTER_EXTENSION, NULL);
121  xx = rpmGlob(globstr, &ac, &av);
122  if (ac == 0)
123  goto exit;
124 
125  *list_raw = (struct filetrigger_raw *) xcalloc(ac, sizeof(**list_raw));
126 
127  for (i = 0; i < ac; i++) {
128  const char * fn = av[i];
129  int fdno = open(fn, O_RDONLY);
130  struct stat sb;
131 
132  if (fdno == -1) {
133  rpmlog(RPMLOG_ERR, "opening %s failed: %s\n", fn, strerror(errno));
134  continue;
135  }
136 
137  if (fstat(fdno, &sb) == 0 && sb.st_size > 0) {
138  size_t bn = sb.st_size;
139  char * b = (char *) xmalloc(bn + 1);
140 
141  if (read(fdno, b, bn) != (ssize_t)bn) {
142  rpmlog(RPMLOG_ERR, "reading %s failed: %s\n", fn,
143  strerror(errno));
144  b = _free(b);
145  } else {
146  char * be = b + bn;
147  *be = '\0';
148  if ((be = strchr(b, '\n')) != NULL) {
149  *be = '\0';
150  while (be > b && xisspace((int)be[-1]))
151  *(--be) = '\0';
152  }
153  (*list_raw)[i].regexp = b;
154  (*list_raw)[i].name = get_filter_name(fn);
155  }
156  }
157  xx = close(fdno);
158  }
159 
160 exit:
161  if (nftp != NULL)
162  *nftp = ac;
163  av = argvFree(av);
164  globstr = _free(globstr);
165  return RPMRC_OK;
166 }
167 
168 static char * computeMatchesAnyFilter(size_t nb,
169  struct filetrigger_raw * list_raw)
170  /*@*/
171 {
172  char * matches_any;
173  char * p;
174  size_t regexp_str_size = 0;
175  int i;
176 
177  for (i = 0; i < (int)nb; i++)
178  regexp_str_size += strlen(list_raw[i].regexp) + 1;
179 
180  matches_any = (char *) xmalloc(regexp_str_size);
181  p = stpcpy(matches_any, list_raw[0].regexp);
182 
183  for (i = 1; i < (int)nb; i++) {
184  *p++ = '|';
185  p = stpcpy(p, list_raw[i].regexp);
186  }
187  rpmlog(RPMLOG_DEBUG, D_("[filetriggers] matches-any regexp is %s\n"),
188  matches_any);
189  return matches_any;
190 }
191 
192 static void compileFiletriggersRegexp(/*@only@*/ char * raw, miRE mire)
193  /*@modifies raw, mire @*/
194 {
195  static int options = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
196  int xx;
197 
198  xx = mireSetCOptions(mire, RPMMIRE_REGEX, 0, options, NULL);
199 
200  if (mireRegcomp(mire, raw) != 0) {
201  rpmlog(RPMLOG_ERR, "failed to compile filetrigger filter: %s\n", raw);
202  mire = mireFree(mire);
203  }
204  raw = _free(raw);
205 }
206 
207 static void getFiletriggers(const char * rootDir, miRE matches_any,
208  int * nftp, struct filetrigger ** list)
209  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
210  /*@modifies matches_any, *nftp, *list, rpmGlobalMacroContext, fileSystem, internalState @*/
211 {
212  struct filetrigger_raw * list_raw = NULL;
213  int xx;
214  int i;
215 
216  xx = getFiletriggers_raw(rootDir, nftp, &list_raw);
217  if (*nftp == 0) return;
218 
219  compileFiletriggersRegexp(computeMatchesAnyFilter(*nftp, list_raw), matches_any);
220 
221  *list = (struct filetrigger *) xcalloc(*nftp, sizeof(**list));
222  for (i = 0; i < *nftp; i++) {
223  (*list)[i].name = list_raw[i].name;
224  (*list)[i].mire = mireNew(0, 0);
225  compileFiletriggersRegexp(list_raw[i].regexp, (*list)[i].mire);
226  }
227  list_raw = _free(list_raw);
228 }
229 
230 static void freeFiletriggers(/*@only@*/ miRE matches_any,
231  int nft, /*@only@*/ struct filetrigger * list)
232  /*@modifies matches_any, list @*/
233 {
234  int i;
235 
236  matches_any = mireFree(matches_any);
237  for (i = 0; i < nft; i++) {
238  list[i].mire = mireFree(list[i].mire);
239  list[i].name = _free(list[i].name);
240  }
241  list = _free(list);
242 }
243 
244 static int is_regexp_matching(miRE mire, const char * s)
245  /*@modifies mire @*/
246 {
247  return mireRegexec(mire, s, (size_t) 0) == 0;
248 }
249 
250 static int popen_with_root(const char * rootDir, const char * cmd,
251  pid_t * pidp)
252  /*@globals fileSystem, internalState @*/
253  /*@modifies *pidp, fileSystem, internalState @*/
254 {
255  int pipes[2];
256  int xx;
257 
258  if (pipe(pipes) != 0)
259  return 0;
260 
261  *pidp = fork();
262  if (*pidp == 0) {
263  const char * argv[2];
264 
265  xx = close(pipes[1]);
266  xx = dup2(pipes[0], STDIN_FILENO);
267  xx = close(pipes[0]);
268 
269  if (rootDir != NULL && strcmp(rootDir, "/") && *rootDir == '/') {
270 /*@-superuser@*/
271  if (Chroot(rootDir) != 0) {
272  rpmlog(RPMLOG_ERR, "chroot to %s failed\n", rootDir);
273  _exit(-1);
274  }
275 /*@=superuser@*/
276  xx = chdir("/");
277  }
278  argv[0] = cmd;
279  argv[1] = NULL;
280  xx = execv(argv[0], (char *const *) argv);
281  _exit(-1);
282  }
283 
284  xx = close(pipes[0]);
285 
286  return pipes[1];
287 }
288 
289 static void mayStartFiletrigger(const char * rootDir,
290  struct filetrigger * trigger)
291  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
292  /*@modifies trigger, rpmGlobalMacroContext, fileSystem, internalState @*/
293 {
294  if (!trigger->command_pipe) {
295  const char * dn = filetriggers_dir();
296  const char * cmd;
297 
298  if (dn == NULL)
299  return;
300 
301  cmd = rpmGetPath(dn, "/", trigger->name, ".script", NULL);
302  rpmlog(RPMLOG_DEBUG, D_("[filetriggers] spawning %s\n"),
303  cmd);
304  trigger->command_pipe = popen_with_root(rootDir, cmd,
305  &trigger->command_pid);
306  cmd = _free(cmd);
307  }
308 }
309 
310 void rpmRunFileTriggers(const char * rootDir)
311 {
312  miRE matches_any = mireNew(RPMMIRE_DEFAULT, 0);
313  int nft = 0;
314  struct filetrigger *list = NULL;
315  const char * fn = NULL;
316  FD_t fd = NULL;
317  FILE * fp = NULL;
318  int xx;
319 
320  rpmlog(RPMLOG_DEBUG, D_("[filetriggers] starting\n"));
321 
322  fn = rpmGenPath(rootDir, files_awaiting_filetriggers, NULL);
323 
324  if (!filetriggers_dir())
325  goto exit;
326 
327  getFiletriggers(rootDir, matches_any, &nft, &list);
328  if (nft <= 0)
329  goto exit;
330 
331  fd = Fopen(fn, "r.fpio");
332  if (fd == NULL)
333  goto exit;
334 
335  fp = fdGetFILE(fd);
336 
337  if (fp != NULL) {
338  void (*oldhandler)(int) = signal(SIGPIPE, SIG_IGN);
339  char tmp[BUFSIZ];
340  int i;
341 
343  D_("[filetriggers] testing files from list: %s\n"), fn);
344 
345  while (fgets(tmp, (int)sizeof(tmp), fp)) {
346  size_t tmplen = strlen(tmp);
347 
348  if (tmplen > 0 && tmp[tmplen-1] == '\n')
349  tmp[--tmplen] = '\0';
350 
351  if (!is_regexp_matching(matches_any, tmp))
352  continue;
353 
355  D_("[filetriggers] matches-any regexp found %s\n"),
356  tmp);
357 
358  for (i = 0; i < nft; i++) {
359  ssize_t nw;
360  if (!is_regexp_matching(list[i].mire, tmp))
361  /*@innercontinue@*/ continue;
362 
364  D_("[filetriggers] file name '%s' matches pattern '%s'\n"),
365  tmp, list[i].mire->pattern);
366 
367  mayStartFiletrigger(rootDir, &list[i]);
368  nw = write(list[i].command_pipe, tmp, tmplen);
369  nw = write(list[i].command_pipe, "\n", 1);
370  }
371  }
372 
373  xx = Fclose(fd);
374  fd = NULL;
375  fp = NULL;
376 
377  for (i = 0; i < nft; i++) {
378  int status;
379  if (list[i].command_pipe) {
380  pid_t pid;
381  xx = close(list[i].command_pipe);
383  D_("[filetriggers] waiting for %s to end\n"),
384  list[i].name);
385  pid = waitpid(list[i].command_pid, &status, 0);
386  list[i].command_pipe = 0;
387  }
388  }
389 
390  oldhandler = signal(SIGPIPE, oldhandler);
391  }
392 
393 exit:
394  freeFiletriggers(matches_any, nft, list);
395 
396  if (fn != NULL)
397  xx = unlink(fn);
398  fn = _free(fn);
399 }
400 #endif /* defined(RPM_VENDOR_MANDRIVA) */
const bson * b
Definition: bson.h:280
miRE mireNew(rpmMireMode mode, int tag)
Create pattern container.
Definition: mire.c:113
const char const char * cmd
Definition: mongo.h:777
int Chroot(const char *path)
chroot(2) clone.
Definition: rpmrpc.c:176
FD_t Fopen(const char *path, const char *_fmode)
fopen(3) clone.
Definition: rpmio.c:2833
char * rpmGetPath(const char *path,...)
Return (malloc'ed) expanded, canonicalized, file path.
Definition: macro.c:3431
Structure(s) used for file info tag sets.
int mireRegcomp(miRE mire, const char *pattern)
Compile pattern match.
Definition: mire.c:332
int mireSetCOptions(miRE mire, rpmMireMode mode, int tag, int options, const unsigned char *table)
Initialize pattern compile options.
Definition: mire.c:121
int errno
const char * rpmfiFN(rpmfi fi)
Return current file name from file info set.
Definition: rpmfi.c:163
static void rpmlog(int code, const char *fmt,...)
Definition: rpmlog.h:299
Yet Another syslog(3) API clone.
int rpmGlob(const char *patterns, int *argcPtr, const char ***argvPtr)
Return URL path(s) from a (URL prefixed) pattern glob.
Definition: macro.c:2607
miRE mireFree(miRE mire)
Free pattern container.
#define fdGetFILE(_fd)
Definition: rpmio.c:159
void * xcalloc(size_t nmemb, size_t size)
Definition: rpmmalloc.c:300
struct rpmfi_s * rpmfi
File info tag sets from a header, so that a header can be discarded early.
Definition: rpmfi.h:83
RPM pattern matching.
static int xisspace(int c)
Definition: rpmiotypes.h:555
ARGV_t argvFree(ARGV_t argv)
Destroy an argv array.
Definition: argv.c:44
struct miRE_s * miRE
Definition: mire.h:60
The FD_t File Handle data structure.
const char * rpmGenPath(const char *urlroot, const char *urlmdir, const char *urlfile)
Merge 3 args into path, any or all of which may be a url.
Definition: macro.c:3477
int mireRegexec(miRE mire, const char *val, size_t vallen)
Execute pattern match.
Definition: mire.c:396
char * rpmExpand(const char *arg,...)
Return (malloc'ed) concatenated macro expansion(s).
Definition: macro.c:3238
int Fclose(FD_t fd)
fclose(3) clone.
Definition: rpmio.c:2534
int rpmfiNext(rpmfi fi)
Return next file iterator index.
Definition: rpmfi.c:526
rpmfi rpmfiInit(rpmfi fi, int fx)
Initialize file iterator index.
Definition: rpmfi.c:548
const char const int i
Definition: bson.h:778
char * stpcpy(char *dest, const char *src)
static void * _free(const void *p)
Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
Definition: rpmiotypes.h:756
rpmfi fi
Definition: filetriggers.h:15
static const char * name
#define _(Text)
Definition: system.h:29
#define xmalloc
Definition: system.h:32
rpmfi int install_or_erase
Definition: filetriggers.h:15
void rpmRunFileTriggers(const char *rootDir)
ARGstr_t * ARGV_t
Definition: argv.h:12
#define D_(Text)
Definition: system.h:526
const char const bson const bson int int int options
Definition: mongo.h:569