The TCPConnect and TCPListen written in Python had issues that were hard
to debug. The main problem was with the test hangs during tests. Another
issue is that these tests are using threads. Since the mixing of fork and
threading is strongly discouraged in Python I rewrote these tests as test
tools tcp_connect and tcp_listen under tcp_conn directory in C language
using direct system calls for networking routines and using processes for
handling connections.
Example usage:
<command_sequence>
<define>
<alias name="my_range" value="10000-10050" />
</define>
<command machine_id="testmachine2" type="exec" from="tcp_conn"
value="./tcp_listen -p {$my_range} -a {ip(testmachine2,testifc1)} -c"
bg_id="1"/>
<command type="ctl_wait" value="3" />
<command machine_id="testmachine1" type="exec" from="tcp_conn"
value="./tcp_connect -p {$my_range} -a {ip(testmachine2,testifc1)} -c"
bg_id="2"/>
<command machine_id="testmachine1" type="exec" from="tcp_conn"
value="./tcp_listen -p {$my_range} -a {ip(testmachine1,testifc1)} -c"
bg_id="3"/>
<command type="ctl_wait" value="3" />
<command machine_id="testmachine2" type="exec" from="tcp_conn"
value="./tcp_connect -p {$my_range} -a {ip(testmachine1,testifc1)} -c"
bg_id="4"/>
<command type="ctl_wait" value="100" />
<command machine_id="testmachine1" type="intr" value="2" />
<command machine_id="testmachine2" type="intr" value="1" />
<command machine_id="testmachine2" type="intr" value="4" />
<command machine_id="testmachine1" type="intr" value="3" />
</command_sequence>
Options are common both for server and client part:
-p port_range ... specifies the range of ports to use under test
-c ... use continuous mode otherwise just do a single
connection and end
-d ... turn on debug output, use with caution since this
produces a lot of data
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
---
test_modules/TestTCPConnect.py | 205 ----------------------------------
test_modules/TestTCPListen.py | 198 --------------------------------
test_tools/tcp_conn/Makefile | 12 ++
test_tools/tcp_conn/lnst-setup.sh | 3 +
test_tools/tcp_conn/tcp_connect.c | 202 +++++++++++++++++++++++++++++++++
test_tools/tcp_conn/tcp_listen.c | 229 ++++++++++++++++++++++++++++++++++++++
6 files changed, 446 insertions(+), 403 deletions(-)
delete mode 100644 test_modules/TestTCPConnect.py
delete mode 100644 test_modules/TestTCPListen.py
create mode 100644 test_tools/tcp_conn/Makefile
create mode 100755 test_tools/tcp_conn/lnst-setup.sh
create mode 100644 test_tools/tcp_conn/tcp_connect.c
create mode 100644 test_tools/tcp_conn/tcp_listen.c
diff --git a/test_modules/TestTCPConnect.py b/test_modules/TestTCPConnect.py
deleted file mode 100644
index 4da2600..0000000
--- a/test_modules/TestTCPConnect.py
+++ /dev/null
@@ -1,205 +0,0 @@
-"""
-This module defines TCPConnect module
-"""
-
-__author__ = """
-jtluka(a)redhat.com (Jan Tluka)
-"""
-
-import sys
-import socket
-import errno
-from multiprocessing import Process, Value
-from signal import signal, SIGINT
-from time import sleep
-from random import randrange, sample
-import logging
-import re
-import ctypes
-from lnst.Common.TestsCommon import TestGeneric
-
-"""
-Test description:
- Test spawns client(s) connecting to TCP port(s) defined by port or
- port_range option. When connected, the client sends random bursts of
- random data to server. If cont option is set the connections are initiated
- again and data is sent to server until interrupted by the controller.
-
-Parameters:
- addr ... mandatory, address to connect to
- port ... mandatory, port to send data
- sleep ... optional, sleep time between bursts, if undefined, the bursts
- are immediate
- cont ... optional, sets continuous mode of connecting, if set connections
- are infinitely re-spawned when closed
-"""
-
-class ConnectionWorker():
- def __init__(self, host, port, sleep_time = None, continuous = None):
- self._host = host
- self._port = port
- self._sleep_time = sleep_time
- self._cont = continuous
- self._ascii = [chr(i) for i in range(0,255)]
-
- def run(self, closecon):
- loop = True
-
- while loop and not closecon.value:
- loop = (self._cont is not None)
- logging.debug("Starting connection to (%s) port %s " % (self._host,
- self._port))
-
- try:
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- except socket.error as msg:
- logging.debug(msg)
- return
-
- s.settimeout(5)
-
- try:
- s.connect((self._host, self._port))
- except socket.timeout as msg:
- logging.debug("Could not connect to %s port %s" %
- (self._host, self._port))
- s.close()
- continue
- except socket.error as msg:
- logging.debug(msg)
- s.close()
- break
-
- s.settimeout(1)
-
- for txs in range(10, randrange(20,100)):
- if closecon.value:
- logging.debug("Terminating connection on port %s" %
- self._port)
- loop = False
- s.settimeout(0)
- try:
- s.shutdown(socket.SHUT_WR)
- except:
- pass
- s.close()
- return
-
- rnd_str = "".join(sample(self._ascii, len(self._ascii)))
- while True and not closecon.value:
- try:
- data = s.sendall(rnd_str)
- except socket.timeout:
- continue
- except socket.error as msg:
- logging.debug(msg)
- s.settimeout(0)
- try:
- s.shutdown(socket.SHUT_WR)
- except:
- pass
- s.close()
- return
-
- break
-
- if (self._sleep_time):
- sleep(self._sleep_time)
-
- s.settimeout(0)
- try:
- s.shutdown(socket.SHUT_WR)
- except:
- pass
- s.close()
-
-
-class TestTCPConnect(TestGeneric):
- def _parse_options(self):
- addr = self.get_mopt("addr")
- if addr:
- self._host = addr
-
- # either port or port_range should be set
- port = self.get_opt("port")
- if port:
- self._port = int(port)
- else:
- port_range = self.get_opt("port_range")
- if port_range:
- self._port_range = port_range
- else:
- e = TestOptionMissing()
- raise e
-
- sleep_time = self.get_opt("sleep")
- if sleep_time:
- self._sleep_time = float(sleep_time)
- else:
- self._sleep_time = 0
-
- cont = self.get_opt("cont")
- if cont:
- self._cont = cont
-
- def parse_port_range(self):
- if self._port_range == None:
- return []
-
- for c in [',','-']:
- s = self._port_range.split(c)
- if len(s) == 2:
- break
-
- if len(s) != 2:
- logging.error("Port range malformed! ", self._port_range)
-
- low = int(s[0])
- high = int(s[1]) + 1
-
- return range(low, high)
-
- def _close_connections(self, signum, frame):
- logging.debug("Termination signal delivered ...")
- self._closecon.value = True
-
- def _set_interrupt_handler(self):
- signal(SIGINT, self._close_connections)
-
- def run(self):
- self._host = None
- self._port = None
- self._cont = None
- self._cw_instances = []
- self._closecon = Value(ctypes.c_bool, False, lock=True)
-
- self._set_interrupt_handler()
-
- self._parse_options()
-
- ports = []
- if self._port:
- ports.append(self._port)
- else:
- r = self.parse_port_range()
- ports.extend(r)
-
- workers = []
- for p in ports:
- cw = ConnectionWorker(self._host, p, self._sleep_time, self._cont)
- self._cw_instances.append(cw)
-
- w = Process(target=cw.run, args=(self._closecon,))
- w.start()
- workers.append(w)
-
- logging.debug("Waiting for workers ...")
- while len(workers) > 0:
- for w in workers:
- try:
- w.join()
- except OSError as e:
- continue
- workers.remove(w)
-
- logging.info("Connect: Finished ...")
diff --git a/test_modules/TestTCPListen.py b/test_modules/TestTCPListen.py
deleted file mode 100644
index eb1697b..0000000
--- a/test_modules/TestTCPListen.py
+++ /dev/null
@@ -1,198 +0,0 @@
-"""
-This module defines TCPListen module
-"""
-
-__author__ = """
-jtluka(a)redhat.com (Jan Tluka)
-"""
-
-import sys
-import socket
-import errno
-import logging
-import re
-from multiprocessing import Process, Value
-from signal import signal, SIGINT
-import ctypes
-from lnst.Common.TestsCommon import TestGeneric
-
-"""
-Test description:
- Test spawns server(s) listening for TCP connection(s) on port(s) defined by
- port or port_range options. When client connects to the port, server reads
- the data sent and close the connection when no more data is available.
- If cont option is set the connection is reopened and server reads data
- again.
-
-Parameters:
- addr ... optional, address to bind to, if undefined listen on all ifaces
- port ... mandatory, port to listen on
- cont ... optional, if set the listening port is reopened when the
- connection is closed
-"""
-
-class TestTCPListen(TestGeneric):
- def __init__(self, command):
- self._addr = None
- self._port = None
- self._cont = None
- self._closecon = Value(ctypes.c_bool, False, lock=True)
-
- TestGeneric.__init__(self, command)
-
- def _parse_options(self):
- addr = self.get_opt("addr")
- if addr:
- self._addr = addr
-
- # either port or port_range should be set
- port = self.get_opt("port")
- if port:
- self._port = int(port)
- else:
- port_range = self.get_opt("port_range")
- if port_range:
- self._port_range = port_range
- else:
- e = TestOptionMissing()
- raise e
-
- cont = self.get_opt("cont")
- if cont:
- self._cont = cont
-
- def _worker(self, host, port, connections, closecon):
- logging.debug("Starting listener (%s) on port %s " % (host, port))
-
- try:
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.settimeout(1)
- except socket.error as msg:
- logging.debug(msg)
- return
-
- try:
- s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- s.bind((host, port))
- s.listen(1)
- except socket.error as msg:
- logging.debug(msg)
- s.close()
- return
-
- loop = 1
-
- while (loop or self._cont) and not closecon.value:
- conn = None
- while conn == None and not closecon.value:
- try:
- conn, addr = s.accept()
- except socket.timeout as e:
- continue
- except socket.error as e:
- logging.debug(e)
- s.close()
- return
- except:
- logging.warning("Unknown exception.")
- s.close()
- return
-
- if conn:
- connections.value += 1
- conn.settimeout(1)
- else:
- continue
-
- logging.debug('Connected from ' + addr[0] + ' port:' +
- str(addr[1]))
-
- while not closecon.value:
- try:
- data = conn.recv(1024)
- except socket.timeout as e:
- continue
- except socket.error as e:
- logging.debug(e)
- if conn:
- conn.shutdown(socket.SHUT_RDWR)
- conn.close()
- s.close()
- return
-
- if not data:
- logging.debug('Client disconnected: ' + addr[0] +
- ' port:' + str(addr[1]))
- break
-
- if conn:
- try:
- conn.shutdown(socket.SHUT_RDWR)
- conn.close()
- except socket.error as msg:
- logging.debug(msg)
- s.close()
- return
-
- loop = 0
-
- s.close()
-
- def _parse_port_range(self):
- if self._port_range == None:
- return []
-
- for c in [',','-']:
- s = self._port_range.split(c)
- if len(s) == 2:
- break
-
- if len(s) != 2:
- logging.error("Port range malformed! ", self._port_range)
-
- low = int(s[0])
- high = int(s[1]) + 1
-
- return range(low, high)
-
- def _terminate(self, signum, frame):
- self._closecon.value = True
- logging.debug("Listen: terminate called")
-
- def _set_interrupt_handler(self):
- signal(SIGINT, self._terminate)
-
- def run(self):
- self._set_interrupt_handler()
-
- self._parse_options()
-
- ports = []
- if self._port:
- ports.append(self._port)
- else:
- r = self._parse_port_range()
- ports.extend(r)
-
- self.workers = []
-
- connections = Value('L', 0, lock=True)
-
- for p in ports:
- w = Process(target=self._worker, args=(self._addr, p, connections, self._closecon))
- w.start()
- self.workers.append(w)
-
-
- logging.debug("Waiting for workers ...")
- while len(self.workers) > 0:
- for w in self.workers:
- try:
- w.join()
- except:
- continue
- self.workers.remove(w)
-
- logging.info("Handled %s TCP connections." % connections.value)
-
- return self.set_pass("Handled %s TCP connections." % connections.value)
diff --git a/test_tools/tcp_conn/Makefile b/test_tools/tcp_conn/Makefile
new file mode 100644
index 0000000..2e8f5e6
--- /dev/null
+++ b/test_tools/tcp_conn/Makefile
@@ -0,0 +1,12 @@
+CC=gcc
+
+all: tcp_listen tcp_connect
+
+tcp_listen: tcp_listen.c
+ $(CC) -o tcp_listen tcp_listen.c
+
+tcp_connect: tcp_connect.c
+ $(CC) -o tcp_connect tcp_connect.c
+
+clean:
+ rm -f tcp_connect tcp_listen
diff --git a/test_tools/tcp_conn/lnst-setup.sh b/test_tools/tcp_conn/lnst-setup.sh
new file mode 100755
index 0000000..2aad486
--- /dev/null
+++ b/test_tools/tcp_conn/lnst-setup.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+make
diff --git a/test_tools/tcp_conn/tcp_connect.c b/test_tools/tcp_conn/tcp_connect.c
new file mode 100644
index 0000000..598ac80
--- /dev/null
+++ b/test_tools/tcp_conn/tcp_connect.c
@@ -0,0 +1,202 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#include <errno.h>
+#include <signal.h>
+
+
+int connection_handlers[1024];
+int handlers_count;
+
+int debug_on = 0;
+int cont = 0;
+
+int usage()
+{
+ printf("./tcp_connect -p [port_range] -a [ipaddr]\n");
+ return 0;
+}
+
+#define MSG_MAX 256
+char msg[MSG_MAX];
+
+int debug(char* msg)
+{
+ if (debug_on)
+ {
+ printf("Debug: %s\n", msg);
+ }
+}
+
+void terminate_connections(int p)
+{
+ int cc;
+
+ debug("signalled");
+ for (cc=0; cc < handlers_count; cc++)
+ {
+ snprintf(msg, MSG_MAX, "killing process %i", connection_handlers[cc]);
+ debug(msg);
+ kill(connection_handlers[cc], SIGKILL);
+ }
+}
+
+int handle_connections(char* host, int port)
+{
+ int conn_sock;
+ int remote_sock;
+ struct sockaddr_in my_addr;
+ struct sockaddr remote;
+ int end_loop = 0;
+ char data[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+ char buf[21*10*strlen(data)+1];
+
+ snprintf(msg, MSG_MAX, "Starting connection on %s port %i", host, port);
+ debug(msg);
+
+ my_addr.sin_family = AF_INET;
+ my_addr.sin_port = htons(port);
+ if (inet_aton(host, &(my_addr.sin_addr)) == 0)
+ {
+ printf("failed on inet_aton()\n");
+ return 1;
+ }
+
+ do
+ {
+ conn_sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (conn_sock == -1)
+ {
+ perror("fail on socket()");
+ return 1;
+ }
+
+ if (connect(conn_sock, (struct sockaddr*) &my_addr, sizeof(struct sockaddr_in)) == -1)
+ {
+ perror("fail on connect");
+ return 1;
+ }
+
+ int bursts = 5*(random() % 10) + 1;
+ int b;
+ int sum = 0;
+
+ for (b=0; b < bursts; b++)
+ {
+ int parts = 20*(random() % 10) + 1;
+ int j;
+
+ sum += parts*strlen(data);
+ for (j = 0; j < parts; j++)
+ {
+ strncpy(buf + (j*strlen(data)), data, strlen(data));
+ }
+
+ int wr_rc = write(conn_sock, buf, parts * strlen(data));
+ usleep(100*(random()%100));
+ }
+
+ snprintf(msg, MSG_MAX, "sent %i bytes (bursts: %i)", sum, bursts);
+ debug(msg);
+ snprintf(msg, MSG_MAX, "closing connection on port %i", port);
+ debug(msg);
+
+ close(conn_sock);
+ } while (cont);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int rc;
+ int p;
+ char port_str[256];
+ char str_port_start[128];
+ char str_port_end[128];
+ char host_str[16] = "\0";
+ int start_port;
+ int end_port;
+ int opt;
+ char *delimiter;
+ struct sigaction sa;
+
+ sa.sa_handler = &terminate_connections;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ handlers_count = 0;
+
+ /* collect program args */
+ while ((opt = getopt(argc, argv, "p:a:dc")) != -1) {
+ switch (opt) {
+ case 'p':
+ strncpy(port_str, optarg, 256);
+ port_str[256-1]='\0';
+ delimiter = strchr(port_str, '-');
+ if (delimiter == NULL)
+ {
+ usage();
+ }
+ strncpy(str_port_start, port_str, delimiter - port_str);
+ str_port_start[delimiter - port_str] = '\0';
+ strncpy(str_port_end, delimiter+1, strlen(port_str) - (delimiter+1-port_str));
+ start_port = atoi(str_port_start);
+ end_port = atoi(str_port_end);
+ break;
+ case 'a':
+ strncpy(host_str, optarg, 64);
+ host_str[16-1] = '\0';
+ break;
+ case 'd':
+ debug_on = 1;
+ break;
+ case 'c':
+ cont = 1;
+ break;
+ }
+ }
+
+ if (strlen(host_str) == 0)
+ {
+ usage();
+ return 1;
+ }
+
+ /* spawn process to handle every port specified */
+ for (p = start_port; p < end_port; p++){
+ if ((rc = fork()) > 0)
+ {
+ /* parent, add pid to the list */
+ connection_handlers[handlers_count++] = rc;
+ }
+ else if (rc == 0)
+ {
+ /* child */
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ handle_connections(host_str, p);
+ return 0;
+ }
+ }
+
+ /* gather children */
+ int child_status;
+ int i;
+ for (i=0; i < handlers_count; i++)
+ {
+ wait(&child_status);
+ debug("worker finished");
+ }
+
+ debug("tcp_connect finished");
+
+ return 0;
+}
diff --git a/test_tools/tcp_conn/tcp_listen.c b/test_tools/tcp_conn/tcp_listen.c
new file mode 100644
index 0000000..df07f69
--- /dev/null
+++ b/test_tools/tcp_conn/tcp_listen.c
@@ -0,0 +1,229 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#include <errno.h>
+#include <signal.h>
+
+int listener_count;
+int listeners[1024];
+
+int connection_count;
+int debug_on = 0;
+int cont = 0;
+
+int usage()
+{
+ printf("./tcp_listen -p [port_range] -a [ipaddr]\n");
+ return 0;
+}
+
+int debug(char* msg)
+{
+ if (debug_on)
+ {
+ printf("Debug: %s\n", msg);
+ }
+}
+
+#define MSG_MAX 256
+char msg[MSG_MAX];
+
+void terminate_connections(int p)
+{
+ int l;
+
+ debug("signal received, killing servers");
+ for (l=0; l < listener_count; l++)
+ {
+ snprintf(msg, MSG_MAX, "killing process %i", listeners[l]);
+ debug(msg);
+ kill(listeners[l], SIGKILL);
+ }
+}
+
+void connection_counter(int p)
+{
+ debug("increase counter");
+ connection_count += 1;
+}
+
+int handle_connections(char* host, int port)
+{
+ int listen_sock;
+ struct sockaddr_in my_addr;
+ socklen_t my_addr_size = sizeof(my_addr);
+ int remote_sock;
+ struct sockaddr_in remote;
+ socklen_t remote_size = sizeof(remote);
+ char data[256];
+
+ snprintf(msg, MSG_MAX, "Starting listener on %s port %i", host, port);
+ debug(msg);
+
+ bzero(&my_addr, my_addr_size);
+ my_addr.sin_family = AF_INET;
+ my_addr.sin_port = htons(port);
+ if (inet_aton(host, &(my_addr.sin_addr)) == 0)
+ {
+ printf("failed on inet_aton()\n");
+ return 1;
+ }
+
+ if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+ {
+ perror("fail on socket creation");
+ return 1;
+ }
+
+ if (bind(listen_sock, (struct sockaddr*) &my_addr, sizeof(struct sockaddr_in)))
+ {
+ perror("fail on bind");
+ return 1;
+ }
+
+ if (listen(listen_sock, 0))
+ {
+ perror("fail on listen");
+ return 1;
+ }
+
+ do
+ {
+ struct in_addr ra = remote.sin_addr;
+ char host_address_str[256];
+ ssize_t read_rc;
+
+ /* handle single connection */
+ bzero(&remote, remote_size);
+ remote_sock = accept(listen_sock, (struct sockaddr*) &remote, &remote_size);
+ if (remote_sock == -1)
+ {
+ perror("failure on accept");
+ close(listen_sock);
+ return 1;
+ }
+
+ inet_ntop(AF_INET, &ra, host_address_str, 255);
+ snprintf(msg, MSG_MAX, "accepted connection from host %s port %i", host_address_str, port);
+ debug(msg);
+
+ int sum = 0;
+ while (read_rc = read(remote_sock, &data, 256))
+ {
+ sum += read_rc;
+ }
+
+ snprintf(msg, MSG_MAX, "connection closed, read %d bytes (%i)", sum, port);
+ debug(msg);
+ close(remote_sock);
+ kill(getppid(), SIGUSR1);
+ } while (cont);
+
+ close(listen_sock);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int rc;
+ int p;
+ char port_str[256];
+ char str_port_start[128];
+ char str_port_end[128];
+ char host_str[16] = "\0";
+ int start_port;
+ int end_port;
+ int opt;
+ char *delimiter;
+ struct sigaction sa;
+ struct sigaction sa2;
+
+ sa.sa_handler = &terminate_connections;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ sa2.sa_handler = &connection_counter;
+ sigaction(SIGUSR1, &sa2, NULL);
+
+ /* collect program args */
+ while ((opt = getopt(argc, argv, "p:a:dc")) != -1) {
+ switch (opt) {
+ case 'p':
+ strncpy(port_str, optarg, 256);
+ port_str[256-1]='\0';
+ delimiter = strchr(port_str, '-');
+ if (delimiter == NULL)
+ {
+ usage();
+ }
+ strncpy(str_port_start, port_str, delimiter - port_str);
+ str_port_start[delimiter - port_str] = '\0';
+ strncpy(str_port_end, delimiter+1, strlen(port_str) - (delimiter+1-port_str));
+ start_port = atoi(str_port_start);
+ end_port = atoi(str_port_end);
+ break;
+ case 'a':
+ strncpy(host_str, optarg, 64);
+ host_str[16-1] = '\0';
+ break;
+ case 'd':
+ debug_on = 1;
+ break;
+ case 'c':
+ cont = 1;
+ }
+ }
+
+ if (strlen(host_str) == 0)
+ {
+ usage();
+ return 1;
+ }
+
+ listener_count = 0;
+ /* spawn process to handle every port specified */
+ for (p = start_port; p < end_port; p++){
+ if ((rc = fork()) > 0)
+ {
+ /* parent, add pid to the list */
+ listeners[listener_count++] = rc;
+ }
+ else if (rc == 0)
+ {
+ /* child */
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ /* run the main processing loop */
+ handle_connections(host_str, p);
+ return 0;
+ }
+ }
+
+ /* gather children */
+ int child_status;
+ int i;
+ for (i=0; i < listener_count; i++)
+ {
+ int rc = -1;
+ while (wait(&child_status) == -1 && errno == EINTR)
+ {
+ ;
+ }
+ debug("worker finished");
+ }
+
+ debug("tcp_listener finished");
+
+ printf("handled %i connections\n", connection_count);
+
+ return 0;
+}
--
1.7.11.7