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