diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b2be92b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +result diff --git a/flake.nix b/flake.nix index 9a402e2..6fb206d 100644 --- a/flake.nix +++ b/flake.nix @@ -9,16 +9,19 @@ { nixpkgs, ... }: let inherit (nixpkgs.lib) genAttrs; - forAllSystems = - f: - genAttrs [ - "x86_64-linux" - "x86_64-darwin" - "aarch64-linux" - "aarch64-darwin" - ] (system: f nixpkgs.legacyPackages.${system}); + systems = [ + "x86_64-linux" + "x86_64-darwin" + "aarch64-linux" + "aarch64-darwin" + ]; + forAllSystems = f: genAttrs systems (system: f (import nixpkgs { inherit system; })); in { + packages = forAllSystems (pkgs: rec { + default = pkgs.callPackage ./meta.nix {inherit tunnel;}; + tunnel = pkgs.callPackage ./tunnel-go { }; + }); devShells = forAllSystems (pkgs: { default = pkgs.mkShell { packages = with pkgs; [ diff --git a/meta.nix b/meta.nix new file mode 100644 index 0000000..d5bec21 --- /dev/null +++ b/meta.nix @@ -0,0 +1,30 @@ +{ +lib, +stdenvNoCC, + +# packages +tunnel, +... +}:stdenvNoCC.mkDerivation (finalAttrs: { + pname = "meta"; + version = "unstable"; + + # src = + buildInputs = [ + tunnel + ]; + + phases = [ "installPhase"]; + + installPhase = '' + mkdir -p $out/bin + ln -s ${tunnel}/bin/tunnel $out/bin + ''; + + meta = { + description = "meta package for utils"; + homepage = "https://git.dayl.in/daylin/utils"; + license = lib.licenses.mit; + maintainers = with lib.maintainers; [ daylinmorgan ]; + }; +}) diff --git a/tunnel-go/default.nix b/tunnel-go/default.nix index 005bc7b..8c7cf49 100644 --- a/tunnel-go/default.nix +++ b/tunnel-go/default.nix @@ -13,12 +13,9 @@ buildGoModule rec { version = "unstable"; src = cleanSource ./.; - vendorHash = ""; + vendorHash = "sha256-PwZJMEVaPHqZs7bM+9XLxVA36GfV3EN6bja86hkfO90="; - nativeBuildInputs = [ - installShellFiles - makeWrapper - ]; + nativeBuildInputs = [ installShellFiles makeWrapper ]; postInstall = '' installShellCompletion --cmd ${pname} \ diff --git a/tunnel-go/main.go b/tunnel-go/main.go index 6bc6410..0a58a69 100644 --- a/tunnel-go/main.go +++ b/tunnel-go/main.go @@ -2,11 +2,13 @@ package main import ( "fmt" + "io/fs" "log" "os" "os/exec" + "path/filepath" + "strings" - "github.com/bitfield/script" "github.com/spf13/cobra" ) @@ -40,28 +42,47 @@ func tunnelUp() { logFail(connectPort(), "failed to connect to host: %s with port: %d", host, port) } -func tunnelDown() { logFail(deactivateSshControl(), "failed to disable ssh control %s", host) } +func tunnelDown() { + logFail(deactivateSshControl(), "failed to disable ssh control %s", host) +} + +func getControlSockets(files []fs.DirEntry) (ret []string) { + for _, f := range files { + if strings.HasPrefix(f.Name(), "control") { + ret = append(ret, f.Name()) + } + } + return +} + func tunnelShow() { - script.ListFiles("/home/daylin/.ssh").Stdout() + files, err := os.ReadDir(sshDir) + if err != nil { + panic(err) + } + controls := getControlSockets(files) + fmt.Printf("%d active connections\n", len(controls)) + if len(controls) > 0 { + fmt.Println("hosts:") + for _, c := range controls { + fmt.Printf(" %s\n", strings.Split(c, "-")[1]) + } + } } var ( + sshDir = filepath.Join(os.Getenv("HOME"), ".ssh") rootCmd = &cobra.Command{Use: "tunnel", Short: "control ssh tunnels"} upCmd = genSubCmd("up hostname [flags]", "activate ssh tunnel", tunnelUp) downCmd = genSubCmd("down hostname [flags]", "deactivate ssh tunnel", tunnelDown) showCmd = &cobra.Command{Use: "show", Short: "show activate tunnels", Run: func(cmd *cobra.Command, args []string) { tunnelShow() }} -) - -var ( - port uint64 - host string + port uint64 + host string ) func init() { rootCmd.CompletionOptions.HiddenDefaultCmd = true - rootCmd.AddCommand(upCmd) - rootCmd.AddCommand(downCmd) - rootCmd.AddCommand(showCmd) + rootCmd.AddCommand(upCmd, downCmd, showCmd) upCmd.Flags().Uint64VarP(&port, "port", "p", 0, "port number") upCmd.MarkFlagRequired("port") } diff --git a/tunnel-nim/.gitignore b/tunnel-nim/.gitignore new file mode 100644 index 0000000..4154f4b --- /dev/null +++ b/tunnel-nim/.gitignore @@ -0,0 +1,3 @@ +nimbledeps/ +nimble.paths +nimble.develop diff --git a/tunnel-nim/config.nims b/tunnel-nim/config.nims new file mode 100644 index 0000000..e3bc522 --- /dev/null +++ b/tunnel-nim/config.nims @@ -0,0 +1,11 @@ +# begin Nimble config (version 2) +--noNimblePath +when withDir(thisDir(), system.fileExists("nimble.paths")): + include "nimble.paths" +# end Nimble config + +task updateLock, "update nix lock.json": + exec "nimble lock -l" + exec "nix run 'github:daylinmorgan/nnl' -- nimble.lock -o:lock.json" + + diff --git a/tunnel-nim/default.nix b/tunnel-nim/default.nix new file mode 100644 index 0000000..05b798b --- /dev/null +++ b/tunnel-nim/default.nix @@ -0,0 +1,14 @@ +{ + lib, + buildNimPackage, + makeWrapper, +}: +buildNimPackage { + pname = "tunnel"; + version = "unstable"; + + src = lib.cleanSource ./.; + lockFile = ./lock.json; + nativeBuildInputs = [ makeWrapper ]; + doCheck = false; +} diff --git a/tunnel-nim/lock.json b/tunnel-nim/lock.json new file mode 100644 index 0000000..1dc5e2b --- /dev/null +++ b/tunnel-nim/lock.json @@ -0,0 +1,16 @@ +{ + "depends": [ + { + "method": "fetchzip", + "path": "/nix/store/5a5aafd2vb3yb39ha5hbz8im9al9shjg-source", + "rev": "0e7b3b3a1fa77c448d3681c5fdf85197844cb7e4", + "sha256": "1c5xdxmgvibbrkn53svawp3wbwb0ylg3fnzbpgiadgnnpz3cdixz", + "srcDir": "", + "url": "https://github.com/c-blake/cligen/archive/0e7b3b3a1fa77c448d3681c5fdf85197844cb7e4.tar.gz", + "subDir": "", + "packages": [ + "cligen" + ] + } + ] +} \ No newline at end of file diff --git a/tunnel-nim/nimble.lock b/tunnel-nim/nimble.lock new file mode 100644 index 0000000..8bb3d4a --- /dev/null +++ b/tunnel-nim/nimble.lock @@ -0,0 +1,16 @@ +{ + "version": 2, + "packages": { + "cligen": { + "version": "1.7.3", + "vcsRevision": "0e7b3b3a1fa77c448d3681c5fdf85197844cb7e4", + "url": "https://github.com/c-blake/cligen.git", + "downloadMethod": "git", + "dependencies": [], + "checksums": { + "sha1": "5a082835594887c0fdaea17dbfd2d3a2e744a2a9" + } + } + }, + "tasks": {} +} diff --git a/tunnel-nim/tunnel.nim b/tunnel-nim/tunnel.nim new file mode 100644 index 0000000..1941630 --- /dev/null +++ b/tunnel-nim/tunnel.nim @@ -0,0 +1,52 @@ +import std/[os, osproc, sequtils, strformat, strutils, sugar] + +import cligen + +proc checkHost(host: seq[string]): string = + case host.len: + of 0: quit "expected hostname" + of 1: return host[0] + else: quit "expected one positinal argument" + + +proc check(name: string): bool = + (execCmd &"ssh -O check {name}") == 0 +proc startSsh(name: string) = + discard execCmd &"ssh -M -f -N {name}" +proc activateTunnel(name: string, port: int): int = + execCmd &"""ssh -fNL "{port}:localhost:{port}" {name}""" +proc exitSsh(name: string):int = + execCmd &"ssh -O exit {name}" + +proc up(port: int, host: seq[string]) = + ## activate a tunnel + let name = checkHost host + echo "activating connection to", name + if not check(name): + startSsh(name) + quit activateTunnel(name, port) + + +proc down(host: seq[string]) = + ## disable all tunnels + let name = checkHost host + echo "deactivating connection to", name + quit exitSsh(name) + +proc show() = + ## show active connections + let sshDir = (getEnv "HOME") / ".ssh" + let controllers = + collect(for _,p in walkDir(sshDir): p) + .map(extractFilename) + .filterIt(it.startsWith("control")) + echo &"{controllers.len} active connections" + if controllers.len == 0: quit 0 + echo "hosts:" + echo controllers.mapIt(" " & it.split('-')[1]).join("\n") + +const + hostUsage = "$command [flags] hostname\n${doc}Options:\n$options" + usage = "$command [flags]\n${doc}Options:\n$options" + +dispatchMulti([up, usage=hostUsage], [down, usage=hostUsage], [show, usage=usage]) diff --git a/tunnel-nim/tunnel.nimble b/tunnel-nim/tunnel.nimble new file mode 100644 index 0000000..904f98e --- /dev/null +++ b/tunnel-nim/tunnel.nimble @@ -0,0 +1,15 @@ +# Package + +version = "0.1.0" +author = "Daylin Morgan" +description = "A new awesome nimble package" +license = "MIT" +srcDir = "." +bin = @["tunnel"] + + +# Dependencies +requires "nim >= 2.0.8" +requires "cligen" + +