StartProcess and prctl(PR_SET_PDEATHSIG) on Linux

1,684 views
Skip to first unread message

Albert Strasheim

unread,
Mar 1, 2011, 12:45:27 AM3/1/11
to golang-dev
Hello all

We are using os.StartProcess on Linux to start a bunch of worker
processes from a Go program.

Some of the worker processes are also Go programs, but others might be
third party binaries.

It would be useful if these workers were automatically killed if the
master process dies abruptly (SIGKILL and the like).

It seems POSIX doesn't define a standard way to achieve this, but
Linux seems to have a workable solution:

http://stackoverflow.com/questions/284325/how-to-make-child-process-die-after-parent-exits

And the right way to use it:

http://www.win.tue.nl/~aeb/linux/lk/lk-5.html#ss5.8

We can already use this system call without help from StartProcess if
we are starting Go worker processes, but it would be quite useful to
have a general solution whereby StartProcess takes care of calling
prctl(PR_SET_PDEATHSIG) before calling exec, similar to the ptrace
code that is already there in exec_unix.go.

However, one would probably want to achieve this without increasing
the size of the StartProcess parameter list, especially if the
functionality is only supported on certain operating systems.

Thoughts?

Regards

Albert

Ian Lance Taylor

unread,
Mar 1, 2011, 10:21:07 AM3/1/11
to Albert Strasheim, golang-dev
Albert Strasheim <ful...@gmail.com> writes:

> It would be useful if these workers were automatically killed if the
> master process dies abruptly (SIGKILL and the like).

If you make your master process be a session leader (i.e., call setsid)
then when it dies all the other processes in the session will get
SIGHUP. However, that doesn't help much with Go since there is no very
good way for a Go program to call setsid correctly.

> We can already use this system call without help from StartProcess if
> we are starting Go worker processes, but it would be quite useful to
> have a general solution whereby StartProcess takes care of calling
> prctl(PR_SET_PDEATHSIG) before calling exec, similar to the ptrace
> code that is already there in exec_unix.go.
>
> However, one would probably want to achieve this without increasing
> the size of the StartProcess parameter list, especially if the
> functionality is only supported on certain operating systems.

The Go library is missing a general approach to daemonizing processes.
Perhaps this could be handled by whatever mechanism we adopt there.

One perhaps over-general approach would be to pass a function which runs
in the child context after the fork but before the exec. That function
could daemonize the process, change process priority, change signal
handling, etc. That is a Unix-specific concept, though, and as such
should probably not be in os.StartProcess itself. Of course
os.StartProcess could simply call some operating system specific
function.

Ian

roger peppe

unread,
Mar 3, 2011, 8:23:31 AM3/3/11
to Ian Lance Taylor, Albert Strasheim, golang-dev
On 1 March 2011 15:21, Ian Lance Taylor <ia...@google.com> wrote:
> One perhaps over-general approach would be to pass a function which runs
> in the child context after the fork but before the exec.  That function
> could daemonize the process, change process priority, change signal
> handling, etc.  That is a Unix-specific concept, though, and as such
> should probably not be in os.StartProcess itself.  Of course
> os.StartProcess could simply call some operating system specific
> function.

the problem with passing an arbitrary function is that the function
can have arbitrary side-effects, including memory allocation and file
opening, which will break things.

i wonder if we can't take a tip from haskell's monads, and have
an object that, *when applied* results in the process changes taking
place before the child process is run.

it might be implemented as a function pointer, but a function pointer
that can only run a certain set of primitives. an alternative could be
a symbolic representation of the available operations.

ProcAttrs would be defined in syscall and redefined in os with
only the portable methods.

for example:

type ProcAttrs struct {
setup func(*procAttrs) os.Error
}

func StartProcess(name string, argv []string, attrs *ProcAttrs) (p
*Process, err Error) {
... fork
if p.setup != nil {
err = p.setup()
if err != nil {
return nil, err
}
}
... exec
}

func (p *ProcAttrs) addFunc(f func(*procAttrs) os.Error) {
setup := p.setup
p.setup = func(attrs *procAttrs) (err os.Error) {
if exec != nil {
err = exec(attrs)
}
if err == nil {
err = f(attrs)
}
return err
}
}

func (p *ProcAttrs) SetSid() {
p.addFunc(func(attrs *procAttrs) (err os.Error) {
_, _, err = Syscall(SETSID)
return
})
}

func (p *ProcAttrs) Close(fd int) {
p.addFunc(func(attrs *procAttrs) os.Error {
return Close(fd)
})
}

func (p *ProcAttrs) Dup(fd1, fd2 int) {
p.addFunc(func(attrs *procAttrs) os.Error {
return Dup(fd1, fd2)
})
}

func (p *ProcAttrs) Chdir(dir string) {
p.addFunc(func(attrs *procAttrs) os.Error {
return Chdir(dir)
})
}

func (p *ProcAttrs) SetEnv(attr, val string) {...}
etc

roger peppe

unread,
Mar 3, 2011, 8:51:18 AM3/3/11
to Ian Lance Taylor, Albert Strasheim, golang-dev
On 3 March 2011 13:23, roger peppe <rogp...@gmail.com> wrote:
> it might be implemented as a function pointer, but a function pointer
> that can only run a certain set of primitives. an alternative could be
> a symbolic representation of the available operations.

of course, given that we can't safely call any functions in
forkExec, the symbolic representation is the only option.

the API could remain the same though.

Ian Lance Taylor

unread,
Mar 3, 2011, 1:26:42 PM3/3/11
to roger peppe, Albert Strasheim, golang-dev
roger peppe <rogp...@gmail.com> writes:

> On 1 March 2011 15:21, Ian Lance Taylor <ia...@google.com> wrote:
>> One perhaps over-general approach would be to pass a function which runs
>> in the child context after the fork but before the exec.  That function
>> could daemonize the process, change process priority, change signal
>> handling, etc.  That is a Unix-specific concept, though, and as such
>> should probably not be in os.StartProcess itself.  Of course
>> os.StartProcess could simply call some operating system specific
>> function.
>
> the problem with passing an arbitrary function is that the function
> can have arbitrary side-effects, including memory allocation and file
> opening, which will break things.

I agree that having arbitrary side-effects is troubling and probably
enough to doom the idea. However, the function would run between the
fork and the exec, so I think there is a relatively limited number of
things it could break. Neither memory allocation nor file opening seem
like a real problem to me.

> ProcAttrs would be defined in syscall and redefined in os with
> only the portable methods.

That seems like a reasonable approach to me.

Ian

Adam Langley

unread,
Mar 3, 2011, 1:31:27 PM3/3/11
to Ian Lance Taylor, roger peppe, Albert Strasheim, golang-dev
On Thu, Mar 3, 2011 at 1:26 PM, Ian Lance Taylor <ia...@google.com> wrote:
> I agree that having arbitrary side-effects is troubling and probably
> enough to doom the idea.  However, the function would run between the
> fork and the exec, so I think there is a relatively limited number of
> things it could break.  Neither memory allocation nor file opening seem
> like a real problem to me.

The typical issue here is that, at the point of forking, another
thread in the parent process can be holding a lock (say a lock in the
memory allocation code). Now, in the child process and code which
tries to allocate memory will try the grab the lock and deadlock
forever.


AGL

roger peppe

unread,
Mar 3, 2011, 1:34:12 PM3/3/11
to Ian Lance Taylor, Albert Strasheim, golang-dev
On 3 March 2011 18:26, Ian Lance Taylor <ia...@google.com> wrote:
>> ProcAttrs would be defined in syscall and redefined in os with
>> only the portable methods.
>
> That seems like a reasonable approach to me.

BTW i tried it and it seems to work ok.
i'll produce a CL if others think it's a good idea.

Adam Langley

unread,
Mar 3, 2011, 1:48:37 PM3/3/11
to roger peppe, Ian Lance Taylor, Albert Strasheim, golang-dev
On Thu, Mar 3, 2011 at 1:34 PM, roger peppe <rogp...@gmail.com> wrote:
> BTW i tried it and it seems to work ok.
> i'll produce a CL if others think it's a good idea.

I don't think that we can run any Go code between fork and exec. The
compiler is free to lift values to the heap at any time and that risks
deadlocking if a thread in the parent process had the memory lock. We
could write code that shouldn't cause values to be lifted, but that
strikes me as leaving landmines.

I think all code between fork and exec needs to be C code, which means
describing any operations in a structure which it can process.

And even fd mapping is complex. See [1] and [2] for the code that
Chrome uses. It's probably over-engineered, but it's never failed
which is more than can be said for the previous couple of attempts,
which resulted in nasty bugs.


Cheers

AGL


[1] http://src.chromium.org/viewvc/chrome/trunk/src/base/file_descriptor_shuffle.h?view=markup
[2] http://src.chromium.org/viewvc/chrome/trunk/src/base/file_descriptor_shuffle.cc?view=markup

roger peppe

unread,
Mar 3, 2011, 2:07:35 PM3/3/11
to Adam Langley, Ian Lance Taylor, Albert Strasheim, golang-dev
On 3 March 2011 18:48, Adam Langley <a...@golang.org> wrote:
> On Thu, Mar 3, 2011 at 1:34 PM, roger peppe <rogp...@gmail.com> wrote:
>> BTW i tried it and it seems to work ok.
>> i'll produce a CL if others think it's a good idea.
>
> I don't think that we can run any Go code between fork and exec.

yes. my function-based suggestion was a red herring.

> I think all code between fork and exec needs to be C code, which means
> describing any operations in a structure which it can process.

yes. this doesn't mean that my suggested API won't work though.
it just changes the intermediate format.

in my little trial, the ProcAttrs structure was defined as:

type ProcAttributes struct {
setsid bool
traceme bool
dir *byte
}

more stuff could be added as needed, including sequenced operations
if desired.

thanks for the response, BTW.

Adam Langley

unread,
Mar 3, 2011, 2:10:00 PM3/3/11
to roger peppe, Ian Lance Taylor, Albert Strasheim, golang-dev
On Thu, Mar 3, 2011 at 2:07 PM, roger peppe <rogp...@gmail.com> wrote:
> type ProcAttributes struct {
>        setsid bool
>        traceme bool
>        dir *byte
> }

With clearer names I think that this is probably the best approach.


AGL

Russ Cox

unread,
Mar 3, 2011, 2:20:22 PM3/3/11
to Adam Langley, roger peppe, Ian Lance Taylor, Albert Strasheim, golang-dev
> I don't think that we can run any Go code between fork and exec. The
> compiler is free to lift values to the heap at any time and that risks
> deadlocking if a thread in the parent process had the memory lock. We
> could write code that shouldn't cause values to be lifted, but that
> strikes me as leaving landmines.
>
> I think all code between fork and exec needs to be C code, which means
> describing any operations in a structure which it can process.

everything you said is true except it needing to be C code.
this is code in the base library, which is already tied in other
ways to the specific compiler being used (gccgo has a
different package os). so it's okay for this to be written
in (careful) Go, as it is now. we just can't execute arbitrary Go.

russ

Reply all
Reply to author
Forward
0 new messages