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