Procházet zdrojové kódy

dpkg: implement "interest-noawait" and "activate-noawait" trigger commands

Those variants do not put triggering packages in triggers-awaited status
and thus do not record the package with the corresponding pending triggers
in the Triggers-Awaited field.

This should be used for triggers which do not provide essential
functionality when we can safely consider that the triggering packages
are able to satisfy dependencies even if the trigger processing
has not yet happened.
Raphaël Hertzog před 15 roky
rodič
revize
cf6b98d373
6 změnil soubory, kde provedl 174 přidání a 33 odebrání
  1. 81 0
      debian/dpkg.prerm
  2. 2 1
      doc/triggers.txt
  3. 55 23
      lib/dpkg/triglib.c
  4. 11 4
      lib/dpkg/triglib.h
  5. 19 1
      man/deb-triggers.5
  6. 6 4
      src/trigproc.c

+ 81 - 0
debian/dpkg.prerm

@@ -0,0 +1,81 @@
+#!/bin/sh -e
+# This script can be called in the following ways:
+#
+# Before the package is removed:
+#      <prerm> remove
+#
+# Before an upgrade:
+#      <old-prerm> upgrade <new-version>
+# if that fails:
+#      <new-prerm> failed-upgrade <old-version>
+#
+#
+# Before package is deconfigured while dependency is replaced due to conflict:
+#      <prerm> deconfigure in-favour <new-package> <version>
+#              removing <old-package> <version>
+#
+# Before the package is replaced due to conflict:
+#      <prerm> remove in-favour <new-package> <version>
+
+ensure_no_triggers_noawait()
+{
+    admindir=${DPKG_ADMINDIR:-/var/lib/dpkg}
+    pkgadmindir=$admindir/info
+
+    trig_noawait=$(find "$pkgadmindir" -name "*.triggers" -type f | \
+                   xargs -r grep -El "^(interest|activate)-(no)?await" | \
+		   sed -e 's,^.*/\([^/.:]\+\)[^/]\+$,\1,')
+
+    # Abort if we cannot possibly downgrade
+    if [ -n "$trig_noawait" ]; then
+        cat <<- MSG
+	dpkg: error: You have packages using the "interest-noawait" and/or
+	"activate-noawait" trigger directives but the dpkg version that
+	you're trying to downgrade to doesn't support them. Aborting
+	downgrade.
+
+	List of affected packages:
+
+	$trig_noawait
+	MSG
+        exit 1
+    fi
+
+    bad_triggers_files=$(find "$admindir/triggers" -type f | \
+                         xargs -r grep -l "/noawait$" || true)
+    if [ -n "$bad_triggers_files" ]; then
+        cat <<- MSG
+	dpkg: error: Some internal trigger files unexpectedly reference
+	packages tagged with "/noawait" while their corresponding
+	infodb files doesn't seem to contain any "interest-noawait"
+	directive. Aborting the downgrade as those tags are not supported
+	by the version you're trying to downgrade to.
+
+	List of internal trigger files that are affected:
+
+	$bad_triggers_files
+	MSG
+	exit 1
+    fi
+}
+
+case "$1" in
+    upgrade)
+        # Allow the downgrade only if no package is using the
+	# (interest|activate)-noawait trigger directives
+        if dpkg --compare-versions "$2" lt 1.16.1~; then
+            ensure_no_triggers_noawait
+        fi
+        ;;
+
+    remove|failed-upgrade|deconfigure)
+        ;;
+
+    *)
+        echo "$0 called with unknown argument \`$1'" 1>&2
+        exit 1
+        ;;
+esac
+
+#DEBHELPER#
+exit 0

+ 2 - 1
doc/triggers.txt

@@ -46,7 +46,8 @@ will not be processed immediately when it is activated; processing is
 deferred until it is convenient (as described below).
 deferred until it is convenient (as described below).
 
 
 At a trigger activation, the interested packages(s) are added to the
 At a trigger activation, the interested packages(s) are added to the
-triggering package's list of triggers-awaited packages; the triggering
+triggering package's list of triggers-awaited packages (unless the
+trigger has been configured to not require it); the triggering
 package is said to await the trigger processing.
 package is said to await the trigger processing.
 
 
 A package which has pending triggers, or which awaits triggers, is not
 A package which has pending triggers, or which awaits triggers, is not

+ 55 - 23
lib/dpkg/triglib.c

