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