1 This adds Service Location Protocol support.
3 To use this patch, run these commands for a successful build:
5 patch -p1 <patches/slp.diff
7 ./configure --enable-slp
10 TODO: the configure changes should abort if the user requests --enable-slp
11 and we can't honor that request.
13 based-on: 8750f64ec7893c263b51e538895af3092bce9f4c
14 diff --git a/Makefile.in b/Makefile.in
17 @@ -13,6 +13,8 @@ CFLAGS=@CFLAGS@
26 @@ -37,7 +39,7 @@ OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
27 OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
28 fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
29 OBJS3=progress.o pipe.o
30 -DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
31 +DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o $(SLPOBJ)
32 popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
33 popt/popthelp.o popt/poptparse.o
34 OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) $(ZLIBOBJ) @BUILD_POPT@
35 @@ -73,7 +75,7 @@ install-strip:
36 $(MAKE) INSTALL_STRIP='-s' install
38 rsync$(EXEEXT): $(OBJS)
39 - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
40 + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(LIBSLP)
43 $(CHECK_OBJS): $(HEADERS)
44 diff --git a/clientserver.c b/clientserver.c
47 @@ -1074,6 +1074,13 @@ int daemon_main(void)
48 * address too. In fact, why not just do inet_ntop on the
52 + if (lp_use_slp() && register_services() != 0) {
54 + "Couldn't register with service discovery protocol, continuing anyway\n");
58 start_accept_loop(rsync_port, start_daemon);
61 diff --git a/configure.ac b/configure.ac
64 @@ -666,6 +666,29 @@ if test $rsync_cv_can_hardlink_special = yes; then
65 AC_DEFINE(CAN_HARDLINK_SPECIAL, 1, [Define to 1 if link() can hard-link special files.])
68 +AC_ARG_ENABLE(slp, [ --disable-slp turn off SLP support, defaults to on])
69 +AC_ARG_WITH(openslp-libs, [ --with-openslp-libs set directory for OpenSLP library],
70 + LDFLAGS="-L$withval $LDFLAGS"
71 + DSOFLAGS="-L$withval $DSOFLAGS",)
72 +AC_ARG_WITH(openslp-includes, [ --with-openslp-includes set directory for OpenSLP includes],
73 + CFLAGS="-I$withval $CFLAGS"
74 + CXXFLAGS="-I$withval $CXXFLAGS"
75 + CPPFLAGS="-I$withval $CPPFLAGS",)
80 +if test x$enable_slp != xno; then
81 + AC_CHECK_HEADER(slp.h,
82 + AC_CHECK_LIB(slp, SLPOpen,
83 + AC_DEFINE(HAVE_LIBSLP, 1, [Define to 1 for SLP support])
84 + SLPOBJ="srvreg.o srvloc.o"
91 AC_CACHE_CHECK([for working socketpair],rsync_cv_HAVE_SOCKETPAIR,[
93 #include <sys/types.h>
94 diff --git a/loadparm.c b/loadparm.c
97 @@ -109,6 +109,13 @@ typedef struct
110 static global Globals;
111 @@ -300,7 +307,13 @@ static struct parm_struct parm_table[] =
112 {"motd file", P_STRING, P_GLOBAL,&Globals.motd_file, NULL,0},
113 {"pid file", P_STRING, P_GLOBAL,&Globals.pid_file, NULL,0},
114 {"port", P_INTEGER,P_GLOBAL,&Globals.rsync_port, NULL,0},
116 + {"slp refresh", P_INTEGER,P_GLOBAL,&Globals.slp_refresh, NULL,0},
118 {"socket options", P_STRING, P_GLOBAL,&Globals.socket_options, NULL,0},
120 + {"use slp", P_BOOL, P_GLOBAL,&Globals.use_slp, NULL,0},
123 {"auth users", P_STRING, P_LOCAL, &sDefault.auth_users, NULL,0},
124 {"charset", P_STRING, P_LOCAL, &sDefault.charset, NULL,0},
125 @@ -354,6 +367,7 @@ static struct parm_struct parm_table[] =
126 static void init_globals(void)
128 memset(&Globals, 0, sizeof Globals);
129 + Globals.use_slp = True;
132 /***************************************************************************
133 @@ -394,6 +408,13 @@ FN_GLOBAL_STRING(lp_pid_file, &Globals.pid_file)
134 FN_GLOBAL_STRING(lp_socket_options, &Globals.socket_options)
136 FN_GLOBAL_INTEGER(lp_rsync_port, &Globals.rsync_port)
138 +FN_GLOBAL_INTEGER(lp_slp_refresh, &Globals.slp_refresh)
142 +FN_GLOBAL_BOOL(lp_use_slp, &Globals.use_slp)
145 FN_LOCAL_STRING(lp_auth_users, auth_users)
146 FN_LOCAL_STRING(lp_charset, charset)
147 diff --git a/main.c b/main.c
150 @@ -1133,6 +1133,18 @@ static int start_client(int argc, char *argv[])
152 if (!read_batch) { /* for read_batch, NO source is specified */
153 char *path = check_for_hostspec(argv[0], &shell_machine, &rsync_port);
155 + if (shell_machine && !shell_machine[0]) {
157 + /* User entered just rsync:// URI */
158 + print_service_list();
160 +#else /* No SLP, die here */
161 + rprintf(FINFO, "No SLP support, cannot browse\n");
162 + exit_cleanup(RERR_SYNTAX);
166 if (path) { /* source is remote */
169 diff --git a/options.c b/options.c
172 @@ -223,6 +223,7 @@ static void print_rsync_version(enum logcode f)
173 char const *links = "no ";
174 char const *iconv = "no ";
175 char const *ipv6 = "no ";
176 + char const *slp = "no ";
177 STRUCT_STAT *dumstat;
179 #if SUBPROTOCOL_VERSION != 0
180 @@ -256,6 +257,9 @@ static void print_rsync_version(enum logcode f)
181 #ifdef CAN_SET_SYMLINK_TIMES
188 rprintf(f, "%s version %s protocol version %d%s\n",
189 RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol);
190 @@ -269,8 +273,8 @@ static void print_rsync_version(enum logcode f)
191 (int)(sizeof (int64) * 8));
192 rprintf(f, " %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
193 got_socketpair, hardlinks, links, ipv6, have_inplace);
194 - rprintf(f, " %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes\n",
195 - have_inplace, acls, xattrs, iconv, symtimes);
196 + rprintf(f, " %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes, %sSLP\n",
197 + have_inplace, acls, xattrs, iconv, symtimes, slp);
199 #ifdef MAINTAINER_MODE
200 rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
201 diff --git a/rsync.h b/rsync.h
205 #define SIGNIFICANT_ITEM_FLAGS (~(\
206 ITEM_BASIS_TYPE_FOLLOWS | ITEM_XNAME_FOLLOWS | ITEM_LOCAL_CHANGE))
208 +/* this is the minimum we'll use, irrespective of config setting */
209 +/* definately don't set to less than about 30 seconds */
210 +#define SLP_MIN_TIMEOUT 120
212 #define CFN_KEEP_DOT_DIRS (1<<0)
213 #define CFN_KEEP_TRAILING_SLASH (1<<1)
214 #define CFN_DROP_TRAILING_DOT_DIR (1<<2)
215 diff --git a/rsync.yo b/rsync.yo
218 @@ -148,7 +148,12 @@ particular rsync daemon by leaving off the module name:
220 quote(tt(rsync somehost.mydomain.com::))
222 -See the following section for more details.
223 +And, if Service Location Protocol is available, the following will list the
224 +available rsync servers:
226 +quote(tt(rsync rsync://))
228 +See the following section for even more usage details.
230 manpagesection(ADVANCED USAGE)
232 diff --git a/rsyncd.conf b/rsyncd.conf
240 diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo
243 @@ -104,6 +104,19 @@ details on some of the options you may be able to set. By default no
244 special socket options are set. These settings can also be specified
245 via the bf(--sockopts) command-line option.
247 +dit(bf(use slp)) This parameter is used to determine if the module names are
248 +advertised via slp. The default is for this to be enabled, which will
249 +advertise your public modules.
251 +dit(bf(slp refresh)) This parameter is used to determine how long service
252 +advertisements are valid (measured in seconds), and is only applicable if
253 +you have Service Location Protocol support compiled in. If this is
254 +not set or is set to zero, then service advertisements never timeout. If
255 +this is set to less than 120 seconds, then 120 seconds is used. If it is
256 +set to more than 65535, then 65535 is used (which is a limitation of SLP).
257 +Using 3600 (one hour) is a good number if you tend to change your
262 manpagesection(MODULE PARAMETERS)
263 @@ -652,6 +665,7 @@ use chroot = yes
265 syslog facility = local5
266 pid file = /var/run/rsyncd.pid
270 path = /var/ftp/./pub
271 diff --git a/socket.c b/socket.c
274 @@ -533,6 +533,16 @@ void start_accept_loop(int port, int (*fn)(int, int))
279 + time_t next_slp_refresh;
280 + short slp_timeout = lp_use_slp() ? lp_slp_refresh() : 0;
282 + if (slp_timeout < SLP_MIN_TIMEOUT)
283 + slp_timeout = SLP_MIN_TIMEOUT;
284 + /* re-register before slp times out */
289 #ifdef HAVE_SIGACTION
290 sigact.sa_flags = SA_NOCLDSTOP;
291 @@ -561,14 +571,25 @@ void start_accept_loop(int port, int (*fn)(int, int))
296 + next_slp_refresh = time(NULL) + slp_timeout;
299 /* now accept incoming connections - forking a new process
300 * for each incoming connection */
306 struct sockaddr_storage addr;
307 socklen_t addrlen = sizeof addr;
309 + struct timeval slp_tv;
311 + slp_tv.tv_sec = 10;
312 + slp_tv.tv_usec = 0;
315 /* close log file before the potentially very long select so
316 * file can be trimmed by another process instead of growing
317 @@ -581,7 +602,18 @@ void start_accept_loop(int port, int (*fn)(int, int))
321 - if (select(maxfd + 1, &fds, NULL, NULL, NULL) < 1)
323 + sel_ret = select(maxfd + 1, &fds, NULL, NULL,
324 + slp_timeout ? &slp_tv : NULL);
325 + if (sel_ret == 0 && slp_timeout && time(NULL) > next_slp_refresh) {
326 + rprintf(FINFO, "Service registration expired, refreshing it\n");
327 + register_services();
328 + next_slp_refresh = time(NULL) + slp_timeout;
331 + sel_ret = select(maxfd + 1, &fds, NULL, NULL, NULL);
336 for (i = 0, fd = -1; sp[i] >= 0; i++) {
337 diff --git a/srvloc.c b/srvloc.c
342 +/* -*- c-file-style: "linux"; -*-
344 + Copyright (C) 2002 by Brad Hards <bradh@frogmouth.net>
346 + This program is free software; you can redistribute it and/or modify
347 + it under the terms of the GNU General Public License as published by
348 + the Free Software Foundation; either version 2 of the License, or
349 + (at your option) any later version.
351 + This program is distributed in the hope that it will be useful,
352 + but WITHOUT ANY WARRANTY; without even the implied warranty of
353 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
354 + GNU General Public License for more details.
356 + You should have received a copy of the GNU General Public License
357 + along with this program; if not, write to the Free Software
358 + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
361 +/* This file implements the service location functionality */
362 +/* Basically, it uses normal Service Location Protocol API */
364 +/* It is really a cheap hack - just to show how it might work
365 + in a real application.
374 +/* This one just prints out the attributes */
375 +static SLPBoolean getAttrCallback(UNUSED(SLPHandle hslp), const char *attrlist,
376 + SLPError errcode, UNUSED(void *cookie))
380 + if (errcode == SLP_OK) {
381 + if (!strcmp(attrlist, "(comment=)"))
382 + rprintf(FINFO, "\t(No description)\n");
384 + cleanstr = strrchr(attrlist, ')') ;
385 + *cleanstr = ' '; /* remove last ')' */
386 + rprintf(FINFO, "\t%s\n", strchr(attrlist, '=') + 1);
392 +static SLPBoolean getSLPSrvURLCallback(UNUSED(SLPHandle hslp),
393 + const char *srvurl, UNUSED(unsigned short lifetime),
394 + SLPError errcode, void *cookie)
397 + SLPHandle attrhslp;
399 + if (errcode == SLP_OK) {
400 + /* chop service: off the front */
401 + rprintf(FINFO, " %s ", (strchr(srvurl, ':') + 1));
402 + /* check for any attributes */
403 + if (SLPOpen("en", SLP_FALSE,&attrhslp) == SLP_OK) {
404 + result = SLPFindAttrs(attrhslp, srvurl,
405 + "", /* return all attributes */
406 + "", /* use configured scopes */
407 + getAttrCallback, NULL);
408 + if (result != SLP_OK) {
409 + rprintf(FERROR, "errorcode: %i\n",result);
411 + SLPClose(attrhslp);
413 + *(SLPError*)cookie = SLP_OK;
415 + *(SLPError*)cookie = errcode;
417 + /* Return SLP_TRUE because we want to be called again
418 + * if more services were found. */
423 +int print_service_list(void)
426 + SLPError callbackerr;
429 + err = SLPOpen("en",SLP_FALSE,&hslp);
430 + if (err != SLP_OK) {
431 + rprintf(FERROR, "Error opening slp handle %i\n", err);
435 + SLPFindSrvs(hslp, "rsync",
436 + 0, /* use configured scopes */
437 + 0, /* no attr filter */
438 + getSLPSrvURLCallback, &callbackerr);
440 + /* Now that we're done using slp, close the slp handle */
445 diff --git a/srvreg.c b/srvreg.c
450 +/* -*- c-file-style: "linux"; -*-
452 + Copyright (C) 2002 by Brad Hards <bradh@frogmouth.net>
454 + This program is free software; you can redistribute it and/or modify
455 + it under the terms of the GNU General Public License as published by
456 + the Free Software Foundation; either version 2 of the License, or
457 + (at your option) any later version.
459 + This program is distributed in the hope that it will be useful,
460 + but WITHOUT ANY WARRANTY; without even the implied warranty of
461 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
462 + GNU General Public License for more details.
464 + You should have received a copy of the GNU General Public License
465 + along with this program; if not, write to the Free Software
466 + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
469 +/* This file implements the service registration functionality */
471 +/* Basically, it uses normal Service Location Protocol API */
477 +extern int rsync_port;
479 +static void slp_callback(UNUSED(SLPHandle hslp), SLPError errcode, void *cookie)
481 + /* return the error code in the cookie */
482 + *(SLPError*)cookie = errcode;
484 + /* You could do something else here like print out
485 + * the errcode, etc. Remember, as a general rule,
486 + * do not try to do too much in a callback because
487 + * it is being executed by the same thread that is
488 + * reading slp packets from the wire. */
491 +int register_services(void)
493 + SLPError err, callbackerr;
499 + char localhost[256];
500 + extern char *config_file;
502 + struct addrinfo aih, *ai = 0;
504 + if (!lp_load(config_file, 0)) {
505 + exit_cleanup(RERR_SYNTAX);
508 + n = lp_numservices();
510 + if (0 == lp_slp_refresh())
511 + timeout = SLP_LIFETIME_MAXIMUM; /* don't expire, ever */
512 + else if (SLP_MIN_TIMEOUT > lp_slp_refresh())
513 + timeout = SLP_MIN_TIMEOUT; /* use a reasonable minimum */
514 + else if (SLP_LIFETIME_MAXIMUM <= lp_slp_refresh())
515 + timeout = (SLP_LIFETIME_MAXIMUM - 1); /* as long as possible */
517 + timeout = lp_slp_refresh();
519 + rprintf(FINFO, "rsyncd registering %d service%s with slpd for %d seconds:\n", n, ((n==1)? "":"s"), timeout);
520 + err = SLPOpen("en",SLP_FALSE,&hslp);
521 + if (err != SLP_OK) {
522 + rprintf(FINFO, "Error opening slp handle %i\n",err);
525 + if (gethostname(localhost, sizeof localhost)) {
526 + rprintf(FINFO, "Could not get hostname: %s\n", strerror(errno));
529 + memset(&aih, 0, sizeof aih);
530 + aih.ai_family = PF_UNSPEC;
531 + aih.ai_flags = AI_CANONNAME;
532 + if (0 != (err = getaddrinfo(localhost, 0, &aih, &ai)) || !ai) {
533 + rprintf(FINFO, "Could not resolve hostname: %s\n", gai_strerror(err));
536 + /* Register each service with SLP */
537 + for (i = 0; i < n; i++) {
541 + snprintf(srv, sizeof srv, "service:rsync://%s:%d/%s",
545 + rprintf(FINFO, " %s\n", srv);
546 + if (lp_comment(i)) {
547 + snprintf(attr, sizeof attr, "(comment=%s)",
551 + srv, /* service to register */
553 + 0, /* this is ignored */
554 + attr, /* attributes */
555 + SLP_TRUE, /* new registration - don't change this */
556 + slp_callback, /* callback */
559 + /* err may contain an error code that occurred as the slp library
560 + * _prepared_ to make the call. */
561 + if (err != SLP_OK || callbackerr != SLP_OK)
562 + rprintf(FINFO, "Error registering service with slp %i\n", err);
564 + /* callbackerr may contain an error code (that was assigned through
565 + * the callback cookie) that occurred as slp packets were sent on
567 + if (callbackerr != SLP_OK)
568 + rprintf(FINFO, "Error registering service with slp %i\n",callbackerr);
571 + /* Now that we're done using slp, close the slp handle */
575 + /* refresh is done in main select loop */