/* source: socat.c */
/* Copyright Gerhard Rieger and contributors (see file CHANGES) */
/* Published under the GNU General Public License V.2, see file COPYING */

/* this is the main source, including command line option parsing and general
   control */

#include "config.h"
#include "xioconfig.h"	/* what features are enabled */

#include "sysincludes.h"

#include "mytypes.h"
#include "compat.h"
#include "error.h"

#include "sycls.h"
#include "sysutils.h"
#include "dalan.h"
#include "filan.h"
#include "xio.h"
#include "xioopts.h"
#include "xiosigchld.h"
#include "xiolockfile.h"

#include "xioopen.h"

/* command line options */
struct {
   bool strictopts;	/* stop on errors in address options */
   xiolock_t lock;	/* a lock file */
} socat_opts = {
   0,		/* strictopts */
   { NULL, 0 },	/* lock */
};

void socat_usage(FILE *fd);
void socat_opt_hint(FILE *fd, char a, char b);
void socat_version(FILE *fd);
int socat(int argc, const char *address1, const char *address2);
int _socat(xiofile_t *xfd1, xiofile_t *xfd2);
void socat_signal(int sig);

void lftocrlf(char **in, ssize_t *len, size_t bufsiz);
void crlftolf(char **in, ssize_t *len, size_t bufsiz);

static int socat_lock(void);
static void socat_unlock(void);
static int socat_newchild(void);

static const char socatversion[] =
#include "./VERSION"
      ;
static const char timestamp[] = BUILD_DATE;

const char copyright_socat[] = "socat by Gerhard Rieger and contributors - see www.dest-unreach.org";
#if WITH_OPENSSL
const char copyright_openssl[] = "This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)";
const char copyright_ssleay[] = "This product includes software written by Tim Hudson (tjh@cryptsoft.com)";
#endif

bool havelock;


