afm-launch: comments and improvements
[src/app-framework-main.git] / src / afm-user-daemon.c
1 /*
2  Copyright 2015 IoT.bzh
3
4  author: José Bollo <jose.bollo@iot.bzh>
5
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
9
10      http://www.apache.org/licenses/LICENSE-2.0
11
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.
17 */
18
19 #include <unistd.h>
20 #include <stdio.h>
21 #include <time.h>
22 #include <getopt.h>
23 #include <string.h>
24
25 #include <json.h>
26
27 #include "verbose.h"
28 #include "utils-jbus.h"
29 #include "utils-json.h"
30 #include "afm.h"
31 #include "afm-db.h"
32 #include "afm-launch-mode.h"
33 #include "afm-run.h"
34
35 static const char appname[] = "afm-user-daemon";
36
37 static void usage()
38 {
39         printf(
40                 "usage: %s [-q] [-v] [-m mode] [-r rootdir]... [-a appdir]...\n"
41                 "\n"
42                 "   -a appdir    adds an application directory\n"
43                 "   -r rootdir   adds a root directory of applications\n"
44                 "   -m mode      set default launch mode (local or remote)\n"
45                 "   -d           run as a daemon\n"
46                 "   -q           quiet\n"
47                 "   -v           verbose\n"
48                 "\n",
49                 appname
50         );
51 }
52
53 static struct option options[] = {
54         { "root",        required_argument, NULL, 'r' },
55         { "application", required_argument, NULL, 'a' },
56         { "mode",        required_argument, NULL, 'm' },
57         { "daemon",      no_argument,       NULL, 'd' },
58         { "quiet",       no_argument,       NULL, 'q' },
59         { "verbose",     no_argument,       NULL, 'v' },
60         { "help",        no_argument,       NULL, 'h' },
61         { NULL, 0, NULL, 0 }
62 };
63
64 static struct jbus *jbuses[2];
65 static struct afm_db *afdb;
66
67 const char error_nothing[] = "[]";
68 const char error_bad_request[] = "\"bad request\"";
69 const char error_not_found[] = "\"not found\"";
70 const char error_cant_start[] = "\"can't start\"";
71 const char error_system[] = "\"system error\"";
72
73 static const char *getappid(struct json_object *obj)
74 {
75         return json_type_string == json_object_get_type(obj) ? json_object_get_string(obj) : NULL;
76 }
77
78 static int getrunid(struct json_object *obj)
79 {
80         return json_type_int == json_object_get_type(obj) ? json_object_get_int(obj) : 0;
81 }
82
83 static void reply(struct jreq *jreq, struct json_object *resp, const char *errstr)
84 {
85         if (resp)
86                 jbus_reply_j(jreq, resp);
87         else
88                 jbus_reply_error_s(jreq, errstr);
89 }
90
91 static void reply_status(struct jreq *jreq, int status)
92 {
93         if (status)
94                 jbus_reply_error_s(jreq, error_not_found);
95         else
96                 jbus_reply_s(jreq, "true");
97 }
98
99 static void on_runnables(struct jreq *jreq, struct json_object *obj)
100 {
101         struct json_object *resp;
102         INFO("method runnables called");
103         resp = afm_db_application_list(afdb);
104         jbus_reply_j(jreq, resp);
105         json_object_put(resp);
106 }
107
108 static void on_detail(struct jreq *jreq, struct json_object *obj)
109 {
110         const char *appid;
111         struct json_object *resp;
112         appid = getappid(obj);
113         INFO("method detail called for %s", appid);
114         resp = afm_db_get_application_public(afdb, appid);
115         reply(jreq, resp, error_not_found);
116         json_object_put(resp);
117 }
118
119 static void on_start(struct jreq *jreq, struct json_object *obj)
120 {
121         const char *appid, *modestr;
122         char *uri;
123         struct json_object *appli, *resp;
124         int runid;
125         char runidstr[20];
126         enum afm_launch_mode mode;
127
128         /* get the parameters */
129         mode = invalid_launch_mode;
130         if (j_read_string(obj, &appid)) {
131                 mode = get_default_launch_mode();
132         } else if (j_read_string_at(obj, "id", &appid)) {
133                 if (j_read_string_at(obj, "mode", &modestr)) {
134                         mode = launch_mode_of_name(modestr);
135                 } else {
136                         mode = get_default_launch_mode();
137                 }
138         }
139         if (!is_valid_launch_mode(mode)) {
140                 jbus_reply_error_s(jreq, error_bad_request);
141                 return;
142         }
143
144         /* get the application */
145         INFO("method start called for %s mode=%s", appid, name_of_launch_mode(mode));
146         appli = afm_db_get_application(afdb, appid);
147         if (appli == NULL) {
148                 jbus_reply_error_s(jreq, error_not_found);
149                 return;
150         }
151
152         /* launch the application */
153         uri = NULL;
154         runid = afm_run_start(appli, mode, &uri);
155         if (runid <= 0) {
156                 jbus_reply_error_s(jreq, error_cant_start);
157                 free(uri);
158                 return;
159         }
160
161         if (uri == NULL) {
162                 /* returns only the runid */
163                 snprintf(runidstr, sizeof runidstr, "%d", runid);
164                 runidstr[sizeof runidstr - 1] = 0;
165                 jbus_reply_s(jreq, runidstr);
166                 return;
167         }
168
169         /* returns the runid and its uri */
170         resp = json_object_new_object();
171         if (resp != NULL && j_add_integer(resp, "runid", runid) && j_add_string(resp, "uri", uri))
172                 jbus_reply_j(jreq, resp);
173         else {
174                 afm_run_stop(runid);
175                 jbus_reply_error_s(jreq, error_cant_start);
176         }
177         json_object_put(resp);
178         free(uri);
179 }
180
181 static void on_stop(struct jreq *jreq, struct json_object *obj)
182 {
183         int runid, status;
184         runid = getrunid(obj);
185         INFO("method stop called for %d", runid);
186         status = afm_run_stop(runid);
187         reply_status(jreq, status);
188 }
189
190 static void on_continue(struct jreq *jreq, struct json_object *obj)
191 {
192         int runid, status;
193         runid = getrunid(obj);
194         INFO("method continue called for %d", runid);
195         status = afm_run_continue(runid);
196         reply_status(jreq, status);
197 }
198
199 static void on_terminate(struct jreq *jreq, struct json_object *obj)
200 {
201         int runid, status;
202         runid = getrunid(obj);
203         INFO("method terminate called for %d", runid);
204         status = afm_run_terminate(runid);
205         reply_status(jreq, status);
206 }
207
208 static void on_runners(struct jreq *jreq, struct json_object *obj)
209 {
210         struct json_object *resp;
211         INFO("method runners called");
212         resp = afm_run_list();
213         jbus_reply_j(jreq, resp);
214         json_object_put(resp);
215 }
216
217 static void on_state(struct jreq *jreq, struct json_object *obj)
218 {
219         int runid;
220         struct json_object *resp;
221         runid = getrunid(obj);
222         INFO("method state called for %d", runid);
223         resp = afm_run_state(runid);
224         reply(jreq, resp, error_not_found);
225         json_object_put(resp);
226 }
227
228 static void propagate(struct jreq *jreq, const char *msg, const char *method)
229 {
230         char *reply;
231         INFO("method %s propagated with %s", method, msg);
232         reply = jbus_call_ss_sync(jbuses[0], method, msg);
233         if (reply) {
234                 jbus_reply_s(jreq, reply);
235                 free(reply);
236         }
237         else
238                 jbus_reply_error_s(jreq, error_system);
239 }
240
241 static void on_install(struct jreq *jreq, const char *msg)
242 {
243         return propagate(jreq, msg, "install");
244 }
245
246 static void on_uninstall(struct jreq *jreq, const char *msg)
247 {
248         return propagate(jreq, msg, "uninstall");
249 }
250
251 static void on_signal_changed(struct json_object *obj)
252 {
253         /* update the database */
254         afm_db_update_applications(afdb);
255         /* re-propagate now */
256         jbus_send_signal_j(jbuses[1], "changed", obj);
257 }
258
259 static int daemonize()
260 {
261         int rc = fork();
262         if (rc < 0)
263                 return rc;
264         if (rc)
265                 _exit(0);
266         return 0;
267 }
268
269 int main(int ac, char **av)
270 {
271         int i, daemon = 0;
272         enum afm_launch_mode mode;
273
274         LOGAUTH(appname);
275
276         /* first interpretation of arguments */
277         while ((i = getopt_long(ac, av, "hdqvr:a:m:", options, NULL)) >= 0) {
278                 switch (i) {
279                 case 'h':
280                         usage();
281                         return 0;
282                 case 'q':
283                         if (verbosity)
284                                 verbosity--;
285                         break;
286                 case 'v':
287                         verbosity++;
288                         break;
289                 case 'd':
290                         daemon = 1;
291                         break;
292                 case 'r':
293                         break;
294                 case 'a':
295                         break;
296                 case 'm':
297                         mode = launch_mode_of_name(optarg);
298                         if (!is_valid_launch_mode(mode)) {
299                                 ERROR("invalid mode '%s'", optarg);
300                                 return 1;
301                         }
302                         set_default_launch_mode(mode);
303                         break;
304                 case ':':
305                         ERROR("missing argument value");
306                         return 1;
307                 default:
308                         ERROR("unrecognized option");
309                         return 1;
310                 }
311         }
312
313         /* init random generator */
314         srandom((unsigned int)time(NULL));
315
316         /* init runners */
317         if (afm_run_init()) {
318                 ERROR("afm_run_init failed");
319                 return 1;
320         }
321
322         /* init framework */
323         afdb = afm_db_create();
324         if (!afdb) {
325                 ERROR("afm_create failed");
326                 return 1;
327         }
328         if (afm_db_add_root(afdb, FWK_APP_DIR)) {
329                 ERROR("can't add root %s", FWK_APP_DIR);
330                 return 1;
331         }
332
333         /* second interpretation of arguments */
334         optind = 1;
335         while ((i = getopt_long(ac, av, "hdqvr:a:m:", options, NULL)) >= 0) {
336                 switch (i) {
337                 case 'r':
338                         if (afm_db_add_root(afdb, optarg)) {
339                                 ERROR("can't add root %s", optarg);
340                                 return 1;
341                         }
342                         break;
343                 case 'a':
344                         if (afm_db_add_application(afdb, optarg)) {
345                                 ERROR("can't add application %s", optarg);
346                                 return 1;
347                         }
348                         break;
349                 }
350         }
351
352         /* update the database */
353         if (afm_db_update_applications(afdb)) {
354                 ERROR("afm_update_applications failed");
355                 return 1;
356         }
357
358         if (daemon && daemonize()) {
359                 ERROR("daemonization failed");
360                 return 1;
361         }
362
363         /* init observers */
364         jbuses[0] = create_jbus_system(AFM_SYSTEM_DBUS_PATH);
365         if (!jbuses[0]) {
366                 ERROR("create_jbus failed for system");
367                 return 1;
368         }
369         if(jbus_on_signal_j(jbuses[0], "changed", on_signal_changed)) {
370                 ERROR("adding signal observer failed");
371                 return 1;
372         }
373
374         /* init service */
375         jbuses[1] = create_jbus_session(AFM_USER_DBUS_PATH);
376         if (!jbuses[1]) {
377                 ERROR("create_jbus failed");
378                 return 1;
379         }
380         if(jbus_add_service_j(jbuses[1], "runnables", on_runnables)
381         || jbus_add_service_j(jbuses[1], "detail", on_detail)
382         || jbus_add_service_j(jbuses[1], "start", on_start)
383         || jbus_add_service_j(jbuses[1], "terminate", on_terminate)
384         || jbus_add_service_j(jbuses[1], "stop", on_stop)
385         || jbus_add_service_j(jbuses[1], "continue", on_continue)
386         || jbus_add_service_j(jbuses[1], "runners", on_runners)
387         || jbus_add_service_j(jbuses[1], "state", on_state)
388         || jbus_add_service_s(jbuses[1], "install", on_install)
389         || jbus_add_service_s(jbuses[1], "uninstall", on_uninstall)) {
390                 ERROR("adding services failed");
391                 return 1;
392         }
393
394         /* start and run */
395         if (jbus_start_serving(jbuses[1])) {
396                 ERROR("can't start server");
397                 return 1;
398         }
399         while (jbus_read_write_dispatch_multiple(jbuses, 2, -1, 20) >= 0);
400         return 0;
401 }
402