Subject: bin/25785: Non strict dependency in rcorder(8)
To: None <gnats-bugs@gnats.NetBSD.org>
From: Mike M. Volokhov <mishka@apk.od.ua>
List: netbsd-bugs
Date: 06/02/2004 15:33:30
>Number:         25785
>Category:       bin
>Synopsis:       Non strict dependency in rcorder(8)
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Wed Jun 02 12:35:00 UTC 2004
>Closed-Date:
>Last-Modified:
>Originator:     Mike M. Volokhov
>Release:        NetBSD current
>Organization:
ISPK
>Environment:
N/V
>Description:

	Greetings!

	[Excuse me please for annoying, but I really need this feature.
	I have wait for response on tech-userlevel@ and current-users@,
	but got no reply.]

	Sometimes a dependencies required within BEFORE/REQUIRE
	keywords at some rcorder-based script may not been available,
	and this must not be treated as error. However, in current
	rcorder(8) implementation if mentioned keywords declare
	such (inavailable) dependencies, this will produce warning
	messages and return code for rcorder will be set to non-zero
	value.

	See also the following messages at the mailing lists:
	http://mail-index.NetBSD.org/tech-userlevel/2004/02/04/0001.html
	http://mail-index.NetBSD.org/tech-userlevel/2004/02/04/0002.html
	http://mail-index.NetBSD.org/tech-userlevel/2004/05/20/0002.html
	http://mail-index.NetBSD.org/tech-userlevel/2004/05/24/0000.html

>How-To-Repeat:

	For example, we have a program which contains three
	independend daemons: network server, processing server,
	and some daemon which will link them both together. All
	three daemons may be splitted on three separate hosts
	accordingly to its architecture, or may be stored on single
	host. In latter case, it must be started in the following
	order:

		1. procsrv,
		2. linkd,
		3. netsrv.

	Thus, we can create three rcorder-based scripts:

		# PROVIDE: procsrv

		# PROVIDE: netsrv

		# PROVIDE: linkd
		# REQUIRE: procsrv
		# BEFORE: netsrv

	or something with similar approach.  But, if all three (or
	even two) daemons will be served by various network hosts,
	that interdependencies for "linkd" will be broken.

	The real example is an sendmail Milter filters. They may
	be accessed via the network, or via the UNIX sockets. In
	second case it must be started *before* sendmail daemon,
	but explicit declaration of this fact will produce an error
	if filter will be used by the first way.

>Fix:

	This problem may be solved using special REQUIRE/BEFORE
	keywords for all thus optional requirements. The provided
	patch declares three keywords:

		"# OPT-REQUIRE:"
		"# OPT-REQUIRES:"
		"# OPT-BEFORE:"

	They may be used together with usual REQUIRE/BEFORE keywords,
	for example:

		# PROVIDE: linkd
		# BEFORE: LOGIN
		# OPT-REQUIRE: procsrv
		# OPT-BEFORE: netsrv

	Thus, if requested by OPT-* keywords dependecies are
	available, they will be processed as usual REQUIRE/BEFORE.
	If some listed dependencies are not available, the corresponded
	OPT-* keywords will be ignored at all.

	--
	TIA, Mishka.


Index: rcorder.8
===================================================================
RCS file: /cvsroot/src/sbin/rcorder/rcorder.8,v
retrieving revision 1.6
diff -u -r1.6 rcorder.8
--- rcorder.8	24 Apr 2003 03:15:45 -0000	1.6
+++ rcorder.8	24 May 2004 09:04:09 -0000
@@ -60,8 +60,10 @@
 .Pp
 Within each file, a block containing a series of
 .Dq REQUIRE ,
+.Dq OPT-REQUIRE ,
 .Dq PROVIDE ,
-.Dq BEFORE
+.Dq BEFORE ,
+.Dq OPT-BEFORE
 and
 .Dq KEYWORD
 lines should appear.
@@ -71,7 +73,9 @@
 followed by a single space, followed by
 .Dq PROVIDE: ,
 .Dq REQUIRE: ,
+.Dq OPT-REQUIRE: ,
 .Dq BEFORE: ,
+.Dq OPT-BEFORE: ,
 or
 .Dq KEYWORD: .
 No deviation is permitted.
@@ -80,7 +84,9 @@
 Multiple
 .Dq PROVIDE ,
 .Dq REQUIRE ,
-.Dq BEFORE
+.Dq OPT-REQUIRE: ,
+.Dq BEFORE ,
+.Dq OPT-BEFORE
 and
 .Dq KEYWORD
 lines may appear, but all such lines must appear in a sequence without
