2 Copyright (C) 2015-2018 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, 2016, 2017 \"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;
197 INFO("method %s propagated for %s", verb, json_object_to_json_string(obj));
199 jbus_reply_error_s(smsg, "disconnected");
201 rc = afb_proto_ws_client_call(pws, verb, obj, sessionid, smsg);
203 ERROR("calling %s(%s) failed: %m\n", verb, json_object_to_json_string(obj));
208 * Tiny routine to daemonize the program
209 * Return 0 on success or -1 on failure.
211 static int daemonize()
222 * Opens a sd-bus connection and returns it in 'ret'.
223 * The sd-bus connexion is intended to be for user if 'isuser'
224 * is not null. The adress is the default address when 'address'
225 * is NULL or, otherwise, the given address.
226 * It might be necessary to pass the address as an argument because
227 * library systemd uses secure_getenv to retrieves the default
228 * addresses and secure_getenv might return NULL in some cases.
230 static int open_bus(sd_bus **ret, int isuser, const char *address)
236 return (isuser ? sd_bus_default_user : sd_bus_default_system)(ret);
242 rc = sd_bus_set_address(b, address);
246 sd_bus_set_bus_client(b, 1);
248 rc = sd_bus_start(b);
261 * ENTRY POINT OF AFM-USER-DAEMON
263 int main(int ac, char **av)
265 int i, daemon = 0, rc;
266 struct sd_bus *usrbus;
267 const char *usr_bus_addr;
272 /* first interpretation of arguments */
274 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
277 printf(usagestr, appname);
280 printf(versionstr, appname);
293 usr_bus_addr = optarg;
296 ERROR("missing argument value");
299 ERROR("unrecognized option");
304 /* check argument count */
306 ERROR("Uri to the framework is missing");
309 if (optind + 1 != ac) {
310 ERROR("Extra parameters found");
316 asprintf(&sessionid, "%d-%s", (int)getuid(), appname);
318 /* daemonize if requested */
319 if (daemon && daemonize()) {
320 ERROR("daemonization failed");
324 /* get systemd objects */
325 rc = sd_event_new(&evloop);
327 ERROR("can't create event loop");
330 rc = open_bus(&usrbus, 1, usr_bus_addr);
332 ERROR("can't create user bus");
335 rc = sd_bus_attach_event(usrbus, evloop, 0);
337 ERROR("can't attach user bus to event loop");
341 /* connect to framework */
342 if (!try_connect_pws()) {
343 ERROR("connection to %s failed: %m\n", uri);
347 /* connect to the session bus */
348 user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
350 ERROR("create_jbus failed");
355 for (iter = methods ; *iter ; iter ++) {
356 if (jbus_add_service_j(user_bus, *iter, propagate, (void*)*iter)) {
357 ERROR("adding services failed");
362 /* start servicing */
363 if (jbus_start_serving(user_bus) < 0) {
364 ERROR("can't start server");
368 /* run until error */
370 sd_event_run(evloop, (uint64_t)-1);