1 /* 2 Copyright (C) 2002-2010 Karl J. Runge <runge (at) karlrunge.com> 3 All rights reserved. 4 5 This file is part of x11vnc. 6 7 x11vnc is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2 of the License, or (at 10 your option) any later version. 11 12 x11vnc is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with x11vnc; if not, write to the Free Software 19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA 20 or see <http://www.gnu.org/licenses/>. 21 22 In addition, as a special exception, Karl J. Runge 23 gives permission to link the code of its release of x11vnc with the 24 OpenSSL project's "OpenSSL" library (or with modified versions of it 25 that use the same license as the "OpenSSL" library), and distribute 26 the linked executables. You must obey the GNU General Public License 27 in all respects for all of the code used other than "OpenSSL". If you 28 modify this file, you may extend this exception to your version of the 29 file, but you are not obligated to do so. If you do not wish to do 30 so, 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 39 void avahi_initialise(void); 40 void avahi_advertise(char *name, char *host, uint16_t port); 41 void avahi_reset(void); 42 void avahi_cleanup(void); 43 44 static pid_t avahi_pid = 0; 45 46 static 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 54 static 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) 146 void avahi_initialise(void) { 147 rfbLog("avahi_initialise: no Avahi support at buildtime.\n"); 148 } 149 150 void 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 162 void avahi_reset(void) { 163 kill_avahi_pid(); 164 rfbLog("avahi_reset: no Avahi support at buildtime.\n"); 165 } 166 167 void 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 182 static AvahiThreadedPoll *_poll = NULL; 183 static AvahiClient *_client = NULL; 184 static AvahiEntryGroup *_group = NULL; 185 186 static int db = 0; 187 188 typedef struct { 189 const char *name; 190 const char *host; 191 uint16_t port; 192 } avahi_service_t; 193 194 typedef struct { 195 char *name; 196 char *host; 197 uint16_t port; 198 } avahi_reg_t; 199 200 #define NREG 16 201 static avahi_reg_t registered[NREG]; 202 203 void 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 219 if (db) fprintf(stderr, "in avahi_initialise\n"); 220 if (_poll) { 221 if (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 } 249 if (db) fprintf(stderr, "out avahi_initialise\n"); 250 } 251 252 static void _avahi_create_services(char *name, char *host, 253 uint16_t port); 254 255 static 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 260 if (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 } 291 if (db) fprintf(stderr, "out _avahi_entry_group_callback\n"); 292 } 293 294 static 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 298 if (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) { 304 if (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 } 328 if (db) fprintf(stderr, "out _avahi_create_services\n"); 329 } 330 331 void 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 338 if (db) fprintf(stderr, "in avahi_advertise: '%s' '%s' %d\n", name, host, port); 339 if (!_client) { 340 if (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 } 361 if (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); 376 if (db) fprintf(stderr, "out avahi_advertise\n"); 377 } 378 379 void avahi_reset(void) { 380 int i; 381 if (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) { 393 if (db) fprintf(stderr, " avahi_reset client/group null\n"); 394 return; 395 } 396 avahi_entry_group_reset(_group); 397 rfbLog("Avahi resetting group.\n"); 398 if (db) fprintf(stderr, "out avahi_reset\n"); 399 } 400 401 static void avahi_timeout (int sig) { 402 rfbLog("sig: %d, avahi_cleanup timed out.\n", sig); 403 exit(1); 404 } 405 406 407 void avahi_cleanup(void) { 408 if (db) fprintf(stderr, "in avahi_cleanup\n"); 409 if (!_client) { 410 if (db) fprintf(stderr, " avahi_cleanup client null\n"); 411 return; 412 } 413 if (db) fprintf(stderr, " avahi_cleanup poll_lock\n"); 414 avahi_threaded_poll_lock(_poll); 415 if (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 423 if (db) fprintf(stderr, " avahi_cleanup client_free\n"); 424 avahi_client_free(_client); 425 _client = NULL; 426 427 if (db) fprintf(stderr, " avahi_cleanup poll_free\n"); 428 avahi_threaded_poll_free(_poll); 429 _poll = NULL; 430 if (db) fprintf(stderr, "out avahi_cleanup\n"); 431 } 432 433 #endif 434 435