@@ -253,7 +253,8 @@ struct trigkindinfo {
 	/* Rest are for everyone: */
 	/* Rest are for everyone: */
 	void (*activate_awaiter)(struct pkginfo *pkg /* may be NULL */);
 	void (*activate_awaiter)(struct pkginfo *pkg /* may be NULL */);
 	void (*activate_done)(void);
 	void (*activate_done)(void);
-	void (*interest_change)(const char *name, struct pkginfo *pkg, int signum);
+	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 tki_explicit, tki_file, tki_unknown;
@@ -318,7 +319,8 @@ trk_unknown_activate_done(void)
 }
 }
 
 
 static void DPKG_ATTR_NORET
 static void DPKG_ATTR_NORET
-trk_unknown_interest_change(const char *trig, struct pkginfo *pkg, int signum)
+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'"
 	ohshit(_("invalid or unknown syntax in trigger name `%.250s'"
 	         " (in trigger interests for package `%.250s')"),
 	         " (in trigger interests for package `%.250s')"),
@@ -393,13 +395,21 @@ trk_explicit_activate_awaiter(struct pkginfo *aw)
 		        trk_explicit_fn.buf);
 		        trk_explicit_fn.buf);
 
 
 	while (trk_explicit_fgets(buf, sizeof(buf)) >= 0) {
 	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);
 		emsg = pkg_name_is_illegal(buf, NULL);
 		if (emsg)
 		if (emsg)
 			ohshit(_("trigger interest file `%.250s' syntax error; "
 			ohshit(_("trigger interest file `%.250s' syntax error; "
 			         "illegal package name `%.250s': %.250s"),
 			         "illegal package name `%.250s': %.250s"),
 			       trk_explicit_fn.buf, buf, emsg);
 			       trk_explicit_fn.buf, buf, emsg);
 		pend = pkg_db_find(buf);
 		pend = pkg_db_find(buf);
-		trig_record_activation(pend, aw, trk_explicit_trig);
+		trig_record_activation(pend, noawait ? NULL : aw,
+		                       trk_explicit_trig);
 	}
 	}
 }
 }
 
 
@@ -435,7 +445,8 @@ trk_explicit_interest_remove(const char *newfilename)
 }
 }
 
 
 static void
 static void
