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