/*
* "$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$".
*/