1 /* source: xioinitialize.c */
2 /* Copyright Gerhard Rieger and contributors (see file CHANGES) */
3 /* Published under the GNU General Public License V.2, see file COPYING */
4 
5 /* this file contains the source for the initialize function */
6 
7 #include "xiosysincludes.h"
8 
9 #include "xioopen.h"
10 #include "xiolockfile.h"
11 
12 #include "xio-openssl.h"	/* xio_reset_fips_mode() */
13 
14 static int xioinitialized;
15 xiofile_t *sock[XIO_MAXSOCK];
16 int (*xiohook_newchild)(void);	/* xio calls this function from a new child
17 				   process */
18 int num_child = 0;
19 
20 /* returns 0 on success or != if an error occurred */
xioinitialize(void)21 int xioinitialize(void) {
22    if (xioinitialized)  return 0;
23 
24    /* configure and .h's cannot guarantee this */
25    assert(sizeof(uint8_t)==1);
26    assert(sizeof(uint16_t)==2);
27    assert(sizeof(uint32_t)==4);
28 
29    /* assertions regarding O_ flags - important for XIO_READABLE() etc. */
30    assert(O_RDONLY==0);
31    assert(O_WRONLY==1);
32    assert(O_RDWR==2);
33 
34    assert(SHUT_RD==0);
35    assert(SHUT_WR==1);
36    assert(SHUT_RDWR==2);
37 
38    /* some assertions about termios */
39 #if WITH_TERMIOS
40 #if defined(CRDLY) && CRDLY_SHIFT >= 0
41    assert(3 << opt_crdly.arg3  == CRDLY);
42 #endif
43 #if defined(TABDLY) && TABDLY_SHIFT >= 0
44    assert(3 << opt_tabdly.arg3 == TABDLY);
45 #endif
46 #if CSIZE_SHIFT >= 0
47    assert(3 << opt_csize.arg3  == CSIZE);
48 #endif
49    {
50       union {
51 	 struct termios termarg;
52 	 tcflag_t flags[4];
53 #if HAVE_TERMIOS_ISPEED
54 	 speed_t speeds[sizeof(struct termios)/sizeof(speed_t)];
55 #endif
56       } tdata;
57       tdata.termarg.c_iflag = 0x12345678;
58       tdata.termarg.c_oflag = 0x23456789;
59       tdata.termarg.c_cflag = 0x3456789a;
60       tdata.termarg.c_lflag = 0x456789ab;
61       assert(tdata.termarg.c_iflag == tdata.flags[0]);
62       assert(tdata.termarg.c_oflag == tdata.flags[1]);
63       assert(tdata.termarg.c_cflag == tdata.flags[2]);
64       assert(tdata.termarg.c_lflag == tdata.flags[3]);
65    }
66 #endif
67 
68    /* these dependencies required in applyopts() for OFUNC_FCNTL */
69    assert(F_GETFD == F_SETFD-1);
70    assert(F_GETFL == F_SETFL-1);
71 
72    {
73       const char *default_ip;
74       default_ip = getenv("SOCAT_DEFAULT_LISTEN_IP");
75       if (default_ip != NULL) {
76 	 switch (default_ip[0]) {
77 	 case '4':
78 	 case '6':
79 	    xioopts.default_ip = default_ip[0]; break;
80 	 }
81       }
82    }
83    {
84       const char *preferred_ip;
85       preferred_ip = getenv("SOCAT_PREFERRED_RESOLVE_IP");
86       if (preferred_ip != NULL) {
87 	 switch (preferred_ip[0]) {
88 	 case '4':
89 	 case '6':
90 	    xioopts.preferred_ip = preferred_ip[0]; break;
91 	 default:
92 	    xioopts.preferred_ip = '0'; break;
93 	 }
94       }
95    }
96 
97    if (Atexit(xioexit) < 0) {
98       Error("atexit(xioexit) failed");
99       return -1;
100    }
101 
102    xioinitialized = 1;
103    return 0;
104 }
105 
106 /* call this function when option -lp (reset program name) has been applied */
xioinitialize2(void)107 int xioinitialize2(void) {
108    pid_t pid = Getpid();
109    xiosetenvulong("PID", pid, 1);
110    xiosetenvulong("PPID", pid, 1);
111    return 0;
112 }
113 
114 
115 /* well, this function is not for initialization, but I could not find a better
116    place for it
117    it is called in the child process after fork
118    it drops the locks of the xiofile's so only the parent owns them
119  */
xiodroplocks(void)120 void xiodroplocks(void) {
121    int i;
122 
123    for (i = 0; i < XIO_MAXSOCK; ++i) {
124       if (sock[i] != NULL && sock[i]->tag != XIO_TAG_INVALID) {
125 	 xiofiledroplock(sock[i]);
126       }
127    }
128 }
129 
130 
131 /* consider an invokation like this:
132    socat -u exec:'some program that accepts data' tcp-l:...,fork
133    we do not want the program to be killed by the first tcp-l sub process, it's
134    better if it survives all sub processes. Thus, it must not be killed when
135    the sub process delivers EOF. Also, a socket that is reused in sub processes
136    should not be shut down (affects the connection), but closed (affects only
137    sub processes copy of file descriptor) */
xio_nokill(xiofile_t * sock)138 static int xio_nokill(xiofile_t *sock) {
139    int result = 0;
140    switch (sock->tag) {
141    case XIO_TAG_INVALID:
142    default:
143       return -1;
144    case XIO_TAG_DUAL:
145       if ((result = xio_nokill((xiofile_t *)sock->dual.stream[0])) != 0)
146 	 return result;
147       result = xio_nokill((xiofile_t *)sock->dual.stream[1]);
148       break;
149    case XIO_TAG_RDONLY:
150    case XIO_TAG_WRONLY:
151    case XIO_TAG_RDWR:
152       /* here is the core of this function */
153       switch (sock->stream.howtoend) {
154       case END_SHUTDOWN_KILL: sock->stream.howtoend = END_CLOSE; break;
155       case END_CLOSE_KILL:    sock->stream.howtoend = END_CLOSE; break;
156       case END_SHUTDOWN:      sock->stream.howtoend = END_CLOSE; break;
157       default: break;
158       }
159       break;
160    }
161    return result;
162 }
163 
164 /* call this function immediately after fork() in child process */
165 /* it performs some neccessary actions
166    returns 0 on success or != 0 if an error occurred */
xio_forked_inchild(void)167 int xio_forked_inchild(void) {
168    int result = 0;
169    int i;
170 
171    diag_fork();
172    for (i=0; i<NUMUNKNOWN; ++i) {
173       diedunknown[i] = 0;
174    }
175    num_child = 0;
176    xiodroplocks();
177 #if WITH_FIPS
178    if (xio_reset_fips_mode() != 0) {
179       result = 1;
180    }
181 #endif /* WITH_FIPS */
182    /* some locks belong to parent process, so "drop" them now */
183    if (xiohook_newchild) {
184       if ((*xiohook_newchild)() != 0) {
185 	 Exit(1);
186       }
187    }
188 
189    /* change XIO_SHUTDOWN_KILL to XIO_SHUTDOWN */
190    if (sock1 != NULL) {
191       int result2;
192       result2 = xio_nokill(sock1);
193       if (result2 < 0)  Exit(1);
194       result |= result2;
195    }
196 
197    return result;
198 }
199 
200 /* subchild != 0 means that the current process is already a child process of
201    the master process and thus the new sub child process should not set the
202    SOCAT_PID variable */
xio_fork(bool subchild,int level)203 pid_t xio_fork(bool subchild, int level) {
204    pid_t pid;
205    const char *forkwaitstring;
206    int forkwaitsecs = 0;
207 
208    if ((pid = Fork()) < 0) {
209       Msg1(level, "fork(): %s", strerror(errno));
210       return pid;
211    }
212 
213    if (pid == 0) {	/* child process */
214       pid_t cpid = Getpid();
215 
216       Info1("just born: child process "F_pid, cpid);
217       if (!subchild) {
218 	 /* set SOCAT_PID to new value */
219 	 xiosetenvulong("PID", pid, 1);
220       }
221       /* gdb recommends to have env controlled sleep after fork */
222       if (forkwaitstring = getenv("SOCAT_FORK_WAIT")) {
223 	 forkwaitsecs = atoi(forkwaitstring);
224 	 Sleep(forkwaitsecs);
225       }
226       if (xio_forked_inchild() != 0) {
227 	 Exit(1);
228       }
229       return 0;
230    }
231 
232    num_child++;
233    /* parent process */
234    Notice1("forked off child process "F_pid, pid);
235    /* gdb recommends to have env controlled sleep after fork */
236    if (forkwaitstring = getenv("SOCAT_FORK_WAIT")) {
237       forkwaitsecs = atoi(forkwaitstring);
238       Sleep(forkwaitsecs);
239    }
240    return pid;
241 }
242