New arg parser and profile support
This commit is contained in:
parent
9708146d81
commit
d96e27f3f3
|
@ -0,0 +1,48 @@
|
||||||
|
import parseopt
|
||||||
|
import options
|
||||||
|
import modes
|
||||||
|
import os
|
||||||
|
|
||||||
|
type Args* = object
|
||||||
|
name*: Option[string]
|
||||||
|
cmd*: Option[string]
|
||||||
|
profile*: Option[string]
|
||||||
|
|
||||||
|
proc getCmd*(args: Args): string =
|
||||||
|
return args.cmd.get(getEnv("SHELL", "/bin/bash"))
|
||||||
|
|
||||||
|
proc getProfile*(args: Args, mode: Modes): string =
|
||||||
|
if args.profile.isSome:
|
||||||
|
return args.profile.unsafeGet
|
||||||
|
|
||||||
|
return case mode
|
||||||
|
of Modes.Shell: "shell"
|
||||||
|
of Modes.Box: "gui"
|
||||||
|
|
||||||
|
proc parseOpt(args: var Args, key: string, value: string): bool =
|
||||||
|
case key
|
||||||
|
of "command", "c":
|
||||||
|
args.cmd = some(value)
|
||||||
|
of "profile", "p":
|
||||||
|
args.profile = some(value)
|
||||||
|
else:
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc parseArgs*(): Option[Args] =
|
||||||
|
var p = initOptParser()
|
||||||
|
var args = Args()
|
||||||
|
|
||||||
|
while true:
|
||||||
|
p.next()
|
||||||
|
case p.kind
|
||||||
|
of cmdEnd: break
|
||||||
|
of cmdShortOption, cmdLongOption:
|
||||||
|
if p.val == "" or args.parseOpt(p.key, p.val) == false:
|
||||||
|
echo "Invalid argument ", p.val
|
||||||
|
return
|
||||||
|
of cmdArgument:
|
||||||
|
args.name = some(p.key.string)
|
||||||
|
|
||||||
|
return some(args)
|
|
@ -1,11 +1,43 @@
|
||||||
|
import sequtils
|
||||||
import options
|
import options
|
||||||
|
import bwrap
|
||||||
|
import utils
|
||||||
|
import json
|
||||||
|
|
||||||
type Link* = object
|
type Link* = object
|
||||||
src*: string
|
src*: string
|
||||||
dst*: string
|
dst*: string
|
||||||
|
|
||||||
type Config* = object
|
type Config* = object
|
||||||
extends*: Option[seq[string]]
|
extends*: Option[string]
|
||||||
mount*: Option[seq[string]]
|
mount*: Option[seq[string]]
|
||||||
romount*: Option[seq[string]]
|
romount*: Option[seq[string]]
|
||||||
symlinks*: Option[seq[Link]]
|
symlinks*: Option[seq[Link]]
|
||||||
|
|
||||||
|
proc applyConfig*(call: var BwrapCall, config: Config) =
|
||||||
|
for mount in config.mount.get(@[]):
|
||||||
|
call.addMount("--bind", checkRelativePath(mount))
|
||||||
|
|
||||||
|
for mount in config.romount.get(@[]):
|
||||||
|
call.addMount("--ro-bind", checkRelativePath(mount))
|
||||||
|
|
||||||
|
for symlink in config.symlinks.get(@[]):
|
||||||
|
call.addArg("--symlink", symlink.src, symlink.dst)
|
||||||
|
|
||||||
|
proc loadConfig*(path: string): Config =
|
||||||
|
return readFile(path)
|
||||||
|
.parseJson()
|
||||||
|
.to(Config)
|
||||||
|
|
||||||
|
proc extendConfig*(config: var Config): Config {.discardable.} =
|
||||||
|
if config.extends.isNone:
|
||||||
|
return
|
||||||
|
|
||||||
|
var eConf = loadConfig(getProfilePath(config.extends.unsafeGet))
|
||||||
|
eConf.extendConfig()
|
||||||
|
|
||||||
|
config.mount = some(config.mount.get(@[]).concat(eConf.mount.get(@[])))
|
||||||
|
config.romount = some(config.romount.get(@[]).concat(eConf.romount.get(@[])))
|
||||||
|
config.symlinks = some(config.symlinks.get(@[]).concat(eConf.symlinks.get(@[])))
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
|
@ -1,59 +1,49 @@
|
||||||
import os
|
import os
|
||||||
import json
|
import args
|
||||||
|
import utils
|
||||||
import modes
|
import modes
|
||||||
import bwrap
|
import bwrap
|
||||||
import config
|
import config
|
||||||
import options
|
import options
|
||||||
|
|
||||||
proc homePath(p: string): string =
|
proc sandboxExec*(mode: Modes, args: Args) =
|
||||||
joinPath(getHomeDir(), p)
|
var call = BwrapCall()
|
||||||
|
var userConfig = none(Config)
|
||||||
|
|
||||||
const CONFIG_LOCATION = homePath(joinPath(".sandboxes", "config.json"))
|
let hostname = args.name.get("sandbox")
|
||||||
|
let profilePath = getProfilePath(args, mode)
|
||||||
|
|
||||||
proc checkRelativePath(p: string): string =
|
if args.name.isSome:
|
||||||
if p[0] == '/':
|
let name = args.name.unsafeGet
|
||||||
return p
|
let sandboxPath = getSandboxPath(name)
|
||||||
homePath(p)
|
let sandboxFiles = sandboxPath.joinPath("files")
|
||||||
|
let configPath = sandboxPath.joinPath("config.json")
|
||||||
|
|
||||||
proc applyConfig(call: var BwrapCall, config: Config) =
|
if fileExists(configPath):
|
||||||
for mount in config.mount.get(@[]):
|
userConfig = some(loadConfig(configPath))
|
||||||
call.addMount("--bind", checkRelativePath(mount))
|
|
||||||
|
|
||||||
for mount in config.romount.get(@[]):
|
|
||||||
call.addMount("--ro-bind", checkRelativePath(mount))
|
|
||||||
|
|
||||||
for symlink in config.symlinks.get(@[]):
|
|
||||||
call.addArg("--symlink", symlink.src, symlink.dst)
|
|
||||||
|
|
||||||
proc loadConfig(path: string): Config =
|
|
||||||
return readFile(path).parseJson().to(Config)
|
|
||||||
|
|
||||||
proc sandboxExec*(name: string, command: string, mode: Modes) =
|
|
||||||
let sandboxPath = homePath(joinPath(".sandboxes", name))
|
|
||||||
let sandboxFiles = joinPath(sandboxPath, "files")
|
|
||||||
let sandboxInfo = joinPath(sandboxPath, "info")
|
|
||||||
|
|
||||||
createDir(sandboxFiles)
|
createDir(sandboxFiles)
|
||||||
var call = BwrapCall()
|
call.addArg("--bind", sandboxFiles, getHomeDir())
|
||||||
|
|
||||||
|
var profile = loadConfig(profilePath)
|
||||||
|
profile.extendConfig()
|
||||||
|
|
||||||
call
|
call
|
||||||
.addArg("--bind", sandboxFiles, getHomeDir())
|
.addMount("--dev-bind", "/dev/null")
|
||||||
.addMount("--dev-bind", "/dev")
|
|
||||||
.addArg("--tmpfs", "/tmp")
|
.addArg("--tmpfs", "/tmp")
|
||||||
.addArg("--proc", "/proc")
|
.addArg("--proc", "/proc")
|
||||||
.addArg("--unshare-all")
|
.addArg("--unshare-all")
|
||||||
.addArg("--share-net")
|
.addArg("--share-net")
|
||||||
.addArg("--die-with-parent")
|
.addArg("--die-with-parent")
|
||||||
.addArg("--hostname", name)
|
.applyConfig(profile)
|
||||||
.applyConfig(loadConfig(CONFIG_LOCATION))
|
|
||||||
|
|
||||||
if mode == Modes.Shell:
|
if mode == Modes.Shell:
|
||||||
call
|
call
|
||||||
.addMount("--bind", getCurrentDir())
|
.addMount("--bind", getCurrentDir())
|
||||||
.addArg("--chdir", getCurrentDir())
|
.addArg("--chdir", getCurrentDir())
|
||||||
|
.addArg("--hostname", hostname)
|
||||||
|
|
||||||
let configPath = sandboxPath.joinPath("config.json")
|
if userConfig.isSome:
|
||||||
if fileExists(configPath):
|
call.applyConfig(userConfig.unsafeGet)
|
||||||
call.applyConfig(loadConfig(configPath))
|
|
||||||
|
|
||||||
call.addArg(command).exec()
|
call.addArg(args.getCmd).exec()
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import os
|
||||||
|
import args
|
||||||
|
import modes
|
||||||
|
|
||||||
|
const APP_NAME = "bwsandbox"
|
||||||
|
|
||||||
|
proc getDataDir*(): string =
|
||||||
|
getEnv("XDG_DATA_DIR", getHomeDir().joinPath(".local/share"))
|
||||||
|
|
||||||
|
proc checkRelativePath*(p: string): string =
|
||||||
|
if p[0] == '/':
|
||||||
|
return p
|
||||||
|
getHomeDir().joinPath(p)
|
||||||
|
|
||||||
|
proc getProfilePath*(profile: string): string =
|
||||||
|
getConfigDir()
|
||||||
|
.joinPath(APP_NAME)
|
||||||
|
.joinPath(profile)
|
||||||
|
|
||||||
|
proc getProfilePath*(args: Args, mode: Modes): string =
|
||||||
|
getProfilePath(args.getProfile(mode))
|
||||||
|
|
||||||
|
proc getSandboxPath*(name: string): string =
|
||||||
|
getDataDir()
|
||||||
|
.joinPath(APP_NAME)
|
||||||
|
.joinPath(name)
|
28
main.nim
28
main.nim
|
@ -1,27 +1,19 @@
|
||||||
import lib/sandbox
|
import lib/sandbox
|
||||||
import lib/modes
|
import lib/modes
|
||||||
|
import lib/args
|
||||||
import strformat
|
import strformat
|
||||||
import strutils
|
import strutils
|
||||||
|
import options
|
||||||
import os
|
import os
|
||||||
|
|
||||||
proc main() =
|
proc main(): int =
|
||||||
let mode = parseEnum[Modes](paramStr(0))
|
let mode = parseEnum[Modes](paramStr(0), Modes.Shell)
|
||||||
let args = commandLineParams()
|
let args = parseArgs()
|
||||||
let argc = paramCount()
|
|
||||||
|
|
||||||
if argc == 0:
|
if args.isNone:
|
||||||
echo &"Usage: {mode} <sandbox> [command]"
|
echo &"Usage: {mode} --command=cmd --profile=profile <sandbox_name>"
|
||||||
quit(1)
|
return 1
|
||||||
|
|
||||||
|
|
||||||
let name = args[0]
|
|
||||||
var command: string
|
|
||||||
|
|
||||||
if argc > 1:
|
|
||||||
command = args[1]
|
|
||||||
else:
|
else:
|
||||||
command = getEnv("SHELL", "/bin/sh")
|
sandboxExec(mode, args.unsafeGet)
|
||||||
|
|
||||||
sandboxExec(name, command, mode)
|
quit(main())
|
||||||
|
|
||||||
main()
|
|
||||||
|
|
Loading…
Reference in New Issue