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.meta.types.Qualifiers;
25 
26 import ocean.core.Enforce: enforce;
27 import Ocean = ocean.core.Test;
28 import ocean.io.select.EpollSelectDispatcher;
29 import ocean.time.timeout.TimeoutManager;
30 import ocean.stdc.posix.sys.un;
31 import ocean.sys.socket.UnixSocket;
32 
33 import core.stdc.errno: ECONNREFUSED;
34 import core.stdc.stdlib;
35 import core.sys.posix.unistd: fork, unlink;
36 import core.sys.posix.sys.socket;
37 import core.sys.posix.sys.types : pid_t;
38 import core.sys.posix.sys.wait: waitpid;
39 import core.thread;
40 import core.time;
41 
42 import integrationtest.selectlistener.UnixServer;
43 
44 
45 /*******************************************************************************
46 
47     Opens a client connection and issues a request. The reply must be exactly
48     what was sent.
49 
50     Params:
51         socket_path = the unix socket path.
52 
53     Returns:
54         0 on success
55 
56     Throws:
57         an exception if something goes wrong (could not connect, wrong reply, etc)
58 
59 *******************************************************************************/
60 
61 int run_client( istring socket_path)
62 {
63     auto local_address = sockaddr_un.create(socket_path);
64     auto client = new UnixSocket();
65 
66     scope (exit) client.close();
67 
68     auto socket_fd = client.socket();
69     enforce(socket_fd >= 0, "socket() call failed!");
70 
71     auto connect_result = client.connect(&local_address);
72 
73     istring str1 = "HELLO, ... !\n";
74 
75     client.write(str1);
76 
77     auto read_buffer = new char[str1.length + 1];
78     read_buffer[] = 0;
79     auto buff = cast(void[])read_buffer;
80 
81     auto read_bytes = client.recv(buff, 0);
82     enforce(read_bytes > 0);
83 
84     read_buffer.length = read_bytes;
85 
86     Ocean.test(read_buffer == str1);
87 
88     return 0;
89 }
90 
91 /*******************************************************************************
92 
93     Makes a test. Starts the client in its own process, then starts the server
94     in the current process and waits for them to finish.
95     The client sends a message, gets an answer, compares the two strings and
96     exits.
97     The server opens the socket, accepts one request, reads the message,
98     replies with the same message and exits (no more requests are handled).
99 
100     Params:
101         socket_path = the unix socket path.
102 
103     Throws:
104         an exception if something goes wrong
105 
106 *******************************************************************************/
107 
108 void run_test ( istring socket_path )
109 {
110     auto timeout_mgr = new TimeoutManager;
111     auto epoll = new EpollSelectDispatcher(timeout_mgr);
112 
113     unlink(socket_path.ptr);
114     auto local_address = sockaddr_un.create(socket_path);
115     auto unix_socket   = new UnixSocket;
116     auto unix_server   = new UnixServer(cast(sockaddr*)&local_address,
117             unix_socket, epoll);
118     epoll.register(unix_server);
119 
120     // UnixServer should already bind and listen at this point,
121     // so it is safe to connect from the client
122     pid_t pid = fork();
123 
124     enforce(pid != -1);
125 
126     if (pid == 0)  // child
127     {
128         run_client(socket_path);
129         _Exit(0);
130     }
131 
132     epoll.eventLoop();
133 
134     enforce((socket_path[0] == '\0') || (unlink(socket_path.ptr) == 0),
135         "Can't remove socket file.");
136 
137     int status;
138     waitpid(pid, &status, 0);
139     enforce(status == 0, "Child exit status should be 0");
140 }
141 
142 /*******************************************************************************
143 
144     Makes two tests, one with standard UNIX socket and one with (LINUX) abstract
145     namespace sockets.
146 
147 *******************************************************************************/
148 
149 version (unittest) {} else
150 int main ( )
151 {
152     run_test("/tmp/ocean_socket_test");
153     run_test("\0ocean_socket_test");
154 
155     return 0;
156 }