binding: remove unused string
[src/app-framework-main.git] / src / afm-main-binding.c
1 /*
2  * Copyright (C) 2015, 2016 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-c/json.h>
23
24 #include <afb/afb-binding.h>
25
26 #include "utils-jbus.h"
27
28 static const char _added_[]     = "added";
29 static const char _auto_[]      = "auto";
30 static const char _continue_[]  = "continue";
31 static const char _changed_[]   = "changed";
32 static const char _detail_[]    = "detail";
33 static const char _id_[]        = "id";
34 static const char _install_[]   = "install";
35 static const char _local_[]     = "local";
36 static const char _mode_[]      = "mode";
37 static const char _pause_[]     = "pause";
38 static const char _remote_[]    = "remote";
39 static const char _resume_[]    = "resume";
40 static const char _runid_[]     = "runid";
41 static const char _runnables_[] = "runnables";
42 static const char _runners_[]   = "runners";
43 static const char _start_[]     = "start";
44 static const char _state_[]     = "state";
45 static const char _stop_[]      = "stop";
46 static const char _terminate_[] = "terminate";
47 static const char _uninstall_[] = "uninstall";
48
49 static const struct afb_binding_interface *binder;
50
51 static struct jbus *jbus;
52
53 /*
54  * Structure for asynchronous call handling
55  */
56 struct memo
57 {
58         struct afb_req request; /* the recorded request */
59         const char *method;     /* the called method */
60 };
61
62 /*
63  * Creates the memo for the 'request' and the 'method'.
64  * Returns the memo in case of success.
65  * In case of error, send a failure answer and returns NULL.
66  */
67 static struct memo *memo_create(struct afb_req request, const char *method)
68 {
69         struct memo *memo = malloc(sizeof *memo);
70         if (memo == NULL)
71                 afb_req_fail(request, "failed", "out of memory");
72         else {
73                 memo->request = request;
74                 memo->method = method;
75                 afb_req_addref(request);
76         }
77         return memo;
78 }
79
80 /*
81  * Sends the asynchronous failed reply to the request recorded by 'memo'
82  * Then fress the resources.
83  */
84 static void memo_fail(struct memo *memo, const char *info)
85 {
86         afb_req_fail(memo->request, "failed", info);
87         afb_req_unref(memo->request);
88         free(memo);
89 }
90
91 /*
92  * Sends the asynchronous success reply to the request recorded by 'memo'
93  * Then fress the resources.
94  */
95 static void memo_success(struct memo *memo, struct json_object *obj, const char *info)
96 {
97         afb_req_success(memo->request, obj, info);
98         afb_req_unref(memo->request);
99         free(memo);
100 }
101
102 /*
103  * Broadcast the event "application-list-changed".
104  * This event is sent was the event "changed" is received from dbus.
105  */
106 static void application_list_changed(const char *data, void *closure)
107 {
108         afb_daemon_broadcast_event(binder->daemon, "application-list-changed", NULL);
109 }
110
111 /*
112  * Builds if possible the json object having one field of name 'tag'
113  * whose value is 'obj' like here: { tag: obj } and returns it.
114  * In case of error or when 'tag'==NULL or 'obj'==NULL, 'obj' is returned.
115  * The reference count of 'obj' is not incremented.
116  */
117 static struct json_object *embed(const char *tag, struct json_object *obj)
118 {
119         struct json_object *result;
120
121         if (obj == NULL || tag == NULL)
122                 result = obj;
123         else {
124                 result = json_object_new_object();
125                 if (result == NULL) {
126                         /* can't embed */
127                         result = obj;
128                 }
129                 else {
130                         /* TODO why is json-c not returning a status? */
131                         json_object_object_add(result, tag, obj);
132                 }
133         }
134         return result;
135 }
136
137 /*
138  * Callback for replies made by 'embed_call_void'.
139  */
140 static void embed_call_void_callback(int iserror, struct json_object *obj, struct memo *memo)
141 {
142         DEBUG(binder, "(afm-main-binding) %s(true) -> %s\n", memo->method,
143                         obj ? json_object_to_json_string(obj) : "NULL");
144
145         if (iserror) {
146                 memo_fail(memo, obj ? json_object_get_string(obj) : "framework daemon failure");
147         } else {
148                 memo_success(memo, embed(memo->method, json_object_get(obj)), NULL);
149         }
150 }
151
152 /*
153  * Calls with DBus the 'method' of the user daemon without arguments.
154  */
155 static void embed_call_void(struct afb_req request, const char *method)
156 {
157         struct memo *memo;
158
159         /* store the request */
160         memo = memo_create(request, method);
161         if (memo == NULL)
162                 return;
163
164         if (jbus_call_sj(jbus, method, "true", (void*)embed_call_void_callback, memo) < 0)
165                 memo_fail(memo, "dbus failure");
166 }
167
168 /*
169  * Callback for replies made by 'call_appid' and 'call_runid'.
170  */
171 static void call_xxxid_callback(int iserror, struct json_object *obj, struct memo *memo)
172 {
173         DEBUG(binder, "(afm-main-binding) %s -> %s\n", memo->method, 
174                         obj ? json_object_to_json_string(obj) : "NULL");
175
176         if (iserror) {
177                 memo_fail(memo, obj ? json_object_get_string(obj) : "framework daemon failure");
178         } else {
179                 memo_success(memo, json_object_get(obj), NULL);
180         }
181 }
182
183 /*
184  * Calls with DBus the 'method' of the user daemon with the argument "id".
185  */
186 static void call_appid(struct afb_req request, const char *method)
187 {
188         struct memo *memo;
189         char *sid;
190         const char *id;
191
192         id = afb_req_value(request, _id_);
193         if (id == NULL) {
194                 afb_req_fail(request, "bad-request", "missing 'id'");
195                 return;
196         }
197
198         memo = memo_create(request, method);
199         if (memo == NULL)
200                 return;
201
202         if (asprintf(&sid, "\"%s\"", id) <= 0) {
203                 memo_fail(memo, "out of memory");
204                 return;
205         }
206
207         if (jbus_call_sj(jbus, method, sid, (void*)call_xxxid_callback, memo) < 0)
208                 memo_fail(memo, "dbus failure");
209
210         free(sid);
211 }
212
213 static void call_runid(struct afb_req request, const char *method)
214 {
215         struct memo *memo;
216         const char *id;
217
218         id = afb_req_value(request, _runid_);
219         if (id == NULL) {
220                 afb_req_fail(request, "bad-request", "missing 'runid'");
221                 return;
222         }
223
224         memo = memo_create(request, method);
225         if (memo == NULL)
226                 return;
227
228         if (jbus_call_sj(jbus, method, id, (void*)call_xxxid_callback, memo) < 0)
229                 memo_fail(memo, "dbus failure");
230 }
231
232 /************************** entries ******************************/
233
234 static void runnables(struct afb_req request)
235 {
236         embed_call_void(request, _runnables_);
237 }
238
239 static void detail(struct afb_req request)
240 {
241         call_appid(request, _detail_);
242 }
243
244 static void start_callback(int iserror, struct json_object *obj, struct memo *memo)
245 {
246         DEBUG(binder, "(afm-main-binding) %s -> %s\n", memo->method, 
247                         obj ? json_object_to_json_string(obj) : "NULL");
248
249         if (iserror) {
250                 memo_fail(memo, obj ? json_object_get_string(obj) : "framework daemon failure");
251         } else {
252                 obj = json_object_get(obj);
253                 if (json_object_get_type(obj) == json_type_int)
254                         obj = embed(_runid_, obj);
255                 memo_success(memo, obj, NULL);
256         }
257 }
258
259 static void start(struct afb_req request)
260 {
261         struct memo *memo;
262         const char *id, *mode;
263         char *query;
264         int rc;
265
266         /* get the id */
267         id = afb_req_value(request, _id_);
268         if (id == NULL) {
269                 afb_req_fail(request, "bad-request", "missing 'id'");
270                 return;
271         }
272
273         /* get the mode */
274         mode = afb_req_value(request, _mode_);
275         if (mode == NULL || !strcmp(mode, _auto_)) {
276                 mode = binder->mode == AFB_MODE_REMOTE ? _remote_ : _local_;
277         }
278
279         /* prepares asynchronous request */
280         memo = memo_create(request, _start_);
281         if (memo == NULL)
282                 return;
283
284         /* create the query */
285         rc = asprintf(&query, "{\"id\":\"%s\",\"mode\":\"%s\"}", id, mode);
286         if (rc < 0) {
287                 memo_fail(memo, "out of memory");
288                 return;
289         }
290
291         /* calls the service asynchronously */
292         if (jbus_call_sj(jbus, _start_, query, (void*)start_callback, memo) < 0)
293                 memo_fail(memo, "dbus failure");
294         free(query);
295 }
296
297 static void terminate(struct afb_req request)
298 {
299         call_runid(request, _terminate_);
300 }
301
302 static void pause(struct afb_req request)
303 {
304         call_runid(request, _pause_);
305 }
306
307 static void resume(struct afb_req request)
308 {
309         call_runid(request, _resume_);
310 }
311
312 static void runners(struct afb_req request)
313 {
314         embed_call_void(request, _runners_);
315 }
316
317 static void state(struct afb_req request)
318 {
319         call_runid(request, _state_);
320 }
321
322 static void install_callback(int iserror, struct json_object *obj, struct memo *memo)
323 {
324         struct json_object *added;
325
326         if (iserror) {
327                 memo_fail(memo, obj ? json_object_get_string(obj) : "framework daemon failure");
328         } else {
329                 if (json_object_object_get_ex(obj, _added_, &added))
330                         obj = added;
331                 obj = json_object_get(obj);
332                 obj = embed(_id_, obj);
333                 memo_success(memo, obj, NULL);
334         }
335 }
336 static void install(struct afb_req request)
337 {
338         struct memo *memo;
339         char *query;
340         const char *filename;
341         struct afb_arg arg;
342
343         /* get the argument */
344         arg = afb_req_get(request, "widget");
345         filename = arg.path;
346         if (filename == NULL) {
347                 afb_req_fail(request, "bad-request", "missing 'widget' file");
348                 return;
349         }
350
351         /* prepares asynchronous request */
352         memo = memo_create(request, _install_);
353         if (memo == NULL)
354                 return;
355
356         /* makes the query */
357         if (0 >= asprintf(&query, "\"%s\"", filename)) {
358                 afb_req_fail(request, "server-error", "out of memory");
359                 return;
360         }
361
362         /* calls the service asynchronously */
363         if (jbus_call_sj(jbus, _install_, query, (void*)install_callback, memo) < 0)
364                 memo_fail(memo, "dbus failure");
365         free(query);
366 }
367
368 static void uninstall(struct afb_req request)
369 {
370         call_appid(request, _uninstall_);
371 }
372
373 static const struct afb_verb_desc_v1 verbs[] =
374 {
375         {_runnables_, AFB_SESSION_CHECK, runnables,  "Get list of runnable applications"},
376         {_detail_   , AFB_SESSION_CHECK, detail, "Get the details for one application"},
377         {_start_    , AFB_SESSION_CHECK, start, "Start an application"},
378         {_terminate_, AFB_SESSION_CHECK, terminate, "Terminate a running application"},
379         {_pause_    , AFB_SESSION_CHECK, pause, "Pause a running application"},
380         {_resume_   , AFB_SESSION_CHECK, resume, "Resume a paused application"},
381         {_stop_     , AFB_SESSION_CHECK, pause, "Obsolete since 2016/11/08, use 'pause' instead"},
382         {_continue_ , AFB_SESSION_CHECK, resume, "Obsolete since 2016/11/08, use 'resume' instead"},
383         {_runners_  , AFB_SESSION_CHECK, runners,  "Get the list of running applications"},
384         {_state_    , AFB_SESSION_CHECK, state, "Get the state of a running application"},
385         {_install_  , AFB_SESSION_CHECK, install,  "Install an application using a widget file"},
386         {_uninstall_, AFB_SESSION_CHECK, uninstall, "Uninstall an application"},
387         { NULL, 0, NULL, NULL }
388 };
389
390 static const struct afb_binding plug_desc = {
391         .type = AFB_BINDING_VERSION_1,
392         .v1 = {
393                 .info = "Application Framework Master Service",
394                 .prefix = "afm-main",
395                 .verbs = verbs
396         }
397 };
398
399 const struct afb_binding *afbBindingV1Register(const struct afb_binding_interface *itf)
400 {
401         int rc;
402         struct sd_bus *sbus;
403
404         /* records the interface */
405         assert (binder == NULL);
406         binder = itf;
407
408         /* creates the jbus for accessing afm-user-daemon */
409         sbus = afb_daemon_get_user_bus(itf->daemon);
410         if (sbus == NULL)
411                 return NULL;
412         jbus = create_jbus(sbus, "/org/AGL/afm/user");
413         if (jbus == NULL)
414                 return NULL;
415
416         /* records the signal handler */
417         rc = jbus_on_signal_s(jbus, _changed_, application_list_changed, NULL);
418         if (rc < 0) {
419                 jbus_unref(jbus);
420                 return NULL;
421         }
422
423         return &plug_desc;
424 }
425