OSDN Git Service

* Makefile.in: Add -lntdll to link line.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygserver / transport_pipes.cc
1 /* transport_pipes.cc
2
3    Copyright 2001, 2002, 2003, 2004 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 #else
17 #include "winsup.h"
18 #endif
19
20 #include <sys/types.h>
21
22 #include <assert.h>
23 #include <netdb.h>
24 #include <pthread.h>
25 #include <unistd.h>
26
27 #include "cygerrno.h"
28 #include "transport.h"
29 #include "transport_pipes.h"
30
31 #ifndef __INSIDE_CYGWIN__
32 #include "cygserver.h"
33 #include "cygserver_ipc.h"
34 #else
35 #include "security.h"
36 #endif
37
38 #ifdef __INSIDE_CYGWIN__
39 #define SET_ERRNO(err)  set_errno (err)
40 #else
41 #define SET_ERRNO(err)  errno = (err)
42 #endif
43
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__
66
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)
72 {
73   assert (_hPipe);
74   assert (_hPipe != INVALID_HANDLE_VALUE);
75
76 }
77
78 #endif /* !__INSIDE_CYGWIN__ */
79
80 transport_layer_pipes::transport_layer_pipes ()
81   : _pipe_name ("\\\\.\\pipe\\cygwin_lpc"),
82     _hPipe (NULL),
83     _is_accepted_endpoint (false),
84     _is_listening_endpoint (false)
85 {
86 }
87
88 transport_layer_pipes::~transport_layer_pipes ()
89 {
90   close ();
91 }
92
93 #ifndef __INSIDE_CYGWIN__
94
95 int
96 transport_layer_pipes::listen ()
97 {
98   assert (!_hPipe);
99   assert (!_is_accepted_endpoint);
100   assert (!_is_listening_endpoint);
101
102   _is_listening_endpoint = true;
103
104   /* no-op */
105   return 0;
106 }
107
108 class transport_layer_pipes *
109 transport_layer_pipes::accept (bool *const recoverable)
110 {
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,
133                      &sec_all_nih);
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)
145     {
146       *recoverable = false;
147       system_printf ("failed to create named pipe: "
148                      "is the daemon already running?");
149       return NULL;
150     }
151
152   if (accept_pipe == INVALID_HANDLE_VALUE)
153     {
154       debug_printf ("error creating pipe (%lu).", GetLastError ());
155       *recoverable = true;      // FIXME: case analysis?
156       return NULL;
157     }
158
159   assert (accept_pipe);
160
161   if (!ConnectNamedPipe (accept_pipe, NULL)
162       && GetLastError () != ERROR_PIPE_CONNECTED)
163     {
164       debug_printf ("error connecting to pipe (%lu)", GetLastError ());
165       (void) CloseHandle (accept_pipe);
166       *recoverable = true;      // FIXME: case analysis?
167       return NULL;
168     }
169
170   return new transport_layer_pipes (accept_pipe);
171 }
172
173 #endif /* !__INSIDE_CYGWIN__ */
174
175 void
176 transport_layer_pipes::close ()
177 {
178   // verbose: debug_printf ("closing pipe %p", _hPipe);
179
180   if (_hPipe)
181     {
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;
207     }
208 }
209
210 ssize_t
211 transport_layer_pipes::read (void *const buf, const size_t len)
212 {
213   // verbose: debug_printf ("reading from pipe %p", _hPipe);
214
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))
221     {
222       debug_printf ("error reading from pipe (%lu)", GetLastError ());
223       SET_ERRNO (EINVAL);       // FIXME?
224       return -1;
225     }
226
227   return count;
228 }
229
230 ssize_t
231 transport_layer_pipes::write (void *const buf, const size_t len)
232 {
233   // verbose: debug_printf ("writing to pipe %p", _hPipe);
234
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))
241     {
242       debug_printf ("error writing to pipe, error = %lu", GetLastError ());
243       SET_ERRNO (EINVAL);       // FIXME?
244       return -1;
245     }
246
247   return count;
248 }
249
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
260 transport_layer_pipes::connect ()
261 {
262   assert (!_hPipe);
263   assert (!_is_accepted_endpoint);
264   assert (!_is_listening_endpoint);
265
266   static bool assume_cygserver = false;
267
268   BOOL rc = TRUE;
269   int retries = 0;
270
271   while (rc)
272     {
273       _hPipe = CreateFile (_pipe_name,
274                            GENERIC_READ | GENERIC_WRITE,
275                            FILE_SHARE_READ | FILE_SHARE_WRITE,
276                            &sec_all_nih,
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)
294         {
295           debug_printf ("Error opening the pipe (%lu)", GetLastError ());
296           return -1;
297         }
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.
303        */
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         }
312     }
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;
322 }
323
324 #ifndef __INSIDE_CYGWIN__
325
326 bool
327 transport_layer_pipes::impersonate_client ()
328 {
329   assert (_hPipe);
330   assert (_hPipe != INVALID_HANDLE_VALUE);
331   assert (_is_accepted_endpoint);
332
333   if (_hPipe && !ImpersonateNamedPipeClient (_hPipe))
334     {
335       debug_printf ("Failed to Impersonate client, (%lu)", GetLastError ());
336       return false;
337     }
338
339   return true;
340 }
341
342 bool
343 transport_layer_pipes::revert_to_self ()
344 {
345   assert (_is_accepted_endpoint);
346
347   if (!RevertToSelf ())
348     {
349       debug_printf ("Failed to RevertToSelf, (%lu)", GetLastError ());
350       return false;
351     }
352   return true;
353 }
354
355 #endif /* !__INSIDE_CYGWIN__ */