aboutsummaryrefslogtreecommitdiff
path: root/tests/server/tftpd.c
diff options
context:
space:
mode:
authorYang Tse <yangsita@gmail.com>2009-12-02 15:02:30 +0000
committerYang Tse <yangsita@gmail.com>2009-12-02 15:02:30 +0000
commit2286f566d03fdab4e3ab3d73998a42ffa95e801d (patch)
tree80ac536613a106dc7a8e5c952d4a7bf08e93bfac /tests/server/tftpd.c
parented2aa87e63fa29df0c60b609fd80645aa99d7c59 (diff)
signal handling to cleanup on SIGINT and SIGTERM
Diffstat (limited to 'tests/server/tftpd.c')
-rw-r--r--tests/server/tftpd.c495
1 files changed, 355 insertions, 140 deletions
diff --git a/tests/server/tftpd.c b/tests/server/tftpd.c
index 1f6418f59..9106836f2 100644
--- a/tests/server/tftpd.c
+++ b/tests/server/tftpd.c
@@ -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,63 +143,290 @@ 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 struct bf bfs[2];
+
+static int nextone; /* index of next buffer to use */
+static int current; /* index of buffer in use */
-static curl_socket_t peer;
-static int maxtimeout = 5*TIMEOUT;
+ /* control flags for crlf conversions */
+static int newline = 0; /* fillbuf: in middle of newline expansion */
+static int prevchar = -1; /* putbuf: previous char (cr check) */
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];
+static curl_socket_t peer = CURL_SOCKET_BAD;
- /* 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 timeout;
+static int maxtimeout = 5 * TIMEOUT;
-static int nextone; /* index of next buffer to use */
-static int current; /* index of buffer in use */
+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() */
- /* control flags for crlf conversions */
-static int newline = 0; /* fillbuf: in middle of newline expansion */
-static int prevchar = -1; /* putbuf: previous char (cr check) */
+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 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 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 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.