1 /*
2  * ftpsigs.c -- handles signals
3  *
4  * Yet Another FTP Client
5  * Copyright (C) 1998-2001, Martin Hedenfalk <mhe@stacken.kth.se>
6  *
7  * This program 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
10  * (at your option) any later version. See COPYING for more details.
11  */
12 
13 #include "syshdr.h"
14 #include "ftp.h"
15 #include "ftpsigs.h"
16 #include "gvars.h"
17 #include "transfer.h"
18 #include "cmd.h"
19 
ftp_set_signal_with_mask(int signum,sighandler_t handler,int onesig)20 void ftp_set_signal_with_mask(int signum, sighandler_t handler, int onesig)
21 {
22 	int saved_errno = errno;
23 #ifdef HAVE_POSIX_SIGNALS
24 	struct sigaction sa;
25 	sigset_t ss;
26 
27 	/* unblock this signal */
28 	sigemptyset(&ss);
29 	sigaddset(&ss, signum);
30 	sigprocmask(SIG_UNBLOCK, &ss, 0);
31 
32 	/* set the signal handler */
33 	sa.sa_handler = handler;
34 	sigemptyset(&sa.sa_mask);
35 	if(onesig)
36 		sigaddset(&sa.sa_mask, onesig); /* a signal to exclude */
37 	sa.sa_flags = 0;
38 #ifdef SA_RESTART
39 	sa.sa_flags |= SA_RESTART;
40 #endif
41 
42 # if 0 /* this causes segfaults!? */
43 	if(signum == SIGALRM) {
44 #  ifdef SA_INTERRUPT
45 		sa.sa_flags |= SA_INTERRUPT; /* old, obsolete flag? */
46 #  endif
47 	} else {
48 #  ifdef SA_RESTART
49 		sa.sa_flags |= SA_RESTART;
50 #  endif
51 	}
52 # endif
53 
54 	/*	sa.sa_restorer = 0;*/
55 
56 	if(sigaction(signum, &sa, 0) != 0)
57 		ftp_trace("couldn't set signalhandler for signal %d\n", signum);
58 #else
59 	signal(signum, handler);
60 #endif
61 	errno = saved_errno;
62 }
63 
ftp_set_signal(int signum,sighandler_t handler)64 void ftp_set_signal(int signum, sighandler_t handler)
65 {
66 	ftp_set_signal_with_mask(signum, handler, 0);
67 }
68 
69 static unsigned int sigints = 0;
70 
sigint_close_handler(int signum)71 void sigint_close_handler(int signum)
72 {
73 	if(gvSighupReceived)
74 		return;
75 
76 	ftp_trace("Interrupt received\n");
77 	sigints++;
78 
79 	gvInterrupted = true;
80 
81 	/* close connection and restart command loop after 4 interrupts
82 	 *
83 	 * If we're in SSH mode, the interrupt signal is intercepted by
84 	 * the ssh program as well, which terminates directly. The ssh
85 	 * program is started by ssh_connect() in ssh_ftp.c by forking and
86 	 * execv'ing the program in the child process. I don't know if
87 	 * there is a way to prevent the interrupt signal to reach the ssh
88 	 * program?
89 	 */
90 
91 	if (sigints >= 4
92 #ifdef HAVE_LIBSSH
93 			|| ftp->session
94 #endif
95 		) {
96 		ftp_close();
97 		sigints = 0;
98 		if(gvJmpBufSet) {
99 			ftp_trace("jumping to gvRestartJmp\n");
100 			ftp_longjmp(gvRestartJmp, 1);
101 		} else {
102 			exit(17);
103 		}
104 	}
105 	else if(sigints == 3) {
106 		if(gvJmpBufSet)
107 			ftp_err(_("OK, one more to close connection...          \n"));
108 		else
109 			ftp_err(_("OK, one more to exit program...          \n"));
110 	}
111 
112 	/* disable any alarm set */
113 	alarm(0);
114 	ftp_set_signal(SIGALRM, SIG_DFL);
115 
116 	/* re-install the signal handler */
117 /*	ftp_set_signal(SIGINT, sigint_close_handler);*/
118 }
119 
sigint_abort_handler(int signum)120 void sigint_abort_handler(int signum)
121 {
122 	if(gvSighupReceived)
123 		return;
124 
125 	ftp_trace("Interrupt received\n");
126 	sigints++;
127 
128 	gvInterrupted = true;
129 
130 	if (sigints >= 3
131 #ifdef HAVE_LIBSSH
132 			|| ftp->session
133 #endif
134 		) {
135 		sigints = 0;
136 		alarm(0);
137 		if(gvJmpBufSet) {
138 			ftp_trace("jumping to gvRestartJmp\n");
139 			ftp_flush_reply();
140 			ftp_longjmp(gvRestartJmp, 1);
141 		} else
142 			exit(17);
143 	} else if(sigints == 2)
144 		ftp_err(_("OK, one more to abort command...          \n"));
145 
146 	/* re-install the signal handler */
147 /*	ftp_set_signal(SIGINT, sigint_abort_handler);*/
148 }
149 
sigint_jmp_handler(int signum)150 void sigint_jmp_handler(int signum)
151 {
152 	if(gvSighupReceived)
153 		return;
154 
155 	ftp_trace("Interrupt received         \n");
156 	alarm(0);
157 	if(gvJmpBufSet) {
158 		ftp_trace("jumping to gvRestartJmp\n");
159 #ifdef HAVE_LIBSSH
160 		if(ftp->session)
161 			ftp_close();
162 #endif
163 		ftp_longjmp(gvRestartJmp, 1);
164 	} else {
165 		ftp_err(_("Interrupt received, exiting...         \n"));
166 		exit(17);
167 	}
168 }
169 
170 #if 0
171 static void sighup_term_handler(int signum)
172 {
173 	printf(_("SIGTERM (terminate) received, exiting...\n"));
174 	ftp_close();
175 	ftp_destroy(gvFtp);
176 	gvars_destroy();
177 	exit(19);
178 }
179 #endif
180 
sighup_handler(int signum)181 void sighup_handler(int signum)
182 {
183 	ftp_set_signal(SIGINT, SIG_IGN);
184 	ftp_set_signal(SIGHUP, sighup_handler);
185 	if(gvSighupReceived)
186 		return;
187 
188 	gvSighupReceived = true;
189 	if(gvInTransfer)
190 		ftp_err(_("Hangup received, continuing transfer in background...\n"));
191 	else {
192 		ftp_err(_("Hangup received, exiting...\n"));
193 		exit_yafc();
194 	}
195 
196 	if(transfer_init_nohup(0) != 0) {
197 		if(transfer_init_nohup("/dev/null") != 0) {
198 			ftp_err(_("Can't redirect output, exiting...\n"));
199 			ftp_quit();
200 			exit(18);
201 		}
202 	}
203 
204 	transfer_begin_nohup(0, 0);
205 	sigints = 0;
206 }
207 
ftp_longjmp(sigjmp_buf restart_jmp,int arg)208 int ftp_longjmp(
209 #ifdef HAVE_POSIX_SIGSETJMP
210 				sigjmp_buf restart_jmp
211 #else
212 				jmp_buf restart_jmp
213 #endif
214 				, int arg)
215 {
216 #ifdef HAVE_POSIX_SIGSETJMP
217 	siglongjmp(restart_jmp, 1);
218 #else
219 	longjmp(restart_jmp, 1);
220 #endif
221 	return 0; /* make cc happy */
222 }
223 
ftp_initsigs(void)224 void ftp_initsigs(void)
225 {
226 	sigints = 0;
227 	ftp_set_signal(SIGPIPE, SIG_IGN);
228 	ftp_set_signal(SIGINT, gvSighupReceived ? SIG_IGN : sigint_jmp_handler);
229 	ftp_set_signal_with_mask(SIGHUP, sighup_handler, SIGINT);
230 }
231 
ftp_sigint_close_reset(void)232 void ftp_sigint_close_reset(void)
233 {
234 	sigints = 0;
235 	ftp_initsigs();
236 }
237 
ftp_set_close_handler(void)238 void ftp_set_close_handler(void)
239 {
240 	sigints = 0;
241 	ftp_set_signal(SIGINT, gvSighupReceived ? SIG_IGN : sigint_close_handler);
242 }
243 
ftp_set_abort_handler(void)244 void ftp_set_abort_handler(void)
245 {
246 	sigints = 0;
247 	ftp_set_signal(SIGINT, gvSighupReceived ? SIG_IGN : sigint_abort_handler);
248 }
249 
ftp_sigints(void)250 unsigned int ftp_sigints(void)
251 {
252 	return sigints;
253 }
254