[cups.development] Native Avahi support

Till Kamppeter till.kamppeter at gmail.com
Wed Dec 29 16:07:59 PST 2010


Tim, Mike, I have applied the Avahi support patch to the CUPS packages 
of Debian and Ubuntu now. See also

https://bugs.launchpad.net/ubuntu/+source/cups/+bug/465916

Unfortunately, I had to do a lot of changes to make the patch working 
correctly in all situations and to keep the CUPS daemon stable.

Once there were a lot of segmentation faults due to some missing 
initializations of fields in data structures and due to many missing 
NULL checks. All this I have fixed.

I have also added support for the Avahi daemon starting after CUPS and 
for the Avahi daemon being stopped and started while CUPS is running. In 
these cases printer registrations got lost and were not recovered. I 
have also taken care that the CUPS daemon gets never trapped in the 
15-second timeout of Avahi when the Avahi client is created but the 
Avahi daemon is not running.

I have attached the improved patch. Please use that patch instead of the 
original. The patch pieces shown below are not guaranteed to be 
complete. They are only to illustrate the changes.

Please see what I have changed on the patch below. The patch pieces are 
of a diff between the original patch and my new patch.

    Till

----------

Changes on the Avahi support patch
==================================

backend/dnssd.c
---------------

Let dnssd backend not stop with an error if it cannot create an Avahi
client.  This is needed to make the CUPS test suite not fail on build
servers which do not have an Avahi service running.

@@ -271,8 +271,8 @@
  +			     0, avahi_client_callback, NULL, &error);
  +  if (!client)
  +  {
-+    perror ("ERROR: Unable to create avahi client");
-+    return (1);
++    perror ("DEBUG: Unable to create avahi client");
++    return (0);
  +  }
  +
  +  avahi_service_browser_new (client, AVAHI_IF_UNSPEC,

scheduler/avahi.c
-----------------

These are missing initializations of some fields of the data structures
for FD watches. Without these initializations the callback functions will
segfault.

@@ -1375,10 +1375,12 @@
  +
  +    watched_fd->fd = fd;
  +    watched_fd->occurred = 0;
++    watched_fd->cups_poll = cups_poll;
  +    watched_fd->watches = cupsArrayNew 
((cups_array_func_t)compare_watches,
  +					NULL);
  +  }
  +
++  watch->watched_fd = watched_fd;
  +  cupsArrayAdd(watched_fd->watches, watch);
  +  watched_fd_add_select (watched_fd);
  +  return (watch);

scheduler/dirsvc.c
------------------

Added new cupsdStartAvahiClient() function so that the Avahi client
can also be created from scheduler/main.c. This way the Avahi client
can be created while the CUPS daemon is running, for example if the
Avahi daemon gets started after the CUPS daemon.

Note that we use the AVAHI_CLIENT_NO_FAIL now when we create an Avahi
client. This prevents CUPS from getting blocked for 15 seconds if the
Avahi daemon is not running.

@@ -1713,7 +1715,15 @@
  diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' 
'--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' 
cups-1.4.5~/scheduler/dirsvc.c cups-1.4.5/scheduler/dirsvc.c
  --- cups-1.4.5~/scheduler/dirsvc.c	2010-04-23 20:56:34.000000000 +0200
  +++ cups-1.4.5/scheduler/dirsvc.c	2010-12-29 23:37:37.398096117 +0100
-@@ -99,6 +99,13 @@
+@@ -27,6 +27,7 @@
+  *   ldap_connect()             - Start new LDAP connection
+  *   ldap_reconnect()           - Reconnect to LDAP Server
+  *   ldap_disconnect()          - Disconnect from LDAP Server
++ *   cupsdStartAvahiClient()    - Start an Avahi client if needed
+  *   cupsdStartBrowsing()       - Start sending and receiving broadcast
+  *                                information.
+  *   cupsdStartPolling()        - Start polling servers as needed.
+@@ -99,6 +100,13 @@
   #endif /* HAVE_DNSSD */


@@ -1803,7 +1813,32 @@
   }


