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