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