Matt added "const" to a couple char pointers.
[rsync/rsync-patches.git] / slp.diff
CommitLineData
9978080e
WD
1This adds Service Location Protocol support.
2
03019e41 3To use this patch, run these commands for a successful build:
9978080e 4
03019e41 5 patch -p1 <patches/slp.diff
27e96866 6 ./prepare-source
9978080e 7 ./configure --enable-slp
9978080e
WD
8 make
9
6bc8e4ec
WD
10TODO: the configure changes should abort if the user requests --enable-slp
11and we can't honor that request.
12
9a7eef96
WD
13--- old/Makefile.in
14+++ new/Makefile.in
03019e41 15@@ -13,6 +13,8 @@ CFLAGS=@CFLAGS@
9978080e
WD
16 CPPFLAGS=@CPPFLAGS@
17 EXEEXT=@EXEEXT@
18 LDFLAGS=@LDFLAGS@
19+LIBSLP=@LIBSLP@
20+SLPOBJ=@SLPOBJ@
21
22 INSTALLCMD=@INSTALL@
23 INSTALLMAN=@INSTALL@
03019e41 24@@ -36,7 +38,7 @@ OBJS1=rsync.o generator.o receiver.o cle
9978080e
WD
25 OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o \
26 fileio.o batch.o clientname.o chmod.o
27 OBJS3=progress.o pipe.o
28-DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
29+DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o $(SLPOBJ)
30 popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
31 popt/popthelp.o popt/poptparse.o
32 OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) $(ZLIBOBJ) @BUILD_POPT@
03019e41 33@@ -70,7 +72,7 @@ install-strip:
9978080e
WD
34 $(MAKE) INSTALL_STRIP='-s' install
35
36 rsync$(EXEEXT): $(OBJS)
37- $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
38+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(LIBSLP)
39
40 $(OBJS): $(HEADERS)
41
9a7eef96
WD
42--- old/clientserver.c
43+++ new/clientserver.c
03019e41 44@@ -849,6 +849,13 @@ int daemon_main(void)
9978080e
WD
45 * address too. In fact, why not just do inet_ntop on the
46 * local address??? */
47
48+#ifdef HAVE_LIBSLP
49+ if (register_services()) {
50+ rprintf(FINFO,
51+ "Couldn't register with service discovery protocol, continuing anyway\n");
52+ }
53+#endif
54+
55 if (((pid_file = lp_pid_file()) != NULL) && (*pid_file != '\0')) {
56 char pidbuf[16];
57 int fd;
9a7eef96
WD
58--- old/configure.in
59+++ new/configure.in
03019e41 60@@ -592,6 +592,29 @@ if test $rsync_cv_can_hardlink_special =
1898c899 61 AC_DEFINE(CAN_HARDLINK_SPECIAL, 1, [Define to 1 if link() can hard-link special files.])
9978080e
WD
62 fi
63
64+AC_ARG_ENABLE(slp, [ --disable-slp turn off SLP support, defaults to on])
65+AC_ARG_WITH(openslp-libs, [ --with-openslp-libs set directory for OpenSLP library],
66+ LDFLAGS="-L$withval $LDFLAGS"
67+ DSOFLAGS="-L$withval $DSOFLAGS",)
68+AC_ARG_WITH(openslp-includes, [ --with-openslp-includes set directory for OpenSLP includes],
69+ CFLAGS="-I$withval $CFLAGS"
70+ CXXFLAGS="-I$withval $CXXFLAGS"
71+ CPPFLAGS="-I$withval $CPPFLAGS",)
72+
73+LIBSLP=""
74+SLPOBJ=""
75+
76+if test x$enable_slp != xno; then
77+ AC_CHECK_HEADER(slp.h,
78+ AC_CHECK_LIB(slp, SLPOpen,
79+ AC_DEFINE(HAVE_LIBSLP, 1, [Define to 1 for SLP support])
80+ SLPOBJ="srvreg.o srvloc.o"
81+ LIBSLP="-lslp"))
82+fi
83+
84+AC_SUBST(LIBSLP)
85+AC_SUBST(SLPOBJ)
86+
87 AC_CACHE_CHECK([for working socketpair],rsync_cv_HAVE_SOCKETPAIR,[
88 AC_TRY_RUN([
89 #include <sys/types.h>
9a7eef96
WD
90--- old/loadparm.c
91+++ new/loadparm.c
0ffcefad 92@@ -108,6 +108,9 @@ typedef struct
9978080e
WD
93 char *socket_options;
94
95 int rsync_port;
6bc8e4ec 96+#ifdef HAVE_LIBSLP
9978080e 97+ int slp_refresh;
6bc8e4ec 98+#endif
9978080e
WD
99 } global;
100
0ffcefad
WD
101 static global Globals;
102@@ -291,6 +294,9 @@ static struct parm_struct parm_table[] =
9978080e
WD
103 {"motd file", P_STRING, P_GLOBAL,&Globals.motd_file, NULL,0},
104 {"pid file", P_STRING, P_GLOBAL,&Globals.pid_file, NULL,0},
105 {"port", P_INTEGER,P_GLOBAL,&Globals.rsync_port, NULL,0},
6bc8e4ec 106+#ifdef HAVE_LIBSLP
9978080e 107+ {"slp refresh", P_INTEGER,P_GLOBAL,&Globals.slp_refresh, NULL,0},
6bc8e4ec 108+#endif
9978080e 109 {"socket options", P_STRING, P_GLOBAL,&Globals.socket_options, NULL,0},
9978080e 110
0ffcefad
WD
111 {"auth users", P_STRING, P_LOCAL, &sDefault.auth_users, NULL,0},
112@@ -381,6 +387,9 @@ FN_GLOBAL_STRING(lp_pid_file, &Globals.p
9978080e
WD
113 FN_GLOBAL_STRING(lp_socket_options, &Globals.socket_options)
114
115 FN_GLOBAL_INTEGER(lp_rsync_port, &Globals.rsync_port)
6bc8e4ec 116+#ifdef HAVE_LIBSLP
9978080e 117+FN_GLOBAL_INTEGER(lp_slp_refresh, &Globals.slp_refresh)
6bc8e4ec 118+#endif
9978080e
WD
119
120 FN_LOCAL_STRING(lp_auth_users, auth_users)
0ffcefad 121 FN_LOCAL_STRING(lp_comment, comment)
9a7eef96
WD
122--- old/main.c
123+++ new/main.c
03019e41 124@@ -1058,6 +1058,18 @@ static int start_client(int argc, char *
3731f97b 125
9978080e 126 if (!read_batch) { /* for read_batch, NO source is specified */
9978080e
WD
127 shell_path = check_for_hostspec(argv[0], &shell_machine, &rsync_port);
128+
129+ if (shell_machine && !shell_machine[0]) {
130+#ifdef HAVE_LIBSLP
131+ /* User entered just rsync:// URI */
132+ print_service_list();
133+ exit_cleanup(0);
134+#else /* No SLP, die here */
135+ rprintf(FINFO, "No SLP support, cannot browse\n");
136+ exit_cleanup(RERR_SYNTAX);
137+#endif
138+ }
139+
140 if (shell_path) { /* source is remote */
141 char *dummy1;
142 int dummy2;
9a7eef96
WD
143--- old/options.c
144+++ new/options.c
03019e41 145@@ -201,6 +201,7 @@ static void print_rsync_version(enum log
9978080e
WD
146 char const *hardlinks = "no ";
147 char const *links = "no ";
148 char const *ipv6 = "no ";
149+ char const *slp = "no ";
150 STRUCT_STAT *dumstat;
151
152 #ifdef HAVE_SOCKETPAIR
03019e41 153@@ -223,6 +224,10 @@ static void print_rsync_version(enum log
9978080e
WD
154 ipv6 = "";
155 #endif
156
157+#if HAVE_LIBSLP
158+ slp = "";
159+#endif
160+
161 rprintf(f, "%s version %s protocol version %d\n",
162 RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION);
163 rprintf(f, "Copyright (C) 1996-2006 by Andrew Tridgell, Wayne Davison, and others.\n");
03019e41 164@@ -235,9 +240,9 @@ static void print_rsync_version(enum log
9978080e
WD
165 /* Note that this field may not have type ino_t. It depends
166 * on the complicated interaction between largefile feature
167 * macros. */
168- rprintf(f, " %sinplace, %sIPv6, "
169+ rprintf(f, " %sinplace, %sIPv6, %sSLP, "
170 "%d-bit system inums, %d-bit internal inums\n",
171- have_inplace, ipv6,
172+ have_inplace, ipv6, slp,
173 (int) (sizeof dumstat->st_ino * 8),
174 (int) (sizeof (int64) * 8));
175 #ifdef MAINTAINER_MODE
9a7eef96
WD
176--- old/rsync.h
177+++ new/rsync.h
0ffcefad 178@@ -157,6 +157,9 @@
9978080e
WD
179 #define SIGNIFICANT_ITEM_FLAGS (~(\
180 ITEM_BASIS_TYPE_FOLLOWS | ITEM_XNAME_FOLLOWS | ITEM_LOCAL_CHANGE))
181
182+/* this is the minimum we'll use, irrespective of config setting */
183+/* definately don't set to less than about 30 seconds */
184+#define SLP_MIN_TIMEOUT 120
185
1898c899
WD
186 /* Log-message categories. Only FERROR and FINFO get sent over the socket,
187 * but FLOG and FSOCKERR can be sent over the receiver -> generator pipe.
9a7eef96
WD
188--- old/rsync.yo
189+++ new/rsync.yo
3731f97b 190@@ -139,7 +139,12 @@ particular rsync daemon by leaving off t
9978080e
WD
191
192 quote(tt(rsync somehost.mydomain.com::))
193
194-See the following section for more details.
195+And, if Service Location Protocol is available, the following will list the
196+available rsync servers:
197+
198+quote(tt(rsync rsync://))
199+
200+See the following section for even more usage details.
201
202 manpagesection(ADVANCED USAGE)
203
9a7eef96
WD
204--- old/rsyncd.conf
205+++ new/rsyncd.conf
9978080e
WD
206@@ -0,0 +1,3 @@
207+
208+slp refresh = 300
209+
9a7eef96
WD
210--- old/rsyncd.conf.yo
211+++ new/rsyncd.conf.yo
0ffcefad 212@@ -103,6 +103,15 @@ details on some of the options you may b
9978080e
WD
213 special socket options are set. These settings are superseded by the
214 bf(--sockopts) command-line option.
215
216+dit(bf(slp refresh)) This option is used to determine how long service
217+advertisements are valid (measured in seconds), and is only applicable if
218+you have Service Location Protocol support compiled in. If this option is
219+not set or is set to zero, then service advertisements never time out. If
220+this is set to less than 120 seconds, then 120 seconds is used. If it is
221+set to more than 65535, then 65535 is used (which is a limitation of SLP).
222+Using 3600 (one hour) is a good number if you tend to change your
223+configuration.
224+
225 enddit()
226
227
03019e41 228@@ -556,6 +565,7 @@ use chroot = no
9978080e
WD
229 max connections = 4
230 syslog facility = local5
231 pid file = /var/run/rsyncd.pid
232+slp refresh = 3600
233
234 [ftp]
235 path = /var/ftp/pub
9a7eef96
WD
236--- old/socket.c
237+++ new/socket.c
03019e41 238@@ -466,6 +466,16 @@ void start_accept_loop(int port, int (*f
9978080e
WD
239 {
240 fd_set deffds;
241 int *sp, maxfd, i;
6bc8e4ec 242+#ifdef HAVE_LIBSLP
9978080e
WD
243+ time_t next_slp_refresh;
244+ short slp_timeout = lp_slp_refresh();
245+ if (slp_timeout) {
246+ if (slp_timeout < SLP_MIN_TIMEOUT)
247+ slp_timeout = SLP_MIN_TIMEOUT;
248+ /* re-register before slp times out */
249+ slp_timeout -= 15;
250+ }
6bc8e4ec 251+#endif
9978080e 252
27e96866 253 #ifdef HAVE_SIGACTION
9978080e 254 sigact.sa_flags = SA_NOCLDSTOP;
03019e41 255@@ -494,14 +504,25 @@ void start_accept_loop(int port, int (*f
9978080e
WD
256 maxfd = sp[i];
257 }
258
6bc8e4ec 259+#ifdef HAVE_LIBSLP
9978080e 260+ next_slp_refresh = time(NULL) + slp_timeout;
6bc8e4ec 261+#endif
9978080e
WD
262+
263 /* now accept incoming connections - forking a new process
264 * for each incoming connection */
265 while (1) {
266 fd_set fds;
267 pid_t pid;
268 int fd;
269+ int sel_ret;
9978080e
WD
270 struct sockaddr_storage addr;
271 socklen_t addrlen = sizeof addr;
6bc8e4ec
WD
272+#ifdef HAVE_LIBSLP
273+ struct timeval slp_tv;
274+
9978080e
WD
275+ slp_tv.tv_sec = 10;
276+ slp_tv.tv_usec = 0;
6bc8e4ec 277+#endif
9978080e
WD
278
279 /* close log file before the potentially very long select so
280 * file can be trimmed by another process instead of growing
03019e41 281@@ -513,8 +534,18 @@ void start_accept_loop(int port, int (*f
9978080e
WD
282 #else
283 fds = deffds;
284 #endif
285-
286- if (select(maxfd + 1, &fds, NULL, NULL, NULL) != 1)
6bc8e4ec
WD
287+#ifdef HAVE_LIBSLP
288+ sel_ret = select(maxfd + 1, &fds, NULL, NULL,
289+ slp_timeout ? &slp_tv : NULL);
9978080e
WD
290+ if (sel_ret == 0 && slp_timeout && time(NULL) > next_slp_refresh) {
291+ rprintf(FINFO, "Service registration expired, refreshing it\n");
292+ register_services();
293+ next_slp_refresh = time(NULL) + slp_timeout;
294+ }
6bc8e4ec
WD
295+#else
296+ sel_ret = select(maxfd + 1, &fds, NULL, NULL, NULL);
297+#endif
9978080e
WD
298+ if (sel_ret != 1)
299 continue;
300
301 for (i = 0, fd = -1; sp[i] >= 0; i++) {
9a7eef96
WD
302--- old/srvloc.c
303+++ new/srvloc.c
6bc8e4ec 304@@ -0,0 +1,103 @@
9978080e
WD
305+/* -*- c-file-style: "linux"; -*-
306+
307+ Copyright (C) 2002 by Brad Hards <bradh@frogmouth.net>
308+
309+ This program is free software; you can redistribute it and/or modify
310+ it under the terms of the GNU General Public License as published by
311+ the Free Software Foundation; either version 2 of the License, or
312+ (at your option) any later version.
313+
314+ This program is distributed in the hope that it will be useful,
315+ but WITHOUT ANY WARRANTY; without even the implied warranty of
316+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
317+ GNU General Public License for more details.
318+
319+ You should have received a copy of the GNU General Public License
320+ along with this program; if not, write to the Free Software
321+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
322+*/
323+
324+/* This file implements the service location functionality */
325+/* Basically, it uses normal Service Location Protocol API */
326+
327+/* It is really a cheap hack - just to show how it might work
328+ in a real application.
329+*/
330+
331+#include "rsync.h"
332+
333+#include <slp.h>
334+#include <stdio.h>
335+#include <string.h>
336+
337+/* This one just prints out the attributes */
338+static SLPBoolean getAttrCallback(UNUSED(SLPHandle hslp), const char *attrlist,
339+ SLPError errcode, UNUSED(void *cookie))
340+{
341+ char *cleanstr;
342+
343+ if (errcode == SLP_OK) {
6bc8e4ec 344+ if (!strcmp(attrlist, "(comment=)"))
9978080e 345+ rprintf(FINFO, "\t(No description)\n");
6bc8e4ec 346+ else {
9978080e
WD
347+ cleanstr = strrchr(attrlist, ')') ;
348+ *cleanstr = ' '; /* remove last ')' */
349+ rprintf(FINFO, "\t%s\n", strchr(attrlist, '=') + 1);
350+ }
351+ }
352+ return SLP_FALSE;
353+}
354+
6bc8e4ec
WD
355+static SLPBoolean getSLPSrvURLCallback(UNUSED(SLPHandle hslp),
356+ const char *srvurl, UNUSED(unsigned short lifetime),
357+ SLPError errcode, void *cookie)
9978080e
WD
358+{
359+ SLPError result;
360+ SLPHandle attrhslp;
361+
362+ if (errcode == SLP_OK) {
363+ /* chop service: off the front */
364+ rprintf(FINFO, " %s ", (strchr(srvurl, ':') + 1));
365+ /* check for any attributes */
366+ if (SLPOpen("en", SLP_FALSE,&attrhslp) == SLP_OK) {
367+ result = SLPFindAttrs(attrhslp, srvurl,
368+ "", /* return all attributes */
369+ "", /* use configured scopes */
370+ getAttrCallback, NULL);
371+ if (result != SLP_OK) {
372+ rprintf(FERROR, "errorcode: %i\n",result);
373+ }
374+ SLPClose(attrhslp);
375+ }
376+ *(SLPError*)cookie = SLP_OK;
6bc8e4ec 377+ } else
9978080e 378+ *(SLPError*)cookie = errcode;
9978080e
WD
379+
380+ /* Return SLP_TRUE because we want to be called again
381+ * if more services were found. */
382+
383+ return SLP_TRUE;
384+}
385+
386+int print_service_list(void)
387+{
388+ SLPError err;
389+ SLPError callbackerr;
390+ SLPHandle hslp;
391+
392+ err = SLPOpen("en",SLP_FALSE,&hslp);
393+ if (err != SLP_OK) {
394+ rprintf(FERROR, "Error opening slp handle %i\n", err);
395+ return err;
396+ }
397+
398+ SLPFindSrvs(hslp, "rsync",
399+ 0, /* use configured scopes */
400+ 0, /* no attr filter */
401+ getSLPSrvURLCallback, &callbackerr);
402+
403+ /* Now that we're done using slp, close the slp handle */
404+ SLPClose(hslp);
405+
406+ return 0;
407+}
9a7eef96
WD
408--- old/srvreg.c
409+++ new/srvreg.c
9978080e
WD
410@@ -0,0 +1,128 @@
411+/* -*- c-file-style: "linux"; -*-
412+
413+ Copyright (C) 2002 by Brad Hards <bradh@frogmouth.net>
414+
415+ This program is free software; you can redistribute it and/or modify
416+ it under the terms of the GNU General Public License as published by
417+ the Free Software Foundation; either version 2 of the License, or
418+ (at your option) any later version.
419+
420+ This program is distributed in the hope that it will be useful,
421+ but WITHOUT ANY WARRANTY; without even the implied warranty of
422+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
423+ GNU General Public License for more details.
424+
425+ You should have received a copy of the GNU General Public License
426+ along with this program; if not, write to the Free Software
427+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
428+*/
429+
430+/* This file implements the service registration functionality */
431+
432+/* Basically, it uses normal Service Location Protocol API */
433+
434+#include "rsync.h"
435+#include "slp.h"
436+#include "netdb.h"
437+
438+extern int rsync_port;
439+
440+static void slp_callback(UNUSED(SLPHandle hslp), SLPError errcode, void *cookie)
441+{
442+ /* return the error code in the cookie */
443+ *(SLPError*)cookie = errcode;
444+
445+ /* You could do something else here like print out
446+ * the errcode, etc. Remember, as a general rule,
447+ * do not try to do too much in a callback because
448+ * it is being executed by the same thread that is
449+ * reading slp packets from the wire. */
450+}
451+
452+int register_services(void)
453+{
454+ SLPError err, callbackerr;
455+ SLPHandle hslp;
456+ int n;
457+ int i;
458+ char srv[120];
459+ char attr[120];
460+ char localhost[256];
461+ extern char *config_file;
462+ short timeout;
463+ struct addrinfo aih, *ai = 0;
464+
465+ if (!lp_load(config_file, 0)) {
466+ exit_cleanup(RERR_SYNTAX);
467+ }
468+
469+ n = lp_numservices();
470+
471+ if (0 == lp_slp_refresh())
472+ timeout = SLP_LIFETIME_MAXIMUM; /* don't expire, ever */
473+ else if (SLP_MIN_TIMEOUT > lp_slp_refresh())
474+ timeout = SLP_MIN_TIMEOUT; /* use a reasonable minimum */
475+ else if (SLP_LIFETIME_MAXIMUM <= lp_slp_refresh())
476+ timeout = (SLP_LIFETIME_MAXIMUM - 1); /* as long as possible */
477+ else
478+ timeout = lp_slp_refresh();
479+
480+ rprintf(FINFO, "rsyncd registering %d service%s with slpd for %d seconds:\n", n, ((n==1)? "":"s"), timeout);
481+ err = SLPOpen("en",SLP_FALSE,&hslp);
482+ if (err != SLP_OK) {
483+ rprintf(FINFO, "Error opening slp handle %i\n",err);
484+ return err;
485+ }
486+ if (gethostname(localhost, sizeof localhost)) {
487+ rprintf(FINFO, "Could not get hostname: %s\n", strerror(errno));
488+ return err;
489+ }
490+ memset(&aih, 0, sizeof aih);
491+ aih.ai_family = PF_UNSPEC;
492+ aih.ai_flags = AI_CANONNAME;
493+ if (0 != (err = getaddrinfo(localhost, 0, &aih, &ai)) || !ai) {
494+ rprintf(FINFO, "Could not resolve hostname: %s\n", gai_strerror(err));
495+ return err;
496+ }
497+ /* Register each service with SLP */
498+ for (i = 0; i < n; i++) {
499+ if (!lp_list(i))
500+ continue;
501+
502+ snprintf(srv, sizeof srv, "service:rsync://%s:%d/%s",
503+ ai->ai_canonname,
504+ rsync_port,
505+ lp_name(i));
506+ rprintf(FINFO, " %s\n", srv);
507+ if (lp_comment(i)) {
508+ snprintf(attr, sizeof attr, "(comment=%s)",
509+ lp_comment(i));
510+ }
511+ err = SLPReg(hslp,
512+ srv, /* service to register */
513+ timeout,
514+ 0, /* this is ignored */
515+ attr, /* attributes */
516+ SLP_TRUE, /* new registration - don't change this */
517+ slp_callback, /* callback */
518+ &callbackerr);
519+
520+ /* err may contain an error code that occurred as the slp library
521+ * _prepared_ to make the call. */
522+ if (err != SLP_OK || callbackerr != SLP_OK)
523+ rprintf(FINFO, "Error registering service with slp %i\n", err);
524+
525+ /* callbackerr may contain an error code (that was assigned through
526+ * the callback cookie) that occurred as slp packets were sent on
527+ * the wire. */
528+ if (callbackerr != SLP_OK)
529+ rprintf(FINFO, "Error registering service with slp %i\n",callbackerr);
530+ }
531+
532+ /* Now that we're done using slp, close the slp handle */
533+ freeaddrinfo(ai);
534+ SLPClose(hslp);
535+
536+ /* refresh is done in main select loop */
537+ return 0;
538+}