aboutsummaryrefslogtreecommitdiffstats
path: root/meta-agl-profile-core/recipes-test
AgeCommit message (Expand)AuthorFilesLines
2019-05-20Bump app-afb-test SRCREV to fix also p-prefixJan-Simon Möller1-3/+3
2019-04-04Upgrade to thudScott Murray2-8/+8
2018-12-20Bump afb-testRomain Forlot1-1/+1
2018-10-30Bump afb-test revisionRomain Forlot1-4/+2
2018-10-19Bump afb-test revisionRomain Forlot1-4/+6
2018-10-08Use iperf3 instead of iperf2Changhyeok Bae5-97/+1
2018-09-27afb-test: Set version from AGLVERSIONRomain Forlot1-1/+1
2018-09-07Update afb-test revision for FF.RC6Romain Forlot1-2/+2
2018-08-31Update afb-test revisionRomain Forlot1-2/+2
2018-08-24Add the afb-test binding in the devel AGL imageRomain Forlot2-0/+28
2018-04-20Introduce meta-agl-profile-core and meta-agl-profile-graphicsJan-Simon Möller28-0/+819
d='n255' href='#n255'>255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
/*
 Copyright (C) 2015-2019 IoT.bzh

 author: José Bollo <jose.bollo@iot.bzh>

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <getopt.h>
#include <string.h>

#include <systemd/sd-bus.h>
#include <systemd/sd-event.h>
#include <json-c/json.h>

#include <afb/afb-ws-client.h>
#include <afb/afb-proto-ws.h>

#include "verbose.h"
#include "utils-jbus.h"
#include "utils-json.h"

#define AFM_USER_DBUS_PATH	"/org/AGL/afm/user"

/*
 * name of the application
 */
static const char appname[] = "afm-user-daemon";

/*
 * string for printing version
 */
static const char versionstr[] =
	"\n"
	"  %s  version="AFM_VERSION"\n"
	"\n"
	"  Copyright (C) 2015-2019 \"IoT.bzh\"\n"
	"  AFB comes with ABSOLUTELY NO WARRANTY.\n"
	"  Licence Apache 2\n"
	"\n";

/*
 * string for printing usage
 */
static const char usagestr[] =
	"usage: %s [option(s)] afm-main-uri\n"
	"\n"
	"   -d           run as a daemon\n"
	"   -u addr      address of user D-Bus to use\n"
	"   -q           quiet\n"
	"   -v           verbose\n"
	"   -V           version\n"
	"\n";

/*
 * Option definition for getopt_long
 */
static const char options_s[] = "hdqvVu:";
static struct option options_l[] = {
	{ "user-dbus",   required_argument, NULL, 'u' },
	{ "daemon",      no_argument,       NULL, 'd' },
	{ "quiet",       no_argument,       NULL, 'q' },
	{ "verbose",     no_argument,       NULL, 'v' },
	{ "help",        no_argument,       NULL, 'h' },
	{ "version",     no_argument,       NULL, 'V' },
	{ NULL, 0, NULL, 0 }
};

/*
 * The methods propagated
 */
static const char *methods[] = {
	"runnables",
	"detail",
	"start",
	"once",
	"terminate",
	"pause",
	"resume",
	"stop",
	"continue",
	"runners",
	"state",
	"install",
	"uninstall",
	NULL
};

/*
 * Connections
 */
static struct sd_event *evloop;
static struct jbus *user_bus;
static struct afb_proto_ws *pws;
static char *sessionid;
static const char *uri;

/*
 * 
 */
static void on_pws_hangup(void *closure);
static void on_pws_reply(void *closure, void *request, struct json_object *obj, const char *error, const char *info);
#if !defined(AFB_PROTO_WS_VERSION) || (AFB_PROTO_WS_VERSION < 3)
static void on_pws_reply_success(void *closure, void *request, struct json_object *result, const char *info)
	{ on_pws_reply(closure, request, result, NULL, info); }
static void on_pws_reply_fail(void *closure, void *request, const char *error, const char *info)
	{ on_pws_reply(closure, request, NULL, error, info); }
#endif
static void on_pws_event_broadcast(void *closure, const char *event_name, struct json_object *data);

/* the callback interface for pws */
static struct afb_proto_ws_client_itf pws_itf = {
#if !defined(AFB_PROTO_WS_VERSION) || (AFB_PROTO_WS_VERSION < 3)
	.on_reply_success = on_pws_reply_success,
	.on_reply_fail = on_pws_reply_fail,
#else
	.on_reply = on_pws_reply,
#endif
	.on_event_broadcast = on_pws_event_broadcast,
};

static int try_connect_pws()
{
	pws = afb_ws_client_connect_api(evloop, uri, &pws_itf, NULL);
	if (pws == NULL) {
		fprintf(stderr, "connection to %s failed: %m\n", uri);
		return 0;
	}
	afb_proto_ws_on_hangup(pws, on_pws_hangup);
	return 1;
}

static void attempt_connect_pws(int count);

static int timehand(sd_event_source *s, uint64_t usec, void *userdata)
{
	sd_event_source_unref(s);
	attempt_connect_pws((int)(intptr_t)userdata);
	return 0;
}

static void attempt_connect_pws(int count)
{
	sd_event_source *s;
	if (!try_connect_pws()) {
		if (--count <= 0) {
			ERROR("Definitely disconnected");
			exit(1);
		}
		sd_event_add_time(evloop, &s, CLOCK_MONOTONIC, 5000000, 0, timehand, (void*)(intptr_t)count);
	}
}

