static void _sendto (cbuf_t cbuf, int fd, struct sockaddr_in *destaddr) { int n, rv; uint8_t buf[IPMIPOWER_PACKET_BUFLEN]; if ((n = cbuf_read (cbuf, buf, IPMIPOWER_PACKET_BUFLEN)) < 0) { IPMIPOWER_ERROR (("cbuf_read: %s", fd, strerror (errno))); exit (EXIT_FAILURE); } if (n == IPMIPOWER_PACKET_BUFLEN) { IPMIPOWER_ERROR (("cbuf_read: buffer full")); exit (EXIT_FAILURE); } do { if (cmd_args.common_args.driver_type == IPMI_DEVICE_LAN) rv = ipmi_lan_sendto (fd, buf, n, 0, (struct sockaddr *)destaddr, sizeof (struct sockaddr_in)); else { if (ipmi_is_ipmi_1_5_packet (buf, n)) rv = ipmi_lan_sendto (fd, buf, n, 0, (struct sockaddr *)destaddr, sizeof (struct sockaddr_in)); else rv = ipmi_rmcpplus_sendto (fd, buf, n, 0, (struct sockaddr *)destaddr, sizeof (struct sockaddr_in)); } } while (rv < 0 && errno == EINTR); if (rv < 0) { IPMIPOWER_ERROR (("ipmi_lan/rmcpplus_sendto: %s", strerror (errno))); exit (EXIT_FAILURE); } /* cbuf should be empty now */ if (!cbuf_is_empty (cbuf)) { IPMIPOWER_ERROR (("cbuf not empty")); exit (EXIT_FAILURE); } }
cbsize_t cbuf_read (cbuf_t* cb, char* data, cbsize_t desired_len) { // cb->read must always be equal when using this function // (do not use cbuf_*_ptr) cbsize_t want = desired_len; cbsize_t ret = 0; while (want && !cbuf_is_empty(cb)) { cbsize_t readable_len = cb->read >= cb->write? cb->size - cb->read: cb->write - cb->read; cbsize_t chunk = (want > readable_len)? readable_len: want; memcpy(data + ret, cb->buf + cb->read, chunk); ret += chunk; want -= chunk; if ((cb->read += chunk) == cb->size) cb->read = 0; if ((cb->empty = cb->allread = (cb->read == cb->write))) do_flush(cb); // bigger_chunk, less loop } return ret; }
/* _poll_loop * - poll on all descriptors */ static void _poll_loop (int non_interactive) { int nfds = 0; struct pollfd *pfds = NULL; int extra_fds; /* number of fds for stdin and stdout we'll need when polling * * Right now, always poll stdout. When non-interactive, * don't need stdin */ extra_fds = 1 + (non_interactive ? 0 : 1); while (non_interactive || ipmipower_prompt_process_cmdline ()) { int i, num, timeout; int powercmd_timeout = -1; int ping_timeout = -1; /* If there are no pending commands before this call, * powercmd_timeout will not be set, leaving it at -1 */ num = ipmipower_powercmd_process_pending (&powercmd_timeout); if (non_interactive && !num) break; /* ping timeout is always set if cmd_args.ping_interval > 0 */ ipmipower_ping_process_pings (&ping_timeout); if (cmd_args.ping_interval) { if (powercmd_timeout == -1) timeout = ping_timeout; else timeout = (ping_timeout < powercmd_timeout) ? ping_timeout : powercmd_timeout; } else timeout = powercmd_timeout; /* achu: I always wonder if this poll() loop could be done far * more elegantly and efficiently without all this crazy * indexing, perhaps through a callback/event mechanism. It'd * probably be more efficient, since most callback/event based * models have min-heap like structures inside for determining * what things timed out. Overall though, I don't think the O(n) * (n being hosts/fds) processing is really that inefficient for * this particular application and is not worth going back and * changing. By going to a callback/event mechanism, there will * still be some O(n) activities within the code, so I am only * going to create a more efficient O(n) poll loop. */ /* Has the number of hosts changed? */ if (nfds != (ics_len*2) + extra_fds) { /* The "*2" is for each host's two fds, one for ipmi * (ipmi_fd) and one for rmcp (ping_fd). */ nfds = (ics_len*2) + extra_fds; free (pfds); if (!(pfds = (struct pollfd *)malloc (nfds * sizeof (struct pollfd)))) { IPMIPOWER_ERROR (("malloc: %s", strerror (errno))); exit (EXIT_FAILURE); } } for (i = 0; i < ics_len; i++) { pfds[i*2].fd = ics[i].ipmi_fd; pfds[i*2+1].fd = ics[i].ping_fd; pfds[i*2].events = pfds[i*2+1].events = 0; pfds[i*2].revents = pfds[i*2+1].revents = 0; pfds[i*2].events |= POLLIN; if (!cbuf_is_empty (ics[i].ipmi_out)) pfds[i*2].events |= POLLOUT; if (!cmd_args.ping_interval) continue; pfds[i*2+1].events |= POLLIN; if (!cbuf_is_empty (ics[i].ping_out)) pfds[i*2+1].events |= POLLOUT; } if (!non_interactive) { pfds[nfds-2].fd = STDIN_FILENO; pfds[nfds-2].events = POLLIN; pfds[nfds-2].revents = 0; } pfds[nfds-1].fd = STDOUT_FILENO; if (!cbuf_is_empty (ttyout)) pfds[nfds-1].events = POLLOUT; else pfds[nfds-1].events = 0; pfds[nfds-1].revents = 0; ipmipower_poll (pfds, nfds, timeout); for (i = 0; i < ics_len; i++) { if (pfds[i*2].revents & POLLERR) { IPMIPOWER_DEBUG (("host = %s; IPMI POLLERR", ics[i].hostname)); /* See comments in _ipmi_recvfrom() regarding ECONNRESET/ECONNREFUSED */ _recvfrom (ics[i].ipmi_in, ics[i].ipmi_fd, &(ics[i].destaddr)); } else { if (pfds[i*2].revents & POLLIN) _recvfrom (ics[i].ipmi_in, ics[i].ipmi_fd, &(ics[i].destaddr)); if (pfds[i*2].revents & POLLOUT) _sendto (ics[i].ipmi_out, ics[i].ipmi_fd, &(ics[i].destaddr)); } if (!cmd_args.ping_interval) continue; if (pfds[i*2+1].revents & POLLERR) { IPMIPOWER_DEBUG (("host = %s; PING_POLLERR", ics[i].hostname)); _recvfrom (ics[i].ping_in, ics[i].ping_fd, &(ics[i].destaddr)); } else { if (pfds[i*2+1].revents & POLLIN) _recvfrom (ics[i].ping_in, ics[i].ping_fd, &(ics[i].destaddr)); if (pfds[i*2+1].revents & POLLOUT) _sendto (ics[i].ping_out, ics[i].ping_fd, &(ics[i].destaddr)); } } if (!non_interactive && (pfds[nfds-2].revents & POLLIN)) { int n, dropped = 0; if ((n = cbuf_write_from_fd (ttyin, STDIN_FILENO, -1, &dropped)) < 0) { IPMIPOWER_ERROR (("cbuf_write_from_fd: %s", strerror (errno))); exit (EXIT_FAILURE); } /* achu: If you are running ipmipower in co-process mode * with powerman, this error condition will probably be hit * with the file descriptor STDIN_FILENO. The powerman * daemon is usually closed by /etc/init.d/powerman stop, * which kills a process through a signal. Thus, powerman * closes stdin and stdout pipes to ipmipower and the call * to cbuf_write_from_fd will give us an EOF reading. We'll * consider this EOF an "ok" error. No need to output an * error message. */ if (!n) exit (EXIT_FAILURE); if (dropped) IPMIPOWER_DEBUG (("cbuf_write_from_fd: read dropped %d bytes", dropped)); } if (!cbuf_is_empty (ttyout) && (pfds[nfds-1].revents & POLLOUT)) { if (cbuf_read_to_fd (ttyout, STDOUT_FILENO, -1) < 0) { IPMIPOWER_ERROR (("cbuf_read_to_fd: %s", strerror (errno))); exit (EXIT_FAILURE); } } } free (pfds); }
static void _recvfrom (cbuf_t cbuf, int fd, struct sockaddr_in *srcaddr) { int n, rv, dropped = 0; uint8_t buf[IPMIPOWER_PACKET_BUFLEN]; struct sockaddr_in from; unsigned int fromlen = sizeof (struct sockaddr_in); do { /* For receive side, ipmi_lan_recvfrom and * ipmi_rmcpplus_recvfrom are identical. So we just use * ipmi_lan_recvfrom for both. * * In event of future change, should use util functions * ipmi_is_ipmi_1_5_packet or ipmi_is_ipmi_2_0_packet * appropriately. */ rv = ipmi_lan_recvfrom (fd, buf, IPMIPOWER_PACKET_BUFLEN, 0, (struct sockaddr *)&from, &fromlen); } while (rv < 0 && errno == EINTR); /* achu & hliebig: * * Premise from ipmitool (http://ipmitool.sourceforge.net/) * * On some OSes (it seems Unixes), the behavior is to not return * port denied errors up to the client for UDP responses (i.e. you * need to timeout). But on some OSes (it seems Windows), the * behavior is to return port denied errors up to the user for UDP * responses via ECONNRESET or ECONNREFUSED. * * If this were just the case, we could return or handle errors * properly and move on. However, it's not the case. * * According to Ipmitool, on some motherboards, both the OS and the * BMC are capable of responding to an IPMI request. That means you * can get an ECONNRESET or ECONNREFUSED, then later on, get your * real IPMI response. * * Our solution is copied from Ipmitool, we'll ignore some specific * errors and try to read again. * * If the ECONNREFUSED or ECONNRESET is from the OS, but we will get * an IPMI response later, the recvfrom later on gets the packet we * want. * * If the ECONNREFUSED or ECONNRESET is from the OS but there is no * BMC (or IPMI disabled, etc.), just do the recvfrom again to * eventually get a timeout, which is the behavior we'd like. */ if (rv < 0 && (errno == ECONNRESET || errno == ECONNREFUSED)) { IPMIPOWER_DEBUG (("ipmi_lan_recvfrom: connection refused: %s", strerror (errno))); return; } if (rv < 0) { IPMIPOWER_ERROR (("ipmi_lan_recvfrom: %s", strerror (errno))); exit (EXIT_FAILURE); } if (!rv) { IPMIPOWER_ERROR (("ipmi_lan_recvfrom: EOF")); exit (EXIT_FAILURE); } /* Don't store if this packet is strange for some reason */ if (from.sin_family != AF_INET || from.sin_addr.s_addr != srcaddr->sin_addr.s_addr) return; /* cbuf should be empty, but if it isn't, empty it */ if (!cbuf_is_empty (cbuf)) { IPMIPOWER_DEBUG (("cbuf not empty, draining")); do { uint8_t tempbuf[IPMIPOWER_PACKET_BUFLEN]; if (cbuf_read (cbuf, tempbuf, IPMIPOWER_PACKET_BUFLEN) < 0) { IPMIPOWER_ERROR (("cbuf_read: %s", strerror (errno))); exit (EXIT_FAILURE); } } while(!cbuf_is_empty (cbuf)); } if ((n = cbuf_write (cbuf, buf, rv, &dropped)) < 0) { IPMIPOWER_ERROR (("cbuf_write: %s", strerror (errno))); exit (EXIT_FAILURE); } if (n != rv) { IPMIPOWER_ERROR (("cbuf_write: rv=%d n=%d", rv, n)); exit (EXIT_FAILURE); } if (dropped) IPMIPOWER_DEBUG (("cbuf_write: read dropped %d bytes", dropped)); }
static int _pstdout_output_finish(pstdout_state_t pstate) { int pstate_mutex_locked = 0; int rc, rv = -1; assert(pstate); assert(pstate->magic == PSTDOUT_STATE_MAGIC); assert(pstate->p_stdout); assert(pstate->p_stderr); if ((rc = pthread_mutex_lock(&(pstate->mutex)))) { if (pstdout_debug_flags & PSTDOUT_DEBUG_STANDARD) fprintf(stderr, "pthread_mutex_lock: %s\n", strerror(rc)); pstdout_errnum = PSTDOUT_ERR_INTERNAL; goto cleanup; } pstate_mutex_locked++; /* If there is remaining junk in the cbufs, write a "\n" to it so we * finish off the line and get it flushed out. */ if (!cbuf_is_empty(pstate->p_stdout)) _pstdout_print_wrapper(pstate, 1, stdout, "\n"); if (!cbuf_is_empty(pstate->p_stderr)) _pstdout_print_wrapper(pstate, 1, stderr, "\n"); if (_pstdout_output_buffer_data(pstate, stdout, &(pstate->buffer_stdout), &(pstate->buffer_stdout_len), PSTDOUT_OUTPUT_STDOUT_PREPEND_HOSTNAME, PSTDOUT_OUTPUT_BUFFER_STDOUT, PSTDOUT_OUTPUT_STDOUT_CONSOLIDATE, pstdout_consolidated_stdout, &pstdout_consolidated_stdout_mutex) < 0) goto cleanup; if (_pstdout_output_buffer_data(pstate, stderr, &(pstate->buffer_stderr), &(pstate->buffer_stderr_len), PSTDOUT_OUTPUT_STDERR_PREPEND_HOSTNAME, PSTDOUT_OUTPUT_BUFFER_STDERR, PSTDOUT_OUTPUT_STDERR_CONSOLIDATE, pstdout_consolidated_stderr, &pstdout_consolidated_stderr_mutex) < 0) goto cleanup; /* Only output from internal to pstdout is allowed */ pstate->no_more_external_output = 1; rv = 0; cleanup: if (pstate_mutex_locked) { if ((rc = pthread_mutex_unlock(&(pstate->mutex)))) { if (pstdout_debug_flags & PSTDOUT_DEBUG_STANDARD) fprintf(stderr, "pthread_mutex_unlock: %s\n", strerror(rc)); /* Don't change error code, just move on */ } } return rv; }