Browse code

cfg framework: support for group instances with delayed commits

cfg_set_delayed() and the related functions, commit, diff,... support
the additinal group instances from now.
The group instance must exist before cfg_set_delayed() is called, i.e.
the function does not create new instances.

Miklos Tirpak authored on 14/09/2010 13:33:29
Showing 2 changed files
... ...
@@ -648,11 +648,13 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str
648 648
 	cfg_group_t	*group;
649 649
 	cfg_mapping_t	*var;
650 650
 	void		*v;
651
-	char		*temp_handle;
651
+	unsigned char	*temp_handle;
652 652
 	int		temp_handle_created;
653
-	cfg_changed_var_t	*changed = NULL;
653
+	cfg_changed_var_t	*changed = NULL, **changed_p;
654 654
 	int		size;
655 655
 	str		s;
656
+	cfg_group_inst_t	*group_inst;
657
+	unsigned char		*var_block;
656 658
 
657 659
 	if (!cfg_shmized)
658 660
 		/* the cfg has not been shmized yet, there is no
... ...
@@ -675,6 +677,19 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str
675 677
 		goto error0;
676 678
 	}
677 679
 
680
+	/* The additional variable instances having per-child process callback
681
+	 * with CFG_CB_ONLY_ONCE flag cannot be rewritten.
682
+	 * The reason is that such variables typically set global parameters
683
+	 * as opposed to per-process variables. Hence, it is not possible to set
684
+	 * the group handle temporary to another block, and then reset it back later. */
685
+	if (group_id
686
+		&& var->def->on_set_child_cb
687
+		&& var->def->type & CFG_CB_ONLY_ONCE
688
+	) {
689
+		LOG(L_ERR, "ERROR: cfg_set_now(): This variable does not support muliple values.\n");
690
+		goto error0;
691
+	}
692
+
678 693
 	/* check whether we have to convert the type */
679 694
 	if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
680 695
 		goto error0;
... ...
@@ -700,31 +715,56 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str
700 715
 		Only the values within the group are applied,
701 716
 		other modifications are not visible to the callback.
702 717
 		The local config is the base. */
718
+		if (!group_id) {
719
+			var_block = *(group->handle);
720
+		} else {
721
+			if (!cfg_local) {
722
+				LOG(L_ERR, "ERROR: cfg_set_delayed(): Local configuration is missing\n");
723
+				goto error;
724
+			}
725
+			group_inst = cfg_find_group(CFG_GROUP_META(cfg_local, group),
726
+							group->size,
727
+							*group_id);
728
+			if (!group_inst) {
729
+				LOG(L_ERR, "ERROR: cfg_set_delayed(): local group instance %.*s[%u] is not found\n",
730
+					group_name->len, group_name->s, *group_id);
731
+				goto error;
732
+			}
733
+			var_block = group_inst->vars;
734
+		}
703 735
 
704 736
 		if (ctx->changed_first) {
705
-			temp_handle = (char *)pkg_malloc(group->size);
737
+			temp_handle = (unsigned char *)pkg_malloc(group->size);
706 738
 			if (!temp_handle) {
707 739
 				LOG(L_ERR, "ERROR: cfg_set_delayed(): "
708 740
 					"not enough memory\n");
709 741
 				goto error;
710 742
 			}
711 743
 			temp_handle_created = 1;
712
-			memcpy(temp_handle, *(group->handle), group->size);
744
+			memcpy(temp_handle, var_block, group->size);
713 745
 
714 746
 			/* apply the changes */
715 747
 			for (	changed = ctx->changed_first;
716 748
 				changed;
717 749
 				changed = changed->next
718 750
 			) {
719
-				if (changed->group != group) continue;
720
-
721
-				memcpy(	temp_handle + changed->var->offset,
722
-					changed->new_val.vraw,
723
-					cfg_var_size(changed->var));
751
+				if (changed->group != group)
752
+					continue;
753
+				if ((!group_id && !changed->group_id_set) /* default values */
754
+					|| (group_id && !changed->group_id_set
755
+						&& !CFG_VAR_TEST(group_inst, changed->var))
756
+							/* default value is changed which affects the group_instance */
757
+					|| (group_id && changed->group_id_set
758
+						&& (*group_id == changed->group_id))
759
+							/* change within the group instance */
760
+				)
761
+					memcpy(	temp_handle + changed->var->offset,
762
+						changed->new_val.vraw,
763
+						cfg_var_size(changed->var));
724 764
 			}
725 765
 		} else {
726 766
 			/* there is not any change */
727
-			temp_handle = *(group->handle);
767
+			temp_handle = var_block;
728 768
 			temp_handle_created = 0;
729 769
 		}