static void on_pws_reply(void *closure, void *request, struct json_object *obj, const char *error, const char *info)
{
	struct sd_bus_message *smsg = request;
	if (error)
		jbus_reply_error_s(smsg, error);
	else
		jbus_reply_j(smsg, obj);
}

static void on_pws_event_broadcast(void *closure, const char *event_name, struct json_object *data)
{
	jbus_send_signal_j(user_bus, "changed", data);
}

/* called when pws hangsup */
static void on_pws_hangup(void *closure)
{
	struct afb_proto_ws *apw = pws;
	pws = NULL;
	afb_proto_ws_unref(apw);
	attempt_connect_pws(10);
}

/* propagate the call to the service */
static void propagate(struct sd_bus_message *smsg, struct json_object *obj, void *closure)
{
	int rc;
	const char *verb = closure;
	const char *onbehalf = NULL; /* TODO: on behalf of the client */

	INFO("method %s propagated for %s", verb, json_object_to_json_string(obj));
	if (!pws)
		jbus_reply_error_s(smsg, "disconnected");
	else {
#if defined(AFB_PROTO_WS_VERSION) && (AFB_PROTO_WS_VERSION >= 3)
		rc = afb_proto_ws_client_call(pws, verb, obj, sessionid, smsg, onbehalf);
#else
		rc = afb_proto_ws_client_call(pws, verb, obj, sessionid, smsg);
#endif
		if (rc < 0)
			ERROR("calling %s(%s) failed: %m\n", verb, json_object_to_json_string(obj));
	} 
}

/*
 * Tiny routine to daemonize the program
 * Return 0 on success or -1 on failure.
 */
static int daemonize()
{
	int rc = fork();
	if (rc < 0)
		return rc;
	if (rc)
		_exit(0);
	return 0;
}

/*
 * Opens a sd-bus connection and returns it in 'ret'.
 * The sd-bus connexion is intended to be for user if 'isuser'
 * is not null. The adress is the default address when 'address'
 * is NULL or, otherwise, the given address.
 * It might be necessary to pass the address as an argument because
 * library systemd uses secure_getenv to retrieves the default
 * addresses and secure_getenv might return NULL in some cases.
 */
static int open_bus(sd_bus **ret, int isuser, const char *address)
{
	sd_bus *b;
	int rc;

	if (address == NULL)
		return (isuser ? sd_bus_default_user : sd_bus_default_system)(ret);

	rc = sd_bus_new(&b);
	if (rc < 0)
		return rc;

	rc = sd_bus_set_address(b, address);
	if (rc < 0)
		goto fail;

	sd_bus_set_bus_client(b, 1);

	rc = sd_bus_start(b);
	if (rc < 0)
		goto fail;

	*ret = b;
	return 0;

fail:
	sd_bus_unref(b);
	return rc;
}

/*
 * ENTRY POINT OF AFM-USER-DAEMON
 */
int main(int ac, char **av)
{
	int i, daemon = 0, rc;
	struct sd_bus *usrbus;
	const char *usr_bus_addr;
	const char **iter;

	LOGAUTH(appname);

	/* first interpretation of arguments */
	usr_bus_addr = NULL;
	while ((i = getopt_long(ac, av, options_s, options_l, NULL)) >= 0) {
		switch (i) {
		case 'h':
			printf(usagestr, appname);
			return 0;
		case 'V':
			printf(versionstr, appname);
			return 0;
		case 'q':
			if (verbosity)
				verbosity--;
			break;
		case 'v':
			verbosity++;
			break;
		case 'd':
			daemon = 1;
			break;
		case 'u':
			usr_bus_addr = optarg;
			break;
		case ':':
			ERROR("missing argument value");
			return 1;
		default:
			ERROR("unrecognized option");
			return 1;
		}
	}

	/* check argument count */
	if (optind >= ac) {
		ERROR("Uri to the framework is missing");
		return 1;
	}
	if (optind + 1 != ac) {
		ERROR("Extra parameters found");
		return 1;
	}
	uri = av[optind];

	/* init sessionid */
	asprintf(&sessionid, "%d-%s", (int)getuid(), appname);

	/* daemonize if requested */
	if (daemon && daemonize()) {
		ERROR("daemonization failed");
		return 1;
	}

	/* get systemd objects */
	rc = sd_event_new(&evloop);
	if (rc < 0) {
		ERROR("can't create event loop");
		return 1;
	}
	rc = open_bus(&usrbus, 1, usr_bus_addr);
	if (rc < 0) {
		ERROR("can't create user bus");
		return 1;
	}
	rc = sd_bus_attach_event(usrbus, evloop, 0);
	if (rc < 0) {
		ERROR("can't attach user bus to event loop");
		return 1;
	}

	/* connect to framework */
	if (!try_connect_pws()) {
		ERROR("connection to %s failed: %m\n", uri);
		return 1;
	}

	/* connect to the session bus */
	user_bus = create_jbus(usrbus, AFM_USER_DBUS_PATH);
	if (!user_bus) {
		ERROR("create_jbus failed");
		return 1;
	}

	/* init services */
	for (iter = methods ; *iter ; iter ++) {
		if (jbus_add_service_j(user_bus, *iter, propagate, (void*)*iter)) {
			ERROR("adding services failed");
			return 1;
		}
	}

	/* start servicing */
	if (jbus_start_serving(user_bus) < 0) {
		ERROR("can't start server");
		return 1;
	}

	/* run until error */
	for(;;)
		sd_event_run(evloop, (uint64_t)-1);
	return 0;
}