1
0
Files
irix-657m-src/irix/cmd/mmscd/main.c
2022-09-29 17:59:04 +03:00

560 lines
12 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <signal.h>
#include <syslog.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/schedctl.h>
#include <sys/ioctl.h>
#include <sys/capability.h>
#include <unistd.h>
#include <math.h>
#include <getopt.h>
#include "cmd.h"
#include "load.h"
#include "mem.h"
#include "oobmsg.h"
#include "error.h"
#include "double_time.h"
#define DEFAULT_DECAY 0.25 /* Decay factor per second */
#define CONTACT_PERIOD 120 /* seconds */
#define RELEASE_WAIT 3 /* seconds; see comment below */
#define MAX_CONS_PCT 25 /* Max % of console B/W to use */
#define MAX_UPDATE_FRQ 3.0
char *opt_device;
int opt_console = 1;
int opt_nodemode = 0;
int opt_memmode = 0;
int opt_graceful;
int opt_gfx;
int opt_meter;
char *opt_debug;
int opt_baud;
double opt_period;
double opt_decay = DEFAULT_DECAY;
char *load_title = "Origin2000 System Activity";
char *mem_title = "Origin2000 Memory Utilization";
char *opt_title = NULL;
char *opt_title_gfx = "Onyx2 System Activity";
int opt_ncpus; /* Undoc. ncpus debug override */
int opt_nofork;
mmsc_t *m; /* NULL if not in contact w/ MMSC */
int graph_started; /* Flag */
int protocol; /* MMSC_PROTOCOL_VERSION number */
int ncpus;
int nnodes;
int bardim; /* Bytes of data per bar */
int want_exit;
double period;
void usage(void)
{
perr("Usage:\n");
perr(" mmscd [-c cdev] [-f sdev] [-b baud] [-u freq]\n");
perr(" [-t \"title\"] [-N cpus] [-D fctr] [-gdlMmnG]\n");
perr("Options:\n");
perr(" -c cdev Use alternate MMSC console device\n");
perr(" (default /dev/console)\n");
perr(" -f sdev Use alternate MMSC non-console device\n");
perr(" -b baud Use baud rate other than the device default\n");
perr(" (applicable only in conjunction with -f)\n");
perr(" -g When shutting down system, do it gracefully\n");
perr(" -G Include graphics information in CPU meter\n");
perr(" -d file Generate debug output to file\n");
perr(" -m Display CPU meter\n");
perr(" -u freq CPU meter update frequency in Hz (default is\n");
perr(" chosen to use at most %d%% of console bandwidth)\n",
MAX_CONS_PCT);
perr(" -D fctr Decay factor per second (default %f)\n",
DEFAULT_DECAY);
perr(" -t title Specify alternate CPU meter title\n");
perr(" -n Do not fork into the background\n");
perr(" -N cpus Simulate an different # of CPUs (testing only)\n");
perr(" -l Operate in node mode\n");
perr(" -M Display node memory instead of CPU meter (implies -l)\n");
fatal("Illegal command-line arguments\n");
}
/*
* contact
*
* Attempts to open the MMSC serial device and exchange some packets
* with the MMSC. If successful, m will be non-NULL.
*
* If silent is non-zero, contact() will not print an error message
* if contact is not made with the MMSC.
*
* The global variable m is set to non-NULL if and only if contact is
* made.
*/
static void contact(int silent)
{
debug("contact\n");
if (m) {
warning("Closing MMSC connection\n");
/*
* If MMSC is already open, an error must have occurred, so now
* we're trying to re-establish contact.
*
* Allow the MMSC to time out in case it's sending a command.
* This also serves to resynchronize if anything goes wrong, such
* as a length field was corrupted and is absurdly large, etc.
* The MMSC's timeout is 2 seconds by default.
*/
mmsc_close(m);
m = 0;
double_sleep((double) RELEASE_WAIT);
}
/*
* Open MMSC device
*/
debug("open device\n");
if ((m = mmsc_open(opt_device, opt_baud, opt_console)) == 0)
fatal("Could not open MMSC device %s\n", opt_device);
debug("ping\n");
if (cmd_ping(m) < 0) {
if (! silent) {
warning("Could not establish connection to MMSC\n");
warning("Retrying in %d seconds\n", CONTACT_PERIOD);
mmsc_warn(m); /* Write warning message to console */
}
mmsc_close(m);
m = 0;
return;
}
if ((protocol = cmd_init(m)) < 0) {
warning("Could not obtain protocol version\n");
mmsc_close(m);
m = 0;
return;
}
debug("Using protocol version %d\n", protocol);
}
/*
* drop
*
* Disconnects from the MMSC. Should be used if communication fails
* for any reason so that the inner loop will periodically try to
* contact the MMSC again.
*/
static void drop(void)
{
if (m)
mmsc_close(m);
graph_started = 0;
m = 0;
}
/*
* note_exit
*
* Signal handler for SIGINT and SIGTERM to achieve clean exit handling
* in case of these signals.
*
* If one of these signals is received a second time, it will not be
* caught and the program will die.
*/
void note_exit(void)
{
debug("received INT or TERM signal\n");
want_exit = 1;
}
/*
* cleanup
*/
void cleanup(void)
{
if (m && graph_started) {
debug("about to end graph\n");
sleep(1);
(void) cmd_graph_end(m);
debug("done ending graph\n");
}
debug("releasing\n");
drop();
}
/*
* init_graph_mode
*/
static int init_graph_mode(void)
{
int r = -1;
/*
* Initialize graph mode
*/
debug("init_graph_mode: ncpus: %u nodemode=%u\n", ncpus, opt_nodemode);
if (cmd_graph_start(m, ncpus, bardim) < 0) {
debug("Graph start failed\n");
goto done;
}
if (cmd_graph_label(m, GRAPH_LABEL_TITLE,
opt_gfx ? opt_title_gfx : opt_title) < 0)
goto done;
if (opt_nodemode) {
if (cmd_graph_label(m, GRAPH_LABEL_HORIZ, "Node") < 0)
goto done; }
else {
if (cmd_graph_label(m, GRAPH_LABEL_HORIZ, "Processor") < 0)
goto done; }
if (opt_memmode) {
char memstr[16];
if (cmd_graph_label(m, GRAPH_LABEL_VERT, "Node Memory") < 0)
goto done;
if (cmd_graph_label(m, GRAPH_LABEL_MIN, "0") < 0)
goto done;
sprintf(memstr, "%d", max_nodemem);
if (cmd_graph_label(m, GRAPH_LABEL_MAX, memstr) < 0)
goto done;
if (cmd_graph_label(m, GRAPH_LEGEND_ADD, "Kernel") < 0)
goto done;
if (cmd_graph_label(m, GRAPH_LEGEND_ADD, "User") < 0)
goto done;
if (cmd_graph_label(m, GRAPH_LEGEND_ADD, "Free") < 0)
goto done;
} else {
if (cmd_graph_label(m, GRAPH_LABEL_VERT, "Activity") < 0)
goto done;
if (cmd_graph_label(m, GRAPH_LABEL_MIN, "0%") < 0)
goto done;
if (cmd_graph_label(m, GRAPH_LABEL_MAX, "100%") < 0)
goto done;
if (cmd_graph_label(m, GRAPH_LEGEND_ADD, "User") < 0)
goto done;
if (cmd_graph_label(m, GRAPH_LEGEND_ADD, "System") < 0)
goto done;
if (cmd_graph_label(m, GRAPH_LEGEND_ADD, "Intr") < 0)
goto done;
if (cmd_graph_label(m, GRAPH_LEGEND_ADD, "IOwait") < 0)
goto done;
if (opt_gfx && cmd_graph_label(m, GRAPH_LEGEND_ADD, "GFXwait") < 0)
goto done;
}
r = 0;
debug("Graph initialized\n");
done:
if (r < 0) {
debug("Could not initialize graph\n");
drop();
} else
graph_started = 1;
return r;
}
int
main(int argc, char **argv)
{
double next = 0.0, now;
u_char *p;
int c;
cap_t ocap;
cap_value_t cap_sched_mgt = CAP_SCHED_MGT;
int ccpus;
/* Open our syslog connection */
openlog("mmscd", LOG_PID, LOG_DAEMON);
while ((c = getopt(argc, argv, "D:GMN:b:d:c:f:glmnt:u:")) >= 0)
switch (c) {
case 'l':
opt_nodemode = 1;
break;
case 'M':
opt_memmode = 1; /* Display node memory instead of cpu usage. */
opt_nodemode = 1; /* Node memory implies node display. */
break;
case 'D':
opt_decay = atof(optarg);
break;
case 'G':
opt_gfx = 1;
break;
case 'N': /* Undocumented */
opt_ncpus = atoi(optarg);
break;
case 'b':
opt_baud = atoi(optarg);
break;
case 'd':
opt_debug = optarg;
break;
case 'c':
opt_device = optarg;
break;
case 'f':
opt_device = optarg;
opt_console = 0;
break;
case 'g':
opt_graceful = 1;
break;
case 'm':
opt_meter = 1;
break;
case 'n':
opt_nofork = 1;
break;
case 't':
opt_title = optarg;
opt_title_gfx = optarg;
break;
case 'u':
opt_period = 1.0 / atof(optarg);
break;
default:
usage();
}
if (optind != argc)
fatal("Extra arguments\n");
ccpus=sysmp(MP_NPROCS);
if (ccpus > 128) {
if (ccpus > 256) {
fatal("The MMSC firmware can only display 128 bars. Exiting.\n");
}
opt_nodemode++;
}
if (opt_memmode)
bardim = MEM_DATA_BARS;
else
bardim = opt_gfx ? 5 : 4;
if (opt_title == NULL)
opt_title = (opt_memmode ? mem_title : load_title);
debug_enable(opt_debug);
/*
* Fork into the background
*/
if (! opt_nofork) {
int pid;
if ((pid = fork()) < 0)
fatal("Could not fork: %s\n", STRERR);
if (pid)
exit(0);
ioctl(0, TIOCNOTTY);
ioctl(1, TIOCNOTTY);
ioctl(2, TIOCNOTTY);
}
/*
* Set a non-degrading priority higher than normal procs, but lower
* than anyone else who asks for such a priority.
*/
ocap = cap_acquire(1, &cap_sched_mgt);
schedctl(NDPRI, NDPHIMIN);
cap_surrender(ocap);
/*
* Attempt to contact the MMSC for the first time. If contact is
* not made, assume there is no MMSC and exit.
*/
silent_enable(1);
contact(1);
silent_enable(0);
if (m == 0)
exit(0);
/*
* Set up CPU load module
*/
if (opt_ncpus)
load_cpu_set(opt_ncpus);
ncpus = load_cpu_get();
if (! opt_graceful)
warning("Clean power-down is not enabled (see chkconfig).\n");
if (opt_nodemode) {
nnodes = sysmp(MP_NUMNODES);
if (get_nodemem_map())
fatal("Can't get node memory map.\n");
}
/*
* Catch TERM and INT signals for clean exit.
*/
signal(SIGTERM, note_exit);
signal(SIGINT, note_exit);
/*
* Ignore SIGHUP for when we're shutting down and send SIGHUP
* to all processes.
*/
signal(SIGHUP, SIG_IGN);
/*
* Main polling loop, loops once every "period" seconds.
*
* This needs to be rewritten. There must be one basic polling
* rate for menu and power-down polling, and one basic rate for
* graph updating, and a third for re-contacting the MMSC.
*/
while (! want_exit) {
debug("Main loop\n");
if (m == 0) {
contact(0);
next = 0.0;
}
/*
* Recalculate update period (needs a connection).
*/
if (opt_period)
period = opt_period;
else if (m == 0)
period = 2.0;
else {
int bits_avail;
int bytes_per_update;
int bits_per_update;
double updates_per_sec;
bits_avail = mmsc_baud_get(m);
bytes_per_update = (5 + ncpus * bardim);
bits_per_update = 10 * bytes_per_update;
updates_per_sec = (bits_avail * MAX_CONS_PCT / 100.0) /
bits_per_update;
if (updates_per_sec > MAX_UPDATE_FRQ)
updates_per_sec = MAX_UPDATE_FRQ;
period = 1.0 / updates_per_sec;
debug("%d %d %d %lf\n",
bits_avail, bytes_per_update, bits_per_update,
updates_per_sec);
}
debug("period: %lf\n", period);
if (next == 0.0)
next = double_time() + period;
if (m && opt_meter && ! graph_started)
init_graph_mode();
if (m && graph_started) {
int nelts;
if (opt_memmode) {
debug("Getting memory graph\n");
p = mem_graph();
nelts = nnodes;
} else {
debug("Getting load graph\n");
p = load_graph(opt_gfx, opt_decay, period);
nelts = ncpus;
}
if (p == 0) {
static int x_graph_warn = 0;
if (x_graph_warn == 0) {
warning("Could not obtain %s graph\n",
opt_memmode ? "memory" : "load");
x_graph_warn = 1;
}
} else {
if (cmd_graph_data(m, p, nelts * bardim, nelts) < 0)
drop();
}
}
if (m == 0)
double_sleep((double) CONTACT_PERIOD);
else {
now = double_time();
if (next < now)
next = now;
else if (next > now)
double_sleep(next - now);
next += period;
}
/*
* want_exit is checked right after the sleep so we'll
* see it immediately if the sleep system call is interrupted.
*/
}
cleanup();
return(0);
}