730 770
 			
... ...
@@ -751,6 +791,10 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str
751 791
 	memset(changed, 0, size);
752 792
 	changed->group = group;
753 793
 	changed->var = var;
794
+	if (group_id) {
795
+		changed->group_id = *group_id;
796
+		changed->group_id_set = 1;
797
+	}
754 798
 
755 799
 	switch (CFG_VAR_TYPE(var)) {
756 800
 
... ...
@@ -779,15 +823,27 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str
779 823
 
780 824
 	}
781 825
 
782
-	/* Add the new item to the end of the linked list,
783
-	The commit will go though the list from the first item,
784
-	so the list is kept in order */
785
-	if (ctx->changed_first)
786
-		ctx->changed_last->next = changed;
787
-	else
788
-		ctx->changed_first = changed;
789
-
790
-	ctx->changed_last = changed;
826
+	/* Order the changes by group + group_id + original order.
827
+	 * Hence, the list is still kept in order within the group.
828
+	 * The changes can be committed faster this way, the group instances
829
+	 * do not have to be looked-up for each and every variable. */
830
+	/* Check whether there is any variable in the list which
831
+	belongs to the same group */
832
+	for (	changed_p = &ctx->changed_first;
833
+		*changed_p && ((*changed_p)->group != changed->group);
834
+		changed_p = &(*changed_p)->next);
835
+	/* try to find the group instance, and move changed_p to the end of
836
+	the instance. */
837
+	for (	;
838
+		*changed_p
839
+			&& ((*changed_p)->group == changed->group)
840
+			&& (!(*changed_p)->group_id_set
841
+				|| ((*changed_p)->group_id_set && changed->group_id_set
842
+					&& ((*changed_p)->group_id <= changed->group_id)));
843
+		changed_p = &(*changed_p)->next);
844
+	/* Add the new variable before *changed_p */
845
+	changed->next = *changed_p;
846
+	*changed_p = changed;
791 847
 
792 848
 	CFG_CTX_UNLOCK(ctx);
793 849
 
... ...
@@ -817,6 +873,11 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str
817 873
 			var_name->len, var_name->s,
818 874
 			((str *)val)->len, ((str *)val)->s,
819 875
 			ctx);
876
+	if (group_id)
877
+		LOG(L_INFO, "INFO: cfg_set_delayed(): group id = %u "
878
+			"[context=%p]\n",
879
+			*group_id,
880
+			ctx);
820 881
 
821 882
 	convert_val_cleanup();
822 883
 	return 0;