int main(int argc, const char *argv[]) {
   const char **arg1, *a;
   char *mainwaitstring;
   char buff[10];
   double rto;
   int i, argc0, result;
   struct utsname ubuf;
   int lockrc;

   if (mainwaitstring = getenv("SOCAT_MAIN_WAIT")) {
       sleep(atoi(mainwaitstring));
   }
   diag_set('p', strchr(argv[0], '/') ? strrchr(argv[0], '/')+1 : argv[0]);

   /* we must init before applying options because env settings have lower
      priority and are to be overridden by options */
   if (xioinitialize(XIO_MAYALL) != 0) {
      Exit(1);
   }

   xiosetopt('p', "%");
   xiosetopt('o', ":");

   argc0 = argc;	/* save for later use */
   arg1 = argv+1;  --argc;
   while (arg1[0] && (arg1[0][0] == '-')) {
      switch (arg1[0][1]) {
      case 'V':  if (arg1[0][2])  { socat_usage(stderr); Exit(1); }
	 socat_version(stdout); Exit(0);
#if WITH_HELP
      case '?':
      case 'h':
	 socat_usage(stdout);
	 xioopenhelp(stdout, (arg1[0][2]=='?'||arg1[0][2]=='h') ? (arg1[0][3]=='?'||arg1[0][3]=='h') ? 2 : 1 : 0);
	 Exit(0);
#endif /* WITH_HELP */
      case 'd':
	 a = *arg1+1;
	 while (*a)  {
	    if (*a == 'd') {
	       diag_set('d', NULL);
	    } else {
	       socat_usage(stderr);
	       Exit(1);
	    }
	    ++a;
	 }
	 break;
#if WITH_FILAN
      case 'D':  if (arg1[0][2])  { socat_opt_hint(stderr, arg1[0][1], arg1[0][2]); Exit(1); }
	 xioopts.debug = true; break;
#endif
      case 'l':
	 switch (arg1[0][2]) {
	 case 'm': /* mixed mode: stderr, then switch to syslog; + facility */
	    diag_set('s', NULL);
	    xiosetopt('l', "m");
	    xioparams->logopt = arg1[0][2];
	    xiosetopt('y', &arg1[0][3]);
	    break;
	 case 'y': /* syslog + facility */
	    diag_set(arg1[0][2], &arg1[0][3]);
	    break;
	 case 'f': /* to file, +filename */
	 case 'p': /* artificial program name */
	    if (arg1[0][3]) {
	       diag_set(arg1[0][2], &arg1[0][3]);
	    } else if (arg1[1]) {
	       diag_set(arg1[0][2], arg1[1]);
	       ++arg1, --argc;
	    } else {
	       Error1("option -l%c requires an argument; use option \"-h\" for help", arg1[0][2]);
	    }
	    break;
	 case 's': /* stderr */
	    diag_set(arg1[0][2], NULL);
	    break;
	 case 'u':
	    diag_set('u', NULL);
	    break;
	 case 'h':
	    diag_set_int('h', true);
	    break;
	 default:
	    Error1("unknown log option \"%s\"; use option \"-h\" for help", arg1[0]);
	    break;
	 }
	 break;
      case 'v':  if (arg1[0][2])  { socat_opt_hint(stderr, arg1[0][1], arg1[0][2]); Exit(1); }
	 xioopts.verbose = true; break;
      case 'x':  if (arg1[0][2])  { socat_opt_hint(stderr, arg1[0][1], arg1[0][2]); Exit(1); }
	 xioopts.verbhex = true; break;
      case 'b': if (arg1[0][2]) {
	    a = *arg1+2;
	 } else {
	    ++arg1, --argc;
	    if ((a = *arg1) == NULL) {
	       Error("option -b requires an argument; use option \"-h\" for help");
	       Exit(1);
	    }
	 }
	 xioparams->bufsiz = strtoul(a, (char **)&a, 0);
	 break;
      case 's':  if (arg1[0][2])  { socat_opt_hint(stderr, arg1[0][1], arg1[0][2]); Exit(1); }
	 diag_set_int('e', E_FATAL); break;
      case 't': if (arg1[0][2]) {
	    a = *arg1+2;
	 } else {
	    ++arg1, --argc;
	    if ((a = *arg1) == NULL) {
	       Error("option -t requires an argument; use option \"-h\" for help");
	       Exit(1);
	    }
	 }
	 rto = strtod(a, (char **)&a);
	 xioparams->closwait.tv_sec = rto;
	 xioparams->closwait.tv_usec =
	    (rto-xioparams->closwait.tv_sec) * 1000000; 
	 break;
      case 'T':  if (arg1[0][2]) {
	    a = *arg1+2;
	 } else {
	    ++arg1, --argc;
	    if ((a = *arg1) == NULL) {
	       Error("option -T requires an argument; use option \"-h\" for help");
	       Exit(1);
	    }
	 }
	 rto = strtod(a, (char **)&a);
	 xioparams->total_timeout.tv_sec = rto;
	 xioparams->total_timeout.tv_usec =
	    (rto-xioparams->total_timeout.tv_sec) * 1000000; 
	 break;
      case 'u':  if (arg1[0][2])  { socat_opt_hint(stderr, arg1[0][1], arg1[0][2]); Exit(1); }
	 xioopts.lefttoright = true; break;
      case 'U':  if (arg1[0][2])  { socat_opt_hint(stderr, arg1[0][1], arg1[0][2]); Exit(1); }
	 xioopts.righttoleft = true; break;
      case 'g':  if (arg1[0][2])  { socat_opt_hint(stderr, arg1[0][1], arg1[0][2]); Exit(1); }
	 xioopts_ignoregroups = true; break;
      case 'L': if (socat_opts.lock.lockfile)
	     Error("only one -L and -W option allowed");
	 if (arg1[0][2]) {
	    socat_opts.lock.lockfile = *arg1+2;
	 } else {
	    ++arg1, --argc;
	    if ((socat_opts.lock.lockfile = *arg1) == NULL) {
	       Error("option -L requires an argument; use option \"-h\" for help");
	       Exit(1);
	    }
	 }
	 break;
      case 'W': if (socat_opts.lock.lockfile)
	    Error("only one -L and -W option allowed");
	 if (arg1[0][2]) {
	    socat_opts.lock.lockfile = *arg1+2;
	 } else {
	    ++arg1, --argc;
	    if ((socat_opts.lock.lockfile = *arg1) == NULL) {
	       Error("option -W requires an argument; use option \"-h\" for help");
	       Exit(1);
	    }
	 }
	 socat_opts.lock.waitlock = true;
	 socat_opts.lock.intervall.tv_sec  = 1;
	 socat_opts.lock.intervall.tv_nsec = 0;
	 break;
#if WITH_IP4 || WITH_IP6
#if WITH_IP4
      case '4':
#endif
#if WITH_IP6
      case '6':
#endif
	 if (arg1[0][2])  { socat_opt_hint(stderr, arg1[0][1], arg1[0][2]); Exit(1); }
	 xioopts.default_ip = arg1[0][1];
	 xioopts.preferred_ip = arg1[0][1];
	 break;
#endif /* WITH_IP4 || WITH_IP6 */
      case 'c':
	 switch (arg1[0][2]) {
	 case 'S': xioparams->pipetype = XIOCOMM_SOCKETPAIRS; break;
	 case 'P':
	 case 'p': xioparams->pipetype = XIOCOMM_PIPES;       break;
	 case 's': xioparams->pipetype = XIOCOMM_SOCKETPAIR;  break;
	 case 'Y': xioparams->pipetype = XIOCOMM_PTYS;        break;
	 case 'y': xioparams->pipetype = XIOCOMM_PTY;         break;
	 case 't': xioparams->pipetype = XIOCOMM_TCP;         break;
	 case '0': case '1': case '2': case '3': case '4':
	 case '5': case '6': case '7': case '8': case '9':
	    xioparams->pipetype = atoi(&arg1[0][2]); break;
	 default: Error1("bad chain communication type \"%s\"", &arg1[0][2]);
	 }
	 break;
      case '\0':
      case '-':	/*! this is hardcoded "--" */
      case ',':
      case ':': break;	/* this "-" is a variation of STDIO or -- */
      default:
	 xioinqopt('p', buff, sizeof(buff));
	 if (arg1[0][1] == buff[0]) {
	    break;
	 }
	 Error1("unknown option \"%s\"; use option \"-h\" for help", arg1[0]);
	 Exit(1);
      }
      /* the leading "-" might be a form of the first address */
      xioinqopt('p', buff, sizeof(buff));
      if (arg1[0][0] == '-' &&
	  (arg1[0][1] == '\0' || arg1[0][1] == ':' ||
	   arg1[0][1] == ',' || arg1[0][1] == '-'/*!*/ ||
	   arg1[0][1] == buff[0]))
	 break;
      ++arg1; --argc;
   }
#if 0
   Info1("%d address arguments", argc);
#else
   if (argc != 2) {
      Error1("exactly 2 addresses required (there are %d); use option \"-h\" for help", argc);
      Exit(1);
   }
#endif
   if (xioparams->lefttoright && xioparams->righttoleft) {
      Error("-U and -u must not be combined");
   }

   xioinitialize2();
   Info(copyright_socat);
#if WITH_OPENSSL
   Info(copyright_openssl);
   Info(copyright_ssleay);
#endif
   Debug2("socat version %s on %s", socatversion, timestamp);
   xiosetenv("VERSION", socatversion, 1, NULL);	/* SOCAT_VERSION */
   uname(&ubuf);	/* ! here we circumvent internal tracing (Uname) */
   Debug4("running on %s version %s, release %s, machine %s\n",
	   ubuf.sysname, ubuf.version, ubuf.release, ubuf.machine);

#if WITH_MSGLEVEL <= E_DEBUG
   for (i = 0; i < argc0; ++i) {
      Debug2("argv[%d]: \"%s\"", i, argv[i]);
   }
#endif /* WITH_MSGLEVEL <= E_DEBUG */

   {
      struct sigaction act;
      sigfillset(&act.sa_mask);
      act.sa_flags = 0;
      act.sa_handler = socat_signal;
      /* not sure which signals should be caught and print a message */
      Sigaction(SIGHUP,  &act, NULL);
      Sigaction(SIGINT,  &act, NULL);
      Sigaction(SIGQUIT, &act, NULL);
      Sigaction(SIGILL,  &act, NULL);
      Sigaction(SIGABRT, &act, NULL);
      Sigaction(SIGBUS,  &act, NULL);
      Sigaction(SIGFPE,  &act, NULL);
      Sigaction(SIGSEGV, &act, NULL);
      Sigaction(SIGTERM, &act, NULL);
   }
   Signal(SIGPIPE, SIG_IGN);
#if HAVE_SIGACTION
   {
      struct sigaction act;
      memset(&act, 0, sizeof(struct sigaction));
      act.sa_flags   = SA_NOCLDSTOP|SA_RESTART|SA_SIGINFO
#ifdef SA_NOMASK
	 |SA_NOMASK
#endif
	 ;
      act.sa_sigaction = xiosigaction_subaddr_ok;
      if (Sigaction(SIGUSR1, &act, NULL) < 0) {
	 /*! Linux man does not explicitely say that errno is defined */
	 Warn1("sigaction(SIGUSR1, {&xiosigaction_subaddr_ok}, NULL): %s", strerror(errno));
      }
   }
#else /* !HAVE_SIGACTION */
   if (Signal(SIGUSR1, xiosigaction_subaddr_ok) == SIG_ERR) {
      Warn1("signal(SIGCHLD, xiosigaction_subaddr_ok): %s", strerror(errno));
   }
#endif /* !HAVE_SIGACTION */

   /* set xio hooks */
   xiohook_newchild = &socat_newchild;

   if (lockrc = socat_lock()) {
      /* =0: goon; >0: locked; <0: error, printed in sub */
      if (lockrc > 0)
	 Error1("could not obtain lock \"%s\"", socat_opts.lock.lockfile);
      Exit(1);
   }

   Atexit(socat_unlock);

   result = socat(argc, arg1[0], arg1[1]);
   Notice1("exiting with status %d", result);
   Exit(result);
   return 0;	/* not reached, just for gcc -Wall */
}


