Branch data Line data Source code
1 : : /*
2 : : * libdpkg - Debian packaging suite library routines
3 : : * triglib.c - trigger handling
4 : : *
5 : : * Copyright © 2007 Canonical Ltd
6 : : * Written by Ian Jackson <ijackson@chiark.greenend.org.uk>
7 : : * Copyright © 2008-2015 Guillem Jover <guillem@debian.org>
8 : : *
9 : : * This is free software; you can redistribute it and/or modify
10 : : * it under the terms of the GNU General Public License as published by
11 : : * the Free Software Foundation; either version 2 of the License, or
12 : : * (at your option) any later version.
13 : : *
14 : : * This is distributed in the hope that it will be useful,
15 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : : * GNU General Public License for more details.
18 : : *
19 : : * You should have received a copy of the GNU General Public License
20 : : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : : */
22 : :
23 : : #include <config.h>
24 : : #include <compat.h>
25 : :
26 : : #include <sys/types.h>
27 : : #include <sys/stat.h>
28 : :
29 : : #include <errno.h>
30 : : #include <stdlib.h>
31 : : #include <unistd.h>
32 : :
33 : : #include <dpkg/i18n.h>
34 : : #include <dpkg/c-ctype.h>
35 : : #include <dpkg/dpkg.h>
36 : : #include <dpkg/dpkg-db.h>
37 : : #include <dpkg/pkg.h>
38 : : #include <dpkg/dlist.h>
39 : : #include <dpkg/dir.h>
40 : : #include <dpkg/pkg-spec.h>
41 : : #include <dpkg/trigdeferred.h>
42 : : #include <dpkg/triglib.h>
43 : :
44 : : /*========== Recording triggers. ==========*/
45 : :
46 : : static char *triggersdir, *triggersfilefile;
47 : :
48 : : static char *
49 : 74 : trig_get_filename(const char *dir, const char *filename)
50 : : {
51 : 74 : return str_fmt("%s/%s", dir, filename);
52 : : }
53 : :
54 : : static struct trig_hooks trigh;
55 : :
56 : : /*---------- Noting trigger activation in memory. ----------*/
57 : :
58 : : /*
59 : : * Called via trig_*activate* et al from:
60 : : * - trig_incorporate: reading of Unincorp (explicit trigger activations)
61 : : * - various places: processing start (‘activate’ in triggers ci file)
62 : : * - namenodetouse: file triggers during unpack / remove
63 : : * - deferred_configure: file triggers during config file processing
64 : : *
65 : : * Not called from trig_transitional_activation; that runs
66 : : * trig_note_pend directly which means that (a) awaiters are not
67 : : * recorded (how would we know?) and (b) we don't enqueue them for
68 : : * deferred processing in this run.
69 : : *
70 : : * We add the trigger to Triggers-Pending first. This makes it
71 : : * harder to get into the state where Triggers-Awaited for aw lists
72 : : * pend but Triggers-Pending for pend is empty. (See also the
73 : : * comment in deppossi_ok_found regarding this situation.)
74 : : */
75 : :
76 : : /*
77 : : * aw might be NULL.
78 : : * trig is not copied!
79 : : */
80 : : static void
81 : 0 : trig_record_activation(struct pkginfo *pend, struct pkginfo *aw, const char *trig)
82 : : {
83 [ # # ]: 0 : if (pend->status < PKG_STAT_TRIGGERSAWAITED)
84 : 0 : return; /* Not interested then. */
85 : :
86 [ # # ]: 0 : if (trig_note_pend(pend, trig))
87 : 0 : modstatdb_note_ifwrite(pend);
88 : :
89 [ # # ]: 0 : if (trigh.enqueue_deferred)
90 : 0 : trigh.enqueue_deferred(pend);
91 : :
92 [ # # # # ]: 0 : if (aw && pend->status > PKG_STAT_CONFIGFILES)
93 [ # # ]: 0 : if (trig_note_aw(pend, aw)) {
94 [ # # ]: 0 : if (aw->status > PKG_STAT_TRIGGERSAWAITED)
95 : 0 : pkg_set_status(aw, PKG_STAT_TRIGGERSAWAITED);
96 : 0 : modstatdb_note_ifwrite(aw);
97 : : }
98 : : }
99 : :
100 : : void
101 : 0 : trig_clear_awaiters(struct pkginfo *notpend)
102 : : {
103 : : struct trigaw *ta;
104 : : struct pkginfo *aw;
105 : :
106 [ # # ]: 0 : if (notpend->trigpend_head)
107 : 0 : internerr("package %s has pending triggers",
108 : : pkg_name(notpend, pnaw_always));
109 : :
110 : 0 : ta = notpend->othertrigaw_head;
111 : 0 : notpend->othertrigaw_head = NULL;
112 [ # # ]: 0 : for (; ta; ta = ta->samepend_next) {
113 : 0 : aw = ta->aw;
114 [ # # ]: 0 : if (!aw)
115 : 0 : continue;
116 [ # # # # ]: 0 : LIST_UNLINK_PART(aw->trigaw, ta, sameaw);
117 [ # # # # ]: 0 : if (!aw->trigaw.head && aw->status == PKG_STAT_TRIGGERSAWAITED) {
118 [ # # ]: 0 : if (aw->trigpend_head)
119 : 0 : pkg_set_status(aw, PKG_STAT_TRIGGERSPENDING);
120 : : else
121 : 0 : pkg_set_status(aw, PKG_STAT_INSTALLED);
122 : 0 : modstatdb_note(aw);
123 : : }
124 : : }
125 : 0 : }
126 : :
127 : : /*
128 : : * Fix up packages in state triggers-awaited w/o the corresponding package
129 : : * with pending triggers. This can happen when dpkg was interrupted
130 : : * while in modstatdb_note, and the package in triggers-pending had its
131 : : * state modified but dpkg could not finish clearing the awaiters.
132 : : *
133 : : * XXX: Possibly get rid of some of the checks done somewhere else for
134 : : * this condition at run-time.
135 : : */
136 : : void
137 : 74 : trig_fixup_awaiters(enum modstatdb_rw cstatus)
138 : : {
139 [ + - ]: 74 : if (cstatus < msdbrw_write)
140 : 74 : return;
141 : :
142 : 0 : trig_awaited_pend_foreach(trig_clear_awaiters);
143 : 0 : trig_awaited_pend_free();
144 : : }
145 : :
146 : : /*---------- Generalized handling of trigger kinds. ----------*/
147 : :
148 : : struct trigkindinfo {
149 : : /* Only for trig_activate_start. */
150 : : void (*activate_start)(void);
151 : :
152 : : /* Rest are for everyone: */
153 : : void (*activate_awaiter)(struct pkginfo *pkg /* may be NULL */);
154 : : void (*activate_done)(void);
155 : : void (*interest_change)(const char *name, struct pkginfo *pkg,
156 : : struct pkgbin *pkgbin,
157 : : int signum, enum trig_options opts);
158 : : };
159 : :
160 : : static const struct trigkindinfo tki_explicit, tki_file, tki_unknown;
161 : : static const struct trigkindinfo *dtki;
162 : :
163 : : /* As passed into activate_start. */
164 : : static char *trig_activating_name;
165 : :
166 : : static const struct trigkindinfo *
167 : 0 : trig_classify_byname(const char *name)
168 : : {
169 [ # # ]: 0 : if (name[0] == '/') {
170 : : const char *slash;
171 : :
172 : 0 : slash = name;
173 [ # # ]: 0 : while (slash) {
174 [ # # # # ]: 0 : if (slash[1] == '\0' || slash[1] == '/')
175 : 0 : goto invalid;
176 : :
177 : 0 : slash = strchr(slash + 2, '/');
178 : : }
179 : 0 : return &tki_file;
180 : : }
181 : :
182 [ # # # # ]: 0 : if (!pkg_name_is_illegal(name) && !strchr(name, '_'))
183 : 0 : return &tki_explicit;
184 : :
185 : 0 : invalid:
186 : 0 : return &tki_unknown;
187 : : }
188 : :
189 : : static const char *
190 : 0 : trig_dump_trigger_options(enum trig_options opts)
191 : : {
192 [ # # ]: 0 : if (opts == TRIG_NOAWAIT)
193 : 0 : return "/noawait";
194 : 0 : return "";
195 : : }
196 : :
197 : : /*
198 : : * Modifies the trigger string by removing the options at the ‘/’ delimiter
199 : : * and truncating it with a NUL character.
200 : : */
201 : : static enum trig_options
202 : 0 : trig_parse_trigger_options(char *trigger)
203 : : {
204 : : char *slash;
205 : :
206 : 0 : slash = strchr(trigger, '/');
207 [ # # ]: 0 : if (slash == NULL)
208 : 0 : return TRIG_AWAIT;
209 : :
210 : 0 : *slash++ = '\0';
211 [ # # ]: 0 : if (strcmp("noawait", slash) == 0)
212 : 0 : return TRIG_NOAWAIT;
213 [ # # ]: 0 : if (strcmp("await", slash) == 0)
214 : 0 : return TRIG_AWAIT;
215 : :
216 : : /* XXX: Should perhaps warn or error for unknown keywords. */
217 : :
218 : 0 : return TRIG_AWAIT;
219 : : }
220 : :
221 : : /*
222 : : * Calling sequence is:
223 : : * trig_activate_start(triggername);
224 : : * dtki->activate_awaiter(awaiting_package); } zero or more times
225 : : * dtki->activate_awaiter(NULL); } in any order
226 : : * dtki->activate_done();
227 : : */
228 : : static void
229 : 0 : trig_activate_start(const char *name)
230 : : {
231 : 0 : dtki = trig_classify_byname(name);
232 : 0 : trig_activating_name = nfstrsave(name);
233 : 0 : dtki->activate_start();
234 : 0 : }
235 : :
236 : : /*---------- Unknown trigger kinds. ----------*/
237 : :
238 : : static void
239 : 0 : trk_unknown_activate_start(void)
240 : : {
241 : 0 : }
242 : :
243 : : static void
244 : 0 : trk_unknown_activate_awaiter(struct pkginfo *aw)
245 : : {
246 : 0 : }
247 : :
248 : : static void
249 : 0 : trk_unknown_activate_done(void)
250 : : {
251 : 0 : }
252 : :
253 : : static void DPKG_ATTR_NORET
254 : 0 : trk_unknown_interest_change(const char *trig, struct pkginfo *pkg,
255 : : struct pkgbin *pkgbin, int signum,
256 : : enum trig_options opts)
257 : : {
258 : 0 : ohshit(_("invalid or unknown syntax in trigger name '%.250s'"
259 : : " (in trigger interests for package '%.250s')"),
260 : : trig, pkgbin_name(pkg, pkgbin, pnaw_nonambig));
261 : : }
262 : :
263 : : static const struct trigkindinfo tki_unknown = {
264 : : .activate_start = trk_unknown_activate_start,
265 : : .activate_awaiter = trk_unknown_activate_awaiter,
266 : : .activate_done = trk_unknown_activate_done,
267 : : .interest_change = trk_unknown_interest_change,
268 : : };
269 : :
270 : : /*---------- Explicit triggers. ----------*/
271 : :
272 : : static FILE *trk_explicit_f;
273 : : static struct varbuf trk_explicit_fn;
274 : : static char *trk_explicit_trig;
275 : :
276 : : static void
277 : 0 : trk_explicit_activate_done(void)
278 : : {
279 [ # # ]: 0 : if (trk_explicit_f) {
280 : 0 : fclose(trk_explicit_f);
281 : 0 : trk_explicit_f = NULL;
282 : : }
283 : 0 : }
284 : :
285 : : static void
286 : 0 : trk_explicit_start(const char *trig)
287 : : {
288 : 0 : trk_explicit_activate_done();
289 : :
290 : 0 : varbuf_reset(&trk_explicit_fn);
291 : 0 : varbuf_add_dir(&trk_explicit_fn, triggersdir);
292 : 0 : varbuf_add_str(&trk_explicit_fn, trig);
293 : 0 : varbuf_end_str(&trk_explicit_fn);
294 : :
295 : 0 : trk_explicit_f = fopen(trk_explicit_fn.buf, "r");
296 [ # # ]: 0 : if (!trk_explicit_f) {
297 [ # # ]: 0 : if (errno != ENOENT)
298 : 0 : ohshite(_("failed to open trigger interest list file '%.250s'"),
299 : : trk_explicit_fn.buf);
300 : : }
301 : 0 : }
302 : :
303 : : static int
304 : 0 : trk_explicit_fgets(char *buf, size_t sz)
305 : : {
306 : 0 : return fgets_checked(buf, sz, trk_explicit_f, trk_explicit_fn.buf);
307 : : }
308 : :
309 : : static void
310 : 0 : trk_explicit_activate_start(void)
311 : : {
312 : 0 : trk_explicit_start(trig_activating_name);
313 : 0 : trk_explicit_trig = trig_activating_name;
314 : 0 : }
315 : :
316 : : static void
317 : 0 : trk_explicit_activate_awaiter(struct pkginfo *aw)
318 : : {
319 : : char buf[1024];
320 : : struct pkginfo *pend;
321 : :
322 [ # # ]: 0 : if (!trk_explicit_f)
323 : 0 : return;
324 : :
325 [ # # ]: 0 : if (fseek(trk_explicit_f, 0, SEEK_SET))
326 : 0 : ohshite(_("failed to rewind trigger interest file '%.250s'"),
327 : : trk_explicit_fn.buf);
328 : :
329 [ # # ]: 0 : while (trk_explicit_fgets(buf, sizeof(buf)) >= 0) {
330 : : struct dpkg_error err;
331 : : enum trig_options opts;
332 : :
333 : 0 : opts = trig_parse_trigger_options(buf);
334 : :
335 : 0 : pend = pkg_spec_parse_pkg(buf, &err);
336 [ # # ]: 0 : if (pend == NULL)
337 : 0 : ohshit(_("trigger interest file '%.250s' syntax error; "
338 : : "illegal package name '%.250s': %.250s"),
339 : : trk_explicit_fn.buf, buf, err.str);
340 : :
341 [ # # ]: 0 : trig_record_activation(pend, opts == TRIG_NOAWAIT ? NULL : aw,
342 : : trk_explicit_trig);
343 : : }
344 : : }
345 : :
346 : : static void
347 : 0 : trk_explicit_interest_change(const char *trig, struct pkginfo *pkg,
348 : : struct pkgbin *pkgbin, int signum,
349 : : enum trig_options opts)
350 : : {
351 : : char buf[1024];
352 : : struct atomic_file *file;
353 : 0 : bool empty = true;
354 : :
355 : 0 : trk_explicit_start(trig);
356 : 0 : file = atomic_file_new(trk_explicit_fn.buf, 0);
357 : 0 : atomic_file_open(file);
358 : :
359 [ # # # # ]: 0 : while (trk_explicit_f && trk_explicit_fgets(buf, sizeof(buf)) >= 0) {
360 : : enum trig_options trig_opts;
361 : : struct pkginfo *pkg_parsed;
362 : : struct dpkg_error err;
363 : :
364 : 0 : trig_opts = trig_parse_trigger_options(buf);
365 : :
366 : 0 : pkg_parsed = pkg_spec_parse_pkg(buf, &err);
367 [ # # ]: 0 : if (pkg_parsed == NULL)
368 : 0 : ohshit(_("trigger interest file '%.250s' syntax error; "
369 : : "illegal package name '%.250s': %.250s"),
370 : : trk_explicit_fn.buf, buf, err.str);
371 : :
372 [ # # ]: 0 : if (pkg == pkg_parsed &&
373 [ # # ]: 0 : (pkgbin == &pkg_parsed->installed ||
374 [ # # ]: 0 : pkgbin == &pkg_parsed->available))
375 : 0 : continue;
376 : :
377 : 0 : fprintf(file->fp, "%s%s\n", buf,
378 : : trig_dump_trigger_options(trig_opts));
379 : 0 : empty = false;
380 : : }
381 [ # # ]: 0 : if (signum > 0) {
382 : 0 : fprintf(file->fp, "%s%s\n",
383 : : pkgbin_name(pkg, pkgbin, pnaw_same),
384 : : trig_dump_trigger_options(opts));
385 : 0 : empty = false;
386 : : }
387 : :
388 [ # # ]: 0 : if (!empty)
389 : 0 : atomic_file_sync(file);
390 : :
391 : 0 : atomic_file_close(file);
392 : :
393 [ # # ]: 0 : if (empty)
394 : 0 : atomic_file_remove(file);
395 : : else
396 : 0 : atomic_file_commit(file);
397 : :
398 : 0 : atomic_file_free(file);
399 : :
400 : 0 : dir_sync_path(triggersdir);
401 : 0 : }
402 : :
403 : : static const struct trigkindinfo tki_explicit = {
404 : : .activate_start = trk_explicit_activate_start,
405 : : .activate_awaiter = trk_explicit_activate_awaiter,
406 : : .activate_done = trk_explicit_activate_done,
407 : : .interest_change = trk_explicit_interest_change,
408 : : };
409 : :
410 : : /*---------- File triggers. ----------*/
411 : :
412 : : static struct {
413 : : /* cppcheck-suppress[unusedStructMember]:
414 : : * False positive, macros from dlist.h use the tail member. */
415 : : struct trigfileint *head, *tail;
416 : : } filetriggers;
417 : :
418 : : /*
419 : : * Values:
420 : : * -1: Not read.
421 : : * 0: Not edited.
422 : : * 1: Edited
423 : : */
424 : : static int filetriggers_edited = -1;
425 : :
426 : : /*
427 : : * Called by various people with signum -1 and +1 to mean remove and add
428 : : * and also by trig_file_interests_ensure() with signum +2 meaning add
429 : : * but die if already present.
430 : : */
431 : : static void
432 : 0 : trk_file_interest_change(const char *trig, struct pkginfo *pkg,
433 : : struct pkgbin *pkgbin, int signum,
434 : : enum trig_options opts)
435 : : {
436 : : struct fsys_namenode *fnn;
437 : : struct trigfileint **search, *tfi;
438 : :
439 : 0 : fnn = trigh.namenode_find(trig, signum <= 0);
440 [ # # ]: 0 : if (!fnn) {
441 [ # # ]: 0 : if (signum >= 0)
442 : 0 : internerr("lost filename node '%s' for package %s "
443 : : "triggered to add", trig,
444 : : pkgbin_name(pkg, pkgbin, pnaw_always));
445 : 0 : return;
446 : : }
447 : :
448 : 0 : for (search = trigh.namenode_interested(fnn);
449 [ # # ]: 0 : (tfi = *search);
450 : 0 : search = &tfi->samefile_next)
451 [ # # ]: 0 : if (tfi->pkg == pkg)
452 : 0 : goto found;
453 : :
454 : : /* Not found. */
455 [ # # ]: 0 : if (signum < 0)
456 : 0 : return;
457 : :
458 : 0 : tfi = nfmalloc(sizeof(*tfi));
459 : 0 : tfi->pkg = pkg;
460 : 0 : tfi->pkgbin = pkgbin;
461 : 0 : tfi->fnn = fnn;
462 : 0 : tfi->options = opts;
463 : 0 : tfi->samefile_next = *trigh.namenode_interested(fnn);
464 : 0 : *trigh.namenode_interested(fnn) = tfi;
465 : :
466 [ # # ]: 0 : LIST_LINK_TAIL_PART(filetriggers, tfi, inoverall);
467 : 0 : goto edited;
468 : :
469 : 0 : found:
470 : 0 : tfi->options = opts;
471 [ # # ]: 0 : if (signum > 1)
472 : 0 : ohshit(_("duplicate file trigger interest for filename '%.250s' "
473 : : "and package '%.250s'"), trig,
474 : : pkgbin_name(pkg, pkgbin, pnaw_nonambig));
475 [ # # ]: 0 : if (signum > 0)
476 : 0 : return;
477 : :
478 : : /* Remove it: */
479 : 0 : *search = tfi->samefile_next;
480 [ # # # # ]: 0 : LIST_UNLINK_PART(filetriggers, tfi, inoverall);
481 : 0 : edited:
482 : 0 : filetriggers_edited = 1;
483 : : }
484 : :
485 : : static void
486 : 0 : trig_file_interests_remove(void)
487 : : {
488 [ # # # # ]: 0 : if (unlink(triggersfilefile) && errno != ENOENT)
489 : 0 : ohshite(_("cannot remove '%.250s'"), triggersfilefile);
490 : 0 : }
491 : :
492 : : static void
493 : 0 : trig_file_interests_update(void)
494 : : {
495 : : struct trigfileint *tfi;
496 : : struct atomic_file *file;
497 : :
498 : 0 : file = atomic_file_new(triggersfilefile, 0);
499 : 0 : atomic_file_open(file);
500 : :
501 [ # # ]: 0 : for (tfi = filetriggers.head; tfi; tfi = tfi->inoverall.next)
502 : 0 : fprintf(file->fp, "%s %s%s\n", trigh.namenode_name(tfi->fnn),
503 : : pkgbin_name(tfi->pkg, tfi->pkgbin, pnaw_same),
504 : : trig_dump_trigger_options(tfi->options));
505 : :
506 : 0 : atomic_file_sync(file);
507 : 0 : atomic_file_close(file);
508 : 0 : atomic_file_commit(file);
509 : 0 : atomic_file_free(file);
510 : 0 : }
511 : :
512 : : void
513 : 0 : trig_file_interests_save(void)
514 : : {
515 [ # # ]: 0 : if (filetriggers_edited <= 0)
516 : 0 : return;
517 : :
518 [ # # ]: 0 : if (!filetriggers.head)
519 : 0 : trig_file_interests_remove();
520 : : else
521 : 0 : trig_file_interests_update();
522 : :
523 : 0 : dir_sync_path(triggersdir);
524 : :
525 : 0 : filetriggers_edited = 0;
526 : : }
527 : :
528 : : void
529 : 74 : trig_file_interests_ensure(void)
530 : : {
531 : : FILE *f;
532 : : char linebuf[1024], *space;
533 : : struct pkginfo *pkg;
534 : : struct pkgbin *pkgbin;
535 : :
536 [ - + ]: 74 : if (filetriggers_edited >= 0)
537 : 0 : return;
538 : :
539 : 74 : f = fopen(triggersfilefile, "r");
540 [ + - ]: 74 : if (!f) {
541 [ + - ]: 74 : if (errno == ENOENT)
542 : 74 : goto ok;
543 : 0 : ohshite(_("unable to read file triggers file '%.250s'"),
544 : : triggersfilefile);
545 : : }
546 : :
547 : 0 : push_cleanup(cu_closestream, ~0, 1, f);
548 [ # # ]: 0 : while (fgets_checked(linebuf, sizeof(linebuf), f, triggersfilefile) >= 0) {
549 : : struct dpkg_error err;
550 : : enum trig_options trig_opts;
551 : :
552 : 0 : space = strchr(linebuf, ' ');
553 [ # # # # ]: 0 : if (!space || linebuf[0] != '/')
554 : 0 : ohshit(_("syntax error in file triggers file '%.250s'"),
555 : : triggersfilefile);
556 : 0 : *space++ = '\0';
557 : :
558 : 0 : trig_opts = trig_parse_trigger_options(space);
559 : :
560 : 0 : pkg = pkg_spec_parse_pkg(space, &err);
561 [ # # ]: 0 : if (pkg == NULL)
562 : 0 : ohshit(_("file triggers record mentions illegal "
563 : : "package name '%.250s' (for interest in file "
564 : : "'%.250s'): %.250s"), space, linebuf, err.str);
565 : 0 : pkgbin = &pkg->installed;
566 : :
567 : 0 : trk_file_interest_change(linebuf, pkg, pkgbin, +2, trig_opts);
568 : : }
569 : 0 : pop_cleanup(ehflag_normaltidy);
570 : 74 : ok:
571 : 74 : filetriggers_edited = 0;
572 : : }
573 : :
574 : : void
575 : 0 : trig_file_activate_byname(const char *trig, struct pkginfo *aw)
576 : : {
577 : 0 : struct fsys_namenode *fnn = trigh.namenode_find(trig, 1);
578 : :
579 [ # # ]: 0 : if (fnn)
580 : 0 : trig_file_activate(fnn, aw);
581 : 0 : }
582 : :
583 : : void
584 : 0 : trig_file_activate(struct fsys_namenode *trig, struct pkginfo *aw)
585 : : {
586 : : struct trigfileint *tfi;
587 : :
588 [ # # ]: 0 : for (tfi = *trigh.namenode_interested(trig); tfi;
589 : 0 : tfi = tfi->samefile_next)
590 [ # # ]: 0 : trig_record_activation(tfi->pkg, (tfi->options == TRIG_NOAWAIT) ?
591 : 0 : NULL : aw, trigh.namenode_name(trig));
592 : 0 : }
593 : :
594 : : static void
595 : 0 : trig_file_activate_parents(const char *trig, struct pkginfo *aw)
596 : : {
597 : : char *path, *slash;
598 : :
599 : : /* Traverse the whole pathname to activate all of its components. */
600 : 0 : path = m_strdup(trig);
601 : :
602 [ # # ]: 0 : while ((slash = strrchr(path, '/'))) {
603 : 0 : *slash = '\0';
604 : 0 : trig_file_activate_byname(path, aw);
605 : : }
606 : :
607 : 0 : free(path);
608 : 0 : }
609 : :
610 : : void
611 : 0 : trig_path_activate(struct fsys_namenode *trig, struct pkginfo *aw)
612 : : {
613 : 0 : trig_file_activate(trig, aw);
614 : 0 : trig_file_activate_parents(trigh.namenode_name(trig), aw);
615 : 0 : }
616 : :
617 : : static void
618 : 0 : trig_path_activate_byname(const char *trig, struct pkginfo *aw)
619 : : {
620 : 0 : struct fsys_namenode *fnn = trigh.namenode_find(trig, 1);
621 : :
622 [ # # ]: 0 : if (fnn)
623 : 0 : trig_file_activate(fnn, aw);
624 : :
625 : 0 : trig_file_activate_parents(trig, aw);
626 : 0 : }
627 : :
628 : : static const char *trk_file_trig;
629 : :
630 : : static void
631 : 0 : trk_file_activate_start(void)
632 : : {
633 : 0 : trk_file_trig = trig_activating_name;
634 : 0 : }
635 : :
636 : : static void
637 : 0 : trk_file_activate_awaiter(struct pkginfo *aw)
638 : : {
639 : 0 : trig_path_activate_byname(trk_file_trig, aw);
640 : 0 : }
641 : :
642 : : static void
643 : 0 : trk_file_activate_done(void)
644 : : {
645 : 0 : }
646 : :
647 : : static const struct trigkindinfo tki_file = {
648 : : .activate_start = trk_file_activate_start,
649 : : .activate_awaiter = trk_file_activate_awaiter,
650 : : .activate_done = trk_file_activate_done,
651 : : .interest_change = trk_file_interest_change,
652 : : };
653 : :
654 : : /*---------- Trigger control info file. ----------*/
655 : :
656 : : static void
657 : 0 : trig_cicb_interest_change(const char *trig, struct pkginfo *pkg,
658 : : struct pkgbin *pkgbin, int signum,
659 : : enum trig_options opts)
660 : : {
661 : 0 : const struct trigkindinfo *tki = trig_classify_byname(trig);
662 : :
663 [ # # ]: 0 : if (filetriggers_edited < 0)
664 : 0 : internerr("trigger control file for package %s not read",
665 : : pkgbin_name(pkg, pkgbin, pnaw_always));
666 : :
667 : 0 : tki->interest_change(trig, pkg, pkgbin, signum, opts);
668 : 0 : }
669 : :
670 : : void
671 : 0 : trig_cicb_interest_delete(const char *trig, struct pkginfo *pkg,
672 : : struct pkgbin *pkgbin, enum trig_options opts)
673 : : {
674 : 0 : trig_cicb_interest_change(trig, pkg, pkgbin, -1, opts);
675 : 0 : }
676 : :
677 : : void
678 : 0 : trig_cicb_interest_add(const char *trig, struct pkginfo *pkg,
679 : : struct pkgbin *pkgbin, enum trig_options opts)
680 : : {
681 : 0 : trig_cicb_interest_change(trig, pkg, pkgbin, +1, opts);
682 : 0 : }
683 : :
684 : : void
685 : 0 : trig_cicb_statuschange_activate(const char *trig, struct pkginfo *pkg,
686 : : struct pkgbin *pkgbin, enum trig_options opts)
687 : : {
688 : 0 : struct pkginfo *aw = pkg;
689 : :
690 : 0 : trig_activate_start(trig);
691 [ # # ]: 0 : dtki->activate_awaiter((opts == TRIG_NOAWAIT) ? NULL : aw);
692 : 0 : dtki->activate_done();
693 : 0 : }
694 : :
695 : : static void
696 : 0 : parse_ci_call(const char *file, const char *cmd, trig_parse_cicb *cb,
697 : : const char *trig, struct pkginfo *pkg, struct pkgbin *pkgbin,
698 : : enum trig_options opts)
699 : : {
700 : : const char *emsg;
701 : :
702 : 0 : emsg = trig_name_is_illegal(trig);
703 [ # # ]: 0 : if (emsg)
704 : 0 : ohshit(_("triggers ci file '%.250s' contains illegal trigger "
705 : : "syntax in trigger name '%.250s': %.250s"),
706 : : file, trig, emsg);
707 [ # # ]: 0 : if (cb)
708 : 0 : cb(trig, pkg, pkgbin, opts);
709 : 0 : }
710 : :
711 : : void
712 : 0 : trig_parse_ci(const char *file, trig_parse_cicb *interest,
713 : : trig_parse_cicb *activate, struct pkginfo *pkg,
714 : : struct pkgbin *pkgbin)
715 : : {
716 : : FILE *f;
717 : : char linebuf[MAXTRIGDIRECTIVE], *cmd, *spc, *eol;
718 : : int l;
719 : :
720 : 0 : f = fopen(file, "r");
721 [ # # ]: 0 : if (!f) {
722 [ # # ]: 0 : if (errno == ENOENT)
723 : 0 : return; /* No file is just like an empty one. */
724 : 0 : ohshite(_("unable to open triggers ci file '%.250s'"), file);
725 : : }
726 : 0 : push_cleanup(cu_closestream, ~0, 1, f);
727 : :
728 [ # # ]: 0 : while ((l = fgets_checked(linebuf, sizeof(linebuf), f, file)) >= 0) {
729 [ # # ]: 0 : for (cmd = linebuf; c_iswhite(*cmd); cmd++) ;
730 [ # # ]: 0 : if (*cmd == '#')
731 : 0 : continue;
732 [ # # # # ]: 0 : for (eol = linebuf + l; eol > cmd && c_iswhite(eol[-1]); eol--) ;
733 [ # # ]: 0 : if (eol == cmd)
734 : 0 : continue;
735 : 0 : *eol = '\0';
736 : :
737 [ # # # # ]: 0 : for (spc = cmd; *spc && !c_iswhite(*spc); spc++) ;
738 [ # # ]: 0 : if (!*spc)
739 : 0 : ohshit(_("triggers ci file contains unknown directive syntax"));
740 : 0 : *spc++ = '\0';
741 [ # # ]: 0 : while (c_iswhite(*spc))
742 : 0 : spc++;
743 [ # # ]: 0 : if (strcmp(cmd, "interest") == 0 ||
744 [ # # ]: 0 : strcmp(cmd, "interest-await") == 0) {
745 : 0 : parse_ci_call(file, cmd, interest, spc, pkg, pkgbin, TRIG_AWAIT);
746 [ # # ]: 0 : } else if (strcmp(cmd, "interest-noawait") == 0) {
747 : 0 : parse_ci_call(file, cmd, interest, spc, pkg, pkgbin, TRIG_NOAWAIT);
748 [ # # ]: 0 : } else if (strcmp(cmd, "activate") == 0 ||
749 [ # # ]: 0 : strcmp(cmd, "activate-await") == 0) {
750 : 0 : parse_ci_call(file, cmd, activate, spc, pkg, pkgbin, TRIG_AWAIT);
751 [ # # ]: 0 : } else if (strcmp(cmd, "activate-noawait") == 0) {
752 : 0 : parse_ci_call(file, cmd, activate, spc, pkg, pkgbin, TRIG_NOAWAIT);
753 : : } else {
754 : 0 : ohshit(_("triggers ci file contains unknown directive '%.250s'"),
755 : : cmd);
756 : : }
757 : : }
758 : 0 : pop_cleanup(ehflag_normaltidy); /* fclose() */
759 : : }
760 : :
761 : : /*---------- Unincorp file incorporation. ----------*/
762 : :
763 : : static void
764 : 0 : tdm_incorp_trig_begin(const char *trig)
765 : : {
766 : 0 : trig_activate_start(trig);
767 : 0 : }
768 : :
769 : : static void
770 : 0 : tdm_incorp_package(const char *awname)
771 : : {
772 : : struct pkginfo *aw;
773 : :
774 [ # # ]: 0 : if (strcmp(awname, "-") == 0)
775 : 0 : aw = NULL;
776 : : else
777 : 0 : aw = pkg_spec_parse_pkg(awname, NULL);
778 : :
779 : 0 : dtki->activate_awaiter(aw);
780 : 0 : }
781 : :
782 : : static void
783 : 0 : tdm_incorp_trig_end(void)
784 : : {
785 : 0 : dtki->activate_done();
786 : 0 : }
787 : :
788 : : static const struct trigdefmeths tdm_incorp = {
789 : : .trig_begin = tdm_incorp_trig_begin,
790 : : .package = tdm_incorp_package,
791 : : .trig_end = tdm_incorp_trig_end
792 : : };
793 : :
794 : : void
795 : 74 : trig_incorporate(enum modstatdb_rw cstatus)
796 : : {
797 : : enum trigdef_update_status ur;
798 : : enum trigdef_update_flags tduf;
799 : :
800 : 74 : free(triggersdir);
801 : 74 : triggersdir = dpkg_db_get_path(TRIGGERSDIR);
802 : :
803 : 74 : free(triggersfilefile);
804 : 74 : triggersfilefile = trig_get_filename(triggersdir, TRIGGERSFILEFILE);
805 : :
806 : 74 : trigdef_set_methods(&tdm_incorp);
807 : 74 : trig_file_interests_ensure();
808 : :
809 : 74 : tduf = TDUF_NO_LOCK_OK;
810 [ - + ]: 74 : if (cstatus >= msdbrw_write) {
811 : 0 : tduf |= TDUF_WRITE;
812 [ # # ]: 0 : if (trigh.transitional_activate)
813 : 0 : tduf |= TDUF_WRITE_IF_ENOENT;
814 : : }
815 : :
816 : 74 : ur = trigdef_update_start(tduf);
817 [ - + - - ]: 74 : if (ur == TDUS_ERROR_NO_DIR && cstatus >= msdbrw_write) {
818 [ # # ]: 0 : if (mkdir(triggersdir, 0755)) {
819 [ # # ]: 0 : if (errno != EEXIST)
820 : 0 : ohshite(_("unable to create triggers state"
821 : : " directory '%.250s'"), triggersdir);
822 : : }
823 : 0 : ur = trigdef_update_start(tduf);
824 : : }
825 [ - + - - : 74 : switch (ur) {
- ]
826 : 0 : case TDUS_ERROR_EMPTY_DEFERRED:
827 : 0 : return;
828 : 74 : case TDUS_ERROR_NO_DIR:
829 : : case TDUS_ERROR_NO_DEFERRED:
830 [ + - ]: 74 : if (!trigh.transitional_activate)
831 : 74 : return;
832 : : /* Fall through. */
833 : : case TDUS_NO_DEFERRED:
834 : 0 : trigh.transitional_activate(cstatus);
835 : 0 : break;
836 : 0 : case TDUS_OK:
837 : : /* Read and incorporate triggers. */
838 : 0 : trigdef_parse();
839 : 0 : break;
840 : 0 : default:
841 : 0 : internerr("unknown trigdef_update_start return value '%d'", ur);
842 : : }
843 : :
844 : : /* Right, that's it. New (empty) Unincorp can be installed. */
845 : 0 : trigdef_process_done();
846 : : }
847 : :
848 : : /*---------- Default hooks. ----------*/
849 : :
850 [ # # ]: 0 : TRIGHOOKS_DEFINE_NAMENODE_ACCESSORS
851 : :
852 : : static struct trig_hooks trigh = {
853 : : .enqueue_deferred = NULL,
854 : : .transitional_activate = NULL,
855 : : .namenode_find = th_nn_find,
856 : : .namenode_interested = th_nn_interested,
857 : : .namenode_name = th_nn_name,
858 : : };
859 : :
860 : : void
861 : 0 : trig_override_hooks(const struct trig_hooks *hooks)
862 : : {
863 : 0 : trigh = *hooks;
864 : 0 : }
|