Line data Source code
1 : /*----------------------------------------------------------------------------*/
2 : /* CP2K: A general program to perform molecular dynamics simulations */
3 : /* Copyright 2000-2026 CP2K developers group <https://cp2k.org> */
4 : /* */
5 : /* SPDX-License-Identifier: GPL-2.0-or-later */
6 : /*----------------------------------------------------------------------------*/
7 :
8 : /*----------------------------------------------------------------------------*/
9 : /* Copyright (C) 2013, Joshua More and Michele Ceriotti */
10 : /* */
11 : /* Permission is hereby granted, free of charge, to any person obtaining */
12 : /* a copy of this software and associated documentation files (the */
13 : /* "Software"), to deal in the Software without restriction, including */
14 : /* without limitation the rights to use, copy, modify, merge, publish, */
15 : /* distribute, sublicense, and/or sell copies of the Software, and to */
16 : /* permit persons to whom the Software is furnished to do so, subject to */
17 : /* the following conditions: */
18 : /* */
19 : /* The above copyright notice and this permission notice shall be included */
20 : /* in all copies or substantial portions of the Software. */
21 : /* */
22 : /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23 : /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24 : /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25 : /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26 : /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27 : /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28 : /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 : /*----------------------------------------------------------------------------*/
30 :
31 : /*******************************************************************************
32 : * \brief A minimal wrapper for socket communication.
33 : * Contains both the functions that transmit data to the socket and read
34 : * the data back out again once finished, and the function which opens
35 : * the socket initially. Can be linked to a FORTRAN code that does not
36 : * support sockets natively.
37 : * \author Joshua More and Michele Ceriotti
38 : ******************************************************************************/
39 : #ifndef __NO_SOCKETS
40 :
41 : #define _POSIX_C_SOURCE 200809L
42 :
43 : #include <errno.h>
44 : #include <math.h>
45 : #include <netdb.h>
46 : #include <netinet/in.h>
47 : #include <stdio.h>
48 : #include <stdlib.h>
49 : #include <string.h>
50 : #include <sys/select.h>
51 : #include <sys/socket.h>
52 : #include <sys/types.h>
53 : #include <sys/un.h>
54 : #include <time.h>
55 : #include <unistd.h>
56 :
57 : /*******************************************************************************
58 : * \brief Opens and connects a socket.
59 : * \param psockfd The id of the socket that will be created.
60 : * \param inet An integer that determines whether the socket will be an inet
61 : * or unix domain socket. Gives unix if 0, inet otherwise.
62 : * \param port The port number for the socket to be created. Low numbers are
63 : * often reserved for important channels, so use of numbers of 4
64 : * or more digits is recommended.
65 : * \param host The name of the host server (inet socket), or the full path
66 : * of the UNIX socket file (unix socket). The caller is
67 : * responsible for building this path, e.g. by prepending a
68 : * prefix such as "/tmp/ipi_".
69 : * \note Fortran passes an extra argument for the string length, but this is
70 : * ignored here for C compatibility.
71 : ******************************************************************************/
72 0 : void open_connect_socket(int *psockfd, int *inet, int *port, char *host) {
73 0 : int sockfd, ai_err;
74 :
75 0 : if (*inet > 0) { // creates an internet socket
76 :
77 : // fetches information on the host
78 0 : struct addrinfo hints, *res;
79 0 : char service[256];
80 :
81 0 : memset(&hints, 0, sizeof(hints));
82 0 : hints.ai_socktype = SOCK_STREAM;
83 0 : hints.ai_family = AF_INET;
84 0 : hints.ai_flags = AI_PASSIVE;
85 :
86 0 : sprintf(service, "%d", *port); // convert the port number to a string
87 0 : ai_err = getaddrinfo(host, service, &hints, &res);
88 0 : if (ai_err != 0) {
89 0 : perror("Error fetching host data. Wrong host name?");
90 0 : exit(-1);
91 : }
92 :
93 : // creates socket
94 0 : sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
95 0 : if (sockfd < 0) {
96 0 : perror("Error opening socket");
97 0 : exit(-1);
98 : }
99 :
100 : // makes connection
101 0 : if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) {
102 0 : perror("Error opening INET socket: wrong port or server unreachable");
103 0 : exit(-1);
104 : }
105 0 : freeaddrinfo(res);
106 : } else { // creates a unix socket
107 0 : struct sockaddr_un serv_addr;
108 :
109 : // fills up details of the socket address
110 0 : memset(&serv_addr, 0, sizeof(serv_addr));
111 0 : serv_addr.sun_family = AF_UNIX;
112 0 : if (strlen(host) >= sizeof(serv_addr.sun_path)) {
113 0 : fprintf(stderr, "UNIX socket path is too long\n");
114 0 : exit(EXIT_FAILURE);
115 : }
116 0 : strcpy(serv_addr.sun_path, host);
117 :
118 : // creates the socket
119 0 : sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
120 :
121 : // connects
122 0 : if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
123 0 : perror(
124 : "Error opening UNIX socket: path unavailable, or already existing");
125 0 : exit(-1);
126 : }
127 : }
128 :
129 0 : *psockfd = sockfd;
130 0 : }
131 :
132 : /*******************************************************************************
133 : * \brief Opens and binds a socket.
134 : * \param psockfd The id of the socket that will be created.
135 : * \param inet An integer that determines whether the socket will be an inet
136 : * or unix domain socket. Gives unix if 0, inet otherwise.
137 : * \param port The port number for the socket to be created. Low numbers are
138 : * often reserved for important channels, so use of numbers of 4
139 : * or more digits is recommended.
140 : * \param host The name of the host server.
141 : * \note Fortran passes an extra argument for the string length, but this is
142 : * ignored here for C compatibility.
143 : ******************************************************************************/
144 0 : void open_bind_socket(int *psockfd, int *inet, int *port, char *host) {
145 0 : int sockfd, ai_err;
146 :
147 0 : if (*inet > 0) { // creates an internet socket
148 :
149 : // fetches information on the host
150 0 : struct addrinfo hints, *res;
151 0 : char service[256];
152 :
153 0 : memset(&hints, 0, sizeof(hints));
154 0 : hints.ai_socktype = SOCK_STREAM;
155 0 : hints.ai_family = AF_INET;
156 0 : hints.ai_flags = AI_PASSIVE;
157 :
158 0 : sprintf(service, "%d", *port); // convert the port number to a string
159 0 : ai_err = getaddrinfo(host, service, &hints, &res);
160 0 : if (ai_err != 0) {
161 0 : perror("Error fetching host data. Wrong host name?");
162 0 : exit(-1);
163 : }
164 :
165 : // creates socket
166 0 : sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
167 0 : if (sockfd < 0) {
168 0 : perror("Error opening socket");
169 0 : exit(-1);
170 : }
171 :
172 : // binds
173 0 : if (bind(sockfd, res->ai_addr, res->ai_addrlen) < 0) {
174 0 : perror("Error binding INET socket: wrong port or server unreachable");
175 0 : exit(-1);
176 : }
177 0 : freeaddrinfo(res);
178 : } else { // creates a unix socket
179 0 : struct sockaddr_un serv_addr;
180 :
181 : // fills up details of the socket address
182 0 : memset(&serv_addr, 0, sizeof(serv_addr));
183 0 : serv_addr.sun_family = AF_UNIX;
184 0 : if (strlen(host) >= sizeof(serv_addr.sun_path)) {
185 0 : fprintf(stderr, "UNIX socket path is too long\n");
186 0 : exit(EXIT_FAILURE);
187 : }
188 0 : strcpy(serv_addr.sun_path, host);
189 :
190 : // creates the socket
191 0 : sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
192 :
193 0 : remove(serv_addr.sun_path);
194 :
195 : // binds
196 0 : if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
197 0 : perror(
198 : "Error binding UNIX socket: path unavailable, or already existing");
199 0 : exit(-1);
200 : }
201 : }
202 :
203 0 : *psockfd = sockfd;
204 0 : }
205 :
206 : /*******************************************************************************
207 : * \brief Writes to a socket.
208 : * \param psockfd The id of the socket that will be written to.
209 : * \param data The data to be written to the socket.
210 : * \param plen The length of the data in bytes.
211 : ******************************************************************************/
212 0 : void writebuffer(int *psockfd, char *data, int *plen) {
213 0 : if (*plen < 0) {
214 0 : fprintf(stderr, "Invalid socket write length\n");
215 0 : exit(EXIT_FAILURE);
216 : }
217 0 : size_t done = 0, len = (size_t)*plen;
218 0 : while (done < len) {
219 0 : ssize_t n = write(*psockfd, data + done, len - done);
220 0 : if (n < 0 && errno == EINTR)
221 0 : continue;
222 0 : if (n <= 0) {
223 0 : perror("Error writing to socket: server has quit or connection broke");
224 0 : exit(EXIT_FAILURE);
225 : }
226 0 : done += (size_t)n;
227 : }
228 0 : }
229 :
230 : /*******************************************************************************
231 : * \brief Reads from a socket.
232 : * \param psockfd The id of the socket that will be read from.
233 : * \param data The storage array for data read from the socket.
234 : * \param plen The length of the data in bytes.
235 : ******************************************************************************/
236 0 : void readbuffer(int *psockfd, char *data, int *plen) {
237 0 : if (*plen < 0) {
238 0 : fprintf(stderr, "Invalid socket read length\n");
239 0 : exit(EXIT_FAILURE);
240 : }
241 0 : size_t done = 0, len = (size_t)*plen;
242 0 : while (done < len) {
243 0 : ssize_t n = read(*psockfd, data + done, len - done);
244 0 : if (n < 0 && errno == EINTR)
245 0 : continue;
246 0 : if (n <= 0) {
247 0 : if (n == 0)
248 0 : fprintf(stderr, "Unexpected EOF reading from socket (%zu/%zu bytes)\n",
249 : done, len);
250 : else
251 0 : perror("Error reading from socket");
252 0 : exit(EXIT_FAILURE);
253 : }
254 0 : done += (size_t)n;
255 : }
256 0 : }
257 :
258 : /*******************************************************************************
259 : * \brief Listens to a socket.
260 : * \param psockfd The id of the socket to listen.
261 : * \param n An integer that determines the number of requests that will
262 : * be queued before further requests are refused.
263 : ******************************************************************************/
264 0 : void listen_socket(int *psockfd, int *backlog) {
265 :
266 0 : if (listen(*psockfd, *backlog) < 0) {
267 0 : perror("Error listening socket");
268 0 : exit(-1);
269 0 : };
270 0 : }
271 :
272 : /*******************************************************************************
273 : * \brief Listens to a socket.
274 : * \param psockfd The id of the socket to listen.
275 : * \param pclientfd The id of the accepted socket.
276 : ******************************************************************************/
277 0 : void accept_socket(int *psockfd, int *pclientfd) {
278 :
279 0 : int client_fd = accept(*psockfd, NULL, NULL);
280 :
281 0 : *pclientfd = client_fd;
282 0 : }
283 :
284 : /*******************************************************************************
285 : * \brief Closes a socket.
286 : * \param psockfd The id of the socket to close.
287 : ******************************************************************************/
288 0 : void close_socket(int *psockfd) { close(*psockfd); }
289 :
290 : /*******************************************************************************
291 : * \brief Removes a socket file.
292 : * \param hostname The name of the socket file to remove.
293 : ******************************************************************************/
294 0 : void remove_socket_file(char *host) { remove(host); }
295 :
296 : /*******************************************************************************
297 : * \brief Mini-wrapper to nanosleep
298 : * \param dsec number of seconds to wait (float values accepted)
299 : ******************************************************************************/
300 0 : void uwait(double *dsec) {
301 0 : struct timespec wt, rem;
302 0 : wt.tv_sec = floor(*dsec);
303 0 : wt.tv_nsec = (*dsec - wt.tv_sec) * 1000000000;
304 0 : nanosleep(&wt, &rem);
305 0 : }
306 :
307 : #endif
|