zqqw / pakku

Pacman wrapper with AUR support
GNU General Public License v3.0
39 stars 3 forks source link

Problem with pakku -Sn when built with nim-git #5

Closed zqqw closed 3 years ago

zqqw commented 4 years ago
$ pakku -Sn hexedit
checking official repositories...
(1/1) cloning repositories                                                                           00:00 [################################################################] 100%
error: hexedit: failed to get package info

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.

zqqw commented 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.

zqqw commented 3 years ago

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