(git:cdd058b)
Loading...
Searching...
No Matches
sockets.c
Go to the documentation of this file.
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 ******************************************************************************/
72void open_connect_socket(int *psockfd, int *inet, int *port, char *host) {
73 int sockfd, ai_err;
74
75 if (*inet > 0) { // creates an internet socket
76
77 // fetches information on the host
78 struct addrinfo hints, *res;
79 char service[256];
80
81 memset(&hints, 0, sizeof(hints));
82 hints.ai_socktype = SOCK_STREAM;
83 hints.ai_family = AF_INET;
84 hints.ai_flags = AI_PASSIVE;
85
86 sprintf(service, "%d", *port); // convert the port number to a string
87 ai_err = getaddrinfo(host, service, &hints, &res);
88 if (ai_err != 0) {
89 perror("Error fetching host data. Wrong host name?");
90 exit(-1);
91 }
92
93 // creates socket
94 sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
95 if (sockfd < 0) {
96 perror("Error opening socket");
97 exit(-1);
98 }
99
100 // makes connection
101 if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) {
102 perror("Error opening INET socket: wrong port or server unreachable");
103 exit(-1);
104 }
105 freeaddrinfo(res);
106 } else { // creates a unix socket
107 struct sockaddr_un serv_addr;
108
109 // fills up details of the socket address
110 memset(&serv_addr, 0, sizeof(serv_addr));
111 serv_addr.sun_family = AF_UNIX;
112 if (strlen(host) >= sizeof(serv_addr.sun_path)) {
113 fprintf(stderr, "UNIX socket path is too long\n");
114 exit(EXIT_FAILURE);
115 }
116 strcpy(serv_addr.sun_path, host);
117
118 // creates the socket
119 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
120
121 // connects
122 if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
123 perror(
124 "Error opening UNIX socket: path unavailable, or already existing");
125 exit(-1);
126 }
127 }
128
129 *psockfd = sockfd;
130}
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 ******************************************************************************/
144void open_bind_socket(int *psockfd, int *inet, int *port, char *host) {
145 int sockfd, ai_err;
146
147 if (*inet > 0) { // creates an internet socket
148
149 // fetches information on the host
150 struct addrinfo hints, *res;
151 char service[256];
152
153 memset(&hints, 0, sizeof(hints));
154 hints.ai_socktype = SOCK_STREAM;
155 hints.ai_family = AF_INET;
156 hints.ai_flags = AI_PASSIVE;
157
158 sprintf(service, "%d", *port); // convert the port number to a string
159 ai_err = getaddrinfo(host, service, &hints, &res);
160 if (ai_err != 0) {
161 perror("Error fetching host data. Wrong host name?");
162 exit(-1);
163 }
164
165 // creates socket
166 sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
167 if (sockfd < 0) {
168 perror("Error opening socket");
169 exit(-1);
170 }
171
172 // binds
173 if (bind(sockfd, res->ai_addr, res->ai_addrlen) < 0) {
174 perror("Error binding INET socket: wrong port or server unreachable");
175 exit(-1);
176 }
177 freeaddrinfo(res);
178 } else { // creates a unix socket
179 struct sockaddr_un serv_addr;
180
181 // fills up details of the socket address
182 memset(&serv_addr, 0, sizeof(serv_addr));
183 serv_addr.sun_family = AF_UNIX;
184 if (strlen(host) >= sizeof(serv_addr.sun_path)) {
185 fprintf(stderr, "UNIX socket path is too long\n");
186 exit(EXIT_FAILURE);
187 }
188 strcpy(serv_addr.sun_path, host);
189
190 // creates the socket
191 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
192
193 remove(serv_addr.sun_path);
194
195 // binds
196 if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
197 perror(
198 "Error binding UNIX socket: path unavailable, or already existing");
199 exit(-1);
200 }
201 }
202
203 *psockfd = sockfd;
204}
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 ******************************************************************************/
212void writebuffer(int *psockfd, char *data, int *plen) {
213 if (*plen < 0) {
214 fprintf(stderr, "Invalid socket write length\n");
215 exit(EXIT_FAILURE);
216 }
217 size_t done = 0, len = (size_t)*plen;
218 while (done < len) {
219 ssize_t n = write(*psockfd, data + done, len - done);
220 if (n < 0 && errno == EINTR)
221 continue;
222 if (n <= 0) {
223 perror("Error writing to socket: server has quit or connection broke");
224 exit(EXIT_FAILURE);
225 }
226 done += (size_t)n;
227 }
228}
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 ******************************************************************************/
236void readbuffer(int *psockfd, char *data, int *plen) {
237 if (*plen < 0) {
238 fprintf(stderr, "Invalid socket read length\n");
239 exit(EXIT_FAILURE);
240 }
241 size_t done = 0, len = (size_t)*plen;
242 while (done < len) {
243 ssize_t n = read(*psockfd, data + done, len - done);
244 if (n < 0 && errno == EINTR)
245 continue;
246 if (n <= 0) {
247 if (n == 0)
248 fprintf(stderr, "Unexpected EOF reading from socket (%zu/%zu bytes)\n",
249 done, len);
250 else
251 perror("Error reading from socket");
252 exit(EXIT_FAILURE);
253 }
254 done += (size_t)n;
255 }
256}
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 ******************************************************************************/
264void listen_socket(int *psockfd, int *backlog) {
265
266 if (listen(*psockfd, *backlog) < 0) {
267 perror("Error listening socket");
268 exit(-1);
269 };
270}
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 ******************************************************************************/
277void accept_socket(int *psockfd, int *pclientfd) {
278
279 int client_fd = accept(*psockfd, NULL, NULL);
280
281 *pclientfd = client_fd;
282}
283
284/*******************************************************************************
285 * \brief Closes a socket.
286 * \param psockfd The id of the socket to close.
287 ******************************************************************************/
288void 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 ******************************************************************************/
294void 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 ******************************************************************************/
300void uwait(double *dsec) {
301 struct timespec wt, rem;
302 wt.tv_sec = floor(*dsec);
303 wt.tv_nsec = (*dsec - wt.tv_sec) * 1000000000;
304 nanosleep(&wt, &rem);
305}
306
307#endif
void open_bind_socket(int *psockfd, int *inet, int *port, char *host)
Opens and binds a socket.
Definition sockets.c:144
void open_connect_socket(int *psockfd, int *inet, int *port, char *host)
Opens and connects a socket.
Definition sockets.c:72
void writebuffer(int *psockfd, char *data, int *plen)
Writes to a socket.
Definition sockets.c:212
void accept_socket(int *psockfd, int *pclientfd)
Listens to a socket.
Definition sockets.c:277
void uwait(double *dsec)
Mini-wrapper to nanosleep.
Definition sockets.c:300
void close_socket(int *psockfd)
Closes a socket.
Definition sockets.c:288
void listen_socket(int *psockfd, int *backlog)
Listens to a socket.
Definition sockets.c:264
void readbuffer(int *psockfd, char *data, int *plen)
Reads from a socket.
Definition sockets.c:236
void remove_socket_file(char *host)
Removes a socket file.
Definition sockets.c:294