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 "conf.h"
|
||||
#include "control.h"
|
||||
#include "control_common.h"
|
||||
#include "logger.h"
|
||||
#include "xalloc.h"
|
||||
|
||||
|
@ -33,17 +34,58 @@ static splay_tree_t *control_socket_tree;
|
|||
extern char *controlsocketname;
|
||||
|
||||
static void handle_control_data(struct bufferevent *event, void *data) {
|
||||
char *line = evbuffer_readline(event->input);
|
||||
if(!line)
|
||||
tinc_ctl_request_t req;
|
||||
size_t size;
|
||||
tinc_ctl_request_t res;
|
||||
struct evbuffer *res_data = NULL;
|
||||
|
||||
if(EVBUFFER_LENGTH(event->input) < sizeof(tinc_ctl_request_t))
|
||||
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"));
|
||||
event_loopexit(NULL);
|
||||
return;
|
||||
goto respond;
|
||||
}
|
||||
|
||||
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);
|
||||
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) {
|
||||
int newfd;
|
||||
struct bufferevent *ev;
|
||||
tinc_ctl_greeting_t greeting;
|
||||
|
||||
newfd = accept(fd, NULL, NULL);
|
||||
|
||||
|
@ -77,6 +120,17 @@ static void handle_new_control_socket(int fd, short events, void *data) {
|
|||
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);
|
||||
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 "protocol.h"
|
||||
#include "xalloc.h"
|
||||
#include "protocol.h"
|
||||
#include "control_common.h"
|
||||
|
||||
/* The name this program was run with. */
|
||||
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 fd;
|
||||
struct sockaddr_un addr;
|
||||
int fd;
|
||||
int len;
|
||||
tinc_ctl_greeting_t greeting;
|
||||
tinc_ctl_request_t req;
|
||||
|
||||
program_name = argv[0];
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
|
@ -399,6 +509,18 @@ int main(int argc, char *argv[], char *envp[]) {
|
|||
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;
|
||||
socklen_t credlen = sizeof cred;
|
||||
|
||||
|
@ -413,18 +535,15 @@ int main(int argc, char *argv[], char *envp[]) {
|
|||
}
|
||||
|
||||
if(!strcasecmp(argv[optind], "stop")) {
|
||||
write(fd, "stop\n", 5);
|
||||
return 0;
|
||||
return send_ctl_request_cooked(fd, REQ_STOP, NULL, 0) != -1;
|
||||
}
|
||||
|
||||
if(!strcasecmp(argv[optind], "reload")) {
|
||||
write(fd, "reload\n", 7);
|
||||
return 0;
|
||||
return send_ctl_request_cooked(fd, REQ_RELOAD, NULL, 0) != -1;
|
||||
}
|
||||
|
||||
if(!strcasecmp(argv[optind], "restart")) {
|
||||
write(fd, "restart\n", 8);
|
||||
return 0;
|
||||
return send_ctl_request_cooked(fd, REQ_RESTART, NULL, 0) != -1;
|
||||
}
|
||||
|
||||
fprintf(stderr, _("Unknown command `%s'.\n"), argv[optind]);
|
||||
|
|
Loading…
Reference in a new issue