first vesrion of working socket based rsync. It still needs a lot of

work, but at least it works :-)
This commit is contained in:
Andrew Tridgell
1998-05-09 13:58:54 +00:00
parent bc2e93eb8e
commit f0fca04e4e
9 changed files with 616 additions and 74 deletions

View File

@@ -21,8 +21,9 @@ SHELL=/bin/sh
.SUFFIXES: .c .o
LIBOBJ=lib/getopt.o lib/fnmatch.o lib/zlib.o lib/compat.o
OBJS1=rsync.o exclude.o util.o md4.o main.o checksum.o match.o syscall.o
OBJS=$(OBJS1) flist.o io.o compat.o hlink.o token.o uidlist.o socket.o $(LIBOBJ)
OBJS1=rsync.o exclude.o util.o md4.o main.o checksum.o match.o syscall.o log.o
DAEMON_OBJ = params.o loadparm.o
OBJS=$(OBJS1) $(DAEMON_OBJ) flist.o io.o compat.o hlink.o token.o uidlist.o socket.o $(LIBOBJ)
# note that the -I. is needed to handle config.h when using VPATH
.c.o:

View File

@@ -42,7 +42,7 @@ AC_FUNC_MMAP
AC_FUNC_UTIME_NULL
AC_CHECK_FUNCS(waitpid strtok pipe getcwd mkdir strdup strerror chown chmod mknod)
AC_CHECK_FUNCS(fchmod fstat strchr bcopy bzero readlink link utime utimes)
AC_CHECK_FUNCS(memmove getopt_long lchown setlinebuf vsnprintf)
AC_CHECK_FUNCS(memmove getopt_long lchown setlinebuf vsnprintf setsid)
echo $ac_n "checking for working fnmatch... $ac_c"
AC_TRY_RUN([#include <fnmatch.h>

48
io.c
View File

@@ -423,10 +423,16 @@ void write_buf(int f,char *buf,int len)
total_written += len;
}
/* write a string to the connection */
void write_sbuf(int f,char *buf)
{
write_buf(f, buf, strlen(buf));
}
void write_byte(int f,unsigned char c)
{
write_buf(f,(char *)&c,1);
write_buf(f,(char *)&c,1);
}
void write_flush(int f)
@@ -434,3 +440,43 @@ void write_flush(int f)
}
int read_line(int f, char *buf, int maxlen)
{
while (maxlen) {
read_buf(f, buf, 1);
if (buf[0] == '\n') {
buf[0] = 0;
break;
}
if (buf[0] != '\r') {
buf++;
maxlen--;
}
}
if (maxlen == 0) {
*buf = 0;
return 0;
}
return 1;
}
void io_printf(int fd, const char *format, ...)
{
va_list ap;
char buf[1024];
int len;
va_start(ap, format);
#if HAVE_VSNPRINTF
len = vsnprintf(buf, sizeof(buf)-1, format, ap);
#else
len = vsprintf(buf, format, ap);
#endif
va_end(ap);
if (len < 0) exit_cleanup(1);
write_sbuf(fd, buf);
}

249
main.c
View File

