1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 /* 29 * This code implements the Starfire Virtual Console host daemon 30 * (see cvcd(1M)). It accepts a connection from netcon_server 31 * and transfers console I/O to/from the SSP across the 32 * network via TLI. The I/O is sent to the cvcredir device 33 * on the host (see cvc(7) and cvcredir(7)). It also sends 34 * disconnect and break ioctl's to the kernel CVC drivers. 35 */ 36 37 #pragma ident "%Z%%M% %I% %E% SMI" 38 39 40 #include <stdio.h> 41 #include <stdarg.h> 42 #include <syslog.h> 43 #include <stdlib.h> 44 #include <tiuser.h> 45 #include <sys/timod.h> 46 #include <fcntl.h> 47 #include <sys/param.h> 48 #include <sys/utsname.h> 49 #include <sys/stat.h> 50 #include <unistd.h> 51 #include <stropts.h> 52 #include <sys/conf.h> 53 #include <pwd.h> 54 #include <errno.h> 55 #include <sys/socket.h> 56 #include <arpa/inet.h> 57 #include <locale.h> 58 #include <termio.h> 59 #include <signal.h> 60 #include <sys/cvc.h> 61 62 #include <string.h> 63 64 #include <sys/ioctl.h> 65 #include <sys/file.h> 66 #include <sys/sockio.h> 67 68 #include <sys/tihdr.h> 69 70 #include <netdb.h> 71 #include <net/if.h> 72 #include <netinet/if_ether.h> 73 74 #include <inet/common.h> 75 #include <sys/systeminfo.h> 76 77 /* Process priority control */ 78 #include <sys/priocntl.h> 79 #include <sys/tspriocntl.h> 80 #include <sys/rtpriocntl.h> 81 82 /* 83 * Misc. defines. 84 */ 85 #define CONREF "connection request from illegal host" 86 #define SSPHOSTNAMEFILE "/etc/ssphostname" 87 #define NODENAME "/etc/nodename" 88 #define MAXIFS 256 89 90 /* 91 * Function prototypes 92 */ 93 static void cvcd_connect(int fd, struct pollfd *); 94 static void cvcd_reject(int fd); 95 static void cvcd_read(struct pollfd *); 96 static void cvcd_write(char *data, int size); 97 static void cvcd_status(int fd); 98 static void cvcd_winch(int, char *, int); 99 static void cvcd_ioctl(int fd, int cmd); 100 static void cvcd_err(int code, char *format, ...); 101 static void usage(void); 102 static id_t schedinfo(char *name, short *maxpri); 103 static void cvcd_setopt(int fd, int name); 104 105 /* 106 * Globals 107 */ 108 static int rconsfd; /* Console redirection driver */ 109 static char progname[MAXPATHLEN]; 110 static char ssphostname[MAXPATHLEN]; 111 static int debug = 0; 112 static int connected = 0; 113 static int peercheck = 1; 114 static char nic_name[32]; 115 116 main(int argc, char **argv) 117 { 118 int opt; 119 int tport = 0; 120 char *hostname; 121 struct utsname utsname; 122 struct t_info tinfo; 123 int cvcd_ssp; 124 int nfd; 125 struct pollfd *cvcd_pfd; 126 int i; 127 int j; 128 struct servent *se; 129 struct sockaddr_in *sin; 130 struct t_bind *reqb; 131 struct t_bind *retb; 132 struct t_optmgmt *topt, *tropt; 133 struct opthdr *sockopt; 134 int on = 1; 135 int tmperr = 0; 136 int event; 137 char prefix[256]; 138 pcparms_t pcparms; 139 tsparms_t *tsparmsp; 140 id_t pid, tsID; 141 short tsmaxpri; 142 static int netcon_fail = 0; 143 144 (void) setlocale(LC_ALL, ""); 145 (void) strcpy(progname, argv[0]); 146 (void) memset(ssphostname, 0, MAXPATHLEN); 147 148 if ((cvcd_ssp = open(SSPHOSTNAMEFILE, O_RDONLY)) < 0) { 149 /* 150 * If there is no /etc/ssphostname disable peer check after 151 * issuing warning. 152 */ 153 tmperr = errno; 154 peercheck = 0; 155 } else { 156 if ((i = read(cvcd_ssp, ssphostname, MAXPATHLEN)) < 0) { 157 cvcd_err(LOG_ERR, "failed to read ssphostname"); 158 } 159 /* 160 * The ssp-config(1M) command newline terminates the 161 * ssphostname in the /etc/ssphostname file 162 */ 163 ssphostname[i-1] = '\0'; 164 (void) close(cvcd_ssp); 165 166 (void) memset(nic_name, 0, sizeof (nic_name)); 167 } 168 169 #if defined(DEBUG) 170 while ((opt = getopt(argc, argv, "dp:r:")) != EOF) { 171 #else 172 while ((opt = getopt(argc, argv, "r:")) != EOF) { 173 #endif /* DEBUG */ 174 switch (opt) { 175 176 #if defined(DEBUG) 177 case 'd' : debug = 1; 178 break; 179 180 case 'p' : tport = atoi(optarg); 181 break; 182 #endif /* DEBUG */ 183 184 case 'r' : (void) strcpy(ssphostname, optarg); 185 break; 186 187 default : usage(); 188 exit(1); 189 } 190 } 191 192 if (uname(&utsname) == -1) { 193 perror("HOSTNAME not defined"); 194 exit(1); 195 } 196 hostname = utsname.nodename; 197 198 /* 199 * hostname may still be NULL, depends on when cvcd was started 200 * in the boot sequence. If it is NULL, try one more time 201 * to get a hostname -> look in the /etc/nodename file. 202 */ 203 if (!strlen(hostname)) { 204 /* 205 * try to get the hostname from the /etc/nodename file 206 * we reuse the utsname.nodename buffer here! hostname 207 * already points to it. 208 */ 209 if ((nfd = open(NODENAME, O_RDONLY)) > 0) { 210 if ((i = read(nfd, utsname.nodename, SYS_NMLN)) <= 0) { 211 cvcd_err(LOG_WARNING, 212 "failed to acquire hostname"); 213 } 214 utsname.nodename[i-1] = '\0'; 215 (void) close(nfd); 216 } 217 } 218 219 /* 220 * Must be root. 221 */ 222 if (debug == 0 && geteuid() != 0) { 223 fprintf(stderr, "cvcd: Must be root"); 224 exit(1); 225 } 226 227 /* 228 * Daemonize... 229 */ 230 if (debug == 0) { 231 for (i = 0; i < NOFILE; i++) { 232 (void) close(i); 233 } 234 (void) chdir("/"); 235 (void) umask(0); 236 if (fork() != 0) { 237 exit(0); 238 } 239 (void) setpgrp(); 240 (void) sprintf(prefix, "%s-(HOSTNAME:%s)", progname, hostname); 241 openlog(prefix, LOG_CONS | LOG_NDELAY, LOG_LOCAL0); 242 } 243 if (peercheck == 0) { 244 cvcd_err(LOG_ERR, "open(SSPHOSTNAMEFILE):%s", 245 strerror(tmperr)); 246 } 247 248 cvcd_pfd = (struct pollfd *)malloc(3*sizeof (struct pollfd)); 249 if (cvcd_pfd == (struct pollfd *)NULL) { 250 cvcd_err(LOG_ERR, "malloc:", strerror(errno)); 251 exit(1); 252 } 253 (void) memset((void *)cvcd_pfd, 0, 3*sizeof (struct pollfd)); 254 cvcd_pfd[0].fd = -1; 255 cvcd_pfd[1].fd = -1; 256 cvcd_pfd[2].fd = -1; 257 258 /* SPR 94004 */ 259 (void) sigignore(SIGTERM); 260 261 /* 262 * SPR 83644: cvc and kadb are not compatible under heavy loads. 263 * Fix: will give cvcd highest TS priority at execution time. 264 */ 265 pid = getpid(); 266 pcparms.pc_cid = PC_CLNULL; 267 tsparmsp = (tsparms_t *)pcparms.pc_clparms; 268 269 /* Get scheduler properties for this PID */ 270 if (priocntl(P_PID, pid, PC_GETPARMS, (caddr_t)&pcparms) == -1L) { 271 cvcd_err(LOG_ERR, 272 "cvcd: GETPARMS failed. Warning: can't get ", 273 "TS priorities."); 274 } else { 275 /* Get class IDs and maximum priorities for a TS process */ 276 if ((tsID = schedinfo("TS", &tsmaxpri)) == -1) { 277 cvcd_err(LOG_ERR, "cvcd: Warning, can't get ", 278 "TS scheduler info."); 279 } else { 280 if (debug) { /* Print priority info */ 281 if (pcparms.pc_cid == tsID) { 282 cvcd_err(LOG_DEBUG, "%s%d%s%d%s%d\n", 283 "cvcd:: PID:", pid, 284 ", TS priority:", 285 tsparmsp->ts_upri, 286 ", TS max_pri:", tsmaxpri); 287 } 288 } 289 /* Change proc's priority to maxtspri */ 290 pcparms.pc_cid = tsID; 291 tsparmsp = (struct tsparms *)pcparms.pc_clparms; 292 tsparmsp->ts_upri = tsmaxpri; 293 tsparmsp->ts_uprilim = tsmaxpri; 294 295 if (priocntl(P_PID, pid, PC_SETPARMS, 296 (caddr_t)&pcparms) == -1L) { 297 cvcd_err(LOG_ERR, "cvcd: Warning, ", 298 "can't set TS maximum priority."); 299 } 300 /* Done */ 301 if (debug) { /* Get new scheduler properties for PID */ 302 if (priocntl(P_PID, pid, PC_GETPARMS, 303 (caddr_t)&pcparms) == -1L) { 304 cvcd_err(LOG_DEBUG, "GETPARMS failed"); 305 exit(1); 306 } else { 307 cvcd_err(LOG_DEBUG, "%s%d%s%d%s%d\n", 308 "cvcd:: PID:", pid, 309 ", New TS priority:", 310 tsparmsp->ts_upri, 311 ", TS max_pri:", tsmaxpri); 312 } 313 } 314 } 315 } 316 317 if (debug == 1) { 318 cvcd_err(LOG_DEBUG, "tport = %d, debug = %d", tport, debug); 319 } 320 321 if (tport == 0) { 322 if ((se = getservbyname(CVCD_SERVICE, "tcp")) == NULL) { 323 cvcd_err(LOG_ERR, "getservbyname(%s) not found", 324 CVCD_SERVICE); 325 exit(1); 326 } 327 tport = se->s_port; 328 } 329 330 cvcd_ssp = t_open(TCP_DEV, O_RDWR, &tinfo); 331 if (cvcd_ssp == -1) { 332 cvcd_err(LOG_ERR, "t_open: %s", t_errlist[t_errno]); 333 exit(1); 334 } 335 336 /* 337 * Set the SO_REUSEADDR option for this TLI endpoint. 338 */ 339 cvcd_setopt(cvcd_ssp, SO_REUSEADDR); 340 341 /* 342 * Set the SO_DONTROUTE option for this TLI endpoint, if 343 * /etc/ssphostname exists. 344 */ 345 if (peercheck == 1) 346 cvcd_setopt(cvcd_ssp, SO_DONTROUTE); 347 348 /* 349 * Bind it. 350 */ 351 if (((reqb = (struct t_bind *)t_alloc(cvcd_ssp, T_BIND, T_ALL)) 352 == (struct t_bind *)NULL)) { 353 cvcd_err(LOG_ERR, "%s", t_errlist[t_errno]); 354 exit(1); 355 } 356 if (((retb = (struct t_bind *)t_alloc(cvcd_ssp, T_BIND, T_ALL)) 357 == (struct t_bind *)NULL)) { 358 cvcd_err(LOG_ERR, "%s", t_errlist[t_errno]); 359 exit(1); 360 } 361 reqb->qlen = 1; 362 reqb->addr.len = sizeof (struct sockaddr_in); 363 sin = (struct sockaddr_in *)reqb->addr.buf; 364 (void) memset((void *)sin, 0, sizeof (struct sockaddr_in)); 365 sin->sin_family = AF_INET; 366 367 368 sin->sin_addr.s_addr = htonl(INADDR_ANY); 369 sin->sin_port = htons(tport); 370 if (t_bind(cvcd_ssp, reqb, retb) == -1) { 371 cvcd_err(LOG_ERR, "t_bind: %s", t_errlist[t_errno]); 372 exit(1); 373 } 374 sin = (struct sockaddr_in *)retb->addr.buf; 375 if (sin->sin_port != htons(tport)) { 376 cvcd_err(LOG_ERR, "t_bind: bound to wrong port"); 377 cvcd_err(LOG_ERR, "Wanted %d, got %d", tport, 378 ntohs(sin->sin_port)); 379 exit(1); 380 } 381 382 t_free((char *)reqb, T_BIND); 383 t_free((char *)retb, T_BIND); 384 385 386 /* 387 * Wait for connect from OBP. 388 */ 389 cvcd_pfd[2].fd = cvcd_ssp; 390 cvcd_pfd[2].events = POLLIN|POLLPRI; 391 if ((event = poll(&cvcd_pfd[2], 1, -1)) == -1) { 392 cvcd_err(LOG_ERR, "poll: %s", strerror(errno)); 393 exit(1); 394 } 395 /* 396 * cvcd_connect sets global 397 * connected = 1 if successful. 398 */ 399 cvcd_connect(cvcd_ssp, cvcd_pfd); 400 401 /* 402 * Now set up the Network Console redirection driver. 403 */ 404 rconsfd = open(CVCREDIR_DEV, O_RDWR|O_NDELAY); 405 if (rconsfd < 0) { 406 cvcd_err(LOG_ERR, "open: %s", strerror(errno)); 407 exit(1); 408 } 409 410 /* 411 * cvcd_pfd holds three file descriptors we need to poll from: 412 * 0 will be connected to in_cvcd; 1 is the CVC Redirection driver; 413 * and 2 is the listen endpoint for new connections. 414 */ 415 cvcd_pfd[1].fd = rconsfd; 416 cvcd_pfd[1].events = POLLIN; 417 /* 418 * Loop through main service routine. We check for inbound in.cvcd 419 * connection and data xfer between host and in.cvcd. 420 */ 421 for (;;) { 422 423 char buf[MAXPKTSZ]; 424 425 /* 426 * Check for in_cvcd connect requests. 427 */ 428 switch ((event = t_look(cvcd_ssp))) { 429 case -1 : 430 cvcd_err(LOG_ERR, "%s", t_errlist[t_errno]); 431 exit(1); 432 /* NOTREACHED */ 433 break; 434 case 0 : /* Nothing to do */ 435 break; 436 case T_LISTEN : 437 if (connected == 1) { 438 /* 439 * Someone already connected. 440 */ 441 cvcd_reject(cvcd_ssp); 442 } else { 443 /* 444 * cvcd_connect sets global 445 * connected = 1 if successful. 446 */ 447 cvcd_connect(cvcd_ssp, cvcd_pfd); 448 449 /* 450 * Re-open the cvcredir driver if 451 * the netcon_fail is true. This 452 * indicates there was a previous 453 * network connection that got closed. 454 */ 455 if (netcon_fail) { 456 rconsfd = open(CVCREDIR_DEV, 457 O_RDWR|O_NDELAY); 458 if (rconsfd < 0) { 459 cvcd_err(LOG_ERR, 460 "open: %s", 461 strerror(errno)); 462 exit(1); 463 } 464 cvcd_pfd[1].fd = rconsfd; 465 cvcd_pfd[1].events = POLLIN; 466 netcon_fail = 0; 467 } 468 } 469 break; 470 default : 471 cvcd_err(LOG_ERR, 472 "Illegal event %d for cvcd_ssp", event); 473 exit(1); 474 } 475 /* 476 * Take a look for console I/O or connect request. 477 */ 478 if ((event = poll(cvcd_pfd, 3, -1)) == -1) { 479 cvcd_err(LOG_ERR, "poll: %s", strerror(errno)); 480 exit(1); 481 } 482 483 /* 484 * The following for loop is to detect any bad 485 * things(ie hangup,errors,invalid fd) have happened 486 * to the file descriptors we're interested in. 487 * If so, disconnect current network console connection. 488 */ 489 for (j = 0; j < 3; j++) { 490 if (cvcd_pfd[j].revents & (POLLERR|POLLHUP|POLLNVAL)) { 491 cvcd_err(LOG_WARNING, 492 "poll: status on %s fd:%s%s%s", 493 ((j == 2) ? "listen" : 494 ((j == 0) ? "network" : "redir")), 495 (cvcd_pfd[j].revents & POLLERR) ? 496 " error" : "", 497 (cvcd_pfd[j].revents & POLLHUP) ? 498 " hangup" : "", 499 (cvcd_pfd[j].revents & POLLNVAL) ? 500 " bad fd" : ""); 501 502 (void) t_close(cvcd_pfd[0].fd); 503 cvcd_pfd[0].fd = -1; 504 505 (void) close(cvcd_pfd[1].fd); 506 cvcd_pfd[1].fd = -1; 507 connected = 0; 508 netcon_fail = 1; 509 break; 510 } 511 } 512 513 /* 514 * Check if dummy netcon_fail flag is set, if set returns 515 * to the beginning of the main service routine. 516 */ 517 if (netcon_fail) 518 continue; 519 520 if (event != 0) { 521 if (cvcd_pfd[0].revents == POLLIN) { 522 /* 523 * Process cvcd_ssp data and commands. 524 */ 525 cvcd_read(cvcd_pfd); 526 } 527 if (cvcd_pfd[1].revents == POLLIN) { 528 int s; 529 530 if ((s = read(rconsfd, buf, MAXPKTSZ)) == -1) { 531 cvcd_err(LOG_ERR, "read: %s", 532 strerror(errno)); 533 exit(1); 534 } 535 if ((s > 0) && (connected == 1)) { 536 if (write(cvcd_pfd[0].fd, buf, s) != 537 s) { 538 cvcd_err(LOG_ERR, 539 "lost data output"); 540 } 541 } 542 } 543 } 544 } /* End forever loop */ 545 546 #ifdef lint 547 /* NOTREACHED */ 548 return (1); 549 #endif /* lint */ 550 } 551 552 static void 553 cvcd_reject(int fd) 554 { 555 struct t_call *tcall; 556 557 tcall = (struct t_call *)t_alloc(fd, T_CALL, T_ALL); 558 if (tcall == (struct t_call *)NULL) { 559 cvcd_err(LOG_ERR, "cvcd_reject: t_alloc: %s", 560 t_errlist[t_errno]); 561 return; 562 } 563 if (t_listen(fd, tcall) == -1) { 564 if (t_errno == TNODATA) { 565 cvcd_err(LOG_ERR, "cvcd_reject: No client data!"); 566 } 567 cvcd_err(LOG_ERR, "cvcd_reject: t_listen: %s", 568 t_errlist[t_errno]); 569 t_free((char *)tcall, T_CALL); 570 return; 571 } 572 if (t_snddis(fd, tcall) < 0) { 573 cvcd_err(LOG_ERR, "cvcd_reject: t_snddis: %s", 574 t_errlist[t_errno]); 575 } 576 t_free((char *)tcall, T_CALL); 577 } 578 579 static void 580 cvcd_connect(int fd, struct pollfd *pfd) 581 { 582 struct t_call *tcall; 583 int newfd; 584 struct sockaddr_in *peer; 585 int badpeer = 1; 586 struct hostent *he; 587 struct netbuf netbuf; 588 char addr[100]; 589 ulong_t tmpaddr; /* network byte order */ 590 char **pp; 591 592 tcall = (struct t_call *)t_alloc(fd, T_CALL, T_ALL); 593 if (tcall == (struct t_call *)NULL) { 594 cvcd_err(LOG_ERR, "cvcd_connect: t_alloc: %s", 595 t_errlist[t_errno]); 596 return; 597 } 598 if (t_listen(fd, tcall) == -1) { 599 if (t_errno == TNODATA) { 600 cvcd_err(LOG_ERR, "cvcd_connect: No client data!"); 601 } 602 cvcd_err(LOG_ERR, "cnctip_connect: t_listen: %s", 603 t_errlist[t_errno]); 604 t_free((char *)tcall, T_CALL); 605 return; 606 } 607 if (pfd[0].fd != -1) { 608 cvcd_err(LOG_ERR, "cvcd_connect: no free file descriptors!"); 609 t_free((char *)tcall, T_CALL); 610 return; 611 } 612 newfd = t_open(TCP_DEV, O_RDWR|O_NDELAY, NULL); 613 if (newfd == -1) { 614 cvcd_err(LOG_ERR, "cvcd_connect: t_open: %s", 615 t_errlist[t_errno]); 616 t_free((char *)tcall, T_CALL); 617 return; 618 } 619 if (t_accept(fd, newfd, tcall) < 0) { 620 cvcd_err(LOG_ERR, "cvcd_connect: t_accept: %s", 621 t_errlist[t_errno]); 622 t_close(newfd); 623 t_free((char *)tcall, T_CALL); 624 return; 625 } 626 t_free((char *)tcall, T_CALL); 627 628 /* 629 * If /etc/ssphostname doesnt exists, dont bother verifying 630 * peer since we cant do gethostbyname. 631 */ 632 if (peercheck == 1) { 633 he = gethostbyname(ssphostname); 634 if (he == NULL) { 635 cvcd_err(LOG_ERR, "gethostbyname: %s", 636 strerror(h_errno)); 637 cvcd_err(LOG_ERR, "unable to get SSP name %s!", 638 ssphostname); 639 exit(1); 640 } 641 /* 642 * Verify peer is from specified host by comparing the 643 * address (in network byte order) of the TLI endpoint 644 * and the address (in network byte order) of the ssp 645 * (using the hostname found in /etc/ssphostname). 646 */ 647 (void) memset(addr, 0, 100); 648 netbuf.buf = addr; 649 netbuf.len = 0; 650 netbuf.maxlen = 100; 651 if (ioctl(newfd, TI_GETPEERNAME, &netbuf) < 0) { 652 cvcd_err(LOG_ERR, "ioctl(TI_GETPEERNAME): %s", 653 strerror(errno)); 654 t_close(newfd); 655 return; 656 } 657 658 /* 659 * cvcd doesn't check multi-homed ssphostname 660 * properly (only checks 1st address) 661 */ 662 peer = (struct sockaddr_in *)addr; 663 for (pp = he->h_addr_list; *pp != 0; pp++) { 664 tmpaddr = htonl(*(ulong_t *)*pp); 665 if (memcmp(&peer->sin_addr.s_addr, &tmpaddr, 666 he->h_length) == 0) { 667 badpeer = 0; 668 break; 669 } 670 } 671 672 if (badpeer) { 673 cvcd_err(LOG_ERR, CONREF); 674 cvcd_err(LOG_ERR, "remote host = %s.", 675 inet_ntoa(peer->sin_addr)); 676 t_close(newfd); 677 return; 678 } 679 } 680 pfd[0].fd = newfd; 681 pfd[0].events = POLLIN; 682 connected = 1; 683 } 684 685 /* 686 * Read in data from client. 687 */ 688 static void 689 cvcd_read(struct pollfd *pd) 690 { 691 register char *data; 692 register int fd = pd[0].fd; 693 char buf[MAXPKTSZ]; 694 int flags = 0; 695 696 data = buf; 697 698 if (pd[0].revents & POLLIN) { 699 int n; 700 701 if ((n = t_rcv(fd, data, MAXPKTSZ, &flags)) == -1) { 702 cvcd_err(LOG_ERR, "cvcd_read: t_rcv: %s", 703 t_errlist[t_errno]); 704 (void) t_close(pd[0].fd); 705 pd[0].fd = -1; 706 connected = 0; 707 return; 708 } 709 if (flags & T_EXPEDITED) { 710 if (n != 1) { 711 cvcd_err(LOG_ERR, 712 "cvcd_read: %d bytes EXD!!", 713 n); 714 } 715 /* 716 * Deal with cvcd_ssp_commands. 717 */ 718 switch (data[n-1]) { 719 case CVC_CONN_BREAK : 720 cvcd_ioctl(rconsfd, CVC_BREAK); 721 break; 722 723 case CVC_CONN_DIS : 724 (void) t_close(pd[0].fd); 725 pd[0].fd = -1; 726 cvcd_ioctl(rconsfd, CVC_DISCONNECT); 727 connected = 0; 728 break; 729 730 case CVC_CONN_STAT : 731 cvcd_status(fd); 732 break; 733 734 default : 735 cvcd_err(LOG_ERR, 736 "Illegal cmd 0x%x", buf[n-1]); 737 break; 738 } 739 } else { 740 if (((data[0] & 0377) == 0377) && 741 ((data[1] & 0377) == 0377)) { 742 /* 743 * Pass on window size changes (TIOCSWINSZ). 744 */ 745 cvcd_winch(rconsfd, data, n); 746 (void) memset(data, 0, n); 747 } else { 748 cvcd_write(buf, n); 749 } 750 } 751 } 752 753 } 754 755 static void 756 cvcd_ioctl(int fd, int flags) 757 { 758 struct strioctl cmd; 759 760 cmd.ic_cmd = flags; 761 cmd.ic_timout = 0; 762 cmd.ic_len = 0; 763 cmd.ic_dp = NULL; 764 765 if (ioctl(fd, I_STR, &cmd) == -1) { 766 cvcd_err(LOG_ERR, "cvcd_ioctl: %s", strerror(errno)); 767 exit(1); 768 } 769 } 770 771 772 /* ARGSUSED */ 773 static void 774 cvcd_status(int fd) 775 { 776 } 777 778 779 /* 780 * Write input to console - called from cvcd_read. 781 */ 782 static void 783 cvcd_write(char *data, int size) 784 { 785 int n; 786 787 if ((n = write(rconsfd, data, size)) == -1) { 788 cvcd_err(LOG_ERR, "cvcd_write: write: %s", strerror(errno)); 789 exit(1); 790 } 791 if (n != size) { 792 cvcd_err(LOG_ERR, "cvcd_write: wrote %d of %d bytes", n, size); 793 } 794 } 795 796 static void 797 usage() 798 { 799 #if defined(DEBUG) 800 (void) printf("%s [-d] [-p port]\n", progname); 801 #else 802 (void) printf("%s -r [ssp host]\n", progname); 803 #endif /* DEBUG */ 804 } 805 806 /* 807 * cvcd_err () 808 * 809 * Description: 810 * Log messages via syslog daemon. 811 * 812 * Input: 813 * code - logging code 814 * format - messages to log 815 * 816 * Output: 817 * void 818 * 819 */ 820 static void 821 cvcd_err(int code, char *format, ...) 822 { 823 va_list varg_ptr; 824 char buf[MAXPKTSZ]; 825 826 va_start(varg_ptr, format); 827 (void) vsprintf(buf, format, varg_ptr); 828 va_end(varg_ptr); 829 830 if (debug == 0) 831 syslog(code, buf); 832 else 833 (void) fprintf(stderr, "%s: %s\n", progname, buf); 834 } 835 836 /* 837 * Handle a "control" request (signaled by magic being present) 838 * in the data stream. For now, we are only willing to handle 839 * window size changes. 840 */ 841 void 842 cvcd_winch(int pty, char *cp, int n) 843 { 844 struct winsize w; 845 846 if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's') 847 return; 848 (void) memcpy(&w, cp + 4, sizeof (w)); 849 w.ws_row = ntohs(w.ws_row); 850 w.ws_col = ntohs(w.ws_col); 851 w.ws_xpixel = ntohs(w.ws_xpixel); 852 w.ws_ypixel = ntohs(w.ws_ypixel); 853 (void) ioctl(pty, TIOCSWINSZ, &w); 854 } 855 856 857 /* 858 * Return class ID and maximum priority of it. 859 * Input: 860 * name: is class name (either TS or RT). 861 * maxpri: maximum priority for the class, returned in *maxpri. 862 * Output: 863 * pc_cid: class ID 864 */ 865 static id_t 866 schedinfo(char *name, short *maxpri) 867 { 868 pcinfo_t info; 869 tsinfo_t *tsinfop; 870 rtinfo_t *rtinfop; 871 872 (void) strcpy(info.pc_clname, name); 873 if (priocntl(0L, 0L, PC_GETCID, (caddr_t)&info) == -1L) { 874 return (-1); 875 } 876 if (strcmp(name, "TS") == 0) { /* Time Shared */ 877 tsinfop = (struct tsinfo *)info.pc_clinfo; 878 *maxpri = tsinfop->ts_maxupri; 879 } else if (strcmp(name, "RT") == 0) { /* Real Time */ 880 rtinfop = (struct rtinfo *)info.pc_clinfo; 881 *maxpri = rtinfop->rt_maxpri; 882 } else { 883 return (-1); 884 } 885 return (info.pc_cid); 886 } 887 888 889 /* 890 * set the tli options for the given endpoint represented by fd 891 */ 892 static void 893 cvcd_setopt(int fd, int name) 894 { 895 struct t_optmgmt *topt, *tropt; 896 struct opthdr *sockopt; 897 int on = 1; 898 899 topt = (struct t_optmgmt *)t_alloc(fd, T_OPTMGMT, 0); 900 if (topt == NULL) { 901 cvcd_err(LOG_ERR, "t_alloc: %s", t_errlist[t_errno]); 902 exit(1); 903 } 904 tropt = (struct t_optmgmt *)t_alloc(fd, T_OPTMGMT, 0); 905 if (tropt == NULL) { 906 cvcd_err(LOG_ERR, "t_alloc: %s", t_errlist[t_errno]); 907 exit(1); 908 } 909 topt->opt.buf = (char *)malloc(sizeof (struct opthdr) + sizeof (int)); 910 topt->opt.maxlen = 0; 911 topt->opt.len = sizeof (struct opthdr) + sizeof (int); 912 topt->flags = T_NEGOTIATE; 913 sockopt = (struct opthdr *)topt->opt.buf; 914 sockopt->level = SOL_SOCKET; 915 sockopt->name = name; 916 sockopt->len = sizeof (int); 917 (void) memcpy((char *)(topt->opt.buf + sizeof (struct opthdr)), 918 (char *)&on, sizeof (on)); 919 tropt->opt.buf = (char *)malloc(sizeof (struct opthdr) + sizeof (int)); 920 tropt->opt.maxlen = sizeof (struct opthdr) + sizeof (int); 921 922 if (t_optmgmt(fd, topt, tropt) == -1) { 923 t_error("t_optmgmt"); 924 exit(1); 925 } 926 927 t_free((char *)topt, T_OPTMGMT); 928 t_free((char *)tropt, T_OPTMGMT); 929 } 930