cmd.ExtraFiles == nil, yet process inherits FDs?

176 views
Skip to first unread message

Peter Galbavy

unread,
Nov 8, 2024, 5:18:27 AMNov 8
to golang-nuts
I have a command line manager tool that builds the starting environment for a process and calls cmd.Start().

The code sets (doesn't need to as it starts as nil, but does to be extra explicit) cmd.ExtraFiles = nil, but even then I seem to inherit a bunch of FDs from the CLI - I noticed because the oddities belong to VSCode terminal (cmd dump first, just before to call to cmd.Start()):

2024-11-08T10:08:09Z DEBUG: pkg/host/local.go:221 host.(*Local).Start() > cordial: cmd: &exec.Cmd{Path:"/home/peter/geneos/packages/gateway/active_prod/gateway2.linux_64", Args:[]string{"/home/peter/geneos/packages/gateway/active_prod/gateway2.linux_64", "Demo Gateway", "-gateway-name", "Demo Gateway", "-resources-dir", "/home/peter/geneos/packages/gateway/active_prod/resources", "-log", "/home/peter/geneos/gateway/gateways/Demo Gateway/gateway.log", "-setup", "/home/peter/geneos/gateway/gateways/Demo Gateway/gateway.setup.xml", "-licd-host", "localhost", "-ssl-certificate", "/home/peter/geneos/gateway/gateways/Demo Gateway/gateway.pem", "-ssl-certificate-key", "/home/peter/geneos/gateway/gateways/Demo Gateway/gateway.key", "-ssl-certificate-chain", "/home/peter/geneos/gateway/gateways/Demo Gateway/chain.pem", "-licd-secure", "-key-file", "/home/peter/geneos/gateway/gateway_shared/keyfiles/06761949.aes", "-previous-key-file", "/home/peter/geneos/gateway/gateway_shared/keyfiles/740424C5.aes"}, Env:[]string{"TEST2=memory", "TEST=mytest2", "LD_LIBRARY_PATH=/home/peter/geneos/packages/gateway/active_prod/lib64:/usr/lib64"}, Dir:"/home/peter/geneos/gateway/gateways/Demo Gateway", Stdin:io.Reader(nil), Stdout:(*os.File)(0xc000166288), Stderr:(*os.File)(0xc000166288), ExtraFiles:[]*os.File(nil), SysProcAttr:(*syscall.SysProcAttr)(0xc000000840), Process:(*os.Process)(nil), ProcessState:(*os.ProcessState)(nil), ctx:context.Context(nil), Err:error(nil), Cancel:(func() error)(nil), WaitDelay:0, childIOFiles:[]io.Closer(nil), parentIOPipes:[]io.Closer(nil), goroutine:[]func() error(nil), goroutineErr:(<-chan error)(nil), ctxResult:(<-chan exec.ctxResult)(nil), createdByStack:[]uint8(nil), lookPathErr:error(nil), cachedLookExtensions:struct { in string; out string }{in:"", out:""}}
gateway "Demo Gateway" started with PID 55903

FDs 19 and above seem to be inherited:

peter@thinkpad:~/cordial/tools/geneos$ lsof -p 55903
COMMAND     PID  USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
gateway2. 55903 peter  cwd    DIR  259,2     4096 22284207 /home/peter/geneos/gateway/gateways/Demo Gateway
gateway2. 55903 peter  rtd    DIR  259,2     4096        2 /
...
gateway2. 55903 peter   19w   REG  259,2     6014 22857682 /home/peter/.vscode-server/data/logs/20241108T084719/remoteagent.log
gateway2. 55903 peter   20w   REG  259,2     2000 22857683 /home/peter/.vscode-server/data/logs/20241108T084719/ptyhost.log
gateway2. 55903 peter   21u   CHR    5,2      0t0       91 /dev/ptmx
gateway2. 55903 peter   22u   CHR    5,2      0t0       91 /dev/ptmx
gateway2. 55903 peter   23u   CHR    5,2      0t0       91 /dev/ptmx

Am I missing anything?

Code (which is far from clean, sorry) is scattered around https://github.com/ITRS-Group/cordial/blob/main/pkg/host/local.go#L214

Peter Galbavy

unread,
Nov 8, 2024, 5:39:12 AMNov 8
to golang-nuts
Having added this, the child process now does not inherit those FDs:

    // mark all non-std fds unshared
    cmd.ExtraFiles = []*os.File{}

    // un-inherit all FDs explicitly
    fds, err := os.ReadDir("/proc/self/fd")
    if err != nil {
        return
    }
    var intfds []int
    for _, f := range fds {
        if i, err := strconv.Atoi(f.Name()); err == nil {
            intfds = append(intfds, i)
        }
    }

    maxfd := slices.Max(intfds)
    for i := 0; i < maxfd-3; i++ {
        cmd.ExtraFiles = append(cmd.ExtraFiles, nil)
    }

Is this a bug (known or otherwise) or am I just doing the original cmd.Start() wrong?

Peter Galbavy

unread,
Nov 8, 2024, 5:42:49 AMNov 8
to golang-nuts
(yes, the above code is dumb and I have squashed it, to just keep the max fd without a slice ...  it just started out with something else)

Ian Lance Taylor

unread,
Nov 8, 2024, 2:03:43 PMNov 8
to Peter Galbavy, golang-nuts
Do you know what part of your program is opening those file
descriptors? In particular, is it Go code or is it in some other
language like C? If in Go, does it use calls like os.Open or does it
use syscall.Open? I ask because calls like os.Open are careful to set
the O_CLOEXEC flag that causes the descriptor to be closed when
exec-ing a new process. Other languages, or syscall.Open, may not be
so careful.

Ian

Peter Galbavy

unread,
Nov 11, 2024, 3:29:12 AMNov 11
to golang-nuts
They are created by the VS Code terminal session before I run my program. Their open flags are not known to me or under my control, but I guess they don't have close-on-exec set.

I would not that in https://github.com/golang/go/issues/37857 you did say "The status of files that are not listed in ExtraFiles is not unpredictable: those files are closed" but it is not clear in the current comments/docs what should happen if ExtraFiles is nil itself, they just say that that FDs listed are inherited, which in turn implies but not explicitly that others are not inherited.

As mentioned, I've worked around my specific issue by walking all FDs (>2) and setting their ExtraFiles slot to nil, rather then the field itself left as nil.

Ian Lance Taylor

unread,
Nov 11, 2024, 2:34:51 PMNov 11
to Peter Galbavy, golang-nuts
On Mon, Nov 11, 2024 at 12:29 AM Peter Galbavy <pe...@wonderland.org> wrote:
>
> They are created by the VS Code terminal session before I run my program. Their open flags are not known to me or under my control, but I guess they don't have close-on-exec set.
>
> I would not that in https://github.com/golang/go/issues/37857 you did say "The status of files that are not listed in ExtraFiles is not unpredictable: those files are closed" but it is not clear in the current comments/docs what should happen if ExtraFiles is nil itself, they just say that that FDs listed are inherited, which in turn implies but not explicitly that others are not inherited.

Yes, as I said later in the issue that comment was inaccurate.

Ian
> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/bcd6b1f8-23a9-4aaa-bb01-de868bffe6f2n%40googlegroups.com.

Peter Galbavy

unread,
Nov 12, 2024, 2:47:57 AMNov 12
to golang-nuts
Thanks Ian, I missed that bit in my enthusiasm. I'll read the whole issue as it seems to be the same thing and if I have anything new to add I'll do it there (it was still Open I recall).

rgds,
Reply all
Reply to author
Forward
0 new messages