first add of asynchonous handling
[src/app-framework-binder.git] / plugins / afm-main-plugin / afm-main-plugin.c
1 /*
2  * Copyright (C) 2015 "IoT.bzh"
3  * Author "Fulup Ar Foll"
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define _GNU_SOURCE         /* See feature_test_macros(7) */
19 #include <stdio.h>
20 #include <string.h>
21 #include <assert.h>
22 #include <json.h>
23
24 #include "afb-plugin.h"
25
26 #include "utils-sbus.h"
27 #include "utils-jbus.h"
28
29 static const char _added_[]     = "added";
30 static const char _auto_[]      = "auto";
31 static const char _continue_[]  = "continue";
32 static const char _changed_[]   = "changed";
33 static const char _detail_[]    = "detail";
34 static const char _id_[]        = "id";
35 static const char _install_[]   = "install";
36 static const char _local_[]     = "local";
37 static const char _mode_[]      = "mode";
38 static const char _remote_[]    = "remote";
39 static const char _runid_[]     = "runid";
40 static const char _runnables_[] = "runnables";
41 static const char _runners_[]   = "runners";
42 static const char _start_[]     = "start";
43 static const char _state_[]     = "state";
44 static const char _stop_[]      = "stop";
45 static const char _terminate_[] = "terminate";
46 static const char _uninstall_[] = "uninstall";
47 static const char _uri_[]       = "uri";
48
49 static const struct AFB_interface *interface;
50 static struct afb_evmgr evmgr;
51
52 static struct jbus *jbus;
53
54 struct memo
55 {
56         struct afb_req request;
57         const char *method;
58 };
59
60 static struct memo *make_memo(struct afb_req request, const char *method)
61 {
62         struct memo *memo = malloc(sizeof *memo);
63         if (memo != NULL) {
64                 memo->request = request;
65                 memo->method = method;
66         }
67         return memo;
68 }
69
70 static void application_list_changed(const char *data, void *closure)
71 {
72         afb_evmgr_push(evmgr, "application-list-changed", NULL);
73 }
74
75 static struct json_object *embed(const char *tag, struct json_object *obj)
76 {
77         struct json_object *result;
78
79         if (obj == NULL)
80                 result = NULL;
81         else if (!tag)
82                 result = obj;
83         else {
84                 result = json_object_new_object();
85                 if (result == NULL) {
86                         /* can't embed */
87                         result = obj;
88                 }
89                 else {
90                         /* TODO why is json-c not returning a status? */
91                         json_object_object_add(result, tag, obj);
92                 }
93         }
94         return result;
95 }
96
97 static void embed_call_void_callback(int status, struct json_object *obj, struct memo *memo)
98 {
99         if (interface->verbosity)
100                 fprintf(stderr, "(afm-main-plugin) %s(true) -> %s\n", memo->method,
101                         obj ? json_object_to_json_string(obj) : "NULL");
102         if (obj == NULL) {
103                 afb_req_fail(memo->request, "failed", "framework daemon failure");
104         } else {
105                 obj = json_object_get(obj);
106                 obj = embed(memo->method, obj);
107                 if (obj == NULL) {
108                         afb_req_fail(memo->request, "failed", "framework daemon failure");
109                 } else {
110                         afb_req_success(memo->request, obj, NULL);
111                 }
112         }
113         free(memo);
114 }
115
116 static void embed_call_void(struct afb_req request, const char *method)
117 {
118         struct memo *memo = make_memo(request, method);
119         if (memo == NULL)
120                 afb_req_fail(request, "failed", "out of memory");
121         else if (jbus_call_sj(jbus, method, "true", (void*)embed_call_void_callback, memo) < 0) {
122                 afb_req_fail(request, "failed", "dbus failure");
123                 free(memo);
124         }
125 }
126
127 static void call_appid_callback(int status, struct json_object *obj, struct memo *memo)
128 {
129         if (interface->verbosity)
130                 fprintf(stderr, "(afm-main-plugin) %s -> %s\n", memo->method, 
131                         obj ? json_object_to_json_string(obj) : "NULL");
132         if (obj == NULL) {
133                 afb_req_fail(memo->request, "failed", "framework daemon failure");
134         } else {
135                 obj = json_object_get(obj);
136                 afb_req_success(memo->request, obj, NULL);
137         }
138         free(memo);
139 }
140
141 static void call_appid(struct afb_req request, const char *method)
142 {
143         struct memo *memo;
144         char *sid;
145         const char *id = afb_req_value(request, _id_);
146         if (id == NULL) {
147                 afb_req_fail(request, "bad-request", "missing 'id'");
148                 return;
149         }
150         memo = make_memo(request, method);
151         if (asprintf(&sid, "\"%s\"", id) <= 0 || memo == NULL) {
152                 afb_req_fail(request, "server-error", "out of memory");
153                 free(memo);
154                 return;
155         }
156         if (jbus_call_sj(jbus, method, sid, (void*)call_appid_callback, memo) < 0) {
157                 afb_req_fail(request, "failed", "dbus failure");
158                 free(memo);
159         }
160         free(sid);
161 }
162
163 static void call_runid(struct afb_req request, const char *method)
164 {
165         struct json_object *obj;
166         const char *id = afb_req_value(request, _runid_);
167         if (id == NULL) {
168                 afb_req_fail(request, "bad-request", "missing 'runid'");
169                 return;
170         }
171         obj = jbus_call_sj_sync(jbus, method, id);
172         if (interface->verbosity)
173                 fprintf(stderr, "(afm-main-plugin) %s(%s) -> %s\n", method, id,
174                                 obj ? json_object_to_json_string(obj) : "NULL");
175         if (obj == NULL) {
176                 afb_req_fail(request, "failed", "framework daemon failure");
177                 return;
178         }
179         obj = json_object_get(obj);
180         afb_req_success(request, obj, NULL);
181 }
182
183 /************************** entries ******************************/
184
185 static void runnables(struct afb_req request)
186 {
187         embed_call_void(request, _runnables_);
188 }
189
190 static void detail(struct afb_req request)
191 {
192         call_appid(request, _detail_);
193 }
194
195 static void start(struct afb_req request)
196 {
197         struct json_object *obj;
198         const char *id, *mode;
199         char *query;
200         int rc;
201
202         /* get the id */
203         id = afb_req_value(request, _id_);
204         if (id == NULL) {
205                 afb_req_fail(request, "bad-request", "missing 'id'");
206                 return;
207         }
208         /* get the mode */
209         mode = afb_req_value(request, _mode_);
210         if (mode == NULL || !strcmp(mode, _auto_)) {
211                 mode = interface->mode == AFB_MODE_REMOTE ? _remote_ : _local_;
212         }
213
214         /* create the query */
215         rc = asprintf(&query, "{\"id\":\"%s\",\"mode\":\"%s\"}", id, mode);
216         if (rc < 0) {
217                 afb_req_fail(request, "server-error", "out of memory");
218                 return;
219         }
220
221         /* calls the service */
222         obj = jbus_call_sj_sync(jbus, _start_, query);
223         if (interface->verbosity)
224                 fprintf(stderr, "(afm-main-plugin) start(%s) -> %s\n", query,
225                         obj ? json_object_to_json_string(obj) : "NULL");
226         free(query);
227
228         /* check status */
229         obj = json_object_get(obj);
230         if (obj == NULL) {
231                 afb_req_fail(request, "failed", "framework daemon failure");
232                 return;
233         }
234
235         /* embed if needed */
236         if (json_object_get_type(obj) == json_type_int)
237                 obj = embed(_runid_, obj);
238         afb_req_success(request, obj, NULL);
239 }
240
241 static void terminate(struct afb_req request)
242 {
243         call_runid(request, _terminate_);
244 }
245
246 static void stop(struct afb_req request)
247 {
248         call_runid(request, _stop_);
249 }
250
251 static void continue_(struct afb_req request)
252 {
253         call_runid(request, _continue_);
254 }
255
256 static void runners(struct afb_req request)
257 {
258         embed_call_void(request, _runners_);
259 }
260
261 static void state(struct afb_req request)
262 {
263         call_runid(request, _state_);
264 }
265
266 static void install(struct afb_req request)
267 {
268         struct json_object *obj, *added;
269         char *query;
270         const char *filename;
271         struct afb_arg arg;
272
273         /* get the argument */
274         arg = afb_req_get(request, "widget");
275         filename = arg.path;
276         if (filename == NULL) {
277                 afb_req_fail(request, "bad-request", "missing 'widget' file");
278                 return;
279         }
280
281         /* makes the query */
282         if (0 >= asprintf(&query, "\"%s\"", filename)) {
283                 afb_req_fail(request, "server-error", "out of memory");
284                 return;
285         }
286
287         obj = jbus_call_sj_sync(jbus, _install_, query);
288         if (interface->verbosity)
289                 fprintf(stderr, "(afm-main-plugin) install(%s) -> %s\n", query,
290                         obj ? json_object_to_json_string(obj) : "NULL");
291         free(query);
292
293         /* check status */
294         if (obj == NULL) {
295                 afb_req_fail(request, "failed", "framework daemon failure");
296                 return;
297         }
298
299         /* embed if needed */
300         if (json_object_object_get_ex(obj, _added_, &added))
301                 obj = added;
302         obj = json_object_get(obj);
303         obj = embed(_id_, obj);
304         afb_req_success(request, obj, NULL);
305 }
306
307 static void uninstall(struct afb_req request)
308 {
309         call_appid(request, _uninstall_);
310 }
311
312 static const struct AFB_restapi plug_apis[] =
313 {
314         {_runnables_, AFB_SESSION_CHECK, runnables,  "Get list of runnable applications"},
315         {_detail_   , AFB_SESSION_CHECK, detail, "Get the details for one application"},
316         {_start_    , AFB_SESSION_CHECK, start, "Start an application"},
317         {_terminate_, AFB_SESSION_CHECK, terminate, "Terminate a running application"},
318         {_stop_     , AFB_SESSION_CHECK, stop, "Stop (pause) a running application"},
319         {_continue_ , AFB_SESSION_CHECK, continue_, "Continue (resume) a stopped application"},
320         {_runners_  , AFB_SESSION_CHECK, runners,  "Get the list of running applications"},
321         {_state_    , AFB_SESSION_CHECK, state, "Get the state of a running application"},
322         {_install_  , AFB_SESSION_CHECK, install,  "Install an application using a widget file"},
323         {_uninstall_, AFB_SESSION_CHECK, uninstall, "Uninstall an application"},
324         { NULL, 0, NULL, NULL }
325 };
326
327 static const struct AFB_plugin plug_desc = {
328         .type = AFB_PLUGIN_JSON,
329         .info = "Application Framework Master Service",
330         .prefix = "afm-main",
331         .apis = plug_apis
332 };
333
334 static struct sbus_itf sbusitf;
335
336 const struct AFB_plugin *pluginRegister(const struct AFB_interface *itf)
337 {
338         int rc;
339         struct afb_pollmgr pollmgr;
340         struct sbus *sbus;
341
342         /* records the interface */
343         assert (interface == NULL);
344         interface = itf;
345         evmgr = afb_daemon_get_evmgr(itf->daemon);
346
347         /* creates the sbus for session */
348         pollmgr = afb_daemon_get_pollmgr(itf->daemon);
349         sbusitf.wait = pollmgr.itf->wait;
350         sbusitf.open = pollmgr.itf->open;
351         sbusitf.on_readable = pollmgr.itf->on_readable;
352         sbusitf.on_writable = pollmgr.itf->on_writable;
353         sbusitf.on_hangup = pollmgr.itf->on_hangup;
354         sbusitf.close = pollmgr.itf->close;
355         sbus = sbus_session(&sbusitf, pollmgr.closure);
356         if (sbus == NULL) {
357                 fprintf(stderr, "ERROR: %s:%d: can't connect to DBUS session\n", __FILE__, __LINE__);
358                 return NULL;
359         }
360
361         /* creates the jbus for accessing afm-user-daemon */
362         jbus = create_jbus(sbus, "/org/AGL/afm/user");
363         if (jbus == NULL) {
364                 sbus_unref(sbus);
365                 return NULL;
366         }
367
368         /* records the signal handler */
369         rc = jbus_on_signal_s(jbus, _changed_, application_list_changed, NULL);
370         if (rc < 0) {
371                 jbus_unref(jbus);
372                 return NULL;
373         }
374
375         return &plug_desc;
376 }
377