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