╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ───╮ │src/main.zig│ ╰────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │35 │ │36 var stdout_writer = std.Io.File.stdout().writer(io, &.{}); │ │37 var stderr_writer = std.Io.File.stderr().writer(io, &.{}); │ │41 - const run_opts = RunOpts{ .out = &stdout_writer.interface, .err = &stderr_writer.interface, .environ_map = init.environ_map };│ │38 + const run_opts = RunOpts{ .out = &stdout_writer.interface, .err = &stderr_writer.interface }; │ │39 │ │40 const cwd_path = try std.process.currentPathAlloc(io, allocator); │ │41 defer allocator.free(cwd_path); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │76 .help => |cmd_kind_maybe| try cmd.printHelp(cmd_kind_maybe, run_opts.out), │ │77 .cli => |cli_cmd| switch (cli_cmd) { │ │78 .serve => |options| try srv.run(repo_kind, any_repo_opts, io, allocator, cwd_path, options, run_opts.err, {}),│ │82 - .ssh_git => |options| try ssh.run(io, allocator, options, run_opts.environ_map), │ │83 - .ssh_tui => |options| try ssh_tui.run(io, allocator, options, run_opts.environ_map), │ │79 }, │ │80 } │ │81 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────╮ │src/serve.zig│ ╰─────────────╯ ╭───────────────────────────────────────────────────────────────────╮ │ 4 const hash = xit.hash; │ │ 5 const ui = @import("./ui.zig"); │ │ 6 const web = @import("./web.zig"); │ │ 7 -const serve_ssh_tui = @import("./serve_ssh_tui.zig"); │ │ 7 +const serve_ssh_protocol = @import("./serve_ssh_protocol.zig");│ │ 8 +const serve_ssh = @import("./serve_ssh.zig"); │ │ 9 │ │10 pub const Options = struct { │ │11 http_listen: []const u8 = "127.0.0.1:8080", │ ╰───────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────╮ │11 - ssh_listen: []const u8 = "127.0.0.1:8081",│ │12 - tui_listen: []const u8 = "127.0.0.1:8082",│ │12 + ssh_listen: []const u8 = "127.0.0.1:8022",│ │13 wui_listen: []const u8 = "127.0.0.1:8000",│ │14 data_dir: []const u8 = ".", │ │15 }; │ ╰──────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────╮ │44 │ │45 try std.Io.Dir.cwd().createDirPath(io, repo_root_path); │ │46 │ │47 - // the admin repo path is where the user/repo metadata events live; │ │48 - // web.zig opens it per request to render the server-side TUI │ │47 + // the admin repo path is where the user/repo metadata events live │ │48 │ │49 const admin_repo_path = try std.fs.path.resolve(allocator, &.{ data_dir_path, "admin" });│ │50 defer allocator.free(admin_repo_path); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │62 const ssh_address = try std.Io.net.IpAddress.parseIp4(ssh_listen_address.host, ssh_listen_address.port);│ │63 var ssh_server = try ssh_address.listen(io, .{ .reuse_address = true }); │ │64 defer ssh_server.deinit(io); │ │66 - │ │67 - // create tui listener │ │65 │ │69 - const tui_listen_address = try parseListenAddress(options.tui_listen); │ │70 - const tui_address = try std.Io.net.IpAddress.parseIp4(tui_listen_address.host, tui_listen_address.port);│ │71 - var tui_server = try tui_address.listen(io, .{ .reuse_address = true }); │ │72 - defer tui_server.deinit(io); │ │66 + // load or generate the SSH host key │ │67 + const host_key = try serve_ssh_protocol.HostKey.loadOrGenerate(io, allocator, data_dir_path); │ │68 │ │69 // create wui listener │ │70 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 85 │ │ 86 runHttpListener(repo_kind, any_repo_opts, io, allocator, repo_root_path, &http_server, &tasks, err); │ │ 87 │ │ 93 - try err.print("serving SSH helper connections on {s}, repo root {s}\n", .{ options.ssh_listen, repo_root_path });│ │ 88 + try err.print("serving SSH on {s}\n", .{options.ssh_listen}); │ │ 89 try err.flush(); │ │ 90 │ │ 96 - runSshListener(repo_kind, any_repo_opts, io, allocator, repo_root_path, &ssh_server, &tasks, err); │ │ 91 + const ssh_session_handler = serve_ssh.SessionHandler{ │ │ 92 + .admin_repo_path = admin_repo_path, │ │ 93 + .repo_root_path = repo_root_path, │ │ 94 + }; │ │ 95 + runSshListener(io, allocator, &host_key, &ssh_session_handler, &ssh_server, &tasks, err); │ │ 96 │ │ 98 - try err.print("serving TUI sessions on {s}\n", .{options.tui_listen}); │ │ 99 - try err.flush(); │ │100 - │ │101 - runTuiListener(io, allocator, admin_repo_path, &tui_server, &tasks, err); │ │102 - │ │ 97 try err.print("serving web UI on http://{s}/\n", .{options.wui_listen}); │ │ 98 try err.flush(); │ │ 99 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────╮ │286 return null; │ │287 } │ │288 │ │295 -const SshService = enum { │ │296 - upload_pack, │ │297 - receive_pack, │ │298 -}; │ │299 - │ │300 -const SshRequest = struct { │ │301 - service: SshService, │ │302 - protocol_version: xit.net_server_common.ProtocolVersion, │ │303 - repo: []const u8, │ │304 - │ │305 - fn deinit(self: SshRequest, allocator: std.mem.Allocator) void {│ │306 - allocator.free(self.repo); │ │307 - } │ │308 -}; │ │309 - │ │289 fn runSshListener( │ │311 - comptime repo_kind: rp.RepoKind, │ │312 - comptime any_repo_opts: rp.AnyRepoOpts(repo_kind), │ │290 io: std.Io, │ │291 allocator: std.mem.Allocator, │ │315 - repo_root_path: []const u8, │ │292 + host_key: *const serve_ssh_protocol.HostKey, │ │293 + session_handler: *const serve_ssh.SessionHandler, │ │294 net_server: *std.Io.net.Server, │ │295 tasks: *std.Io.Group, │ │296 err: *std.Io.Writer, │ ╰─────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────╮ │298 const Listener = struct { │ │299 io: std.Io, │ │300 allocator: std.mem.Allocator, │ │323 - repo_root_path: []const u8, │ │301 + host_key: *const serve_ssh_protocol.HostKey, │ │302 + session_handler: *const serve_ssh.SessionHandler,│ │303 net_server: *std.Io.net.Server, │ │304 tasks: *std.Io.Group, │ │305 err: *std.Io.Writer, │ ╰──────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────╮ │315 const Connection = struct { │ │316 io: std.Io, │ │317 allocator: std.mem.Allocator, │ │339 - repo_root_path: []const u8, │ │318 + host_key: *const serve_ssh_protocol.HostKey, │ │319 + session_handler: *const serve_ssh.SessionHandler,│ │320 stream: std.Io.net.Stream, │ │321 err: *std.Io.Writer, │ │322 │ ╰──────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