afm-user-daemon: Update to future version of app-framework-binder
[src/app-framework-main.git] / src / afm-user-daemon.c
1 /*
2  Copyright (C) 2015-2018 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 #define _GNU_SOURCE
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <time.h>
23 #include <getopt.h>
24 #include <string.h>
25
26 #include <systemd/sd-bus.h>
27 #include <systemd/sd-event.h>
28 #include <json-c/json.h>
29
30 #include <afb/afb-ws-client.h>
31 #include <afb/afb-proto-ws.h>
32
33 #include "verbose.h"
34 #include "utils-jbus.h"
35 #include "utils-json.h"
36
37 #define AFM_USER_DBUS_PATH      "/org/AGL/afm/user"
38
39 /*
40  * name of the application
41  */
42 static const char appname[] = "afm-user-daemon";
43
44 /*
45  * string for printing version
46  */
47 static const char versionstr[] =
48         "\n"
49         "  %s  version="AFM_VERSION"\n"
50         "\n"
51         "  Copyright (C) 2015, 2016, 2017 \"IoT.bzh\"\n"
52         "  AFB comes with ABSOLUTELY NO WARRANTY.\n"
53         "  Licence Apache 2\n"
54         "\n";
55
56 /*
57  * string for printing usage
58  */
59 static const char usagestr[] =
60         "usage: %s [option(s)] afm-main-uri\n"
61         "\n"
62         "   -d           run as a daemon\n"
63         "   -u addr      address of user D-Bus to use\n"
64         "   -q           quiet\n"
65         "   -v           verbose\n"
66         "   -V           version\n"
67         "\n";
68
69 /*
70  * Option definition for getopt_long
71  */
72 static const char options_s[] = "hdqvVu:";
73 static struct option options_l[] = {
74         { "user-dbus",   required_argument, NULL, 'u' },
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  * The methods propagated
85  */
86 static const char *methods[] = {
87         "runnables",
88         "detail",
89         "start",
90         "once",
91         "terminate",
92         "pause",
93         "resume",
94         "stop",
95         "continue",
96         "runners",
97         "state",
98         "install",
99         "uninstall",
100         NULL
101 };
102
103 /*
104  * Connections
105  */
106 static struct sd_event *evloop;
107 static struct jbus *user_bus;
108 static struct afb_proto_ws *pws;
109 static char *sessionid;
110 static const char *uri;
111
112 /*
113  * 
114  */
115 static void on_pws_hangup(void *closure);
116 static void on_pws_reply(void *closure, void *request, struct json_object *obj, const char *error, const char *info);
117 #if !defined(AFB_PROTO_WS_VERSION) || (AFB_PROTO_WS_VERSION < 3)
118 static void on_pws_reply_success(void *closure, void *request, struct json_object *result, const char *info)
119         { on_pws_reply(closure, request, result, NULL, info); }
120 static void on_pws_reply_fail(void *closure, void *request, const char *error, const char *info)
121         { on_pws_reply(closure, request, NULL, error, info); }
122 #endif
123 static void on_pws_event_broadcast(void *closure, const char *event_name, struct json_object *data);
124
125 /* the callback interface for pws */
126 static struct afb_proto_ws_client_itf pws_itf = {
127 #if !defined(AFB_PROTO_WS_VERSION) || (AFB_PROTO_WS_VERSION < 3)
128         .on_reply_success = on_pws_reply_success,
129         .on_reply_fail = on_pws_reply_fail,
130 #else
131         .on_reply = on_pws_reply,
132 #endif
133         .on_event_broadcast = on_pws_event_broadcast,
134 };
135
136 static int try_connect_pws()
137 {
138         pws = afb_ws_client_connect_api(evloop, uri, &pws_itf, NULL);
139         if (pws == NULL) {
140                 fprintf(stderr, "connection to %s failed: %m\n", uri);
141                 return 0;
142         }
143         afb_proto_ws_on_hangup(pws, on_pws_hangup);
144         return 1;
145 }
146
147 static void attempt_connect_pws(int count);
148
149 static int timehand(sd_event_source *s, uint64_t usec, void *userdata)
150 {
151         sd_event_source_unref(s);
152         attempt_connect_pws((int)(intptr_t)userdata);
153         return 0;
154 }
155
156 static void attempt_connect_pws(int count)
157 {
158         sd_event_source *s;
159         if (!try_connect_pws()) {
160                 if (--count <= 0) {
161                         ERROR("Definitely disconnected");
162                         exit(1);
163                 }
164                 sd_event_add_time(evloop, &s, CLOCK_MONOTONIC, 5000000, 0, timehand, (void*)(intptr_t)count);
165         }
166 }
167
168 static void on_pws_reply(void *closure, void *request, struct json_object *obj, const char *error, const char *info)
169 {
170         struct sd_bus_message *smsg = request;
171         if (error)
172                 jbus_reply_error_s(smsg, error);
173         else
174                 jbus_reply_j(smsg, obj);
175 }
176
177 static void on_pws_event_broadcast(void *closure, const char *event_name, struct json_object *data)
178 {
179         jbus_send_signal_j(user_bus, "changed", data);
180 }
181
182 /* called when pws hangsup */
183 static void on_pws_hangup(void *closure)
184 {
185         struct afb_proto_ws *apw = pws;
186         pws = NULL;
187         afb_proto_ws_unref(apw);
188         attempt_connect_pws(10);
189 }
190
191 /* propagate the call to the service */
192 static void propagate(struct sd_bus_message *smsg, struct json_object *obj, void *closure)
193 {
194         int rc;
195         const char *verb = closure;
196         const char *onbehalf = NULL; /* TODO: on behalf of the client */
197
198         INFO("method %s propagated for %s", verb, json_object_to_json_string(obj));
199         if (!pws)
200                 jbus_reply_error_s(smsg, "disconnected");
201         else {
202 #if defined(AFB_PROTO_WS_VERSION) && (AFB_PROTO_WS_VERSION >= 3)
203                 rc = afb_proto_ws_client_call(pws, verb, obj, sessionid, smsg, onbehalf);
204 #else
205                 rc = afb_proto_ws_client_call(pws, verb, obj, sessionid, smsg);
206 #endif
207                 if (rc < 0)
208                         ERROR("calling %s(%s) failed: %m\n", verb, json_object_to_json_string(obj));
209         } 
210 }
211
212 /*
213  * Tiny routine to daemonize the program
214  * Return 0 on success or -1 on failure.
215  */
216 static int daemonize()
217 {
218         int rc = fork();
219         if (rc < 0)
220                 return rc;
221         if (rc)
222                 _exit(0);
223         return 0;
224 }
225
226 /*
227  * Opens a sd-bus connection and returns it in 'ret'.
228  * The sd-bus connexion is intended to be for user if 'isuser'
229  * is not null. The adress is the default address when 'address'
230  * is NULL or, otherwise, the given address.
231  * It might be necessary to pass the address as an argument because
232  * library systemd uses secure_getenv to retrieves the default
233  * addresses and secure_getenv might return NULL in some cases.
234  */
235 static int open_bus(sd_bus **ret, int isuser, const char *address)
236 {
237         sd_bus *b;
238         int rc;
239
240         if (address == NULL)
241                 return (isuser ? sd_bus_default_user : sd_bus_default_system)(ret);
242
243         rc = sd_bus_new(&b);
244         if (rc < 0)
245                 return rc;
246
247         rc = sd_bus_set_address(b, address);
248         if (rc < 0)
249                 goto fail;
250
251         sd_bus_set_bus_client(b, 1);
252
253         rc = sd_bus_start(b);
254         if (rc < 0)
255                 goto fail;
256
257         *ret = b;
258         return 0;
259
260 fail:
261         sd_bus_unref(b);
262         return rc;
263 }
264
265 /*
266  * ENTRY POINT OF AFM-USER-DAEMON
267  */
268 int main(int ac, char **av)
269 {
270         int i, daemon = 0, rc;
271         struct sd_bus *usrbus;
272         const char *usr_bus_addr;
273         const char **iter;
274
275         LOGAUTH(appname);
276
277         /* first interpretation of arguments */
278         usr_bus_addr = NULL;
279         while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
280                 switch (i) {
281                 case 'h':
282                         printf(usagestr, appname);
283                         return 0;
284                 case 'V':
285                         printf(versionstr, appname);
286                         return 0;
287                 case 'q':
288                         if (verbosity)
289                                 verbosity--;
290                         break;
291                 case 'v':
292                         verbosity++;
293                         break;
294                 case 'd':
295                         daemon = 1;
296                         break;
297                 case 'u':
298                         usr_bus_addr = optarg;
299                         break;
300                 case ':':
301                         ERROR("missing argument value");
302                         return 1;
303                 default:
304                         ERROR("unrecognized option");
305                         return 1;
306                 }
307         }
308
309         /* check argument count */
310         if (optind >= ac) {
311                 ERROR("Uri to the framework is missing");
312                 return 1;
313         }
314         if (optind + 1 != ac) {
315                 ERROR("Extra parameters found");
316                 return 1;
317         }
318         uri = av[optind];
319
320         /* init sessionid */
321         asprintf(&sessionid, "%d-%s", (int)getuid(), appname);
322
323         /* daemonize if requested */
324         if (daemon && daemonize()) {
325                 ERROR("daemonization failed");
326                 return 1;
327         }
328
329         /* get systemd objects */
330         rc = sd_event_new(&evloop);
331         if (rc < 0) {
332                 ERROR("can't create event loop");
333                 return 1;
334         }
335         rc = open_bus(&usrbus, 1, usr_bus_addr);
336         if (rc < 0) {
337                 ERROR("can't create user bus");
338                 return 1;
339         }
340         rc = sd_bus_attach_event(usrbus, evloop, 0);
341         if (rc < 0) {
342                 ERROR("can't attach user bus to event loop");
343                 return 1;
344         }
345
346         /* connect to framework */
347         if (!try_connect_pws()) {
348                 ERROR("connection to %s failed: %m\n", uri);
349                 return 1;
350         }
351
352         /* connect to the session bus */
353         user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
354         if (!user_bus) {
355                 ERROR("create_jbus failed");
356                 return 1;
357         }
358
359         /* init services */
360         for (iter = methods ; *iter ; iter ++) {
361                 if (jbus_add_service_j(user_bus, *iter, propagate, (void*)*iter)) {
362                         ERROR("adding services failed");
363                         return 1;
364                 }
365         }
366
367         /* start servicing */
368         if (jbus_start_serving(user_bus) < 0) {
369                 ERROR("can't start server");
370                 return 1;
371         }
372
373         /* run until error */
374         for(;;)
375                 sd_event_run(evloop, (uint64_t)-1);
376         return 0;
377 }
378