summaryrefslogtreecommitdiff
path: root/recipes/sccd/files/sccd.c
diff options
context:
space:
mode:
Diffstat (limited to 'recipes/sccd/files/sccd.c')
-rw-r--r--recipes/sccd/files/sccd.c523
1 files changed, 523 insertions, 0 deletions
diff --git a/recipes/sccd/files/sccd.c b/recipes/sccd/files/sccd.c
new file mode 100644
index 0000000000..4f74c1d49c
--- /dev/null
+++ b/recipes/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);
+}
+