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