diff options
Diffstat (limited to 'packages/sccd/files/sccd.c')
-rw-r--r-- | packages/sccd/files/sccd.c | 523 |
1 files changed, 523 insertions, 0 deletions
diff --git a/packages/sccd/files/sccd.c b/packages/sccd/files/sccd.c new file mode 100644 index 0000000000..4f74c1d49c --- /dev/null +++ b/packages/sccd/files/sccd.c @@ -0,0 +1,523 @@ +/* + * Copyright (c) 2006 + * Protium Computing, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Protium Computing, Inc. + * 4. The name of Protium Computing, Inc. may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY PROTIUM COMPUTING ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PROTIUM COMPUTING BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <stdio.h> +#include <fcntl.h> +#include <time.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <utmp.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <sys/syslog.h> +#include <sys/param.h> +#include <termios.h> + +#include "scc.h" + +#define HALT "/sbin/halt" +#define REBOOT "/sbin/reboot" + +char * prog; +int verbose = 0; + +extern int scc_die; + +void +usage(void) +{ + fprintf(stderr, "Usage: %s OPTIONS\n", prog); + fprintf(stderr, + "StorCenter Control Daemon manages the StorCenter's LED, fan and soft power.\n\n"); + fprintf(stderr," -?\t\tthis message\n"); + fprintf(stderr," -h\t\tthis message\n"); + fprintf(stderr," -c\t\tallow core files\n"); + fprintf(stderr," -f\t\trun in the foreground\n"); + fprintf(stderr," -v\t\tverbose, show all packets (must be used with -f)\n"); + (void)exit(2); +} + +int +sendrecv(int fd, int *i, int *j) +{ + char buf[8]; + int rc; + fd_set fds; + struct timeval sec = {1, 0}; + struct timespec tenth_sec = {0, 100000}; + + + /* build the buffer */ + *(int *)(buf) = *i; + *(int *)(buf + 4) = *j; + + if (verbose) + printf("Send: [0x%08x 0x%08x]\n", *i, *j); + + if ((rc = write(fd, buf, 8)) != 8) { + syslog(LOG_ERR, "Write failed to controller"); + return(-1); + } + + /* Wait for data */ + FD_ZERO(&fds); + FD_SET(fd, &fds); + rc = select(1024, &fds, NULL, NULL, &sec); + + if (rc == -1) { + syslog(LOG_ERR, "Controller select failed"); + return (-1); + } else if (!rc) { + syslog(LOG_ERR, "Controller timeout"); + return (-1); + } + + /* Consume it */ + if ((rc = read(fd, buf, 8)) != 8) { + syslog(LOG_ERR, "Read from controller failed"); + return(-1); + } + + *i = *(int *)(buf); + *j = *(int *)(buf + 4); + + if (verbose) + printf("Recv: [0x%08x 0x%08x]\n", *i, *j); + + /* Let it rest */ + nanosleep(&tenth_sec, NULL); + + return(0); +} + +int +setup_clnt() +{ + int s; + struct sockaddr_un local; + int len; + + if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + perror("socket"); + return(-1); + } + + local.sun_family = AF_UNIX; + strcpy(local.sun_path, SCC_SOCKET); + unlink(local.sun_path); + + len = strlen(local.sun_path) + sizeof(local.sun_family); + if (bind(s, (struct sockaddr *)&local, len) == -1) { + perror("bind"); + close(s); + return(-1); + } + + if (listen(s, 5) == -1) { + perror("listen"); + close(s); + return(-1); + } + + /* Force the permission on the unix socket */ + if (chmod(SCC_SOCKET, (S_IWUSR | S_IRUSR)) < 0) { + perror("chmod socket"); + } + + return(s); +} + +int +setup_cntlr() +{ + int i, j, fd; + char *dev = SCC_DEVICE; + struct termios ti; + + /* setup the serial connection to 9600, 8n1 */ + if ((fd = open(dev, O_RDWR|O_NOCTTY)) < 0) { + perror("Open failed"); + return(-1); + } + + if (ioctl(fd, TCGETS, &ti) < 0) { + perror("TCGETS failed"); + return(-1); + } + + if (ioctl(fd, TCFLSH, 0) < 0) { + perror("TCFLSH failed"); + return(-1); + } + + ti.c_iflag = IGNPAR; + ti.c_oflag = 0; + ti.c_cflag = (B9600 | CS8 | CREAD | CLOCAL); + ti.c_lflag = 0; + ti.c_line = N_TTY; + bzero(ti.c_cc, NCCS); + ti.c_cc[VMIN] = 0x08; + + if (ioctl(fd, TCSETS, &ti) < 0) { + perror("TCSETS failed"); + return(-1); + } + + if (verbose) printf("Resetting microcontroller\n"); + i = SCC_RESETW1; + j = SCC_RESETW2; + scc_setval(&j, SCC_CKSUMMASK, (int)scc_cksum(&i, &j)); + if (sendrecv(fd, &i, &j) < 0) { + return(-1); + } + + return(fd); +} + +int +handle_clnt(int clnt, int *w1, int *w2) +{ + char buf[SCC_PACKETLEN]; + int s, t, i, j, *x, *y; + struct sockaddr_un remote; + + t = sizeof(remote); + s = accept(clnt, (struct sockaddr *)&remote, &t); + if (s < 0) { + syslog(LOG_ERR, "Unable to accept client"); + return(-1); + } + + if (recv(s, buf, SCC_PACKETLEN, 0) != SCC_PACKETLEN) { + return(-1); + } + close(s); + + x = (int *)&buf[0]; + y = (int *)&buf[4]; + + /* + * Clients only send fields they want enacted. This allows this daemon + * to keep the other values constant without informing the clients + * of the current setting. It allows the protocol to unidirectional. + * + * Take the data from the client and add it to the current + * state then validate the resulting state. If valid, return + * the new state. If not don't corrupt the existing state. + */ + i = *w1; + j = *w2; + + if (*x & SCC_POWERMASK) + scc_setval(&i, SCC_POWERMASK, (*x & SCC_POWERMASK)); + if (*x & SCC_LEDMASK) + scc_setval(&i, SCC_LEDMASK, (*x & SCC_LEDMASK)); + if (*x & SCC_LEDRATEMASK) + scc_setval(&i, SCC_LEDRATEMASK, (*x & SCC_LEDRATEMASK)); + if (*x & SCC_FANMASK) + scc_setval(&i, SCC_FANMASK, (*x & SCC_FANMASK)); + + if (*y & SCC_FANTEMPONMASK) + scc_setval(&j, SCC_FANTEMPONMASK, (*y & SCC_FANTEMPONMASK)); + if (*y & SCC_FANTEMPOFFMASK) + scc_setval(&j, SCC_FANTEMPOFFMASK, (*y & SCC_FANTEMPOFFMASK)); + scc_setval(&j, SCC_IDMASK, SCC_HOST); + scc_setval(&j, SCC_CKSUMMASK, (int)scc_cksum(&i, &j)); + + if (scc_validate(&i, &j, verbose) < 0) { + /* log it and return */ + return(-1); + } + + /* + * If we are here we got a good packet, return it. + */ + *w1 = i; + *w2 = j; + return(0); +} + +#ifdef NOTUSED +char +runlevel() +{ + struct utmp *ut; + time_t boot; + + /* + * Find last boot time + */ + time(&boot); + boot -= (times(NULL) / HZ); + + setutent(); + while ((ut = getutent()) != NULL) { + /* + * Find runlevel after our last boot + */ + if (ut->ut_type == RUN_LVL && ut->ut_time > boot) + return(ut->ut_pid & 0xff); + } + endutent(); + + return(0xff); +} +#endif + +int +work_loop(int cntlr, int clnt) +{ + + int rc, w1, w2, c1, c2, saverate; + int shutdown_called = 0; + pid_t pid; + fd_set fds; + struct timeval tv; + + /* + * If we are booting set to red flash and + * wait for someone to change it. + * If we are already in runlevel 5 then set it to blue + */ + scc_defaults(&w1, &w2); + scc_setval(&w1, SCC_LEDMASK, SCC_LEDREDFLASH); + scc_setval(&w2, SCC_IDMASK, SCC_HOST); + scc_setval(&w2, SCC_CKSUMMASK, (int)scc_cksum(&w1, &w2)); + + if (verbose) { + printf("New State: \n"); + scc_validate(&w1, &w2, verbose); + } + + if (sendrecv(cntlr, &w1, &w2) < 0) { + /* log then carry on */ + } + + while (!scc_die) { + /* + * Wait for data. How long? Well detecting soft power + * events should happen fairly quickly, 1 second between + * probes should be enough, but detecting disk activity + * probably needs to be a little faster. So we try 0.5s or + * 500,000 micro seconds. If that steals too much CPU + * then we backoff here. + */ + FD_ZERO(&fds); + FD_SET(clnt, &fds); + tv.tv_sec = 0; + tv.tv_usec = 500000; + rc = select(1024, &fds, NULL, NULL, &tv); + + if (rc < 0) { + /* Select failed try again */ + continue; + } else if (rc) { + /* Got a clnt request */ + if (handle_clnt(clnt, &w1, &w2) < 0) + continue; + } else { + /* timeout - just fallthrough to refresh ctlr */ + } + + /* + * Here's how we do blinking lights for disk activity. + * If there is disk activity and the LED is blue, then + * set the led to blueflash and the rate to io rate. + * Next time through if there is still activity, the + * LED is unchanged. If no activity and the led is flashing + * blue then return it to solid blue and restore the rate. + */ + if (disk_activity()) { + if ((w1 & SCC_LEDMASK) == SCC_LEDBLUE) { + saverate = (w1 & SCC_LEDRATEMASK); + scc_setval(&w1, SCC_LEDMASK, SCC_LEDBLUEFLASH); + scc_setrate(&w1, SCC_LEDRATEMASK, + SCC_LEDRATESHIFT, SCC_LEDIORATE); + } + } else { + if ((w1 & SCC_LEDMASK) == SCC_LEDBLUEFLASH) { + scc_setval(&w1, SCC_LEDMASK, SCC_LEDBLUE); + scc_setval(&w1, SCC_LEDRATEMASK, saverate); + } + } + + /* + * Getting here because of a timeout or client request + * in either case refresh the cntlr. Don't use w1 and w2 + * to refresh, cause the daemon should not incorporate + * the controller's view of the state into the daemon's + * view. Only a client should alter w1 and w2. + */ + c1 = w1; + c2 = w2; + if (sendrecv(cntlr, &c1, &c2) < 0) { + continue; + } + + /* + * Now examine the packet from the controller to see + * if action needs to be taken. + * If the power state is either: stop, restart or reset + * start shutting down the box. Once shutdown (reboot or halt) + * has beed call don't do it again. + */ + if (shutdown_called) + continue; + + switch (c1 & SCC_POWERMASK) { + case SCC_STOP: + /* exec halt */ + syslog(LOG_INFO, "Halt requested"); + if ((pid = fork()) == -1) { + break; + } + + if (pid == 0) { + /* i am child */ + char *argv[] = {HALT, NULL}; + execv(HALT, argv); + exit(EXIT_FAILURE); + } + + shutdown_called = 1; + break; + + case SCC_RESTART: + case SCC_RESET: + /* exec reboot */ + syslog(LOG_INFO, "Reboot requested"); + if ((pid = fork()) == -1) { + break; + } + if (pid == 0) { + /* i am child */ + char *argv[] = {REBOOT, NULL}; + execv(REBOOT, argv); + exit(EXIT_FAILURE); + } + + shutdown_called = 1; + break; + + default: + /* Nothing to do */ + break; + } + } +} + +int +main(int argc, char *argv[]) +{ + + int cntlr, clnt, w1, w2; + int c, daemon, cores; + + struct stat st; + extern char *optarg; + extern int optind, opterr, optopt; + + prog = argv[0]; + daemon = 1; + cores = 0; + + while ((c = getopt(argc, argv, "cfhv?")) != EOF) { + switch (c) { + case 'h': + usage(); + break; + case 'c': + cores=1; + break; + case 'f': + daemon=0; + break; + case 'v': + verbose=1; + break; + case '?': + usage(); + break; + + } + } + + if (verbose && daemon) + usage(); + + if (stat(HALT, &st) < 0) { + perror("Couldn't find halt"); + exit(EXIT_FAILURE); + } + + if (stat(REBOOT, &st) < 0) { + perror("Couldn't find reboot"); + exit(EXIT_FAILURE); + } + + if (daemon) { + if (scc_daemonize(cores) < 0) + exit(EXIT_FAILURE); + } else { + /* + * Open syslog + */ + openlog("sccd", LOG_PID, LOG_DAEMON); + syslog(LOG_INFO, "Starting"); + } + + /* + * Setup the controller connection + */ + if ((cntlr = setup_cntlr()) < 0) + exit(EXIT_FAILURE); + + /* + * Setup the client connection + */ + if ((clnt = setup_clnt()) < 0) + exit(EXIT_FAILURE); + + work_loop(cntlr, clnt); + + close(cntlr); + close(clnt); + unlink(SCC_PIDFILE); + exit(EXIT_SUCCESS); +} + |