From f0fca04e4e136c4a487a922e8fb09acf46aeafa0 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sat, 9 May 1998 13:58:54 +0000 Subject: [PATCH] first vesrion of working socket based rsync. It still needs a lot of work, but at least it works :-) --- Makefile.in | 5 +- configure.in | 2 +- io.c | 48 ++++++++- main.c | 249 +++++++++++++++++++++++++++++++++++++++++++--- mkproto.awk | 35 +++++++ rsync.h | 10 ++ socket.c | 272 ++++++++++++++++++++++++++++++++++++++++++++++++++- syscall.c | 14 +++ util.c | 55 ----------- 9 files changed, 616 insertions(+), 74 deletions(-) diff --git a/Makefile.in b/Makefile.in index 8d1c173f..f8ed5464 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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: diff --git a/configure.in b/configure.in index 9749558d..7755272a 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/io.c b/io.c index f5816ed9..587b3c74 100644 --- a/io.c +++ b/io.c @@ -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); +} diff --git a/main.c b/main.c index 527daeb6..0bcc7f54 100644 --- a/main.c +++ b/main.c @@ -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 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 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 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 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); diff --git a/mkproto.awk b/mkproto.awk index 0e3f5bf3..a08b1aff 100644 --- a/mkproto.awk +++ b/mkproto.awk @@ -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; } diff --git a/rsync.h b/rsync.h index e5206409..e2d77453 100644 --- a/rsync.h +++ b/rsync.h @@ -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 #include +#include +#include +#include #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 diff --git a/socket.c b/socket.c index 71aa6c2a..7027338b 100644 --- a/socket.c +++ b/socket.c @@ -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); } diff --git a/syscall.c b/syscall.c index c6fd354b..c655969e 100644 --- a/syscall.c +++ b/syscall.c @@ -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); } diff --git a/util.c b/util.c index 59f94182..f74228ef 100644 --- a/util.c +++ b/util.c @@ -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); -} -- 2.34.1