@@ -21,7 +21,7 @@
int verbose = 0;
int always_checksum = 0;
time_t starttime;
time_t starttime = 0;
int64 total_size = 0;
int block_size=BLOCK_SIZE;
@@ -57,6 +57,9 @@ int numeric_ids = 0;
int force_delete = 0;
int io_timeout = 0;
int io_error = 0;
int read_only = 0;
static int module_id;
static int port = RSYNC_PORT;
static char *shell_cmd;
@@ -64,9 +67,9 @@ static char *shell_cmd;
extern int csum_length;
int am_server = 0;
int am_sender;
int am_sender=0;
int recurse = 0;
int am_daemon;
int am_daemon=0;
static void usage(int fd);
@@ -335,12 +338,24 @@ static void do_server_sender(int f_in, int f_out, int argc,char *argv[])
argv[i] += l+1;
}
if (am_daemon) {
char *name = lp_name(module_id);
int l = strlen(name);
for (i=0;i<argc;i++) {
if (strncmp(argv[i], name, l) == 0) {
argv[i] += l;
if (!*argv[i]) argv[i] = ".";
}
}
}
if (argc == 0 && recurse) {
argc=1;
argv--;
argv[0] = ".";
}
rprintf(FINFO,"sending file list\n");
flist = send_file_list(f_out,argc,argv);
send_files(flist,f_out,f_in);
@@ -389,11 +404,23 @@ static void do_server_recv(int f_in, int f_out, int argc,char *argv[])
if (verbose > 2)
rprintf(FINFO,"server_recv(%d) starting pid=%d\n",argc,(int)getpid());
if (am_daemon) {
char *name = lp_name(module_id);
int i, l = strlen(name);
for (i=0;i<argc;i++) {
if (strncmp(argv[i], name, l) == 0) {
argv[i] += l;
if (!*argv[i]) argv[i] = ".";
}
rprintf(FINFO,"argv[%d]=%s\n", i, argv[i]);
}
}
if (argc > 0) {
dir = argv[0];
argc--;
argv++;
if (chdir(dir) != 0) {
if (!am_daemon && chdir(dir) != 0) {
rprintf(FERROR,"chdir %s : %s (4)\n",
dir,strerror(errno));
exit_cleanup(1);
@@ -487,13 +514,15 @@ static int client_run(int f_in, int f_out, int pid, int argc, char *argv[])
int start_socket_client(char *host, char *path, int argc, char *argv[])
{
int fd;
char *sargs[100];
int fd, i;
char *sargs[MAX_ARGS];
int sargc=0;
char line[1024];
char *p;
int version;
fd = open_socket_out(host, port);
if (fd == -1) {
rprintf(FERROR,"failed to connect to %s - %s\n", host, strerror(errno));
exit_cleanup(1);
}
@@ -506,6 +535,41 @@ int start_socket_client(char *host, char *path, int argc, char *argv[])
sargs[sargc] = NULL;
p = strchr(path,'/');
if (p) *p = 0;
io_printf(fd,"%s\n",path);
if (p) *p = '/';
if (!read_line(fd, line, sizeof(line)-1)) {
return -1;
}
if (sscanf(line,"RSYNCD %d", &version) != 1) {
return -1;
}
while (1) {
if (!read_line(fd, line, sizeof(line)-1)) {
return -1;
}
if (strcmp(line,"RSYNCD: OK") == 0) break;
rprintf(FINFO,"%s\n", line);
}
for (i=0;i<sargc;i++) {
io_printf(fd,"%s\n", sargs[i]);
}
io_printf(fd,"\n");
#if 0
while (1) {
if (!read_line(fd, line, sizeof(line)-1)) {
return -1;
}
rprintf(FINFO,"%s\n", line);
}
#endif
return client_run(fd, fd, -1, argc, argv);
}
@@ -874,6 +938,161 @@ static void parse_arguments(int argc, char *argv[])
}
}
static int rsync_module(int fd, int i)
{
int argc=0;
char *argv[MAX_ARGS];
char **argp;
char line[1024];
module_id = i;
if (lp_read_only(i))
read_only = 1;
rprintf(FERROR,"rsyncd starting\n");
if (chroot(lp_path(i))) {
io_printf(fd,"ERROR: chroot failed\n");
return -1;
}
if (chdir("/")) {
io_printf(fd,"ERROR: chdir failed\n");
return -1;
}
if (setgid(lp_gid(i))) {
io_printf(fd,"ERROR: setgid failed\n");
return -1;
}
if (setuid(lp_uid(i))) {
io_printf(fd,"ERROR: setuid failed\n");
return -1;
}
io_printf(fd,"RSYNCD: OK\n");
argv[argc++] = "rsyncd";
while (1) {
if (!read_line(fd, line, sizeof(line)-1)) {
return -1;
}
if (!*line) break;
argv[argc] = strdup(line);
if (!argv[argc]) {
return -1;
}
argc++;
if (argc == MAX_ARGS) {
return -1;
}
}
parse_arguments(argc, argv);
/* don't allow the logs to be flooded too fast */
if (verbose > 1) verbose = 1;
argc -= optind;
argp = argv + optind;
optind = 0;
start_server(fd, fd, argc, argp);
return 0;
}
static void send_listing(int fd)
{
int n = lp_numservices();
int i;
for (i=0;i<n;i++)
if (lp_list(i))
io_printf(fd, "%-15s\t%s\n", lp_name(i), lp_comment(i));
}
/* this is called when a socket connection is established to a client
and we want to start talking. The setup of the system is done from
here */
static int start_daemon(int fd)
{
char line[1024];
char *motd;
set_socket_options(fd,"SO_KEEPALIVE");
io_printf(fd,"RSYNCD %d\n", PROTOCOL_VERSION);
motd = lp_motd_file();
if (*motd) {
FILE *f = fopen(motd,"r");
while (f && !feof(f)) {
int len = fread(line, 1, sizeof(line)-1, f);
if (len > 0) {
line[len] = 0;
io_printf(fd,"%s", line);
}
}
if (f) fclose(f);
io_printf(fd,"\n");
}
/* read a single line indicating the resource that is wanted */
while (1) {
int i;
line[0] = 0;
if (!read_line(fd, line, sizeof(line)-1)) {
return -1;
}
if (!*line || strcmp(line,"#list")==0) {
send_listing(fd);
return -1;
}
if (*line == '#') {
/* it's some sort of command that I don't understand */
io_printf(fd,"ERROR: Unknown command '%s'\n", line);
return -1;
}
i = lp_number(line);
if (i == -1) {
io_printf(fd,"ERROR: Unknown module '%s'\n", line);
return -1;
}
return rsync_module(fd, i);
}
return 0;
}
static int daemon_main(void)
{
if (!lp_load(RSYNCD_CONF)) {
exit_cleanup(1);
}
if (is_a_socket(STDIN_FILENO)) {
/* we are running via inetd */
return start_daemon(STDIN_FILENO);
}
become_daemon();
return start_accept_loop(port, start_daemon);
}
int main(int argc,char *argv[])
{
@@ -888,17 +1107,19 @@ int main(int argc,char *argv[])
parse_arguments(argc, argv);
while (optind) {
argc--;
argv++;
optind--;
}
argc -= optind;
argv += optind;
optind = 0;
signal(SIGCHLD,SIG_IGN);
signal(SIGINT,SIGNAL_CAST sig_int);
signal(SIGPIPE,SIGNAL_CAST sig_int);
signal(SIGHUP,SIGNAL_CAST sig_int);
if (am_daemon) {
return daemon_main();
}
if (dry_run)
verbose = MAX(verbose,1);

View File

@@ -19,6 +19,41 @@ BEGIN {
}
}
/^FN_LOCAL_BOOL/ {
split($0,a,"[,()]")
printf "BOOL %s(int );\n", a[2]
}
/^FN_LOCAL_STRING/ {
split($0,a,"[,()]")
printf "char *%s(int );\n", a[2]
}
/^FN_LOCAL_INT/ {
split($0,a,"[,()]")
printf "int %s(int );\n", a[2]
}
/^FN_LOCAL_CHAR/ {
split($0,a,"[,()]")
printf "char %s(int );\n", a[2]
}
/^FN_GLOBAL_BOOL/ {
split($0,a,"[,()]")
printf "BOOL %s(void);\n", a[2]
}
/^FN_GLOBAL_STRING/ {
split($0,a,"[,()]")
printf "char *%s(void);\n", a[2]
}
/^FN_GLOBAL_INT/ {
split($0,a,"[,()]")
printf "int %s(void);\n", a[2]
}
/^static|^extern/ || !/^[a-zA-Z]/ || /[;]/ {
next;
}

10
rsync.h
View File

@@ -21,6 +21,9 @@
#define RSYNC_RSH_ENV "RSYNC_RSH"
#define RSYNC_NAME "rsync"
#define RSYNCD_CONF "/etc/rsyncd.conf"
#define RSYNCD_LOG "/var/adm/rsyncd.log"
#define BACKUP_SUFFIX "~"
/* a non-zero CHAR_OFFSET makes the rolling sum stronger, but is
@@ -50,6 +53,8 @@
#define CHUNK_SIZE (32*1024)
#define MAX_MAP_SIZE (4*1024*1024)
#define MAX_ARGS 100
#define BLOCKING_TIMEOUT 10
#define FERROR 1
@@ -170,6 +175,9 @@
#include <grp.h>
#include <stdarg.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#ifndef S_IFLNK
#define S_IFLNK 0120000
@@ -179,6 +187,8 @@
#define S_ISLNK(mode) (((mode) & S_IFLNK) == S_IFLNK)
#endif
#define BOOL int
#ifndef uchar
#define uchar unsigned char
#endif

272
socket.c
View File

@@ -21,7 +21,277 @@
*/
#include "rsync.h"
/* open a socket to a tcp remote host with the specified port
based on code from Warren */
int open_socket_out(char *host, int port)
{
return -1;
int type = SOCK_STREAM;
struct sockaddr_in sock_out;
int res;
struct hostent *hp;
res = socket(PF_INET, type, 0);
if (res == -1) {
return -1;
}
hp = gethostbyname(host);
if (!hp) {
rprintf(FERROR,"unknown host: %s\n", host);
return -1;
}
memcpy(&sock_out.sin_addr, hp->h_addr, hp->h_length);
sock_out.sin_port = htons(port);
sock_out.sin_family = PF_INET;
if (connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out))) {
close(res);
rprintf(FERROR,"failed to connect to %s - %s\n", host, strerror(errno));
return -1;
}
return res;
}
/****************************************************************************
open a socket of the specified type, port and address for incoming data
****************************************************************************/
static int open_socket_in(int type, int port)
{
struct hostent *hp;
struct sockaddr_in sock;
char host_name[200];
int res;
int one=1;
/* get my host name */
if (gethostname(host_name, sizeof(host_name)) == -1) {
rprintf(FERROR,"gethostname failed\n");
return -1;
}
/* get host info */
if ((hp = gethostbyname(host_name)) == 0) {
rprintf(FERROR,"gethostbyname: Unknown host %s\n",host_name);
return -1;
}
bzero((char *)&sock,sizeof(sock));
memcpy((char *)&sock.sin_addr,(char *)hp->h_addr, hp->h_length);
sock.sin_port = htons(port);
sock.sin_family = hp->h_addrtype;
sock.sin_addr.s_addr = INADDR_ANY;
res = socket(hp->h_addrtype, type, 0);
if (res == -1) {
rprintf(FERROR,"socket failed\n");
return -1;
}
setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one));
/* now we've got a socket - we need to bind it */
if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) == -1) {
rprintf(FERROR,"bind failed on port %d\n", port);
close(res);
return -1;
}
return res;
}
/****************************************************************************
determine if a file descriptor is in fact a socket
****************************************************************************/
int is_a_socket(int fd)
{
int v,l;
l = sizeof(int);
return(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0);
}
int start_accept_loop(int port, int (*fn)(int ))
{
int s;
signal(SIGCLD, SIG_IGN);
/* open an incoming socket */
s = open_socket_in(SOCK_STREAM, port);
if (s == -1)
return(-1);
/* ready to listen */
if (listen(s, 5) == -1) {
close(s);
return -1;
}
/* now accept incoming connections - forking a new process
for each incoming connection */
while (1) {
fd_set fds;
int fd;
struct sockaddr addr;
int in_addrlen = sizeof(addr);
FD_ZERO(&fds);
FD_SET(s, &fds);
if (select(s+1, &fds, NULL, NULL, NULL) != 1) {
continue;
}
if(!FD_ISSET(s, &fds)) continue;
fd = accept(s,&addr,&in_addrlen);
if (fd == -1) continue;
if (fork()==0) {
close(s);
_exit(fn(fd));
}
close(fd);
}
return 0;
}
enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
struct
{
char *name;
int level;
int option;
int value;
int opttype;
} socket_options[] = {
{"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
{"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
{"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
#ifdef TCP_NODELAY
{"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
#endif
#ifdef IPTOS_LOWDELAY
{"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
#endif
#ifdef IPTOS_THROUGHPUT
{"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
#endif
#ifdef SO_SNDBUF
{"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
#endif
#ifdef SO_RCVBUF
{"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
#endif
#ifdef SO_SNDLOWAT
{"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
#endif
#ifdef SO_RCVLOWAT
{"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
#endif
#ifdef SO_SNDTIMEO
{"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
#endif
#ifdef SO_RCVTIMEO
{"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
#endif
{NULL,0,0,0,0}};
/****************************************************************************
set user socket options
****************************************************************************/
void set_socket_options(int fd, char *options)
{
char *tok;
options = strdup(options);
if (!options) out_of_memory("set_socket_options");
for (tok=strtok(options, " \t,"); tok; tok=strtok(NULL," \t,")) {
int ret=0,i;
int value = 1;
char *p;
int got_value = 0;
if ((p = strchr(tok,'='))) {
*p = 0;
value = atoi(p+1);
got_value = 1;
}
for (i=0;socket_options[i].name;i++)
if (strcmp(socket_options[i].name,tok)==0)
break;
if (!socket_options[i].name) {
rprintf(FERROR,"Unknown socket option %s\n",tok);
continue;
}
switch (socket_options[i].opttype) {
case OPT_BOOL:
case OPT_INT:
ret = setsockopt(fd,socket_options[i].level,
socket_options[i].option,(char *)&value,sizeof(int));
break;
case OPT_ON:
if (got_value)
rprintf(FERROR,"syntax error - %s does not take a value\n",tok);
{
int on = socket_options[i].value;
ret = setsockopt(fd,socket_options[i].level,
socket_options[i].option,(char *)&on,sizeof(int));
}
break;
}
if (ret != 0)
rprintf(FERROR,"Failed to set socket option %s\n",tok);
}
free(options);
}
/****************************************************************************
become a daemon, discarding the controlling terminal
****************************************************************************/
void become_daemon(void)
{
if (fork())
_exit(0);
/* detach from the terminal */
#ifdef HAVE_SETSID
setsid();
#else
#ifdef TIOCNOTTY
{
int i = open("/dev/tty", O_RDWR);
if (i >= 0)
{
ioctl(i, (int) TIOCNOTTY, (char *)0);
close(i);
}
}
#endif /* TIOCNOTTY */
#endif
close(0);
close(1);
close(2);
}

View File

@@ -23,16 +23,21 @@
#include "rsync.h"
extern int dry_run;
extern int read_only;
#define CHECK_RO if (read_only) {errno = EROFS; return -1;}
int do_unlink(char *fname)
{
if (dry_run) return 0;
CHECK_RO
return unlink(fname);
}
int do_symlink(char *fname1, char *fname2)
{
if (dry_run) return 0;
CHECK_RO
return symlink(fname1, fname2);
}
@@ -40,6 +45,7 @@ int do_symlink(char *fname1, char *fname2)
int do_link(char *fname1, char *fname2)
{
if (dry_run) return 0;
CHECK_RO
return link(fname1, fname2);
}
#endif
@@ -47,6 +53,7 @@ int do_link(char *fname1, char *fname2)
int do_lchown(const char *path, uid_t owner, gid_t group)
{
if (dry_run) return 0;
CHECK_RO
return lchown(path, owner, group);
}
@@ -54,6 +61,7 @@ int do_lchown(const char *path, uid_t owner, gid_t group)
int do_mknod(char *pathname, mode_t mode, dev_t dev)
{
if (dry_run) return 0;
CHECK_RO
return mknod(pathname, mode, dev);
}
#endif
@@ -61,12 +69,14 @@ int do_mknod(char *pathname, mode_t mode, dev_t dev)
int do_rmdir(char *pathname)
{
if (dry_run) return 0;
CHECK_RO
return rmdir(pathname);
}
int do_open(char *pathname, int flags, mode_t mode)
{
if (dry_run) return -1;
CHECK_RO
return open(pathname, flags, mode);
}
@@ -74,6 +84,7 @@ int do_open(char *pathname, int flags, mode_t mode)
int do_chmod(const char *path, mode_t mode)
{
if (dry_run) return 0;
CHECK_RO
return chmod(path, mode);
}
#endif
@@ -81,18 +92,21 @@ int do_chmod(const char *path, mode_t mode)
int do_rename(char *fname1, char *fname2)
{
if (dry_run) return 0;
CHECK_RO
return rename(fname1, fname2);
}
int do_mkdir(char *fname, mode_t mode)
{
if (dry_run) return 0;
CHECK_RO
return mkdir(fname, mode);
}
char *do_mktemp(char *template)
{
if (dry_run) return NULL;
if (read_only) {errno = EROFS; return NULL;}
return mktemp(template);
}

55
util.c
View File

@@ -446,58 +446,3 @@ void kill_all(int sig)
}
}
/* this is the rsync debugging function. Call it with FINFO or FERROR */
void rprintf(int fd, const char *format, ...)
{
va_list ap;
char buf[1024];
int len;
FILE *f=NULL;
va_start(ap, format);
#if HAVE_VSNPRINTF
len = vsnprintf(buf, sizeof(buf)-1, format, ap);
#else
len = vsprintf(buf, format, ap);
#endif
va_end(ap);
if (len < 0) exit_cleanup(1);
if (fd == FERROR) {
f = stderr;
}
if (fd == FINFO) {
extern int am_server;
if (am_server)
f = stderr;
else
f = stdout;
}
if (!f) exit_cleanup(1);
if (fwrite(buf, len, 1, f) != 1) exit_cleanup(1);
}
void rflush(int fd)
{
FILE *f = NULL;
if (fd == FERROR) {
f = stderr;
}
if (fd == FINFO) {
extern int am_server;
if (am_server)
f = stderr;
else
f = stdout;
}
if (!f) exit_cleanup(1);
fflush(f);
}