Blob Blame History Raw
/*
 * "$Id$"
 *
 *   Avahi poll implementation for the CUPS scheduler.
 *
 *   Copyright (C) 2010, 2011 Red Hat, Inc.
 *   Authors:
 *    Tim Waugh <twaugh@redhat.com>
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *
 *   Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 *   Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *   COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 *   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 *   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 *   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 *   OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Contents:
 *
 *   watch_read_cb         - Read callback for file descriptor
 *   watch_write_cb        - Write callback for file descriptor
 *   watched_fd_add_select() - Call cupsdAddSelect() as needed
 *   watch_new()           - Create a new file descriptor watch
 *   watch_free()          - Free a file descriptor watch
 *   watch_update()        - Update watched events for a file descriptor
 *   watch_get_events()    - Get events that happened for a file descriptor
 *   timeout_cb()          - Run a timed Avahi callback
 *   timeout_new()         - Set a wakeup time
 *   timeout_update()      - Update the expiration time for a timeout
 *   timeout_free()        - Free a timeout
 *   compare_watched_fds() - Compare watched file descriptors for array sorting
 *   avahi_cups_poll_new() - Create a new Avahi main loop object for CUPS
 *   avahi_cups_poll_free() - Free an Avahi main loop object for CUPS
 *   avahi_cups_poll_get() - Get the abstract poll API structure
 */

#include <config.h>

#ifdef HAVE_AVAHI /* Applies to entire file... */

/*
 * Include necessary headers...
 */

#include "cupsd.h"

#if defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO)
#  include <malloc.h>
#endif /* HAVE_MALLOC_H && HAVE_MALLINFO */

#ifdef HAVE_AVAHI
#  include <avahi-common/timeval.h>
#endif /* HAVE_AVAHI */


typedef struct
{
  AvahiCupsPoll *cups_poll;

  int fd;
  AvahiWatchEvent occurred;
  cups_array_t *watches;
} cupsd_watched_fd_t;

struct AvahiWatch
{
  cupsd_watched_fd_t *watched_fd;

  AvahiWatchEvent events;
  AvahiWatchCallback callback;
  void *userdata;
};

struct AvahiTimeout
{
  AvahiCupsPoll *cups_poll;
  AvahiTimeoutCallback callback;
  void *userdata;
  cupsd_timeout_t *cupsd_timeout;
};

/*
 * Local functions...
 */

static AvahiWatch *	watch_new(const AvahiPoll *api,
				  int fd,
				  AvahiWatchEvent events,
				  AvahiWatchCallback callback,
				  void *userdata);
static void		watch_free(AvahiWatch *watch);
static void		watch_update(AvahiWatch *watch,
				     AvahiWatchEvent events);
static AvahiWatchEvent	watch_get_events(AvahiWatch *watch);


/*
 * 'watch_read_cb' - Read callback for file descriptor
 */

static void
watch_read_cb (void *userdata)
{
  AvahiWatch *watch;
  cupsd_watched_fd_t *watched_fd = userdata;
  watched_fd->occurred |= AVAHI_WATCH_IN;
  for (watch = (AvahiWatch *)cupsArrayFirst(watched_fd->watches);
       watch;
       watch = (AvahiWatch *)cupsArrayNext(watched_fd->watches))
  {
    if (watch->events & watched_fd->occurred)
    {
      (watch->callback) (watch, watched_fd->fd,
			 AVAHI_WATCH_IN, watch->userdata);
      watched_fd->occurred &= ~AVAHI_WATCH_IN;
      break;
    }
  }
}


/*
 * 'watch_write_cb' - Write callback for file descriptor
 */

static void
watch_write_cb (void *userdata)
{
  AvahiWatch *watch;
  cupsd_watched_fd_t *watched_fd = userdata;
  watched_fd->occurred |= AVAHI_WATCH_OUT;
  for (watch = (AvahiWatch *)cupsArrayFirst(watched_fd->watches);
       watch;
       watch = (AvahiWatch *)cupsArrayNext(watched_fd->watches))
  {
    if (watch->events & watched_fd->occurred)
    {
      (watch->callback) (watch, watched_fd->fd,
			 AVAHI_WATCH_OUT, watch->userdata);
      watched_fd->occurred &= ~AVAHI_WATCH_OUT;
      break;
    }
  }
}


