cfengine  3.15.4
About: CFEngine is a configuration management system for configuring and maintaining Unix-like computers (using an own high level policy language). Community version.
  Fossies Dox: cfengine-3.15.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

var_expressions.c
Go to the documentation of this file.
1 /*
2  Copyright 2019 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 
25 #include <var_expressions.h>
26 
27 #include <cf3.defs.h>
28 #include <buffer.h>
29 #include <misc_lib.h>
30 #include <string_lib.h>
31 #include <scope.h>
32 
33 
34 // This is not allowed to be the part of VarRef.indices so looks safe
35 // to be used as multi array indices separator while hashing.
36 #define ARRAY_SEPARATOR_HASH ']'
37 
38 static unsigned VarRefHash(const VarRef *ref)
39 {
40  unsigned int h = 0;
41 
42  if (VarRefIsQualified(ref))
43  {
44  if (ref->ns)
45  {
46  for (int i = 0; ref->ns[i] != '\0'; i++)
47  {
48  h += ref->ns[i];
49  h += (h << 10);
50  h ^= (h >> 6);
51  }
52  }
53  else
54  {
55  h = 1195645448; // hash of "default"
56  }
57 
58  int len = strlen(ref->scope);
59  for (int i = 0; i < len; i++)
60  {
61  h += ref->scope[i];
62  h += (h << 10);
63  h ^= (h >> 6);
64  }
65  }
66 
67  for (int i = 0; ref->lval[i] != '\0'; i++)
68  {
69  h += ref->lval[i];
70  h += (h << 10);
71  h ^= (h >> 6);
72  }
73 
74  for (size_t k = 0; k < ref->num_indices; k++)
75  {
76  // Fixing multi index arrays hashing collisions - Redmine 6674
77  // Multi index arrays with indexes expanded to the same string
78  // (e.g. v[te][st], v[t][e][s][t]) will not be hashed to the same value.
80  h += (h << 10);
81  h ^= (h >> 6);
82 
83  for (int i = 0; ref->indices[k][i] != '\0'; i++)
84  {
85  h += ref->indices[k][i];
86  h += (h << 10);
87  h ^= (h >> 6);
88  }
89  }
90 
91  h += (h << 3);
92  h ^= (h >> 11);
93  h += (h << 15);
94 
95  return h;
96 }
97 
98 unsigned int VarRefHash_untyped(const void *ref,
99  unsigned int seed ARG_UNUSED)
100 {
101  return VarRefHash(ref);
102 }
103 
104 VarRef VarRefConst(const char *ns, const char *scope, const char *lval)
105 {
106  VarRef ref;
107 
108  ref.ns = (char *)ns;
109  ref.scope = (char *)scope;
110  ref.lval = (char *)lval;
111  ref.num_indices = 0;
112  ref.indices = NULL;
113 
114  return ref;
115 }
116 
118 {
119  VarRef *copy = xmalloc(sizeof(VarRef));
120 
121  copy->ns = ref->ns ? xstrdup(ref->ns) : NULL;
122  copy->scope = ref->scope ? xstrdup(ref->scope) : NULL;
123  copy->lval = ref->lval ? xstrdup(ref->lval) : NULL;
124 
125  copy->num_indices = ref->num_indices;
126  if (ref->num_indices > 0)
127  {
128  copy->indices = xmalloc(ref->num_indices * sizeof(char*));
129  for (size_t i = 0; i < ref->num_indices; i++)
130  {
131  copy->indices[i] = xstrdup(ref->indices[i]);
132  }
133  }
134  else
135  {
136  copy->indices = NULL;
137  }
138 
139  return copy;
140 }
141 
143 {
144  VarRef *copy = xmalloc(sizeof(VarRef));
145 
146  copy->ns = NULL;
147  copy->scope = xstrdup("this");
148  copy->lval = ref->lval ? xstrdup(ref->lval) : NULL;
149 
150  copy->num_indices = ref->num_indices;
151  if (ref->num_indices > 0)
152  {
153  copy->indices = xmalloc(ref->num_indices * sizeof(char*));
154  for (size_t i = 0; i < ref->num_indices; i++)
155  {
156  copy->indices[i] = xstrdup(ref->indices[i]);
157  }
158  }
159  else
160  {
161  copy->indices = NULL;
162  }
163 
164  return copy;
165 }
166 
168 {
169  VarRef *copy = xmalloc(sizeof(VarRef));
170 
171  copy->ns = ref->ns ? xstrdup(ref->ns) : NULL;
172  copy->scope = ref->scope ? xstrdup(ref->scope) : NULL;
173  copy->lval = ref->lval ? xstrdup(ref->lval) : NULL;
174  copy->num_indices = 0;
175  copy->indices = NULL;
176 
177  return copy;
178 }
179 
180 
181 
182 static bool IndexBracketsBalance(const char *var_string)
183 {
184  int count = 0;
185  for (const char *c = var_string; *c != '\0'; c++)
186  {
187  if (*c == '[')
188  {
189  count++;
190  }
191  if (*c == ']')
192  {
193  count--;
194  }
195  }
196 
197  return count == 0;
198 }
199 
200 
201 static size_t IndexCount(const char *var_string)
202 {
203  size_t count = 0;
204  size_t level = 0;
205 
206  for (const char *c = var_string; *c != '\0'; c++)
207  {
208  if (*c == '[')
209  {
210  if (level == 0)
211  {
212  count++;
213  }
214  level++;
215  }
216  if (*c == ']')
217  {
218  level--;
219  }
220  }
221 
222  return count;
223 }
224 
225 VarRef *VarRefParseFromNamespaceAndScope(const char *qualified_name,
226  const char *_ns, const char *_scope,
227  char ns_separator, char scope_separator)
228 {
229  assert(qualified_name);
230  char *ns = NULL;
231 
232  const char *indices_start = strchr(qualified_name, '[');
233 
234  const char *scope_start = strchr(qualified_name, ns_separator);
235  if (scope_start && (!indices_start || scope_start < indices_start))
236  {
237  ns = xstrndup(qualified_name, scope_start - qualified_name);
238  scope_start++;
239  }
240  else
241  {
242  scope_start = qualified_name;
243  }
244 
245  char *scope = NULL;
246 
247  const char *lval_start = strchr(scope_start, scope_separator);
248 
249  if (lval_start && (!indices_start || lval_start < indices_start))
250  {
251  lval_start++;
252  scope = xstrndup(scope_start, lval_start - scope_start - 1);
253  }
254  else
255  {
256  lval_start = scope_start;
257  }
258 
259  char *lval = NULL;
260  char **indices = NULL;
261  size_t num_indices = 0;
262 
263  if (indices_start)
264  {
265  indices_start++;
266  lval = xstrndup(lval_start, indices_start - lval_start - 1);
267 
268  if (!IndexBracketsBalance(indices_start - 1))
269  {
270  Log(LOG_LEVEL_ERR, "Broken variable expression, index brackets do not balance, in '%s'", qualified_name);
271  }
272  else
273  {
274  num_indices = IndexCount(indices_start - 1);
275  indices = xmalloc(num_indices * sizeof(char *));
276 
277  Buffer *buf = BufferNew();
278  size_t cur_index = 0;
279  size_t open_count = 1;
280 
281  for (const char *c = indices_start; *c != '\0'; c++)
282  {
283  if (*c == '[')
284  {
285  if (open_count++ == 0)
286  {
287  cur_index++;
288  continue;
289  }
290  }
291  else if (*c == ']')
292  {
293  if (open_count-- == 1)
294  {
295  indices[cur_index] = xstrdup(BufferData(buf));
296  BufferClear(buf);
297  continue;
298  }
299  }
300 
301  BufferAppend(buf, c, sizeof(char));
302  }
303  BufferDestroy(buf);
304  }
305  }
306  else
307  {
308  lval = xstrdup(lval_start);
309  }
310 
311  assert(lval);
312 
313  if (scope)
314  {
316  {
317  _ns = NULL;
318  }
319 
320  /*
321  * Force considering non-special "this." variables as unqualified.
322  * This allows qualifying bundle parameters passed as reference with a "this" scope
323  * in the calling bundle.
324  */
325  if (is_this_not_special(scope, lval)) {
326  free(scope);
327  scope = NULL;
328  }
329  }
330 
331  VarRef *ref = xmalloc(sizeof(VarRef));
332 
333  ref->ns = ns ? ns : (_ns ? xstrdup(_ns) : NULL);
334  ref->scope = scope ? scope : (_scope ? xstrdup(_scope) : NULL);
335  ref->lval = lval;
336  ref->indices = indices;
337  ref->num_indices = num_indices;
338 
339  return ref;
340 }
341 
342 /*
343  * This function will return true if the given variable is
344  * a this.something variable that is an alias to a non-special local variable.
345  */
346 bool is_this_not_special(const char *scope, const char *lval) {
347  // TODO: better way to get this list?
348  const char *special_this_variables[] = {"v","k","this","service_policy","promiser","promiser_uid","promiser_gid","promiser_pid","promiser_ppid","bundle","handle","namespace","promise_filename","promise_dirname","promise_linenumber", NULL};
349 
350  if (!scope) {
351  return false;
352  }
353 
355  return false;
356  }
357 
358  if (IsStrIn(lval, special_this_variables)) {
359  return false;
360  }
361 
362  return true;
363 }
364 
365 VarRef *VarRefParse(const char *var_ref_string)
366 {
367  return VarRefParseFromNamespaceAndScope(var_ref_string, NULL, NULL, CF_NS, '.');
368 }
369 
370 VarRef *VarRefParseFromScope(const char *var_ref_string, const char *scope)
371 {
372  if (!scope)
373  {
374  return VarRefParseFromNamespaceAndScope(var_ref_string, NULL, NULL, CF_NS, '.');
375  }
376 
377  const char *scope_start = strchr(scope, CF_NS);
378  if (scope_start)
379  {
380  char *ns = xstrndup(scope, scope_start - scope);
381  VarRef *ref = VarRefParseFromNamespaceAndScope(var_ref_string, ns, scope_start + 1, CF_NS, '.');
382  free(ns);
383  return ref;
384  }
385  else
386  {
387  return VarRefParseFromNamespaceAndScope(var_ref_string, NULL, scope, CF_NS, '.');
388  }
389 }
390 
391 /**
392  * @brief Parse the variable reference in the context of a bundle. This means
393  * that the VarRef will inherit scope and namespace of the bundle if
394  * these are not specified explicitly in the string.
395  */
396 VarRef *VarRefParseFromBundle(const char *var_ref_string, const Bundle *bundle)
397 {
398  if (bundle)
399  {
400  return VarRefParseFromNamespaceAndScope(var_ref_string,
401  bundle->ns, bundle->name,
402  CF_NS, '.');
403  }
404  else
405  {
406  return VarRefParse(var_ref_string);
407  }
408 }
409 
411 {
412  if (ref)
413  {
414  free(ref->ns);
415  free(ref->scope);
416  free(ref->lval);
417  if (ref->num_indices > 0)
418  {
419  for (int i = 0; i < ref->num_indices; ++i)
420  {
421  free(ref->indices[i]);
422  }
423  free(ref->indices);
424  }
425 
426  free(ref);
427  }
428 
429 }
430 
431 void VarRefDestroy_untyped(void *ref)
432 {
433  VarRefDestroy(ref);
434 }
435 
436 char *VarRefToString(const VarRef *ref, bool qualified)
437 {
438  assert(ref->lval);
439 
440  Buffer *buf = BufferNew();
441  if (qualified && VarRefIsQualified(ref))
442  {
443  const char *ns = ref->ns ? ref->ns : "default";
444 
445  BufferAppend(buf, ns, strlen(ns));
446  BufferAppend(buf, ":", sizeof(char));
447  BufferAppend(buf, ref->scope, strlen(ref->scope));
448  BufferAppend(buf, ".", sizeof(char));
449  }
450 
451  BufferAppend(buf, ref->lval, strlen(ref->lval));
452 
453  for (size_t i = 0; i < ref->num_indices; i++)
454  {
455  BufferAppend(buf, "[", sizeof(char));
456  BufferAppend(buf, ref->indices[i], strlen(ref->indices[i]));
457  BufferAppend(buf, "]", sizeof(char));
458  }
459 
460  return BufferClose(buf);
461 }
462 
463 char *VarRefMangle(const VarRef *ref)
464 {
465  char *suffix = VarRefToString(ref, false);
466 
467  if (!ref->scope)
468  {
469  return suffix;
470  }
471  else
472  {
473  if (ref->ns)
474  {
475  char *mangled = StringFormat("%s*%s#%s", ref->ns, ref->scope, suffix);
476  free(suffix);
477  return mangled;
478  }
479  else
480  {
481  char *mangled = StringFormat("%s#%s", ref->scope, suffix);
482  free(suffix);
483  return mangled;
484  }
485  }
486 }
487 
488 VarRef *VarRefDeMangle(const char *mangled_var_ref)
489 {
490  return VarRefParseFromNamespaceAndScope(mangled_var_ref, NULL, NULL,
492 }
493 
494 static bool VarRefIsMeta(VarRef *ref)
495 {
496  return StringEndsWith(ref->scope, "_meta");
497 }
498 
499 void VarRefSetMeta(VarRef *ref, bool enabled)
500 {
501  if (enabled)
502  {
503  if (!VarRefIsMeta(ref))
504  {
505  char *tmp = StringConcatenate(2, ref->scope, "_meta");
506  free(ref->scope);
507  ref->scope = tmp;
508  }
509  }
510  else
511  {
512  if (VarRefIsMeta(ref))
513  {
514  char *tmp = ref->scope;
515  size_t len = strlen(ref->scope);
516  memcpy(ref->scope, StringSubstring(ref->scope, len, 0, len - strlen("_meta")), len - strlen("_meta"));
517  free(tmp);
518  }
519  }
520 }
521 
522 bool VarRefIsQualified(const VarRef *ref)
523 {
524  return ref->scope != NULL;
525 }
526 
527 void VarRefQualify(VarRef *ref, const char *ns, const char *scope)
528 {
529  assert(scope);
530 
531  free(ref->ns);
532  ref->ns = NULL;
533 
534  free(ref->scope);
535  ref->scope = NULL;
536 
537  ref->ns = ns ? xstrdup(ns) : NULL;
538  ref->scope = xstrdup(scope);
539 }
540 
541 void VarRefAddIndex(VarRef *ref, const char *index)
542 {
543  if (ref->indices)
544  {
545  assert(ref->num_indices > 0);
546  ref->indices = xrealloc(ref->indices, sizeof(char *) * (ref->num_indices + 1));
547  }
548  else
549  {
550  assert(ref->num_indices == 0);
551  ref->indices = xmalloc(sizeof(char *));
552  }
553 
554  ref->indices[ref->num_indices] = xstrdup(index);
555  ref->num_indices++;
556 }
557 
558 int VarRefCompare(const VarRef *a, const VarRef *b)
559 {
560  int ret = strcmp(a->lval, b->lval);
561  if (ret != 0)
562  {
563  return ret;
564  }
565 
566  ret = strcmp(NULLStringToEmpty(a->scope), NULLStringToEmpty(b->scope));
567  if (ret != 0)
568  {
569  return ret;
570  }
571 
572  const char *a_ns = a->ns ? a->ns : "default";
573  const char *b_ns = b->ns ? b->ns : "default";
574 
575  ret = strcmp(a_ns, b_ns);
576  if (ret != 0)
577  {
578  return ret;
579  }
580 
581  ret = a->num_indices - b->num_indices;
582  if (ret != 0)
583  {
584  return ret;
585  }
586 
587  for (size_t i = 0; i < a->num_indices; i++)
588  {
589  ret = strcmp(a->indices[i], b->indices[i]);
590  if (ret != 0)
591  {
592  return ret;
593  }
594  }
595 
596  return 0;
597 }
598 
599 bool VarRefEqual_untyped(const void *a, const void *b)
600 {
601  return (VarRefCompare(a, b) == 0);
602 }
void * xmalloc(size_t size)
Definition: alloc-mini.c:46
char * xstrdup(const char *str)
Definition: alloc-mini.c:56
void * xrealloc(void *ptr, size_t size)
Definition: alloc.c:51
char * xstrndup(const char *str, size_t n)
Definition: alloc.c:61
void BufferDestroy(Buffer *buffer)
Destroys a buffer and frees the memory associated with it.
Definition: buffer.c:72
Buffer * BufferNew(void)
Buffer initialization routine.
Definition: buffer.c:48
const char * BufferData(const Buffer *buffer)
Provides a pointer to the internal data.
Definition: buffer.c:470
void BufferClear(Buffer *buffer)
Clears the buffer.
Definition: buffer.c:457
void BufferAppend(Buffer *buffer, const char *bytes, unsigned int length)
Definition: buffer.c:269
char * BufferClose(Buffer *buffer)
Destroys a buffer structure returning the its contents.
Definition: buffer.c:81
#define ARG_UNUSED
Definition: cf-net.c:47
#define CF_NS
Definition: cf3.defs.h:109
#define CF_MANGLED_SCOPE
Definition: cf3.defs.h:115
#define CF_MANGLED_NS
Definition: cf3.defs.h:114
void free(void *)
#define NULL
Definition: getopt1.c:56
void Log(LogLevel level, const char *fmt,...)
Definition: logging.c:409
@ LOG_LEVEL_ERR
Definition: logging.h:42
SpecialScope SpecialScopeFromString(const char *scope)
Definition: scope.c:67
@ SPECIAL_SCOPE_THIS
Definition: scope.h:39
@ SPECIAL_SCOPE_NONE
Definition: scope.h:43
bool StringEndsWith(const char *str, const char *suffix)
Check if a string ends with the given suffix.
Definition: string_lib.c:1330
char * StringSubstring(const char *source, size_t source_len, int start, int len)
Definition: string_lib.c:391
bool IsStrIn(const char *str, const char *const strs[])
Definition: string_lib.c:782
char * NULLStringToEmpty(char *str)
Definition: string_lib.c:748
char * StringConcatenate(size_t count, const char *first,...)
Definition: string_lib.c:348
char * StringFormat(const char *fmt,...)
Format string like sprintf and return formatted string allocated on heap as a return value.
Definition: string_lib.c:51
Definition: buffer.h:50
Definition: policy.h:70
char * name
Definition: policy.h:74
char * ns
Definition: policy.h:75
char ** indices
char * ns
char * scope
size_t num_indices
char * lval
VarRef * VarRefParse(const char *var_ref_string)
VarRef * VarRefParseFromScope(const char *var_ref_string, const char *scope)
bool VarRefEqual_untyped(const void *a, const void *b)
void VarRefAddIndex(VarRef *ref, const char *index)
void VarRefDestroy(VarRef *ref)
static bool VarRefIsMeta(VarRef *ref)
bool VarRefIsQualified(const VarRef *ref)
unsigned int VarRefHash_untyped(const void *ref, unsigned int seed)
VarRef VarRefConst(const char *ns, const char *scope, const char *lval)
void VarRefSetMeta(VarRef *ref, bool enabled)
VarRef * VarRefParseFromBundle(const char *var_ref_string, const Bundle *bundle)
Parse the variable reference in the context of a bundle. This means that the VarRef will inherit scop...
static bool IndexBracketsBalance(const char *var_string)
void VarRefQualify(VarRef *ref, const char *ns, const char *scope)
int VarRefCompare(const VarRef *a, const VarRef *b)
VarRef * VarRefParseFromNamespaceAndScope(const char *qualified_name, const char *_ns, const char *_scope, char ns_separator, char scope_separator)
static unsigned VarRefHash(const VarRef *ref)
static size_t IndexCount(const char *var_string)
VarRef * VarRefCopyIndexless(const VarRef *ref)
#define ARRAY_SEPARATOR_HASH
VarRef * VarRefDeMangle(const char *mangled_var_ref)
void VarRefDestroy_untyped(void *ref)
char * VarRefMangle(const VarRef *ref)
VarRef * VarRefCopyLocalized(const VarRef *ref)
VarRef * VarRefCopy(const VarRef *ref)
char * VarRefToString(const VarRef *ref, bool qualified)
bool is_this_not_special(const char *scope, const char *lval)