diff --git a/docs/content/programs/_index.org b/docs/content/programs/_index.org index f383dbb..e4f71d7 100644 --- a/docs/content/programs/_index.org +++ b/docs/content/programs/_index.org @@ -1,6 +1,6 @@ #+TITLE: Programs #+DATE: 2023-10-21T12:26:45+0800 -#+LASTMOD: 2024-08-17T18:17:57+0800 +#+LASTMOD: 2024-09-01T09:57:50+0800 #+TYPE: docs #+WEIGHT: 20 #+DESCRIPTION: Binary programs which can be used directly @@ -15,8 +15,4 @@ Then build with make build #+end_src -#+begin_quote -To build zigcli locally, Zig master is required, which can be downloaded [[https://ziglang.org/download/][here]]. -#+end_quote - * Available Programs diff --git a/docs/content/programs/dark-mode.org b/docs/content/programs/dark-mode.org index a2afc50..b4f540b 100644 --- a/docs/content/programs/dark-mode.org +++ b/docs/content/programs/dark-mode.org @@ -1,14 +1,18 @@ #+TITLE: dark-mode #+DATE: 2024-08-17T17:52:00+0800 -#+LASTMOD: 2024-08-17T19:01:59+0800 +#+LASTMOD: 2024-09-01T11:59:08+0800 #+TYPE: docs #+AUTHOR: Jiacai Liu #+DESCRIPTION: Configuring "Dark mode" for macOS. -#+begin_src bash - $ dark-mode -h +#+begin_src bash :results verbatim :exports results :wrap example :dir ../../.. +./zig-out/bin/dark-mode -h +#+end_src + +#+RESULTS: +#+begin_example USAGE: - dark-mode [OPTIONS] [--] + ./zig-out/bin/dark-mode [OPTIONS] [--] Available commands: status View dark mode status @@ -19,4 +23,4 @@ OPTIONS: -v, --version Print version -h, --help Print help information -#+end_src +#+end_example diff --git a/docs/content/programs/loc.org b/docs/content/programs/loc.org index bd35801..deaa1eb 100644 --- a/docs/content/programs/loc.org +++ b/docs/content/programs/loc.org @@ -1,28 +1,30 @@ #+TITLE: loc #+DATE: 2024-08-17T17:52:51+0800 -#+LASTMOD: 2024-08-17T18:57:02+0800 +#+LASTMOD: 2024-09-01T11:49:49+0800 #+TYPE: docs #+DESCRIPTION: Lines of code -#+begin_src bash :results verbatim code :exports both +#+begin_src bash :results verbatim :exports result :dir ../../.. ./zig-out/bin/loc #+end_src #+RESULTS: -#+begin_src bash -┌───────────┬───────┬───────┬───────┬──────────┬────────┬──────────┐ -│Language │File │Line │Code │Comment │Blank │Size │ -├───────────┼───────┼───────┼───────┼──────────┼────────┼──────────┤ -│Zig │13 │2774 │2362 │84 │328 │83.41K │ -│YAML │8 │321 │298 │5 │18 │7.95K │ -│TOML │1 │30 │24 │0 │6 │541.00B │ -│Makefile │1 │22 │15 │0 │7 │354.00B │ -│Markdown │3 │16 │15 │0 │1 │240.00B │ -│Python │1 │10 │7 │2 │1 │166.00B │ -│C │1 │9 │2 │4 │3 │34.00B │ -│Ruby │1 │8 │5 │2 │1 │201.00B │ -│JSON │1 │1 │1 │0 │0 │188.00B │ -├───────────┼───────┼───────┼───────┼──────────┼────────┼──────────┤ -│Total │30 │3191 │2729 │97 │365 │93.04K │ -└───────────┴───────┴───────┴───────┴──────────┴────────┴──────────┘ -#+end_src +#+begin_example +┌───────────┬───────┬────────┬───────┬──────────┬────────┬──────────┐ +│Language │File │Line │Code │Comment │Blank │Size │ +├───────────┼───────┼────────┼───────┼──────────┼────────┼──────────┤ +│Zig │363 │10808 │9369 │1050 │389 │632.19K │ +│YAML │8 │317 │292 │4 │21 │7.84K │ +│TOML │1 │32 │27 │0 │5 │698.00B │ +│Makefile │1 │23 │16 │0 │7 │365.00B │ +│Python │1 │10 │7 │2 │1 │166.00B │ +│C │1 │9 │2 │4 │3 │34.00B │ +│Ruby │1 │8 │5 │2 │1 │201.00B │ +│Markdown │1 │5 │5 │0 │0 │102.00B │ +│CHeader │1 │2 │2 │0 │0 │44.00B │ +│JSON │2 │2 │2 │0 │0 │247.00B │ +├───────────┼───────┼────────┼───────┼──────────┼────────┼──────────┤ +│Total │380 │11216 │9727 │1062 │427 │641.84K │ +└───────────┴───────┴────────┴───────┴──────────┴────────┴──────────┘ + +#+end_example diff --git a/docs/content/programs/night-shift.org b/docs/content/programs/night-shift.org index 8c4c772..c21a864 100644 --- a/docs/content/programs/night-shift.org +++ b/docs/content/programs/night-shift.org @@ -1,15 +1,15 @@ #+TITLE: night-shift #+DATE: 2024-08-17T17:52:12+0800 -#+LASTMOD: 2024-08-17T19:01:46+0800 +#+LASTMOD: 2024-09-01T11:59:23+0800 #+TYPE: docs #+DESCRIPTION: Configuring "Night Shift" for macOS. 🌕🌖🌗🌘🌑 -#+begin_src bash :results verbatim code :exports both +#+begin_src bash :results verbatim :exports results :wrap example :dir ../../.. ./zig-out/bin/night-shift -h #+end_src #+RESULTS: -#+begin_src bash +#+begin_example USAGE: ./zig-out/bin/night-shift [OPTIONS] [--] @@ -33,7 +33,7 @@ OPTIONS: -v, --version Print version -h, --help Print help information -#+end_src +#+end_example * Acknowledgment - https://github.com/smudge/nightlight diff --git a/docs/content/programs/pidof.org b/docs/content/programs/pidof.org index 6522385..5110c0f 100644 --- a/docs/content/programs/pidof.org +++ b/docs/content/programs/pidof.org @@ -1,11 +1,15 @@ #+TITLE: pidof #+DATE: 2024-08-17T17:52:44+0800 -#+LASTMOD: 2024-08-17T18:57:58+0800 +#+LASTMOD: 2024-09-01T11:56:51+0800 #+TYPE: docs #+DESCRIPTION: Linux has this command, but not macOS, so I write it for you. -#+begin_src bash -$ ./zig-out/bin/pidof -h +#+begin_src bash :results verbatim :exports results :wrap example :dir ../../.. +./zig-out/bin/pidof -h +#+end_src + +#+RESULTS: +#+begin_example USAGE: ./zig-out/bin/pidof [OPTIONS] [--] [program] @@ -15,4 +19,4 @@ $ ./zig-out/bin/pidof -h -u, --user_only Only show process belonging to current user. -v, --version Print version. -h, --help Print help message. -#+end_src +#+end_example diff --git a/docs/content/programs/repeat.org b/docs/content/programs/repeat.org index de37e36..8c70af8 100644 --- a/docs/content/programs/repeat.org +++ b/docs/content/programs/repeat.org @@ -1,17 +1,21 @@ #+TITLE: repeat #+DATE: 2024-08-17T17:52:32+0800 -#+LASTMOD: 2024-08-17T18:58:53+0800 +#+LASTMOD: 2024-09-01T11:57:51+0800 #+TYPE: docs #+DESCRIPTION: Execute a command repeatly until it succeeds. -#+begin_src bash - $ ./zig-out/bin/repeat -h +#+begin_src bash :results verbatim :exports results :wrap example :dir ../../.. +./zig-out/bin/repeat -h +#+end_src + +#+RESULTS: +#+begin_example USAGE: ./zig-out/bin/repeat [OPTIONS] [--] command OPTIONS: - -m, --max INTEGER Max times to repeat - -i, --interval INTEGER Pause interval(in seconds) between repeats - -v, --version Print version - -h, --help Print help information -#+end_src + -m, --max INTEGER Max times to repeat + -i, --interval INTEGER Pause interval(in seconds) between repeats + -v, --version Print version + -h, --help Print help information +#+end_example diff --git a/docs/content/programs/tcp-proxy.org b/docs/content/programs/tcp-proxy.org index 53766d5..6ac762d 100644 --- a/docs/content/programs/tcp-proxy.org +++ b/docs/content/programs/tcp-proxy.org @@ -1,12 +1,12 @@ #+TITLE: tcp-proxy #+DATE: 2024-09-01T00:02:43+0800 -#+LASTMOD: 2024-09-01T00:14:17+0800 +#+LASTMOD: 2024-09-01T11:57:00+0800 #+TYPE: docs #+DESCRIPTION: Forward TCP requests hitting a specified port on the localhost to a different port on another host Both IPv4 and IPv6 are supported. -#+begin_src bash :results verbatim :exports result :dir ../../.. +#+begin_src bash :results verbatim :exports results :wrap example :dir ../../.. ./zig-out/bin/tcp-proxy -h #+end_src @@ -16,12 +16,12 @@ Both IPv4 and IPv6 are supported. ./zig-out/bin/tcp-proxy [OPTIONS] OPTIONS: - -b, --bind_address STRING Local bind address(required) + -b, --bind_host STRING Local bind host(required) -p, --local_port INTEGER Local bind port(required) -H, --remote_host STRING Remote host(required) -P, --remote_port INTEGER Remote port(required) --buf_size INTEGER Buffer size for tcp read/write(default: 1024) - --thread_pool_size INTEGER (default: 24) + --server_threads INTEGER Server worker threads num(default: 24) -h, --help -v, --version --verbose diff --git a/docs/content/programs/tree.org b/docs/content/programs/tree.org index 8971d77..b7a01a5 100644 --- a/docs/content/programs/tree.org +++ b/docs/content/programs/tree.org @@ -1,25 +1,27 @@ #+TITLE: tree #+DATE: 2024-08-17T17:52:22+0800 -#+LASTMOD: 2024-08-17T19:02:08+0800 +#+LASTMOD: 2024-09-01T11:56:26+0800 #+TYPE: docs #+DESCRIPTION: Display the directory structure of a path in a tree-like format -#+begin_src -$ ./zig-out/bin/tree -h +#+begin_src bash :results verbatim :exports results :wrap example :dir ../../.. +./zig-out/bin/tree -h +#+end_src + +#+RESULTS: +#+begin_example USAGE: ./zig-out/bin/tree [OPTIONS] [--] [directory] OPTIONS: - -m, --mode STRING Line drawing characters. (valid: ascii|box|dos)(default: box) - -a, --all All files are printed. - -s, --size Print the size of each file in bytes along with the name. - -d, --directory List directories only. - -L, --level INTEGER Max display depth of the directory tree. - -v, --version Print version. - -h, --help Print help information. - - -#+end_src + -m, --mode STRING Line drawing characters. (valid: ascii|box|dos)(default: box) + -a, --all All files are printed. + -s, --size Print the size of each file in bytes along with the name. + -d, --directory List directories only. + -L, --level INTEGER Max display depth of the directory tree. + -v, --version Print version. + -h, --help Print help information. +#+end_example ** Demo #+begin_src bash diff --git a/src/bin/tcp-proxy.zig b/src/bin/tcp-proxy.zig index eae17c6..a2ab60a 100644 --- a/src/bin/tcp-proxy.zig +++ b/src/bin/tcp-proxy.zig @@ -1,26 +1,12 @@ const std = @import("std"); const simargs = @import("simargs"); const util = @import("util.zig"); -const process = std.process; -const fs = std.fs; +const debugPrint = util.debugPrint; const net = std.net; const mem = std.mem; -var verbose: bool = false; - -fn debugPrint( - comptime format: []const u8, - args: anytype, -) void { - if (verbose) { - std.debug.print(format, args); - } -} - pub fn main() !void { - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer arena.deinit(); - const allocator = arena.allocator(); + const allocator = std.heap.page_allocator; const opt = try simargs.parse(allocator, struct { bind_host: []const u8, @@ -28,7 +14,7 @@ pub fn main() !void { remote_host: []const u8, remote_port: u16, buf_size: usize = 1024, - thread_pool_size: u32 = 24, + server_threads: u32 = 24, help: bool = false, version: bool = false, verbose: bool = false, @@ -48,10 +34,13 @@ pub fn main() !void { .remote_host = "Remote host", .remote_port = "Remote port", .buf_size = "Buffer size for tcp read/write", + .server_threads = "Server worker threads num", }; }, null, util.get_build_info()); - verbose = opt.args.verbose; + if (opt.args.verbose) { + util.enableVerbose.call(); + } const bind_addr = try net.Address.resolveIp(opt.args.bind_host, opt.args.local_port); const remote_addr = try net.Address.resolveIp(opt.args.remote_host, opt.args.remote_port); @@ -66,18 +55,18 @@ pub fn main() !void { try pool.init(.{ .allocator = allocator, - .n_jobs = opt.args.thread_pool_size, + .n_jobs = opt.args.server_threads, }); while (true) { const client = try server.accept(); debugPrint("Got new connection, addr:{any}\n", .{client.address}); - const proxy = Proxy.init(allocator, client, remote_addr) catch |e| { + const proxy = Proxy.init(allocator, client, remote_addr, opt.args.buf_size) catch |e| { std.log.err("Init proxy failed, remote:{any}, err:{any}", .{ remote_addr, e }); client.stream.close(); continue; }; - proxy.nonblockingCommunicate(pool, opt.args.buf_size) catch |e| { + proxy.nonblockingCommunicate(pool) catch |e| { proxy.deinit(); std.log.err("Communicate, remote:{any}, err:{any}", .{ remote_addr, e }); }; @@ -90,82 +79,90 @@ const Proxy = struct { remote_addr: net.Address, allocator: mem.Allocator, - pub fn init(allocator: mem.Allocator, conn: net.Server.Connection, remote: net.Address) !Proxy { + doubled_buf: []u8, + buf_size: usize, + + pub fn init(allocator: mem.Allocator, conn: net.Server.Connection, remote: net.Address, buf_size: usize) !Proxy { const remote_conn = try net.tcpConnectToAddress(remote); + const buf = try allocator.alloc(u8, buf_size * 2); return .{ .allocator = allocator, .conn = conn, .remote_conn = remote_conn, .remote_addr = remote, - }; - } - - fn copyStreamNoError( - allocator: mem.Allocator, - src: net.Stream, - dst: net.Stream, - buf_size: usize, - ) void { - Proxy.copyStream(allocator, src, dst, buf_size) catch |e| - switch (e) { - error.NotOpenForReading => {}, - else => { - std.log.err("copy stream error: {any}\n", .{e}); - }, + .doubled_buf = buf, + .buf_size = buf_size, }; } fn copyStream( - allocator: mem.Allocator, + buf: []u8, src: net.Stream, dst: net.Stream, - buf_size: usize, - ) !void { - var buf = try allocator.alloc(u8, buf_size); - defer allocator.free(buf); + ) void { + while (true) { + const read = src.read(buf) catch |e| + switch (e) { + error.NotOpenForReading => return, + else => { + std.log.err("Read stream failed, err:{any}", .{e}); + return; + }, + }; + + if (read == 0) { + return; + } - var read = try src.read(buf); - while (read > 0) : (read = try src.read(buf)) { - _ = try dst.writeAll(buf[0..read]); + _ = dst.writeAll(buf[0..read]) catch |e| { + std.log.err("Write stream failed, err:{any}", .{e}); + return; + }; } } pub fn nonblockingCommunicate( self: Proxy, pool: *std.Thread.Pool, - buf_size: usize, ) !void { try pool.spawn(struct { fn run( proxy: Proxy, pool_inner: *std.Thread.Pool, - buf_size_inner: usize, + src_to_remote_buf: []u8, + remote_to_src_buf: []u8, ) void { var wg = std.Thread.WaitGroup{}; - defer { - wg.wait(); - proxy.deinit(); - } - - // When conn.stream is closed, we close this proxy. - pool_inner.spawnWg(&wg, Proxy.copyStreamNoError, .{ - proxy.allocator, + pool_inner.spawnWg(&wg, Proxy.copyStream, .{ + src_to_remote_buf, proxy.conn.stream, proxy.remote_conn, - buf_size_inner, }); - pool_inner.spawn(Proxy.copyStreamNoError, .{ - proxy.allocator, + pool_inner.spawn(Proxy.copyStream, .{ + remote_to_src_buf, proxy.remote_conn, proxy.conn.stream, - buf_size_inner, - }) catch unreachable; + }) catch |e| { + proxy.deinit(); + std.log.err("Spawn task failed, err:{any}", .{e}); + return; + }; + + // Bi-directional transmissions are established, we wait until conn.stream is closed. + wg.wait(); + proxy.deinit(); } - }.run, .{ self, pool, buf_size }); + }.run, .{ + self, + pool, + self.doubled_buf[0..self.buf_size], + self.doubled_buf[self.buf_size..], + }); } fn deinit(self: Proxy) void { debugPrint("Close proxy, src:{any}\n", .{self.conn.address}); + self.conn.stream.close(); self.remote_conn.close(); } diff --git a/src/bin/util.zig b/src/bin/util.zig index fc3a260..54331ee 100644 --- a/src/bin/util.zig +++ b/src/bin/util.zig @@ -64,3 +64,25 @@ test "slice iter" { try std.testing.expectEqual(iter.next().?, 3); try std.testing.expectEqual(iter.next(), null); } + +// global var, used in one binary program. +var verbose: bool = false; + +pub var enableVerbose = std.once(struct { + fn do() void { + verbose = true; + } +}.do); + +pub fn debugPrint( + comptime format: []const u8, + args: anytype, +) void { + if (verbose) { + std.debug.print(format, args); + } +} + +pub fn getCpuCount() u32 { + return std.Thread.getCpuCount() orelse 1; +}