2 /* Copyright Gerhard Rieger */
3 /* Published under the GNU General Public License V.2, see file COPYING */
5 /* this is the main source, including command line option parsing, general
6 control, and the data shuffler */
9 #include "xioconfig.h" /* what features are enabled */
11 #include "sysincludes.h"
23 #include "xiolockfile.h"
26 /* command line options */
31 struct timeval pollintv
; /* with ignoreeof, reread after seconds */
32 struct timeval closwait
; /* after close of x, die after seconds */
33 struct timeval total_timeout
;/* when nothing happens, die after seconds */
35 bool strictopts
; /* stop on errors in address options */
36 char logopt
; /* y..syslog; s..stderr; f..file; m..mixed */
37 bool lefttoright
; /* first addr ro, second addr wo */
38 bool righttoleft
; /* first addr wo, second addr ro */
39 xiolock_t lock
; /* a lock file */
45 {0,500000}, /* closwait */
46 {0,0}, /* total_timeout */
50 false, /* lefttoright */
51 false, /* righttoleft */
52 { NULL
, 0 }, /* lock */
55 void socat_usage(FILE *fd
);
56 void socat_version(FILE *fd
);
57 int socat(const char *address1
, const char *address2
);
59 int cv_newline(unsigned char **buff
, ssize_t
*bytes
, int lineterm1
, int lineterm2
);
60 void socat_signal(int sig
);
61 static int socat_sigchild(struct single
*file
);
63 void lftocrlf(char **in
, ssize_t
*len
, size_t bufsiz
);
64 void crlftolf(char **in
, ssize_t
*len
, size_t bufsiz
);
66 static int socat_lock(void);
67 static void socat_unlock(void);
68 static int socat_newchild(void);
70 static const char socatversion
[] =
73 static const char timestamp
[] = BUILD_DATE
;
75 const char copyright_socat
[] = "socat by Gerhard Rieger - see www.dest-unreach.org";
77 const char copyright_openssl
[] = "This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)";
78 const char copyright_ssleay
[] = "This product includes software written by Tim Hudson (tjh@cryptsoft.com)";
84 int main(int argc
, const char *argv
[]) {
85 const char **arg1
, *a
;
93 if (mainwaitstring
= getenv("SOCAT_MAIN_WAIT")) {
94 sleep(atoi(mainwaitstring
));
96 diag_set('p', strchr(argv
[0], '/') ? strrchr(argv
[0], '/')+1 : argv
[0]);
98 /* we must init before applying options because env settings have lower
99 priority and are to be overridden by options */
100 if (xioinitialize() != 0) {
104 xiosetopt('p', "!!");
107 argc0
= argc
; /* save for later use */
108 arg1
= argv
+1; --argc
;
109 while (arg1
[0] && (arg1
[0][0] == '-')) {
110 switch (arg1
[0][1]) {
111 case 'V': socat_version(stdout
); Exit(0);
116 xioopenhelp(stdout
, (arg1
[0][2]=='?'||arg1
[0][2]=='h') ? (arg1
[0][3]=='?'||arg1
[0][3]=='h') ? 2 : 1 : 0);
118 #endif /* WITH_HELP */
119 case 'd': diag_set('d', NULL
); break;
121 case 'D': socat_opts
.debug
= true; break;
124 switch (arg1
[0][2]) {
125 case 'm': /* mixed mode: stderr, then switch to syslog; + facility */
128 socat_opts
.logopt
= arg1
[0][2];
129 xiosetopt('y', &arg1
[0][3]);
131 case 'y': /* syslog + facility */
132 diag_set(arg1
[0][2], &arg1
[0][3]);
134 case 'f': /* to file, +filename */
135 case 'p': /* artificial program name */
137 diag_set(arg1
[0][2], &arg1
[0][3]);
138 } else if (arg1
[1]) {
139 diag_set(arg1
[0][2], arg1
[1]);
142 Error1("option -l%c requires an argument; use option \"-h\" for help", arg1
[0][2]);
145 case 's': /* stderr */
146 diag_set(arg1
[0][2], NULL
);
152 diag_set_int('h', true);
155 Error1("unknown log option \"%s\"; use option \"-h\" for help", arg1
[0]);
159 case 'v': socat_opts
.verbose
= true; break;
160 case 'x': socat_opts
.verbhex
= true; break;
161 case 'b': if (arg1
[0][2]) {
165 if ((a
= *arg1
) == NULL
) {
166 Error("option -b requires an argument; use option \"-h\" for help");
170 socat_opts
.bufsiz
= strtoul(a
, (char **)&a
, 0);
173 diag_set_int('e', E_FATAL
); break;
174 case 't': if (arg1
[0][2]) {
178 if ((a
= *arg1
) == NULL
) {
179 Error("option -t requires an argument; use option \"-h\" for help");
183 rto
= strtod(a
, (char **)&a
);
184 socat_opts
.closwait
.tv_sec
= rto
;
185 socat_opts
.closwait
.tv_usec
=
186 (rto
-socat_opts
.closwait
.tv_sec
) * 1000000;
188 case 'T': if (arg1
[0][2]) {
192 if ((a
= *arg1
) == NULL
) {
193 Error("option -T requires an argument; use option \"-h\" for help");
197 rto
= strtod(a
, (char **)&a
);
198 socat_opts
.total_timeout
.tv_sec
= rto
;
199 socat_opts
.total_timeout
.tv_usec
=
200 (rto
-socat_opts
.total_timeout
.tv_sec
) * 1000000;
202 case 'u': socat_opts
.lefttoright
= true; break;
203 case 'U': socat_opts
.righttoleft
= true; break;
204 case 'g': xioopts_ignoregroups
= true; break;
205 case 'L': if (socat_opts
.lock
.lockfile
)
206 Error("only one -L and -W option allowed");
208 socat_opts
.lock
.lockfile
= *arg1
+2;
211 if ((socat_opts
.lock
.lockfile
= *arg1
) == NULL
) {
212 Error("option -L requires an argument; use option \"-h\" for help");
217 case 'W': if (socat_opts
.lock
.lockfile
)
218 Error("only one -L and -W option allowed");
220 socat_opts
.lock
.lockfile
= *arg1
+2;
223 if ((socat_opts
.lock
.lockfile
= *arg1
) == NULL
) {
224 Error("option -W requires an argument; use option \"-h\" for help");
228 socat_opts
.lock
.waitlock
= true;
229 socat_opts
.lock
.intervall
.tv_sec
= 1;
230 socat_opts
.lock
.intervall
.tv_nsec
= 0;
232 #if WITH_IP4 || WITH_IP6
239 xioopts
.default_ip
= arg1
[0][1];
240 xioopts
.preferred_ip
= arg1
[0][1];
242 #endif /* WITH_IP4 || WITH_IP6 */
245 case ':': break; /* this "-" is a variation of STDIO */
247 xioinqopt('p', buff
, sizeof(buff
));
248 if (arg1
[0][1] == buff
[0]) {
251 Error1("unknown option \"%s\"; use option \"-h\" for help", arg1
[0]);
254 /* the leading "-" might be a form of the first address */
255 xioinqopt('p', buff
, sizeof(buff
));
256 if (arg1
[0][0] == '-' &&
257 (arg1
[0][1] == '\0' || arg1
[0][1] == ':' ||
258 arg1
[0][1] == ',' || arg1
[0][1] == buff
[0]))
263 Error1("exactly 2 addresses required (there are %d); use option \"-h\" for help", argc
);
266 if (socat_opts
.lefttoright
&& socat_opts
.righttoleft
) {
267 Error("-U and -u must not be combined");
271 Info(copyright_socat
);
273 Info(copyright_openssl
);
274 Info(copyright_ssleay
);
276 Debug2("socat version %s on %s", socatversion
, timestamp
);
277 xiosetenv("VERSION", socatversion
, 1, NULL
); /* SOCAT_VERSION */
278 uname(&ubuf
); /* ! here we circumvent internal tracing (Uname) */
279 Debug4("running on %s version %s, release %s, machine %s\n",
280 ubuf
.sysname
, ubuf
.version
, ubuf
.release
, ubuf
.machine
);
282 #if WITH_MSGLEVEL <= E_DEBUG
283 for (i
= 0; i
< argc0
; ++i
) {
284 Debug2("argv[%d]: \"%s\"", i
, argv
[i
]);
286 #endif /* WITH_MSGLEVEL <= E_DEBUG */
289 struct sigaction act
;
290 sigfillset(&act
.sa_mask
);
292 act
.sa_handler
= socat_signal
;
293 /* not sure which signals should be cauhgt and print a message */
294 Sigaction(SIGHUP
, &act
, NULL
);
295 Sigaction(SIGINT
, &act
, NULL
);
296 Sigaction(SIGQUIT
, &act
, NULL
);
297 Sigaction(SIGILL
, &act
, NULL
);
298 Sigaction(SIGABRT
, &act
, NULL
);
299 Sigaction(SIGBUS
, &act
, NULL
);
300 Sigaction(SIGFPE
, &act
, NULL
);
301 Sigaction(SIGSEGV
, &act
, NULL
);
302 Sigaction(SIGTERM
, &act
, NULL
);
304 Signal(SIGPIPE
, SIG_IGN
);
307 xiohook_newchild
= &socat_newchild
;
309 if (lockrc
= socat_lock()) {
310 /* =0: goon; >0: locked; <0: error, printed in sub */
312 Error1("could not obtain lock \"%s\"", socat_opts
.lock
.lockfile
);
316 Atexit(socat_unlock
);
318 result
= socat(arg1
[0], arg1
[1]);
319 Notice1("exiting with status %d", result
);
321 return 0; /* not reached, just for gcc -Wall */
325 void socat_usage(FILE *fd
) {
326 fputs(copyright_socat
, fd
); fputc('\n', fd
);
327 fputs("Usage:\n", fd
);
328 fputs("socat [options] <bi-address> <bi-address>\n", fd
);
329 fputs(" options:\n", fd
);
330 fputs(" -V print version and feature information to stdout, and exit\n", fd
);
332 fputs(" -h|-? print a help text describing command line options and addresses\n", fd
);
333 fputs(" -hh like -h, plus a list of all common address option names\n", fd
);
334 fputs(" -hhh like -hh, plus a list of all available address option names\n", fd
);
335 #endif /* WITH_HELP */
336 fputs(" -d increase verbosity (use up to 4 times; 2 are recommended)\n", fd
);
338 fputs(" -D analyze file descriptors before loop\n", fd
);
340 fputs(" -ly[facility] log to syslog, using facility (default is daemon)\n", fd
);
341 fputs(" -lf<logfile> log to file\n", fd
);
342 fputs(" -ls log to stderr (default if no other log)\n", fd
);
343 fputs(" -lm[facility] mixed log mode (stderr during initialization, then syslog)\n", fd
);
344 fputs(" -lp<progname> set the program name used for logging\n", fd
);
345 fputs(" -lu use microseconds for logging timestamps\n", fd
);
346 fputs(" -lh add hostname to log messages\n", fd
);
347 fputs(" -v verbose data traffic, text\n", fd
);
348 fputs(" -x verbose data traffic, hexadecimal\n", fd
);
349 fputs(" -b<size_t> set data buffer size (8192)\n", fd
);
350 fputs(" -s sloppy (continue on error)\n", fd
);
351 fputs(" -t<timeout> wait seconds before closing second channel\n", fd
);
352 fputs(" -T<timeout> total inactivity timeout in seconds\n", fd
);
353 fputs(" -u unidirectional mode (left to right)\n", fd
);
354 fputs(" -U unidirectional mode (right to left)\n", fd
);
355 fputs(" -g do not check option groups\n", fd
);
356 fputs(" -L <lockfile> try to obtain lock, or fail\n", fd
);
357 fputs(" -W <lockfile> try to obtain lock, or wait\n", fd
);
359 fputs(" -4 prefer IPv4 if version is not explicitly specified\n", fd
);
362 fputs(" -6 prefer IPv6 if version is not explicitly specified\n", fd
);
367 void socat_version(FILE *fd
) {
370 fputs(copyright_socat
, fd
); fputc('\n', fd
);
371 fprintf(fd
, "socat version %s on %s\n", socatversion
, timestamp
);
373 fprintf(fd
, " running on %s version %s, release %s, machine %s\n",
374 ubuf
.sysname
, ubuf
.version
, ubuf
.release
, ubuf
.machine
);
375 fputs("features:\n", fd
);
377 fprintf(fd
, " #define WITH_STDIO %d\n", WITH_STDIO
);
379 fputs(" #undef WITH_STDIO\n", fd
);
382 fprintf(fd
, " #define WITH_FDNUM %d\n", WITH_FDNUM
);
384 fputs(" #undef WITH_FDNUM\n", fd
);
387 fprintf(fd
, " #define WITH_FILE %d\n", WITH_FILE
);
389 fputs(" #undef WITH_FILE\n", fd
);
392 fprintf(fd
, " #define WITH_CREAT %d\n", WITH_CREAT
);
394 fputs(" #undef WITH_CREAT\n", fd
);
397 fprintf(fd
, " #define WITH_GOPEN %d\n", WITH_GOPEN
);
399 fputs(" #undef WITH_GOPEN\n", fd
);
402 fprintf(fd
, " #define WITH_TERMIOS %d\n", WITH_TERMIOS
);
404 fputs(" #undef WITH_TERMIOS\n", fd
);
407 fprintf(fd
, " #define WITH_PIPE %d\n", WITH_PIPE
);
409 fputs(" #undef WITH_PIPE\n", fd
);
412 fprintf(fd
, " #define WITH_UNIX %d\n", WITH_UNIX
);
414 fputs(" #undef WITH_UNIX\n", fd
);
415 #endif /* WITH_UNIX */
416 #ifdef WITH_ABSTRACT_UNIXSOCKET
417 fprintf(fd
, " #define WITH_ABSTRACT_UNIXSOCKET %d\n", WITH_ABSTRACT_UNIXSOCKET
);
419 fputs(" #undef WITH_ABSTRACT_UNIXSOCKET\n", fd
);
420 #endif /* WITH_ABSTRACT_UNIXSOCKET */
422 fprintf(fd
, " #define WITH_IP4 %d\n", WITH_IP4
);
424 fputs(" #undef WITH_IP4\n", fd
);
427 fprintf(fd
, " #define WITH_IP6 %d\n", WITH_IP6
);
429 fputs(" #undef WITH_IP6\n", fd
);
432 fprintf(fd
, " #define WITH_RAWIP %d\n", WITH_RAWIP
);
434 fputs(" #undef WITH_RAWIP\n", fd
);
436 #ifdef WITH_GENERICSOCKET
437 fprintf(fd
, " #define WITH_GENERICSOCKET %d\n", WITH_GENERICSOCKET
);
439 fputs(" #undef WITH_GENERICSOCKET\n", fd
);
441 #ifdef WITH_INTERFACE
442 fprintf(fd
, " #define WITH_INTERFACE %d\n", WITH_INTERFACE
);
444 fputs(" #undef WITH_INTERFACE\n", fd
);
447 fprintf(fd
, " #define WITH_TCP %d\n", WITH_TCP
);
449 fputs(" #undef WITH_TCP\n", fd
);
452 fprintf(fd
, " #define WITH_UDP %d\n", WITH_UDP
);
454 fputs(" #undef WITH_UDP\n", fd
);
457 fprintf(fd
, " #define WITH_SCTP %d\n", WITH_SCTP
);
459 fputs(" #undef WITH_SCTP\n", fd
);
462 fprintf(fd
, " #define WITH_LISTEN %d\n", WITH_LISTEN
);
464 fputs(" #undef WITH_LISTEN\n", fd
);
467 fprintf(fd
, " #define WITH_SOCKS4 %d\n", WITH_SOCKS4
);
469 fputs(" #undef WITH_SOCKS4\n", fd
);
472 fprintf(fd
, " #define WITH_SOCKS4A %d\n", WITH_SOCKS4A
);
474 fputs(" #undef WITH_SOCKS4A\n", fd
);
477 fprintf(fd
, " #define WITH_PROXY %d\n", WITH_PROXY
);
479 fputs(" #undef WITH_PROXY\n", fd
);
482 fprintf(fd
, " #define WITH_SYSTEM %d\n", WITH_SYSTEM
);
484 fputs(" #undef WITH_SYSTEM\n", fd
);
487 fprintf(fd
, " #define WITH_EXEC %d\n", WITH_EXEC
);
489 fputs(" #undef WITH_EXEC\n", fd
);
492 fprintf(fd
, " #define WITH_READLINE %d\n", WITH_READLINE
);
494 fputs(" #undef WITH_READLINE\n", fd
);
497 fprintf(fd
, " #define WITH_TUN %d\n", WITH_TUN
);
499 fputs(" #undef WITH_TUN\n", fd
);
502 fprintf(fd
, " #define WITH_PTY %d\n", WITH_PTY
);
504 fputs(" #undef WITH_PTY\n", fd
);
507 fprintf(fd
, " #define WITH_OPENSSL %d\n", WITH_OPENSSL
);
509 fputs(" #undef WITH_OPENSSL\n", fd
);
512 fprintf(fd
, " #define WITH_FIPS %d\n", WITH_FIPS
);
514 fputs(" #undef WITH_FIPS\n", fd
);
517 fprintf(fd
, " #define WITH_LIBWRAP %d\n", WITH_LIBWRAP
);
519 fputs(" #undef WITH_LIBWRAP\n", fd
);
522 fprintf(fd
, " #define WITH_SYCLS %d\n", WITH_SYCLS
);
524 fputs(" #undef WITH_SYCLS\n", fd
);
527 fprintf(fd
, " #define WITH_FILAN %d\n", WITH_FILAN
);
529 fputs(" #undef WITH_FILAN\n", fd
);
532 fprintf(fd
, " #define WITH_RETRY %d\n", WITH_RETRY
);
534 fputs(" #undef WITH_RETRY\n", fd
);
537 fprintf(fd
, " #define WITH_MSGLEVEL %d /*%s*/\n", WITH_MSGLEVEL
,
538 &"debug\0\0\0info\0\0\0\0notice\0\0warn\0\0\0\0error\0\0\0fatal\0\0\0"[WITH_MSGLEVEL
<<3]);
540 fputs(" #undef WITH_MSGLEVEL\n", fd
);
545 xiofile_t
*sock1
, *sock2
;
546 int closing
= 0; /* 0..no eof yet, 1..first eof just occurred,
547 2..counting down closing timeout */
549 /* call this function when the common command line options are parsed, and the
550 addresses are extracted (but not resolved). */
551 int socat(const char *address1
, const char *address2
) {
554 if (socat_opts
.lefttoright
) {
555 if ((sock1
= xioopen(address1
, XIO_RDONLY
|XIO_MAYFORK
|XIO_MAYCHILD
|XIO_MAYCONVERT
)) == NULL
) {
558 xiosetsigchild(sock1
, socat_sigchild
);
559 } else if (socat_opts
.righttoleft
) {
560 if ((sock1
= xioopen(address1
, XIO_WRONLY
|XIO_MAYFORK
|XIO_MAYCHILD
|XIO_MAYCONVERT
)) == NULL
) {
563 xiosetsigchild(sock1
, socat_sigchild
);
565 if ((sock1
= xioopen(address1
, XIO_RDWR
|XIO_MAYFORK
|XIO_MAYCHILD
|XIO_MAYCONVERT
)) == NULL
) {
568 xiosetsigchild(sock1
, socat_sigchild
);
571 if (XIO_READABLE(sock1
) &&
572 (XIO_RDSTREAM(sock1
)->howtoend
== END_KILL
||
573 XIO_RDSTREAM(sock1
)->howtoend
== END_CLOSE_KILL
||
574 XIO_RDSTREAM(sock1
)->howtoend
== END_SHUTDOWN_KILL
)) {
575 if (XIO_RDSTREAM(sock1
)->para
.exec
.pid
== diedunknown1
) {
576 /* child has alread died... but it might have put regular data into
577 the communication channel, so continue */
578 Info1("child "F_pid
" has already died (diedunknown1)",
579 XIO_RDSTREAM(sock1
)->para
.exec
.pid
);
581 XIO_RDSTREAM(sock1
)->para
.exec
.pid
= 0;
582 /* return STAT_RETRYLATER; */
583 } else if (XIO_RDSTREAM(sock1
)->para
.exec
.pid
== diedunknown2
) {
584 Info1("child "F_pid
" has already died (diedunknown2)",
585 XIO_RDSTREAM(sock1
)->para
.exec
.pid
);
587 XIO_RDSTREAM(sock1
)->para
.exec
.pid
= 0;
588 } else if (XIO_RDSTREAM(sock1
)->para
.exec
.pid
== diedunknown3
) {
589 Info1("child "F_pid
" has already died (diedunknown3)",
590 XIO_RDSTREAM(sock1
)->para
.exec
.pid
);
592 XIO_RDSTREAM(sock1
)->para
.exec
.pid
= 0;
593 } else if (XIO_RDSTREAM(sock1
)->para
.exec
.pid
== diedunknown4
) {
594 Info1("child "F_pid
" has already died (diedunknown4)",
595 XIO_RDSTREAM(sock1
)->para
.exec
.pid
);
597 XIO_RDSTREAM(sock1
)->para
.exec
.pid
= 0;
602 mayexec
= (sock1
->common
.flags
&XIO_DOESCONVERT
? 0 : XIO_MAYEXEC
);
603 if (XIO_WRITABLE(sock1
)) {
604 if (XIO_READABLE(sock1
)) {
605 if ((sock2
= xioopen(address2
, XIO_RDWR
|XIO_MAYFORK
|XIO_MAYCHILD
|mayexec
|XIO_MAYCONVERT
)) == NULL
) {
608 xiosetsigchild(sock2
, socat_sigchild
);
610 if ((sock2
= xioopen(address2
, XIO_RDONLY
|XIO_MAYFORK
|XIO_MAYCHILD
|mayexec
|XIO_MAYCONVERT
)) == NULL
) {
613 xiosetsigchild(sock2
, socat_sigchild
);
615 } else { /* assuming sock1 is readable */
616 if ((sock2
= xioopen(address2
, XIO_WRONLY
|XIO_MAYFORK
|XIO_MAYCHILD
|mayexec
|XIO_MAYCONVERT
)) == NULL
) {
619 xiosetsigchild(sock2
, socat_sigchild
);
622 if (XIO_READABLE(sock2
) &&
623 (XIO_RDSTREAM(sock2
)->howtoend
== END_KILL
||
624 XIO_RDSTREAM(sock2
)->howtoend
== END_CLOSE_KILL
||
625 XIO_RDSTREAM(sock2
)->howtoend
== END_SHUTDOWN_KILL
)) {
626 if (XIO_RDSTREAM(sock2
)->para
.exec
.pid
== diedunknown1
) {
627 /* child has alread died... but it might have put regular data into
628 the communication channel, so continue */
629 Info1("child "F_pid
" has already died (diedunknown1)",
630 XIO_RDSTREAM(sock2
)->para
.exec
.pid
);
632 XIO_RDSTREAM(sock2
)->para
.exec
.pid
= 0;
633 /* return STAT_RETRYLATER; */
634 } else if (XIO_RDSTREAM(sock2
)->para
.exec
.pid
== diedunknown2
) {
635 Info1("child "F_pid
" has already died (diedunknown2)",
636 XIO_RDSTREAM(sock2
)->para
.exec
.pid
);
638 XIO_RDSTREAM(sock2
)->para
.exec
.pid
= 0;
639 } else if (XIO_RDSTREAM(sock2
)->para
.exec
.pid
== diedunknown3
) {
640 Info1("child "F_pid
" has already died (diedunknown3)",
641 XIO_RDSTREAM(sock2
)->para
.exec
.pid
);
643 XIO_RDSTREAM(sock2
)->para
.exec
.pid
= 0;
644 } else if (XIO_RDSTREAM(sock2
)->para
.exec
.pid
== diedunknown4
) {
645 Info1("child "F_pid
" has already died (diedunknown4)",
646 XIO_RDSTREAM(sock2
)->para
.exec
.pid
);
648 XIO_RDSTREAM(sock2
)->para
.exec
.pid
= 0;
653 Info("resolved and opened all sock addresses");
655 _socat(); /* nsocks, sockets are visible outside function */
658 /* checks if this is a connection to a child process, and if so, sees if the
659 child already died, leaving some data for us.
660 returns <0 if an error occurred;
661 returns 0 if no child or not yet died or died without data (sets eof);
662 returns >0 if child died and left data
664 int childleftdata(xiofile_t
*xfd
) {
668 /* have to check if a child process died before, but left read data */
669 if (XIO_READABLE(xfd
) &&
670 (XIO_RDSTREAM(xfd
)->howtoend
== END_KILL
||
671 XIO_RDSTREAM(xfd
)->howtoend
== END_CLOSE_KILL
||
672 XIO_RDSTREAM(xfd
)->howtoend
== END_SHUTDOWN_KILL
) &&
673 XIO_RDSTREAM(xfd
)->para
.exec
.pid
== 0) {
674 struct timeval timeout
= { 0, 0 };
676 if (XIO_READABLE(xfd
) && !(XIO_RDSTREAM(xfd
)->eof
>= 2 && !XIO_RDSTREAM(xfd
)->ignoreeof
)) {
677 in
.fd
= XIO_GETRDFD(xfd
);
678 in
.events
= POLLIN
/*|POLLRDBAND*/;
683 retval
= xiopoll(&in
, 1, &timeout
);
684 _errno
= errno
; diag_flush(); errno
= _errno
; /* just in case it's not debug level and Msg() not been called */
685 } while (retval
< 0 && errno
== EINTR
);
688 Error5("xiopoll({%d,%0o}, 1, {"F_tv_sec
"."F_tv_usec
"}): %s",
689 in
.fd
, in
.events
, timeout
.tv_sec
, timeout
.tv_usec
,
694 Info("terminated child did not leave data for us");
695 XIO_RDSTREAM(xfd
)->eof
= 2;
697 closing
= MAX(closing
, 1);
703 int xiotransfer(xiofile_t
*inpipe
, xiofile_t
*outpipe
,
704 unsigned char **buff
, size_t bufsiz
, bool righttoleft
);
706 bool mayrd1
; /* sock1 has read data or eof, according to poll() */
707 bool mayrd2
; /* sock2 has read data or eof, according to poll() */
708 bool maywr1
; /* sock1 can be written to, according to poll() */
709 bool maywr2
; /* sock2 can be written to, according to poll() */
711 /* here we come when the sockets are opened (in the meaning of C language),
712 and their options are set/applied
713 returns -1 on error or 0 on success */
715 struct pollfd fds
[4],
722 ssize_t bytes1
, bytes2
;
723 int polling
= 0; /* handling ignoreeof */
724 int wasaction
= 1; /* last poll was active, do NOT sleep before next */
725 struct timeval total_timeout
; /* the actual total timeout timer */
728 if (socat_opts
.debug
) {
730 int msglevel
, exitlevel
;
732 msglevel
= diag_get_int('D'); /* save current message level */
733 diag_set_int('D', E_ERROR
); /* only print errors and fatals in filan */
734 exitlevel
= diag_get_int('e'); /* save current exit level */
735 diag_set_int('e', E_FATAL
); /* only exit on fatals */
737 fdi
= XIO_GETRDFD(sock1
);
738 fdo
= XIO_GETWRFD(sock1
);
739 filan_fd(fdi
, stderr
);
741 filan_fd(fdo
, stderr
);
744 fdi
= XIO_GETRDFD(sock2
);
745 fdo
= XIO_GETWRFD(sock2
);
746 filan_fd(fdi
, stderr
);
748 filan_fd(fdo
, stderr
);
751 diag_set_int('e', exitlevel
); /* restore old exit level */
752 diag_set_int('D', msglevel
); /* restore old message level */
754 #endif /* WITH_FILAN */
756 /* when converting nl to crnl, size might double */
757 buff
= Malloc(2*socat_opts
.bufsiz
+1);
758 if (buff
== NULL
) return -1;
760 if (socat_opts
.logopt
== 'm' && xioinqopt('l', NULL
, 0) == 'm') {
761 Info("switching to syslog");
762 diag_set('y', xioopts
.syslogfac
);
763 xiosetopt('l', "\0");
765 total_timeout
= socat_opts
.total_timeout
;
767 Notice4("starting data transfer loop with FDs [%d,%d] and [%d,%d]",
768 XIO_GETRDFD(sock1
), XIO_GETWRFD(sock1
),
769 XIO_GETRDFD(sock2
), XIO_GETWRFD(sock2
));
770 while (XIO_RDSTREAM(sock1
)->eof
<= 1 ||
771 XIO_RDSTREAM(sock2
)->eof
<= 1) {
772 struct timeval timeout
, *to
= NULL
;
774 Debug6("data loop: sock1->eof=%d, sock2->eof=%d, closing=%d, wasaction=%d, total_to={"F_tv_sec
"."F_tv_usec
"}",
775 XIO_RDSTREAM(sock1
)->eof
, XIO_RDSTREAM(sock2
)->eof
,
777 total_timeout
.tv_sec
, total_timeout
.tv_usec
);
782 if (socat_opts
.total_timeout
.tv_sec
!= 0 ||
783 socat_opts
.total_timeout
.tv_usec
!= 0) {
784 if (total_timeout
.tv_usec
< socat_opts
.pollintv
.tv_usec
) {
785 total_timeout
.tv_usec
+= 1000000;
786 total_timeout
.tv_sec
-= 1;
788 total_timeout
.tv_sec
-= socat_opts
.pollintv
.tv_sec
;
789 total_timeout
.tv_usec
-= socat_opts
.pollintv
.tv_usec
;
790 if (total_timeout
.tv_sec
< 0 ||
791 total_timeout
.tv_sec
== 0 && total_timeout
.tv_usec
< 0) {
792 Notice("inactivity timeout triggered");
804 /* there is a ignoreeof poll timeout, use it */
805 timeout
= socat_opts
.pollintv
;
807 } else if (socat_opts
.total_timeout
.tv_sec
!= 0 ||
808 socat_opts
.total_timeout
.tv_usec
!= 0) {
809 /* there might occur a total inactivity timeout */
810 timeout
= socat_opts
.total_timeout
;
817 /* first eof already occurred, start end timer */
818 timeout
= socat_opts
.pollintv
;
823 /* frame 1: set the poll parameters and loop over poll() EINTR) */
824 do { /* loop over poll() EINTR */
827 childleftdata(sock1
);
828 childleftdata(sock2
);
831 /* first eof already occurred, start end timer */
832 timeout
= socat_opts
.closwait
;
837 /* use the ignoreeof timeout if appropriate */
840 (socat_opts
.pollintv
.tv_sec
< timeout
.tv_sec
) ||
841 ((socat_opts
.pollintv
.tv_sec
== timeout
.tv_sec
) &&
842 socat_opts
.pollintv
.tv_usec
< timeout
.tv_usec
)) {
843 timeout
= socat_opts
.pollintv
;
847 /* now the fds will be assigned */
848 if (XIO_READABLE(sock1
) &&
849 !(XIO_RDSTREAM(sock1
)->eof
> 1 && !XIO_RDSTREAM(sock1
)->ignoreeof
) &&
850 !socat_opts
.righttoleft
) {
851 if (!mayrd1
&& !(XIO_RDSTREAM(sock1
)->eof
> 1)) {
852 fd1in
->fd
= XIO_GETRDFD(sock1
);
853 fd1in
->events
= POLLIN
;
858 fd2out
->fd
= XIO_GETWRFD(sock2
);
859 fd2out
->events
= POLLOUT
;
867 if (XIO_READABLE(sock2
) &&
868 !(XIO_RDSTREAM(sock2
)->eof
> 1 && !XIO_RDSTREAM(sock2
)->ignoreeof
) &&
869 !socat_opts
.lefttoright
) {
870 if (!mayrd2
&& !(XIO_RDSTREAM(sock2
)->eof
> 1)) {
871 fd2in
->fd
= XIO_GETRDFD(sock2
);
872 fd2in
->events
= POLLIN
;
877 fd1out
->fd
= XIO_GETWRFD(sock1
);
878 fd1out
->events
= POLLOUT
;
886 /* frame 0: innermost part of the transfer loop: check FD status */
887 retval
= xiopoll(fds
, 4, to
);
888 if (retval
>= 0 || errno
!= EINTR
) {
892 Info1("poll(): %s", strerror(errno
));
897 when an exec'd process sends data and terminates, it is unpredictable
898 whether the data or the sigchild arrives first.
902 Error11("xiopoll({%d,%0o}{%d,%0o}{%d,%0o}{%d,%0o}, 4, {"F_tv_sec
"."F_tv_usec
"}): %s",
903 fds
[0].fd
, fds
[0].events
, fds
[1].fd
, fds
[1].events
,
904 fds
[2].fd
, fds
[2].events
, fds
[3].fd
, fds
[3].events
,
905 timeout
.tv_sec
, timeout
.tv_usec
, strerror(errno
));
908 } else if (retval
== 0) {
909 Info2("poll timed out (no data within %ld.%06ld seconds)",
910 closing
>=1?socat_opts
.closwait
.tv_sec
:socat_opts
.total_timeout
.tv_sec
,
911 closing
>=1?socat_opts
.closwait
.tv_usec
:socat_opts
.total_timeout
.tv_usec
);
912 if (polling
&& !wasaction
) {
913 /* there was a ignoreeof poll timeout, use it */
915 if (XIO_RDSTREAM(sock1
)->ignoreeof
) {
918 if (XIO_RDSTREAM(sock2
)->ignoreeof
) {
921 } else if (polling
&& wasaction
) {
924 } else if (socat_opts
.total_timeout
.tv_sec
!= 0 ||
925 socat_opts
.total_timeout
.tv_usec
!= 0) {
926 /* there was a total inactivity timeout */
927 Notice("inactivity timeout triggered");
935 /* one possibility to come here is ignoreeof on some fd, but no EOF
936 and no data on any descriptor - this is no indication for end! */
940 if (XIO_READABLE(sock1
) && XIO_GETRDFD(sock1
) >= 0 &&
941 (fd1in
->revents
/*&(POLLIN|POLLHUP|POLLERR)*/)) {
942 if (fd1in
->revents
& POLLNVAL
) {
943 /* this is what we find on Mac OS X when poll()'ing on a device or
944 named pipe. a read() might imm. return with 0 bytes, resulting
946 Error1("poll(...[%d]: invalid request", fd1in
->fd
);
952 if (XIO_READABLE(sock2
) && XIO_GETRDFD(sock2
) >= 0 &&
954 if (fd2in
->revents
& POLLNVAL
) {
955 Error1("poll(...[%d]: invalid request", fd2in
->fd
);
961 if (XIO_GETWRFD(sock1
) >= 0 && fd1out
->fd
>= 0 && fd1out
->revents
) {
962 if (fd1out
->revents
& POLLNVAL
) {
963 Error1("poll(...[%d]: invalid request", fd1out
->fd
);
969 if (XIO_GETWRFD(sock2
) >= 0 && fd2out
->fd
>= 0 && fd2out
->revents
) {
970 if (fd2out
->revents
& POLLNVAL
) {
971 Error1("poll(...[%d]: invalid request", fd2out
->fd
);
978 if (mayrd1
&& maywr2
) {
980 if ((bytes1
= xiotransfer(sock1
, sock2
, &buff
, socat_opts
.bufsiz
, false))
982 if (errno
!= EAGAIN
) {
983 closing
= MAX(closing
, 1);
984 Notice("socket 1 to socket 2 is in error");
985 if (socat_opts
.lefttoright
) {
989 } else if (bytes1
> 0) {
991 total_timeout
= socat_opts
.total_timeout
;
993 /* is more data available that has already passed poll()? */
994 mayrd1
= (xiopending(sock1
) > 0);
995 if (XIO_RDSTREAM(sock1
)->readbytes
!= 0 &&
996 XIO_RDSTREAM(sock1
)->actbytes
== 0) {
997 /* avoid idle when all readbytes already there */
1000 /* escape char occurred? */
1001 if (XIO_RDSTREAM(sock1
)->actescape
) {
1002 bytes1
= 0; /* indicate EOF */
1005 /* (bytes1 == 0) handled later */
1010 if (mayrd2
&& maywr1
) {
1012 if ((bytes2
= xiotransfer(sock2
, sock1
, &buff
, socat_opts
.bufsiz
, true))
1014 if (errno
!= EAGAIN
) {
1015 closing
= MAX(closing
, 1);
1016 Notice("socket 2 to socket 1 is in error");
1017 if (socat_opts
.righttoleft
) {
1021 } else if (bytes2
> 0) {
1023 total_timeout
= socat_opts
.total_timeout
;
1025 /* is more data available that has already passed poll()? */
1026 mayrd2
= (xiopending(sock2
) > 0);
1027 if (XIO_RDSTREAM(sock2
)->readbytes
!= 0 &&
1028 XIO_RDSTREAM(sock2
)->actbytes
== 0) {
1029 /* avoid idle when all readbytes already there */
1032 /* escape char occurred? */
1033 if (XIO_RDSTREAM(sock2
)->actescape
) {
1034 bytes2
= 0; /* indicate EOF */
1037 /* (bytes2 == 0) handled later */
1042 /* NOW handle EOFs */
1044 /*0 Debug4("bytes1=F_Zd, XIO_RDSTREAM(sock1)->eof=%d, XIO_RDSTREAM(sock1)->ignoreeof=%d, closing=%d",
1045 bytes1, XIO_RDSTREAM(sock1)->eof, XIO_RDSTREAM(sock1)->ignoreeof,
1047 if (bytes1
== 0 || XIO_RDSTREAM(sock1
)->eof
>= 2) {
1048 if (XIO_RDSTREAM(sock1
)->ignoreeof
&&
1049 !XIO_RDSTREAM(sock1
)->actescape
&& !closing
) {
1050 Debug1("socket 1 (fd %d) is at EOF, ignoring",
1051 XIO_RDSTREAM(sock1
)->fd
); /*! */
1053 polling
= 1; /* do not hook this eof fd to poll for pollintv*/
1055 Notice1("socket 1 (fd %d) is at EOF", XIO_GETRDFD(sock1
));
1056 xioshutdown(sock2
, SHUT_WR
);
1057 XIO_RDSTREAM(sock1
)->eof
= 2;
1058 XIO_RDSTREAM(sock1
)->ignoreeof
= false;
1060 } else if (polling
&& XIO_RDSTREAM(sock1
)->ignoreeof
) {
1063 if (XIO_RDSTREAM(sock1
)->eof
>= 2) {
1064 if (socat_opts
.lefttoright
) {
1070 if (bytes2
== 0 || XIO_RDSTREAM(sock2
)->eof
>= 2) {
1071 if (XIO_RDSTREAM(sock2
)->ignoreeof
&&
1072 !XIO_RDSTREAM(sock2
)->actescape
&& !closing
) {
1073 Debug1("socket 2 (fd %d) is at EOF, ignoring",
1074 XIO_RDSTREAM(sock2
)->fd
);
1076 polling
= 1; /* do not hook this eof fd to poll for pollintv*/
1078 Notice1("socket 2 (fd %d) is at EOF", XIO_GETRDFD(sock2
));
1079 xioshutdown(sock1
, SHUT_WR
);
1080 XIO_RDSTREAM(sock2
)->eof
= 2;
1081 XIO_RDSTREAM(sock2
)->ignoreeof
= false;
1083 } else if (polling
&& XIO_RDSTREAM(sock2
)->ignoreeof
) {
1086 if (XIO_RDSTREAM(sock2
)->eof
>= 2) {
1087 if (socat_opts
.righttoleft
) {
1094 /* close everything that's still open */
1103 #define MAXTIMESTAMPLEN 128
1104 /* prints the timestamp to the buffer and terminates it with '\0'. This buffer
1105 should be at least MAXTIMESTAMPLEN bytes long.
1106 returns 0 on success or -1 if an error occurred */
1107 int gettimestamp(char *timestamp
) {
1109 #if HAVE_GETTIMEOFDAY || 1
1113 #else /* !HAVE_GETTIMEOFDAY */
1115 #endif /* !HAVE_GETTIMEOFDAY */
1117 #if HAVE_GETTIMEOFDAY || 1
1118 result
= gettimeofday(&now
, NULL
);
1124 bytes
= strftime(timestamp
, 20, "%Y/%m/%d %H:%M:%S", localtime(&nowt
));
1125 bytes
+= sprintf(timestamp
+19, "."F_tv_usec
" ", now
.tv_usec
);
1127 strcpy(timestamp
, ctime(&nowt
));
1128 bytes
= strlen(timestamp
);
1131 #else /* !HAVE_GETTIMEOFDAY */
1132 now
= time(NULL
); if (now
== (time_t)-1) {
1136 bytes
= strftime(timestamp
, 21, "%Y/%m/%d %H:%M:%S ", localtime(&now
));
1138 strcpy(timestamp
, ctime(&now
));
1139 bytes
= strlen(timestamp
);
1142 #endif /* !HAVE_GETTIMEOFDAY */
1146 static const char *prefixltor
= "> ";
1147 static const char *prefixrtol
= "< ";
1148 static unsigned long numltor
;
1149 static unsigned long numrtol
;
1150 /* print block header (during verbose or hex dump)
1151 returns 0 on success or -1 if an error occurred */
1153 xioprintblockheader(FILE *file
, size_t bytes
, bool righttoleft
) {
1154 char timestamp
[MAXTIMESTAMPLEN
];
1155 char buff
[128+MAXTIMESTAMPLEN
];
1156 if (gettimestamp(timestamp
) < 0) {
1160 sprintf(buff
, "%s%s length="F_Zu
" from=%lu to=%lu\n",
1161 prefixrtol
, timestamp
, bytes
, numrtol
, numrtol
+bytes
-1);
1164 sprintf(buff
, "%s%s length="F_Zu
" from=%lu to=%lu\n",
1165 prefixltor
, timestamp
, bytes
, numltor
, numltor
+bytes
-1);
1173 /* inpipe is suspected to have read data available; read at most bufsiz bytes
1174 and transfer them to outpipe. Perform required data conversions.
1175 buff must be a malloc()'ed storage and might be realloc()'ed in this
1176 function if more space is required after conversions.
1177 Returns the number of bytes written, or 0 on EOF or <0 if an
1178 error occurred or when data was read but none written due to conversions
1179 (with EAGAIN). EAGAIN also occurs when reading from a nonblocking FD where
1180 the file has a mandatory lock.
1181 If 0 bytes were read (EOF), it does NOT shutdown or close a channel, and it
1182 does NOT write a zero bytes block.
1184 /* inpipe, outpipe must be single descriptors (not dual!) */
1185 int xiotransfer(xiofile_t
*inpipe
, xiofile_t
*outpipe
,
1186 unsigned char **buff
, size_t bufsiz
, bool righttoleft
) {
1187 ssize_t bytes
, writt
= 0;
1189 bytes
= xioread(inpipe
, *buff
, bufsiz
);
1191 if (errno
!= EAGAIN
)
1192 XIO_RDSTREAM(inpipe
)->eof
= 2;
1193 /*xioshutdown(inpipe, SHUT_RD);*/
1196 if (bytes
== 0 && XIO_RDSTREAM(inpipe
)->ignoreeof
&& !closing
) {
1198 } else if (bytes
== 0) {
1199 XIO_RDSTREAM(inpipe
)->eof
= 2;
1200 closing
= MAX(closing
, 1);
1204 /* handle escape char */
1205 if (XIO_RDSTREAM(inpipe
)->escape
!= -1) {
1206 /* check input data for escape char */
1207 unsigned char *ptr
= *buff
;
1209 while (ctr
< bytes
) {
1210 if (*ptr
== XIO_RDSTREAM(inpipe
)->escape
) {
1211 /* found: set flag, truncate input data */
1212 XIO_RDSTREAM(inpipe
)->actescape
= true;
1214 Info("escape char found in input");
1220 XIO_RDSTREAM(inpipe
)->eof
= 2;
1227 if (XIO_RDSTREAM(inpipe
)->lineterm
!=
1228 XIO_WRSTREAM(outpipe
)->lineterm
) {
1229 cv_newline(buff
, &bytes
,
1230 XIO_RDSTREAM(inpipe
)->lineterm
,
1231 XIO_WRSTREAM(outpipe
)->lineterm
);
1234 errno
= EAGAIN
; return -1;
1237 if (socat_opts
.verbose
&& socat_opts
.verbhex
) {
1242 const unsigned char *end
, *s
, *t
;
1244 end
= (*buff
)+bytes
;
1245 xioprintblockheader(stderr
, bytes
, righttoleft
);
1248 j
= Min(N
, (size_t)(end
-s
));
1255 fprintf(stderr
, " %02x", c
);
1257 if (c
== '\n') break;
1260 /* fill hex column */
1282 fputc('\n', stderr
);
1285 fputs("--\n", stderr
);
1286 } else if (socat_opts
.verbose
) {
1288 xioprintblockheader(stderr
, bytes
, righttoleft
);
1289 while (i
< (size_t)bytes
) {
1291 if (i
> 0 && (*buff
)[i
-1] == '\n')
1294 case '\a' : fputs("\\a", stderr
); break;
1295 case '\b' : fputs("\\b", stderr
); break;
1296 case '\t' : fputs("\t", stderr
); break;
1297 case '\n' : fputs("\n", stderr
); break;
1298 case '\v' : fputs("\\v", stderr
); break;
1299 case '\f' : fputs("\\f", stderr
); break;
1300 case '\r' : fputs("\\r", stderr
); break;
1301 case '\\' : fputs("\\\\", stderr
); break;
1310 } else if (socat_opts
.verbhex
) {
1313 xioprintblockheader(stderr
, bytes
, righttoleft
);
1314 for (i
= 0; i
< bytes
; ++i
) {
1315 fprintf(stderr
, " %02x", (*buff
)[i
]);
1317 fputc('\n', stderr
);
1320 writt
= xiowrite(outpipe
, *buff
, bytes
);
1322 /* EAGAIN when nonblocking but a mandatory lock is on file.
1323 the problem with EAGAIN is that the read cannot be repeated,
1324 so we need to buffer the data and try to write it later
1325 again. not yet implemented, sorry. */
1327 if (errno
== EPIPE
) {
1328 return 0; /* can no longer write; handle like EOF */
1333 Info3("transferred "F_Zu
" bytes from %d to %d",
1334 writt
, XIO_GETRDFD(inpipe
), XIO_GETWRFD(outpipe
));
1344 /* converts the newline characters (or character sequences) from the one
1345 specified in lineterm1 to that of lineterm2. Possible values are
1346 LINETERM_CR, LINETERM_CRNL, LINETERM_RAW.
1347 buff points to the malloc()'ed data, input and output. It may be subject to
1348 realloc(). bytes specifies the number of bytes input and output */
1349 int cv_newline(unsigned char **buff
, ssize_t
*bufsiz
,
1350 int lineterm1
, int lineterm2
) {
1351 ssize_t
*bytes
= bufsiz
;
1352 /* must perform newline changes */
1353 if (lineterm1
<= LINETERM_CR
&& lineterm2
<= LINETERM_CR
) {
1354 /* no change in data length */
1355 unsigned char from
, to
, *p
, *z
;
1356 if (lineterm1
== LINETERM_RAW
) {
1357 from
= '\n'; to
= '\r';
1359 from
= '\r'; to
= '\n';
1364 if (*p
== from
) *p
= to
;
1368 } else if (lineterm1
== LINETERM_CRNL
) {
1369 /* buffer becomes shorter */
1370 unsigned char to
, *s
, *t
, *z
;
1371 if (lineterm2
== LINETERM_RAW
) {
1389 *bufsiz
= t
- *buff
;
1391 /* buffer becomes longer, must alloc another space */
1392 unsigned char *buf2
;
1393 unsigned char from
; unsigned char *s
, *t
, *z
;
1394 if (lineterm1
== LINETERM_RAW
) {
1399 if ((buf2
= Malloc(2*socat_opts
.bufsiz
/*sic!*/+1)) == NULL
) {
1402 s
= *buff
; t
= buf2
; z
= *buff
+ *bytes
;
1405 *t
++ = '\r'; *t
++ = '\n';
1414 *bufsiz
= t
- buf2
;;
1419 void socat_signal(int signum
) {
1422 diag_in_handler
= 1;
1423 Notice1("socat_signal(): handling signal %d", signum
);
1432 diag_set_int('x', 128+signum
); /* in case Error exits for us */
1433 Error1("exiting on signal %d", signum
);
1434 diag_set_int('x', 0); /* in case Error did not exit */
1437 Warn1("exiting on signal %d", signum
); break;
1440 Notice1("exiting on signal %d", signum
); break;
1443 Notice1("socat_signal(): finishing signal %d", signum
);
1444 diag_exit(128+signum
); /*!!! internal cleanup + _exit() */
1445 diag_in_handler
= 0;
1449 /* this is the callback when the child of an address died */
1450 static int socat_sigchild(struct single
*file
) {
1451 if (file
->ignoreeof
&& !closing
) {
1454 file
->eof
= MAX(file
->eof
, 1);
1460 static int socat_lock(void) {
1464 if ((lockrc
= xiolock(&socat_opts
.lock
)) < 0) {
1472 if (socat_opts
.lock
.lockfile
) {
1473 if ((lockrc
= xiolock(socat_opts
.lock
.lockfile
)) < 0) {
1474 /*Error1("error with lockfile \"%s\"", socat_opts.lock.lockfile);*/
1481 /*0 Info1("obtained lock \"%s\"", socat_opts.lock.lockfile);*/
1484 if (socat_opts
.lock
.waitlock
) {
1485 if (xiowaitlock(socat_opts
.lock
.waitlock
, socat_opts
.lock
.intervall
)) {
1486 /*Error1("error with lockfile \"%s\"", socat_opts.lock.lockfile);*/
1490 /*0 Info1("obtained lock \"%s\"", socat_opts.lock.waitlock);*/
1497 static void socat_unlock(void) {
1498 if (!havelock
) return;
1499 if (socat_opts
.lock
.lockfile
) {
1500 if (Unlink(socat_opts
.lock
.lockfile
) < 0) {
1501 if (!diag_in_handler
) {
1502 Warn2("unlink(\"%s\"): %s",
1503 socat_opts
.lock
.lockfile
, strerror(errno
));
1505 Warn1("unlink(\"%s\"): "F_strerror
,
1506 socat_opts
.lock
.lockfile
);
1509 Info1("released lock \"%s\"", socat_opts
.lock
.lockfile
);
1514 /* this is a callback function that may be called by the newchild hook of xio
1516 static int socat_newchild(void) {