2 Copyright 2016, 2017 IoT.bzh
4 author: José Bollo <jose.bollo@iot.bzh>
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
23 #include <sys/types.h>
30 #include "utils-file.h"
32 #include "wgtpkg-mustach.h"
33 #include "wgtpkg-unit.h"
38 #define isblank(c) ((c)==' '||(c)=='\t')
41 /* the template for all units */
42 static char *template;
45 * Search for the 'pattern' in 'text'.
46 * Returns 1 if 'text' matches the 'pattern' or else returns 0.
47 * When returning 1 and 'after' isn't NULL, the pointer to the
48 * first character after the pettern in 'text' is stored in 'after'.
49 * The characters '\n' and ' ' have a special mening in the search:
50 * * '\n': matches any space or tabs (including none) followed
51 * either by '\n' or '\0' (end of the string)
52 * * ' ': matches any space or tabs but at least one.
54 static int matches(const char *pattern, char *text, char **after)
92 * Pack a null terminated 'text' by removing empty lines,
93 * lines made of blanks and terminated with \, lines
94 * starting with the 'purge' character (can be null).
95 * Lines made of the 'purge' character followed with
96 * "nl" exactly (without quotes ") are replaced with
99 * Returns the size after packing (offset of the ending null).
101 static size_t pack(char *text, char purge)
103 char *read; /* read iterator */
104 char *write; /* write iterator */
105 char *begin; /* begin the copied text of the line */
106 char *start; /* first character of the line that isn't blanck */
107 char c; /* currently scanned character (pointed by read) */
108 char emit; /* flag telling whether line is to be copied */
109 char cont; /* flag telling whether the line continues the previous one */
110 char nextcont; /* flag telling whether the line will continues the next one */
113 c = *(write = read = text);
115 /* iteration over lines */
117 /* computes emit, nextcont, emit and start for the current line */
121 while (c && c != '\n') {
123 if (c == '\\' && read[1] == '\n')
135 /* emit the line if not empty */
137 /* removes the blanks on the left of not continuing lines */
140 /* check if purge applies */
141 if (purge && *begin == purge) {
142 /* yes, insert new line if requested */
143 if (!strncmp(begin+1, "nl\n",3))
146 /* copies the line */
147 while (begin != read)
154 return (size_t)(write - text);
158 * Searchs the first character of the next line
159 * of the 'text' and returns its address
160 * Returns NULL if there is no next line.
162 static inline char *nextline(char *text)
164 char *result = strchr(text, '\n');
165 return result + !!result;
169 * Search in 'text' the offset of a line beginning with the 'pattern'
170 * Returns NULL if not found or the address of the line contning the pattern
171 * If args isn't NULL and the pattern is found, the pointed pattern is
172 * updated with the address of the character following the found pattern.
174 static char *offset(char *text, const char *pattern, char **args)
176 while (text && !matches(pattern, text, args))
177 text = nextline(text);
184 static int process_one_unit(char *spec, struct unitdesc *desc)
186 char *nsoc, *nsrv, *name;
187 int isuser, issystem, issock, isserv;
190 /* finds the configuration directive of the unit */
191 isuser = !!offset(spec, "%systemd-unit user\n", NULL);
192 issystem = !!offset(spec, "%systemd-unit system\n", NULL);
193 issock = !!offset(spec, "%systemd-unit socket ", &nsoc);
194 isserv = !!offset(spec, "%systemd-unit service ", &nsrv);
196 /* check the unit scope */
197 if ((isuser + issystem) == 1) {
198 desc->scope = isuser ? unitscope_user : unitscope_system;
200 desc->scope = unitscope_unknown;
203 /* check the unit type */
204 if ((issock + isserv) == 1) {
206 desc->type = unittype_socket;
209 desc->type = unittype_service;
212 len = (size_t)(strchrnul(name, '\n') - name);
213 desc->name = strndup(name, len);
214 desc->name_length = desc->name ? len : 0;
216 desc->type = unittype_unknown;
218 desc->name_length = 0;
221 desc->content = spec;
222 desc->content_length = pack(spec, '%');
228 * Processes all the units of the 'corpus'.
229 * Each unit of the corpus is separated and packed and its
230 * charactistics are stored in a descriptor.
231 * At the end if no error was found, calls the function 'process'
232 * with its given 'closure' and the array descripbing the units.
233 * Return 0 in case of success or a negative value in case of error.
235 static int process_all_units(char *corpus, int (*process)(void *closure, const struct unitdesc descs[], unsigned count), void *closure)
240 struct unitdesc *descs, *d;
246 /* while there is a unit in the corpus */
247 while(offset(corpus, "%begin systemd-unit\n", &beg)) {
249 /* get the end of the unit */
250 end = offset(beg, "%end systemd-unit\n", &corpus);
252 /* unterminated unit !! */
253 ERROR("unterminated unit description!! %s", beg);
257 /* separate the unit from the corpus */
260 /* allocates a descriptor for the unit */
261 d = realloc(descs, (n + 1) * sizeof *descs);
265 /* creates the unit description */
266 memset(&d[n], 0, sizeof *d);
268 rc2 = process_one_unit(beg, &descs[n]);
273 /* records the error if there is an error */
280 /* call the function that processes the units */
281 if (rc == 0 && process)
282 rc = process(closure, descs, n);
284 /* cleanup and frees */
286 free((char *)(descs[--n].name));
293 * Clear the unit generator
295 void unit_generator_off()
302 * Initialises the unit generator with 'filename'.
303 * Returns 0 in case of success or a negative number in case of error.
305 int unit_generator_on(const char *filename)
311 unit_generator_off();
312 rc = getfile(filename ? : FWK_UNIT_CONF, &template, NULL);
314 size = pack(template, ';');
315 tmp = realloc(template, 1 + size);
323 * Applies the object 'jdesc' to the current unit generator.
324 * The current unit generator will be set to the default one if not unit
325 * was previously set using the function 'unit_generator_on'.
326 * The callback function 'process' is then called with the
327 * unit descriptors array and the expected closure.
328 * Return what returned process in case of success or a negative
331 int unit_generator_process(struct json_object *jdesc, int (*process)(void *closure, const struct unitdesc descs[], unsigned count), void *closure)
337 rc = template ? 0 : unit_generator_on(NULL);
340 rc = apply_mustach(template, jdesc, &instance, &size);
342 rc = process_all_units(instance, process, closure);