Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

program launcher API for C++?

303 views
Skip to first unread message

Agents Marlow

unread,
May 31, 2012, 3:05:54 PM5/31/12
to
Hello,

Does anyone know of a program launching API in C++? I don't mean
system() or fork(), I mean something that acts as a service, i.e. is
launched by an external launcher that a client program talks to.

I have just discovered that according to POSIX, fork and mult-
threading are not safe to be used together. On MIPS where I am using
uclibc instead of libgc, fork can hang because when thread A forks and
thread B mallocs there is a race condition for the malloc lock. So the
launch will have to be done by some external service. Although this
problem bites me when using uclibc I think it can, in theory, bite
anyone that is using POSIX. A service API could use sockets or some
other form of IPC, I don't mind (e.g DBUS, CORBA...). It does need to
be C++ though (or C at a pinch), java won't be good in this particular
case.

Regards,

Andrew Marlow


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Daniel Krügler

unread,
May 31, 2012, 7:05:54 PM5/31/12
to
Am 31.05.2012 21:05, schrieb Agents Marlow:
> Does anyone know of a program launching API in C++? I don't mean
> system() or fork(), I mean something that acts as a service, i.e. is
> launched by an external launcher that a client program talks to.

Have you looked at

http://www.highscore.de/boost/process/

or

http://www.highscore.de/boost/gsoc2010/

? (Nota bene: I have no personal experience of either of these)

HTH & Greetings from Bremen,

Daniel Krügler

Joshua Maurice

unread,
May 31, 2012, 7:10:53 PM5/31/12
to
On May 31, 12:05 pm, Agents Marlow <marlow.age...@gmail.com> wrote:
> [...]

I'm surprised this off-topic post got through. Perhaps because it was
on-topic enough because it asked for a portable process spawning
solution in C++?

{ Yes. -mod/sk }

[...]

> I have just discovered that according to POSIX, fork and mult-
> threading are not safe to be used together. On MIPS where I am using
> uclibc instead of libgc, fork can hang because when thread A forks and
> thread B mallocs there is a race condition for the malloc lock.

It is simply not true that it's unsafe to spawn a new process from a
threaded program in POSIX. You merely have to be exceptionally careful
about doing it. It is true that the design of POSIX in this regard is
slightly braindead. There are a lot of caveats. I know I'm straying
off-topic here, but let me at least point you in the right direction
and correct some misinformation.

First, try Boost. They've dealt with a lot of this already.

If you want a relevant newsgroup for POSIX programming, I'd suggest
comp.unix.programmer.

Short version for POSIX: To spawn a new process, you need to call
fork(), and call exec() in the new child process soon thereafter. In
the child process between the fork() and exec() calls, you can only
call async-signal safe functions. malloc() is not in the list of async-
signal safe functions, and thus it is not asnyc-signal safe. You can
do all you need to do (for the most part) with only async-signal safe
functions in that time window between fork() and exec() in the child.

POSIX 2008:
http://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html
http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html

Search for "async-signal safe", and read the related sections.
http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html

There's at least one other bug in the POSIX API as well, but that
shouldn't stop you from making a toy program. It's exceedingly
difficult and very non-portable to try and ensure that you don't
inadvertently leak a unintended file descriptor to the new process. I
leave you to google that one.

Agents Marlow

unread,
Jun 1, 2012, 5:18:51 AM6/1/12
to
On Jun 1, 12:05 am, Daniel Kr=FCgler <daniel.krueg...@googlemail.com>
wrote:
> Am 31.05.2012 21:05, schrieb Agents Marlow:
>
> > Does anyone know of a program launching API in C++? I don't mean
> > system() or fork(), I mean something that acts as a service, i.e. is
> > launched by an external launcher that a client program talks to.
>
> Have you looked at
>
> http://www.highscore.de/boost/process/
>
> or
>
> http://www.highscore.de/boost/gsoc2010/