void socat_usage(FILE *fd) {
   fputs(copyright_socat, fd); fputc('\n', fd);
   fputs("Usage:\n", fd);
   fputs("socat [options] <bi-address> <bi-address>\n", fd);
   fputs("   options:\n", fd);
   fputs("      -V     print version and feature information to stdout, and exit\n", fd);
#if WITH_HELP
   fputs("      -h|-?  print a help text describing command line options and addresses\n", fd);
   fputs("      -hh    like -h, plus a list of all common address option names\n", fd);
   fputs("      -hhh   like -hh, plus a list of all available address option names\n", fd);
#endif /* WITH_HELP */
   fputs("      -d[ddd]         increase verbosity (use up to 4 times; 2 are recommended)\n", fd);
#if WITH_FILAN
   fputs("      -D     analyze file descriptors before loop\n", fd);
#endif
   fputs("      -ly[facility]  log to syslog, using facility (default is daemon)\n", fd);
   fputs("      -lf<logfile>   log to file\n", fd);
   fputs("      -ls            log to stderr (default if no other log)\n", fd);
   fputs("      -lm[facility]  mixed log mode (stderr during initialization, then syslog)\n", fd);
   fputs("      -lp<progname>  set the program name used for logging\n", fd);
   fputs("      -lu            use microseconds for logging timestamps\n", fd);
   fputs("      -lh            add hostname to log messages\n", fd);
   fputs("      -v     verbose data traffic, text\n", fd);
   fputs("      -x     verbose data traffic, hexadecimal\n", fd);
   fputs("      -b<size_t>     set data buffer size (8192)\n", fd);
   fputs("      -s     sloppy (continue on error)\n", fd);
   fputs("      -t<timeout>    wait seconds before closing second channel\n", fd);
   fputs("      -T<timeout>    total inactivity timeout in seconds\n", fd);
   fputs("      -u     unidirectional mode (left to right)\n", fd);
   fputs("      -U     unidirectional mode (right to left)\n", fd);
   fputs("      -g     do not check option groups\n", fd);
   fputs("      -L <lockfile>  try to obtain lock, or fail\n", fd);
   fputs("      -W <lockfile>  try to obtain lock, or wait\n", fd);
#if WITH_IP4
   fputs("      -4     prefer IPv4 if version is not explicitly specified\n", fd);
#endif
#if WITH_IP6
   fputs("      -6     prefer IPv6 if version is not explicitly specified\n", fd);
#endif
}

