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