No, I hadn't heard of these. Thank you for drawing my attention to
these libraries (actually just the one library in two places).
Unfortunately it doesn't quite cut it. This process library is not in
boost 1.49.0 so I presume it is just in the Google Summer of Code
project. That project seems to date back to 2006, so I am not sure why/
how it never made it into boost proper (perhaps a boost person can
comment on this). Also it seems to still use fork/exec so any multi-
threaded project that used it with uclibc would still be vunerable to
the malloc lock problem.

-Andrew M.

Joshua Maurice

unread,
Jun 1, 2012, 6:09:15 PM6/1/12
to
Agents Marlow wrote:
> Also it [Boost] seems to still use fork/exec so any multi-
> threaded project that used it with uclibc would still be vunerable to
> the malloc lock problem.

Again, I'll try to keep this off-topic post short - but you're "wrong"
in large part. The only way to spawn a new process in POSIX is fork().
(Which is "usually" followed quickly thereafter by an exec() in the
child.) You are correct that you cannot use malloc in the child before
an exec(). However, you can write the necessary child code without
calling malloc nor any other async-signal unsafe function. You can
safely use fork() + exec() in a multi-threaded process.

Please, google example code, which I'm sure is easy to find.

Wil Evers

unread,
Jun 2, 2012, 1:16:04 PM6/2/12
to
Agents Marlow wrote:

> Unfortunately it doesn't quite cut it. This process library is not in
> boost 1.49.0 so I presume it is just in the Google Summer of Code
> project. That project seems to date back to 2006, so I am not sure why/
> how it never made it into boost proper

For a video about boost.process and its history, see

http://blip.tv/boostcon/boost-process-process-management-in-c-5368233

- Wil

Wil Evers

unread,
Jun 2, 2012, 1:16:52 PM6/2/12
to
Joshua Maurice wrote:

> The only way to spawn a new process in POSIX is fork(). (Which is
> "usually" followed quickly thereafter by an exec() in the child.)
> You are correct that you cannot use malloc in the child before an
> exec(). However, you can write the necessary child code without
> calling malloc nor any other async-signal unsafe function. You can
> safely use fork() + exec() in a multi-threaded process.

Another issue with calling fork() + exec() from a multi-threaded
process is that the child may unexpectedly inherit open file
descriptors. Depending on what other threads are doing, it can be
very hard so ensure that all file descriptors that should not be
inherited have the close-on-exec flag set at the moment one of the
threads calls fork().

- Wil

Mathias Gaunard

unread,
Jun 3, 2012, 2:03:06 AM6/3/12
to
On Jun 2, 12:09 am, Joshua Maurice <joshuamaur...@gmail.com> wrote:
> Agents Marlow wrote:
> > Also it [Boost] seems to still use fork/exec so any multi-
> > threaded project that used it with uclibc would still be vunerable to
> > the malloc lock problem.
>
> Again, I'll try to keep this off-topic post short - but you're "wrong"
> in large part. The only way to spawn a new process in POSIX is fork().

That's not true.
You may also use vfork or posix_spawn (the latter not being available
on many POSIX systems).

Joshua Maurice

unread,
Jun 3, 2012, 2:06:53 AM6/3/12
to
Wil Evers wrote:
> Joshua Maurice wrote:
>
> > The only way to spawn a new process in POSIX is fork(). (Which is
> > "usually" followed quickly thereafter by an exec() in the child.)
> > You are correct that you cannot use malloc in the child before an
> > exec(). However, you can write the necessary child code without
> > calling malloc nor any other async-signal unsafe function. You can
> > safely use fork() + exec() in a multi-threaded process.
>
> Another issue with calling fork() + exec() from a multi-threaded
> process is that the child may unexpectedly inherit open file
> descriptors. Depending on what other threads are doing, it can be
> very hard so ensure that all file descriptors that should not be
> inherited have the close-on-exec flag set at the moment one of the
> threads calls fork().

Indeed. Mentioned it in my first post on the topic.

I've never understood why people would want "inherit on fork" by
default. The process is the default go-to for fault tolerance and
fault isolation, and security. This is just a huge gaping hole that
is /so/ onerous to plug.

