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)  

vars.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 <vars.h>
26 
27 #include <conversion.h>
28 #include <expand.h>
29 #include <scope.h>
30 #include <matching.h>
31 #include <unix.h>
32 #include <misc_lib.h>
33 #include <rlist.h>
34 #include <policy.h>
35 #include <eval_context.h>
36 
37 static bool IsCf3Scalar(char *str);
38 
39 /*******************************************************************/
40 
41 bool RlistIsUnresolved(const Rlist *list)
42 {
43  for (const Rlist *rp = list; rp != NULL; rp = rp->next)
44  {
45  // JSON data container values are never expanded, except with the
46  // data_expand() function which see.
47  if (rp->val.type == RVAL_TYPE_CONTAINER)
48  {
49  continue;
50  }
51 
52  if (rp->val.type != RVAL_TYPE_SCALAR)
53  {
54  return true;
55  }
56 
58  {
59  if (strstr(RlistScalarValue(rp), "$(this)") || strstr(RlistScalarValue(rp), "${this}") ||
60  strstr(RlistScalarValue(rp), "$(this.k)") || strstr(RlistScalarValue(rp), "${this.k}") ||
61  strstr(RlistScalarValue(rp), "$(this.k[1])") || strstr(RlistScalarValue(rp), "${this.k[1]}") ||
62  strstr(RlistScalarValue(rp), "$(this.v)") || strstr(RlistScalarValue(rp), "${this.v}"))
63  {
64  // We should allow this in function args for substitution in maplist() etc
65  // We should allow this.k and this.k[1] and this.v in function args for substitution in maparray() etc
66  }
67  else
68  {
69  return true;
70  }
71  }
72  }
73 
74  return false;
75 }
76 
77 /******************************************************************/
78 
79 bool StringContainsVar(const char *s, const char *v)
80 {
81  int vlen = strlen(v);
82 
83  if (s == NULL)
84  {
85  return false;
86  }
87 
88 /* Look for ${v}, $(v), @{v}, $(v) */
89 
90  for (;;)
91  {
92  /* Look for next $ or @ */
93  s = strpbrk(s, "$@");
94  if (s == NULL)
95  {
96  return false;
97  }
98  /* If next symbol */
99  if (*++s == '\0')
100  {
101  return false;
102  }
103  /* is { or ( */
104  if (*s != '(' && *s != '{')
105  {
106  continue;
107  }
108  /* Then match the variable starting from next symbol */
109  if (strncmp(s + 1, v, vlen) != 0)
110  {
111  continue;
112  }
113  /* And if it matched, match the closing bracket */
114  if ((s[0] == '(' && s[vlen + 1] == ')') || (s[0] == '{' && s[vlen + 1] == '}'))
115  {
116  return true;
117  }
118  }
119 }
120 
121 /*********************************************************************/
122 
123 bool IsCf3VarString(const char *str)
124 {
125  char left = 'x', right = 'x';
126  int dollar = false;
127  int bracks = 0, vars = 0;
128 
129  if (str == NULL)
130  {
131  return false;
132  }
133 
134  for (const char *sp = str; *sp != '\0'; sp++) /* check for varitems */
135  {
136  switch (*sp)
137  {
138  case '$':
139  case '@':
140  if (*(sp + 1) == '{' || *(sp + 1) == '(')
141  {
142  dollar = true;
143  }
144  break;
145  case '(':
146  case '{':
147  if (dollar)
148  {
149  left = *sp;
150  bracks++;
151  }
152  break;
153  case ')':
154  case '}':
155  if (dollar)
156  {
157  bracks--;
158  right = *sp;
159  }
160  break;
161  }
162 
163  /* Some chars cannot be in variable ids, e.g.
164  $(/bin/cat file) is legal in bash */
165 
166  if (bracks > 0)
167  {
168  switch (*sp)
169  {
170  case '/':
171  return false;
172  }
173  }
174 
175  if (left == '(' && right == ')' && dollar && (bracks == 0))
176  {
177  vars++;
178  dollar = false;
179  }
180 
181  if (left == '{' && right == '}' && dollar && (bracks == 0))
182  {
183  vars++;
184  dollar = false;
185  }
186  }
187 
188  if (dollar && (bracks != 0))
189  {
190  char output[CF_BUFSIZE];
191 
192  snprintf(output, CF_BUFSIZE, "Broken variable syntax or bracket mismatch in string (%s)", str);
193  yyerror(output);
194  return false;
195  }
196 
197  return (vars != 0);
198 }
199 
200 /*********************************************************************/
201 
202 static bool IsCf3Scalar(char *str)
203 {
204  char *sp;
205  char left = 'x', right = 'x';
206  int dollar = false;
207  int bracks = 0, vars = 0;
208 
209  if (str == NULL)
210  {
211  return false;
212  }
213 
214  for (sp = str; *sp != '\0'; sp++) /* check for varitems */
215  {
216  switch (*sp)
217  {
218  case '$':
219  if (*(sp + 1) == '{' || *(sp + 1) == '(')
220  {
221  dollar = true;
222  }
223  break;
224  case '(':
225  case '{':
226  if (dollar)
227  {
228  left = *sp;
229  bracks++;
230  }
231  break;
232  case ')':
233  case '}':
234  if (dollar)
235  {
236  bracks--;
237  right = *sp;
238  }
239  break;
240  }
241 
242  /* Some chars cannot be in variable ids, e.g.
243  $(/bin/cat file) is legal in bash */
244 
245  if (bracks > 0)
246  {
247  switch (*sp)
248  {
249  case '/':
250  return false;
251  }
252  }
253 
254  if (left == '(' && right == ')' && dollar && (bracks == 0))
255  {
256  vars++;
257  dollar = false;
258  }
259 
260  if (left == '{' && right == '}' && dollar && (bracks == 0))
261  {
262  vars++;
263  dollar = false;
264  }
265  }
266 
267  if (dollar && (bracks != 0))
268  {
269  char output[CF_BUFSIZE];
270 
271  snprintf(output, CF_BUFSIZE, "Broken scalar variable syntax or bracket mismatch in '%s'", str);
272  yyerror(output);
273  return false;
274  }
275 
276  return (vars != 0);
277 }
278 
279 /* Extract everything up to the dollar sign. */
280 size_t ExtractScalarPrefix(Buffer *out, const char *str, size_t len)
281 {
282  assert(str);
283  if (len == 0)
284  {
285  return 0;
286  }
287 
288  const char *dollar_point = NULL;
289  for (size_t i = 0; i < (len - 1); i++)
290  {
291  if (str[i] == '$')
292  {
293  if (str[i + 1] == '(' || str[i + 1] == '{')
294  {
295  dollar_point = str + i;
296  break;
297  }
298  }
299  }
300 
301  if (!dollar_point)
302  {
303  BufferAppend(out, str, len);
304  return len;
305  }
306  else if (dollar_point > str)
307  {
308  size_t prefix_len = dollar_point - str;
309  if (prefix_len > 0)
310  {
311  BufferAppend(out, str, prefix_len);
312  }
313  return prefix_len;
314  }
315  return 0;
316 }
317 
318 static const char *ReferenceEnd(const char *str, size_t len)
319 {
320  assert(len > 1);
321  assert(str[0] == '$');
322  assert(str[1] == '{' || str[1] == '(');
323 
324 #define MAX_VARIABLE_REFERENCE_LEVELS 10
325  char stack[MAX_VARIABLE_REFERENCE_LEVELS] = { 0, str[1], 0 };
326  int level = 1;
327 
328  for (size_t i = 2; i < len; i++)
329  {
330  switch (str[i])
331  {
332  case '{':
333  case '(':
334  if (level < MAX_VARIABLE_REFERENCE_LEVELS - 1)
335  {
336  level++;
337  stack[level] = str[i];
338  }
339  else
340  {
341  Log(LOG_LEVEL_ERR, "Stack overflow in variable reference parsing. More than %d levels", MAX_VARIABLE_REFERENCE_LEVELS);
342  return NULL;
343  }
344  break;
345 
346  case '}':
347  if (stack[level] != '{')
348  {
349  Log(LOG_LEVEL_ERR, "Variable reference bracket mismatch '%.*s'",
350  (int) len, str);
351  return NULL;
352  }
353  level--;
354  break;
355  case ')':
356  if (stack[level] != '(')
357  {
358  Log(LOG_LEVEL_ERR, "Variable reference bracket mismatch '%.*s'",
359  (int) len, str);
360  return NULL;
361  }
362  level--;
363  break;
364  }
365 
366  if (level == 0)
367  {
368  return str + i;
369  }
370  }
371 
372  return NULL;
373 }
374 
375 /**
376  * Extract variable inside dollar-paren.
377  * @param extract_inner ignore opening dollar-paren and closing paren.
378  */
379 bool ExtractScalarReference(Buffer *out, const char *str, size_t len, bool extract_inner)
380 {
381  if (len <= 1)
382  {
383  return false;
384  }
385 
386  const char *dollar_point = memchr(str, '$', len);
387  if (!dollar_point || (dollar_point - str) == len)
388  {
389  return false;
390  }
391  else
392  {
393  const char *close_point = NULL;
394  {
395  size_t remaining = len - (dollar_point - str);
396  if (*(dollar_point + 1) == '{' || *(dollar_point + 1) == '(')
397  {
398  close_point = ReferenceEnd(dollar_point, remaining);
399  }
400  else
401  {
402  return ExtractScalarReference(out, dollar_point + 1,
403  remaining - 1, extract_inner);
404  }
405  }
406 
407  if (!close_point)
408  {
409  Log(LOG_LEVEL_ERR, "Variable reference close mismatch '%.*s'",
410  (int) len, str);
411  return false;
412  }
413 
414  size_t outer_len = close_point - dollar_point + 1;
415  if (outer_len <= 3)
416  {
417  Log(LOG_LEVEL_ERR, "Empty variable reference close mismatch '%.*s'",
418  (int) len, str);
419  return false;
420  }
421 
422  if (extract_inner)
423  {
424  BufferAppend(out, dollar_point + 2, outer_len - 3);
425  }
426  else
427  {
428  BufferAppend(out, dollar_point, outer_len);
429  }
430  return true;
431  }
432 }
433 
434 /*********************************************************************/
435 
436 bool IsQualifiedVariable(const char *var)
437 {
438  int isarraykey = false;
439 
440  for (const char *sp = var; *sp != '\0'; sp++)
441  {
442  if (*sp == '[')
443  {
444  isarraykey = true;
445  }
446 
447  if (isarraykey)
448  {
449  return false;
450  }
451  else
452  {
453  if (*sp == '.')
454  {
455  return true;
456  }
457  }
458  }
459 
460  return false;
461 }
void BufferAppend(Buffer *buffer, const char *bytes, unsigned int length)
Definition: buffer.c:269
@ RVAL_TYPE_CONTAINER
Definition: cf3.defs.h:609
@ RVAL_TYPE_SCALAR
Definition: cf3.defs.h:606
void yyerror(const char *str)
Definition: cf3parse.c:2924
#define CF_BUFSIZE
Definition: definitions.h:50
#define NULL
Definition: getopt1.c:56
void Log(LogLevel level, const char *fmt,...)
Definition: logging.c:409
@ LOG_LEVEL_ERR
Definition: logging.h:42
char * RlistScalarValue(const Rlist *rlist)
Definition: rlist.c:83
char * strstr(const char *haystack, const char *needle)
Definition: strstr.c:35
Definition: buffer.h:50
Definition: rlist.h:35
#define MAX_VARIABLE_REFERENCE_LEVELS
bool StringContainsVar(const char *s, const char *v)
Definition: vars.c:79
bool RlistIsUnresolved(const Rlist *list)
Definition: vars.c:41
bool ExtractScalarReference(Buffer *out, const char *str, size_t len, bool extract_inner)
Definition: vars.c:379
static bool IsCf3Scalar(char *str)
Definition: vars.c:202
bool IsQualifiedVariable(const char *var)
Definition: vars.c:436
size_t ExtractScalarPrefix(Buffer *out, const char *str, size_t len)
Definition: vars.c:280
static const char * ReferenceEnd(const char *str, size_t len)
Definition: vars.c:318
bool IsCf3VarString(const char *str)
Definition: vars.c:123