2008-01-27 12:00:08 +00:00
|
|
|
/* $Id: xioshutdown.c,v 1.21 2007/01/25 21:36:11 gerhard Exp $ */
|
|
|
|
/* Copyright Gerhard Rieger 2001-2007 */
|
|
|
|
/* Published under the GNU General Public License V.2, see file COPYING */
|
|
|
|
|
|
|
|
/* this is the source of the extended shutdown function */
|
|
|
|
|
|
|
|
|
|
|
|
#include "xiosysincludes.h"
|
|
|
|
#include "xioopen.h"
|
|
|
|
|
2008-02-17 13:59:16 +00:00
|
|
|
|
|
|
|
static int xioshut_sleep_kill(pid_t sub, unsigned long usec, int sig);
|
|
|
|
|
2008-01-27 12:00:08 +00:00
|
|
|
static pid_t socat_kill_pid; /* here we pass the pid to be killed in sighandler */
|
|
|
|
|
|
|
|
static void signal_kill_pid(int dummy) {
|
|
|
|
Notice("SIGALRM while waiting for w/o child process to die, killing it now");
|
|
|
|
Kill(socat_kill_pid, SIGTERM);
|
|
|
|
}
|
|
|
|
|
2008-02-17 13:59:16 +00:00
|
|
|
/* how: SHUT_RD, SHUT_WR, or SHUT_RDWR */
|
2008-01-27 12:00:08 +00:00
|
|
|
int xioshutdown(xiofile_t *sock, int how) {
|
|
|
|
int result = 0;
|
|
|
|
|
2008-02-17 13:59:16 +00:00
|
|
|
Debug2("xioshutdown(%p, %d)", sock, how);
|
|
|
|
Debug2("xioshutdown(): dtype=0x%x, howtoshut=0x%04x",
|
|
|
|
sock->stream.dtype, sock->stream.howtoshut);
|
|
|
|
|
2008-01-27 12:00:08 +00:00
|
|
|
if (sock->tag == XIO_TAG_INVALID) {
|
|
|
|
Error("xioshutdown(): invalid file descriptor");
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-02-17 13:59:16 +00:00
|
|
|
/*Debug3("xioshutdown: flags=%d, dtype=%d, howtoclose=%d", sock->stream.flags, sock->stream.dtype, sock->stream.howtoclose);*/
|
2008-01-27 12:00:08 +00:00
|
|
|
if (sock->tag == XIO_TAG_DUAL) {
|
|
|
|
if ((how+1)&1) {
|
|
|
|
result = xioshutdown((xiofile_t *)sock->dual.stream[0], 0);
|
|
|
|
}
|
|
|
|
if ((how+1)&2) {
|
|
|
|
result |= xioshutdown((xiofile_t *)sock->dual.stream[1], 1);
|
|
|
|
}
|
2008-02-17 13:59:16 +00:00
|
|
|
return result;
|
|
|
|
}
|
2008-01-27 12:00:08 +00:00
|
|
|
|
2008-02-17 13:59:16 +00:00
|
|
|
/* let us bring how nearer to the resulting action */
|
|
|
|
if ((sock->stream.flags&XIO_ACCMODE) == XIO_WRONLY) {
|
|
|
|
how = ((how+1) & ~(SHUT_RD+1)) - 1;
|
|
|
|
} else if ((sock->stream.flags&XIO_ACCMODE) == XIO_RDONLY) {
|
|
|
|
how = ((how+1) & ~(SHUT_WR+1)) - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* here handle special shutdown functions */
|
|
|
|
switch (sock->stream.howtoshut) {
|
2008-01-27 12:00:08 +00:00
|
|
|
#if WITH_OPENSSL
|
2008-02-17 13:59:16 +00:00
|
|
|
case XIOSHUT_OPENSSL:
|
|
|
|
sycSSL_shutdown(sock->stream.para.openssl.ssl);
|
2008-01-27 12:00:08 +00:00
|
|
|
/*! what about half/full close? */
|
2008-02-17 13:59:16 +00:00
|
|
|
return 0;
|
2008-01-27 12:00:08 +00:00
|
|
|
#endif /* WITH_OPENSSL */
|
2008-02-17 13:59:16 +00:00
|
|
|
default: break;
|
|
|
|
}
|
2008-01-27 12:00:08 +00:00
|
|
|
|
2008-02-17 13:59:16 +00:00
|
|
|
if (how == SHUT_RDWR) {
|
|
|
|
/* in this branch we handle only shutdown actions where read and write
|
|
|
|
shutdown are not independent */
|
|
|
|
|
|
|
|
switch (sock->stream.howtoshut) {
|
|
|
|
#if 0
|
|
|
|
case XIODATA_STREAM:
|
|
|
|
switch (sock->stream.howtoclose) {
|
2008-01-27 12:00:08 +00:00
|
|
|
#if WITH_SOCKET
|
2008-02-17 13:59:16 +00:00
|
|
|
case END_SHUTDOWN:
|
|
|
|
if ((result = Shutdown(sock->stream.fd1, how)) < 0) {
|
2008-01-27 12:00:08 +00:00
|
|
|
Info3("shutdown(%d, %d): %s",
|
2008-02-17 13:59:16 +00:00
|
|
|
sock->stream.fd1, how, strerror(errno));
|
2008-01-27 12:00:08 +00:00
|
|
|
}
|
2008-02-17 13:59:16 +00:00
|
|
|
break;
|
|
|
|
case END_SHUTDOWN_KILL:
|
|
|
|
if ((result = Shutdown(sock->stream.fd1, how)) < 0) {
|
2008-01-27 12:00:08 +00:00
|
|
|
Info3("shutdown(%d, %d): %s",
|
2008-02-17 13:59:16 +00:00
|
|
|
sock->stream.fd1, how, strerror(errno));
|
2008-01-27 12:00:08 +00:00
|
|
|
}
|
2008-02-17 13:59:16 +00:00
|
|
|
break;
|
|
|
|
#endif /* WITH_SOCKET */
|
|
|
|
case END_CLOSE:
|
|
|
|
Close(sock->stream.fd1);
|
|
|
|
#if WITH_TERMIOS
|
|
|
|
if (sock->stream.ttyvalid) {
|
|
|
|
if (Tcsetattr(sock->stream.fd1, 0, &sock->stream.savetty) < 0) {
|
|
|
|
Warn2("cannot restore terminal settings on fd %d: %s",
|
|
|
|
sock->stream.fd1, strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* WITH_TERMIOS */
|
|
|
|
/*PASSTHROUGH*/
|
|
|
|
case END_NONE:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Error1("xioshutdown(): bad end action 0x%x", sock->stream.howtoclose);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#if WITH_SOCKET
|
|
|
|
case XIODATA_RECVFROM:
|
2008-01-27 12:00:08 +00:00
|
|
|
if (how >= 1) {
|
2008-02-17 13:59:16 +00:00
|
|
|
if (Close(sock->stream.fd1) < 0) {
|
2008-01-27 12:00:08 +00:00
|
|
|
Info2("close(%d): %s",
|
2008-02-17 13:59:16 +00:00
|
|
|
sock->stream.fd1, strerror(errno));
|
2008-01-27 12:00:08 +00:00
|
|
|
}
|
|
|
|
sock->stream.eof = 2;
|
2008-02-17 13:59:16 +00:00
|
|
|
sock->stream.fd1 = -1;
|
2008-01-27 12:00:08 +00:00
|
|
|
}
|
2008-02-17 13:59:16 +00:00
|
|
|
break;
|
2008-01-27 12:00:08 +00:00
|
|
|
#endif /* WITH_SOCKET */
|
2008-02-17 13:59:16 +00:00
|
|
|
#endif /* 0 */
|
|
|
|
default:
|
|
|
|
Error1("xioshutdown(): bad data type specification 0x%x", sock->stream.dtype);
|
2008-01-27 12:00:08 +00:00
|
|
|
return -1;
|
2008-02-17 13:59:16 +00:00
|
|
|
}
|
|
|
|
}
|
2008-01-27 12:00:08 +00:00
|
|
|
|
2008-02-17 13:59:16 +00:00
|
|
|
if ((how+1) & 1) { /* contains SHUT_RD */
|
|
|
|
switch (sock->stream.dtype & XIODATA_READMASK) {
|
|
|
|
/* shutdown read channel */
|
|
|
|
|
|
|
|
case XIOREAD_STREAM:
|
|
|
|
case XIODATA_2PIPE:
|
|
|
|
if (Close(sock->stream.fd1) < 0) {
|
|
|
|
Info2("close(%d): %s",
|
|
|
|
sock->stream.fd1, strerror(errno));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2008-01-27 12:00:08 +00:00
|
|
|
}
|
2008-02-17 13:59:16 +00:00
|
|
|
|
|
|
|
if ((how+1) & 2) { /* contains SHUT_WR */
|
|
|
|
/* shutdown write channel */
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
if (sock->stream.fdtype == FDTYPE_DOUBLE) {
|
|
|
|
fd = sock->stream.fd2;
|
|
|
|
} else {
|
|
|
|
fd = sock->stream.fd1;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (sock->stream.howtoshut & XIOSHUTWR_MASK) {
|
|
|
|
|
|
|
|
case XIOSHUTWR_CLOSE:
|
|
|
|
if (Close(fd) < 0) {
|
|
|
|
Info2("close(%d): %s", fd, strerror(errno));
|
|
|
|
}
|
|
|
|
/*PASSTHROUGH*/
|
|
|
|
case XIOSHUTWR_NONE:
|
|
|
|
break;
|
|
|
|
|
|
|
|
#if WITH_SOCKET
|
|
|
|
case XIOSHUTWR_DOWN:
|
|
|
|
if (Shutdown(fd, SHUT_WR) < 0) {
|
|
|
|
Info2("shutdown(%d, SHUT_WR): %s", fd, strerror(errno));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif /* WITH_SOCKET */
|
|
|
|
|
2008-01-27 12:00:08 +00:00
|
|
|
#if 0
|
2008-02-17 13:59:16 +00:00
|
|
|
case XIOSHUTWR_DOWN_KILL:
|
|
|
|
if (Shutdown(fd, SHUT_WR) < 0) {
|
|
|
|
Info2("shutdown(%d, SHUT_WR): %s", fd, strerror(errno));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
case XIOSHUTWR_SIGHUP:
|
|
|
|
/* the child process might want to flush some data before
|
|
|
|
terminating */
|
|
|
|
xioshut_sleep_kill(sock->stream.child.pid, 0, SIGHUP);
|
|
|
|
break;
|
|
|
|
case XIOSHUTWR_SIGTERM:
|
|
|
|
/* the child process might want to flush some data before
|
|
|
|
terminating */
|
|
|
|
xioshut_sleep_kill(sock->stream.child.pid, 1000000, SIGTERM);
|
|
|
|
break;
|
|
|
|
case XIOSHUTWR_SIGKILL:
|
|
|
|
/* the child process might want to flush some data before
|
|
|
|
terminating */
|
|
|
|
xioshut_sleep_kill(sock->stream.child.pid, 1000000, SIGKILL);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Error1("xioshutdown(): unhandled howtoshut=0x%x during SHUT_WR",
|
|
|
|
sock->stream.howtoshut);
|
2008-01-27 12:00:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2008-02-17 13:59:16 +00:00
|
|
|
|
|
|
|
/* wait some time and then send signal to sub process. This is useful after
|
|
|
|
shutting down the connection to give process some time to flush its output
|
|
|
|
data */
|
|
|
|
static int xioshut_sleep_kill(pid_t sub, unsigned long usec, int sig) {
|
|
|
|
struct sigaction act;
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
/* we wait for the child process to die, but to prevent timeout
|
|
|
|
we raise an alarm after some time. */
|
|
|
|
/* NOTE: the alarm does not terminate waitpid() on Linux/glibc
|
|
|
|
(BUG?),
|
|
|
|
therefore we have to do the kill in the signal handler */
|
|
|
|
Signal(SIGALRM, signal_kill_pid);
|
|
|
|
socat_kill_pid = sub;
|
|
|
|
#if HAVE_SETITIMER
|
|
|
|
/*! with next feature release, we get usec resolution and an option
|
|
|
|
*/
|
|
|
|
#else
|
|
|
|
Alarm(1 /*! sock->stream.child.waitdie */);
|
|
|
|
#endif /* !HAVE_SETITIMER */
|
|
|
|
if (Waitpid(sub, &status, 0) < 0) {
|
|
|
|
Warn3("waitpid("F_pid", %p, 0): %s",
|
|
|
|
sub, &status, strerror(errno));
|
|
|
|
}
|
|
|
|
Alarm(0);
|
|
|
|
return 0;
|
|
|
|
}
|