Some of the "reliable" but non-portable ways to solve this problem
are: closefrom, /proc/self/fd, F_MAXFD, and some obscure option to
CreateProcess available for Windows Vista or later. This makes sure
you don't accidentally create a process with extra handles, but it
does nothing to help the correctness of "broken" third party libraries
in use. If you use a library that does its own process spawning, you
could try to use the close-on-exec flag and the Windows equivalent,
but you're really hosed if you use two libraries and they're both
broken - one opens a handle in one thread, and another creates a
process to inherit it. You also need the POSIX 2008 O_CLOEXEC flag for
this to actually work.

Also, 2008? Why so late? I don't understand how operating system
design can be so bad... This problem has to have been known for much
longer.

(As a meager attempt to stay on topic): If C++ were to ever have a
standard process spawning library, or even to anyone from Boost here
listening, could we please have the semantics be to create a fresh new
process, and you have to go out of your way to inherit things to
children, instead of the other way around? I'm talking file handles,
signal handlers, the works. Have security and sanity be the default,
not something you have to opt into.

POSIX people don't seem to even acknowledge a problem. For example, in
POSIX 2008, we got "O_CLOEXEC", which IMHO is a poor and inadequate
solution on its own. It's better than nothing - at least if you code
super carefully, a broken third party library can't accidentally leak
your handles. In one regard, the win32 API is far superior - it allows
process spawning while inheriting only the handles in a specific list.
However, as far as I can tell barely documented at all.

With the current state of affairs, the most plausible fix for the
problem seems to be both O_CLOEXEC and some equivalent of closefrom -
and super careful coding.

PS: Fun fact: I actually misused the win32 CreateProcess option
because of the poor documentation, and got a blue screen. (After a
hard reset, I took another guess and it worked.) Apparently it doesn't
get much use - probably again because you have to opt-in to have
security and be leak-free, which reeks IMO of bad design.

</rant>

Wil Evers

unread,
Jun 3, 2012, 7:54:36 PM6/3/12
to
Joshua Maurice wrote:
> Wil Evers wrote:
>> Joshua Maurice wrote:
>>
>> > The only way to spawn a new process in POSIX is fork(). (Which
>> > is "usually" followed quickly thereafter by an exec() in the
>> > child.) You are correct that you cannot use malloc in the child
>> > before an exec(). However, you can write the necessary child code
>> > without calling malloc nor any other async-signal unsafe
>> > function. You can safely use fork() + exec() in a multi-threaded
>> > process.
>>
>> Another issue with calling fork() + exec() from a multi-threaded
>> process is that the child may unexpectedly inherit open file
>> descriptors. Depending on what other threads are doing, it can be
>> very hard so ensure that all file descriptors that should not be
>> inherited have the close-on-exec flag set at the moment one of the
>> threads calls fork().
>
> Indeed. Mentioned it in my first post on the topic.

You did. I reacted to your "you can safely use fork() + exec() in a
multithreaded process" without remembering your previous posting; I'm
sorry about that. On the other hand, this issue is non-obvious and
real, developers should be aware of it, so it doesn't really hurt to
mention it twice :-).

[snip]

> (As a meager attempt to stay on topic): If C++ were to ever have a
> standard process spawning library, or even to anyone from Boost here
> listening, could we please have the semantics be to create a fresh
> new process, and you have to go out of your way to inherit things to
> children, instead of the other way around?

Well, I can certainly see why it makes sense to inherit (or redirect)
the standard input, output and error channels. The thing I don't
understand is why any other descriptor should be inherited. How is an
exec'ed()/CreatedProcess()'ed program even supposed to be aware of any
other descriptor? It seems to me that implicitly passing on any handle
other than a standard one is just asking for trouble.

Regards,

- Wil

Jens Schmidt

unread,
Jun 3, 2012, 11:46:40 PM6/3/12
to
Wil Evers wrote:

> Well, I can certainly see why it makes sense to inherit (or redirect)
> the standard input, output and error channels. The thing I don't
> understand is why any other descriptor should be inherited. How is an
> exec'ed()/CreatedProcess()'ed program even supposed to be aware of any
> other descriptor? It seems to me that implicitly passing on any handle
> other than a standard one is just asking for trouble.