... ...
@@ -862,7 +923,7 @@ int cfg_commit(cfg_ctx_t *ctx)
862 923
 {
863 924
 	int	replaced_num = 0;
864 925
 	cfg_changed_var_t	*changed, *changed2;
865
-	cfg_block_t	*block;
926
+	cfg_block_t	*block = NULL;
866 927
 	void	**replaced = NULL;
867 928
 	cfg_child_cb_t	*child_cb;
868 929
 	cfg_child_cb_t	*child_cb_first = NULL;
... ...
@@ -870,6 +931,8 @@ int cfg_commit(cfg_ctx_t *ctx)
870 931
 	int	size;
871 932
 	void	*p;
872 933
 	str	s, s2;
934
+	cfg_group_t	*group;
935
+	cfg_group_inst_t	*group_inst;
873 936
 
874 937
 	if (!ctx) {
875 938
 		LOG(L_ERR, "ERROR: cfg_commit(): context is undefined\n");
... ...
@@ -885,19 +948,30 @@ int cfg_commit(cfg_ctx_t *ctx)
885 948
 	/* is there any change? */
886 949
 	if (!ctx->changed_first) goto done;
887 950
 
888
-	/* count the number of replaced strings,
889
-	and prepare the linked list of per-child process
890
-	callbacks, that will be added to the global list */
891
-	for (	changed = ctx->changed_first;
951
+	/* Count the number of replaced strings,
952
+	and replaced group arrays.
953
+	Prepare the linked list of per-child process
954
+	callbacks, that will be added to the global list. */
955
+	for (	changed = ctx->changed_first, group = NULL;
892 956
 		changed;
893 957
 		changed = changed->next
894 958
 	) {
959
+		/* Each string/str potentially causes an old string to be freed
960
+		 * unless the variable of an additional group instance is set
961
+		 * which uses the default value. This case cannot be determined
962
+		 * without locking *cfg_global, hence, it is better to count these
963
+		 * strings as well even though the slot might not be used later. */
895 964
 		if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
896 965
 		|| (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR))
897 966
 			replaced_num++;
898 967
 
968
+		/* See the above comments for strings */
969
+		if (group != changed->group) {
970
+			replaced_num++;
971
+			group = changed->group;
972
+		}
899 973
 
900
-		if (changed->var->def->on_set_child_cb) {
974
+		if (!changed->group_id_set && changed->var->def->on_set_child_cb) {
901 975
 			s.s = changed->group->name;
902 976
 			s.len = changed->group->name_len;
903 977
 			s2.s = changed->var->def->name;
... ...
@@ -921,7 +995,7 @@ int cfg_commit(cfg_ctx_t *ctx)
921 995
 		replaced = (void **)shm_malloc(size);
922 996
 		if (!replaced) {
923 997
 			LOG(L_ERR, "ERROR: cfg_commit(): not enough shm memory\n");
924
-			goto error;
998
+			goto error0;
925 999
 		}
926 1000
 		memset(replaced, 0 , size);
927 1001
 	}
... ...
@@ -931,22 +1005,54 @@ int cfg_commit(cfg_ctx_t *ctx)
931 1005
 	CFG_WRITER_LOCK();
932 1006
 
933 1007
 	/* clone the memory block, and prepare the modification */
934
-	if (!(block = cfg_clone_global())) {
935
-		CFG_WRITER_UNLOCK();
1008
+	if (!(block = cfg_clone_global()))
936 1009
 		goto error;
937
-	}
938 1010
 
939
-	/* apply the modifications to the buffer */
1011
+	/* Apply the modifications to the buffer.
1012
+	Note that the cycle relies on the order of the groups and group instances, i.e.
1013
+	the order is group + group_id + order of commits. */
940 1014
 	replaced_num = 0;
941
-	for (	changed = ctx->changed_first;
1015
+	for (	changed = ctx->changed_first, group = NULL; /* group points to the
1016
+							last group array that has been cloned */
942 1017
 		changed;
943 1018
 		changed = changed->next
944 1019
 	) {
945
-		p = CFG_GROUP_DATA(block, changed->group)
946
-			+ changed->var->offset;
1020
+		if (!changed->group_id_set) {
1021
+			p = CFG_GROUP_DATA(block, changed->group)
1022
+				+ changed->var->offset;
1023
+			group_inst = NULL; /* fore the look-up of the next group_inst */
1024
+		} else {
1025
+			if (group != changed->group) {
1026
+				/* The group array has not been cloned yet. */
1027
+				group = changed->group;
1028
+				if (!(CFG_GROUP_META(block, group)->array = 
1029
+					cfg_clone_array(CFG_GROUP_META(*cfg_global, group), group))
1030
+				)
1031
+					goto error;
947 1032
 
948
-		if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
949
-		|| (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) {
1033
+				replaced[replaced_num] = CFG_GROUP_META(*cfg_global, group)->array;
1034
+				replaced_num++;
1035
+
1036
+				group_inst = NULL; /* fore the look-up of group_inst */
1037
+			}
1038
+			if (!group_inst || (group_inst->id != changed->group_id)) {
1039
+				group_inst = cfg_find_group(CFG_GROUP_META(block, group),
1040
+								group->size,
1041
+								changed->group_id);
1042
+			}
1043
+			if (!group_inst) {
1044
+				LOG(L_ERR, "ERROR: cfg_set_now(): global group instance %.*s[%u] is not found\n",
1045
+					group->name_len, group->name, changed->group_id);
1046
+				goto error;
1047
+			}
1048
+			p = group_inst->vars + changed->var->offset;
1049
+		}
1050
+
1051
+		if (((changed->group_id_set && CFG_VAR_TEST_AND_SET(group_inst, changed->var))
1052
+			|| !changed->group_id_set)
1053
+		&& ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
1054
+			|| (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) 
1055
+		) {
950 1056
 			replaced[replaced_num] = *(char **)p;
951 1057
 			if (replaced[replaced_num])
952 1058
 				replaced_num++;
... ...
@@ -958,13 +1064,33 @@ int cfg_commit(cfg_ctx_t *ctx)
958 1064
 		memcpy(	p,
959 1065
 			changed->new_val.vraw,
960 1066
 			cfg_var_size(changed->var));
1067
+
1068
+		if (!changed->group_id_set) {
1069
+			/* the default value is changed, the copies of this value
1070
+			need to be also updated */
1071
+			if (cfg_update_defaults(CFG_GROUP_META(block, changed->group),
1072
+						changed->group, changed->var, p,
1073
+						(group != changed->group)) /* clone if the array
1074
+									has not been cloned yet */
1075
+                        )
1076
+                                goto error;
1077
+                        if ((group != changed->group)
1078
+				&& (CFG_GROUP_META(block, changed->group)->array != CFG_GROUP_META(*cfg_global, changed->group)->array)
1079
+			) {
1080
+				/* The array has been cloned */
1081
+				group = changed->group;
1082
+
1083
+				replaced[replaced_num] = CFG_GROUP_META(*cfg_global, group)->array;
1084
+				replaced_num++;
1085
+			}
1086
+		}
961 1087
 	}
962 1088
 
963 1089
 	/* replace the global config with the new one */
964 1090
 	cfg_install_global(block, replaced, child_cb_first, child_cb_last);
965 1091
 	CFG_WRITER_UNLOCK();
966 1092
 
967
-	/* free the changed list */	
1093
+	/* free the changed list */
968 1094
 	for (	changed = ctx->changed_first;
969 1095
 		changed;
970 1096
 		changed = changed2
... ...
@@ -973,7 +1099,6 @@ int cfg_commit(cfg_ctx_t *ctx)
973 1099
 		shm_free(changed);
974 1100
 	}
975 1101
 	ctx->changed_first = NULL;
976
-	ctx->changed_last = NULL;
977 1102
 
978 1103
 done:
979 1104
 	LOG(L_INFO, "INFO: cfg_commit(): config changes have been applied "
... ...
@@ -984,9 +1109,24 @@ done:
984 1109
 	return 0;
985 1110
 
986 1111
 error:
987
-	CFG_CTX_UNLOCK(ctx);
1112
+	if (block) {
1113
+		/* clean the new block from the cloned arrays */
1114
+		for (	group = cfg_group;
1115
+			group;
1116
+			group = group->next
1117
+		)
1118
+			if (CFG_GROUP_META(block, group)->array
1119
+				&& (CFG_GROUP_META(block, group)->array != CFG_GROUP_META(*cfg_global, group)->array)
1120
+			)
1121
+				shm_free(CFG_GROUP_META(block, group)->array);
1122
+		/* the block can be freed outside of the writer lock */
1123
+	}
1124
+	CFG_WRITER_UNLOCK();
1125
+	if (block)
1126
+		shm_free(block);
988 1127
 
989 1128
 error0:
1129
+	CFG_CTX_UNLOCK(ctx);
990 1130
 
991 1131
 	if (child_cb_first) cfg_child_cb_free(child_cb_first);
992 1132
 	if (replaced) shm_free(replaced);
... ...
@@ -1028,7 +1168,6 @@ int cfg_rollback(cfg_ctx_t *ctx)
1028 1168
 		shm_free(changed);
1029 1169
 	}
1030 1170
 	ctx->changed_first = NULL;
1031
-	ctx->changed_last = NULL;
1032 1171
 
1033 1172
 	CFG_CTX_UNLOCK(ctx);
1034 1173
 
... ...
@@ -1181,11 +1320,12 @@ int cfg_diff_init(cfg_ctx_t *ctx,
1181 1320
  * committed yet
1182 1321
  */
1183 1322
 int cfg_diff_next(void **h,
1184
-			str *gname, str *vname,
1323
+			str *gname, unsigned int **gid, str *vname,
1185 1324
 			void **old_val, void **new_val,
1186 1325
 			unsigned int *val_type)
1187 1326
 {
1188 1327
 	cfg_changed_var_t	*changed;
1328
+	cfg_group_inst_t	*group_inst;
1189 1329
 	union cfg_var_value* pval;
1190 1330
 	static str	old_s, new_s;	/* we need the value even
1191 1331
 					after the function returns */
... ...
@@ -1195,14 +1335,32 @@ int cfg_diff_next(void **h,
1195 1335
 
1196 1336
 	gname->s = changed->group->name;
1197 1337
 	gname->len = changed->group->name_len;
1338
+	*gid = (changed->group_id_set ? &changed->group_id : NULL);
1198 1339
 	vname->s = changed->var->def->name;
1199 1340
 	vname->len = changed->var->name_len;
1200 1341
 
1201 1342
 	/* use the module's handle to access the variable
1202 1343
 	It means that the variable is read from the local config
1203 1344
 	after forking */
1204
-	pval = (union cfg_var_value*)
1205
-			(*(changed->group->handle) + changed->var->offset);
1345
+	if (!changed->group_id_set) {
1346
+		pval = (union cfg_var_value*)
1347
+				(*(changed->group->handle) + changed->var->offset);
1348
+	} else {
1349
+		if (!cfg_local) {
1350
+			LOG(L_ERR, "ERROR: cfg_diff_next(): Local configuration is missing\n");
1351
+			return 0;
1352
+		}
1353
+		group_inst = cfg_find_group(CFG_GROUP_META(cfg_local, changed->group),
1354
+						changed->group->size,
1355
+						changed->group_id);
1356
+		if (!group_inst) {
1357
+			LOG(L_ERR, "ERROR: cfg_diff_next(): local group instance %.*s[%u] is not found\n",
1358
+				changed->group->name_len, changed->group->name, changed->group_id);
1359
+			return 0;
1360
+		}
1361
+		pval = (union cfg_var_value*)
1362
+				(group_inst->vars + changed->var->offset);
1363
+	}
1206 1364
 
1207 1365
 	switch (CFG_VAR_TYPE(changed->var)) {
1208 1366
 	case CFG_VAR_INT:
... ...
@@ -49,6 +49,9 @@ typedef struct _cfg_changed_var {
49 49
 	cfg_mapping_t	*var;
50 50
 	struct _cfg_changed_var	*next;
51 51
 
52
+	unsigned int	group_id; /* valid only if group_id_set==1 */
53
+	unsigned char	group_id_set;
54
+
52 55
 	/* blob that contains the new value */
53 56
 	union cfg_var_value new_val; /* variable size */
54 57
 } cfg_changed_var_t;
... ...
@@ -61,7 +64,6 @@ typedef struct _cfg_ctx {
61 64
 	/* variables that are already changed
62 65
 	but have not been committed yet */
63 66
 	cfg_changed_var_t	*changed_first;
64
-	cfg_changed_var_t	*changed_last;
65 67
 	/* lock protecting the linked-list of
66 68
 	changed variables */
67 69
 	gen_lock_t		lock;
... ...
@@ -161,7 +163,7 @@ int cfg_diff_init(cfg_ctx_t *ctx,
161 163
  * void *handle;
162 164
  * if (cfg_diff_init(ctx, &handle)) return -1
163 165
  * while (cfg_diff_next(&handle
164
- *			&group_name, &var_name,
166
+ *			&group_name, &group_id, &var_name,
165 167
  *			&old_val, &new_val
166 168
  *			&val_type)
167 169
  * ) {
... ...
@@ -170,7 +172,7 @@ int cfg_diff_init(cfg_ctx_t *ctx,
170 172
  * cfg_diff_release(ctx);
171 173
  */
172 174
 int cfg_diff_next(void **h,
173
-			str *gname, str *vname,
175
+			str *gname, unsigned int **gid, str *vname,
174 176
 			void **old_val, void **new_val,
175 177
 			unsigned int *val_type);
176 178