1988 lines
44 KiB
C
1988 lines
44 KiB
C
/**************************************************************************
|
|
* *
|
|
* Copyright (C) 1993, Silicon Graphics, Inc. *
|
|
* *
|
|
* These coded instructions, statements, and computer programs contain *
|
|
* unpublished proprietary information of Silicon Graphics, Inc., and *
|
|
* are protected by Federal copyright law. They may not be disclosed *
|
|
* to third parties or copied or duplicated in any form, in whole or *
|
|
* in part, without the prior written consent of Silicon Graphics, Inc. *
|
|
* *
|
|
**************************************************************************/
|
|
/*
|
|
* $Id: sysctlrd.c,v 1.33 1997/11/07 23:19:24 jfd Exp $
|
|
*/
|
|
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/sysinfo.h>
|
|
#include <sys/sysmp.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <limits.h>
|
|
#include <getopt.h>
|
|
#include <sys/EVEREST/sysctlr.h>
|
|
#include <sys/EVEREST/evconfig.h>
|
|
#include <sys/EVEREST/evdiag.h>
|
|
#include <syslog.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <sys/ipc.h>
|
|
#include <sys/shm.h>
|
|
#include <sys/schedctl.h>
|
|
#include <sys/syssgi.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/time.h>
|
|
#include <sys/capability.h>
|
|
|
|
/* #define SYSCTLR_VOLTAGE_WAR
|
|
* SYSCTLR_VOLTAGE_WAR was used to screen out system controllers that needed
|
|
* rework in the early days of Challenge. Now these parts are out of
|
|
* circulation so the code is unnecessary. Out of range voltages will be
|
|
* detected and reported by the system controller itself.
|
|
*/
|
|
|
|
#define NULL_SEQ -1
|
|
#define MAXCPU 36
|
|
#define SMOOTH
|
|
|
|
#define MAX_WAIT 18000 /* 5 minutes in seconds*/
|
|
|
|
#define MAX_RETRIES 5 /* Max times to retry request */
|
|
|
|
#define MAX_ENV_INFO 256 /* Max bytes of environ info. */
|
|
#define MAX_LOG_INFO 1024 /* Max bytes of log info. */
|
|
|
|
#define LOG_INTERVAL 300 /* Seconds between log fetches */
|
|
|
|
#define EVEREADY_SCALE 16 /* y pixels on small display */
|
|
#define EVEREADY_UPDATE (clk_tck / 2)
|
|
#define TERMINATOR_SCALE 72 /* y pixels on large display */
|
|
#define TERMINATOR_UPDATE ((3 * clk_tck) / 4)
|
|
|
|
#define TIMEOUT_GIVEUP 20 /* How many consec failures
|
|
* before quitting.
|
|
*/
|
|
|
|
#define ABS(_x) (((_x) > 0)?(_x):-(_x))
|
|
|
|
/* Blower max/min macros */
|
|
#define BLOWER_A_MAX(x) (x)->blower_limits[0]
|
|
#define BLOWER_A_MIN(x) (x)->blower_limits[1]
|
|
#define BLOWER_B_MAX(y) (y)->blower_limits[2]
|
|
#define BLOWER_B_MIN(y) (y)->blower_limits[3]
|
|
|
|
/* Set the below if you want to test the output logging of variances in
|
|
* the blower speeds.
|
|
*/
|
|
/* #define TESTING */
|
|
#ifdef TESTING
|
|
#define MAX_BLOWER_SPEED 777 /* good number */
|
|
#else
|
|
#define MAX_BLOWER_SPEED 3000
|
|
#endif
|
|
/* If we detect that the blower is out of range (e.g. the speed exceeds
|
|
* MAX_BLOWER_SPEED above), then we log a message that the blower speed
|
|
* exceeds this maximum. Without ONE_HOUR_TIME defined to be 60 minutes
|
|
* as below, the logging would repeat every time the daemon updates its
|
|
* environment variables (which would give a LOT of output). This define
|
|
* determines how often to log repeat occurences of blower variance events
|
|
* which fall over the range of MAX_BLOWER_SPEED.
|
|
*/
|
|
#define ONE_HOUR_TIME 60 /* units of minutes */
|
|
#define START_TIMER 0
|
|
#define CURRENT_TIMER 1
|
|
|
|
|
|
typedef struct cpu_time_s {
|
|
time_t cpu[6];
|
|
} cpu_time_t;
|
|
|
|
typedef struct display_s {
|
|
cpu_time_t *lri[MAXCPU];
|
|
cpu_time_t *nri[MAXCPU];
|
|
cpu_time_t *mri[MAXCPU];
|
|
cpu_time_t *cpus[MAXCPU];
|
|
cpu_time_t *tmpi[MAXCPU];
|
|
int cpuvalid[MAXCPU];
|
|
int ncpu;
|
|
int sc_scale;
|
|
int sc_update;
|
|
int sinfosz;
|
|
int sysctlr;
|
|
key_t key;
|
|
int shmid;
|
|
char *shmaddr;
|
|
int consec_wtouts;
|
|
int consec_rtouts;
|
|
char raw_env_info[MAX_ENV_INFO];
|
|
uint cmd_fails;
|
|
uint log_fails;
|
|
uint env_fails;
|
|
time_t start_time;
|
|
} display_t;
|
|
|
|
|
|
typedef struct sysctlr_env_s {
|
|
int valid;
|
|
int display_type;
|
|
int volts[6];
|
|
int temp;
|
|
int rpms[2];
|
|
int dtoa;
|
|
time_t timestamp;
|
|
char seq;
|
|
int firmware_rev;
|
|
int temp_limits[2];
|
|
int blower_limits[8];
|
|
int firmware_debug[4];
|
|
int invalid_commands;
|
|
uint cmd_fails;
|
|
uint log_fails;
|
|
uint env_fails;
|
|
time_t start_time;
|
|
int vdc[6][2] ; /* Voltage Extrema */
|
|
struct timeval udt[2];
|
|
} sysctlr_env_t;
|
|
|
|
|
|
char *powersupply_names[] = {
|
|
"\n 48.0V supply = ",
|
|
"\n 12.0V supply = ",
|
|
"\n 5.0V supply = ",
|
|
"\n 1.6V supply = ",
|
|
"\n -5.2V supply = ",
|
|
"\n-12.0V supply = "
|
|
};
|
|
|
|
char *voltages[] = {
|
|
" 48.0V ", /* volts[0] */
|
|
" 12.0V ", /* volts[1] */
|
|
" 5.0V ", /* volts[2] */
|
|
" 1.6V ", /* volts[3] */
|
|
" -5.2V ", /* volts[4] */
|
|
"-12.0V " /* volts[5] */
|
|
};
|
|
|
|
#define MAX_V 0
|
|
#define MIN_V 1
|
|
#define NUM_VOLTAGES 6
|
|
|
|
|
|
/* Prototypes */
|
|
int sendcmd(int, char, char, char);
|
|
void display_string(int, char, char *);
|
|
int getresult(display_t *, char, char, char *);
|
|
void check_inventory(evcfginfo_t *);
|
|
int check_ccrev(evcfginfo_t *);
|
|
int protected_read(int, char *, int);
|
|
void resync(int, int);
|
|
void getlog(display_t *sd, int seq);
|
|
|
|
int no_powermeter;
|
|
display_t display;
|
|
int verbose;
|
|
int graceful_powerdown;
|
|
volatile int kid_dead;
|
|
int cmd_len;
|
|
int clk_tck;
|
|
int on_initial_setup = 0xff;
|
|
int log_vdc_delta = 0;
|
|
int blower_a_faults = 0;
|
|
int blower_b_faults = 0;
|
|
|
|
/*
|
|
* Returns the elapsed number of minutes between the two time values.
|
|
*/
|
|
int
|
|
elapsed_minutes(struct timeval* time_start, struct timeval* time_stop)
|
|
{
|
|
double start = ((double)time_start->tv_sec) * 1000000.0 +time_start->tv_usec;
|
|
double stop = ((double) time_stop->tv_sec) * 1000000.0 + time_stop->tv_usec;
|
|
double seconds = (stop - start) / 1000000.0;
|
|
#ifdef DEBUG
|
|
return (int) seconds; /* Don't want to wait around for this */
|
|
#else
|
|
return (int)(seconds / 60.0);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Updates a timeval.
|
|
*/
|
|
void
|
|
update_timer(struct timeval* tv)
|
|
{
|
|
if (gettimeofday(tv, (struct timezone *) 0) < 0)
|
|
perror("gettimeofday");
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns true if a message should be logged. This is determined
|
|
* by whether or not an hour's worth of time has elapsed or whether
|
|
* or not this is the first time this is called.
|
|
*
|
|
* The following are invariants:
|
|
* 1) The first call to should_log_message() will ALWAYS return true.
|
|
* 2) Subsequent calls to this routine will return true iff. an hour
|
|
* has passed.
|
|
*
|
|
* IMPORTANT NOTE: You must call update_timer() on your start value.
|
|
*/
|
|
int
|
|
should_log_message(struct timeval* start, struct timeval* finish)
|
|
{
|
|
int x = 0;
|
|
static int logged_once = 0;
|
|
update_timer(finish);
|
|
x = elapsed_minutes(start, finish);
|
|
if(!logged_once++){
|
|
return ONE_HOUR_TIME;
|
|
}
|
|
else{
|
|
if(x >= ONE_HOUR_TIME){
|
|
update_timer(start); /* Reset start time */
|
|
return x;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
initinfo(display_t *sd, sysctlr_env_t *ei)
|
|
{
|
|
register int i;
|
|
|
|
sd->ncpu = sysmp(MP_NPROCS);
|
|
|
|
if ((sd->sinfosz = sysmp(MP_SASZ, MPSA_SINFO)) < 0) {
|
|
fprintf(stderr, "sysinfo scall interface not supported\n");
|
|
exit(1);
|
|
}
|
|
|
|
sd->sinfosz = sizeof(cpu_time_t);
|
|
|
|
/* NOTE: This is to reduce the time spent copying system info.
|
|
* If the interface ever changes (would break binary compatibility)
|
|
* this code won't work. We're assuming the cpu[6] array is at the
|
|
* beginning of the sysinfo structure.
|
|
*/
|
|
|
|
for (i = 0; i < sd->ncpu; i++) {
|
|
sd->lri[i] = (cpu_time_t *) calloc(1, sd->sinfosz);
|
|
sd->nri[i] = (cpu_time_t *) calloc(1, sd->sinfosz);
|
|
sd->mri[i] = (cpu_time_t *) calloc(1, sd->sinfosz);
|
|
sd->cpus[i] = (cpu_time_t *) calloc(1, sd->sinfosz);
|
|
sysmp(MP_SAGET1, MPSA_SINFO, (char *) sd->nri[i],
|
|
sd->sinfosz, i);
|
|
sysmp(MP_SAGET1, MPSA_SINFO, (char *) sd->mri[i],
|
|
sd->sinfosz, i);
|
|
}
|
|
|
|
/* initialize the environment structure */
|
|
for (i = 0; i < NUM_VOLTAGES; i++)
|
|
ei->volts[i] = 0;
|
|
|
|
/* Initialize the voltage(s) max/min to small/high values */
|
|
for(i = 0; i < NUM_VOLTAGES; i++){
|
|
if(i < 4){
|
|
ei->vdc[i][MAX_V] = -10000; /* Max */
|
|
ei->vdc[i][MIN_V] = 10000; /* Min */
|
|
} else {
|
|
/* These are negative voltage supplies ( -5.2, -12) */
|
|
ei->vdc[i][MAX_V] = -10000; /* Max */
|
|
ei->vdc[i][MIN_V] = 10000; /* Min */
|
|
}
|
|
}
|
|
|
|
ei->temp = 0;
|
|
ei->rpms[0] = ei->rpms[1] = 0;
|
|
ei->dtoa = 0;
|
|
ei->valid = 0;
|
|
ei->display_type = '?';
|
|
ei->seq = '0';
|
|
|
|
/* No consecutive timeouts */
|
|
sd->consec_rtouts = 0;
|
|
sd->consec_wtouts = 0;
|
|
|
|
/* No fetch failures */
|
|
sd->cmd_fails = 0;
|
|
sd->log_fails = 0;
|
|
sd->env_fails = 0;
|
|
|
|
/* Get the current time */
|
|
sd->start_time = time((time_t *)NULL);
|
|
|
|
/* Clear the environment info. */
|
|
sd->raw_env_info[0] = '\0';
|
|
|
|
|
|
/* Init BLOWER_{A,B} max/min to small/large seed values */
|
|
#ifdef COMMENT /* @@ Not Needed since we get these every spin */
|
|
/* @@ Verify this with Morrow. */
|
|
|
|
BLOWER_A_MAX(ei) = -40000;
|
|
BLOWER_A_MIN(ei) = 40000;
|
|
BLOWER_B_MAX(ei) = -40000;
|
|
BLOWER_B_MIN(ei) = 40000;
|
|
#endif
|
|
update_timer(&ei->udt[START_TIMER]);
|
|
}
|
|
|
|
|
|
void openport(display_t *sd)
|
|
{
|
|
int fd;
|
|
|
|
if ((sd->sysctlr = open("/dev/sysctlr", O_RDWR)) < 0) {
|
|
syslog(LOG_ERR, "Couldn't open /dev/sysctlr. Exiting\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* Resync but don't flush */
|
|
resync(sd->sysctlr, 0);
|
|
|
|
/* Make our working directory / */
|
|
chdir("/");
|
|
|
|
/* Move our stderr to /dev/console. */
|
|
fd = open("/dev/console", O_WRONLY);
|
|
if (fd < 0) {
|
|
fprintf(stderr,
|
|
"sysctlrd: Cannot open console for writing!\n");
|
|
syslog(LOG_ERR, "Cannot open console for writing!\n");
|
|
return;
|
|
}
|
|
|
|
/* Close stderr */
|
|
close(2);
|
|
|
|
/* Copy the /dev/console file descriptor to stderr */
|
|
dup(fd);
|
|
|
|
/* Close the original descriptor */
|
|
close(fd);
|
|
}
|
|
|
|
|
|
void flush_buffer(int fd) {
|
|
|
|
int i;
|
|
char buffer[512];
|
|
|
|
if (!ioctl(fd, SYSC_QLEN))
|
|
return;
|
|
|
|
for (i = 0; (!protected_read(fd, &buffer[i], 1)
|
|
&& (buffer[i] != '\0')); i++)
|
|
;
|
|
|
|
buffer[i] = '\0';
|
|
|
|
if (verbose) {
|
|
syslog(LOG_DEBUG, "%d garbage chars in read queue!\n", i);
|
|
syslog(LOG_DEBUG, "Garbage == %s\n", buffer);
|
|
}
|
|
}
|
|
|
|
|
|
void resync(int fd, int flush)
|
|
{
|
|
char dummy;
|
|
|
|
dummy = 0x1a; /* Ctrl-Z, the system controller resync character */
|
|
|
|
write(fd, &dummy, 1);
|
|
sginap(clk_tck/10);
|
|
|
|
if (flush) {
|
|
/* This also means it's not the initial resync */
|
|
if (verbose)
|
|
syslog(LOG_DEBUG, "Resyncing the system controller.\n");
|
|
|
|
flush_buffer(fd);
|
|
}
|
|
}
|
|
|
|
|
|
int protected_read(int fd, char *buf, int len) {
|
|
int i;
|
|
int retval;
|
|
|
|
if ((retval = read(fd, buf, len)) < len) {
|
|
buf[retval] = '\0';
|
|
if (verbose) {
|
|
syslog(LOG_DEBUG,
|
|
"read of %d returned %d (%s)\n", len, retval,
|
|
buf);
|
|
if (retval < len)
|
|
for (i = 0; i < retval; i++)
|
|
printf("byte %d == 0x%x\n", i, buf[i]);
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int protected_write(int fd, char *buf, unsigned len) {
|
|
|
|
int retval;
|
|
|
|
if ((retval = write(fd, buf, len)) < len) {
|
|
if (no_powermeter == 0)
|
|
(display.consec_wtouts)++;
|
|
if (verbose)
|
|
syslog(LOG_DEBUG,
|
|
"System controller write timed out.\n");
|
|
return 1;
|
|
}
|
|
display.consec_wtouts = 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void display_string(int fd, char cmd, char *string)
|
|
{
|
|
char buf[40];
|
|
int index;
|
|
char lenstr[5];
|
|
|
|
buf[0] = SC_ESCAPE;
|
|
buf[1] = SC_SET;
|
|
buf[2] = cmd;
|
|
|
|
if (cmd_len)
|
|
index = 5;
|
|
else
|
|
index = 3;
|
|
|
|
strcpy(buf + index, string);
|
|
index += strlen(buf + index);
|
|
buf[index++] = SC_TERM;
|
|
|
|
if (cmd_len) {
|
|
sprintf(lenstr, "%02x", index - 2);
|
|
buf[3] = lenstr[0];
|
|
buf[4] = lenstr[1];
|
|
}
|
|
|
|
/* Display the message */
|
|
protected_write(fd, buf, index);
|
|
|
|
buf[index++] = '\0';
|
|
|
|
sginap(clk_tck / 10);
|
|
}
|
|
|
|
|
|
void power_down(display_t *sd)
|
|
{
|
|
/* Power off the system */
|
|
fprintf(stderr, "sysctlrd: Powering down the system.\n");
|
|
fflush(stderr);
|
|
sginap(clk_tck);
|
|
sendcmd(sd->sysctlr, SC_SET, SC_OFF, 0);
|
|
sginap(clk_tck * 60);
|
|
/* not reached */
|
|
fprintf(stderr,
|
|
"sysctlrd: WARNING: The system has not powered down!\n");
|
|
exit(1);
|
|
}
|
|
|
|
|
|
wait_and_snooze(int fd)
|
|
{
|
|
char spinner[] = "-/|/";
|
|
time_t start_time;
|
|
int which_char = 0;
|
|
char string[2];
|
|
|
|
display_string(fd, SC_MESSAGE, "Syncing Disks...");
|
|
|
|
start_time = time((time_t *)NULL);
|
|
string[1] = '\0';
|
|
|
|
while (!kid_dead && ((time((time_t *)NULL) - start_time) < MAX_WAIT)){
|
|
sendcmd(fd, SC_SET, SC_SNOOZE, 0); /* Get more time */
|
|
which_char = (which_char + 1) % 4;
|
|
string[0] = spinner[which_char];
|
|
display_string(fd, SC_PROCSTAT, string);
|
|
|
|
/* Nap for a 1/2 second */
|
|
sginap(clk_tck/2);
|
|
}
|
|
if (kid_dead) {
|
|
display_string(fd, SC_MESSAGE, "Powering down...");
|
|
} else {
|
|
fprintf(stderr, "sysctlrd: Sync timed out!\n");
|
|
display_string(fd, SC_MESSAGE, "SYNC TIMED OUT!");
|
|
}
|
|
/* Wait for two seconds. */
|
|
sginap(clk_tck * 2);
|
|
|
|
fprintf(stderr, "sysctlrd: Powering down.\n");
|
|
fflush(stderr);
|
|
|
|
/* Power off the system */
|
|
sendcmd(fd, SC_SET, SC_OFF, 0);
|
|
|
|
while(1)
|
|
;/* not reached */
|
|
}
|
|
|
|
|
|
void kid_died()
|
|
{
|
|
kid_dead = 1;
|
|
return;
|
|
}
|
|
|
|
void shut_down_power(display_t *sd)
|
|
{
|
|
pid_t child;
|
|
int i;
|
|
|
|
for (i = 0; i < 10; i++)
|
|
sendcmd(sd->sysctlr, SC_SET, SC_SNOOZE, 0); /* Get more time */
|
|
|
|
/* Delete shared memory */
|
|
if (display.shmid >= 0)
|
|
shmctl(display.shmid, IPC_RMID);
|
|
|
|
if (sd->raw_env_info[0])
|
|
syslog(LOG_DEBUG, "Last info: %s\n", sd->raw_env_info);
|
|
|
|
for (i = 0; i < 10; i++)
|
|
sendcmd(sd->sysctlr, SC_SET, SC_SNOOZE, 0); /* Get more time */
|
|
|
|
if (!getresult(sd, SC_ENV, SC_ENV, sd->raw_env_info))
|
|
syslog(LOG_DEBUG, "Current info: %s\n", sd->raw_env_info);
|
|
else
|
|
syslog(LOG_DEBUG, "Current info: Unavailable.\n");
|
|
|
|
if (!graceful_powerdown) {
|
|
power_down(sd);
|
|
}
|
|
|
|
#if PLOCK_GETS_FIXED
|
|
/* Lock out text and data into memory */
|
|
plock(PROCLOCK);
|
|
#endif
|
|
|
|
/* Ignore the hang-up signal we're about to send. */
|
|
signal(SIGHUP, SIG_IGN);
|
|
|
|
kid_dead = 0;
|
|
|
|
/* Catch child signal */
|
|
signal(SIGCLD, kid_died);
|
|
|
|
fprintf(stderr, "sysctlrd: Sending hang-up signal to processes.\n");
|
|
system("/etc/killall HUP");
|
|
display_string(sd->sysctlr, SC_MESSAGE, "Sending hang-up...");
|
|
|
|
/* Get lots more time */
|
|
for (i = 0; i < 5; i++)
|
|
sendcmd(sd->sysctlr, SC_SET, SC_SNOOZE, 0);
|
|
|
|
/* Give processes a brief chance to clean up */
|
|
fprintf(stderr, "sysctlrd: Waiting for processes to clean-up\n");
|
|
sginap(clk_tck * 2);
|
|
|
|
sendcmd(sd->sysctlr, SC_SET, SC_SNOOZE, 0); /* Get more time */
|
|
fprintf(stderr, "sysctlrd: Killing all processes.\n");
|
|
display_string(sd->sysctlr, SC_MESSAGE, "Killing proceses...");
|
|
sendcmd(sd->sysctlr, SC_SET, SC_SNOOZE, 0); /* Get more time */
|
|
system("/etc/killall");
|
|
|
|
if (child = fork()) {
|
|
wait_and_snooze(sd->sysctlr);
|
|
} else {
|
|
fprintf(stderr, "sysctlrd: Doing synchronous sync...\n");
|
|
syssgi(SGI_SSYNC);
|
|
|
|
fprintf(stderr, "sysctlrd: Done.\n");
|
|
fflush(stdout);
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
|
|
int doalarm(display_t *sd, char *preamble)
|
|
{
|
|
int retval = 0;
|
|
|
|
if (preamble[0] == 'A') {
|
|
switch(preamble[1]) {
|
|
case 'T':
|
|
sendcmd(sd->sysctlr, SC_SET, SC_SNOOZE, 0);
|
|
syslog(LOG_EMERG, "Overtemp alarm!\n");
|
|
fprintf(stderr,
|
|
"\007\nsysctlrd: Overtemp alarm!\n");
|
|
shut_down_power(sd);
|
|
break;
|
|
case 'O':
|
|
sendcmd(sd->sysctlr, SC_SET, SC_SNOOZE, 0);
|
|
syslog(LOG_EMERG, "Keyswitch off!\n");
|
|
fprintf(stderr,
|
|
"\007\nsysctlrd: Keyswitch off!\n");
|
|
shut_down_power(sd);
|
|
break;
|
|
case 'B':
|
|
syslog(LOG_EMERG, "Blower failure!\n");
|
|
fprintf(stderr,
|
|
"\007\nsysctlrd: Blower failure!\n");
|
|
fflush(stderr);
|
|
shut_down_power(sd);
|
|
break;
|
|
|
|
default:
|
|
if(verbose){
|
|
syslog(LOG_ALERT, "Unknown alarm: 0x%x\n",
|
|
preamble[1]);
|
|
fprintf(stderr,
|
|
"\007\nsysctlrd: Unknown alarm!\n");
|
|
}
|
|
retval = -1;
|
|
break;
|
|
|
|
|
|
}
|
|
} else if (preamble[0] == 'W') {
|
|
switch(preamble[1]) {
|
|
case 'T':
|
|
syslog(LOG_ERR, "COP timer reset error!\n");
|
|
break;
|
|
case 'C':
|
|
syslog(LOG_ERR,
|
|
"System controller crystal oscillator failed!\n");
|
|
break;
|
|
case 'I':
|
|
syslog(LOG_ERR,
|
|
"Firmware detected illegal opcode!\n");
|
|
break;
|
|
case 'V':
|
|
syslog(LOG_ALERT,
|
|
"Voltage out of tolerance!\n");
|
|
break;
|
|
case 'B':
|
|
syslog(LOG_ALERT,
|
|
"Firmware compensating for blower RPM problem.\n");
|
|
break;
|
|
case 'S':
|
|
syslog(LOG_ERR,
|
|
"System controller firmware reset.\n");
|
|
break;
|
|
default:
|
|
if(verbose){
|
|
syslog(LOG_ERR, "Unknown Warning: 0x%x\n",
|
|
preamble[1]);
|
|
}
|
|
retval = -1;
|
|
break;
|
|
}
|
|
} else {
|
|
syslog(LOG_ERR, "Bogus alarm or error: 0x%x 0x%x\n",
|
|
preamble[0], preamble[1]);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
|
|
void checkmsgs(display_t *sd)
|
|
{
|
|
int qlen;
|
|
char preamble[2000];
|
|
int i;
|
|
int alarm_length;
|
|
|
|
alarm_length = (cmd_len ? 5: 3);
|
|
while ((qlen = ioctl(sd->sysctlr, SYSC_QLEN)) > 0) {
|
|
|
|
if (qlen == -1) {
|
|
syslog(LOG_ERR, "qlen ioctl not supported. Exiting\n");
|
|
exit(1);
|
|
}
|
|
|
|
for (i= 0; i < alarm_length; i++) {
|
|
if (protected_read(sd->sysctlr, (&preamble[i]), 1)) {
|
|
syslog(LOG_DEBUG, "Message read timed out.\n");
|
|
return;
|
|
}
|
|
|
|
if (preamble[i] == '\0') {
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((i < alarm_length) && (preamble[i] == '\0') && verbose)
|
|
syslog(LOG_DEBUG, "Short message in queue.\n");
|
|
|
|
if ((preamble[alarm_length - 1] == '\0')
|
|
&& ((preamble[0] == SC_ALARM) ||
|
|
(preamble[0] == SC_WARNING))) {
|
|
if(doalarm(sd, preamble) < 0){
|
|
/* Unknown Alarm: Log Event */
|
|
getlog(sd, NULL_SEQ);
|
|
}
|
|
} else {
|
|
flush_buffer(sd->sysctlr);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
void getcpuinfo(display_t *sd)
|
|
{
|
|
register int i, j, k;
|
|
|
|
/*
|
|
* If we do a per-cpu readout, then we might encounter an
|
|
* empty slot for CPUs. Compress this into the given number.
|
|
*/
|
|
for (i = 0, j = 0; j < MAXCPU && i < sd->ncpu; j++) {
|
|
sd->tmpi[j] = sd->lri[j];
|
|
sd->lri[j] = sd->nri[j];
|
|
sd->nri[j] = sd->mri[j];
|
|
sd->mri[j] = sd->tmpi[j];
|
|
|
|
if (sysmp(MP_SAGET1, MPSA_SINFO, (char *) sd->mri[j],
|
|
sd->sinfosz, j) == -1) {
|
|
sd->cpuvalid[j] = 0;
|
|
} else {
|
|
sd->cpuvalid[j] = 1;
|
|
i++;
|
|
}
|
|
|
|
if (!sd->cpuvalid[j])
|
|
continue;
|
|
|
|
for (k = 0; k < 6; k++) {
|
|
#ifdef SMOOTH
|
|
sd->cpus[j]->cpu[k] = sd->mri[j]->cpu[k] -
|
|
sd->lri[j]->cpu[k];
|
|
#else
|
|
sd->cpus[j]->cpu[k] = sd->mri[j]->cpu[k] -
|
|
sd->nri[j]->cpu[k];
|
|
#endif
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
int sendcmd(int fd, char cmd, char parm, char seq)
|
|
{
|
|
char outputbuf[10];
|
|
int index = 2;
|
|
|
|
outputbuf[0] = SC_ESCAPE;
|
|
outputbuf[1] = cmd;
|
|
|
|
/* On "get" commands, we need a "sequence number."
|
|
* For now, just use the subcommand number.
|
|
*/
|
|
if (cmd == SC_GET)
|
|
outputbuf[index++] = seq;
|
|
outputbuf[index++] = parm;
|
|
|
|
if (cmd_len) {
|
|
outputbuf[index++] = '0';
|
|
outputbuf[index] = '0' + index;
|
|
index++;
|
|
}
|
|
outputbuf[index++] = SC_TERM;
|
|
outputbuf[index] = '\0';
|
|
|
|
if (protected_write(fd, outputbuf, index))
|
|
if (verbose)
|
|
syslog(LOG_ERR, "Command write timed out!\n");
|
|
else
|
|
return 1;
|
|
|
|
sginap(clk_tck / 10);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int getresult(display_t *sd, char cmd, char seq, char *buffer)
|
|
{
|
|
char preamble[6];
|
|
char lenstr[3];
|
|
unsigned int length;
|
|
unsigned int actual;
|
|
int i, j;
|
|
int timeout = 0;
|
|
int retries = 0;
|
|
int done = 0;
|
|
|
|
preamble[0] = preamble[1] = preamble[2] = preamble[3] = '\0';
|
|
|
|
while ((retries < MAX_RETRIES) && (!done)) {
|
|
if (retries > 0) {
|
|
resync(sd->sysctlr, 1);
|
|
sd->cmd_fails++;
|
|
}
|
|
if (sendcmd(sd->sysctlr, SC_GET, cmd, seq)) {
|
|
preamble[0] = SC_TERM;
|
|
/* Terminate partial command. */
|
|
write(sd->sysctlr, preamble, 1);
|
|
retries++;
|
|
continue;
|
|
}
|
|
|
|
j = 0;
|
|
do {
|
|
if (protected_read(sd->sysctlr, (&preamble[j++]), 1)) {
|
|
/* Resync the port. */
|
|
resync(sd->sysctlr, 1);
|
|
sendcmd(sd->sysctlr, SC_GET, cmd, seq);
|
|
break;
|
|
}
|
|
} while ((j < 3) && (preamble[j - 1] != '\0'));
|
|
|
|
if (j != 3) {
|
|
retries++;
|
|
if (verbose)
|
|
if (j < 3) {
|
|
syslog(LOG_DEBUG,
|
|
"Result preamble read timed out.\n");
|
|
} else {
|
|
syslog(LOG_DEBUG,
|
|
"Short response string.\n");
|
|
}
|
|
continue;
|
|
}
|
|
if((preamble[0] == SC_ALARM) || (preamble[0] == SC_WARNING)) {
|
|
/* If length checking is on, we need to fetch the
|
|
* length bytes here too.
|
|
*/
|
|
if (cmd_len)
|
|
if (protected_read(sd->sysctlr, preamble + 3,
|
|
2)) {
|
|
retries++;
|
|
continue;
|
|
} else {
|
|
preamble[5] = '\0';
|
|
}
|
|
if(doalarm(sd, preamble) < 0){
|
|
/* Unknown Alarm: Log event */
|
|
getlog(sd, seq);
|
|
getlog(sd,NULL_SEQ);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* Make sure this is a response */
|
|
if(preamble[0] != SC_RESP) {
|
|
if (verbose)
|
|
syslog(LOG_DEBUG,
|
|
"Unexpected response code: 0x%x\n",
|
|
preamble[0]);
|
|
retries++;
|
|
continue;
|
|
}
|
|
|
|
/* Check the sequence number */
|
|
if (preamble[1] != seq) {
|
|
if (verbose)
|
|
syslog(LOG_DEBUG,
|
|
"Bogus sequence number: 0x%x\n",
|
|
preamble[1]);
|
|
retries++;
|
|
continue;
|
|
}
|
|
|
|
/* Check the request code */
|
|
if (preamble[2] != cmd) {
|
|
if (verbose)
|
|
syslog(LOG_DEBUG, "Wrong request code: 0x%x\n",
|
|
preamble[2]);
|
|
retries++;
|
|
continue;
|
|
}
|
|
|
|
if ((cmd_len == 1) && protected_read(sd->sysctlr, lenstr, 2)) {
|
|
if (verbose)
|
|
syslog(LOG_DEBUG,
|
|
"Result length read timed out!\n");
|
|
retries++;
|
|
continue;
|
|
}
|
|
|
|
lenstr[2] = '\0';
|
|
|
|
i = 0;
|
|
do {
|
|
timeout = protected_read(sd->sysctlr, buffer + i, 1);
|
|
} while ((buffer[i++] != '\0') && !timeout);
|
|
|
|
if (timeout) {
|
|
if (verbose)
|
|
syslog(LOG_DEBUG,
|
|
"Result data read timed out!\n");
|
|
buffer[i] = '\0';
|
|
retries++;
|
|
continue;
|
|
}
|
|
|
|
if (cmd_len == 1) {
|
|
sscanf(lenstr, "%2x", &length);
|
|
actual = (i + 4);
|
|
|
|
#ifdef FIRMWARE_200
|
|
while (actual >= 0x100)
|
|
actual >>= 4;
|
|
#else
|
|
actual &= 0xff;
|
|
#endif
|
|
|
|
if (length != actual) {
|
|
if(verbose)
|
|
syslog(LOG_DEBUG,
|
|
"Result length mismatch!\n");
|
|
retries++;
|
|
continue;
|
|
}
|
|
}
|
|
done = 1;
|
|
|
|
}
|
|
|
|
/* We timed out or got bogus responses too many times. */
|
|
if (done)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
|
|
int what_length_mode(char *buffer)
|
|
{
|
|
if (!strcmp(buffer, "N")) {
|
|
return 0;
|
|
} else if (!strcmp(buffer, "06Y")) {
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
void set_length_mode(display_t *sd)
|
|
{
|
|
char buffer[40];
|
|
int i;
|
|
|
|
/* For the get check command, we want the length numbers
|
|
* turned on so the commands won't be ignored.
|
|
* We set it to the correct value later.
|
|
* Responses are only length-checked if cmd_len == 1. This setting
|
|
* causes lengths to be sent but not checked. Sure it's ugly.
|
|
* We have to be compatible in four different combinations.
|
|
*/
|
|
|
|
cmd_len = 2;
|
|
|
|
/* Try a few times in case we lose characters or something. */
|
|
for (i = 0; i < 3; i++) {
|
|
/* If this command times out, the feature isn't available. */
|
|
if (getresult(sd, SC_CHECK, SC_CHECK, buffer)) {
|
|
cmd_len = 0;
|
|
return;
|
|
}
|
|
|
|
if (what_length_mode(buffer) == 1) {
|
|
cmd_len = 1;
|
|
return;
|
|
} else if (what_length_mode(buffer) == 0) {
|
|
/* Toggle the length check mode. */
|
|
sendcmd(sd->sysctlr, SC_SET, SC_CHECK, SC_CHECK);
|
|
|
|
/* Deleting the next 10 lines has no effect on the
|
|
* function of this code (well, one less retry),
|
|
* but I thought it would be confusing so I didn't
|
|
* go for it.
|
|
*/
|
|
|
|
/* Check the result */
|
|
getresult(sd, SC_CHECK, SC_CHECK, buffer);
|
|
|
|
/* If the "set" took, set cmd_len and return */
|
|
if (what_length_mode(buffer) == 1) {
|
|
cmd_len = 1;
|
|
return;
|
|
}
|
|
/* Else fall through and try again */
|
|
}
|
|
|
|
/* Falling through means a response was bogus. */
|
|
|
|
} /* for i */
|
|
|
|
cmd_len = 0;
|
|
|
|
}
|
|
|
|
|
|
void settime(display_t *sd)
|
|
{
|
|
time_t now;
|
|
char textbuf[40];
|
|
int index;
|
|
char lenstr[3];
|
|
|
|
time(&now);
|
|
|
|
textbuf[0] = SC_ESCAPE;
|
|
textbuf[1] = SC_SET;
|
|
textbuf[2] = SC_TIME;
|
|
|
|
if (cmd_len) {
|
|
index = 5;
|
|
} else {
|
|
index = 3;
|
|
}
|
|
|
|
cftime(textbuf + index, "%m%d%y%H%M%S", &now);
|
|
index += strlen(textbuf + index);
|
|
textbuf[index++] = SC_TERM;
|
|
textbuf[index] = '\0';
|
|
|
|
if (cmd_len) {
|
|
sprintf(lenstr, "%02x", index - 2);
|
|
textbuf[3] = lenstr[0];
|
|
textbuf[4] = lenstr[1];
|
|
}
|
|
|
|
protected_write(sd->sysctlr, textbuf, index);
|
|
}
|
|
|
|
|
|
void dumpinfo(display_t *sd, sysctlr_env_t *ei)
|
|
{
|
|
unsigned int idle;
|
|
unsigned int used;
|
|
unsigned int i, j;
|
|
unsigned int index;
|
|
char number[5];
|
|
unsigned int fraction;
|
|
char outputbuf[MAXCPU * 5 + 5];
|
|
|
|
outputbuf[0] = SC_ESCAPE;
|
|
outputbuf[1] = SC_SET;
|
|
outputbuf[2] = SC_HISTO;
|
|
|
|
if (cmd_len)
|
|
index = 5;
|
|
else
|
|
index = 3;
|
|
|
|
for (i = 0; i < sd->ncpu; i++) {
|
|
if (sd->cpuvalid[i]) {
|
|
idle = sd->cpus[i]->cpu[CPU_IDLE] +
|
|
sd->cpus[i]->cpu[CPU_WAIT];
|
|
used = (sd->cpus[i]->cpu[CPU_USER] +
|
|
sd->cpus[i]->cpu[CPU_KERNEL] +
|
|
sd->cpus[i]->cpu[CPU_SXBRK] +
|
|
sd->cpus[i]->cpu[CPU_INTR]);
|
|
if (idle + used == 0)
|
|
idle += 1;
|
|
fraction = MAX(sd->sc_scale * used / (idle + used), 1);
|
|
} else {
|
|
fraction = 0;
|
|
}
|
|
|
|
if (i)
|
|
sprintf(number, ",%d", fraction);
|
|
else
|
|
sprintf(number, "%d", fraction);
|
|
|
|
for (j = 0; j < strlen(number); j++)
|
|
outputbuf[index++] = number[j];
|
|
|
|
}
|
|
|
|
if ((ei->display_type == 'E') && (!cmd_len))
|
|
for (i = sd->ncpu; i < 16; i++) {
|
|
outputbuf[index++] = ',';
|
|
outputbuf[index++] = '0';
|
|
}
|
|
|
|
outputbuf[index++] = SC_TERM;
|
|
|
|
/* Now that we know what the length should be, set it! */
|
|
if (cmd_len) {
|
|
sprintf(number, "%02x", index - 2);
|
|
outputbuf[3] = number[0];
|
|
outputbuf[4] = number[1];
|
|
}
|
|
|
|
/* Dump to sysctlr here */
|
|
protected_write(sd->sysctlr, outputbuf, index);
|
|
|
|
outputbuf[index++] = '\0';
|
|
|
|
sginap(clk_tck / 10);
|
|
}
|
|
|
|
|
|
void print_dec(int number)
|
|
{
|
|
printf("%3d.%02d", number/100, ABS(number) % 100);
|
|
}
|
|
|
|
|
|
void printenvinfo(sysctlr_env_t *ei)
|
|
{
|
|
int i;
|
|
|
|
printf("As of %s", ctime(&ei->timestamp));
|
|
printf("Display type: ");
|
|
switch(ei->display_type) {
|
|
case 'T':
|
|
printf("40 x 8\n");
|
|
break;
|
|
case 'E':
|
|
printf("20 x 2\n");
|
|
|
|
break;
|
|
default:
|
|
printf("Unknown\n");
|
|
break;
|
|
}
|
|
/* Print voltages */
|
|
for(i = 0; i < 6; i++){
|
|
printf("%s", powersupply_names[i]);
|
|
print_dec(ei->volts[i]);
|
|
}
|
|
|
|
printf("\nSystem air temp =");
|
|
print_dec(ei->temp);
|
|
printf(" C\n");
|
|
printf("Blower A speed = %4d RPM\n", ei->rpms[0]);
|
|
if (ei->display_type == 'T')
|
|
printf("Blower B speed = %4d RPM\n", ei->rpms[1]);
|
|
if (verbose)
|
|
printf("D/A setting = %d\n", ei->dtoa);
|
|
if (ei->valid >= 2) {
|
|
printf("Firmware version");
|
|
print_dec(ei->firmware_rev);
|
|
|
|
printf("\n-- Extrema since power-up --");
|
|
printf("\nMax temp = ");
|
|
print_dec(ei->temp_limits[0]);
|
|
printf("\nMin temp = ");
|
|
print_dec(ei->temp_limits[1]);
|
|
|
|
printf("\nMax blower A speed = %d", ei->blower_limits[0]);
|
|
printf("\nMin blower A speed = %d", ei->blower_limits[1]);
|
|
|
|
if (ei->display_type == 'T') {
|
|
printf("\nMax blower B speed = %d",
|
|
ei->blower_limits[2]);
|
|
printf("\nMin blower B speed = %d",
|
|
ei->blower_limits[3]);
|
|
}
|
|
/* Print Voltage VDC Extrema */
|
|
for(i = 0; i < NUM_VOLTAGES; i++){
|
|
printf("\n%ssupply max/min (", voltages[i]);
|
|
print_dec(ei->vdc[i][MAX_V]);
|
|
printf(",");
|
|
print_dec(ei->vdc[i][MIN_V]);
|
|
printf(")");
|
|
}
|
|
|
|
if (verbose) {
|
|
printf("\n-- Firmware internal variables --\n");
|
|
for (i = 0; i < 4; i++)
|
|
printf("B%d: %d ", i, ei->blower_limits[4 + i]);
|
|
printf("\n");
|
|
for (i = 0; i < 4; i++)
|
|
printf("F%d: %d ", i, ei->firmware_debug[i]);
|
|
printf("\nInvalid CPU commands = %d",
|
|
ei->invalid_commands);
|
|
}
|
|
|
|
}
|
|
if (verbose && (ei->timestamp - ei->start_time)) {
|
|
printf("\nGet env info failures: %u (%u / hour)\n",
|
|
ei->env_fails,
|
|
(ei->env_fails * 3600U) /
|
|
(ei->timestamp - ei->start_time));
|
|
printf("Get log info failures: %u (%u / hour)\n",
|
|
ei->log_fails,
|
|
(ei->log_fails * 3600U) /
|
|
(ei->timestamp - ei->start_time));
|
|
printf("Command retries: %u (%u / hour)",
|
|
ei->cmd_fails,
|
|
(ei->cmd_fails * 3600U) /
|
|
(ei->timestamp - ei->start_time));
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
|
|
void printrawinfo(sysctlr_env_t *ei)
|
|
{
|
|
int i;
|
|
|
|
printf("%d;%c;", ei->firmware_rev, ei->display_type);
|
|
for (i = 0; i < 6; i++)
|
|
printf("%d;", ei->volts[i]);
|
|
printf("%d;%d;%d;%d;", ei->temp, ei->rpms[0], ei->rpms[1], ei->dtoa);
|
|
printf("%d;%d;%d;", ei->timestamp, ei->temp_limits[0],
|
|
ei->temp_limits[1]);
|
|
for (i = 0; i < 4; i++)
|
|
printf("%d;", ei->blower_limits[i]);
|
|
printf("%d\n", ei->invalid_commands);
|
|
}
|
|
|
|
|
|
int decimal_to_fixed(char *number)
|
|
{
|
|
int cent, decimal;
|
|
int result;
|
|
|
|
cent = atoi(number);
|
|
|
|
if (strchr(number, '.')) {
|
|
number = strchr(number, '.') + 1;
|
|
if (strlen(number) > 1)
|
|
decimal = atoi(number);
|
|
else
|
|
decimal = atoi(number) * 10;
|
|
} else
|
|
decimal = 0;
|
|
|
|
if (cent >= 0)
|
|
result = 100 * cent + decimal;
|
|
else
|
|
result = 100 * cent - decimal;
|
|
|
|
return result;
|
|
}
|
|
|
|
int check_sysctlr_rev(sysctlr_env_t *ei)
|
|
{
|
|
int complained = 0;
|
|
|
|
if (!ei->valid)
|
|
return 0;
|
|
|
|
if (ei->firmware_rev < 202) {
|
|
syslog(LOG_ERR, "System controller firmware is downrev -\n");
|
|
syslog(LOG_ERR, " Rev is %d.%02d - should be at least 2.02\n",
|
|
ei->firmware_rev / 100, ei->firmware_rev % 100);
|
|
complained++;
|
|
}
|
|
|
|
#ifdef SYSCTLR_VOLTAGE_WAR
|
|
|
|
if (ei->volts[3] < 160) {
|
|
syslog(LOG_ERR, "System controller likely needs rework -\n");
|
|
syslog(LOG_ERR, " Voltage is %d.%02d - should be at least 1.60\n", ei->volts[3] / 100, ei->volts[3] % 100);
|
|
complained++;
|
|
}
|
|
|
|
#endif /* SYSCTLR_VOLTAGE_WAR */
|
|
|
|
return complained;
|
|
}
|
|
|
|
|
|
int getenvinfo(display_t *sd, sysctlr_env_t *ei)
|
|
{
|
|
char buffp[1024];
|
|
char *next_field;
|
|
int index,j;
|
|
char env_info_copy[MAX_ENV_INFO];
|
|
|
|
if (getresult(sd, SC_ENV, ei->seq, sd->raw_env_info)) {
|
|
if (verbose)
|
|
syslog(LOG_DEBUG, "Get env info failed.\n");
|
|
(sd->env_fails)++;
|
|
(sd->consec_rtouts)++;
|
|
ei->valid = 0;
|
|
ei->seq++;
|
|
if (ei->seq > '9')
|
|
ei->seq = '0';
|
|
return -1;
|
|
}
|
|
|
|
sd->consec_rtouts = 0;
|
|
|
|
/* Increment the sequence number */
|
|
ei->seq++;
|
|
if (ei->seq > '9')
|
|
ei->seq = '0';
|
|
/*
|
|
printf("Env string == %s\n", sd->raw_env_info);
|
|
*/
|
|
|
|
strcpy(env_info_copy, sd->raw_env_info);
|
|
index = 0;
|
|
next_field = strtok(env_info_copy, ";");
|
|
|
|
/* Mark this structure invalid. */
|
|
ei->valid = 0;
|
|
|
|
ei->firmware_rev = 0;
|
|
while (index < 26 && next_field) {
|
|
if (index < 6) {
|
|
/* It's a voltage. Do fixed point convert */
|
|
ei->volts[index] = decimal_to_fixed(next_field);
|
|
} else if (index < 7) {
|
|
/* It's a temperature */
|
|
ei->temp = decimal_to_fixed(next_field);
|
|
} else if (index < 9) {
|
|
ei->rpms[index - 7] = atoi(next_field);
|
|
} else if (index == 9) {
|
|
ei->dtoa = atoi(next_field);
|
|
} else if (index == 10) {
|
|
ei->firmware_rev = decimal_to_fixed(next_field);
|
|
} else if (index < 13) {
|
|
ei->temp_limits[index - 11] =
|
|
decimal_to_fixed(next_field);
|
|
} else if (index < 21) {
|
|
ei->blower_limits[index - 13] = atoi(next_field);
|
|
} else if (index < 25) {
|
|
ei->firmware_debug[index - 21] = atoi(next_field);
|
|
} else {
|
|
ei->invalid_commands = atoi(next_field);
|
|
}
|
|
index++;
|
|
next_field = strtok(NULL, ";");
|
|
}
|
|
|
|
if (index == 10) {
|
|
ei->valid = 1;
|
|
} else if (index == 26) {
|
|
ei->valid = 2;
|
|
} else {
|
|
if (verbose) {
|
|
syslog(LOG_DEBUG,
|
|
"Received short environmental response string (%d fields).\n",
|
|
index);
|
|
}
|
|
ei->valid = 0;
|
|
}
|
|
|
|
ei->timestamp = time((time_t *)NULL);
|
|
|
|
/* XXX - eventually move these into ei struct. More risk, though
|
|
* so don't do it now
|
|
*/
|
|
ei->log_fails = sd->log_fails;
|
|
ei->env_fails = sd->env_fails;
|
|
ei->cmd_fails = sd->cmd_fails;
|
|
ei->start_time = sd->start_time;
|
|
|
|
/* Update VDC Input extrema */
|
|
for( j = 0; j < NUM_VOLTAGES; j++){
|
|
|
|
if( ei->volts[j] > ei->vdc[j][MAX_V]){
|
|
|
|
ei->vdc[j][MAX_V] = ei->volts[j] ; /* Max */
|
|
if(!on_initial_setup){
|
|
if(log_vdc_delta){
|
|
sprintf(buffp,"%ssupply saw new maximum: %3d.%02dV\n",
|
|
voltages[j],
|
|
ei->vdc[j][MAX_V] / 100,
|
|
ABS(ei->vdc[j][MAX_V]) % 100 );
|
|
syslog(LOG_DEBUG,buffp);
|
|
}
|
|
}
|
|
}
|
|
if (ei->volts[j] < ei->vdc[j][MIN_V]){
|
|
ei->vdc[j][MIN_V] = ei->volts[j] ; /* Min */
|
|
if(!on_initial_setup){
|
|
if(log_vdc_delta){
|
|
sprintf(buffp,"%ssupply saw new minimum: %3d.%02dV\n",
|
|
voltages[j],
|
|
ei->vdc[j][MIN_V] / 100,
|
|
ABS(ei->vdc[j][MIN_V]) % 100);
|
|
syslog(LOG_DEBUG,buffp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Notes from Brad Morrow: (morrow@engr.sgi.com)
|
|
*
|
|
* The system controller will send a warning message when it
|
|
* measures a RPM higher then the upper tolerance. Normally
|
|
* this message is sent immediately after the system controller
|
|
* powers up. This is only for the system that have the 2700
|
|
* PRM blower installed. The problem we want to detect is when
|
|
* the blower sends out bad RPM data. The measurement data
|
|
* shows RPM values > 28000 RPM. It seems the tach pulse from
|
|
* the blower has noise on it, cause these bad high readings.
|
|
* The blower Brad Morrow checked was actually spinning near
|
|
* 1400 RPM when it was reporting a RPM value of 27K! We want
|
|
* to catch this condition, and have an action item for AOL to
|
|
* do so. We should never see a RPM value > 3000. So, if we
|
|
* see this condition on either blower, we send a message to
|
|
* the syslog as well as log it to the console.
|
|
*
|
|
* NOTE: this output is sent to both the console and the
|
|
* SYSLOG only under the following conditions:
|
|
* 1) It is the first time the event has occurred.
|
|
* 2) One hour's time has elapsed since the last occurence of
|
|
* this event.
|
|
*/
|
|
if(BLOWER_A_MAX(ei) > MAX_BLOWER_SPEED){
|
|
if(should_log_message(&ei->udt[START_TIMER],
|
|
&ei->udt[CURRENT_TIMER])){
|
|
sprintf(buffp,
|
|
"Blower A maximum (%d) > %d RPM. Notification[%d].\n",
|
|
BLOWER_A_MAX(ei), MAX_BLOWER_SPEED, ++blower_a_faults);
|
|
fprintf(stderr, "sysctlrd: %s", buffp);
|
|
syslog(LOG_ALERT, buffp);
|
|
}
|
|
}
|
|
if(BLOWER_B_MAX(ei) > MAX_BLOWER_SPEED){
|
|
if(should_log_message(&ei->udt[START_TIMER],
|
|
&ei->udt[CURRENT_TIMER])){
|
|
sprintf(buffp,
|
|
"Blower B maximum (%d) > %d RPM. Notification[%d]\n",
|
|
BLOWER_B_MAX(ei), MAX_BLOWER_SPEED, ++blower_b_faults);
|
|
fprintf(stderr, "sysctlrd: %s", buffp);
|
|
syslog(LOG_ALERT, buffp);
|
|
}
|
|
}
|
|
|
|
/* Every time we update our environment information, get the
|
|
* event log. This will only print out if there is something
|
|
* to see. */
|
|
getlog(sd,ei->seq);
|
|
|
|
#ifdef COMMENT
|
|
printenvinfo(ei);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
int getscale(display_t *sd, char panel, sysctlr_env_t *ei)
|
|
{
|
|
char buffer[80];
|
|
int retries = 0;
|
|
int result = 0;
|
|
|
|
if (panel == 'E') {
|
|
sd->sc_scale = EVEREADY_SCALE;
|
|
sd->sc_update = EVEREADY_UPDATE;
|
|
ei->display_type = 'E';
|
|
return 0;
|
|
} else if (panel == 'T') {
|
|
ei->display_type = 'T';
|
|
sd->sc_scale = TERMINATOR_SCALE;
|
|
sd->sc_update = TERMINATOR_UPDATE;
|
|
return 0;
|
|
}
|
|
|
|
while ((result = getresult(sd, SC_PANEL, SC_PANEL, buffer))
|
|
&& (retries < 3)) {
|
|
retries++;
|
|
}
|
|
|
|
if (result) {
|
|
syslog(LOG_ERR, "Get scale failed! Assuming small panel\n");
|
|
sd->sc_scale = EVEREADY_SCALE;
|
|
sd->sc_update = TERMINATOR_UPDATE;
|
|
return -1;
|
|
}
|
|
|
|
if (buffer[0] == 'E') {
|
|
ei->display_type = 'E';
|
|
sd->sc_scale = EVEREADY_SCALE;
|
|
sd->sc_update = EVEREADY_UPDATE;
|
|
} else if (buffer[0] == 'T') {
|
|
ei->display_type = 'T';
|
|
sd->sc_scale = TERMINATOR_SCALE;
|
|
sd->sc_update = TERMINATOR_UPDATE;
|
|
} else {
|
|
ei->display_type = '?';
|
|
syslog(LOG_ERR, "Unknown display type: 0x%x\n", buffer[0]);
|
|
sd->sc_scale = EVEREADY_SCALE;
|
|
sd->sc_update = TERMINATOR_UPDATE;
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void decode_entry(char *string)
|
|
{
|
|
int i;
|
|
char *time_stamp;
|
|
char time_text[20];
|
|
|
|
i = 0;
|
|
while((string[i] != ',') && (string[i] != '\0'))
|
|
i++;
|
|
|
|
if (string[i] == '\0') {
|
|
/* If the format's messed up, use null timestamp. */
|
|
time_stamp = string + i;
|
|
} else {
|
|
/* End the text, start the timestamp */
|
|
string[i] = '\0';
|
|
time_stamp = string + i + 1;
|
|
}
|
|
|
|
if (time_stamp[0] == '\0') {
|
|
strcpy(time_text, "Unknown time");
|
|
} else {
|
|
time_text[0] = time_stamp[6];
|
|
time_text[1] = time_stamp[7];
|
|
time_text[2] = '/';
|
|
time_text[3] = time_stamp[8];
|
|
time_text[4] = time_stamp[9];
|
|
time_text[5] = '/';
|
|
time_text[6] = time_stamp[10];
|
|
time_text[7] = time_stamp[11];
|
|
time_text[8] = ' ';
|
|
time_text[9] = time_stamp[0];
|
|
time_text[10] = time_stamp[1];
|
|
time_text[11] = ':';
|
|
time_text[12] = time_stamp[2];
|
|
time_text[13] = time_stamp[3];
|
|
time_text[14] = ':';
|
|
time_text[15] = time_stamp[4];
|
|
time_text[16] = time_stamp[5];
|
|
time_text[17] = '\0';
|
|
}
|
|
|
|
/* Don't log those pesky "INVALID CPU COMMAND" messages that arise
|
|
* from the system controller's UART's interrupt priority being
|
|
* too low.
|
|
*/
|
|
if (strncmp(string, "INVALID CPU COMMAND", 19))
|
|
syslog(LOG_NOTICE, "Event: %s, %s",
|
|
time_text, string);
|
|
}
|
|
|
|
|
|
void getlog(display_t *sd, int seq)
|
|
{
|
|
char buffer[MAX_LOG_INFO];
|
|
int i;
|
|
|
|
if ( seq == NULL_SEQ)
|
|
seq = SC_LOG;
|
|
|
|
if(getresult(sd, SC_LOG, seq, buffer)) {
|
|
if (verbose)
|
|
syslog(LOG_NOTICE, "Get log failed!\n");
|
|
(sd->log_fails++);
|
|
} else {
|
|
/* If we were successful in retrieving the log, clear it */
|
|
sendcmd(sd->sysctlr, SC_SET, SC_KILLLOG, 0);
|
|
}
|
|
|
|
if (strlen(buffer)) {
|
|
/* Nail that trailing semicolon */
|
|
buffer[strlen(buffer) - 1] = '\0';
|
|
|
|
i = strlen(buffer) - 1;
|
|
while (i >= 0) {
|
|
if ((i == 0) || (buffer[i - 1] == ';')) {
|
|
decode_entry(buffer + i);
|
|
}
|
|
if (buffer[i] == ';')
|
|
buffer[i] = '\0';
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Check to see that the system serial number is valid.
|
|
*/
|
|
valid_serial(char *serial_num)
|
|
{
|
|
int i;
|
|
|
|
if (!serial_num || !strlen(serial_num))
|
|
return 0;
|
|
|
|
i = 0;
|
|
while(serial_num[i] != '\0') {
|
|
if ((serial_num[i] < 0x20) || (serial_num[i] > 0x7e)
|
|
|| (i >= SERIALNUMSIZE - 1)) {
|
|
return 0;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
void printpbstatus(void)
|
|
{
|
|
evcfginfo_t evconfig;
|
|
|
|
if (syssgi(SGI_GET_EVCONF, &evconfig, sizeof(evconfig))) {
|
|
printf("Unable to report piggyback status\n");
|
|
return;
|
|
}
|
|
|
|
check_ccrev(&evconfig);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void getpromerrs(void)
|
|
{
|
|
evcfginfo_t evconfig;
|
|
|
|
if (syssgi(SGI_GET_EVCONF, &evconfig, sizeof(evconfig))) {
|
|
syslog(LOG_ERR, "Unable to retrieve PROM errors.\n");
|
|
return;
|
|
}
|
|
|
|
check_inventory(&evconfig);
|
|
|
|
if (!valid_serial(evconfig.ecfg_snum)) {
|
|
fprintf(stderr, "\007\nWARNING: The serial number on this system controller is not valid!\n");
|
|
syslog(LOG_ALERT, "WARNING: The serial number on this system controller is not valid!\n");
|
|
}
|
|
}
|
|
|
|
|
|
void logswitches(display_t *sd)
|
|
{
|
|
char buffer[10];
|
|
|
|
if (getresult(sd, SC_DEBUG, SC_DEBUG, buffer)) {
|
|
syslog(LOG_DEBUG, "Get switch settings failed!\n");
|
|
return;
|
|
}
|
|
|
|
if (strtol(buffer, (char **)NULL, 16))
|
|
syslog(LOG_NOTICE, "Debug settings: 0x%s", buffer);
|
|
}
|
|
|
|
|
|
int createshmem(display_t *sd)
|
|
{
|
|
sd->shmid = shmget(sd->key, sizeof(sysctlr_env_t), IPC_CREAT | 0644);
|
|
|
|
if (sd->shmid < 0) {
|
|
syslog(LOG_NOTICE, "Unable to set up IPC\n");
|
|
sd->shmaddr = 0;
|
|
return -1;
|
|
}
|
|
|
|
sd->shmaddr = shmat(sd->shmid, (void *)NULL, 0);
|
|
|
|
if ((int)sd->shmaddr < 0) {
|
|
syslog(LOG_NOTICE, "Unable to attach shared memory\n");
|
|
sd->shmaddr = 0;
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
attach_ro_shmem(display_t *sd)
|
|
{
|
|
sd->shmid = shmget(sd->key, sizeof(sysctlr_env_t), 0644);
|
|
|
|
if (sd->shmid < 0)
|
|
sd->shmid = shmget(sd->key, 56, 0644);
|
|
|
|
if (sd->shmid < 0) {
|
|
fprintf(stderr, "sysctlrd: Can't contact daemon. Exiting.\n");
|
|
exit(1);
|
|
}
|
|
|
|
sd->shmaddr = shmat(sd->shmid, (void *)NULL, SHM_RDONLY);
|
|
|
|
if ((int)sd->shmaddr < 0) {
|
|
fprintf(stderr,
|
|
"sysctlrd: Unable to attach shared memory. Exiting.\n");
|
|
exit(1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void cleanup()
|
|
{
|
|
display_string(display.sysctlr, SC_MESSAGE, " ");
|
|
display_string(display.sysctlr, SC_MESSAGE, "Sysctlrd killed!");
|
|
|
|
if (display.shmid >= 0)
|
|
shmctl(display.shmid, IPC_RMID);
|
|
|
|
syslog(LOG_NOTICE, "System controller daemon killed.\n");
|
|
exit(0);
|
|
}
|
|
|
|
|
|
void copy_out_env(display_t *sd, sysctlr_env_t *ei)
|
|
{
|
|
if((int)sd->shmaddr <= 0)
|
|
return;
|
|
|
|
if (ei->valid)
|
|
*((sysctlr_env_t *)sd->shmaddr) = *ei;
|
|
}
|
|
|
|
void copy_in_env(display_t *sd, sysctlr_env_t *ei)
|
|
{
|
|
if((int)sd->shmaddr <= 0) {
|
|
fprintf(stderr,
|
|
"sysctlrd: Can't copy from shared memory. Exiting.\n");
|
|
exit(1);
|
|
}
|
|
|
|
*ei = *((sysctlr_env_t *)sd->shmaddr);
|
|
}
|
|
|
|
|
|
main(int argc, char *argv[])
|
|
{
|
|
int c;
|
|
sysctlr_env_t environ;
|
|
int daemon_mode = 0;
|
|
int suicide_mode = 0;
|
|
int print_info = 0;
|
|
int raw_info = 0;
|
|
int no_env_info = 0;
|
|
char panel = '?';
|
|
unsigned int cycle;
|
|
int frequency = -1;
|
|
int already_complained = 0;
|
|
cap_t ocap;
|
|
cap_value_t cap_sched_mgt = CAP_SCHED_MGT;
|
|
|
|
clk_tck = CLK_TCK; /* This is a system call so cache it! */
|
|
no_powermeter = 0;
|
|
verbose = 0;
|
|
graceful_powerdown = 0;
|
|
|
|
/* What mode is the system controller in? 0 = don't send command
|
|
* lengths, 1 = send them.
|
|
*/
|
|
cmd_len = 0;
|
|
|
|
/* The e,n,s,t options are temporary. Please don't use them for
|
|
* programs that will see the outside of B7
|
|
*/
|
|
while((c = getopt(argc, argv, "dgprnestf:vlhk")) != -1) {
|
|
switch(c) {
|
|
case 'd':
|
|
if (getuid() != 0) {
|
|
fprintf(stderr,
|
|
"Must be root to run daemon.\n");
|
|
exit(1);
|
|
} else {
|
|
daemon_mode = 1;
|
|
}
|
|
break;
|
|
case 's':
|
|
no_env_info = 1;
|
|
break;
|
|
case 'n':
|
|
no_powermeter = 1;
|
|
break;
|
|
case 'p':
|
|
print_info = 1;
|
|
break;
|
|
case 'r':
|
|
raw_info = 1;
|
|
break;
|
|
case 'e':
|
|
panel = 'E';
|
|
break;
|
|
case 't':
|
|
panel = 'T';
|
|
break;
|
|
case 'f':
|
|
frequency = clk_tck * atoi(optarg) / 10;
|
|
break;
|
|
case 'h':
|
|
printpbstatus();
|
|
exit(0);
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
case 'g':
|
|
graceful_powerdown = 1;
|
|
break;
|
|
case 'l' :
|
|
log_vdc_delta = 1;
|
|
break;
|
|
case 'k':
|
|
if (getuid() != 0) {
|
|
fprintf(stderr,
|
|
"Must be root to power down.\n");
|
|
exit(1);
|
|
} else {
|
|
suicide_mode = 1;
|
|
}
|
|
break;
|
|
case '?':
|
|
fprintf(stderr,
|
|
"Usage: %s -d | -p | -r | -h\n",
|
|
argv[0]);
|
|
exit(1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (suicide_mode) {
|
|
daemon_mode = 1;
|
|
}
|
|
|
|
if (!daemon_mode && !print_info && !raw_info) {
|
|
fprintf(stderr, "Usage: %s -d [-g] | -p | -r\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
display.key = 0x53637444; /* SctD */
|
|
|
|
if (!daemon_mode) {
|
|
attach_ro_shmem(&display);
|
|
|
|
copy_in_env(&display, &environ);
|
|
|
|
if (!environ.valid) {
|
|
fprintf(stderr, "sysctlrd: Environment information is uninitialized!\n");
|
|
return 1;
|
|
}
|
|
|
|
if (print_info) {
|
|
printenvinfo(&environ);
|
|
} else if (raw_info) {
|
|
printrawinfo(&environ);
|
|
}
|
|
|
|
exit(0);
|
|
}
|
|
|
|
/* If we've made it here, we're the daemon. */
|
|
|
|
/* Open our syslog connection */
|
|
openlog("sysctlrd", LOG_PID, LOG_DAEMON);
|
|
|
|
if (!graceful_powerdown && !suicide_mode)
|
|
syslog(LOG_NOTICE, "Clean power-down turned off (see chkconfig).\n");
|
|
if (no_powermeter && !suicide_mode)
|
|
syslog(LOG_NOTICE, "CPU meter turned off (see chkconfig).\n");
|
|
|
|
/* 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);
|
|
|
|
signal(SIGTERM, cleanup);
|
|
signal(SIGQUIT, cleanup);
|
|
signal(SIGINT, cleanup);
|
|
|
|
/* We'll be sending sig power so ignore it. */
|
|
signal(SIGHUP, SIG_IGN);
|
|
|
|
initinfo(&display, &environ);
|
|
|
|
/* Get prom errors but don't report piggyback read state. */
|
|
getpromerrs();
|
|
|
|
openport(&display);
|
|
|
|
/* Wait longer for this stuff! */
|
|
ioctl(display.sysctlr, SYSC_RTOUT, 3 * HZ);
|
|
|
|
checkmsgs(&display);
|
|
|
|
set_length_mode(&display);
|
|
|
|
if (suicide_mode)
|
|
shut_down_power(&display);
|
|
|
|
getscale(&display, panel, &environ);
|
|
|
|
if (display.sc_scale <= 0)
|
|
exit(1);
|
|
|
|
/* If we haven't requested an update rate set the one for that panel. */
|
|
if (frequency == -1)
|
|
frequency = display.sc_update;
|
|
|
|
getlog(&display, NULL_SEQ);
|
|
|
|
logswitches(&display);
|
|
|
|
settime(&display);
|
|
|
|
createshmem(&display);
|
|
|
|
cycle = 0;
|
|
|
|
/* Back to a more reasonable timeout! */
|
|
ioctl(display.sysctlr, SYSC_RTOUT, HZ);
|
|
|
|
while (1) {
|
|
checkmsgs(&display);
|
|
if (!no_powermeter) {
|
|
getcpuinfo(&display);
|
|
dumpinfo(&display, &environ);
|
|
}
|
|
if (!(cycle % (30 * clk_tck / frequency)) && !no_env_info) {
|
|
/* First time through we initialize the extrema array,
|
|
* after that, if we detect a change we log it to SYSLOG.
|
|
*/
|
|
getenvinfo(&display, &environ);
|
|
on_initial_setup = 0;
|
|
|
|
copy_out_env(&display, &environ);
|
|
if (display.consec_rtouts >= TIMEOUT_GIVEUP) {
|
|
no_env_info = 1;
|
|
syslog(LOG_ERR,
|
|
"Giving up on fetching environmental information.\n");
|
|
}
|
|
if (!already_complained) {
|
|
already_complained =
|
|
check_sysctlr_rev(&environ);
|
|
}
|
|
}
|
|
if (!no_env_info) {
|
|
cycle++;
|
|
if (!(cycle % (LOG_INTERVAL * clk_tck / frequency)))
|
|
getlog(&display, NULL_SEQ);
|
|
}
|
|
#define DEBUG
|
|
#ifdef DEBUG
|
|
getlog (&display, environ.seq);
|
|
getlog (&display, NULL_SEQ);
|
|
#endif
|
|
sginap (frequency * (1 + display.consec_wtouts));
|
|
/* Wait 1/2 or 3 seconds */
|
|
/* If we've been timing out on writes, slow down. */
|
|
}
|
|
}
|
|
|