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