Remove legacy mode without systemd
[src/app-framework-main.git] / src / afm-user-daemon.c
1 /*
2  Copyright 2015, 2016, 2017 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 <systemd/sd-bus.h>
26 #include <systemd/sd-event.h>
27 #include <json-c/json.h>
28
29 #include "verbose.h"
30 #include "utils-jbus.h"
31 #include "utils-json.h"
32 #include "utils-systemd.h"
33 #include "afm.h"
34 #include "afm-udb.h"
35 #include "afm-urun.h"
36
37 /*
38  * name of the application
39  */
40 static const char appname[] = "afm-user-daemon";
41
42 /*
43  * string for printing version
44  */
45 static const char versionstr[] =
46         "\n"
47         "  %s  version="AFM_VERSION"\n"
48         "\n"
49         "  Copyright (C) 2015, 2016, 2017 \"IoT.bzh\"\n"
50         "  AFB comes with ABSOLUTELY NO WARRANTY.\n"
51         "  Licence Apache 2\n"
52         "\n";
53
54 /*
55  * string for printing usage
56  */
57 static const char usagestr[] =
58         "usage: %s [option(s)]\n"
59         "\n"
60         "   -d           run as a daemon\n"
61         "   -u addr      address of user D-Bus to use\n"
62         "   -s addr      address of system D-Bus to use\n"
63         "   -q           quiet\n"
64         "   -v           verbose\n"
65         "   -V           version\n"
66         "\n";
67
68 /*
69  * Option definition for getopt_long
70  */
71 static const char options_s[] = "hdqvV";
72 static struct option options_l[] = {
73         { "user-dbus",   required_argument, NULL, 'u' },
74         { "system-dbus", required_argument, NULL, 's' },
75         { "daemon",      no_argument,       NULL, 'd' },
76         { "quiet",       no_argument,       NULL, 'q' },
77         { "verbose",     no_argument,       NULL, 'v' },
78         { "help",        no_argument,       NULL, 'h' },
79         { "version",     no_argument,       NULL, 'V' },
80         { NULL, 0, NULL, 0 }
81 };
82
83 /*
84  * Connections to D-Bus
85  * This is an array for using the function
86  *    jbus_read_write_dispatch_multiple
87  * directly without transformations.
88  */
89 static struct jbus *jbuses[2];
90 #define system_bus  jbuses[0]
91 #define user_bus    jbuses[1]
92
93 /*
94  * Handle to the database of applications
95  */
96 static struct afm_udb *afudb;
97
98 /*
99  * Returned error strings
100  */
101 const char error_nothing[] = "[]";
102 const char error_bad_request[] = "\"bad request\"";
103 const char error_not_found[] = "\"not found\"";
104 const char error_cant_start[] = "\"can't start\"";
105 const char error_system[] = "\"system error\"";
106
107
108 /*
109  * retrieves the 'runid' in 'obj' parameters received with the
110  * request 'smsg' for the 'method'.
111  *
112  * Returns 1 in case of success.
113  * Otherwise, if the 'runid' can't be retrived, an error stating
114  * the bad request is replied for 'smsg' and 0 is returned.
115  */
116 static int onrunid(struct sd_bus_message *smsg, struct json_object *obj,
117                                                 const char *method, int *runid)
118 {
119         if (!j_read_integer(obj, runid)
120                                 && !j_read_integer_at(obj, "runid", runid)) {
121                 INFO("bad request method %s: %s", method,
122                                         json_object_to_json_string(obj));
123                 jbus_reply_error_s(smsg, error_bad_request);
124                 return 0;
125         }
126
127         INFO("method %s called for %d", method, *runid);
128         return 1;
129 }
130
131 /*
132  * Sends the reply 'resp' to the request 'smsg' if 'resp' is not NULL.
133  * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
134  */
135 static void reply(struct sd_bus_message *smsg, struct json_object *resp,
136                                                 const char *errstr)
137 {
138         if (resp)
139                 jbus_reply_j(smsg, resp);
140         else
141                 jbus_reply_error_s(smsg, errstr);
142 }
143
144 /*
145  * Sends the reply "true" to the request 'smsg' if 'status' is zero.
146  * Otherwise, when 'status' is not zero replies the error string 'errstr'.
147  */
148 static void reply_status(struct sd_bus_message *smsg, int status, const char *errstr)
149 {
150         if (status)
151                 jbus_reply_error_s(smsg, errstr);
152         else
153                 jbus_reply_s(smsg, "true");
154 }
155
156 /*
157  * On query "runnables" from 'smsg' with parameters of 'obj'.
158  *
159  * Nothing is expected in 'obj' that can be anything.
160  */
161 static void on_runnables(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
162 {
163         struct json_object *resp;
164         INFO("method runnables called");
165         resp = afm_udb_applications_public(afudb);
166         jbus_reply_j(smsg, resp);
167         json_object_put(resp);
168 }
169
170 /*
171  * On query "detail" from 'smsg' with parameters of 'obj'.
172  */
173 static void on_detail(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
174 {
175         const char *appid;
176         struct json_object *resp;
177
178         /* get the parameters */
179         if (j_read_string(obj, &appid))
180                 ; /* appid as a string */
181         else if (j_read_string_at(obj, "id", &appid))
182                 ; /* appid as obj.id string */
183         else {
184                 INFO("method detail called but bad request!");
185                 jbus_reply_error_s(smsg, error_bad_request);
186                 return;
187         }
188
189         /* wants details for appid */
190         INFO("method detail called for %s", appid);
191         resp = afm_udb_get_application_public(afudb, appid);
192         reply(smsg, resp, error_not_found);
193         json_object_put(resp);
194 }
195
196 /*
197  * On query "start" from 'smsg' with parameters of 'obj'.
198  */
199 static void on_start(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
200 {
201         const char *appid;
202         char *uri;
203         struct json_object *appli, *resp;
204         int runid;
205         char runidstr[20];
206
207         /* get the parameters */
208         if (!j_read_string(obj, &appid)) {
209                 if (!j_read_string_at(obj, "id", &appid)) {
210                         jbus_reply_error_s(smsg, error_bad_request);
211                         return;
212                 }
213         }
214
215         /* get the application */
216         INFO("method start called for %s", appid);
217         appli = afm_udb_get_application_private(afudb, appid);
218         if (appli == NULL) {
219                 jbus_reply_error_s(smsg, error_not_found);
220                 return;
221         }
222
223         /* launch the application */
224         uri = NULL;
225         runid = afm_urun_start(appli);
226         if (runid <= 0) {
227                 jbus_reply_error_s(smsg, error_cant_start);
228                 free(uri);
229                 return;
230         }
231
232         if (uri == NULL) {
233                 /* returns only the runid */
234                 snprintf(runidstr, sizeof runidstr, "%d", runid);
235                 runidstr[sizeof runidstr - 1] = 0;
236                 jbus_reply_s(smsg, runidstr);
237                 return;
238         }
239
240         /* returns the runid and its uri */
241         resp = json_object_new_object();
242         if (resp != NULL && j_add_integer(resp, "runid", runid)
243                                         && j_add_string(resp, "uri", uri))
244                 jbus_reply_j(smsg, resp);
245         else {
246                 afm_urun_terminate(runid);
247                 jbus_reply_error_s(smsg, error_system);
248         }
249         json_object_put(resp);
250         free(uri);
251 }
252
253 /*
254  * On query "once" from 'smsg' with parameters of 'obj'.
255  */
256 static void on_once(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
257 {
258         const char *appid;
259         struct json_object *appli, *resp;
260         int runid;
261
262         /* get the parameters */
263         if (!j_read_string(obj, &appid) && !j_read_string_at(obj, "id", &appid)) {
264                 jbus_reply_error_s(smsg, error_bad_request);
265                 return;
266         }
267
268         /* get the application */
269         INFO("method once called for %s", appid);
270         appli = afm_udb_get_application_private(afudb, appid);
271         if (appli == NULL) {
272                 jbus_reply_error_s(smsg, error_not_found);
273                 return;
274         }
275
276         /* launch the application */
277         runid = afm_urun_once(appli);
278         if (runid <= 0) {
279                 jbus_reply_error_s(smsg, error_cant_start);
280                 return;
281         }
282
283         /* returns the state */
284         resp = afm_urun_state(afudb, runid);
285         reply(smsg, resp, error_not_found);
286         json_object_put(resp);
287 }
288
289 /*
290  * On query "pause" from 'smsg' with parameters of 'obj'.
291  */
292 static void on_pause(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
293 {
294         int runid, status;
295         if (onrunid(smsg, obj, "pause", &runid)) {
296                 status = afm_urun_pause(runid);
297                 reply_status(smsg, status, error_not_found);
298         }
299 }
300
301 /*
302  * On query "resume" from 'smsg' with parameters of 'obj'.
303  */
304 static void on_resume(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
305 {
306         int runid, status;
307         if (onrunid(smsg, obj, "resume", &runid)) {
308                 status = afm_urun_resume(runid);
309                 reply_status(smsg, status, error_not_found);
310         }
311 }
312
313 /*
314  * On query "stop" from 'smsg' with parameters of 'obj'.
315  */
316 static void on_stop(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
317 {
318         NOTICE("call to obsolete 'stop'");
319         on_pause(smsg, obj, unused);
320 }
321
322 /*
323  * On query "continue" from 'smsg' with parameters of 'obj'.
324  */
325 static void on_continue(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
326 {
327         NOTICE("call to obsolete 'continue'");
328         on_resume(smsg, obj, unused);
329 }
330
331 /*
332  * On query "terminate" from 'smsg' with parameters of 'obj'.
333  */
334 static void on_terminate(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
335 {
336         int runid, status;
337         if (onrunid(smsg, obj, "terminate", &runid)) {
338                 status = afm_urun_terminate(runid);
339                 reply_status(smsg, status, error_not_found);
340         }
341 }
342
343 /*
344  * On query "runners" from 'smsg' with parameters of 'obj'.
345  */
346 static void on_runners(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
347 {
348         struct json_object *resp;
349         INFO("method runners called");
350         resp = afm_urun_list(afudb);
351         jbus_reply_j(smsg, resp);
352         json_object_put(resp);
353 }
354
355 /*
356  * On query "state" from 'smsg' with parameters of 'obj'.
357  */
358 static void on_state(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
359 {
360         int runid;
361         struct json_object *resp;
362         if (onrunid(smsg, obj, "state", &runid)) {
363                 resp = afm_urun_state(afudb, runid);
364                 reply(smsg, resp, error_not_found);
365                 json_object_put(resp);
366         }
367 }
368
369 /*
370  * Calls the system daemon to achieve application management of
371  * the 'method' gotten from 'smsg' with the parameter's string 'msg'.
372  *
373  * The principle is very simple: call the corresponding system method
374  * and reply its response to the caller.
375  *
376  * The request and reply is synchronous and is blocking.
377  * It is possible to implment it in an asynchrounous way but it
378  * would brake the common behaviour. It would be a call like
379  * jbus_call_ss(system_bus, method, msg, callback, smsg)
380  */
381 static void propagate(struct sd_bus_message *smsg, const char *msg, const char *method)
382 {
383         char *reply;
384         INFO("method %s propagated with %s", method, msg);
385         reply = jbus_call_ss_sync(system_bus, method, msg);
386         if (reply) {
387                 jbus_reply_s(smsg, reply);
388                 free(reply);
389         }
390         else
391                 jbus_reply_error_s(smsg, error_system);
392 }
393
394 #if defined(EXPLICIT_CALL)
395 /*
396  * On query "install" from 'smsg' with parameters of 'msg'.
397  */
398 static void on_install(struct sd_bus_message *smsg, const char *msg, void *unused)
399 {
400         return propagate(smsg, msg, "install");
401 }
402
403 /*
404  * On query "uninstall" from 'smsg' with parameters of 'msg'.
405  */
406 static void on_uninstall(struct sd_bus_message *smsg, const char *msg, void *unused)
407 {
408         return propagate(smsg, msg, "uninstall");
409 }
410 #endif
411
412 /*
413  * On system signaling that applications list changed
414  */
415 static void on_signal_changed(struct json_object *obj, void *unused)
416 {
417         /* enforce daemon reload */
418         systemd_daemon_reload(1);
419         systemd_unit_restart_name(1, "sockets.target");
420
421         /* update the database */
422         afm_udb_update(afudb);
423
424         /* re-propagate now */
425         jbus_send_signal_j(user_bus, "changed", obj);
426 }
427
428 /*
429  * Tiny routine to daemonize the program
430  * Return 0 on success or -1 on failure.
431  */
432 static int daemonize()
433 {
434         int rc = fork();
435         if (rc < 0)
436                 return rc;
437         if (rc)
438                 _exit(0);
439         return 0;
440 }
441
442 /*
443  * Opens a sd-bus connection and returns it in 'ret'.
444  * The sd-bus connexion is intended to be for user if 'isuser'
445  * is not null. The adress is the default address when 'address'
446  * is NULL or, otherwise, the given address.
447  * It might be necessary to pass the address as an argument because
448  * library systemd uses secure_getenv to retrieves the default
449  * addresses and secure_getenv might return NULL in some cases.
450  */
451 static int open_bus(sd_bus **ret, int isuser, const char *address)
452 {
453         sd_bus *b;
454         int rc;
455
456         if (address == NULL)
457                 return (isuser ? sd_bus_default_user : sd_bus_default_system)(ret);
458
459         rc = sd_bus_new(&b);
460         if (rc < 0)
461                 return rc;
462
463         rc = sd_bus_set_address(b, address);
464         if (rc < 0)
465                 goto fail;
466
467         sd_bus_set_bus_client(b, 1);
468
469         rc = sd_bus_start(b);
470         if (rc < 0)
471                 goto fail;
472
473         *ret = b;
474         return 0;
475
476 fail:
477         sd_bus_unref(b);
478         return rc;
479 }
480
481 /*
482  * ENTRY POINT OF AFM-USER-DAEMON
483  */
484 int main(int ac, char **av)
485 {
486         int i, daemon = 0, rc;
487         struct sd_event *evloop;
488         struct sd_bus *sysbus, *usrbus;
489         const char *sys_bus_addr, *usr_bus_addr;
490
491         LOGAUTH(appname);
492
493         /* first interpretation of arguments */
494         sys_bus_addr = NULL;
495         usr_bus_addr = NULL;
496         while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
497                 switch (i) {
498                 case 'h':
499                         printf(usagestr, appname);
500                         return 0;
501                 case 'V':
502                         printf(versionstr, appname);
503                         return 0;
504                 case 'q':
505                         if (verbosity)
506                                 verbosity--;
507                         break;
508                 case 'v':
509                         verbosity++;
510                         break;
511                 case 'd':
512                         daemon = 1;
513                         break;
514                 case 'u':
515                         usr_bus_addr = optarg;
516                         break;
517                 case 's':
518                         sys_bus_addr = optarg;
519                         break;
520                 case ':':
521                         ERROR("missing argument value");
522                         return 1;
523                 default:
524                         ERROR("unrecognized option");
525                         return 1;
526                 }
527         }
528
529         /* init random generator */
530         srandom((unsigned int)time(NULL));
531
532         /* init database */
533         afudb = afm_udb_create(0, 1, "afm-appli-");
534         if (!afudb) {
535                 ERROR("afm_udb_create failed");
536                 return 1;
537         }
538
539         /* daemonize if requested */
540         if (daemon && daemonize()) {
541                 ERROR("daemonization failed");
542                 return 1;
543         }
544
545         /* get systemd objects */
546         rc = sd_event_new(&evloop);
547         if (rc < 0) {
548                 ERROR("can't create event loop");
549                 return 1;
550         }
551         rc = open_bus(&sysbus, 0, sys_bus_addr);
552         if (rc < 0) {
553                 ERROR("can't create system bus");
554                 return 1;
555         }
556         rc = sd_bus_attach_event(sysbus, evloop, 0);
557         if (rc < 0) {
558                 ERROR("can't attach system bus to event loop");
559                 return 1;
560         }
561         rc = open_bus(&usrbus, 1, usr_bus_addr);
562         if (rc < 0) {
563                 ERROR("can't create user bus");
564                 return 1;
565         }
566         rc = sd_bus_attach_event(usrbus, evloop, 0);
567         if (rc < 0) {
568                 ERROR("can't attach user bus to event loop");
569                 return 1;
570         }
571
572         /* connects to the system bus */
573         system_bus = create_jbus(sysbus, AFM_SYSTEM_DBUS_PATH);
574         if (!system_bus) {
575                 ERROR("create_jbus failed for system");
576                 return 1;
577         }
578
579         /* observe signals of system */
580         if(jbus_on_signal_j(system_bus, "changed", on_signal_changed, NULL)) {
581                 ERROR("adding signal observer failed");
582                 return 1;
583         }
584
585         /* connect to the session bus */
586         user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
587         if (!user_bus) {
588                 ERROR("create_jbus failed");
589                 return 1;
590         }
591
592         /* init services */
593         if (jbus_add_service_j(user_bus, "runnables", on_runnables, NULL)
594          || jbus_add_service_j(user_bus, "detail",    on_detail, NULL)
595          || jbus_add_service_j(user_bus, "start",     on_start, NULL)
596          || jbus_add_service_j(user_bus, "once",      on_once, NULL)
597          || jbus_add_service_j(user_bus, "terminate", on_terminate, NULL)
598          || jbus_add_service_j(user_bus, "pause",     on_pause, NULL)
599          || jbus_add_service_j(user_bus, "resume",    on_resume, NULL)
600          || jbus_add_service_j(user_bus, "stop",      on_stop, NULL)
601          || jbus_add_service_j(user_bus, "continue",  on_continue, NULL)
602          || jbus_add_service_j(user_bus, "runners",   on_runners, NULL)
603          || jbus_add_service_j(user_bus, "state",     on_state, NULL)
604 #if defined(EXPLICIT_CALL)
605          || jbus_add_service_s(user_bus, "install",   on_install, NULL)
606          || jbus_add_service_s(user_bus, "uninstall", on_uninstall, NULL)
607 #else
608          || jbus_add_service_s(user_bus, "install",   (void (*)(struct sd_bus_message *, const char *, void *))propagate, "install")
609          || jbus_add_service_s(user_bus, "uninstall", (void (*)(struct sd_bus_message *, const char *, void *))propagate, "uninstall")
610 #endif
611          ) {
612                 ERROR("adding services failed");
613                 return 1;
614         }
615
616         /* start servicing */
617         if (jbus_start_serving(user_bus) < 0) {
618                 ERROR("can't start server");
619                 return 1;
620         }
621
622         /* run until error */
623         for(;;)
624                 sd_event_run(evloop, (uint64_t)-1);
625         return 0;
626 }
627