-@@ -1535,13 +1556,16 @@
+@@ -1411,6 +1433,24 @@
+ }
+ #endif /* HAVE_LDAP */
+
++#ifdef HAVE_AVAHI
++/*
++ * 'cupsdStartAvahiClient()' - Start an Avahi client if needed
++ */
++
++void
++cupsdStartAvahiClient(void) {
++  if ((!AvahiCupsClient) &&
++      (!AvahiCupsClientConnecting || !(*AvahiCupsClientConnecting)))
++  {
++    if (!AvahiCupsPollHandle)
++      AvahiCupsPollHandle = avahi_cups_poll_new ();
++    if (AvahiCupsPollHandle)
++      avahi_client_new (avahi_cups_poll_get (AvahiCupsPollHandle),
++			AVAHI_CLIENT_NO_FAIL, avahi_client_cb, NULL, NULL);
++  }
++}
++#endif /* HAVE_AVAHI */
+
+ /*
+  * 'cupsdStartBrowsing()' - Start sending and receiving broadcast 
information.
+@@ -1535,13 +1575,16 @@
     else
       BrowseSocket = -1;

@@ -1842,16 +1877,12 @@
  +
  +#ifdef HAVE_AVAHI
  +  if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_DNSSD)
-+  {
-+    AvahiCupsPollHandle = avahi_cups_poll_new ();
-+    avahi_client_new (avahi_cups_poll_get (AvahiCupsPollHandle),
-+		      0, avahi_client_cb, NULL, NULL);
-+  }
++    cupsdStartAvahiClient();
  +#endif /* HAVE_AVAHI */

   #ifdef HAVE_LIBSLP
     if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP)
-@@ -1835,10 +1871,10 @@
+@@ -1835,10 +1886,10 @@
       BrowseSocket = -1;
     }

scheduler/dirsvc.c
------------------

NULL checks for dnssdComparePrinters(). Otherwise CUPS segfaults in
certain cases.

@@ -2102,7 +2133,25 @@
   /*
    * 'dnssdComparePrinters()' - Compare the registered names of two 
printers.
    */
-@@ -2479,6 +2402,10 @@
+@@ -2464,7 +2402,16 @@
+ dnssdComparePrinters(cupsd_printer_t *a,/* I - First printer */
+                      cupsd_printer_t *b)/* I - Second printer */
+ {
+-  return (strcasecmp(a->reg_name, b->reg_name));
++  if (!a->reg_name)
++    if (!b->reg_name)
++      return 0;
++    else
++      return -1;
++  else
++    if (!b->reg_name)
++      return 1;
++    else
++      return (strcasecmp(a->reg_name, b->reg_name));
+ }
+
+
+@@ -2479,6 +2426,10 @@
   {
     cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdDeregisterPrinter(%s)", 
p->name);

scheduler/dirsvc.c
------------------

NULL check for the case that creation of p->avahi_group fails. I got a
segfault also here. The second hunk prevents the error handling from
segfaulting if the p->avahi_group NULL check kicks in.

@@ -2378,6 +2427,9 @@
  +		    "Registering Avahi printer %s with name \"%s\" and "
  +		    "type \"%s\"", p->name, name, regtype);
  +
++    if (!p->avahi_group)
++      goto add_failed;
++
  +    ret = avahi_entry_group_add_service_strlst (p->avahi_group,
  +						AVAHI_IF_UNSPEC,
  +						AVAHI_PROTO_UNSPEC,
@@ -2418,9 +2470,12 @@
  +      cupsdLogMessage (CUPSD_LOG_ERROR,
  +		       "Failed to add Avahi entry for %s: %d",
  +		       name, ret);
-+      avahi_entry_group_reset (p->avahi_group);
-+      avahi_entry_group_free (p->avahi_group);
-+      p->avahi_group = NULL;
++      if (p->avahi_group)
++      {
++	avahi_entry_group_reset (p->avahi_group);
++	avahi_entry_group_free (p->avahi_group);
++	p->avahi_group = NULL;
++      }
  +      ipp_txt = p->ipp_txt;
  +      p->ipp_txt = NULL;
  +    }

scheduler/dirsvc.c
------------------

Handling of the Avahi daemon being stopped and started while CUPS is
running. In this case we once must take care of the loss of all
printer registrations. We ghave to make sure that the printers get
re-registered when the Avahi daemon comes back. Second, we must assure
that CUPS stays stable when the Avahi daemon disappears and that a new
Avahi client gets created when it reappears. For all this we remove
the Avahi client and unregister all printers from DNSSD if the Avahi
daemon disappears and re-register all printers when the Avahi client
is started.

In addition, we mark if an Avahi client was created but not started
because the Avahi daemon is not running (the client is in a waiting
state). We use a pointer to a pointer to the client here, so that we
can discover if the waiting client times out (pointer to client gets
NULL). We can get waiting clients, as we have to use the
AVAHI_CLIENT_NO_FAIL mode when creating an Avahi client. Not using
this mode will block CUPS for 15 seconds if it creates an Avahi client
while the Avahi daemon is not running.

