d5a5cfb4184d58c3592758fbf6dcb6d247368397
[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, 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 <errno.h>
56 #include <string.h>
57
58 #include "gromacs/imd/imd.h"
59 #include "gromacs/utility/fatalerror.h"
60 #include "gromacs/utility/smalloc.h"
61
62 #if GMX_NATIVE_WINDOWS
63 #ifdef GMX_HAVE_WINSOCK
64 /*! \brief Define socklen type on Windows. */
65 typedef int socklen_t;
66
67 /*! \brief Define a function to initialize winsock. */
68 extern int imdsock_winsockinit()
69 {
70     int ret = -1;
71
72
73     WSADATA wsd;
74
75     /* We use winsock 1.1 compatibility for now. Though I guess no one will try on Windows 95. */
76     ret = WSAStartup(MAKEWORD(1, 1), &wsd);
77     return ret;
78 }
79 #endif
80 #else
81 /* On UNIX, we can use nice errors from errno.h */
82 #include <unistd.h>
83 #ifdef GMX_IMD
84 #include <time.h>
85
86 #include <sys/select.h>
87 #include <sys/time.h>
88 #endif
89 #endif
90
91
92 /*! \brief Simple error handling. */
93 #if GMX_NATIVE_WINDOWS
94 #define ERR_ARGS __FILE__, __LINE__, NULL
95 #else
96 #define ERR_ARGS __FILE__, __LINE__, strerror(errno)
97 #endif
98
99
100 /*! \brief Currently only 1 client connection is supported. */
101 #define MAXIMDCONNECTIONS 1
102
103
104 /*! \brief Print a nice error message on UNIX systems, using errno.h. */
105 static void print_IMD_error(const char *file, int line, char *msg)
106 {
107     fprintf(stderr, "%s Error in file %s on line %d.\n", IMDstr, file, line);
108
109     if (nullptr != msg)
110     {
111         fprintf(stderr, "%s\n", msg);
112     }
113 }
114
115
116 extern IMDSocket* imdsock_create()
117 {
118     IMDSocket *sock = nullptr;
119
120
121 #ifdef GMX_IMD
122     snew(sock, 1);
123     /* Try to create socket: */
124     if ((sock->sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
125     {
126         print_IMD_error(ERR_ARGS);
127         sfree(sock);
128
129         return nullptr;
130     }
131     else
132 #endif
133     {
134         return sock;
135     }
136 }
137
138
139 extern int imdsock_bind(IMDSocket *sock, int port)
140 {
141     int ret;
142
143
144 #ifdef GMX_IMD
145     memset(&(sock->address), 0, sizeof(sock->address));
146     sock->address.sin_family = PF_INET;
147     sock->address.sin_port   = htons(port);
148
149     /* Try to bind to address and port ...*/
150     ret = bind(sock->sockfd, reinterpret_cast<struct sockaddr *>(&sock->address), sizeof(sock->address));
151 #else
152     ret = -1;
153 #endif
154
155     if (ret)
156     {
157         print_IMD_error(ERR_ARGS);
158     }
159
160     return ret;
161 }
162
163
164 extern int imd_sock_listen(IMDSocket *sock)
165 {
166     int ret;
167
168
169 #ifdef GMX_IMD
170     /* Try to set to listening state */
171     ret = listen(sock->sockfd, MAXIMDCONNECTIONS);
172 #else
173     ret = -1;
174 #endif
175
176     if (ret)
177     {
178         print_IMD_error(ERR_ARGS);
179     }
180
181     return ret;
182 }
183
184
185 extern IMDSocket* imdsock_accept(IMDSocket *sock)
186 {
187     int       ret;
188
189 #ifdef GMX_IMD
190     socklen_t length;
191
192
193     length = sizeof(sock->address);
194     ret    = accept(sock->sockfd, reinterpret_cast<struct sockaddr *>(&sock->address), &length);
195
196     /* successful, redirect to distinct clientsocket */
197     if (ret >= 0)
198     {
199         IMDSocket *newsock;
200
201         snew(newsock, 1);
202         newsock->address    = sock->address;
203         newsock->sockfd     = ret;
204
205         return newsock;
206     }
207     else
208 #endif
209     {
210         print_IMD_error(ERR_ARGS);
211
212         return nullptr;
213     }
214 }
215
216
217 extern int imdsock_getport(IMDSocket *sock, int *port)
218 {
219     int                ret;
220 #ifdef GMX_IMD
221     socklen_t          len;
222
223
224     len = sizeof(struct sockaddr_in);
225     ret = getsockname(sock->sockfd, reinterpret_cast<struct sockaddr *>(&(sock->address)), &len);
226     if (ret)
227     {
228         fprintf(stderr, "%s getsockname failed with error %d.\n", IMDstr, ret);
229         print_IMD_error(ERR_ARGS);
230     }
231     else
232     {
233         *port = ntohs(sock->address.sin_port);
234     }
235 #else
236     gmx_incons("imdsock_getport called without IMD support.");
237     ret = -1;
238 #endif
239
240     return ret;
241 }
242
243
244 extern int imdsock_write(IMDSocket *sock, const char *buffer, int length)
245 {
246     /* No read and write on windows, we have to use send and recv instead... */
247 #if GMX_NATIVE_WINDOWS
248 #ifdef GMX_HAVE_WINSOCK
249     return send(sock->sockfd, (const char *) buffer, length, NOFLAGS);
250 #else
251     return -1;
252 #endif
253 #else
254     return write(sock->sockfd, buffer, length);
255 #endif
256 }
257
258
259 extern int imdsock_read(IMDSocket *sock, char *buffer, int length)
260 {
261     /* See above... */
262 #if GMX_NATIVE_WINDOWS
263 #ifdef GMX_HAVE_WINSOCK
264     return recv(sock->sockfd, (char *) buffer, length, NOFLAGS);
265 #else
266     return -1;
267 #endif
268 #else
269     return read(sock->sockfd, buffer, length);
270 #endif
271 }
272
273
274 extern void imdsock_shutdown(IMDSocket *sock)
275 {
276     int ret = -1;
277
278
279     /* is the socket already NULL? */
280     if (sock == nullptr)
281     {
282         return;
283     }
284
285 #ifdef GMX_IMD
286     /* If not, try to properly shut down. */
287     ret = shutdown(sock->sockfd, 1);
288 #endif
289
290     if (ret == -1)
291     {
292         fprintf(stderr, "%s Failed to shutdown socket. Did the client already disconnect?\n", IMDstr);
293         print_IMD_error(ERR_ARGS);
294     }
295 }
296
297
298 extern int imdsock_destroy(IMDSocket *sock)
299 {
300     int ret = -1;
301
302
303     if (sock == nullptr)
304     {
305         return 1;
306     }
307
308 #if GMX_NATIVE_WINDOWS
309     /* On Windows, this function is called closesocket */
310 #ifdef GMX_HAVE_WINSOCK
311     ret = closesocket(sock->sockfd);
312 #endif
313 #else
314     ret = close(sock->sockfd);
315 #endif
316
317     if (ret == -1)
318     {
319         sfree(sock);
320         print_IMD_error(ERR_ARGS);
321
322         return 0;
323     }
324
325     return 1;
326 }
327
328
329 extern int imdsock_tryread(IMDSocket *sock, int timeoutsec, int timeoutusec)
330 {
331     int             ret = -1;
332
333
334 #ifdef GMX_IMD
335     fd_set          readfds;
336     /* Create new time structure with sec and usec. */
337     struct timeval *tval;
338
339
340     snew(tval, 1);
341
342     /* clear the set */
343     FD_ZERO(&readfds);
344     /* add the socket to the read set */
345     FD_SET(sock->sockfd, &readfds);
346
347     /* set the timeout */
348     tval->tv_sec  = timeoutsec;
349     tval->tv_usec = timeoutusec;
350     do
351     {
352         /* check the set for read readiness. */
353         ret = select(sock->sockfd + 1, &readfds, nullptr, nullptr, tval);
354         /* redo on system interrupt */
355     }
356     while (ret < 0 && errno == EINTR);
357
358     sfree(tval);
359 #endif
360
361     if (ret < 0)
362     {
363         print_IMD_error(ERR_ARGS);
364     }
365
366     return ret;
367 }