diff options
Diffstat (limited to '')
-rw-r--r-- | linux/avahi-client/client.h | 119 | ||||
-rw-r--r-- | linux/avahi-client/lookup.h | 295 | ||||
-rw-r--r-- | linux/avahi-client/publish.h | 174 | ||||
-rw-r--r-- | linux/avahi-common/address.h | 121 | ||||
-rw-r--r-- | linux/avahi-common/alternative.h | 45 | ||||
-rw-r--r-- | linux/avahi-common/cdecl.h | 40 | ||||
-rw-r--r-- | linux/avahi-common/defs.h | 354 | ||||
-rw-r--r-- | linux/avahi-common/domain.h | 131 | ||||
-rw-r--r-- | linux/avahi-common/error.h | 109 | ||||
-rw-r--r-- | linux/avahi-common/gccmacro.h | 67 | ||||
-rw-r--r-- | linux/avahi-common/llist.h | 77 | ||||
-rw-r--r-- | linux/avahi-common/malloc.h | 98 | ||||
-rw-r--r-- | linux/avahi-common/rlist.h | 51 | ||||
-rw-r--r-- | linux/avahi-common/simple-watch.h | 87 | ||||
-rw-r--r-- | linux/avahi-common/strlst.h | 182 | ||||
-rw-r--r-- | linux/avahi-common/thread-watch.h | 82 | ||||
-rw-r--r-- | linux/avahi-common/timeval.h | 56 | ||||
-rw-r--r-- | linux/avahi-common/watch.h | 99 | ||||
-rw-r--r-- | linux/cusblinux.c | 673 | ||||
-rw-r--r-- | linux/zeroconf_avahi.c | 1056 |
20 files changed, 3916 insertions, 0 deletions
diff --git a/linux/avahi-client/client.h b/linux/avahi-client/client.h new file mode 100644 index 0000000..52fccb8 --- /dev/null +++ b/linux/avahi-client/client.h @@ -0,0 +1,119 @@ +#ifndef fooclienthfoo +#define fooclienthfoo + +/* $Id: client.h 1477 2007-05-09 19:45:54Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <inttypes.h> + +#include <avahi-common/cdecl.h> +#include <avahi-common/address.h> +#include <avahi-common/strlst.h> +#include <avahi-common/defs.h> +#include <avahi-common/watch.h> +#include <avahi-common/gccmacro.h> + +/** \file client.h Definitions and functions for the client API over D-Bus */ + +AVAHI_C_DECL_BEGIN + +/** A connection context */ +typedef struct AvahiClient AvahiClient; + +/** States of a client object, a superset of AvahiServerState */ +typedef enum { + AVAHI_CLIENT_S_REGISTERING = AVAHI_SERVER_REGISTERING, /**< Server state: REGISTERING */ + AVAHI_CLIENT_S_RUNNING = AVAHI_SERVER_RUNNING, /**< Server state: RUNNING */ + AVAHI_CLIENT_S_COLLISION = AVAHI_SERVER_COLLISION, /**< Server state: COLLISION */ + AVAHI_CLIENT_FAILURE = 100, /**< Some kind of error happened on the client side */ + AVAHI_CLIENT_CONNECTING = 101 /**< We're still connecting. This state is only entered when AVAHI_CLIENT_NO_FAIL has been passed to avahi_client_new() and the daemon is not yet available. */ +} AvahiClientState; + +typedef enum { + AVAHI_CLIENT_IGNORE_USER_CONFIG = 1, /**< Don't read user configuration */ + AVAHI_CLIENT_NO_FAIL = 2 /**< Don't fail if the daemon is not available when avahi_client_new() is called, instead enter AVAHI_CLIENT_CONNECTING state and wait for the daemon to appear */ +} AvahiClientFlags; + +/** The function prototype for the callback of an AvahiClient */ +typedef void (*AvahiClientCallback) ( + AvahiClient *s, + AvahiClientState state /**< The new state of the client */, + void* userdata /**< The user data that was passed to avahi_client_new() */); + +/** @{ \name Construction and destruction */ + +/** Creates a new client instance */ +AvahiClient* avahi_client_new ( + const AvahiPoll *poll_api /**< The abstract event loop API to use */, + AvahiClientFlags flags /**< Some flags to modify the behaviour of the client library */, + AvahiClientCallback callback /**< A callback that is called whenever the state of the client changes. This may be NULL. Please note that this function is called for the first time from within the avahi_client_new() context! Thus, in the callback you should not make use of global variables that are initialized only after your call to avahi_client_new(). A common mistake is to store the AvahiClient pointer returned by avahi_client_new() in a global variable and assume that this global variable already contains the valid pointer when the callback is called for the first time. A work-around for this is to always use the AvahiClient pointer passed to the callback function instead of the global pointer. */, + void *userdata /**< Some arbitrary user data pointer that will be passed to the callback function */, + int *error /**< If creation of the client fails, this integer will contain the error cause. May be NULL if you aren't interested in the reason why avahi_client_new() failed. */); + +/** Free a client instance. This will automatically free all + * associated browser, resolve and entry group objects. All pointers + * to such objects become invalid! */ +void avahi_client_free(AvahiClient *client); + +/** @} */ + +/** @{ \name Properties */ + +/** Get the version of the server */ +const char* avahi_client_get_version_string (AvahiClient*); + +/** Get host name */ +const char* avahi_client_get_host_name (AvahiClient*); + +/** Set host name. \since 0.6.13 */ +int avahi_client_set_host_name(AvahiClient*, const char *name); + +/** Get domain name */ +const char* avahi_client_get_domain_name (AvahiClient*); + +/** Get FQDN domain name */ +const char* avahi_client_get_host_name_fqdn (AvahiClient*); + +/** Get state */ +AvahiClientState avahi_client_get_state(AvahiClient *client); + +/** @{ \name Error Handling */ + +/** Get the last error number. See avahi_strerror() for converting this error code into a human readable string. */ +int avahi_client_errno (AvahiClient*); + +/** @} */ + +/** \cond fulldocs */ +/** Return the local service cookie. returns AVAHI_SERVICE_COOKIE_INVALID on failure. */ +uint32_t avahi_client_get_local_service_cookie(AvahiClient *client); +/** \endcond */ + +/** @{ \name Libc NSS Support */ + +/** Return 1 if gethostbyname() supports mDNS lookups, 0 otherwise. \since 0.6.5 */ +int avahi_nss_support(void); + +/** @} */ + +AVAHI_C_DECL_END + +#endif diff --git a/linux/avahi-client/lookup.h b/linux/avahi-client/lookup.h new file mode 100644 index 0000000..c417fcf --- /dev/null +++ b/linux/avahi-client/lookup.h @@ -0,0 +1,295 @@ +#ifndef fooclientlookuphfoo +#define fooclientlookuphfoo + +/* $Id: lookup.h 1477 2007-05-09 19:45:54Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <inttypes.h> + +#include <avahi-common/cdecl.h> +#include <avahi-common/address.h> +#include <avahi-common/strlst.h> +#include <avahi-common/defs.h> +#include <avahi-common/watch.h> +#include <avahi-common/gccmacro.h> + +#include <avahi-client/client.h> + +/** \file avahi-client/lookup.h Lookup Client API */ + +/** \example client-browse-services.c Example how to browse for DNS-SD + * services using the client interface to avahi-daemon. */ + +AVAHI_C_DECL_BEGIN + +/** @{ \name Domain Browser */ + +/** A domain browser object */ +typedef struct AvahiDomainBrowser AvahiDomainBrowser; + +/** The function prototype for the callback of an AvahiDomainBrowser */ +typedef void (*AvahiDomainBrowserCallback) ( + AvahiDomainBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *domain, + AvahiLookupResultFlags flags, + void *userdata); + +/** Browse for domains on the local network */ +AvahiDomainBrowser* avahi_domain_browser_new ( + AvahiClient *client, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiDomainBrowserType btype, + AvahiLookupFlags flags, + AvahiDomainBrowserCallback callback, + void *userdata); + +/** Get the parent client of an AvahiDomainBrowser object */ +AvahiClient* avahi_domain_browser_get_client (AvahiDomainBrowser *); + +/** Cleans up and frees an AvahiDomainBrowser object */ +int avahi_domain_browser_free (AvahiDomainBrowser *); + +/** @} */ + +/** @{ \name Service Browser */ + +/** A service browser object */ +typedef struct AvahiServiceBrowser AvahiServiceBrowser; + +/** The function prototype for the callback of an AvahiServiceBrowser */ +typedef void (*AvahiServiceBrowserCallback) ( + AvahiServiceBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AvahiLookupResultFlags flags, + void *userdata); + +/** Browse for services of a type on the local network */ +AvahiServiceBrowser* avahi_service_browser_new ( + AvahiClient *client, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *type, + const char *domain, + AvahiLookupFlags flags, + AvahiServiceBrowserCallback callback, + void *userdata); + +/** Get the parent client of an AvahiServiceBrowser object */ +AvahiClient* avahi_service_browser_get_client (AvahiServiceBrowser *); + +/** Cleans up and frees an AvahiServiceBrowser object */ +int avahi_service_browser_free (AvahiServiceBrowser *); + +/** @} */ + +/** \cond fulldocs */ +/** A service type browser object */ +typedef struct AvahiServiceTypeBrowser AvahiServiceTypeBrowser; + +/** The function prototype for the callback of an AvahiServiceTypeBrowser */ +typedef void (*AvahiServiceTypeBrowserCallback) ( + AvahiServiceTypeBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *type, + const char *domain, + AvahiLookupResultFlags flags, + void *userdata); + +/** Browse for service types on the local network */ +AvahiServiceTypeBrowser* avahi_service_type_browser_new ( + AvahiClient *client, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiLookupFlags flags, + AvahiServiceTypeBrowserCallback callback, + void *userdata); + +/** Get the parent client of an AvahiServiceTypeBrowser object */ +AvahiClient* avahi_service_type_browser_get_client (AvahiServiceTypeBrowser *); + +/** Cleans up and frees an AvahiServiceTypeBrowser object */ +int avahi_service_type_browser_free (AvahiServiceTypeBrowser *); + +/** \endcond */ + +/** @{ \name Service Resolver */ + +/** A service resolver object */ +typedef struct AvahiServiceResolver AvahiServiceResolver; + +/** The function prototype for the callback of an AvahiServiceResolver */ +typedef void (*AvahiServiceResolverCallback) ( + AvahiServiceResolver *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char *type, + const char *domain, + const char *host_name, + const AvahiAddress *a, + uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags flags, + void *userdata); + +/** Create a new service resolver object. Please make sure to pass all + * the service data you received via avahi_service_browser_new()'s + * callback function, especially interface and protocol. */ +AvahiServiceResolver * avahi_service_resolver_new( + AvahiClient *client, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *name, + const char *type, + const char *domain, + AvahiProtocol aprotocol, + AvahiLookupFlags flags, + AvahiServiceResolverCallback callback, + void *userdata); + +/** Get the parent client of an AvahiServiceResolver object */ +AvahiClient* avahi_service_resolver_get_client (AvahiServiceResolver *); + +/** Free a service resolver object */ +int avahi_service_resolver_free(AvahiServiceResolver *r); + +/** @} */ + +/** \cond fulldocs */ +/** A service resolver object */ +typedef struct AvahiHostNameResolver AvahiHostNameResolver; + +/** The function prototype for the callback of an AvahiHostNameResolver */ +typedef void (*AvahiHostNameResolverCallback) ( + AvahiHostNameResolver *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const AvahiAddress *a, + AvahiLookupResultFlags flags, + void *userdata); + +/** Create a new hostname resolver object */ +AvahiHostNameResolver * avahi_host_name_resolver_new( + AvahiClient *client, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *name, + AvahiProtocol aprotocol, + AvahiLookupFlags flags, + AvahiHostNameResolverCallback callback, + void *userdata); + +/** Get the parent client of an AvahiHostNameResolver object */ +AvahiClient* avahi_host_name_resolver_get_client (AvahiHostNameResolver *); + +/** Free a hostname resolver object */ +int avahi_host_name_resolver_free(AvahiHostNameResolver *r); + +/** An address resolver object */ +typedef struct AvahiAddressResolver AvahiAddressResolver; + +/** The function prototype for the callback of an AvahiAddressResolver */ +typedef void (*AvahiAddressResolverCallback) ( + AvahiAddressResolver *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const AvahiAddress *a, + const char *name, + AvahiLookupResultFlags flags, + void *userdata); + +/** Create a new address resolver object from an AvahiAddress object */ +AvahiAddressResolver* avahi_address_resolver_new( + AvahiClient *client, + AvahiIfIndex interface, + AvahiProtocol protocol, + const AvahiAddress *a, + AvahiLookupFlags flags, + AvahiAddressResolverCallback callback, + void *userdata); + +/** Get the parent client of an AvahiAddressResolver object */ +AvahiClient* avahi_address_resolver_get_client (AvahiAddressResolver *); + +/** Free a AvahiAddressResolver resolver object */ +int avahi_address_resolver_free(AvahiAddressResolver *r); + +/** \endcond */ + +/** @{ \name Record Browser */ + +/** A record browser object */ +typedef struct AvahiRecordBrowser AvahiRecordBrowser; + +/** The function prototype for the callback of an AvahiRecordBrowser */ +typedef void (*AvahiRecordBrowserCallback) ( + AvahiRecordBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + uint16_t clazz, + uint16_t type, + const void *rdata, + size_t size, + AvahiLookupResultFlags flags, + void *userdata); + +/** Browse for records of a type on the local network */ +AvahiRecordBrowser* avahi_record_browser_new( + AvahiClient *client, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *name, + uint16_t clazz, + uint16_t type, + AvahiLookupFlags flags, + AvahiRecordBrowserCallback callback, + void *userdata); + +/** Get the parent client of an AvahiRecordBrowser object */ +AvahiClient* avahi_record_browser_get_client(AvahiRecordBrowser *); + +/** Cleans up and frees an AvahiRecordBrowser object */ +int avahi_record_browser_free(AvahiRecordBrowser *); + +/** @} */ + +AVAHI_C_DECL_END + +#endif diff --git a/linux/avahi-client/publish.h b/linux/avahi-client/publish.h new file mode 100644 index 0000000..714a689 --- /dev/null +++ b/linux/avahi-client/publish.h @@ -0,0 +1,174 @@ +#ifndef fooclientpublishhfoo +#define fooclientpublishhfoo + +/* $Id: publish.h 1502 2007-07-30 20:12:20Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include <inttypes.h> + +#include <avahi-common/cdecl.h> +#include <avahi-common/address.h> +#include <avahi-common/strlst.h> +#include <avahi-common/defs.h> +#include <avahi-common/watch.h> +#include <avahi-common/gccmacro.h> + +#include <avahi-client/client.h> + +/** \file avahi-client/publish.h Publishing Client API */ + +/** \example client-publish-service.c Example how to register a DNS-SD + * service using the client interface to avahi-daemon. It behaves like a network + * printer registering both an IPP and a BSD LPR service. */ + +AVAHI_C_DECL_BEGIN + +/** An entry group object */ +typedef struct AvahiEntryGroup AvahiEntryGroup; + +/** The function prototype for the callback of an AvahiEntryGroup */ +typedef void (*AvahiEntryGroupCallback) ( + AvahiEntryGroup *g, + AvahiEntryGroupState state /**< The new state of the entry group */, + void* userdata /* The arbitrary user data pointer originally passed to avahi_entry_group_new()*/); + +/** @{ \name Construction and destruction */ + +/** Create a new AvahiEntryGroup object */ +AvahiEntryGroup* avahi_entry_group_new( + AvahiClient* c, + AvahiEntryGroupCallback callback /**< This callback is called whenever the state of this entry group changes. May not be NULL. Please note that this function is called for the first time from within the avahi_entry_group_new() context! Thus, in the callback you should not make use of global variables that are initialized only after your call to avahi_entry_group_new(). A common mistake is to store the AvahiEntryGroup pointer returned by avahi_entry_group_new() in a global variable and assume that this global variable already contains the valid pointer when the callback is called for the first time. A work-around for this is to always use the AvahiEntryGroup pointer passed to the callback function instead of the global pointer. */, + void *userdata /**< This arbitrary user data pointer will be passed to the callback functon */); + +/** Clean up and free an AvahiEntryGroup object */ +int avahi_entry_group_free (AvahiEntryGroup *); + +/** @} */ + +/** @{ \name State */ + +/** Commit an AvahiEntryGroup. The entries in the entry group are now registered on the network. Commiting empty entry groups is considered an error. */ +int avahi_entry_group_commit (AvahiEntryGroup*); + +/** Reset an AvahiEntryGroup. This takes effect immediately. */ +int avahi_entry_group_reset (AvahiEntryGroup*); + +/** Get an AvahiEntryGroup's state */ +int avahi_entry_group_get_state (AvahiEntryGroup*); + +/** Check if an AvahiEntryGroup is empty */ +int avahi_entry_group_is_empty (AvahiEntryGroup*); + +/** Get an AvahiEntryGroup's owning client instance */ +AvahiClient* avahi_entry_group_get_client (AvahiEntryGroup*); + +/** @} */ + +/** @{ \name Adding and updating entries */ + +/** Add a service. Takes a variable NULL terminated list of TXT record strings as last arguments. Please note that this service is not announced on the network before avahi_entry_group_commit() is called. */ +int avahi_entry_group_add_service( + AvahiEntryGroup *group, + AvahiIfIndex interface /**< The interface this service shall be announced on. We recommend to pass AVAHI_IF_UNSPEC here, to announce on all interfaces. */, + AvahiProtocol protocol /**< The protocol this service shall be announced with, i.e. MDNS over IPV4 or MDNS over IPV6. We recommend to pass AVAHI_PROTO_UNSPEC here, to announce this service on all protocols the daemon supports. */, + AvahiPublishFlags flags /**< Usually 0, unless you know what you do */, + const char *name /**< The name for the new service. Must be valid service name. i.e. a string shorter than 63 characters and valid UTF-8. May not be NULL. */, + const char *type /**< The service type for the new service, such as _http._tcp. May not be NULL. */, + const char *domain /**< The domain to register this domain in. We recommend to pass NULL here, to let the daemon decide */, + const char *host /**< The host this services is residing on. We recommend to pass NULL here, the daemon will than automatically insert the local host name in that case */, + uint16_t port /**< The IP port number of this service */, + ...) AVAHI_GCC_SENTINEL; + +/** Add a service, takes an AvahiStringList for TXT records. Arguments have the same meaning as for avahi_entry_group_add_service(). */ +int avahi_entry_group_add_service_strlst( + AvahiEntryGroup *group, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + const char *host, + uint16_t port, + AvahiStringList *txt /**< The TXT data for this service. You may free this object after calling this function, it is not referenced any further */); + +/** Add a subtype for a service. The service should already be existent in the entry group. You may add as many subtypes for a service as you wish. */ +int avahi_entry_group_add_service_subtype( + AvahiEntryGroup *group, + AvahiIfIndex interface /**< The interface this subtype shall be announced on. This should match the value passed for the original avahi_entry_group_add_service() call. */, + AvahiProtocol protocol /**< The protocol this subtype shall be announced with. This should match the value passed for the original avahi_entry_group_add_service() call. */, + AvahiPublishFlags flags /**< Only != 0 if you really know what you do */, + const char *name /**< The name of the service, as passed to avahi_entry_group_add_service(). May not be NULL. */, + const char *type /**< The type of the service, as passed to avahi_entry_group_add_service(). May not be NULL. */, + const char *domain /**< The domain this service resides is, as passed to avahi_entry_group_add_service(). May be NULL. */, + const char *subtype /**< The new subtype to register for the specified service. May not be NULL. */); + +/** Update a TXT record for an existing service. The service should already be existent in the entry group. */ +int avahi_entry_group_update_service_txt( + AvahiEntryGroup *g, + AvahiIfIndex interface /**< The interface this service is announced on. This should match the value passed to the original avahi_entry_group_add_service() call. */, + AvahiProtocol protocol /**< The protocol this service is announced with. This should match the value passed to the original avahi_entry_group_add_service() call. */, + AvahiPublishFlags flags /**< Only != 0 if you really know what you do */, + const char *name /**< The name of the service, as passed to avahi_entry_group_add_service(). May not be NULL. */, + const char *type /**< The type of the service, as passed to avahi_entry_group_add_service(). May not be NULL. */, + const char *domain /**< The domain this service resides is, as passed to avahi_entry_group_add_service(). May be NULL. */, + ...) AVAHI_GCC_SENTINEL; + +/** Update a TXT record for an existing service. Similar to avahi_entry_group_update_service_txt() but takes an AvahiStringList for the TXT strings, instead of a NULL terminated list of arguments. */ +int avahi_entry_group_update_service_txt_strlst( + AvahiEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + const char *type, + const char *domain, + AvahiStringList *strlst); + +/** \cond fulldocs */ +/** Add a host/address pair */ +int avahi_entry_group_add_address( + AvahiEntryGroup *group, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name /**< The FDQN of the new hostname to register */, + const AvahiAddress *a /**< The address this host name shall map to */); +/** \endcond */ + +/** Add an arbitrary record. I hope you know what you do. */ +int avahi_entry_group_add_record( + AvahiEntryGroup *group, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const char *name, + uint16_t clazz, + uint16_t type, + uint32_t ttl, + const void *rdata, + size_t size); + +/** @} */ + +AVAHI_C_DECL_END + +#endif diff --git a/linux/avahi-common/address.h b/linux/avahi-common/address.h new file mode 100644 index 0000000..b6df0df --- /dev/null +++ b/linux/avahi-common/address.h @@ -0,0 +1,121 @@ +#ifndef fooaddresshfoo +#define fooaddresshfoo + +/* $Id: address.h 1477 2007-05-09 19:45:54Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file address.h Definitions and functions to manipulate IP addresses. */ + +#include <inttypes.h> +#include <sys/types.h> + +#include <avahi-common/cdecl.h> + +AVAHI_C_DECL_BEGIN + +/** Protocol family specification, takes the values AVAHI_PROTO_INET, AVAHI_PROTO_INET6, AVAHI_PROTO_UNSPEC */ +typedef int AvahiProtocol; + +/** Numeric network interface index. Takes OS dependent values and the special constant AVAHI_IF_UNSPEC */ +typedef int AvahiIfIndex; + +/** Values for AvahiProtocol */ +enum { + AVAHI_PROTO_INET = 0, /**< IPv4 */ + AVAHI_PROTO_INET6 = 1, /**< IPv6 */ + AVAHI_PROTO_UNSPEC = -1 /**< Unspecified/all protocol(s) */ +}; + +/** Special values for AvahiIfIndex */ +enum { + AVAHI_IF_UNSPEC = -1 /**< Unspecified/all interface(s) */ +}; + +/** Maximum size of an address in string form */ +#define AVAHI_ADDRESS_STR_MAX 40 /* IPv6 Max = 4*8 + 7 + 1 for NUL */ + +/** Return TRUE if the specified interface index is valid */ +#define AVAHI_IF_VALID(ifindex) (((ifindex) >= 0) || ((ifindex) == AVAHI_IF_UNSPEC)) + +/** Return TRUE if the specified protocol is valid */ +#define AVAHI_PROTO_VALID(protocol) (((protocol) == AVAHI_PROTO_INET) || ((protocol) == AVAHI_PROTO_INET6) || ((protocol) == AVAHI_PROTO_UNSPEC)) + +/** An IPv4 address */ +typedef struct AvahiIPv4Address { + uint32_t address; /**< Address data in network byte order. */ +} AvahiIPv4Address; + +/** An IPv6 address */ +typedef struct AvahiIPv6Address { + uint8_t address[16]; /**< Address data */ +} AvahiIPv6Address; + +/** Protocol (address family) independent address structure */ +typedef struct AvahiAddress { + AvahiProtocol proto; /**< Address family */ + + union { + AvahiIPv6Address ipv6; /**< Address when IPv6 */ + AvahiIPv4Address ipv4; /**< Address when IPv4 */ + uint8_t data[1]; /**< Type independant data field */ + } data; +} AvahiAddress; + +/** @{ \name Comparison */ + +/** Compare two addresses. Returns 0 when equal, a negative value when a < b, a positive value when a > b. */ +int avahi_address_cmp(const AvahiAddress *a, const AvahiAddress *b); + +/** @} */ + +/** @{ \name String conversion */ + +/** Convert the specified address *a to a human readable character string, use AVAHI_ADDRESS_STR_MAX to allocate an array of the right size */ +char *avahi_address_snprint(char *ret_s, size_t length, const AvahiAddress *a); + +/** Convert the specified human readable character string to an + * address structure. Set af to AVAHI_UNSPEC for automatic address + * family detection. */ +AvahiAddress *avahi_address_parse(const char *s, AvahiProtocol af, AvahiAddress *ret_addr); + +/** @} */ + +/** \cond fulldocs */ +/** Generate the DNS reverse lookup name for an IPv4 or IPv6 address. */ +char* avahi_reverse_lookup_name(const AvahiAddress *a, char *ret_s, size_t length); +/** \endcond */ + +/** @{ \name Protocol/address family handling */ + +/** Map AVAHI_PROTO_xxx constants to Unix AF_xxx constants */ +int avahi_proto_to_af(AvahiProtocol proto); + +/** Map Unix AF_xxx constants to AVAHI_PROTO_xxx constants */ +AvahiProtocol avahi_af_to_proto(int af); + +/** Return a textual representation of the specified protocol number. i.e. "IPv4", "IPv6" or "UNSPEC" */ +const char* avahi_proto_to_string(AvahiProtocol proto); + +/** @} */ + +AVAHI_C_DECL_END + +#endif diff --git a/linux/avahi-common/alternative.h b/linux/avahi-common/alternative.h new file mode 100644 index 0000000..2d561be --- /dev/null +++ b/linux/avahi-common/alternative.h @@ -0,0 +1,45 @@ +#ifndef fooalternativehfoo +#define fooalternativehfoo + +/* $Id: alternative.h 874 2005-10-26 01:34:48Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file alternative.h Functions to find alternative names for hosts and services in the case of name collision */ + +#include <avahi-common/cdecl.h> + +AVAHI_C_DECL_BEGIN + +/** Find an alternative for the specified host name. If called with an + * original host name, "2" is appended, Afterwards the number is + * increased on each call. (i.e. "foo" becomes "foo2" becomes "foo3" + * and so on.) avahi_free() the result. */ +char *avahi_alternative_host_name(const char *s); + +/** Find an alternative for the specified service name. If called with + * an original service name, " #2" is appended. Afterwards the number + * is increased on each call (i.e. "foo" becomes "foo #2" becomes "foo + * #3" and so on.) avahi_free() the result. */ +char *avahi_alternative_service_name(const char *s); + +AVAHI_C_DECL_END + +#endif diff --git a/linux/avahi-common/cdecl.h b/linux/avahi-common/cdecl.h new file mode 100644 index 0000000..516d825 --- /dev/null +++ b/linux/avahi-common/cdecl.h @@ -0,0 +1,40 @@ +#ifndef foocdeclhfoo +#define foocdeclhfoo + +/* $Id: cdecl.h 872 2005-10-26 01:21:30Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file cdecl.h C++ compatibility */ +#ifdef __cplusplus +/** If using C++ this macro enables C mode, otherwise does nothing */ +#define AVAHI_C_DECL_BEGIN extern "C" { +/** If using C++ this macros switches back to C++ mode, otherwise does nothing */ +#define AVAHI_C_DECL_END } + +#else +/** If using C++ this macro enables C mode, otherwise does nothing */ +#define AVAHI_C_DECL_BEGIN +/** If using C++ this macros switches back to C++ mode, otherwise does nothing */ +#define AVAHI_C_DECL_END + +#endif + +#endif diff --git a/linux/avahi-common/defs.h b/linux/avahi-common/defs.h new file mode 100644 index 0000000..21807fc --- /dev/null +++ b/linux/avahi-common/defs.h @@ -0,0 +1,354 @@ +#ifndef foodefshfoo +#define foodefshfoo + +/* $Id: defs.h 1511 2007-08-12 15:41:45Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file defs.h Some common definitions */ + +#include <avahi-common/cdecl.h> + +/** \mainpage + * + * \section choose_api Choosing an API + * + * Avahi provides three programming APIs for integration of + * mDNS/DNS-SD features into your C progams: + * + * \li <b>avahi-core</b>: an API for embedding a complete mDNS/DNS-SD stack + * into your software. This is intended for developers of embedded + * ampliances only. We dissuade from using this API in normal desktop + * applications since it is not a good idea to run multiple mDNS + * stacks simultaneously on the same host. + * \li <b>the D-Bus API</b>: an extensive D-Bus interface for browsing and + * registering mDNS/DNS-SD services using avahi-daemon. We recommend + * to use this API for software written in any language but + * C. (i.e. Python) + * \li <b>avahi-client</b>: a simplifying C wrapper around the D-Bus API. We + * recommend to use this API in C or C++ progams. The D-Bus internals + * are hidden completely. + * + * All three APIs are very similar, however avahi-core is the most powerful. + * + * In addition to the three APIs described above Avahi supports two + * compatibility libraries: + * + * \li <b>avahi-compat-libdns_sd</b>: the original Bonjour API as documented + * in the header file "dns_sd.h" by Apple Computer, Inc. + * + * \li <b>avahi-compat-howl</b>: the HOWL API as released with HOWL 0.9.8 by + * Porchdog Software. + * + * Please note that these compatibility layers are incomplete and + * generally a waste of resources. We strongly encourage everyone to + * use our native APIs for newly written programs and to port older + * programs to avahi-client! + * + * The native APIs (avahi-client and avahi-core) can be integrated + * into external event loops. We provide adapters for the following + * event loop implementations: + * + * \li <b>avahi-glib</b>: The GLIB main loop as used by GTk+/GNOME + * + * \li <b>avahi-qt</b>: The Qt main loop as used by Qt/KDE + * + * Finally, we provide a high-level Gtk+ GUI dialog called + * <b>avahi-ui</b> for user-friendly browsing for services. + * + * The doxygen-generated API documentation covers avahi-client + * (including its auxiliary APIs), the event loop adapters and + * avahi-ui. For the other APIs please consult the original + * documentation (for the compatibility APIs) or the header files. + * + * Please note that the doxygen-generated API documentation of the + * native Avahi API is not complete. A few definitions that are part + * of the Avahi API have been removed from this documentation, either + * because they are only relevant in a very few low-level applications + * or because they are considered obsolete. Please consult the C header + * files for all definitions that are part of the Avahi API. Please + * note that these hidden definitions are considered part of the Avahi + * API and will stay available in the API in the future. + * + * \section error_reporting Error Reporting + * + * Some notes on the Avahi error handling: + * + * - Error codes are negative integers and defined as AVAHI_ERR_xx + * - If a function returns some kind of non-negative integer value on + * success, a failure is indicated by returning the error code + * directly. + * - If a function returns a pointer of some kind on success, a + * failure is indicated by returning NULL + * - The last error number may be retrieved by calling + * avahi_client_errno() + * - Just like the libc errno variable the Avahi errno is NOT reset to + * AVAHI_OK if a function call succeeds. + * - You may convert a numeric error code into a human readable string + * using avahi_strerror() + * - The constructor function avahi_client_new() returns the error + * code in a call-by-reference argument + * + * \section event_loop Event Loop Abstraction + * + * Avahi uses a simple event loop abstraction laye. A table AvahiPoll + * which contains function pointers for user defined timeout and I/O + * condition event source implementations needs to be passed to + * avahi_client_new(). An adapter for this abstraction layer is + * available for the GLib main loop in the object AvahiGLibPoll. A + * simple stand-alone implementation is available under the name + * AvahiSimplePoll. An adpater for the Qt main loop is available from + * avahi_qt_poll_get(). + * + * \section good_publish How to Register Services + * + * - Subscribe to server state changes. Pass a callback function + * pointer to avahi_client_new(). It will be called + * whenever the server state changes. + * - Only register your services when the server is in state + * AVAHI_SERVER_RUNNING. If you register your services in other server + * states they might not be accessible since the local host name might not necessarily + * be established. + * - Remove your services when the server enters + * AVAHI_SERVER_COLLISION or AVAHI_SERVER_REGISTERING state. Your + * services may no be reachable anymore since the local host name is + * no longer established or is currently in the process of being + * established. + * - When registering services, use the following algorithm: + * - Create a new entry group (i.e. avahi_entry_group_new()) + * - Add your service(s)/additional RRs/subtypes (e.g. avahi_entry_group_add_service()) + * - Commit the entry group (i.e. avahi_entry_group_commit()) + * - Subscribe to entry group state changes. + * - If the entry group enters AVAHI_ENTRY_GROUP_COLLISION state the + * services of the entry group are automatically removed from the + * server. You may immediately add your services back to the entry + * group (but with new names, perhaps using + * avahi_alternative_service_name()) and commit again. Please do not + * free the entry group and create a new one. This would inhibit some + * traffic limiting algorithms in mDNS. + * - When you need to modify your services (i.e. change the TXT data + * or the port number), use the AVAHI_PUBLISH_UPDATE flag. Please do + * not free the entry group and create a new one. This would inhibit + * some traffic limiting algorithms in mDNS. When changing just the + * TXT data avahi_entry_group_update_txt() is a shortcut for + * AVAHI_PUBLISH_UPDATE. Please note that you cannot use + * AVAHI_PUBLISH_UPDATE when changing the service name! Renaming a + * DNS-SD service is identical to deleting and creating a new one, and + * that's exactly what you should do in that case. First call + * avahi_entry_group_reset() to remove it and than readd it normally. + * + * \section good_browse How to Browse for Services + * + * - For normal applications you need to call avahi_service_browser_new() + * for the service type you want to browse for. Use + * avahi_service_resolver_new() to acquire service data for a service + * name. + * - You can use avahi_domain_browser_new() to get a list of announced + * browsing domains. Please note that not all domains whith services + * on the LAN are mandatorily announced. + * - There is no need to subscribe to server state changes. + * + * \section daemon_dies How to Write a Client That Can Deal with Daemon Restarts + * + * With Avahi it is possible to write client applications that can + * deal with Avahi daemon restarts. To accomplish that make sure to + * pass AVAHI_CLIENT_NO_FAIL to avahi_client_new()'s flags + * parameter. That way avahi_client_new() will succeed even when the + * daemon is not running. In that case the object will enter + * AVAHI_CLIENT_CONNECTING state. As soon as the daemon becomes + * available the object will enter one of the AVAHI_CLIENT_S_xxx + * states. Make sure to not create browsers or entry groups before the + * client object has entered one of those states. As usual you will be + * informed about state changes with the callback function supplied to + * avahi_client_new(). If the client is forced to disconnect from the + * server it will enter AVAHI_CLIENT_FAILURE state with + * avahi_client_errno() == AVAHI_ERR_DISCONNECTED. Free the + * AvahiClient object in that case and reconnect to the server anew - + * again with passing AVAHI_CLIENT_NO_FAIL to avahi_client_new(). + * + * We encourage to implement this in all software where service + * discovery is not an integral part of application. e.g. use it in + * all kinds of background daemons, but not in software like iChat + * compatible IM software. + * + * For now AVAHI_CLIENT_NO_FAIL cannot deal with D-Bus daemon restarts. + * + * \section domains How to Deal Properly with Browsing Domains + * + * Due to the introduction of wide-area DNS-SD the correct handling of + * domains becomes more important for Avahi enabled applications. All + * applications that offer the user a list of services discovered with + * Avahi should offer some kind of editable drop down box where the + * user can either enter his own domain or select one of those offered + * by AvahiDomainBrowser. The default domain to browse should be the + * one returned by avahi_client_get_domain_name(). The list of domains + * returned by AvahiDomainBrowser is assembled by the browsing domains + * configured in the daemon's configuration file, the domains + * announced inside the default domain, the domains set with the + * environment variable $AVAHI_BROWSE_DOMAINS (colon-seperated) on the + * client side and the domains set in the XDG configuration file + * ~/.config/avahi/browse-domains on the client side (seperated by + * newlines). File managers offering some kind of "Network + * Neighborhood" folder should show the entries of the default domain + * right inside that and offer subfolders for the browsing domains + * returned by AvahiDomainBrowser. + */ + +AVAHI_C_DECL_BEGIN + +/** @{ \name States */ + +/** States of a server object */ +typedef enum { + AVAHI_SERVER_INVALID, /**< Invalid state (initial) */ + AVAHI_SERVER_REGISTERING, /**< Host RRs are being registered */ + AVAHI_SERVER_RUNNING, /**< All host RRs have been established */ + AVAHI_SERVER_COLLISION, /**< There is a collision with a host RR. All host RRs have been withdrawn, the user should set a new host name via avahi_server_set_host_name() */ + AVAHI_SERVER_FAILURE /**< Some fatal failure happened, the server is unable to proceed */ +} AvahiServerState; + +/** States of an entry group object */ +typedef enum { + AVAHI_ENTRY_GROUP_UNCOMMITED, /**< The group has not yet been commited, the user must still call avahi_entry_group_commit() */ + AVAHI_ENTRY_GROUP_REGISTERING, /**< The entries of the group are currently being registered */ + AVAHI_ENTRY_GROUP_ESTABLISHED, /**< The entries have successfully been established */ + AVAHI_ENTRY_GROUP_COLLISION, /**< A name collision for one of the entries in the group has been detected, the entries have been withdrawn */ + AVAHI_ENTRY_GROUP_FAILURE /**< Some kind of failure happened, the entries have been withdrawn */ +} AvahiEntryGroupState; + +/** @} */ + +/** @{ \name Flags */ + +/** Some flags for publishing functions */ +typedef enum { + AVAHI_PUBLISH_UNIQUE = 1, /**< For raw records: The RRset is intended to be unique */ + AVAHI_PUBLISH_NO_PROBE = 2, /**< For raw records: Though the RRset is intended to be unique no probes shall be sent */ + AVAHI_PUBLISH_NO_ANNOUNCE = 4, /**< For raw records: Do not announce this RR to other hosts */ + AVAHI_PUBLISH_ALLOW_MULTIPLE = 8, /**< For raw records: Allow multiple local records of this type, even if they are intended to be unique */ +/** \cond fulldocs */ + AVAHI_PUBLISH_NO_REVERSE = 16, /**< For address records: don't create a reverse (PTR) entry */ + AVAHI_PUBLISH_NO_COOKIE = 32, /**< For service records: do not implicitly add the local service cookie to TXT data */ +/** \endcond */ + AVAHI_PUBLISH_UPDATE = 64, /**< Update existing records instead of adding new ones */ +/** \cond fulldocs */ + AVAHI_PUBLISH_USE_WIDE_AREA = 128, /**< Register the record using wide area DNS (i.e. unicast DNS update) */ + AVAHI_PUBLISH_USE_MULTICAST = 256 /**< Register the record using multicast DNS */ +/** \endcond */ +} AvahiPublishFlags; + +/** Some flags for lookup functions */ +typedef enum { +/** \cond fulldocs */ + AVAHI_LOOKUP_USE_WIDE_AREA = 1, /**< Force lookup via wide area DNS */ + AVAHI_LOOKUP_USE_MULTICAST = 2, /**< Force lookup via multicast DNS */ +/** \endcond */ + AVAHI_LOOKUP_NO_TXT = 4, /**< When doing service resolving, don't lookup TXT record */ + AVAHI_LOOKUP_NO_ADDRESS = 8 /**< When doing service resolving, don't lookup A/AAAA record */ +} AvahiLookupFlags; + +/** Some flags for lookup callback functions */ +typedef enum { + AVAHI_LOOKUP_RESULT_CACHED = 1, /**< This response originates from the cache */ + AVAHI_LOOKUP_RESULT_WIDE_AREA = 2, /**< This response originates from wide area DNS */ + AVAHI_LOOKUP_RESULT_MULTICAST = 4, /**< This response originates from multicast DNS */ + AVAHI_LOOKUP_RESULT_LOCAL = 8, /**< This record/service resides on and was announced by the local host. Only available in service and record browsers and only on AVAHI_BROWSER_NEW. */ + AVAHI_LOOKUP_RESULT_OUR_OWN = 16, /**< This service belongs to the same local client as the browser object. Only available in avahi-client, and only for service browsers and only on AVAHI_BROWSER_NEW. */ + AVAHI_LOOKUP_RESULT_STATIC = 32 /**< The returned data has been defined statically by some configuration option */ +} AvahiLookupResultFlags; + +/** @} */ + +/** @{ \name Events */ + +/** Type of callback event when browsing */ +typedef enum { + AVAHI_BROWSER_NEW, /**< The object is new on the network */ + AVAHI_BROWSER_REMOVE, /**< The object has been removed from the network */ + AVAHI_BROWSER_CACHE_EXHAUSTED, /**< One-time event, to notify the user that all entries from the caches have been send */ + AVAHI_BROWSER_ALL_FOR_NOW, /**< One-time event, to notify the user that more records will probably not show up in the near future, i.e. all cache entries have been read and all static servers been queried */ + AVAHI_BROWSER_FAILURE /**< Browsing failed due to some reason which can be retrieved using avahi_server_errno()/avahi_client_errno() */ +} AvahiBrowserEvent; + +/** Type of callback event when resolving */ +typedef enum { + AVAHI_RESOLVER_FOUND, /**< RR found, resolving successful */ + AVAHI_RESOLVER_FAILURE /**< Resolving failed due to some reason which can be retrieved using avahi_server_errno()/avahi_client_errno() */ +} AvahiResolverEvent; + +/** @} */ + +/** @{ \name Other definitions */ + +/** The type of domain to browse for */ +typedef enum { + AVAHI_DOMAIN_BROWSER_BROWSE, /**< Browse for a list of available browsing domains */ + AVAHI_DOMAIN_BROWSER_BROWSE_DEFAULT, /**< Browse for the default browsing domain */ + AVAHI_DOMAIN_BROWSER_REGISTER, /**< Browse for a list of available registering domains */ + AVAHI_DOMAIN_BROWSER_REGISTER_DEFAULT, /**< Browse for the default registering domain */ + AVAHI_DOMAIN_BROWSER_BROWSE_LEGACY, /**< Legacy browse domain - see DNS-SD spec for more information */ + AVAHI_DOMAIN_BROWSER_MAX +} AvahiDomainBrowserType; + +/** @} */ + +/** \cond fulldocs */ +/** For every service a special TXT item is implicitly added, which + * contains a random cookie which is private to the local daemon. This + * can be used by clients to determine if two services on two + * different subnets are effectively the same. */ +#define AVAHI_SERVICE_COOKIE "org.freedesktop.Avahi.cookie" + +/** In invalid cookie as special value */ +#define AVAHI_SERVICE_COOKIE_INVALID (0) +/** \endcond fulldocs */ + +/** @{ \name DNS RR definitions */ + +/** DNS record types, see RFC 1035 */ +enum { + AVAHI_DNS_TYPE_A = 0x01, + AVAHI_DNS_TYPE_NS = 0x02, + AVAHI_DNS_TYPE_CNAME = 0x05, + AVAHI_DNS_TYPE_SOA = 0x06, + AVAHI_DNS_TYPE_PTR = 0x0C, + AVAHI_DNS_TYPE_HINFO = 0x0D, + AVAHI_DNS_TYPE_MX = 0x0F, + AVAHI_DNS_TYPE_TXT = 0x10, + AVAHI_DNS_TYPE_AAAA = 0x1C, + AVAHI_DNS_TYPE_SRV = 0x21 +}; + +/** DNS record classes, see RFC 1035 */ +enum { + AVAHI_DNS_CLASS_IN = 0x01 /**< Probably the only class we will ever use */ +}; + +/** @} */ + +/** The default TTL for RRs which contain a host name of some kind. */ +#define AVAHI_DEFAULT_TTL_HOST_NAME (120) + +/** The default TTL for all other records. */ +#define AVAHI_DEFAULT_TTL (75*60) + +AVAHI_C_DECL_END + +#endif diff --git a/linux/avahi-common/domain.h b/linux/avahi-common/domain.h new file mode 100644 index 0000000..accbd60 --- /dev/null +++ b/linux/avahi-common/domain.h @@ -0,0 +1,131 @@ +#ifndef foodomainhfoo +#define foodomainhfoo + +/* $Id: domain.h 1477 2007-05-09 19:45:54Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file domain.h Domain name handling functions */ + +#include <inttypes.h> +#include <sys/types.h> + +#include <avahi-common/cdecl.h> + +AVAHI_C_DECL_BEGIN + +/** The maximum length of a a fully escaped domain name C string. This + * is calculated like this: RFC1034 mandates maximum length of FQDNs + * is 255. The maximum label length is 63. To minimize the number of + * (non-escaped) dots, we comprise our maximum-length domain name of + * four labels á 63 characters plus three inner dots. Escaping the + * four labels quadruples their length at maximum. An escaped domain + * name has the therefore the maximum length of 63*4*4+3=1011. A + * trailing NUL and perhaps two unnecessary dots leading and trailing + * the string brings us to 1014. */ +#define AVAHI_DOMAIN_NAME_MAX 1014 + +/** Maximum size of an unescaped label */ +#define AVAHI_LABEL_MAX 64 + +/** @{ \name Normalization */ + +/** Normalize a domain name into canonical form. This drops trailing + * dots and removes useless backslash escapes. */ +char *avahi_normalize_name(const char *s, char *ret_s, size_t size); + +/** Normalize a domain name into canonical form. This drops trailing + * dots and removes useless backslash escapes. avahi_free() the + * result! */ +char *avahi_normalize_name_strdup(const char *s); + +/** @} */ + +/** @{ \name Comparison */ + +/** Return 1 when the specified domain names are equal, 0 otherwise */ +int avahi_domain_equal(const char *a, const char *b); + +/** Return some kind of hash value for the domain, useful for using domains as hash table keys. */ +unsigned avahi_domain_hash(const char *name); + +/** @} */ + +/** @{ \name Escaping */ + +/** Read the first label from the textual domain name *name, unescape + * it and write it to dest, *name is changed to point to the next label*/ +char *avahi_unescape_label(const char **name, char *dest, size_t size); + +/** Escape the domain name in *src and write it to *ret_name */ +char *avahi_escape_label(const char* src, size_t src_length, char **ret_name, size_t *ret_size); + +/** @} */ + +/** @{ \name Validity Checks */ + +/** Return 1 when the specified string contains a valid generic DNS-SD + * service type (i.e. a series of words starting with "_"), 0 + * otherwise */ +int avahi_is_valid_service_type_generic(const char *t); + +/** Return 1 when the specified string contains a valid strict DNS-SD + * service type (i.e. consisting of only two words, the latter being + * either _udp or _tcp), 0 otherwise */ +int avahi_is_valid_service_type_strict(const char *t); + +/** Return 1 when the specified string contains a valid DNS-SD service + * subtype, 0 otherwise */ +int avahi_is_valid_service_subtype(const char *t); + +/** Return 1 when the specified string contains a valid domain name, 0 otherwise */ +int avahi_is_valid_domain_name(const char *t); + +/** Return 1 when the specified string contains a valid DNS-SD service name, 0 otherwise */ +int avahi_is_valid_service_name(const char *t); + +/** Return 1 when the specified string contains a valid non-FQDN host name (i.e. without dots), 0 otherwise */ +int avahi_is_valid_host_name(const char *t); + +/** Return 1 when the specified string contains a valid FQDN host name (i.e. with more than one label and non-numerical), 0 otherwise. \since 0.6.9 */ +int avahi_is_valid_fqdn(const char *t); + +/** @} */ + +/** @{ \name DNS-SD service name handling */ + +/** Construct a valid complete DNS-SD service name from a name, a type and a domain */ +int avahi_service_name_join(char *p, size_t size, const char *name, const char *type, const char *domain); + +/** Split a full service name into name, type and domain */ +int avahi_service_name_split(const char *p, char *name, size_t name_size, char *type, size_t type_size, char *domain, size_t domain_size); + +/** @} */ + +/** @{ \name DNS-SD Subtype handling */ + +/** Return a pointer to the type section of a subtype i.e. _foo._sub._bar._tcp => _bar._tcp */ +const char *avahi_get_type_from_subtype(const char *t); + +/** @} */ + +AVAHI_C_DECL_END + +#endif diff --git a/linux/avahi-common/error.h b/linux/avahi-common/error.h new file mode 100644 index 0000000..7d63282 --- /dev/null +++ b/linux/avahi-common/error.h @@ -0,0 +1,109 @@ +#ifndef fooerrorhfoo +#define fooerrorhfoo + +/* $Id: error.h 1298 2006-08-31 17:26:29Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file error.h Error codes and auxiliary functions */ + +#include <avahi-common/cdecl.h> + +AVAHI_C_DECL_BEGIN + +/** Error codes used by avahi */ +enum { + AVAHI_OK = 0, /**< OK */ + AVAHI_ERR_FAILURE = -1, /**< Generic error code */ + AVAHI_ERR_BAD_STATE = -2, /**< Object was in a bad state */ + AVAHI_ERR_INVALID_HOST_NAME = -3, /**< Invalid host name */ + AVAHI_ERR_INVALID_DOMAIN_NAME = -4, /**< Invalid domain name */ + AVAHI_ERR_NO_NETWORK = -5, /**< No suitable network protocol available */ + AVAHI_ERR_INVALID_TTL = -6, /**< Invalid DNS TTL */ + AVAHI_ERR_IS_PATTERN = -7, /**< RR key is pattern */ + AVAHI_ERR_COLLISION = -8, /**< Name collision */ + AVAHI_ERR_INVALID_RECORD = -9, /**< Invalid RR */ + + AVAHI_ERR_INVALID_SERVICE_NAME = -10, /**< Invalid service name */ + AVAHI_ERR_INVALID_SERVICE_TYPE = -11, /**< Invalid service type */ + AVAHI_ERR_INVALID_PORT = -12, /**< Invalid port number */ + AVAHI_ERR_INVALID_KEY = -13, /**< Invalid key */ + AVAHI_ERR_INVALID_ADDRESS = -14, /**< Invalid address */ + AVAHI_ERR_TIMEOUT = -15, /**< Timeout reached */ + AVAHI_ERR_TOO_MANY_CLIENTS = -16, /**< Too many clients */ + AVAHI_ERR_TOO_MANY_OBJECTS = -17, /**< Too many objects */ + AVAHI_ERR_TOO_MANY_ENTRIES = -18, /**< Too many entries */ + AVAHI_ERR_OS = -19, /**< OS error */ + + AVAHI_ERR_ACCESS_DENIED = -20, /**< Access denied */ + AVAHI_ERR_INVALID_OPERATION = -21, /**< Invalid operation */ + AVAHI_ERR_DBUS_ERROR = -22, /**< An unexpected D-Bus error occured */ + AVAHI_ERR_DISCONNECTED = -23, /**< Daemon connection failed */ + AVAHI_ERR_NO_MEMORY = -24, /**< Memory exhausted */ + AVAHI_ERR_INVALID_OBJECT = -25, /**< The object passed to this function was invalid */ + AVAHI_ERR_NO_DAEMON = -26, /**< Daemon not running */ + AVAHI_ERR_INVALID_INTERFACE = -27, /**< Invalid interface */ + AVAHI_ERR_INVALID_PROTOCOL = -28, /**< Invalid protocol */ + AVAHI_ERR_INVALID_FLAGS = -29, /**< Invalid flags */ + + AVAHI_ERR_NOT_FOUND = -30, /**< Not found */ + AVAHI_ERR_INVALID_CONFIG = -31, /**< Configuration error */ + AVAHI_ERR_VERSION_MISMATCH = -32, /**< Verson mismatch */ + AVAHI_ERR_INVALID_SERVICE_SUBTYPE = -33, /**< Invalid service subtype */ + AVAHI_ERR_INVALID_PACKET = -34, /**< Invalid packet */ + AVAHI_ERR_INVALID_DNS_ERROR = -35, /**< Invlaid DNS return code */ + AVAHI_ERR_DNS_FORMERR = -36, /**< DNS Error: Form error */ + AVAHI_ERR_DNS_SERVFAIL = -37, /**< DNS Error: Server Failure */ + AVAHI_ERR_DNS_NXDOMAIN = -38, /**< DNS Error: No such domain */ + AVAHI_ERR_DNS_NOTIMP = -39, /**< DNS Error: Not implemented */ + + AVAHI_ERR_DNS_REFUSED = -40, /**< DNS Error: Operation refused */ + AVAHI_ERR_DNS_YXDOMAIN = -41, + AVAHI_ERR_DNS_YXRRSET = -42, + AVAHI_ERR_DNS_NXRRSET = -43, + AVAHI_ERR_DNS_NOTAUTH = -44, /**< DNS Error: Not authorized */ + AVAHI_ERR_DNS_NOTZONE = -45, + AVAHI_ERR_INVALID_RDATA = -46, /**< Invalid RDATA */ + AVAHI_ERR_INVALID_DNS_CLASS = -47, /**< Invalid DNS class */ + AVAHI_ERR_INVALID_DNS_TYPE = -48, /**< Invalid DNS type */ + AVAHI_ERR_NOT_SUPPORTED = -49, /**< Not supported */ + + AVAHI_ERR_NOT_PERMITTED = -50, /**< Operation not permitted */ + AVAHI_ERR_INVALID_ARGUMENT = -51, /**< Invalid argument */ + AVAHI_ERR_IS_EMPTY = -52, /**< Is empty */ + AVAHI_ERR_NO_CHANGE = -53, /**< The requested operation is invalid because redundant */ + + /**** + **** IF YOU ADD A NEW ERROR CODE HERE, PLEASE DON'T FORGET TO ADD + **** IT TO THE STRING ARRAY IN avahi_strerror() IN error.c AND + **** TO THE ARRAY IN dbus.c AND FINALLY TO dbus.h! + **** + **** Also remember to update the MAX value below. + ****/ + + AVAHI_ERR_MAX = -54 +}; + +/** Return a human readable error string for the specified error code */ +const char *avahi_strerror(int error); + +AVAHI_C_DECL_END + +#endif diff --git a/linux/avahi-common/gccmacro.h b/linux/avahi-common/gccmacro.h new file mode 100644 index 0000000..c8d69ac --- /dev/null +++ b/linux/avahi-common/gccmacro.h @@ -0,0 +1,67 @@ +#ifndef foogccmacrohfoo +#define foogccmacrohfoo + +/* $Id: gccmacro.h 931 2005-11-06 15:00:43Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file gccmacro.h Defines some macros for GCC extensions */ + +#include <avahi-common/cdecl.h> + +AVAHI_C_DECL_BEGIN + +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define AVAHI_GCC_SENTINEL __attribute__ ((sentinel)) +#else +/** Macro for usage of GCC's sentinel compilation warnings */ +#define AVAHI_GCC_SENTINEL +#endif + +#ifdef __GNUC__ +#define AVAHI_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b))) +#else +/** Macro for usage of GCC's printf compilation warnings */ +#define AVAHI_GCC_PRINTF_ATTR(a,b) +#endif + +/** Same as AVAHI_GCC_PRINTF_ATTR but hard coded to arguments 1 and 2 */ +#define AVAHI_GCC_PRINTF_ATTR12 AVAHI_GCC_PRINTF_ATTR(1,2) + +/** Same as AVAHI_GCC_PRINTF_ATTR but hard coded to arguments 2 and 3 */ +#define AVAHI_GCC_PRINTF_ATTR23 AVAHI_GCC_PRINTF_ATTR(2,3) + +#ifdef __GNUC__ +#define AVAHI_GCC_NORETURN __attribute__((noreturn)) +#else +/** Macro for no-return functions */ +#define AVAHI_GCC_NORETURN +#endif + +#ifdef __GNUC__ +#define AVAHI_GCC_UNUSED __attribute__ ((unused)) +#else +/** Macro for not used parameter */ +#define AVAHI_GCC_UNUSED +#endif + +AVAHI_C_DECL_END + +#endif diff --git a/linux/avahi-common/llist.h b/linux/avahi-common/llist.h new file mode 100644 index 0000000..ec69caa --- /dev/null +++ b/linux/avahi-common/llist.h @@ -0,0 +1,77 @@ +#ifndef foollistfoo +#define foollistfoo + +/* $Id: llist.h 872 2005-10-26 01:21:30Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file llist.h A simple macro based linked list implementation */ + +#include <assert.h> + +#include <avahi-common/cdecl.h> + +AVAHI_C_DECL_BEGIN + +/** The head of the linked list. Use this in the structure that shall + * contain the head of the linked list */ +#define AVAHI_LLIST_HEAD(t,name) t *name + +/** The pointers in the linked list's items. Use this in the item structure */ +#define AVAHI_LLIST_FIELDS(t,name) t *name##_next, *name##_prev + +/** Initialize the list's head */ +#define AVAHI_LLIST_HEAD_INIT(t,head) do { (head) = NULL; } while(0) + +/** Initialize a list item */ +#define AVAHI_LLIST_INIT(t,name,item) do { \ + t *_item = (item); \ + assert(_item); \ + _item->name##_prev = _item->name##_next = NULL; \ + } while(0) + +/** Prepend an item to the list */ +#define AVAHI_LLIST_PREPEND(t,name,head,item) do { \ + t **_head = &(head), *_item = (item); \ + assert(_item); \ + if ((_item->name##_next = *_head)) \ + _item->name##_next->name##_prev = _item; \ + _item->name##_prev = NULL; \ + *_head = _item; \ + } while (0) + +/** Remove an item from the list */ +#define AVAHI_LLIST_REMOVE(t,name,head,item) do { \ + t **_head = &(head), *_item = (item); \ + assert(_item); \ + if (_item->name##_next) \ + _item->name##_next->name##_prev = _item->name##_prev; \ + if (_item->name##_prev) \ + _item->name##_prev->name##_next = _item->name##_next; \ + else {\ + assert(*_head == _item); \ + *_head = _item->name##_next; \ + } \ + _item->name##_next = _item->name##_prev = NULL; \ + } while(0) + +AVAHI_C_DECL_END + +#endif diff --git a/linux/avahi-common/malloc.h b/linux/avahi-common/malloc.h new file mode 100644 index 0000000..d375cef --- /dev/null +++ b/linux/avahi-common/malloc.h @@ -0,0 +1,98 @@ +#ifndef foomallochfoo +#define foomallochfoo + +/* $Id: malloc.h 1477 2007-05-09 19:45:54Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file malloc.h Memory allocation */ + +#include <sys/types.h> +#include <stdarg.h> +#include <limits.h> +#include <assert.h> + +#include <avahi-common/cdecl.h> +#include <avahi-common/gccmacro.h> + +AVAHI_C_DECL_BEGIN + +/** Allocate some memory, just like the libc malloc() */ +void *avahi_malloc(size_t size); + +/** Similar to avahi_malloc() but set the memory to zero */ +void *avahi_malloc0(size_t size); + +/** Free some memory */ +void avahi_free(void *p); + +/** Similar to libc's realloc() */ +void *avahi_realloc(void *p, size_t size); + +/** Internal helper for avahi_new() */ +static inline void* avahi_new_internal(unsigned n, size_t k) { + assert(n < INT_MAX/k); + return avahi_malloc(n*k); +} + +/** Allocate n new structures of the specified type. */ +#define avahi_new(type, n) ((type*) avahi_new_internal((n), sizeof(type))) + +/** Internal helper for avahi_new0() */ +static inline void* avahi_new0_internal(unsigned n, size_t k) { + assert(n < INT_MAX/k); + return avahi_malloc0(n*k); +} + +/** Same as avahi_new() but set the memory to zero */ +#define avahi_new0(type, n) ((type*) avahi_new0_internal((n), sizeof(type))) + +/** Just like libc's strdup() */ +char *avahi_strdup(const char *s); + +/** Just like libc's strndup() */ +char *avahi_strndup(const char *s, size_t l); + +/** Duplicate the given memory block into a new one allocated with avahi_malloc() */ +void *avahi_memdup(const void *s, size_t l); + +/** Wraps allocator functions */ +typedef struct AvahiAllocator { + void* (*malloc)(size_t size); + void (*free)(void *p); + void* (*realloc)(void *p, size_t size); + void* (*calloc)(size_t nmemb, size_t size); /**< May be NULL */ +} AvahiAllocator; + +/** Change the allocator. May be NULL to return to default (libc) + * allocators. The structure is not copied! */ +void avahi_set_allocator(const AvahiAllocator *a); + +/** Like sprintf() but store the result in a freshly allocated buffer. Free this with avahi_free() */ +char *avahi_strdup_printf(const char *fmt, ... ) AVAHI_GCC_PRINTF_ATTR12; + +/** \cond fulldocs */ +/** Same as avahi_strdup_printf() but take a va_list instead of varargs */ +char *avahi_strdup_vprintf(const char *fmt, va_list ap); +/** \endcond */ + +AVAHI_C_DECL_END + +#endif diff --git a/linux/avahi-common/rlist.h b/linux/avahi-common/rlist.h new file mode 100644 index 0000000..a418ca9 --- /dev/null +++ b/linux/avahi-common/rlist.h @@ -0,0 +1,51 @@ +#ifndef foorlistfoo +#define foorlistfoo + +/* $Id: rlist.h 872 2005-10-26 01:21:30Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file rlist.h A simple linked list implementation */ + +#include "llist.h" + +AVAHI_C_DECL_BEGIN + +/** A doubly linked list type */ +typedef struct AvahiRList AvahiRList; + +/** A doubly linked list type */ +struct AvahiRList { + AVAHI_LLIST_FIELDS(AvahiRList, rlist); + void *data; +}; + +/** Prepend a new item to the beginning of the list and return the new beginning */ +AvahiRList* avahi_rlist_prepend(AvahiRList *r, void *data); + +/** Remove the first occurence of the specified item from the list and return the new beginning */ +AvahiRList* avahi_rlist_remove(AvahiRList *r, void *data); + +/** Remove the specified item from the list and return the new beginning */ +AvahiRList* avahi_rlist_remove_by_link(AvahiRList *r, AvahiRList *n); + +AVAHI_C_DECL_END + +#endif diff --git a/linux/avahi-common/simple-watch.h b/linux/avahi-common/simple-watch.h new file mode 100644 index 0000000..50410f6 --- /dev/null +++ b/linux/avahi-common/simple-watch.h @@ -0,0 +1,87 @@ +#ifndef foosimplewatchhfoo +#define foosimplewatchhfoo + +/* $Id: simple-watch.h 1151 2006-02-20 16:21:29Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file simple-watch.h Simple poll() based main loop implementation */ + +#include <sys/poll.h> +#include <avahi-common/cdecl.h> +#include <avahi-common/watch.h> + +AVAHI_C_DECL_BEGIN + +/** A main loop object. Main loops of this type aren't very flexible + * since they only support a single wakeup type. Nevertheless it + * should suffice for small test and example applications. */ +typedef struct AvahiSimplePoll AvahiSimplePoll; + +/** Create a new main loop object */ +AvahiSimplePoll *avahi_simple_poll_new(void); + +/** Free a main loop object */ +void avahi_simple_poll_free(AvahiSimplePoll *s); + +/** Return the abstracted poll API object for this main loop + * object. The is will return the same pointer each time it is + * called. */ +const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s); + +/** Run a single main loop iteration of this main loop. If sleep_time +is < 0 this will block until any of the registered events happens, +then it will execute the attached callback function. If sleep_time is +0 the routine just checks if any event is pending. If yes the attached +callback function is called, otherwise the function returns +immediately. If sleep_time > 0 the function will block for at most the +specified time in msecs. Returns -1 on error, 0 on success and 1 if a +quit request has been scheduled. Usually this function should be called +in a loop until it returns a non-zero value*/ +int avahi_simple_poll_iterate(AvahiSimplePoll *s, int sleep_time); + +/** Request that the main loop quits. If this is called the next + call to avahi_simple_poll_iterate() will return 1 */ +void avahi_simple_poll_quit(AvahiSimplePoll *s); + +/** Prototype for a poll() type function */ +typedef int (*AvahiPollFunc)(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata); + +/** Replace the internally used poll() function. By default the system's poll() will be used */ +void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func, void *userdata); + +/** The first stage of avahi_simple_poll_iterate(), use this function only if you know what you do */ +int avahi_simple_poll_prepare(AvahiSimplePoll *s, int timeout); + +/** The second stage of avahi_simple_poll_iterate(), use this function only if you know what you do */ +int avahi_simple_poll_run(AvahiSimplePoll *s); + +/** The third and final stage of avahi_simple_poll_iterate(), use this function only if you know what you do */ +int avahi_simple_poll_dispatch(AvahiSimplePoll *s); + +/** Call avahi_simple_poll_iterate() in a loop and return if it returns non-zero */ +int avahi_simple_poll_loop(AvahiSimplePoll *s); + +/** Wakeup the main loop. (for threaded environments) */ +void avahi_simple_poll_wakeup(AvahiSimplePoll *s); + +AVAHI_C_DECL_END + +#endif diff --git a/linux/avahi-common/strlst.h b/linux/avahi-common/strlst.h new file mode 100644 index 0000000..cddb75c --- /dev/null +++ b/linux/avahi-common/strlst.h @@ -0,0 +1,182 @@ +#ifndef footxtlisthfoo +#define footxtlisthfoo + +/* $Id: strlst.h 1477 2007-05-09 19:45:54Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file strlst.h Implementation of a data type to store lists of strings */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdarg.h> + +#include <avahi-common/cdecl.h> +#include <avahi-common/gccmacro.h> + +AVAHI_C_DECL_BEGIN + +/** Linked list of strings that can contain any number of binary + * characters, including NUL bytes. An empty list is created by + * assigning a NULL to a pointer to AvahiStringList. The string list + * is stored in reverse order, so that appending to the string list is + * effectively a prepending to the linked list. This object is used + * primarily for storing DNS TXT record data. */ +typedef struct AvahiStringList { + struct AvahiStringList *next; /**< Pointer to the next linked list element */ + size_t size; /**< Size of text[] */ + uint8_t text[1]; /**< Character data */ +} AvahiStringList; + +/** @{ \name Construction and destruction */ + +/** Create a new string list by taking a variable list of NUL + * terminated strings. The strings are copied using g_strdup(). The + * argument list must be terminated by a NULL pointer. */ +AvahiStringList *avahi_string_list_new(const char *txt, ...) AVAHI_GCC_SENTINEL; + +/** \cond fulldocs */ +/** Same as avahi_string_list_new() but pass a va_list structure */ +AvahiStringList *avahi_string_list_new_va(va_list va); +/** \endcond */ + +/** Create a new string list from a string array. The strings are + * copied using g_strdup(). length should contain the length of the + * array, or -1 if the array is NULL terminated*/ +AvahiStringList *avahi_string_list_new_from_array(const char **array, int length); + +/** Free a string list */ +void avahi_string_list_free(AvahiStringList *l); + +/** @} */ + +/** @{ \name Adding strings */ + +/** Append a NUL terminated string to the specified string list. The + * passed string is copied using g_strdup(). Returns the new list + * start. */ +AvahiStringList *avahi_string_list_add(AvahiStringList *l, const char *text); + +/** Append a new NUL terminated formatted string to the specified string list */ +AvahiStringList *avahi_string_list_add_printf(AvahiStringList *l, const char *format, ...) AVAHI_GCC_PRINTF_ATTR23; + +/** \cond fulldocs */ +/** Append a new NUL terminated formatted string to the specified string list */ +AvahiStringList *avahi_string_list_add_vprintf(AvahiStringList *l, const char *format, va_list va); +/** \endcond */ + +/** Append an arbitrary length byte string to the list. Returns the + * new list start. */ +AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const uint8_t *text, size_t size); + +/** Append a new entry to the string list. The string is not filled +with data. The caller should fill in string data afterwards by writing +it to l->text, where l is the pointer returned by this function. This +function exists solely to optimize a few operations where otherwise +superfluous string copying would be necessary. */ +AvahiStringList*avahi_string_list_add_anonymous(AvahiStringList *l, size_t size); + +/** Same as avahi_string_list_add(), but takes a variable number of + * NUL terminated strings. The argument list must be terminated by a + * NULL pointer. Returns the new list start. */ +AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...) AVAHI_GCC_SENTINEL; + +/** \cond fulldocs */ +/** Same as avahi_string_list_add_many(), but use a va_list + * structure. Returns the new list start. */ +AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va); +/** \endcond */ + +/** @} */ + +/** @{ \name String list operations */ + +/** Convert the string list object to a single character string, + * seperated by spaces and enclosed in "". avahi_free() the result! This + * function doesn't work well with string that contain NUL bytes. */ +char* avahi_string_list_to_string(AvahiStringList *l); + +/** \cond fulldocs */ +/** Serialize the string list object in a way that is compatible with + * the storing of DNS TXT records. Strings longer than 255 bytes are truncated. */ +size_t avahi_string_list_serialize(AvahiStringList *l, void * data, size_t size); + +/** Inverse of avahi_string_list_serialize() */ +int avahi_string_list_parse(const void *data, size_t size, AvahiStringList **ret); +/** \endcond */ + +/** Compare to string lists */ +int avahi_string_list_equal(const AvahiStringList *a, const AvahiStringList *b); + +/** Copy a string list */ +AvahiStringList *avahi_string_list_copy(const AvahiStringList *l); + +/** Reverse the string list. */ +AvahiStringList* avahi_string_list_reverse(AvahiStringList *l); + +/** Return the number of elements in the string list */ +unsigned avahi_string_list_length(const AvahiStringList *l); + +/** @} */ + +/** @{ \name Accessing items */ + +/** Returns the next item in the string list */ +AvahiStringList *avahi_string_list_get_next(AvahiStringList *l); + +/** Returns the text for the current item */ +uint8_t *avahi_string_list_get_text(AvahiStringList *l); + +/** Returns the size of the current text */ +size_t avahi_string_list_get_size(AvahiStringList *l); + +/** @} */ + +/** @{ \name DNS-SD TXT pair handling */ + +/** Find the string list entry for the given DNS-SD TXT key */ +AvahiStringList *avahi_string_list_find(AvahiStringList *l, const char *key); + +/** Return the DNS-SD TXT key and value for the specified string list + * item. If size is not NULL it will be filled with the length of + * value. (for strings containing NUL bytes). If the entry doesn't + * contain a value *value will be set to NULL. You need to + * avahi_free() the strings returned in *key and *value. */ +int avahi_string_list_get_pair(AvahiStringList *l, char **key, char **value, size_t *size); + +/** Add a new DNS-SD TXT key value pair to the string list. value may + * be NULL in case you want to specify a key without a value */ +AvahiStringList *avahi_string_list_add_pair(AvahiStringList *l, const char *key, const char *value); + +/** Same as avahi_string_list_add_pair() but allow strings containing NUL bytes in *value. */ +AvahiStringList *avahi_string_list_add_pair_arbitrary(AvahiStringList *l, const char *key, const uint8_t *value, size_t size); + +/** @} */ + +/** \cond fulldocs */ +/** Try to find a magic service cookie in the specified DNS-SD string + * list. Or return AVAHI_SERVICE_COOKIE_INVALID if none is found. */ +uint32_t avahi_string_list_get_service_cookie(AvahiStringList *l); +/** \endcond */ + +AVAHI_C_DECL_END + +#endif + diff --git a/linux/avahi-common/thread-watch.h b/linux/avahi-common/thread-watch.h new file mode 100644 index 0000000..d5b3c86 --- /dev/null +++ b/linux/avahi-common/thread-watch.h @@ -0,0 +1,82 @@ +#ifndef foothreadedwatchhfoo +#define foothreadedwatchhfoo + +/* $Id: thread-watch.h 1244 2006-08-06 11:53:01Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file thread-watch.h Threaded poll() based main loop implementation */ + +#include <sys/poll.h> +#include <avahi-common/cdecl.h> +#include <avahi-common/watch.h> + +AVAHI_C_DECL_BEGIN + +/** A main loop object that runs an AvahiSimplePoll in its own thread. \since 0.6.4 */ +typedef struct AvahiThreadedPoll AvahiThreadedPoll; + +/** Create a new event loop object. This will allocate the internal + * AvahiSimplePoll, but will not start the helper thread. \since 0.6.4 */ +AvahiThreadedPoll *avahi_threaded_poll_new(void); + +/** Free an event loop object. Ths will stop the associated evet loop + * thread (if it is running). \since 0.6.4 */ +void avahi_threaded_poll_free(AvahiThreadedPoll *p); + +/** Return the abstracted poll API object for this event loop + * object. The will return the same pointer each time it is + * called. \since 0.6.4 */ +const AvahiPoll* avahi_threaded_poll_get(AvahiThreadedPoll *p); + +/** Start the event loop helper thread. After the thread has started + * you must make sure to access the event loop object + * (AvahiThreadedPoll, AvahiPoll and all its associated objects) + * synchronized, i.e. with proper locking. You may want to use + * avahi_threaded_poll_lock()/avahi_threaded_poll_unlock() for this, + * which will lock the the entire event loop. Please note that event + * loop callback functions are called from the event loop helper thread + * with that lock held, i.e. avahi_threaded_poll_lock() calls are not + * required from event callbacks. \since 0.6.4 */ +int avahi_threaded_poll_start(AvahiThreadedPoll *p); + +/** Request that the event loop quits and the associated thread + stops. Call this from outside the helper thread if you want to shut + it down. \since 0.6.4 */ +int avahi_threaded_poll_stop(AvahiThreadedPoll *p); + +/** Request that the event loop quits and the associated thread + stops. Call this from inside the helper thread if you want to shut it + down. \since 0.6.4 */ +void avahi_threaded_poll_quit(AvahiThreadedPoll *p); + +/** Lock the main loop object. Use this if you want to access the event + * loop objects (such as creating a new event source) from anything + * else but the event loop helper thread, i.e. from anything else but event + * loop callbacks \since 0.6.4 */ +void avahi_threaded_poll_lock(AvahiThreadedPoll *p); + +/** Unlock the event loop object, use this as counterpart to + * avahi_threaded_poll_lock() \since 0.6.4 */ +void avahi_threaded_poll_unlock(AvahiThreadedPoll *p); + +AVAHI_C_DECL_END + +#endif diff --git a/linux/avahi-common/timeval.h b/linux/avahi-common/timeval.h new file mode 100644 index 0000000..718b49e --- /dev/null +++ b/linux/avahi-common/timeval.h @@ -0,0 +1,56 @@ +#ifndef footimevalhfoo +#define footimevalhfoo + +/* $Id: timeval.h 1244 2006-08-06 11:53:01Z lennart $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file timeval.h Functions to facilitate timeval handling */ + +#include <inttypes.h> +#include <sys/time.h> + +#include <avahi-common/cdecl.h> + +AVAHI_C_DECL_BEGIN + +/** A numeric data type for storing microsecond values. (signed 64bit integer) */ +typedef int64_t AvahiUsec; + +/** Compare two timeval structures and return a negative value when a < b, 0 when a == b and a positive value otherwise */ +int avahi_timeval_compare(const struct timeval *a, const struct timeval *b); + +/** Calculate the difference between two timeval structures as microsecond value */ +AvahiUsec avahi_timeval_diff(const struct timeval *a, const struct timeval *b); + +/** Add a number of microseconds to the specified timeval structure and return it. *a is modified. */ +struct timeval* avahi_timeval_add(struct timeval *a, AvahiUsec usec); + +/** Return the difference between the current time and *a. Positive if *a was earlier */ +AvahiUsec avahi_age(const struct timeval *a); + +/** Fill *tv with the current time plus "ms" milliseconds plus an + * extra jitter of "j" milliseconds. Pass 0 for j if you don't want + * the jitter */ +struct timeval *avahi_elapse_time(struct timeval *tv, unsigned ms, unsigned j); + +AVAHI_C_DECL_END + +#endif diff --git a/linux/avahi-common/watch.h b/linux/avahi-common/watch.h new file mode 100644 index 0000000..c9b71a8 --- /dev/null +++ b/linux/avahi-common/watch.h @@ -0,0 +1,99 @@ +#ifndef foowatchhfoo +#define foowatchhfoo + +/* $Id: watch.h 1335 2006-11-19 08:04:07Z lathiat $ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file watch.h Simplistic main loop abstraction */ + +#include <sys/poll.h> +#include <sys/time.h> + +#include <avahi-common/cdecl.h> + +AVAHI_C_DECL_BEGIN + +/** An I/O watch object */ +typedef struct AvahiWatch AvahiWatch; + +/** A timeout watch object */ +typedef struct AvahiTimeout AvahiTimeout; + +/** An event polling abstraction object */ +typedef struct AvahiPoll AvahiPoll; + +/** Type of watch events */ +typedef enum { + AVAHI_WATCH_IN = POLLIN, /**< Input event */ + AVAHI_WATCH_OUT = POLLOUT, /**< Output event */ + AVAHI_WATCH_ERR = POLLERR, /**< Error event */ + AVAHI_WATCH_HUP = POLLHUP /**< Hangup event */ +} AvahiWatchEvent; + +/** Called whenever an I/O event happens on an I/O watch */ +typedef void (*AvahiWatchCallback)(AvahiWatch *w, int fd, AvahiWatchEvent event, void *userdata); + +/** Called when the timeout is reached */ +typedef void (*AvahiTimeoutCallback)(AvahiTimeout *t, void *userdata); + +/** Defines an abstracted event polling API. This may be used to + connect Avahi to other main loops. This is losely based on Unix + poll(2). A consumer will call watch_new() for all file descriptors it + wants to listen for events on. In addition he can call timeout_new() + to define time based events .*/ +struct AvahiPoll { + + /** Some abstract user data usable by the provider of the API */ + void* userdata; + + /** Create a new watch for the specified file descriptor and for + * the specified events. The API will call the callback function + * whenever any of the events happens. */ + AvahiWatch* (*watch_new)(const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata); + + /** Update the events to wait for. It is safe to call this function from an AvahiWatchCallback */ + void (*watch_update)(AvahiWatch *w, AvahiWatchEvent event); + + /** Return the events that happened. It is safe to call this function from an AvahiWatchCallback */ + AvahiWatchEvent (*watch_get_events)(AvahiWatch *w); + + /** Free a watch. It is safe to call this function from an AvahiWatchCallback */ + void (*watch_free)(AvahiWatch *w); + + /** Set a wakeup time for the polling loop. The API will call the + callback function when the absolute time *tv is reached. If tv is + NULL, the timeout is disabled. After the timeout expired the + callback function will be called and the timeout is disabled. You + can reenable it by calling timeout_update() */ + AvahiTimeout* (*timeout_new)(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata); + + /** Update the absolute expiration time for a timeout, If tv is + * null, the timeout is disabled. It is safe to call this function from an AvahiTimeoutCallback */ + void (*timeout_update)(AvahiTimeout *, const struct timeval *tv); + + /** Free a timeout. It is safe to call this function from an AvahiTimeoutCallback */ + void (*timeout_free)(AvahiTimeout *t); +}; + +AVAHI_C_DECL_END + +#endif + diff --git a/linux/cusblinux.c b/linux/cusblinux.c new file mode 100644 index 0000000..3a12fca --- /dev/null +++ b/linux/cusblinux.c @@ -0,0 +1,673 @@ +/* + * cusblinux.cpp + * + * Filled in by Daniel Risacher on 6/21/05 + * Filled in more by Patrick McNeil on 6/27/05 + * Copyright 2005 Phidgets Inc. All rights reserved. + * + */ + +#include "stdafx.h" +#include "cusb.h" + +int CUSBCloseHandle(CPhidgetHandle phid) { + int ret = 0; + int result = EPHIDGET_OK; + + if (!phid) + return EPHIDGET_INVALIDARG; + + CPhidget_clearStatusFlag(&phid->status, PHIDGET_ATTACHED_FLAG, &phid->lock); + + if (phid->deviceHandle == NULL) + return EPHIDGET_NOTATTACHED; + + CThread_join(&phid->readThread); + + if((ret = usb_release_interface((usb_dev_handle *) phid->deviceHandle, phid->deviceDef->pdd_iid)) < 0) + { + switch(ret) + { + case -ENODEV: + //usb_release_interface called after the device was unplugged + LOG(PHIDGET_LOG_WARNING, "usb_release_interface called on unplugged device."); + break; + default: + LOG(PHIDGET_LOG_ERROR, "usb_release_interface failed with error code: %d \"%s\"", ret, strerror(-ret)); + } + } + + //if we notice that PHIDGET_USB_ERROR_FLAG is set, then reset this device before closing + //this gives us a better chance of getting it back if something has gone wrong. + if(CPhidget_statusFlagIsSet(phid->status, PHIDGET_USB_ERROR_FLAG)) + { + LOG(PHIDGET_LOG_WARNING,"PHIDGET_USB_ERROR_FLAG is set - resetting device."); + if((ret = usb_reset((usb_dev_handle *) phid->deviceHandle)) < 0) + { + LOG(PHIDGET_LOG_ERROR, "usb_reset failed with error code: %d \"%s\"", ret, strerror(-ret)); + result = EPHIDGET_UNEXPECTED; + } + } + + if((ret = usb_close((usb_dev_handle *) phid->deviceHandle)) < 0) + { + LOG(PHIDGET_LOG_ERROR, "usb_close failed with error code: %d \"%s\"", ret, strerror(-ret)); + result = EPHIDGET_UNEXPECTED; + } + + phid->deviceHandle = NULL; + + return result; +} + +int CUSBSendPacket(CPhidgetHandle phid, unsigned char *buffer) { + int BytesWritten = 0; + + if (!phid) + return EPHIDGET_INVALIDARG; + + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG) + && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHING_FLAG)) + return EPHIDGET_NOTATTACHED; + + if (phid->deviceHandle == NULL) + { + LOG(PHIDGET_LOG_WARNING,"Handle for writing is not valid"); + return EPHIDGET_UNEXPECTED; + } + + if(phid->interruptOutEndpoint) + { + BytesWritten = usb_interrupt_write((usb_dev_handle *)phid->deviceHandle, + phid->deviceDef->pdd_iid+1, + (char *)buffer, + phid->outputReportByteLength, /* size */ + 500); /* FIXME? timeout */ + } + else + { + BytesWritten = usb_control_msg((usb_dev_handle *)phid->deviceHandle, + USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + USB_REQ_SET_CONFIGURATION, + 0x0200, /* value */ + phid->deviceDef->pdd_iid, /* index*/ + (char *)buffer, + phid->outputReportByteLength, /* size */ + 500); /* FIXME? timeout */ + } + + if(BytesWritten < 0) + { + switch(BytesWritten) + { + case -ETIMEDOUT: //important case? + return EPHIDGET_TIMEOUT; + case -ENODEV: + //device is gone - unplugged. + LOG(PHIDGET_LOG_INFO, "Device was unplugged - detach."); + return EPHIDGET_NOTATTACHED; + default: + LOG(PHIDGET_LOG_ERROR, "usb_control_msg failed with error code: %d \"%s\"", BytesWritten, strerror(-BytesWritten)); + return EPHIDGET_UNEXPECTED; + } + } + + if (BytesWritten != phid->outputReportByteLength) + { + LOG(PHIDGET_LOG_WARNING,"Failure in CUSBSendPacket - Report Length" + ": %d, bytes written: %d", + (int)phid->outputReportByteLength, (int)BytesWritten); + return EPHIDGET_UNEXPECTED; + } + + return EPHIDGET_OK; +} + +int CUSBSetLabel(CPhidgetHandle phid, char *buffer) { + int BytesWritten = 0; + int size = buffer[0]; + + if(size>22) return EPHIDGET_INVALID; + + if (!phid) + return EPHIDGET_INVALIDARG; + + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG) + && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHING_FLAG)) + return EPHIDGET_NOTATTACHED; + + if (phid->deviceHandle == NULL) + { + LOG(PHIDGET_LOG_WARNING,"Handle for writing is not valid"); + return EPHIDGET_UNEXPECTED; + } + + BytesWritten = usb_control_msg(phid->deviceHandle, + USB_ENDPOINT_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_SET_DESCRIPTOR, + 0x0304, /* value */ + 0x0409, /* index*/ + (char *)buffer, + size, /* size */ + 500); /* FIXME? timeout */ + + if(BytesWritten < 0) + { + switch(BytesWritten) + { + case -ETIMEDOUT: //important case? + default: + LOG(PHIDGET_LOG_INFO, "usb_control_msg failed with error code: %d \"%s\"", BytesWritten, strerror(-BytesWritten)); + return EPHIDGET_UNSUPPORTED; + } + } + + if (BytesWritten != size) + { + LOG(PHIDGET_LOG_WARNING,"Failure in CUSBSetLabel - Report Length" + ": %d, bytes written: %d", + size, (int)BytesWritten); + return EPHIDGET_UNEXPECTED; + } + + return EPHIDGET_OK; +} + +/* Buffer should be at least 8 bytes long */ +int CUSBReadPacket(CPhidgetHandle phid, unsigned char *buffer) { + int BytesRead = 0; + + if (!phid) + return EPHIDGET_INVALIDARG; + + if (!CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG) + && !CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHING_FLAG)) + return EPHIDGET_NOTATTACHED; + + if (phid->deviceHandle == NULL) + { + LOG(PHIDGET_LOG_WARNING,"Handle for writing is not valid"); + return EPHIDGET_UNEXPECTED; + } + + BytesRead = usb_interrupt_read((usb_dev_handle *)phid->deviceHandle, + phid->deviceDef->pdd_iid+1, + (char *)buffer, + phid->inputReportByteLength, + 500); + + if (BytesRead < 0) + { + switch(BytesRead) + { + // A timeout occured, but we'll just try again + case -ETIMEDOUT: + LOG(PHIDGET_LOG_VERBOSE, "usb_interrupt_read timeout: %d \"%s\"", BytesRead, strerror(-BytesRead)); + return EPHIDGET_TIMEOUT; + case -EBUSY: + //This happens when someone else calls claim_interface on this interface (a manager for ex.) - basically just wait until they release it. + //This will happen if an open occurs in another app which (for some reason) can steal the interface from this one. + LOG(PHIDGET_LOG_INFO, "Device is busy on Read - try again."); + return EPHIDGET_TRYAGAIN; + case -ENOSPC: + //This happens when too many Interrupt devices are plugged in (Phidgets, HID devices, etc. + LOG(PHIDGET_LOG_ERROR, "USB Interrupt bandwidth exceeded. Try distributing devices more evenly. Reads will continue, but data is being lost."); + goto tryagain; + case -ENODEV: + //device is gone - unplugged. + LOG(PHIDGET_LOG_INFO, "Device was unplugged - detach."); + return EPHIDGET_NOTATTACHED; + default: + LOG(PHIDGET_LOG_ERROR, "usb_interrupt_read returned: %d \"%s\"", BytesRead, strerror(-BytesRead)); + goto tryagain; + } + } + + if (BytesRead != phid->inputReportByteLength) + { + //Generally means the device was unplugged, but can mean that there is not enough Interrupt bandwidth + //We keep trying and we'll get data, just not all data + LOG(PHIDGET_LOG_WARNING,"Failure in CUSBReadPacket - Report Length" + ": %d, bytes read: %d. Probably trying to use too many Phidgets at once, and some data is being lost.", + (int)phid->inputReportByteLength, (int)BytesRead); + goto tryagain; + } + + phid->tryAgainCounter = 0; + return EPHIDGET_OK; + + //if we see too many tryagains in a row, then we assume something has actually gone wrong and reset the device +tryagain: + phid->tryAgainCounter++; + if(phid->tryAgainCounter > 30) //this will be hit in < 1 second for all devices + { + LOG(PHIDGET_LOG_ERROR, "CUSBReadPacket returned EPHIDGET_TRYAGAIN too many times in a row - reset device.", BytesRead, strerror(-BytesRead)); + phid->tryAgainCounter = 0; + return EPHIDGET_UNEXPECTED; + } + return EPHIDGET_TRYAGAIN; +} + +static int getLabelString(CPhidgetHandle phid, struct usb_dev_handle *udev) +{ + int len = 0; + char labelBuf[22]; + memset(labelBuf, 0, sizeof(labelBuf)); + + //Note that this returns the whole descriptor, including the length and type bytes + len = usb_get_string(udev, 4, 0, (char *)labelBuf, 22); + + if(len < 0) + { + switch(len) + { + case -ETIMEDOUT: //important case? + default: + LOG(PHIDGET_LOG_INFO, "usb_get_string_simple failed in CUSBGetDeviceCapabilities with error code: %d \"%s\" while reading label - this probably just means the device doesn't support labels, so this is fine.", len, strerror(-len)); + } + phid->label[0]='\0'; + return EPHIDGET_OK; + } + else + return decodeLabelString(labelBuf, phid->label, phid->serialNumber); +} + +int CUSBRefreshLabelString(CPhidgetHandle phid) +{ + return getLabelString(phid, (struct usb_dev_handle *)phid->deviceHandle); +} + +int CUSBGetDeviceCapabilities(CPhidgetHandle phid, struct usb_device *dev, struct usb_dev_handle *udev) { + unsigned char buf[255]; + int len = 0, i = 0; + struct usb_interface_descriptor *interfaceDesc; + struct usb_config_descriptor configDesc; + + memset(buf, 0, sizeof(buf)); + +// if(usb_device->config->interface->bNumEndpoints == 2) +// { +// LOG(PHIDGET_LOG_INFO, "Using Interrupt OUT endpoing to Host->Device communication."); +// phid->interruptOutEndpoint = PTRUE; +// } +// else +// { +// LOG(PHIDGET_LOG_INFO, "Using Control endpoing to Host->Device communication."); +// phid->interruptOutEndpoint = PFALSE; +// } + + //Get config descriptor + len = usb_get_descriptor(udev, USB_DT_CONFIG, 0, (void *)&configDesc, USB_DT_CONFIG_SIZE); + + if(len == USB_DT_CONFIG_SIZE) + { + //Get the rest + len = usb_get_descriptor(udev, USB_DT_CONFIG, 0, (void *)buf, configDesc.wTotalLength); + + if(len == configDesc.wTotalLength) + { + int i; + interfaceDesc = NULL; + struct usb_descriptor_header *currentDesc; + + //Find the interface Descriptor + for(i=0;i<configDesc.wTotalLength-2;i+=currentDesc->bLength) + { + currentDesc = (struct usb_descriptor_header *)&buf[i]; + if (currentDesc->bDescriptorType == USB_DT_INTERFACE) + { + interfaceDesc = (struct usb_interface_descriptor *)&buf[i]; + if(interfaceDesc->bInterfaceNumber == phid->deviceDef->pdd_iid) + break; + else + interfaceDesc = NULL; + } + } + + if(interfaceDesc == NULL) + { + LOG(PHIDGET_LOG_ERROR, "Couldn't find interface descriptor!"); + return EPHIDGET_UNEXPECTED; + } + + if(interfaceDesc->bNumEndpoints == 2) + { + LOG(PHIDGET_LOG_INFO, "Using Interrupt OUT Endpoint for Host->Device communication."); + phid->interruptOutEndpoint = PTRUE; + } + else + { + LOG(PHIDGET_LOG_INFO, "Using Control Endpoint for Host->Device communication."); + phid->interruptOutEndpoint = PFALSE; + } + } + else if (len < 0) + { + LOG(PHIDGET_LOG_ERROR, "usb_get_descriptor failed in CUSBGetDeviceCapabilities with error code: %d \"%s\"", len, strerror(-len)); + return EPHIDGET_UNEXPECTED; + } + else + { + LOG(PHIDGET_LOG_ERROR, "Couldn't get interface descriptor in CUSBGetDeviceCapabilities"); + return EPHIDGET_UNEXPECTED; + } + } + else if (len < 0) + { + LOG(PHIDGET_LOG_ERROR, "usb_get_descriptor failed in CUSBGetDeviceCapabilities with error code: %d \"%s\"", len, strerror(-len)); + return EPHIDGET_UNEXPECTED; + } + else + { + LOG(PHIDGET_LOG_ERROR, "Couldn't get interface descriptor in CUSBGetDeviceCapabilities"); + return EPHIDGET_UNEXPECTED; + } + + //Get a HID descriptor + len = usb_control_msg(udev, USB_ENDPOINT_IN+1, + USB_REQ_GET_DESCRIPTOR, + (USB_DT_REPORT << 8) + 0, phid->deviceDef->pdd_iid, (char*)buf, + sizeof(buf), 500 /* ms timeout */); + + if(len < 0) + { + switch(len) + { + case -ETIMEDOUT: //important case? + default: + LOG(PHIDGET_LOG_ERROR, "usb_control_msg failed in CUSBGetDeviceCapabilities with error code: %d \"%s\"", len, strerror(-len)); + return EPHIDGET_UNEXPECTED; + } + } + + if(len >= 10) + { + for(i=10;i<len;i++) { + if(buf[i]==0x81 && buf[i-2]==0x95) + phid->inputReportByteLength=buf[i-1]; + else if(buf[i]==0x81 && buf[i-4]==0x95) + phid->inputReportByteLength=buf[i-3]; + if(buf[i]==0x91 && buf[i-2]==0x95) + phid->outputReportByteLength=buf[i-1]; + else if(buf[i]==0x91 && buf[i-4]==0x95) + phid->outputReportByteLength=buf[i-3]; + } + } + else + { + LOG(PHIDGET_LOG_ERROR, "Couldn't get report lengths in CUSBGetDeviceCapabilities"); + return EPHIDGET_UNEXPECTED; + } + + return getLabelString(phid, udev); +} + +/* + This needs to maintain a list of devices - it's used by the phidget manager for + keeping track of attach and detach events. (On Mac this is handled with + notifications, so this is not needed.) - PM +*/ +int CUSBBuildList(CPhidgetList **curList) { + int MemberIndex, i, ret, found; + unsigned long Length; + CPhidgetList *traverse; + Length = 0; + MemberIndex = 0; + struct usb_bus *bus; + struct usb_device *dev; + usb_dev_handle *udev; + CPhidgetHandle phid; + char unique_name[1024]; + + TESTPTR(curList) + + usb_init(); + if((ret = usb_find_busses()) < 0) + { + LOG(PHIDGET_LOG_ERROR, "usb_find_busses failed with error code: %d \"%s\"", ret, strerror(-ret)); + } + if((ret = usb_find_devices()) < 0) + { + LOG(PHIDGET_LOG_ERROR, "usb_find_devices failed with error code: %d \"%s\"", ret, strerror(-ret)); + } + + //search through all USB devices + for (bus = usb_busses; bus; bus = bus->next) { + for (dev = bus->devices; dev; dev = dev->next) { + + snprintf(unique_name,1024,"%s%s",bus->dirname, dev->filename); + + //LOG(PHIDGET_LOG_VERBOSE,"New Device: %s", unique_name); + + found = PFALSE; + if (AttachedDevices) { + // we need to loop all the way through because composite devices will appear twice in the list with the same 'unique' name + for (traverse = AttachedDevices; traverse; traverse=traverse->next) { + if (!strcmp((char *)traverse->phid->CPhidgetFHandle, unique_name)) { + CList_addToList((CListHandle *)curList, traverse->phid, CPhidget_areEqual); + found = PTRUE; + } + } + if(found) goto next; + } + + + for (i = 1; i<PHIDGET_DEVICE_COUNT; i++) { + if ((dev->descriptor.idVendor == Phid_Device_Def[i].pdd_vid) && + (dev->descriptor.idProduct == Phid_Device_Def[i].pdd_pid)) + { + if (!(phid = (CPhidgetHandle)malloc(sizeof (*phid)))) + return EPHIDGET_NOMEMORY; + ZEROMEM(phid, sizeof(*phid)); + + //LOG(PHIDGET_LOG_DEBUG,"New Device: %s",(char *)Phid_DeviceName[Phid_Device_Def[i].pdd_did]); + + udev = usb_open(dev); + if(udev) { + if (dev->descriptor.bcdDevice < 0x100) + phid->deviceVersion = dev->descriptor.bcdDevice * 100; + else + phid->deviceVersion = ((dev->descriptor.bcdDevice >> 8) * 100) + ((dev->descriptor.bcdDevice & 0xff)); + phid->deviceType = (char *)Phid_DeviceName[Phid_Device_Def[i].pdd_did]; + + CPhidget_setStatusFlag(&phid->status, PHIDGET_ATTACHED_FLAG, &phid->lock); + phid->deviceIDSpec = Phid_Device_Def[i].pdd_sdid; + phid->deviceDef = &Phid_Device_Def[i]; + phid->deviceID = Phid_Device_Def[i].pdd_did; + phid->ProductID = dev->descriptor.idProduct; + phid->VendorID = dev->descriptor.idVendor; + + if (dev->descriptor.iSerialNumber) { + char string[256]; + memset(string, 0, 256); + if((ret = usb_get_string_simple(udev, dev->descriptor.iSerialNumber, string, sizeof(string))) < 0) + { + LOG(PHIDGET_LOG_ERROR, "usb_get_string_simple failed with error code: %d \"%s\"", ret, strerror(-ret)); + LOG(PHIDGET_LOG_INFO, "This usually means you need to run as root"); + if((ret = usb_close(udev)) < 0) + { + LOG(PHIDGET_LOG_ERROR, "usb_close failed with error code: %d \"%s\"", ret, strerror(-ret)); + } + free(phid); + goto next; + } + else + { + phid->serialNumber = atol(string); + getLabelString(phid, udev); + } + } + phid->specificDevice = TRUE; + phid->attr = Phid_Device_Def[i].pdd_attr; + + if(!(phid->CPhidgetFHandle = strdup(unique_name))) + return EPHIDGET_NOMEMORY; + + LOG(PHIDGET_LOG_INFO, "New device in CUSBBuildList: %s", (char *)phid->CPhidgetFHandle); + + if((ret = usb_close(udev)) < 0) + { + LOG(PHIDGET_LOG_ERROR, "usb_close failed with error code: %d \"%s\"", ret, strerror(-ret)); + } + CList_addToList((CListHandle *)curList, phid, CPhidget_areEqual); + } //if(udev) + else + { + free(phid); + if((ret = usb_close(udev)) < 0) + { + LOG(PHIDGET_LOG_ERROR, "usb_close failed with error code: %d \"%s\"", ret, strerror(-ret)); + } + } + } //vendor, product ids match + } /* iterate over phidget device table */ +next: ; + } /* iterate over USB devices */ + } /* iterate over USB busses */ + return EPHIDGET_OK; +} + +void CUSBCleanup(void) +{ + ; +} +/* + CUSBOpenHandle takes a CPhidgetInfo structure, with + ProductID/VendorID/SerialNumber filled in. + + Serial number is always filled in. +*/ +int CUSBOpenHandle(CPhidgetHandle phid) +{ + int idVendor; + int idProduct; + int serial = 0; + int i,ret; + struct usb_bus *bus; + struct usb_device *dev; + usb_dev_handle *udev; + + usb_init(); + if((ret = usb_find_busses()) < 0) + { + LOG(PHIDGET_LOG_ERROR, "usb_find_busses failed with error code: %d \"%s\"", ret, strerror(-ret)); + } + if((ret = usb_find_devices()) < 0) + { + LOG(PHIDGET_LOG_ERROR, "usb_find_devices failed with error code: %d \"%s\"", ret, strerror(-ret)); + } + + for (bus = usb_busses; bus; bus = bus->next) { + for (dev = bus->devices; dev; dev = dev->next) { + for (i = 1; i<PHIDGET_DEVICE_COUNT; i++) { + if (Phid_Device_Def[i].pdd_did == phid->deviceID) { + idVendor = Phid_Device_Def[i].pdd_vid; + idProduct = Phid_Device_Def[i].pdd_pid; + if ((dev->descriptor.idVendor == idVendor) && (dev->descriptor.idProduct == idProduct)) { + /* the vend/prod matches! */ + udev = usb_open(dev); + if (udev) { + serial = -1; + if (dev->descriptor.iSerialNumber) { + char string[256]; + if((ret = usb_get_string_simple(udev, dev->descriptor.iSerialNumber, string, sizeof(string))) < 0) + { + LOG(PHIDGET_LOG_WARNING, "usb_get_string_simple failed with error code: %d \"%s\"", ret, strerror(-ret)); + LOG(PHIDGET_LOG_INFO, "This usually means you need to run as root"); + if((ret = usb_close(udev)) < 0) + { + LOG(PHIDGET_LOG_ERROR, "usb_close failed with error code: %d \"%s\"", ret, strerror(-ret)); + } + goto next; + } + else + { + serial = atol(string); + } + } + if (serial == phid->serialNumber) { + /* On Linux, the HID driver likes to claim Phidgets - we can disconnect it here. + Maybe the user has installed the kernel drivers for the interface kit or servo - disconnect them too (does this work) + Note this is Linux Only - PM + Note that we don't need to claim it if usbfs has it - this is what libusb uses. */ +#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP + char name[32]; + if((ret = usb_get_driver_np(udev, Phid_Device_Def[i].pdd_iid, name, 32)) < 0) + { + LOG(PHIDGET_LOG_WARNING, "usb_get_driver_np failed with error code: %d \"%s\"", ret, strerror(-ret)); + } + else + { + LOG(PHIDGET_LOG_INFO, "Kernel driver name: %s", name); + if(strncmp(name, "usbfs", 5)) //not usbfs + { + if((ret = usb_detach_kernel_driver_np(udev, Phid_Device_Def[i].pdd_iid)) < 0) + { + LOG(PHIDGET_LOG_WARNING, "usb_detach_kernel_driver_np failed with error code: %d \"%s\"", ret, strerror(-ret)); + } + else + { + LOG(PHIDGET_LOG_INFO, "Successfully detached kernel driver: %s", name); + } + } + } +#endif + if((ret = usb_claim_interface(udev, Phid_Device_Def[i].pdd_iid)) < 0) + { + LOG(PHIDGET_LOG_WARNING, "usb_claim_interface failed with error code: %d \"%s\"", ret, strerror(-ret)); + if((ret = usb_close(udev)) < 0) + { + LOG(PHIDGET_LOG_ERROR, "usb_close failed with error code: %d \"%s\"", ret, strerror(-ret)); + } + } + else + { + /* the serialnum is okay */ + + phid->deviceHandle = (HANDLE)udev; + phid->deviceIDSpec = Phid_Device_Def[i].pdd_sdid; + phid->deviceDef = &Phid_Device_Def[i]; + phid->deviceType = (char *)Phid_DeviceName[Phid_Device_Def[i].pdd_did]; + + phid->ProductID = idProduct; + phid->VendorID = idVendor; + if (dev->descriptor.bcdDevice < 0x100) + phid->deviceVersion = dev->descriptor.bcdDevice * 100; + else + phid->deviceVersion = ((dev->descriptor.bcdDevice >> 8) * 100) + ((dev->descriptor.bcdDevice & 0xff)); + phid->serialNumber = serial; + + if((ret = CUSBGetDeviceCapabilities(phid, dev, udev))) + { + LOG(PHIDGET_LOG_ERROR, "CUSBGetDeviceCapabilities returned nonzero code: %d", ret); + } + + phid->attr = Phid_Device_Def[i].pdd_attr; + + return EPHIDGET_OK; + } /* usb_claim_interface */ + } /* serial matches */ + else + { + if((ret = usb_close(udev)) < 0) + { + LOG(PHIDGET_LOG_ERROR, "usb_close failed with error code: %d \"%s\"", ret, strerror(-ret)); + } + } + } /* udev open */ + else + { + if((ret = usb_close(udev)) < 0) + { + LOG(PHIDGET_LOG_ERROR, "usb_close failed with error code: %d \"%s\"", ret, strerror(-ret)); + } + LOG(PHIDGET_LOG_WARNING, "usb_open failed - bad permission or what?"); + } + } /* vendor/product match */ + } /* deviceID matches in table */ + } /* iterate over phidget device table */ +next: ; + } /* iterate over USB devices */ + } /* iterate over USB busses */ + return EPHIDGET_NOTFOUND; +} diff --git a/linux/zeroconf_avahi.c b/linux/zeroconf_avahi.c new file mode 100644 index 0000000..98187bf --- /dev/null +++ b/linux/zeroconf_avahi.c @@ -0,0 +1,1056 @@ +#include "stdafx.h"
+#include "csocket.h"
+#include "csocketevents.h"
+#include "cphidgetlist.h"
+#include "cphidgetmanager.h"
+#include "cphidgetdictionary.h"
+#include "cphidgetsbc.h"
+#include "zeroconf.h"
+
+#include "avahi-client/client.h"
+#include "avahi-client/lookup.h"
+
+#include "avahi-common/thread-watch.h"
+#include "avahi-common/malloc.h"
+#include "avahi-common/error.h"
+#include "avahi-common/domain.h"
+
+static int UninitializeZeroconf1(int lock);
+
+struct AvahiThreadedPoll {
+ void *simple_poll;
+ pthread_t thread_id;
+ pthread_mutex_t mutex;
+ int thread_running;
+ int retval;
+} ;
+
+#ifdef ZEROCONF_RUNTIME_LINKING
+typedef AvahiClient * (* avahi_client_new_type) (
+ const AvahiPoll *poll_api /**< The abstract event loop API to use */,
+ AvahiClientFlags flags /**< Some flags to modify the behaviour of the client library */,
+ AvahiClientCallback callback /**< A callback that is called whenever the state of the client changes. This may be NULL */,
+ void *userdata /**< Some arbitrary user data pointer that will be passed to the callback function */,
+ int *error /**< If creation of the client fails, this integer will contain the error cause. May be NULL if you aren't interested in the reason why avahi_client_new() failed. */);
+typedef void (* avahi_client_free_type)(AvahiClient *client);
+typedef const char * (* avahi_client_get_host_name_type) (AvahiClient *);
+typedef AvahiServiceBrowser * (* avahi_service_browser_new_type) (
+ AvahiClient *client,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *type,
+ const char *domain,
+ AvahiLookupFlags flags,
+ AvahiServiceBrowserCallback callback,
+ void *userdata);
+typedef AvahiServiceResolver * (* avahi_service_resolver_new_type)(
+ AvahiClient *client,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AvahiProtocol aprotocol,
+ AvahiLookupFlags flags,
+ AvahiServiceResolverCallback callback,
+ void *userdata);
+typedef int (* avahi_service_resolver_free_type)(AvahiServiceResolver *r);
+typedef AvahiRecordBrowser * (* avahi_record_browser_new_type)(
+ AvahiClient *client,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *name,
+ uint16_t clazz,
+ uint16_t type,
+ AvahiLookupFlags flags,
+ AvahiRecordBrowserCallback callback,
+ void *userdata);
+typedef int (* avahi_record_browser_free_type)(AvahiRecordBrowser *);
+typedef int (* avahi_service_name_join_type)(char *p, size_t size, const char *name, const char *type, const char *domain);
+typedef const char *(* avahi_strerror_type)(int error);
+typedef int (* avahi_client_errno_type) (AvahiClient*);
+typedef AvahiThreadedPoll *(* avahi_threaded_poll_new_type)(void);
+typedef void (* avahi_threaded_poll_free_type)(AvahiThreadedPoll *p);
+typedef const AvahiPoll* (* avahi_threaded_poll_get_type)(AvahiThreadedPoll *p);
+typedef int (* avahi_threaded_poll_start_type)(AvahiThreadedPoll *p);
+typedef int (* avahi_threaded_poll_stop_type)(AvahiThreadedPoll *p);
+typedef void (* avahi_threaded_poll_quit_type)(AvahiThreadedPoll *p);
+typedef void (* avahi_threaded_poll_lock_type)(AvahiThreadedPoll *p);
+typedef void (* avahi_threaded_poll_unlock_type)(AvahiThreadedPoll *p);
+typedef const char *(* avahi_client_get_version_string_type)(AvahiClient *c);
+
+avahi_service_browser_new_type avahi_service_browser_new_ptr = NULL;
+avahi_service_resolver_new_type avahi_service_resolver_new_ptr = NULL;
+avahi_service_resolver_free_type avahi_service_resolver_free_ptr = NULL;
+avahi_record_browser_new_type avahi_record_browser_new_ptr = NULL;
+avahi_record_browser_free_type avahi_record_browser_free_ptr = NULL;
+avahi_service_name_join_type avahi_service_name_join_ptr = NULL;
+avahi_client_new_type avahi_client_new_ptr = NULL;
+avahi_client_free_type avahi_client_free_ptr = NULL;
+avahi_strerror_type avahi_strerror_ptr = NULL;
+avahi_client_errno_type avahi_client_errno_ptr = NULL;
+avahi_threaded_poll_new_type avahi_threaded_poll_new_ptr = NULL;
+avahi_threaded_poll_free_type avahi_threaded_poll_free_ptr = NULL;
+avahi_threaded_poll_get_type avahi_threaded_poll_get_ptr = NULL;
+avahi_threaded_poll_start_type avahi_threaded_poll_start_ptr = NULL;
+avahi_threaded_poll_stop_type avahi_threaded_poll_stop_ptr = NULL;
+avahi_threaded_poll_quit_type avahi_threaded_poll_quit_ptr = NULL;
+avahi_threaded_poll_lock_type avahi_threaded_poll_lock_ptr = NULL;
+avahi_threaded_poll_unlock_type avahi_threaded_poll_unlock_ptr = NULL;
+avahi_client_get_version_string_type avahi_client_get_version_string_ptr = NULL;
+#else
+#define avahi_service_browser_new_ptr avahi_service_browser_new
+#define avahi_service_resolver_new_ptr avahi_service_resolver_new
+#define avahi_service_resolver_free_ptr avahi_service_resolver_free
+#define avahi_record_browser_new_ptr avahi_record_browser_new
+#define avahi_record_browser_free_ptr avahi_record_browser_free
+#define avahi_service_name_join_ptr avahi_service_name_join
+#define avahi_client_new_ptr avahi_client_new
+#define avahi_client_free_ptr avahi_client_free
+#define avahi_strerror_ptr avahi_strerror
+#define avahi_client_errno_ptr avahi_client_errno
+#define avahi_threaded_poll_new_ptr avahi_threaded_poll_new
+#define avahi_threaded_poll_free_ptr avahi_threaded_poll_free
+#define avahi_threaded_poll_get_ptr avahi_threaded_poll_get
+#define avahi_threaded_poll_start_ptr avahi_threaded_poll_start
+#define avahi_threaded_poll_stop_ptr avahi_threaded_poll_stop
+#define avahi_threaded_poll_quit_ptr avahi_threaded_poll_quit
+#define avahi_threaded_poll_lock_ptr avahi_threaded_poll_lock
+#define avahi_threaded_poll_unlock_ptr avahi_threaded_poll_unlock
+#define avahi_client_get_version_string_ptr avahi_client_get_version_string
+#endif
+
+/*
+ * TXT record version - this should be 1 for a long time
+ * - only need to change if we really change the TXT record format
+ */
+const char *dnssd_txt_ver = "1";
+
+int Dns_sdInitialized = FALSE;
+
+static AvahiThreadedPoll *threaded_poll = NULL;
+static AvahiClient *client = NULL;
+
+static AvahiServiceBrowser *zeroconf_browse_sbc_ref = NULL;
+static AvahiServiceBrowser *zeroconf_browse_ws_ref = NULL;
+static AvahiServiceBrowser *zeroconf_browse_phidget_ref = NULL;
+
+//pthread_t dns_thread_ws, dns_thread_phid;
+
+void *avahiLibHandle = NULL;
+
+static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
+ assert(c);
+
+ /* Called whenever the client or server state changes */
+
+ switch (state) {
+ case AVAHI_CLIENT_S_RUNNING:
+
+ /* The server has startup successfully and registered its host
+ * name on the network */
+ Dns_sdInitialized = TRUE;
+ break;
+
+ case AVAHI_CLIENT_FAILURE:
+
+ LOG(PHIDGET_LOG_ERROR, "Client failure: %s", avahi_strerror_ptr(avahi_client_errno_ptr(c)));
+ //avahi_threaded_poll_quit_ptr(threaded_poll);
+
+ break;
+
+ case AVAHI_CLIENT_S_COLLISION:
+
+ /* Let's drop our registered services. When the server is back
+ * in AVAHI_SERVER_RUNNING state we will register them
+ * again with the new host name. */
+
+ case AVAHI_CLIENT_S_REGISTERING:
+
+ /* The server records are now being established. This
+ * might be caused by a host name change. We need to wait
+ * for our own records to register until the host name is
+ * properly esatblished. */
+
+ //if (group)
+ // avahi_entry_group_reset_ptr(group);
+
+ break;
+
+ case AVAHI_CLIENT_CONNECTING:
+ break;
+ }
+}
+
+static uint8_t *InternalTXTRecordSearch
+ (
+ uint16_t txtLen,
+ const void *txtRecord,
+ const char *key,
+ unsigned long *keylen
+ )
+{
+ uint8_t *p = (uint8_t*)txtRecord;
+ uint8_t *e = p + txtLen;
+ *keylen = (unsigned long) strlen(key);
+ while (p<e)
+ {
+ uint8_t *x = p;
+ p += 1 + p[0];
+ if (p <= e && *keylen <= x[0] && !strncmp(key, (char*)x+1, *keylen))
+ if (*keylen == x[0] || x[1+*keylen] == '=') return(x);
+ }
+ return(NULL);
+}
+
+const void * TXTRecordGetValuePtr
+ (
+ uint16_t txtLen,
+ const void *txtRecord,
+ const char *key,
+ uint8_t *valueLen
+ )
+{
+ unsigned long keylen;
+ uint8_t *item = InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen);
+ if (!item || item[0] <= keylen) return(NULL); // If key not found, or found with no value, return NULL
+ *valueLen = (uint8_t)(item[0] - (keylen + 1));
+ return (item + 1 + keylen + 1);
+}
+
+void PhidFromTXT(CPhidgetHandle phid, uint16_t txtLen, const char *txtRecord)
+{
+ int i = 0;
+ short txtver;
+
+ uint8_t valLen = 0;
+ const char *valPtr = NULL;
+
+ //txt version
+ if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "txtvers", &valLen))) return;
+ txtver = (short)strtol(valPtr, NULL, 10);
+
+ //Serial Number
+ if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "serial", &valLen))) return;
+ phid->serialNumber = strtol(valPtr, NULL, 10);
+ phid->specificDevice = PTRUE;
+
+ //version
+ if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "version", &valLen))) return;
+ phid->deviceVersion = strtol(valPtr, NULL, 10);
+
+ //label
+ if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "label", &valLen))) return;
+ if(valLen > MAX_LABEL_STORAGE-1) valLen = MAX_LABEL_STORAGE-1;
+ memcpy(phid->label, valPtr, valLen);
+ phid->label[valLen] = '\0';
+
+ //server_id
+ if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "server_id", &valLen))) return;
+ free(phid->networkInfo->zeroconf_server_id);
+ if(!(phid->networkInfo->zeroconf_server_id = malloc(valLen+1))) return;
+ ZEROMEM(phid->networkInfo->zeroconf_server_id, valLen+1);
+ memcpy(phid->networkInfo->zeroconf_server_id, valPtr, valLen);
+
+ // things added in version 2 of the txt
+ if(txtver >= 2)
+ {
+ //Device ID
+ if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "id", &valLen))) return;
+ phid->deviceIDSpec = strtol(valPtr, NULL, 10);
+
+ for(i = 1;i<PHIDGET_DEVICE_COUNT;i++)
+ if(phid->deviceIDSpec == Phid_Device_Def[i].pdd_sdid) break;
+ phid->deviceDef = &Phid_Device_Def[i];
+ phid->attr = Phid_Device_Def[i].pdd_attr;
+
+ //Device Class
+ if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "class", &valLen))) return;
+ phid->deviceID = strtol(valPtr, NULL, 10);
+ phid->deviceType = Phid_DeviceName[phid->deviceID];
+ }
+ //Old version uses string searching, but some devices have the same name with different IDs
+ else
+ {
+ char *name = NULL;
+ char *type = NULL;
+
+ //name
+ if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "name", &valLen))) return;
+ if(!(name = malloc(valLen+1))) return;
+ ZEROMEM(name, valLen+1);
+ memcpy(name, valPtr, valLen);
+ for(i = 0;i<PHIDGET_DEVICE_COUNT;i++)
+ {
+ if(!strcmp(name, Phid_Device_Def[i].pdd_name))
+ {
+ phid->deviceIDSpec = Phid_Device_Def[i].pdd_sdid;
+ phid->deviceDef = &Phid_Device_Def[i];
+ phid->attr = Phid_Device_Def[i].pdd_attr;
+ break;
+ }
+ }
+ free(name);
+
+ //type
+ if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "type", &valLen))) return;
+ if(!(type = malloc(valLen+1))) return;
+ ZEROMEM(type, valLen+1);
+ memcpy(type, valPtr, valLen);
+ phid->deviceID = phidget_type_to_id(type);
+ phid->deviceType = Phid_DeviceName[phid->deviceID];
+ free(type);
+ }
+
+ phid->networkInfo->mdns = PTRUE;
+
+}
+
+void SBCFromTXT(CPhidgetSBCHandle sbc, uint16_t txtLen, const char *txtRecord)
+{
+ char *hversion = NULL, *txtver = NULL;
+
+ uint8_t valLen = 0;
+ const char *valPtr = NULL;
+
+ //txt version
+ if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "txtvers", &valLen))) return;
+ if(!(txtver = malloc(valLen+1))) return;
+ ZEROMEM(txtver, valLen+1);
+ memcpy(txtver, valPtr, valLen);
+ sbc->txtver = (short)strtol(txtver, NULL, 10);
+ free(txtver);
+
+ //Firmware version
+ if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "fversion", &valLen))) return;
+ if(valLen > 12) valLen = 12;
+ memcpy(sbc->fversion, valPtr, valLen);
+ sbc->fversion[valLen] = '\0';
+
+ //Hardware version
+ if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "hversion", &valLen))) return;
+ if(!(hversion = malloc(valLen+1))) return;
+ ZEROMEM(hversion, valLen+1);
+ memcpy(hversion, valPtr, valLen);
+ sbc->hversion = (short)strtol(hversion, NULL, 10);
+ free(hversion);
+
+ // things added in version 2 of the txt
+ if(sbc->txtver >= 2)
+ {
+ //Hostname
+ if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "hostname", &valLen))) return;
+ if(valLen > 128) valLen = 128;
+ memcpy(sbc->hostname, valPtr, valLen);
+ sbc->hostname[valLen] = '\0';
+ }
+
+ // things added in version 3 of the txt
+ if(sbc->txtver >= 3)
+ {
+ //Device Name
+ if(!(valPtr = TXTRecordGetValuePtr(txtLen, txtRecord, "name", &valLen))) return;
+ if(valLen > 128) valLen = 128;
+ memcpy(sbc->deviceName, valPtr, valLen);
+ sbc->deviceName[valLen] = '\0';
+ }
+ else
+ {
+ sprintf(sbc->deviceName, "PhidgetSBC");
+ }
+}
+
+void DNSServiceResolve_CallBack(
+ AvahiServiceResolver *r,
+ AVAHI_GCC_UNUSED AvahiIfIndex interface,
+ AVAHI_GCC_UNUSED AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host_name,
+ const AvahiAddress *address,
+ uint16_t port,
+ AvahiStringList *txt,
+ AvahiLookupResultFlags flags,
+ AVAHI_GCC_UNUSED void* userdata)
+{
+
+ CPhidgetRemoteHandle networkInfo = (CPhidgetRemoteHandle)userdata;
+ switch (event) {
+ case AVAHI_RESOLVER_FAILURE:
+ LOG(PHIDGET_LOG_ERROR, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s", name, type, domain, avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ networkInfo->zeroconf_host = strdup("err");
+ break;
+ case AVAHI_RESOLVER_FOUND:
+ {
+ LOG(PHIDGET_LOG_INFO, "DNSServiceResolve_CallBack: %s",name);
+ networkInfo->zeroconf_host = strdup(host_name);
+ networkInfo->zeroconf_port = malloc(10);
+ snprintf(networkInfo->zeroconf_port, 9, "%d", port);
+ }
+ }
+
+ avahi_service_resolver_free_ptr(r);
+}
+
+void DNSServiceQueryRecord_Phidget_CallBack
+ (
+ AvahiRecordBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ uint16_t clazz,
+ uint16_t type,
+ const void *rdata,
+ size_t size,
+ AvahiLookupResultFlags flags,
+ void *userdata
+ )
+{
+ CPhidgetHandle phid = (CPhidgetHandle)userdata;
+ CPhidgetManagerList *trav;
+
+ switch(event)
+ {
+ case AVAHI_BROWSER_NEW:
+ PhidFromTXT(phid, size, rdata);
+ LOG(PHIDGET_LOG_INFO, "DNSServiceQueryRecord_Phidget_CallBack: %s",name);
+ CThread_mutex_lock(&zeroconfPhidgetsLock);
+ CThread_mutex_lock(&activeRemoteManagersLock);
+
+ CPhidget_setStatusFlag(&phid->status, PHIDGET_ATTACHED_FLAG, &phid->lock);
+ CPhidget_setStatusFlag(&phid->status, PHIDGET_REMOTE_FLAG, &phid->lock);
+ CPhidget_setStatusFlag(&phid->status, PHIDGET_SERVER_CONNECTED_FLAG, &phid->lock);
+
+ //now add it
+ CList_addToList((CListHandle *)&zeroconfPhidgets, phid, CPhidget_areExtraEqual);
+ //managers
+ for (trav=activeRemoteManagers; trav; trav = trav->next)
+ {
+ if(trav->phidm->networkInfo->requested_address==NULL
+ && (trav->phidm->networkInfo->requested_serverID == NULL || !strcmp(trav->phidm->networkInfo->requested_serverID,phid->networkInfo->zeroconf_server_id)))
+ {
+ CList_addToList((CListHandle *)&trav->phidm->AttachedPhidgets, phid, CPhidget_areExtraEqual);
+
+ if (trav->phidm->fptrAttachChange && trav->phidm->state == PHIDGETMANAGER_ACTIVE)
+ trav->phidm->fptrAttachChange((CPhidgetHandle)phid, trav->phidm->fptrAttachChangeptr);
+ }
+ }
+ CThread_mutex_unlock(&activeRemoteManagersLock);
+ CThread_mutex_unlock(&zeroconfPhidgetsLock);
+ break;
+ case AVAHI_BROWSER_FAILURE:
+ LOG(PHIDGET_LOG_ERROR, "DNSServiceQueryRecord_Phidget_CallBack returned error: %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ break;
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ avahi_record_browser_free_ptr(b);
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ LOG(PHIDGET_LOG_INFO, "DNSServiceQueryRecord_Phidget_CallBack %s", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
+ break;
+ case AVAHI_BROWSER_REMOVE:
+ break;
+ }
+}
+
+void DNSServiceQueryRecord_SBC_CallBack
+(
+ AvahiRecordBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ uint16_t clazz,
+ uint16_t type,
+ const void *rdata,
+ size_t size,
+ AvahiLookupResultFlags flags,
+ void *userdata
+ )
+{
+ CPhidgetSBCHandle sbc = (CPhidgetSBCHandle)userdata, found_sbc;
+ CPhidgetSBCManagerList *trav;
+
+ switch(event)
+ {
+ case AVAHI_BROWSER_NEW:
+ SBCFromTXT(sbc, size, rdata);
+ LOG(PHIDGET_LOG_INFO, "DNSServiceQueryRecord_SBC_CallBack: %s",name);
+ CThread_mutex_lock(&zeroconfSBCsLock);
+ CThread_mutex_lock(&activeSBCManagersLock);
+
+ //Check if it's in the list and if it's different, remove it to make way for the new one
+ // (Sometimes, we don't get a proper detach notification)
+ if(CList_findInList((CListHandle)zeroconfSBCs, sbc, CPhidgetSBC_areEqual, (void **)&found_sbc) == EPHIDGET_OK)
+ {
+ if(CPhidgetSBC_areExtraEqual(found_sbc, sbc) != PTRUE) //A version number has changed
+ {
+ //Remove from list - don't free until after detach event
+ CList_removeFromList((CListHandle *)&zeroconfSBCs, found_sbc, CPhidgetSBC_areEqual, PFALSE, NULL);
+
+ for (trav=activeSBCManagers; trav; trav = trav->next)
+ {
+ if (trav->sbcm->fptrDetachChange && trav->sbcm->state == PHIDGETMANAGER_ACTIVE)
+ trav->sbcm->fptrDetachChange((CPhidgetSBCHandle)found_sbc, trav->sbcm->fptrDetachChangeptr);
+ }
+
+ CPhidgetSBC_free(found_sbc);
+
+ //now we fall through and add back to new one
+ }
+ else //Nothing has changed, we didn't remove, don't add
+ {
+ CPhidgetSBC_free(sbc);
+ goto dontadd;
+ }
+ }
+
+ //now add it
+ CList_addToList((CListHandle *)&zeroconfSBCs, sbc, CPhidgetSBC_areEqual);
+
+ //send out events
+ for (trav=activeSBCManagers; trav; trav = trav->next)
+ {
+ if (trav->sbcm->fptrAttachChange && trav->sbcm->state == PHIDGETMANAGER_ACTIVE)
+ trav->sbcm->fptrAttachChange((CPhidgetSBCHandle)sbc, trav->sbcm->fptrAttachChangeptr);
+
+ }
+ dontadd:
+
+ CThread_mutex_unlock(&activeSBCManagersLock);
+ CThread_mutex_unlock(&zeroconfSBCsLock);
+ break;
+ case AVAHI_BROWSER_FAILURE:
+ LOG(PHIDGET_LOG_ERROR, "DNSServiceQueryRecord_SBC_CallBack returned error: %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ break;
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ avahi_record_browser_free_ptr(b);
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ LOG(PHIDGET_LOG_INFO, "DNSServiceQueryRecord_SBC_CallBack %s", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
+ break;
+ case AVAHI_BROWSER_REMOVE:
+ break;
+ }
+}
+
+void DNSServiceBrowse_Phidget_CallBack(
+ AvahiServiceBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ void* userdata)
+{
+
+ CPhidgetHandle phid;
+ CPhidgetManagerList *trav;
+
+ int ret;
+
+ switch (event) {
+
+ case AVAHI_BROWSER_FAILURE:
+
+ LOG(PHIDGET_LOG_WARNING, "(Browser) %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ avahi_threaded_poll_quit_ptr(threaded_poll);
+ return;
+
+ case AVAHI_BROWSER_NEW:
+ {
+ char fullname[AVAHI_DOMAIN_NAME_MAX];
+
+ if((CPhidget_create(&phid))) return;
+ if((CPhidgetRemote_create(&phid->networkInfo))) return;
+
+ phid->networkInfo->zeroconf_name = strdup(name);
+ phid->networkInfo->zeroconf_type = strdup(type);
+ phid->networkInfo->zeroconf_domain = strdup(domain);
+
+ LOG(PHIDGET_LOG_INFO, "(Browser) NEW: service '%s' of type '%s' in domain '%s'", name, type, domain);
+
+ if((ret = avahi_service_name_join_ptr(fullname, AVAHI_DOMAIN_NAME_MAX, name, type, domain)) != AVAHI_OK)
+ LOG(PHIDGET_LOG_ERROR, "Failed avahi_service_name_join_ptr '%s': %s", name, avahi_strerror_ptr(ret));
+
+ if(!(avahi_record_browser_new_ptr(client, interface, protocol, fullname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, 0, DNSServiceQueryRecord_Phidget_CallBack, phid)))
+ LOG(PHIDGET_LOG_ERROR, "Failed to resolve service '%s': %s", name, avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ //gets added to list in callback
+ }
+ break;
+
+ case AVAHI_BROWSER_REMOVE:
+ {
+ if((CPhidget_create(&phid))) return;
+ if((CPhidgetRemote_create(&phid->networkInfo))) return;
+
+ phid->networkInfo->zeroconf_name = strdup(name);
+ phid->networkInfo->zeroconf_type = strdup(type);
+ phid->networkInfo->zeroconf_domain = strdup(domain);
+
+ LOG(PHIDGET_LOG_INFO, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'", name, type, domain);
+
+ //have to fill in phid manually from just the name
+ int i;
+ CPhidgetHandle found_phid;
+ char *name_copy = strdup(name);
+ for(i=0;i<strlen(name_copy);i++)
+ if(name_copy[i] == '(') break;
+ if(i<=1) return;
+ name_copy[strlen(name_copy)-1]='\0';
+ name_copy[i-1] = '\0';
+ phid->serialNumber = strtol(name_copy+i+1, NULL, 10);
+ phid->specificDevice = PTRUE;
+ for(i = 0;i<PHIDGET_DEVICE_COUNT;i++)
+ if(!strcmp(name_copy, Phid_Device_Def[i].pdd_name)) break;
+ phid->deviceIDSpec = 0;
+ phid->deviceDef = &Phid_Device_Def[i];
+ phid->attr = Phid_Device_Def[i].pdd_attr;
+ phid->deviceID = Phid_Device_Def[i].pdd_did;
+ phid->deviceType = Phid_DeviceName[phid->deviceID];
+ phid->networkInfo->mdns = PTRUE;
+
+ CThread_mutex_lock(&zeroconfPhidgetsLock);
+ CThread_mutex_lock(&activeRemoteManagersLock);
+
+ CPhidget_clearStatusFlag(&phid->status, PHIDGET_ATTACHED_FLAG, &phid->lock);
+ CPhidget_setStatusFlag(&phid->status, PHIDGET_DETACHING_FLAG, &phid->lock);
+
+ if(!CList_findInList((CListHandle)zeroconfPhidgets, phid, CPhidget_areEqual, (void **)&found_phid))
+ {
+ CPhidget_clearStatusFlag(&found_phid->status, PHIDGET_ATTACHED_FLAG, &found_phid->lock);
+ CPhidget_setStatusFlag(&found_phid->status, PHIDGET_DETACHING_FLAG, &found_phid->lock);
+ CPhidget_setStatusFlag(&found_phid->status, PHIDGET_REMOTE_FLAG, &found_phid->lock);
+ CPhidget_clearStatusFlag(&found_phid->status, PHIDGET_SERVER_CONNECTED_FLAG, &found_phid->lock);
+
+ CList_removeFromList((CListHandle *)&zeroconfPhidgets, found_phid, CPhidget_areExtraEqual, FALSE, NULL);
+ //managers
+ for (trav=activeRemoteManagers; trav; trav = trav->next)
+ {
+ if(trav->phidm->networkInfo->requested_address==NULL
+ && (trav->phidm->networkInfo->requested_serverID == NULL || !strcmp(trav->phidm->networkInfo->requested_serverID,found_phid->networkInfo->zeroconf_server_id)))
+ {
+ CList_removeFromList((CListHandle *)&trav->phidm->AttachedPhidgets, found_phid, CPhidget_areExtraEqual, PFALSE, NULL);
+
+ if (trav->phidm->fptrDetachChange && trav->phidm->state == PHIDGETMANAGER_ACTIVE)
+ trav->phidm->fptrDetachChange((CPhidgetHandle)found_phid, trav->phidm->fptrDetachChangeptr);
+ }
+ }
+ CPhidget_clearStatusFlag(&found_phid->status, PHIDGET_DETACHING_FLAG, &found_phid->lock);
+ CPhidgetRemote_free(found_phid->networkInfo);
+ CPhidget_free(found_phid);
+ }
+ CPhidgetRemote_free(phid->networkInfo);
+ CPhidget_free(phid);
+
+ CThread_mutex_unlock(&activeRemoteManagersLock);
+ CThread_mutex_unlock(&zeroconfPhidgetsLock);
+ free(name_copy);
+ }
+ break;
+
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ LOG(PHIDGET_LOG_INFO, "(Browser) %s", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
+ break;
+ }
+}
+
+void DNSServiceBrowse_SBC_CallBack(
+ AvahiServiceBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ void* userdata)
+{
+
+ CPhidgetSBCHandle sbc, found_sbc;
+ CPhidgetSBCManagerList *trav;
+ int ret;
+
+ switch (event) {
+
+ case AVAHI_BROWSER_FAILURE:
+
+ LOG(PHIDGET_LOG_WARNING, "(Browser) %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ avahi_threaded_poll_quit_ptr(threaded_poll);
+ return;
+
+ case AVAHI_BROWSER_NEW:
+ {
+ char fullname[AVAHI_DOMAIN_NAME_MAX];
+
+ if((CPhidgetSBC_create(&sbc))) return;
+ if((CPhidgetRemote_create(&sbc->networkInfo))) return;
+
+ sbc->networkInfo->zeroconf_name = strdup(name);
+ sbc->networkInfo->zeroconf_type = strdup(type);
+ sbc->networkInfo->zeroconf_domain = strdup(domain);
+ sbc->networkInfo->mdns = PTRUE;
+
+ strncpy(sbc->mac, name+12, 18); //name == 'PhidgetSBC (??:??:??:??:??:??)'
+ sbc->mac[17] = '\0';
+
+ LOG(PHIDGET_LOG_INFO, "(Browser) NEW: service '%s' of type '%s' in domain '%s'", name, type, domain);
+
+ if((ret = avahi_service_name_join_ptr(fullname, AVAHI_DOMAIN_NAME_MAX, name, type, domain)) != AVAHI_OK)
+ LOG(PHIDGET_LOG_ERROR, "Failed avahi_service_name_join_ptr '%s': %s", name, avahi_strerror_ptr(ret));
+
+ if(!(avahi_record_browser_new_ptr(client, interface, protocol, fullname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, 0, DNSServiceQueryRecord_SBC_CallBack, sbc)))
+ LOG(PHIDGET_LOG_ERROR, "Failed to resolve service '%s': %s", name, avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ //gets added to list in callback
+ }
+ break;
+
+ case AVAHI_BROWSER_REMOVE:
+ {
+ if((CPhidgetSBC_create(&sbc))) return;
+ if((CPhidgetRemote_create(&sbc->networkInfo))) return;
+
+ sbc->networkInfo->zeroconf_name = strdup(name);
+ sbc->networkInfo->zeroconf_type = strdup(type);
+ sbc->networkInfo->zeroconf_domain = strdup(domain);
+ sbc->networkInfo->mdns = PTRUE;
+
+ strncpy(sbc->mac, name+12, 18); //name == 'PhidgetSBC (??:??:??:??:??:??)'
+ sbc->mac[17] = '\0';
+
+ LOG(PHIDGET_LOG_INFO, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'", name, type, domain);
+
+
+ CThread_mutex_lock(&zeroconfSBCsLock);
+ CThread_mutex_lock(&activeSBCManagersLock);
+
+ if(CList_findInList((CListHandle)zeroconfSBCs, sbc, CPhidgetSBC_areEqual, (void **)&found_sbc) == EPHIDGET_OK)
+ {
+ CList_removeFromList((CListHandle *)&zeroconfSBCs, found_sbc, CPhidgetSBC_areEqual, PFALSE, NULL);
+ //managers
+ for (trav=activeSBCManagers; trav; trav = trav->next)
+ {
+ if (trav->sbcm->fptrDetachChange && trav->sbcm->state == PHIDGETMANAGER_ACTIVE)
+ trav->sbcm->fptrDetachChange((CPhidgetSBCHandle)found_sbc, trav->sbcm->fptrDetachChangeptr);
+ }
+ CPhidgetSBC_free(found_sbc);
+ }
+
+ CThread_mutex_unlock(&activeSBCManagersLock);
+ CThread_mutex_unlock(&zeroconfSBCsLock);
+
+ CPhidgetSBC_free(sbc);
+ }
+ break;
+
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ LOG(PHIDGET_LOG_INFO, "(Browser) %s", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
+ break;
+ }
+}
+
+void DNSServiceBrowse_ws_CallBack(
+ AvahiServiceBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ void* userdata)
+{
+
+ switch (event) {
+
+ case AVAHI_BROWSER_FAILURE:
+
+ LOG(PHIDGET_LOG_ERROR, "(Browser) %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ //avahi_threaded_poll_quit_ptr(threaded_poll);
+ return;
+
+ case AVAHI_BROWSER_NEW:
+ {
+ CPhidgetRemoteHandle networkInfo;
+ if((CPhidgetRemote_create(&networkInfo))) return;
+
+ networkInfo->zeroconf_name = strdup(name);
+ networkInfo->zeroconf_server_id = strdup(name);
+ networkInfo->zeroconf_type = strdup(type);
+ networkInfo->zeroconf_domain = strdup(domain);
+
+ LOG(PHIDGET_LOG_INFO, "(Browser) NEW: service '%s' of type '%s' in domain '%s'", name, type, domain);
+
+ CThread_mutex_lock(&zeroconfServersLock);
+ CList_addToList((CListHandle *)&zeroconfServers, networkInfo, CPhidgetRemote_areEqual);
+ CThread_mutex_unlock(&zeroconfServersLock);
+ }
+ break;
+
+ case AVAHI_BROWSER_REMOVE:
+ {
+ CPhidgetRemoteHandle networkInfo;
+ if((CPhidgetRemote_create(&networkInfo))) return;
+
+ networkInfo->zeroconf_name = strdup(name);
+ networkInfo->zeroconf_server_id = strdup(name);
+ networkInfo->zeroconf_type = strdup(type);
+ networkInfo->zeroconf_domain = strdup(domain);
+ LOG(PHIDGET_LOG_INFO, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'", name, type, domain);
+
+ CThread_mutex_lock(&zeroconfServersLock);
+ CList_removeFromList((CListHandle *)&zeroconfServers, networkInfo, CPhidgetRemote_areEqual, TRUE, CPhidgetRemote_free);
+ CThread_mutex_unlock(&zeroconfServersLock);
+ }
+ break;
+
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ LOG(PHIDGET_LOG_INFO, "(Browser) %s", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
+ break;
+ }
+}
+
+//Does nothing in Avahi
+int cancelPendingZeroconfLookups(CPhidgetRemoteHandle networkInfo)
+{
+ return EPHIDGET_OK;
+}
+
+int getZeroconfHostPort(CPhidgetRemoteHandle networkInfo)
+{
+ int timeout = 200; //2000ms
+
+ if(networkInfo->zeroconf_host) free(networkInfo->zeroconf_host);
+ networkInfo->zeroconf_host = NULL;
+ if(networkInfo->zeroconf_port) free(networkInfo->zeroconf_port);
+ networkInfo->zeroconf_port = NULL;
+
+ //lock the thread before accessing the client - nope, it messes up if this is from the same thread (via attach/detach)
+ //avahi_threaded_poll_lock_ptr(threaded_poll);
+ if (!(avahi_service_resolver_new_ptr(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ networkInfo->zeroconf_name, //name
+ networkInfo->zeroconf_type, // service type
+ networkInfo->zeroconf_domain, //domain
+ AVAHI_PROTO_UNSPEC, 0, DNSServiceResolve_CallBack, networkInfo)))
+ {
+ LOG(PHIDGET_LOG_ERROR, "Failed to resolve service '%s': %s", networkInfo->zeroconf_name, avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ //avahi_threaded_poll_unlock_ptr(threaded_poll);
+ return EPHIDGET_UNEXPECTED;
+ }
+ //avahi_threaded_poll_unlock_ptr(threaded_poll);
+
+ while(!networkInfo->zeroconf_host)
+ {
+ usleep(10000);
+ timeout--;
+ if(!timeout)
+ {
+ LOG(PHIDGET_LOG_ERROR, "getZeroconfHostPort didn't work (timeout)");
+ return EPHIDGET_UNEXPECTED;
+ }
+ }
+
+ if(!strcmp(networkInfo->zeroconf_host, "err"))
+ {
+ LOG(PHIDGET_LOG_ERROR, "getZeroconfHostPort didn't work (error)");
+ free(networkInfo->zeroconf_host);
+ networkInfo->zeroconf_host = NULL;
+ return EPHIDGET_UNEXPECTED;
+ }
+
+ return EPHIDGET_OK;
+}
+
+int refreshZeroconfSBC(CPhidgetSBCHandle sbc)
+{
+ return EPHIDGET_OK;
+}
+
+int refreshZeroconfPhidget(CPhidgetHandle phid)
+{
+ return EPHIDGET_OK;
+}
+
+int InitializeZeroconf()
+{
+ int error;
+ //int ret = 1;
+ int timeout = 50; //500ms
+ const char *avahiVersion;
+
+ CThread_mutex_lock(&zeroconfInitLock);
+ if(Dns_sdInitialized)
+ {
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_OK;
+ }
+
+#ifdef ZEROCONF_RUNTIME_LINKING
+
+ avahiLibHandle = dlopen("libavahi-client.so",RTLD_LAZY);
+ if(!avahiLibHandle)
+ {
+ avahiLibHandle = dlopen("libavahi-client.so.3",RTLD_LAZY);
+ }
+ if(!avahiLibHandle)
+ {
+ LOG(PHIDGET_LOG_WARNING, "dlopen failed with error: %s", dlerror());
+ LOG(PHIDGET_LOG_WARNING, "Assuming that zeroconf is not supported on this machine.");
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNSUPPORTED;
+ }
+
+ //These are always in Avahi
+ if(!(avahi_client_get_version_string_ptr = (avahi_client_get_version_string_type)dlsym(avahiLibHandle, "avahi_client_get_version_string"))) goto dlsym_err;
+ if(!(avahi_service_browser_new_ptr = (avahi_service_browser_new_type)dlsym(avahiLibHandle, "avahi_service_browser_new"))) goto dlsym_err;
+ if(!(avahi_service_resolver_new_ptr = (avahi_service_resolver_new_type)dlsym(avahiLibHandle, "avahi_service_resolver_new"))) goto dlsym_err;
+ if(!(avahi_service_resolver_free_ptr = (avahi_service_resolver_free_type)dlsym(avahiLibHandle, "avahi_service_resolver_free"))) goto dlsym_err;
+ if(!(avahi_record_browser_new_ptr = (avahi_record_browser_new_type)dlsym(avahiLibHandle, "avahi_record_browser_new"))) goto dlsym_err;
+ if(!(avahi_record_browser_free_ptr = (avahi_record_browser_free_type)dlsym(avahiLibHandle, "avahi_record_browser_free"))) goto dlsym_err;
+ if(!(avahi_service_name_join_ptr = (avahi_service_name_join_type)dlsym(avahiLibHandle, "avahi_service_name_join"))) goto dlsym_err;
+ if(!(avahi_client_new_ptr = (avahi_client_new_type)dlsym(avahiLibHandle, "avahi_client_new"))) goto dlsym_err;
+ if(!(avahi_client_free_ptr = (avahi_client_free_type)dlsym(avahiLibHandle, "avahi_client_free"))) goto dlsym_err;
+ if(!(avahi_strerror_ptr = (avahi_strerror_type)dlsym(avahiLibHandle, "avahi_strerror"))) goto dlsym_err;
+ if(!(avahi_client_errno_ptr = (avahi_client_errno_type)dlsym(avahiLibHandle, "avahi_client_errno"))) goto dlsym_err;
+
+ //These are in Avahi > 0.6.4
+ if(!(avahi_threaded_poll_new_ptr = (avahi_threaded_poll_new_type)dlsym(avahiLibHandle, "avahi_threaded_poll_new"))) goto dlsym_err2;
+ if(!(avahi_threaded_poll_free_ptr = (avahi_threaded_poll_free_type)dlsym(avahiLibHandle, "avahi_threaded_poll_free"))) goto dlsym_err2;
+ if(!(avahi_threaded_poll_get_ptr = (avahi_threaded_poll_get_type)dlsym(avahiLibHandle, "avahi_threaded_poll_get"))) goto dlsym_err2;
+ if(!(avahi_threaded_poll_start_ptr = (avahi_threaded_poll_start_type)dlsym(avahiLibHandle, "avahi_threaded_poll_start"))) goto dlsym_err2;
+ if(!(avahi_threaded_poll_stop_ptr = (avahi_threaded_poll_stop_type)dlsym(avahiLibHandle, "avahi_threaded_poll_stop"))) goto dlsym_err2;
+ if(!(avahi_threaded_poll_quit_ptr = (avahi_threaded_poll_quit_type)dlsym(avahiLibHandle, "avahi_threaded_poll_quit"))) goto dlsym_err2;
+ if(!(avahi_threaded_poll_lock_ptr = (avahi_threaded_poll_lock_type)dlsym(avahiLibHandle, "avahi_threaded_poll_lock"))) goto dlsym_err2;
+ if(!(avahi_threaded_poll_unlock_ptr = (avahi_threaded_poll_unlock_type)dlsym(avahiLibHandle, "avahi_threaded_poll_unlock"))) goto dlsym_err2;
+
+ goto dlsym_good;
+
+dlsym_err:
+ LOG(PHIDGET_LOG_WARNING, "dlsym failed with error: %s", dlerror());
+ LOG(PHIDGET_LOG_WARNING, "Assuming that zeroconf is not supported on this machine.");
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNSUPPORTED;
+
+ //Old avahi didn't have the thread functions
+dlsym_err2:
+ LOG(PHIDGET_LOG_WARNING, "dlsym failed with error: %s", dlerror());
+ LOG(PHIDGET_LOG_WARNING, "Avahi is too old, upgrade to at least version 0.6.4.");
+ LOG(PHIDGET_LOG_WARNING, "Zeroconf will not be used on this machine.");
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNSUPPORTED;
+
+dlsym_good:
+
+#endif
+
+ /* Allocate main loop object */
+ if (!(threaded_poll = avahi_threaded_poll_new_ptr())) {
+ LOG(PHIDGET_LOG_ERROR, "Failed to create threaded poll object.");
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNEXPECTED;
+ }
+
+ /* Allocate a new client */
+ client = avahi_client_new_ptr(avahi_threaded_poll_get_ptr(threaded_poll), 0, client_callback, NULL, &error);
+
+ /* Check wether creating the client object succeeded */
+ if (!client) {
+ LOG(PHIDGET_LOG_ERROR, "Failed to create client: %s", avahi_strerror_ptr(error));
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNEXPECTED;
+ }
+
+ //get version
+ avahiVersion = avahi_client_get_version_string_ptr(client);
+
+ /* Create the service browsers */
+ if (!(zeroconf_browse_ws_ref = avahi_service_browser_new_ptr(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_phidget_ws._tcp", NULL, 0, DNSServiceBrowse_ws_CallBack, client))) {
+ LOG(PHIDGET_LOG_ERROR, "Failed to create service browser: %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNEXPECTED;
+ }
+ if (!(zeroconf_browse_phidget_ref = avahi_service_browser_new_ptr(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_phidget._tcp", NULL, 0, DNSServiceBrowse_Phidget_CallBack, client))) {
+ LOG(PHIDGET_LOG_ERROR, "Failed to create service browser: %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNEXPECTED;
+ }
+ if (!(zeroconf_browse_sbc_ref = avahi_service_browser_new_ptr(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_phidget_sbc._tcp", NULL, 0, DNSServiceBrowse_SBC_CallBack, client))) {
+ LOG(PHIDGET_LOG_ERROR, "Failed to create service browser: %s", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNEXPECTED;
+ }
+
+ if(avahi_threaded_poll_start_ptr(threaded_poll))
+ {
+ LOG(PHIDGET_LOG_ERROR, "avahi_threaded_poll_start_ptr failed");
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNEXPECTED;
+ }
+ //Thread is started successfully
+ else
+ {
+ //There is a bug in at least Avahi 0.6.16 (Debian Etch default) where thread_running is not set, so quit doesn't work!!!???!?!?!?!?!?!
+ //This is fixed in 0.6.24
+ //So I'll set it myself here
+ if(strcmp(avahiVersion, "avahi 0.6.24") < 0)
+ {
+ LOG(PHIDGET_LOG_INFO, "Fixing thread_running bug in avahi < 0.6.24");
+ threaded_poll->thread_running = 1;
+ }
+ }
+
+ while(!Dns_sdInitialized)
+ {
+ usleep(10000);
+ timeout--;
+ if(!timeout)
+ {
+ UninitializeZeroconf1(PFALSE);
+ LOG(PHIDGET_LOG_ERROR, "InitializeZeroconf Seems bad... Dns_sdInitialized wasn't set to true.");
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_UNEXPECTED;
+ }
+ }
+
+ LOG(PHIDGET_LOG_INFO, "InitializeZeroconf Seems good... (%s)",avahiVersion);
+
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_OK;
+
+}
+
+
+static int UninitializeZeroconf1(int lock)
+{
+ int ret;
+ /* Cleanup things */
+ if(lock)
+ CThread_mutex_lock(&zeroconfInitLock);
+ if(Dns_sdInitialized)
+ {
+ if (threaded_poll)
+ {
+ if((ret = avahi_threaded_poll_stop_ptr(threaded_poll)) == -1)
+ LOG(PHIDGET_LOG_WARNING, "avahi_threaded_poll_stop failed",ret);
+ avahi_client_free_ptr(client);
+ avahi_threaded_poll_free_ptr(threaded_poll);
+ threaded_poll = NULL;
+ client = NULL;
+ }
+ }
+
+ Dns_sdInitialized = FALSE;
+ if(lock)
+ CThread_mutex_unlock(&zeroconfInitLock);
+ return EPHIDGET_OK;
+}
+
+int UninitializeZeroconf()
+{
+ return UninitializeZeroconf1(PTRUE);
+}
|