2 Copyright 2015, 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.
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"
38 static const char x_afm_prefix[] = "X-AFM-";
39 static const char service_extension[] = ".service";
40 static const char key_unit_path[] = "-unit-path";
41 static const char key_unit_name[] = "-unit-name";
42 static const char key_unit_scope[] = "-unit-scope";
43 static const char scope_user[] = "user";
44 static const char scope_system[] = "system";
45 static const char key_id[] = "id";
47 #define x_afm_prefix_length (sizeof x_afm_prefix - 1)
48 #define service_extension_length (sizeof service_extension - 1)
51 * The structure afm_apps records the data about applications
52 * for several accesses.
55 struct json_object *prvarr; /* array of the private data of apps */
56 struct json_object *pubarr; /* array of the public data of apps */
57 struct json_object *pubobj; /* hash of application's publics */
58 struct json_object *prvobj; /* hash of application's privates */
62 * The structure afm_udb records the applications
63 * for a set of directories recorded as a linked list
66 struct afm_apps applications; /* the data about applications */
67 int refcount; /* count of references to the structure */
68 int system; /* is managing system units? */
69 int user; /* is managing user units? */
70 size_t prefixlen; /* length of the prefix */
71 char prefix[1]; /* filtering prefix */
75 * The structure afm_updt is internally used for updates
78 struct afm_udb *afudb;
79 struct afm_apps applications;
83 * Release the data of the afm_apps object 'apps'.
85 static void apps_put(struct afm_apps *apps)
87 json_object_put(apps->prvarr);
88 json_object_put(apps->pubarr);
89 json_object_put(apps->pubobj);
90 json_object_put(apps->prvobj);
94 * Append the field 'data' to the field 'name' of the 'object'.
95 * When a second append is done to one field, it is automatically
96 * transformed to an array.
97 * Return 0 in case of success or -1 in case of error.
99 static int append_field(
100 struct json_object *object,
102 struct json_object *data
105 struct json_object *item, *array;
107 if (!json_object_object_get_ex(object, name, &item))
108 json_object_object_add(object, name, data);
110 if (json_object_is_type(item, json_type_array))
113 array = json_object_new_array();
116 json_object_array_add(array, item);
117 json_object_object_add(object, name, array);
119 json_object_array_add(array, data);
123 json_object_put(data);
129 * Adds the field of 'name' and 'value' in 'priv' and also if possible in 'pub'
130 * Returns 0 on success or -1 on error.
132 static int add_field(
133 struct json_object *priv,
134 struct json_object *pub,
141 struct json_object *v;
143 /* try to adapt the value to its type */
145 ival = strtol(value, &end, 10);
146 if (*value && !*end && !errno) {
148 v = json_object_new_int64(ival);
151 v = json_object_new_string(value);
159 if (name[0] == '-') {
160 append_field(priv, &name[1], v);
162 append_field(priv, name, json_object_get(v));
163 append_field(pub, name, v);
169 * Adds the field of 'name' and 'value' in 'priv' and also if possible in 'pub'
170 * Returns 0 on success or -1 on error.
172 static int add_fields_of_content(
173 struct json_object *priv,
174 struct json_object *pub,
179 char *name, *value, *read, *write;
181 read = strstr(content, x_afm_prefix);
183 name = read + x_afm_prefix_length;
184 value = strchr(name, '=');
186 read = strstr(name, x_afm_prefix);
189 read = write = value;
190 while(*read && *read != '\n') {
195 case 'n': *write++ = '\n'; break;
196 case '\n': *write++ = ' '; break;
197 default: *write++ = '\\'; *write++ = *read; break;
202 read = strstr(read, x_afm_prefix);
204 if (add_field(priv, pub, name, value) < 0)
212 * Adds the application widget 'desc' of the directory 'path' to the
213 * afm_apps object 'apps'.
214 * Returns 0 in case of success.
215 * Returns -1 and set errno in case of error
218 struct afm_apps *apps,
220 const char *unitpath,
221 const char *unitname,
226 struct json_object *priv, *pub, *id;
231 /* create the application structure */
232 priv = json_object_new_object();
236 pub = json_object_new_object();
240 /* make the unit name */
241 len = strlen(unitname);
242 assert(len >= (sizeof service_extension - 1));
243 assert(!memcmp(&unitname[len - (sizeof service_extension - 1)], service_extension, sizeof service_extension));
244 if (unitname[len - sizeof service_extension] == '@') {
246 size_t l = (size_t)snprintf(buffer, sizeof buffer, "%d", (int)getuid());
247 un = malloc(len + l + 1);
250 memcpy(&un[0], unitname, len - (sizeof service_extension - 1));
252 memcpy(&un[len - (sizeof service_extension - 1)], buffer, l);
253 memcpy(&un[len - (sizeof service_extension - 1) + l], service_extension, sizeof service_extension);
256 /* adds the values */
257 if (add_fields_of_content(priv, pub, content, length)
258 || add_field(priv, pub, key_unit_path, unitpath)
259 || add_field(priv, pub, key_unit_name, un ? : unitname)
260 || add_field(priv, pub, key_unit_scope, isuser ? scope_user : scope_system))
266 if (!json_object_object_get_ex(pub, key_id, &id)) {
270 strid = json_object_get_string(id);
272 /* record the application structure */
273 json_object_get(pub);
274 json_object_array_add(apps->pubarr, pub);
275 json_object_object_add(apps->pubobj, strid, pub);
276 json_object_get(priv);
277 json_object_array_add(apps->prvarr, priv);
278 json_object_object_add(apps->prvobj, strid, priv);
283 json_object_put(pub);
284 json_object_put(priv);
291 static int read_unit_file(const char *path, char **content, size_t *length)
294 char c, *read, *write;
297 rc = getfile(path, content, length);
299 /* removes any comment and join lines */
301 read = write = *content;
303 do { c = *read++; } while (c == '\r');
308 if (c == ';' || c == '#') {
309 st = 3; /* removes lines starting with ; or # */
313 break; /* removes empty lines */
341 *length = (size_t)(write - *content);
342 *content = realloc(*content, *length + 1);
348 * called for each unit
350 static int update_cb(void *closure, const char *name, const char *path, int isuser)
352 struct afm_updt *updt = closure;
357 /* prefix filtering */
358 length = updt->afudb->prefixlen;
359 if (length && strncmp(updt->afudb->prefix, name, length))
363 length = strlen(name);
364 if (length < service_extension_length || strcmp(service_extension, name + length - service_extension_length))
368 rc = read_unit_file(path, &content, &length);
372 /* process the file */
373 rc = addunit(&updt->applications, isuser, path, name, content, length);
379 * Creates an afm_udb object and returns it with one reference added.
380 * Return NULL with errno = ENOMEM if memory exhausted.
382 struct afm_udb *afm_udb_create(int sys, int usr, const char *prefix)
385 struct afm_udb *afudb;
387 length = prefix ? strlen(prefix) : 0;
388 afudb = malloc(length + sizeof * afudb);
393 afudb->applications.prvarr = NULL;
394 afudb->applications.pubarr = NULL;
395 afudb->applications.pubobj = NULL;
396 afudb->applications.prvobj = NULL;
399 afudb->prefixlen = length;
401 memcpy(afudb->prefix, prefix, length);
402 afudb->prefix[length] = 0;
403 if (afm_udb_update(afudb) < 0) {
404 afm_udb_unref(afudb);
412 * Adds a reference to an existing afm_udb.
414 void afm_udb_addref(struct afm_udb *afudb)
421 * Removes a reference to an existing afm_udb object.
422 * Removes the objet if there no more reference to it.
424 void afm_udb_unref(struct afm_udb *afudb)
427 if (!--afudb->refcount) {
428 /* no more reference, clean the memory used by the object */
429 apps_put(&afudb->applications);
435 * Regenerate the list of applications of the afm_bd object 'afudb'.
436 * Returns 0 in case of success.
437 * Returns -1 and set errno in case of error
439 int afm_udb_update(struct afm_udb *afudb)
441 struct afm_updt updt;
445 afm_udb_addref(afudb);
448 /* create the result */
449 updt.applications.prvarr = json_object_new_array();
450 updt.applications.pubarr = json_object_new_array();
451 updt.applications.pubobj = json_object_new_object();
452 updt.applications.prvobj = json_object_new_object();
453 if (updt.applications.pubarr == NULL
454 || updt.applications.prvarr == NULL
455 || updt.applications.pubobj == NULL
456 || updt.applications.prvobj == NULL) {
463 if (systemd_unit_list(1, update_cb, &updt) < 0)
466 if (systemd_unit_list(0, update_cb, &updt) < 0)
469 /* commit the result */
470 tmp = afudb->applications;
471 afudb->applications = updt.applications;
473 afm_udb_addref(afudb);
477 apps_put(&updt.applications);
478 afm_udb_addref(afudb);
483 * Get the list of the applications private data of the afm_udb object 'afudb'.
484 * The list is returned as a JSON-array that must be released using
486 * Returns NULL in case of error.
488 struct json_object *afm_udb_applications_private(struct afm_udb *afudb)
490 return json_object_get(afudb->applications.prvarr);
494 * Get the list of the applications public data of the afm_udb object 'afudb'.
495 * The list is returned as a JSON-array that must be released using
497 * Returns NULL in case of error.
499 struct json_object *afm_udb_applications_public(struct afm_udb *afudb)
501 return json_object_get(afudb->applications.pubarr);
505 * Get the private data of the applications of 'id' in the afm_udb object 'afudb'.
506 * It returns a JSON-object that must be released using 'json_object_put'.
507 * Returns NULL in case of error.
509 static struct json_object *get_no_case(struct json_object *object, const char *id)
511 struct json_object *result;
512 struct json_object_iter i;
514 /* search case sensitively */
515 if (json_object_object_get_ex(object, id, &result))
516 return json_object_get(result);
518 /* fallback to a case insensitive search */
519 json_object_object_foreachC(object, i) {
520 if (!strcasecmp(i.key, id))
521 return json_object_get(i.val);
527 * Get the private data of the applications of 'id' in the afm_udb object 'afudb'.
528 * It returns a JSON-object that must be released using 'json_object_put'.
529 * Returns NULL in case of error.
531 struct json_object *afm_udb_get_application_private(struct afm_udb *afudb, const char *id)
533 return get_no_case(afudb->applications.prvobj, id);
537 * Get the public data of the applications of 'id' in the afm_udb object 'afudb'.
538 * It returns a JSON-object that must be released using 'json_object_put'.
539 * Returns NULL in case of error.
541 struct json_object *afm_udb_get_application_public(struct afm_udb *afudb,
544 return get_no_case(afudb->applications.pubobj, id);
549 #if defined(TESTAPPFWK)
553 struct afm_udb *afudb = afm_udb_create(1, 1, NULL);
554 printf("array = %s\n", json_object_to_json_string_ext(afudb->applications.pubarr, 3));
555 printf("pubobj = %s\n", json_object_to_json_string_ext(afudb->applications.pubobj, 3));
556 printf("prvobj = %s\n", json_object_to_json_string_ext(afudb->applications.prvobj, 3));