1 /*******************************************************************************
2 
3     Test-suite for UnixSockets.
4 
5     The tests involve unix sockets and forking processes.
6 
7     FLAKY: the unittests in this module are very flaky, as they rely on making
8     various system calls (fork(), waitpid(), epoll_wait(), epoll_ctl(), etc)
9     which could, under certain environmental conditions, fail.
10 
11     Copyright:
12         Copyright (c) 2016-2017 dunnhumby Germany GmbH.
13         All rights reserved.
14 
15     License:
16         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
17         Alternatively, this file may be distributed under the terms of the Tango
18         3-Clause BSD License (see LICENSE_BSD.txt for details).
19 
20 *******************************************************************************/
21 
22 module integrationtest.selectlistener.main;
23 
24 import ocean.transition;
25 
26 import ocean.core.Enforce: enforce;
27 import Ocean = ocean.core.Test;
28 import ocean.core.Time: seconds;
29 import ocean.io.select.EpollSelectDispatcher;
30 import core.stdc.errno: ECONNREFUSED;
31 import ocean.stdc.posix.sys.types : pid_t;
32 import ocean.stdc.posix.sys.wait: waitpid;
33 import core.sys.posix.unistd: fork, unlink;
34 import ocean.time.timeout.TimeoutManager;
35 import ocean.sys.socket.UnixSocket;
36 import ocean.stdc.posix.sys.un;
37 import core.sys.posix.sys.socket;
38 import core.stdc.stdlib;
39 import core.thread;
40 
41 import integrationtest.selectlistener.UnixServer;
42 
43 
44 /*******************************************************************************
45 
46     Opens a client connection and issues a request. The reply must be exactly
47     what was sent.
48 
49     Params:
50         socket_path = the unix socket path.
51 
52     Returns:
53         0 on success
54 
55     Throws:
56         an exception if something goes wrong (could not connect, wrong reply, etc)
57 
58 *******************************************************************************/
59 
60 int run_client( istring socket_path)
61 {
62     auto local_address = sockaddr_un.create(socket_path);
63     auto client = new UnixSocket();
64 
65     scope (exit) client.close();
66 
67     auto socket_fd = client.socket();
68     enforce(socket_fd >= 0, "socket() call failed!");
69 
70     auto connect_result = client.connect(&local_address);
71 
72     istring str1 = "HELLO, ... !\n";
73 
74     client.write(str1);
75 
76     auto read_buffer = new char[str1.length + 1];
77     read_buffer[] = 0;
78     auto buff = cast(void[])read_buffer;
79 
80     auto read_bytes = client.recv(buff, 0);
81     enforce(read_bytes > 0);
82 
83     read_buffer.length = read_bytes;
84 
85     Ocean.test(read_buffer == str1);
86 
87     return 0;
88 }
89 
90 /*******************************************************************************
91 
92     Makes a test. Starts the client in its own process, then starts the server
93     in the current process and waits for them to finish.
94     The client sends a message, gets an answer, compares the two strings and
95     exits.
96     The server opens the socket, accepts one request, reads the message,
97     replies with the same message and exits (no more requests are handled).
98 
99     Params:
100         socket_path = the unix socket path.
101 
102     Throws:
103         an exception if something goes wrong
104 
105 *******************************************************************************/
106 
107 void run_test ( istring socket_path )
108 {
109     auto timeout_mgr = new TimeoutManager;
110     auto epoll = new EpollSelectDispatcher(timeout_mgr);
111 
112     unlink(socket_path.ptr);
113     auto local_address = sockaddr_un.create(socket_path);
114     auto unix_socket   = new UnixSocket;
115     auto unix_server   = new UnixServer(cast(sockaddr*)&local_address,
116             unix_socket, epoll);
117     epoll.register(unix_server);
118 
119     // UnixServer should already bind and listen at this point,
120     // so it is safe to connect from the client
121     pid_t pid = fork();
122 
123     enforce(pid != -1);
124 
125     if (pid == 0)  // child
126     {
127         run_client(socket_path);
128         _Exit(0);
129     }
130 
131     epoll.eventLoop();
132 
133     enforce((socket_path[0] == '\0') || (unlink(socket_path.ptr) == 0),
134         "Can't remove socket file.");
135 
136     int status;
137     waitpid(pid, &status, 0);
138     enforce(status == 0, "Child exit status should be 0");
139 }
140 
141 /*******************************************************************************
142 
143     Makes two tests, one with standard UNIX socket and one with (LINUX) abstract
144     namespace sockets.
145 
146 *******************************************************************************/
147 
148 version(UnitTest) {} else
149 int main ( )
150 {
151     run_test("/tmp/ocean_socket_test");
152     run_test("\0ocean_socket_test");
153 
154     return 0;
155 }