void socat_opt_hint(FILE *fd, char a, char b) {
   fprintf(fd, "Do not merge single character options, i.e. use \"-%c -%c\" instead of \"-%c%c\"\n",
	   a, b, a, b);
}


void socat_version(FILE *fd) {
   struct utsname ubuf;

   fputs(copyright_socat, fd); fputc('\n', fd);
   fprintf(fd, "socat version %s on %s\n", socatversion, timestamp);
   Uname(&ubuf);
   fprintf(fd, "   running on %s version %s, release %s, machine %s\n",
	   ubuf.sysname, ubuf.version, ubuf.release, ubuf.machine);
   fputs("features:\n", fd);
#ifdef WITH_STDIO
   fprintf(fd, "  #define WITH_STDIO %d\n", WITH_STDIO);
#else
   fputs("  #undef WITH_STDIO\n", fd);
#endif
#ifdef WITH_FDNUM
   fprintf(fd, "  #define WITH_FDNUM %d\n", WITH_FDNUM);
#else
   fputs("  #undef WITH_FDNUM\n", fd);
#endif
#ifdef WITH_FILE
   fprintf(fd, "  #define WITH_FILE %d\n", WITH_FILE);
#else
   fputs("  #undef WITH_FILE\n", fd);
#endif
#ifdef WITH_CREAT
   fprintf(fd, "  #define WITH_CREAT %d\n", WITH_CREAT);
#else
   fputs("  #undef WITH_CREAT\n", fd);
#endif
#ifdef WITH_GOPEN
   fprintf(fd, "  #define WITH_GOPEN %d\n", WITH_GOPEN);
#else
   fputs("  #undef WITH_GOPEN\n", fd);
#endif
#ifdef WITH_TERMIOS
   fprintf(fd, "  #define WITH_TERMIOS %d\n", WITH_TERMIOS);
#else
   fputs("  #undef WITH_TERMIOS\n", fd);
#endif
#ifdef WITH_PIPE
   fprintf(fd, "  #define WITH_PIPE %d\n", WITH_PIPE);
#else
   fputs("  #undef WITH_PIPE\n", fd);
#endif
#ifdef WITH_UNIX
   fprintf(fd, "  #define WITH_UNIX %d\n", WITH_UNIX);
#else
   fputs("  #undef WITH_UNIX\n", fd);
#endif /* WITH_UNIX */
#ifdef WITH_ABSTRACT_UNIXSOCKET
   fprintf(fd, "  #define WITH_ABSTRACT_UNIXSOCKET %d\n", WITH_ABSTRACT_UNIXSOCKET);
#else
   fputs("  #undef WITH_ABSTRACT_UNIXSOCKET\n", fd);
#endif /* WITH_ABSTRACT_UNIXSOCKET */
#ifdef WITH_IP4
   fprintf(fd, "  #define WITH_IP4 %d\n", WITH_IP4);
#else
   fputs("  #undef WITH_IP4\n", fd);
#endif
#ifdef WITH_IP6
   fprintf(fd, "  #define WITH_IP6 %d\n", WITH_IP6);
#else
   fputs("  #undef WITH_IP6\n", fd);
#endif
#ifdef WITH_RAWIP
   fprintf(fd, "  #define WITH_RAWIP %d\n", WITH_RAWIP);
#else
   fputs("  #undef WITH_RAWIP\n", fd);
#endif
#ifdef WITH_GENERICSOCKET
   fprintf(fd, "  #define WITH_GENERICSOCKET %d\n", WITH_GENERICSOCKET);
#else
   fputs("  #undef WITH_GENERICSOCKET\n", fd);
#endif
#ifdef WITH_INTERFACE
   fprintf(fd, "  #define WITH_INTERFACE %d\n", WITH_INTERFACE);
#else
   fputs("  #undef WITH_INTERFACE\n", fd);
#endif
#ifdef WITH_TCP
   fprintf(fd, "  #define WITH_TCP %d\n", WITH_TCP);
#else
   fputs("  #undef WITH_TCP\n", fd);
#endif
#ifdef WITH_UDP
   fprintf(fd, "  #define WITH_UDP %d\n", WITH_UDP);
#else
   fputs("  #undef WITH_UDP\n", fd);
#endif
#ifdef WITH_SCTP
   fprintf(fd, "  #define WITH_SCTP %d\n", WITH_SCTP);
#else
   fputs("  #undef WITH_SCTP\n", fd);
#endif
#ifdef WITH_LISTEN
   fprintf(fd, "  #define WITH_LISTEN %d\n", WITH_LISTEN);
#else
   fputs("  #undef WITH_LISTEN\n", fd);
#endif
#ifdef WITH_SOCKS4
   fprintf(fd, "  #define WITH_SOCKS4 %d\n", WITH_SOCKS4);
#else
   fputs("  #undef WITH_SOCKS4\n", fd);
#endif
#ifdef WITH_SOCKS4A
   fprintf(fd, "  #define WITH_SOCKS4A %d\n", WITH_SOCKS4A);
#else
   fputs("  #undef WITH_SOCKS4A\n", fd);
#endif
#ifdef WITH_PROXY
   fprintf(fd, "  #define WITH_PROXY %d\n", WITH_PROXY);
#else
   fputs("  #undef WITH_PROXY\n", fd);
#endif
#ifdef WITH_SYSTEM
   fprintf(fd, "  #define WITH_SYSTEM %d\n", WITH_SYSTEM);
#else
   fputs("  #undef WITH_SYSTEM\n", fd);
#endif
#ifdef WITH_EXEC
   fprintf(fd, "  #define WITH_EXEC %d\n", WITH_EXEC);
#else
   fputs("  #undef WITH_EXEC\n", fd);
#endif
#ifdef WITH_READLINE
   fprintf(fd, "  #define WITH_READLINE %d\n", WITH_READLINE);
#else
   fputs("  #undef WITH_READLINE\n", fd);
#endif
#ifdef WITH_TUN
   fprintf(fd, "  #define WITH_TUN %d\n", WITH_TUN);
#else
   fputs("  #undef WITH_TUN\n", fd);
#endif
#ifdef WITH_PTY
   fprintf(fd, "  #define WITH_PTY %d\n", WITH_PTY);
#else
   fputs("  #undef WITH_PTY\n", fd);
#endif
#ifdef WITH_OPENSSL
   fprintf(fd, "  #define WITH_OPENSSL %d\n", WITH_OPENSSL);
#else
   fputs("  #undef WITH_OPENSSL\n", fd);
#endif
#ifdef WITH_FIPS
   fprintf(fd, "  #define WITH_FIPS %d\n", WITH_FIPS);
#else
   fputs("  #undef WITH_FIPS\n", fd);
#endif
#ifdef WITH_LIBWRAP
   fprintf(fd, "  #define WITH_LIBWRAP %d\n", WITH_LIBWRAP);
#else
   fputs("  #undef WITH_LIBWRAP\n", fd);
#endif
#ifdef WITH_SYCLS
   fprintf(fd, "  #define WITH_SYCLS %d\n", WITH_SYCLS);
#else
   fputs("  #undef WITH_SYCLS\n", fd);
#endif
#ifdef WITH_FILAN
   fprintf(fd, "  #define WITH_FILAN %d\n", WITH_FILAN);
#else
   fputs("  #undef WITH_FILAN\n", fd);
#endif
#ifdef WITH_RETRY
   fprintf(fd, "  #define WITH_RETRY %d\n", WITH_RETRY);
#else
   fputs("  #undef WITH_RETRY\n", fd);
#endif
#ifdef WITH_MSGLEVEL
   fprintf(fd, "  #define WITH_MSGLEVEL %d /*%s*/\n", WITH_MSGLEVEL,
	   &"debug\0\0\0info\0\0\0\0notice\0\0warn\0\0\0\0error\0\0\0fatal\0\0\0"[WITH_MSGLEVEL<<3]);
#else
   fputs("  #undef WITH_MSGLEVEL\n", fd);
#endif
}


