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)  

verify_classes.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 <verify_classes.h>
26 
27 #include <attributes.h>
28 #include <matching.h>
29 #include <files_names.h>
30 #include <fncall.h>
31 #include <rlist.h>
32 #include <expand.h>
33 #include <promises.h>
34 #include <conversion.h>
35 #include <logic_expressions.h>
36 #include <string_lib.h> /* StringHash */
37 #include <regex.h> /* StringMatchFull */
38 
39 
40 static bool EvalClassExpression(EvalContext *ctx, Constraint *cp, const Promise *pp);
41 
42 static bool ValidClassName(const char *str)
43 {
44  ParseResult res = ParseExpression(str, 0, strlen(str));
45 
46  if (res.result)
47  {
49  }
50 
51  return res.result && res.position == strlen(str);
52 }
53 
55 {
56  assert(param == NULL);
57 
58  Log(LOG_LEVEL_DEBUG, "Evaluating classes promise: %s", pp->promiser);
59 
61 
62  if (!StringMatchFull("[a-zA-Z0-9_]+", pp->promiser))
63  {
64  Log(LOG_LEVEL_VERBOSE, "Class identifier '%s' contains illegal characters - canonifying", pp->promiser);
66  }
67 
68  if (a.context.nconstraints > 1)
69  {
70  cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, &a, "Irreconcilable constraints in classes for '%s'", pp->promiser);
71  return PROMISE_RESULT_FAIL;
72  }
73 
74  if (a.context.expression == NULL ||
76  {
77  if (a.context.expression == NULL)
78  {
79  Log(LOG_LEVEL_DEBUG, "Setting class '%s' without an expression, implying 'any'", pp->promiser);
80  }
81 
82  if (!ValidClassName(pp->promiser))
83  {
85  "Attempted to name a class '%s', which is an illegal class identifier", pp->promiser);
86  return PROMISE_RESULT_FAIL;
87  }
88  else
89  {
90  Buffer *tag_buffer = BufferNew();
91  BufferAppendString(tag_buffer, "source=promise");
92 
93  for (const Rlist *rp = PromiseGetConstraintAsList(ctx, "meta", pp); rp; rp = rp->next)
94  {
95  BufferAppendChar(tag_buffer, ',');
96  BufferAppendString(tag_buffer, RlistScalarValue(rp));
97  }
98  char *tags = BufferClose(tag_buffer);
99 
100  if (/* Persistent classes are always global: */
101  a.context.persistent > 0 ||
102  /* Namespace-scope is global: */
104  /* If there is no explicit scope, common bundles define global
105  * classes, other bundles define local classes: */
107  strcmp(PromiseGetBundle(pp)->type, "common") == 0))
108  {
109  Log(LOG_LEVEL_VERBOSE, "C: + Global class: %s",
110  pp->promiser);
112  }
113  else
114  {
115  Log(LOG_LEVEL_VERBOSE, "C: + Private class: %s",
116  pp->promiser);
118  }
119 
120  if (a.context.persistent > 0)
121  {
123  "C: + Persistent class: '%s' (%d minutes)",
124  pp->promiser, a.context.persistent);
127  }
128 
129  free(tags);
130 
131  return PROMISE_RESULT_NOOP;
132  }
133  }
134 
135  return PROMISE_RESULT_NOOP;
136 }
137 
138 static bool SelectClass(EvalContext *ctx, const Rlist *list, const Promise *pp)
139 {
140  int count = RlistLen(list);
141 
142  if (count == 0)
143  {
144  Log(LOG_LEVEL_ERR, "No classes to select on RHS");
146  return false;
147  }
148  else if (count == 1 && IsVarList(RlistScalarValue(list)))
149  {
151  "select_class: Can not expand list '%s' for setting class.",
152  RlistScalarValue(list));
154  return false;
155  }
156 
157  assert(list);
158 
159  char splay[CF_MAXVARSIZE];
160  snprintf(splay, CF_MAXVARSIZE, "%s+%s+%ju",
161  VFQNAME, VIPADDRESS, (uintmax_t)getuid());
162  unsigned int hash = StringHash(splay, 0);
163  int n = hash % count;
164 
165  while (n > 0 && list->next != NULL)
166  {
167  n--;
168  list = list->next;
169  }
170 
171  /* We are not having expanded variable or list at this point,
172  * so we can not set select_class. */
173  if (IsExpandable(RlistScalarValue(list)))
174  {
176  "select_class: Can not use not expanded element '%s' for setting class.",
177  RlistScalarValue(list));
179  return false;
180  }
181 
183  CONTEXT_SCOPE_NAMESPACE, "source=promise");
184  return true;
185 }
186 
187 static bool DistributeClass(EvalContext *ctx, const Rlist *dist, const Promise *pp)
188 {
189  int total = 0;
190  const Rlist *rp;
191 
192  for (rp = dist; rp != NULL; rp = rp->next)
193  {
194  int result = IntFromString(RlistScalarValue(rp));
195 
196  if (result < 0)
197  {
198  Log(LOG_LEVEL_ERR, "Negative integer in class distribution");
200  return false;
201  }
202 
203  total += result;
204  }
205 
206  if (total == 0)
207  {
208  Log(LOG_LEVEL_ERR, "An empty distribution was specified on RHS");
210  return false;
211  }
212 
213  double fluct = drand48() * total;
214  assert(0 <= fluct && fluct < total);
215 
216  for (rp = dist; rp != NULL; rp = rp->next)
217  {
218  fluct -= IntFromString(RlistScalarValue(rp));
219  if (fluct < 0)
220  {
221  break;
222  }
223  }
224  assert(rp);
225 
226  char buffer[CF_MAXVARSIZE];
227  snprintf(buffer, CF_MAXVARSIZE, "%s_%s", pp->promiser, RlistScalarValue(rp));
228 
229  if (strcmp(PromiseGetBundle(pp)->type, "common") == 0)
230  {
232  "source=promise");
233  }
234  else
235  {
237  "source=promise");
238  }
239 
240  return true;
241 }
242 
243 enum combine_t { c_or, c_and, c_xor }; // Class combinations
244 static bool EvalBoolCombination(EvalContext *ctx, const Rlist *list,
245  enum combine_t logic)
246 {
247  bool result = (logic == c_and);
248 
249  for (const Rlist *rp = list; rp != NULL; rp = rp->next)
250  {
251  // tolerate unexpanded entries here and interpret as "class not set"
252  bool here = (rp->val.type == RVAL_TYPE_SCALAR &&
253  IsDefinedClass(ctx, RlistScalarValue(rp)));
254 
255  // shortcut "and" and "or"
256  switch (logic)
257  {
258  case c_or:
259  if (here)
260  {
261  return true;
262  }
263  break;
264 
265  case c_and:
266  if (!here)
267  {
268  return false;
269  }
270  break;
271 
272  default:
273  result ^= here;
274  break;
275  }
276  }
277 
278  return result;
279 }
280 
281 static bool EvalClassExpression(EvalContext *ctx, Constraint *cp, const Promise *pp)
282 {
283  assert(pp);
284 
285  if (cp == NULL) // ProgrammingError ? We'll crash RSN anyway ...
286  {
288  "EvalClassExpression internal diagnostic discovered an ill-formed condition");
289  }
290 
291  if (!IsDefinedClass(ctx, pp->classes))
292  {
293  return false;
294  }
295 
296  if (IsDefinedClass(ctx, pp->promiser))
297  {
298  if (PromiseGetConstraintAsInt(ctx, "persistence", pp) == 0)
299  {
301  " ?> Cancelling cached persistent class %s",
302  pp->promiser);
304  }
305  return false;
306  }
307 
308  switch (cp->rval.type)
309  {
310  Rval rval;
311  FnCall *fp;
312 
313  case RVAL_TYPE_FNCALL:
314  fp = RvalFnCallValue(cp->rval);
315  /* Special expansion of functions for control, best effort only: */
316  FnCallResult res = FnCallEvaluate(ctx, PromiseGetPolicy(pp), fp, pp);
317 
318  FnCallDestroy(fp);
319  cp->rval = res.rval;
320  break;
321 
322  case RVAL_TYPE_LIST:
323  for (Rlist *rp = cp->rval.item; rp != NULL; rp = rp->next)
324  {
325  rval = EvaluateFinalRval(ctx, PromiseGetPolicy(pp), NULL,
326  "this", rp->val, true, pp);
327  RvalDestroy(rp->val);
328  rp->val = rval;
329  }
330  break;
331 
332  default:
333  rval = ExpandPrivateRval(ctx, NULL, "this", cp->rval.item, cp->rval.type);
334  RvalDestroy(cp->rval);
335  cp->rval = rval;
336  break;
337  }
338 
339  if (strcmp(cp->lval, "expression") == 0)
340  {
341  return (cp->rval.type == RVAL_TYPE_SCALAR &&
342  IsDefinedClass(ctx, RvalScalarValue(cp->rval)));
343  }
344 
345  if (strcmp(cp->lval, "not") == 0)
346  {
347  return (cp->rval.type == RVAL_TYPE_SCALAR &&
348  !IsDefinedClass(ctx, RvalScalarValue(cp->rval)));
349  }
350 
351  /* If we get here, anything remaining on the RHS must be a clist */
352  if (cp->rval.type != RVAL_TYPE_LIST)
353  {
354  Log(LOG_LEVEL_ERR, "RHS of promise body attribute '%s' is not a list", cp->lval);
356  return true;
357  }
358 
359  // Class selection
360  if (strcmp(cp->lval, "select_class") == 0)
361  {
362  return SelectClass(ctx, cp->rval.item, pp);
363  }
364 
365  // Class distributions
366  if (strcmp(cp->lval, "dist") == 0)
367  {
368  return DistributeClass(ctx, cp->rval.item, pp);
369  }
370 
371  /* Combine with and/or/xor: */
372  if (strcmp(cp->lval, "or") == 0)
373  {
374  return EvalBoolCombination(ctx, cp->rval.item, c_or);
375  }
376  else if (strcmp(cp->lval, "and") == 0)
377  {
378  return EvalBoolCombination(ctx, cp->rval.item, c_and);
379  }
380  else if (strcmp(cp->lval, "xor") == 0)
381  {
382  return EvalBoolCombination(ctx, cp->rval.item, c_xor);
383  }
384 
385  return false;
386 }
Attributes GetClassContextAttributes(const EvalContext *ctx, const Promise *pp)
Definition: attributes.c:231
Buffer * BufferNew(void)
Buffer initialization routine.
Definition: buffer.c:48
void BufferAppendChar(Buffer *buffer, char byte)
Appends a char to an existing buffer.
Definition: buffer.c:299
char * BufferClose(Buffer *buffer)
Destroys a buffer structure returning the its contents.
Definition: buffer.c:81
void BufferAppendString(Buffer *buffer, const char *str)
Definition: buffer.c:246
#define ARG_UNUSED
Definition: cf-net.c:47
@ RVAL_TYPE_LIST
Definition: cf3.defs.h:607
@ RVAL_TYPE_SCALAR
Definition: cf3.defs.h:606
@ RVAL_TYPE_FNCALL
Definition: cf3.defs.h:608
PromiseResult
Definition: cf3.defs.h:122
@ PROMISE_RESULT_NOOP
Definition: cf3.defs.h:124
@ PROMISE_RESULT_FAIL
Definition: cf3.defs.h:127
@ CONTEXT_SCOPE_NAMESPACE
Definition: cf3.defs.h:946
@ CONTEXT_SCOPE_NONE
Definition: cf3.defs.h:948
@ CONTEXT_SCOPE_BUNDLE
Definition: cf3.defs.h:947
void free(void *)
char VFQNAME[]
Definition: cf3globals.c:58
char VIPADDRESS[64]
Definition: cf3globals.c:81
long IntFromString(const char *s)
Definition: conversion.c:390
@ CONTEXT_STATE_POLICY_RESET
Definition: db_structs.h:137
#define CF_MAXVARSIZE
Definition: definitions.h:36
double drand48(void)
Definition: drand48.c:36
void EvalContextHeapPersistentSave(EvalContext *ctx, const char *name, unsigned int ttl_minutes, PersistentClassPolicy policy, const char *tags)
Definition: eval_context.c:643
void cfPS(EvalContext *ctx, LogLevel level, PromiseResult status, const Promise *pp, const Attributes *attr, const char *fmt,...)
void EvalContextHeapPersistentRemove(const char *context)
Definition: eval_context.c:715
bool EvalContextClassPutSoft(EvalContext *ctx, const char *name, ContextScope scope, const char *tags)
static bool IsDefinedClass(const EvalContext *ctx, const char *context)
Definition: eval_context.h:213
bool IsVarList(const char *var)
Definition: expand.c:1274
Rval EvaluateFinalRval(EvalContext *ctx, const Policy *policy, const char *ns, const char *scope, Rval rval, bool forcelist, const Promise *pp)
Definition: expand.c:610
bool IsExpandable(const char *str)
Definition: expand.c:1117
Rval ExpandPrivateRval(EvalContext *ctx, const char *ns, const char *scope, const void *rval_item, RvalType rval_type)
Definition: expand.c:313
void FnCallDestroy(FnCall *fp)
Definition: fncall.c:185
FnCallResult FnCallEvaluate(EvalContext *ctx, const Policy *policy, FnCall *fp, const Promise *caller)
Definition: fncall.c:309
#define NULL
Definition: getopt1.c:56
void Log(LogLevel level, const char *fmt,...)
Definition: logging.c:409
@ LOG_LEVEL_ERR
Definition: logging.h:42
@ LOG_LEVEL_DEBUG
Definition: logging.h:47
@ LOG_LEVEL_VERBOSE
Definition: logging.h:46
ParseResult ParseExpression(const char *expr, int start, int end)
void FreeExpression(Expression *e)
uid_t getuid(void)
Rlist * PromiseGetConstraintAsList(const EvalContext *ctx, const char *lval, const Promise *pp)
Get the Rlist value of the first effective constraint found matching, from a promise.
Definition: policy.c:2920
int PromiseGetConstraintAsInt(const EvalContext *ctx, const char *lval, const Promise *pp)
Get the int value of the first effective constraint found matching, from a promise.
Definition: policy.c:2724
const Policy * PromiseGetPolicy(const Promise *pp)
Definition: policy.c:2676
const Bundle * PromiseGetBundle(const Promise *pp)
Definition: policy.c:2671
void PromiseRef(LogLevel level, const Promise *pp)
Definition: promises.c:769
bool StringMatchFull(const char *regex, const char *str)
Definition: regex.c:106
char * RlistScalarValue(const Rlist *rlist)
Definition: rlist.c:83
FnCall * RvalFnCallValue(Rval rval)
Definition: rlist.c:141
char * RvalScalarValue(Rval rval)
Definition: rlist.c:129
void RvalDestroy(Rval rval)
Definition: rlist.c:940
int RlistLen(const Rlist *start)
Definition: rlist.c:672
unsigned int StringHash(const char *str, unsigned int seed)
Definition: string_lib.c:90
void CanonifyNameInPlace(char *s)
Definition: string_lib.c:1574
ContextConstraint context
Definition: cf3.defs.h:1546
Definition: buffer.h:50
Rval rval
Definition: policy.h:133
char * lval
Definition: policy.h:132
Constraint * expression
Definition: cf3.defs.h:1193
ContextScope scope
Definition: cf3.defs.h:1194
Rval rval
Definition: fncall.h:47
Definition: fncall.h:31
Expression * result
char * promiser
Definition: policy.h:115
char * classes
Definition: policy.h:113
Definition: rlist.h:35
Rlist * next
Definition: rlist.h:37
Definition: cf3.defs.h:614
RvalType type
Definition: cf3.defs.h:616
void * item
Definition: cf3.defs.h:615
static bool EvalBoolCombination(EvalContext *ctx, const Rlist *list, enum combine_t logic)
static bool DistributeClass(EvalContext *ctx, const Rlist *dist, const Promise *pp)
combine_t
@ c_and
@ c_or
@ c_xor
static bool ValidClassName(const char *str)
PromiseResult VerifyClassPromise(EvalContext *ctx, const Promise *pp, void *param)
static bool SelectClass(EvalContext *ctx, const Rlist *list, const Promise *pp)
static bool EvalClassExpression(EvalContext *ctx, Constraint *cp, const Promise *pp)