0f3905b084aec04639c7fd4eb5a7d122145e038c
[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, 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 (NULL != msg)
110     {
111         fprintf(stderr, "%s\n", msg);
112     }
113 }
114
115 /*! \brief Byte swap in case we are little-endian */
116 static uint16_t gmx_htons(uint16_t src)
117 {
118     uint16_t num = 1;
119
120     if (*(char *)&num == 1)
121     {
122         return src;
123     }
124     else
125     {
126         uint16_t dest = 0;
127
128         dest |= (src & 0x0000FF00) >> 8;
129         dest |= (src & 0x000000FF) << 8;
130
131         return dest;
132     }
133 }
134
135 /*! \brief Byte-unswap 16 bit word in case we are little-endian */
136 static uint16_t gmx_ntohs(uint16_t src)
137 {
138     return gmx_htons(src);
139 }
140
141
142 extern IMDSocket* imdsock_create()
143 {
144     IMDSocket *sock = NULL;
145
146
147 #ifdef GMX_IMD
148     snew(sock, 1);
149     /* Try to create socket: */
150     if ((sock->sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
151     {
152         print_IMD_error(ERR_ARGS);
153         sfree(sock);
154
155         return NULL;
156     }
157     else
158 #endif
159     {
160         return sock;
161     }
162 }
163
164
165 extern int imdsock_bind(IMDSocket *sock, int port)
166 {
167     int ret;
168
169
170 #ifdef GMX_IMD
171     memset(&(sock->address), 0, sizeof(sock->address));
172     sock->address.sin_family = PF_INET;
173     sock->address.sin_port   = gmx_htons(port);
174
175     /* Try to bind to address and port ...*/
176     ret = bind(sock->sockfd, (struct sockaddr *) &sock->address, sizeof(sock->address));
177 #else
178     ret = -1;
179 #endif
180
181     if (ret)
182     {
183         print_IMD_error(ERR_ARGS);
184     }
185
186     return ret;
187 }
188
189
190 extern int imd_sock_listen(IMDSocket *sock)
191 {
192     int ret;
193
194
195 #ifdef GMX_IMD
196     /* Try to set to listening state */
197     ret = listen(sock->sockfd, MAXIMDCONNECTIONS);
198 #else
199     ret = -1;
200 #endif
201
202     if (ret)
203     {
204         print_IMD_error(ERR_ARGS);
205     }
206
207     return ret;
208 }
209
210
211 extern IMDSocket* imdsock_accept(IMDSocket *sock)
212 {
213     int       ret;
214
215 #ifdef GMX_IMD
216     socklen_t length;
217
218
219     length = sizeof(sock->address);
220     ret    = accept(sock->sockfd, (struct sockaddr *) &sock->address, &length);
221
222     /* successful, redirect to distinct clientsocket */
223     if (ret >= 0)
224     {
225         IMDSocket *newsock;
226
227         snew(newsock, 1);
228         newsock->address    = sock->address;
229         newsock->sockfd     = ret;
230
231         return newsock;
232     }
233     else
234 #endif
235     {
236         print_IMD_error(ERR_ARGS);
237
238         return NULL;
239     }
240 }
241
242
243 extern int imdsock_getport(IMDSocket *sock, int *port)
244 {
245     int                ret;
246 #ifdef GMX_IMD
247     socklen_t          len;
248
249
250     len = sizeof(struct sockaddr_in);
251     ret = getsockname(sock->sockfd, (struct sockaddr *) &(sock->address), &len);
252     if (ret)
253     {
254         fprintf(stderr, "%s getsockname failed with error %d.\n", IMDstr, ret);
255         print_IMD_error(ERR_ARGS);
256     }
257     else
258     {
259         *port = gmx_ntohs(sock->address.sin_port);
260     }
261 #else
262     gmx_incons("imdsock_getport called without IMD support.");
263     ret = -1;
264 #endif
265
266     return ret;
267 }
268
269
270 extern int imdsock_write(IMDSocket *sock, const char *buffer, int length)
271 {
272     /* No read and write on windows, we have to use send and recv instead... */
273 #if GMX_NATIVE_WINDOWS
274 #ifdef GMX_HAVE_WINSOCK
275     return send(sock->sockfd, (const char *) buffer, length, NOFLAGS);
276 #else
277     return -1;
278 #endif
279 #else
280     return write(sock->sockfd, buffer, length);
281 #endif
282 }
283
284
285 extern int imdsock_read(IMDSocket *sock, char *buffer, int length)
286 {
287     /* See above... */
288 #if GMX_NATIVE_WINDOWS
289 #ifdef GMX_HAVE_WINSOCK
290     return recv(sock->sockfd, (char *) buffer, length, NOFLAGS);
291 #else
292     return -1;
293 #endif
294 #else
295     return read(sock->sockfd, buffer, length);
296 #endif
297 }
298
299
300 extern void imdsock_shutdown(IMDSocket *sock)
301 {
302     int ret = -1;
303
304
305     /* is the socket already NULL? */
306     if (sock == NULL)
307     {
308         return;
309     }
310
311 #ifdef GMX_IMD
312     /* If not, try to properly shut down. */
313     ret = shutdown(sock->sockfd, 1);
314 #endif
315
316     if (ret == -1)
317     {
318         fprintf(stderr, "%s Failed to shutdown socket. Did the client already disconnect?\n", IMDstr);
319         print_IMD_error(ERR_ARGS);
320     }
321 }
322
323
324 extern int imdsock_destroy(IMDSocket *sock)
325 {
326     int ret = -1;
327
328
329     if (sock == NULL)
330     {
331         return 1;
332     }
333
334 #if GMX_NATIVE_WINDOWS
335     /* On Windows, this function is called closesocket */
336 #ifdef GMX_HAVE_WINSOCK
337     ret = closesocket(sock->sockfd);
338 #endif
339 #else
340     ret = close(sock->sockfd);
341 #endif
342
343     if (ret == -1)
344     {
345         sfree(sock);
346         print_IMD_error(ERR_ARGS);
347
348         return 0;
349     }
350
351     return 1;
352 }
353
354
355 extern int imdsock_tryread(IMDSocket *sock, int timeoutsec, int timeoutusec)
356 {
357     int             ret = -1;
358
359
360 #ifdef GMX_IMD
361     fd_set          readfds;
362     /* Create new time structure with sec and usec. */
363     struct timeval *tval;
364
365
366     snew(tval, 1);
367
368     /* clear the set */
369     FD_ZERO(&readfds);
370     /* add the socket to the read set */
371     FD_SET(sock->sockfd, &readfds);
372
373     /* set the timeout */
374     tval->tv_sec  = timeoutsec;
375     tval->tv_usec = timeoutusec;
376     do
377     {
378         /* check the set for read readiness. */
379         ret = select(sock->sockfd + 1, &readfds, NULL, NULL, tval);
380         /* redo on system interrupt */
381     }
382     while (ret < 0 && errno == EINTR);
383
384     sfree(tval);
385 #endif
386
387     if (ret < 0)
388     {
389         print_IMD_error(ERR_ARGS);
390     }
391
392     return ret;
393 }