/* -- cleanup.c -- */ #include "x11vnc.h" #include "xwrappers.h" #include "xdamage.h" #include "remote.h" #include "keyboard.h" #include "scan.h" #include "gui.h" #include "solid.h" #include "unixpw.h" #include "sslcmds.h" #include "sslhelper.h" #include "connections.h" #include "macosx.h" #include "macosxCG.h" #include "avahi.h" /* * Exiting and error handling routines */ int trapped_xerror = 0; int trapped_xioerror = 0; int trapped_getimage_xerror = 0; int trapped_record_xerror = 0; XErrorEvent *trapped_xerror_event; /* XXX CHECK BEFORE RELEASE */ int crash_debug = 0; void clean_shm(int quick); void clean_up_exit (int ret); int trap_xerror(Display *d, XErrorEvent *error); int trap_xioerror(Display *d); int trap_getimage_xerror(Display *d, XErrorEvent *error); char *xerror_string(XErrorEvent *error); void initialize_crash_handler(void); void initialize_signals(void); void unset_signals(void); void close_exec_fds(void); int known_sigpipe_mode(char *s); static int exit_flag = 0; static int exit_sig = 0; static void clean_icon_mode(void); static int Xerror(Display *d, XErrorEvent *error); static int XIOerr(Display *d); static void crash_shell_help(void); static void crash_shell(void); static void interrupted (int sig); void clean_shm(int quick) { int i, cnt = 0; /* * to avoid deadlock, etc, under quick=1 we just delete the shm * areas and leave the X stuff hanging. */ if (quick) { shm_delete(&scanline_shm); shm_delete(&fullscreen_shm); shm_delete(&snaprect_shm); } else { shm_clean(&scanline_shm, scanline); shm_clean(&fullscreen_shm, fullscreen); shm_clean(&snaprect_shm, snaprect); } /* * Here we have to clean up quite a few shm areas for all * the possible tile row runs (40 for 1280), not as robust * as one might like... sometimes need to run ipcrm(1). */ for(i=1; i<=ntiles_x; i++) { if (i > tile_shm_count) { break; } if (quick) { shm_delete(&tile_row_shm[i]); } else { shm_clean(&tile_row_shm[i], tile_row[i]); } cnt++; if (single_copytile_count && i >= single_copytile_count) { break; } } if (!quiet && cnt > 0) { rfbLog("deleted %d tile_row polling images.\n", cnt); } } static void clean_icon_mode(void) { if (icon_mode && icon_mode_fh) { fprintf(icon_mode_fh, "quit\n"); fflush(icon_mode_fh); fclose(icon_mode_fh); icon_mode_fh = NULL; if (icon_mode_file) { unlink(icon_mode_file); icon_mode_file = NULL; } } } /* * Normal exiting */ void clean_up_exit (int ret) { static int depth = 0; exit_flag = 1; if (depth++ > 2) { exit(ret); } if (icon_mode) { clean_icon_mode(); } /* remove the shm areas: */ clean_shm(0); stop_stunnel(); if (use_openssl) { ssl_helper_pid(0, 0); /* killall */ } if (avahi) { avahi_cleanup(); } #ifdef MACOSX if (client_connect_file) { if (strstr(client_connect_file, "/tmp/x11vnc-macosx-channel.") == client_connect_file) { unlink(client_connect_file); } } if (macosx_console) { macosxCG_fini(); } #endif if (! dpy) exit(ret); /* raw_rb hack */ /* X keyboard cleanups */ delete_added_keycodes(0); if (clear_mods == 1) { clear_modifiers(0); } else if (clear_mods == 2) { clear_keys(); } if (no_autorepeat) { autorepeat(1, 0); } if (use_solid_bg) { solid_bg(1); } if (ncache || ncache0) { kde_no_animate(1); } X_LOCK; XTestDiscard_wr(dpy); #if LIBVNCSERVER_HAVE_LIBXDAMAGE if (xdamage) { XDamageDestroy(dpy, xdamage); } #endif #if LIBVNCSERVER_HAVE_LIBXTRAP if (trap_ctx) { XEFreeTC(trap_ctx); } #endif /* XXX rdpy_ctrl, etc. cannot close w/o blocking */ XCloseDisplay_wr(dpy); X_UNLOCK; fflush(stderr); exit(ret); } /* X11 error handlers */ static XErrorHandler Xerror_def; static XIOErrorHandler XIOerr_def; int trap_xerror(Display *d, XErrorEvent *error) { trapped_xerror = 1; trapped_xerror_event = error; if (d) {} /* unused vars warning: */ return 0; } int trap_xioerror(Display *d) { trapped_xioerror = 1; if (d) {} /* unused vars warning: */ return 0; } int trap_getimage_xerror(Display *d, XErrorEvent *error) { trapped_getimage_xerror = 1; trapped_xerror_event = error; if (d) {} /* unused vars warning: */ return 0; } static int Xerror(Display *d, XErrorEvent *error) { X_UNLOCK; interrupted(0); if (d) {} /* unused vars warning: */ return (*Xerror_def)(d, error); } static int XIOerr(Display *d) { X_UNLOCK; interrupted(-1); if (d) {} /* unused vars warning: */ return (*XIOerr_def)(d); } static char *xerrors[] = { "Success", "BadRequest", "BadValue", "BadWindow", "BadPixmap", "BadAtom", "BadCursor", "BadFont", "BadMatch", "BadDrawable", "BadAccess", "BadAlloc", "BadColor", "BadGC", "BadIDChoice", "BadName", "BadLength", "BadImplementation", "unknown" }; static int xerrors_max = BadImplementation; char *xerror_string(XErrorEvent *error) { int index = -1; if (error) { index = (int) error->error_code; } if (0 <= index && index <= xerrors_max) { return xerrors[index]; } else { return xerrors[xerrors_max+1]; } } static char *crash_stack_command1 = NULL; static char *crash_stack_command2 = NULL; static char *crash_debug_command = NULL; void initialize_crash_handler(void) { int pid = program_pid; crash_stack_command1 = (char *) malloc(1000); crash_stack_command2 = (char *) malloc(1000); crash_debug_command = (char *) malloc(1000); snprintf(crash_stack_command1, 500, "echo where > /tmp/gdb.%d;" " env PATH=$PATH:/usr/local/bin:/usr/sfw/bin:/usr/bin" " gdb -x /tmp/gdb.%d -batch -n %s %d;" " rm -f /tmp/gdb.%d", pid, pid, program_name, pid, pid); snprintf(crash_stack_command2, 500, "pstack %d", program_pid); snprintf(crash_debug_command, 500, "gdb %s %d", program_name, pid); } static void crash_shell_help(void) { int pid = program_pid; fprintf(stderr, "\n"); fprintf(stderr, " *** Welcome to the x11vnc crash shell! ***\n"); fprintf(stderr, "\n"); fprintf(stderr, "PROGRAM: %s PID: %d\n", program_name, pid); fprintf(stderr, "\n"); fprintf(stderr, "POSSIBLE DEBUGGER COMMAND:\n"); fprintf(stderr, "\n"); fprintf(stderr, " %s\n", crash_debug_command); fprintf(stderr, "\n"); fprintf(stderr, "Press \"q\" to quit.\n"); fprintf(stderr, "Press \"h\" or \"?\" for this help.\n"); fprintf(stderr, "Press \"s\" to try to run some commands to" " show a stack trace (gdb/pstack).\n"); fprintf(stderr, "\n"); fprintf(stderr, "Anything else is passed to -Q query function.\n"); fprintf(stderr, "\n"); } static void crash_shell(void) { char qry[1000], cmd[1000], line[1000]; char *str, *p; crash_shell_help(); fprintf(stderr, "\ncrash> "); while (fgets(line, 1000, stdin) != NULL) { str = lblanks(line); p = str; while(*p) { if (*p == '\n') { *p = '\0'; } p++; } if (*str == 'q' && *(str+1) == '\0') { fprintf(stderr, "quiting.\n"); return; } else if (*str == 'h' && *(str+1) == '\0') { crash_shell_help(); } else if (*str == '?' && *(str+1) == '\0') { crash_shell_help(); } else if (*str == 's' && *(str+1) == '\0') { sprintf(cmd, "sh -c '(%s) &'", crash_stack_command1); /* crash */ if (no_external_cmds || !cmd_ok("crash")) { fprintf(stderr, "\nno_external_cmds=%d\n", no_external_cmds); goto crash_prompt; } fprintf(stderr, "\nrunning:\n\t%s\n\n", crash_stack_command1); system(cmd); usleep(1000*1000); sprintf(cmd, "sh -c '(%s) &'", crash_stack_command2); fprintf(stderr, "\nrunning:\n\t%s\n\n", crash_stack_command2); system(cmd); usleep(1000*1000); } else { snprintf(qry, 1000, "qry=%s", str); p = process_remote_cmd(qry, 1); fprintf(stderr, "\n\nresult:\n%s\n", p); free(p); } crash_prompt: fprintf(stderr, "crash> "); } } /* * General problem handler */ static void interrupted (int sig) { exit_sig = sig; if (exit_flag) { fprintf(stderr, "extra[%d] signal: %d\n", exit_flag, sig); exit_flag++; if (use_threads) { usleep2(250 * 1000); } else if (exit_flag <= 2) { return; } exit(4); } exit_flag++; if (sig == 0) { fprintf(stderr, "caught X11 error:\n"); if (crash_debug) { crash_shell(); } } else if (sig == -1) { fprintf(stderr, "caught XIO error:\n"); } else { fprintf(stderr, "caught signal: %d\n", sig); } if (sig == SIGINT) { shut_down = 1; return; } if (crash_debug) { crash_shell(); } X_UNLOCK; if (icon_mode) { clean_icon_mode(); } /* remove the shm areas with quick=1: */ clean_shm(1); if (sig == -1) { /* not worth trying any more cleanup, X server probably gone */ exit(3); } /* X keyboard cleanups */ delete_added_keycodes(0); if (clear_mods == 1) { clear_modifiers(0); } else if (clear_mods == 2) { clear_keys(); } if (no_autorepeat) { autorepeat(1, 0); } if (use_solid_bg) { solid_bg(1); } if (ncache || ncache0) { kde_no_animate(1); } stop_stunnel(); if (crash_debug) { crash_shell(); } if (sig) { exit(2); } } static void ignore_sigs(char *list) { char *str, *p; int ignore = 1; if (list == NULL || *list == '\0') { return; } str = strdup(list); p = strtok(str, ":,"); #define SETSIG(x, y) \ if (strstr(p, x)) { \ if (ignore) { \ signal(y, SIG_IGN); \ } else { \ signal(y, interrupted); \ } \ } #ifdef SIG_IGN while (p) { if (!strcmp(p, "ignore")) { ignore = 1; } else if (!strcmp(p, "exit")) { ignore = 0; } /* Take off every 'sig' ;-) */ #ifdef SIGHUP SETSIG("HUP", SIGHUP); #endif #ifdef SIGINT SETSIG("INT", SIGINT); #endif #ifdef SIGQUIT SETSIG("QUIT", SIGQUIT); #endif #ifdef SIGTRAP SETSIG("TRAP", SIGTRAP); #endif #ifdef SIGABRT SETSIG("ABRT", SIGABRT); #endif #ifdef SIGBUS SETSIG("BUS", SIGBUS); #endif #ifdef SIGFPE SETSIG("FPE", SIGFPE); #endif #ifdef SIGSEGV SETSIG("SEGV", SIGSEGV); #endif #ifdef SIGPIPE SETSIG("PIPE", SIGPIPE); #endif #ifdef SIGTERM SETSIG("TERM", SIGTERM); #endif #ifdef SIGUSR1 SETSIG("USR1", SIGUSR1); #endif #ifdef SIGUSR2 SETSIG("USR2", SIGUSR2); #endif #ifdef SIGCONT SETSIG("CONT", SIGCONT); #endif #ifdef SIGSTOP SETSIG("STOP", SIGSTOP); #endif #ifdef SIGTSTP SETSIG("TSTP", SIGTSTP); #endif p = strtok(NULL, ":,"); } #endif /* SIG_IGN */ free(str); } /* signal handlers */ void initialize_signals(void) { signal(SIGHUP, interrupted); signal(SIGINT, interrupted); signal(SIGQUIT, interrupted); signal(SIGABRT, interrupted); signal(SIGTERM, interrupted); signal(SIGBUS, interrupted); signal(SIGSEGV, interrupted); signal(SIGFPE, interrupted); if (!sigpipe || *sigpipe == '\0' || !strcmp(sigpipe, "skip")) { ; } else if (strstr(sigpipe, "ignore:") == sigpipe) { ignore_sigs(sigpipe); } else if (strstr(sigpipe, "exit:") == sigpipe) { ignore_sigs(sigpipe); } else if (!strcmp(sigpipe, "ignore")) { #ifdef SIG_IGN signal(SIGPIPE, SIG_IGN); #endif } else if (!strcmp(sigpipe, "exit")) { rfbLog("initialize_signals: will exit on SIGPIPE\n"); signal(SIGPIPE, interrupted); } #if NO_X11 return; #else X_LOCK; Xerror_def = XSetErrorHandler(Xerror); XIOerr_def = XSetIOErrorHandler(XIOerr); X_UNLOCK; #endif /* NO_X11 */ } void unset_signals(void) { signal(SIGHUP, SIG_DFL); signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGABRT, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGBUS, SIG_DFL); signal(SIGSEGV, SIG_DFL); signal(SIGFPE, SIG_DFL); signal(SIGPIPE, SIG_DFL); } void close_exec_fds(void) { int fd; #ifdef FD_CLOEXEC for (fd = 3; fd < 64; fd++) { int flags = fcntl(fd, F_GETFD); if (flags != -1) { flags |= FD_CLOEXEC; fcntl(fd, F_SETFD, flags); } } #endif } int known_sigpipe_mode(char *s) { /* * skip, ignore, exit */ if (strstr(s, "ignore:") == s) { return 1; } if (strstr(s, "exit:") == s) { return 1; } if (strcmp(s, "skip") && strcmp(s, "ignore") && strcmp(s, "exit")) { return 0; } else { return 1; } }