1/* 2 Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com> 3 All rights reserved. 4 5This file is part of x11vnc. 6 7x11vnc is free software; you can redistribute it and/or modify 8it under the terms of the GNU General Public License as published by 9the Free Software Foundation; either version 2 of the License, or (at 10your option) any later version. 11 12x11vnc is distributed in the hope that it will be useful, 13but WITHOUT ANY WARRANTY; without even the implied warranty of 14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15GNU General Public License for more details. 16 17You should have received a copy of the GNU General Public License 18along with x11vnc; if not, write to the Free Software 19Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA 20or see <http://www.gnu.org/licenses/>. 21 22In addition, as a special exception, Karl J. Runge 23gives permission to link the code of its release of x11vnc with the 24OpenSSL project's "OpenSSL" library (or with modified versions of it 25that use the same license as the "OpenSSL" library), and distribute 26the linked executables. You must obey the GNU General Public License 27in all respects for all of the code used other than "OpenSSL". If you 28modify this file, you may extend this exception to your version of the 29file, but you are not obligated to do so. If you do not wish to do 30so, delete this exception statement from your version. 31*/ 32 33/* -- avahi.c -- */ 34 35#include "x11vnc.h" 36#include "connections.h" 37#include "cleanup.h" 38 39void avahi_initialise(void); 40void avahi_advertise(char *name, char *host, uint16_t port); 41void avahi_reset(void); 42void avahi_cleanup(void); 43 44static pid_t avahi_pid = 0; 45 46static void kill_avahi_pid(void) { 47 if (avahi_pid != 0) { 48 rfbLog("kill_avahi_pid: %d\n", (int) avahi_pid); 49 kill(avahi_pid, SIGTERM); 50 avahi_pid = 0; 51 } 52} 53 54static int try_avahi_helper(char *name, char *host, uint16_t port) { 55#if LIBVNCSERVER_HAVE_FORK 56 char *cmd, *p, *path = getenv("PATH"), portstr[32]; 57 int i; 58 59 if (!name || !host || !port) {} 60 61 /* avahi-publish */ 62 if (no_external_cmds || !cmd_ok("zeroconf")) { 63 return 0; 64 } 65 66 if (!path) { 67 return 0; 68 } 69 70 path = strdup(path); 71 cmd = (char *) malloc(strlen(path) + 100); 72 sprintf(portstr, "%d", (int) port); 73 74 p = strtok(path, ":"); 75 while (p) { 76 struct stat sbuf; 77 78 sprintf(cmd, "%s/avahi-publish", p); 79 if (stat(cmd, &sbuf) == 0) { 80 break; 81 } 82 sprintf(cmd, "%s/dns-sd", p); 83 if (stat(cmd, &sbuf) == 0) { 84 break; 85 } 86 sprintf(cmd, "%s/mDNS", p); 87 if (stat(cmd, &sbuf) == 0) { 88 break; 89 } 90 cmd[0] = '\0'; 91 92 p = strtok(NULL, ":"); 93 } 94 free(path); 95 96 if (!strcmp(cmd, "")) { 97 free(cmd); 98 rfbLog("Could not find an external avahi/zeroconf helper program.\n"); 99 return 0; 100 } 101 102 avahi_pid = fork(); 103 104 if (avahi_pid < 0) { 105 rfbLogPerror("fork"); 106 avahi_pid = 0; 107 free(cmd); 108 return 0; 109 } 110 111 if (avahi_pid != 0) { 112 int status; 113 114 usleep(500 * 1000); 115 waitpid(avahi_pid, &status, WNOHANG); 116 if (kill(avahi_pid, 0) != 0) { 117 waitpid(avahi_pid, &status, WNOHANG); 118 avahi_pid = 0; 119 free(cmd); 120 return 0; 121 } 122 if (! quiet) { 123 rfbLog("%s helper pid is: %d\n", cmd, (int) avahi_pid); 124 } 125 free(cmd); 126 return 1; 127 } 128 129 for (i=3; i<256; i++) { 130 close(i); 131 } 132 133 if (strstr(cmd, "/avahi-publish")) { 134 execlp(cmd, cmd, "-s", name, "_rfb._tcp", portstr, (char *) NULL); 135 } else { 136 execlp(cmd, cmd, "-R", name, "_rfb._tcp", ".", portstr, (char *) NULL); 137 } 138 exit(1); 139#else 140 if (!name || !host || !port) {} 141 return 0; 142#endif 143} 144 145#if !defined(LIBVNCSERVER_HAVE_AVAHI) || !defined(LIBVNCSERVER_HAVE_LIBPTHREAD) 146void avahi_initialise(void) { 147 rfbLog("avahi_initialise: no Avahi support at buildtime.\n"); 148} 149 150void avahi_advertise(char *name, char *host, uint16_t port) { 151 char *t; 152 t = getenv("X11VNC_AVAHI_NAME"); if (t) name = t; 153 t = getenv("X11VNC_AVAHI_HOST"); if (t) host = t; 154 t = getenv("X11VNC_AVAHI_PORT"); if (t) port = atoi(t); 155 156 if (!try_avahi_helper(name, host, port)) { 157 rfbLog("avahi_advertise: no Avahi support at buildtime.\n"); 158 avahi = 0; 159 } 160} 161 162void avahi_reset(void) { 163 kill_avahi_pid(); 164 rfbLog("avahi_reset: no Avahi support at buildtime.\n"); 165} 166 167void avahi_cleanup(void) { 168 kill_avahi_pid(); 169 rfbLog("avahi_cleanup: no Avahi support at buildtime.\n"); 170} 171#else 172 173#include <avahi-common/thread-watch.h> 174#include <avahi-common/alternative.h> 175#include <avahi-client/client.h> 176#include <avahi-client/publish.h> 177 178#include <avahi-common/malloc.h> 179#include <avahi-common/error.h> 180 181 182static AvahiThreadedPoll *_poll = NULL; 183static AvahiClient *_client = NULL; 184static AvahiEntryGroup *_group = NULL; 185 186static int db = 0; 187 188typedef struct { 189 const char *name; 190 const char *host; 191 uint16_t port; 192} avahi_service_t; 193 194typedef struct { 195 char *name; 196 char *host; 197 uint16_t port; 198} avahi_reg_t; 199 200#define NREG 16 201static avahi_reg_t registered[NREG]; 202 203void avahi_initialise(void) { 204 int ret; 205 static int first = 1; 206 207 if (getenv("AVAHI_DEBUG")) { 208 db = 1; 209 } 210 if (first) { 211 int i; 212 for (i=0; i<NREG; i++) { 213 registered[i].name = NULL; 214 registered[i].host = NULL; 215 } 216 first = 0; 217 } 218 219if (db) fprintf(stderr, "in avahi_initialise\n"); 220 if (_poll) { 221if (db) fprintf(stderr, " avahi_initialise: poll not null\n"); 222 return; 223 } 224 225 if (! (_poll = avahi_threaded_poll_new()) ) { 226 rfbLog("warning: unable to open Avahi poll.\n"); 227 return; 228 } 229 230 _client = avahi_client_new(avahi_threaded_poll_get(_poll), 231 0, NULL, NULL, &ret); 232 if (! _client) { 233 rfbLog("warning: unable to open Avahi client: %s\n", 234 avahi_strerror(ret)); 235 236 avahi_threaded_poll_free(_poll); 237 _poll = NULL; 238 return; 239 } 240 241 if (avahi_threaded_poll_start(_poll) < 0) { 242 rfbLog("warning: unable to start Avahi poll.\n"); 243 avahi_client_free(_client); 244 _client = NULL; 245 avahi_threaded_poll_free(_poll); 246 _poll = NULL; 247 return; 248 } 249if (db) fprintf(stderr, "out avahi_initialise\n"); 250} 251 252static void _avahi_create_services(char *name, char *host, 253 uint16_t port); 254 255static void _avahi_entry_group_callback(AvahiEntryGroup *g, 256 AvahiEntryGroupState state, void *userdata) { 257 char *new_name; 258 avahi_service_t *svc = (avahi_service_t *)userdata; 259 260if (db) fprintf(stderr, "in _avahi_entry_group_callback %d 0x%p\n", state, svc); 261 if (g != _group && _group != NULL) { 262 rfbLog("avahi_entry_group_callback fatal error (group).\n"); 263 clean_up_exit(1); 264 } 265 if (userdata == NULL) { 266 rfbLog("avahi_entry_group_callback fatal error (userdata).\n"); 267 clean_up_exit(1); 268 } 269 270 switch(state) { 271 case AVAHI_ENTRY_GROUP_ESTABLISHED: 272 rfbLog("Avahi group %s established.\n", svc->name); 273#if 0 /* is this the segv problem? */ 274 free(svc); 275#endif 276 break; 277 case AVAHI_ENTRY_GROUP_COLLISION: 278 new_name = avahi_alternative_service_name(svc->name); 279 _avahi_create_services(new_name, svc->host, svc->port); 280 rfbLog("Avahi Entry group collision\n"); 281 avahi_free(new_name); 282 break; 283 case AVAHI_ENTRY_GROUP_FAILURE: 284 rfbLog("Avahi Entry group failure: %s\n", 285 avahi_strerror(avahi_client_errno( 286 avahi_entry_group_get_client(g)))); 287 break; 288 default: 289 break; 290 } 291if (db) fprintf(stderr, "out _avahi_entry_group_callback\n"); 292} 293 294static void _avahi_create_services(char *name, char *host, uint16_t port) { 295 avahi_service_t *svc = (avahi_service_t *)malloc(sizeof(avahi_service_t)); 296 int ret = 0; 297 298if (db) fprintf(stderr, "in _avahi_create_services '%s' '%s' %d\n", name, host, port); 299 svc->name = name; 300 svc->host = host; 301 svc->port = port; 302 303 if (!_group) { 304if (db) fprintf(stderr, " _avahi_create_services create group\n"); 305 _group = avahi_entry_group_new(_client, 306 _avahi_entry_group_callback, svc); 307 } 308 if (!_group) { 309 rfbLog("avahi_entry_group_new() failed: %s\n", 310 avahi_strerror(avahi_client_errno(_client))); 311 return; 312 } 313 314 ret = avahi_entry_group_add_service(_group, AVAHI_IF_UNSPEC, 315 AVAHI_PROTO_UNSPEC, 0, name, "_rfb._tcp", NULL, NULL, port, NULL); 316 if (ret < 0) { 317 rfbLog("Failed to add _rfb._tcp service: %s\n", 318 avahi_strerror(ret)); 319 return; 320 } 321 322 ret = avahi_entry_group_commit(_group); 323 if (ret < 0) { 324 rfbLog("Failed to commit entry_group:: %s\n", 325 avahi_strerror(ret)); 326 return; 327 } 328if (db) fprintf(stderr, "out _avahi_create_services\n"); 329} 330 331void avahi_advertise(char *name, char *host, uint16_t port) { 332 int i; 333 char *t; 334 t = getenv("X11VNC_AVAHI_NAME"); if (t) name = t; 335 t = getenv("X11VNC_AVAHI_HOST"); if (t) host = t; 336 t = getenv("X11VNC_AVAHI_PORT"); if (t) port = atoi(t); 337 338if (db) fprintf(stderr, "in avahi_advertise: '%s' '%s' %d\n", name, host, port); 339 if (!_client) { 340if (db) fprintf(stderr, " avahi_advertise client null\n"); 341 return; 342 } 343 if (_poll == NULL) { 344 rfbLog("Avahi poll not initialized.\n"); 345 return; 346 } 347 /* well, we just track it ourselves... */ 348 for (i=0; i<NREG; i++) { 349 if (!registered[i].name) { 350 continue; 351 } 352 if (strcmp(registered[i].name, name)) { 353 continue; 354 } 355 if (strcmp(registered[i].host, host)) { 356 continue; 357 } 358 if (registered[i].port != port) { 359 continue; 360 } 361if (db) fprintf(stderr, " avahi_advertise already did this one\n"); 362 return; 363 } 364 for (i=0; i<NREG; i++) { 365 if (!registered[i].name) { 366 registered[i].name = strdup(name); 367 registered[i].host = strdup(host); 368 registered[i].port = port; 369 break; 370 } 371 } 372 373 avahi_threaded_poll_lock(_poll); 374 _avahi_create_services(name, host, port >= 5900 ? port : 5900+port); 375 avahi_threaded_poll_unlock(_poll); 376if (db) fprintf(stderr, "out avahi_advertise\n"); 377} 378 379void avahi_reset(void) { 380 int i; 381if (db) fprintf(stderr, "in avahi_reset\n"); 382 for (i=0; i<NREG; i++) { 383 if (registered[i].name) { 384 free(registered[i].name); 385 registered[i].name = NULL; 386 } 387 if (registered[i].host) { 388 free(registered[i].host); 389 registered[i].host = NULL; 390 } 391 } 392 if (!_client || !_group) { 393if (db) fprintf(stderr, " avahi_reset client/group null\n"); 394 return; 395 } 396 avahi_entry_group_reset(_group); 397 rfbLog("Avahi resetting group.\n"); 398if (db) fprintf(stderr, "out avahi_reset\n"); 399} 400 401static void avahi_timeout (int sig) { 402 rfbLog("sig: %d, avahi_cleanup timed out.\n", sig); 403 exit(1); 404} 405 406 407void avahi_cleanup(void) { 408if (db) fprintf(stderr, "in avahi_cleanup\n"); 409 if (!_client) { 410if (db) fprintf(stderr, " avahi_cleanup client null\n"); 411 return; 412 } 413if (db) fprintf(stderr, " avahi_cleanup poll_lock\n"); 414 avahi_threaded_poll_lock(_poll); 415if (db) fprintf(stderr, " avahi_cleanup poll_stop\n"); 416 417 signal(SIGALRM, avahi_timeout); 418 alarm(3); 419 avahi_threaded_poll_stop(_poll); 420 alarm(0); 421 signal(SIGALRM, SIG_DFL); 422 423if (db) fprintf(stderr, " avahi_cleanup client_free\n"); 424 avahi_client_free(_client); 425 _client = NULL; 426 427if (db) fprintf(stderr, " avahi_cleanup poll_free\n"); 428 avahi_threaded_poll_free(_poll); 429 _poll = NULL; 430if (db) fprintf(stderr, "out avahi_cleanup\n"); 431} 432 433#endif 434 435