Apply clang-format to source tree
[alexxy/gromacs.git] / src / gromacs / imd / imdsocket.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35
36 /*! \internal \file
37  *
38  * \brief
39  * Implements functions of imdsocket.h.
40  *
41  * This file re-implements vmdsock.c functions from original IMD API from scratch,
42  * see imdsocket.h for references to the IMD API.
43  *
44  * \author Martin Hoefling, Carsten Kutzner <ckutzne@gwdg.de>
45  *
46  * \ingroup module_imd
47  */
48
49 #include "gmxpre.h"
50
51 #include "imdsocket.h"
52
53 #include "config.h"
54
55 #include <cerrno>
56 #include <cstring>
57 #include <ctime>
58
59 #include "gromacs/imd/imd.h"
60 #include "gromacs/utility/fatalerror.h"
61 #include "gromacs/utility/smalloc.h"
62
63 #if GMX_IMD
64
65 #    if GMX_NATIVE_WINDOWS
66
67 #        include <Windows.h>
68 #        include <Winsock.h>
69
70 //! Constant for passing no flags
71 constexpr int c_noFlags = 0;
72 /*! \brief Define socklen type on Windows. */
73 typedef int socklen_t;
74
75 #    else
76
77 #        include <unistd.h>
78 #        include <netinet/in.h>
79 #        include <sys/select.h>
80 #        include <sys/socket.h>
81 #        include <sys/time.h>
82
83 #    endif
84
85 #endif
86
87 namespace gmx
88 {
89
90 /*! \internal
91  *
92  * \brief
93  * IMD (interactive molecular dynamics) socket structure
94  *
95  */
96 struct IMDSocket
97 {
98 #if GMX_IMD
99     struct sockaddr_in address; /**< address of socket                   */
100     int                sockfd;  /**< socket file descriptor              */
101 #endif
102 };
103
104 /*! \brief Define a function to initialize winsock. */
105 int imdsock_winsockinit()
106 {
107 #if GMX_IMD && GMX_NATIVE_WINDOWS
108     fprintf(stderr, "%s Initializing winsock.\n", IMDstr);
109     int ret = -1;
110
111     WSADATA wsd;
112
113     /* We use winsock 1.1 compatibility for now. Though I guess no one will try on Windows 95. */
114     ret = WSAStartup(MAKEWORD(1, 1), &wsd);
115     return ret;
116 #else
117     return 0;
118 #endif
119 }
120
121
122 /*! \brief Simple error handling. */
123 #if GMX_NATIVE_WINDOWS
124 #    define ERR_ARGS __FILE__, __LINE__, NULL
125 #else
126 #    define ERR_ARGS __FILE__, __LINE__, strerror(errno)
127 #endif
128
129
130 /*! \brief Currently only 1 client connection is supported. */
131 constexpr int c_maxConnections = 1;
132
133
134 /*! \brief Print a nice error message on UNIX systems, using errno.h. */
135 static void print_IMD_error(const char* file, int line, char* msg)
136 {
137     fprintf(stderr, "%s Error in file %s on line %d.\n", IMDstr, file, line);
138
139     if (nullptr != msg)
140     {
141         fprintf(stderr, "%s\n", msg);
142     }
143 }
144
145
146 IMDSocket* imdsock_create()
147 {
148     IMDSocket* sock = nullptr;
149
150
151 #if GMX_IMD
152     snew(sock, 1);
153     /* Try to create socket: */
154     if ((sock->sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
155     {
156         print_IMD_error(ERR_ARGS);
157         sfree(sock);
158
159         return nullptr;
160     }
161     else
162 #endif
163     {
164         return sock;
165     }
166 }
167
168 void imd_sleep(unsigned int seconds)
169 {
170 #if GMX_IMD
171 #    if GMX_NATIVE_WINDOWS
172     Sleep(seconds);
173 #    else
174     sleep(seconds);
175 #    endif
176 #else
177     GMX_UNUSED_VALUE(seconds);
178 #endif
179 }
180
181
182 int imdsock_bind(IMDSocket* sock, int port)
183 {
184     int ret;
185
186
187 #if GMX_IMD
188     memset(&(sock->address), 0, sizeof(sock->address));
189     sock->address.sin_family = PF_INET;
190     sock->address.sin_port   = htons(port);
191
192     /* Try to bind to address and port ...*/
193     ret = bind(sock->sockfd, reinterpret_cast<struct sockaddr*>(&sock->address), sizeof(sock->address));
194 #else
195     GMX_UNUSED_VALUE(port);
196     GMX_UNUSED_VALUE(sock);
197     ret = -1;
198 #endif
199
200     if (ret)
201     {
202         print_IMD_error(ERR_ARGS);
203     }
204
205     return ret;
206 }
207
208
209 int imd_sock_listen(IMDSocket* sock)
210 {
211     int ret;
212
213
214 #if GMX_IMD
215     /* Try to set to listening state */
216     ret = listen(sock->sockfd, c_maxConnections);
217 #else
218     GMX_UNUSED_VALUE(c_maxConnections);
219     GMX_UNUSED_VALUE(sock);
220     ret = -1;
221 #endif
222
223     if (ret)
224     {
225         print_IMD_error(ERR_ARGS);
226     }
227
228     return ret;
229 }
230
231
232 IMDSocket* imdsock_accept(IMDSocket* sock)
233 {
234
235 #if GMX_IMD
236     socklen_t length = sizeof(sock->address);
237     int ret = accept(sock->sockfd, reinterpret_cast<struct sockaddr*>(&sock->address), &length);
238
239     /* successful, redirect to distinct clientsocket */
240     if (ret >= 0)
241     {
242         IMDSocket* newsock;
243
244         snew(newsock, 1);
245         newsock->address = sock->address;
246         newsock->sockfd  = ret;
247
248         return newsock;
249     }
250     else
251 #else
252     GMX_UNUSED_VALUE(sock);
253 #endif
254     {
255         print_IMD_error(ERR_ARGS);
256
257         return nullptr;
258     }
259 }
260
261
262 int imdsock_getport(IMDSocket* sock, int* port)
263 {
264     int ret;
265 #if GMX_IMD
266     socklen_t len;
267
268
269     len = sizeof(struct sockaddr_in);
270     ret = getsockname(sock->sockfd, reinterpret_cast<struct sockaddr*>(&(sock->address)), &len);
271     if (ret)
272     {
273         fprintf(stderr, "%s getsockname failed with error %d.\n", IMDstr, ret);
274         print_IMD_error(ERR_ARGS);
275     }
276     else
277     {
278         *port = ntohs(sock->address.sin_port);
279     }
280 #else
281     GMX_UNUSED_VALUE(port);
282     GMX_UNUSED_VALUE(sock);
283     gmx_incons("imdsock_getport called without IMD support.");
284     ret = -1;
285 #endif
286
287     return ret;
288 }
289
290
291 int imd_htonl(int src)
292 {
293 #if GMX_IMD
294     return htonl(src);
295 #else
296     return src;
297 #endif
298 }
299
300 int imd_ntohl(int src)
301 {
302 #if GMX_IMD
303     return ntohl(src);
304 #else
305     return src;
306 #endif
307 }
308
309 int imdsock_write(IMDSocket* sock, const char* buffer, int length)
310 {
311 #if GMX_IMD
312     /* No read and write on windows, we have to use send and recv instead... */
313 #    if GMX_NATIVE_WINDOWS
314     return send(sock->sockfd, (const char*)buffer, length, c_noFlags);
315 #    else
316     return write(sock->sockfd, buffer, length);
317 #    endif
318 #else
319     GMX_UNUSED_VALUE(buffer);
320     GMX_UNUSED_VALUE(length);
321     GMX_UNUSED_VALUE(sock);
322     return 0;
323 #endif
324 }
325
326
327 int imdsock_read(IMDSocket* sock, char* buffer, int length)
328 {
329 #if GMX_IMD
330     /* See above... */
331 #    if GMX_NATIVE_WINDOWS
332     return recv(sock->sockfd, (char*)buffer, length, c_noFlags);
333 #    else
334     return read(sock->sockfd, buffer, length);
335 #    endif
336 #else
337     GMX_UNUSED_VALUE(buffer);
338     GMX_UNUSED_VALUE(length);
339     GMX_UNUSED_VALUE(sock);
340     return 0;
341 #endif
342 }
343
344
345 void imdsock_shutdown(IMDSocket* sock)
346 {
347     int ret = -1;
348
349
350     /* is the socket already NULL? */
351     if (sock == nullptr)
352     {
353         return;
354     }
355
356 #if GMX_IMD
357     /* If not, try to properly shut down. */
358     ret = shutdown(sock->sockfd, 1);
359 #endif
360
361     if (ret == -1)
362     {
363         fprintf(stderr, "%s Failed to shutdown socket. Did the client already disconnect?\n", IMDstr);
364         print_IMD_error(ERR_ARGS);
365     }
366 }
367
368
369 int imdsock_destroy(IMDSocket* sock)
370 {
371     int ret = -1;
372
373
374     if (sock == nullptr)
375     {
376         return 1;
377     }
378
379 #if GMX_IMD
380 #    if GMX_NATIVE_WINDOWS
381     ret = closesocket(sock->sockfd);
382 #    else
383     ret = close(sock->sockfd);
384 #    endif
385 #endif
386
387     if (ret == -1)
388     {
389         sfree(sock);
390         print_IMD_error(ERR_ARGS);
391
392         return 0;
393     }
394
395     return 1;
396 }
397
398
399 int imdsock_tryread(IMDSocket* sock, int timeoutsec, int timeoutusec)
400 {
401     int ret = -1;
402
403
404 #if GMX_IMD
405     fd_set readfds;
406     /* Create new time structure with sec and usec. */
407     struct timeval* tval;
408
409
410     snew(tval, 1);
411
412     /* clear the set */
413     FD_ZERO(&readfds);
414     /* add the socket to the read set */
415     FD_SET(sock->sockfd, &readfds);
416
417     /* set the timeout */
418     tval->tv_sec  = timeoutsec;
419     tval->tv_usec = timeoutusec;
420     do
421     {
422         /* check the set for read readiness. */
423         ret = select(sock->sockfd + 1, &readfds, nullptr, nullptr, tval);
424         /* redo on system interrupt */
425     } while (ret < 0 && errno == EINTR);
426
427     sfree(tval);
428 #else
429     GMX_UNUSED_VALUE(sock);
430     GMX_UNUSED_VALUE(timeoutsec);
431     GMX_UNUSED_VALUE(timeoutusec);
432 #endif
433
434     if (ret < 0)
435     {
436         print_IMD_error(ERR_ARGS);
437     }
438
439     return ret;
440 }
441
442 } // namespace gmx