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