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