/* call this function when the common command line options are parsed, and the
   addresses are extracted (but not resolved). */
int socat(int argc, const char *address1, const char *address2) {
   xiofile_t *xfd1, *xfd2;

   xioinitialize(XIO_MAYALL);

   /* open the first (left most) address */
   if (xioparams->lefttoright) {
      if ((xfd1 = socat_open(address1, XIO_RDONLY, XIO_MAYFORK|XIO_MAYCHILD|XIO_MAYCONVERT)) == NULL) {
	 return -1;
      }
   } else if (xioparams->righttoleft) {
      if ((xfd1 = socat_open(address1, XIO_WRONLY, XIO_MAYFORK|XIO_MAYCHILD|XIO_MAYCONVERT)) == NULL) {
	 return -1;
      }
   } else {
      if ((xfd1 = socat_open(address1, XIO_RDWR, XIO_MAYFORK|XIO_MAYCHILD|XIO_MAYCONVERT)) == NULL) {
	 return -1;
      }
   }
   sock[0] = xfd1;
   xiosetsigchild(xfd1, socat_sigchild);
#if 1	/*! */
   if (XIO_RDSTREAM(xfd1)->subaddrstat < 0) {
      Info1("child "F_pid" has already died",
	    XIO_RDSTREAM(xfd1)->child.pid);
      XIO_RDSTREAM(xfd1)->child.pid = 0;
      /* return STAT_RETRYLATER; */
   }
#endif

   /* second (right) addresses chain */
   if (XIO_WRITABLE(xfd1)) {
      if (XIO_READABLE(xfd1)) {
	 if ((xfd2 = socat_open(address2, XIO_RDWR, XIO_MAYFORK|XIO_MAYCHILD|XIO_MAYEXEC|XIO_MAYCONVERT)) == NULL) {
	    return -1;
	 }
      } else {
	 if ((xfd2 = socat_open(address2, XIO_RDONLY, XIO_MAYFORK|XIO_MAYCHILD|XIO_MAYEXEC|XIO_MAYCONVERT)) == NULL) {
	    return -1;
	 }
      }
   } else {	/* assuming sock1 is readable */
      if ((xfd2 = socat_open(address2, XIO_WRONLY, XIO_MAYFORK|XIO_MAYCHILD|XIO_MAYEXEC|XIO_MAYCONVERT)) == NULL) {
	 return -1;
      }
   }
   sock[1] = xfd2;
   xiosetsigchild(xfd2, socat_sigchild);
#if 1	/*! */
   if (XIO_RDSTREAM(xfd2)->subaddrstat < 0) {
      Info1("child "F_pid" has already died",
	    XIO_RDSTREAM(xfd2)->child.pid);
      XIO_RDSTREAM(xfd2)->child.pid = 0;
      /* return STAT_RETRYLATER; */
   }
#endif

   Info("resolved and opened all sock addresses");
   return 
      _socat(xfd1, xfd2);
}


