Remove afm-system-daemon
[src/app-framework-main.git] / src / afm-udb.c
1 /*
2  Copyright 2015, 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 #include <stdlib.h>
20 #include <stdio.h>
21 #include <assert.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <dirent.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28
29 #include <json-c/json.h>
30
31 #include "utils-json.h"
32 #include "utils-systemd.h"
33 #include "utils-file.h"
34
35 #include "afm-udb.h"
36
37 static const char x_afm_prefix[] = "X-AFM-";
38 static const char service_extension[] = ".service";
39 static const char key_unit_path[] = "-unit-path";
40 static const char key_unit_name[] = "-unit-name";
41 static const char key_unit_scope[] = "-unit-scope";
42 static const char scope_user[] = "user";
43 static const char scope_system[] = "system";
44 static const char key_id[] = "id";
45
46 #define x_afm_prefix_length  (sizeof x_afm_prefix - 1)
47 #define service_extension_length  (sizeof service_extension - 1)
48
49 /*
50  * The structure afm_apps records the data about applications
51  * for several accesses.
52  */
53 struct afm_apps {
54         struct json_object *prvarr; /* array of the private data of apps */
55         struct json_object *pubarr; /* array of the public data of apps */
56         struct json_object *pubobj; /* hash of application's publics */
57         struct json_object *prvobj; /* hash of application's privates */
58 };
59
60 /*
61  * The structure afm_udb records the applications
62  * for a set of directories recorded as a linked list
63  */
64 struct afm_udb {
65         struct afm_apps applications;   /* the data about applications */
66         int refcount;                   /* count of references to the structure */
67         int system;                     /* is managing system units? */
68         int user;                       /* is managing user units? */
69         size_t prefixlen;               /* length of the prefix */
70         char prefix[1];                 /* filtering prefix */
71 };
72
73 /*
74  * The structure afm_updt is internally used for updates
75  */
76 struct afm_updt {
77         struct afm_udb *afudb;
78         struct afm_apps applications;
79 };
80
81 /*
82  * Release the data of the afm_apps object 'apps'.
83  */
84 static void apps_put(struct afm_apps *apps)
85 {
86         json_object_put(apps->prvarr);
87         json_object_put(apps->pubarr);
88         json_object_put(apps->pubobj);
89         json_object_put(apps->prvobj);
90 }
91
92 /*
93  * Append the field 'data' to the field 'name' of the 'object'.
94  * When a second append is done to one field, it is automatically
95  * transformed to an array.
96  * Return 0 in case of success or -1 in case of error.
97  */
98 static int append_field(
99                 struct json_object *object,
100                 const char *name,
101                 struct json_object *data
102 )
103 {
104         struct json_object *item, *array;
105
106         if (!json_object_object_get_ex(object, name, &item))
107                 json_object_object_add(object, name, data);
108         else {
109                 if (json_object_is_type(item, json_type_array))
110                         array = item;
111                 else {
112                         array = json_object_new_array();
113                         if (!array)
114                                 goto error;
115                         json_object_array_add(array, item);
116                         json_object_object_add(object, name, array);
117                 }
118                 json_object_array_add(array, data);
119         }
120         return 0;
121  error:
122         json_object_put(data);
123         errno = ENOMEM;
124         return -1;
125 }
126
127 /*
128  * Adds the field of 'name' and 'value' in 'priv' and also if possible in 'pub'
129  * Returns 0 on success or -1 on error.
130  */
131 static int add_field(
132                 struct json_object *priv,
133                 struct json_object *pub,
134                 const char *name,
135                 const char *value
136 )
137 {
138         long int ival;
139         char *end;
140         struct json_object *v;
141
142         /* try to adapt the value to its type */
143         errno = 0;
144         ival = strtol(value, &end, 10);
145         if (*value && !*end && !errno) {
146                 /* integer value */
147                 v = json_object_new_int64(ival);
148         } else {
149                 /* string value */
150                 v = json_object_new_string(value);
151         }
152         if (!v) {
153                 errno = ENOMEM;
154                 return -1;
155         }
156
157         /* add the value */
158         if (name[0] == '-') {
159                 append_field(priv, &name[1], v);
160         } else {
161                 append_field(priv, name, json_object_get(v));
162                 append_field(pub, name, v);
163         }
164         return 0;
165 }
166
167 /*
168  * Adds the field of 'name' and 'value' in 'priv' and also if possible in 'pub'
169  * Returns 0 on success or -1 on error.
170  */
171 static int add_fields_of_content(
172                 struct json_object *priv,
173                 struct json_object *pub,
174                 char *content,
175                 size_t length
176 )
177 {
178         char *name, *value, *read, *write;
179
180         read = strstr(content, x_afm_prefix);
181         while (read) {
182                 name = read + x_afm_prefix_length;
183                 value = strchr(name, '=');
184                 if (value == NULL)
185                         read = strstr(name, x_afm_prefix);
186                 else {
187                         *value++ = 0;
188                         read = write = value;
189                         while(*read && *read != '\n') {
190                                 if (read[0] != '\\')
191                                         *write++ = *read++;
192                                 else {
193                                         switch(*++read) {
194                                         case 'n': *write++ = '\n'; break;
195                                         case '\n': *write++ = ' '; break;
196                                         default: *write++ = '\\'; *write++ = *read; break;
197                                         }
198                                         read += !!*read;
199                                 }
200                         }
201                         read = strstr(read, x_afm_prefix);
202                         *write = 0;
203                         if (add_field(priv, pub, name, value) < 0)
204                                 return -1;
205                 }
206         }
207         return 0;
208 }
209
210 /*
211  * Adds the application widget 'desc' of the directory 'path' to the
212  * afm_apps object 'apps'.
213  * Returns 0 in case of success.
214  * Returns -1 and set errno in case of error
215  */
216 static int addunit(
217                 struct afm_apps *apps,
218                 int isuser,
219                 const char *unitpath,
220                 const char *unitname,
221                 char *content,
222                 size_t length
223 )
224 {
225         struct json_object *priv, *pub, *id;
226         const char *strid;
227         size_t len;
228
229         /* create the application structure */
230         priv = json_object_new_object();
231         if (!priv)
232                 return -1;
233
234         pub = json_object_new_object();
235         if (!pub)
236                 goto error;
237
238         /* make the unit name */
239         len = strlen(unitname);
240         assert(len >= (sizeof service_extension - 1));
241         assert(!memcmp(&unitname[len - (sizeof service_extension - 1)], service_extension, sizeof service_extension));
242
243         /* adds the values */
244         if (add_fields_of_content(priv, pub, content, length)
245          || add_field(priv, pub, key_unit_path, unitpath)
246          || add_field(priv, pub, key_unit_name, unitname)
247          || add_field(priv, pub, key_unit_scope, isuser ? scope_user : scope_system))
248                 goto error;
249
250         /* get the id */
251         if (!json_object_object_get_ex(pub, key_id, &id)) {
252                 errno = EINVAL;
253                 goto error;
254         }
255         strid = json_object_get_string(id);
256
257         /* record the application structure */
258         json_object_get(pub);
259         json_object_array_add(apps->pubarr, pub);
260         json_object_object_add(apps->pubobj, strid, pub);
261         json_object_get(priv);
262         json_object_array_add(apps->prvarr, priv);
263         json_object_object_add(apps->prvobj, strid, priv);
264         return 0;
265
266 error:
267         json_object_put(pub);
268         json_object_put(priv);
269         return -1;
270 }
271
272 /*
273  * read a unit file
274  */
275 static int read_unit_file(const char *path, char **content, size_t *length)
276 {
277         int rc, st;
278         char c, *read, *write;
279
280         /* read the file */
281         rc = getfile(path, content, length);
282         if (rc >= 0) {
283                 /* removes any comment and join lines */
284                 st = 0;
285                 read = write = *content;
286                 for (;;) {
287                         do { c = *read++; } while (c == '\r');
288                         if (!c)
289                                 break;
290                         switch (st) {
291                         case 0:
292                                 if (c == ';' || c == '#') {
293                                         st = 3; /* removes lines starting with ; or # */
294                                         break;
295                                 }
296                                 if (c == '\n')
297                                         break; /* removes empty lines */
298 enter_state_1:
299                                 st = 1;
300                                 /*@fallthrough@*/
301                         case 1:
302                                 if (c == '\\')
303                                         st = 2;
304                                 else {
305                                         *write++ = c;
306                                         if (c == '\n')
307                                                 st = 0;
308                                 }
309                                 break;
310                         case 2:
311                                 if (c == '\n')
312                                         c = ' ';
313                                 else
314                                         *write++ = '\\';
315                                 goto enter_state_1;
316                         case 3:
317                                 if (c == '\n')
318                                         st = 0;
319                                 break;
320                         }
321                 }
322                 if (st == 1)
323                         *write++ = '\n';
324                 *write = 0;
325                 *length = (size_t)(write - *content);
326                 *content = realloc(*content, *length + 1);
327         }
328         return rc;
329 }
330
331 /*
332  * called for each unit
333  */
334 static int update_cb(void *closure, const char *name, const char *path, int isuser)
335 {
336         struct afm_updt *updt = closure;
337         char *content;
338         size_t length;
339         int rc;
340
341         /* prefix filtering */
342         length = updt->afudb->prefixlen;
343         if (length && strncmp(updt->afudb->prefix, name, length))
344                 return 0;
345
346         /* only services */
347         length = strlen(name);
348         if (length < service_extension_length || strcmp(service_extension, name + length - service_extension_length))
349                 return 0;
350
351         /* reads the file */
352         rc = read_unit_file(path, &content, &length);
353         if (rc < 0)
354                 return 0;
355
356         /* process the file */
357         rc = addunit(&updt->applications, isuser, path, name, content, length);
358         /* TODO: if (rc < 0)
359                 ERROR("Ignored boggus unit %s (error: %m)", path); */
360         free(content);
361         return 0;
362 }
363
364 /*
365  * Creates an afm_udb object and returns it with one reference added.
366  * Return NULL with errno = ENOMEM if memory exhausted.
367  */
368 struct afm_udb *afm_udb_create(int sys, int usr, const char *prefix)
369 {
370         size_t length;
371         struct afm_udb *afudb;
372
373         length = prefix ? strlen(prefix) : 0;
374         afudb = malloc(length + sizeof * afudb);
375         if (afudb == NULL)
376                 errno = ENOMEM;
377         else {
378                 afudb->refcount = 1;
379                 afudb->applications.prvarr = NULL;
380                 afudb->applications.pubarr = NULL;
381                 afudb->applications.pubobj = NULL;
382                 afudb->applications.prvobj = NULL;
383                 afudb->system = sys;
384                 afudb->user = usr;
385                 afudb->prefixlen = length;
386                 if (length)
387                         memcpy(afudb->prefix, prefix, length);
388                 afudb->prefix[length] = 0;
389                 if (afm_udb_update(afudb) < 0) {
390                         afm_udb_unref(afudb);
391                         afudb = NULL;
392                 }
393         }
394         return afudb;
395 }
396
397 /*
398  * Adds a reference to an existing afm_udb.
399  */
400 void afm_udb_addref(struct afm_udb *afudb)
401 {
402         assert(afudb);
403         afudb->refcount++;
404 }
405
406 /*
407  * Removes a reference to an existing afm_udb object.
408  * Removes the objet if there no more reference to it.
409  */
410 void afm_udb_unref(struct afm_udb *afudb)
411 {
412         assert(afudb);
413         if (!--afudb->refcount) {
414                 /* no more reference, clean the memory used by the object */
415                 apps_put(&afudb->applications);
416                 free(afudb);
417         }
418 }
419
420 /*
421  * Regenerate the list of applications of the afm_bd object 'afudb'.
422  * Returns 0 in case of success.
423  * Returns -1 and set errno in case of error
424  */
425 int afm_udb_update(struct afm_udb *afudb)
426 {
427         struct afm_updt updt;
428         struct afm_apps tmp;
429
430         /* lock the db */
431         afm_udb_addref(afudb);
432         updt.afudb = afudb;
433
434         /* create the result */
435         updt.applications.prvarr = json_object_new_array();
436         updt.applications.pubarr = json_object_new_array();
437         updt.applications.pubobj = json_object_new_object();
438         updt.applications.prvobj = json_object_new_object();
439         if (updt.applications.pubarr == NULL
440          || updt.applications.prvarr == NULL
441          || updt.applications.pubobj == NULL
442          || updt.applications.prvobj == NULL) {
443                 errno = ENOMEM;
444                 goto error;
445         }
446
447         /* scan the units */
448         if (afudb->user)
449                 if (systemd_unit_list(1, update_cb, &updt) < 0)
450                         goto error;
451         if (afudb->system)
452                 if (systemd_unit_list(0, update_cb, &updt) < 0)
453                         goto error;
454
455         /* commit the result */
456         tmp = afudb->applications;
457         afudb->applications = updt.applications;
458         apps_put(&tmp);
459         afm_udb_addref(afudb);
460         return 0;
461
462 error:
463         apps_put(&updt.applications);
464         afm_udb_addref(afudb);
465         return -1;
466 }
467
468 /*
469  * Get the list of the applications private data of the afm_udb object 'afudb'.
470  * The list is returned as a JSON-array that must be released using
471  * 'json_object_put'.
472  * Returns NULL in case of error.
473  */
474 struct json_object *afm_udb_applications_private(struct afm_udb *afudb, int uid)
475 {
476         return json_object_get(afudb->applications.prvarr);
477 }
478
479 /*
480  * Get the list of the applications public data of the afm_udb object 'afudb'.
481  * The list is returned as a JSON-array that must be released using
482  * 'json_object_put'.
483  * Returns NULL in case of error.
484  */
485 struct json_object *afm_udb_applications_public(struct afm_udb *afudb, int uid)
486 {
487         return json_object_get(afudb->applications.pubarr);
488 }
489
490 /*
491  * Get the private data of the applications of 'id' in the afm_udb object 'afudb'.
492  * It returns a JSON-object that must be released using 'json_object_put'.
493  * Returns NULL in case of error.
494  */
495 static struct json_object *get_no_case(struct json_object *object, const char *id, int uid)
496 {
497         struct json_object *result;
498         struct json_object_iter i;
499
500         /* search case sensitively */
501         if (json_object_object_get_ex(object, id, &result))
502                 return json_object_get(result);
503
504         /* fallback to a case insensitive search */
505         json_object_object_foreachC(object, i) {
506                 if (!strcasecmp(i.key, id))
507                         return json_object_get(i.val);
508         }
509         return NULL;
510 }
511
512 /*
513  * Get the private data of the applications of 'id' in the afm_udb object 'afudb'.
514  * It returns a JSON-object that must be released using 'json_object_put'.
515  * Returns NULL in case of error.
516  */
517 struct json_object *afm_udb_get_application_private(struct afm_udb *afudb, const char *id, int uid)
518 {
519         return get_no_case(afudb->applications.prvobj, id, uid);
520 }
521
522 /*
523  * Get the public data of the applications of 'id' in the afm_udb object 'afudb'.
524  * It returns a JSON-object that must be released using 'json_object_put'.
525  * Returns NULL in case of error.
526  */
527 struct json_object *afm_udb_get_application_public(struct afm_udb *afudb,
528                                                         const char *id, int uid)
529 {
530         return get_no_case(afudb->applications.pubobj, id, uid);
531 }
532
533
534
535 #if defined(TESTAPPFWK)
536 #include <stdio.h>
537 int main()
538 {
539 struct afm_udb *afudb = afm_udb_create(1, 1, NULL);
540 printf("array = %s\n", json_object_to_json_string_ext(afudb->applications.pubarr, 3));
541 printf("pubobj = %s\n", json_object_to_json_string_ext(afudb->applications.pubobj, 3));
542 printf("prvobj = %s\n", json_object_to_json_string_ext(afudb->applications.prvobj, 3));
543 return 0;
544 }
545 #endif
546