diff options
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/statrep/clock.ha | 46 | ||||
| -rw-r--r-- | cmd/statrep/generic.ha | 41 | ||||
| -rw-r--r-- | cmd/statrep/main.ha | 288 |
3 files changed, 375 insertions, 0 deletions
diff --git a/cmd/statrep/clock.ha b/cmd/statrep/clock.ha new file mode 100644 index 0000000..f1e65fb --- /dev/null +++ b/cmd/statrep/clock.ha @@ -0,0 +1,46 @@ +use time::date; +use memio; +use strings; +use encoding::utf8; +use io; +use ev; + +type clock = struct { + section: section, + value: str, +}; + +fn buildclock() (clock | io::error | utf8::invalid) = { + const d = date::now(); + let s = memio::dynamic(); + defer io::close(&s)!; + date::format(&s, "%A %F %T", &d)?; + const val = strings::dup(memio::string(&s)?); + return clock { + section = section { + label = "clock: ", + }, + value = val, + }; +}; + +fn updateclock(clock: *clock) (void | io::error | utf8::invalid) = { + const d = date::now(); + let s = memio::dynamic(); + defer io::close(&s)!; + date::format(&s, "%A %F %T", &d)?; + const val = strings::dup(memio::string(&s)?); + free(clock.value); + clock.value = val; +}; + +fn finishclock(clock: *clock) void = { + free(clock.value); +}; + +fn clocktimerf(file: *ev::file) void = { + let server = ev::getuser(file): *server; + updateclock(&server.status.clock)!; + printstatus(server.status); +}; + diff --git a/cmd/statrep/generic.ha b/cmd/statrep/generic.ha new file mode 100644 index 0000000..fb61ab0 --- /dev/null +++ b/cmd/statrep/generic.ha @@ -0,0 +1,41 @@ +use os::exec; +use shlex; +use strings; +use io; +use os; + +type generic = struct { + section: section, + cmd: str, + value: str, +}; + +fn buildgeneric(cmd: str) generic = { + let gen = generic { + section = section { + label = "generic: ", + }, + cmd = cmd, + ... + }; + updategeneric(&gen)!; + return gen; +}; + +fn updategeneric(gen: *generic) (void | shlex::syntaxerr | exec::error) = { + const spl = shlex::split(gen.cmd)?; + const cmd = exec::cmd(spl[0], spl[1..]...)?; + let pipe = exec::pipe(); + exec::addfile(&cmd, os::stdout_file, pipe.1); + let proc = exec::start(&cmd)?; + io::close(pipe.1)!; + let data = io::drain(pipe.0)!; + defer free(data); + io::close(pipe.0)!; + exec::wait(&proc)!; + gen.value = strings::dup(strings::trim(strings::fromutf8(data)!)); +}; + +fn finishgeneric(gen: *generic) void = { + free(gen.value); +}; diff --git a/cmd/statrep/main.ha b/cmd/statrep/main.ha new file mode 100644 index 0000000..aa174b7 --- /dev/null +++ b/cmd/statrep/main.ha @@ -0,0 +1,288 @@ +use fmt; +use log; +use os; +use net; +use ev; +use unix::signal; +use io; +use memio; +use time::date; +use encoding::utf8; +use strings; +use bytes; +use shlex; +use time; +use getopt; +use dirs; +use path; + +type server = struct { + status: *status, + loop: *ev::loop, + sock: *ev::file, + clients: []*client, + socketpath: str, + exit: int, +}; + +type client = struct { + server: *server, + sock: *ev::file, + buf: [os::BUFSZ]u8, + wbuf: []u8, +}; + +type status = struct { + clock: clock, + host: hostname, + generics: []generic, +}; + +type section = struct { + label: str, +}; + +type hostname = struct { + section: section, + value: str, +}; + +export fn main() void = { + const cmd = getopt::parse(os::args, + "status daemon", + ('s', "path", "unix socket location") + ); + defer getopt::finish(&cmd); + + let socketpath = path::string(&path::init(dirs::runtime()!, "statrepsocket")!); + log::println(socketpath); + for (let opt .. cmd.opts) { + switch (opt.0) { + case 's' => + socketpath = opt.1; + case => + abort(); + }; + }; + + const loop = ev::newloop()!; + defer ev::finish(&loop); + const sock = ev::listen_unix(&loop, socketpath)!; + defer ev::close(sock); + + let status = newstatus()!; + defer finishstatus(&status); + + let state = server { + status = &status, + loop = &loop, + sock = sock, + socketpath = socketpath, + ... + }; + + const clocktimer = ev::newtimer(&loop, &clocktimerf, time::clock::MONOTONIC)!; + ev::timer_configure(clocktimer, 1 * time::SECOND, 1 * time::SECOND); + + ev::setuser(clocktimer, &state); + ev::setuser(sock, &state); + ev::accept(sock, &accept); + + const sig = ev::signal(&loop, &signal, signal::sig::INT, + signal::sig::TERM)!; + ev::setuser(sig, &state); + defer ev::close(sig); + + for (ev::dispatch(&loop, -1)!) void; + os::exit(state.exit); +}; + +fn newstatus() (status | io::error | utf8::invalid) = { + return status { + clock = buildclock()?, + host = buildhostname(), + generics = alloc([ + buildgeneric("/home/jhurst/.local/share/statusbar/vol.sh"), + ]), + }; +}; + +fn finishstatus(status: *status) void = { + finishclock(&status.clock); + for (let gen &.. status.generics) { + finishgeneric(gen); + }; + free(status.generics); +}; + +fn buildhostname() hostname = { + return hostname { + section = section { + label = "hostname: ", + }, + value = os::hostname(), + }; +}; + +fn printstatus(state: *status) str = { + const s = getstatus(state); + defer free(s); + fmt::println(strings::trim(s))!; + return s; +}; + +// Must free return value +fn getstatus(state: *status) str = { + const st = memio::dynamic(); + defer io::close(&st)!; + for (let gen .. state.generics) { + memio::concat(&st, gen.value)!; + memio::concat(&st, " | ")!; + }; + memio::concat(&st, + state.host.section.label, state.host.value, " | ", + state.clock.section.label, state.clock.value, "\n" + )!; + //const s = strings::concat( + // state.generic.section.label, state.generic.value, " | ", + // state.host.section.label, state.host.value, " | ", + // state.clock.section.label, state.clock.value, "\n"); + const s = strings::dup(memio::string(&st)!); + return s; +}; + +fn accept(sock: *ev::file, result: (*ev::file | net::error)) void = { + let server = ev::getuser(sock): *server; + const sock = match (result) { + case let sock: *ev::file => + yield sock; + case let err: net::error => + log::printfln("Error: accept: {}", net::strerror(err)); + ev::stop(server.loop); + server.exit = 1; + return; + }; + log::println("accepted"); + const client = alloc(client { + server = server, + sock = sock, + ... + }); + append(server.clients, client); + ev::setuser(client.sock, client); + ev::read(client.sock, &read, client.buf); + ev::accept(server.sock, &accept); +}; + +fn execmd(s: *status, cmd: []str) (void | str) = { + switch (cmd[0]) { + case "print" => + printstatus(s); + case "status" => + return getstatus(s); + case "clock" => + updateclock(&s.clock)!; + case "generic" => + for (let gen &.. s.generics) { + updategeneric(gen)!; + }; + case => + assert(false); + }; +}; + +fn write(file: *ev::file, result: (size | io::error)) void = { + const client = ev::getuser(file): *client; + const n = match (result) { + case let err: io::error => + log::printfln("Error: write: {}", + io::strerror(err)); + client_close(client); + return; + case let n: size => + yield n; + }; + static delete(client.wbuf[..n]); + if (len(client.wbuf) != 0) { + ev::write(client.sock, &write, client.wbuf); + } else { + ev::read(client.sock, &read, client.buf); + }; +}; + +fn read(file: *ev::file, result: (size | io::EOF | io::error)) void = { + const client = ev::getuser(file): *client; + const n = match (result) { + case let err: io::error => + log::printfln("Error: read: {}", + io::strerror(err)); + client_close(client); + return; + case io::EOF => + client_close(client); + return; + case let n: size => + yield n; + }; + const b = client.buf[..n]; + if (!bytes::contains(b, strings::toutf8("\n"))) { + ev::read(client.sock, &read, client.buf); + } else { + const s = match (strings::fromutf8(b)) { + case let s: str => + yield s; + case let e: utf8::invalid => + log::printfln("Error: read: {}", utf8::strerror(e)); + client_close(client); + return; + }; + const i = strings::index(s, "\n") as size; + const cmd = strings::sub(s, 0z, i); + log::println(cmd); + + const spl = match (shlex::split(cmd)) { + case let e: shlex::syntaxerr => + log::printfln("Error: read: {}", shlex::strerror(e)); + client_close(client); + return; + case let spl: []str => + yield spl; + }; + defer strings::freeall(spl); + match (execmd(client.server.status, spl)) { + case let s: str => + defer free(s); + log::println(strings::trim(s)); + append(client.wbuf, strings::toutf8(s)...); + case => + void; + }; + + if (len(client.wbuf) != 0) { + ev::write(client.sock, &write, client.wbuf); + } else { + ev::read(client.sock, &read, client.buf); + }; + }; +}; + +fn client_close(client: *client) void = { + const server = client.server; + for (let i = 0z; i < len(server.clients); i += 1) { + if (server.clients[i] == client) { + delete(server.clients[i]); + break; + }; + }; + log::println("Connection closed"); + ev::close(client.sock); + free(client); +}; + +fn signal(file: *ev::file, sig: signal::sig) void = { + let server = ev::getuser(file): *server; + log::printfln("Exiting due to {}", signal::signame(sig)); + const l = ev::getloop(file); + ev::stop(l); + os::remove(server.socketpath)!; +}; |
