2 Copyright (C) 2015-2019 IoT.bzh
4 author: José Bollo <jose.bollo@iot.bzh>
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
10 http://www.apache.org/licenses/LICENSE-2.0
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.
26 #include <systemd/sd-bus.h>
27 #include <systemd/sd-event.h>
28 #include <json-c/json.h>
30 #include <afb/afb-ws-client.h>
31 #include <afb/afb-proto-ws.h>
34 #include "utils-jbus.h"
35 #include "utils-json.h"
37 #define AFM_USER_DBUS_PATH "/org/AGL/afm/user"
40 * name of the application
42 static const char appname[] = "afm-user-daemon";
45 * string for printing version
47 static const char versionstr[] =
49 " %s version="AFM_VERSION"\n"
51 " Copyright (C) 2015-2019 \"IoT.bzh\"\n"
52 " AFB comes with ABSOLUTELY NO WARRANTY.\n"
57 * string for printing usage
59 static const char usagestr[] =
60 "usage: %s [option(s)] afm-main-uri\n"
62 " -d run as a daemon\n"
63 " -u addr address of user D-Bus to use\n"
70 * Option definition for getopt_long
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' },
84 * The methods propagated
86 static const char *methods[] = {
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;
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); }
123 static void on_pws_event_broadcast(void *closure, const char *event_name, struct json_object *data);
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,
131 .on_reply = on_pws_reply,
133 .on_event_broadcast = on_pws_event_broadcast,
136 static int try_connect_pws()
138 pws = afb_ws_client_connect_api(evloop, uri, &pws_itf, NULL);
140 fprintf(stderr, "connection to %s failed: %m\n", uri);
143 afb_proto_ws_on_hangup(pws, on_pws_hangup);
147 static void attempt_connect_pws(int count);
149 static int timehand(sd_event_source *s, uint64_t usec, void *userdata)
151 sd_event_source_unref(s);
152 attempt_connect_pws((int)(intptr_t)userdata);
156 static void attempt_connect_pws(int count)
159 if (!try_connect_pws()) {
161 ERROR("Definitely disconnected");
164 sd_event_add_time(evloop, &s, CLOCK_MONOTONIC, 5000000, 0, timehand, (void*)(intptr_t)count);
168 static void on_pws_reply(void *closure, void *request, struct json_object *obj, const char *error, const char *info)
170 struct sd_bus_message *smsg = request;
172 jbus_reply_error_s(smsg, error);
174 jbus_reply_j(smsg, obj);
177 static void on_pws_event_broadcast(void *closure, const char *event_name, struct json_object *data)
179 jbus_send_signal_j(user_bus, "changed", data);
182 /* called when pws hangsup */
183 static void on_pws_hangup(void *closure)
185 struct afb_proto_ws *apw = pws;
187 afb_proto_ws_unref(apw);
188 attempt_connect_pws(10);
191 /* propagate the call to the service */
192 static void propagate(struct sd_bus_message *smsg, struct json_object *obj, void *closure)
195 const char *verb = closure;
196 const char *onbehalf = NULL; /* TODO: on behalf of the client */
198 INFO("method %s propagated for %s", verb, json_object_to_json_string(obj));
200 jbus_reply_error_s(smsg, "disconnected");
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);
205 rc = afb_proto_ws_client_call(pws, verb, obj, sessionid, smsg);
208 ERROR("calling %s(%s) failed: %m\n", verb, json_object_to_json_string(obj));
213 * Tiny routine to daemonize the program
214 * Return 0 on success or -1 on failure.
216 static int daemonize()
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.
235 static int open_bus(sd_bus **ret, int isuser, const char *address)
241 return (isuser ? sd_bus_default_user : sd_bus_default_system)(ret);
247 rc = sd_bus_set_address(b, address);
251 sd_bus_set_bus_client(b, 1);
253 rc = sd_bus_start(b);
266 * ENTRY POINT OF AFM-USER-DAEMON
268 int main(int ac, char **av)
270 int i, daemon = 0, rc;
271 struct sd_bus *usrbus;
272 const char *usr_bus_addr;
277 /* first interpretation of arguments */
279 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
282 printf(usagestr, appname);
285 printf(versionstr, appname);
298 usr_bus_addr = optarg;
301 ERROR("missing argument value");
304 ERROR("unrecognized option");
309 /* check argument count */
311 ERROR("Uri to the framework is missing");
314 if (optind + 1 != ac) {
315 ERROR("Extra parameters found");
321 asprintf(&sessionid, "%d-%s", (int)getuid(), appname);
323 /* daemonize if requested */
324 if (daemon && daemonize()) {
325 ERROR("daemonization failed");
329 /* get systemd objects */
330 rc = sd_event_new(&evloop);
332 ERROR("can't create event loop");
335 rc = open_bus(&usrbus, 1, usr_bus_addr);
337 ERROR("can't create user bus");
340 rc = sd_bus_attach_event(usrbus, evloop, 0);
342 ERROR("can't attach user bus to event loop");
346 /* connect to framework */
347 if (!try_connect_pws()) {
348 ERROR("connection to %s failed: %m\n", uri);
352 /* connect to the session bus */
353 user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
355 ERROR("create_jbus failed");
360 for (iter = methods ; *iter ; iter ++) {
361 if (jbus_add_service_j(user_bus, *iter, propagate, (void*)*iter)) {
362 ERROR("adding services failed");
367 /* start servicing */
368 if (jbus_start_serving(user_bus) < 0) {
369 ERROR("can't start server");
373 /* run until error */
375 sd_event_run(evloop, (uint64_t)-1);