copied everything over from 2012 and removed all of the actual robot code except the drivetrain stuff
git-svn-id: https://robotics.mvla.net/svn/frc971/2013/trunk/src@4078 f308d9b7-e957-4cde-b6ac-9a88185e7312
diff --git a/aos/atom_code/starter/starter.cpp b/aos/atom_code/starter/starter.cpp
new file mode 100644
index 0000000..0cfddd5
--- /dev/null
+++ b/aos/atom_code/starter/starter.cpp
@@ -0,0 +1,354 @@
+#include "aos/atom_code/starter/starter.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/signalfd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/inotify.h>
+#include <sys/stat.h>
+
+#include "aos/aos_core.h"
+
+void niceexit(int status);
+
+pid_t start(const char *cmd, uint8_t times) {
+ char *which_cmd, *which_cmd_stm;
+ if (asprintf(&which_cmd, "which %s", cmd) == -1) {
+ LOG_IFINIT(ERROR, "creating \"which %s\" failed with %d: %s\n",
+ cmd, errno, strerror(errno));
+ niceexit(EXIT_FAILURE);
+ }
+ if (asprintf(&which_cmd_stm, "which %s.stm", cmd) == -1) {
+ LOG_IFINIT(ERROR, "creating \"which %s.stm\" failed with %d: %s\n",
+ cmd, errno, strerror(errno));
+ niceexit(EXIT_FAILURE);
+ }
+ FILE *which = popen(which_cmd, "r");
+ char exe[CMDLEN + 5], orig_exe[CMDLEN];
+ size_t ret;
+ if ((ret = fread(orig_exe, 1, sizeof(orig_exe), which)) == CMDLEN) {
+ LOG_IFINIT(ERROR, "`which %s` was too long. not starting '%s'\n", cmd, cmd);
+ return 0;
+ }
+ orig_exe[ret] = '\0';
+ if (pclose(which) == -1) {
+ LOG_IFINIT(WARNING, "pclose failed with %d: %s\n", errno, strerror(errno));
+ }
+ free(which_cmd);
+ if (strlen(orig_exe) == 0) { // which returned nothing; check if stm exists
+ LOG_IFINIT(INFO, "%s didn't exist. trying %s.stm\n", cmd, cmd);
+ FILE *which_stm = popen(which_cmd_stm, "r");
+ if ((ret = fread(orig_exe, 1, sizeof(orig_exe), which_stm)) == CMDLEN) {
+ LOG_IFINIT(ERROR, "`which %s.stm` was too long. not starting %s\n", cmd, cmd);
+ return 0;
+ }
+ orig_exe[ret] = '\0';
+ if (pclose(which) == -1) {
+ LOG_IFINIT(WARNING, "pclose failed with %d: %s\n", errno, strerror(errno));
+ }
+ }
+ if (strlen(orig_exe) == 0) {
+ LOG_IFINIT(WARNING, "couldn't find file '%s[.stm]'. not going to start it\n",
+ cmd);
+ return 0;
+ }
+ if (orig_exe[strlen(orig_exe) - 1] != '\n') {
+ LOG_IFINIT(WARNING, "no \\n on the end of `which %s[.stm]` output ('%s')\n",
+ cmd, orig_exe);
+ } else {
+ orig_exe[strlen(orig_exe) - 1] = '\0'; // get rid of the \n
+ }
+ strncpy(exe, orig_exe, CMDLEN);
+
+ strcat(exe, ".stm");
+ struct stat st;
+ errno = 0;
+ if (stat(orig_exe, &st) != 0 && errno != ENOENT) {
+ LOG_IFINIT(ERROR, "killing everything because stat('%s') failed with %d: %s\n",
+ orig_exe, errno, strerror(errno));
+ niceexit(EXIT_FAILURE);
+ } else if (errno == ENOENT) {
+ LOG_IFINIT(WARNING, "binary '%s' doesn't exist. not starting it\n", orig_exe);
+ return 0;
+ }
+ struct stat st2;
+ // if we can confirm it's already 0 size
+ bool orig_zero = stat(orig_exe, &st2) == 0 && st2.st_size == 0;
+ if (!orig_zero) {
+ // if it failed and it wasn't because it was missing
+ if (unlink(exe) != 0 && (errno != EROFS && errno != ENOENT)) {
+ LOG_IFINIT(ERROR,
+ "killing everything because unlink('%s') failed with %d: %s\n",
+ exe, errno, strerror(errno));
+ niceexit(EXIT_FAILURE);
+ }
+ if (link(orig_exe, exe) != 0) {
+ LOG_IFINIT(ERROR,
+ "killing everything because link('%s', '%s') failed with %d: %s\n",
+ orig_exe, exe, errno, strerror(errno));
+ niceexit(EXIT_FAILURE);
+ }
+ }
+ if (errno == EEXIST) {
+ LOG_IFINIT(INFO, "exe ('%s') already existed\n", exe);
+ }
+
+ pid_t child;
+ if ((child = fork()) == 0) {
+ execlp(exe, orig_exe, static_cast<char *>(NULL));
+ LOG_IFINIT(ERROR,
+ "killing everything because execlp('%s', '%s', NULL) "
+ "failed with %d: %s\n",
+ exe, cmd, errno, strerror(errno));
+ _exit(EXIT_FAILURE); // don't niceexit or anything because this is the child!!
+ }
+ if (child == -1) {
+ LOG_IFINIT(WARNING, "fork on '%s' failed with %d: %s",
+ cmd, errno, strerror(errno));
+ if (times < 100) {
+ return start(cmd, times + 1);
+ } else {
+ LOG_IFINIT(ERROR, "tried to start '%s' too many times. giving up\n", cmd);
+ return 0;
+ }
+ } else {
+ children[child] = cmd;
+ files[child] = orig_exe;
+ int ret = inotify_add_watch(notifyfd, orig_exe, IN_ATTRIB | IN_MODIFY);
+ if (ret < 0) {
+ LOG_IFINIT(WARNING, "inotify_add_watch('%s') failed: "
+ "not going to watch for changes to it because of %d: %s\n",
+ orig_exe, errno, strerror(errno));
+ } else {
+ watches[ret] = child;
+ mtimes[ret] = st2.st_mtime;
+ }
+ return child;
+ }
+}
+
+static bool exited = false;
+void exit_handler() {
+ if(exited) {
+ return;
+ } else {
+ exited = true;
+ }
+ fputs("starter: killing all children for exit\n", stdout);
+ for (auto it = children.begin(); it != children.end(); ++it) {
+ printf("starter: killing child %d ('%s') for exit\n", it->first, it->second);
+ kill(it->first, SIGKILL);
+ }
+ if (sigfd != 0) {
+ close(sigfd);
+ }
+ if (notifyfd != 0) {
+ close(notifyfd);
+ }
+}
+void niceexit(int status) {
+ printf("starter: niceexit(%d) EXIT_SUCCESS=%d EXIT_FAILURE=%d\n",
+ status, EXIT_SUCCESS, EXIT_FAILURE);
+ exit_handler();
+ exit(status);
+}
+
+int main(int argc, char *argv[]) {
+ if (argc < 2) {
+ fputs("starter: error: need an argument specifying what file to use\n", stderr);
+ niceexit(EXIT_FAILURE);
+ } else if(argc > 2) {
+ fputs("starter: warning: too many arguments\n", stderr);
+ }
+
+ atexit(exit_handler);
+
+ notifyfd = inotify_init1(IN_NONBLOCK);
+
+ pid_t core = start("core", 0);
+ if (core == 0) {
+ fputs("starter: error: core didn't exist\n", stderr);
+ niceexit(EXIT_FAILURE);
+ }
+ fprintf(stderr, "starter: info: core's pid is %jd\n", static_cast<intmax_t>(core));
+ FILE *pid_file = fopen("/tmp/starter.pid", "w");
+ if (pid_file == NULL) {
+ perror("fopen(/tmp/starter.pid)");
+ } else {
+ if (fprintf(pid_file, "%d", core) == -1) {
+ fprintf(stderr, "starter: error: fprintf(pid_file, core(=%d)) failed "
+ "with %d: %s",
+ core, errno, strerror(errno));
+ }
+ fclose(pid_file);
+ }
+ sleep(1);
+ if (kill(core, 0) != 0) {
+ fprintf(stderr, "starter: couldn't kill(%jd(=core), 0) because of %d: %s\n",
+ static_cast<intmax_t>(core), errno, strerror(errno));
+ niceexit(EXIT_FAILURE);
+ }
+ fputs("starter: before init\n", stdout);
+ aos::InitNRT();
+ fputs("starter: after init\n", stdout);
+
+ FILE *list = fopen(argv[1], "re");
+ char line[CMDLEN + 1];
+ char *line_copy;
+ uint8_t too_long = 0;
+ while (fgets(line, sizeof(line), list) != NULL) {
+ if (line[strlen(line) - 1] != '\n') {
+ LOG(WARNING, "command segment '%s' is too long. "
+ "increase the size of the line char[] above " __FILE__ ": %d\n",
+ line, __LINE__);
+ too_long = 1;
+ continue;
+ }
+ if (too_long) {
+ too_long = 0;
+ LOG(WARNING, "\tgot last chunk of too long line: '%s'\n", line);
+ continue; // don't try running the last little chunk
+ }
+ line[strlen(line) - 1] = '\0'; // get rid of the \n
+ line_copy = new char[strlen(line) + 1];
+ memcpy(line_copy, line, strlen(line) + 1);
+ fprintf(stderr, "starter: info: going to start \"%s\"\n", line_copy);
+ start(line_copy, 0);
+ }
+ fclose(list);
+ LOG(INFO, "started everything\n");
+
+ sigset_t mask;
+ sigemptyset (&mask);
+ sigaddset (&mask, SIGCHLD);
+ sigprocmask (SIG_BLOCK, &mask, NULL);
+ sigfd = signalfd (-1, &mask, O_NONBLOCK);
+
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ siginfo_t infop;
+ signalfd_siginfo fdsi;
+ inotify_event notifyevt;
+ int ret;
+ while (1) {
+ FD_SET(sigfd, &readfds);
+ FD_SET(notifyfd, &readfds);
+ timeval timeout;
+ timeout.tv_sec = restarts.empty() ? 2 : 0;
+ timeout.tv_usec = 100000;
+ ret = select (FD_SETSIZE, &readfds, NULL, NULL, &timeout);
+
+ if (ret == 0) { // timeout
+ auto it = restarts.begin();
+ // WARNING because the message about it dying will be
+ for (; it != restarts.end(); it++) {
+ LOG(WARNING, "restarting process %d ('%s') by giving it a SIGKILL(%d)\n",
+ *it, children[*it], SIGKILL);
+ kill(*it, SIGKILL);
+ }
+ restarts.clear();
+ }
+
+ if (FD_ISSET(notifyfd, &readfds)) {
+ if ((ret = read(notifyfd, ¬ifyevt, sizeof(notifyevt))) ==
+ sizeof(notifyevt)) {
+ if (watches.count(notifyevt.wd)) {
+ struct stat st;
+ if (!children.count(watches[notifyevt.wd]) ||
+ stat(files[watches[notifyevt.wd]], &st) == 0) {
+ if (mtimes[notifyevt.wd] == st.st_mtime) {
+ LOG(DEBUG, "ignoring trigger of watch id %d (file '%s')"
+ " because mtime didn't change\n",
+ notifyevt.wd, files[watches[notifyevt.wd]]);
+ } else if (children.count(watches[notifyevt.wd])) {
+ LOG(DEBUG, "adding process %d to the restart list\n",
+ watches[notifyevt.wd]);
+ restarts.insert(watches[notifyevt.wd]);
+ } else {
+ LOG(DEBUG, "children doesn't have entry for PID %d\n",
+ watches[notifyevt.wd]);
+ }
+ } else {
+ LOG(ERROR, "stat('%s') failed with %d: %s\n",
+ files[watches[notifyevt.wd]], errno, strerror(errno));
+ }
+ } else {
+ LOG(WARNING, "no PID for watch id %d\n", notifyevt.wd);
+ }
+ } else {
+ if (ret == -1) {
+ LOG(WARNING, "read(notifyfd) failed with %d: %s", errno, strerror(errno));
+ } else {
+ LOG(WARNING, "couldn't get a whole inotify_event(%d) (only got %d)\n",
+ sizeof(notifyevt), ret);
+ }
+ }
+ }
+
+ if (FD_ISSET(sigfd, &readfds)) {
+ while(read (sigfd, &fdsi, sizeof fdsi) > 0);
+ }
+ while (1) {
+ infop.si_pid = 0;
+ if (waitid(P_ALL, 0, &infop, WEXITED | WSTOPPED | WNOHANG) == 0) {
+ if (infop.si_pid == 0) {
+ goto after_loop; // no more child process changes pending
+ }
+ switch (infop.si_code) {
+ case CLD_EXITED:
+ LOG(WARNING, "child %d (%s) exited with status %d\n",
+ infop.si_pid, children[infop.si_pid], infop.si_status);
+ break;
+ case CLD_DUMPED:
+ LOG(INFO, "child %d actually dumped core. "
+ "falling through to killed by signal case\n", infop.si_pid);
+ case CLD_KILLED:
+ LOG(WARNING, "child %d (%s) was killed by signal %d (%s)\n",
+ infop.si_pid, children[infop.si_pid], infop.si_status,
+ strsignal(infop.si_status));
+ break;
+ case CLD_STOPPED:
+ LOG(WARNING, "child %d (%s) was stopped by signal %d "
+ "(giving it a SIGCONT(%d))\n",
+ infop.si_pid, children[infop.si_pid], infop.si_status, SIGCONT);
+ kill(infop.si_pid, SIGCONT);
+ continue;
+ default:
+ LOG(WARNING, "something happened to child %d (%s) (killing it)\n",
+ infop.si_pid, children[infop.si_pid]);
+ kill(infop.si_pid, SIGKILL);
+ continue;
+ }
+ if (infop.si_pid == core) {
+ fprintf(stderr, "starter: si_code=%d CLD_EXITED=%d CLD_DUMPED=%d "
+ "CLD_KILLED=%d CLD_STOPPED=%d si_status=%d (sig '%s')\n",
+ infop.si_code, CLD_EXITED, CLD_DUMPED, CLD_KILLED,
+ CLD_STOPPED, infop.si_status, strsignal(infop.si_status));
+ // core has died. logging is down too
+ fputs("starter: error: core died. exiting\n", stderr);
+ niceexit(EXIT_FAILURE);
+ }
+
+ /*// remove all of the watches assigned to the pid that just died
+ for (auto it = watches.begin(); it != watches.end(); ++it) {
+ if (it->second == infop.si_pid) {
+ watches_to_ignore.insert(it->first);
+ }
+ }
+ for (auto it = watches_to_ignore.begin();
+ it != watches_to_ignore.end(); ++it) {
+ LOG(DEBUG, "watch id %d was on PID %d\n", *it, infop.si_pid);
+ watches.erase(*it);
+ }*/
+
+ start(children[infop.si_pid], 0);
+ LOG(DEBUG, "erasing %d from children\n", infop.si_pid);
+ children.erase(infop.si_pid);
+ } else {
+ LOG(WARNING, "waitid failed with %d: %s", errno, strerror(errno));
+ }
+ }
+after_loop: ;
+ }
+}
diff --git a/aos/atom_code/starter/starter.gyp b/aos/atom_code/starter/starter.gyp
new file mode 100644
index 0000000..44e4b08
--- /dev/null
+++ b/aos/atom_code/starter/starter.gyp
@@ -0,0 +1,23 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'starter_exe',
+ 'type': 'executable',
+ 'sources': [
+ 'starter.cpp',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:libaos',
+ ],
+ 'copies': [
+ {
+ 'destination': '<(rsync_dir)',
+ 'files': [
+ 'starter.sh',
+ 'starter_loop.sh',
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/aos/atom_code/starter/starter.h b/aos/atom_code/starter/starter.h
new file mode 100644
index 0000000..16bbe01
--- /dev/null
+++ b/aos/atom_code/starter/starter.h
@@ -0,0 +1,28 @@
+#ifndef __AOS_STARTER_H_
+#define __AOS_STARTER_H_
+
+#include <map>
+#include <sys/types.h>
+#include <signal.h>
+#include <stdint.h>
+#include <string>
+#include <errno.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <set>
+
+using namespace std;
+
+map<pid_t, const char *> children;
+map<pid_t, const char *> files; // the names of the actual files
+map<int, pid_t> watches;
+set<pid_t> restarts;
+map<int, time_t> mtimes;
+
+int sigfd = 0;
+int notifyfd = 0;
+
+const size_t CMDLEN = 5000;
+
+#endif
+
diff --git a/aos/atom_code/starter/starter.sh b/aos/atom_code/starter/starter.sh
new file mode 100755
index 0000000..103180e
--- /dev/null
+++ b/aos/atom_code/starter/starter.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+#echo $$ > /var/run/`basename $0`.pid IT FORKS AFTER THIS!!!!
+insmod /home/driver/robot_code/bin/aos_module.ko
+#chrt -p 45 `pidof sshd`
+chrt -o 0 bash -c "export PATH=$PATH:/home/driver/robot_code/bin; starter_loop.sh $*" &
+#chrt -o 0 bash -c "while true; do cd /home/driver/mjpg-streamer2; ./server.sh; sleep 5; done" &
+
+# Log everything from the serial port...
+#SERIAL_LOG_FILE=$(date "/home/driver/tmp/robot_logs/serial_log.%F_%H-%M-%S")
+#chrt -o 0 bash -c "( stty -echo -echoe -echok 9600; cat > ${SERIAL_LOG_FILE} ) < /dev/ttyUSB0" &
+
+# Wireshark _everything_ we can see...
+#DUMPCAP_LOG_FILE=$(date "/home/driver/tmp/robot_logs/dumpcap.%F_%H-%M-%S")
+#DUMPCAP_STDOUT_FILE=$(date "/home/driver/tmp/robot_logs/stdout_dumpcap.%F_%H-%M-%S")
+#chrt -o 0 bash -c "dumpcap -i eth0 -w ${DUMPCAP_LOG_FILE} -f 'not port 8080 and not net 10.9.71.13' > ${DUMPCAP_STDOUT_FILE}" &
+
diff --git a/aos/atom_code/starter/starter_loop.sh b/aos/atom_code/starter/starter_loop.sh
new file mode 100755
index 0000000..b4e1d5c
--- /dev/null
+++ b/aos/atom_code/starter/starter_loop.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+for ((i=1; 1; i++)); do
+ starter_exe $* 1>/tmp/starter${i}_stdout 2>/tmp/starter${i}_stderr
+ sleep 2
+done
+
diff --git a/aos/atom_code/starter/testing_list.txt b/aos/atom_code/starter/testing_list.txt
new file mode 100644
index 0000000..96d412d
--- /dev/null
+++ b/aos/atom_code/starter/testing_list.txt
@@ -0,0 +1,3 @@
+../bin/LogReader
+../bin/JoystickCode
+../bin/AutoMode