@@ -2830,26 +2885,44 @@
  +    * Avahi client started successfully.
  +    */
  +    AvahiCupsClient = client;
++    AvahiCupsClientConnecting = NULL;
  +    cupsdLogMessage (CUPSD_LOG_DEBUG, "Avahi client started");
  +
  +    cupsdUpdateDNSSDName ();
  +
-+    for (printer = (cupsd_printer_t *)cupsArrayFirst(DNSSDPrinters);
++    for (printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
  +	 printer;
-+	 printer = (cupsd_printer_t *)cupsArrayNext(DNSSDPrinters))
-+    {
-+      if (!printer->avahi_group)
++	 printer = (cupsd_printer_t *)cupsArrayNext(Printers))
++      if (Browsing && (BrowseLocalProtocols & BROWSE_DNSSD) &&
++	  (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT |
++			      CUPS_PRINTER_SCANNER))) && printer->shared)
  +	dnssdRegisterPrinter (printer);
-+    }
  +
  +    break;
  +
  +  case AVAHI_CLIENT_CONNECTING:
++   /*
++    * No Avahi daemon, client is waiting.
++    */
++    AvahiCupsClientConnecting = &client;
  +    cupsdLogMessage (CUPSD_LOG_DEBUG, "Avahi client connecting");
  +    break;
  +
  +  case AVAHI_CLIENT_FAILURE:
-+    cupsdLogMessage (CUPSD_LOG_ERROR, "Avahi client failed");
++   /*
++    * Avahi client failed, close it to allow a clean restart.
++    */
++    cupsdLogMessage (CUPSD_LOG_ERROR, "Avahi client failed, closing 
client to allow a clean restart");
++
++    for (printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
++	 printer;
++	 printer = (cupsd_printer_t *)cupsArrayNext(Printers))
++      dnssdDeregisterPrinter (printer);
++
++    avahi_client_free(AvahiCupsClient);
++    AvahiCupsClientConnecting = NULL;
++    AvahiCupsClient = NULL;
++
  +    break;
  +
  +  default:

scheduler/dirsvc.h
------------------

Additional global variable to mark that the Avahi client is waiting
for an Avahi daemon.

@@ -2911,6 +2984,8 @@
  +					/* AvahiCupsPoll object */
  +VAR AvahiClient		*AvahiCupsClient	VALUE(NULL);
  +					/* AvahiClient object */
++VAR AvahiClient		**AvahiCupsClientConnecting	VALUE(NULL);
++					/* AvahiClient object (waiting) */
  +VAR AvahiEntryGroup	*AvahiWebIFGroup	VALUE(NULL);
  +					/* Web interface entry group */
  +#endif /* HAVE_AVAHI */

scheduler/dirsvc.h
------------------

Function to create Avahi client, accessible for main.c.

@@ -2918,7 +2993,12 @@
   #ifdef HAVE_LIBSLP
   VAR SLPHandle		BrowseSLPHandle	VALUE(NULL);
   					/* SLP API handle */
-@@ -198,9 +214,9 @@
+@@ -194,13 +212,14 @@
+ extern void	cupsdRestartPolling(void);
+ extern void	cupsdSaveRemoteCache(void);
+ extern void	cupsdSendBrowseList(void);
++extern void	cupsdStartAvahiClient(void);
+ extern void	cupsdStartBrowsing(void);
   extern void	cupsdStartPolling(void);
   extern void	cupsdStopBrowsing(void);
   extern void	cupsdStopPolling(void);

scheduler/main.c
----------------

Every 10 seconds check whether there is no Avahi client running or
waiting and if so, create an Avahi client. If the Avahi daemon is
running, DNS-SD support gets started, if not, the client will wait for
the daemon.

@@ -2933,7 +3013,18 @@
  diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' 
'--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' 
cups-1.4.5~/scheduler/main.c cups-1.4.5/scheduler/main.c
  --- cups-1.4.5~/scheduler/main.c	2010-09-22 00:34:57.000000000 +0200
  +++ cups-1.4.5/scheduler/main.c	2010-12-26 18:00:30.983004655 +0100
-@@ -161,6 +161,10 @@
+@@ -137,6 +137,10 @@
+   cupsd_listener_t	*lis;		/* Current listener */
+   time_t		current_time,	/* Current time */
+ 			activity,	/* Client activity timer */
++#ifdef HAVE_AVAHI
++			avahi_client_time, /* Time for next Avahi client
++					   check */
++#endif /* HAVE_AVAHI */
+ 			browse_time,	/* Next browse send time */
+ 			senddoc_time,	/* Send-Document time */
+ 			expire_time,	/* Subscription expire time */
+@@ -163,6 +167,10 @@
     int			launchd_idle_exit;
   					/* Idle exit on select timeout? */
   #endif	/* HAVE_LAUNCHD */
@@ -2959,7 +3050,17 @@
     cupsdStartServer();

    /*
-@@ -890,6 +902,16 @@
+@@ -683,6 +699,9 @@
+   */
+
+   current_time  = time(NULL);
++#ifdef HAVE_AVAHI
++  avahi_client_time = current_time;
++#endif /* HAVE_AVAHI */
+   browse_time   = current_time;
+   event_time    = current_time;
+   expire_time   = current_time;
+@@ -897,6 +916,26 @@
       }
   #endif /* __APPLE__ */

