{"id":50847672,"url":"https://github.com/dtxdf/prokshy","last_synced_at":"2026-06-14T11:02:52.933Z","repository":{"id":355766279,"uuid":"1229364303","full_name":"DtxdF/prokshy","owner":"DtxdF","description":"Small and lightweight bhyve agent","archived":false,"fork":false,"pushed_at":"2026-05-05T05:47:22.000Z","size":5,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-05T07:27:05.143Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DtxdF.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-05T01:05:57.000Z","updated_at":"2026-05-05T05:47:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/DtxdF/prokshy","commit_stats":null,"previous_names":["dtxdf/prokshy"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/DtxdF/prokshy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DtxdF%2Fprokshy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DtxdF%2Fprokshy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DtxdF%2Fprokshy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DtxdF%2Fprokshy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DtxdF","download_url":"https://codeload.github.com/DtxdF/prokshy/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DtxdF%2Fprokshy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34318525,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-14T02:00:07.365Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-14T11:02:49.060Z","updated_at":"2026-06-14T11:02:52.909Z","avatar_url":"https://github.com/DtxdF.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# prokshy\n\n**prokshy** is a small and lightweight script that uses [unixexec](https://github.com/DtxdF/unixexec) to exchange data through the special character file (`/dev/vtcon/prokshy`) created by `virtio_console(4)`. This script is designed to \"standardize\" the way a command is executed from the host into the virtual machine. The host connects to the `unix(4)` socket created by `bhyve(8)` and then sends a command, a space, and then the data (or the command’s argument). prokshy doesn’t care about the argument format: you can use netstring, JSON, base64, or any other format that can be sent for the command you’ve created. That’s the other advantage of prokshy: it allows you to implement a \"command,\" making it truly flexible.\n\n## Getting Started\n\nInside the VM, install, enable and start prokshy.\n\n```sh\npkg install -y prokshy\nsysrc prokshy_enable=YES\nservice prokshy start\n```\n\nThis will wait to receive data from `/dev/vtcon/prokshy`, but since no command has been implemented, prokshy will simply ignore any data sent by the host.\n\n**/usr/local/etc/prokshy/command.d/statgrab**:\n\n```sh\n#!/bin/sh\n\nPATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin; export PATH\n\nif ! pkg info -e statgrab; then\n    pkg install -y statgrab || exit $?\nfi\n\nexec statgrab\n```\n\nThis command implements the “statgrab” command to run the executable file provided by [devel/libstatgrab](https://freshports.org/devel/libstatgrab), which displays useful information about the system. Note that the argument is completely ignored, and you must set the execute bit on this file for prokshy to run it.\n\n```console\n# chmod +x /usr/local/etc/prokshy/command.d/statgrab\n```\n\nNext, from the host, you can use `nc(1)` to connect to the `unix(4)` socket created by `bhyve(8)` and run the command we've implemented.\n\n```sh\n# echo \"statgrab\" | nc -U vtcon.prokshy\nstatgrab\nconst.0 = 0\ncpu.ctxsw = 0\ncpu.idle = 7212359\ncpu.intrs = 0\ncpu.iowait = 0\ncpu.kernel = 14792\ncpu.nice = 0\ncpu.nvctxsw = 0\ncpu.softintrs = 0\ncpu.swap = 0\ncpu.syscalls = 0\ncpu.systime = 1777959159\ncpu.total = 7241438\ncpu.user = 7018\ncpu.vctxsw = 0\ndisk.nda0.disk_name = nda0\ndisk.nda0.read_bytes = 677440000\ndisk.nda0.systime = 1777959159\ndisk.nda0.write_bytes = 29893522944\ndisk.pass0.disk_name = pass0\ndisk.pass0.read_bytes = 0\ndisk.pass0.systime = 1777959159\ndisk.pass0.write_bytes = 0\nfs.devfs.avail = 1024\nfs.devfs.avail_blocks = 2\nfs.devfs.avail_inodes = 0\nfs.devfs.block_size = 512\nfs.devfs.device_canonical = devfs\nfs.devfs.device_name = devfs\nfs.devfs.free_blocks = 2\nfs.devfs.free_inodes = 0\nfs.devfs.fs_type = devfs\nfs.devfs.io_size = 512\nfs.devfs.mnt_point = /dev\nfs.devfs.size = 1024\nfs.devfs.total_blocks = 2\nfs.devfs.total_inodes = 0\nfs.devfs.used = 0\nfs.devfs.used_blocks = 0\nfs.devfs.used_inodes = 0\nfs.nda0p3.avail = 1780576256\nfs.nda0p3.avail_blocks = 434711\nfs.nda0p3.avail_inodes = 1071470\nfs.nda0p3.block_size = 4096\nfs.nda0p3.device_canonical = /dev/nda0p3\nfs.nda0p3.device_name = /dev/nda0p3\nfs.nda0p3.free_blocks = 617382\nfs.nda0p3.free_inodes = 1071470\nfs.nda0p3.fs_type = ufs\nfs.nda0p3.io_size = 32768\nfs.nda0p3.mnt_point = /\nfs.nda0p3.size = 9352802304\nfs.nda0p3.total_blocks = 2283399\nfs.nda0p3.total_inodes = 1201918\nfs.nda0p3.used = 6824005632\nfs.nda0p3.used_blocks = 1666017\nfs.nda0p3.used_inodes = 130448\ngeneral.bitwidth = 64\ngeneral.hostname = test-vm\ngeneral.hoststate = unknown\ngeneral.ncpus = 2\ngeneral.ncpus_cfg = 2\ngeneral.os_name = FreeBSD\ngeneral.os_release = 15.1-STABLE\ngeneral.os_version = FreeBSD 15.1-STABLE stable/15-n283454-4702f6ab1514 GENERIC\ngeneral.platform = amd64\ngeneral.uptime = 28510\nload.min1 = 0.104004\nload.min15 = 0.222168\nload.min5 = 0.184570\nmem.cache = 0\nmem.free = 1606631424\nmem.total = 2054467584\nmem.used = 447836160\nnet.lo0.collisions = 0\nnet.lo0.duplex = unknown\nnet.lo0.ierrors = 0\nnet.lo0.interface_name = lo0\nnet.lo0.ipackets = 389\nnet.lo0.oerrors = 0\nnet.lo0.opackets = 389\nnet.lo0.rx = 23360\nnet.lo0.speed = 0\nnet.lo0.systime = 1777959159\nnet.lo0.tx = 23360\nnet.lo0.up = true\nnet.vtnet0.collisions = 0\nnet.vtnet0.duplex = full\nnet.vtnet0.ierrors = 0\nnet.vtnet0.interface_name = vtnet0\nnet.vtnet0.ipackets = 621114\nnet.vtnet0.oerrors = 0\nnet.vtnet0.opackets = 442069\nnet.vtnet0.rx = 735707386\nnet.vtnet0.speed = 10000\nnet.vtnet0.systime = 1777959159\nnet.vtnet0.tx = 36502321\nnet.vtnet0.up = true\npage.in = 2\npage.out = 718\npage.systime = 1777959159\nproc.running = 2\nproc.sleeping = 31\nproc.stopped = 0\nproc.total = 33\nproc.zombie = 0\nswap.free = 1073676288\nswap.total = 1073741824\nswap.used = 65536\nuser.names = root\nuser.num = 1\nuser.pts/1.from = ajnet.appjail\nuser.pts/1.login_name = root\nuser.pts/1.login_time = 1777931158\nuser.pts/1.tty = pts/1\n^C\n```\n\nA slightly more complex command may require a more robust programming language, such as Python, and, as mentioned earlier, it’s up to you how you parse the argument passed via stdin.\n\n**/usr/local/etc/prokshy/command.d/OpenFile**:\n\n```python\n#!/usr/local/bin/python\n\nimport shlex\nimport sys\n\nlex = shlex.shlex(posix=True)\nlex.whitespace = \",\"\nlex.whitespace_split = True\n\nparams = {}\n\nfor arg in lex:\n    arg = arg.split(\"=\", 1)\n\n    if len(arg) == 1:\n        (key, value) = (arg, None)\n\n    else:\n        (key, value) = arg\n\n    params[key] = value\n\nfilename = params.get(\"filename\")\nassert filename is not None\n\nmode = params.get(\"mode\", \"read\")\nassert not (mode != \"read\" and mode != \"write\")\n\nif mode == \"read\":\n    from base64 import b64encode\n\n    with open(filename, \"rb\") as fd:\n        data = b64encode(fd.read())\n        sys.stdout.buffer.write(data)\n        sys.stdout.buffer.flush()\n\nelse:\n    from base64 import b64decode\n\n    content = params.get(\"content\", \"\")\n    content = b64decode(content)\n\n    with open(filename, \"wb\") as fd:\n        fd.write(content)\n```\n\n**Console**:\n\nIn the following example, let's use the CLI client, which is more powerful.\n\n```console\n# pkg install -y prokshy-cli\n...\n# prokshy --command OpenFile --from-string \"filename=/tmp/hello.txt,mode=write,content=SGVsbG8hCg==\" --socket-path vtcon.prokshy\n# prokshy --command OpenFile --from-string \"filename=/tmp/hello.txt\" --socket-path vtcon.prokshy | base64 -d\nHello!\n```\n\n## Caveats\n\nAs you have probably noticed, in the case of `virtio_console(4)`, there is always a connected peer; therefore, if you send data through the `unix(4)` socket (host) or the special character file (VM), the data is always sent, but this does not mean that the other side will perform something useful (if no one sees the data, there's nothing to do).\n\n## Notes\n\n1. Command names are limited to: `^[a-zA-Z0-9_.-]+$`. If the host attempts to execute a command that contains a character not allowed, the data is simply ignored.\n2. When the input (`--from-file` or `--from-string`) in the CLI contains a line break, only the first line is actually used. That's how the \"protocol\" works.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdtxdf%2Fprokshy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdtxdf%2Fprokshy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdtxdf%2Fprokshy/lists"}