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