OSDN Git Service

* transport_pipes.cc: Include ntdef.h to accommodate cygerrno.h.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygserver / client.cc
1 /* client.cc
2
3    Copyright 2001, 2002, 2003, 2004, 2008, 2009 Red Hat Inc.
4
5    Written by Egor Duda <deo@logos-m.ru>
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 <assert.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <unistd.h>
24
25 #include "sigproc.h"
26
27 #include "cygserver_msg.h"
28 #include "cygserver_sem.h"
29 #include "cygserver_shm.h"
30 #include "cygserver_setpwd.h"
31
32 #include "cygserver.h"
33 #include "transport.h"
34
35 int cygserver_running = CYGSERVER_UNKNOWN; // Nb: inherited by children.
36
37 client_request_get_version::client_request_get_version ()
38   : client_request (CYGSERVER_REQUEST_GET_VERSION, &version, sizeof (version))
39 {
40   msglen (0);                   // No parameters for request.
41
42   // verbose: syscall_printf ("created");
43 }
44
45 /*
46  * client_request_get_version::check_version ()
47  *
48  * The major version and API version numbers must match exactly.  An
49  * older than expected minor version number is accepted (as long as
50  * the first numbers match, that is).
51  */
52
53 #ifdef __INSIDE_CYGWIN__
54
55 bool
56 client_request_get_version::check_version () const
57 {
58   const bool ok = (version.major == CYGWIN_SERVER_VERSION_MAJOR
59                    && version.api == CYGWIN_SERVER_VERSION_API
60                    && version.minor <= CYGWIN_SERVER_VERSION_MINOR);
61
62   if (!ok)
63     syscall_printf (("incompatible version of cygwin server: "
64                      "client version %d.%d.%d.%d, "
65                      "server version %ld.%ld.%ld.%ld"),
66                     CYGWIN_SERVER_VERSION_MAJOR,
67                     CYGWIN_SERVER_VERSION_API,
68                     CYGWIN_SERVER_VERSION_MINOR,
69                     CYGWIN_SERVER_VERSION_PATCH,
70                     version.major,
71                     version.api,
72                     version.minor,
73                     version.patch);
74
75   return ok;
76 }
77
78 client_request_attach_tty::client_request_attach_tty (DWORD nmaster_pid,
79                                                       HANDLE nfrom_master,
80                                                       HANDLE nto_master)
81   : client_request (CYGSERVER_REQUEST_ATTACH_TTY, &req, sizeof (req))
82 {
83   req.pid = GetCurrentProcessId ();
84   req.master_pid = nmaster_pid;
85   req.from_master = nfrom_master;
86   req.to_master = nto_master;
87
88   syscall_printf (("created: pid = %lu, master_pid = %lu, "
89                    "from_master = %lu, to_master = %lu"),
90                   req.pid, req.master_pid, req.from_master, req.to_master);
91 }
92 #endif /* __INSIDE_CYGWIN__ */
93
94 /*
95  * client_request_attach_tty::send ()
96  *
97  * Wraps the base method to provide error handling support.  If the
98  * reply contains a body but is flagged as an error, close any handles
99  * that have been returned by cygserver and then discard the message
100  * body, i.e. the client either sees a successful result with handles
101  * or an unsuccessful result with no handles.
102  */
103
104 void
105 client_request_attach_tty::send (transport_layer_base * const conn)
106 {
107   client_request::send (conn);
108
109   if (msglen () && error_code ())
110     {
111       if (from_master ())
112         CloseHandle (from_master ());
113       if (to_master ())
114         CloseHandle (to_master ());
115       msglen (0);
116     }
117 }
118
119 client_request::header_t::header_t (const request_code_t request_code,
120                                     const size_t msglen)
121   : msglen (msglen),
122     request_code (request_code)
123 {
124   assert (request_code >= 0 && request_code < CYGSERVER_REQUEST_LAST);
125 }
126
127 // FIXME: also check write and read result for -1.
128
129 void
130 client_request::send (transport_layer_base * const conn)
131 {
132   assert (conn);
133   assert (!(msglen () && !_buf)); // i.e., msglen () implies _buf
134   assert (msglen () <= _buflen);
135
136   {
137     const ssize_t count = conn->write (&_header, sizeof (_header));
138
139     if (count != sizeof (_header))
140       {
141         assert (errno);
142         error_code (errno);
143         syscall_printf (("request header write failure: "
144                          "only %ld bytes sent of %ld, "
145                          "error = %d(%lu)"),
146                         count, sizeof (_header),
147                         errno, GetLastError ());
148         return;
149       }
150   }
151
152   if (msglen ())
153     {
154       const ssize_t count = conn->write (_buf, msglen ());
155
156       if (count == -1 || (size_t) count != msglen ())
157         {
158           assert (errno);
159           error_code (errno);
160           syscall_printf (("request body write failure: "
161                            "only %ld bytes sent of %ld, "
162                            "error = %d(%lu)"),
163                           count, msglen (),
164                           errno, GetLastError ());
165           return;
166         }
167     }
168
169   // verbose: syscall_printf ("request sent (%ld + %ld bytes)",
170   //                          sizeof (_header), msglen ());
171
172   {
173     const ssize_t count = conn->read (&_header, sizeof (_header));
174
175     if (count != sizeof (_header))
176       {
177         assert (errno);
178         error_code (errno);
179         syscall_printf (("reply header read failure: "
180                          "only %ld bytes received of %ld, "
181                          "error = %d(%lu)"),
182                         count, sizeof (_header),
183                         errno, GetLastError ());
184         return;
185       }
186   }
187
188   if (msglen () && !_buf)
189     {
190       system_printf ("no client buffer for reply body: %ld bytes needed",
191                      msglen ());
192       error_code (EINVAL);
193       return;
194     }
195
196   if (msglen () > _buflen)
197     {
198       system_printf (("client buffer too small for reply body: "
199                       "have %ld bytes and need %ld"),
200                      _buflen, msglen ());
201       error_code (EINVAL);
202       return;
203     }
204
205   if (msglen ())
206     {
207       const ssize_t count = conn->read (_buf, msglen ());
208
209       if (count == -1 || (size_t) count != msglen ())
210         {
211           assert (errno);
212           error_code (errno);
213           syscall_printf (("reply body read failure: "
214                            "only %ld bytes received of %ld, "
215                            "error = %d(%lu)"),
216                           count, msglen (),
217                           errno, GetLastError ());
218           return;
219         }
220     }
221
222   // verbose: syscall_printf ("reply received (%ld + %ld bytes)",
223   //                          sizeof (_header), msglen ());
224 }
225
226 #ifdef __OUTSIDE_CYGWIN__
227
228 client_request_attach_tty::client_request_attach_tty ()
229   : client_request (CYGSERVER_REQUEST_ATTACH_TTY, &req, sizeof (req))
230 {
231 }
232
233 /*
234  * client_request::handle_request ()
235  *
236  * A server-side method.
237  *
238  * This is a factory method for the client_request subclasses.  It
239  * reads the incoming request header and, based on its request code,
240  * creates an instance of the appropriate class.
241  *
242  * FIXME: If the incoming packet is malformed, the server drops it on
243  * the floor.  Should it try and generate some sort of reply for the
244  * client?  As it is, the client will simply get a broken connection.
245  *
246  * FIXME: also check write and read result for -1.
247  */
248
249 /* static */ void
250 client_request::handle_request (transport_layer_base *const conn,
251                                 process_cache *const cache)
252 {
253   // verbose: debug_printf ("about to read");
254
255   header_t header;
256
257   {
258     const ssize_t count = conn->read (&header, sizeof (header));
259
260     if (count != sizeof (header))
261       {
262         syscall_printf (("request header read failure: "
263                          "only %ld bytes received of %ld, "
264                          "error = %d(%lu)"),
265                         count, sizeof (header),
266                         errno, GetLastError ());
267         return;
268       }
269
270       // verbose: debug_printf ("got header (%ld)", count);
271   }
272
273   client_request *req = NULL;
274
275   switch (header.request_code)
276     {
277     case CYGSERVER_REQUEST_GET_VERSION:
278       req = new client_request_get_version;
279       break;
280     case CYGSERVER_REQUEST_SHUTDOWN:
281       req = new client_request_shutdown;
282       break;
283     case CYGSERVER_REQUEST_ATTACH_TTY:
284       req = new client_request_attach_tty;
285       break;
286     case CYGSERVER_REQUEST_MSG:
287       req = new client_request_msg;
288       break;
289     case CYGSERVER_REQUEST_SEM:
290       req = new client_request_sem;
291       break;
292     case CYGSERVER_REQUEST_SHM:
293       req = new client_request_shm;
294       break;
295     case CYGSERVER_REQUEST_SETPWD:
296       req = new client_request_setpwd;
297       break;
298     default:
299       syscall_printf ("unknown request code %d received: request ignored",
300                       header.request_code);
301       return;
302     }
303
304   assert (req);
305
306   req->msglen (header.msglen);
307   req->handle (conn, cache);
308
309   delete req;
310 }
311
312 /*
313  * client_request::handle ()
314  *
315  * A server-side method.
316  *
317  * At this point, the header of an incoming request has been read and
318  * an appropriate client_request object constructed.  This method has
319  * to read the request body into its buffer, if there is such a body,
320  * then perform the request and send back the results to the client.
321  *
322  * FIXME: If the incoming packet is malformed, the server drops it on
323  * the floor.  Should it try and generate some sort of reply for the
324  * client?  As it is, the client will simply get a broken connection.
325  *
326  * FIXME: also check write and read result for -1.
327  */
328
329 void
330 client_request::handle (transport_layer_base *const conn,
331                         process_cache *const cache)
332 {
333   if (msglen () && !_buf)
334     {
335       system_printf ("no buffer for request body: %ld bytes needed",
336                      msglen ());
337       error_code (EINVAL);
338       return;
339     }
340
341   if (msglen () > _buflen)
342     {
343       system_printf (("buffer too small for request body: "
344                       "have %ld bytes and need %ld"),
345                      _buflen, msglen ());
346       error_code (EINVAL);
347       return;
348     }
349
350   if (msglen ())
351     {
352       const ssize_t count = conn->read (_buf, msglen ());
353
354       if (count == -1 || (size_t) count != msglen ())
355         {
356           assert (errno);
357           error_code (errno);
358           syscall_printf (("request body read failure: "
359                            "only %ld bytes received of %ld, "
360                            "error = %d(%lu)"),
361                           count, msglen (),
362                           errno, GetLastError ());
363           return;
364         }
365     }
366
367   // verbose: syscall_printf ("request received (%ld + %ld bytes)",
368   //                          sizeof (_header), msglen ());
369
370   error_code (0);               // Overwrites the _header.request_code field.
371
372   /*
373    * This is not allowed to fail. We must return ENOSYS at a minimum
374    * to the client.
375    */
376   serve (conn, cache);
377
378   {
379     const ssize_t count = conn->write (&_header, sizeof (_header));
380
381     if (count != sizeof (_header))
382       {
383         assert (errno);
384         error_code (errno);
385         syscall_printf (("reply header write failure: "
386                          "only %ld bytes sent of %ld, "
387                          "error = %d(%lu)"),
388                         count, sizeof (_header),
389                         errno, GetLastError ());
390         return;
391       }
392   }
393
394   if (msglen ())
395     {
396       const ssize_t count = conn->write (_buf, msglen ());
397
398       if (count == -1 || (size_t) count != msglen ())
399         {
400           assert (errno);
401           error_code (errno);
402           syscall_printf (("reply body write failure: "
403                            "only %ld bytes sent of %ld, "
404                            "error = %d(%lu)"),
405                           count, msglen (),
406                           errno, GetLastError ());
407           return;
408         }
409     }
410
411   // verbose: syscall_printf ("reply sent (%ld + %ld bytes)",
412   //                          sizeof (_header), msglen ());
413 }
414
415 /* The server side implementation of make_request.  Very simple. */
416 int
417 client_request::make_request ()
418 {
419   transport_layer_base *const transport = create_server_transport ();
420   assert (transport);
421   if (transport->connect () == -1)
422     {
423       if (errno)
424         error_code (errno);
425       else
426         error_code (ENOSYS);
427       delete transport;
428       return -1;
429     }
430   send (transport);
431   delete transport;
432   return 0;
433 }
434 #endif /* __OUTSIDE_CYGWIN__ */
435
436 client_request::client_request (request_code_t const id,
437                                 void * const buf,
438                                 size_t const buflen)
439   : _header (id, buflen),
440     _buf (buf),
441     _buflen (buflen)
442 {
443   assert ((!_buf && !_buflen) || (_buf && _buflen));
444 }
445
446 client_request::~client_request ()
447 {}
448
449 #ifdef __INSIDE_CYGWIN__
450 int
451 client_request::make_request ()
452 {
453   assert (cygserver_running == CYGSERVER_UNKNOWN        \
454           || cygserver_running == CYGSERVER_OK          \
455           || cygserver_running == CYGSERVER_UNAVAIL);
456
457   if (cygserver_running == CYGSERVER_UNKNOWN)
458     cygserver_init ();
459
460   assert (cygserver_running == CYGSERVER_OK             \
461           || cygserver_running == CYGSERVER_UNAVAIL);
462
463   /* Don't retry every request if the server's not there */
464   if (cygserver_running == CYGSERVER_UNAVAIL)
465     {
466       syscall_printf ("cygserver un-available");
467       error_code (ENOSYS);
468       return -1;
469     }
470
471   transport_layer_base *const transport = create_server_transport ();
472
473   assert (transport);
474
475   if (transport->connect () == -1)
476     {
477       if (errno)
478         error_code (errno);
479       else
480         error_code (ENOSYS);
481       delete transport;
482       return -1;
483     }
484
485   // verbose: debug_printf ("connected to server %p", transport);
486
487   send (transport);
488
489   delete transport;
490
491   return 0;
492 }
493
494 bool
495 check_cygserver_available ()
496 {
497   assert (cygserver_running == CYGSERVER_UNKNOWN        \
498           || cygserver_running == CYGSERVER_UNAVAIL);
499
500   cygserver_running = CYGSERVER_OK; // For make_request ().
501
502   client_request_get_version req;
503
504   /* This indicates that we failed to connect to cygserver at all but
505    * that's fine as cygwin doesn't need it to be running.
506    */
507   if (req.make_request () == -1)
508     return false;
509
510   /* We connected to the server but something went wrong after that
511    * (in sending the message, in cygserver itself, or in receiving the
512    * reply).
513    */
514   if (req.error_code ())
515     {
516       syscall_printf ("failure in cygserver version request: %d",
517                       req.error_code ());
518       syscall_printf ("process will continue without cygserver support");
519       return false;
520     }
521
522   return req.check_version ();
523 }
524
525 void
526 cygserver_init ()
527 {
528   assert (cygserver_running == CYGSERVER_UNKNOWN        \
529           || cygserver_running == CYGSERVER_OK          \
530           || cygserver_running == CYGSERVER_UNAVAIL);
531
532   if (cygserver_running == CYGSERVER_OK)
533     return;
534
535   if (!check_cygserver_available ())
536     cygserver_running = CYGSERVER_UNAVAIL;
537 }
538 #endif /* __INSIDE_CYGWIN__ */