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