1 /*
2 Copyright 2020 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 <class.h>
25
26 #include <map.h>
27 #include <alloc.h>
28 #include <string_lib.h> /* String*() */
29 #include <regex.h> /* CompileRegex,StringMatchFullWithPrecompiledRegex */
30 #include <files_names.h>
31
32
33 static void ClassDestroy(Class *cls); /* forward declaration */
34
ClassDestroy_untyped(void * p)35 static void ClassDestroy_untyped(void *p)
36 {
37 ClassDestroy(p);
38 }
39
40
41 /**
42 Define ClassMap.
43 Key: a string which is always the fully qualified class name,
44 for example "default:127_0_0_1"
45 */
46
47 TYPED_MAP_DECLARE(Class, char *, Class *)
48
49 TYPED_MAP_DEFINE(Class, char *, Class *,
50 StringHash_untyped,
51 StringEqual_untyped,
52 free,
53 ClassDestroy_untyped)
54
55 struct ClassTable_
56 {
57 ClassMap *classes;
58 };
59
60 struct ClassTableIterator_
61 {
62 MapIterator iter;
63 char *ns;
64 bool is_hard;
65 bool is_soft;
66 };
67
68
69 /**
70 * @param #tags is a comma separated string of words.
71 * Both "" or NULL are equivalent.
72 */
ClassInit(Class * cls,const char * ns,const char * name,bool is_soft,ContextScope scope,const char * tags)73 static void ClassInit(Class *cls,
74 const char *ns, const char *name,
75 bool is_soft, ContextScope scope, const char *tags)
76 {
77 if (ns == NULL || strcmp(ns, "default") == 0)
78 {
79 cls->ns = NULL;
80 }
81 else
82 {
83 cls->ns = xstrdup(ns);
84 }
85
86 cls->name = xstrdup(name);
87 CanonifyNameInPlace(cls->name);
88
89 cls->is_soft = is_soft;
90 cls->scope = scope;
91
92 cls->tags = StringSetFromString(tags, ',');
93 if (!is_soft && !StringSetContains(cls->tags, "hardclass"))
94 {
95 StringSetAdd(cls->tags, xstrdup("hardclass"));
96 }
97 }
98
ClassDestroySoft(Class * cls)99 static void ClassDestroySoft(Class *cls)
100 {
101 if (cls)
102 {
103 free(cls->ns);
104 free(cls->name);
105 StringSetDestroy(cls->tags);
106 }
107 }
108
ClassDestroy(Class * cls)109 static void ClassDestroy(Class *cls)
110 {
111 if (cls)
112 {
113 ClassDestroySoft(cls);
114 free(cls);
115 }
116 }
117
ClassTableNew(void)118 ClassTable *ClassTableNew(void)
119 {
120 ClassTable *table = xmalloc(sizeof(*table));
121
122 table->classes = ClassMapNew();
123
124 return table;
125 }
126
ClassTableDestroy(ClassTable * table)127 void ClassTableDestroy(ClassTable *table)
128 {
129 if (table)
130 {
131 ClassMapDestroy(table->classes);
132 free(table);
133 }
134 }
135
ClassTablePut(ClassTable * table,const char * ns,const char * name,bool is_soft,ContextScope scope,const char * tags)136 bool ClassTablePut(ClassTable *table,
137 const char *ns, const char *name,
138 bool is_soft, ContextScope scope, const char *tags)
139 {
140 assert(name);
141 assert(is_soft || (!ns || strcmp("default", ns) == 0)); // hard classes should have default namespace
142 assert(is_soft || scope == CONTEXT_SCOPE_NAMESPACE); // hard classes cannot be local
143
144 if (ns == NULL)
145 {
146 ns = "default";
147 }
148
149 Class *cls = xmalloc(sizeof(*cls));
150 ClassInit(cls, ns, name, is_soft, scope, tags);
151
152 /* (cls->name != name) because canonification has happened. */
153 char *fullname = StringConcatenate(3, ns, ":", cls->name);
154
155 Log(LOG_LEVEL_DEBUG, "Setting %sclass: %s",
156 is_soft ? "" : "hard ",
157 fullname);
158
159 return ClassMapInsert(table->classes, fullname, cls);
160 }
161
ClassTableGet(const ClassTable * table,const char * ns,const char * name)162 Class *ClassTableGet(const ClassTable *table, const char *ns, const char *name)
163 {
164 if (ns == NULL)
165 {
166 ns = "default";
167 }
168
169 char fullname[ strlen(ns) + 1 + strlen(name) + 1 ];
170 xsnprintf(fullname, sizeof(fullname), "%s:%s", ns, name);
171
172 return ClassMapGet(table->classes, fullname);
173 }
174
ClassTableMatch(const ClassTable * table,const char * regex)175 Class *ClassTableMatch(const ClassTable *table, const char *regex)
176 {
177 ClassTableIterator *it = ClassTableIteratorNew(table, NULL, true, true);
178 Class *cls = NULL;
179
180 pcre *pattern = CompileRegex(regex);
181 if (pattern == NULL)
182 {
183 // TODO: perhaps pcre has can give more info on this error?
184 Log(LOG_LEVEL_ERR, "Unable to pcre compile regex '%s'", regex);
185 return NULL;
186 }
187
188 while ((cls = ClassTableIteratorNext(it)))
189 {
190 bool matched;
191 if (cls->ns)
192 {
193 char *class_expr = ClassRefToString(cls->ns, cls->name);
194 matched = StringMatchFullWithPrecompiledRegex(pattern, class_expr);
195 free(class_expr);
196 }
197 else
198 {
199 matched = StringMatchFullWithPrecompiledRegex(pattern, cls->name);
200 }
201
202 if (matched)
203 {
204 break;
205 }
206 }
207
208 pcre_free(pattern);
209
210 ClassTableIteratorDestroy(it);
211 return cls;
212 }
213
ClassTableRemove(ClassTable * table,const char * ns,const char * name)214 bool ClassTableRemove(ClassTable *table, const char *ns, const char *name)
215 {
216 if (ns == NULL)
217 {
218 ns = "default";
219 }
220
221 char fullname[ strlen(ns) + 1 + strlen(name) + 1 ];
222 xsnprintf(fullname, sizeof(fullname), "%s:%s", ns, name);
223
224 return ClassMapRemove(table->classes, fullname);
225 }
226
ClassTableClear(ClassTable * table)227 bool ClassTableClear(ClassTable *table)
228 {
229 bool has_classes = (ClassMapSize(table->classes) > 0);
230 ClassMapClear(table->classes);
231 return has_classes;
232 }
233
ClassTableIteratorNew(const ClassTable * table,const char * ns,bool is_hard,bool is_soft)234 ClassTableIterator *ClassTableIteratorNew(const ClassTable *table,
235 const char *ns,
236 bool is_hard, bool is_soft)
237 {
238 ClassTableIterator *iter = xmalloc(sizeof(*iter));
239
240 iter->ns = ns ? xstrdup(ns) : NULL;
241 iter->iter = MapIteratorInit(table->classes->impl);
242 iter->is_soft = is_soft;
243 iter->is_hard = is_hard;
244
245 return iter;
246 }
247
ClassTableIteratorNext(ClassTableIterator * iter)248 Class *ClassTableIteratorNext(ClassTableIterator *iter)
249 {
250 MapKeyValue *keyvalue;
251
252 while ((keyvalue = MapIteratorNext(&iter->iter)) != NULL)
253 {
254 Class *cls = keyvalue->value;
255
256 /* Make sure we never store "default" as namespace in the ClassTable,
257 * instead we have always ns==NULL in that case. */
258 CF_ASSERT_FIX(cls->ns == NULL ||
259 strcmp(cls->ns, "default") != 0,
260 (cls->ns = NULL),
261 "Class table contained \"default\" namespace,"
262 " should never happen!");
263
264 const char *key_ns = cls->ns ? cls->ns : "default";
265
266 if (iter->ns && strcmp(key_ns, iter->ns) != 0)
267 {
268 continue;
269 }
270
271 if (iter->is_soft && !iter->is_soft)
272 {
273 continue;
274 }
275 if (iter->is_hard && !iter->is_hard)
276 {
277 continue;
278 }
279
280 return cls;
281 }
282
283 return NULL;
284 }
285
ClassTableIteratorDestroy(ClassTableIterator * iter)286 void ClassTableIteratorDestroy(ClassTableIterator *iter)
287 {
288 if (iter)
289 {
290 free(iter->ns);
291 free(iter);
292 }
293 }
294
ClassRefParse(const char * expr)295 ClassRef ClassRefParse(const char *expr)
296 {
297 char *name_start = strchr(expr, ':');
298 if (!name_start)
299 {
300 return (ClassRef) { .ns = NULL, .name = xstrdup(expr) };
301 }
302 else
303 {
304 char *ns = NULL;
305 if ((name_start - expr) > 0)
306 {
307 ns = xstrndup(expr, name_start - expr);
308 }
309 else
310 {
311 // this would be invalid syntax
312 ns = xstrdup("");
313 }
314 char *name = xstrdup(name_start + 1);
315 return (ClassRef) { .ns = ns, .name = name };
316 }
317 }
318
ClassRefToString(const char * ns,const char * name)319 char *ClassRefToString(const char *ns, const char *name)
320 {
321 assert(name != NULL);
322
323 if (ns == NULL ||
324 strcmp("default", ns) == 0)
325 {
326 return xstrdup(name);
327 }
328 else
329 {
330 return StringConcatenate(3, ns, ":", name);
331 }
332 }
333
ClassRefIsQualified(ClassRef ref)334 bool ClassRefIsQualified(ClassRef ref)
335 {
336 return ref.ns != NULL;
337 }
338
ClassRefQualify(ClassRef * ref,const char * ns)339 void ClassRefQualify(ClassRef *ref, const char *ns)
340 {
341 free(ref->ns);
342 ref->ns = xstrdup(ns);
343 }
344
ClassRefDestroy(ClassRef ref)345 void ClassRefDestroy(ClassRef ref)
346 {
347 free(ref.ns);
348 free(ref.name);
349 }
350