@@ -129,11 +135,24 @@
 A file containing no
 .Dq PROVIDE ,
 .Dq REQUIRE ,
+.Dq OPT-REQUIRE ,
+.Dq BEFORE ,
 or
-.Dq BEFORE
+.Dq OPT-BEFORE
 lines may be output at an arbitrary position in the dependency
 ordering.
 .Pp
+A special lines
+.Dq OPT-REQUIRE
+and
+.Dq OPT-BEFORE
+should be used in case if given conditions may not have any providers,
+but this must not be treated as error. If providers are existent,
+they works just like plain
+.Dq REQUIRE
+and
+.Dq BEFORE .
+.Pp
 There must be at least one file with no dependencies in the set of
 arguments passed to
 .Nm
Index: rcorder.c
===================================================================
RCS file: /cvsroot/src/sbin/rcorder/rcorder.c,v
retrieving revision 1.12
diff -u -r1.12 rcorder.c
--- rcorder.c	13 Oct 2003 14:22:20 -0000	1.12
+++ rcorder.c	24 May 2004 09:04:34 -0000
@@ -81,12 +81,18 @@
 #define REQUIRE_LEN	(sizeof(REQUIRE_STR) - 1)
 #define REQUIRES_STR	"# REQUIRES:"
 #define REQUIRES_LEN	(sizeof(REQUIRES_STR) - 1)
+#define OPT_REQUIRE_STR	"# OPT-REQUIRE:"
+#define OPT_REQUIRE_LEN	(sizeof(OPT_REQUIRE_STR) - 1)
+#define OPT_REQUIRES_STR	"# OPT-REQUIRES:"
+#define OPT_REQUIRES_LEN	(sizeof(OPT_REQUIRES_STR) - 1)
 #define PROVIDE_STR	"# PROVIDE:"
 #define PROVIDE_LEN	(sizeof(PROVIDE_STR) - 1)
 #define PROVIDES_STR	"# PROVIDES:"
 #define PROVIDES_LEN	(sizeof(PROVIDES_STR) - 1)
 #define BEFORE_STR	"# BEFORE:"
 #define BEFORE_LEN	(sizeof(BEFORE_STR) - 1)
+#define OPT_BEFORE_STR	"# OPT-BEFORE:"
+#define OPT_BEFORE_LEN	(sizeof(OPT_BEFORE_STR) - 1)
 #define KEYWORD_STR	"# KEYWORD:"
 #define KEYWORD_LEN	(sizeof(KEYWORD_STR) - 1)
 #define KEYWORDS_STR	"# KEYWORDS:"
