nexmon – Rev 1

Subversion Repositories:
Rev:
/* source: xioshutdown.c */
/* Copyright Gerhard Rieger */
/* 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"


static int xioshut_sleep_kill(pid_t sub, unsigned long usec, int sig);

static pid_t socat_kill_pid;    /* here we pass the pid to be killed in sighandler */

static void signal_kill_pid(int dummy) {
   int _errno;
   _errno = errno;
   diag_in_handler = 1;
   Notice("SIGALRM while waiting for w/o child process to die, killing it now");
   Kill(socat_kill_pid, SIGTERM);
   diag_in_handler = 0;
   errno = _errno;
}

/* how: SHUT_RD, SHUT_WR, or SHUT_RDWR */
int xioshutdown(xiofile_t *sock, int how) {
   int fd;
   int result = 0;

   Debug2("xioshutdown(%p, %d)", sock, how);
   Debug2("xioshutdown(): dtype=0x%x, howtoshut=0x%04x",
          sock->stream.dtype, sock->stream.howtoshut);

   if (sock->tag == XIO_TAG_INVALID) {
      Error("xioshutdown(): invalid file descriptor");
      errno = EINVAL;
      return -1;
   }

   /*Debug3("xioshutdown: flags=%d, dtype=%d, howtoclose=%d", sock->stream.flags, sock->stream.dtype, sock->stream.howtoclose);*/
   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);
      }
      return result;
   }

   fd = XIO_GETWRFD(sock);

   /* 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;
   }

   switch (sock->stream.howtoshut) {
#if WITH_PTY
   case XIOSHUT_PTYEOF:
      {
         struct termios termarg;
         int result;
         Debug1("tcdrain(%d)", sock->stream.wfd);
         result = tcdrain(sock->stream.wfd);
         Debug1("tcdrain() -> %d", result);
         if (Tcgetattr(sock->stream.wfd, &termarg) < 0) {
            Error3("tcgetattr(%d, %p): %s",
                   sock->stream.wfd, &termarg, strerror(errno));
         }
#if 0
         /* these settings might apply to data still in the buff (despite the
            TCSADRAIN */
         termarg.c_iflag |= (IGNBRK | BRKINT | PARMRK | ISTRIP
                           | INLCR | IGNCR | ICRNL | IXON);
         termarg.c_oflag |= OPOST;
         termarg.c_lflag |= (/*ECHO | ECHONL |*/ ICANON | ISIG | IEXTEN);
         //termarg.c_cflag |= (PARENB);
#else
         termarg.c_lflag |= ICANON;
#endif
         if (Tcsetattr(sock->stream.wfd, TCSADRAIN, &termarg) < 0) {
            Error3("tcsetattr(%d, TCSADRAIN, %p): %s",
                   sock->stream.wfd, &termarg, strerror(errno));
         }
         if (Write(sock->stream.wfd, &termarg.c_cc[VEOF], 1) < 1) {
            Warn3("write(%d, 0%o, 1): %s",
                  sock->stream.wfd, termarg.c_cc[VEOF], strerror(errno));
         }
      }
      return 0;
#endif /* WITH_PTY */
#if WITH_OPENSSL
   case XIOSHUT_OPENSSL:
      sycSSL_shutdown(sock->stream.para.openssl.ssl);
      /*! what about half/full close? */
      return 0;
#endif /* WITH_OPENSSL */
   default:
      break;
   }

   /* here handle special shutdown functions */
   switch (sock->stream.howtoshut & XIOSHUTWR_MASK) {
      char writenull;
   case XIOSHUTWR_NONE:
      return 0;
   case XIOSHUTWR_CLOSE:
      if (Close(fd) < 0) {
         Info2("close(%d): %s", fd, strerror(errno));
      }
      return 0;
   case XIOSHUTWR_DOWN:
      if ((result = Shutdown(fd, how)) < 0) {
         Info3("shutdown(%d, %d): %s", fd, how, strerror(errno));
      }
      return 0;
#if _WITH_SOCKET
   case XIOSHUTWR_NULL:
      /* send an empty packet; only useful on datagram sockets? */
      xiowrite(sock, &writenull, 0);
      return 0;
#endif /* _WITH_SOCKET */
   default: break;
   }

#if 0
  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 _WITH_SOCKET
     case XIOSHUT_DOWN:
      if ((result = Shutdown(fd, how)) < 0) {
         Info3("shutdown(%d, %d): %s", fd, how, strerror(errno));
      }
      break;
     case XIOSHUT_KILL:
        if ((result = Shutdown(fd, how)) < 0) {
         Info3("shutdown(%d, %d): %s", fd, how, strerror(errno));
      }
      break;
#endif /* _WITH_SOCKET */
     case XIOSHUT_CLOSE:
        Close(fd);
#if WITH_TERMIOS
        if (sock->stream.ttyvalid) {
           if (Tcsetattr(fd, 0, &sock->stream.savetty) < 0) {
              Warn2("cannot restore terminal settings on fd %d: %s",
                    fd, strerror(errno));
           }
        }
#endif /* WITH_TERMIOS */
        /*PASSTHROUGH*/
     case XIOSHUT_NONE:
        break;
     default:
        Error1("xioshutdown(): bad shutdown action 0x%x", sock->stream.howtoshut);
        return -1;
     }

#if 0 && _WITH_SOCKET
   case XIODATA_RECVFROM:
      if (how >= 1) {
         if (Close(fd) < 0) {
            Info2("close(%d): %s",
                  fd, strerror(errno));
         }
         sock->stream.eof = 2;
         sock->stream.rfd = -1;
      }
      break;
#endif /* _WITH_SOCKET */
  }
#endif

   if ((how+1) & 1) {   /* contains SHUT_RD */
      switch (sock->stream.dtype & XIODATA_READMASK) {
         /* shutdown read channel */

      case XIOREAD_STREAM:
      case XIODATA_2PIPE:
         if (Close(fd) < 0) {
            Info2("close(%d): %s", fd, strerror(errno));
         }
         break;
      }
   }

   if ((how+1) & 2) {   /* contains SHUT_WR */
      /* shutdown write channel */

      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 */

#if 0
      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&XIOSHUTWR_MASK);
      }
      sock->stream.wfd = -1;
   }

   return result;
}

/* 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 */
         {
            struct sigaction act;
            sigfillset(&act.sa_mask);
            act.sa_flags = 0;
            act.sa_handler = signal_kill_pid;
            Sigaction(SIGALRM, &act, NULL);
         }

   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;
}