UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
platform_pipe.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2015-2021 CESNET, z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#ifdef HAVE_CONFIG_H
39#include "config.h"
40#endif // HAVE_CONFIG_H
41#include "config_unix.h"
42#include "config_win32.h"
43
45#include "rtp/net_udp.h" // socket_error
46
47#include <thread>
48
49using std::thread;
50
51#ifdef _WIN32
52#define DECLARE_TIMEOUT(name, init_val_s) DWORD name = init_val_s * 1000
53typedef char *sockopt_t;
54#else
55#define DECLARE_TIMEOUT(name, init_val_s) struct timeval name = { init_val_s, 0 }
56typedef void *sockopt_t;
57#endif
58
59#ifndef HAVE_CONFIG_H // compiled outside of UltraGrid
60#define socket_error(...) fprintf(stderr, __VA_ARGS__)
61#endif
62
63static fd_t open_socket(int *port)
64{
65 fd_t sock;
66 sock = socket(AF_INET, SOCK_STREAM, 0);
67 if (sock == INVALID_SOCKET) {
68 return INVALID_SOCKET;
69 }
70
71 struct sockaddr_in s_in;
72 memset(&s_in, 0, sizeof(s_in));
73 s_in.sin_family = AF_INET;
74 s_in.sin_addr.s_addr = htonl(INADDR_ANY);
75 s_in.sin_port = htons(0);
76 if (::bind(sock, (const struct sockaddr *) &s_in,
77 sizeof(s_in)) != 0) {
78 CLOSESOCKET(sock);
79 return INVALID_SOCKET;
80 }
81 if (listen(sock, 10) != 0) {
82 CLOSESOCKET(sock);
83 return INVALID_SOCKET;
84 }
85 socklen_t len = sizeof(s_in);
86 if (getsockname(sock, (struct sockaddr *) &s_in, &len) != 0) {
87 CLOSESOCKET(sock);
88 return INVALID_SOCKET;
89 }
90 *port = ntohs(s_in.sin_port);
91 return sock;
92}
93
94static fd_t connect_to_socket(int local_port)
95{
96 struct sockaddr_in s_in;
97 memset(&s_in, 0, sizeof(s_in));
98 s_in.sin_family = AF_INET;
99 s_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
100 s_in.sin_port = htons(local_port);
101 fd_t fd = socket(AF_INET, SOCK_STREAM, 0);
102 if (fd == INVALID_SOCKET) {
103 return INVALID_SOCKET;
104 }
105 int ret;
106
107 DECLARE_TIMEOUT(timeout, 1);
108 DECLARE_TIMEOUT(old_timeout, 0);
109 socklen_t old_timeout_len = sizeof old_timeout;
110 if (getsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<sockopt_t>(&old_timeout), &old_timeout_len) != 0) {
111 socket_error("pipe getsockopt");
112 }
113 if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<sockopt_t>(&timeout), sizeof timeout) != 0) {
114 socket_error("pipe setsockopt");
115 }
116 ret = connect(fd, (struct sockaddr *) &s_in,
117 sizeof(s_in));
118 if (ret != 0) {
119 socket_error("pipe connect");
120 CLOSESOCKET(fd);
121 return INVALID_SOCKET;
122 }
123 if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<sockopt_t>(&old_timeout), old_timeout_len) != 0) {
124 socket_error("pipe setsockopt");
125 }
126
127 return fd;
128}
129
130struct params {
131 int port;
133};
134
135static void * worker(void *args)
136{
137 struct params *p = (struct params *) args;
138 p->sock = connect_to_socket(p->port);
139
140 return NULL;
141}
142
144static int system_pipe(fd_t p[2])
145{
146#ifdef _WIN32
147 (void) p;
148 return -1;
149#else
150 fprintf(stderr, "Using native pipe instead of custom implementaton.\n");
151 return pipe(p);
152#endif
153}
154
156{
157#ifdef _WIN32
158 // This needs to be called twice, indeed, since WSACleanup will be run
159 // on both ends. This call is also required because not all pipes are
160 // destroyed before common_cleanup which calls WSACleanup
161 WSADATA wsaData;
162 int err = WSAStartup(MAKEWORD(2, 2), &wsaData);
163 if (err != 0) {
164 fprintf(stderr, "WSAStartup failed with error %d.", err);
165 return -1;
166 }
167 if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
168 fprintf(stderr, "Counld not found usable version of Winsock.\n");
169 WSACleanup();
170 return -1;
171 }
172 err = WSAStartup(MAKEWORD(2, 2), &wsaData);
173 if (err != 0) {
174 fprintf(stderr, "WSAStartup failed with error %d.", err);
175 WSACleanup();
176 return -1;
177 }
178 if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
179 fprintf(stderr, "Counld not found usable version of Winsock.\n");
180 WSACleanup();
181 WSACleanup();
182 return -1;
183 }
184#endif
185
186 struct params par{};
187 fd_t sock = open_socket(&par.port);
188 if (sock == INVALID_SOCKET) {
189 perror("open_socket");
190 return system_pipe(p);
191 }
192
193 DECLARE_TIMEOUT(timeout, 1);
194 DECLARE_TIMEOUT(old_timeout, 0);
195 socklen_t old_timeout_len = sizeof old_timeout;
196 if (getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<sockopt_t>(&old_timeout), &old_timeout_len) != 0) {
197 socket_error("pipe getsockopt");
198 }
199 if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<sockopt_t>(&timeout), sizeof timeout) != 0) {
200 socket_error("pipe setsockopt");
201 }
202
203 thread thr(worker, &par);
204
205 p[0] = accept(sock, NULL, NULL);
206 if (p[0] == INVALID_SOCKET) {
207 perror("pipe accept");
208 thr.join();
209 CLOSESOCKET(sock);
210 return system_pipe(p);
211 }
212 thr.join();
213 if (setsockopt(p[0], SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<sockopt_t>(&old_timeout), old_timeout_len) != 0) {
214 socket_error("pipe setsockopt");
215 }
216 p[1] = par.sock;
217 if (p[1] == INVALID_SOCKET) {
218 CLOSESOCKET(sock);
219 perror("accept");
220 return system_pipe(p);
221 }
222 CLOSESOCKET(sock);
223
224 return 0;
225}
226
228{
229 CLOSESOCKET(pipe);
230#ifdef _WIN32
231 WSACleanup();
232#endif
233}
234
int void size_t * len
int fd_t
#define INVALID_SOCKET
#define CLOSESOCKET
#define DECLARE_TIMEOUT(name, init_val_s)
int platform_pipe_init(fd_t p[2])
static fd_t connect_to_socket(int local_port)
#define socket_error(...)
void * sockopt_t
static fd_t open_socket(int *port)
void platform_pipe_close(fd_t pipe)
static void * worker(void *args)
static int system_pipe(fd_t p[2])
return regular pipe if available if our implementation fails