Unnamed pipes are a convenient method for passing data securely between
the processes. Other methods like parameters, environment variables, or
files can be observed by other processes.

This is e.g. used by programs asking for passwords in GUI environments.
Here all the security implications (often these are open questions) of
using a GUI are removed from the parent process.
--
Greetings,
Jens Schmidt

Joshua Maurice

unread,
Jun 4, 2012, 3:30:49 AM6/4/12
to
Mathias Gaunard wrote:
> On Jun 2, 12:09 am, Joshua Maurice <joshuamaur...@gmail.com> wrote:
> >
> > Again, I'll try to keep this off-topic post short - but you're "wrong"
> > in large part. The only way to spawn a new process in POSIX is fork().
>
> That's not true.
> You may also use vfork or posix_spawn (the latter not being available
> on many POSIX systems).

I stand corrected. Pedantically yes, but for the purposes of this
thread they have the same API bugs. The other two have their semantics
defined "as if by fork" with a couple of minor (but notable)
exceptions, and thus they also have the leak handle leak problem.
(However, posix_spawn does remove some of the silly overcommit memory
problems. This isn't its design purpose, but an intimately related
purpose.)

And yes, some people say "but it's much less powerful - there's so
many nifty things you can do with fork + exec that you can't do with
posix_spawn". Well, AFAIK, that's simply untrue. You can create a
separate executable with all of the prep code that you would normally
write inline with fork, and you can exec twice - once for your prep
executable, and another exec to load the "actual" executable. More
annoying? Sure. Cannot be done without fork? No.

George Neuner

unread,
Jun 4, 2012, 9:54:07 PM6/4/12
to
On Sat, 2 Jun 2012 23:06:53 -0700 (PDT), Joshua Maurice
<joshua...@gmail.com> wrote:


>I've never understood why people would want "inherit on fork" by
>default. The process is the default go-to for fault tolerance and
>fault isolation, and security. This is just a huge gaping hole that
>is /so/ onerous to plug.

I agree that inheritance should not be default, but historically the
reason is obvious - forking a new process was the only way to
multiprogram an application, and inheriting resources allowed the
independent parts to communicate.

Fork() was most interesting because the child could continue executing
the parent's code from the point where the fork occurred. This is a
useful feature even if 99.44% of the time the child process intends to
immediately execute a new program. CreateProcess always starts the
program from the beginning ... although technically possible, it is
very cumbersome to clone a Windows process and achieve a Unix-like
fork.


>Some of the "reliable" but non-portable ways to solve this problem
>are: closefrom, /proc/self/fd, F_MAXFD, and some obscure option to
>CreateProcess available for Windows Vista or later.
> :
>In one regard, the win32 API is far superior [to POSIX] - it allows
>process spawning while inheriting only the handles in a specific list.
>However, as far as I can tell barely documented at all.

Not exactly.

The Win32 API has had process handle inheritance control right from
the beginning, and it always has been documented. I'll certainly
agree that the online help provided with the compilers could have been
better (though it is all there), but there have been numerous books
that covered these security issues thoroughly.

There is no way to provide to CreateProcess a list of particular
parent handles to be inherited by the child. Inheritence is
controlled indirectly by the security attributes of the individual
open handles.

The SECURITY_ATTRIBUTES (SA) structure contains the boolean flag
"bInheritHandle" which determines whether a handle created with the
attributes can be inherited. Any Create/CreateEx call that takes a
SECURITY_ATTRIBUTES structure can permit or restrict inheritance of
the resulting open handle.

CreateProcess has a boolean argument "bInheritHandles" which is a
blanket restriction on inheritance. When set to false it prohibits
any handles from being duplicated in the child. When set to true,
only those parent handles whose security attributes permit inheritance
are duplicated in the child.

