]> sourceware.org Git - newlib-cygwin.git/blob - winsup/cygserver/transport_pipes.cc
Cygwin: set NTDDI_VERSION to enable more recent windows definitions
[newlib-cygwin.git] / winsup / cygserver / transport_pipes.cc
1 /* transport_pipes.cc
2
3 Written by Robert Collins <rbtcollins@hotmail.com>
4
5 This file is part of Cygwin.
6
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
9 details. */
10
11 /* to allow this to link into cygwin and the .dll, a little magic is needed. */
12 #ifdef __OUTSIDE_CYGWIN__
13 #include "woutsup.h"
14 #include <ntdef.h>
15 #else
16 #include "winsup.h"
17 #endif
18
19 #include <sys/types.h>
20
21 #include <assert.h>
22 #include <netdb.h>
23 #include <pthread.h>
24 #include <unistd.h>
25 #include <wchar.h>
26 #include <sys/cygwin.h>
27
28 #include "cygerrno.h"
29 #include "transport.h"
30 #include "transport_pipes.h"
31
32 #ifndef __INSIDE_CYGWIN__
33 #include "cygserver.h"
34 #include "cygserver_ipc.h"
35 #else
36 #include "security.h"
37 #endif
38
39 #ifdef __INSIDE_CYGWIN__
40 #define SET_ERRNO(err) set_errno (err)
41 #else
42 #define SET_ERRNO(err) errno = (err)
43 #endif
44
45 enum
46 {
47 MAX_WAIT_NAMED_PIPE_RETRY = 64,
48 WAIT_NAMED_PIPE_TIMEOUT = 10 // milliseconds
49 };
50
51 #ifndef __INSIDE_CYGWIN__
52
53 transport_layer_pipes::transport_layer_pipes (const HANDLE hPipe)
54 : _hPipe (hPipe),
55 _is_accepted_endpoint (true),
56 _is_listening_endpoint (false)
57 {
58 assert (_hPipe);
59 assert (_hPipe != INVALID_HANDLE_VALUE);
60 _pipe_name[0] = L'\0';
61 }
62
63 #endif /* !__INSIDE_CYGWIN__ */
64
65 transport_layer_pipes::transport_layer_pipes ()
66 : _hPipe (NULL),
67 _is_accepted_endpoint (false),
68 _is_listening_endpoint (false)
69 {
70 wchar_t cyg_instkey[18];
71
72 wchar_t *p = wcpcpy (_pipe_name, PIPE_NAME_PREFIX);
73 if (!cygwin_internal (CW_GET_INSTKEY, cyg_instkey))
74 wcpcpy (wcpcpy (p, cyg_instkey), PIPE_NAME_SUFFIX);
75 }
76
77 transport_layer_pipes::~transport_layer_pipes ()
78 {
79 close ();
80 }
81
82 #ifndef __INSIDE_CYGWIN__
83
84 static HANDLE listen_pipe;
85 static HANDLE connect_pipe;
86
87 int
88 transport_layer_pipes::listen ()
89 {
90 assert (!_hPipe);
91 assert (!_is_accepted_endpoint);
92 assert (!_is_listening_endpoint);
93
94 _is_listening_endpoint = true;
95
96 debug ("Try to create named pipe: %ls", _pipe_name);
97
98 /* We have to create the first instance of the listening pipe here, and
99 we also have to create at least one instance of the client side to avoid
100 a race condition.
101 See https://cygwin.com/ml/cygwin/2012-11/threads.html#00144 */
102 listen_pipe =
103 CreateNamedPipeW (_pipe_name,
104 PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
105 PIPE_TYPE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
106 0, 0, 1000, &sec_all_nih);
107 if (listen_pipe != INVALID_HANDLE_VALUE)
108 {
109 connect_pipe = CreateFileW (_pipe_name, GENERIC_READ | GENERIC_WRITE, 0,
110 &sec_all_nih, OPEN_EXISTING, 0, NULL);
111 if (connect_pipe == INVALID_HANDLE_VALUE)
112 {
113 CloseHandle (listen_pipe);
114 listen_pipe = INVALID_HANDLE_VALUE;
115 }
116 }
117
118 if (listen_pipe == INVALID_HANDLE_VALUE)
119 {
120 system_printf ("failed to create named pipe: "
121 "is the daemon already running?");
122 return -1;
123 }
124
125 return 0;
126 }
127
128 class transport_layer_pipes *
129 transport_layer_pipes::accept (bool *const recoverable)
130 {
131 assert (!_hPipe);
132 assert (!_is_accepted_endpoint);
133 assert (_is_listening_endpoint);
134
135 debug ("Try to create named pipe instance: %ls", _pipe_name);
136
137 const HANDLE accept_pipe =
138 CreateNamedPipeW (_pipe_name, PIPE_ACCESS_DUPLEX,
139 PIPE_TYPE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
140 0, 0, 1000, &sec_all_nih);
141
142 if (accept_pipe == INVALID_HANDLE_VALUE)
143 {
144 debug_printf ("error creating pipe (%u).", GetLastError ());
145 *recoverable = true; // FIXME: case analysis?
146 return NULL;
147 }
148
149 if (!ConnectNamedPipe (accept_pipe, NULL)
150 && GetLastError () != ERROR_PIPE_CONNECTED)
151 {
152 debug_printf ("error connecting to pipe (%u)", GetLastError ());
153 (void) CloseHandle (accept_pipe);
154 *recoverable = true; // FIXME: case analysis?
155 return NULL;
156 }
157
158 return new transport_layer_pipes (accept_pipe);
159 }
160
161 #endif /* !__INSIDE_CYGWIN__ */
162
163 void
164 transport_layer_pipes::close ()
165 {
166 // verbose: debug_printf ("closing pipe %p", _hPipe);
167
168 if (_hPipe)
169 {
170 assert (_hPipe != INVALID_HANDLE_VALUE);
171
172 #ifndef __INSIDE_CYGWIN__
173
174 if (_is_accepted_endpoint)
175 {
176 (void) FlushFileBuffers (_hPipe); // Blocks until client reads.
177 (void) DisconnectNamedPipe (_hPipe);
178 (void) CloseHandle (_hPipe);
179 }
180 else
181 (void) CloseHandle (_hPipe);
182
183 #else /* __INSIDE_CYGWIN__ */
184
185 assert (!_is_accepted_endpoint);
186 (void) ForceCloseHandle (_hPipe);
187
188 #endif /* __INSIDE_CYGWIN__ */
189
190 _hPipe = NULL;
191 }
192 }
193
194 ssize_t
195 transport_layer_pipes::read (void *const buf, const size_t len)
196 {
197 // verbose: debug_printf ("reading from pipe %p", _hPipe);
198
199 assert (_hPipe);
200 assert (_hPipe != INVALID_HANDLE_VALUE);
201 assert (!_is_listening_endpoint);
202
203 DWORD count;
204 if (!ReadFile (_hPipe, buf, len, &count, NULL))
205 {
206 debug_printf ("error reading from pipe (%u)", GetLastError ());
207 SET_ERRNO (EINVAL); // FIXME?
208 return -1;
209 }
210
211 return count;
212 }
213
214 ssize_t
215 transport_layer_pipes::write (void *const buf, const size_t len)
216 {
217 // verbose: debug_printf ("writing to pipe %p", _hPipe);
218
219 assert (_hPipe);
220 assert (_hPipe != INVALID_HANDLE_VALUE);
221 assert (!_is_listening_endpoint);
222
223 DWORD count;
224 if (!WriteFile (_hPipe, buf, len, &count, NULL))
225 {
226 debug_printf ("error writing to pipe, error = %u", GetLastError ());
227 SET_ERRNO (EINVAL); // FIXME?
228 return -1;
229 }
230
231 return count;
232 }
233
234 /*
235 * This routine holds a static variable, assume_cygserver, that is set
236 * if the transport has good reason to think that cygserver is
237 * running, i.e. if if successfully connected to it with the previous
238 * attempt. If this is set, the code tries a lot harder to get a
239 * connection, making the assumption that any failures are just
240 * congestion and overloading problems.
241 */
242
243 int
244 transport_layer_pipes::connect ()
245 {
246 assert (!_hPipe);
247 assert (!_is_accepted_endpoint);
248 assert (!_is_listening_endpoint);
249
250 static bool assume_cygserver = false;
251
252 BOOL rc = TRUE;
253 int retries = 0;
254
255 debug_printf ("Try to connect to named pipe: %W", _pipe_name);
256 while (rc)
257 {
258 _hPipe = CreateFileW (_pipe_name,
259 GENERIC_READ | GENERIC_WRITE,
260 FILE_SHARE_READ | FILE_SHARE_WRITE,
261 &sec_all_nih,
262 OPEN_EXISTING,
263 SECURITY_IMPERSONATION,
264 NULL);
265
266 if (_hPipe != INVALID_HANDLE_VALUE)
267 {
268 assert (_hPipe);
269 #ifdef __INSIDE_CYGWIN__
270 ProtectHandle (_hPipe);
271 #endif
272 assume_cygserver = true;
273 return 0;
274 }
275
276 _hPipe = NULL;
277
278 if (!assume_cygserver && GetLastError () != ERROR_PIPE_BUSY)
279 {
280 debug_printf ("Error opening the pipe (%u)", GetLastError ());
281 return -1;
282 }
283
284 /* Note: `If no instances of the specified named pipe exist, the
285 * WaitNamedPipe function returns immediately, regardless of the
286 * time-out value.' Thus the explicit Sleep if the call fails
287 * with ERROR_FILE_NOT_FOUND.
288 */
289 while (retries != MAX_WAIT_NAMED_PIPE_RETRY
290 && !(rc = WaitNamedPipeW (_pipe_name, WAIT_NAMED_PIPE_TIMEOUT)))
291 {
292 if (GetLastError () == ERROR_FILE_NOT_FOUND)
293 Sleep (0); // Give the server a chance.
294
295 retries += 1;
296 }
297 }
298
299 assert (retries == MAX_WAIT_NAMED_PIPE_RETRY);
300
301 system_printf ("lost connection to cygserver, error = %u",
302 GetLastError ());
303
304 assume_cygserver = false;
305
306 return -1;
307 }
308
309 #ifndef __INSIDE_CYGWIN__
310
311 bool
312 transport_layer_pipes::impersonate_client ()
313 {
314 assert (_hPipe);
315 assert (_hPipe != INVALID_HANDLE_VALUE);
316 assert (_is_accepted_endpoint);
317
318 if (_hPipe && !ImpersonateNamedPipeClient (_hPipe))
319 {
320 debug_printf ("Failed to Impersonate client, (%u)", GetLastError ());
321 return false;
322 }
323
324 return true;
325 }
326
327 bool
328 transport_layer_pipes::revert_to_self ()
329 {
330 assert (_is_accepted_endpoint);
331
332 if (!RevertToSelf ())
333 {
334 debug_printf ("Failed to RevertToSelf, (%u)", GetLastError ());
335 return false;
336 }
337 return true;
338 }
339
340 #endif /* !__INSIDE_CYGWIN__ */
This page took 0.050786 seconds and 5 git commands to generate.