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";
46 #define x_afm_prefix_length (sizeof x_afm_prefix - 1)
47 #define service_extension_length (sizeof service_extension - 1)
50 * The structure afm_apps records the data about applications
51 * for several accesses.
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 */
61 * The structure afm_udb records the applications
62 * for a set of directories recorded as a linked list
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 */
74 * The structure afm_updt is internally used for updates
77 struct afm_udb *afudb;
78 struct afm_apps applications;
82 * The default language
84 static char *default_lang;
87 * Release the data of the afm_apps object 'apps'.
89 static void apps_put(struct afm_apps *apps)
91 json_object_put(apps->prvarr);
92 json_object_put(apps->pubarr);
93 json_object_put(apps->pubobj);
94 json_object_put(apps->prvobj);
98 * Append the field 'data' to the field 'name' of the 'object'.
99 * When a second append is done to one field, it is automatically
100 * transformed to an array.
101 * Return 0 in case of success or -1 in case of error.
103 static int append_field(
104 struct json_object *object,
106 struct json_object *data
109 struct json_object *item, *array;
111 if (!json_object_object_get_ex(object, name, &item))
112 json_object_object_add(object, name, data);
114 if (json_object_is_type(item, json_type_array))
117 array = json_object_new_array();
120 json_object_array_add(array, json_object_get(item));
121 json_object_object_add(object, name, array);
123 json_object_array_add(array, data);
127 json_object_put(data);
133 * Adds the field of 'name' and 'value' in 'priv' and also if possible in 'pub'
134 * Returns 0 on success or -1 on error.
136 static int add_field(
137 struct json_object *priv,
138 struct json_object *pub,
145 struct json_object *v;
147 /* try to adapt the value to its type */
149 ival = strtol(value, &end, 10);
150 if (*value && !*end && !errno) {
152 v = json_object_new_int64(ival);
155 v = json_object_new_string(value);
163 if (name[0] == '-') {
164 append_field(priv, &name[1], v);
166 append_field(priv, name, json_object_get(v));
167 append_field(pub, name, v);
173 * Adds the field of 'name' and 'value' in 'priv' and also if possible in 'pub'
174 * Returns 0 on success or -1 on error.
176 static int add_fields_of_content(
177 struct json_object *priv,
178 struct json_object *pub,
183 char *name, *value, *read, *write;
185 /* start at the beginning */
188 /* search the next key */
189 read = strstr(read, x_afm_prefix);
193 /* search to equal */
194 name = read + x_afm_prefix_length;
195 value = strchr(name, '=');
197 read = name; /* not found */
199 /* get the value (translate it) */
201 read = write = value;
202 while(*read && *read != '\n') {
207 case 'n': *write++ = '\n'; break;
208 case '\n': *write++ = ' '; break;
209 default: *write++ = '\\'; *write++ = *read; break;
217 /* add the found field now */
218 if (add_field(priv, pub, name, value) < 0)
225 * Adds the application widget 'desc' of the directory 'path' to the
226 * afm_apps object 'apps'.
227 * Returns 0 in case of success.
228 * Returns -1 and set errno in case of error
231 struct afm_apps *apps,
233 const char *unitpath,
234 const char *unitname,
239 struct json_object *priv, *pub, *id;
243 /* create the application structure */
244 priv = json_object_new_object();
248 pub = json_object_new_object();
252 /* make the unit name */
253 len = strlen(unitname);
254 assert(len >= (sizeof service_extension - 1));
255 assert(!memcmp(&unitname[len - (sizeof service_extension - 1)], service_extension, sizeof service_extension));
257 /* adds the values */
258 if (add_fields_of_content(priv, pub, content, length)
259 || add_field(priv, pub, key_unit_path, unitpath)
260 || add_field(priv, pub, key_unit_name, unitname)
261 || add_field(priv, pub, key_unit_scope, isuser ? scope_user : scope_system))
265 if (!json_object_object_get_ex(pub, key_id, &id)) {
269 strid = json_object_get_string(id);
271 /* record the application structure */
272 json_object_get(pub);
273 json_object_array_add(apps->pubarr, pub);
274 json_object_object_add(apps->pubobj, strid, pub);
275 json_object_get(priv);
276 json_object_array_add(apps->prvarr, priv);
277 json_object_object_add(apps->prvobj, strid, priv);
281 json_object_put(pub);
282 json_object_put(priv);
287 * Crop and trim unit 'content' of 'length'. Return the new length.
289 static size_t crop_and_trim_unit_content(char *content, size_t length)
292 char c, *read, *write;
294 /* removes any comment and join continued lines */
296 read = write = content;
298 do { c = *read++; } while (c == '\r');
303 /* state 0: begin of a line */
304 if (c == ';' || c == '#') {
305 st = 3; /* removes lines starting with ; or # */
309 break; /* removes empty lines */
314 /* state 1: emitting a normal line */
324 /* state 2: character after '\' */
331 /* state 3: inside a comment, wait its end */
340 return (size_t)(write - content);
346 static int read_unit_file(const char *path, char **content, size_t *length)
352 rc = getfile(path, content, length);
354 /* crop and trim it */
355 *length = nl = crop_and_trim_unit_content(*content, *length);
356 *content = realloc(*content, nl + 1);
362 * called for each unit
364 static int update_cb(void *closure, const char *name, const char *path, int isuser)
366 struct afm_updt *updt = closure;
371 /* prefix filtering */
372 length = updt->afudb->prefixlen;
373 if (length && strncmp(updt->afudb->prefix, name, length))
377 length = strlen(name);
378 if (length < service_extension_length || strcmp(service_extension, name + length - service_extension_length))
382 rc = read_unit_file(path, &content, &length);
386 /* process the file */
387 rc = addunit(&updt->applications, isuser, path, name, content, length);
389 ERROR("Ignored boggus unit %s (error: %m)", path); */
395 * Creates an afm_udb object and returns it with one reference added.
396 * Return NULL with errno = ENOMEM if memory exhausted.
398 struct afm_udb *afm_udb_create(int sys, int usr, const char *prefix)
401 struct afm_udb *afudb;
403 length = prefix ? strlen(prefix) : 0;
404 afudb = malloc(length + sizeof * afudb);
409 afudb->applications.prvarr = NULL;
410 afudb->applications.pubarr = NULL;
411 afudb->applications.pubobj = NULL;
412 afudb->applications.prvobj = NULL;
415 afudb->prefixlen = length;
417 memcpy(afudb->prefix, prefix, length);
418 afudb->prefix[length] = 0;
419 if (afm_udb_update(afudb) < 0) {
420 afm_udb_unref(afudb);
428 * Adds a reference to an existing afm_udb.
430 void afm_udb_addref(struct afm_udb *afudb)
437 * Removes a reference to an existing afm_udb object.
438 * Removes the objet if there no more reference to it.
440 void afm_udb_unref(struct afm_udb *afudb)
443 if (!--afudb->refcount) {
444 /* no more reference, clean the memory used by the object */
445 apps_put(&afudb->applications);
451 * Regenerate the list of applications of the afm_bd object 'afudb'.
452 * Returns 0 in case of success.
453 * Returns -1 and set errno in case of error
455 int afm_udb_update(struct afm_udb *afudb)
457 struct afm_updt updt;
461 afm_udb_addref(afudb);
464 /* create the result */
465 updt.applications.prvarr = json_object_new_array();
466 updt.applications.pubarr = json_object_new_array();
467 updt.applications.pubobj = json_object_new_object();
468 updt.applications.prvobj = json_object_new_object();
469 if (updt.applications.pubarr == NULL
470 || updt.applications.prvarr == NULL
471 || updt.applications.pubobj == NULL
472 || updt.applications.prvobj == NULL) {
479 if (systemd_unit_list(1, update_cb, &updt) < 0)
482 if (systemd_unit_list(0, update_cb, &updt) < 0)
485 /* commit the result */
486 tmp = afudb->applications;
487 afudb->applications = updt.applications;
489 afm_udb_unref(afudb);
493 apps_put(&updt.applications);
494 afm_udb_unref(afudb);
499 * set the default language to 'lang'
501 void afm_udb_set_default_lang(const char *lang)
503 char *oldval = default_lang;
504 default_lang = lang ? strdup(lang) : NULL;
509 * Get the list of the applications private data of the afm_udb object 'afudb'.
510 * The list is returned as a JSON-array that must be released using
512 * Returns NULL in case of error.
514 struct json_object *afm_udb_applications_private(struct afm_udb *afudb, int uid)
516 return json_object_get(afudb->applications.prvarr);
520 * Get the list of the applications public data of the afm_udb object 'afudb'.
521 * The list is returned as a JSON-array that must be released using
523 * Returns NULL in case of error.
525 struct json_object *afm_udb_applications_public(struct afm_udb *afudb, int uid, const char *lang)
527 return json_object_get(afudb->applications.pubarr);
531 * Get the private data of the applications of 'id' in the afm_udb object 'afudb'.
532 * It returns a JSON-object that must be released using 'json_object_put'.
533 * Returns NULL in case of error.
535 static struct json_object *get_no_case(struct json_object *object, const char *id, int uid, const char *lang)
537 struct json_object *result;
538 struct json_object_iter i;
540 /* search case sensitively */
541 if (json_object_object_get_ex(object, id, &result))
542 return json_object_get(result);
544 /* fallback to a case insensitive search */
545 json_object_object_foreachC(object, i) {
546 if (!strcasecmp(i.key, id))
547 return json_object_get(i.val);
553 * Get the private data of the applications of 'id' in the afm_udb object 'afudb'.
554 * It returns a JSON-object that must be released using 'json_object_put'.
555 * Returns NULL in case of error.
557 struct json_object *afm_udb_get_application_private(struct afm_udb *afudb, const char *id, int uid)
559 return get_no_case(afudb->applications.prvobj, id, uid, NULL);
563 * Get the public data of the applications of 'id' in the afm_udb object 'afudb'.
564 * It returns a JSON-object that must be released using 'json_object_put'.
565 * Returns NULL in case of error.
567 struct json_object *afm_udb_get_application_public(struct afm_udb *afudb,
568 const char *id, int uid, const char *lang)
570 return get_no_case(afudb->applications.pubobj, id, uid, lang);
575 #if defined(TESTAPPFWK)
579 struct afm_udb *afudb = afm_udb_create(1, 1, NULL);
580 printf("array = %s\n", json_object_to_json_string_ext(afudb->applications.pubarr, 3));
581 printf("pubobj = %s\n", json_object_to_json_string_ext(afudb->applications.pubobj, 3));
582 printf("prvobj = %s\n", json_object_to_json_string_ext(afudb->applications.prvobj, 3));