@@ -2971,12 +3072,22 @@
  +    tmo = cupsdNextTimeout (&tmo_delay);
  +    if (tmo && tmo_delay == 0)
  +      cupsdRunTimeout (tmo);
++
++   /*
++    * Try to restart the Avahi client every 10 seconds if needed...
++    */
++
++    if ((current_time - avahi_client_time) >= 10)
++    {
++      avahi_client_time = current_time;
++      cupsdStartAvahiClient();
++    }
  +#endif /* HAVE_AVAHI */
  +
   #ifndef __APPLE__
      /*
       * Update the network interfaces once a minute...
-@@ -1915,6 +1937,10 @@
+@@ -1953,6 +1992,10 @@
     cupsd_job_t		*job;		/* Job information */
     cupsd_subscription_t	*sub;		/* Subscription information */
     const char		*why;		/* Debugging aid */

scheduler/timeout.c
-------------------

Added NULL checks to the cupsdRunTimeout() function. Also this
prevented segfaults for me.

@@ -3275,7 +3386,11 @@
  +void
  +cupsdRunTimeout(cupsd_timeout_t *timeout)	/* I - Timeout */
  +{
++  if (!timeout)
++    return;
  +  timeout->enabled = 0;
++  if (!timeout->callback)
++    return;
  +  timeout->callback (timeout, timeout->data);
  +}
  +



On 12/24/2010 02:37 PM, Tim Waugh wrote:
> Hi,
>
> I've been working on adding native Avahi support to CUPS for the past
> week, and now have a working implementation.
>
> The patch is quite large, and has several "layers":
>
> * adding Avahi name resolution support to cups/http-support.c;
> * adding Avahi discovery to backend/dnssd.c;
> * adding timeout support to the CUPS main loop;
> * creating an Avahi poll API implementation for the CUPS main loop;
> * and finally using Avahi to announce printers and the web interface in
> dirsvc.c.
>
> What's the best way to submit this feature?
>
>   backend/dnssd.c              |  580 +++++++++++++++++++++----
>   config-scripts/cups-dnssd.m4 |   15
>   config.h.in                  |    7
>   cups/http-support.c          |  190 ++++++++
>   scheduler/Makefile           |    4
>   scheduler/avahi.c            |  441 +++++++++++++++++++
>   scheduler/avahi.h            |   49 ++
>   scheduler/cupsd.h            |   24 +
>   scheduler/dirsvc.c           |  986 +++++++++++++++++++++++++++++--------------
>   scheduler/dirsvc.h           |   30 +
>   scheduler/main.c             |   39 +
>   scheduler/printers.c         |   20
>   scheduler/printers.h         |   18
>   scheduler/timeout.c          |  191 ++++++++
>   14 files changed, 2187 insertions(+), 407 deletions(-)
>
> The full patch against CUPS 1.4.5 is here:
> http://pkgs.fedoraproject.org/gitweb/?p=cups.git;a=blob;f=cups-avahi.patch;hb=HEAD
>
> but obviously I'll split it up into the various parts before submitting
> it.
>
> Tim.
> */
>

-------------- next part --------------
A non-text attachment was scrubbed...
Name: cups-avahi.patch
Type: text/x-patch
Size: 92351 bytes
Desc: not available
URL: <http://lists.cups.org/pipermail/cups-devel/attachments/20101229/e7c99816/attachment.bin>


More information about the cups-devel mailing list