2 Copyright (C) 2015-2018 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.
27 #include <sys/types.h>
29 #include <json-c/json.h>
31 #include "utils-json.h"
32 #include "utils-systemd.h"
33 #include "utils-file.h"
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 static const char key_visibility[] = "visibility";
46 static const char value_visible[] = "visible";
48 #define x_afm_prefix_length (sizeof x_afm_prefix - 1)
49 #define service_extension_length (sizeof service_extension - 1)
52 * The structure afm_apps records the data about applications
53 * for several accesses.
57 struct json_object *visibles; /* array of the private data of visible apps */
58 struct json_object *all; /* array of the private data of all apps */
59 struct json_object *byname; /* hash of application's privates */
64 * The structure afm_udb records the applications
65 * for a set of directories recorded as a linked list
68 struct afm_apps applications; /* the data about applications */
69 int refcount; /* count of references to the structure */
70 int system; /* is managing system units? */
71 int user; /* is managing user units? */
72 size_t prefixlen; /* length of the prefix */
73 char prefix[1]; /* filtering prefix */
77 * The structure afm_updt is internally used for updates
80 struct afm_udb *afudb;
81 struct afm_apps applications;
85 * The default language
87 static char *default_lang;
90 * initilize object 'apps'.
91 * returns 1 if okay or 0 on case of memory depletion
93 static int apps_init(struct afm_apps *apps)
95 apps->publics.all = json_object_new_array();
96 apps->publics.visibles = json_object_new_array();
97 apps->publics.byname = json_object_new_object();
99 apps->privates.all = json_object_new_array();
100 apps->privates.visibles = json_object_new_array();
101 apps->privates.byname = json_object_new_object();
103 return apps->publics.all
104 && apps->publics.visibles
105 && apps->publics.byname
106 && apps->privates.all
107 && apps->privates.visibles
108 && apps->privates.byname;
112 * Release the data of the afm_apps object 'apps'.
114 static void apps_put(struct afm_apps *apps)
116 json_object_put(apps->publics.all);
117 json_object_put(apps->publics.visibles);
118 json_object_put(apps->publics.byname);
119 json_object_put(apps->privates.all);
120 json_object_put(apps->privates.visibles);
121 json_object_put(apps->privates.byname);
125 * Append the field 'data' to the field 'name' of the 'object'.
126 * When a second append is done to one field, it is automatically
127 * transformed to an array.
128 * Return 0 in case of success or -1 in case of error.
130 static int append_field(
131 struct json_object *object,
133 struct json_object *data
136 struct json_object *item, *array;
138 if (!json_object_object_get_ex(object, name, &item))
139 json_object_object_add(object, name, data);
141 if (json_object_is_type(item, json_type_array))
144 array = json_object_new_array();
147 json_object_array_add(array, json_object_get(item));
148 json_object_object_add(object, name, array);
150 json_object_array_add(array, data);
154 json_object_put(data);
160 * Adds the field of 'name' and 'value' in 'priv' and also if possible in 'pub'
161 * Returns 0 on success or -1 on error.
163 static int add_field(
164 struct json_object *priv,
165 struct json_object *pub,
172 struct json_object *v;
174 /* try to adapt the value to its type */
176 ival = strtol(value, &end, 10);
177 if (*value && !*end && !errno) {
179 v = json_object_new_int64(ival);
182 v = json_object_new_string(value);
190 if (name[0] == '-') {
192 append_field(priv, &name[1], v);
195 append_field(priv, name, json_object_get(v));
196 append_field(pub, name, v);
202 * Adds the field of 'name' and 'value' in 'priv' and also if possible in 'pub'
203 * Returns 0 on success or -1 on error.
205 static int add_fields_of_content(
206 struct json_object *priv,
207 struct json_object *pub,
212 char *name, *value, *read, *write;
214 /* start at the beginning */
217 /* search the next key */
218 read = strstr(read, x_afm_prefix);
222 /* search to equal */
223 name = read + x_afm_prefix_length;
224 value = strchr(name, '=');
226 read = name; /* not found */
228 /* get the value (translate it) */
230 read = write = value;
231 while(*read && *read != '\n') {
236 case 'n': *write++ = '\n'; break;
237 case '\n': *write++ = ' '; break;
238 default: *write++ = '\\'; *write++ = *read; break;
246 /* add the found field now */
247 if (add_field(priv, pub, name, value) < 0)
254 * Adds the application widget 'desc' of the directory 'path' to the
255 * afm_apps object 'apps'.
256 * Returns 0 in case of success.
257 * Returns -1 and set errno in case of error
260 struct afm_apps *apps,
262 const char *unitpath,
263 const char *unitname,
268 struct json_object *priv, *pub, *id, *visi;
272 /* create the application structure */
273 priv = json_object_new_object();
277 pub = json_object_new_object();
281 /* make the unit name */
282 len = strlen(unitname);
283 assert(len >= (sizeof service_extension - 1));
284 assert(!memcmp(&unitname[len - (sizeof service_extension - 1)], service_extension, sizeof service_extension));
286 /* adds the values */
287 if (add_fields_of_content(priv, pub, content, length)
288 || add_field(priv, pub, key_unit_path, unitpath)
289 || add_field(priv, pub, key_unit_name, unitname)
290 || add_field(priv, pub, key_unit_scope, isuser ? scope_user : scope_system))
294 if (!json_object_object_get_ex(pub, key_id, &id)) {
298 strid = json_object_get_string(id);
300 /* record the application structure */
301 json_object_get(pub);
302 json_object_array_add(apps->publics.all, pub);
303 json_object_object_add(apps->publics.byname, strid, pub);
304 json_object_get(priv);
305 json_object_array_add(apps->privates.all, priv);
306 json_object_object_add(apps->privates.byname, strid, priv);
308 /* handle visibility */
309 if (json_object_object_get_ex(priv, key_visibility, &visi)
310 && !strcasecmp(json_object_get_string(visi), value_visible)) {
311 json_object_array_add(apps->publics.visibles, json_object_get(pub));
312 json_object_array_add(apps->privates.visibles, json_object_get(priv));
318 json_object_put(pub);
319 json_object_put(priv);
324 * Crop and trim unit 'content' of 'length'. Return the new length.
326 static size_t crop_and_trim_unit_content(char *content, size_t length)
329 char c, *read, *write;
331 /* removes any comment and join continued lines */
333 read = write = content;
335 do { c = *read++; } while (c == '\r');
340 /* state 0: begin of a line */
341 if (c == ';' || c == '#') {
342 st = 3; /* removes lines starting with ; or # */
346 break; /* removes empty lines */
351 /* state 1: emitting a normal line */
361 /* state 2: character after '\' */
368 /* state 3: inside a comment, wait its end */
377 return (size_t)(write - content);
383 static int read_unit_file(const char *path, char **content, size_t *length)
389 rc = getfile(path, content, length);
391 /* crop and trim it */
392 *length = nl = crop_and_trim_unit_content(*content, *length);
393 *content = realloc(*content, nl + 1);
399 * called for each unit
401 static int update_cb(void *closure, const char *name, const char *path, int isuser)
403 struct afm_updt *updt = closure;
408 /* prefix filtering */
409 length = updt->afudb->prefixlen;
410 if (length && strncmp(updt->afudb->prefix, name, length))
414 length = strlen(name);
415 if (length < service_extension_length || strcmp(service_extension, name + length - service_extension_length))
419 rc = read_unit_file(path, &content, &length);
423 /* process the file */
424 rc = addunit(&updt->applications, isuser, path, name, content, length);
426 ERROR("Ignored boggus unit %s (error: %m)", path); */
432 * Creates an afm_udb object and returns it with one reference added.
433 * Return NULL with errno = ENOMEM if memory exhausted.
435 struct afm_udb *afm_udb_create(int sys, int usr, const char *prefix)
438 struct afm_udb *afudb;
440 length = prefix ? strlen(prefix) : 0;
441 afudb = malloc(length + sizeof * afudb);
446 memset(&afudb->applications, 0, sizeof afudb->applications);
449 afudb->prefixlen = length;
451 memcpy(afudb->prefix, prefix, length);
452 afudb->prefix[length] = 0;
453 if (afm_udb_update(afudb) < 0) {
454 afm_udb_unref(afudb);
462 * Adds a reference to an existing afm_udb.
464 void afm_udb_addref(struct afm_udb *afudb)
471 * Removes a reference to an existing afm_udb object.
472 * Removes the objet if there no more reference to it.
474 void afm_udb_unref(struct afm_udb *afudb)
477 if (!--afudb->refcount) {
478 /* no more reference, clean the memory used by the object */
479 apps_put(&afudb->applications);
485 * Regenerate the list of applications of the afm_bd object 'afudb'.
486 * Returns 0 in case of success.
487 * Returns -1 and set errno in case of error
489 int afm_udb_update(struct afm_udb *afudb)
491 struct afm_updt updt;
496 afm_udb_addref(afudb);
499 /* create the apps */
500 if (!apps_init(&updt.applications))
504 if (afudb->user && systemd_unit_list(1, update_cb, &updt) < 0)
506 else if (afudb->system && systemd_unit_list(0, update_cb, &updt) < 0)
509 /* commit the result */
510 tmp = afudb->applications;
511 afudb->applications = updt.applications;
512 updt.applications = tmp;
515 apps_put(&updt.applications);
517 /* unlock the db and return status */
518 afm_udb_unref(afudb);
523 * set the default language to 'lang'
525 void afm_udb_set_default_lang(const char *lang)
527 char *oldval = default_lang;
528 default_lang = lang ? strdup(lang) : NULL;
533 * Get the list of the applications private data of the afm_udb object 'afudb'.
534 * The list is returned as a JSON-array that must be released using
536 * Returns NULL in case of error.
538 struct json_object *afm_udb_applications_private(struct afm_udb *afudb, int all, int uid)
540 return json_object_get(all ? afudb->applications.privates.all : afudb->applications.privates.visibles);
544 * Get the list of the applications public data of the afm_udb object 'afudb'.
545 * The list is returned as a JSON-array that must be released using
547 * Returns NULL in case of error.
549 struct json_object *afm_udb_applications_public(struct afm_udb *afudb, int all, int uid, const char *lang)
551 return json_object_get(all ? afudb->applications.publics.all : afudb->applications.publics.visibles);
555 * Get the private data of the applications of 'id' in the afm_udb object 'afudb'.
556 * It returns a JSON-object that must be released using 'json_object_put'.
557 * Returns NULL in case of error.
559 static struct json_object *get_no_case(struct json_object *object, const char *id, int uid, const char *lang)
561 struct json_object *result;
562 struct json_object_iter i;
564 /* search case sensitively */
565 if (json_object_object_get_ex(object, id, &result))
566 return json_object_get(result);
568 /* fallback to a case insensitive search */
569 json_object_object_foreachC(object, i) {
570 if (!strcasecmp(i.key, id))
571 return json_object_get(i.val);
577 * Get the private data of the applications of 'id' in the afm_udb object 'afudb'.
578 * It returns a JSON-object that must be released using 'json_object_put'.
579 * Returns NULL in case of error.
581 struct json_object *afm_udb_get_application_private(struct afm_udb *afudb, const char *id, int uid)
583 return get_no_case(afudb->applications.privates.byname, id, uid, NULL);
587 * Get the public data of the applications of 'id' in the afm_udb object 'afudb'.
588 * It returns a JSON-object that must be released using 'json_object_put'.
589 * Returns NULL in case of error.
591 struct json_object *afm_udb_get_application_public(struct afm_udb *afudb,
592 const char *id, int uid, const char *lang)
594 return get_no_case(afudb->applications.publics.byname, id, uid, lang);
599 #if defined(TESTAPPFWK)
603 struct afm_udb *afudb = afm_udb_create(1, 1, NULL);
604 printf("publics.all = %s\n", json_object_to_json_string_ext(afudb->applications.publics.all, 3));
605 printf("publics.byname = %s\n", json_object_to_json_string_ext(afudb->applications.publics.byname, 3));
606 printf("privates.byname = %s\n", json_object_to_json_string_ext(afudb->applications.privates.byname, 3));