@@ -122,11 +128,13 @@
 };
 
 struct f_reqnode {
+	int		optional;
 	Hash_Entry	*entry;
 	f_reqnode	*next;
 };
 
 struct strnodelist {
+	int		optional;
 	filenode	*node;
 	strnodelist	*next;
 	char		s[1];
@@ -156,8 +164,10 @@
 void parse_line(filenode *, char *, void (*)(filenode *, char *));
 filenode *filenode_new(char *);
 void add_require(filenode *, char *);
+void add_opt_require(filenode *, char *);
 void add_provide(filenode *, char *);
 void add_before(filenode *, char *);
+void add_opt_before(filenode *, char *);
 void add_keyword(filenode *, char *);
 void insert_before(void);
 Hash_Entry *make_fake_provision(filenode *);
@@ -227,6 +237,7 @@
 	strnodelist *ent;
 
 	ent = emalloc(sizeof *ent + strlen(s));
+	ent->optional = RESET;
 	ent->node = fnode;
 	strcpy(ent->s, s);
 	ent->next = *listp;
@@ -285,6 +296,18 @@
 	rnode->entry = entry;
 	rnode->next = fnode->req_list;
 	fnode->req_list = rnode;
+	fnode->req_list->optional = RESET;
+}
+
+/*
+ * add an optional requirement to a filenode.
+ */
+void
+add_opt_require(filenode *fnode, char *s)
+{
+
+	add_require(fnode, s);
+	fnode->req_list->optional = SET;
 }
 
 /*
@@ -376,6 +399,16 @@
 
 	strnode_add(&bl_list, s, fnode);
 }
+/*
+ * put the OPT-BEFORE: lines to a list and handle them later.
+ */
+void
+add_opt_before(filenode *fnode, char *s)
+{
+
+	add_before(fnode, s);
+	bl_list->optional = SET;
+}
 
 /*
  * add a key to a filenode.
@@ -411,6 +444,7 @@
 	FILE *fp;
 	char *buf;
 	int require_flag, provide_flag, before_flag, keyword_flag;
+	int opt_require_flag, opt_before_flag;
 	enum { BEFORE_PARSING, PARSING, PARSING_DONE } state;
 	filenode *node;
 	char delims[3] = { '\\', '\\', '\0' };
@@ -444,16 +478,23 @@
 	for (state = BEFORE_PARSING; state != PARSING_DONE &&
 	    (buf = fparseln(fp, NULL, NULL, delims, 0)) != NULL; free(buf)) {
 		require_flag = provide_flag = before_flag = keyword_flag = 0;
+		opt_require_flag = opt_before_flag = 0;
 		if (strncmp(REQUIRE_STR, buf, REQUIRE_LEN) == 0)
 			require_flag = REQUIRE_LEN;
 		else if (strncmp(REQUIRES_STR, buf, REQUIRES_LEN) == 0)
 			require_flag = REQUIRES_LEN;
+		else if (strncmp(OPT_REQUIRE_STR, buf, OPT_REQUIRE_LEN) == 0)
+			opt_require_flag = OPT_REQUIRE_LEN;
+		else if (strncmp(OPT_REQUIRES_STR, buf, OPT_REQUIRES_LEN) == 0)
+			opt_require_flag = OPT_REQUIRES_LEN;
 		else if (strncmp(PROVIDE_STR, buf, PROVIDE_LEN) == 0)
 			provide_flag = PROVIDE_LEN;
 		else if (strncmp(PROVIDES_STR, buf, PROVIDES_LEN) == 0)
 			provide_flag = PROVIDES_LEN;
 		else if (strncmp(BEFORE_STR, buf, BEFORE_LEN) == 0)
 			before_flag = BEFORE_LEN;
+		else if (strncmp(OPT_BEFORE_STR, buf, OPT_BEFORE_LEN) == 0)
+			opt_before_flag = OPT_BEFORE_LEN;
 		else if (strncmp(KEYWORD_STR, buf, KEYWORD_LEN) == 0)
 			keyword_flag = KEYWORD_LEN;
 		else if (strncmp(KEYWORDS_STR, buf, KEYWORDS_LEN) == 0)
@@ -467,10 +508,14 @@
 		state = PARSING;
 		if (require_flag)
 			parse_line(node, buf + require_flag, add_require);
+		else if (opt_require_flag)
+			parse_line(node, buf+opt_require_flag, add_opt_require);
 		else if (provide_flag)
 			parse_line(node, buf + provide_flag, add_provide);
 		else if (before_flag)
-			parse_line(node, buf + before_flag,  add_before);
+			parse_line(node, buf + before_flag, add_before);
+		else if (opt_before_flag)
+			parse_line(node, buf + opt_before_flag, add_opt_before);
 		else if (keyword_flag)
 			parse_line(node, buf + keyword_flag, add_keyword);
 	}
@@ -538,8 +583,16 @@
 		fake_prov_entry = make_fake_provision(bl_list->node);
 
 		entry = Hash_CreateEntry(provide_hash, bl_list->s, &new);
-		if (new == 1)
-			warnx("file `%s' is before unknown provision `%s'", bl_list->node->filename, bl_list->s);
+		if (new == 1) {
+			if (bl_list->optional == SET) {
+			    DPRINTF((stderr,
+				"unresolved OPT-BEFORE `%s' in file `%s'\n",
+				bl_list->s, bl_list->node->filename));
+			} else {
+			    warnx("file `%s' is before unknown provision `%s'",
+				bl_list->node->filename, bl_list->s);
+			}
+		}
 
 		for (pnode = Hash_GetValue(entry); pnode; pnode = pnode->next) {
 			if (pnode->head)
@@ -596,10 +649,17 @@
 	head = Hash_GetValue(entry);
 
 	if (head == NULL) {
-		warnx("requirement `%s' in file `%s' has no providers.",
-		    Hash_GetKey(entry), filename);
-		exit_code = 1;
-		return;
+		if (rnode->optional == SET) {
+			DPRINTF((stderr,
+			    "unresolved OPT-REQUIRE `%s' in file `%s'\n",
+			    Hash_GetKey(entry), filename));
+			return;
+		} else {
+			warnx("requirement `%s' in file `%s' has no providers.",
+			    Hash_GetKey(entry), filename);
+			exit_code = 1;
+			return;
+		}
 	}
 
 	/* return if the requirement is already satisfied. */

>Release-Note:
>Audit-Trail:
>Unformatted: