]>
Commit | Line | Data |
---|---|---|
282113ba | 1 | /* client.cc |
f449bfef | 2 | |
7ffaa17c | 3 | Copyright 2001, 2002, 2003, 2004, 2008, 2009 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 | |
282113ba CV |
25 | #include "cygserver_msg.h" |
26 | #include "cygserver_sem.h" | |
1c001dd2 | 27 | #include "cygserver_shm.h" |
d4db08d7 | 28 | #include "cygserver_setpwd.h" |
1c001dd2 | 29 | |
56797078 | 30 | #include "cygserver.h" |
282113ba | 31 | #include "transport.h" |
1c001dd2 CS |
32 | |
33 | int cygserver_running = CYGSERVER_UNKNOWN; // Nb: inherited by children. | |
f449bfef | 34 | |
1c001dd2 CS |
35 | client_request_get_version::client_request_get_version () |
36 | : client_request (CYGSERVER_REQUEST_GET_VERSION, &version, sizeof (version)) | |
f449bfef | 37 | { |
1c001dd2 CS |
38 | msglen (0); // No parameters for request. |
39 | ||
40 | // verbose: syscall_printf ("created"); | |
f449bfef RC |
41 | } |
42 | ||
1c001dd2 CS |
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 | ||
282113ba CV |
51 | #ifdef __INSIDE_CYGWIN__ |
52 | ||
1c001dd2 CS |
53 | bool |
54 | client_request_get_version::check_version () const | |
f449bfef | 55 | { |
1c001dd2 CS |
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 %ld.%ld.%ld.%ld"), | |
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; | |
f449bfef RC |
74 | } |
75 | ||
1c001dd2 CS |
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)) | |
f449bfef | 80 | { |
1c001dd2 | 81 | req.pid = GetCurrentProcessId (); |
f449bfef RC |
82 | req.master_pid = nmaster_pid; |
83 | req.from_master = nfrom_master; | |
84 | req.to_master = nto_master; | |
f449bfef | 85 | |
1c001dd2 CS |
86 | syscall_printf (("created: pid = %lu, master_pid = %lu, " |
87 | "from_master = %lu, to_master = %lu"), | |
88 | req.pid, req.master_pid, req.from_master, req.to_master); | |
f449bfef | 89 | } |
1c001dd2 CS |
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) | |
f449bfef | 104 | { |
1c001dd2 CS |
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 | } | |
f449bfef RC |
115 | } |
116 | ||
1c001dd2 CS |
117 | client_request::header_t::header_t (const request_code_t request_code, |
118 | const size_t msglen) | |
119 | : msglen (msglen), | |
120 | request_code (request_code) | |
f449bfef | 121 | { |
1c001dd2 | 122 | assert (request_code >= 0 && request_code < CYGSERVER_REQUEST_LAST); |
f449bfef RC |
123 | } |
124 | ||
1c001dd2 CS |
125 | // FIXME: also check write and read result for -1. |
126 | ||
f449bfef | 127 | void |
1c001dd2 | 128 | client_request::send (transport_layer_base * const conn) |
f449bfef | 129 | { |
1c001dd2 CS |
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 %ld bytes sent of %ld, " | |
143 | "error = %d(%lu)"), | |
144 | count, sizeof (_header), | |
145 | errno, GetLastError ()); | |
146 | return; | |
147 | } | |
148 | } | |
149 | ||
150 | if (msglen ()) | |
f449bfef | 151 | { |
1c001dd2 CS |
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 %ld bytes sent of %ld, " | |
160 | "error = %d(%lu)"), | |
161 | count, msglen (), | |
162 | errno, GetLastError ()); | |
163 | return; | |
164 | } | |
165 | } | |
166 | ||
167 | // verbose: syscall_printf ("request sent (%ld + %ld bytes)", | |
168 | // sizeof (_header), msglen ()); | |
169 | ||
170 | { | |
171 | const ssize_t count = conn->read (&_header, sizeof (_header)); | |
172 | ||
173 | if (count != sizeof (_header)) | |
174 | { | |
175 | assert (errno); | |
176 | error_code (errno); | |
177 | syscall_printf (("reply header read failure: " | |
178 | "only %ld bytes received of %ld, " | |
179 | "error = %d(%lu)"), | |
180 | count, sizeof (_header), | |
181 | errno, GetLastError ()); | |
182 | return; | |
183 | } | |
184 | } | |
185 | ||
186 | if (msglen () && !_buf) | |
187 | { | |
188 | system_printf ("no client buffer for reply body: %ld bytes needed", | |
189 | msglen ()); | |
190 | error_code (EINVAL); | |
191 | return; | |
192 | } | |
193 | ||
194 | if (msglen () > _buflen) | |
195 | { | |
196 | system_printf (("client buffer too small for reply body: " | |
197 | "have %ld bytes and need %ld"), | |
198 | _buflen, msglen ()); | |
199 | error_code (EINVAL); | |
f449bfef RC |
200 | return; |
201 | } | |
202 | ||
1c001dd2 CS |
203 | if (msglen ()) |
204 | { | |
205 | const ssize_t count = conn->read (_buf, msglen ()); | |
206 | ||
207 | if (count == -1 || (size_t) count != msglen ()) | |
208 | { | |
209 | assert (errno); | |
210 | error_code (errno); | |
211 | syscall_printf (("reply body read failure: " | |
212 | "only %ld bytes received of %ld, " | |
213 | "error = %d(%lu)"), | |
214 | count, msglen (), | |
215 | errno, GetLastError ()); | |
216 | return; | |
217 | } | |
218 | } | |
219 | ||
220 | // verbose: syscall_printf ("reply received (%ld + %ld bytes)", | |
221 | // sizeof (_header), msglen ()); | |
222 | } | |
223 | ||
282113ba CV |
224 | #ifdef __OUTSIDE_CYGWIN__ |
225 | ||
226 | client_request_attach_tty::client_request_attach_tty () | |
227 | : client_request (CYGSERVER_REQUEST_ATTACH_TTY, &req, sizeof (req)) | |
228 | { | |
229 | } | |
1c001dd2 CS |
230 | |
231 | /* | |
232 | * client_request::handle_request () | |
233 | * | |
234 | * A server-side method. | |
235 | * | |
236 | * This is a factory method for the client_request subclasses. It | |
237 | * reads the incoming request header and, based on its request code, | |
238 | * creates an instance of the appropriate class. | |
239 | * | |
240 | * FIXME: If the incoming packet is malformed, the server drops it on | |
241 | * the floor. Should it try and generate some sort of reply for the | |
242 | * client? As it is, the client will simply get a broken connection. | |
243 | * | |
244 | * FIXME: also check write and read result for -1. | |
245 | */ | |
246 | ||
247 | /* static */ void | |
248 | client_request::handle_request (transport_layer_base *const conn, | |
249 | process_cache *const cache) | |
250 | { | |
251 | // verbose: debug_printf ("about to read"); | |
252 | ||
253 | header_t header; | |
254 | ||
255 | { | |
256 | const ssize_t count = conn->read (&header, sizeof (header)); | |
257 | ||
258 | if (count != sizeof (header)) | |
259 | { | |
260 | syscall_printf (("request header read failure: " | |
261 | "only %ld bytes received of %ld, " | |
262 | "error = %d(%lu)"), | |
263 | count, sizeof (header), | |
264 | errno, GetLastError ()); | |
265 | return; | |
266 | } | |
267 | ||
268 | // verbose: debug_printf ("got header (%ld)", count); | |
269 | } | |
f449bfef | 270 | |
1c001dd2 CS |
271 | client_request *req = NULL; |
272 | ||
273 | switch (header.request_code) | |
f449bfef | 274 | { |
1c001dd2 | 275 | case CYGSERVER_REQUEST_GET_VERSION: |
282113ba | 276 | req = new client_request_get_version; |
1c001dd2 CS |
277 | break; |
278 | case CYGSERVER_REQUEST_SHUTDOWN: | |
282113ba | 279 | req = new client_request_shutdown; |
1c001dd2 CS |
280 | break; |
281 | case CYGSERVER_REQUEST_ATTACH_TTY: | |
282113ba CV |
282 | req = new client_request_attach_tty; |
283 | break; | |
284 | case CYGSERVER_REQUEST_MSG: | |
285 | req = new client_request_msg; | |
286 | break; | |
287 | case CYGSERVER_REQUEST_SEM: | |
288 | req = new client_request_sem; | |
1c001dd2 CS |
289 | break; |
290 | case CYGSERVER_REQUEST_SHM: | |
282113ba | 291 | req = new client_request_shm; |
1c001dd2 | 292 | break; |
d4db08d7 CV |
293 | case CYGSERVER_REQUEST_SETPWD: |
294 | req = new client_request_setpwd; | |
295 | break; | |
1c001dd2 CS |
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 | { | |
1c001dd2 CS |
526 | assert (cygserver_running == CYGSERVER_UNKNOWN \ |
527 | || cygserver_running == CYGSERVER_OK \ | |
528 | || cygserver_running == CYGSERVER_UNAVAIL); | |
529 | ||
530 | if (cygserver_running == CYGSERVER_OK) | |
f449bfef RC |
531 | return; |
532 | ||
1c001dd2 CS |
533 | if (!check_cygserver_available ()) |
534 | cygserver_running = CYGSERVER_UNAVAIL; | |
f449bfef | 535 | } |
282113ba | 536 | #endif /* __INSIDE_CYGWIN__ */ |