From 403ef24eb92d24f1bbf84356630fe998c58f093a Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Fri, 16 Dec 2022 13:40:26 +0100 Subject: [PATCH] Implement signal handling --- cmd/tcpserv/main.ha | 11 +++++++++++ ev/+linux/file.ha | 9 +++++++++ ev/+linux/loop.ha | 3 +++ ev/+linux/signal.ha | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/cmd/tcpserv/main.ha b/cmd/tcpserv/main.ha index 7cdecfff4d03ae3dedd8d1bf1c1db91e5d5bed00..5892e087be412914d7e480621592fcddbeb10481 100644 --- a/cmd/tcpserv/main.ha +++ b/cmd/tcpserv/main.ha @@ -6,6 +6,7 @@ use net; use net::ip; use net::tcp; use os; +use unix::signal; type server = struct { loop: *ev::loop, @@ -45,9 +46,19 @@ }; ev::setuser(sock, &state); ev::accept(sock, &server_accept); + const sig = ev::signal(&loop, &signal, signal::SIGINT, signal::SIGTERM)!; + defer ev::close(sig); + ev::setuser(sig, &state); + log::println("Listening on 127.0.0.1:12345"); for (ev::dispatch(&loop, -1)!) void; os::exit(state.exit); +}; + +fn signal(file: *ev::file, sig: signal::signal) void = { + log::printfln("Exiting due to signal {}", sig: int); + const server = ev::getuser(file): *server; + ev::stop(server.loop); }; fn server_accept(sock: *ev::file, r: (*ev::file | net::error)) void = { diff --git a/ev/+linux/file.ha b/ev/+linux/file.ha index c85eb2ee26e22b3279d72ba9fc3f27a81e923629..662cb7125ed10ba1c294afc35f96a95e54e5d181 100644 --- a/ev/+linux/file.ha +++ b/ev/+linux/file.ha @@ -2,6 +2,7 @@ use errors; use io; use net; use rt; +use unix::signal; export type op = enum u64 { NONE = 0, @@ -12,6 +13,7 @@ READABLE = 1 << 16, WRITABLE = 2 << 16, ACCEPT = 3 << 16, CONNECT = 4 << 16, + SIGNAL = 5 << 16, }; export type fflags = enum uint { @@ -37,6 +39,7 @@ wvbuf: rt::iovec, wvec: []rt::iovec, }, sockflags: net::sockflags, + sigmask: signal::sigset, }, }; @@ -84,6 +87,9 @@ // custom [[file]] which was never registered, so assert on // error. rt::epoll_ctl(loop.fd, rt::EPOLL_CTL_DEL, file.fd, null)!; }; + if (file.op == op::SIGNAL) { + signal_restore(file); + }; free(file); }; @@ -132,6 +138,9 @@ events |= rt::EPOLLIN; }; if (file.op & op::CONNECT != 0) { events |= rt::EPOLLOUT; + }; + if (file.op & op::SIGNAL != 0) { + events |= rt::EPOLLIN; }; let ev = rt::epoll_event { diff --git a/ev/+linux/loop.ha b/ev/+linux/loop.ha index 425356e2882fe49060994886920ddc45780f1d39..a5942b88e352f447018663c659f0f6bf748e3835 100644 --- a/ev/+linux/loop.ha +++ b/ev/+linux/loop.ha @@ -3,6 +3,7 @@ use io; use rt; use time; use types; +use unix::signal; export type loop = struct { fd: io::file, @@ -105,6 +106,8 @@ case op::ACCEPT => accept_ready(file, ev); case op::CONNECT => connect_ready(file, ev); + case op::SIGNAL => + signal_ready(file, ev); case => yield; }; diff --git a/ev/+linux/signal.ha b/ev/+linux/signal.ha new file mode 100644 index 0000000000000000000000000000000000000000..e5b344b13f0c8194bf0bc8b35d10c679730dd23e --- /dev/null +++ b/ev/+linux/signal.ha @@ -0,0 +1,51 @@ +// TODO: Expose full siginfo data for non-portable use +use errors; +use rt; +use unix::signal; + +// Callback function for [[signal]] operations. +export type signalcb = fn(file: *file, sig: signal::signal) void; + +// Registers a signal handler with this event loop. The signals specified will +// be masked so they are only raised via the provided callback. Closing this +// file will unmask the signals. +// +// It is not necessary to call [[signal]] again after the callback has +// processed; it will automatically re-register the operation for subsequent +// signals. +export fn signal( + loop: *loop, + cb: *signalcb, + signals: signal::signal... +) (*file | errors::error) = { + const fd = signal::signalfd(signals...)?; + const file = register(loop, fd)?; + file.op = op::SIGNAL; + file.cb = cb; + file_epoll_ctl(file); + signal::sigset_empty(&file.sigmask); + signal::sigset_add(&file.sigmask, signals...); + signal::block(signals...); + return file; +}; + +fn signal_restore(file: *file) void = { + assert(file.op == op::SIGNAL); + let buf: [rt::NSIG]signal::signal = [0...]; + let signals = buf[..0]; + for (let i = 1; i < rt::NSIG; i += 1) { + const sig = i: signal::signal; + if (signal::sigset_member(&file.sigmask, sig)) { + static append(signals, sig); + }; + }; + signal::unblock(signals...); +}; + +fn signal_ready(file: *file, ev: *rt::epoll_event) void = { + assert(file.op == op::SIGNAL); + assert(file.cb != null); + const cb = file.cb: *signalcb; + const info = signal::read(file.fd)!; + cb(file, info.signo); +}; -- 2.48.1