1 /*
2 Copyright (C) 2002-2010 Karl J. Runge <runge@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 /* -- cleanup.c -- */
34
35 #include "x11vnc.h"
36 #include "xwrappers.h"
37 #include "xdamage.h"
38 #include "remote.h"
39 #include "keyboard.h"
40 #include "scan.h"
41 #include "gui.h"
42 #include "solid.h"
43 #include "unixpw.h"
44 #include "sslcmds.h"
45 #include "sslhelper.h"
46 #include "connections.h"
47 #include "macosx.h"
48 #include "macosxCG.h"
49 #include "avahi.h"
50 #include "screen.h"
51 #include "xrecord.h"
52 #include "xevents.h"
53 #include "uinput.h"
54
55 /*
56 * Exiting and error handling routines
57 */
58
59 int trapped_xerror = 0;
60 int trapped_xioerror = 0;
61 int trapped_getimage_xerror = 0;
62 int trapped_record_xerror = 0;
63 XErrorEvent *trapped_xerror_event;
64
65 /* XXX CHECK BEFORE RELEASE */
66 int crash_debug = 0;
67
68 void clean_shm(int quick);
69 void clean_up_exit(int ret);
70 int trap_xerror(Display *d, XErrorEvent *error);
71 int trap_xioerror(Display *d);
72 int trap_getimage_xerror(Display *d, XErrorEvent *error);
73 char *xerror_string(XErrorEvent *error);
74 void initialize_crash_handler(void);
75 void initialize_signals(void);
76 void unset_signals(void);
77 void close_exec_fds(void);
78 int known_sigpipe_mode(char *s);
79
80
81 static int exit_flag = 0;
82 static int exit_sig = 0;
83
84 static void clean_icon_mode(void);
85 static int Xerror(Display *d, XErrorEvent *error);
86 static int XIOerr(Display *d);
87 static void crash_shell_help(void);
88 static void crash_shell(void);
89 static void interrupted (int sig);
90
91
clean_shm(int quick)92 void clean_shm(int quick) {
93 int i, cnt = 0;
94
95 /*
96 * to avoid deadlock, etc, under quick=1 we just delete the shm
97 * areas and leave the X stuff hanging.
98 */
99 if (quick) {
100 shm_delete(&scanline_shm);
101 shm_delete(&fullscreen_shm);
102 shm_delete(&snaprect_shm);
103 } else {
104 shm_clean(&scanline_shm, scanline);
105 shm_clean(&fullscreen_shm, fullscreen);
106 shm_clean(&snaprect_shm, snaprect);
107 }
108
109 /*
110 * Here we have to clean up quite a few shm areas for all
111 * the possible tile row runs (40 for 1280), not as robust
112 * as one might like... sometimes need to run ipcrm(1).
113 */
114 for(i=1; i<=ntiles_x; i++) {
115 if (i > tile_shm_count) {
116 break;
117 }
118 if (quick) {
119 shm_delete(&tile_row_shm[i]);
120 } else {
121 shm_clean(&tile_row_shm[i], tile_row[i]);
122 }
123 cnt++;
124 if (single_copytile_count && i >= single_copytile_count) {
125 break;
126 }
127 }
128 if (!quiet && cnt > 0) {
129 rfbLog("deleted %d tile_row polling images.\n", cnt);
130 }
131 }
132
clean_icon_mode(void)133 static void clean_icon_mode(void) {
134 if (icon_mode && icon_mode_fh) {
135 fprintf(icon_mode_fh, "quit\n");
136 fflush(icon_mode_fh);
137 fclose(icon_mode_fh);
138 icon_mode_fh = NULL;
139 if (icon_mode_file) {
140 unlink(icon_mode_file);
141 icon_mode_file = NULL;
142 }
143 }
144 }
145
146 /*
147 * Normal exiting
148 */
clean_up_exit(int ret)149 void clean_up_exit(int ret) {
150 static int depth = 0;
151 exit_flag = 1;
152
153 if (depth++ > 2) {
154 exit(ret);
155 }
156
157 if (icon_mode) {
158 clean_icon_mode();
159 }
160
161 /* remove the shm areas: */
162 clean_shm(0);
163
164 stop_stunnel();
165 if (use_openssl) {
166 ssl_helper_pid(0, 0); /* killall */
167 }
168
169 if (ssh_pid > 0) {
170 kill(ssh_pid, SIGTERM);
171 ssh_pid = 0;
172 }
173
174 #ifdef MACOSX
175 if (client_connect_file) {
176 if (strstr(client_connect_file, "/tmp/x11vnc-macosx-remote")
177 == client_connect_file) {
178 unlink(client_connect_file);
179 }
180 }
181 if (macosx_console) {
182 macosxCG_fini();
183 }
184 #endif
185
186 if (pipeinput_fh != NULL) {
187 pclose(pipeinput_fh);
188 pipeinput_fh = NULL;
189 }
190
191 shutdown_uinput();
192
193 if (unix_sock) {
194 if (unix_sock_fd >= 0) {
195 rfbLog("deleting unix sock: %s\n", unix_sock);
196 close(unix_sock_fd);
197 unix_sock_fd = -1;
198 unlink(unix_sock);
199 }
200 }
201
202 if (! dpy) { /* raw_rb hack */
203 if (rm_flagfile) {
204 unlink(rm_flagfile);
205 rm_flagfile = NULL;
206 }
207 exit(ret);
208 }
209
210 /* X keyboard cleanups */
211 delete_added_keycodes(0);
212
213 if (clear_mods == 1) {
214 clear_modifiers(0);
215 } else if (clear_mods == 2) {
216 clear_keys();
217 } else if (clear_mods == 3) {
218 clear_keys();
219 clear_locks();
220 }
221
222 if (no_autorepeat) {
223 autorepeat(1, 0);
224 }
225 if (use_solid_bg) {
226 solid_bg(1);
227 }
228 if (ncache || ncache0) {
229 kde_no_animate(1);
230 }
231 X_LOCK;
232 XTestDiscard_wr(dpy);
233 #if LIBVNCSERVER_HAVE_LIBXDAMAGE
234 if (xdamage) {
235 XDamageDestroy(dpy, xdamage);
236 }
237 #endif
238 #if LIBVNCSERVER_HAVE_LIBXTRAP
239 if (trap_ctx) {
240 XEFreeTC(trap_ctx);
241 }
242 #endif
243 /* XXX rdpy_ctrl, etc. cannot close w/o blocking */
244 XCloseDisplay_wr(dpy);
245 X_UNLOCK;
246
247 fflush(stderr);
248
249 if (rm_flagfile) {
250 unlink(rm_flagfile);
251 rm_flagfile = NULL;
252 }
253
254 if (avahi) {
255 avahi_cleanup();
256 fflush(stderr);
257 }
258
259 exit(ret);
260 }
261
262 /* X11 error handlers */
263
264 static XErrorHandler Xerror_def;
265 static XIOErrorHandler XIOerr_def;
266
trap_xerror(Display * d,XErrorEvent * error)267 int trap_xerror(Display *d, XErrorEvent *error) {
268 trapped_xerror = 1;
269 trapped_xerror_event = error;
270
271 if (d) {} /* unused vars warning: */
272
273 return 0;
274 }
275
trap_xioerror(Display * d)276 int trap_xioerror(Display *d) {
277 trapped_xioerror = 1;
278
279 if (d) {} /* unused vars warning: */
280
281 return 0;
282 }
283
trap_getimage_xerror(Display * d,XErrorEvent * error)284 int trap_getimage_xerror(Display *d, XErrorEvent *error) {
285 trapped_getimage_xerror = 1;
286 trapped_xerror_event = error;
287
288 if (d) {} /* unused vars warning: */
289
290 return 0;
291 }
292
293 /* Are silly Xorg people removing X_ShmAttach from XShm.h? */
294 /* INDEED! What stupid, myopic morons... */
295 /* Maintenance Monkeys busy typing at their keyboards... */
296 #ifndef X_ShmAttach
297 #define X_ShmAttach 1
298 #endif
299
Xerror(Display * d,XErrorEvent * error)300 static int Xerror(Display *d, XErrorEvent *error) {
301 X_UNLOCK;
302
303 if (getenv("X11VNC_PRINT_XERROR")) {
304 fprintf(stderr, "Xerror: major_opcode: %d minor_opcode: %d error_code: %d\n",
305 error->request_code, error->minor_code, error->error_code);
306 }
307
308 if (xshm_opcode > 0 && error->request_code == xshm_opcode) {
309 if (error->minor_code == X_ShmAttach) {
310 char *dstr = DisplayString(dpy);
311 fprintf(stderr, "\nX11 MIT Shared Memory Attach failed:\n");
312 fprintf(stderr, " Is your DISPLAY=%s on a remote machine?\n", dstr);
313 if (strstr(dstr, "localhost:")) {
314 fprintf(stderr, " Note: DISPLAY=localhost:N suggests a SSH X11 redir to a remote machine.\n");
315 } else if (dstr[0] != ':') {
316 fprintf(stderr, " Note: DISPLAY=hostname:N suggests a remote display.\n");
317 }
318 fprintf(stderr, " Suggestion, use: x11vnc -display :0 ... for local display :0\n\n");
319 }
320 }
321
322 interrupted(0);
323
324 if (d) {} /* unused vars warning: */
325
326 return (*Xerror_def)(d, error);
327 }
328
329 void watch_loop(void);
330
XIOerr(Display * d)331 static int XIOerr(Display *d) {
332 static int reopen = 0, rmax = 1;
333 X_UNLOCK;
334
335 if (getenv("X11VNC_REOPEN_DISPLAY")) {
336 rmax = atoi(getenv("X11VNC_REOPEN_DISPLAY"));
337 }
338
339 #if !NO_X11
340 if (reopen < rmax && getenv("X11VNC_REOPEN_DISPLAY")) {
341 int db = getenv("X11VNC_REOPEN_DEBUG") ? 1 : 0;
342 int sleepmax = 10, i;
343 Display *save_dpy = dpy;
344 char *dstr = strdup(DisplayString(save_dpy));
345 reopen++;
346 if (getenv("X11VNC_REOPEN_SLEEP_MAX")) {
347 sleepmax = atoi(getenv("X11VNC_REOPEN_SLEEP_MAX"));
348 }
349 rfbLog("*** XIO error: Trying to reopen[%d/%d] display '%s'\n", reopen, rmax, dstr);
350 rfbLog("*** XIO error: Note the reopened state may be unstable.\n");
351 for (i=0; i < sleepmax; i++) {
352 usleep (1000 * 1000);
353 dpy = XOpenDisplay_wr(dstr);
354 rfbLog("dpy[%d/%d]: %p\n", i+1, sleepmax, dpy);
355 if (dpy) {
356 break;
357 }
358 }
359 last_open_xdisplay = time(NULL);
360 if (dpy) {
361 rfbLog("*** XIO error: Reopened display '%s' successfully.\n", dstr);
362 if (db) rfbLog("*** XIO error: '%s' 0x%x\n", dstr, dpy);
363 scr = DefaultScreen(dpy);
364 rootwin = RootWindow(dpy, scr);
365 if (db) rfbLog("*** XIO error: disable_grabserver\n");
366 disable_grabserver(dpy, 0);
367 if (db) rfbLog("*** XIO error: xrecord\n");
368 zerodisp_xrecord();
369 initialize_xrecord();
370 if (db) rfbLog("*** XIO error: xdamage\n");
371 create_xdamage_if_needed(1);
372 if (db) rfbLog("*** XIO error: do_new_fb\n");
373 if (using_shm) {
374 if (db) rfbLog("*** XIO error: clean_shm\n");
375 clean_shm(1);
376 }
377 do_new_fb(1);
378 if (db) rfbLog("*** XIO error: check_xevents\n");
379 check_xevents(1);
380
381 /* sadly, we can never return... */
382 if (db) rfbLog("*** XIO error: watch_loop\n");
383 watch_loop();
384 clean_up_exit(1);
385 }
386 }
387 #endif
388
389 interrupted(-1);
390
391 if (d) {} /* unused vars warning: */
392
393 return (*XIOerr_def)(d);
394 }
395
396 static char *xerrors[] = {
397 "Success",
398 "BadRequest",
399 "BadValue",
400 "BadWindow",
401 "BadPixmap",
402 "BadAtom",
403 "BadCursor",
404 "BadFont",
405 "BadMatch",
406 "BadDrawable",
407 "BadAccess",
408 "BadAlloc",
409 "BadColor",
410 "BadGC",
411 "BadIDChoice",
412 "BadName",
413 "BadLength",
414 "BadImplementation",
415 "unknown"
416 };
417 static int xerrors_max = BadImplementation;
418
xerror_string(XErrorEvent * error)419 char *xerror_string(XErrorEvent *error) {
420 int index = -1;
421 if (error) {
422 index = (int) error->error_code;
423 }
424 if (0 <= index && index <= xerrors_max) {
425 return xerrors[index];
426 } else {
427 return xerrors[xerrors_max+1];
428 }
429 }
430
431 static char *crash_stack_command1 = NULL;
432 static char *crash_stack_command2 = NULL;
433 static char *crash_debug_command = NULL;
434
initialize_crash_handler(void)435 void initialize_crash_handler(void) {
436 int pid = program_pid;
437 crash_stack_command1 = (char *) malloc(1000);
438 crash_stack_command2 = (char *) malloc(1000);
439 crash_debug_command = (char *) malloc(1000);
440
441 snprintf(crash_stack_command1, 500, "echo where > /tmp/gdb.%d;"
442 " env PATH=$PATH:/usr/local/bin:/usr/sfw/bin:/usr/bin"
443 " gdb -x /tmp/gdb.%d -batch -n %s %d;"
444 " rm -f /tmp/gdb.%d", pid, pid, program_name, pid, pid);
445 snprintf(crash_stack_command2, 500, "pstack %d", program_pid);
446
447 snprintf(crash_debug_command, 500, "gdb %s %d", program_name, pid);
448 }
449
crash_shell_help(void)450 static void crash_shell_help(void) {
451 int pid = program_pid;
452 fprintf(stderr, "\n");
453 fprintf(stderr, " *** Welcome to the x11vnc crash shell! ***\n");
454 fprintf(stderr, "\n");
455 fprintf(stderr, "PROGRAM: %s PID: %d\n", program_name, pid);
456 fprintf(stderr, "\n");
457 fprintf(stderr, "POSSIBLE DEBUGGER COMMAND:\n");
458 fprintf(stderr, "\n");
459 fprintf(stderr, " %s\n", crash_debug_command);
460 fprintf(stderr, "\n");
461 fprintf(stderr, "Press \"q\" to quit.\n");
462 fprintf(stderr, "Press \"h\" or \"?\" for this help.\n");
463 fprintf(stderr, "Press \"s\" to try to run some commands to"
464 " show a stack trace (gdb/pstack).\n");
465 fprintf(stderr, "\n");
466 fprintf(stderr, "Anything else is passed to -Q query function.\n");
467 fprintf(stderr, "\n");
468 }
469
crash_shell(void)470 static void crash_shell(void) {
471 char qry[1000], cmd[1000], line[1000];
472 char *str, *p;
473
474 crash_shell_help();
475 fprintf(stderr, "\ncrash> ");
476 while (fgets(line, 1000, stdin) != NULL) {
477 str = lblanks(line);
478
479 p = str;
480 while(*p) {
481 if (*p == '\n') {
482 *p = '\0';
483 }
484 p++;
485 }
486
487 if (*str == 'q' && *(str+1) == '\0') {
488 fprintf(stderr, "quiting.\n");
489 return;
490 } else if (*str == 'h' && *(str+1) == '\0') {
491 crash_shell_help();
492 } else if (*str == '?' && *(str+1) == '\0') {
493 crash_shell_help();
494 } else if (*str == 's' && *(str+1) == '\0') {
495 sprintf(cmd, "sh -c '(%s) &'", crash_stack_command1);
496 /* crash */
497 if (no_external_cmds || !cmd_ok("crash")) {
498 fprintf(stderr, "\nno_external_cmds=%d\n",
499 no_external_cmds);
500 goto crash_prompt;
501 }
502 fprintf(stderr, "\nrunning:\n\t%s\n\n",
503 crash_stack_command1);
504 system(cmd);
505 usleep(1000*1000);
506
507 sprintf(cmd, "sh -c '(%s) &'", crash_stack_command2);
508 fprintf(stderr, "\nrunning:\n\t%s\n\n",
509 crash_stack_command2);
510 system(cmd);
511 usleep(1000*1000);
512 } else {
513 snprintf(qry, 1000, "qry=%s", str);
514 p = process_remote_cmd(qry, 1);
515 fprintf(stderr, "\n\nresult:\n%s\n", p);
516 free(p);
517 }
518
519 crash_prompt:
520 fprintf(stderr, "crash> ");
521 }
522 }
523
524 /*
525 * General problem handler
526 */
interrupted(int sig)527 static void interrupted (int sig) {
528 exit_sig = sig;
529 if (exit_flag) {
530 fprintf(stderr, "extra[%d] signal: %d\n", exit_flag, sig);
531 exit_flag++;
532 if (use_threads) {
533 usleep2(250 * 1000);
534 } else if (exit_flag <= 2) {
535 return;
536 }
537 if (rm_flagfile) {
538 unlink(rm_flagfile);
539 rm_flagfile = NULL;
540 }
541 exit(4);
542 }
543 exit_flag++;
544 if (sig == 0) {
545 fprintf(stderr, "caught X11 error:\n");
546 if (crash_debug) { crash_shell(); }
547 } else if (sig == -1) {
548 fprintf(stderr, "caught XIO error:\n");
549 } else {
550 fprintf(stderr, "caught signal: %d\n", sig);
551 }
552 if (sig == SIGINT) {
553 shut_down = 1;
554 return;
555 }
556
557 if (crash_debug) {
558 crash_shell();
559 }
560
561 X_UNLOCK;
562
563 if (icon_mode) {
564 clean_icon_mode();
565 }
566 /* remove the shm areas with quick=1: */
567 clean_shm(1);
568
569 if (sig == -1) {
570 /* not worth trying any more cleanup, X server probably gone */
571 if (rm_flagfile) {
572 unlink(rm_flagfile);
573 rm_flagfile = NULL;
574 }
575 exit(3);
576 }
577
578 /* X keyboard cleanups */
579 delete_added_keycodes(0);
580
581 if (clear_mods == 1) {
582 clear_modifiers(0);
583 } else if (clear_mods == 2) {
584 clear_keys();
585 } else if (clear_mods == 3) {
586 clear_keys();
587 clear_locks();
588 }
589 if (no_autorepeat) {
590 autorepeat(1, 0);
591 }
592 if (use_solid_bg) {
593 solid_bg(1);
594 }
595 if (ncache || ncache0) {
596 kde_no_animate(1);
597 }
598 stop_stunnel();
599
600 if (crash_debug) {
601 crash_shell();
602 }
603
604 if (sig) {
605 if (rm_flagfile) {
606 unlink(rm_flagfile);
607 rm_flagfile = NULL;
608 }
609 exit(2);
610 }
611 }
612
ignore_sigs(char * list)613 static void ignore_sigs(char *list) {
614 char *str, *p;
615 int ignore = 1;
616 if (list == NULL || *list == '\0') {
617 return;
618 }
619 str = strdup(list);
620 p = strtok(str, ":,");
621
622 #define SETSIG(x, y) \
623 if (strstr(p, x)) { \
624 if (ignore) { \
625 signal(y, SIG_IGN); \
626 } else { \
627 signal(y, interrupted); \
628 } \
629 }
630
631 #ifdef SIG_IGN
632 while (p) {
633 if (!strcmp(p, "ignore")) {
634 ignore = 1;
635 } else if (!strcmp(p, "exit")) {
636 ignore = 0;
637 }
638 /* Take off every 'sig' ;-) */
639 #ifdef SIGHUP
640 SETSIG("HUP", SIGHUP);
641 #endif
642 #ifdef SIGINT
643 SETSIG("INT", SIGINT);
644 #endif
645 #ifdef SIGQUIT
646 SETSIG("QUIT", SIGQUIT);
647 #endif
648 #ifdef SIGTRAP
649 SETSIG("TRAP", SIGTRAP);
650 #endif
651 #ifdef SIGABRT
652 SETSIG("ABRT", SIGABRT);
653 #endif
654 #ifdef SIGBUS
655 SETSIG("BUS", SIGBUS);
656 #endif
657 #ifdef SIGFPE
658 SETSIG("FPE", SIGFPE);
659 #endif
660 #ifdef SIGSEGV
661 SETSIG("SEGV", SIGSEGV);
662 #endif
663 #ifdef SIGPIPE
664 SETSIG("PIPE", SIGPIPE);
665 #endif
666 #ifdef SIGTERM
667 SETSIG("TERM", SIGTERM);
668 #endif
669 #ifdef SIGUSR1
670 SETSIG("USR1", SIGUSR1);
671 #endif
672 #ifdef SIGUSR2
673 SETSIG("USR2", SIGUSR2);
674 #endif
675 #ifdef SIGCONT
676 SETSIG("CONT", SIGCONT);
677 #endif
678 #ifdef SIGSTOP
679 SETSIG("STOP", SIGSTOP);
680 #endif
681 #ifdef SIGTSTP
682 SETSIG("TSTP", SIGTSTP);
683 #endif
684 p = strtok(NULL, ":,");
685 }
686 #endif /* SIG_IGN */
687 free(str);
688 }
689
690 /* signal handlers */
initialize_signals(void)691 void initialize_signals(void) {
692 signal(SIGHUP, interrupted);
693 signal(SIGINT, interrupted);
694 signal(SIGQUIT, interrupted);
695 signal(SIGABRT, interrupted);
696 signal(SIGTERM, interrupted);
697 signal(SIGBUS, interrupted);
698 signal(SIGSEGV, interrupted);
699 signal(SIGFPE, interrupted);
700
701 if (!sigpipe || *sigpipe == '\0' || !strcmp(sigpipe, "skip")) {
702 ;
703 } else if (strstr(sigpipe, "ignore:") == sigpipe) {
704 ignore_sigs(sigpipe);
705 } else if (strstr(sigpipe, "exit:") == sigpipe) {
706 ignore_sigs(sigpipe);
707 } else if (!strcmp(sigpipe, "ignore")) {
708 #ifdef SIG_IGN
709 signal(SIGPIPE, SIG_IGN);
710 #endif
711 } else if (!strcmp(sigpipe, "exit")) {
712 rfbLog("initialize_signals: will exit on SIGPIPE\n");
713 signal(SIGPIPE, interrupted);
714 }
715
716 #if NO_X11
717 return;
718 #else
719 X_LOCK;
720 Xerror_def = XSetErrorHandler(Xerror);
721 XIOerr_def = XSetIOErrorHandler(XIOerr);
722 X_UNLOCK;
723 #endif /* NO_X11 */
724 }
725
unset_signals(void)726 void unset_signals(void) {
727 signal(SIGHUP, SIG_DFL);
728 signal(SIGINT, SIG_DFL);
729 signal(SIGQUIT, SIG_DFL);
730 signal(SIGABRT, SIG_DFL);
731 signal(SIGTERM, SIG_DFL);
732 signal(SIGBUS, SIG_DFL);
733 signal(SIGSEGV, SIG_DFL);
734 signal(SIGFPE, SIG_DFL);
735 signal(SIGPIPE, SIG_DFL);
736 }
737
close_exec_fds(void)738 void close_exec_fds(void) {
739 int fd;
740 #ifdef FD_CLOEXEC
741 for (fd = 3; fd < 64; fd++) {
742 int flags = fcntl(fd, F_GETFD);
743 if (flags != -1) {
744 flags |= FD_CLOEXEC;
745 fcntl(fd, F_SETFD, flags);
746 }
747 }
748 #endif
749 }
750
known_sigpipe_mode(char * s)751 int known_sigpipe_mode(char *s) {
752 /*
753 * skip, ignore, exit
754 */
755 if (strstr(s, "ignore:") == s) {
756 return 1;
757 }
758 if (strstr(s, "exit:") == s) {
759 return 1;
760 }
761 if (strcmp(s, "skip") && strcmp(s, "ignore") &&
762 strcmp(s, "exit")) {
763 return 0;
764 } else {
765 return 1;
766 }
767 }
768
769
770