blob: c5f6abe4da4e18093bcf3ced243f409a8e7453b0 [file] [log] [blame]
/* source: xioinitialize.c */
/* Copyright Gerhard Rieger and contributors (see file CHANGES) */
/* 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 "xioopen.h"
#include "xiolockfile.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 from a new child
process */
int num_child = 0;
/* returns 0 on success or != if an error occurred */
int xioinitialize(void) {
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]);
}
#endif
/* 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':
xioparms.default_ip = default_ip[0];
break;
default:
xioparms.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':
xioparms.preferred_ip = preferred_ip[0];
break;
default:
xioparms.preferred_ip = '0';
break;
}
}
}
if (Atexit(xioexit) < 0) {
Error("atexit(xioexit) failed");
return -1;
}
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
place for it
it is called in the child process after fork
it drops the locks 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 &&
!(sock[i]->tag & XIO_TAG_CLOSED)) {
xiofiledroplock(sock[i]);
}
}
}
/* Consider an invocation 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;
if (sock->tag & XIO_TAG_CLOSED) {
return -1;
}
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.howtoend) {
case END_SHUTDOWN_KILL: sock->stream.howtoend = END_CLOSE; break;
case END_CLOSE_KILL: sock->stream.howtoend = END_CLOSE; break;
case END_SHUTDOWN: sock->stream.howtoend = END_CLOSE; break;
default: break;
}
break;
}
return result;
}
/* 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;
int i;
diag_fork();
for (i=0; i<NUMUNKNOWN; ++i) {
diedunknown[i] = 0;
}
num_child = 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);
}
}
/* change XIO_SHUTDOWN_KILL to XIO_SHUTDOWN */
if (sock1 != NULL) {
int result2;
result2 = xio_nokill(sock1);
if (result2 < 0) Exit(1);
result |= result2;
}
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, /* log level */
int shutup) /* decrease log level in child process */
{
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: child process "F_pid, cpid);
if (!subchild) {
/* set SOCAT_PID to new value */
xiosetenvulong("PID", pid, 1);
} else {
/* Make sure the sub process does not hold the trigger pipe open */
if (sock1 != NULL) {
struct single *sfd;
sfd = XIO_RDSTREAM(sock1);
if (sfd->triggerfd >= 0) Close(sfd->triggerfd);
sfd = XIO_WRSTREAM(sock1);
if (sfd->triggerfd >= 0) Close(sfd->triggerfd);
}
}
/* 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);
}
diag_set_int('u', shutup);
return 0;
}
num_child++;
Info1("number of children increased to %d", num_child);
/* 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;
}