/*
 * 'watched_fd_add_select' - Call cupsdAddSelect() as needed
 */

static int						/* O - Watches? */
watched_fd_add_select (cupsd_watched_fd_t *watched_fd)
{
  AvahiWatch *watch;
  cupsd_selfunc_t read_cb = NULL, write_cb = NULL;
  int any_watches = 0;

  for (watch = (AvahiWatch *)cupsArrayFirst(watched_fd->watches);
       watch;
       watch = (AvahiWatch *)cupsArrayNext(watched_fd->watches))
  {
    any_watches = 1;
    if (watch->events & (AVAHI_WATCH_IN |
			     AVAHI_WATCH_ERR |
			     AVAHI_WATCH_HUP))
    {
      read_cb = (cupsd_selfunc_t)watch_read_cb;
      if (write_cb != NULL)
	break;
    }

    if (watch->events & AVAHI_WATCH_OUT)
    {
      write_cb = (cupsd_selfunc_t)watch_write_cb;
      if (read_cb != NULL)
	break;
    }
  }

  if (read_cb || write_cb)
    cupsdAddSelect (watched_fd->fd, read_cb, write_cb, watched_fd);
  else
    cupsdRemoveSelect (watched_fd->fd);

  return (any_watches);
}

/*
 * 'watch_new' - Create a new file descriptor watch
 */

static AvahiWatch *
watch_new (const AvahiPoll *api,
	   int fd,
	   AvahiWatchEvent events,
	   AvahiWatchCallback callback,
	   void *userdata)
{
  cupsd_watched_fd_t key, *watched_fd;
  AvahiCupsPoll *cups_poll = api->userdata;
  AvahiWatch *watch = malloc(sizeof(AvahiWatch));
  if (watch == NULL)
    return (NULL);

  watch->events = events;
  watch->callback = callback;
  watch->userdata = userdata;

  key.fd = fd;
  watched_fd = cupsArrayFind (cups_poll->watched_fds, &key);
  if (watched_fd == NULL)
  {
    watched_fd = malloc(sizeof(cupsd_watched_fd_t));
    if (watched_fd == NULL)
    {
      free (watch);
      return (NULL);
    }

    watched_fd->fd = fd;
    watched_fd->occurred = 0;
    watched_fd->cups_poll = cups_poll;
    watched_fd->watches = cupsArrayNew (NULL, NULL);
    cupsArrayAdd (cups_poll->watched_fds, watched_fd);
  }

  watch->watched_fd = watched_fd;
  cupsArrayAdd(watched_fd->watches, watch);
  watched_fd_add_select (watched_fd);
  return (watch);
}


/*
 * 'watch_free' - Free a file descriptor watch
 */

static void
watch_free (AvahiWatch *watch)
{
  cupsd_watched_fd_t *watched_fd = watch->watched_fd;
  AvahiCupsPoll *cups_poll = watched_fd->cups_poll;

  cupsArrayRemove (watched_fd->watches, watch);
  free (watch);

  if (!watched_fd_add_select (watched_fd))
  {
    /* No more watches */
    cupsArrayRemove (cups_poll->watched_fds, watched_fd);
    free (watched_fd);
  }
}


/*
 * 'watch_update' - Update watched events for a file descriptor
 */

static void
watch_update (AvahiWatch *watch,
	      AvahiWatchEvent events)
{
  watch->events = events;
  watched_fd_add_select (watch->watched_fd);
}


/*
 * 'watch_get_events' - Get events that happened for a file descriptor
 */

static AvahiWatchEvent
watch_get_events (AvahiWatch *watch)
{
  return (watch->watched_fd->occurred);
}


/*
 * 'timeout_cb()' - Run a timed Avahi callback
 */

