]> sourceware.org Git - newlib-cygwin.git/blob - winsup/cygserver/client.cc
Cygwin: set NTDDI_VERSION to enable more recent windows definitions
[newlib-cygwin.git] / winsup / cygserver / client.cc
1 /* client.cc
2
3 Written by Egor Duda <deo@logos-m.ru>
4
5 This file is part of Cygwin.
6
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. */
10
11 /* to allow this to link into cygwin and the .dll, a little magic is needed. */
12 #ifdef __OUTSIDE_CYGWIN__
13 #include "woutsup.h"
14 #else
15 #include "winsup.h"
16 #endif
17
18 #include <assert.h>
19 #include <errno.h>
20 #include <stdio.h>
21 #include <unistd.h>
22
23 #include "cygserver_msg.h"
24 #include "cygserver_sem.h"
25 #include "cygserver_shm.h"
26 #include "cygserver_setpwd.h"
27 #include "cygserver_pwdgrp.h"
28
29 #include "cygserver.h"
30 #include "transport.h"
31
32 int cygserver_running = CYGSERVER_UNKNOWN; // Nb: inherited by children.
33
34 client_request_get_version::client_request_get_version ()
35 : client_request (CYGSERVER_REQUEST_GET_VERSION, &version, sizeof (version))
36 {
37 msglen (0); // No parameters for request.
38
39 // verbose: syscall_printf ("created");
40 }
41
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
50 #ifdef __INSIDE_CYGWIN__
51
52 bool
53 client_request_get_version::check_version () const
54 {
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, "
62 "server version %d.%d.%d.%d"),
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;
73 }
74
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))
79 {
80 req.pid = GetCurrentProcessId ();
81 req.master_pid = nmaster_pid;
82 req.from_master = nfrom_master;
83 req.to_master = nto_master;
84
85 syscall_printf (("created: pid = %u, master_pid = %u, "
86 "from_master = %p, to_master = %p"),
87 req.pid, req.master_pid, req.from_master, req.to_master);
88 }
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)
103 {
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 }
114 }
115
116 client_request::header_t::header_t (const request_code_t request_code,
117 const size_t len)
118 : request_code (request_code)
119 {
120 assert (request_code >= 0 && request_code < CYGSERVER_REQUEST_LAST);
121 msglen = len;
122 }
123
124 // FIXME: also check write and read result for -1.
125
126 void
127 client_request::send (transport_layer_base * const conn)
128 {
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: "
141 "only %ld bytes sent of %lu, "
142 "error = %d(%u)"),
143 count, sizeof (_header),
144 errno, GetLastError ());
145 return;
146 }
147 }
148
149 if (msglen ())
150 {
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: "
158 "only %ld bytes sent of %lu, "
159 "error = %d(%u)"),
160 count, msglen (),
161 errno, GetLastError ());
162 return;
163 }
164 }
165
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: "
174 "only %ld bytes received of %lu, "
175 "error = %d(%u)"),
176 count, sizeof (_header),
177 errno, GetLastError ());
178 return;
179 }
180 }
181
182 if (msglen () && !_buf)
183 {
184 system_printf ("no client buffer for reply body: %lu bytes needed",
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: "
193 "have %lu bytes and need %lu"),
194 _buflen, msglen ());
195 error_code (EINVAL);
196 return;
197 }
198
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: "
208 "only %ld bytes received of %lu, "
209 "error = %d(%u)"),
210 count, msglen (),
211 errno, GetLastError ());
212 return;
213 }
214 }
215 }
216
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 }
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: "
254 "only %ld bytes received of %lu, "
255 "error = %d(%u)"),
256 count, sizeof (header),
257 errno, GetLastError ());
258 return;
259 }
260 }
261
262 client_request *req = NULL;
263
264 switch (header.request_code)
265 {
266 case CYGSERVER_REQUEST_GET_VERSION:
267 req = new client_request_get_version;
268 break;
269 case CYGSERVER_REQUEST_SHUTDOWN:
270 req = new client_request_shutdown;
271 break;
272 case CYGSERVER_REQUEST_ATTACH_TTY:
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;
280 break;
281 case CYGSERVER_REQUEST_SHM:
282 req = new client_request_shm;
283 break;
284 case CYGSERVER_REQUEST_SETPWD:
285 req = new client_request_setpwd;
286 break;
287 case CYGSERVER_REQUEST_PWDGRP:
288 req = new client_request_pwdgrp;
289 break;
290 default:
291 syscall_printf ("unknown request code %d received: request ignored",
292 header.request_code);
293 return;
294 }
295
296 assert (req);
297
298 req->msglen (header.msglen);
299 req->handle (conn, cache);
300
301 delete req;
302 }
303
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 {
327 system_printf ("no buffer for request body: %lu bytes needed",
328 msglen ());
329 error_code (EINVAL);
330 return;
331 }
332
333 if (msglen () > _buflen)
334 {
335 system_printf (("buffer too small for request body: "
336 "have %lu bytes and need %lu"),
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: "
351 "only %ld bytes received of %lu, "
352 "error = %d(%u)"),
353 count, msglen (),
354 errno, GetLastError ());
355 return;
356 }
357 }
358
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: "
375 "only %ld bytes sent of %lu, "
376 "error = %d(%u)"),
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: "
392 "only %ld bytes sent of %lu, "
393 "error = %d(%u)"),
394 count, msglen (),
395 errno, GetLastError ());
396 return;
397 }
398 }
399 }
400
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 }
479
480 bool
481 check_cygserver_available ()
482 {
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 ();
509 }
510
511 void
512 cygserver_init ()
513 {
514 assert (cygserver_running == CYGSERVER_UNKNOWN \
515 || cygserver_running == CYGSERVER_OK \
516 || cygserver_running == CYGSERVER_UNAVAIL);
517
518 if (cygserver_running == CYGSERVER_OK)
519 return;
520
521 if (!check_cygserver_available ())
522 cygserver_running = CYGSERVER_UNAVAIL;
523 }
524 #endif /* __INSIDE_CYGWIN__ */
This page took 0.053529 seconds and 5 git commands to generate.