From 31cd2a3450eafbe0d22af41449276a90ea3dcb28 Mon Sep 17 00:00:00 2001 From: Julian Hurst Date: Wed, 23 Oct 2024 10:04:58 +0200 Subject: statrep -> statusdaemon --- cmd/statrep/clock.ha | 48 -------- cmd/statrep/generic.ha | 41 ------- cmd/statrep/main.ha | 287 -------------------------------------------- cmd/statusdaemon/clock.ha | 48 ++++++++ cmd/statusdaemon/generic.ha | 41 +++++++ cmd/statusdaemon/main.ha | 287 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 376 insertions(+), 376 deletions(-) delete mode 100644 cmd/statrep/clock.ha delete mode 100644 cmd/statrep/generic.ha delete mode 100644 cmd/statrep/main.ha create mode 100644 cmd/statusdaemon/clock.ha create mode 100644 cmd/statusdaemon/generic.ha create mode 100644 cmd/statusdaemon/main.ha diff --git a/cmd/statrep/clock.ha b/cmd/statrep/clock.ha deleted file mode 100644 index db31975..0000000 --- a/cmd/statrep/clock.ha +++ /dev/null @@ -1,48 +0,0 @@ -use time::date; -use time::chrono; -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) = { - let d = date::now(); - d = date::in(chrono::LOCAL, d)!; - 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 deleted file mode 100644 index fb61ab0..0000000 --- a/cmd/statrep/generic.ha +++ /dev/null @@ -1,41 +0,0 @@ -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 deleted file mode 100644 index bbe2864..0000000 --- a/cmd/statrep/main.ha +++ /dev/null @@ -1,287 +0,0 @@ -use fmt; -use log; -use os; -use net; -use ev; -use unix::signal; -use io; -use memio; -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)!; -}; diff --git a/cmd/statusdaemon/clock.ha b/cmd/statusdaemon/clock.ha new file mode 100644 index 0000000..db31975 --- /dev/null +++ b/cmd/statusdaemon/clock.ha @@ -0,0 +1,48 @@ +use time::date; +use time::chrono; +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) = { + let d = date::now(); + d = date::in(chrono::LOCAL, d)!; + 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/statusdaemon/generic.ha b/cmd/statusdaemon/generic.ha new file mode 100644 index 0000000..fb61ab0 --- /dev/null +++ b/cmd/statusdaemon/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/statusdaemon/main.ha b/cmd/statusdaemon/main.ha new file mode 100644 index 0000000..1356d32 --- /dev/null +++ b/cmd/statusdaemon/main.ha @@ -0,0 +1,287 @@ +use fmt; +use log; +use os; +use net; +use ev; +use unix::signal; +use io; +use memio; +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()!, "statusdsocket")!); + 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)!; +}; -- cgit v1.2.3