Home | History | Annotate | Download | only in avahi-utils
      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 <stdlib.h>
     25 #include <stdio.h>
     26 #include <getopt.h>
     27 #include <assert.h>
     28 #include <string.h>
     29 #include <sys/types.h>
     30 #include <errno.h>
     31 #include <locale.h>
     32 
     33 #include <avahi-common/simple-watch.h>
     34 #include <avahi-common/error.h>
     35 #include "avahi-common/avahi-malloc.h"
     36 #include <avahi-common/alternative.h>
     37 #include <avahi-common/i18n.h>
     38 #include <avahi-client/client.h>
     39 #include <avahi-client/publish.h>
     40 
     41 #include "sigint.h"
     42 
     43 typedef enum {
     44     COMMAND_UNSPEC,
     45     COMMAND_HELP,
     46     COMMAND_VERSION,
     47     COMMAND_PUBLISH_SERVICE,
     48     COMMAND_PUBLISH_ADDRESS
     49 } Command;
     50 
     51 typedef struct Config {
     52     int verbose, no_fail, no_reverse;
     53     Command command;
     54     char *name, *stype, *domain, *host;
     55     uint16_t port;
     56     AvahiStringList *txt, *subtypes;
     57     AvahiAddress address;
     58 } Config;
     59 
     60 static AvahiSimplePoll *simple_poll = NULL;
     61 static AvahiClient *client = NULL;
     62 static AvahiEntryGroup *entry_group = NULL;
     63 
     64 static int register_stuff(Config *config);
     65 
     66 static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
     67     Config *config = userdata;
     68 
     69     assert(g);
     70     assert(config);
     71 
     72     switch (state) {
     73 
     74         case AVAHI_ENTRY_GROUP_ESTABLISHED:
     75 
     76             fprintf(stderr, _("Established under name '%s'\n"), config->name);
     77             break;
     78 
     79         case AVAHI_ENTRY_GROUP_FAILURE:
     80 
     81             fprintf(stderr, _("Failed to register: %s\n"), avahi_strerror(avahi_client_errno(client)));
     82             break;
     83 
     84         case AVAHI_ENTRY_GROUP_COLLISION: {
     85             char *n;
     86 
     87             if (config->command == COMMAND_PUBLISH_SERVICE)
     88                 n = avahi_alternative_service_name(config->name);
     89             else {
     90                 assert(config->command == COMMAND_PUBLISH_ADDRESS);
     91                 n = avahi_alternative_host_name(config->name);
     92             }
     93 
     94             fprintf(stderr, _("Name collision, picking new name '%s'.\n"), n);
     95             avahi_free(config->name);
     96             config->name = n;
     97 
     98             register_stuff(config);
     99 
    100             break;
    101         }
    102 
    103         case AVAHI_ENTRY_GROUP_UNCOMMITED:
    104         case AVAHI_ENTRY_GROUP_REGISTERING:
    105             ;
    106     }
    107 }
    108 
    109 static int register_stuff(Config *config) {
    110     assert(config);
    111 
    112     if (!entry_group) {
    113         if (!(entry_group = avahi_entry_group_new(client, entry_group_callback, config))) {
    114             fprintf(stderr, _("Failed to create entry group: %s\n"), avahi_strerror(avahi_client_errno(client)));
    115             return -1;
    116         }
    117     }
    118 
    119     assert(avahi_entry_group_is_empty(entry_group));
    120 
    121     if (config->command == COMMAND_PUBLISH_ADDRESS) {
    122 
    123         if (avahi_entry_group_add_address(entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, config->no_reverse ? AVAHI_PUBLISH_NO_REVERSE : 0, config->name, &config->address) < 0) {
    124             fprintf(stderr, _("Failed to add address: %s\n"), avahi_strerror(avahi_client_errno(client)));
    125             return -1;
    126         }
    127 
    128     } else {
    129         AvahiStringList *i;
    130 
    131         assert(config->command == COMMAND_PUBLISH_SERVICE);
    132 
    133         if (avahi_entry_group_add_service_strlst(entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, config->name, config->stype, config->domain, config->host, config->port, config->txt) < 0) {
    134             fprintf(stderr, _("Failed to add service: %s\n"), avahi_strerror(avahi_client_errno(client)));
    135             return -1;
    136         }
    137 
    138         for (i = config->subtypes; i; i = i->next)
    139             if (avahi_entry_group_add_service_subtype(entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, config->name, config->stype, config->domain, (char*) i->text) < 0) {
    140                 fprintf(stderr, _("Failed to add subtype '%s': %s\n"), i->text, avahi_strerror(avahi_client_errno(client)));
    141                 return -1;
    142             }
    143     }
    144 
    145     avahi_entry_group_commit(entry_group);
    146 
    147     return 0;
    148 }
    149 
    150 static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
    151     Config *config = userdata;
    152 
    153     client = c;
    154 
    155     switch (state) {
    156         case AVAHI_CLIENT_FAILURE:
    157 
    158             if (config->no_fail && avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
    159                 int error;
    160 
    161                 /* We have been disconnected, so let reconnect */
    162 
    163                 fprintf(stderr, _("Disconnected, reconnecting ...\n"));
    164 
    165                 avahi_client_free(client);
    166                 client = NULL;
    167                 entry_group = NULL;
    168 
    169                 if (!(client = avahi_client_new(avahi_simple_poll_get(simple_poll), AVAHI_CLIENT_NO_FAIL, client_callback, config, &error))) {
    170                     fprintf(stderr, _("Failed to create client object: %s\n"), avahi_strerror(error));
    171                     avahi_simple_poll_quit(simple_poll);
    172                 }
    173 
    174             } else {
    175                 fprintf(stderr, _("Client failure, exiting: %s\n"), avahi_strerror(avahi_client_errno(c)));
    176                 avahi_simple_poll_quit(simple_poll);
    177             }
    178 
    179             break;
    180 
    181         case AVAHI_CLIENT_S_RUNNING:
    182 
    183             if (register_stuff(config) < 0)
    184                 avahi_simple_poll_quit(simple_poll);
    185 
    186             break;
    187 
    188         case AVAHI_CLIENT_S_COLLISION:
    189 
    190             if (config->verbose)
    191                 fprintf(stderr, _("Host name conflict\n"));
    192 
    193             /* Fall through */
    194 
    195         case AVAHI_CLIENT_S_REGISTERING:
    196 
    197             if (entry_group) {
    198                 avahi_entry_group_free(entry_group);
    199                 entry_group = NULL;
    200             }
    201             break;
    202 
    203         case AVAHI_CLIENT_CONNECTING:
    204 
    205             if (config->verbose)
    206                 fprintf(stderr, _("Waiting for daemon ...\n"));
    207 
    208             break;
    209 
    210             ;
    211     }
    212 }
    213 
    214 static void help(FILE *f, const char *argv0) {
    215     fprintf(f,
    216             _("%s [options] %s <name> <type> <port> [<txt ...>]\n"
    217               "%s [options] %s <host-name> <address>\n\n"
    218               "    -h --help            Show this help\n"
    219               "    -V --version         Show version\n"
    220               "    -s --service         Publish service\n"
    221               "    -a --address         Publish address\n"
    222               "    -v --verbose         Enable verbose mode\n"
    223               "    -d --domain=DOMAIN   Domain to publish service in\n"
    224               "    -H --host=DOMAIN     Host where service resides\n"
    225               "       --subtype=SUBTYPE An additional subtype to register this service with\n"
    226               "    -R --no-reverse      Do not publish reverse entry with address\n"
    227               "    -f --no-fail         Don't fail if the daemon is not available\n"),
    228               argv0, strstr(argv0, "service") ? "[-s]" : "-s",
    229               argv0, strstr(argv0, "address") ? "[-a]" : "-a");
    230 }
    231 
    232 static int parse_command_line(Config *c, const char *argv0, int argc, char *argv[]) {
    233     int o;
    234 
    235     enum {
    236         ARG_SUBTYPE = 256
    237     };
    238 
    239     static const struct option long_options[] = {
    240         { "help",           no_argument,       NULL, 'h' },
    241         { "version",        no_argument,       NULL, 'V' },
    242         { "service",        no_argument,       NULL, 's' },
    243         { "address",        no_argument,       NULL, 'a' },
    244         { "verbose",        no_argument,       NULL, 'v' },
    245         { "domain",         required_argument, NULL, 'd' },
    246         { "host",           required_argument, NULL, 'H' },
    247         { "subtype",        required_argument, NULL, ARG_SUBTYPE},
    248         { "no-reverse",     no_argument,       NULL, 'R' },
    249         { "no-fail",        no_argument,       NULL, 'f' },
    250         { NULL, 0, NULL, 0 }
    251     };
    252 
    253     assert(c);
    254 
    255     c->command = strstr(argv0, "address") ? COMMAND_PUBLISH_ADDRESS : (strstr(argv0, "service") ? COMMAND_PUBLISH_SERVICE : COMMAND_UNSPEC);
    256     c->verbose = c->no_fail = c->no_reverse = 0;
    257     c->host = c->name = c->domain = c->stype = NULL;
    258     c->port = 0;
    259     c->txt = c->subtypes = NULL;
    260 
    261     while ((o = getopt_long(argc, argv, "hVsavRd:H:f", long_options, NULL)) >= 0) {
    262 
    263         switch(o) {
    264             case 'h':
    265                 c->command = COMMAND_HELP;
    266                 break;
    267             case 'V':
    268                 c->command = COMMAND_VERSION;
    269                 break;
    270             case 's':
    271                 c->command = COMMAND_PUBLISH_SERVICE;
    272                 break;
    273             case 'a':
    274                 c->command = COMMAND_PUBLISH_ADDRESS;
    275                 break;
    276             case 'v':
    277                 c->verbose = 1;
    278                 break;
    279             case 'R':
    280                 c->no_reverse = 1;
    281                 break;
    282             case 'd':
    283                 avahi_free(c->domain);
    284                 c->domain = avahi_strdup(optarg);
    285                 break;
    286             case 'H':
    287                 avahi_free(c->host);
    288                 c->host = avahi_strdup(optarg);
    289                 break;
    290             case 'f':
    291                 c->no_fail = 1;
    292                 break;
    293             case ARG_SUBTYPE:
    294                 c->subtypes = avahi_string_list_add(c->subtypes, optarg);
    295                 break;
    296             default:
    297                 return -1;
    298         }
    299     }
    300 
    301     if (c->command == COMMAND_PUBLISH_ADDRESS) {
    302         if (optind+2 !=  argc) {
    303             fprintf(stderr, _("Bad number of arguments\n"));
    304             return -1;
    305         }
    306 
    307         avahi_free(c->name);
    308         c->name = avahi_strdup(argv[optind]);
    309         avahi_address_parse(argv[optind+1], AVAHI_PROTO_UNSPEC, &c->address);
    310 
    311     } else if (c->command == COMMAND_PUBLISH_SERVICE) {
    312 
    313         char *e;
    314         long int p;
    315         int i;
    316 
    317         if (optind+3 > argc) {
    318             fprintf(stderr, _("Bad number of arguments\n"));
    319             return -1;
    320         }
    321 
    322         c->name = avahi_strdup(argv[optind]);
    323         c->stype = avahi_strdup(argv[optind+1]);
    324 
    325         errno = 0;
    326         p = strtol(argv[optind+2], &e, 0);
    327 
    328         if (errno != 0 || *e || p < 0 || p > 0xFFFF) {
    329             fprintf(stderr, _("Failed to parse port number: %s\n"), argv[optind+2]);
    330             return -1;
    331         }
    332 
    333         c->port = p;
    334 
    335         for (i = optind+3; i < argc; i++)
    336             c->txt = avahi_string_list_add(c->txt, argv[i]);
    337     }
    338 
    339     return 0;
    340 }
    341 
    342 int main(int argc, char *argv[]) {
    343     int ret = 1, error;
    344     Config config;
    345     const char *argv0;
    346 
    347     avahi_init_i18n();
    348     setlocale(LC_ALL, "");
    349 
    350     if ((argv0 = strrchr(argv[0], '/')))
    351         argv0++;
    352     else
    353         argv0 = argv[0];
    354 
    355     if (parse_command_line(&config, argv0, argc, argv) < 0)
    356         goto fail;
    357 
    358     switch (config.command) {
    359         case COMMAND_UNSPEC:
    360             ret = 1;
    361             fprintf(stderr, _("No command specified.\n"));
    362             break;
    363 
    364         case COMMAND_HELP:
    365             help(stdout, argv0);
    366             ret = 0;
    367             break;
    368 
    369         case COMMAND_VERSION:
    370             printf("%s "PACKAGE_VERSION"\n", argv0);
    371             ret = 0;
    372             break;
    373 
    374         case COMMAND_PUBLISH_SERVICE:
    375         case COMMAND_PUBLISH_ADDRESS:
    376 
    377             if (!(simple_poll = avahi_simple_poll_new())) {
    378                 fprintf(stderr, _("Failed to create simple poll object.\n"));
    379                 goto fail;
    380             }
    381 
    382             if (sigint_install(simple_poll) < 0)
    383                 goto fail;
    384 
    385             if (!(client = avahi_client_new(avahi_simple_poll_get(simple_poll), config.no_fail ? AVAHI_CLIENT_NO_FAIL : 0, client_callback, &config, &error))) {
    386                 fprintf(stderr, _("Failed to create client object: %s\n"), avahi_strerror(error));
    387                 goto fail;
    388             }
    389 
    390             if (avahi_client_get_state(client) != AVAHI_CLIENT_CONNECTING && config.verbose) {
    391                 const char *version, *hn;
    392 
    393                 if (!(version = avahi_client_get_version_string(client))) {
    394                     fprintf(stderr, _("Failed to query version string: %s\n"), avahi_strerror(avahi_client_errno(client)));
    395                     goto fail;
    396                 }
    397 
    398                 if (!(hn = avahi_client_get_host_name_fqdn(client))) {
    399                     fprintf(stderr, _("Failed to query host name: %s\n"), avahi_strerror(avahi_client_errno(client)));
    400                     goto fail;
    401                 }
    402 
    403                 fprintf(stderr, _("Server version: %s; Host name: %s\n"), version, hn);
    404             }
    405 
    406             avahi_simple_poll_loop(simple_poll);
    407             ret = 0;
    408             break;
    409     }
    410 
    411 fail:
    412 
    413     if (client)
    414         avahi_client_free(client);
    415 
    416     sigint_uninstall();
    417 
    418     if (simple_poll)
    419         avahi_simple_poll_free(simple_poll);
    420 
    421     avahi_free(config.host);
    422     avahi_free(config.name);
    423     avahi_free(config.stype);
    424     avahi_free(config.domain);
    425     avahi_string_list_free(config.subtypes);
    426     avahi_string_list_free(config.txt);
    427 
    428     return ret;
    429 }
    430