Преглед на файлове

Select fixup is postponed until the config is shmized, if the fixup
fails at parsing time. It can happen that the config group is not ready
at parsing time (dynamic group), but a module tries to parse a @cfg_get
select call in mod_init, or when the modparams are parsed.

Miklos Tirpak преди 17 години
родител
ревизия
ad634e3476
променени са 3 файла, в които са добавени 135 реда и са изтрити 2 реда
  1. 119 2
      cfg/cfg_select.c
  2. 8 0
      cfg/cfg_select.h
  3. 8 0
      cfg/cfg_struct.c

+ 119 - 2
cfg/cfg_select.c

@@ -35,6 +35,100 @@
 #include "cfg_struct.h"
 #include "cfg_select.h"
 
+/* It may happen that the select calls cannot be fixed up before shmizing
+ * the config, because for example the mapping structures have not been
+ * allocated for the dynamic groups yet. So we have to keep track of all the
+ * selects that we failed to fix-up, and retry the fixup once more just
+ * before forking */
+typedef struct _cfg_selects {
+	str	gname;
+	str	vname;
+	void	**group_p;
+	void	**var_p;
+	struct _cfg_selects	*next;
+} cfg_selects_t;
+
+/* linked list of non-fixed selects */
+static cfg_selects_t	*cfg_non_fixed_selects = NULL;
+
+/* add a new select item to the linked list */
+static int cfg_new_select(str *gname, str *vname, void **group_p, void **var_p)
+{
+	cfg_selects_t	*sel;
+
+	sel = (cfg_selects_t *)pkg_malloc(sizeof(cfg_selects_t));
+	if (!sel) goto error;
+	memset(sel, 0, sizeof(cfg_selects_t));
+
+	sel->gname.s = (char *)pkg_malloc(sizeof(char)*gname->len);
+	if (!sel->gname.s) goto error;
+	memcpy(sel->gname.s, gname->s, gname->len);
+	sel->gname.len = gname->len;
+
+	sel->vname.s = (char *)pkg_malloc(sizeof(char)*vname->len);
+	if (!sel->vname.s) goto error;
+	memcpy(sel->vname.s, vname->s, vname->len);
+	sel->vname.len = vname->len;
+
+	sel->group_p = group_p;
+	sel->var_p = var_p;
+
+	sel->next = cfg_non_fixed_selects;
+	cfg_non_fixed_selects = sel;
+
+	return 0;
+
+error:
+	LOG(L_ERR, "ERROR: cfg_new_select(): not enough memory\n");
+	if (sel) {
+		if (sel->gname.s) pkg_free(sel->gname.s);
+		if (sel->vname.s) pkg_free(sel->vname.s);
+		pkg_free(sel);
+	}
+	return -1;
+}
+
+/* free the list of not yet fixed selects */
+void cfg_free_selects()
+{
+	cfg_selects_t	*sel, *next_sel;
+
+	sel = cfg_non_fixed_selects;
+	while (sel) {
+		next_sel = sel->next;
+
+		if (sel->gname.s) pkg_free(sel->gname.s);
+		if (sel->vname.s) pkg_free(sel->vname.s);
+		pkg_free(sel);
+
+		sel = next_sel;
+	}
+	cfg_non_fixed_selects = NULL;
+}
+
+/* fix-up the select calls */
+int cfg_fixup_selects()
+{
+	cfg_selects_t	*sel;
+	cfg_group_t	*group;
+	cfg_mapping_t	*var;
+
+	for (sel=cfg_non_fixed_selects; sel; sel=sel->next) {
+
+		if (cfg_lookup_var(&sel->gname, &sel->vname, &group, &var)) {
+			LOG(L_ERR, "ERROR: cfg_parse_selects(): unknown variable: %.*s.%.*s\n",
+				sel->gname.len, sel->gname.s,
+				sel->vname.len, sel->vname.s);
+			return -1;
+		}
+		*(sel->group_p) = (void *)group;
+		*(sel->var_p) = (void *)var;
+	}
+	/* the select list is not needed anymore */
+	cfg_free_selects();
+	return 0;
+}
+
 int select_cfg_var(str *res, select_t *s, struct sip_msg *msg)
 {
 	cfg_group_t	*group;
@@ -60,8 +154,29 @@ int select_cfg_var(str *res, select_t *s, struct sip_msg *msg)
 
 		/* look-up the group and the variable */
 		if (cfg_lookup_var(&s->params[1].v.s, &s->params[2].v.s, &group, &var)) {
-			LOG(L_ERR, "ERROR: select_cfg_var(): unknown variable\n");
-			return -1;
+			if (cfg_shmized) {
+				LOG(L_ERR, "ERROR: select_cfg_var(): unknown variable: %.*s.%.*s\n",
+					s->params[1].v.s.len, s->params[1].v.s.s,
+					s->params[2].v.s.len, s->params[2].v.s.s);
+				return -1;
+			}
+			/* The variable was not found, add it to the non-fixed select list.
+			 * So we act as if the fixup was successful, and we retry it later */
+			if (cfg_new_select(&s->params[1].v.s, &s->params[2].v.s,
+						&s->params[1].v.p, &s->params[2].v.p))
+				return -1;
+
+			LOG(L_DBG, "DEBUG: select_cfg_var(): select fixup is postponed: %.*s.%.*s\n",
+				s->params[1].v.s.len, s->params[1].v.s.s,
+				s->params[2].v.s.len, s->params[2].v.s.s);
+
+			s->params[1].type = SEL_PARAM_PTR;
+			s->params[1].v.p = NULL;
+
+			s->params[2].type = SEL_PARAM_PTR;
+			s->params[2].v.p = NULL;
+
+			return 0;
 		}
 
 		if (var->def->on_change_cb) {
@@ -82,6 +197,8 @@ int select_cfg_var(str *res, select_t *s, struct sip_msg *msg)
 	group = (cfg_group_t *)s->params[1].v.p;
 	var = (cfg_mapping_t *)s->params[2].v.p;
 
+	if (!group || !var) return -1;
+
 	/* use the module's handle to access the variable, so the variables
 	are read from private memory */
 	p = *(group->handle) + var->offset;

+ 8 - 0
cfg/cfg_select.h

@@ -32,6 +32,14 @@
 #ifndef _CFG_SELECT_H
 #define _CFG_SELECT_H
 
+#include "../select.h"
+
+/* free the list of not yet fixed selects */
+void cfg_free_selects();
+
+/* fix-up the select calls */
+int cfg_fixup_selects();
+
 int select_cfg_var(str *res, select_t *s, struct sip_msg *msg);
 
 #endif /* _CFG_SELECT_H */

+ 8 - 0
cfg/cfg_struct.c

@@ -39,6 +39,7 @@
 #include "../locking.h"
 #include "cfg_ctx.h"
 #include "cfg_script.h"
+#include "cfg_select.h"
 #include "cfg_struct.h"
 
 cfg_group_t	*cfg_group = NULL;	/* linked list of registered cfg groups */
@@ -210,6 +211,8 @@ int cfg_shmize(void)
 					group->mapping->def);
 		}
 	}
+	/* try to fixup the selects that failed to be fixed-up previously */
+	if (cfg_fixup_selects()) goto error;
 
 	/* install the new config */
 	cfg_install_global(block, NULL, NULL, NULL);
@@ -335,6 +338,9 @@ void cfg_destroy(void)
 	/* free the list of groups */
 	cfg_destory_groups(cfg_global ? (*cfg_global)->vars : NULL);
 
+	/* free the select list */
+	cfg_free_selects();
+
 	if (cfg_child_cb_first) {
 		if (*cfg_child_cb_first) cfg_child_cb_free(*cfg_child_cb_first);
 		shm_free(cfg_child_cb_first);
@@ -460,6 +466,8 @@ int cfg_lookup_var(str *gname, str *vname,
 		if ((g->name_len == gname->len)
 		&& (memcmp(g->name, gname->s, gname->len)==0)) {
 
+			if (!g->mapping) return -1; /* dynamic group is not ready */
+
 			for (	i = 0;
 				i < g->num;
 				i++