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>
35 #include "utils-jbus.h"
36 #include "utils-json.h"
39 * name of the application
41 static const char appname[] = "afm-user-daemon";
44 * string for printing version
46 static const char versionstr[] =
48 " %s version="AFM_VERSION"\n"
50 " Copyright (C) 2015, 2016, 2017 \"IoT.bzh\"\n"
51 " AFB comes with ABSOLUTELY NO WARRANTY.\n"
56 * string for printing usage
58 static const char usagestr[] =
59 "usage: %s [option(s)] afm-main-uri\n"
61 " -d run as a daemon\n"
62 " -u addr address of user D-Bus to use\n"
69 * Option definition for getopt_long
71 static const char options_s[] = "hdqvVu:";
72 static struct option options_l[] = {
73 { "user-dbus", required_argument, NULL, 'u' },
74 { "daemon", no_argument, NULL, 'd' },
75 { "quiet", no_argument, NULL, 'q' },
76 { "verbose", no_argument, NULL, 'v' },
77 { "help", no_argument, NULL, 'h' },
78 { "version", no_argument, NULL, 'V' },
83 * The methods propagated
85 static const char *methods[] = {
105 static struct sd_event *evloop;
106 static struct jbus *user_bus;
107 static struct afb_proto_ws *pws;
108 static char *sessionid;
109 static const char *uri;
114 static void on_pws_hangup(void *closure);
115 static void on_pws_reply_success(void *closure, void *request, struct json_object *result, const char *info);
116 static void on_pws_reply_fail(void *closure, void *request, const char *status, const char *info);
117 static void on_pws_event_broadcast(void *closure, const char *event_name, struct json_object *data);
119 /* the callback interface for pws */
120 static struct afb_proto_ws_client_itf pws_itf = {
121 .on_reply_success = on_pws_reply_success,
122 .on_reply_fail = on_pws_reply_fail,
123 .on_event_broadcast = on_pws_event_broadcast,
126 static int try_connect_pws()
128 pws = afb_ws_client_connect_api(evloop, uri, &pws_itf, NULL);
130 fprintf(stderr, "connection to %s failed: %m\n", uri);
133 afb_proto_ws_on_hangup(pws, on_pws_hangup);
137 static void attempt_connect_pws(int count);
139 static int timehand(sd_event_source *s, uint64_t usec, void *userdata)
141 sd_event_source_unref(s);
142 attempt_connect_pws((int)(intptr_t)userdata);
146 static void attempt_connect_pws(int count)
149 if (!try_connect_pws()) {
151 ERROR("Definitely disconnected");
154 sd_event_add_time(evloop, &s, CLOCK_MONOTONIC, 5000000, 0, timehand, (void*)(intptr_t)count);
158 static void on_pws_reply_success(void *closure, void *request, struct json_object *result, const char *info)
160 struct sd_bus_message *smsg = request;
161 jbus_reply_j(smsg, result);
164 static void on_pws_reply_fail(void *closure, void *request, const char *status, const char *info)
166 struct sd_bus_message *smsg = request;
167 jbus_reply_error_s(smsg, status);
170 static void on_pws_event_broadcast(void *closure, const char *event_name, struct json_object *data)
172 jbus_send_signal_j(user_bus, "changed", data);
175 /* called when pws hangsup */
176 static void on_pws_hangup(void *closure)
178 struct afb_proto_ws *apw = pws;
180 afb_proto_ws_unref(apw);
181 attempt_connect_pws(10);
184 /* propagate the call to the service */
185 static void propagate(struct sd_bus_message *smsg, struct json_object *obj, void *closure)
188 const char *verb = closure;
190 INFO("method %s propagated for %s", verb, json_object_to_json_string(obj));
192 jbus_reply_error_s(smsg, "disconnected");
194 rc = afb_proto_ws_client_call(pws, verb, obj, sessionid, smsg);
196 ERROR("calling %s(%s) failed: %m\n", verb, json_object_to_json_string(obj));
201 * Tiny routine to daemonize the program
202 * Return 0 on success or -1 on failure.
204 static int daemonize()
215 * Opens a sd-bus connection and returns it in 'ret'.
216 * The sd-bus connexion is intended to be for user if 'isuser'
217 * is not null. The adress is the default address when 'address'
218 * is NULL or, otherwise, the given address.
219 * It might be necessary to pass the address as an argument because
220 * library systemd uses secure_getenv to retrieves the default
221 * addresses and secure_getenv might return NULL in some cases.
223 static int open_bus(sd_bus **ret, int isuser, const char *address)
229 return (isuser ? sd_bus_default_user : sd_bus_default_system)(ret);
235 rc = sd_bus_set_address(b, address);
239 sd_bus_set_bus_client(b, 1);
241 rc = sd_bus_start(b);
254 * ENTRY POINT OF AFM-USER-DAEMON
256 int main(int ac, char **av)
258 int i, daemon = 0, rc;
259 struct sd_bus *usrbus;
260 const char *usr_bus_addr;
265 /* first interpretation of arguments */
267 while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
270 printf(usagestr, appname);
273 printf(versionstr, appname);
286 usr_bus_addr = optarg;
289 ERROR("missing argument value");
292 ERROR("unrecognized option");
297 /* check argument count */
299 ERROR("Uri to the framework is missing");
302 if (optind + 1 != ac) {
303 ERROR("Extra parameters found");
309 asprintf(&sessionid, "%d-%s", (int)getuid(), appname);
311 /* daemonize if requested */
312 if (daemon && daemonize()) {
313 ERROR("daemonization failed");
317 /* get systemd objects */
318 rc = sd_event_new(&evloop);
320 ERROR("can't create event loop");
323 rc = open_bus(&usrbus, 1, usr_bus_addr);
325 ERROR("can't create user bus");
328 rc = sd_bus_attach_event(usrbus, evloop, 0);
330 ERROR("can't attach user bus to event loop");
334 /* connect to framework */
335 if (!try_connect_pws()) {
336 ERROR("connection to %s failed: %m\n", uri);
340 /* connect to the session bus */
341 user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
343 ERROR("create_jbus failed");
348 for (iter = methods ; *iter ; iter ++) {
349 if (jbus_add_service_j(user_bus, *iter, propagate, (void*)*iter)) {
350 ERROR("adding services failed");
355 /* start servicing */
356 if (jbus_start_serving(user_bus) < 0) {
357 ERROR("can't start server");
361 /* run until error */
363 sd_event_run(evloop, (uint64_t)-1);