--- nono/host/netdriver.cpp 2026/04/29 17:04:30 1.1.1.2 +++ nono/host/netdriver.cpp 2026/04/29 17:05:04 1.1.1.7 @@ -1,31 +1,34 @@ // // nono -// Copyright (C) 2019 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -#include "header.h" #include "netdriver.h" +#include "autofd.h" +#include "cvprompt.h" +#include "kevent.h" #include "mainapp.h" -#include "mystring.h" +#include "mythread.h" +#include static void *netdriver_run(void *); std::unique_ptr gNetDriver; // コンストラクタ -NetDriver::NetDriver(const char *name, EthernetDevice *p, - std::mutex *m, std::condition_variable *c) -{ - logname = "netdriver"; - devname = "NetDriver"; - - monitor.Init(30, 7); - +NetDriver::NetDriver(const char *drivername_, EthernetDevice *parent_, + int loglevel_) + : inherited(std::string("HostNet") + drivername_) +{ + loglevel = loglevel_; + drivername = drivername_; + parent = parent_; fd = -1; - drivername = name; - parent = p; - mtx = m; - cv = c; + + monitor.func = (MonitorCallback_t)&NetDriver::MonitorUpdate; + monitor.SetSize(30, 7); + monitor.Regist(ID_MONITOR_HOSTNET); } // デストラクタ @@ -33,38 +36,45 @@ NetDriver::~NetDriver() { } -// モニタ +// 初期化 bool -NetDriver::MonitorUpdate() +NetDriver::Init() { - monitor.Clear(); - - monitor.Print(0, 0, "NetDriver: %s", drivername); - monitor.Print(0, 1, "Device : %s", devpath.c_str()); - monitor.Print(0, 2, "Interface: %s", ifname.c_str()); - - monitor.Print(5, 4, "%9s %9s", "Packets", "Bytes"); - monitor.Print(0, 5, "Send %9" PRIu64 " %9" PRIu64, tx_pkts, tx_bytes); - monitor.Print(0, 6, "Recv %9" PRIu64 " %9" PRIu64, rx_pkts, rx_bytes); + // 対応する EthernetDevice があれば受信スレッド起動。 + // NetDriverNone は連携不要なので parent == NULL になっている。 + if (parent) { + pthread_t th; + int error; + + error = pthread_create(&th, NULL, netdriver_run, NULL); + if (error) { + putmsg(0, "pthread_create: %s", strerror(error)); + return false; + } + } return true; } -// スレッドが必要な継承クラスが呼ぶ -bool -NetDriver::InitRecvThread() +// モニタ +void +NetDriver::MonitorUpdate(Monitor *, TextScreen& screen) { - // 受信スレッド起動 - pthread_t th; - pthread_create(&th, NULL, netdriver_run, NULL); + screen.Clear(); - return true; + screen.Print(0, 0, "HostNet Driver: %s", drivername); + // 次の2行はドライバ依存情報 + MonitorUpdateMD(screen); + + screen.Print(5, 4, "%9s %9s", "Packets", "Bytes"); + screen.Print(0, 5, "Send %9" PRIu64 " %9" PRIu64, tx_pkts, tx_bytes); + screen.Print(0, 6, "Recv %9" PRIu64 " %9" PRIu64, rx_pkts, rx_bytes); } // 受信スレッドのエントリポイント void * netdriver_run(void *dummy) { - pthread_setname_np("netdriver"); + PTHREAD_SETNAME("NetDriver"); pthread_detach(pthread_self()); gNetDriver->ThreadRun(); @@ -89,29 +99,139 @@ NetDriver::Run_ifdown() bool NetDriver::RunScript(const char *filename) { + std::string cmd; + int r; + // ファイルが存在するか std::string path = gMainApp.SearchFile(filename); if (path.empty()) { - warnx("%s not found", filename); - return false; + errmsg = string_format("%s not found", filename); + goto abort; } // コマンドラインを作成 - std::string cmd = path + " " + ifname; + cmd = path + " " + ifname; + putmsg(1, "%s: command: %s", __func__, cmd.c_str()); // 実行 - int rv = system(cmd.c_str()); - if (rv == -1) { - warn("%s: system() failed", path.c_str()); - return false; - } - if (rv == 127) { - warnx("%s: system(): shell failed", path.c_str()); - return false; - } - if (rv != 0) { - warnx("%s failed with %d", path.c_str(), rv); - return false; + r = system(cmd.c_str()); + if (r == -1) { + errmsg = string_format("system(\"%s\"): %s", + cmd.c_str(), strerror(errno)); + goto abort; + } + + if (WIFEXITED(r)) { + int exitcode = WEXITSTATUS(r); + if (exitcode == 0) { + // ここが正常終了 + putmsg(1, "%s: \"%s\" exited successfully", __func__, cmd.c_str()); + return true; + } + if (exitcode == 127) { + errmsg = string_format("\"%s\": shell execution failed", + cmd.c_str()); + } else { + errmsg = string_format("\"%s\" exited with code %d", + cmd.c_str(), exitcode); + } + } else if (WIFSIGNALED(r)) { + int signo = WTERMSIG(r); + errmsg = string_format("\"%s\" terminated with signal %d", + cmd.c_str(), signo); + } else { + // どうしたらいいかよく分からんのでとりあえず表示だけ。 + errmsg = string_format("\"%s\" terminated:" + "IFSTOPPED=%d STOPSIG=%d IFCONTINUED=%d COREDUMP=%d", + cmd.c_str(), + WIFSTOPPED(r), WSTOPSIG(r), WIFCONTINUED(r), WCOREDUMP(r)); + } + + abort: + putmsg(1, "%s: %s", __func__, errmsg.c_str()); + return false; +} + +// VM 側の受信パケットキューにパケットを一つ追加する +void +NetDriver::RecvEnqueue(uint8 *ptr, size_t len) +{ + RXPacket packet { new qvector(ptr, ptr + len) }; + + // ホストカーネルから直接着信したパケット(というかフレームか)は + // 60バイトパディングされないまま読み出せるので、ここでパディングする。 + if (len < 60) { + for (int i = len ; i < 60; i++) { + packet->append(0); + } + } + + // CRC XXX 計算すること + for (int i = 0; i < 4; i++) { + packet->append(0); + } + + size_t n = packet->size(); + + // 受信処理に引き渡す + // ただしデバッガプロンプトで停止してる状態なら VM に干渉しない。 + if (gCVPrompt->IsPrompt() == false) { + putlog(2, "RecvPacket %zd bytes", n); + rx_pkts++; + rx_bytes += n; + parent->RecvPacket(std::move(packet)); + } +} + +// 受信スレッド +void +NetDriver::ThreadRun() +{ + struct kevent kev; + autofd kq; + int r; + + kq = kqueue(); + if (kq == -1) { + warn("NetDriver: kqueue"); + return; + } + + EV_SET(&kev, fd, EVFILT_READ, EV_ADD, 0, 0, (kevent_udata_t)0); + r = kevent_set(kq, &kev, 1); + if (r == -1) { + warn("NetDriver: kevent_set(fd=%d)", fd); + return; + } + + for (;;) { + ssize_t n; + + // 何か起きるまで待つ + r = kevent_poll(kq, &kev, 1, NULL); + if (r == -1) { + if (errno == EINTR) { + continue; + } + // XXX ? + warn("NetDriver: kevent_poll"); + break; + } + + // 自動的にデバイスに着信があったことになるはず + assert(r > 0); + + // パケットを読み込んで VM に引き渡す。 + n = RecvPacket(); + if (n == -1) { + if (errno == EINTR) { + continue; + } + warn("NetDriver: read"); + break; + } + if (n == 0) { // EOF + return; + } } - return true; }