Print this page
5700 add zlogin -d option to allow graceful disconnect when zone is halted
Reviewed by: Andrew Gabriel <illumos@cucumber.demon.co.uk>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/zoneadmd/zcons.c
+++ new/usr/src/cmd/zoneadmd/zcons.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
↓ open down ↓ |
15 lines elided |
↑ open up ↑ |
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 24 * Use is subject to license terms.
25 25 * Copyright 2012 Joyent, Inc. All rights reserved.
26 + * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
26 27 */
27 28
28 29 /*
29 30 * Console support for zones requires a significant infrastructure. The
30 31 * core pieces are contained in this file, but other portions of note
31 32 * are in the zlogin(1M) command, the zcons(7D) driver, and in the
32 33 * devfsadm(1M) misc_link generator.
33 34 *
34 35 * Care is taken to make the console behave in an "intuitive" fashion for
35 36 * administrators. Essentially, we try as much as possible to mimic the
36 37 * experience of using a system via a tip line and system controller.
37 38 *
38 39 * The zone console architecture looks like this:
39 40 *
40 41 * Global Zone | Non-Global Zone
41 42 * .--------------. |
42 43 * .-----------. | zoneadmd -z | | .--------. .---------.
43 44 * | zlogin -C | | myzone | | | ttymon | | syslogd |
44 45 * `-----------' `--------------' | `--------' `---------'
45 46 * | | | | | | |
46 47 * User | | | | | V V
47 48 * - - - - - - - - -|- - - -|- - - -|-|- - - - - - -|- - /dev/zconsole - - -
48 49 * Kernel V V | | |
49 50 * [AF_UNIX Socket] | `--------. .-------------'
50 51 * | | |
51 52 * | V V
52 53 * | +-----------+
53 54 * | | ldterm, |
54 55 * | | etc. |
55 56 * | +-----------+
56 57 * | +-[Anchor]--+
57 58 * | | ptem |
58 59 * V +-----------+
59 60 * +---master---+---slave---+
60 61 * | |
61 62 * | zcons driver |
62 63 * | zonename="myzone" |
63 64 * +------------------------+
64 65 *
65 66 * There are basically two major tasks which the console subsystem in
66 67 * zoneadmd accomplishes:
67 68 *
68 69 * - Setup and teardown of zcons driver instances. One zcons instance
69 70 * is maintained per zone; we take advantage of the libdevice APIs
70 71 * to online new instances of zcons as needed. Care is taken to
71 72 * prune and manage these appropriately; see init_console_dev() and
72 73 * destroy_console_dev(). The end result is the creation of the
73 74 * zcons(7D) instance and an open file descriptor to the master side.
74 75 * zcons instances are associated with zones via their zonename device
75 76 * property. This the console instance to persist across reboots,
76 77 * and while the zone is halted.
77 78 *
78 79 * - Acting as a server for 'zlogin -C' instances. When zlogin -C is
79 80 * run, zlogin connects to zoneadmd via unix domain socket. zoneadmd
80 81 * functions as a two-way proxy for console I/O, relaying user input
81 82 * to the master side of the console, and relaying output from the
82 83 * zone to the user.
83 84 */
84 85
85 86 #include <sys/types.h>
86 87 #include <sys/socket.h>
87 88 #include <sys/stat.h>
88 89 #include <sys/termios.h>
89 90 #include <sys/zcons.h>
90 91 #include <sys/mkdev.h>
91 92
92 93 #include <assert.h>
93 94 #include <ctype.h>
94 95 #include <errno.h>
95 96 #include <fcntl.h>
96 97 #include <stdarg.h>
97 98 #include <stdio.h>
98 99 #include <stdlib.h>
99 100 #include <strings.h>
100 101 #include <stropts.h>
101 102 #include <thread.h>
102 103 #include <ucred.h>
103 104 #include <unistd.h>
104 105 #include <zone.h>
105 106
106 107 #include <libdevinfo.h>
107 108 #include <libdevice.h>
108 109 #include <libzonecfg.h>
109 110
110 111 #include <syslog.h>
111 112 #include <sys/modctl.h>
112 113
113 114 #include "zoneadmd.h"
114 115
115 116 #define ZCONSNEX_DEVTREEPATH "/pseudo/zconsnex@1"
116 117 #define ZCONSNEX_FILEPATH "/devices/pseudo/zconsnex@1"
117 118
118 119 #define CONSOLE_SOCKPATH ZONES_TMPDIR "/%s.console_sock"
119 120
120 121 static int serverfd = -1; /* console server unix domain socket fd */
121 122 char boot_args[BOOTARGS_MAX];
122 123 char bad_boot_arg[BOOTARGS_MAX];
123 124
124 125 /*
125 126 * The eventstream is a simple one-directional flow of messages from the
126 127 * door server to the console subsystem, implemented with a pipe.
127 128 * It is used to wake up the console poller when it needs to take action,
128 129 * message the user, die off, etc.
129 130 */
130 131 static int eventstream[2];
131 132
132 133
133 134
134 135 int
135 136 eventstream_init()
136 137 {
137 138 if (pipe(eventstream) == -1)
138 139 return (-1);
139 140 return (0);
140 141 }
141 142
142 143 void
143 144 eventstream_write(zone_evt_t evt)
144 145 {
145 146 (void) write(eventstream[0], &evt, sizeof (evt));
146 147 }
147 148
148 149 static zone_evt_t
149 150 eventstream_read(void)
150 151 {
151 152 zone_evt_t evt = Z_EVT_NULL;
152 153
153 154 (void) read(eventstream[1], &evt, sizeof (evt));
154 155 return (evt);
155 156 }
156 157
157 158 /*
158 159 * count_console_devs() and its helper count_cb() do a walk of the
159 160 * subtree of the device tree where zone console nodes are represented.
160 161 * The goal is to count zone console instances already setup for a zone
161 162 * with the given name. More than 1 is anomolous, and our caller will
162 163 * have to deal with that if we find that's the case.
163 164 *
164 165 * Note: this algorithm is a linear search of nodes in the zconsnex subtree
165 166 * of the device tree, and could be a scalability problem, but I don't see
166 167 * how to avoid it.
167 168 */
168 169
169 170 /*
170 171 * cb_data is shared by count_cb and destroy_cb for simplicity.
171 172 */
172 173 struct cb_data {
173 174 zlog_t *zlogp;
174 175 int found;
175 176 int killed;
176 177 };
177 178
178 179 static int
179 180 count_cb(di_node_t node, void *arg)
180 181 {
181 182 struct cb_data *cb = (struct cb_data *)arg;
182 183 char *prop_data;
183 184
184 185 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "zonename",
185 186 &prop_data) != -1) {
186 187 assert(prop_data != NULL);
187 188 if (strcmp(prop_data, zone_name) == 0) {
188 189 cb->found++;
189 190 return (DI_WALK_CONTINUE);
190 191 }
191 192 }
192 193 return (DI_WALK_CONTINUE);
193 194 }
194 195
195 196 static int
196 197 count_console_devs(zlog_t *zlogp)
197 198 {
198 199 di_node_t root;
199 200 struct cb_data cb;
200 201
201 202 bzero(&cb, sizeof (cb));
202 203 cb.zlogp = zlogp;
203 204
204 205 if ((root = di_init(ZCONSNEX_DEVTREEPATH, DINFOCPYALL)) ==
205 206 DI_NODE_NIL) {
206 207 zerror(zlogp, B_TRUE, "%s failed", "di_init");
207 208 return (-1);
208 209 }
209 210
210 211 (void) di_walk_node(root, DI_WALK_CLDFIRST, (void *)&cb, count_cb);
211 212 di_fini(root);
212 213 return (cb.found);
213 214 }
214 215
215 216 /*
216 217 * destroy_console_devs() and its helper destroy_cb() tears down any console
217 218 * instances associated with this zone. If things went very wrong, we
218 219 * might have more than one console instance hanging around. This routine
219 220 * hunts down and tries to remove all of them. Of course, if the console
220 221 * is open, the instance will not detach, which is a potential issue.
221 222 */
222 223 static int
223 224 destroy_cb(di_node_t node, void *arg)
224 225 {
225 226 struct cb_data *cb = (struct cb_data *)arg;
226 227 char *prop_data;
227 228 char *tmp;
228 229 char devpath[MAXPATHLEN];
229 230 devctl_hdl_t hdl;
230 231
231 232 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "zonename",
232 233 &prop_data) == -1)
233 234 return (DI_WALK_CONTINUE);
234 235
235 236 assert(prop_data != NULL);
236 237 if (strcmp(prop_data, zone_name) != 0) {
237 238 /* this is the console for a different zone */
238 239 return (DI_WALK_CONTINUE);
239 240 }
240 241
241 242 cb->found++;
242 243 tmp = di_devfs_path(node);
243 244 (void) snprintf(devpath, sizeof (devpath), "/devices/%s", tmp);
244 245 di_devfs_path_free(tmp);
245 246
246 247 if ((hdl = devctl_device_acquire(devpath, 0)) == NULL) {
247 248 zerror(cb->zlogp, B_TRUE, "WARNING: console %s found, "
248 249 "but it could not be controlled.", devpath);
249 250 return (DI_WALK_CONTINUE);
250 251 }
251 252 if (devctl_device_remove(hdl) == 0) {
252 253 cb->killed++;
253 254 } else {
254 255 zerror(cb->zlogp, B_TRUE, "WARNING: console %s found, "
255 256 "but it could not be removed.", devpath);
256 257 }
257 258 devctl_release(hdl);
258 259 return (DI_WALK_CONTINUE);
259 260 }
260 261
261 262 static int
262 263 destroy_console_devs(zlog_t *zlogp)
263 264 {
264 265 char conspath[MAXPATHLEN];
265 266 di_node_t root;
266 267 struct cb_data cb;
267 268 int masterfd;
268 269 int slavefd;
269 270
270 271 /*
271 272 * Signal the master side to release its handle on the slave side by
272 273 * issuing a ZC_RELEASESLAVE ioctl.
273 274 */
274 275 (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s",
275 276 zone_name, ZCONS_MASTER_NAME);
276 277 if ((masterfd = open(conspath, O_RDWR | O_NOCTTY)) != -1) {
277 278 (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s",
278 279 zone_name, ZCONS_SLAVE_NAME);
279 280 if ((slavefd = open(conspath, O_RDWR | O_NOCTTY)) != -1) {
280 281 if (ioctl(masterfd, ZC_RELEASESLAVE,
281 282 (caddr_t)(intptr_t)slavefd) != 0)
282 283 zerror(zlogp, B_TRUE, "WARNING: error while "
283 284 "releasing slave handle of zone console for"
284 285 " %s", zone_name);
285 286 (void) close(slavefd);
286 287 } else {
287 288 zerror(zlogp, B_TRUE, "WARNING: could not open slave "
288 289 "side of zone console for %s to release slave "
289 290 "handle", zone_name);
290 291 }
291 292 (void) close(masterfd);
292 293 } else {
293 294 zerror(zlogp, B_TRUE, "WARNING: could not open master side of "
294 295 "zone console for %s to release slave handle", zone_name);
295 296 }
296 297
297 298 bzero(&cb, sizeof (cb));
298 299 cb.zlogp = zlogp;
299 300
300 301 if ((root = di_init(ZCONSNEX_DEVTREEPATH, DINFOCPYALL)) ==
301 302 DI_NODE_NIL) {
302 303 zerror(zlogp, B_TRUE, "%s failed", "di_init");
303 304 return (-1);
304 305 }
305 306
306 307 (void) di_walk_node(root, DI_WALK_CLDFIRST, (void *)&cb, destroy_cb);
307 308 if (cb.found > 1) {
308 309 zerror(zlogp, B_FALSE, "WARNING: multiple zone console "
309 310 "instances detected for zone '%s'; %d of %d "
310 311 "successfully removed.",
311 312 zone_name, cb.killed, cb.found);
312 313 }
313 314
314 315 di_fini(root);
315 316 return (0);
316 317 }
317 318
318 319 /*
319 320 * init_console_dev() drives the device-tree configuration of the zone
320 321 * console device. The general strategy is to use the libdevice (devctl)
321 322 * interfaces to instantiate a new zone console node. We do a lot of
322 323 * sanity checking, and are careful to reuse a console if one exists.
323 324 *
324 325 * Once the device is in the device tree, we kick devfsadm via di_init_devs()
325 326 * to ensure that the appropriate symlinks (to the master and slave console
326 327 * devices) are placed in /dev in the global zone.
327 328 */
328 329 static int
329 330 init_console_dev(zlog_t *zlogp)
330 331 {
331 332 char conspath[MAXPATHLEN];
332 333 devctl_hdl_t bus_hdl = NULL;
333 334 devctl_hdl_t dev_hdl = NULL;
334 335 devctl_ddef_t ddef_hdl = NULL;
335 336 di_devlink_handle_t dl = NULL;
336 337 int rv = -1;
337 338 int ndevs;
338 339 int masterfd;
339 340 int slavefd;
340 341 int i;
341 342
342 343 /*
343 344 * Don't re-setup console if it is working and ready already; just
344 345 * skip ahead to making devlinks, which we do for sanity's sake.
345 346 */
346 347 ndevs = count_console_devs(zlogp);
347 348 if (ndevs == 1) {
348 349 goto devlinks;
349 350 } else if (ndevs > 1 || ndevs == -1) {
350 351 /*
351 352 * For now, this seems like a reasonable but harsh punishment.
352 353 * If needed, we could try to get clever and delete all but
353 354 * the console which is pointed at by the current symlink.
354 355 */
355 356 if (destroy_console_devs(zlogp) == -1) {
356 357 goto error;
357 358 }
358 359 }
359 360
360 361 /*
361 362 * Time to make the consoles!
362 363 */
363 364 if ((bus_hdl = devctl_bus_acquire(ZCONSNEX_FILEPATH, 0)) == NULL) {
364 365 zerror(zlogp, B_TRUE, "%s failed", "devctl_bus_acquire");
365 366 goto error;
366 367 }
367 368 if ((ddef_hdl = devctl_ddef_alloc("zcons", 0)) == NULL) {
368 369 zerror(zlogp, B_TRUE, "failed to allocate ddef handle");
369 370 goto error;
370 371 }
371 372 /*
372 373 * Set three properties on this node; the first is the name of the
373 374 * zone; the second is a flag which lets pseudo know that it is
374 375 * OK to automatically allocate an instance # for this device;
375 376 * the third tells the device framework not to auto-detach this
376 377 * node-- we need the node to still be there when we ask devfsadmd
377 378 * to make links, and when we need to open it.
378 379 */
379 380 if (devctl_ddef_string(ddef_hdl, "zonename", zone_name) == -1) {
380 381 zerror(zlogp, B_TRUE, "failed to create zonename property");
381 382 goto error;
382 383 }
383 384 if (devctl_ddef_int(ddef_hdl, "auto-assign-instance", 1) == -1) {
384 385 zerror(zlogp, B_TRUE, "failed to create auto-assign-instance "
385 386 "property");
386 387 goto error;
387 388 }
388 389 if (devctl_ddef_int(ddef_hdl, "ddi-no-autodetach", 1) == -1) {
389 390 zerror(zlogp, B_TRUE, "failed to create ddi-no-auto-detach "
390 391 "property");
391 392 goto error;
392 393 }
393 394 if (devctl_bus_dev_create(bus_hdl, ddef_hdl, 0, &dev_hdl) == -1) {
394 395 zerror(zlogp, B_TRUE, "failed to create console node");
395 396 goto error;
396 397 }
397 398
398 399 devlinks:
399 400 if ((dl = di_devlink_init("zcons", DI_MAKE_LINK)) != NULL) {
400 401 (void) di_devlink_fini(&dl);
401 402 } else {
402 403 zerror(zlogp, B_TRUE, "failed to create devlinks");
403 404 goto error;
404 405 }
405 406
406 407 /*
407 408 * Open the master side of the console and issue the ZC_HOLDSLAVE ioctl,
408 409 * which will cause the master to retain a reference to the slave.
409 410 * This prevents ttymon from blowing through the slave's STREAMS anchor.
410 411 */
411 412 (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s",
412 413 zone_name, ZCONS_MASTER_NAME);
413 414 if ((masterfd = open(conspath, O_RDWR | O_NOCTTY)) == -1) {
414 415 zerror(zlogp, B_TRUE, "ERROR: could not open master side of "
415 416 "zone console for %s to acquire slave handle", zone_name);
416 417 goto error;
417 418 }
418 419 (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s",
419 420 zone_name, ZCONS_SLAVE_NAME);
420 421 if ((slavefd = open(conspath, O_RDWR | O_NOCTTY)) == -1) {
421 422 zerror(zlogp, B_TRUE, "ERROR: could not open slave side of zone"
422 423 " console for %s to acquire slave handle", zone_name);
423 424 (void) close(masterfd);
424 425 goto error;
425 426 }
426 427 /*
427 428 * This ioctl can occasionally return ENXIO if devfs doesn't have
428 429 * everything plumbed up yet due to heavy zone startup load. Wait for
429 430 * 1 sec. and retry a few times before we fail to boot the zone.
430 431 */
431 432 for (i = 0; i < 5; i++) {
432 433 if (ioctl(masterfd, ZC_HOLDSLAVE, (caddr_t)(intptr_t)slavefd)
433 434 == 0) {
434 435 rv = 0;
435 436 break;
436 437 } else if (errno != ENXIO) {
437 438 break;
438 439 }
439 440 (void) sleep(1);
440 441 }
441 442 if (rv != 0)
442 443 zerror(zlogp, B_TRUE, "ERROR: error while acquiring slave "
443 444 "handle of zone console for %s", zone_name);
444 445
445 446 (void) close(slavefd);
446 447 (void) close(masterfd);
447 448
448 449 error:
449 450 if (ddef_hdl)
450 451 devctl_ddef_free(ddef_hdl);
451 452 if (bus_hdl)
452 453 devctl_release(bus_hdl);
453 454 if (dev_hdl)
454 455 devctl_release(dev_hdl);
455 456 return (rv);
456 457 }
457 458
458 459 static int
459 460 init_console_sock(zlog_t *zlogp)
460 461 {
461 462 int servfd;
462 463 struct sockaddr_un servaddr;
463 464
464 465 bzero(&servaddr, sizeof (servaddr));
465 466 servaddr.sun_family = AF_UNIX;
466 467 (void) snprintf(servaddr.sun_path, sizeof (servaddr.sun_path),
467 468 CONSOLE_SOCKPATH, zone_name);
468 469
469 470 if ((servfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
470 471 zerror(zlogp, B_TRUE, "console setup: could not create socket");
471 472 return (-1);
472 473 }
473 474 (void) unlink(servaddr.sun_path);
474 475
475 476 if (bind(servfd, (struct sockaddr *)&servaddr,
476 477 sizeof (servaddr)) == -1) {
477 478 zerror(zlogp, B_TRUE,
478 479 "console setup: could not bind to socket");
479 480 goto out;
480 481 }
481 482
482 483 if (listen(servfd, 4) == -1) {
483 484 zerror(zlogp, B_TRUE,
484 485 "console setup: could not listen on socket");
485 486 goto out;
486 487 }
487 488 return (servfd);
488 489
489 490 out:
490 491 (void) unlink(servaddr.sun_path);
491 492 (void) close(servfd);
492 493 return (-1);
493 494 }
494 495
495 496 static void
496 497 destroy_console_sock(int servfd)
497 498 {
498 499 char path[MAXPATHLEN];
499 500
500 501 (void) snprintf(path, sizeof (path), CONSOLE_SOCKPATH, zone_name);
501 502 (void) unlink(path);
↓ open down ↓ |
466 lines elided |
↑ open up ↑ |
502 503 (void) shutdown(servfd, SHUT_RDWR);
503 504 (void) close(servfd);
504 505 }
505 506
506 507 /*
507 508 * Read the "ident" string from the client's descriptor; this routine also
508 509 * tolerates being called with pid=NULL, for times when you want to "eat"
509 510 * the ident string from a client without saving it.
510 511 */
511 512 static int
512 -get_client_ident(int clifd, pid_t *pid, char *locale, size_t locale_len)
513 +get_client_ident(int clifd, pid_t *pid, char *locale, size_t locale_len,
514 + int *disconnect)
513 515 {
514 516 char buf[BUFSIZ], *bufp;
515 517 size_t buflen = sizeof (buf);
516 518 char c = '\0';
517 519 int i = 0, r;
518 520
519 521 /* "eat up the ident string" case, for simplicity */
520 522 if (pid == NULL) {
521 523 assert(locale == NULL && locale_len == 0);
522 524 while (read(clifd, &c, 1) == 1) {
523 525 if (c == '\n')
524 526 return (0);
525 527 }
526 528 }
527 529
528 530 bzero(buf, sizeof (buf));
529 531 while ((buflen > 1) && (r = read(clifd, &c, 1)) == 1) {
530 532 buflen--;
531 533 if (c == '\n')
532 534 break;
533 535
534 536 buf[i] = c;
535 537 i++;
536 538 }
537 539 if (r == -1)
538 540 return (-1);
539 541
540 542 /*
541 543 * We've filled the buffer, but still haven't seen \n. Keep eating
↓ open down ↓ |
19 lines elided |
↑ open up ↑ |
542 544 * until we find it; we don't expect this to happen, but this is
543 545 * defensive.
544 546 */
545 547 if (c != '\n') {
546 548 while ((r = read(clifd, &c, sizeof (c))) > 0)
547 549 if (c == '\n')
548 550 break;
549 551 }
550 552
551 553 /*
552 - * Parse buffer for message of the form: IDENT <pid> <locale>
554 + * Parse buffer for message of the form:
555 + * IDENT <pid> <locale> <disconnect flag>
553 556 */
554 557 bufp = buf;
555 558 if (strncmp(bufp, "IDENT ", 6) != 0)
556 559 return (-1);
557 560 bufp += 6;
558 561 errno = 0;
559 562 *pid = strtoll(bufp, &bufp, 10);
560 563 if (errno != 0)
561 564 return (-1);
562 565
563 566 while (*bufp != '\0' && isspace(*bufp))
564 567 bufp++;
568 + buflen = strlen(bufp) - 1;
569 + *disconnect = atoi(&bufp[buflen]);
570 + bufp[buflen - 1] = '\0';
565 571 (void) strlcpy(locale, bufp, locale_len);
566 572
567 573 return (0);
568 574 }
569 575
570 576 static int
571 -accept_client(int servfd, pid_t *pid, char *locale, size_t locale_len)
577 +accept_client(int servfd, pid_t *pid, char *locale, size_t locale_len,
578 + int *disconnect)
572 579 {
573 580 int connfd;
574 581 struct sockaddr_un cliaddr;
575 582 socklen_t clilen;
576 583
577 584 clilen = sizeof (cliaddr);
578 585 connfd = accept(servfd, (struct sockaddr *)&cliaddr, &clilen);
579 586 if (connfd == -1)
580 587 return (-1);
581 - if (get_client_ident(connfd, pid, locale, locale_len) == -1) {
588 + if (get_client_ident(connfd, pid, locale, locale_len,
589 + disconnect) == -1) {
582 590 (void) shutdown(connfd, SHUT_RDWR);
583 591 (void) close(connfd);
584 592 return (-1);
585 593 }
586 594 (void) write(connfd, "OK\n", 3);
587 595 return (connfd);
588 596 }
589 597
590 598 static void
591 599 reject_client(int servfd, pid_t clientpid)
592 600 {
593 601 int connfd;
↓ open down ↓ |
2 lines elided |
↑ open up ↑ |
594 602 struct sockaddr_un cliaddr;
595 603 socklen_t clilen;
596 604 char nak[MAXPATHLEN];
597 605
598 606 clilen = sizeof (cliaddr);
599 607 connfd = accept(servfd, (struct sockaddr *)&cliaddr, &clilen);
600 608
601 609 /*
602 610 * After hear its ident string, tell client to get lost.
603 611 */
604 - if (get_client_ident(connfd, NULL, NULL, 0) == 0) {
612 + if (get_client_ident(connfd, NULL, NULL, 0, NULL) == 0) {
605 613 (void) snprintf(nak, sizeof (nak), "%lu\n",
606 614 clientpid);
607 615 (void) write(connfd, nak, strlen(nak));
608 616 }
609 617 (void) shutdown(connfd, SHUT_RDWR);
610 618 (void) close(connfd);
611 619 }
612 620
613 621 static void
614 -event_message(int clifd, char *clilocale, zone_evt_t evt)
622 +event_message(int clifd, char *clilocale, zone_evt_t evt, int dflag)
615 623 {
616 624 char *str, *lstr = NULL;
617 625 char lmsg[BUFSIZ];
618 626 char outbuf[BUFSIZ];
619 627
620 628 if (clifd == -1)
621 629 return;
622 630
623 631 switch (evt) {
624 632 case Z_EVT_ZONE_BOOTING:
625 633 if (*boot_args == '\0') {
626 634 str = "NOTICE: Zone booting up";
627 635 break;
↓ open down ↓ |
3 lines elided |
↑ open up ↑ |
628 636 }
629 637 /*LINTED*/
630 638 (void) snprintf(lmsg, sizeof (lmsg), localize_msg(clilocale,
631 639 "NOTICE: Zone booting up with arguments: %s"), boot_args);
632 640 lstr = lmsg;
633 641 break;
634 642 case Z_EVT_ZONE_READIED:
635 643 str = "NOTICE: Zone readied";
636 644 break;
637 645 case Z_EVT_ZONE_HALTED:
638 - str = "NOTICE: Zone halted";
646 + if (dflag)
647 + str = "NOTICE: Zone halted. Disconnecting...";
648 + else
649 + str = "NOTICE: Zone halted";
639 650 break;
640 651 case Z_EVT_ZONE_REBOOTING:
641 652 if (*boot_args == '\0') {
642 653 str = "NOTICE: Zone rebooting";
643 654 break;
644 655 }
645 656 /*LINTED*/
646 657 (void) snprintf(lmsg, sizeof (lmsg), localize_msg(clilocale,
647 658 "NOTICE: Zone rebooting with arguments: %s"), boot_args);
648 659 lstr = lmsg;
649 660 break;
650 661 case Z_EVT_ZONE_UNINSTALLING:
651 662 str = "NOTICE: Zone is being uninstalled. Disconnecting...";
652 663 break;
653 664 case Z_EVT_ZONE_BOOTFAILED:
654 - str = "NOTICE: Zone boot failed";
665 + if (dflag)
666 + str = "NOTICE: Zone boot failed. Disconnecting...";
667 + else
668 + str = "NOTICE: Zone boot failed";
655 669 break;
656 670 case Z_EVT_ZONE_BADARGS:
657 671 /*LINTED*/
658 672 (void) snprintf(lmsg, sizeof (lmsg),
659 673 localize_msg(clilocale,
660 674 "WARNING: Ignoring invalid boot arguments: %s"),
661 675 bad_boot_arg);
662 676 lstr = lmsg;
663 677 break;
664 678 default:
665 679 return;
666 680 }
667 681
668 682 if (lstr == NULL)
669 683 lstr = localize_msg(clilocale, str);
670 684 (void) snprintf(outbuf, sizeof (outbuf), "\r\n[%s]\r\n", lstr);
671 685 (void) write(clifd, outbuf, strlen(outbuf));
672 686 }
673 687
674 688 /*
675 689 * Check to see if the client at the other end of the socket is still
676 690 * alive; we know it is not if it throws EPIPE at us when we try to write
677 691 * an otherwise harmless 0-length message to it.
678 692 */
679 693 static int
680 694 test_client(int clifd)
681 695 {
682 696 if ((write(clifd, "", 0) == -1) && errno == EPIPE)
683 697 return (-1);
684 698 return (0);
685 699 }
686 700
687 701 /*
688 702 * This routine drives the console I/O loop. It polls for input from the
689 703 * master side of the console (output to the console), and from the client
690 704 * (input from the console user). Additionally, it polls on the server fd,
691 705 * and disconnects any clients that might try to hook up with the zone while
692 706 * the console is in use.
693 707 *
694 708 * When the client first calls us up, it is expected to send a line giving
695 709 * its "identity"; this consists of the string 'IDENT <pid> <locale>'.
696 710 * This is so that we can report that the console is busy along with
697 711 * some diagnostics about who has it busy; the locale is used so that
698 712 * asynchronous messages about zone state (like the NOTICE: zone halted
699 713 * messages) can be output in the user's locale.
700 714 */
↓ open down ↓ |
36 lines elided |
↑ open up ↑ |
701 715 static void
702 716 do_console_io(zlog_t *zlogp, int consfd, int servfd)
703 717 {
704 718 struct pollfd pollfds[4];
705 719 char ibuf[BUFSIZ];
706 720 int cc, ret;
707 721 int clifd = -1;
708 722 int pollerr = 0;
709 723 char clilocale[MAXPATHLEN];
710 724 pid_t clipid = 0;
725 + int disconnect = 0;
711 726
712 727 /* console side, watch for read events */
713 728 pollfds[0].fd = consfd;
714 729 pollfds[0].events = POLLIN | POLLRDNORM | POLLRDBAND |
715 730 POLLPRI | POLLERR | POLLHUP | POLLNVAL;
716 731
717 732 /* client side, watch for read events */
718 733 pollfds[1].fd = clifd;
719 734 pollfds[1].events = pollfds[0].events;
720 735
721 736 /* the server socket; watch for events (new connections) */
722 737 pollfds[2].fd = servfd;
723 738 pollfds[2].events = pollfds[0].events;
724 739
725 740 /* the eventstram; watch for events (e.g.: zone halted) */
726 741 pollfds[3].fd = eventstream[1];
727 742 pollfds[3].events = pollfds[0].events;
728 743
729 744 for (;;) {
730 745 pollfds[0].revents = pollfds[1].revents = 0;
731 746 pollfds[2].revents = pollfds[3].revents = 0;
732 747
733 748 ret = poll(pollfds,
734 749 sizeof (pollfds) / sizeof (struct pollfd), -1);
735 750 if (ret == -1 && errno != EINTR) {
736 751 zerror(zlogp, B_TRUE, "poll failed");
737 752 /* we are hosed, close connection */
738 753 break;
739 754 }
740 755
741 756 /* event from console side */
742 757 if (pollfds[0].revents) {
743 758 if (pollfds[0].revents &
744 759 (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) {
745 760 errno = 0;
746 761 cc = read(consfd, ibuf, BUFSIZ);
747 762 if (cc <= 0 && (errno != EINTR) &&
748 763 (errno != EAGAIN))
749 764 break;
750 765 /*
751 766 * Lose I/O if no one is listening
752 767 */
753 768 if (clifd != -1 && cc > 0)
754 769 (void) write(clifd, ibuf, cc);
755 770 } else {
756 771 pollerr = pollfds[0].revents;
757 772 zerror(zlogp, B_FALSE,
758 773 "closing connection with (console) "
759 774 "pollerr %d\n", pollerr);
760 775 break;
761 776 }
762 777 }
763 778
764 779 /* event from client side */
765 780 if (pollfds[1].revents) {
766 781 if (pollfds[1].revents &
767 782 (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) {
768 783 errno = 0;
769 784 cc = read(clifd, ibuf, BUFSIZ);
770 785 if (cc <= 0 && (errno != EINTR) &&
771 786 (errno != EAGAIN))
772 787 break;
773 788 (void) write(consfd, ibuf, cc);
774 789 } else {
775 790 pollerr = pollfds[1].revents;
776 791 zerror(zlogp, B_FALSE,
777 792 "closing connection with (client) "
778 793 "pollerr %d\n", pollerr);
779 794 break;
780 795 }
781 796 }
782 797
783 798 /* event from server socket */
784 799 if (pollfds[2].revents &&
785 800 (pollfds[2].revents & (POLLIN | POLLRDNORM))) {
786 801 if (clifd != -1) {
787 802 /*
788 803 * Test the client to see if it is really
789 804 * still alive. If it has died but we
790 805 * haven't yet detected that, we might
791 806 * deny a legitimate connect attempt. If it
792 807 * is dead, we break out; once we tear down
793 808 * the old connection, the new connection
↓ open down ↓ |
73 lines elided |
↑ open up ↑ |
794 809 * will happen.
795 810 */
796 811 if (test_client(clifd) == -1) {
797 812 break;
798 813 }
799 814 /* we're already handling a client */
800 815 reject_client(servfd, clipid);
801 816
802 817
803 818 } else if ((clifd = accept_client(servfd, &clipid,
804 - clilocale, sizeof (clilocale))) != -1) {
819 + clilocale, sizeof (clilocale),
820 + &disconnect)) != -1) {
805 821 pollfds[1].fd = clifd;
806 822
807 823 } else {
808 824 break;
809 825 }
810 826 }
811 827
812 828 /*
813 829 * Watch for events on the eventstream. This is how we get
814 830 * notified of the zone halting, etc. It provides us a
815 831 * "wakeup" from poll when important things happen, which
816 832 * is good.
817 833 */
818 834 if (pollfds[3].revents) {
819 835 int evt = eventstream_read();
↓ open down ↓ |
5 lines elided |
↑ open up ↑ |
820 836 /*
821 837 * After we drain out the event, if we aren't servicing
822 838 * a console client, we hop back out to our caller,
823 839 * which will check to see if it is time to shutdown
824 840 * the daemon, or if we should take another console
825 841 * service lap.
826 842 */
827 843 if (clifd == -1) {
828 844 break;
829 845 }
830 - event_message(clifd, clilocale, evt);
846 + event_message(clifd, clilocale, evt, disconnect);
831 847 /*
832 848 * Special handling for the message that the zone is
833 849 * uninstalling; we boot the client, then break out
834 850 * of this function. When we return to the
835 851 * serve_console loop, we will see that the zone is
836 852 * in a state < READY, and so zoneadmd will shutdown.
837 853 */
838 854 if (evt == Z_EVT_ZONE_UNINSTALLING) {
839 855 break;
840 856 }
857 + /*
858 + * Diconnect if -C and -d options were specified and
859 + * zone was halted or failed to boot.
860 + */
861 + if ((evt == Z_EVT_ZONE_HALTED ||
862 + evt == Z_EVT_ZONE_BOOTFAILED) && disconnect) {
863 + break;
864 + }
841 865 }
842 866
843 867 }
844 868
845 869 if (clifd != -1) {
846 870 (void) shutdown(clifd, SHUT_RDWR);
847 871 (void) close(clifd);
848 872 }
849 873 }
850 874
851 875 int
852 876 init_console(zlog_t *zlogp)
853 877 {
854 878 if (init_console_dev(zlogp) == -1) {
855 879 zerror(zlogp, B_FALSE,
856 880 "console setup: device initialization failed");
857 881 return (-1);
858 882 }
859 883
860 884 if ((serverfd = init_console_sock(zlogp)) == -1) {
861 885 zerror(zlogp, B_FALSE,
862 886 "console setup: socket initialization failed");
863 887 return (-1);
864 888 }
865 889 return (0);
866 890 }
867 891
868 892 /*
869 893 * serve_console() is the master loop for driving console I/O. It is also the
870 894 * routine which is ultimately responsible for "pulling the plug" on zoneadmd
871 895 * when it realizes that the daemon should shut down.
872 896 *
873 897 * The rules for shutdown are: there must be no console client, and the zone
874 898 * state must be < ready. However, we need to give things a chance to actually
875 899 * get going when the daemon starts up-- otherwise the daemon would immediately
876 900 * exit on startup if the zone was in the installed state, so we first drop
877 901 * into the do_console_io() loop in order to give *something* a chance to
878 902 * happen.
879 903 */
880 904 void
881 905 serve_console(zlog_t *zlogp)
882 906 {
883 907 int masterfd;
884 908 zone_state_t zstate;
885 909 char conspath[MAXPATHLEN];
886 910
887 911 (void) snprintf(conspath, sizeof (conspath),
888 912 "/dev/zcons/%s/%s", zone_name, ZCONS_MASTER_NAME);
889 913
890 914 for (;;) {
891 915 masterfd = open(conspath, O_RDWR|O_NONBLOCK|O_NOCTTY);
892 916 if (masterfd == -1) {
893 917 zerror(zlogp, B_TRUE, "failed to open console master");
894 918 (void) mutex_lock(&lock);
895 919 goto death;
896 920 }
897 921
898 922 /*
899 923 * Setting RPROTDIS on the stream means that the control
900 924 * portion of messages received (which we don't care about)
901 925 * will be discarded by the stream head. If we allowed such
902 926 * messages, we wouldn't be able to use read(2), as it fails
903 927 * (EBADMSG) when a message with a control element is received.
904 928 */
905 929 if (ioctl(masterfd, I_SRDOPT, RNORM|RPROTDIS) == -1) {
906 930 zerror(zlogp, B_TRUE, "failed to set options on "
907 931 "console master");
908 932 (void) mutex_lock(&lock);
909 933 goto death;
910 934 }
911 935
912 936 do_console_io(zlogp, masterfd, serverfd);
913 937
914 938 /*
915 939 * We would prefer not to do this, but hostile zone processes
916 940 * can cause the stream to become tainted, and reads will
917 941 * fail. So, in case something has gone seriously ill,
918 942 * we dismantle the stream and reopen the console when we
919 943 * take another lap.
920 944 */
921 945 (void) close(masterfd);
922 946
923 947 (void) mutex_lock(&lock);
924 948 /*
925 949 * We need to set death_throes (see below) atomically with
926 950 * respect to noticing that (a) we have no console client and
927 951 * (b) the zone is not installed. Otherwise we could get a
928 952 * request to boot during this time. Once we set death_throes,
929 953 * any incoming door stuff will be turned away.
930 954 */
931 955 if (zone_get_state(zone_name, &zstate) == Z_OK) {
932 956 if (zstate < ZONE_STATE_READY)
933 957 goto death;
934 958 } else {
935 959 zerror(zlogp, B_FALSE,
936 960 "unable to determine state of zone");
937 961 goto death;
938 962 }
939 963 /*
940 964 * Even if zone_get_state() fails, stay conservative, and
941 965 * take another lap.
942 966 */
943 967 (void) mutex_unlock(&lock);
944 968 }
945 969
946 970 death:
947 971 assert(MUTEX_HELD(&lock));
948 972 in_death_throes = B_TRUE;
949 973 (void) mutex_unlock(&lock);
950 974
951 975 destroy_console_sock(serverfd);
952 976 (void) destroy_console_devs(zlogp);
953 977 }
↓ open down ↓ |
103 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX