Compare commits

..

No commits in common. "d2749e74049ec07756a203f0d09c133767ee0a60" and "c1b08412cf436f2ab1b3e0966566597f3bdd1679" have entirely different histories.

2 changed files with 121 additions and 329 deletions

185
d3d.py
View file

@ -1,184 +1,9 @@
#! /usr/bin/python3
import os, sys, os.path, time, math
import os, sys, os.path, time
os.chdir(os.path.dirname(sys.argv[0]))
os.system("screen ./d3d.rc")
class watchdog:
period = 60
def __init__(self):
self.wd = open("d3d/irena-live", "w")
self.time = time.time() + self.period
def due(self):
return time.time() >= self.time
def kick(self):
self.wd.write(".");
self.wd.flush()
self.time += self.period
dog = watchdog()
from led import LED
Rate_LED = LED("/var/run/gpio/Rate_LED")
Error_LED = LED("/var/run/gpio/Error_LED")
class parse_status:
"parse the lines of the `irena-status` file"
verbosity = 1
def __init__(self, lines):
self.hosttime = time.time()
self.parse(lines)
def parse(self, lines):
rates = False
self.states = {}
self.rates = {}
for l in lines:
ll = l.split()
if ll[0]=="irena":
self.version = ll[2]
self.time=int(ll[4])
fhosttime = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(self.hosttime))
if verbosity >= 1:
print(f"Host time {fhosttime}, Status time {ll[5]}")
if ll[0]=="data":
rates = True
if not l[0].isdigit():
continue
if not rates:
self.rates[ll[1]] = parse_state(ll)
else:
self.rate[ll[1]] = parse_rate(ll)
max_age = 300
def old(self):
return self.time + self.max_age < self.hosttime
def analyse(self):
"Try to identify failure modes and lauch mitigations"
pass
def set_Rate(self, LED):
"Set the green LED frequency based on trigger rate"
r = self["T_rate"].value.percent()
r = max(0, min(100, r))
f = log(r+1)/log(100)*4+0.1
LED.sine_freq(f)
def set_ERROR(self, LED):
"Set the yellow LED based on warning or error status counts"
ne = 0
nw = 0
for s in self.status:
if not s.is_ok():
if s.is_warn():
nw += 1
else:
ne += 1
if ne:
LED.blink_freq(10 - min(ne,19)/2)
elif nw:
LED.on(amp = 20*min(5,nw))
else:
LED.off()
class parse_state:
def __init__(self, ll):
self.ix = int(ll[0])
self.name = ll[1]
self.value = float(ll[2])
self.mean = float(ll[3])
self.units = ll[4]
self.age = int(ll[5])
self.status = ll[6]
self.reset = int(ll[7])
self.limits = list(map(float, ll[8:]))
if !self.is_ok() and verbosity>=1:
print(self)
def is_ok(self):
return self.status == "OK"
def is_warn(self):
return self.status == "WARN"
def is_error(self):
return self.status == "ERROR"
def percent(self):
return 100*(self.value - self.limits[1])/(self.limits[2]-self.limits[1])
def __str__(self):
return " ".join([f"{self.name}:",
f"{self.mean} {self.units}",
f"{self.status}",
f"{self.percent():.1f} %",
])
class parse_rate:
def __init__(self, ll):
self.ix = int(ll[0])
self.name = ll[1]
self.alloc_per_sec = int(ll[2])
self.alloc_bytes = int(ll[3])
self.used_percent = float(ll[4])
self.used_bytes = int(ll[5])
self.dropped_percent = float(ll[6])
self.dropped_bytes = int(ll[7])
self.burst = int(ll[8])
def __str__(self):
return " ".join([f"{self.name}:",
f"{self.alloc_per_sec}/s,",
f"used: {self.used_percent:.1f} %,",
f"dropped: {self.dropped_percent:.1f} %",
])
def restart_irena():
pass
last_status = 0
wd = open("d3d/irena-live", "w")
while True:
t = time.time()
t0 = t+10
t1 = Rate_LED.update()
t2 = Rate_LED.update()
dt = t-min(t1,t2,t3)
if dt>0:
time.sleep(dt)
if t > last_status + 10:
try:
with open("d3d/irena-status") as st:
status=parse_status(st.readlines(hint=0x2000))
except Exception as e:
if verbosity >= 0:
print(repr(e))
continue
if last_status != status.time:
status.analyse()
status.set_Rate(Rate_LED)
status.set_Error(Error_LED)
last_status = status.time
if not dog.due():
continue
if status.old():
restart_irena()
if verbosity >= 0:
print(f"not kicking the dog because status is {status.hosttime - status.time} s old")
continue
dog.kick()
wd.write(".");
wd.flush()
time.sleep(10)

265
irena.c
View file

@ -28,71 +28,37 @@ const char version[] = IRENA_VERSION;
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <stdarg.h>
FILE *mout;
int maintainance = 0;
int verbosity[2] = {1,1};
int in_prompt;
const char *prompt;
void check_prompt()
{
if (in_prompt==1) {
fprintf(stderr, "\r\e[K");
fprintf(mout, "\n");
in_prompt = 2;
}
}
void restore_prompt()
{
if (in_prompt != 2)
return;
in_prompt = 1;
// int nread;
// TIOCINQ/FIONREAD returns what is available to read immediately,
// not the contents of the incomplete line. Not usefull!
// Inject a CR, read the line, reinject all those characters?
// if (ioctl(0, TIOCINQ, &nread))
// do not call merror()!
// return;
if (prompt) {
fprintf(stderr, "%s…(^R)…", prompt);
fflush(stderr);
}
if (0) {
char rprnt = 'R' & 0x1f;
ioctl(0, TIOCSTI, &rprnt);
}
}
static inline void silent(int v) { if (verbosity[0] < v) verbosity[1] = 0; }
static inline void unsilent() { verbosity[1] = verbosity[0]; }
static inline void loud(int v) { if (v > verbosity[0]) verbosity[1] = v; }
static inline int verbose(int v) { v = verbosity[1] >= v; unsilent(); if (v) check_prompt(); return v; }
static inline void unverbose() { restore_prompt(); }
int mprintf(int v, const char *restrict format, ...)
{
if (!verbose(v)) return 0;
va_list ap;
va_start (ap, format);
int r = vfprintf(mout, format, ap);
unverbose();
return r;
}
// replacement for perror()
void merror(const char *s)
{
int e = errno;
mprintf(-1, "%s: %s\n", s, strerror(e));
if (verbose(-1))
fprintf(mout, "%s: %s\n", s, strerror(e));
}
#define DEBUG 3
#ifdef DEBUG
# define debug(fmt, ...) mprintf(DEBUG, fmt, __VA_ARGS__)
# define debug(fmt, ...) if (verbose(DEBUG)) fprintf(mout, fmt, __VA_ARGS__)
#else
# define debug(fmt, ...)
#endif
@ -241,10 +207,13 @@ void file_close(struct file_format *ff)
if (rename(ff->filename, fn))
merror(ff->filename);
}
mprintf(2, "closed and renamed «%s» to «%s»\n", ff->filename, fn);
if (verbose(2))
fprintf(mout, "closed and renamed «%s» to «%s»\n", ff->filename, fn);
}
else {
if (verbose(2))
fprintf(mout, "closed file «%s»\n", ff->filename);
}
else
mprintf(2, "closed file «%s»\n", ff->filename);
}
void file_open(struct file_format *ff, time_t t)
@ -267,7 +236,7 @@ void file_open(struct file_format *ff, time_t t)
}
if (!strcmp(ff->filename, "stderr")) {
if (!(ff->flags & FILE_STDIO)) {
mprintf(-1, "cannot use write() on «stderr»\n");
fprintf(mout, "cannot use write() on «stderr»\n");
ff->flags |= FILE_ERROR;
return;
}
@ -279,7 +248,9 @@ void file_open(struct file_format *ff, time_t t)
char *mode = ff->flags&FILE_APPEND ? "a" : "w";
ff->f = fopen(ff->filename, mode);
if (ff->f) {
mprintf(2, "fopen(%s, %s)\n", ff->filename, mode);
if (verbose(2))
fprintf(mout, "fopen(%s, %s)\n",
ff->filename, mode);
return;
}
}
@ -289,7 +260,8 @@ void file_open(struct file_format *ff, time_t t)
f = O_CREAT|O_WRONLY|O_TRUNC;
ff->fd = open(ff->filename, f, 0664);
if (ff->fd>=0) {
mprintf(2, "open(%s)\n", ff->filename);
if (verbose(2))
fprintf(mout, "open(%s)\n", ff->filename);
return;
}
}
@ -328,7 +300,7 @@ int config_file(struct file_format *f, int na, char **av)
int do_close = 0;
int do_open = 0;
if (na<1 || na>5) {
mprintf(-2,
fprintf(mout,
"usage: %s [«strftime» [«rotate» [«modulus» [«flags»]]]] [close|rotate]\n"
"- «strftime»: filename strftime format | stdout | stderr\n"
"- «rotate»: rotation interval, seconds (-1 for no change)\n"
@ -370,7 +342,7 @@ int config_file(struct file_format *f, int na, char **av)
if (flags & FILE_STDOUT) fnf = "stdout";
}
if (strlen(fnf) > MAX_FN-2) {
mprintf(-2, "filename format too long (>%d): %s\n", MAX_FN-2, fnf);
fprintf(mout, "filename format too long (>%d): %s\n", MAX_FN-2, fnf);
return -1;
}
strncpy(f->format, fnf, MAX_FN-1);
@ -382,7 +354,7 @@ int config_file(struct file_format *f, int na, char **av)
else if (do_open)
file_open(f, 0);
int io = is_open(f);
mprintf(-2, "%s '%s' %d %d 0x%x '%s'\n",
fprintf(mout, "%s '%s' %d %d 0x%x '%s'\n",
av[0], f->format, f->rotate, f->rotate_modulus, f->flags,
io ? f->filename : "CLOSED");
return io;
@ -610,7 +582,7 @@ int config_priority(int na, char **av)
na--;
}
if (na<2 || na>6) {
mprintf(-2,
fprintf(mout,
"usage: %s [«index» [«rate» [«inst» [«burst» [«flags»]]]]] [global] [reset]\n"
"- configure data rate limits\n"
"- «index»: 0…%d: table index\n"
@ -632,7 +604,7 @@ int config_priority(int na, char **av)
|| number(av[1], &index))
return -1;
if (index<0 || index>=NPRIO) {
mprintf(-2, "rate index %d out of range 0…%d\n",
fprintf(mout, "rate index %d out of range 0…%d\n",
index, NPRIO-1);
return -1;
}
@ -642,7 +614,7 @@ int config_priority(int na, char **av)
if (burst >= 0) ep->inst_burst = burst;
if (flags != -1) ep->flags = flags;
flags |= fflags;
mprintf(-2, "%s: %s %u %u %u %u 0x%x -> %llu(%llu)/%llu %u/%u\n",
fprintf(mout, "%s: %s %u %u %u %u 0x%x -> %llu(%llu)/%llu %u/%u\n",
ep->name, av[0], index, ep->rate, ep->inst_rate, ep->inst_burst, ep->flags,
ep->sent, ep->dropped, ep->allowed, ep->inst_sent, ep->inst_burst);
if (do_reset)
@ -978,7 +950,7 @@ void print_status(FILE *f, time_t t)
did_reset = 1;
tried_reset = 1;
if (did_reset) {
mprintf(-2, "IRENA ARM RESET SENT %s = %-.*f exceeds (%g, %g)\n",
fprintf(mout, "IRENA ARM RESET SENT %s = %-.*f exceeds (%g, %g)\n",
st->name, st->digits, mean, st->limits[0], st->limits[3]);
st->did_reset = t;
last_status_reset = t;
@ -999,14 +971,11 @@ void print_status(FILE *f, time_t t)
int config_status(int na, char **av)
{
if (na==1) {
if (verbose(-2)) {
print_status(mout, 0);
unverbose();
}
print_status(mout, 0);
return 0;
}
if (na!=2 && (na<6 || na>9)) {
mprintf(-2,
fprintf(mout,
"usage: %s [«index» [«red» «yellow» «yellow» «red» [«tau» [«reset»] [«flags»]]]]\n"
"- «index»: 0…%d, table index\n"
"- «red»: lower/upper error limit\n"
@ -1029,7 +998,7 @@ int config_status(int na, char **av)
if (number(av[1], &index))
return -1;
if (index<0 || index>=NSTAT) {
mprintf(-2, "limits index %d out of range 0…%d\n",
fprintf(mout, "limits index %d out of range 0…%d\n",
index, NSTAT-1);
return -1;
}
@ -1058,7 +1027,7 @@ int config_status(int na, char **av)
else
st->flags |= ST_DO_RESET;
else
mprintf(-2, "%s … «reset» = reset | noreset | «seconds»\n", av[0]);
fprintf(mout, "%s … «reset» = reset | noreset | «seconds»\n", av[0]);
if (na>=9) {
int f;
if (!number(av[8], &f)) {
@ -1067,7 +1036,7 @@ int config_status(int na, char **av)
}
}
mprintf(-2, "%s %s[%u] %g %g %g %g %s tau %d reset %d flags 0x%04x\n",
fprintf(mout, "%s %s[%u] %g %g %g %g %s tau %d reset %d flags 0x%04x\n",
av[0], st->name, index,
st->limits[0], st->limits[1], st->limits[2], st->limits[3],
st->units, st->tau, st->reset_delay, st->flags);
@ -1228,7 +1197,8 @@ int arm_reset()
arm_reset_uart(i_buf.fd, reset_flags);
else
return -1;
mprintf(0, "Sent hardware RESET, mode %d\n", reset_flags);
if (verbose(0))
fprintf(mout, "Sent hardware RESET, mode %d\n", reset_flags);
return 0;
}
@ -1312,18 +1282,18 @@ int test_serial_read_buffer(int na, char **av)
{
int s = 1;
if (na>=2 && number(av[1], &s)) {
mprintf(-2, "usage: %s [«seconds»]\n", av[0]);
fprintf(mout, "usage: %s [«seconds»]\n", av[0]);
return -1;
}
if (i_buf.fd<0) {
mprintf(-2, "uart is closed\n");
return -1;
fprintf(mout, "uart is closed\n");
}
buffer_reset(&i_buf);
sleep(s);
char buf[65536];
int n = read(i_buf.fd, &buf, sizeof(buf)-1);
mprintf(-2, "uart read buffer size >= %d\n", n);
fprintf(mout, "uart read buffer size >= %d\n", n);
return n;
}
@ -1358,7 +1328,8 @@ int base64_decode(const char *l, unsigned char *b, size_t n)
else if (!i && (!c || c=='\n'))
return m;
else if (c&0x80 || !bb[i] && c!='A') {
mprintf(0, "Base64 format error: %s\n", l);
if (verbose(0))
fprintf(mout, "Base64 format error: %s\n", l);
return 0;
}
}
@ -1423,8 +1394,8 @@ int base85_decode(const char *l, unsigned char *b, size_t n)
l++;
int ll = base85_decode1(l, (unsigned int*)(b+m));
if (!ll) {
if (*l)
mprintf(0, "invalid base85: «%s»\n", l);
if (*l && verbose(0))
fprintf(mout, "invalid base85: «%s»\n", l);
return m;
}
l += ll;
@ -2187,21 +2158,23 @@ int send_irena_command(const char *c)
{
static int no_uart;
if (i_buf.fd <= 0) {
if (!no_uart)
mprintf(-1, "irena: no uart: %s\n", c);
if (!no_uart && verbose(-1))
fprintf(mout, "irena: no uart: %s\n", c);
no_uart++;
return -1;
}
no_uart=0;
time_t t = time(0);
if (last_irena_command_sent+1 >= t) {
mprintf(3, "IRENA CMD postponed: %s\n", c);
if (verbose(3))
fprintf(mout, "IRENA CMD postponed: %s\n", c);
return -2;
}
size_t sl = strlen(c);
int n = write(i_buf.fd, c, sl);
last_irena_command_sent = t;
mprintf(2, "IRENA CMD sent: «%s»\n", c);
if (verbose(2))
fprintf(mout, "IRENA CMD sent: «%s»\n", c);
if (n==sl)
n += write(i_buf.fd, "\n", 1);
if (n != sl+1)
@ -2210,10 +2183,11 @@ int send_irena_command(const char *c)
return -3;
}
else {
mprintf(-1, "incomplete write: %d/%lu %s\n", n, strlen(c), c);
if (verbose(-1))
fprintf(mout, "incomplete write: %d/%lu %s\n", n, strlen(c), c);
return -4;
}
return 0;
return 0;
}
int fct_status;
@ -2261,8 +2235,6 @@ void send_fct(time_t t)
void process_line(const char *l)
{
// ack a sent command when we see the response
if (!*l)
return;
if (*l=='.' || *l=='m')
last_irena_command_sent = 0;
int fmt = 0;
@ -2271,16 +2243,19 @@ void process_line(const char *l)
if (fmt) {
fct_got_data++;
fct_packets++;
mprintf(3, "DATA received: «%s»\n", l);
if (verbose(3))
fprintf(mout, "DATA received: «%s»\n", l);
process_base64(fmt, l+5);
return;
}
if (*l && !strncmp(l+1, "FCT ", 4)) {
fct_ack++;
mprintf(4, "FCT ACK received %d, «%s»\n", fct_ack, l);
if (verbose(4))
fprintf(mout, "FCT ACK received %d, «%s»\n", fct_ack, l);
return;
}
mprintf(1, "IRENA: %s\n", l);
if (verbose(1))
fprintf(mout, "IRENA: %s\n", l);
eprintf(EP_M, "I %s\n", l);
}
@ -2356,12 +2331,14 @@ int send_file(const char *fn, const char *cfmt, unsigned int drop)
continue;
}
if (i >= 2) {
mprintf(-1, "more than two in parameters «%s»\n", cfmt);
if (verbose(-1))
fprintf(mout, "more than two in parameters «%s»\n", cfmt);
return -3;
}
while (index(".-+#0123456789", *s)) s++;
if (!*s || !index("diuoOxX", *s)) {
mprintf(-1, "invalid format «%s»\n", cfmt);
if (verbose(-1))
fprintf(mout, "invalid format «%s»\n", cfmt);
return -3;
}
i++;
@ -2383,7 +2360,8 @@ int send_file(const char *fn, const char *cfmt, unsigned int drop)
int d = 1;
if (drop) while (d<128 && buf[d]==buf[0]) d++;
if (d>=128 && buf[0]==0xffffffff) {
mprintf(2, "dropping block %d\n", a/128);
if (verbose(2))
fprintf(mout, "dropping block %d\n", a/128);
continue;
}
for (int i=0; i<128; ) {
@ -2398,9 +2376,9 @@ int send_file(const char *fn, const char *cfmt, unsigned int drop)
poll_uart(1);
char *res = poll_response(2);
if (!res)
mprintf(-2, "timeout for cmd «%s»\n", cmd);
fprintf(mout, "timeout for cmd «%s»\n", cmd);
else if (strncmp(res+1, "303", 3))
mprintf(-2, "invalid response to «%s» → «%s»\n", cmd, res);
fprintf(mout, "invalid response to «%s» → «%s»\n", cmd, res);
else
break;
poll_uart(0);
@ -2414,8 +2392,8 @@ int send_file(const char *fn, const char *cfmt, unsigned int drop)
int cs = snprintf(cmd, 128, cfmt, a, n);
if (cs <= 127)
process_cmd(cmd);
else
mprintf(-1, "sendfile format too long «%s»\n", cmd);
else if (verbose(-1))
fprintf(mout, "sendfile format too long «%s»\n", cmd);
}
do_cron(0, CR_BUFFER, 1);
a += n;
@ -2439,7 +2417,8 @@ int set_clock(int do_sleep)
while (ts.tv_nsec < 900000000) {
ts.tv_nsec += 50000000;
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, 0);
mprintf(3, "slept 50ms ns=%lu\n", ts.tv_nsec);
if (verbose(3))
fprintf(mout, "slept 50ms ns=%lu\n", ts.tv_nsec);
poll_uart(1);
clock_gettime(CLOCK_REALTIME, &ts);
}
@ -2516,7 +2495,8 @@ const char *cmd_socket_name() {
int close_cmd_socket()
{
if (cmd_socket_fd >= 0) {
mprintf(1, "closing cmd socket: «%s»\n", cmd_socket_name());
if (verbose(1))
fprintf(mout, "closing cmd socket: «%s»\n", cmd_socket_name());
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, cmd_socket_fd, 0);
close(cmd_socket_fd);
cmd_socket_fd = -1;
@ -2586,20 +2566,17 @@ int open_cmd_socket(int na, char **av)
cmd_socket_addr.u.sun_family = AF_UNIX;
strncpy(cmd_socket_addr.u.sun_path, av[na-1], sizeof(cmd_socket_addr.u.sun_path)-1);
if (force && !unlink(cmd_socket_addr.u.sun_path))
mprintf(-2, "socket «%s» unlinked\n", cmd_socket_addr.u.sun_path);
fprintf(mout, "socket «%s» unlinked\n", cmd_socket_addr.u.sun_path);
}
else if (na==1) {
is_open:
if (verbose(-1)) {
fprintf(mout, "command socket is %s«%s»\n", mode, cmd_socket_name());
print_asocket(mout);
unverbose();
}
fprintf(mout, "command socket is %s«%s»\n", mode, cmd_socket_name());
print_asocket(mout);
return 0;
}
else {
usage:
mprintf(-2,
fprintf(mout,
"usage: %s [unix «path» | tcp «port»]"
" [interactive|noninteractive] [close]\n", av[0]);
return -1;
@ -2631,7 +2608,7 @@ int open_cmd_socket(int na, char **av)
return -1;
}
mprintf(-2, "opened %ssocket «%s»\n", mode, cmd_socket_name());
fprintf(mout, "opened %ssocket «%s»\n", mode, cmd_socket_name());
return 0;
}
@ -2647,7 +2624,7 @@ int accept_cmd_socket(int fd)
}
if (s_buf.fd >= 0) {
mprintf(-2, "command socket busy\n");
fprintf(mout, "command socket busy\n");
close(sfd);
return -1;
}
@ -2675,13 +2652,11 @@ int accept_cmd_socket(int fd)
return sfd;
}
cmd_socket_flags |= SOCK_IS_MOUT;
mprintf(-2, "irena %s\n", version);
fprintf(mout, "irena %s\n", version);
}
if (verbose(0)) {
if (verbose(0))
print_asocket(stderr);
unverbose();
}
return sfd;
}
@ -2700,7 +2675,6 @@ int close_acmd_socket()
if (verbose(0)) {
print_asocket(stderr);
fprintf(stderr, "closing socket connection\n");
unverbose();
}
if (s_buf.fd >= 0) {
if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, s_buf.fd, 0))
@ -2720,7 +2694,7 @@ int number(char *s, int *i)
char *e;
int ii = strtol(s, &e, 0);
if (*e) {
mprintf(-2, "invalid number: %s\n", s);
fprintf(mout, "invalid number: %s\n", s);
return -1;
}
*i = ii;
@ -2733,7 +2707,7 @@ int index_number(char *s, int *i)
char *e;
int ii = strtol(s+1, &e, 0);
if (*e != ']') {
mprintf(-2, "invalid index: %s\n", s);
fprintf(mout, "invalid index: %s\n", s);
return -1;
}
*i = ii;
@ -2745,7 +2719,7 @@ int float_number(char *s, double *d)
char *e;
double dd = strtod(s, &e);
if (*e) {
mprintf(-2, "invalid float number: %s\n", s);
fprintf(mout, "invalid float number: %s\n", s);
return -1;
}
*d = dd;
@ -2854,7 +2828,7 @@ int process_script(const char *fn)
{
static int recursion = 0;
if (recursion>10) {
mprintf(-2, "script: «%s»: nested too deep, limit 10\n", fn);
fprintf(mout, "script: «%s»: nested too deep, limit 10\n", fn);
return -2;
}
@ -2868,7 +2842,7 @@ int process_script(const char *fn)
char cmd[128];
while (fgets(cmd, sizeof(cmd), f)) {
if (!index(cmd, '\n')) {
mprintf(-2, "script: unterminated or long line: «%s»\n", cmd);
fprintf(mout, "script: unterminated or long line: «%s»\n", cmd);
break;
}
process_cmd(cmd);
@ -2889,27 +2863,26 @@ int process_cmd(char *l)
int na = split(l, 10, av);
if (!na || av[0][0]=='#')
return 0;
if (verbose(2) || !strcmp(av[0], "echo") && verbose(-2)) {
check_prompt();
if (verbose(2) || !strcmp(av[0], "echo")) {
fprintf(mout, "CMD:");
for (int i=0; i<na; i++)
fprintf(mout, " «%s»", av[i]);
fprintf(mout, "\n");
unverbose();
}
if (!strcmp(av[0], "echo")) {
return 0;
}
if (!strcmp(av[0], "version")) {
mprintf(-2, "irena version %s\n", version);
fprintf(mout, "irena version %s\n", version);
return 0;
}
int help = na>=2 && !strcmp(av[1], "help");
if (!strcmp(av[0], "help")) {
if (!verbose(-2)) return 0;
mprintf(-2, "available commands\n"
fprintf(mout, "available commands\n"
"commands:\n"
"\thelp\n"
"\tversion\n"
@ -2938,13 +2911,10 @@ int process_cmd(char *l)
"\n"
);
print_numbers(mout);
unverbose();
return 0;
}
if (!strcmp(av[0], "numbers")) {
if (!verbose(-2)) return 0;
print_numbers(mout);
unverbose();
return 0;
}
@ -2963,9 +2933,9 @@ int process_cmd(char *l)
}
if (help || ii<0 || na+ii > nn) {
if (nn>1)
mprintf(-2, "usage: %s [[«i»]] «%s» [… %d items]\n", n->name, n->doc, nn);
fprintf(mout, "usage: %s [[«i»]] «%s» [… %d items]\n", n->name, n->doc, nn);
else
mprintf(-2, "usage: %s «%s»\n", n->name, n->doc);
fprintf(mout, "usage: %s «%s»\n", n->name, n->doc);
na = 0;
}
@ -2988,17 +2958,14 @@ int process_cmd(char *l)
if (r)
return -1;
}
if (verbose(-2)) {
print_number(mout, n);
unverbose();
}
print_number(mout, n);
return 0;
}
if (!strcmp(av[0], "exit")) {
int code = 0;
if (help || na>2 || na==2 && number(av[1], &code)) {
mprintf(-2, "usage: exit [«status»]\n");
fprintf(mout, "usage: exit [«status»]\n");
return -1;
}
file_close(&data_file);
@ -3011,7 +2978,7 @@ int process_cmd(char *l)
if (!strcmp(av[0], "sleep")) {
int sec;
if (help || na!=2 || number(av[1], &sec)) {
mprintf(-2, "usage: sleep «seconds»\n");
fprintf(mout, "usage: sleep «seconds»\n");
return -1;
}
sleep(sec);
@ -3020,7 +2987,7 @@ int process_cmd(char *l)
if (!strcmp(av[0], "script")) {
if (help || na!=2) {
mprintf(-2, "usage: script «filename»\n");
fprintf(mout, "usage: script «filename»\n");
return -1;
}
return process_script(av[1]);
@ -3031,7 +2998,7 @@ int process_cmd(char *l)
int drop = 0;
int i = 2;
if (help) {
mprintf(-2, "usage: sendfile [drop] «filename» [«command»]\n"
fprintf(mout, "usage: sendfile [drop] «filename» [«command»]\n"
"- send contents of «filename» as 512 Byte blocks into the flashbuffer,\n"
"- optionally, send irena «command»,\n"
"- execute any cron items flagged `buffer`.\n"
@ -3069,14 +3036,14 @@ int process_cmd(char *l)
if (!strcmp(av[2], "1000000")) baud = B1000000;
if (!strcmp(av[2], "1500000")) baud = B1500000;
if (baud==B0) {
mprintf(-2, "uart: illegal baud rate: %s\n"
fprintf(mout, "uart: illegal baud rate: %s\n"
"valid: 9600, 38400, 57600, 115200, 921600, 1000000, 1500000\n"
"default: 921600\n", av[2]);
return -1;
}
}
if (help || na<2 || na>4 || na==4 && !do_reset) {
mprintf(-2,
fprintf(mout,
"usage: uart «tty» [«baudrate»] [«reset»]\n"
"- open the serial port,\n"
"- optionally reset the µC, and\n"
@ -3097,7 +3064,7 @@ int process_cmd(char *l)
if (!strcmp(av[0], "irena") || !strcmp(av[0],"var")) {
if (help || na <= 1) {
mprintf(-2,
fprintf(mout,
"usage: irena «command»\n"
"usage: var «variable command»\n"
" (short for `irena var «command»`)\n"
@ -3128,7 +3095,7 @@ int process_cmd(char *l)
if (na>3 || na>2 && !config)
do_sleep = -1;
if (help || do_sleep < 0) {
mprintf(-2,
fprintf(mout,
"usage: set_clock [nosleep|sleep|drift|fudge] [config]\n"
"- set the µC RTC to the current host time.\n"
"option `sleep`: Wait until the end of the current second.\n"
@ -3178,14 +3145,9 @@ int process_cmd(char *l)
if (!strcmp(av[0], "cron")) {
if (help || config_cron(na, av) == -1)
mprintf(-2, "usage:\tcron [«index» [«flags»… [«delay» «cmd»]]]\n"
fprintf(mout, "usage:\tcron [«index» [«flags»… [«delay» «cmd»]]]\n"
"\tcron force «flags»… [«times»]\n"
"\tcron «index» delete\n"
"\t\tindex: `next`, 0 … 15\n"
"\t\tflags: init reset once ever buffer\n"
"\t\t\tgot_hk got_cntr got_bate did_let\n"
"\t\t\trotate_data rotate_cntr rotate_stat\n"
);
"\tcron «index» delete\n");
return 0;
}
@ -3197,14 +3159,14 @@ int process_cmd(char *l)
for (pp=paths; pp->name; pp++)
if (na<2 || !strcmp(av[1], pp->name)) {
if (na<3)
mprintf(-2, "path %s %s\n", pp->name, *pp->path);
fprintf(mout, "path %s %s\n", pp->name, *pp->path);
else if (na==3)
strncpy(*pp->path, av[2], MAX_FN-1);
if (na>1)
break;
}
if (help || na>3 || na>1 && !pp->name)
mprintf(-2, "usage:\tpath [«name» [«value»]]\n");
fprintf(mout, "usage:\tpath [«name» [«value»]]\n");
return 0;
}
@ -3212,7 +3174,7 @@ int process_cmd(char *l)
// #! /usr/bin/env ./irena
return process_script(av[0]);
mprintf(-2, "unknown command '%s', try help\n", av[0]);
fprintf(mout, "unknown command '%s', try help\n", av[0]);
return -1;
}
@ -3276,7 +3238,7 @@ int config_cron(int na, char **av)
struct cron *cr = &cron[i];
if (!*cr->cmd)
continue;
mprintf(-2, "%s %d 0x%04x %d (%d@%ld) «%s»\n",
fprintf(mout, "%s %d 0x%04x %d (%d@%ld) «%s»\n",
av[0], i, cr->flags, cr->delay, cr->times, cr->when, cr->cmd);
}
return 0;
@ -3290,7 +3252,7 @@ int config_cron(int na, char **av)
break;
}
if (idx<0) {
mprintf(-2, "no free crontab entries\n");
fprintf(mout, "no free crontab entries\n");
return -2;
}
}
@ -3352,7 +3314,7 @@ int config_cron(int na, char **av)
if (idx<0) {
if (!flags || ai!=na)
return -1;
mprintf(-2, "forcing cron flags 0x%04x, %d times\n", flags, delay);
fprintf(mout, "forcing cron flags 0x%04x, %d times\n", flags, delay);
do_cron(0, flags, delay);
return 0;
}
@ -3370,7 +3332,7 @@ int config_cron(int na, char **av)
strncpy(cr->cmd, av[ai], CRON_CMD_SIZE-1);
}
mprintf(-2, "%s %d 0x%04x %d (%d@%ld) «%s»\n",
fprintf(mout, "%s %d 0x%04x %d (%d@%ld) «%s»\n",
av[0], idx, cr->flags, cr->delay, cr->times, cr->when, cr->cmd);
return 0;
}
@ -3409,9 +3371,10 @@ int main(int argc, char **argv)
for (int i=1; i<argc && argv[i]; i++)
process_cmd(argv[i]);
const char *prompt = NULL;
if (isatty(0) && isatty(2)) {
prompt = "IRENA> ";
fprintf(stderr, prompt);
fprintf(mout, prompt);
in_prompt = 1;
}
@ -3427,8 +3390,8 @@ int main(int argc, char **argv)
int n = epoll_wait(epoll_fd, eev, 2, 1000);
time_t t = time(0);
for (int i=0; i<n; i++) {
if (eev[i].events != EPOLLIN)
mprintf(2, "epoll: fd=%d events=0x%x\n", eev[i].data.fd, eev[i].events);
if (eev[i].events != EPOLLIN && verbose(2))
fprintf(mout, "epoll: fd=%d events=0x%x\n", eev[i].data.fd, eev[i].events);
if (eev[i].data.fd == cmd_socket_fd)
accept_cmd_socket(cmd_socket_fd);
else if (eev[i].data.fd == s_buf.fd) {
@ -3436,9 +3399,7 @@ int main(int argc, char **argv)
if (!l)
close_acmd_socket(&s_buf);
while (l) {
in_prompt = 0;
process_cmd(l);
in_prompt = !!prompt;
l = get_line(&s_buf, 0);
}
}
@ -3446,7 +3407,14 @@ int main(int argc, char **argv)
poll_uart(eev[i].events);
}
else {
char *l = get_line(&c_buf, eev[i].events);
int do_read = eev[i].events;
if (prompt && in_prompt==2 && do_read) {
fprintf(stderr, prompt);
reprint_line(stderr, &c_buf);
in_prompt = 1;
do_read = 0;
}
char *l = get_line(&c_buf, do_read);
while (l) {
in_prompt = 0;
process_cmd(l);
@ -3455,8 +3423,7 @@ int main(int argc, char **argv)
fprintf(stderr, "M:%s", prompt);
else
fprintf(stderr, prompt);
if (mout==stderr)
in_prompt = 1;
in_prompt = 1;
}
l = get_line(&c_buf, 0);
}