Closed zqqw closed 3 years ago
#[
Getting closer now to this, if anyone was wondering if anything was happening, and to share the ongoing progress
- replacing posix fork and exec with osproc exec and threads -
blocking signals and fitting in place still to do. This could simplify what was there and fix the Sn problem.
]#
import os, strtabs, options, posix, sequtils, strutils, sugar, osproc, streams
type
User* = tuple[
name: string,
uid: int,
gid: int,
groups: seq[int],
home: string,
shell: string
]
execChan = object
val: string
ecode: int
ThreadData = tuple[cmd: string, wd: string, args: seq[string], user: User]
var chan: Channel[execChan]
proc getgrouplist*(user: cstring, group: Gid, groups: ptr cint, ngroups: var cint): cint
{.importc, header: "<grp.h>".}
proc setgroups*(size: csize_t, groups: ptr cint): cint
{.importc, header: "<grp.h>".}
proc getUser(uid: int): User =
var pw = getpwuid(Uid(uid))
if pw == nil:
raise newException(CatchableError, "")
var groups: array[100, cint]
var ngroups: cint = 100
if getgrouplist(pw.pw_name, pw.pw_gid, addr(groups[0]), ngroups) < 0:
raise newException(CatchableError, "")
else:
let groupsSeq = groups[0 .. ngroups - 1].map(x => x.int)
let res = ($pw.pw_name, pw.pw_uid.int, pw.pw_gid.int, groupsSeq,
$pw.pw_dir, $pw.pw_shell)
return res
let currentUser* = getUser(getuid().int)
let initialUser* = try:
let sudoUid = getEnv("SUDO_UID")
let polkitUid = getEnv("PKEXEC_UID")
let uidString = if sudoUid.len > 0:
some(sudoUid)
elif polkitUid.len > 0:
some(polkitUid)
else:
none(string)
let uid = uidString.get.parseInt
if uid == 0 or currentUser.uid != 0: none(User) else: some(getUser(uid))
except:
none(User)
proc firstWorker(cmdInfo: ThreadData) =
var
tmp: tuple[output : string, exitCode : int]
chval: execChan
p: owned(Process)
envs: StringTableRef = nil
envs = newStringTable(modeCaseSensitive)
if cmdInfo.user.name != "":
var groups = cmdInfo.user.groups.map(x => x.cint)
if setgroups(cast[csize_t](cmdInfo.user.groups.len), addr(groups[0])) < 0:
envs = nil
if setgid((Gid) cmdInfo.user.gid) != 0:
envs = nil
if setuid((Uid) cmdInfo.user.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"]: # these don't exist with sudo so they are not set?
envs[key] = cmdInfo.user.name
continue
if key in ["HOME"]:
envs[key] = cmdInfo.user.home
continue
if key in ["SHELL"]:
envs[key] = cmdInfo.user.shell
continue
envs[key] = value
if envs == nil:
chval.val = "Error: failed to drop privileges"
chval.ecode = -12345
chan.send(chval)
return
try:
p = startProcess(command = cmdInfo.cmd, args = cmdInfo.args, env = envs, workingDir = cmdInfo.wd, options = {poStdErrToStdOut, poUsePath})
except:
chval.val = "Error: " & getCurrentExceptionMsg()
chval.ecode = -12345
chan.send(chval)
return
var outp = outputStream(p)
close inputStream(p)
tmp = ("", -1)
var line = newStringOfCap(120).TaintedString
while true:
if outp.readLine(line):
tmp[0].add(line.string)
tmp[0].add("\n")
else:
tmp[1] = peekExitCode(p)
if tmp[1] != -1: break
close(p)
chval.val = tmp.output
chval.ecode = tmp.exitCode
chan.send(chval)
chan.open()
var
worker1: Thread[ThreadData]
argSeq: seq[string]
recvVal: execChan
dpriv1: bool = true
iUser: User
argSeq.add("--printsrcinfo")
if dpriv1 == true:
if initialUser.isSome:
iUser = initialUser.unsafeGet
else:
echo "Houston we have a problem"
createThread[ThreadData](worker1, firstWorker, (cmd: "makepkg", wd: "/home/me/projects/nim/exec", args: argSeq, user: iUser))
recvVal = chan.recv()
echo recvVal.val
echo recvVal.ecode
worker1.joinThread()
chan.close()
# original code in Pakku blocks SIGINT SIGTERM
But this turned out to be no good at all, as threads don't have the same process separation as a fork so blocking and unblocking signals in the thread also affected the parent.
Closed by https://github.com/zqqw/pakku/commit/0601052833bfb97cc178cdbd7aa150ecec4c7e5b This builds and works (afaict) with the current Nim 1.4 and the latest 1.5 Nim git
It works with 1.2.6. let srcInfo = obtainSrcInfo(repoPath.unsafeGet & "/" & git.path) is returning nothing in src/common.nim execResult(makePkgCmd, "--printsrcinfo") in obtainSrcInfo where makePkgCmd is "/usr/bin/makepkg" doesn't seem to return anything although there is a PKGBUILD in the dir at that point. execResult itself seems to work OK. The forks, exec, and redirection make it tricky to find out what happens. I think I have the answer now - it will just take a little while to get it sorted and pushed here.