Fancier protocol for control socket
* pass error status back * pass message boundaries
This commit is contained in:
parent
b0b5299184
commit
50ad3f2a89
3 changed files with 232 additions and 13 deletions
|
@ -24,6 +24,7 @@
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "control.h"
|
#include "control.h"
|
||||||
|
#include "control_common.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "xalloc.h"
|
#include "xalloc.h"
|
||||||
|
|
||||||
|
@ -33,17 +34,58 @@ static splay_tree_t *control_socket_tree;
|
||||||
extern char *controlsocketname;
|
extern char *controlsocketname;
|
||||||
|
|
||||||
static void handle_control_data(struct bufferevent *event, void *data) {
|
static void handle_control_data(struct bufferevent *event, void *data) {
|
||||||
char *line = evbuffer_readline(event->input);
|
tinc_ctl_request_t req;
|
||||||
if(!line)
|
size_t size;
|
||||||
|
tinc_ctl_request_t res;
|
||||||
|
struct evbuffer *res_data = NULL;
|
||||||
|
|
||||||
|
if(EVBUFFER_LENGTH(event->input) < sizeof(tinc_ctl_request_t))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(!strcasecmp(line, "stop")) {
|
/* Copy the structure to ensure alignment */
|
||||||
|
memcpy(&req, EVBUFFER_DATA(event->input), sizeof(tinc_ctl_request_t));
|
||||||
|
|
||||||
|
if(EVBUFFER_LENGTH(event->input) < req.length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(req.length < sizeof(tinc_ctl_request_t))
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
memset(&res, 0, sizeof res);
|
||||||
|
res.type = req.type;
|
||||||
|
res.id = req.id;
|
||||||
|
|
||||||
|
res_data = evbuffer_new();
|
||||||
|
if (res_data == NULL) {
|
||||||
|
res.res_errno = ENOMEM;
|
||||||
|
goto respond;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(req.type == REQ_STOP) {
|
||||||
logger(LOG_NOTICE, _("Got stop command"));
|
logger(LOG_NOTICE, _("Got stop command"));
|
||||||
event_loopexit(NULL);
|
event_loopexit(NULL);
|
||||||
return;
|
goto respond;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger(LOG_DEBUG, _("Malformed control command received"));
|
logger(LOG_DEBUG, _("Malformed control command received"));
|
||||||
|
res.res_errno = EINVAL;
|
||||||
|
|
||||||
|
respond:
|
||||||
|
res.length = (sizeof res)
|
||||||
|
+ ((res_data == NULL) ? 0 : EVBUFFER_LENGTH(res_data));
|
||||||
|
evbuffer_drain(event->input, req.length);
|
||||||
|
if(bufferevent_write(event, &res, sizeof res) == -1)
|
||||||
|
goto failure;
|
||||||
|
if(res_data != NULL) {
|
||||||
|
if(bufferevent_write_buffer(event, res_data) == -1)
|
||||||
|
goto failure;
|
||||||
|
evbuffer_free(res_data);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
failure:
|
||||||
|
logger(LOG_INFO, _("Closing control socket on error"));
|
||||||
|
evbuffer_free(res_data);
|
||||||
close(event->ev_read.ev_fd);
|
close(event->ev_read.ev_fd);
|
||||||
splay_delete(control_socket_tree, event);
|
splay_delete(control_socket_tree, event);
|
||||||
}
|
}
|
||||||
|
@ -61,6 +103,7 @@ static void handle_control_error(struct bufferevent *event, short what, void *da
|
||||||
static void handle_new_control_socket(int fd, short events, void *data) {
|
static void handle_new_control_socket(int fd, short events, void *data) {
|
||||||
int newfd;
|
int newfd;
|
||||||
struct bufferevent *ev;
|
struct bufferevent *ev;
|
||||||
|
tinc_ctl_greeting_t greeting;
|
||||||
|
|
||||||
newfd = accept(fd, NULL, NULL);
|
newfd = accept(fd, NULL, NULL);
|
||||||
|
|
||||||
|
@ -77,6 +120,17 @@ static void handle_new_control_socket(int fd, short events, void *data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(&greeting, 0, sizeof greeting);
|
||||||
|
greeting.version = TINC_CTL_VERSION_CURRENT;
|
||||||
|
if(bufferevent_write(ev, &greeting, sizeof greeting) == -1) {
|
||||||
|
logger(LOG_ERR,
|
||||||
|
_("Cannot send greeting for new control connection: %s"),
|
||||||
|
strerror(errno));
|
||||||
|
bufferevent_free(ev);
|
||||||
|
close(newfd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bufferevent_enable(ev, EV_READ);
|
bufferevent_enable(ev, EV_READ);
|
||||||
splay_insert(control_socket_tree, ev);
|
splay_insert(control_socket_tree, ev);
|
||||||
|
|
||||||
|
|
46
src/control_common.h
Normal file
46
src/control_common.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
control_protocol.h -- control socket protocol.
|
||||||
|
Copyright (C) 2007 Scott Lamb <slamb@slamb.org>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __TINC_CONTROL_PROTOCOL_H__
|
||||||
|
#define __TINC_CONTROL_PROTOCOL_H__
|
||||||
|
|
||||||
|
enum request_type {
|
||||||
|
REQ_STOP,
|
||||||
|
REQ_RELOAD,
|
||||||
|
REQ_RESTART,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TINC_CTL_VERSION_CURRENT 0
|
||||||
|
|
||||||
|
/* This greeting is sent by the server on socket open. */
|
||||||
|
typedef struct tinc_ctl_greeting_t {
|
||||||
|
int version;
|
||||||
|
} tinc_ctl_greeting_t;
|
||||||
|
|
||||||
|
/* A single request or response header. */
|
||||||
|
typedef struct tinc_ctl_request_t {
|
||||||
|
size_t length; /* total length, including the header */
|
||||||
|
enum request_type type;
|
||||||
|
int id;
|
||||||
|
int res_errno; /* used only for responses */
|
||||||
|
} tinc_ctl_request_t;
|
||||||
|
|
||||||
|
#endif
|
135
src/tincctl.c
135
src/tincctl.c
|
@ -30,8 +30,9 @@
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
|
||||||
#include "protocol.h"
|
|
||||||
#include "xalloc.h"
|
#include "xalloc.h"
|
||||||
|
#include "protocol.h"
|
||||||
|
#include "control_common.h"
|
||||||
|
|
||||||
/* The name this program was run with. */
|
/* The name this program was run with. */
|
||||||
char *program_name = NULL;
|
char *program_name = NULL;
|
||||||
|
@ -327,9 +328,118 @@ static void make_names(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int fullread(int fd, void *data, size_t datalen) {
|
||||||
|
int rv, len = 0;
|
||||||
|
|
||||||
|
while (len < datalen) {
|
||||||
|
rv = read(fd, data + len, datalen - len);
|
||||||
|
if(rv == -1 && errno == EINTR)
|
||||||
|
continue;
|
||||||
|
else if (rv == -1)
|
||||||
|
return rv;
|
||||||
|
else if (rv == 0) {
|
||||||
|
errno = ENODATA;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
len += rv;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Send a request (raw)
|
||||||
|
*/
|
||||||
|
static int send_ctl_request(int fd, enum request_type type,
|
||||||
|
void const *outdata, size_t outdatalen,
|
||||||
|
int *res_errno_p, void **indata_p,
|
||||||
|
size_t *indatalen_p) {
|
||||||
|
tinc_ctl_request_t req;
|
||||||
|
int rv;
|
||||||
|
struct iovec vector[2] = {
|
||||||
|
{&req, sizeof(req)},
|
||||||
|
{(void*) outdata, outdatalen}
|
||||||
|
};
|
||||||
|
void *indata;
|
||||||
|
|
||||||
|
if(res_errno_p == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memset(&req, 0, sizeof req);
|
||||||
|
req.length = sizeof req + outdatalen;
|
||||||
|
req.type = type;
|
||||||
|
req.res_errno = 0;
|
||||||
|
|
||||||
|
while((rv = writev(fd, vector, 2)) == -1 && errno == EINTR) ;
|
||||||
|
if(rv != req.length)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(fullread(fd, &req, sizeof req) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(req.length < sizeof req) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(req.length > sizeof req) {
|
||||||
|
if (indata_p == NULL) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
indata = xmalloc(req.length - sizeof req);
|
||||||
|
|
||||||
|
if(fullread(fd, indata, req.length - sizeof req) == -1) {
|
||||||
|
free(indata);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*indata_p = indata;
|
||||||
|
if(indatalen_p != NULL)
|
||||||
|
*indatalen_p = req.length - sizeof req;
|
||||||
|
}
|
||||||
|
|
||||||
|
*res_errno_p = req.res_errno;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Send a request (with printfs)
|
||||||
|
*/
|
||||||
|
static int send_ctl_request_cooked(int fd, enum request_type type,
|
||||||
|
void const *outdata, size_t outdatalen)
|
||||||
|
{
|
||||||
|
int res_errno = -1;
|
||||||
|
char *buf = NULL;
|
||||||
|
size_t buflen = 0;
|
||||||
|
|
||||||
|
if(send_ctl_request(fd, type, outdata, outdatalen, &res_errno,
|
||||||
|
(void**) &buf, &buflen)) {
|
||||||
|
fprintf(stderr, _("Error sending request: %s\n"), strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(buf != NULL) {
|
||||||
|
printf("%*s", buflen, buf);
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(res_errno != 0) {
|
||||||
|
fprintf(stderr, _("Server reported error: %s\n"), strerror(res_errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[], char *envp[]) {
|
int main(int argc, char *argv[], char *envp[]) {
|
||||||
int fd;
|
|
||||||
struct sockaddr_un addr;
|
struct sockaddr_un addr;
|
||||||
|
int fd;
|
||||||
|
int len;
|
||||||
|
tinc_ctl_greeting_t greeting;
|
||||||
|
tinc_ctl_request_t req;
|
||||||
|
|
||||||
program_name = argv[0];
|
program_name = argv[0];
|
||||||
|
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
|
@ -399,6 +509,18 @@ int main(int argc, char *argv[], char *envp[]) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(fullread(fd, &greeting, sizeof greeting) == -1) {
|
||||||
|
fprintf(stderr, _("Cannot read greeting from control socket: %s\n"),
|
||||||
|
strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(greeting.version != TINC_CTL_VERSION_CURRENT) {
|
||||||
|
fprintf(stderr, _("Version mismatch: server %d, client %d\n"),
|
||||||
|
greeting.version, TINC_CTL_VERSION_CURRENT);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
struct ucred cred;
|
struct ucred cred;
|
||||||
socklen_t credlen = sizeof cred;
|
socklen_t credlen = sizeof cred;
|
||||||
|
|
||||||
|
@ -413,18 +535,15 @@ int main(int argc, char *argv[], char *envp[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!strcasecmp(argv[optind], "stop")) {
|
if(!strcasecmp(argv[optind], "stop")) {
|
||||||
write(fd, "stop\n", 5);
|
return send_ctl_request_cooked(fd, REQ_STOP, NULL, 0) != -1;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!strcasecmp(argv[optind], "reload")) {
|
if(!strcasecmp(argv[optind], "reload")) {
|
||||||
write(fd, "reload\n", 7);
|
return send_ctl_request_cooked(fd, REQ_RELOAD, NULL, 0) != -1;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!strcasecmp(argv[optind], "restart")) {
|
if(!strcasecmp(argv[optind], "restart")) {
|
||||||
write(fd, "restart\n", 8);
|
return send_ctl_request_cooked(fd, REQ_RESTART, NULL, 0) != -1;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, _("Unknown command `%s'.\n"), argv[optind]);
|
fprintf(stderr, _("Unknown command `%s'.\n"), argv[optind]);
|
||||||
|
|
Loading…
Reference in a new issue