(git:34ef472)
sockets.c
Go to the documentation of this file.
1 /*----------------------------------------------------------------------------*/
2 /* CP2K: A general program to perform molecular dynamics simulations */
3 /* Copyright 2000-2024 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 <math.h>
44 #include <netdb.h>
45 #include <netinet/in.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <sys/select.h>
50 #include <sys/socket.h>
51 #include <sys/types.h>
52 #include <sys/un.h>
53 #include <time.h>
54 #include <unistd.h>
55 
56 /*******************************************************************************
57  * \brief Opens and connects a socket.
58  * \param psockfd The id of the socket that will be created.
59  * \param inet An integer that determines whether the socket will be an inet
60  * or unix domain socket. Gives unix if 0, inet otherwise.
61  * \param port The port number for the socket to be created. Low numbers are
62  * often reserved for important channels, so use of numbers of 4
63  * or more digits is recommended.
64  * \param host The name of the host server.
65  * \note Fortran passes an extra argument for the string length, but this is
66  * ignored here for C compatibility.
67  ******************************************************************************/
68 void open_connect_socket(int *psockfd, int *inet, int *port, char *host) {
69  int sockfd, ai_err;
70 
71  if (*inet > 0) { // creates an internet socket
72 
73  // fetches information on the host
74  struct addrinfo hints, *res;
75  char service[256];
76 
77  memset(&hints, 0, sizeof(hints));
78  hints.ai_socktype = SOCK_STREAM;
79  hints.ai_family = AF_INET;
80  hints.ai_flags = AI_PASSIVE;
81 
82  sprintf(service, "%d", *port); // convert the port number to a string
83  ai_err = getaddrinfo(host, service, &hints, &res);
84  if (ai_err != 0) {
85  perror("Error fetching host data. Wrong host name?");
86  exit(-1);
87  }
88 
89  // creates socket
90  sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
91  if (sockfd < 0) {
92  perror("Error opening socket");
93  exit(-1);
94  }
95 
96  // makes connection
97  if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) {
98  perror("Error opening INET socket: wrong port or server unreachable");
99  exit(-1);
100  }
101  freeaddrinfo(res);
102  } else { // creates a unix socket
103  struct sockaddr_un serv_addr;
104 
105  // fills up details of the socket address
106  memset(&serv_addr, 0, sizeof(serv_addr));
107  serv_addr.sun_family = AF_UNIX;
108  strcpy(serv_addr.sun_path, "/tmp/qiskit_");
109  strcpy(serv_addr.sun_path + 12, host);
110 
111  // creates the socket
112  sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
113 
114  // connects
115  if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
116  perror(
117  "Error opening UNIX socket: path unavailable, or already existing");
118  exit(-1);
119  }
120  }
121 
122  *psockfd = sockfd;
123 }
124 
125 /*******************************************************************************
126  * \brief Opens and binds a socket.
127  * \param psockfd The id of the socket that will be created.
128  * \param inet An integer that determines whether the socket will be an inet
129  * or unix domain socket. Gives unix if 0, inet otherwise.
130  * \param port The port number for the socket to be created. Low numbers are
131  * often reserved for important channels, so use of numbers of 4
132  * or more digits is recommended.
133  * \param host The name of the host server.
134  * \note Fortran passes an extra argument for the string length, but this is
135  * ignored here for C compatibility.
136  ******************************************************************************/
137 void open_bind_socket(int *psockfd, int *inet, int *port, char *host) {
138  int sockfd, ai_err;
139 
140  if (*inet > 0) { // creates an internet socket
141 
142  // fetches information on the host
143  struct addrinfo hints, *res;
144  char service[256];
145 
146  memset(&hints, 0, sizeof(hints));
147  hints.ai_socktype = SOCK_STREAM;
148  hints.ai_family = AF_INET;
149  hints.ai_flags = AI_PASSIVE;
150 
151  sprintf(service, "%d", *port); // convert the port number to a string
152  ai_err = getaddrinfo(host, service, &hints, &res);
153  if (ai_err != 0) {
154  perror("Error fetching host data. Wrong host name?");
155  exit(-1);
156  }
157 
158  // creates socket
159  sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
160  if (sockfd < 0) {
161  perror("Error opening socket");
162  exit(-1);
163  }
164 
165  // binds
166  if (bind(sockfd, res->ai_addr, res->ai_addrlen) < 0) {
167  perror("Error binding INET socket: wrong port or server unreachable");
168  exit(-1);
169  }
170  freeaddrinfo(res);
171  } else { // creates a unix socket
172  struct sockaddr_un serv_addr;
173 
174  // fills up details of the socket address
175  memset(&serv_addr, 0, sizeof(serv_addr));
176  serv_addr.sun_family = AF_UNIX;
177  strcpy(serv_addr.sun_path, host);
178 
179  // creates the socket
180  sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
181 
182  remove(serv_addr.sun_path);
183 
184  // binds
185  if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
186  perror(
187  "Error binding UNIX socket: path unavailable, or already existing");
188  exit(-1);
189  }
190  }
191 
192  *psockfd = sockfd;
193 }
194 
195 /*******************************************************************************
196  * \brief Writes to a socket.
197  * \param psockfd The id of the socket that will be written to.
198  * \param data The data to be written to the socket.
199  * \param plen The length of the data in bytes.
200  ******************************************************************************/
201 void writebuffer(int *psockfd, char *data, int *plen) {
202  int n;
203  int sockfd = *psockfd;
204  int len = *plen;
205 
206  n = write(sockfd, data, len);
207  if (n < 0) {
208  perror("Error writing to socket: server has quit or connection broke");
209  exit(-1);
210  }
211 }
212 
213 /*******************************************************************************
214  * \brief Reads from a socket.
215  * \param psockfd The id of the socket that will be read from.
216  * \param data The storage array for data read from the socket.
217  * \param plen The length of the data in bytes.
218  ******************************************************************************/
219 void readbuffer(int *psockfd, char *data, int *plen) {
220  int n, nr;
221  int sockfd = *psockfd;
222  int len = *plen;
223 
224  n = nr = read(sockfd, data, len);
225 
226  while (nr > 0 && n < len) {
227  nr = read(sockfd, &data[n], len - n);
228  n += nr;
229  }
230 
231  if (n == 0) {
232  perror("Error reading from socket: server has quit or connection broke");
233  exit(-1);
234  }
235 }
236 
237 /*******************************************************************************
238  * \brief Listens to a socket.
239  * \param psockfd The id of the socket to listen.
240  * \param n An integer that determines the number of requests that will
241  * be queued before further requests are refused.
242  ******************************************************************************/
243 void listen_socket(int *psockfd, int *backlog) {
244 
245  if (listen(*psockfd, *backlog) < 0) {
246  perror("Error listening socket");
247  exit(-1);
248  };
249 }
250 
251 /*******************************************************************************
252  * \brief Listens to a socket.
253  * \param psockfd The id of the socket to listen.
254  * \param pclientfd The id of the accepted socket.
255  ******************************************************************************/
256 void accept_socket(int *psockfd, int *pclientfd) {
257 
258  int client_fd = accept(*psockfd, NULL, NULL);
259 
260  *pclientfd = client_fd;
261 }
262 
263 /*******************************************************************************
264  * \brief Closes a socket.
265  * \param psockfd The id of the socket to close.
266  ******************************************************************************/
267 void close_socket(int *psockfd) { close(*psockfd); }
268 
269 /*******************************************************************************
270  * \brief Removes a socket file.
271  * \param hostname The name of the socket file to remove.
272  ******************************************************************************/
273 void remove_socket_file(char *host) { remove(host); }
274 
275 /*******************************************************************************
276  * \brief Mini-wrapper to nanosleep
277  * \param dsec number of seconds to wait (float values accepted)
278  ******************************************************************************/
279 void uwait(double *dsec) {
280  struct timespec wt, rem;
281  wt.tv_sec = floor(*dsec);
282  wt.tv_nsec = (*dsec - wt.tv_sec) * 1000000000;
283  nanosleep(&wt, &rem);
284 }
285 
286 #endif
void open_bind_socket(int *psockfd, int *inet, int *port, char *host)
Opens and binds a socket.
Definition: sockets.c:137
void open_connect_socket(int *psockfd, int *inet, int *port, char *host)
Opens and connects a socket.
Definition: sockets.c:68
void writebuffer(int *psockfd, char *data, int *plen)
Writes to a socket.
Definition: sockets.c:201
void accept_socket(int *psockfd, int *pclientfd)
Listens to a socket.
Definition: sockets.c:256
void uwait(double *dsec)
Mini-wrapper to nanosleep.
Definition: sockets.c:279
void close_socket(int *psockfd)
Closes a socket.
Definition: sockets.c:267
void listen_socket(int *psockfd, int *backlog)
Listens to a socket.
Definition: sockets.c:243
void readbuffer(int *psockfd, char *data, int *plen)
Reads from a socket.
Definition: sockets.c:219
void remove_socket_file(char *host)
Removes a socket file.
Definition: sockets.c:273