Prepare the Integration with systemd
[src/app-framework-main.git] / src / wgtpkg-unit.c
1 /*
2  Copyright 2016, 2017 IoT.bzh
3
4  author: José Bollo <jose.bollo@iot.bzh>
5
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
9
10      http://www.apache.org/licenses/LICENSE-2.0
11
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.
17 */
18
19 #define _GNU_SOURCE
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <errno.h>
28
29 #include "verbose.h"
30 #include "utils-file.h"
31
32 #include "wgtpkg-mustach.h"
33 #include "wgtpkg-unit.h"
34
35 static char *template;
36
37 /*
38  * Pack a null terminated 'text' by removing empty lines,
39  * lines made of blanks and terminated with \, lines
40  * starting with the 'purge' character (can be null).
41  * Lines made of the 'purge' character followed with
42  * "nl" exactly (without quotes ") are replaced with
43  * an empty line.
44  *
45  * Returns the pointer to the ending null.
46  */
47 static char *pack(char *text, char purge)
48 {
49         char *read, *write, *begin, *start, c, emit, cont, nextcont;
50
51         cont = 0;
52         c = *(begin = write = read = text);
53         while (c) {
54                 emit = nextcont = 0;
55                 start = NULL;
56                 begin = read;
57                 while (c && c != '\n') {
58                         if (c != ' ' && c != '\t') {
59                                 if (c == '\\' && read[1] == '\n')
60                                         nextcont = 1;
61                                 else {
62                                         emit = 1;
63                                         if (!start)
64                                                 start = read;
65                                 }
66                         }
67                         c = *++read;
68                 }
69                 if (c)
70                         c = *++read;
71                 if (emit) {
72                         if (!cont && start)
73                                 begin = start;
74                         if (purge && *begin == purge) {
75                                 if (!strncmp(begin+1, "nl\n",3))
76                                         *write++ = '\n';
77                         } else {
78                                 while (begin != read)
79                                         *write++ = *begin++;
80                         }
81                 }
82                 cont = nextcont;
83         }
84         *write = 0;
85         return write;
86 }
87
88 /*
89  * Searchs the first character of the next line
90  * of the 'text' and returns its address
91  * Returns NULL if there is no next line.
92  */
93 static inline char *nextline(char *text)
94 {
95         char *result = strchr(text, '\n');
96         return result + !!result;
97 }
98
99 /*
100  * Search in 'text' the offset of a line beginning with the 'pattern'
101  * Returns NULL if not found or the address of the line contning the pattern
102  * If args isn't NULL and the pattern is found, the pointed pattern is
103  * updated with the address of the character following the found pattern.
104  */
105 static char *offset(char *text, const char *pattern, char **args)
106 {
107         size_t len;
108
109         if (text) {
110                 len = strlen(pattern);
111                 do {
112                         if (strncmp(text, pattern, len))
113                                 text = nextline(text);
114                         else {
115                                 if (args)
116                                         *args = &text[len];
117                                 break;
118                         }
119                 } while (text);
120         }
121         return text;
122 }
123
124 /*
125  * process one unit
126  */
127
128 static int process_one_unit(char *spec, struct unitdesc *desc)
129 {
130         char *nsoc, *nsrv;
131         int isuser, issystem, issock, isserv;
132
133         /* found the configuration directive of the unit */
134         isuser = !!offset(spec, "%systemd-unit user\n", NULL);
135         issystem = !!offset(spec, "%systemd-unit system\n", NULL);
136         issock  = !!offset(spec, "%systemd-unit socket ", &nsoc);
137         isserv  = !!offset(spec, "%systemd-unit service ", &nsrv);
138
139         if (isuser ^ issystem) {
140                 desc->scope = isuser ? unitscope_user : unitscope_system;
141         } else {
142                 desc->scope = unitscope_unknown;
143         }
144
145         if (issock ^ isserv) {
146                 if (issock) {
147                         desc->type = unittype_socket;
148                         desc->name = nsoc;
149                 } else {
150                         desc->type = unittype_service;
151                         desc->name = nsrv;
152                 }
153                 desc->name_length = (size_t)(strchrnul(desc->name, '\n') - desc->name);
154                 desc->name = strndup(desc->name, desc->name_length);
155         } else {
156                 desc->type = unittype_unknown;
157                 desc->name = NULL;
158                 desc->name_length = 0;
159         }
160
161         desc->content = spec;
162         desc->content_length = (size_t)(pack(spec, '%') - spec);
163
164         return 0;
165 }
166
167 /*
168  */
169 static int process_all_units(char *spec, int (*process)(void *closure, const struct unitdesc descs[], unsigned count), void *closure)
170 {
171         int rc, rc2;
172         unsigned n;
173         char *beg, *end, *after;
174         struct unitdesc *descs, *d;
175
176         descs = NULL;
177         n = 0;
178         rc = 0;
179         beg = offset(spec, "%begin systemd-unit\n", NULL);
180         while(beg) {
181                 beg = nextline(beg);
182                 end = offset(beg, "%end systemd-unit\n", &after);
183                 if (!end) {
184                         /* unterminated unit !! */
185                         ERROR("unterminated unit description!! %s", beg);
186                         break;
187                 }
188                 *end = 0;
189
190                 d = realloc(descs, (n + 1) * sizeof *descs);
191                 if (d == NULL)
192                         rc2 = -ENOMEM;
193                 else {
194                         memset(&d[n], 0, sizeof *d);
195                         descs = d;
196                         rc2 = process_one_unit(beg, &descs[n]);
197                 }
198
199                 if (rc2 >= 0)
200                         n++;
201                 else if (rc == 0)
202                         rc = rc2;
203                 beg = offset(after, "%begin systemd-unit\n", NULL);
204         }
205
206         if (rc == 0 && process)
207                 rc = process(closure, descs, n);
208         while(n)
209                 free((char *)(descs[--n].name));
210         free(descs);
211
212         return rc;
213 }
214
215 int unit_generator_on(const char *filename)
216 {
217         size_t size;
218         char *tmp;
219         int rc;
220
221         rc = getfile(filename ? : FWK_UNIT_CONF, &template, NULL);
222         if (!rc) {
223                 size = (size_t)(pack(template, ';') - template);
224                 tmp = realloc(template, 1 + size);
225                 if (tmp)
226                         template = tmp;
227         }
228         return rc;
229 }
230
231 void unit_generator_off()
232 {
233         free(template);
234         template = NULL;
235 }
236
237 int unit_generator_process(struct json_object *jdesc, int (*process)(void *closure, const struct unitdesc descs[], unsigned count), void *closure)
238 {
239         int rc;
240         size_t size;
241         char *instance;
242
243         rc = template ? 0 : unit_generator_on(NULL);
244         if (!rc) {
245                 instance = NULL;
246                 rc = apply_mustach(template, jdesc, &instance, &size);
247                 if (!rc) {
248                         rc = process_all_units(instance, process, closure);
249                 }
250                 free(instance);
251         }
252         return rc;
253 }
254