/* source: xioinitialize.c */ /* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this file contains the source for the initialize function */ #include "xiosysincludes.h" #include "xiostatic.h" #include "xioopen.h" #include "xiolockfile.h" #include "xiosigchld.h" #include "xio-openssl.h" /* xio_reset_fips_mode() */ static int xioinitialized; xiofile_t *sock[XIO_MAXSOCK]; int (*xiohook_newchild)(void); /* xio calls this function in every new child process */ /* call this function before calling any other xio function. With xioflags, you have to set the features that xio can make use of. Use XIO_MAYALL for unrestricted use. */ /* returns 0 on success or != if an error occurred */ int xioinitialize(int xioflags) { int xio_flags; if (xioinitialized) return 0; /* configure and .h's cannot guarantee this */ assert(sizeof(uint8_t)==1); assert(sizeof(uint16_t)==2); assert(sizeof(uint32_t)==4); /* assertions regarding O_ flags - important for XIO_READABLE() etc. */ assert(O_RDONLY==0); assert(O_WRONLY==1); assert(O_RDWR==2); assert(SHUT_RD==0); assert(SHUT_WR==1); assert(SHUT_RDWR==2); /* some assertions about termios */ #if WITH_TERMIOS #if defined(CRDLY) && CRDLY_SHIFT >= 0 assert(3 << opt_crdly.arg3 == CRDLY); #endif #if defined(TABDLY) && TABDLY_SHIFT >= 0 assert(3 << opt_tabdly.arg3 == TABDLY); #endif #if CSIZE_SHIFT >= 0 assert(3 << opt_csize.arg3 == CSIZE); #endif { union { struct termios termarg; tcflag_t flags[4]; #if HAVE_TERMIOS_ISPEED speed_t speeds[sizeof(struct termios)/sizeof(speed_t)]; #endif } tdata; tdata.termarg.c_iflag = 0x12345678; tdata.termarg.c_oflag = 0x23456789; tdata.termarg.c_cflag = 0x3456789a; tdata.termarg.c_lflag = 0x456789ab; assert(tdata.termarg.c_iflag == tdata.flags[0]); assert(tdata.termarg.c_oflag == tdata.flags[1]); assert(tdata.termarg.c_cflag == tdata.flags[2]); assert(tdata.termarg.c_lflag == tdata.flags[3]); #if HAVE_TERMIOS_ISPEED tdata.termarg.c_ispeed = 0x56789abc; tdata.termarg.c_ospeed = 0x6789abcd; assert(tdata.termarg.c_ispeed == tdata.speeds[ISPEED_OFFSET]); assert(tdata.termarg.c_ospeed == tdata.speeds[OSPEED_OFFSET]); #endif } #endif /* WITH_TERMIOS */ /* these dependencies required in applyopts() for OFUNC_FCNTL */ assert(F_GETFD == F_SETFD-1); assert(F_GETFL == F_SETFL-1); { const char *default_ip; default_ip = getenv("SOCAT_DEFAULT_LISTEN_IP"); if (default_ip != NULL) { switch (default_ip[0]) { case '4': case '6': xioopts.default_ip = default_ip[0]; break; } } } { const char *preferred_ip; preferred_ip = getenv("SOCAT_PREFERRED_RESOLVE_IP"); if (preferred_ip != NULL) { switch (preferred_ip[0]) { case '4': case '6': xioopts.preferred_ip = preferred_ip[0]; break; default: xioopts.preferred_ip = '0'; break; } } } if (Atexit(xioexit) < 0) { Error("atexit(xioexit) failed"); return -1; } xio_flags = xioflags; if ((xio_flags|XIO_MAYFORK) || (xio_flags|XIO_MAYCHILD) || (xio_flags|XIO_MAYCHAIN)) { #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 = childdied; if (Sigaction(SIGCHLD, &act, NULL) < 0) { /*! Linux man does not explicitely say that errno is defined */ Warn2("sigaction(SIGCHLD, %p, NULL): %s", childdied, strerror(errno)); } #else /* !HAVE_SIGACTION */ act.sa_handler = childdied; if (Signal(SIGCHLD, childdied) == SIG_ERR) { Warn2("signal(SIGCHLD, %p): %s", childdied, strerror(errno)); } #endif /* !HAVE_SIGACTION */ } xioinitialized = 1; return 0; } /* call this function when option -lp (reset program name) has been applied */ int xioinitialize2(void) { pid_t pid = Getpid(); xiosetenvulong("PID", pid, 1); xiosetenvulong("PPID", pid, 1); return 0; } /* well, this function is not for initialization, but I could not find a better source file for it it is called in the child process after fork it drops the lock references of the xiofile's so only the parent owns them */ void xiodroplocks(void) { int i; for (i = 0; i < XIO_MAXSOCK; ++i) { if (sock[i] != NULL && sock[i]->tag != XIO_TAG_INVALID) { xiofiledroplock(sock[i]); } } } #if 0 /* consider an invokation like this: socat -u exec:'some program that accepts data' tcp-l:...,fork we do not want the program to be killed by the first tcp-l sub process, it's better if it survives all sub processes. Thus, it must not be killed when the sub process delivers EOF. Also, a socket that is reused in sub processes should not be shut down (affects the connection), but closed (affects only sub processes copy of file descriptor) */ static int xio_nokill(xiofile_t *sock) { int result = 0; switch (sock->tag) { case XIO_TAG_INVALID: default: return -1; case XIO_TAG_DUAL: if ((result = xio_nokill((xiofile_t *)sock->dual.stream[0])) != 0) return result; result = xio_nokill((xiofile_t *)sock->dual.stream[1]); break; case XIO_TAG_RDONLY: case XIO_TAG_WRONLY: case XIO_TAG_RDWR: /* here is the core of this function */ switch (sock->stream.howtoclose) { case END_SHUTDOWN_KILL: sock->stream.howtoclose = END_CLOSE; break; case END_CLOSE_KILL: sock->stream.howtoclose = END_CLOSE; break; case END_SHUTDOWN: sock->stream.howtoclose = END_CLOSE; break; default: break; } break; } return result; } #endif /* 0 */ /* call this function immediately after fork() in child process */ /* it performs some neccessary actions returns 0 on success or != 0 if an error occurred */ int xio_forked_inchild(void) { int result = 0; xiodroplocks(); #if WITH_FIPS if (xio_reset_fips_mode() != 0) { result = 1; } #endif /* WITH_FIPS */ /* some locks belong to parent process, so "drop" them now */ if (xiohook_newchild) { if ((*xiohook_newchild)() != 0) { Exit(1); } } #if 0 /* change XIO_SHUTDOWN_KILL to XIO_SHUTDOWN */ if (sock1 != NULL) { int result2; result2 = xio_nokill(sock1); if (result2 < 0) Exit(1); result |= result2; } #endif return result; } /* subchild != 0 means that the current process is already a child process of the master process and thus the new sub child process should not set the SOCAT_PID variable */ pid_t xio_fork(bool subchild, int level) { pid_t pid; const char *forkwaitstring; int forkwaitsecs = 0; if ((pid = Fork()) < 0) { Msg1(level, "fork(): %s", strerror(errno)); return pid; } if (pid == 0) { /* child process */ pid_t cpid = Getpid(); Info1("just born: client process "F_pid, cpid); if (!subchild) { /* set SOCAT_PID to new value */ xiosetenvulong("PID", pid, 1); } /* gdb recommends to have env controlled sleep after fork */ if (forkwaitstring = getenv("SOCAT_FORK_WAIT")) { forkwaitsecs = atoi(forkwaitstring); Sleep(forkwaitsecs); } if (xio_forked_inchild() != 0) { Exit(1); } return 0; } /* parent process */ Notice1("forked off child process "F_pid, pid); /* gdb recommends to have env controlled sleep after fork */ if (forkwaitstring = getenv("SOCAT_FORK_WAIT")) { forkwaitsecs = atoi(forkwaitstring); Sleep(forkwaitsecs); } return pid; }