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