-trk_explicit_interest_change(const char *trig,  struct pkginfo *pkg, int signum)
+trk_explicit_interest_change(const char *trig,  struct pkginfo *pkg, int signum,
+                             enum trig_options opts)
 {
 {
 	static struct varbuf newfn;
 	static struct varbuf newfn;
 	char buf[1024];
 	char buf[1024];
@@ -453,13 +464,16 @@ trk_explicit_interest_change(const char *trig,  struct pkginfo *pkg, int signum)
 	push_cleanup(cu_closestream, ~ehflag_normaltidy, NULL, 0, 1, nf);
 	push_cleanup(cu_closestream, ~ehflag_normaltidy, NULL, 0, 1, nf);
 
 
 	while (trk_explicit_f && trk_explicit_fgets(buf, sizeof(buf)) >= 0) {
 	while (trk_explicit_f && trk_explicit_fgets(buf, sizeof(buf)) >= 0) {
-		if (!strcmp(buf, pkg->name))
+		int len = strlen(pkg->name);
+		if (strncmp(buf, pkg->name, len) == 0 &&
+		    (buf[len] == '\0' || buf[len] == '/'))
 			continue;
 			continue;
 		fprintf(nf, "%s\n", buf);
 		fprintf(nf, "%s\n", buf);
 		empty = false;
 		empty = false;
 	}
 	}
 	if (signum > 0) {
 	if (signum > 0) {
-		fprintf(nf, "%s\n", pkg->name);
+		fprintf(nf, "%s%s\n", pkg->name,
+		        (opts == trig_noawait) ? "/noawait" : "");
 		empty = false;
 		empty = false;
 	}
 	}
 
 
@@ -506,7 +520,8 @@ static int filetriggers_edited = -1;
  * but die if already present.
  * but die if already present.
  */
  */
 static void
 static void
-trk_file_interest_change(const char *trig, struct pkginfo *pkg, int signum)
+trk_file_interest_change(const char *trig, struct pkginfo *pkg, int signum,
+                         enum trig_options opts)
 {
 {
 	struct filenamenode *fnn;
 	struct filenamenode *fnn;
 	struct trigfileint **search, *tfi;
 	struct trigfileint **search, *tfi;
@@ -530,6 +545,7 @@ trk_file_interest_change(const char *trig, struct pkginfo *pkg, int signum)
 	tfi = nfmalloc(sizeof(*tfi));
 	tfi = nfmalloc(sizeof(*tfi));
 	tfi->pkg = pkg;
 	tfi->pkg = pkg;
 	tfi->fnn = fnn;
 	tfi->fnn = fnn;
+	tfi->options = opts;
 	tfi->samefile_next = *trigh.namenode_interested(fnn);
 	tfi->samefile_next = *trigh.namenode_interested(fnn);
 	*trigh.namenode_interested(fnn) = tfi;
 	*trigh.namenode_interested(fnn) = tfi;
 
 
@@ -537,6 +553,7 @@ trk_file_interest_change(const char *trig, struct pkginfo *pkg, int signum)
 	goto edited;
 	goto edited;
 
 
 found:
 found:
+	tfi->options = opts;
 	if (signum > 1)
 	if (signum > 1)
 		ohshit(_("duplicate file trigger interest for filename `%.250s' "
 		ohshit(_("duplicate file trigger interest for filename `%.250s' "
 		         "and package `%.250s'"), trig, pkg->name);
 		         "and package `%.250s'"), trig, pkg->name);
@@ -570,8 +587,9 @@ trig_file_interests_update(void)
 	push_cleanup(cu_closestream, ~ehflag_normaltidy, NULL, 0, 1, nf);
 	push_cleanup(cu_closestream, ~ehflag_normaltidy, NULL, 0, 1, nf);
 
 
 	for (tfi = filetriggers.head; tfi; tfi = tfi->inoverall.next)
 	for (tfi = filetriggers.head; tfi; tfi = tfi->inoverall.next)
-		fprintf(nf, "%s %s\n", trigh.namenode_name(tfi->fnn),
-		        tfi->pkg->name);
+		fprintf(nf, "%s %s%s\n", trigh.namenode_name(tfi->fnn),
+		        tfi->pkg->name,
+		        (tfi->options == trig_noawait) ? "/noawait" : "");
 
 
 	if (ferror(nf))
 	if (ferror(nf))
 		ohshite(_("unable to write new file triggers file `%.250s'"),
 		ohshite(_("unable to write new file triggers file `%.250s'"),
@@ -629,19 +647,26 @@ trig_file_interests_ensure(void)
 
 
 	push_cleanup(cu_closestream, ~0, NULL, 0, 1, f);
 	push_cleanup(cu_closestream, ~0, NULL, 0, 1, f);
 	while (fgets_checked(linebuf, sizeof(linebuf), f, triggersfilefile) >= 0) {
 	while (fgets_checked(linebuf, sizeof(linebuf), f, triggersfilefile) >= 0) {
+		char *slash;
+		enum trig_options trig_opts = trig_await;
 		space = strchr(linebuf, ' ');
 		space = strchr(linebuf, ' ');
 		if (!space || linebuf[0] != '/')
 		if (!space || linebuf[0] != '/')
 			ohshit(_("syntax error in file triggers file `%.250s'"),
 			ohshit(_("syntax error in file triggers file `%.250s'"),
 			       triggersfilefile);
 			       triggersfilefile);
 		*space++ = '\0';
 		*space++ = '\0';
 
 
+		slash = strchr(space, '/');
+		if (slash && strcmp("/noawait", slash) == 0) {
+			trig_opts = trig_noawait;
+			*slash = '\0';
+		}
 		emsg = pkg_name_is_illegal(space, NULL);
 		emsg = pkg_name_is_illegal(space, NULL);
 		if (emsg)
 		if (emsg)
 			ohshit(_("file triggers record mentions illegal "
 			ohshit(_("file triggers record mentions illegal "
 			         "package name `%.250s' (for interest in file "
 			         "package name `%.250s' (for interest in file "
 				 "`%.250s'): %.250s"), space, linebuf, emsg);
 				 "`%.250s'): %.250s"), space, linebuf, emsg);
 		pkg = pkg_db_find(space);
 		pkg = pkg_db_find(space);
-		trk_file_interest_change(linebuf, pkg, +2);
+		trk_file_interest_change(linebuf, pkg, +2, trig_opts);
 	}
 	}
 	pop_cleanup(ehflag_normaltidy);
 	pop_cleanup(ehflag_normaltidy);
 ok:
 ok:
@@ -666,7 +691,8 @@ trig_file_activate(struct filenamenode *trig, struct pkginfo *aw)
 
 
 	for (tfi = *trigh.namenode_interested(trig); tfi;
 	for (tfi = *trigh.namenode_interested(trig); tfi;
 	     tfi = tfi->samefile_next)
 	     tfi = tfi->samefile_next)
-		trig_record_activation(tfi->pkg, aw, trigh.namenode_name(trig));
+		trig_record_activation(tfi->pkg, (tfi->options == trig_noawait) ?
+		                       NULL : aw, trigh.namenode_name(trig));
 }
 }
 
 
 static void
 static void
@@ -698,39 +724,41 @@ static const struct trigkindinfo tki_file = {
 /*---------- Trigger control info file. ----------*/
 /*---------- Trigger control info file. ----------*/
 
 
 static void
 static void
-trig_cicb_interest_change(const char *trig, struct pkginfo *pkg, int signum)
+trig_cicb_interest_change(const char *trig, struct pkginfo *pkg, int signum,
+                          enum trig_options opts)
 {
 {
 	const struct trigkindinfo *tki = trig_classify_byname(trig);
 	const struct trigkindinfo *tki = trig_classify_byname(trig);
 
 
 	assert(filetriggers_edited >= 0);
 	assert(filetriggers_edited >= 0);
-	tki->interest_change(trig, pkg, signum);
+	tki->interest_change(trig, pkg, signum, opts);
 }
 }
 
 
 void
 void
-trig_cicb_interest_delete(const char *trig, void *user)
+trig_cicb_interest_delete(const char *trig, void *user, enum trig_options opts)
 {
 {
-	trig_cicb_interest_change(trig, user, -1);
+	trig_cicb_interest_change(trig, user, -1, opts);
 }
 }
 
 
 void
 void
-trig_cicb_interest_add(const char *trig, void *user)
+trig_cicb_interest_add(const char *trig, void *user, enum trig_options opts)
 {
 {
-	trig_cicb_interest_change(trig, user, +1);
+	trig_cicb_interest_change(trig, user, +1, opts);
 }
 }
 
 
 void
 void
-trig_cicb_statuschange_activate(const char *trig, void *user)
+trig_cicb_statuschange_activate(const char *trig, void *user,
+                                enum trig_options opts)
 {
 {
 	struct pkginfo *aw = user;
 	struct pkginfo *aw = user;
 
 
 	trig_activate_start(trig);
 	trig_activate_start(trig);
-	dtki->activate_awaiter(aw);
+	dtki->activate_awaiter((opts == trig_noawait) ? NULL : aw);
 	dtki->activate_done();
 	dtki->activate_done();
 }
 }
 
 
 static void
 static void
 parse_ci_call(const char *file, const char *cmd, trig_parse_cicb *cb,
 parse_ci_call(const char *file, const char *cmd, trig_parse_cicb *cb,
-              const char *trig, void *user)
+              const char *trig, void *user, enum trig_options opts)
 {
 {
 	const char *emsg;
 	const char *emsg;
 
 
@@ -740,7 +768,7 @@ parse_ci_call(const char *file, const char *cmd, trig_parse_cicb *cb,
 		         "syntax in trigger name `%.250s': %.250s"),
 		         "syntax in trigger name `%.250s': %.250s"),
 		       file, trig, emsg);
 		       file, trig, emsg);
 	if (cb)
 	if (cb)
-		cb(trig, user);
+		cb(trig, user, opts);
 }
 }
 
 
 void
 void
@@ -775,9 +803,13 @@ trig_parse_ci(const char *file, trig_parse_cicb *interest,
 		while (cisspace(*spc))
 		while (cisspace(*spc))
 			spc++;
 			spc++;
 		if (!strcmp(cmd, "interest")) {
 		if (!strcmp(cmd, "interest")) {
-			parse_ci_call(file, cmd, interest, spc, user);
+			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")) {
 		} else if (!strcmp(cmd, "activate")) {
-			parse_ci_call(file, cmd, activate, spc, user);
+			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 {
 		} else {
 			ohshit(_("triggers ci file contains unknown directive `%.250s'"),
 			ohshit(_("triggers ci file contains unknown directive `%.250s'"),
 			       cmd);
 			       cmd);

+ 11 - 4
lib/dpkg/triglib.h

@@ -38,9 +38,15 @@ DPKG_BEGIN_DECLS
 
 
 const char *trig_name_is_illegal(const char *p);
 const char *trig_name_is_illegal(const char *p);
 
 
+enum trig_options {
+	trig_await,
+	trig_noawait
+};
+
 struct trigfileint {
 struct trigfileint {
 	struct pkginfo *pkg;
 	struct pkginfo *pkg;
 	struct filenamenode *fnn;
 	struct filenamenode *fnn;
+	enum trig_options options;
 	struct trigfileint *samefile_next;
 	struct trigfileint *samefile_next;
 	struct {
 	struct {
 		struct trigfileint *next, *prev;
 		struct trigfileint *next, *prev;
@@ -83,10 +89,11 @@ void trig_fixup_awaiters(enum modstatdb_rw cstatus);
 void trig_file_interests_ensure(void);
 void trig_file_interests_ensure(void);
 void trig_file_interests_save(void);
 void trig_file_interests_save(void);
 
 
-typedef void trig_parse_cicb(const char *trig, void *user);
-void trig_cicb_interest_delete(const char *trig, void *user);
-void trig_cicb_interest_add(const char *trig, void *user);
-void trig_cicb_statuschange_activate(const char *trig, void *user);
+typedef void trig_parse_cicb(const char *trig, void *user, enum trig_options to);
+void trig_cicb_interest_delete(const char *trig, void *user, enum trig_options to);
+void trig_cicb_interest_add(const char *trig, void *user, enum trig_options to);
+void trig_cicb_statuschange_activate(const char *trig, void *user,
+                                     enum trig_options to);
 void trig_parse_ci(const char *file, trig_parse_cicb *interest,
 void trig_parse_ci(const char *file, trig_parse_cicb *interest,
                    trig_parse_cicb *activate, void *user);
                    trig_parse_cicb *activate, void *user);
 
 

+ 19 - 1
man/deb-triggers.5

@@ -21,19 +21,32 @@ The trigger control directives currently supported are:
 .I trigger-name
 .I trigger-name
 .PP
 .PP
 .in +5
 .in +5
+.B interest-noawait
+.I trigger-name
+.PP
+.in +5
 Specifies that the package is interested in the named trigger. All
 Specifies that the package is interested in the named trigger. All
 triggers in which a package is interested must be listed using this
 triggers in which a package is interested must be listed using this
-directive in the triggers control file.
+directive in the triggers control file. The "noawait" variant does
+not put the triggering packages in triggers-awaited state. This should
+be used when the functionality provided by the trigger is not crucial.
 .PP
 .PP
 .in +5
 .in +5
 .B activate
 .B activate
 .I trigger-name
 .I trigger-name
 .PP
 .PP
 .in +5
 .in +5
+.B activate-noawait
+.I trigger-name
+.PP
+.in +5
 Arranges that changes to this package's state will activate the
 Arranges that changes to this package's state will activate the
 specified trigger. The trigger will be activated at the start of
 specified trigger. The trigger will be activated at the start of
 the following operations: unpack, configure, remove (including for
 the following operations: unpack, configure, remove (including for
 the benefit of a conflicting package), purge and deconfigure.
 the benefit of a conflicting package), purge and deconfigure.
+The "noawait" variant does not put the triggering packages in
+triggers-awaited state. This should be used when the functionality
+provided by the trigger is not crucial.
 .PP
 .PP
 .in +5
 .in +5
 If this package disappears during the unpacking of another package
 If this package disappears during the unpacking of another package
@@ -45,6 +58,11 @@ versions of the package will be activated.
 .PP
 .PP
 Unknown directives are an error which will prevent installation of the
 Unknown directives are an error which will prevent installation of the
 package.
 package.
+.PP
+The "\-noawait" variants are only supported by dpkg 1.16.1 or newer, and
+will lead to errors if used with an older dpkg. It is thus recommended
+to add a "Pre-Depends: dpkg (>= 1.16.1)" to any package that wish to use
+those directives.
 .
 .
 .SH SEE ALSO
 .SH SEE ALSO
 .BR dpkg\-trigger (1),
 .BR dpkg\-trigger (1),

+ 6 - 4
src/trigproc.c

@@ -345,7 +345,8 @@ trigproc(struct pkginfo *pkg)
 /*========== Transitional global activation. ==========*/
 /*========== Transitional global activation. ==========*/
 
 
 static void
 static void
-transitional_interest_callback_ro(const char *trig, void *user)
+transitional_interest_callback_ro(const char *trig, void *user,
+                                  enum trig_options opts)
 {
 {
 	struct pkginfo *pend = user;
 	struct pkginfo *pend = user;
 
 
@@ -357,12 +358,13 @@ transitional_interest_callback_ro(const char *trig, void *user)
 }
 }
 
 
 static void
 static void
-transitional_interest_callback(const char *trig, void *user)
+transitional_interest_callback(const char *trig, void *user,
+                               enum trig_options opts)
 {
 {
 	struct pkginfo *pend = user;
 	struct pkginfo *pend = user;
 
 
-	trig_cicb_interest_add(trig, pend);
-	transitional_interest_callback_ro(trig, user);
+	trig_cicb_interest_add(trig, pend, opts);
+	transitional_interest_callback_ro(trig, user, opts);
 }
 }
 
 
 /*
 /*