From 0601052833bfb97cc178cdbd7aa150ecec4c7e5b Mon Sep 17 00:00:00 2001 From: zqqw Date: Mon, 26 Oct 2020 02:11:42 +0000 Subject: execRedirect - pipe stdout from child --- src/common.nim | 28 +++++++------- src/feature/syncinstall.nim | 23 ++++++++--- src/main.nim | 2 +- src/pacman.nim | 4 +- src/utils.nim | 93 ++++++++++++++++++++++++++++++++++++--------- 5 files changed, 109 insertions(+), 41 deletions(-) diff --git a/src/common.nim b/src/common.nim index f7de141..71073cd 100644 --- a/src/common.nim +++ b/src/common.nim @@ -346,8 +346,8 @@ proc getGitFiles*(repoPath: string, gitSubdir: Option[string], dropPrivileges: bool): seq[string] = if gitSubdir.isSome: forkWaitRedirect(() => (block: - if not dropPrivileges or dropPrivileges(): - execResult(gitCmd, "-C", repoPath, "ls-tree", "-r", "--name-only", "@", + if not dropPrivileges or dropPrivRedirect(): + execRedirect(gitCmd, "-C", repoPath, "ls-tree", "-r", "--name-only", "@", gitSubdir.unsafeGet & "/") else: quit(1))) @@ -355,8 +355,8 @@ proc getGitFiles*(repoPath: string, gitSubdir: Option[string], .map(s => s[gitSubdir.unsafeGet.len + 1 .. ^1]) else: forkWaitRedirect(() => (block: - if not dropPrivileges or dropPrivileges(): - execResult(gitCmd, "-C", repoPath, "ls-tree", "-r", "--name-only", "@") + if not dropPrivileges or dropPrivRedirect(): + execRedirect(gitCmd, "-C", repoPath, "ls-tree", "-r", "--name-only", "@") else: quit(1))) .output @@ -380,16 +380,16 @@ proc bisectVersion(repoPath: string, debug: bool, firstCommit: Option[string], (firstCommit, false) else: (forkWaitRedirect(() => (block: - if not dropPrivileges or dropPrivileges(): - execResult(gitCmd, "-C", repoPath, + if not dropPrivileges or dropPrivRedirect(): + execRedirect(gitCmd, "-C", repoPath, "rev-list", "--max-parents=0", "@") else: quit(1))) .output.optLast, true) let (realLastThreeCommits, _) = forkWaitRedirect(() => (block: - if not dropPrivileges or dropPrivileges(): - execResult(gitCmd, "-C", repoPath, + if not dropPrivileges or dropPrivRedirect(): + execRedirect(gitCmd, "-C", repoPath, "rev-list", "--max-count=3", "@") else: quit(1))) @@ -408,8 +408,8 @@ proc bisectVersion(repoPath: string, debug: bool, firstCommit: Option[string], none(string) else: let foundVersion = forkWaitRedirect(() => (block: - if not dropPrivileges or dropPrivileges(): - execResult(pkgLibDir & "/bisect", + if not dropPrivileges or dropPrivRedirect(): + execRedirect(pkgLibDir & "/bisect", compareMethod, repoPath & "/" & gitSubdir, version) else: quit(1))) @@ -452,8 +452,8 @@ proc bisectVersion(repoPath: string, debug: bool, firstCommit: Option[string], "bisect", "run", pkgLibDir & "/bisect", compareMethod, gitSubdir, version) let commit = forkWaitRedirect(() => (block: - if not dropPrivileges or dropPrivileges(): - execResult(gitCmd, "-C", repoPath, + if not dropPrivileges or dropPrivRedirect(): + execRedirect(gitCmd, "-C", repoPath, "rev-list", "--max-count=1", "refs/bisect/bad") else: quit(1))) @@ -477,8 +477,8 @@ proc bisectVersion(repoPath: string, debug: bool, firstCommit: Option[string], proc obtainSrcInfo*(path: string): string = let (output, code) = forkWaitRedirect(() => (block: - if dropPrivileges() and chdir(path) == 0: - execResult(makePkgCmd, "--printsrcinfo") + if dropPrivRedirect() and chdir(path) == 0: + execRedirect(makePkgCmd, "--printsrcinfo") else: quit(1))) diff --git a/src/feature/syncinstall.nim b/src/feature/syncinstall.nim index 813eb45..8f26d0d 100644 --- a/src/feature/syncinstall.nim +++ b/src/feature/syncinstall.nim @@ -305,6 +305,17 @@ template dropPrivilegesAndChdir(path: Option[string], body: untyped): int = printError(config.color, tr"failed to drop privileges") quit(1) +template dropPrivRedirectAndChdir(path: Option[string], body: untyped): int = + if dropPrivRedirect(): + if path.isNone or chdir(path.unsafeGet) == 0: + body + else: + printError(config.color, tr"chdir failed: $#" % [path.unsafeGet]) + quit(1) + else: + printError(config.color, tr"failed to drop privileges") + quit(1) + template createViewTag(repo: string, base: string): string = "view-" & repo & "/" & base @@ -376,8 +387,8 @@ proc editLoop(config: Config, repo: string, base: string, repoPath: string, let (hasChanges, noTag) = if repo == config.aurRepo: (block: let revisions = forkWaitRedirect(() => (block: - dropPrivilegesAndChdir(none(string)): - execResult(gitCmd, "-C", repoPath, "rev-list", tag & "..@"))) + dropPrivRedirectAndChdir(none(string)): + execRedirect(gitCmd, "-C", repoPath, "rev-list", tag & "..@"))) if revisions.code != 0: (false, true) @@ -385,8 +396,8 @@ proc editLoop(config: Config, repo: string, base: string, repoPath: string, (false, false) else: (block: let diff = forkWaitRedirect(() => (block: - dropPrivilegesAndChdir(none(string)): - execResult(gitCmd, "-C", repoPath, "diff", tag & "..@", gitSubdir.get(".")))) + dropPrivRedirectAndChdir(none(string)): + execRedirect(gitCmd, "-C", repoPath, "diff", tag & "..@", gitSubdir.get(".")))) (diff.output.len > 0, false))) else: (false, true) @@ -436,8 +447,8 @@ proc buildLoop(config: Config, pkgInfos: seq[PackageInfo], skipDeps: bool, let envExt = getEnv("PKGEXT") let confExt = if envExt.len == 0: forkWaitRedirect(() => (block: - dropPrivilegesAndChdir(none(string)): - execResult(bashCmd, "-c", + dropPrivRedirectAndChdir(none(string)): + execRedirect(bashCmd, "-c", "source \"$@\" && echo \"$PKGEXT\"", "bash", workConfFile))) .output.optFirst.get("") diff --git a/src/main.nim b/src/main.nim index d8ad5a5..73030d2 100644 --- a/src/main.nim +++ b/src/main.nim @@ -177,7 +177,7 @@ proc handleHelp(operation: OperationType) = .map(o => @["-" & o.pair.short.get]) .optFirst.get(@[]) & @["-h"] - let (lines, _) = forkWaitRedirect(() => execResult(pacmanCmd & operationArgs)) + let (lines, _) = forkWaitRedirect(() => execRedirect(pacmanCmd & operationArgs)) for line in lines: echo(line.replace(re"\bpacman\b", "pakku")) diff --git a/src/pacman.nim b/src/pacman.nim index ed80311..5f1cb4a 100644 --- a/src/pacman.nim +++ b/src/pacman.nim @@ -407,8 +407,8 @@ proc obtainPacmanConfig*(args: seq[Argument]): PacmanConfig = {x} let hasKeyserver = forkWaitRedirect(() => (block: - if dropPrivileges(): - execResult(gpgConfCmd, "--list-options", "gpg") + if dropPrivRedirect(): + execRedirect(gpgConfCmd, "--list-options", "gpg") else: quit(1))) .output diff --git a/src/utils.nim b/src/utils.nim index da9618b..1f58b8f 100644 --- a/src/utils.nim +++ b/src/utils.nim @@ -1,5 +1,5 @@ import - hashes, options, os, posix, sequtils, strutils, sugar, tables + hashes, options, os, posix, sequtils, strutils, sugar, tables, osproc, streams, strtabs type HaltError* = object of CatchableError @@ -170,22 +170,18 @@ proc forkWaitInternal(call: () -> int, beforeWait: () -> void): int = proc forkWait*(call: () -> int): int = forkWaitInternal(call, proc = discard) -proc forkWaitRedirect*(call: () -> int): tuple[output: seq[string], code: int] = - var fd: array[2, cint] - discard pipe(fd) +var dPriv: bool = false +var writeFlag: bool = false +var fd: array[2, cint] +proc forkWaitRedirect*(call: () -> int): tuple[output: seq[string], code: int] = + if pipe(fd) == -1: + raiseOSError(osLastError()) var data = newSeq[char]() - - let code = forkWaitInternal(() => (block: + writeFlag = true + let code = forkWaitInternal(() => (block: # "call" child process discard close(fd[0]) - discard close(1) - discard dup(fd[1]) - discard close(fd[1]) - discard close(0) - discard open("/dev/null") - discard close(2) - discard open("/dev/null") - call()), () => (block: + call()), () => (block: # "beforewait" parent process discard close(fd[1]) var buffer: array[80, char] while true: @@ -194,24 +190,21 @@ proc forkWaitRedirect*(call: () -> int): tuple[output: seq[string], code: int] = break data &= buffer[0 .. count - 1] discard close(fd[0]))) - + writeFlag = false var output = newStringOfCap(data.len) for c in data: output &= c - let lines = if output.len == 0: @[] elif output.len > 0 and $output[^1] == "\n": output[0 .. ^2].split("\n") else: output.split("\n") - (lines, code) proc getgrouplist*(user: cstring, group: Gid, groups: ptr cint, ngroups: var cint): cint {.importc, header: "".} - proc setgroups*(size: csize_t, groups: ptr cint): cint {.importc, header: "".} @@ -250,6 +243,70 @@ except: proc canDropPrivileges*(): bool = initialUser.isSome +proc dropPrivRedirect*(): bool = + dPriv = true + return true + +proc execRedirect*(args: varargs[string]): int = + var + code: int + p: owned(Process) + argSeq: seq[string] = @args + iUser: User + envs: StringTableRef = nil + + if dPriv == true: + if initialUser.isSome: + iUser = initialUser.unsafeGet + envs = newStringTable(modeCaseSensitive) + if iUser.name != "": + var groups = iUser.groups.map(x => x.cint) + if setgroups(cast[csize_t](iUser.groups.len), addr(groups[0])) < 0: + envs = nil + if setgid((Gid) iUser.gid) != 0: + envs = nil + if setuid((Uid) iUser.uid) != 0: + envs = nil + for key, value in envPairs(): + if key in ["SUDO_COMMAND", "SUDO_USER", "SUDO_UID", "SUDO_GID", "PKEXEC_UID"]: + continue + if key in ["USER", "USERNAME", "LOGNAME"]: + envs[key] = iUser.name + continue + if key in ["HOME"]: + envs[key] = iUser.home + continue + if key in ["SHELL"]: + envs[key] = iUser.shell + continue + envs[key] = value + if envs == nil: + echo "Error: failed to drop privileges" + return -1 + discard close(fd[0]) + argSeq.delete(0) + try: + p = startProcess(command = args[0], args = argSeq, env = envs, options = {poStdErrToStdOut, poUsePath}) + except: + echo "Error: " & getCurrentExceptionMsg() + return -1 + var outp = outputStream(p) + close(inputStream(p)) + var line = newStringOfCap(120).TaintedString + while true: + if outp.readLine(line): + if writeFlag == false: + echo line.string + else: + discard write(fd[1], line.string.cstring, len(line.string)) + discard write(fd[1], "\n".cstring, 1) + else: + code = peekExitCode(p) + if code != -1: break + close(p) + discard close(fd[1]) + return code + proc dropPrivileges*(): bool = if initialUser.isSome: let user = initialUser.unsafeGet -- cgit v1.2.3-70-g09d2