Unfortunately, most resource handles can be inherited by default. You
have to deliberately choose to restrict them - either by prohibiting
all inheritance in CreateProcess or by setting inheritance attributes
on a per-handle basis. It definitely would be easier if you *could*
directly provide a list of handles to CreateProcess.


Windows is not POSIX compliant[*] and certain POSIX semantics simply
can't be supported. YMMV, but when developing serious applications on
Windows, IMO it is better to use the native API as it was intended
rather than trust in the interpretation of a quasi-POSIX C/C++
library.

[*] AFAIHS, there is no system that is fully POSIX compliant.
Moreover, many of POSIX's semantics are unclear - some particularly
troublesome issues have been discussed at length in comp.arch (where
it seems nothing computer related actually is off topic 8-)


>PS: Fun fact: I actually misused the win32 CreateProcess option
>because of the poor documentation, and got a blue screen. (After a
>hard reset, I took another guess and it worked.) Apparently it doesn't
>get much use - probably again because you have to opt-in to have
>security and be leak-free, which reeks IMO of bad design.

Never had CreateProcess itself blue screen, but I can easily see the
child process blowing up due to a broken environment.

George

Joshua Maurice

unread,
Jun 5, 2012, 5:44:00 AM6/5/12
to
George Neuner wrote:
> There is no way to provide to CreateProcess a list of particular
> parent handles to be inherited by the child. Inheritence is
> controlled indirectly by the security attributes of the individual
> open handles.

[Oh, how off topic we've gone. Hopefully this will be the last.]
{ Agreed. -mod/sk }
As I was saying, there is this obscure barely documented option which
allows you to do just that, for Windows Vista and higher. (This is
also how I got a blue screen when I misused the API.) I would merely
link to the msdn pages, but "obscure barely documented", so here's
some example code. (I reworked it and snipped it for demonstration
purposes - which is my excuse if I got it wrong.) I have tested that
it actually inherits only the specified handles, even if there are
other inheritable handles.

char * memoryBackingAttributeList = 0;
PPROC_THREAD_ATTRIBUTE_LIST attribList;
{
SIZE_T requiredSize = 0;
InitializeProcThreadAttributeList(0, 1, 0, & requiredSize);
memoryBackingAttributeList = new unsigned char[requiredSize];
attribList =
reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(memoryBackingAttributeList);
if ( ! InitializeProcThreadAttributeList(attribList, 1, 0, &
requiredSize))
FATAL(0, 0);
}

HANDLE handlesList = new HANDLE[numInheritedHandles];
{
int x = 0;
handlesList[x++] = inHandle;
handlesList[x++] = outHandle;
handlesList[x++] = errHandle;
if ( ! UpdateProcThreadAttribute(attribList, 0,
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
handlesList, sizeof(HANDLE) * numInheritedHandles,
0, 0))
{ FATAL(0, 0);
}
}

STARTUPINFOEX startUpInfoEx;
{
ZeroMemory(&startUpInfoEx, sizeof(STARTUPINFOEX));
startUpInfoEx.StartupInfo.cb = sizeof(STARTUPINFOEX);
startUpInfoEx.lpAttributeList = attribList;
startUpInfoEx.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
startUpInfoEx.StartupInfo.hStdInput = inHandle;
startUpInfoEx.StartupInfo.hStdOutput = outHandle;
startUpInfoEx.StartupInfo.hStdError = errHandle;
}

//...

BOOL const createProcessResult = CreateProcess(
exeAbsPath,
processArguments.t,
0, //security attributes, use default
0, //thread attributes, use default
inheritHandlesFlag,
EXTENDED_STARTUPINFO_PRESENT,
m_hasCustomEnv ? envWrapper.env : 0, //0 is default env
m_dir.c_str(), //working dir for new process
& startUpInfoEx.StartupInfo,
& processInformation
);

CreateProcess
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx

STARTUPINFOEX
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686329%28v=vs.85%29.aspx

InitializeProcThreadAttributeList
http://msdn.microsoft.com/en-us/library/windows/desktop/ms683481%28v=vs.85%29.aspx

UpdateProcThreadAttribute
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686880%28v=vs.85%29.aspx
0 new messages