static void check_console_state(obj_t *console, obj_t *client) { /* Checks the state of the console and warns the client if needed. * Informs the newly-connected client if strange things are afoot. * Attempts an immediate reconnect if the console connection is down. */ char buf[MAX_LINE]; assert(is_console_obj(console)); assert(is_client_obj(client)); if (is_process_obj(console) && (console->fd < 0)) { snprintf(buf, sizeof(buf), "%sConsole [%s] is currently disconnected from \"%s\"%s", CONMAN_MSG_PREFIX, console->name, console->aux.process.prog, CONMAN_MSG_SUFFIX); strcpy(&buf[sizeof(buf) - 3], "\r\n"); write_obj_data(client, buf, strlen(buf), 1); open_process_obj(console); } else if (is_serial_obj(console) && (console->fd < 0)) { snprintf(buf, sizeof(buf), "%sConsole [%s] is currently disconnected from \"%s\"%s", CONMAN_MSG_PREFIX, console->name, console->aux.serial.dev, CONMAN_MSG_SUFFIX); strcpy(&buf[sizeof(buf) - 3], "\r\n"); write_obj_data(client, buf, strlen(buf), 1); open_serial_obj(console); } else if (is_telnet_obj(console) && (console->aux.telnet.state != CONMAN_TELNET_UP)) { snprintf(buf, sizeof(buf), "%sConsole [%s] is currently disconnected from <%s:%d>%s", CONMAN_MSG_PREFIX, console->name, console->aux.telnet.host, console->aux.telnet.port, CONMAN_MSG_SUFFIX); strcpy(&buf[sizeof(buf) - 3], "\r\n"); write_obj_data(client, buf, strlen(buf), 1); console->aux.telnet.delay = TELNET_MIN_TIMEOUT; /* * Do not call connect_telnet_obj() while in the PENDING state since * it would be misinterpreted as the completion of the non-blocking * connect(). */ if (console->aux.telnet.state == CONMAN_TELNET_DOWN) { open_telnet_obj(console); } } else if (is_unixsock_obj(console) && (console->fd < 0)) { assert(console->aux.unixsock.state == CONMAN_UNIXSOCK_DOWN); snprintf(buf, sizeof(buf), "%sConsole [%s] is currently disconnected from \"%s\"%s", CONMAN_MSG_PREFIX, console->name, console->aux.unixsock.dev, CONMAN_MSG_SUFFIX); strcpy(&buf[sizeof(buf) - 3], "\r\n"); write_obj_data(client, buf, strlen(buf), 1); open_unixsock_obj(console); } #if WITH_FREEIPMI else if (is_ipmi_obj(console) && (console->aux.ipmi.state != CONMAN_IPMI_UP)) { snprintf(buf, sizeof(buf), "%sConsole [%s] is currently disconnected from <%s>%s", CONMAN_MSG_PREFIX, console->name, console->aux.ipmi.host, CONMAN_MSG_SUFFIX); strcpy(&buf[sizeof(buf) - 3], "\r\n"); write_obj_data(client, buf, strlen(buf), 1); if (console->aux.ipmi.state == CONMAN_IPMI_DOWN) { open_ipmi_obj(console); } } #endif /* WITH_FREEIPMI */ return; }
static void mux_io(server_conf_t *conf) { /* Multiplexes I/O between all of the objs in the configuration. * This routine is the heart of ConMan. */ ListIterator i; int n; obj_t *obj; int inevent_fd; assert(conf->tp != NULL); assert(!list_is_empty(conf->objs)); i = list_iterator_create(conf->objs); while (!done) { if (reconfig) { /* * FIXME: A reconfig should pro'ly resurrect "downed" serial objs * and reset reconnect timers of "downed" telnet objs. */ log_msg(LOG_NOTICE, "Performing reconfig on signal=%d", reconfig); reopen_logfiles(conf); reconfig = 0; } /* FIXME: Switch from recomputing the tpoll set on each loop iteration * to modifying it based on events. This will eliminate the 1sec * tpoll() sleep timeout and greatly reduce cpu utilization. * It will also eliminate the maze of twisty conditions below. */ DPRINTF((25, "Recomputing tpoll fd set\n")); (void) tpoll_zero(conf->tp, TPOLL_ZERO_FDS); tpoll_set(conf->tp, conf->ld, POLLIN); inevent_fd = inevent_get_fd(); if (inevent_fd >= 0) { tpoll_set(conf->tp, inevent_get_fd(), POLLIN); } list_iterator_reset(i); while ((obj = list_next(i))) { if (obj->gotReset) { reset_console(obj, conf->resetCmd); } if (obj->fd < 0) { continue; } if ( ( ( is_telnet_obj(obj) && obj->aux.telnet.state == CONMAN_TELNET_UP ) || ( is_process_obj(obj) && obj->aux.process.state == CONMAN_PROCESS_UP ) || #if WITH_FREEIPMI ( is_ipmi_obj(obj) && obj->aux.ipmi.state == CONMAN_IPMI_UP ) || #endif /* WITH_FREEIPMI */ ( is_unixsock_obj(obj) && obj->aux.unixsock.state == CONMAN_UNIXSOCK_UP ) || is_serial_obj(obj) || is_client_obj(obj) ) && ( ! obj->gotEOF ) ) { tpoll_set(conf->tp, obj->fd, POLLIN); } if ( ( (obj->bufInPtr != obj->bufOutPtr) || (obj->gotEOF) ) && ( ! (is_telnet_obj(obj) && obj->aux.telnet.state != CONMAN_TELNET_UP) ) && ( ! (is_process_obj(obj) && obj->aux.process.state != CONMAN_PROCESS_UP) ) && #if WITH_FREEIPMI ( ! (is_ipmi_obj(obj) && obj->aux.ipmi.state != CONMAN_IPMI_UP) ) && #endif /* WITH_FREEIPMI */ ( ! (is_unixsock_obj(obj) && obj->aux.unixsock.state != CONMAN_UNIXSOCK_UP) ) && ( ! (is_client_obj(obj) && obj->aux.client.gotSuspend) ) ) { tpoll_set(conf->tp, obj->fd, POLLOUT); } if (is_telnet_obj(obj) && obj->aux.telnet.state == CONMAN_TELNET_PENDING) { tpoll_set(conf->tp, obj->fd, POLLIN | POLLOUT); } } DPRINTF((25, "Calling tpoll\n")); while ((n = tpoll(conf->tp, 1000)) < 0) { if (errno != EINTR) { log_err(errno, "Unable to multiplex I/O"); } else if (done || reconfig) { break; } } if (n <= 0) { continue; } if (tpoll_is_set(conf->tp, conf->ld, POLLIN)) { accept_client(conf); } if ((inevent_fd >= 0) && tpoll_is_set(conf->tp, inevent_fd, POLLIN)) { inevent_process(); } /* If read_from_obj() or write_to_obj() returns -1, * the obj's buffer has been flushed. If it is a telnet obj, * retain it and attempt to re-establish the connection; * o/w, give up and remove it from the master objs list. */ list_iterator_reset(i); while ((obj = list_next(i))) { if (obj->fd < 0) { continue; } if (is_telnet_obj(obj) && tpoll_is_set(conf->tp, obj->fd, POLLIN | POLLOUT) && (obj->aux.telnet.state == CONMAN_TELNET_PENDING)) { open_telnet_obj(obj); continue; } if (tpoll_is_set(conf->tp, obj->fd, POLLIN | POLLHUP | POLLERR)) { if (read_from_obj(obj, conf->tp) < 0) { list_delete(i); continue; } if (obj->fd < 0) { continue; } } if (tpoll_is_set(conf->tp, obj->fd, POLLOUT)) { if (write_to_obj(obj) < 0) { list_delete(i); continue; } if (obj->fd < 0) { continue; } } } } log_msg(LOG_NOTICE, "Exiting on signal=%d", done); list_iterator_destroy(i); return; }
static int check_busy_consoles(req_t *req) { /* Checks to see if a "writable" request affects any consoles * that are currently busy (unless the force or join option is enabled). * Returns 0 if the request is valid, or -1 on error. */ List busy; ListIterator i; obj_t *console; obj_t *writer; int gotBcast; char *tty; time_t t; char *delta; char buf[MAX_LINE]; assert(!list_is_empty(req->consoles)); if ((req->command == CONMAN_CMD_QUERY) || (req->command == CONMAN_CMD_MONITOR)) return(0); if (req->enableForce || req->enableJoin) return(0); busy = list_create(NULL); i = list_iterator_create(req->consoles); while ((console = list_next(i))) { assert(is_console_obj(console)); if (!list_is_empty(console->writers)) list_append(busy, console); } list_iterator_destroy(i); if (list_is_empty(busy)) { list_destroy(busy); return(0); } if (list_count(busy) == 1) { snprintf(buf, sizeof(buf), "Found console already in use"); } else { snprintf(buf, sizeof(buf), "Found %d consoles already in use", list_count(busy)); } send_rsp(req, CONMAN_ERR_BUSY_CONSOLES, buf); /* Note: the "busy" list contains object references, * so they DO NOT get destroyed here when removed from the list. */ while ((console = list_pop(busy))) { i = list_iterator_create(console->writers); while ((writer = list_next(i))) { assert(is_client_obj(writer)); x_pthread_mutex_lock(&writer->bufLock); t = writer->aux.client.timeLastRead; gotBcast = list_is_empty(writer->writers); tty = writer->aux.client.req->tty; x_pthread_mutex_unlock(&writer->bufLock); delta = create_time_delta_string(t, -1); snprintf(buf, sizeof(buf), "Console [%s] open %s by <%s@%s>%s%s (idle %s).\n", console->name, (gotBcast ? "B/C" : "R/W"), writer->aux.client.req->user, writer->aux.client.req->host, (tty ? " on " : ""), (tty ? tty : ""), (delta ? delta : "???")); buf[sizeof(buf) - 2] = '\n'; buf[sizeof(buf) - 1] = '\0'; if (delta) free(delta); if (write_n(req->sd, buf, strlen(buf)) < 0) { log_msg(LOG_NOTICE, "Unable to write to <%s:%d>: %s", req->fqdn, req->port, strerror(errno)); break; } } list_iterator_destroy(i); } list_destroy(busy); return(-1); }