]>
Commit | Line | Data |
---|---|---|
282113ba | 1 | /* client.cc |
f449bfef | 2 | |
1dcd520b | 3 | Copyright 2001, 2002, 2003, 2004 Red Hat Inc. |
f449bfef RC |
4 | |
5 | Written by Egor Duda <deo@logos-m.ru> | |
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. */ | |
f449bfef | 12 | |
1c001dd2 | 13 | /* to allow this to link into cygwin and the .dll, a little magic is needed. */ |
f449bfef | 14 | #ifdef __OUTSIDE_CYGWIN__ |
1c001dd2 | 15 | #include "woutsup.h" |
f449bfef RC |
16 | #else |
17 | #include "winsup.h" | |
18 | #endif | |
19 | ||
1c001dd2 | 20 | #include <assert.h> |
1dcd520b | 21 | #include <errno.h> |
1c001dd2 | 22 | #include <stdio.h> |
f449bfef | 23 | #include <unistd.h> |
1c001dd2 | 24 | |
373a036f CV |
25 | #include "sigproc.h" |
26 | ||
282113ba CV |
27 | #include "cygserver_msg.h" |
28 | #include "cygserver_sem.h" | |
1c001dd2 | 29 | #include "cygserver_shm.h" |
1c001dd2 | 30 | |
56797078 | 31 | #include "cygserver.h" |
282113ba | 32 | #include "transport.h" |
1c001dd2 CS |
33 | |
34 | int cygserver_running = CYGSERVER_UNKNOWN; // Nb: inherited by children. | |
f449bfef | 35 | |
29c1c508 | 36 | bool allow_server = false; // Nb: inherited by children. |
f449bfef | 37 | |
1c001dd2 CS |
38 | client_request_get_version::client_request_get_version () |
39 | : client_request (CYGSERVER_REQUEST_GET_VERSION, &version, sizeof (version)) | |
f449bfef | 40 | { |
1c001dd2 CS |
41 | msglen (0); // No parameters for request. |
42 | ||
43 | // verbose: syscall_printf ("created"); | |
f449bfef RC |
44 | } |
45 | ||
1c001dd2 CS |
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 | ||
282113ba CV |
54 | #ifdef __INSIDE_CYGWIN__ |
55 | ||
1c001dd2 CS |
56 | bool |
57 | client_request_get_version::check_version () const | |
f449bfef | 58 | { |
1c001dd2 CS |
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; | |
f449bfef RC |
77 | } |
78 | ||
1c001dd2 CS |
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)) | |
f449bfef | 83 | { |
1c001dd2 | 84 | req.pid = GetCurrentProcessId (); |
f449bfef RC |
85 | req.master_pid = nmaster_pid; |
86 | req.from_master = nfrom_master; | |
87 | req.to_master = nto_master; | |
f449bfef | 88 | |
1c001dd2 CS |
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); | |
f449bfef | 92 | } |
1c001dd2 CS |
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) | |
f449bfef | 107 | { |
1c001dd2 CS |
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 | } | |
f449bfef RC |
118 | } |
119 | ||
1c001dd2 CS |
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) | |
f449bfef | 124 | { |
1c001dd2 | 125 | assert (request_code >= 0 && request_code < CYGSERVER_REQUEST_LAST); |
f449bfef RC |
126 | } |
127 | ||
1c001dd2 CS |
128 | // FIXME: also check write and read result for -1. |
129 | ||
f449bfef | 130 | void |
1c001dd2 | 131 | client_request::send (transport_layer_base * const conn) |
f449bfef | 132 | { |
1c001dd2 CS |
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 ()) | |
f449bfef | 154 | { |
1c001dd2 CS |
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); | |
f449bfef RC |
203 | return; |
204 | } | |
205 | ||
1c001dd2 CS |
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 | ||
282113ba CV |
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 | } | |
1c001dd2 CS |
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 | } | |
f449bfef | 273 | |
1c001dd2 CS |
274 | client_request *req = NULL; |
275 | ||
276 | switch (header.request_code) | |
f449bfef | 277 | { |
1c001dd2 | 278 | case CYGSERVER_REQUEST_GET_VERSION: |
282113ba | 279 | req = new client_request_get_version; |
1c001dd2 CS |
280 | break; |
281 | case CYGSERVER_REQUEST_SHUTDOWN: | |
282113ba | 282 | req = new client_request_shutdown; |
1c001dd2 CS |
283 | break; |
284 | case CYGSERVER_REQUEST_ATTACH_TTY: | |
282113ba CV |
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; | |
1c001dd2 CS |
292 | break; |
293 | case CYGSERVER_REQUEST_SHM: | |
282113ba | 294 | req = new client_request_shm; |
1c001dd2 CS |
295 | break; |
296 | default: | |
297 | syscall_printf ("unknown request code %d received: request ignored", | |
298 | header.request_code); | |
f449bfef RC |
299 | return; |
300 | } | |
1c001dd2 CS |
301 | |
302 | assert (req); | |
303 | ||
304 | req->msglen (header.msglen); | |
305 | req->handle (conn, cache); | |
306 | ||
282113ba | 307 | delete req; |
f449bfef RC |
308 | } |
309 | ||
1c001dd2 CS |
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 | ||
282113ba CV |
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 | } | |
1c001dd2 CS |
491 | |
492 | bool | |
f449bfef RC |
493 | check_cygserver_available () |
494 | { | |
1c001dd2 CS |
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 (); | |
f449bfef | 521 | } |
f449bfef RC |
522 | |
523 | void | |
524 | cygserver_init () | |
525 | { | |
29c1c508 | 526 | if (!allow_server) |
f449bfef | 527 | { |
1c001dd2 CS |
528 | syscall_printf ("cygserver use disabled in client"); |
529 | cygserver_running = CYGSERVER_UNAVAIL; | |
f449bfef RC |
530 | return; |
531 | } | |
532 | ||
1c001dd2 CS |
533 | assert (cygserver_running == CYGSERVER_UNKNOWN \ |
534 | || cygserver_running == CYGSERVER_OK \ | |
535 | || cygserver_running == CYGSERVER_UNAVAIL); | |
536 | ||
537 | if (cygserver_running == CYGSERVER_OK) | |
f449bfef RC |
538 | return; |
539 | ||
1c001dd2 CS |
540 | if (!check_cygserver_available ()) |
541 | cygserver_running = CYGSERVER_UNAVAIL; | |
f449bfef | 542 | } |
282113ba | 543 | #endif /* __INSIDE_CYGWIN__ */ |