0ede96aa0c5579c8c5bae272acb1dd19781c5f08
[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)
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                 while (appfwk->nrroots)
70                         free(appfwk->roots[--appfwk->nrroots]);
71                 free(appfwk->roots);
72                 free(appfwk);
73         }
74 }
75
76 int appfwk_add_root(struct appfwk *appfwk, const char *path)
77 {
78         int i, n;
79         char *r, **roots;
80
81         assert(appfwk);
82
83         /* don't depend on the cwd and unique name */
84         r = realpath(path, NULL);
85         if (!r)
86                 return -1;
87
88         /* avoiding duplications */
89         n = appfwk->nrroots;
90         roots = appfwk->roots;
91         for (i = 0 ; i < n ; i++) {
92                 if (!strcmp(r, roots[i])) {
93                         free(r);
94                         return 0;
95                 }
96         }
97
98         /* add */
99         roots = realloc(roots, (n + 1) * sizeof(roots[0]));
100         if (!roots) {
101                 free(r);
102                 errno = ENOMEM;
103                 return -1;
104         }
105         roots[n++] = r;
106         appfwk->roots = roots;
107         appfwk->nrroots = n;
108         return 0;
109 }
110
111 static int json_add(struct json_object *obj, const char *key, struct json_object *val)
112 {
113         json_object_object_add(obj, key, val);
114         return 0;
115 }
116
117 static int json_add_str(struct json_object *obj, const char *key, const char *val)
118 {
119         struct json_object *str = json_object_new_string (val ? val : "");
120         return str ? json_add(obj, key, str) : -1;
121 }
122
123 static int json_add_int(struct json_object *obj, const char *key, int val)
124 {
125         struct json_object *v = json_object_new_int (val);
126         return v ? json_add(obj, key, v) : -1;
127 }
128
129 static int addapp(struct afapps *apps, const char *path)
130 {
131         struct wgt_info *info;
132         const struct wgt_desc *desc;
133         const struct wgt_desc_feature *feat;
134         struct json_object *priv = NULL, *pub, *bya, *plugs, *str;
135         char *appid, *end;
136
137         /* connect to the widget */
138         info = wgt_info_createat(AT_FDCWD, path, 0, 1, 0);
139         if (info == NULL)
140                 goto error;
141         desc = wgt_info_desc(info);
142
143         /* create the application id */
144         appid = alloca(2 + strlen(desc->id) + strlen(desc->version));
145         end = stpcpy(appid, desc->id);
146         *end++ = '@';
147         strcpy(end, desc->version);
148
149         /* create the application structure */
150         priv = json_object_new_object();
151         if (!priv)
152                 goto error2;
153
154         pub = json_object_new_object();
155         if (!priv)
156                 goto error2;
157
158         if (json_add(priv, "public", pub)) {
159                 json_object_put(pub);
160                 goto error2;
161         }
162
163         plugs = json_object_new_array();
164         if (!priv)
165                 goto error2;
166
167         if (json_add(priv, "plugins", plugs)) {
168                 json_object_put(plugs);
169                 goto error2;
170         }
171
172         if(json_add_str(pub, "id", appid)
173         || json_add_str(priv, "id", desc->id)
174         || json_add_str(pub, "version", desc->version)
175         || json_add_str(priv, "path", path)
176         || json_add_int(pub, "width", desc->width)
177         || json_add_int(pub, "height", desc->height)
178         || json_add_str(pub, "name", desc->name)
179         || json_add_str(pub, "description", desc->description)
180         || json_add_str(pub, "shortname", desc->name_short)
181         || json_add_str(pub, "author", desc->author))
182                 goto error2;
183
184         feat = desc->features;
185         while (feat) {
186                 static const char prefix[] = FWK_PREFIX_PLUGIN;
187                 if (!memcmp(feat->name, prefix, sizeof prefix - 1)) {
188                         str = json_object_new_string (feat->name + sizeof prefix - 1);
189                         if (str == NULL)
190                                 goto error2;
191                         if (json_object_array_add(plugs, str)) {
192                                 json_object_put(str);
193                                 goto error2;
194                         }
195                 }
196                 feat = feat->next;
197         }
198
199         /* record the application structure */
200         if (!json_object_object_get_ex(apps->byapp, desc->id, &bya)) {
201                 bya = json_object_new_object();
202                 if (!bya)
203                         goto error2;
204                 if (json_add(apps->byapp, desc->id, bya)) {
205                         json_object_put(bya);
206                         goto error2;
207                 }
208         }
209
210         if (json_add(apps->direct, appid, priv))
211                 goto error2;
212         json_object_get(priv);
213
214         if (json_add(bya, desc->version, priv)) {
215                 json_object_put(priv);
216                 goto error2;
217         }
218
219         if (json_object_array_add(apps->pubarr, pub))
220                 goto error2;
221
222         wgt_info_unref(info);
223         return 0;
224
225 error2:
226         json_object_put(priv);
227         wgt_info_unref(info);
228 error:
229         return -1;
230 }
231
232 struct enumdata {
233         char path[PATH_MAX];
234         int length;
235         struct afapps apps;
236 };
237
238 static int enumentries(struct enumdata *data, int (*callto)(struct enumdata *))
239 {
240         DIR *dir;
241         int rc;
242         char *beg, *end;
243         struct dirent entry, *e;
244
245         /* opens the directory */
246         dir = opendir(data->path);
247         if (!dir)
248                 return -1;
249
250         /* prepare appending entry names */
251         beg = data->path + data->length;
252         *beg++ = '/';
253
254         /* enumerate entries */
255         rc = readdir_r(dir, &entry, &e);
256         while (!rc && e) {
257                 if (entry.d_name[0] != '.' || (entry.d_name[1] && (entry.d_name[1] != '.' || entry.d_name[2]))) {
258                         /* prepare callto */
259                         end = stpcpy(beg, entry.d_name);
260                         data->length = end - data->path;
261                         /* call the function */
262                         rc = callto(data);
263                         if (rc)
264                                 break;
265                 }       
266                 rc = readdir_r(dir, &entry, &e);
267         }
268         closedir(dir);
269         return rc;
270 }
271
272 static int recordapp(struct enumdata *data)
273 {
274         return addapp(&data->apps, data->path);
275 }
276
277 /* enumerate the versions */
278 static int enumvers(struct enumdata *data)
279 {
280         int rc = enumentries(data, recordapp);
281         return !rc || errno != ENOTDIR ? 0 : rc;
282 }
283
284 /* regenerate the list of applications */
285 int appfwk_update_applications(struct appfwk *af)
286 {
287         int rc, iroot;
288         struct enumdata edata;
289         struct afapps oldapps;
290
291         /* create the result */
292         edata.apps.pubarr = json_object_new_array();
293         edata.apps.direct = json_object_new_object();
294         edata.apps.byapp = json_object_new_object();
295         if (edata.apps.pubarr == NULL || edata.apps.direct == NULL || edata.apps.byapp == NULL) {
296                 errno = ENOMEM;
297                 goto error;
298         }
299         /* for each root */
300         for (iroot = 0 ; iroot < af->nrroots ; iroot++) {
301                 edata.length = stpcpy(edata.path, af->roots[iroot]) - edata.path;
302                 assert(edata.length < sizeof edata.path);
303                 /* enumerate the applications */
304                 rc = enumentries(&edata, enumvers);
305                 if (rc)
306                         goto error;
307         }
308         /* commit the result */
309         oldapps = af->applications;
310         af->applications = edata.apps;
311         json_object_put(oldapps.pubarr);
312         json_object_put(oldapps.direct);
313         json_object_put(oldapps.byapp);
314         return 0;
315
316 error:
317         json_object_put(edata.apps.pubarr);
318         json_object_put(edata.apps.direct);
319         json_object_put(edata.apps.byapp);
320         return -1;
321 }
322
323 int appfwk_ensure_applications(struct appfwk *af)
324 {
325         return af->applications.pubarr ? 0 : appfwk_update_applications(af);
326 }
327
328 /* regenerate the list of applications */
329 struct json_object *appfwk_application_list(struct appfwk *af)
330 {
331         return appfwk_ensure_applications(af) ? NULL : af->applications.pubarr;
332 }
333
334 #include <stdio.h>
335 int main()
336 {
337 struct appfwk *af = appfwk_create();
338 appfwk_add_root(af,FWK_APP_DIR);
339 appfwk_update_applications(af);
340 printf("array = %s\n", json_object_to_json_string_ext(af->applications.pubarr, 3));
341 printf("direct = %s\n", json_object_to_json_string_ext(af->applications.direct, 3));
342 printf("byapp = %s\n", json_object_to_json_string_ext(af->applications.byapp, 3));
343 return 0;
344 }
345