2 Copyright 2015, 2016, 2017 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_success(void *closure, void *request, struct json_object *result, const char *info);
117 static void on_pws_reply_fail(void *closure, void *request, const char *status, const char *info);
118 static void on_pws_event_broadcast(void *closure, const char *event_name, struct json_object *data);
120 /* the callback interface for pws */
121 static struct afb_proto_ws_client_itf pws_itf = {
122 .on_reply_success = on_pws_reply_success,
123 .on_reply_fail = on_pws_reply_fail,
124 .on_event_broadcast = on_pws_event_broadcast,
127 static int try_connect_pws()
129 pws = afb_ws_client_connect_api(evloop, uri, &pws_itf, NULL);
131 fprintf(stderr, "connection to %s failed: %m\n", uri);
134 afb_proto_ws_on_hangup(pws, on_pws_hangup);
138 static void attempt_connect_pws(int count);
140 static int timehand(sd_event_source *s, uint64_t usec, void *userdata)
142 sd_event_source_unref(s);
143 attempt_connect_pws((int)(intptr_t)userdata);
147 static void attempt_connect_pws(int count)
150 if (!try_connect_pws()) {
152 ERROR("Definitely disconnected");
155 sd_event_add_time(evloop, &s, CLOCK_MONOTONIC, 5000000, 0, timehand, (void*)(intptr_t)count);
159 static void on_pws_reply_success(void *closure, void *request, struct json_object *result, const char *info)
161 struct sd_bus_message *smsg = request;
162 jbus_reply_j(smsg, result);
165 static void on_pws_reply_fail(void *closure, void *request, const char *status, const char *info)
167 struct sd_bus_message *smsg = request;
168 jbus_reply_error_s(smsg, status);
171 static void on_pws_event_broadcast(void *closure, const char *event_name, struct json_object *data)
173 jbus_send_signal_j(user_bus, "changed", data);
176 /* called when pws hangsup */
177 static void on_pws_hangup(void *closure)
179 struct afb_proto_ws *apw = pws;
181 afb_proto_ws_unref(apw);
182 attempt_connect_pws(10);
185 /* propagate the call to the service */
186 static void propagate(struct sd_bus_message *smsg, struct json_object *obj, void *closure)
189 const char *verb = closure;
191 INFO("method %s propagated for %s", verb, json_object_to_json_string(obj));
193 jbus_reply_error_s(smsg, "disconnected");
195 rc = afb_proto_ws_client_call(pws, verb, obj, sessionid, smsg);
197 ERROR("calling %s(%s) failed: %m\n", verb, json_object_to_json_string(obj));
202 * Tiny routine to daemonize the program
203 * Return 0 on success or -1 on failure.
205 static int daemonize()
216 * Opens a sd-bus connection and returns it in 'ret'.
217 * The sd-bus connexion is intended to be for user if 'isuser'
218 * is not null. The adress is the default address when 'address'
219 * is NULL or, otherwise, the given address.
220 * It might be necessary to pass the address as an argument because
221 * library systemd uses secure_getenv to retrieves the default
222 * addresses and secure_getenv might return NULL in some cases.
224 static int open_bus(sd_bus **ret, int isuser, const char *address)
230 return (isuser ? sd_bus_default_user : sd_bus_default_system)(ret);
236 rc = sd_bus_set_address(b, address);
240 sd_bus_set_bus_client(b, 1);
242 rc = sd_bus_start(b);
255 * ENTRY POINT OF AFM-USER-DAEMON
257 int main(int ac, char **av)
259 int i, daemon = 0, rc;
260 struct sd_bus *usrbus;
261 const char *usr_bus_addr;
266 /* first interpretation of arguments */
268 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
271 printf(usagestr, appname);
274 printf(versionstr, appname);
287 usr_bus_addr = optarg;
290 ERROR("missing argument value");
293 ERROR("unrecognized option");
298 /* check argument count */
300 ERROR("Uri to the framework is missing");
303 if (optind + 1 != ac) {
304 ERROR("Extra parameters found");
310 asprintf(&sessionid, "%d-%s", (int)getuid(), appname);
312 /* daemonize if requested */
313 if (daemon && daemonize()) {
314 ERROR("daemonization failed");
318 /* get systemd objects */
319 rc = sd_event_new(&evloop);
321 ERROR("can't create event loop");
324 rc = open_bus(&usrbus, 1, usr_bus_addr);
326 ERROR("can't create user bus");
329 rc = sd_bus_attach_event(usrbus, evloop, 0);
331 ERROR("can't attach user bus to event loop");
335 /* connect to framework */
336 if (!try_connect_pws()) {
337 ERROR("connection to %s failed: %m\n", uri);
341 /* connect to the session bus */
342 user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
344 ERROR("create_jbus failed");
349 for (iter = methods ; *iter ; iter ++) {
350 if (jbus_add_service_j(user_bus, *iter, propagate, (void*)*iter)) {
351 ERROR("adding services failed");
356 /* start servicing */
357 if (jbus_start_serving(user_bus) < 0) {
358 ERROR("can't start server");
362 /* run until error */
364 sd_event_run(evloop, (uint64_t)-1);