/*
 * Original author : tridge@samba.org, January 2002
 *
 * Copyright (c) 2005 Christophe Varoqui
 * Copyright (c) 2005 Alasdair Kergon, Redhat
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <poll.h>
#include <signal.h>
#include <errno.h>
#ifdef USE_SYSTEMD
#include <systemd/sd-daemon.h>
#endif
#include "mpath_cmd.h"

#include "memory.h"
#include "uxsock.h"
#include "debug.h"

/*
 * Code is similar with mpath_recv_reply() with data size limitation
 * and debug-able malloc.
 * When limit == 0, it means no limit on data size, used for socket client
 * to receiving data from multipathd.
 */
static int _recv_packet(int fd, char **buf, unsigned int timeout,
			ssize_t limit);

/*
 * create a unix domain socket and start listening on it
 * return a file descriptor open on the socket
 */
int ux_socket_listen(const char *name)
{
	int fd;
	size_t len;
#ifdef USE_SYSTEMD
	int num;
#endif
	struct sockaddr_un addr;

#ifdef USE_SYSTEMD
	num = sd_listen_fds(0);
	if (num > 1) {
		condlog(3, "sd_listen_fds returned %d fds", num);
		return -1;
	} else if (num == 1) {
		fd = SD_LISTEN_FDS_START + 0;
		condlog(3, "using fd %d from sd_listen_fds", fd);
		return fd;
	}
#endif
	fd = socket(AF_LOCAL, SOCK_STREAM, 0);
	if (fd == -1) {
		condlog(3, "Couldn't create ux_socket, error %d", errno);
		return -1;
	}

	memset(&addr, 0, sizeof(addr));
	addr.sun_family = AF_LOCAL;
	addr.sun_path[0] = '\0';
	len = strlen(name) + 1;
	if (len >= sizeof(addr.sun_path))
		len = sizeof(addr.sun_path) - 1;
	memcpy(&addr.sun_path[1], name, len);

	len += sizeof(sa_family_t);
	if (bind(fd, (struct sockaddr *)&addr, len) == -1) {
		condlog(3, "Couldn't bind to ux_socket, error %d", errno);
		close(fd);
		return -1;
	}

	if (listen(fd, 10) == -1) {
		condlog(3, "Couldn't listen to ux_socket, error %d", errno);
		close(fd);
		return -1;
	}
	return fd;
}

/*
 * send a packet in length prefix format
 */
int send_packet(int fd, const char *buf)
{
	if (mpath_send_cmd(fd, buf) < 0)
		return -errno;
	return 0;
}

static int _recv_packet(int fd, char **buf, unsigned int timeout, ssize_t limit)
{
	int err = 0;
	ssize_t len = 0;

	*buf = NULL;
	len = mpath_recv_reply_len(fd, timeout);
	if (len == 0)
		return len;
	if (len < 0)
		return -errno;
	if ((limit > 0) && (len > limit))
		return -EINVAL;
	(*buf) = MALLOC(len);
	if (!*buf)
		return -ENOMEM;
	err = mpath_recv_reply_data(fd, *buf, len, timeout);
	if (err != 0) {
		FREE(*buf);
		(*buf) = NULL;
		return -errno;
	}
	return err;
}

/*
 * receive a packet in length prefix format
 */
int recv_packet(int fd, char **buf, unsigned int timeout)
{
	return _recv_packet(fd, buf, timeout, 0 /* no limit */);
}

int recv_packet_from_client(int fd, char **buf, unsigned int timeout)
{
	return _recv_packet(fd, buf, timeout, _MAX_CMD_LEN);
}
