|
Packit |
e9ba0d |
/* -*- mode: c; c-file-style: "openbsd" -*- */
|
|
Packit |
e9ba0d |
/*
|
|
Packit |
e9ba0d |
* Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
|
|
Packit |
e9ba0d |
*
|
|
Packit |
e9ba0d |
* Permission to use, copy, modify, and/or distribute this software for any
|
|
Packit |
e9ba0d |
* purpose with or without fee is hereby granted, provided that the above
|
|
Packit |
e9ba0d |
* copyright notice and this permission notice appear in all copies.
|
|
Packit |
e9ba0d |
*
|
|
Packit |
e9ba0d |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
Packit |
e9ba0d |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
Packit |
e9ba0d |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
Packit |
e9ba0d |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
Packit |
e9ba0d |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
Packit |
e9ba0d |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
Packit |
e9ba0d |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
Packit |
e9ba0d |
*/
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
#include <stdlib.h>
|
|
Packit |
e9ba0d |
#include <unistd.h>
|
|
Packit |
e9ba0d |
#include <fcntl.h>
|
|
Packit |
e9ba0d |
#include <errno.h>
|
|
Packit |
e9ba0d |
#include <sys/types.h>
|
|
Packit |
e9ba0d |
#include <sys/socket.h>
|
|
Packit |
e9ba0d |
#include <sys/un.h>
|
|
Packit |
e9ba0d |
#include <string.h>
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
#include "ctl.h"
|
|
Packit |
e9ba0d |
#include "marshal.h"
|
|
Packit |
e9ba0d |
#include "log.h"
|
|
Packit |
e9ba0d |
#include "compat/compat.h"
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
/**
|
|
Packit |
e9ba0d |
* Create a new listening Unix socket for control protocol.
|
|
Packit |
e9ba0d |
*
|
|
Packit |
e9ba0d |
* @param name The name of the Unix socket.
|
|
Packit |
e9ba0d |
* @return The socket when successful, -1 otherwise.
|
|
Packit |
e9ba0d |
*/
|
|
Packit |
e9ba0d |
int
|
|
Packit |
e9ba0d |
ctl_create(const char *name)
|
|
Packit |
e9ba0d |
{
|
|
Packit |
e9ba0d |
int s;
|
|
Packit |
e9ba0d |
struct sockaddr_un su;
|
|
Packit |
e9ba0d |
int rc;
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
log_debug("control", "create control socket %s", name);
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
|
|
Packit |
e9ba0d |
return -1;
|
|
Packit |
e9ba0d |
if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {
|
|
Packit |
e9ba0d |
close(s);
|
|
Packit |
e9ba0d |
return -1;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
su.sun_family = AF_UNIX;
|
|
Packit |
e9ba0d |
strlcpy(su.sun_path, name, sizeof(su.sun_path));
|
|
Packit |
e9ba0d |
if (bind(s, (struct sockaddr *)&su, sizeof(struct sockaddr_un)) == -1) {
|
|
Packit |
e9ba0d |
rc = errno; close(s); errno = rc;
|
|
Packit |
e9ba0d |
return -1;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
log_debug("control", "listen to control socket %s", name);
|
|
Packit |
e9ba0d |
if (listen(s, 5) == -1) {
|
|
Packit |
e9ba0d |
rc = errno; close(s); errno = rc;
|
|
Packit |
e9ba0d |
log_debug("control", "cannot listen to control socket %s", name);
|
|
Packit |
e9ba0d |
return -1;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
return s;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
/**
|
|
Packit |
e9ba0d |
* Connect to the control Unix socket.
|
|
Packit |
e9ba0d |
*
|
|
Packit |
e9ba0d |
* @param name The name of the Unix socket.
|
|
Packit |
e9ba0d |
* @return The socket when successful, -1 otherwise.
|
|
Packit |
e9ba0d |
*/
|
|
Packit |
e9ba0d |
int
|
|
Packit |
e9ba0d |
ctl_connect(const char *name)
|
|
Packit |
e9ba0d |
{
|
|
Packit |
e9ba0d |
int s;
|
|
Packit |
e9ba0d |
struct sockaddr_un su;
|
|
Packit |
e9ba0d |
int rc;
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
log_debug("control", "connect to control socket %s", name);
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
|
|
Packit |
e9ba0d |
return -1;
|
|
Packit |
e9ba0d |
su.sun_family = AF_UNIX;
|
|
Packit |
e9ba0d |
strlcpy(su.sun_path, name, sizeof(su.sun_path));
|
|
Packit |
e9ba0d |
if (connect(s, (struct sockaddr *)&su, sizeof(struct sockaddr_un)) == -1) {
|
|
Packit |
e9ba0d |
rc = errno;
|
|
Packit |
e9ba0d |
log_warn("control", "unable to connect to socket %s", name);
|
|
Packit |
e9ba0d |
close(s);
|
|
Packit |
e9ba0d |
errno = rc; return -1;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
return s;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
/**
|
|
Packit |
e9ba0d |
* Remove the control Unix socket.
|
|
Packit |
e9ba0d |
*
|
|
Packit |
e9ba0d |
* @param name The name of the Unix socket.
|
|
Packit |
e9ba0d |
*/
|
|
Packit |
e9ba0d |
void
|
|
Packit |
e9ba0d |
ctl_cleanup(const char *name)
|
|
Packit |
e9ba0d |
{
|
|
Packit |
e9ba0d |
log_debug("control", "cleanup control socket");
|
|
Packit |
e9ba0d |
if (unlink(name) == -1)
|
|
Packit |
e9ba0d |
log_warn("control", "unable to unlink %s", name);
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
/**
|
|
Packit |
e9ba0d |
* Serialize and "send" a structure through the control protocol.
|
|
Packit |
e9ba0d |
*
|
|
Packit |
e9ba0d |
* This function does not really send the message but outputs it to a buffer.
|
|
Packit |
e9ba0d |
*
|
|
Packit |
e9ba0d |
* @param output_buffer A pointer to a buffer to which the message will be
|
|
Packit |
e9ba0d |
* appended. Can be @c NULL. In this case, the buffer will
|
|
Packit |
e9ba0d |
* be allocated.
|
|
Packit |
e9ba0d |
* @param[in,out] output_len The length of the provided buffer. Will be updated
|
|
Packit |
e9ba0d |
* with the new length
|
|
Packit |
e9ba0d |
* @param type The type of message we want to send.
|
|
Packit |
e9ba0d |
* @param t The structure to be serialized and sent.
|
|
Packit |
e9ba0d |
* @param mi The appropriate marshal structure for serialization.
|
|
Packit |
e9ba0d |
* @return -1 in case of failure, 0 in case of success.
|
|
Packit |
e9ba0d |
*
|
|
Packit |
e9ba0d |
* Make sure this function logic matches the server-side one: @c levent_ctl_recv().
|
|
Packit |
e9ba0d |
*/
|
|
Packit |
e9ba0d |
int
|
|
Packit |
e9ba0d |
ctl_msg_send_unserialized(uint8_t **output_buffer, size_t *output_len,
|
|
Packit |
e9ba0d |
enum hmsg_type type,
|
|
Packit |
e9ba0d |
void *t, struct marshal_info *mi)
|
|
Packit |
e9ba0d |
{
|
|
Packit |
e9ba0d |
ssize_t len = 0, newlen;
|
|
Packit |
e9ba0d |
void *buffer = NULL;
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
log_debug("control", "send a message through control socket");
|
|
Packit |
e9ba0d |
if (t) {
|
|
Packit |
e9ba0d |
len = marshal_serialize_(mi, t, &buffer, 0, NULL, 0);
|
|
Packit |
e9ba0d |
if (len <= 0) {
|
|
Packit |
e9ba0d |
log_warnx("control", "unable to serialize data");
|
|
Packit |
e9ba0d |
return -1;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
newlen = len + sizeof(struct hmsg_header);
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
if (*output_buffer == NULL) {
|
|
Packit |
e9ba0d |
*output_len = 0;
|
|
Packit |
e9ba0d |
if ((*output_buffer = malloc(newlen)) == NULL) {
|
|
Packit |
e9ba0d |
log_warn("control", "no memory available");
|
|
Packit |
e9ba0d |
free(buffer);
|
|
Packit |
e9ba0d |
return -1;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
} else {
|
|
Packit |
e9ba0d |
void *new = realloc(*output_buffer, *output_len + newlen);
|
|
Packit |
e9ba0d |
if (new == NULL) {
|
|
Packit |
e9ba0d |
log_warn("control", "no memory available");
|
|
Packit |
e9ba0d |
free(buffer);
|
|
Packit |
e9ba0d |
return -1;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
*output_buffer = new;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
struct hmsg_header hdr;
|
|
Packit |
e9ba0d |
memset(&hdr, 0, sizeof(struct hmsg_header));
|
|
Packit |
e9ba0d |
hdr.type = type;
|
|
Packit |
e9ba0d |
hdr.len = len;
|
|
Packit |
e9ba0d |
memcpy(*output_buffer + *output_len, &hdr, sizeof(struct hmsg_header));
|
|
Packit |
e9ba0d |
if (t)
|
|
Packit |
e9ba0d |
memcpy(*output_buffer + *output_len + sizeof(struct hmsg_header), buffer, len);
|
|
Packit |
e9ba0d |
*output_len += newlen;
|
|
Packit |
e9ba0d |
free(buffer);
|
|
Packit |
e9ba0d |
return 0;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
/**
|
|
Packit |
e9ba0d |
* "Receive" and unserialize a structure through the control protocol.
|
|
Packit |
e9ba0d |
*
|
|
Packit |
e9ba0d |
* Like @c ctl_msg_send_unserialized(), this function uses buffer to receive the
|
|
Packit |
e9ba0d |
* incoming message.
|
|
Packit |
e9ba0d |
*
|
|
Packit |
e9ba0d |
* @param[in,out] input_buffer The buffer with the incoming message. Will be
|
|
Packit |
e9ba0d |
* updated once the message has been unserialized to
|
|
Packit |
e9ba0d |
* point to the remaining of the message or will be
|
|
Packit |
e9ba0d |
* freed if all the buffer has been consumed. Can be
|
|
Packit |
e9ba0d |
* @c NULL.
|
|
Packit |
e9ba0d |
* @param[in,out] input_len The length of the provided buffer. Will be updated
|
|
Packit |
e9ba0d |
* to the length of remaining data once the message
|
|
Packit |
e9ba0d |
* has been unserialized.
|
|
Packit |
e9ba0d |
* @param expected_type The expected message type.
|
|
Packit |
e9ba0d |
* @param[out] t Will contain a pointer to the unserialized structure.
|
|
Packit |
e9ba0d |
* Can be @c NULL if we don't want to store the
|
|
Packit |
e9ba0d |
* answer.
|
|
Packit |
e9ba0d |
* @param mi The appropriate marshal structure for unserialization.
|
|
Packit |
e9ba0d |
*
|
|
Packit |
e9ba0d |
* @return -1 in case of error, 0 in case of success and the number of bytes we
|
|
Packit |
e9ba0d |
* request to complete unserialization.
|
|
Packit |
e9ba0d |
*
|
|
Packit |
e9ba0d |
* When requesting a notification, the input buffer is left untouched if we
|
|
Packit |
e9ba0d |
* don't get one and we fail silently.
|
|
Packit |
e9ba0d |
*/
|
|
Packit |
e9ba0d |
size_t
|
|
Packit |
e9ba0d |
ctl_msg_recv_unserialized(uint8_t **input_buffer, size_t *input_len,
|
|
Packit |
e9ba0d |
enum hmsg_type expected_type,
|
|
Packit |
e9ba0d |
void **t, struct marshal_info *mi)
|
|
Packit |
e9ba0d |
{
|
|
Packit |
e9ba0d |
struct hmsg_header hdr;
|
|
Packit |
e9ba0d |
int rc = -1;
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
if (*input_buffer == NULL ||
|
|
Packit |
e9ba0d |
*input_len < sizeof(struct hmsg_header)) {
|
|
Packit |
e9ba0d |
/* Not enough data. */
|
|
Packit |
e9ba0d |
return sizeof(struct hmsg_header) - *input_len;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
log_debug("control", "receive a message through control socket");
|
|
Packit |
e9ba0d |
memcpy(&hdr, *input_buffer, sizeof(struct hmsg_header));
|
|
Packit |
e9ba0d |
if (hdr.len > HMSG_MAX_SIZE) {
|
|
Packit |
e9ba0d |
log_warnx("control", "message received is too large");
|
|
Packit |
e9ba0d |
/* We discard the whole buffer */
|
|
Packit |
e9ba0d |
free(*input_buffer);
|
|
Packit |
e9ba0d |
*input_buffer = NULL;
|
|
Packit |
e9ba0d |
*input_len = 0;
|
|
Packit |
e9ba0d |
return -1;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
if (*input_len < sizeof(struct hmsg_header) + hdr.len) {
|
|
Packit |
e9ba0d |
/* Not enough data. */
|
|
Packit |
e9ba0d |
return sizeof(struct hmsg_header) + hdr.len - *input_len;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
if (hdr.type != expected_type) {
|
|
Packit |
e9ba0d |
if (expected_type == NOTIFICATION) return -1;
|
|
Packit |
e9ba0d |
log_warnx("control", "incorrect received message type (expected: %d, received: %d)",
|
|
Packit |
e9ba0d |
expected_type, hdr.type);
|
|
Packit |
e9ba0d |
goto end;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
if (t && !hdr.len) {
|
|
Packit |
e9ba0d |
log_warnx("control", "no payload available in answer");
|
|
Packit |
e9ba0d |
goto end;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
if (t) {
|
|
Packit |
e9ba0d |
/* We have data to unserialize. */
|
|
Packit |
e9ba0d |
if (marshal_unserialize_(mi, *input_buffer + sizeof(struct hmsg_header),
|
|
Packit |
e9ba0d |
hdr.len, t, NULL, 0, 0) <= 0) {
|
|
Packit |
e9ba0d |
log_warnx("control", "unable to deserialize received data");
|
|
Packit |
e9ba0d |
goto end;
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
}
|
|
Packit |
e9ba0d |
|
|
Packit |
e9ba0d |
rc = 0;
|
|
Packit |
e9ba0d |
end:
|
|
Packit |
e9ba0d |
/* Discard input buffer */
|
|
Packit |
e9ba0d |
*input_len -= sizeof(struct hmsg_header) + hdr.len;
|
|
Packit |
e9ba0d |
if (*input_len == 0) {
|
|
Packit |
e9ba0d |
free(*input_buffer);
|
|
Packit |
e9ba0d |
*input_buffer = NULL;
|
|
Packit |
e9ba0d |
} else
|
|
Packit |
e9ba0d |
memmove(*input_buffer,
|
|
Packit |
e9ba0d |
*input_buffer + sizeof(struct hmsg_header) + hdr.len,
|
|
Packit |
e9ba0d |
*input_len);
|
|
Packit |
e9ba0d |
return rc;
|
|
Packit |
e9ba0d |
}
|