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