]>
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 | |
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" |
d4db08d7 | 30 | #include "cygserver_setpwd.h" |
1c001dd2 | 31 | |
56797078 | 32 | #include "cygserver.h" |
282113ba | 33 | #include "transport.h" |
1c001dd2 CS |
34 | |
35 | int cygserver_running = CYGSERVER_UNKNOWN; // Nb: inherited by children. | |
f449bfef | 36 | |
1c001dd2 CS |
37 | client_request_get_version::client_request_get_version () |
38 | : client_request (CYGSERVER_REQUEST_GET_VERSION, &version, sizeof (version)) | |
f449bfef | 39 | { |
1c001dd2 CS |
40 | msglen (0); // No parameters for request. |
41 | ||
42 | // verbose: syscall_printf ("created"); | |
f449bfef RC |
43 | } |
44 | ||
1c001dd2 CS |
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 | ||
282113ba CV |
53 | #ifdef __INSIDE_CYGWIN__ |
54 | ||
1c001dd2 CS |
55 | bool |
56 | client_request_get_version::check_version () const | |
f449bfef | 57 | { |
1c001dd2 CS |
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; | |
f449bfef RC |
76 | } |
77 | ||
1c001dd2 CS |
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)) | |
f449bfef | 82 | { |
1c001dd2 | 83 | req.pid = GetCurrentProcessId (); |
f449bfef RC |
84 | req.master_pid = nmaster_pid; |
85 | req.from_master = nfrom_master; | |
86 | req.to_master = nto_master; | |
f449bfef | 87 | |
1c001dd2 CS |
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); | |
f449bfef | 91 | } |
1c001dd2 CS |
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) | |
f449bfef | 106 | { |
1c001dd2 CS |
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 | } | |
f449bfef RC |
117 | } |
118 | ||
1c001dd2 CS |
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) | |
f449bfef | 123 | { |
1c001dd2 | 124 | assert (request_code >= 0 && request_code < CYGSERVER_REQUEST_LAST); |
f449bfef RC |
125 | } |
126 | ||
1c001dd2 CS |
127 | // FIXME: also check write and read result for -1. |
128 | ||
f449bfef | 129 | void |
1c001dd2 | 130 | client_request::send (transport_layer_base * const conn) |
f449bfef | 131 | { |
1c001dd2 CS |
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 ()) | |
f449bfef | 153 | { |
1c001dd2 CS |
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); | |
f449bfef RC |
202 | return; |
203 | } | |
204 | ||
1c001dd2 CS |
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 | ||
282113ba CV |
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 | } | |
1c001dd2 CS |
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 | } | |
f449bfef | 272 | |
1c001dd2 CS |
273 | client_request *req = NULL; |
274 | ||
275 | switch (header.request_code) | |
f449bfef | 276 | { |
1c001dd2 | 277 | case CYGSERVER_REQUEST_GET_VERSION: |
282113ba | 278 | req = new client_request_get_version; |
1c001dd2 CS |
279 | break; |
280 | case CYGSERVER_REQUEST_SHUTDOWN: | |
282113ba | 281 | req = new client_request_shutdown; |
1c001dd2 CS |
282 | break; |
283 | case CYGSERVER_REQUEST_ATTACH_TTY: | |
282113ba CV |
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; | |
1c001dd2 CS |
291 | break; |
292 | case CYGSERVER_REQUEST_SHM: | |
282113ba | 293 | req = new client_request_shm; |
1c001dd2 | 294 | break; |
d4db08d7 CV |
295 | case CYGSERVER_REQUEST_SETPWD: |
296 | req = new client_request_setpwd; | |
297 | break; | |
1c001dd2 CS |
298 | default: |
299 | syscall_printf ("unknown request code %d received: request ignored", | |
300 | header.request_code); | |
f449bfef RC |
301 | return; |
302 | } | |
1c001dd2 CS |
303 | |
304 | assert (req); | |
305 | ||
306 | req->msglen (header.msglen); | |
307 | req->handle (conn, cache); | |
308 | ||
282113ba | 309 | delete req; |
f449bfef RC |
310 | } |
311 | ||
1c001dd2 CS |
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 | ||
282113ba CV |
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 | } | |
1c001dd2 CS |
493 | |
494 | bool | |
f449bfef RC |
495 | check_cygserver_available () |
496 | { | |
1c001dd2 CS |
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 (); | |
f449bfef | 523 | } |
f449bfef RC |
524 | |
525 | void | |
526 | cygserver_init () | |
527 | { | |
1c001dd2 CS |
528 | assert (cygserver_running == CYGSERVER_UNKNOWN \ |
529 | || cygserver_running == CYGSERVER_OK \ | |
530 | || cygserver_running == CYGSERVER_UNAVAIL); | |
531 | ||
532 | if (cygserver_running == CYGSERVER_OK) | |
f449bfef RC |
533 | return; |
534 | ||
1c001dd2 CS |
535 | if (!check_cygserver_available ()) |
536 | cygserver_running = CYGSERVER_UNAVAIL; | |
f449bfef | 537 | } |
282113ba | 538 | #endif /* __INSIDE_CYGWIN__ */ |