| /* |
| * libdpkg - Debian packaging suite library routines |
| * triglib.c - trigger handling |
| * |
| * Copyright © 2007 Canonical Ltd |
| * Written by Ian Jackson <ian@chiark.greenend.org.uk> |
| * Copyright © 2008-2011 Guillem Jover <guillem@debian.org> |
| * |
| * This is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include <config.h> |
| #include <compat.h> |
| |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| |
| #include <dpkg/i18n.h> |
| #include <dpkg/dpkg.h> |
| #include <dpkg/dpkg-db.h> |
| #include <dpkg/dlist.h> |
| #include <dpkg/dir.h> |
| #include <dpkg/trigdeferred.h> |
| #include <dpkg/triglib.h> |
| |
| /*========== Recording triggers. ==========*/ |
| |
| static char *triggersdir, *triggersfilefile, *triggersnewfilefile; |
| |
| static char * |
| trig_get_filename(const char *dir, const char *filename) |
| { |
| char *path; |
| |
| m_asprintf(&path, "%s/%s", dir, filename); |
| |
| return path; |
| } |
| |
| static struct trig_hooks trigh; |
| |
| /*---------- Noting trigger activation in memory. ----------*/ |
| |
| /* |
| * Called via trig_*activate* et al from: |
| * - trig_incorporate: reading of Unincorp (explicit trigger activations) |
| * - various places: processing start (‘activate’ in triggers ci file) |
| * - namenodetouse: file triggers during unpack / remove |
| * - deferred_configure: file triggers during config file processing |
| * |
| * Not called from trig_transitional_activation; that runs |
| * trig_note_pend directly which means that (a) awaiters are not |
| * recorded (how would we know?) and (b) we don't enqueue them for |
| * deferred processing in this run. |
| * |
| * We add the trigger to Triggers-Pending first. This makes it |
| * harder to get into the state where Triggers-Awaited for aw lists |
| * pend but Triggers-Pending for pend is empty. (See also the |
| * comment in deppossi_ok_found regarding this situation.) |
| */ |
| |
| /* |
| * aw might be NULL. |
| * trig is not copied! |
| */ |
| static void |
| trig_record_activation(struct pkginfo *pend, struct pkginfo *aw, const char *trig) |
| { |
| if (pend->status < stat_triggersawaited) |
| return; /* Not interested then. */ |
| |
| if (trig_note_pend(pend, trig)) |
| modstatdb_note_ifwrite(pend); |
| |
| if (trigh.enqueue_deferred) |
| trigh.enqueue_deferred(pend); |
| |
| if (aw && pend->status > stat_configfiles) |
| if (trig_note_aw(pend, aw)) { |
| if (aw->status > stat_triggersawaited) |
| aw->status = stat_triggersawaited; |
| modstatdb_note_ifwrite(aw); |
| } |
| } |
| |
| void |
| trig_clear_awaiters(struct pkginfo *notpend) |
| { |
| struct trigaw *ta; |
| struct pkginfo *aw; |
| |
| assert(!notpend->trigpend_head); |
| |
| ta = notpend->othertrigaw_head; |
| notpend->othertrigaw_head = NULL; |
| for (; ta; ta = ta->samepend_next) { |
| aw = ta->aw; |
| if (!aw) |
| continue; |
| LIST_UNLINK_PART(aw->trigaw, ta, sameaw.); |
| if (!aw->trigaw.head && aw->status == stat_triggersawaited) { |
| aw->status = aw->trigpend_head ? stat_triggerspending : |
| stat_installed; |
| modstatdb_note(aw); |
| } |
| } |
| } |
| |
| /* |
| * Fix up packages in state triggers-awaited w/o the corresponding package |
| * with pending triggers. This can happen when dpkg was interrupted |
| * while in modstatdb_note, and the package in triggers-pending had its |
| * state modified but dpkg could not finish clearing the awaiters. |
| * |
| * XXX: Possibly get rid of some of the checks done somewhere else for |
| * this condition at run-time. |
| */ |
| void |
| trig_fixup_awaiters(enum modstatdb_rw cstatus) |
| { |
| if (cstatus < msdbrw_write) |
| return; |
| |
| trig_awaited_pend_foreach(trig_clear_awaiters); |
| trig_awaited_pend_free(); |
| } |
| |
| /*---------- Generalized handling of trigger kinds. ----------*/ |
| |
| struct trigkindinfo { |
| /* Only for trig_activate_start. */ |
| void (*activate_start)(void); |
| |
| /* Rest are for everyone: */ |
| void (*activate_awaiter)(struct pkginfo *pkg /* may be NULL */); |
| void (*activate_done)(void); |
| void (*interest_change)(const char *name, struct pkginfo *pkg, int signum, |
| enum trig_options opts); |
| }; |
| |
| static const struct trigkindinfo tki_explicit, tki_file, tki_unknown; |
| static const struct trigkindinfo *dtki; |
| |
| /* As passed into activate_start. */ |
| static const char *trig_activating_name; |
| |
| static const struct trigkindinfo * |
| trig_classify_byname(const char *name) |
| { |
| if (name[0] == '/') { |
| const char *slash; |
| |
| slash = name; |
| while (slash) { |
| if (slash[1] == '\0' || slash[1] == '/') |
| goto invalid; |
| |
| slash = strchr(slash + 2, '/'); |
| } |
| return &tki_file; |
| } |
| |
| if (!pkg_name_is_illegal(name, NULL) && !strchr(name, '_')) |
| return &tki_explicit; |
| |
| invalid: |
| return &tki_unknown; |
| } |
| |
| /* |
| * Calling sequence is: |
| * trig_activate_start(triggername) |
| * dtki->activate_awaiter(awaiting_package) } zero or more times |
| * dtki->activate_awaiter(0) } in any order |
| * dtki->activate_done(0) |
| */ |
| static void |
| trig_activate_start(const char *name) |
| { |
| dtki = trig_classify_byname(name); |
| trig_activating_name = name; |
| dtki->activate_start(); |
| } |
| |
| /*---------- Unknown trigger kinds. ----------*/ |
| |
| static void |
| trk_unknown_activate_start(void) |
| { |
| } |
| |
| static void |
| trk_unknown_activate_awaiter(struct pkginfo *aw) |
| { |
| } |
| |
| static void |
| trk_unknown_activate_done(void) |
| { |
| } |
| |
| static void DPKG_ATTR_NORET |
| trk_unknown_interest_change(const char *trig, struct pkginfo *pkg, int signum, |
| enum trig_options opts) |
| { |
| ohshit(_("invalid or unknown syntax in trigger name `%.250s'" |
| " (in trigger interests for package `%.250s')"), |
| trig, pkg->name); |
| } |
| |
| static const struct trigkindinfo tki_unknown = { |
| .activate_start = trk_unknown_activate_start, |
| .activate_awaiter = trk_unknown_activate_awaiter, |
| .activate_done = trk_unknown_activate_done, |
| .interest_change = trk_unknown_interest_change, |
| }; |
| |
| /*---------- Explicit triggers. ----------*/ |
| |
| static FILE *trk_explicit_f; |
| static struct varbuf trk_explicit_fn; |
| static char *trk_explicit_trig; |
| |
| static void |
| trk_explicit_activate_done(void) |
| { |
| if (trk_explicit_f) { |
| fclose(trk_explicit_f); |
| trk_explicit_f = NULL; |
| } |
| } |
| |
| static void |
| trk_explicit_start(const char *trig) |
| { |
| trk_explicit_activate_done(); |
| |
| varbuf_reset(&trk_explicit_fn); |
| varbuf_add_str(&trk_explicit_fn, triggersdir); |
| varbuf_add_char(&trk_explicit_fn, '/'); |
| varbuf_add_str(&trk_explicit_fn, trig); |
| varbuf_end_str(&trk_explicit_fn); |
| |
| trk_explicit_f = fopen(trk_explicit_fn.buf, "r"); |
| if (!trk_explicit_f) { |
| if (errno != ENOENT) |
| ohshite(_("failed to open trigger interest list file `%.250s'"), |
| trk_explicit_fn.buf); |
| } |
| } |
| |
| static int |
| trk_explicit_fgets(char *buf, size_t sz) |
| { |
| return fgets_checked(buf, sz, trk_explicit_f, trk_explicit_fn.buf); |
| } |
| |
| static void |
| trk_explicit_activate_start(void) |
| { |
| trk_explicit_start(trig_activating_name); |
| trk_explicit_trig = nfstrsave(trig_activating_name); |
| } |
| |
| static void |
| trk_explicit_activate_awaiter(struct pkginfo *aw) |
| { |
| char buf[1024]; |
| const char *emsg; |
| struct pkginfo *pend; |
| |
| if (!trk_explicit_f) |
| return; |
| |
| if (fseek(trk_explicit_f, 0, SEEK_SET)) |
| ohshite(_("failed to rewind trigger interest file `%.250s'"), |
| trk_explicit_fn.buf); |
| |
| while (trk_explicit_fgets(buf, sizeof(buf)) >= 0) { |
| char *slash; |
| bool noawait = false; |
| slash = strchr(buf, '/'); |
| if (slash && strcmp("/noawait", slash) == 0) { |
| noawait = true; |
| *slash = '\0'; |
| } |
| emsg = pkg_name_is_illegal(buf, NULL); |
| if (emsg) |
| ohshit(_("trigger interest file `%.250s' syntax error; " |
| "illegal package name `%.250s': %.250s"), |
| trk_explicit_fn.buf, buf, emsg); |
| pend = pkg_db_find(buf); |
| trig_record_activation(pend, noawait ? NULL : aw, |
| trk_explicit_trig); |
| } |
| } |
| |
| static void |
| trk_explicit_interest_flush(const char *newfilename, FILE *nf) |
| { |
| if (ferror(nf)) |
| ohshite(_("unable to write new trigger interest file `%.250s'"), |
| newfilename); |
| if (fflush(nf)) |
| ohshite(_("unable to flush new trigger interest file '%.250s'"), |
| newfilename); |
| if (fsync(fileno(nf))) |
| ohshite(_("unable to sync new trigger interest file '%.250s'"), |
| newfilename); |
| } |
| |
| static void |
| trk_explicit_interest_commit(const char *newfilename) |
| { |
| if (rename(newfilename, trk_explicit_fn.buf)) |
| ohshite(_("unable to install new trigger interest file `%.250s'"), |
| trk_explicit_fn.buf); |
| } |
| |
| static void |
| trk_explicit_interest_remove(const char *newfilename) |
| { |
| if (unlink(newfilename)) |
| ohshite(_("cannot remove `%.250s'"), newfilename); |
| if (unlink(trk_explicit_fn.buf) && errno != ENOENT) |
| ohshite(_("cannot remove `%.250s'"), trk_explicit_fn.buf); |
| } |
| |
| static void |
| trk_explicit_interest_change(const char *trig, struct pkginfo *pkg, int signum, |
| enum trig_options opts) |
| { |
| static struct varbuf newfn; |
| char buf[1024]; |
| FILE *nf; |
| bool empty = true; |
| |
| trk_explicit_start(trig); |
| varbuf_reset(&newfn); |
| varbuf_printf(&newfn, "%s/%s.new", triggersdir, trig); |
| |
| nf = fopen(newfn.buf, "w"); |
| if (!nf) |
| ohshite(_("unable to create new trigger interest file `%.250s'"), |
| newfn.buf); |
| push_cleanup(cu_closestream, ~ehflag_normaltidy, NULL, 0, 1, nf); |
| |
| while (trk_explicit_f && trk_explicit_fgets(buf, sizeof(buf)) >= 0) { |
| int len = strlen(pkg->name); |
| if (strncmp(buf, pkg->name, len) == 0 && |
| (buf[len] == '\0' || buf[len] == '/')) |
| continue; |
| fprintf(nf, "%s\n", buf); |
| empty = false; |
| } |
| if (signum > 0) { |
| fprintf(nf, "%s%s\n", pkg->name, |
| (opts == trig_noawait) ? "/noawait" : ""); |
| empty = false; |
| } |
| |
| if (!empty) |
| trk_explicit_interest_flush(newfn.buf, nf); |
| |
| pop_cleanup(ehflag_normaltidy); |
| if (fclose(nf)) |
| ohshite(_("unable to close new trigger interest file `%.250s'"), |
| newfn.buf); |
| |
| if (empty) |
| trk_explicit_interest_remove(newfn.buf); |
| else |
| trk_explicit_interest_commit(newfn.buf); |
| |
| dir_sync_path(triggersdir); |
| } |
| |
| static const struct trigkindinfo tki_explicit = { |
| .activate_start = trk_explicit_activate_start, |
| .activate_awaiter = trk_explicit_activate_awaiter, |
| .activate_done = trk_explicit_activate_done, |
| .interest_change = trk_explicit_interest_change, |
| }; |
| |
| /*---------- File triggers. ----------*/ |
| |
| static struct { |
| struct trigfileint *head, *tail; |
| } filetriggers; |
| |
| /* |
| * Values: |
| * -1: Not read. |
| * 0: Not edited. |
| * 1: Edited |
| */ |
| static int filetriggers_edited = -1; |
| |
| /* |
| * Called by various people with signum -1 and +1 to mean remove and add |
| * and also by trig_file_interests_ensure() with signum +2 meaning add |
| * but die if already present. |
| */ |
| static void |
| trk_file_interest_change(const char *trig, struct pkginfo *pkg, int signum, |
| enum trig_options opts) |
| { |
| struct filenamenode *fnn; |
| struct trigfileint **search, *tfi; |
| |
| fnn = trigh.namenode_find(trig, signum <= 0); |
| if (!fnn) { |
| assert(signum < 0); |
| return; |
| } |
| |
| for (search = trigh.namenode_interested(fnn); |
| (tfi = *search); |
| search = &tfi->samefile_next) |
| if (tfi->pkg == pkg) |
| goto found; |
| |
| /* Not found. */ |
| if (signum < 0) |
| return; |
| |
| tfi = nfmalloc(sizeof(*tfi)); |
| tfi->pkg = pkg; |
| tfi->fnn = fnn; |
| tfi->options = opts; |
| tfi->samefile_next = *trigh.namenode_interested(fnn); |
| *trigh.namenode_interested(fnn) = tfi; |
| |
| LIST_LINK_TAIL_PART(filetriggers, tfi, inoverall.); |
| goto edited; |
| |
| found: |
| tfi->options = opts; |
| if (signum > 1) |
| ohshit(_("duplicate file trigger interest for filename `%.250s' " |
| "and package `%.250s'"), trig, pkg->name); |
| if (signum > 0) |
| return; |
| |
| /* Remove it: */ |
| *search = tfi->samefile_next; |
| LIST_UNLINK_PART(filetriggers, tfi, inoverall.); |
| edited: |
| filetriggers_edited = 1; |
| } |
| |
| static void |
| trig_file_interests_remove(void) |
| { |
| if (unlink(triggersfilefile) && errno != ENOENT) |
| ohshite(_("cannot remove `%.250s'"), triggersfilefile); |
| } |
| |
| static void |
| trig_file_interests_update(void) |
| { |
| struct trigfileint *tfi; |
| FILE *nf; |
| |
| nf = fopen(triggersnewfilefile, "w"); |
| if (!nf) |
| ohshite(_("unable to create new file triggers file `%.250s'"), |
| triggersnewfilefile); |
| push_cleanup(cu_closestream, ~ehflag_normaltidy, NULL, 0, 1, nf); |
| |
| for (tfi = filetriggers.head; tfi; tfi = tfi->inoverall.next) |
| fprintf(nf, "%s %s%s\n", trigh.namenode_name(tfi->fnn), |
| tfi->pkg->name, |
| (tfi->options == trig_noawait) ? "/noawait" : ""); |
| |
| if (ferror(nf)) |
| ohshite(_("unable to write new file triggers file `%.250s'"), |
| triggersnewfilefile); |
| if (fflush(nf)) |
| ohshite(_("unable to flush new file triggers file '%.250s'"), |
| triggersnewfilefile); |
| if (fsync(fileno(nf))) |
| ohshite(_("unable to sync new file triggers file '%.250s'"), |
| triggersnewfilefile); |
| pop_cleanup(ehflag_normaltidy); |
| if (fclose(nf)) |
| ohshite(_("unable to close new file triggers file `%.250s'"), |
| triggersnewfilefile); |
| |
| if (rename(triggersnewfilefile, triggersfilefile)) |
| ohshite(_("unable to install new file triggers file as `%.250s'"), |
| triggersfilefile); |
| } |
| |
| void |
| trig_file_interests_save(void) |
| { |
| if (filetriggers_edited <= 0) |
| return; |
| |
| if (!filetriggers.head) |
| trig_file_interests_remove(); |
| else |
| trig_file_interests_update(); |
| |
| dir_sync_path(triggersdir); |
| |
| filetriggers_edited = 0; |
| } |
| |
| void |
| trig_file_interests_ensure(void) |
| { |
| FILE *f; |
| char linebuf[1024], *space; |
| struct pkginfo *pkg; |
| const char *emsg; |
| |
| if (filetriggers_edited >= 0) |
| return; |
| |
| f = fopen(triggersfilefile, "r"); |
| if (!f) { |
| if (errno == ENOENT) |
| goto ok; |
| ohshite(_("unable to read file triggers file `%.250s'"), |
| triggersfilefile); |
| } |
| |
| push_cleanup(cu_closestream, ~0, NULL, 0, 1, f); |
| while (fgets_checked(linebuf, sizeof(linebuf), f, triggersfilefile) >= 0) { |
| char *slash; |
| enum trig_options trig_opts = trig_await; |
| space = strchr(linebuf, ' '); |
| if (!space || linebuf[0] != '/') |
| ohshit(_("syntax error in file triggers file `%.250s'"), |
| triggersfilefile); |
| *space++ = '\0'; |
| |
| slash = strchr(space, '/'); |
| if (slash && strcmp("/noawait", slash) == 0) { |
| trig_opts = trig_noawait; |
| *slash = '\0'; |
| } |
| emsg = pkg_name_is_illegal(space, NULL); |
| if (emsg) |
| ohshit(_("file triggers record mentions illegal " |
| "package name `%.250s' (for interest in file " |
| "`%.250s'): %.250s"), space, linebuf, emsg); |
| pkg = pkg_db_find(space); |
| trk_file_interest_change(linebuf, pkg, +2, trig_opts); |
| } |
| pop_cleanup(ehflag_normaltidy); |
| ok: |
| filetriggers_edited = 0; |
| } |
| |
| static struct filenamenode *filetriggers_activating; |
| |
| void |
| trig_file_activate_byname(const char *trig, struct pkginfo *aw) |
| { |
| struct filenamenode *fnn = trigh.namenode_find(trig, 1); |
| |
| if (fnn) |
| trig_file_activate(fnn, aw); |
| } |
| |
| void |
| trig_file_activate(struct filenamenode *trig, struct pkginfo *aw) |
| { |
| struct trigfileint *tfi; |
| |
| for (tfi = *trigh.namenode_interested(trig); tfi; |
| tfi = tfi->samefile_next) |
| trig_record_activation(tfi->pkg, (tfi->options == trig_noawait) ? |
| NULL : aw, trigh.namenode_name(trig)); |
| } |
| |
| static void |
| trk_file_activate_start(void) |
| { |
| filetriggers_activating = trigh.namenode_find(trig_activating_name, 1); |
| } |
| |
| static void |
| trk_file_activate_awaiter(struct pkginfo *aw) |
| { |
| if (!filetriggers_activating) |
| return; |
| trig_file_activate(filetriggers_activating, aw); |
| } |
| |
| static void |
| trk_file_activate_done(void) |
| { |
| } |
| |
| static const struct trigkindinfo tki_file = { |
| .activate_start = trk_file_activate_start, |
| .activate_awaiter = trk_file_activate_awaiter, |
| .activate_done = trk_file_activate_done, |
| .interest_change = trk_file_interest_change, |
| }; |
| |
| /*---------- Trigger control info file. ----------*/ |
| |
| static void |
| trig_cicb_interest_change(const char *trig, struct pkginfo *pkg, int signum, |
| enum trig_options opts) |
| { |
| const struct trigkindinfo *tki = trig_classify_byname(trig); |
| |
| assert(filetriggers_edited >= 0); |
| tki->interest_change(trig, pkg, signum, opts); |
| } |
| |
| void |
| trig_cicb_interest_delete(const char *trig, void *user, enum trig_options opts) |
| { |
| trig_cicb_interest_change(trig, user, -1, opts); |
| } |
| |
| void |
| trig_cicb_interest_add(const char *trig, void *user, enum trig_options opts) |
| { |
| trig_cicb_interest_change(trig, user, +1, opts); |
| } |
| |
| void |
| trig_cicb_statuschange_activate(const char *trig, void *user, |
| enum trig_options opts) |
| { |
| struct pkginfo *aw = user; |
| |
| trig_activate_start(trig); |
| dtki->activate_awaiter((opts == trig_noawait) ? NULL : aw); |
| dtki->activate_done(); |
| } |
| |
| static void |
| parse_ci_call(const char *file, const char *cmd, trig_parse_cicb *cb, |
| const char *trig, void *user, enum trig_options opts) |
| { |
| const char *emsg; |
| |
| emsg = trig_name_is_illegal(trig); |
| if (emsg) |
| ohshit(_("triggers ci file `%.250s' contains illegal trigger " |
| "syntax in trigger name `%.250s': %.250s"), |
| file, trig, emsg); |
| if (cb) |
| cb(trig, user, opts); |
| } |
| |
| void |
| trig_parse_ci(const char *file, trig_parse_cicb *interest, |
| trig_parse_cicb *activate, void *user) |
| { |
| FILE *f; |
| char linebuf[MAXTRIGDIRECTIVE], *cmd, *spc, *eol; |
| int l; |
| |
| f = fopen(file, "r"); |
| if (!f) { |
| if (errno == ENOENT) |
| return; /* No file is just like an empty one. */ |
| ohshite(_("unable to open triggers ci file `%.250s'"), file); |
| } |
| push_cleanup(cu_closestream, ~0, NULL, 0, 1, f); |
| |
| while ((l = fgets_checked(linebuf, sizeof(linebuf), f, file)) >= 0) { |
| for (cmd = linebuf; cisspace(*cmd); cmd++); |
| if (*cmd == '#') |
| continue; |
| for (eol = linebuf + l; eol > cmd && cisspace(eol[-1]); eol--); |
| if (eol == cmd) |
| continue; |
| *eol = '\0'; |
| |
| for (spc = cmd; *spc && !cisspace(*spc); spc++); |
| if (!*spc) |
| ohshit(_("triggers ci file contains unknown directive syntax")); |
| *spc++ = '\0'; |
| while (cisspace(*spc)) |
| spc++; |
| if (!strcmp(cmd, "interest")) { |
| parse_ci_call(file, cmd, interest, spc, user, trig_await); |
| } else if (!strcmp(cmd, "interest-noawait")) { |
| parse_ci_call(file, cmd, interest, spc, user, trig_noawait); |
| } else if (!strcmp(cmd, "activate")) { |
| parse_ci_call(file, cmd, activate, spc, user, trig_await); |
| } else if (!strcmp(cmd, "activate-noawait")) { |
| parse_ci_call(file, cmd, activate, spc, user, trig_noawait); |
| } else { |
| ohshit(_("triggers ci file contains unknown directive `%.250s'"), |
| cmd); |
| } |
| } |
| pop_cleanup(ehflag_normaltidy); /* fclose() */ |
| } |
| |
| /*---------- Unincorp file incorporation. ----------*/ |
| |
| static void |
| tdm_incorp_trig_begin(const char *trig) |
| { |
| trig_activate_start(trig); |
| } |
| |
| static void |
| tdm_incorp_package(const char *awname) |
| { |
| struct pkginfo *aw = strcmp(awname, "-") ? pkg_db_find(awname) : NULL; |
| |
| dtki->activate_awaiter(aw); |
| } |
| |
| static void |
| tdm_incorp_trig_end(void) |
| { |
| dtki->activate_done(); |
| } |
| |
| static const struct trigdefmeths tdm_incorp = { |
| .trig_begin = tdm_incorp_trig_begin, |
| .package = tdm_incorp_package, |
| .trig_end = tdm_incorp_trig_end |
| }; |
| |
| void |
| trig_incorporate(enum modstatdb_rw cstatus) |
| { |
| enum trigdef_update_status ur; |
| enum trigdef_updateflags tduf; |
| |
| free(triggersdir); |
| triggersdir = dpkg_db_get_path(TRIGGERSDIR); |
| |
| free(triggersfilefile); |
| triggersfilefile = trig_get_filename(triggersdir, "File"); |
| |
| free(triggersnewfilefile); |
| triggersnewfilefile = trig_get_filename(triggersdir, "File.new"); |
| |
| trigdef_set_methods(&tdm_incorp); |
| trig_file_interests_ensure(); |
| |
| tduf = tduf_nolockok; |
| if (cstatus >= msdbrw_write) { |
| tduf |= tduf_write; |
| if (trigh.transitional_activate) |
| tduf |= tduf_writeifenoent; |
| } |
| |
| ur = trigdef_update_start(tduf); |
| if (ur == tdus_error_no_dir && cstatus >= msdbrw_write) { |
| if (mkdir(triggersdir, 0755)) { |
| if (errno != EEXIST) |
| ohshite(_("unable to create triggers state" |
| " directory `%.250s'"), triggersdir); |
| } else if (chown(triggersdir, 0, 0)) { |
| ohshite(_("unable to set ownership of triggers state" |
| " directory `%.250s'"), triggersdir); |
| } |
| ur = trigdef_update_start(tduf); |
| } |
| switch (ur) { |
| case tdus_error_empty_deferred: |
| return; |
| case tdus_error_no_dir: |
| case tdus_error_no_deferred: |
| if (!trigh.transitional_activate) |
| return; |
| /* Fall through. */ |
| case tdus_no_deferred: |
| trigh.transitional_activate(cstatus); |
| break; |
| case tdus_ok: |
| /* Read and incorporate triggers. */ |
| trigdef_parse(); |
| break; |
| default: |
| internerr("unknown trigdef_update_start return value '%d'", ur); |
| } |
| |
| /* Right, that's it. New (empty) Unincorp can be installed. */ |
| trigdef_process_done(); |
| } |
| |
| /*---------- Default hooks. ----------*/ |
| |
| struct filenamenode { |
| struct filenamenode *next; |
| const char *name; |
| struct trigfileint *trig_interested; |
| }; |
| |
| static struct filenamenode *trigger_files; |
| |
| static struct filenamenode * |
| th_simple_nn_find(const char *name, bool nonew) |
| { |
| struct filenamenode *search; |
| |
| for (search = trigger_files; search; search = search->next) |
| if (!strcmp(search->name, name)) |
| return search; |
| |
| /* Not found. */ |
| if (nonew) |
| return NULL; |
| |
| search = nfmalloc(sizeof(*search)); |
| search->name = nfstrsave(name); |
| search->trig_interested = NULL; |
| search->next = trigger_files; |
| trigger_files = search; |
| |
| return search; |
| } |
| |
| TRIGHOOKS_DEFINE_NAMENODE_ACCESSORS |
| |
| static struct trig_hooks trigh = { |
| .enqueue_deferred = NULL, |
| .transitional_activate = NULL, |
| .namenode_find = th_simple_nn_find, |
| .namenode_interested = th_nn_interested, |
| .namenode_name = th_nn_name, |
| }; |
| |
| void |
| trig_override_hooks(const struct trig_hooks *hooks) |
| { |
| trigh = *hooks; |
| } |