/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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); }