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