daemon ready for the API
[src/app-framework-main.git] / src / appfwk.c
1 /*
2  Copyright 2015 IoT.bzh
3
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7
8      http://www.apache.org/licenses/LICENSE-2.0
9
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16
17 #include <stdlib.h>
18 #include <assert.h>
19 #include <string.h>
20 #include <errno.h>
21 #include <dirent.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <sys/types.h>
25
26 #include <json.h>
27
28 #include <wgt-info.h>
29
30 struct afapps {
31         struct json_object *pubarr;
32         struct json_object *direct;
33         struct json_object *byapp;
34 };
35
36 struct appfwk {
37         int refcount;
38         int nrroots;
39         char **roots;
40         struct afapps applications;
41 };
42
43 struct appfwk *appfwk_create()
44 {
45         struct appfwk *appfwk = malloc(sizeof * appfwk);
46         if (appfwk == NULL)
47                 errno = ENOMEM;
48         else {
49                 appfwk->refcount = 1;
50                 appfwk->nrroots = 0;
51                 appfwk->roots = NULL;
52                 appfwk->applications.pubarr = NULL;
53                 appfwk->applications.direct = NULL;
54                 appfwk->applications.byapp = NULL;
55         }
56         return appfwk;
57 }
58
59 void appfwk_addref(struct appfwk *appfwk)
60 {
61         assert(appfwk);
62         appfwk->refcount++;
63 }
64
65 void appfwk_unref(struct appfwk *appfwk)
66 {
67         assert(appfwk);
68         if (!--appfwk->refcount) {
69                 json_object_put(appfwk->applications.pubarr);
70                 json_object_put(appfwk->applications.direct);
71                 json_object_put(appfwk->applications.byapp);
72                 while (appfwk->nrroots)
73                         free(appfwk->roots[--appfwk->nrroots]);
74                 free(appfwk->roots);
75                 free(appfwk);
76         }
77 }
78
79 int appfwk_add_root(struct appfwk *appfwk, const char *path)
80 {
81         int i, n;
82         char *r, **roots;
83
84         assert(appfwk);
85
86         /* don't depend on the cwd and unique name */
87         r = realpath(path, NULL);
88         if (!r)
89                 return -1;
90
91         /* avoiding duplications */
92         n = appfwk->nrroots;
93         roots = appfwk->roots;
94         for (i = 0 ; i < n ; i++) {
95                 if (!strcmp(r, roots[i])) {
96                         free(r);
97                         return 0;
98                 }
99         }
100
101         /* add */
102         roots = realloc(roots, (n + 1) * sizeof(roots[0]));
103         if (!roots) {
104                 free(r);
105                 errno = ENOMEM;
106                 return -1;
107         }
108         roots[n++] = r;
109         appfwk->roots = roots;
110         appfwk->nrroots = n;
111         return 0;
112 }
113
114 static int json_add(struct json_object *obj, const char *key, struct json_object *val)
115 {
116         json_object_object_add(obj, key, val);
117         return 0;
118 }
119
120 static int json_add_str(struct json_object *obj, const char *key, const char *val)
121 {
122         struct json_object *str = json_object_new_string (val ? val : "");
123         return str ? json_add(obj, key, str) : -1;
124 }
125
126 static int json_add_int(struct json_object *obj, const char *key, int val)
127 {
128         struct json_object *v = json_object_new_int (val);
129         return v ? json_add(obj, key, v) : -1;
130 }
131
132 static int addapp(struct afapps *apps, const char *path)
133 {
134         struct wgt_info *info;
135         const struct wgt_desc *desc;
136         const struct wgt_desc_feature *feat;
137         struct json_object *priv = NULL, *pub, *bya, *plugs, *str;
138         char *appid, *end;
139
140         /* connect to the widget */
141         info = wgt_info_createat(AT_FDCWD, path, 0, 1, 0);
142         if (info == NULL)
143                 goto error;
144         desc = wgt_info_desc(info);
145
146         /* create the application id */
147         appid = alloca(2 + strlen(desc->id) + strlen(desc->version));
148         end = stpcpy(appid, desc->id);
149         *end++ = '@';
150         strcpy(end, desc->version);
151
152         /* create the application structure */
153         priv = json_object_new_object();
154         if (!priv)
155                 goto error2;
156
157         pub = json_object_new_object();
158         if (!priv)
159                 goto error2;
160
161         if (json_add(priv, "public", pub)) {
162                 json_object_put(pub);
163                 goto error2;
164         }
165
166         plugs = json_object_new_array();
167         if (!priv)
168                 goto error2;
169
170         if (json_add(priv, "plugins", plugs)) {
171                 json_object_put(plugs);
172                 goto error2;
173         }
174
175         if(json_add_str(pub, "id", appid)
176         || json_add_str(priv, "id", desc->id)
177         || json_add_str(pub, "version", desc->version)
178         || json_add_str(priv, "path", path)
179         || json_add_int(pub, "width", desc->width)
180         || json_add_int(pub, "height", desc->height)
181         || json_add_str(pub, "name", desc->name)
182         || json_add_str(pub, "description", desc->description)
183         || json_add_str(pub, "shortname", desc->name_short)
184         || json_add_str(pub, "author", desc->author))
185                 goto error2;
186
187         feat = desc->features;
188         while (feat) {
189                 static const char prefix[] = FWK_PREFIX_PLUGIN;
190                 if (!memcmp(feat->name, prefix, sizeof prefix - 1)) {
191                         str = json_object_new_string (feat->name + sizeof prefix - 1);
192                         if (str == NULL)
193                                 goto error2;
194                         if (json_object_array_add(plugs, str)) {
195                                 json_object_put(str);
196                                 goto error2;
197                         }
198                 }
199                 feat = feat->next;
200         }
201
202         /* record the application structure */
203         if (!json_object_object_get_ex(apps->byapp, desc->id, &bya)) {
204                 bya = json_object_new_object();
205                 if (!bya)
206                         goto error2;
207                 if (json_add(apps->byapp, desc->id, bya)) {
208                         json_object_put(bya);
209                         goto error2;
210                 }
211         }
212
213         if (json_add(apps->direct, appid, priv))
214                 goto error2;
215         json_object_get(priv);
216
217         if (json_add(bya, desc->version, priv)) {
218                 json_object_put(priv);
219                 goto error2;
220         }
221
222         if (json_object_array_add(apps->pubarr, pub))
223                 goto error2;
224
225         wgt_info_unref(info);
226         return 0;
227
228 error2:
229         json_object_put(priv);
230         wgt_info_unref(info);
231 error:
232         return -1;
233 }
234
235 struct enumdata {
236         char path[PATH_MAX];
237         int length;
238         struct afapps apps;
239 };
240
241 static int enumentries(struct enumdata *data, int (*callto)(struct enumdata *))
242 {
243         DIR *dir;
244         int rc;
245         char *beg, *end;
246         struct dirent entry, *e;
247
248         /* opens the directory */
249         dir = opendir(data->path);
250         if (!dir)
251                 return -1;
252
253         /* prepare appending entry names */
254         beg = data->path + data->length;
255         *beg++ = '/';
256
257         /* enumerate entries */
258         rc = readdir_r(dir, &entry, &e);
259         while (!rc && e) {
260                 if (entry.d_name[0] != '.' || (entry.d_name[1] && (entry.d_name[1] != '.' || entry.d_name[2]))) {
261                         /* prepare callto */
262                         end = stpcpy(beg, entry.d_name);
263                         data->length = end - data->path;
264                         /* call the function */
265                         rc = callto(data);
266                         if (rc)
267                                 break;
268                 }       
269                 rc = readdir_r(dir, &entry, &e);
270         }
271         closedir(dir);
272         return rc;
273 }
274
275 static int recordapp(struct enumdata *data)
276 {
277         return addapp(&data->apps, data->path);
278 }
279
280 /* enumerate the versions */
281 static int enumvers(struct enumdata *data)
282 {
283         int rc = enumentries(data, recordapp);
284         return !rc || errno != ENOTDIR ? 0 : rc;
285 }
286
287 /* regenerate the list of applications */
288 int appfwk_update_applications(struct appfwk *af)
289 {
290         int rc, iroot;
291         struct enumdata edata;
292         struct afapps oldapps;
293
294         /* create the result */
295         edata.apps.pubarr = json_object_new_array();
296         edata.apps.direct = json_object_new_object();
297         edata.apps.byapp = json_object_new_object();
298         if (edata.apps.pubarr == NULL || edata.apps.direct == NULL || edata.apps.byapp == NULL) {
299                 errno = ENOMEM;
300                 goto error;
301         }
302         /* for each root */
303         for (iroot = 0 ; iroot < af->nrroots ; iroot++) {
304                 edata.length = stpcpy(edata.path, af->roots[iroot]) - edata.path;
305                 assert(edata.length < sizeof edata.path);
306                 /* enumerate the applications */
307                 rc = enumentries(&edata, enumvers);
308                 if (rc)
309                         goto error;
310         }
311         /* commit the result */
312         oldapps = af->applications;
313         af->applications = edata.apps;
314         json_object_put(oldapps.pubarr);
315         json_object_put(oldapps.direct);
316         json_object_put(oldapps.byapp);
317         return 0;
318
319 error:
320         json_object_put(edata.apps.pubarr);
321         json_object_put(edata.apps.direct);
322         json_object_put(edata.apps.byapp);
323         return -1;
324 }
325
326 int appfwk_ensure_applications(struct appfwk *af)
327 {
328         return af->applications.pubarr ? 0 : appfwk_update_applications(af);
329 }
330
331 struct json_object *appfwk_application_list(struct appfwk *af)
332 {
333         return appfwk_ensure_applications(af) ? NULL : af->applications.pubarr;
334 }
335
336 struct json_object *appfwk_get_application(struct appfwk *af, const char *id)
337 {
338         struct json_object *result;
339         if (!appfwk_ensure_applications(af) && json_object_object_get_ex(af->applications.direct, id, &result))
340                 return result;
341         return NULL;
342 }
343
344 struct json_object *appfwk_get_application_public(struct appfwk *af, const char *id)
345 {
346         struct json_object *result = appfwk_get_application(af, id);
347         return result && json_object_object_get_ex(result, "public", &result) ? result : NULL;
348 }
349
350
351
352
353 #if defined(TESTAPPFWK)
354 #include <stdio.h>
355 int main()
356 {
357 struct appfwk *af = appfwk_create();
358 appfwk_add_root(af,FWK_APP_DIR);
359 appfwk_update_applications(af);
360 printf("array = %s\n", json_object_to_json_string_ext(af->applications.pubarr, 3));
361 printf("direct = %s\n", json_object_to_json_string_ext(af->applications.direct, 3));
362 printf("byapp = %s\n", json_object_to_json_string_ext(af->applications.byapp, 3));
363 return 0;
364 }
365 #endif
366