summaryrefslogtreecommitdiffstats
path: root/lib/dpkg/trigdeferred.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dpkg/trigdeferred.c')
-rw-r--r--lib/dpkg/trigdeferred.c285
1 files changed, 285 insertions, 0 deletions
diff --git a/lib/dpkg/trigdeferred.c b/lib/dpkg/trigdeferred.c
new file mode 100644
index 0000000..6e49e25
--- /dev/null
+++ b/lib/dpkg/trigdeferred.c
@@ -0,0 +1,285 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * trigdeferred.c - parsing of triggers/Unincorp (was …/Deferred)
+ *
+ * Copyright © 2007 Canonical Ltd
+ * written by Ian Jackson <ijackson@chiark.greenend.org.uk>
+ * Copyright © 2008-2016 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include <dpkg/i18n.h>
+#include <dpkg/dpkg.h>
+#include <dpkg/dpkg-db.h>
+#include <dpkg/c-ctype.h>
+#include <dpkg/file.h>
+#include <dpkg/dir.h>
+#include <dpkg/trigdeferred.h>
+#include <dpkg/triglib.h>
+
+static struct varbuf fn, newfn;
+
+static const struct trigdefmeths *trigdef;
+
+/*---------- Deferred file handling ----------*/
+
+static char *triggersdir;
+static int lock_fd = -1;
+static FILE *old_deferred;
+static FILE *trig_new_deferred;
+
+static void
+constructfn(struct varbuf *vb, const char *dir, const char *tail)
+{
+ varbuf_reset(vb);
+ varbuf_add_dir(vb, dir);
+ varbuf_add_str(vb, tail);
+ varbuf_end_str(vb);
+}
+
+/**
+ * Start processing of the triggers deferred file.
+ *
+ * @retval -1 Lock ENOENT with O_CREAT (directory does not exist).
+ * @retval -2 Unincorp empty, TDUF_WRITE_IF_EMPTY unset.
+ * @retval -3 Unincorp ENOENT, TDUF_WRITE_IF_ENOENT unset.
+ * @retval 1 Unincorp ENOENT, TDUF_WRITE_IF_ENOENT set.
+ * @retval 2 Ok.
+ *
+ * For positive return values the caller must call trigdef_update_done!
+ */
+enum trigdef_update_status
+trigdef_update_start(enum trigdef_update_flags uf)
+{
+ free(triggersdir);
+ triggersdir = dpkg_db_get_path(TRIGGERSDIR);
+
+ if (uf & TDUF_WRITE) {
+ constructfn(&fn, triggersdir, TRIGGERSLOCKFILE);
+ if (lock_fd == -1) {
+ lock_fd = open(fn.buf, O_RDWR | O_CREAT | O_TRUNC, 0600);
+ if (lock_fd == -1) {
+ if (!(errno == ENOENT && (uf & TDUF_NO_LOCK_OK)))
+ ohshite(_("unable to open/create "
+ "triggers lock file '%.250s'"),
+ fn.buf);
+ return TDUS_ERROR_NO_DIR;
+ }
+ }
+
+ file_lock(&lock_fd, FILE_LOCK_WAIT, fn.buf,
+ _("triggers database lock"));
+ }
+
+ constructfn(&fn, triggersdir, TRIGGERSDEFERREDFILE);
+
+ if (old_deferred)
+ fclose(old_deferred);
+ old_deferred = fopen(fn.buf, "r");
+ if (!old_deferred) {
+ if (errno != ENOENT)
+ ohshite(_("unable to open triggers deferred file '%.250s'"),
+ fn.buf);
+ if (!(uf & TDUF_WRITE_IF_ENOENT)) {
+ if (uf & TDUF_WRITE)
+ pop_cleanup(ehflag_normaltidy);
+ return TDUS_ERROR_NO_DEFERRED;
+ }
+ } else {
+ struct stat stab;
+ int rc;
+
+ setcloexec(fileno(old_deferred), fn.buf);
+
+ rc = fstat(fileno(old_deferred), &stab);
+ if (rc < 0)
+ ohshite(_("unable to stat triggers deferred file '%.250s'"),
+ fn.buf);
+
+ if (stab.st_size == 0 && !(uf & TDUF_WRITE_IF_EMPTY)) {
+ if (uf & TDUF_WRITE)
+ pop_cleanup(ehflag_normaltidy);
+ return TDUS_ERROR_EMPTY_DEFERRED;
+ }
+ }
+
+ if (uf & TDUF_WRITE) {
+ constructfn(&newfn, triggersdir, TRIGGERSDEFERREDFILE ".new");
+ if (trig_new_deferred)
+ fclose(trig_new_deferred);
+ trig_new_deferred = fopen(newfn.buf, "w");
+ if (!trig_new_deferred)
+ ohshite(_("unable to open/create new triggers deferred file '%.250s'"),
+ newfn.buf);
+
+ setcloexec(fileno(trig_new_deferred), newfn.buf);
+ }
+
+ if (!old_deferred)
+ return TDUS_NO_DEFERRED;
+
+ return TDUS_OK;
+}
+
+void
+trigdef_set_methods(const struct trigdefmeths *methods)
+{
+ trigdef = methods;
+}
+
+void
+trigdef_update_printf(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(trig_new_deferred, format, ap);
+ va_end(ap);
+}
+
+static void
+trigdef_parse_error(int line_num, const char *line, const char *ptr)
+{
+ ohshit(_("syntax error in triggers deferred file '%.250s' at "
+ "line %d character %zd '%s'"),
+ fn.buf, line_num, ptr - line + 1, ptr);
+}
+
+/* Trim leading space. */
+static char *
+trigdef_skip_whitespace(char *ptr)
+{
+ while (*ptr) {
+ if (!c_iswhite(*ptr))
+ break;
+ ptr++;
+ }
+
+ return ptr;
+}
+
+int
+trigdef_parse(void)
+{
+ char line[_POSIX2_LINE_MAX];
+ char *ptr, *ptr_ini;
+ int line_num = 0;
+
+ while (fgets_checked(line, sizeof(line), old_deferred, fn.buf) > 0) {
+ line_num++;
+
+ ptr = trigdef_skip_whitespace(line);
+
+ /* Skip comments and empty lines. */
+ if (*ptr == '\0' || *ptr == '#')
+ continue;
+
+ /* Parse the trigger directive. */
+ ptr_ini = ptr;
+ while (*ptr) {
+ if (*ptr < 0x21 || *ptr > 0x7e)
+ break;
+ ptr++;
+ }
+
+ if (*ptr == '\0' || ptr_ini == ptr)
+ trigdef_parse_error(line_num, line, ptr);
+ *ptr++ = '\0';
+
+ /* Set the trigger directive. */
+ trigdef->trig_begin(ptr_ini);
+
+ /* Parse the package names. */
+ while (*ptr) {
+ ptr = trigdef_skip_whitespace(ptr);
+
+ ptr_ini = ptr;
+ if (*ptr == '\0' ||
+ !(c_isdigit(*ptr) || c_islower(*ptr) || *ptr == '-'))
+ trigdef_parse_error(line_num, line, ptr);
+
+ while (*++ptr) {
+ if (!c_isdigit(*ptr) && !c_islower(*ptr) &&
+ *ptr != '-' && *ptr != ':' &&
+ *ptr != '+' && *ptr != '.')
+ break;
+ }
+
+ if (*ptr != '\0' && *ptr != '#' && !c_iswhite(*ptr))
+ trigdef_parse_error(line_num, line, ptr);
+ *ptr++ = '\0';
+
+ if (ptr_ini[0] == '-' && ptr_ini[1])
+ ohshit(_("invalid package name '%.250s' in "
+ "triggers deferred file '%.250s'"),
+ ptr_ini, fn.buf);
+
+ /* Set the package name. */
+ trigdef->package(ptr_ini);
+ }
+
+ trigdef->trig_end();
+ }
+
+ return 0;
+}
+
+void
+trigdef_process_done(void)
+{
+ int r;
+
+ if (old_deferred) {
+ if (ferror(old_deferred))
+ ohshite(_("error reading triggers deferred file '%.250s'"),
+ fn.buf);
+ fclose(old_deferred);
+ old_deferred = NULL;
+ }
+
+ if (trig_new_deferred) {
+ if (ferror(trig_new_deferred))
+ ohshite(_("unable to write new triggers deferred "
+ "file '%.250s'"), newfn.buf);
+ r = fclose(trig_new_deferred);
+ trig_new_deferred = NULL;
+ if (r)
+ ohshite(_("unable to close new triggers deferred "
+ "file '%.250s'"), newfn.buf);
+
+ if (rename(newfn.buf, fn.buf))
+ ohshite(_("unable to install new triggers deferred "
+ "file '%.250s'"), fn.buf);
+
+ dir_sync_path(triggersdir);
+ }
+
+ free(triggersdir);
+ triggersdir = NULL;
+
+ /* Unlock. */
+ if (lock_fd >= 0)
+ pop_cleanup(ehflag_normaltidy);
+}