Home | History | Annotate | Download | only in avahi-compat-howl
      1 /***
      2   This file is part of avahi.
      3 
      4   avahi is free software; you can redistribute it and/or modify it
      5   under the terms of the GNU Lesser General Public License as
      6   published by the Free Software Foundation; either version 2.1 of the
      7   License, or (at your option) any later version.
      8 
      9   avahi is distributed in the hope that it will be useful, but WITHOUT
     10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
     12   Public License for more details.
     13 
     14   You should have received a copy of the GNU Lesser General Public
     15   License along with avahi; if not, write to the Free Software
     16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
     17   USA.
     18 ***/
     19 
     20 #ifdef HAVE_CONFIG_H
     21 #include <config.h>
     22 #endif
     23 
     24 #include <assert.h>
     25 
     26 #include <pthread.h>
     27 
     28 #include <avahi-common/strlst.h>
     29 #include "avahi-common/avahi-malloc.h"
     30 #include <avahi-common/domain.h>
     31 #include <avahi-common/simple-watch.h>
     32 #include <avahi-common/error.h>
     33 #include <avahi-common/llist.h>
     34 
     35 #include <avahi-client/client.h>
     36 #include <avahi-client/publish.h>
     37 #include <avahi-client/lookup.h>
     38 
     39 #include "howl.h"
     40 #include "warn.h"
     41 
     42 #define OID_MAX 50
     43 
     44 enum {
     45     COMMAND_POLL = 'p',
     46     COMMAND_QUIT = 'q',
     47     COMMAND_POLL_DONE = 'P',
     48     COMMAND_POLL_FAILED = 'F'
     49 };
     50 
     51 typedef enum {
     52     OID_UNUSED = 0,
     53     OID_SERVICE_BROWSER,
     54     OID_SERVICE_RESOLVER,
     55     OID_DOMAIN_BROWSER,
     56     OID_ENTRY_GROUP
     57 } oid_type;
     58 
     59 typedef struct service_data service_data;
     60 
     61 typedef struct oid_data {
     62     oid_type type;
     63     sw_opaque extra;
     64     sw_discovery discovery;
     65     void *object;
     66     sw_result (*reply)(void);
     67     service_data *service_data;
     68 } oid_data;
     69 
     70 
     71 struct service_data {
     72     char *name, *regtype, *domain, *host;
     73     uint16_t port;
     74     AvahiIfIndex interface;
     75     AvahiStringList *txt;
     76     AVAHI_LLIST_FIELDS(service_data, services);
     77 };
     78 
     79 struct _sw_discovery {
     80     int n_ref;
     81     AvahiSimplePoll *simple_poll;
     82     AvahiClient *client;
     83 
     84     oid_data oid_table[OID_MAX];
     85     sw_discovery_oid oid_index;
     86 
     87     int thread_fd, main_fd;
     88 
     89     pthread_t thread;
     90     int thread_running;
     91 
     92     pthread_mutex_t mutex, salt_mutex;
     93 
     94     AVAHI_LLIST_HEAD(service_data, services);
     95 };
     96 
     97 #define ASSERT_SUCCESS(r) { int __ret = (r); assert(__ret == 0); }
     98 
     99 #define OID_GET_INDEX(data) ((sw_discovery_oid) (((data) - ((data)->discovery->oid_table))))
    100 
    101 static sw_discovery discovery_ref(sw_discovery self);
    102 static void discovery_unref(sw_discovery self);
    103 
    104 static const char *add_trailing_dot(const char *s, char *buf, size_t buf_len) {
    105     if (!s)
    106         return NULL;
    107 
    108     if (*s == 0)
    109         return s;
    110 
    111     if (s[strlen(s)-1] == '.')
    112         return s;
    113 
    114     snprintf(buf, buf_len, "%s.", s);
    115     return buf;
    116 }
    117 
    118 static sw_result map_error(int error) {
    119     switch (error) {
    120         case AVAHI_OK:
    121             return SW_OKAY;
    122 
    123         case AVAHI_ERR_NO_MEMORY:
    124             return SW_E_MEM;
    125     }
    126 
    127     return SW_E_UNKNOWN;
    128 }
    129 
    130 static int read_command(int fd) {
    131     ssize_t r;
    132     char command;
    133 
    134     assert(fd >= 0);
    135 
    136     if ((r = read(fd, &command, 1)) != 1) {
    137         fprintf(stderr, __FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
    138         return -1;
    139     }
    140 
    141     return command;
    142 }
    143 
    144 static int write_command(int fd, char reply) {
    145     assert(fd >= 0);
    146 
    147     if (write(fd, &reply, 1) != 1) {
    148         fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno));
    149         return -1;
    150     }
    151 
    152     return 0;
    153 }
    154 
    155 static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) {
    156     sw_discovery self = userdata;
    157     int ret;
    158 
    159     assert(self);
    160 
    161     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
    162     ret = poll(ufds, nfds, timeout);
    163     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
    164 
    165     return ret;
    166 }
    167 
    168 static void * thread_func(void *data) {
    169     sw_discovery self = data;
    170     sigset_t mask;
    171 
    172     sigfillset(&mask);
    173     pthread_sigmask(SIG_BLOCK, &mask, NULL);
    174 
    175     self->thread = pthread_self();
    176     self->thread_running = 1;
    177 
    178     for (;;) {
    179         char command;
    180 
    181         if ((command = read_command(self->thread_fd)) < 0)
    182             break;
    183 
    184 /*         fprintf(stderr, "Command: %c\n", command); */
    185 
    186         switch (command) {
    187 
    188             case COMMAND_POLL: {
    189                 int ret;
    190 
    191                 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
    192 
    193                 for (;;) {
    194                     errno = 0;
    195 
    196                     if ((ret = avahi_simple_poll_run(self->simple_poll)) < 0) {
    197 
    198                         if (errno == EINTR)
    199                             continue;
    200 
    201                         fprintf(stderr, __FILE__": avahi_simple_poll_run() failed: %s\n", strerror(errno));
    202                     }
    203 
    204                     break;
    205                 }
    206 
    207                 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
    208 
    209                 if (write_command(self->thread_fd, ret < 0 ? COMMAND_POLL_FAILED : COMMAND_POLL_DONE) < 0)
    210                     break;
    211 
    212                 break;
    213             }
    214 
    215             case COMMAND_QUIT:
    216                 return NULL;
    217         }
    218 
    219     }
    220 
    221     return NULL;
    222 }
    223 
    224 static int oid_alloc(sw_discovery self, oid_type type) {
    225     sw_discovery_oid i;
    226     assert(self);
    227 
    228     for (i = 0; i < OID_MAX; i++) {
    229 
    230         while (self->oid_index >= OID_MAX)
    231             self->oid_index -= OID_MAX;
    232 
    233         if (self->oid_table[self->oid_index].type == OID_UNUSED) {
    234             self->oid_table[self->oid_index].type = type;
    235             self->oid_table[self->oid_index].discovery = self;
    236 
    237             assert(OID_GET_INDEX(&self->oid_table[self->oid_index]) == self->oid_index);
    238 
    239             return self->oid_index ++;
    240         }
    241 
    242         self->oid_index ++;
    243     }
    244 
    245     /* No free entry found */
    246 
    247     return (sw_discovery_oid) -1;
    248 }
    249 
    250 static void oid_release(sw_discovery self, sw_discovery_oid oid) {
    251     assert(self);
    252     assert(oid < OID_MAX);
    253 
    254     assert(self->oid_table[oid].type != OID_UNUSED);
    255 
    256     self->oid_table[oid].type = OID_UNUSED;
    257     self->oid_table[oid].discovery = NULL;
    258     self->oid_table[oid].reply = NULL;
    259     self->oid_table[oid].object = NULL;
    260     self->oid_table[oid].extra = NULL;
    261     self->oid_table[oid].service_data = NULL;
    262 }
    263 
    264 static oid_data* oid_get(sw_discovery self, sw_discovery_oid oid) {
    265     assert(self);
    266 
    267     if (oid >= OID_MAX)
    268         return NULL;
    269 
    270     if (self->oid_table[oid].type == OID_UNUSED)
    271         return NULL;
    272 
    273     return &self->oid_table[oid];
    274 }
    275 
    276 static service_data* service_data_new(sw_discovery self) {
    277     service_data *sdata;
    278 
    279     assert(self);
    280 
    281     if (!(sdata = avahi_new0(service_data, 1)))
    282         return NULL;
    283 
    284     AVAHI_LLIST_PREPEND(service_data, services, self->services, sdata);
    285 
    286     return sdata;
    287 
    288 }
    289 
    290 static void service_data_free(sw_discovery self, service_data* sdata) {
    291     assert(self);
    292     assert(sdata);
    293 
    294     AVAHI_LLIST_REMOVE(service_data, services, self->services, sdata);
    295 
    296     avahi_free(sdata->name);
    297     avahi_free(sdata->regtype);
    298     avahi_free(sdata->domain);
    299     avahi_free(sdata->host);
    300     avahi_string_list_free(sdata->txt);
    301     avahi_free(sdata);
    302 }
    303 
    304 static void reg_client_callback(oid_data *data, AvahiClientState state);
    305 
    306 static void client_callback(AvahiClient *s, AvahiClientState state, void* userdata) {
    307     sw_discovery self = userdata;
    308     sw_discovery_oid oid;
    309 
    310     assert(s);
    311     assert(self);
    312 
    313     discovery_ref(self);
    314 
    315     for (oid = 0; oid < OID_MAX; oid++) {
    316 
    317         switch (self->oid_table[oid].type) {
    318 
    319             case OID_ENTRY_GROUP:
    320                 reg_client_callback(&self->oid_table[oid], state);
    321                 break;
    322 
    323             case OID_DOMAIN_BROWSER:
    324             case OID_SERVICE_BROWSER:
    325                 ((sw_discovery_browse_reply) self->oid_table[oid].reply)(self, oid, SW_DISCOVERY_BROWSE_INVALID, 0, NULL, NULL, NULL, self->oid_table[oid].extra);
    326                 break;
    327 
    328             case OID_SERVICE_RESOLVER:
    329             case OID_UNUSED:
    330                 ;
    331         }
    332     }
    333 
    334     discovery_unref(self);
    335 }
    336 
    337 sw_result sw_discovery_init(sw_discovery * self) {
    338     int fd[2] = { -1, -1};
    339     sw_result result = SW_E_UNKNOWN;
    340     pthread_mutexattr_t mutex_attr;
    341     int error;
    342 
    343     assert(self);
    344 
    345     AVAHI_WARN_LINKAGE;
    346 
    347     *self = NULL;
    348 
    349     if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0)
    350         goto fail;
    351 
    352     if (!(*self = avahi_new(struct _sw_discovery, 1))) {
    353         result = SW_E_MEM;
    354         goto fail;
    355     }
    356 
    357     (*self)->n_ref = 1;
    358     (*self)->thread_fd = fd[0];
    359     (*self)->main_fd = fd[1];
    360 
    361     (*self)->client = NULL;
    362     (*self)->simple_poll = NULL;
    363 
    364     memset((*self)->oid_table, 0, sizeof((*self)->oid_table));
    365     (*self)->oid_index = 0;
    366 
    367     (*self)->thread_running = 0;
    368 
    369     AVAHI_LLIST_HEAD_INIT(service_info, (*self)->services);
    370 
    371     ASSERT_SUCCESS(pthread_mutexattr_init(&mutex_attr));
    372     pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
    373     ASSERT_SUCCESS(pthread_mutex_init(&(*self)->mutex, &mutex_attr));
    374     ASSERT_SUCCESS(pthread_mutex_init(&(*self)->salt_mutex, &mutex_attr));
    375 
    376     if (!((*self)->simple_poll = avahi_simple_poll_new()))
    377         goto fail;
    378 
    379     avahi_simple_poll_set_func((*self)->simple_poll, poll_func, *self);
    380 
    381     if (!((*self)->client = avahi_client_new(avahi_simple_poll_get((*self)->simple_poll), 0, client_callback, *self, &error))) {
    382         result = map_error(error);
    383         goto fail;
    384     }
    385 
    386     /* Start simple poll */
    387     if (avahi_simple_poll_prepare((*self)->simple_poll, -1) < 0)
    388         goto fail;
    389 
    390     /* Queue an initial POLL command for the thread */
    391     if (write_command((*self)->main_fd, COMMAND_POLL) < 0)
    392         goto fail;
    393 
    394     if (pthread_create(&(*self)->thread, NULL, thread_func, *self) != 0)
    395         goto fail;
    396 
    397     (*self)->thread_running = 1;
    398 
    399     return SW_OKAY;
    400 
    401 fail:
    402 
    403     if (*self)
    404         sw_discovery_fina(*self);
    405 
    406     return result;
    407 }
    408 
    409 static int stop_thread(sw_discovery self) {
    410     assert(self);
    411 
    412     if (!self->thread_running)
    413         return 0;
    414 
    415     if (write_command(self->main_fd, COMMAND_QUIT) < 0)
    416         return -1;
    417 
    418     avahi_simple_poll_wakeup(self->simple_poll);
    419 
    420     ASSERT_SUCCESS(pthread_join(self->thread, NULL));
    421     self->thread_running = 0;
    422     return 0;
    423 }
    424 
    425 static sw_discovery discovery_ref(sw_discovery self) {
    426     assert(self);
    427     assert(self->n_ref >= 1);
    428 
    429     self->n_ref++;
    430 
    431     return self;
    432 }
    433 
    434 static void discovery_unref(sw_discovery self) {
    435     assert(self);
    436     assert(self->n_ref >= 1);
    437 
    438     if (--self->n_ref > 0)
    439         return;
    440 
    441     stop_thread(self);
    442 
    443     if (self->client)
    444         avahi_client_free(self->client);
    445 
    446     if (self->simple_poll)
    447         avahi_simple_poll_free(self->simple_poll);
    448 
    449     if (self->thread_fd >= 0)
    450         close(self->thread_fd);
    451 
    452     if (self->main_fd >= 0)
    453         close(self->main_fd);
    454 
    455     ASSERT_SUCCESS(pthread_mutex_destroy(&self->mutex));
    456     ASSERT_SUCCESS(pthread_mutex_destroy(&self->salt_mutex));
    457 
    458     while (self->services)
    459         service_data_free(self, self->services);
    460 
    461     avahi_free(self);
    462 }
    463 
    464 sw_result sw_discovery_fina(sw_discovery self) {
    465     assert(self);
    466 
    467     AVAHI_WARN_LINKAGE;
    468 
    469     stop_thread(self);
    470     discovery_unref(self);
    471 
    472     return SW_OKAY;
    473 }
    474 
    475 sw_result sw_discovery_run(sw_discovery self) {
    476     assert(self);
    477 
    478     AVAHI_WARN_LINKAGE;
    479 
    480     return sw_salt_run((sw_salt) self);
    481 }
    482 
    483 sw_result sw_discovery_stop_run(sw_discovery self) {
    484     assert(self);
    485 
    486     AVAHI_WARN_LINKAGE;
    487 
    488     return sw_salt_stop_run((sw_salt) self);
    489 }
    490 
    491 int sw_discovery_socket(sw_discovery self) {
    492     assert(self);
    493 
    494     AVAHI_WARN_LINKAGE;
    495 
    496     return self->main_fd;
    497 }
    498 
    499 sw_result sw_discovery_read_socket(sw_discovery self) {
    500     sw_result result = SW_E_UNKNOWN;
    501 
    502     assert(self);
    503 
    504     discovery_ref(self);
    505 
    506     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
    507 
    508     /* Cleanup notification socket */
    509     if (read_command(self->main_fd) != COMMAND_POLL_DONE)
    510         goto finish;
    511 
    512     if (avahi_simple_poll_dispatch(self->simple_poll) < 0)
    513         goto finish;
    514 
    515     if (self->n_ref > 1) /* Perhaps we should die */
    516 
    517         /* Dispatch events */
    518         if (avahi_simple_poll_prepare(self->simple_poll, -1) < 0)
    519             goto finish;
    520 
    521     if (self->n_ref > 1)
    522 
    523         /* Request the poll */
    524         if (write_command(self->main_fd, COMMAND_POLL) < 0)
    525             goto finish;
    526 
    527     result = SW_OKAY;
    528 
    529 finish:
    530 
    531     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
    532 
    533     discovery_unref(self);
    534 
    535     return result;
    536 }
    537 
    538 sw_result sw_discovery_salt(sw_discovery self, sw_salt *salt) {
    539     assert(self);
    540     assert(salt);
    541 
    542     AVAHI_WARN_LINKAGE;
    543 
    544     *salt = (sw_salt) self;
    545 
    546     return SW_OKAY;
    547 }
    548 
    549 sw_result sw_salt_step(sw_salt self, sw_uint32 * msec) {
    550     struct pollfd p;
    551     int r;
    552     sw_result result;
    553 
    554     AVAHI_WARN_LINKAGE;
    555 
    556     if (!((sw_discovery) self)->thread_running)
    557         return SW_E_UNKNOWN;
    558 
    559     memset(&p, 0, sizeof(p));
    560     p.fd = ((sw_discovery) self)->main_fd;
    561     p.events = POLLIN;
    562 
    563     if ((r = poll(&p, 1, msec ? (int) *msec : -1)) < 0) {
    564 
    565         /* Don't treat EINTR as error */
    566         if (errno == EINTR)
    567             return SW_OKAY;
    568 
    569         return SW_E_UNKNOWN;
    570 
    571     } else if (r == 0) {
    572 
    573         /* Timeoout */
    574         return SW_OKAY;
    575 
    576     } else {
    577         /* Success */
    578 
    579         if (p.revents != POLLIN)
    580             return SW_E_UNKNOWN;
    581 
    582         if ((result = sw_discovery_read_socket((sw_discovery) self)) != SW_OKAY)
    583             return result;
    584     }
    585 
    586     return SW_OKAY;
    587 }
    588 
    589 sw_result sw_salt_run(sw_salt self) {
    590     sw_result ret;
    591 
    592     AVAHI_WARN_LINKAGE;
    593 
    594     assert(self);
    595 
    596     for (;;)
    597         if ((ret = sw_salt_step(self, NULL)) != SW_OKAY)
    598             return ret;
    599 }
    600 
    601 sw_result sw_salt_stop_run(sw_salt self) {
    602     AVAHI_WARN_LINKAGE;
    603 
    604     assert(self);
    605 
    606     if (stop_thread((sw_discovery) self) < 0)
    607         return SW_E_UNKNOWN;
    608 
    609     return SW_OKAY;
    610 }
    611 
    612 sw_result sw_salt_lock(sw_salt self) {
    613     AVAHI_WARN_LINKAGE;
    614 
    615     assert(self);
    616     ASSERT_SUCCESS(pthread_mutex_lock(&((sw_discovery) self)->salt_mutex));
    617 
    618     return SW_OKAY;
    619 }
    620 
    621 sw_result sw_salt_unlock(sw_salt self) {
    622     assert(self);
    623 
    624     AVAHI_WARN_LINKAGE;
    625 
    626     ASSERT_SUCCESS(pthread_mutex_unlock(&((sw_discovery) self)->salt_mutex));
    627 
    628     return SW_OKAY;
    629 }
    630 
    631 static void reg_report_status(oid_data *data, sw_discovery_publish_status status) {
    632     sw_discovery_publish_reply reply;
    633 
    634     assert(data);
    635 
    636     reply = (sw_discovery_publish_reply) data->reply;
    637 
    638     reply(data->discovery,
    639           OID_GET_INDEX(data),
    640           status,
    641           data->extra);
    642 }
    643 
    644 static int reg_create_service(oid_data *data) {
    645     int ret;
    646     const char *real_type;
    647 
    648     assert(data);
    649 
    650     real_type = avahi_get_type_from_subtype(data->service_data->regtype);
    651 
    652     if ((ret = avahi_entry_group_add_service_strlst(
    653              data->object,
    654              data->service_data->interface,
    655              AVAHI_PROTO_INET,
    656              0,
    657              data->service_data->name,
    658              real_type ? real_type : data->service_data->regtype,
    659              data->service_data->domain,
    660              data->service_data->host,
    661              data->service_data->port,
    662              data->service_data->txt)) < 0)
    663         return ret;
    664 
    665     if (real_type) {
    666         /* Create a subtype entry */
    667 
    668         if (avahi_entry_group_add_service_subtype(
    669                 data->object,
    670                 data->service_data->interface,
    671                 AVAHI_PROTO_INET,
    672                 0,
    673                 data->service_data->name,
    674                 real_type,
    675                 data->service_data->domain,
    676                 data->service_data->regtype) < 0)
    677             return ret;
    678 
    679     }
    680 
    681     if ((ret = avahi_entry_group_commit(data->object)) < 0)
    682         return ret;
    683 
    684     return 0;
    685 }
    686 
    687 static void reg_client_callback(oid_data *data, AvahiClientState state) {
    688     assert(data);
    689 
    690     /* We've not been setup completely */
    691     if (!data->object)
    692         return;
    693 
    694     switch (state) {
    695         case AVAHI_CLIENT_FAILURE:
    696             reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID);
    697             break;
    698 
    699         case AVAHI_CLIENT_S_RUNNING: {
    700             int ret;
    701 
    702             /* Register the service */
    703             if ((ret = reg_create_service(data)) < 0) {
    704                 reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID);
    705                 return;
    706             }
    707 
    708             break;
    709         }
    710 
    711         case AVAHI_CLIENT_S_COLLISION:
    712         case AVAHI_CLIENT_S_REGISTERING:
    713 
    714             /* Remove our entry */
    715             avahi_entry_group_reset(data->object);
    716             break;
    717 
    718         case AVAHI_CLIENT_CONNECTING:
    719             /* Ignore */
    720             break;
    721     }
    722 
    723 }
    724 
    725 static void reg_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
    726     oid_data *data = userdata;
    727 
    728     assert(g);
    729     assert(data);
    730 
    731     switch (state) {
    732         case AVAHI_ENTRY_GROUP_ESTABLISHED:
    733 
    734             reg_report_status(data, SW_DISCOVERY_PUBLISH_STARTED);
    735             break;
    736 
    737         case AVAHI_ENTRY_GROUP_COLLISION:
    738 
    739             reg_report_status(data, SW_DISCOVERY_PUBLISH_NAME_COLLISION);
    740             break;
    741 
    742         case AVAHI_ENTRY_GROUP_REGISTERING:
    743         case AVAHI_ENTRY_GROUP_UNCOMMITED:
    744             /* Ignore */
    745             break;
    746 
    747         case AVAHI_ENTRY_GROUP_FAILURE:
    748             reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID);
    749             break;
    750 
    751     }
    752 }
    753 
    754 sw_result sw_discovery_publish(
    755     sw_discovery self,
    756     sw_uint32 interface_index,
    757     sw_const_string name,
    758     sw_const_string type,
    759     sw_const_string domain,
    760     sw_const_string host,
    761     sw_port port,
    762     sw_octets text_record,
    763     sw_uint32 text_record_len,
    764     sw_discovery_publish_reply reply,
    765     sw_opaque extra,
    766     sw_discovery_oid * oid) {
    767 
    768     oid_data *data;
    769     sw_result result = SW_E_UNKNOWN;
    770     service_data *sdata;
    771     AvahiStringList *txt = NULL;
    772 
    773     assert(self);
    774     assert(name);
    775     assert(type);
    776     assert(reply);
    777     assert(oid);
    778 
    779     AVAHI_WARN_LINKAGE;
    780 
    781     if (text_record && text_record_len > 0)
    782         if (avahi_string_list_parse(text_record, text_record_len, &txt) < 0)
    783             return SW_E_UNKNOWN;
    784 
    785     if ((*oid = oid_alloc(self, OID_ENTRY_GROUP)) == (sw_discovery_oid) -1) {
    786         avahi_string_list_free(txt);
    787         return SW_E_UNKNOWN;
    788     }
    789 
    790     if (!(sdata = service_data_new(self))) {
    791         avahi_string_list_free(txt);
    792         oid_release(self, *oid);
    793         return SW_E_MEM;
    794     }
    795 
    796     data = oid_get(self, *oid);
    797     assert(data);
    798     data->reply = (sw_result (*)(void)) reply;
    799     data->extra = extra;
    800     data->service_data = sdata;
    801 
    802     sdata->interface = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
    803     sdata->name = avahi_strdup(name);
    804     sdata->regtype = type ? avahi_normalize_name_strdup(type) : NULL;
    805     sdata->domain = domain ? avahi_normalize_name_strdup(domain) : NULL;
    806     sdata->host = host ? avahi_normalize_name_strdup(host) : NULL;
    807     sdata->port = port;
    808     sdata->txt = txt;
    809 
    810     /* Some OOM checking would be cool here */
    811 
    812     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
    813 
    814     if (!(data->object = avahi_entry_group_new(self->client, reg_entry_group_callback, data))) {
    815         result = map_error(avahi_client_errno(self->client));
    816         goto finish;
    817     }
    818 
    819     if (avahi_client_get_state(self->client) == AVAHI_CLIENT_S_RUNNING) {
    820         int error;
    821 
    822         if ((error = reg_create_service(data)) < 0) {
    823             result = map_error(error);
    824             goto finish;
    825         }
    826     }
    827 
    828     result = SW_OKAY;
    829 
    830 finish:
    831 
    832     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
    833 
    834     if (result != SW_OKAY)
    835         if (*oid != (sw_discovery_oid) -1)
    836             sw_discovery_cancel(self, *oid);
    837 
    838     return result;
    839 }
    840 
    841 static void domain_browser_callback(
    842     AvahiDomainBrowser *b,
    843     AvahiIfIndex interface,
    844     AVAHI_GCC_UNUSED AvahiProtocol protocol,
    845     AvahiBrowserEvent event,
    846     const char *domain,
    847     AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
    848     void *userdata) {
    849 
    850     oid_data* data = userdata;
    851     sw_discovery_browse_reply reply;
    852     static char domain_fixed[AVAHI_DOMAIN_NAME_MAX];
    853 
    854     assert(b);
    855     assert(data);
    856 
    857     reply = (sw_discovery_browse_reply) data->reply;
    858 
    859     domain  = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
    860 
    861     switch (event) {
    862         case AVAHI_BROWSER_NEW:
    863             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_DOMAIN, interface, NULL, NULL, domain, data->extra);
    864             break;
    865 
    866         case AVAHI_BROWSER_REMOVE:
    867             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_DOMAIN, interface, NULL, NULL, domain, data->extra);
    868             break;
    869 
    870         case AVAHI_BROWSER_FAILURE:
    871             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, NULL, NULL, domain, data->extra);
    872             break;
    873 
    874         case AVAHI_BROWSER_CACHE_EXHAUSTED:
    875         case AVAHI_BROWSER_ALL_FOR_NOW:
    876             break;
    877     }
    878 }
    879 
    880 sw_result sw_discovery_browse_domains(
    881     sw_discovery self,
    882     sw_uint32 interface_index,
    883     sw_discovery_browse_reply reply,
    884     sw_opaque extra,
    885     sw_discovery_oid * oid) {
    886 
    887     oid_data *data;
    888     AvahiIfIndex ifindex;
    889     sw_result result = SW_E_UNKNOWN;
    890 
    891     assert(self);
    892     assert(reply);
    893     assert(oid);
    894 
    895     AVAHI_WARN_LINKAGE;
    896 
    897     if ((*oid = oid_alloc(self, OID_DOMAIN_BROWSER)) == (sw_discovery_oid) -1)
    898         return SW_E_UNKNOWN;
    899 
    900     data = oid_get(self, *oid);
    901     assert(data);
    902     data->reply = (sw_result (*)(void)) reply;
    903     data->extra = extra;
    904 
    905     ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
    906 
    907     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
    908 
    909     if (!(data->object = avahi_domain_browser_new(self->client, ifindex, AVAHI_PROTO_INET, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, domain_browser_callback, data))) {
    910         result = map_error(avahi_client_errno(self->client));
    911         goto finish;
    912     }
    913 
    914     result = SW_OKAY;
    915 
    916 finish:
    917 
    918     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
    919 
    920     if (result != SW_OKAY)
    921         if (*oid != (sw_discovery_oid) -1)
    922             sw_discovery_cancel(self, *oid);
    923 
    924     return result;
    925 }
    926 
    927 static void service_resolver_callback(
    928     AvahiServiceResolver *r,
    929     AvahiIfIndex interface,
    930     AVAHI_GCC_UNUSED AvahiProtocol protocol,
    931     AvahiResolverEvent event,
    932     const char *name,
    933     const char *type,
    934     const char *domain,
    935     const char *host_name,
    936     const AvahiAddress *a,
    937     uint16_t port,
    938     AvahiStringList *txt,
    939     AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
    940     void *userdata) {
    941 
    942     oid_data* data = userdata;
    943     sw_discovery_resolve_reply reply;
    944 
    945     assert(r);
    946     assert(data);
    947 
    948     reply = (sw_discovery_resolve_reply) data->reply;
    949 
    950     switch (event) {
    951         case AVAHI_RESOLVER_FOUND: {
    952 
    953             char host_name_fixed[AVAHI_DOMAIN_NAME_MAX];
    954             uint8_t *p = NULL;
    955             size_t l = 0;
    956             sw_ipv4_address addr;
    957 
    958             sw_ipv4_address_init_from_saddr(&addr, a->data.ipv4.address);
    959 
    960             host_name = add_trailing_dot(host_name, host_name_fixed, sizeof(host_name_fixed));
    961 
    962             if ((p = avahi_new0(uint8_t, (l = avahi_string_list_serialize(txt, NULL, 0))+1)))
    963                 avahi_string_list_serialize(txt, p, l);
    964 
    965             reply(data->discovery, OID_GET_INDEX(data), interface, name, type, domain, addr, port, p, l, data->extra);
    966 
    967             avahi_free(p);
    968             break;
    969         }
    970 
    971         case AVAHI_RESOLVER_FAILURE:
    972 
    973             /* Apparently there is no way in HOWL to inform about failed resolvings ... */
    974 
    975             avahi_warn("A service failed to resolve in the HOWL compatiblity layer of Avahi which is used by '%s'. "
    976                        "Since the HOWL API doesn't offer any means to inform the application about this, we have to ignore the failure. "
    977                        "Please fix your application to use the native API of Avahi!",
    978                        avahi_exe_name());
    979 
    980             break;
    981     }
    982 }
    983 
    984 sw_result sw_discovery_resolve(
    985     sw_discovery self,
    986     sw_uint32 interface_index,
    987     sw_const_string name,
    988     sw_const_string type,
    989     sw_const_string domain,
    990     sw_discovery_resolve_reply reply,
    991     sw_opaque extra,
    992     sw_discovery_oid * oid) {
    993 
    994     oid_data *data;
    995     AvahiIfIndex ifindex;
    996     sw_result result = SW_E_UNKNOWN;
    997 
    998     assert(self);
    999     assert(name);
   1000     assert(type);
   1001     assert(reply);
   1002     assert(oid);
   1003 
   1004     AVAHI_WARN_LINKAGE;
   1005 
   1006     if ((*oid = oid_alloc(self, OID_SERVICE_RESOLVER)) == (sw_discovery_oid) -1)
   1007         return SW_E_UNKNOWN;
   1008 
   1009     data = oid_get(self, *oid);
   1010     assert(data);
   1011     data->reply = (sw_result (*)(void)) reply;
   1012     data->extra = extra;
   1013 
   1014     ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
   1015 
   1016     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
   1017 
   1018     if (!(data->object = avahi_service_resolver_new(self->client, ifindex, AVAHI_PROTO_INET, name, type, domain, AVAHI_PROTO_INET, 0, service_resolver_callback, data))) {
   1019         result = map_error(avahi_client_errno(self->client));
   1020         goto finish;
   1021     }
   1022 
   1023     result = SW_OKAY;
   1024 
   1025 finish:
   1026 
   1027     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
   1028 
   1029     if (result != SW_OKAY)
   1030         if (*oid != (sw_discovery_oid) -1)
   1031             sw_discovery_cancel(self, *oid);
   1032 
   1033     return result;
   1034 }
   1035 
   1036 static void service_browser_callback(
   1037     AvahiServiceBrowser *b,
   1038     AvahiIfIndex interface,
   1039     AVAHI_GCC_UNUSED AvahiProtocol protocol,
   1040     AvahiBrowserEvent event,
   1041     const char *name,
   1042     const char *type,
   1043     const char *domain,
   1044     AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
   1045     void *userdata) {
   1046 
   1047     oid_data* data = userdata;
   1048     char type_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX];
   1049     sw_discovery_browse_reply reply;
   1050 
   1051     assert(b);
   1052     assert(data);
   1053 
   1054     reply = (sw_discovery_browse_reply) data->reply;
   1055 
   1056     type = add_trailing_dot(type, type_fixed, sizeof(type_fixed));
   1057     domain = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
   1058 
   1059     switch (event) {
   1060         case AVAHI_BROWSER_NEW:
   1061             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_SERVICE, interface, name, type, domain, data->extra);
   1062             break;
   1063 
   1064         case AVAHI_BROWSER_REMOVE:
   1065             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_SERVICE, interface, name, type, domain, data->extra);
   1066             break;
   1067 
   1068         case AVAHI_BROWSER_FAILURE:
   1069             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, name, type, domain, data->extra);
   1070             break;
   1071 
   1072         case AVAHI_BROWSER_CACHE_EXHAUSTED:
   1073         case AVAHI_BROWSER_ALL_FOR_NOW:
   1074             break;
   1075     }
   1076 }
   1077 
   1078 sw_result sw_discovery_browse(
   1079     sw_discovery self,
   1080     sw_uint32 interface_index,
   1081     sw_const_string type,
   1082     sw_const_string domain,
   1083     sw_discovery_browse_reply reply,
   1084     sw_opaque extra,
   1085     sw_discovery_oid * oid) {
   1086 
   1087     oid_data *data;
   1088     AvahiIfIndex ifindex;
   1089     sw_result result = SW_E_UNKNOWN;
   1090 
   1091     assert(self);
   1092     assert(type);
   1093     assert(reply);
   1094     assert(oid);
   1095 
   1096     AVAHI_WARN_LINKAGE;
   1097 
   1098     if ((*oid = oid_alloc(self, OID_SERVICE_BROWSER)) == (sw_discovery_oid) -1)
   1099         return SW_E_UNKNOWN;
   1100 
   1101     data = oid_get(self, *oid);
   1102     assert(data);
   1103     data->reply = (sw_result (*)(void)) reply;
   1104     data->extra = extra;
   1105 
   1106     ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
   1107 
   1108     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
   1109 
   1110     if (!(data->object = avahi_service_browser_new(self->client, ifindex, AVAHI_PROTO_INET, type, domain, 0, service_browser_callback, data))) {
   1111         result = map_error(avahi_client_errno(self->client));
   1112         goto finish;
   1113     }
   1114 
   1115     result = SW_OKAY;
   1116 
   1117 finish:
   1118 
   1119     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
   1120 
   1121     if (result != SW_OKAY)
   1122         if (*oid != (sw_discovery_oid) -1)
   1123             sw_discovery_cancel(self, *oid);
   1124 
   1125     return result;
   1126 }
   1127 
   1128 sw_result sw_discovery_cancel(sw_discovery self, sw_discovery_oid oid) {
   1129     oid_data *data;
   1130     assert(self);
   1131 
   1132     AVAHI_WARN_LINKAGE;
   1133 
   1134     if (!(data = oid_get(self, oid)))
   1135         return SW_E_UNKNOWN;
   1136 
   1137     if (data->object) {
   1138         switch (data->type) {
   1139             case OID_SERVICE_BROWSER:
   1140                 avahi_service_browser_free(data->object);
   1141                 break;
   1142 
   1143             case OID_SERVICE_RESOLVER:
   1144                 avahi_service_resolver_free(data->object);
   1145                 break;
   1146 
   1147             case OID_DOMAIN_BROWSER:
   1148                 avahi_domain_browser_free(data->object);
   1149                 break;
   1150 
   1151             case OID_ENTRY_GROUP:
   1152                 avahi_entry_group_free(data->object);
   1153                 break;
   1154 
   1155             case OID_UNUSED:
   1156             ;
   1157         }
   1158     }
   1159 
   1160     if (data->service_data) {
   1161         assert(data->type == OID_ENTRY_GROUP);
   1162         service_data_free(self, data->service_data);
   1163     }
   1164 
   1165     oid_release(self, oid);
   1166 
   1167     return SW_OKAY;
   1168 }
   1169 
   1170 sw_result sw_discovery_init_with_flags(
   1171     sw_discovery * self,
   1172     sw_discovery_init_flags flags) {
   1173 
   1174     assert(self);
   1175 
   1176     AVAHI_WARN_LINKAGE;
   1177 
   1178     if (flags != SW_DISCOVERY_USE_SHARED_SERVICE)
   1179         return SW_E_NO_IMPL;
   1180 
   1181     return sw_discovery_init(self);
   1182 }
   1183