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"
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 * Release the data of the afm_apps object 'apps'.
84 static void apps_put(struct afm_apps *apps)
86 json_object_put(apps->prvarr);
87 json_object_put(apps->pubarr);
88 json_object_put(apps->pubobj);
89 json_object_put(apps->prvobj);
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.
98 static int append_field(
99 struct json_object *object,
101 struct json_object *data
104 struct json_object *item, *array;
106 if (!json_object_object_get_ex(object, name, &item))
107 json_object_object_add(object, name, data);
109 if (json_object_is_type(item, json_type_array))
112 array = json_object_new_array();
115 json_object_array_add(array, item);
116 json_object_object_add(object, name, array);
118 json_object_array_add(array, data);
122 json_object_put(data);
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.
131 static int add_field(
132 struct json_object *priv,
133 struct json_object *pub,
140 struct json_object *v;
142 /* try to adapt the value to its type */
144 ival = strtol(value, &end, 10);
145 if (*value && !*end && !errno) {
147 v = json_object_new_int64(ival);
150 v = json_object_new_string(value);
158 if (name[0] == '-') {
159 append_field(priv, &name[1], v);
161 append_field(priv, name, json_object_get(v));
162 append_field(pub, name, v);
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.
171 static int add_fields_of_content(
172 struct json_object *priv,
173 struct json_object *pub,
178 char *name, *value, *read, *write;
180 read = strstr(content, x_afm_prefix);
182 name = read + x_afm_prefix_length;
183 value = strchr(name, '=');
185 read = strstr(name, x_afm_prefix);
188 read = write = value;
189 while(*read && *read != '\n') {
194 case 'n': *write++ = '\n'; break;
195 case '\n': *write++ = ' '; break;
196 default: *write++ = '\\'; *write++ = *read; break;
201 read = strstr(read, x_afm_prefix);
203 if (add_field(priv, pub, name, value) < 0)
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
217 struct afm_apps *apps,
219 const char *unitpath,
220 const char *unitname,
225 struct json_object *priv, *pub, *id;
230 /* create the application structure */
231 priv = json_object_new_object();
235 pub = json_object_new_object();
239 /* make the unit name */
240 len = strlen(unitname);
241 assert(len >= (sizeof service_extension - 1));
242 assert(!memcmp(&unitname[len - (sizeof service_extension - 1)], service_extension, sizeof service_extension));
243 if (unitname[len - sizeof service_extension] == '@') {
245 size_t l = (size_t)snprintf(buffer, sizeof buffer, "%d", (int)getuid());
246 un = malloc(len + l + 1);
249 memcpy(&un[0], unitname, len - (sizeof service_extension - 1));
251 memcpy(&un[len - (sizeof service_extension - 1)], buffer, l);
252 memcpy(&un[len - (sizeof service_extension - 1) + l], service_extension, sizeof service_extension);
255 /* adds the values */
256 if (add_fields_of_content(priv, pub, content, length)
257 || add_field(priv, pub, key_unit_path, unitpath)
258 || add_field(priv, pub, key_unit_name, un ? : unitname)
259 || 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);
282 json_object_put(pub);
283 json_object_put(priv);
290 static int read_unit_file(const char *path, char **content, size_t *length)
293 char c, *read, *write;
296 rc = getfile(path, content, length);
298 /* removes any comment and join lines */
300 read = write = *content;
302 do { c = *read++; } while (c == '\r');
307 if (c == ';' || c == '#') {
308 st = 3; /* removes lines starting with ; or # */
312 break; /* removes empty lines */
340 *length = (size_t)(write - *content);
341 *content = realloc(*content, *length + 1);
347 * called for each unit
349 static int update_cb(void *closure, const char *name, const char *path, int isuser)
351 struct afm_updt *updt = closure;
356 /* prefix filtering */
357 length = updt->afudb->prefixlen;
358 if (length && strncmp(updt->afudb->prefix, name, length))
362 length = strlen(name);
363 if (length < service_extension_length || strcmp(service_extension, name + length - service_extension_length))
367 rc = read_unit_file(path, &content, &length);
371 /* process the file */
372 rc = addunit(&updt->applications, isuser, path, name, content, length);
374 ERROR("Ignored boggus unit %s (error: %m)", path); */
380 * Creates an afm_udb object and returns it with one reference added.
381 * Return NULL with errno = ENOMEM if memory exhausted.
383 struct afm_udb *afm_udb_create(int sys, int usr, const char *prefix)
386 struct afm_udb *afudb;
388 length = prefix ? strlen(prefix) : 0;
389 afudb = malloc(length + sizeof * afudb);
394 afudb->applications.prvarr = NULL;
395 afudb->applications.pubarr = NULL;
396 afudb->applications.pubobj = NULL;
397 afudb->applications.prvobj = NULL;
400 afudb->prefixlen = length;
402 memcpy(afudb->prefix, prefix, length);
403 afudb->prefix[length] = 0;
404 if (afm_udb_update(afudb) < 0) {
405 afm_udb_unref(afudb);
413 * Adds a reference to an existing afm_udb.
415 void afm_udb_addref(struct afm_udb *afudb)
422 * Removes a reference to an existing afm_udb object.
423 * Removes the objet if there no more reference to it.
425 void afm_udb_unref(struct afm_udb *afudb)
428 if (!--afudb->refcount) {
429 /* no more reference, clean the memory used by the object */
430 apps_put(&afudb->applications);
436 * Regenerate the list of applications of the afm_bd object 'afudb'.
437 * Returns 0 in case of success.
438 * Returns -1 and set errno in case of error
440 int afm_udb_update(struct afm_udb *afudb)
442 struct afm_updt updt;
446 afm_udb_addref(afudb);
449 /* create the result */
450 updt.applications.prvarr = json_object_new_array();
451 updt.applications.pubarr = json_object_new_array();
452 updt.applications.pubobj = json_object_new_object();
453 updt.applications.prvobj = json_object_new_object();
454 if (updt.applications.pubarr == NULL
455 || updt.applications.prvarr == NULL
456 || updt.applications.pubobj == NULL
457 || updt.applications.prvobj == NULL) {
464 if (systemd_unit_list(1, update_cb, &updt) < 0)
467 if (systemd_unit_list(0, update_cb, &updt) < 0)
470 /* commit the result */
471 tmp = afudb->applications;
472 afudb->applications = updt.applications;
474 afm_udb_addref(afudb);
478 apps_put(&updt.applications);
479 afm_udb_addref(afudb);
484 * Get the list of the applications private data of the afm_udb object 'afudb'.
485 * The list is returned as a JSON-array that must be released using
487 * Returns NULL in case of error.
489 struct json_object *afm_udb_applications_private(struct afm_udb *afudb)
491 return json_object_get(afudb->applications.prvarr);
495 * Get the list of the applications public data of the afm_udb object 'afudb'.
496 * The list is returned as a JSON-array that must be released using
498 * Returns NULL in case of error.
500 struct json_object *afm_udb_applications_public(struct afm_udb *afudb)
502 return json_object_get(afudb->applications.pubarr);
506 * Get the private data of the applications of 'id' in the afm_udb object 'afudb'.
507 * It returns a JSON-object that must be released using 'json_object_put'.
508 * Returns NULL in case of error.
510 static struct json_object *get_no_case(struct json_object *object, const char *id)
512 struct json_object *result;
513 struct json_object_iter i;
515 /* search case sensitively */
516 if (json_object_object_get_ex(object, id, &result))
517 return json_object_get(result);
519 /* fallback to a case insensitive search */
520 json_object_object_foreachC(object, i) {
521 if (!strcasecmp(i.key, id))
522 return json_object_get(i.val);
528 * Get the private data of the applications of 'id' in the afm_udb object 'afudb'.
529 * It returns a JSON-object that must be released using 'json_object_put'.
530 * Returns NULL in case of error.
532 struct json_object *afm_udb_get_application_private(struct afm_udb *afudb, const char *id)
534 return get_no_case(afudb->applications.prvobj, id);
538 * Get the public data of the applications of 'id' in the afm_udb object 'afudb'.
539 * It returns a JSON-object that must be released using 'json_object_put'.
540 * Returns NULL in case of error.
542 struct json_object *afm_udb_get_application_public(struct afm_udb *afudb,
545 return get_no_case(afudb->applications.pubobj, id);
550 #if defined(TESTAPPFWK)
554 struct afm_udb *afudb = afm_udb_create(1, 1, NULL);
555 printf("array = %s\n", json_object_to_json_string_ext(afudb->applications.pubarr, 3));
556 printf("pubobj = %s\n", json_object_to_json_string_ext(afudb->applications.pubobj, 3));
557 printf("prvobj = %s\n", json_object_to_json_string_ext(afudb->applications.prvobj, 3));