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