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…
	
	Add table
		Add a link
		
	
		Reference in a new issue