1 /*
2 Copyright 2021 Northern.tech AS
3
4 This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; version 3.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18
19 To the extent this program is licensed as part of the Enterprise
20 versions of CFEngine, the applicable Commercial Open Source License
21 (COSL) may apply to this file if you as a licensee so wish it. See
22 included file COSL.txt.
23 */
24 #include <variable.h>
25
26 #include <map.h>
27 #include <rlist.h>
28 #include <writer.h>
29 #include <conversion.h> /* DataTypeToString */
30
31 #define VARIABLE_TAG_SECRET "secret"
32
33 struct Variable_
34 {
35 VarRef *ref;
36 Rval rval;
37 DataType type;
38 StringSet *tags;
39 char *comment;
40 const Promise *promise; // The promise that set the present value
41 };
42
VariableNew(VarRef * ref,Rval rval,DataType type,StringSet * tags,char * comment,const Promise * promise)43 static Variable *VariableNew(VarRef *ref, Rval rval, DataType type,
44 StringSet *tags, char *comment,
45 const Promise *promise)
46 {
47 Variable *var = xmalloc(sizeof(Variable));
48
49 var->ref = ref;
50 var->rval = rval;
51 var->type = type;
52 var->tags = tags;
53 var->comment = comment;
54 var->promise = promise;
55
56 return var;
57 }
58
59 /* DO NOT EXPORT, this is for internal (hash table) use only, and it doesn't
60 * free everything in Variable, in particular it leaves var->ref to be handled
61 * by the Map implementation calling the key-destroy function. */
VariableDestroy(Variable * var)62 static void VariableDestroy(Variable *var)
63 {
64 if (var != NULL)
65 {
66 RvalDestroy(var->rval);
67 StringSetDestroy(var->tags);
68 free(var->comment);
69 // Nothing to do for ->promise
70
71 free(var);
72 }
73 }
74
VariableDestroy_untyped(void * var)75 static void VariableDestroy_untyped(void *var)
76 {
77 VariableDestroy(var);
78 }
79
VariableGetRef(const Variable * var)80 const VarRef *VariableGetRef(const Variable *var)
81 {
82 assert(var != NULL);
83 return var->ref;
84 }
85
VariableGetType(const Variable * var)86 DataType VariableGetType(const Variable *var)
87 {
88 assert(var != NULL);
89 return var->type;
90 }
91
VariableGetRvalType(const Variable * var)92 RvalType VariableGetRvalType(const Variable *var)
93 {
94 assert(var != NULL);
95 return var->rval.type;
96 }
97
VariableGetTags(const Variable * var)98 StringSet *VariableGetTags(const Variable *var)
99 {
100 assert(var != NULL);
101 return var->tags;
102 }
103
VariableGetComment(const Variable * var)104 const char *VariableGetComment(const Variable *var)
105 {
106 assert(var != NULL);
107 return var->comment;
108 }
109
VariableGetPromise(const Variable * var)110 const Promise *VariableGetPromise(const Variable *var)
111 {
112 assert(var != NULL);
113 return var->promise;
114 }
115
VariableIsSecret(const Variable * var)116 bool VariableIsSecret(const Variable *var)
117 {
118 assert(var != NULL);
119 return ((var->tags != NULL) && StringSetContains(var->tags, VARIABLE_TAG_SECRET));
120 }
121
VariableGetRval(const Variable * var,bool get_secret)122 Rval VariableGetRval(const Variable *var, bool get_secret)
123 {
124 assert(var != NULL);
125 if (!get_secret && VariableIsSecret(var))
126 {
127 return RvalNewSecret();
128 }
129 return var->rval;
130 }
131
VariableSetRval(Variable * var,Rval new_rval)132 void VariableSetRval(Variable *var, Rval new_rval)
133 {
134 assert(var != NULL);
135 RvalDestroy(var->rval);
136 var->rval = new_rval;
137 }
138
139 /**
140 Define "VarMap" hash table.
141 Key: VarRef
142 Value: Variable
143 */
144 TYPED_MAP_DECLARE(Var, VarRef *, Variable *)
145 TYPED_MAP_DEFINE(Var, VarRef *, Variable *,
146 VarRefHash_untyped,
147 VarRefEqual_untyped,
148 VarRefDestroy_untyped,
149 VariableDestroy_untyped)
150
151
152 struct VariableTable_
153 {
154 VarMap *vars;
155 };
156
157 struct VariableTableIterator_
158 {
159 VarRef *ref;
160 MapIterator iter;
161 };
162
VariableTableNew(void)163 VariableTable *VariableTableNew(void)
164 {
165 VariableTable *table = xmalloc(sizeof(VariableTable));
166
167 table->vars = VarMapNew();
168
169 return table;
170 }
171
VariableTableDestroy(VariableTable * table)172 void VariableTableDestroy(VariableTable *table)
173 {
174 if (table)
175 {
176 VarMapDestroy(table->vars);
177 free(table);
178 }
179 }
180
181 /* NULL return value means variable not found. */
VariableTableGet(const VariableTable * table,const VarRef * ref)182 Variable *VariableTableGet(const VariableTable *table, const VarRef *ref)
183 {
184 Variable *v = VarMapGet(table->vars, ref);
185
186 char *ref_s = VarRefToString(ref, true); /* TODO optimise */
187
188 if (v != NULL)
189 {
190 CF_ASSERT(v->rval.item != NULL || DataTypeIsIterable(v->type),
191 "VariableTableGet(%s): "
192 "Only iterables (Rlists) are allowed to be NULL",
193 ref_s);
194 }
195
196 if (LogModuleEnabled(LOG_MOD_VARTABLE))
197 {
198 Buffer *buf = BufferNew();
199 BufferPrintf(buf, "VariableTableGet(%s): %s", ref_s,
200 v ? DataTypeToString(v->type) : "NOT FOUND");
201 if (v != NULL)
202 {
203 char *value_s;
204 BufferAppendString(buf, " => ");
205 if (DataTypeIsIterable(v->type) &&
206 v->rval.item == NULL)
207 {
208 value_s = xstrdup("EMPTY");
209 }
210 else
211 {
212 value_s = RvalToString(v->rval);
213 }
214
215 BufferAppendString(buf, value_s);
216 free(value_s);
217 }
218
219 LogDebug(LOG_MOD_VARTABLE, "%s", BufferGet(buf));
220
221 BufferDestroy(buf);
222 }
223
224 free(ref_s);
225 return v;
226 }
227
VariableTableRemove(VariableTable * table,const VarRef * ref)228 bool VariableTableRemove(VariableTable *table, const VarRef *ref)
229 {
230 return VarMapRemove(table->vars, ref);
231 }
232
VariableTablePut(VariableTable * table,const VarRef * ref,const Rval * rval,DataType type,StringSet * tags,char * comment,const Promise * promise)233 bool VariableTablePut(VariableTable *table, const VarRef *ref,
234 const Rval *rval, DataType type,
235 StringSet *tags, char *comment,
236 const Promise *promise)
237 {
238 assert(VarRefIsQualified(ref));
239
240 /* TODO assert there are no CF_NS or '.' in the variable name? */
241
242 if (LogModuleEnabled(LOG_MOD_VARTABLE))
243 {
244 char *value_s = RvalToString(*rval);
245 LogDebug(LOG_MOD_VARTABLE, "VariableTablePut(%s): %s => %s",
246 ref->lval, DataTypeToString(type),
247 rval->item ? value_s : "EMPTY");
248 free(value_s);
249 }
250
251 CF_ASSERT(rval != NULL || DataTypeIsIterable(type),
252 "VariableTablePut(): "
253 "Only iterables (Rlists) are allowed to be NULL");
254
255 Variable *var = VariableNew(VarRefCopy(ref), RvalCopy(*rval), type,
256 tags, comment, promise);
257 return VarMapInsert(table->vars, var->ref, var);
258 }
259
VariableTableClear(VariableTable * table,const char * ns,const char * scope,const char * lval)260 bool VariableTableClear(VariableTable *table, const char *ns, const char *scope, const char *lval)
261 {
262 const size_t vars_num = VarMapSize(table->vars);
263
264 if (!ns && !scope && !lval)
265 {
266 VarMapClear(table->vars);
267 bool has_vars = (vars_num > 0);
268 return has_vars;
269 }
270
271 /* We can't remove elements from the hash table while we are iterating
272 * over it. So we first store the VarRef pointers on a list. */
273
274 VarRef **to_remove = xmalloc(vars_num * sizeof(*to_remove));
275 size_t remove_count = 0;
276
277 {
278 VariableTableIterator *iter = VariableTableIteratorNew(table, ns, scope, lval);
279
280 for (Variable *v = VariableTableIteratorNext(iter);
281 v != NULL;
282 v = VariableTableIteratorNext(iter))
283 {
284 to_remove[remove_count] = v->ref;
285 remove_count++;
286 }
287 VariableTableIteratorDestroy(iter);
288 }
289
290 if (remove_count == 0)
291 {
292 free(to_remove);
293 return false;
294 }
295
296 size_t removed = 0;
297 for(size_t i = 0; i < remove_count; i++)
298 {
299 if (VariableTableRemove(table, to_remove[i]))
300 {
301 removed++;
302 }
303 }
304
305 free(to_remove);
306 assert(removed == remove_count);
307 return true;
308 }
309
VariableTableCount(const VariableTable * table,const char * ns,const char * scope,const char * lval)310 size_t VariableTableCount(const VariableTable *table, const char *ns, const char *scope, const char *lval)
311 {
312 if (!ns && !scope && !lval)
313 {
314 return VarMapSize(table->vars);
315 }
316
317 VariableTableIterator *iter = VariableTableIteratorNew(table, ns, scope, lval);
318 size_t count = 0;
319 while (VariableTableIteratorNext(iter))
320 {
321 count++;
322 }
323 VariableTableIteratorDestroy(iter);
324 return count;
325 }
326
VariableTableIteratorNewFromVarRef(const VariableTable * table,const VarRef * ref)327 VariableTableIterator *VariableTableIteratorNewFromVarRef(const VariableTable *table, const VarRef *ref)
328 {
329 VariableTableIterator *iter = xmalloc(sizeof(VariableTableIterator));
330
331 iter->ref = VarRefCopy(ref);
332 iter->iter = MapIteratorInit(table->vars->impl);
333
334 return iter;
335 }
336
VariableTableIteratorNew(const VariableTable * table,const char * ns,const char * scope,const char * lval)337 VariableTableIterator *VariableTableIteratorNew(const VariableTable *table, const char *ns, const char *scope, const char *lval)
338 {
339 VarRef ref = { 0 };
340 ref.ns = (char *)ns;
341 ref.scope = (char *)scope;
342 ref.lval = (char *)lval;
343
344 return VariableTableIteratorNewFromVarRef(table, &ref);
345 }
346
VariableTableIteratorNext(VariableTableIterator * iter)347 Variable *VariableTableIteratorNext(VariableTableIterator *iter)
348 {
349 MapKeyValue *keyvalue;
350
351 while ((keyvalue = MapIteratorNext(&iter->iter)) != NULL)
352 {
353 Variable *var = keyvalue->value;
354 const char *key_ns = var->ref->ns ? var->ref->ns : "default";
355
356 if (iter->ref->ns && strcmp(key_ns, iter->ref->ns) != 0)
357 {
358 continue;
359 }
360
361 if (iter->ref->scope && strcmp(var->ref->scope, iter->ref->scope) != 0)
362 {
363 continue;
364 }
365
366 if (iter->ref->lval && strcmp(var->ref->lval, iter->ref->lval) != 0)
367 {
368 continue;
369 }
370
371 if (iter->ref->num_indices > 0)
372 {
373 if (iter->ref->num_indices > var->ref->num_indices)
374 {
375 continue;
376 }
377
378 bool match = true;
379 for (size_t i = 0; i < iter->ref->num_indices; i++)
380 {
381 if (strcmp(var->ref->indices[i], iter->ref->indices[i]) != 0)
382 {
383 match = false;
384 break;
385 }
386 }
387
388 if (!match)
389 {
390 continue;
391 }
392 }
393
394 return var;
395 }
396
397 return NULL;
398 }
399
VariableTableIteratorDestroy(VariableTableIterator * iter)400 void VariableTableIteratorDestroy(VariableTableIterator *iter)
401 {
402 if (iter)
403 {
404 VarRefDestroy(iter->ref);
405 free(iter);
406 }
407 }
408