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