static void
timeout_cb (cupsd_timeout_t *cupsd_timeout, void *userdata)
{
  AvahiTimeout *timeout = userdata;
  (timeout->callback) (timeout, timeout->userdata);
}


/*
 * 'timeout_new' - Set a wakeup time
 */

static AvahiTimeout *
timeout_new (const AvahiPoll *api,
	     const struct timeval *tv,
	     AvahiTimeoutCallback callback,
	     void *userdata)
{
  AvahiTimeout *timeout;
  AvahiCupsPoll *cups_poll = api->userdata;

  timeout = malloc(sizeof(AvahiTimeout));
  if (timeout == NULL)
    return (NULL);

  timeout->cups_poll = cups_poll;
  timeout->callback = callback;
  timeout->userdata = userdata;
  timeout->cupsd_timeout = cupsdAddTimeout (tv,
					    (cupsd_timeoutfunc_t)timeout_cb,
					    timeout);
  cupsArrayAdd (cups_poll->timeouts, timeout);
  return (timeout);
}


/*
 * 'timeout_update' - Update the expiration time for a timeout
 */

static void
timeout_update (AvahiTimeout *timeout,
		const struct timeval *tv)
{
  cupsdUpdateTimeout (timeout->cupsd_timeout, tv);
}


/*
 * ' timeout_free' - Free a timeout
 */

static void
timeout_free (AvahiTimeout *timeout)
{
  cupsArrayRemove (timeout->cups_poll->timeouts, timeout);
  cupsdRemoveTimeout (timeout->cupsd_timeout);
  free (timeout);
}


/*
 * 'compare_watched_fds' - Compare watched file descriptors for array sorting
 */
static int
compare_watched_fds(cupsd_watched_fd_t *p0,
		    cupsd_watched_fd_t *p1)
{
  /*
   * Compare by fd (no two elements have the same fd)
   */

  if (p0->fd == p1->fd)
    return 0;

  return (p0->fd < p1->fd ? -1 : 1);
}


/*
 * 'avahi_cups_poll_new' - Create a new Avahi main loop object for CUPS
 */

AvahiCupsPoll *
avahi_cups_poll_new (void)
{
  AvahiCupsPoll *cups_poll = malloc(sizeof(AvahiCupsPoll));
  if (cups_poll == NULL)
    return (NULL);

  cups_poll->watched_fds = cupsArrayNew ((cups_array_func_t)compare_watched_fds,
					 NULL);
  cups_poll->timeouts = cupsArrayNew (NULL, NULL);

  cups_poll->api.userdata = cups_poll;
  cups_poll->api.watch_new = watch_new;
  cups_poll->api.watch_free = watch_free;
  cups_poll->api.watch_update = watch_update;
  cups_poll->api.watch_get_events = watch_get_events;

  cups_poll->api.timeout_new = timeout_new;
  cups_poll->api.timeout_update = timeout_update;
  cups_poll->api.timeout_free = timeout_free;

  return (cups_poll);
}


/*
 * 'avahi_cups_poll_free' - Free an Avahi main loop object for CUPS
 */
void
avahi_cups_poll_free (AvahiCupsPoll *cups_poll)
{
  cupsd_watched_fd_t *watched_fd;

  for (watched_fd = (cupsd_watched_fd_t*)cupsArrayFirst(cups_poll->watched_fds);
       watched_fd;
       watched_fd = (cupsd_watched_fd_t*)cupsArrayNext(cups_poll->watched_fds))
    cupsArrayClear (watched_fd->watches);

  cupsArrayClear (cups_poll->watched_fds);
  cupsArrayClear (cups_poll->timeouts);
}


/*
 * 'avahi_cups_poll_get' - Get the abstract poll API structure
 */

const AvahiPoll *
avahi_cups_poll_get (AvahiCupsPoll *cups_poll)
{
  return (&cups_poll->api);
}


#endif /* HAVE_AVAHI ... from top of file */

/*
 * End of "$Id$".
 */