git alias
[hband-tools.git] / user-tools / sockio
blobb505e52c0fffbbba0ddff947635c87b6537853d9
1 #!/usr/bin/env perl
3 # Takes a command and arguments as arguments.
4 # Connects the command stdio to a tcp/udp/unix socket.
6 use Socket qw/AF_UNIX AF_INET SOCK_STREAM pack_sockaddr_in inet_aton sockaddr_un/;
7 use POSIX ':sys_wait_h';
8 use Data::Dumper;
9 use Fcntl qw/F_GETFL F_SETFL O_NONBLOCK O_RDONLY O_WRONLY/;
10 no if ($] >= 5.018), 'warnings' => 'experimental::smartmatch';
13 sub blockio
15 flag($_[0], ~O_NONBLOCK);
17 sub nonblockio
19 flag($_[0], O_NONBLOCK);
21 sub flag
23 fcntl($_[0], F_SETFL, fcntl($_[0], F_GETFL, 0) | $_[1]);
25 sub sigchld
27 # This handler makes select(2) interrupted, and child process reaped,
28 # and stdin excluded from watched fds.
33 if(defined $ENV{'SOCKIO_AF'})
35 $af = $ENV{'SOCKIO_AF'};
37 else
39 print STDERR "SOCKIO_AF not given, fallback to 'tcp'\n";
40 $af = 'tcp';
42 unless($af ~~ [qw/file abstract tcp/])
44 die "invalid SOCKIO_AF given, choose one of file, abstract, tcp\n";
47 if($af ~~ [qw/file abstract/])
49 socket $srv, AF_UNIX, SOCK_STREAM, 0 or die $!;
50 setsockopt($srv, SOL_SOCKET, SO_PASSCRED, 1);
51 if($af eq 'abstract')
53 $addr = sockaddr_un "\x00sockio-pid-$$";
55 else
57 $addr = sockaddr_un "/tmp/sockio-pid-$$";
59 bind $srv, $addr or die $!;
61 elsif($af eq 'tcp')
63 socket $srv, AF_INET, SOCK_STREAM, 0 or die $!;
64 BIND_TCP:
65 $port = int(rand() * (65_535 - 10_000)) + 10_000;
66 $addr = pack_sockaddr_in($port, inet_aton('localhost'));
67 print STDERR "bind to tcp/$port ... ";
68 if(bind $srv, $addr)
70 print STDERR "ok\n";
72 else
74 print STDERR "$!\n";
75 if($!{'EADDRINUSE'}) { goto BIND_TCP; }
76 else { exit $!; }
80 listen $srv, 1 or die $!;
83 $pid = fork;
84 if($pid == 0)
86 if($af ~~ [qw/file abstract/])
88 socket $cln, AF_UNIX, SOCK_STREAM, 0 or die $!;
90 elsif($af == 'tcp')
92 socket $cln, AF_INET, SOCK_STREAM, 0 or die $!;
94 CONNECT:
95 if(not connect $cln, $addr)
97 if($!{'ECONNREFUSED'}) { sleep 0.1; goto CONNECT; }
98 else { die $!; }
100 open STDIN, '<&=', fileno $cln or die $!;
101 open STDOUT, '>&=', fileno $cln or die $!;
102 flag(STDIN, O_RDONLY);
103 flag(STDOUT, O_WRONLY);
104 exec {$ARGV[0]} @ARGV;
105 exit 127;
109 accept $clnt, $srv;
110 select $clnt;
111 $|++;
112 select STDOUT;
113 $|++;
115 @watch_in = (\*STDIN, $clnt);
116 undef $exitcode;
117 nonblockio(STDIN);
118 nonblockio($clnt);
119 $SIG{'CHLD'} = \&sigchld;
123 while(1)
125 if(scalar @watch_in == 0)
127 last;
130 $fds_in = '';
131 for my $fd (@watch_in)
133 vec($fds_in, fileno($fd), 1) = 1;
136 select($fds_in, undef, undef, undef);
138 if(vec($fds_in, fileno(STDIN), 1) == 1)
140 READ_STDIN:
141 $stdin_bytes = sysread STDIN, $buf, 4096;
142 if($stdin_bytes > 0)
144 print {$clnt} $buf;
145 goto READ_STDIN;
148 if($stdin_bytes eq 0)
150 shutdown $clnt, 1;
151 close STDIN;
152 @watch_in = grep {$_ ne \*STDIN} @watch_in;
155 if(vec($fds_in, fileno($clnt), 1) == 1)
157 READ_CLNT:
158 $clnt_bytes = sysread $clnt, $buf, 4096;
159 if($clnt_bytes > 0)
161 print {STDOUT} $buf;
162 goto READ_CLNT;
165 if($clnt_bytes eq 0)
167 shutdown $clnt, 0;
168 close STDOUT;
169 @watch_in = grep {$_ ne $clnt} @watch_in;
173 if(not defined $exitcode and waitpid $pid, WNOHANG)
175 $exitcode = $? >> 8;
177 shutdown $clnt, 1;
178 close STDIN;
179 @watch_in = grep {$_ ne \*STDIN} @watch_in;
184 if(not defined $exitcode)
186 waitpid $pid, 0;
187 $exitcode = $? >> 8;
189 exit $exitcode;