cvcd.c revision 360e6f5e7a29d5950aa1985f56811731715da7e5
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This code implements the Starcat Virtual Console host daemon (see cvcd(1M)).
* It accepts one TCP connection at a time on a well-known port. Once a
* connection is accepted, the console redirection driver (cvcdredir(7D)) is
* opened, and console I/O is routed back and forth between the two file
* descriptors (network and redirection driver). No security is provided or
* enforced within the daemon, as the Starcat platform uses a network security
* solution that is transparent to domain-side daemons.
*/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stropts.h>
#include <signal.h>
#include <syslog.h>
#include <locale.h>
#include <limits.h>
#include <sys/priocntl.h>
#include <sys/tspriocntl.h>
#include <sys/rtpriocntl.h>
#include <netdb.h>
#include <tiuser.h>
#include <sys/sc_cvcio.h>
/*
* Misc. defines.
*/
#define NETWORK_PFD 0
#define REDIR_PFD 1
#define LISTEN_PFD 2
#define NUM_PFDS 3
/*
* Function prototypes
*/
static void cvcd_set_priority(void);
static int cvcd_init_host_socket(int port);
static void cvcd_do_network_console(void);
static void cvcd_usage(void);
/*
* Globals
*/
static char progname[MAXPATHLEN];
static int debug = 0;
int
{
int err;
int opt;
int tport = 0;
char *hostname;
int fd;
int i;
char prefix[256];
#ifdef DEBUG
#else
#endif
switch (opt) {
#ifdef DEBUG
break;
break;
#endif /* DEBUG */
default : cvcd_usage();
exit(1);
}
}
perror("HOSTNAME not defined");
exit(1);
}
/*
* hostname may still be NULL, depends on when cvcd was started
* in the boot sequence. If it is NULL, try one more time
*/
/*
* we reuse the utsname.nodename buffer here! hostname
* already points to it.
*/
"failed to acquire hostname");
} else {
}
}
}
/*
* If all attempts to get the hostname have failed, put something
* meaningful in the buffer.
*/
}
/*
* Must be root.
*/
exit(1);
}
/*
* Daemonize...
*/
if (debug == 0) {
for (i = 0; i < OPEN_MAX; i++) {
if (debug && (i == STDERR_FILENO)) {
/* Don't close stderr in debug mode! */
continue;
}
(void) close(i);
}
(void) chdir("/");
(void) umask(0);
if (fork() != 0) {
exit(0);
}
(void) setpgrp();
}
/*
* Initialize the array of pollfds used to track the listening socket,
* the connection to the console redirection driver, and the network
* connection.
*/
for (i = 0; i < NUM_PFDS; i++) {
}
/* SPR 94004 */
/*
* SPR 83644: cvc and kadb are not compatible under heavy loads.
* Fix: will give cvcd highest TS priority at execution time.
*/
/*
* If not already determined by a command-line flag, figure out which
* port we're supposed to be listening on.
*/
if (tport == 0) {
exit(1);
}
}
if (debug == 1) {
}
/*
* Attempt to initialize the socket we'll use to listen for incoming
* connections. No need to check the return value, as the call will
* exit if it fails.
*/
/*
* Now that we're all set up, we loop forever waiting for connections
* (one at a time) and then driving network console activity over them.
*/
for (;;) {
/*
* Start by waiting for an incoming connection.
*/
do {
if (err == -1) {
exit(1);
}
if ((err > 0) &&
exit(1);
}
}
} while (fd == -1);
/*
* We have a connection. Set the new socket nonblocking, and
* initialize the appropriate pollfd. In theory, the new socket
* is _already_ non-blocking because accept() is supposed to
* hand us a socket with the same properties as the socket we're
* listening on, but it won't hurt to make sure.
*/
opt = 1;
if (err == -1) {
continue;
}
/*
* Since we're ready to do network console stuff, go ahead and
* open the Network Console redirection driver, which will
* switch traffic from the IOSRAM path to the network path if
* the network path has been selected in cvc.
*/
if (fd == -1) {
exit(1);
}
/*
* We have a network connection and we have the redirection
* driver open, so drive the network console until something
* changes.
*/
/*
* cvcd_do_network_console doesn't return until there's a
* problem, so we need to close the network connection and the
* redirection driver and start the whole loop over again.
*/
}
/* NOTREACHED */
return (1);
}
/*
* cvcd_set_priority
*
* DESCRIBE
* SPR 83644: cvc and kadb are not compatible under heavy loads.
* Fix: will give cvcd highest TS priority at execution time.
*/
static void
cvcd_set_priority(void)
{
short tsmaxpri;
/* Get scheduler properties for this PID */
return;
}
/* Get class ID and maximum priority for TS process class */
return;
}
/* Print priority info in debug mode */
if (debug) {
"PID: %d, current priority: %d, Max priority: %d.",
}
}
/* Change proc's priority to maxtspri */
}
/* Print new priority info in debug mode */
if (debug) {
-1L) {
} else {
}
}
}
/*
* cvcd_init_host_socket
*
* Given a TCP port number, create and initialize a socket appropriate for
* accepting incoming connections to that port.
*/
static int
{
int err;
int fd;
int optval;
struct sockaddr_in6 sin6;
/*
* Start by creating the socket, which needs to support IPv6.
*/
if (fd == -1) {
exit(1);
}
/*
* Set the SO_REUSEADDR option, and make the socket non-blocking.
*/
optval = 1;
if (err == -1) {
exit(1);
}
if (err == -1) {
exit(1);
}
/*
* Bind the socket to our local address and port.
*/
if (err == -1) {
exit(1);
}
/*
* Indicate that we want to accept connections on this socket. Since we
* only allow one connection at a time anyway, specify a maximum backlog
* of 1.
*/
if (err == -1) {
exit(1);
}
return (fd);
}
/*
* cvcd_do_network_console
*
* With established connections to the network and the redirection driver,
* shuttle data between the two until something goes wrong.
*/
static void
cvcd_do_network_console(void)
{
int i;
int err;
int count;
short revents;
int input_len = 0;
int output_len = 0;
int input_off = 0;
int output_off = 0;
char output_buf[MAXPKTSZ];
for (;;) {
/*
* Wait for activity on any of the open file descriptors, which
* includes the ability to write data if we have any to write.
* If poll() fails, break out of the network console processing
* loop.
*/
if (output_len != 0) {
}
if (input_len != 0) {
}
if (err == -1) {
break;
}
/*
* If any errors or hangups were detected, or one of our file
* descriptors is bad, bail out of the network console
* processing loop.
*/
for (i = 0; i < NUM_PFDS; i++) {
"poll: status on %s fd:%s%s%s",
((i == LISTEN_PFD) ? "listen" :
goto fail; /* 'break' wouldn't work here */
}
}
/*
* Start by rejecting any connection attempts, since we only
* allow one network connection at a time.
*/
int fd;
if (fd > 0) {
}
}
/*
* If we have data waiting to be written in one direction or the
* other, go ahead and try to send the data on its way. We're
* going to attempt the writes regardless of whether the poll
* indicated that the destinations are ready, because we want to
* find out if either descriptor has a problem (e.g. broken
* network link).
* If an "unexpected" error is detected, give up and break out
* of the network console processing loop.
*/
if (output_len != 0) {
break;
} else if (count > 0) {
output_len -= count;
if (output_len == 0) {
output_off = 0;
} else {
output_off += count;
}
}
}
if (input_len != 0) {
break;
} else if (count > 0) {
if (input_len == 0) {
input_off = 0;
} else {
}
}
}
/*
* Finally, take a look at each data source and, if there isn't
* any residual data from that source still waiting to be
* processed, see if more data can be read. We don't want to
* read more data from a source if we haven't finished
* processing the last data we read from it because doing so
* would maximize the amount of data lost if the network console
* failed or was closed.
* If an "unexpected" error is detected, give up and break out
* of the network console processing loop.
* The call to read() appears to be in the habit of returning 0
* when you've read all of the data from a stream that has been
* hung up, and poll apparently feels that that condition
* justifies setting POLLIN, so we're going to treat 0 as an
* error return from read().
*/
if (count <= 0) {
/*
* Reading 0 simply means there is no data
* available, since this is a terminal.
*/
break;
}
} else {
output_len = count;
output_off = 0;
}
}
if (count <= 0) {
/*
* Reading 0 here implies a hangup, since this
* is a non-blocking socket that poll() reported
* as having data available. This will
* typically occur when the console user drops
* to OBP or intentially switches to IOSRAM
* mode.
*/
if (count == 0) {
"read(network): hangup detected");
break;
break;
}
} else {
input_off = 0;
}
}
} /* End forever loop */
/*
* If we get here, something bad happened during an attempt to access
* either the redirection driver or the network connection. There
* doesn't appear to be any way to avoid the possibility of losing
* the loss if it happens.
* XXX - We could do more, but is it worth the effort? Logging the
* lost data would be pretty easy... actually preserving it
* in the console flow would be a lot harder. We're more robust
* than the previous generation at this point, at least, so
* perhaps that's enough for now?
*/
fail:
if (input_len != 0) {
}
if (output_len != 0) {
}
}
static void
{
#if defined(DEBUG)
#else
#endif /* DEBUG */
}
/*
* cvcd_err ()
*
* Description:
* Log messages via syslog daemon.
*
* Input:
* code - logging code
* format - messages to log
*
* Output:
* void
*
*/
static void
{
if (debug == 0) {
} else {
}
}