utils-jbus: implments multiple bus handling
[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
24 #include <json.h>
25
26 #include "verbose.h"
27 #include "utils-jbus.h"
28 #include "afm.h"
29 #include "afm-db.h"
30 #include "afm-run.h"
31
32 static const char appname[] = "afm-user-daemon";
33
34 static void usage()
35 {
36         printf(
37                 "usage: %s [-q] [-v] [-r rootdir]... [-a appdir]...\n"
38                 "\n"
39                 "   -a appdir    adds an application directory\n"
40                 "   -r rootdir   adds a root directory of applications\n"
41                 "   -d           run as a daemon\n"
42                 "   -q           quiet\n"
43                 "   -v           verbose\n"
44                 "\n",
45                 appname
46         );
47 }
48
49 static struct option options[] = {
50         { "root",        required_argument, NULL, 'r' },
51         { "application", required_argument, NULL, 'a' },
52         { "daemon",      no_argument,       NULL, 'd' },
53         { "quiet",       no_argument,       NULL, 'q' },
54         { "verbose",     no_argument,       NULL, 'v' },
55         { "help",        no_argument,       NULL, 'h' },
56         { NULL, 0, NULL, 0 }
57 };
58
59 static struct jbus *jbuses[2];
60 static struct afm_db *afdb;
61
62 const char error_nothing[] = "[]";
63 const char error_bad_request[] = "\"bad request\"";
64 const char error_not_found[] = "\"not found\"";
65 const char error_cant_start[] = "\"can't start\"";
66
67 static const char *getappid(struct json_object *obj)
68 {
69         return json_type_string == json_object_get_type(obj) ? json_object_get_string(obj) : NULL;
70 }
71
72 static int getrunid(struct json_object *obj)
73 {
74         return json_type_int == json_object_get_type(obj) ? json_object_get_int(obj) : 0;
75 }
76
77 static void reply(struct jreq *jreq, struct json_object *resp, const char *errstr)
78 {
79         if (resp)
80                 jbus_reply_j(jreq, resp);
81         else
82                 jbus_reply_error_s(jreq, errstr);
83 }
84
85 static void reply_status(struct jreq *jreq, int status)
86 {
87         if (status)
88                 jbus_reply_error_s(jreq, error_not_found);
89         else
90                 jbus_reply_s(jreq, "true");
91 }
92
93 static void on_runnables(struct jreq *jreq, struct json_object *obj)
94 {
95         struct json_object *resp = afm_db_application_list(afdb);
96         jbus_reply_j(jreq, resp);
97         json_object_put(resp);
98 }
99
100 static void on_detail(struct jreq *jreq, struct json_object *obj)
101 {
102         const char *appid = getappid(obj);
103         struct json_object *resp = afm_db_get_application_public(afdb, appid);
104         reply(jreq, resp, error_not_found);
105         json_object_put(resp);
106 }
107
108 static void on_start(struct jreq *jreq, struct json_object *obj)
109 {
110         const char *appid;
111         struct json_object *appli;
112         int runid;
113         char runidstr[20];
114
115         appid = getappid(obj);
116         if (appid == NULL)
117                 jbus_reply_error_s(jreq, error_bad_request);
118         else {
119                 appli = afm_db_get_application(afdb, appid);
120                 if (appli == NULL)
121                         jbus_reply_error_s(jreq, error_not_found);
122                 else {
123                         runid = afm_run_start(appli);
124                         if (runid <= 0)
125                                 jbus_reply_error_s(jreq, error_cant_start);
126                         else {
127                                 snprintf(runidstr, sizeof runidstr, "%d", runid);
128                                 runidstr[sizeof runidstr - 1] = 0;
129                                 jbus_reply_s(jreq, runidstr);
130                         }
131                 }
132         }
133 }
134
135 static void on_stop(struct jreq *jreq, struct json_object *obj)
136 {
137         int runid = getrunid(obj);
138         int status = afm_run_stop(runid);
139         reply_status(jreq, status);
140 }
141
142 static void on_continue(struct jreq *jreq, struct json_object *obj)
143 {
144         int runid = getrunid(obj);
145         int status = afm_run_continue(runid);
146         reply_status(jreq, status);
147 }
148
149 static void on_terminate(struct jreq *jreq, struct json_object *obj)
150 {
151         int runid = getrunid(obj);
152         int status = afm_run_terminate(runid);
153         reply_status(jreq, status);
154 }
155
156 static void on_runners(struct jreq *jreq, struct json_object *obj)
157 {
158         struct json_object *resp = afm_run_list();
159         jbus_reply_j(jreq, resp);
160         json_object_put(resp);
161 }
162
163 static void on_state(struct jreq *jreq, struct json_object *obj)
164 {
165         int runid = getrunid(obj);
166         struct json_object *resp = afm_run_state(runid);
167         reply(jreq, resp, error_not_found);
168         json_object_put(resp);
169 }
170
171 static void on_signal_changed(struct json_object *obj)
172 {
173         /* update the database */
174         afm_db_update_applications(afdb);
175         /* propagate now */
176         jbus_send_signal_j(jbuses[1], "changed", obj);
177 }
178
179 static int daemonize()
180 {
181         int rc = fork();
182         if (rc < 0)
183                 return rc;
184         if (rc)
185                 _exit(0);
186         return 0;
187 }
188
189 int main(int ac, char **av)
190 {
191         int i, daemon = 0;
192
193         LOGAUTH(appname);
194
195         /* first interpretation of arguments */
196         while ((i = getopt_long(ac, av, "hdqvr:a:", options, NULL)) >= 0) {
197                 switch (i) {
198                 case 'h':
199                         usage();
200                         return 0;
201                 case 'q':
202                         if (verbosity)
203                                 verbosity--;
204                         break;
205                 case 'v':
206                         verbosity++;
207                         break;
208                 case 'd':
209                         daemon = 1;
210                         break;
211                 case 'r':
212                         break;
213                 case 'a':
214                         break;
215                 case ':':
216                         ERROR("missing argument value");
217                         return 1;
218                 default:
219                         ERROR("unrecognized option");
220                         return 1;
221                 }
222         }
223
224         /* init random generator */
225         srandom((unsigned int)time(NULL));
226
227         /* init runners */
228         if (afm_run_init()) {
229                 ERROR("afm_run_init failed");
230                 return 1;
231         }
232
233         /* init framework */
234         afdb = afm_db_create();
235         if (!afdb) {
236                 ERROR("afm_create failed");
237                 return 1;
238         }
239         if (afm_db_add_root(afdb, FWK_APP_DIR)) {
240                 ERROR("can't add root %s", FWK_APP_DIR);
241                 return 1;
242         }
243
244         /* second interpretation of arguments */
245         optind = 1;
246         while ((i = getopt_long(ac, av, "hdqvr:a:", options, NULL)) >= 0) {
247                 switch (i) {
248                 case 'r':
249                         if (afm_db_add_root(afdb, optarg)) {
250                                 ERROR("can't add root %s", optarg);
251                                 return 1;
252                         }
253                         break;
254                 case 'a':
255                         if (afm_db_add_application(afdb, optarg)) {
256                                 ERROR("can't add application %s", optarg);
257                                 return 1;
258                         }
259                         break;
260                 }
261         }
262
263         /* update the database */
264         if (afm_db_update_applications(afdb)) {
265                 ERROR("afm_update_applications failed");
266                 return 1;
267         }
268
269         if (daemon && daemonize()) {
270                 ERROR("daemonization failed");
271                 return 1;
272         }
273
274         /* init observers */
275         jbuses[0] = create_jbus(0, AFM_SYSTEM_DBUS_PATH);
276         if (!jbuses[0]) {
277                 ERROR("create_jbus failed for system");
278                 return 1;
279         }
280         if(jbus_on_signal_j(jbuses[0], "changed", on_signal_changed)) {
281                 ERROR("adding signal observer failed");
282                 return 1;
283         }
284
285         /* init service */
286         jbuses[1] = create_jbus(1, AFM_USER_DBUS_PATH);
287         if (!jbuses[1]) {
288                 ERROR("create_jbus failed");
289                 return 1;
290         }
291         if(jbus_add_service_j(jbuses[1], "runnables", on_runnables)
292         || jbus_add_service_j(jbuses[1], "detail", on_detail)
293         || jbus_add_service_j(jbuses[1], "start", on_start)
294         || jbus_add_service_j(jbuses[1], "terminate", on_terminate)
295         || jbus_add_service_j(jbuses[1], "stop", on_stop)
296         || jbus_add_service_j(jbuses[1], "continue", on_continue)
297         || jbus_add_service_j(jbuses[1], "runners", on_runners)
298         || jbus_add_service_j(jbuses[1], "state", on_state)) {
299                 ERROR("adding services failed");
300                 return 1;
301         }
302
303         /* start and run */
304         if (jbus_start_serving(jbuses[1])) {
305                 ERROR("can't start server");
306                 return 1;
307         }
308         while (jbus_read_write_dispatch_multiple(jbuses, 2, -1, 20) >= 0);
309         return 0;
310 }
311