/* childleftdata() has been moved to xioengine.c */


/* _socat() has been moved to xioengine.c */


/* gettimestamp() has been moved to xiotransfer.c */


/* xioprintblockheader has been moved to xiotransfer.c */


/* xiotransfer has been moved to xiotransfer.c */


/* cv_newline has been moved to xiotransfer.c */


void socat_signal(int signum) {
   int _errno;
   _errno = errno;
   diag_in_handler = 1;
   Notice1("socat_signal(): handling signal %d", signum);
   switch (signum) {
   case SIGILL:
   case SIGABRT:
   case SIGBUS:
   case SIGFPE:
   case SIGSEGV:
      diag_immediate_exit = 1;
   case SIGQUIT:
   case SIGPIPE:
      diag_set_int('x', 128+signum);	/* in case Error exits for us */
      Error1("exiting on signal %d", signum);
      diag_set_int('x', 0);	/* in case Error did not exit */
      break;
   case SIGTERM:
      Warn1("exiting on signal %d", signum); break;
   case SIGHUP:  
   case SIGINT:
      Notice1("exiting on signal %d", signum); break;
   }
   //Exit(128+signum);	/* internal cleanup + _exit() */
   Notice1("socat_signal(): finishing signal %d", signum);
   diag_exit(128+signum);    /*!!! internal cleanup + _exit() */
   diag_in_handler = 0;
   errno = _errno;
}

