1
0
mirror of https://github.com/moparisthebest/curl synced 2025-02-28 17:31:46 -05:00

signal handling to cleanup on SIGINT and SIGTERM

This commit is contained in:
Yang Tse 2009-12-02 15:02:30 +00:00
parent ed2aa87e63
commit 2286f566d0

View File

@ -99,11 +99,11 @@
/* include memdebug.h last */
#include "memdebug.h"
#ifdef ENABLE_IPV6
static bool use_ipv6 = FALSE;
#endif
static const char *ipv_inuse = "IPv4";
static int serverlogslocked = 0;
/*****************************************************************************
* STRUCT DECLARATIONS AND DEFINES *
*****************************************************************************/
#define PKTSIZE (SEGSIZE + 4) /* SEGSIZE defined in arpa/tftp.h */
struct testcase {
char *buffer; /* holds the file data to send to the client */
@ -114,12 +114,28 @@ struct testcase {
int ofile; /* file descriptor for output file when uploading to us */
};
static int synchnet(curl_socket_t);
static struct tftphdr *r_init(void);
static struct tftphdr *w_init(void);
static int readit(struct testcase *test, struct tftphdr **dpp, int convert);
static int writeit(struct testcase *test, struct tftphdr **dpp, int ct,
int convert);
struct formats {
const char *f_mode;
int f_convert;
};
struct errmsg {
int e_code;
const char *e_msg;
};
/*
* bf.counter values in range [-1 .. SEGSIZE] represents size of data in the
* bf.buf buffer. Additionally it can also hold flags BF_ALLOC or BF_FREE.
*/
struct bf {
int counter; /* size of data in buffer, or flag */
char buf[PKTSIZE]; /* room for data packet */
};
#define BF_ALLOC -3 /* alloc'd but not yet filled */
#define BF_FREE -2 /* free */
#define opcode_RRQ 1
#define opcode_WRQ 2
@ -127,45 +143,43 @@ static int writeit(struct testcase *test, struct tftphdr **dpp, int ct,
#define opcode_ACK 4
#define opcode_ERROR 5
#define TIMEOUT 5
#define TIMEOUT 5
#define PKTSIZE SEGSIZE+4
#undef MIN
#define MIN(x,y) ((x)<(y)?(x):(y))
struct formats {
const char *f_mode;
int f_convert;
#ifndef DEFAULT_LOGFILE
#define DEFAULT_LOGFILE "log/tftpd.log"
#endif
#define REQUEST_DUMP "log/server.input"
#define DEFAULT_PORT 8999 /* UDP */
/*****************************************************************************
* GLOBAL VARIABLES *
*****************************************************************************/
static struct errmsg errmsgs[] = {
{ EUNDEF, "Undefined error code" },
{ ENOTFOUND, "File not found" },
{ EACCESS, "Access violation" },
{ ENOSPACE, "Disk full or allocation exceeded" },
{ EBADOP, "Illegal TFTP operation" },
{ EBADID, "Unknown transfer ID" },
{ EEXISTS, "File already exists" },
{ ENOUSER, "No such user" },
{ -1, 0 }
};
static struct formats formata[] = {
{ "netascii", 1 },
{ "octet", 0 },
{ NULL, 0 }
};
static int tftp(struct testcase *test, struct tftphdr *tp, ssize_t size);
static void nak(int error);
static void sendtftp(struct testcase *test, struct formats *pf);
static void recvtftp(struct testcase *test, struct formats *pf);
static int validate_access(struct testcase *test, const char *, int);
static curl_socket_t peer;
static int maxtimeout = 5*TIMEOUT;
static char buf[PKTSIZE];
static char ackbuf[PKTSIZE];
static struct sockaddr_in from;
static curl_socklen_t fromlen;
struct bf {
int counter; /* size of data in buffer, or flag */
char buf[PKTSIZE]; /* room for data packet */
};
static struct bf bfs[2];
/* Values for bf.counter */
#define BF_ALLOC -3 /* alloc'd but not yet filled */
#define BF_FREE -2 /* free */
/* [-1 .. SEGSIZE] = size of data in the data buffer */
static int nextone; /* index of next buffer to use */
static int current; /* index of buffer in use */
@ -173,17 +187,246 @@ static int current; /* index of buffer in use */
static int newline = 0; /* fillbuf: in middle of newline expansion */
static int prevchar = -1; /* putbuf: previous char (cr check) */
static void read_ahead(struct testcase *test,
int convert /* if true, convert to ascii */);
static ssize_t write_behind(struct testcase *test, int convert);
static struct tftphdr *rw_init(int);
static struct tftphdr *w_init(void) { return rw_init(0); } /* write-behind */
static struct tftphdr *r_init(void) { return rw_init(1); } /* read-ahead */
static char buf[PKTSIZE];
static char ackbuf[PKTSIZE];
static struct tftphdr *
rw_init(int x) /* init for either read-ahead or write-behind */
{ /* zero for write-behind, one for read-head */
newline = 0; /* init crlf flag */
static struct sockaddr_in from;
static curl_socklen_t fromlen;
static curl_socket_t peer = CURL_SOCKET_BAD;
static int timeout;
static int maxtimeout = 5 * TIMEOUT;
static unsigned short sendblock; /* block count used by sendtftp() */
static struct tftphdr *sdp; /* data buffer used by sendtftp() */
static struct tftphdr *sap; /* ack buffer used by sendtftp() */
static unsigned short recvblock; /* block count used by recvtftp() */
static struct tftphdr *rdp; /* data buffer used by recvtftp() */
static struct tftphdr *rap; /* ack buffer used by recvtftp() */
#ifdef ENABLE_IPV6
static bool use_ipv6 = FALSE;
#endif
static const char *ipv_inuse = "IPv4";
const char *serverlogfile = DEFAULT_LOGFILE;
static char *pidname= (char *)".tftpd.pid";
static int serverlogslocked = 0;
static int wrotepidfile = 0;
#ifdef HAVE_SIGSETJMP
static sigjmp_buf timeoutbuf;
#endif
#if defined(HAVE_ALARM) && defined(SIGALRM)
static int rexmtval = TIMEOUT;
#endif
/* do-nothing macro replacement for systems which lack siginterrupt() */
#ifndef HAVE_SIGINTERRUPT
#define siginterrupt(x,y) do {} while(0)
#endif
/* vars used to keep around previous signal handlers */
typedef RETSIGTYPE (*SIGHANDLER_T)(int);
#ifdef SIGHUP
static SIGHANDLER_T old_sighup_handler = SIG_ERR;
#endif
#ifdef SIGPIPE
static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
#endif
#ifdef SIGINT
static SIGHANDLER_T old_sigint_handler = SIG_ERR;
#endif
#ifdef SIGTERM
static SIGHANDLER_T old_sigterm_handler = SIG_ERR;
#endif
/* var which if set indicates that the program should finish execution */
SIG_ATOMIC_T got_exit_signal = 0;
/* if next is set indicates the first signal handled in exit_signal_handler */
static volatile int exit_signal = 0;
/*****************************************************************************
* FUNCTION PROTOTYPES *
*****************************************************************************/
static struct tftphdr *rw_init(int);
static struct tftphdr *w_init(void);
static struct tftphdr *r_init(void);
static int readit(struct testcase *test,
struct tftphdr **dpp,
int convert);
static int writeit(struct testcase *test,
struct tftphdr **dpp,
int ct,
int convert);
static void read_ahead(struct testcase *test, int convert);
static ssize_t write_behind(struct testcase *test, int convert);
static int synchnet(curl_socket_t);
static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size);
static int validate_access(struct testcase *test, const char *fname, int mode);
static void sendtftp(struct testcase *test, struct formats *pf);
static void recvtftp(struct testcase *test, struct formats *pf);
static void nak(int error);
#if defined(HAVE_ALARM) && defined(SIGALRM)
static void mysignal(int sig, void (*handler)(int));
static void timer(int signum);
static void justtimeout(int signum);
#endif /* HAVE_ALARM && SIGALRM */
static RETSIGTYPE exit_signal_handler(int signum);
static void install_signal_handlers(void);
static void restore_signal_handlers(void);
/*****************************************************************************
* FUNCTION IMPLEMENTATIONS *
*****************************************************************************/
#if defined(HAVE_ALARM) && defined(SIGALRM)
/*
* Like signal(), but with well-defined semantics.
*/
static void mysignal(int sig, void (*handler)(int))
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handler;
sigaction(sig, &sa, NULL);
}
static void timer(int signum)
{
(void)signum;
logmsg("alarm!");
timeout += rexmtval;
if(timeout >= maxtimeout) {
if(wrotepidfile) {
wrotepidfile = 0;
unlink(pidname);
}
if(serverlogslocked) {
serverlogslocked = 0;
clear_advisor_read_lock(SERVERLOGS_LOCK);
}
exit(1);
}
#ifdef HAVE_SIGSETJMP
siglongjmp(timeoutbuf, 1);
#endif
}
static void justtimeout(int signum)
{
(void)signum;
}
#endif /* HAVE_ALARM && SIGALRM */
/* signal handler that will be triggered to indicate that the program
should finish its execution in a controlled manner as soon as possible.
The first time this is called it will set got_exit_signal to one and
store in exit_signal the signal that triggered its execution. */
static RETSIGTYPE exit_signal_handler(int signum)
{
int old_errno = ERRNO;
if(got_exit_signal == 0) {
got_exit_signal = 1;
exit_signal = signum;
}
(void)signal(signum, exit_signal_handler);
SET_ERRNO(old_errno);
}
static void install_signal_handlers(void)
{
#ifdef SIGHUP
/* ignore SIGHUP signal */
if((old_sighup_handler = signal(SIGHUP, SIG_IGN)) == SIG_ERR)
logmsg("cannot install SIGHUP handler: %s", strerror(ERRNO));
#endif
#ifdef SIGPIPE
/* ignore SIGPIPE signal */
if((old_sigpipe_handler = signal(SIGPIPE, SIG_IGN)) == SIG_ERR)
logmsg("cannot install SIGPIPE handler: %s", strerror(ERRNO));
#endif
#ifdef SIGINT
/* handle SIGINT signal with our exit_signal_handler */
if((old_sigint_handler = signal(SIGINT, exit_signal_handler)) == SIG_ERR)
logmsg("cannot install SIGINT handler: %s", strerror(ERRNO));
else
siginterrupt(SIGINT, 1);
#endif
#ifdef SIGTERM
/* handle SIGTERM signal with our exit_signal_handler */
if((old_sigterm_handler = signal(SIGTERM, exit_signal_handler)) == SIG_ERR)
logmsg("cannot install SIGTERM handler: %s", strerror(ERRNO));
else
siginterrupt(SIGTERM, 1);
#endif
}
static void restore_signal_handlers(void)
{
#ifdef SIGHUP
if(SIG_ERR != old_sighup_handler)
(void)signal(SIGHUP, old_sighup_handler);
#endif
#ifdef SIGPIPE
if(SIG_ERR != old_sigpipe_handler)
(void)signal(SIGPIPE, old_sigpipe_handler);
#endif
#ifdef SIGINT
if(SIG_ERR != old_sigint_handler)
(void)signal(SIGINT, old_sigint_handler);
#endif
#ifdef SIGTERM
if(SIG_ERR != old_sigterm_handler)
(void)signal(SIGTERM, old_sigterm_handler);
#endif
}
/*
* init for either read-ahead or write-behind.
* zero for write-behind, one for read-head.
*/
static struct tftphdr *rw_init(int x)
{
newline = 0; /* init crlf flag */
prevchar = -1;
bfs[0].counter = BF_ALLOC; /* pass out the first buffer */
current = 0;
@ -192,6 +435,15 @@ rw_init(int x) /* init for either read-ahead or write-behind */
return (struct tftphdr *)bfs[0].buf;
}
static struct tftphdr *w_init(void)
{
return rw_init(0); /* write-behind */
}
static struct tftphdr *r_init(void)
{
return rw_init(1); /* read-ahead */
}
/* Have emptied current buffer by sending to net and getting ack.
Free it and return next buffer filled with data.
@ -212,9 +464,6 @@ static int readit(struct testcase *test, struct tftphdr **dpp,
return b->counter;
}
#undef MIN /* some systems have this defined already, some don't */
#define MIN(x,y) ((x)<(y)?(x):(y));
/*
* fill the input buffer, doing ascii conversions if requested
* conversions are lf -> cr,lf and cr -> cr, nul
@ -354,7 +603,6 @@ static ssize_t write_behind(struct testcase *test, int convert)
return count;
}
/* When an error has occurred, it is possible that the two sides are out of
* synch. Ie: that what I think is the other side's response to packet N is
* really their response to packet N-1.
@ -397,29 +645,6 @@ static int synchnet(curl_socket_t f /* socket to flush */)
return j;
}
#if defined(HAVE_ALARM) && defined(SIGALRM)
/*
* Like signal(), but with well-defined semantics.
*/
static void mysignal(int sig, void (*handler)(int))
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handler;
sigaction(sig, &sa, NULL);
}
#endif
#ifndef DEFAULT_LOGFILE
#define DEFAULT_LOGFILE "log/tftpd.log"
#endif
#define DEFAULT_PORT 8999 /* UDP */
const char *serverlogfile = DEFAULT_LOGFILE;
#define REQUEST_DUMP "log/server.input"
int main(int argc, char **argv)
{
struct sockaddr_in me;
@ -430,12 +655,12 @@ int main(int argc, char **argv)
struct tftphdr *tp;
ssize_t n = 0;
int arg = 1;
char *pidname= (char *)".tftpd.pid";
unsigned short port = DEFAULT_PORT;
curl_socket_t sock = CURL_SOCKET_BAD;
int flag;
int rc;
int error;
long pid;
struct testcase test;
int result = 0;
@ -477,6 +702,10 @@ int main(int argc, char **argv)
atexit(win32_cleanup);
#endif
install_signal_handlers();
pid = (long)getpid();
#ifdef ENABLE_IPV6
if(!use_ipv6)
#endif
@ -490,7 +719,8 @@ int main(int argc, char **argv)
error = SOCKERRNO;
logmsg("Error creating socket: (%d) %s",
error, strerror(error));
return 1;
result = 1;
goto tftpd_cleanup;
}
flag = 1;
@ -499,8 +729,8 @@ int main(int argc, char **argv)
error = SOCKERRNO;
logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
error, strerror(error));
sclose(sock);
return 1;
result = 1;
goto tftpd_cleanup;
}
#ifdef ENABLE_IPV6
@ -525,13 +755,14 @@ int main(int argc, char **argv)
error = SOCKERRNO;
logmsg("Error binding socket on port %hu: (%d) %s",
port, error, strerror(error));
sclose(sock);
return 1;
result = 1;
goto tftpd_cleanup;
}
if(!write_pidfile(pidname)) {
sclose(sock);
return 1;
wrotepidfile = write_pidfile(pidname);
if(!wrotepidfile) {
result = 1;
goto tftpd_cleanup;
}
logmsg("Running %s version on port UDP/%d", ipv_inuse, (int)port);
@ -540,6 +771,8 @@ int main(int argc, char **argv)
fromlen = sizeof(from);
n = (ssize_t)recvfrom(sock, buf, sizeof(buf), 0,
(struct sockaddr *)&from, &fromlen);
if(got_exit_signal)
break;
if (n < 0) {
logmsg("recvfrom");
result = 3;
@ -569,12 +802,16 @@ int main(int argc, char **argv)
tp->th_opcode = ntohs(tp->th_opcode);
if (tp->th_opcode == opcode_RRQ || tp->th_opcode == opcode_WRQ) {
memset(&test, 0, sizeof(test));
if (tftp(&test, tp, n) < 0)
if (do_tftp(&test, tp, n) < 0)
break;
if(test.buffer)
free(test.buffer);
}
sclose(peer);
peer = CURL_SOCKET_BAD;
if(got_exit_signal)
break;
if(serverlogslocked) {
serverlogslocked = 0;
@ -585,18 +822,46 @@ int main(int argc, char **argv)
}
tftpd_cleanup:
if((peer != sock) && (peer != CURL_SOCKET_BAD))
sclose(peer);
if(sock != CURL_SOCKET_BAD)
sclose(sock);
if(got_exit_signal)
logmsg("signalled to die");
if(wrotepidfile)
unlink(pidname);
if(serverlogslocked) {
serverlogslocked = 0;
clear_advisor_read_lock(SERVERLOGS_LOCK);
}
restore_signal_handlers();
if(got_exit_signal) {
logmsg("========> %s tftpd (port: %d pid: %ld) exits with signal (%d)",
ipv_inuse, (int)port, pid, exit_signal);
/*
* To properly set the return status of the process we
* must raise the same signal SIGINT or SIGTERM that we
* caught and let the old handler take care of it.
*/
raise(exit_signal);
}
logmsg("========> tftpd quits");
return result;
}
/*
* Handle initial connection protocol.
*/
static int tftp(struct testcase *test, struct tftphdr *tp, ssize_t size)
static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size)
{
char *cp;
int first = 1, ecode;
@ -758,42 +1023,6 @@ static int validate_access(struct testcase *test,
return 0;
}
static int timeout;
#ifdef HAVE_SIGSETJMP
static sigjmp_buf timeoutbuf;
#endif
#if defined(HAVE_ALARM) && defined(SIGALRM)
static int rexmtval = TIMEOUT;
static void timer(int signum)
{
(void)signum;
logmsg("alarm!");
timeout += rexmtval;
if(timeout >= maxtimeout) {
if(serverlogslocked) {
serverlogslocked = 0;
clear_advisor_read_lock(SERVERLOGS_LOCK);
}
exit(1);
}
#ifdef HAVE_SIGSETJMP
siglongjmp(timeoutbuf, 1);
#endif
}
static void justtimeout(int signum)
{
(void)signum;
}
#endif /* HAVE_ALARM && SIGALRM */
static unsigned short sendblock;
static struct tftphdr *sdp;
static struct tftphdr *sap; /* ack packet */
/*
* Send the requested file.
*/
@ -833,6 +1062,8 @@ static void sendtftp(struct testcase *test, struct formats *pf)
#ifdef HAVE_ALARM
alarm(0);
#endif
if(got_exit_signal)
return;
if (n < 0) {
logmsg("read: fail");
return;
@ -861,10 +1092,6 @@ static void sendtftp(struct testcase *test, struct formats *pf)
} while (size == SEGSIZE);
}
static unsigned short recvblock;
static struct tftphdr *rdp;
static struct tftphdr *rap; /* ack buffer */
/*
* Receive a file.
*/
@ -899,6 +1126,8 @@ send_ack:
#ifdef HAVE_ALARM
alarm(0);
#endif
if(got_exit_signal)
goto abort;
if (n < 0) { /* really? */
logmsg("read: fail\n");
goto abort;
@ -940,6 +1169,8 @@ send_ack:
#ifdef HAVE_ALARM
alarm(0);
#endif
if(got_exit_signal)
goto abort;
if (n >= 4 && /* if read some data */
rdp->th_opcode == opcode_DATA && /* and got a data block */
recvblock == rdp->th_block) { /* then my last ack was lost */
@ -949,22 +1180,6 @@ abort:
return;
}
struct errmsg {
int e_code;
const char *e_msg;
};
static struct errmsg errmsgs[] = {
{ EUNDEF, "Undefined error code" },
{ ENOTFOUND, "File not found" },
{ EACCESS, "Access violation" },
{ ENOSPACE, "Disk full or allocation exceeded" },
{ EBADOP, "Illegal TFTP operation" },
{ EBADID, "Unknown transfer ID" },
{ EEXISTS, "File already exists" },
{ ENOUSER, "No such user" },
{ -1, 0 }
};
/*
* Send a nak packet (error message). Error code passed in is one of the
* standard TFTP codes, or a UNIX errno offset by 100.