/*
 * dslmngr_nl.c - converts netlink messages to UBUS events
 *
 * Copyright (C) 2019 iopsys Software Solutions AB. All rights reserved.
 *
 * Author: anjan.chanda@iopsys.eu
 *         yalu.zhang@iopsys.eu
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <netlink/netlink.h>
#include <netlink/socket.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/genl.h>
#include <netlink/attr.h>
#include "libubox/blobmsg_json.h"
#include "libubus.h"

#define NETLINK_FAMILY_NAME "easysoc"
#define NETLINK_GROUP_NAME  "notify"

#define MAX_MSG 512

/* nl attributes */
enum {
	XDSL_NL_UNSPEC,
	XDSL_NL_MSG,
	__XDSL_NL_MAX,
};

static struct nla_policy nl_notify_policy[__XDSL_NL_MAX] = {
	[XDSL_NL_MSG] = { .type = NLA_STRING },
};

static struct nlattr *attrs[__XDSL_NL_MAX];

static int dslmngr_ubus_event(struct ubus_context *ctx, char *message)
{
	char data[MAX_MSG] = {0};
	char event[32] = {0};
	struct blob_buf b;

	sscanf(message, "%32s '%512[^\n]s'", event, data);

	/* ignore non-dsl events */
	if (!strstr(event, "dsl."))
		return 0;

	memset(&b, 0, sizeof(b));
	blob_buf_init(&b, 0);

	if (!blobmsg_add_json_from_string(&b, data)) {
		fprintf(stderr, "Failed to parse message data: %s\n", data);
		return -1;
	}

	ubus_send_event(ctx, event, b.head);
	blob_buf_free(&b);

	return 0;
}

static int dslmngr_nl_to_ubus_event(struct nl_msg *msg, void *arg)
{
	struct nlmsghdr *nlh = nlmsg_hdr(msg);
	struct ubus_context *ctx = (struct ubus_context *)arg;
	char message[MAX_MSG] = {0};
	int ret;

	if (!genlmsg_valid_hdr(nlh, 0)) {
		fprintf(stderr, "received invalid message\n");
		return 0;
	}

	ret = genlmsg_parse(nlh, 0, attrs, XDSL_NL_MSG, nl_notify_policy);
	if (!ret) {
		if (attrs[XDSL_NL_MSG] ) {
			memcpy(message, nla_get_string(attrs[XDSL_NL_MSG]),
						nla_len(attrs[XDSL_NL_MSG]));

			dslmngr_ubus_event(ctx, message);
		}
	}
	return 0;
}

int dslmngr_nl_msgs_handler(void *p)
{
	struct ubus_context *ctx = (struct ubus_context *)p;
	struct nl_sock *sock;
	int grp;
	int err;

	sock = nl_socket_alloc();
	if(!sock){
		fprintf(stderr, "Error: nl_socket_alloc\n");
		return -1;
	}

	nl_socket_disable_seq_check(sock);
	err = nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM,
				dslmngr_nl_to_ubus_event, ctx);

	if ((err = genl_connect(sock)) < 0){
		fprintf(stderr, "Error: %s\n", nl_geterror(err));
		return -1;
	}

	if ((grp = genl_ctrl_resolve_grp(sock,
					NETLINK_FAMILY_NAME,
					NETLINK_GROUP_NAME)) < 0) {
		return -1;
	}

	nl_socket_add_membership(sock, grp);

	while (1) {
		err = nl_recvmsgs_default(sock);
		if (err < 0) {
			fprintf(stderr, "Error: %s (%s grp %s)\n",
					nl_geterror(err),
					NETLINK_FAMILY_NAME,
					NETLINK_GROUP_NAME);
		}
	}

	return 0;
}