#if 0
/* this is the callback when the child of an address died */
static int socat_sigchild(struct single *file) {
   if (file->ignoreeof && !closing) {
      ;
   } else {
      file->eof = MAX(file->eof, 1);
      closing = 1;
   }
   return 0;
}
#endif

static int socat_lock(void) {
   int lockrc;

#if 1
   if ((lockrc = xiolock(&socat_opts.lock)) < 0) {
      return -1;
   }
   if (lockrc == 0) {
      havelock = true;
   }
   return lockrc;
#else
   if (socat_opts.lock.lockfile) {
      if ((lockrc = xiolock(socat_opts.lock.lockfile)) < 0) {
	 /*Error1("error with lockfile \"%s\"", socat_opts.lock.lockfile);*/
	 return -1;
      }
      if (lockrc) {
	 return 1;
      }
      havelock = true;
      /*0 Info1("obtained lock \"%s\"", socat_opts.lock.lockfile);*/
   }

   if (socat_opts.lock.waitlock) {
      if (xiowaitlock(socat_opts.lock.waitlock, socat_opts.lock.intervall)) {
	 /*Error1("error with lockfile \"%s\"", socat_opts.lock.lockfile);*/
	 return -1;
      } else {
	 havelock = true;
	 /*0 Info1("obtained lock \"%s\"", socat_opts.lock.waitlock);*/
      }
   }
   return 0;
#endif
}

static void socat_unlock(void) {
   if (!havelock)  return;
   if (socat_opts.lock.lockfile) {
      if (Unlink(socat_opts.lock.lockfile) < 0) {
	 if (!diag_in_handler) {
	    Warn2("unlink(\"%s\"): %s",
		  socat_opts.lock.lockfile, strerror(errno));
	 } else {
	    Warn1("unlink(\"%s\"): "F_strerror,
		  socat_opts.lock.lockfile);
	 }
      } else {
	 Info1("released lock \"%s\"", socat_opts.lock.lockfile);
      }
   }
}

/* this is a callback function that may be called by the newchild hook of xio
 */
static int socat_newchild(void) {
   havelock = false;
   return 0;
}
