Unix Programming - Starting an external process from C++

This is Interesting: Free IT Magazines  
Home > Archive > Unix Programming > October 2006 > Starting an external process from C++





You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

Author Starting an external process from C++
Henrik Goldman

2006-10-27, 7:19 am

Hi,

I'm writing some test code and I would like to startup an additional
application (in background) and run some communication tests together with
it.
I'd like the code to work under a number of platforms including aix, hp-ux,
macosx, solaris and linux.
For starters I'm just trying to get it working reliably under linux though.
Currently I'm on a older Redhat with 2.6.9 kernel and gcc 3.3.

The problem is that even though it works using the method shown below the
process does not get killed cleanly (which I can observe by it's own
logfile). When running a new test it can fail because it says that the
process cannot startup properly (due to conflicting lock files):

bool CProcess::Start()

{

FILE *fp;

char szOutput[4096];

m_nPID = fork();

if (m_nPID == -1)

return false;

if (m_nPID == 0)

{

if ((fp = popen((char *) m_strCommand.c_str(), "r")) == NULL)

{

exit(1);

}

while (fgets(szOutput, sizeof(szOutput)-1, fp) != NULL)

{

}

pclose(fp);

exit(0);

}

// Wait on unix for the process to startup

sleep(5);

m_bStarted = true;

return true;

}

void CProcess::Stop()

{

if (m_bStarted)

{

kill(m_nPID, SIGKILL);

waitpid(m_PID, NULL, 0);

m_bStarted = false;

}

}



I've been trying to look into execl() but I find the parameters associated
quite clumsy and I failed to get the process started. It's located in a
relative path like "../../out/somewherre/app".



Thanks in advance.

-- Henrik


Thomas Maier-Komor

2006-10-27, 7:19 am

Henrik Goldman schrieb:
> Hi,
>
> I'm writing some test code and I would like to startup an additional
> application (in background) and run some communication tests together with
> it.
> I'd like the code to work under a number of platforms including aix, hp-ux,
> macosx, solaris and linux.
> For starters I'm just trying to get it working reliably under linux though.
> Currently I'm on a older Redhat with 2.6.9 kernel and gcc 3.3.
>
> The problem is that even though it works using the method shown below the
> process does not get killed cleanly (which I can observe by it's own
> logfile). When running a new test it can fail because it says that the
> process cannot startup properly (due to conflicting lock files):
>
> bool CProcess::Start()
>
> {
>
> FILE *fp;
>
> char szOutput[4096];
>
> m_nPID = fork();
>
> if (m_nPID == -1)
>
> return false;
>
> if (m_nPID == 0)
>
> {
>
> if ((fp = popen((char *) m_strCommand.c_str(), "r")) == NULL)
>
> {
>
> exit(1);
>
> }
>
> while (fgets(szOutput, sizeof(szOutput)-1, fp) != NULL)
>
> {
>
> }
>
> pclose(fp);
>
> exit(0);
>
> }
>
> // Wait on unix for the process to startup
>
> sleep(5);
>
> m_bStarted = true;
>
> return true;
>
> }
>
> void CProcess::Stop()
>
> {
>
> if (m_bStarted)
>
> {
>
> kill(m_nPID, SIGKILL);
>
> waitpid(m_PID, NULL, 0);
>
> m_bStarted = false;
>
> }
>
> }
>
>
>
> I've been trying to look into execl() but I find the parameters associated
> quite clumsy and I failed to get the process started. It's located in a
> relative path like "../../out/somewherre/app".
>
>
>
> Thanks in advance.
>
> -- Henrik
>
>


If you are using popen to create a child process, then there is no need
to fork(). To successfully create a child process, you have three
alternatives:
- fork()/exec() and its alternative - i.e. vfork, execve, etc.
- popen
- posix_spawn

If popen satisfies your needs, then it is the way to go. fork/exec will
work almost everywhere, and posix_spawn. For examples and more
information concerning this topic, I'd recommend you to take a look into
APUE2e (Advanced programming in the UNIX Environment, 2nd edition).

HTH,
Tom
Jens Thoms Toerring

2006-10-27, 7:19 am

Henrik Goldman <henrik_goldman@mail.tele.dk> wrote:
> I'm writing some test code and I would like to startup an additional
> application (in background) and run some communication tests together with
> it.
>
> The problem is that even though it works using the method shown below the
> process does not get killed cleanly (which I can observe by it's own
> logfile). When running a new test it can fail because it says that the
> process cannot startup properly (due to conflicting lock files):


(Code re-indented to make it a bit more readable.)

> bool CProcess::Start()
> {
> FILE *fp;
> char szOutput[4096];
>
> m_nPID = fork();
>
> if (m_nPID == -1)
> return false;
>
> if (m_nPID == 0)
> {
> if ((fp = popen((char *) m_strCommand.c_str(), "r")) == NULL)
> {
> exit(1);
> }
>
> while (fgets(szOutput, sizeof(szOutput)-1, fp) != NULL)
> {
> }
>
> pclose(fp);
> exit(0);
> }
>
> // Wait on unix for the process to startup
>
> sleep(5);
> m_bStarted = true;
>
> return true;
> }
>
> void CProcess::Stop()
> {
> if (m_bStarted)
> {
> kill(m_nPID, SIGKILL);
> waitpid(m_PID, NULL, 0);


What's the difference between 'm_nPID' and 'M_PID' or is this a typo?

> m_bStarted = false;
> }
> }


This is all bit strange: you create two new processes, but you're
talking only about "starting an external application in the background".
Moreover, the term "in the background" makes only sense in the
context of a shell. Finally, it's also unclear what you mean by
"not get killed cleanly". Dead is dead, so what's the difference
between getting killed cleanly and uncleanly?

From your choice of the variable and function names it looks as if
what you consider the "new process" to be the one you create via the
fork() call, But this is, of course, not "an external application",
but just a copy of the already running process. So, to get things a
bit clearer let's give the processes some names: A for the primary
process, A' is the one started via the call of fork() (because it's
just a copy of the initial process A) and, finally, let's say B is
the process started by popen().

What you do is first creating process A', which in turn tries to
start an "external application", thereby creating process B. In
process A' you now try to read strings written by process B to its
standard output. You are prepared to stop reading and have process
A' exit in case that process B exits (or closes its standard output).

Meanwhile process A sleeps a bit and then (but here I can only guess
since you don't show what's really happening) tries to kill its child,
process A' (is SIGKILL really necessary, won't e.g. SIGTERM also do?).
There are now the following scenarios:

a) A' has already quit, so trying to kill it doesn't do anything.
b) A' (and thus probably B) are still running. By killing A' the
process B loses its standard output and probably dies due to
a SIGPIPE signal (again, this is just guesswork since I have no
idea what actually happens in that "external application").

Perhaps what you see is scenario b) and the "not get killed cleanly"
bit you are complaining about is the process B not being able to
cope with a SIGPIPE signal and thus not shutting down in the way you
expect it to (but then, if it would receive a SIGKILL signal it also
would be dead immediately, not being able to do any cleanup).

> I've been trying to look into execl() but I find the parameters associated
> quite clumsy and I failed to get the process started. It's located in a
> relative path like "../../out/somewherre/app".


Perhaps you should explain a bit more clearly what exactly you're
trying to do. Do you really want to create two extra process? And
what do you consider being "killed cleanly"? Moreover, what are
your problems with the execl() function? Do you understand that
popen() is just a wrapper function for setting up a pipe, calling
fork(), readjusting standard input or output in the child and then
doing an exec(), so you don't have to do all that yourself (but,
of course, losing some flexibility in the process)?

Regards, Jens
--
\ Jens Thoms Toerring ___ jt@toerring.de
\__________________________ http://toerring.de
Henrik Goldman

2006-10-27, 7:19 am


> If popen satisfies your needs, then it is the way to go. fork/exec will
> work almost everywhere, and posix_spawn. For examples and more
> information concerning this topic, I'd recommend you to take a look into
> APUE2e (Advanced programming in the UNIX Environment, 2nd edition).
>


Thanks for your suggestions. The only problem about the popen aproach is
that I loose the pid of the child process. The child is really a daemon so I
need to send a command to it to make it shutdown. Otherwise it would hang
around forever.

Is there a way to signal to the opened process?

Thanks.

-- Henrik


Henrik Goldman

2006-10-27, 7:19 am

waitpid(m_PID, NULL, 0);
>
> What's the difference between 'm_nPID' and 'M_PID' or is this a typo?


Yes, a typo.

> This is all bit strange: you create two new processes, but you're
> talking only about "starting an external application in the background".
> Moreover, the term "in the background" makes only sense in the
> context of a shell. Finally, it's also unclear what you mean by
> "not get killed cleanly". Dead is dead, so what's the difference
> between getting killed cleanly and uncleanly?


Whats with the unfriendly "i am smarter then you" attitude? If it's such a
pain for you to help other people then perhaps you should not do that
anymore.

Let me try to describe what I want so I ensure that YOU understand it:

I would like to spawn an external application written by me as well. This
application is a daemon which can be communicated with and writes a logfile.

My plan is to start up this 2nd application as a child process, communicate
with it to do some tests and then shut it down again.
It needs to be shutdown by sending a "kill" signal or similar so the
application will invoke it's own signal handler and shutdown gracefully.
It's important so other tests won't fail. If it gets shutdown ungracefully
it might take a few seconds for the OS to cleanup resources (based on
experience) and this can make other code fail.
As for background it means that I don't see anything on screen. This is
because I don't want to clutter up the screen with 2 applications at once.
So even though the 2nd application writes to stdout I don't want to see it.
Do you understand now?

>
> What you do is first creating process A', which in turn tries to
> start an "external application", thereby creating process B. In
> process A' you now try to read strings written by process B to its
> standard output. You are prepared to stop reading and have process
> A' exit in case that process B exits (or closes its standard output).


Correct.

> Meanwhile process A sleeps a bit and then (but here I can only guess
> since you don't show what's really happening) tries to kill its child,
> process A' (is SIGKILL really necessary, won't e.g. SIGTERM also do?).
> There are now the following scenarios:


A communicates with B but what A does it not relevant except that when A
requests B to stop then it should do so.
SIGTERM is probably fine but it didn't work on B.

> Perhaps you should explain a bit more clearly what exactly you're
> trying to do. Do you really want to create two extra process? And
> what do you consider being "killed cleanly"? Moreover, what are
> your problems with the execl() function? Do you understand that
> popen() is just a wrapper function for setting up a pipe, calling
> fork(), readjusting standard input or output in the child and then
> doing an exec(), so you don't have to do all that yourself (but,
> of course, losing some flexibility in the process)?


As I found out popen will do ok except that I cannot send a kill signal to
the running process. When I call pclose it will hang since the daemon will
still be running.

-- Henrik


Thomas Maier-Komor

2006-10-27, 7:19 am

Henrik Goldman schrieb:
>
> Thanks for your suggestions. The only problem about the popen aproach is
> that I loose the pid of the child process. The child is really a daemon so I
> need to send a command to it to make it shutdown. Otherwise it would hang
> around forever.
>
> Is there a way to signal to the opened process?
>
> Thanks.
>
> -- Henrik
>
>


spawning a daemon works by forking two times in sequence and terminating
the process in the middle. So if the daemon is spawned by the process
you start from your application, there will be no direct way of
determining the pid of the spawned daemon. You will have to rely on some
facility provided by the executable used to spawn the daemon. E.g. a pid
file or something similar.

In contrast, if you spawn the daemon by yourself using fork/exec, you
will have full control over what is happening.

Either way, the whole topic isn't really straight forward. So I'd
recommend you to get a book that describes UNIX's processes and the
semantics of fork and exec in detail.

Tom
Jens Thoms Toerring

2006-10-27, 7:19 am

Henrik Goldman <henrik_goldman@mail.tele.dk> wrote:
[vbcol=seagreen]
> Whats with the unfriendly "i am smarter then you" attitude? If it's such a
> pain for you to help other people then perhaps you should not do that
> anymore.


Huh? I was just pointing out as clearly as possible what I don't
understand about what you had written, especially when compared
to the bit of code you posted before applying guesswork to figure
out what you may have meant. I don't see what would make that an
'"i am smarter then you" attitude' or what's unfriendly about it.

> Let me try to describe what I want so I ensure that YOU understand it:


Now, who of us has a '"i am smarter then you" attitude'?

> I would like to spawn an external application written by me as well. This
> application is a daemon which can be communicated with and writes a logfile.


> My plan is to start up this 2nd application as a child process, communicate
> with it to do some tests and then shut it down again.
> It needs to be shutdown by sending a "kill" signal or similar so the


If you send a SIGKILL signal then there's nothing the receiver can do
anymore since a SIGKILL signal can't be caught (or blocked). So better
go for one of the "or similar", typically one would use SIGTERM.

> application will invoke it's own signal handler and shutdown gracefully.
> It's important so other tests won't fail. If it gets shutdown ungracefully
> it might take a few seconds for the OS to cleanup resources (based on
> experience) and this can make other code fail.
> As for background it means that I don't see anything on screen. This is
> because I don't want to clutter up the screen with 2 applications at once.
> So even though the 2nd application writes to stdout I don't want to see it.
> Do you understand now?


[vbcol=seagreen]
> Correct.


[vbcol=seagreen]
> A communicates with B


No, A does not communicate with B, only A' reads from B. A doesn't
see anything of what's happening between A' and B (unless you have
some aditional mechanisms in your program that make A' pass on
messages it received from B to A, but that's something you haven't
told about).

> but what A does it not relevant except that when A
> requests B to stop then it should do so.
> SIGTERM is probably fine but it didn't work on B.


Obviously, since process B won't receive a signal you send to A'. If
you want A to send a signal to B then A needs the PID of B, but which
it doesn't have. Killing A' may, in turn, lead to B's demise if B con-
tinues to try to write to its standard out (which is gone once A' has
kicked the bucket) and thus B receives a SIGPIPE signal that will kill
it if the signal is not caught (or blocked).

[vbcol=seagreen]
> As I found out popen will do ok except that I cannot send a kill signal to
> the running process. When I call pclose it will hang since the daemon will
> still be running.


Then you can't use popen(). If you want finer-grained control or
additional information than what popen() gives you, you need to
use the fork()/exec() combination and do the pipe creation and
redirections yourself. There's no standard way you can make popen()
divulge the PID of the proceess it created.

But there might be a way to safe the day if you're willing to modify
your external application. If A' gets killed the standard output of
B suddenly vanishes and, as I already pointed out, B's next attempt
to write() to it will result in a SIGPIPE signal. Catch that signal,
do your cleanup in the signal handler and then have it exit.

Regards, Jens
--
\ Jens Thoms Toerring ___ jt@toerring.de
\__________________________ http://toerring.de
Barry Margolin

2006-10-28, 1:31 am

In article <4541c764$0$177$edfadb0f@dread11.news.tele.dk>,
"Henrik Goldman" <henrik_goldman@mail.tele.dk> wrote:

> Whats with the unfriendly "i am smarter then you" attitude? If it's such a
> pain for you to help other people then perhaps you should not do that
> anymore.
>


Get used to it, or forget about using Usenet as a resource. I'm not
trying to excuse the behavior, just acknowledge that it's common and not
likely to change. You're getting free help, live with the attitude that
comes with it.

--
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
Bill Pursell

2006-10-28, 7:24 am


Henrik Goldman wrote:
> Jens Thoms Toerring wrote:

(attribution to Jens added by me)>
>
> Whats with the unfriendly "i am smarter then you" attitude? If it's such a
> pain for you to help other people then perhaps you should not do that
> anymore.


I don't read that attitude in Jens' post at all. I read his post as
being strictly informational. It is true that a lot of posters adopt
the attitude you describe, but I don't think this is an example of
that.

>
> Let me try to describe what I want so I ensure that YOU understand it:
>
> I would like to spawn an external application written by me as well. This
> application is a daemon which can be communicated with and writes a logfile.
>
> My plan is to start up this 2nd application as a child process, communicate
> with it to do some tests and then shut it down again.


When the other application is running, do you want it to be a daemon?
In my mind, the goal of having it run as a child process contradicts
having it run as a daemon. For me, part of the definition of being
a daemon is having init be your parent.

> It needs to be shutdown by sending a "kill" signal or similar so the
> application will invoke it's own signal handler and shutdown gracefully.
> It's important so other tests won't fail. If it gets shutdown ungracefully
> it might take a few seconds for the OS to cleanup resources (based on
> experience) and this can make other code fail.
> As for background it means that I don't see anything on screen.


If it's a daemon, it should be completely dissassociated with a
terminal.
Are you asking about how to start a daemon?

>This is
> because I don't want to clutter up the screen with 2 applications at once.
> So even though the 2nd application writes to stdout I don't want to see it.
> Do you understand now?


If the second app is writing to stdout, then you will need to make
sure that it's stdout is not the terminal that you're running from.
One way to do that is to fork, redirect the child's stdout
somewhere else, and then have the child exec the process
you are trying to run. However, if you intend for this other
process to be a daemon, it shouldn't be writing to stdout.
The general process of implementing a daemon usually
involves closing stdout and stdin, then forking twice and
letting the middle process terminate so that the daemon is
adopted by init. If your process is writing to stdout, I would
argue that it is NOT a daemon.


>
>
> Correct.
>
>
> A communicates with B but what A does it not relevant except that when A
> requests B to stop then it should do so.
> SIGTERM is probably fine but it didn't work on B.
>
>
> As I found out popen will do ok except that I cannot send a kill signal to
> the running process. When I call pclose it will hang since the daemon will
> still be running.


You can always send a signal to a process (as long as you have
permission to do so.) The issue is that the process is no longer
a child of your app, so your'e having a difficult time locating
the correct pid to which to send the signal.
Here's my interpretation of your situation:

process A spawns process B. Process B spawns process C
which is a true daemon, and is therefore a child of B for only
a short time at start up--once it is running, it is a direct child
of init. Process A kills process B, and this has no effect on C,
because C is no longer related to process B (it has demonized
itself) by the time A kills B.

In short, if process C is a daemon process, then it is not
surprising that killing B has no effect on C.

--
Bill Pursell

phil-news-nospam@ipal.net

2006-10-28, 1:40 pm

On Fri, 27 Oct 2006 10:46:28 +0200 Henrik Goldman <henrik_goldman@mail.tele.dk> wrote:

|> This is all bit strange: you create two new processes, but you're
|> talking only about "starting an external application in the background".
|> Moreover, the term "in the background" makes only sense in the
|> context of a shell. Finally, it's also unclear what you mean by
|> "not get killed cleanly". Dead is dead, so what's the difference
|> between getting killed cleanly and uncleanly?
|
| Whats with the unfriendly "i am smarter then you" attitude? If it's such a
| pain for you to help other people then perhaps you should not do that
| anymore.

I've seen a lot of "i am smarter then you" attitude on Usenet. I've even
been on the receiving end of a lot of it. But this is not it. Maybe it's
confusion about what you want. That can happen when people know enough to
be dangerous. So what I suggest is ...


| Let me try to describe what I want so I ensure that YOU understand it:

.... that. Clear descriptions always help. The internet has no mind
reading protocol. If your English is poor, we can understand that.
It's the technical terms that need to be in the right place. And maybe
someone who knows your native language can correspond directly.


| I would like to spawn an external application written by me as well. This
| application is a daemon which can be communicated with and writes a logfile.
|
| My plan is to start up this 2nd application as a child process, communicate
| with it to do some tests and then shut it down again.
| It needs to be shutdown by sending a "kill" signal or similar so the
| application will invoke it's own signal handler and shutdown gracefully.
| It's important so other tests won't fail. If it gets shutdown ungracefully
| it might take a few seconds for the OS to cleanup resources (based on
| experience) and this can make other code fail.
| As for background it means that I don't see anything on screen. This is
| because I don't want to clutter up the screen with 2 applications at once.
| So even though the 2nd application writes to stdout I don't want to see it.
| Do you understand now?

If the log daemon is being communicated with, how? Is it with a pipe
sending it stuff to work with? If so, why isn't closing the pipe on
the sending end sufficient to tell it to gracefully shutdown as it
would get the EOF while reading its end of the pipe?


|> Perhaps you should explain a bit more clearly what exactly you're
|> trying to do. Do you really want to create two extra process? And
|> what do you consider being "killed cleanly"? Moreover, what are
|> your problems with the execl() function? Do you understand that
|> popen() is just a wrapper function for setting up a pipe, calling
|> fork(), readjusting standard input or output in the child and then
|> doing an exec(), so you don't have to do all that yourself (but,
|> of course, losing some flexibility in the process)?
|
| As I found out popen will do ok except that I cannot send a kill signal to
| the running process. When I call pclose it will hang since the daemon will
| still be running.

The daemon should check for EOF while reading from the pipe (stdin) and
when it gets that, do its graceful completion thing.

If you really must send a signal, you need details that go beyond the
popen() facility and into the Unix interfaces. That will involve fork()
from where you get the child process ID, exec*() of some kind if the code
is not in the parent image, and maybe other things like setsid().
Of course that code will need to properly handle the signal(s) you intend
to send. And you'll need to syncronize to be sure the daemon has read
everything from the pipe it needs to read when the signal arrives (keep
in mind that timing is not always convenient and the parent doing a write
followed by a signal might not result in the daemon reading the data then
receiving the signal).

If at all possible, the daemon should detect the ending scenario based on
getting EOF from the pipe. If it's also going to be listening for work
in other ways like sockets, then EOF from the pipe won't be an option.
You may need to have 2 signal semantics. SIGUSR1 might mean to it to
finish the work it is doing as gracefully as it can (reading everything
to EOF and finishing up processing) and SIGTERM might mean hurry up and
just cache everything somewhere immediately because "we are going down".

--
|---------------------------------------/----------------------------------|
| Phil Howard KA9WGN (ka9wgn.ham.org) / Do not send to the address below |
| first name lower case at ipal.net / spamtrap-2006-10-28-1123@ipal.net |
|------------------------------------/-------------------------------------|
Henrik Goldman

2006-10-29, 7:17 am

>
> If you send a SIGKILL signal then there's nothing the receiver can do
> anymore since a SIGKILL signal can't be caught (or blocked). So better
> go for one of the "or similar", typically one would use SIGTERM.


Yes you're right. I changed it now.
However I noticed that on solaris 8 on sparc it might backfire a SIG_ALRM
and on Solaris 10 x86 it might kick a SIG_PIPE.
For now I am catching those in my daemon since the parent process (the
process which started the daemon) might display the error on console which I
do not want.

> Then you can't use popen(). If you want finer-grained control or
> additional information than what popen() gives you, you need to
> use the fork()/exec() combination and do the pipe creation and
> redirections yourself. There's no standard way you can make popen()
> divulge the PID of the proceess it created.
>
> But there might be a way to safe the day if you're willing to modify
> your external application. If A' gets killed the standard output of
> B suddenly vanishes and, as I already pointed out, B's next attempt
> to write() to it will result in a SIGPIPE signal. Catch that signal,
> do your cleanup in the signal handler and then have it exit.


Yeah thats what I did. So far I only saw SIG_PIPE and SIG_ALRM on Solaris
but I don't know which other platforms works like this too. I'm compiling
for linux, solaris, hp-ux, macosx and aix but only solaris behaved like you
described. So far I'm just catching the signals on solaris only for this
particular reason.

-- Henrik


Henrik Goldman

2006-10-29, 7:17 am

> When the other application is running, do you want it to be a daemon?
> In my mind, the goal of having it run as a child process contradicts
> having it run as a daemon. For me, part of the definition of being
> a daemon is having init be your parent.


My daemon has two ways to run: Either in foreground or background.
Currently I'm running it in foreground which means it also interacts with
stdout.

> If it's a daemon, it should be completely dissassociated with a
> terminal.
> Are you asking about how to start a daemon?


Correct and correct. I wrote some code which seems to work now though.
However as said my daemon also has the option to run in foreground.

> If the second app is writing to stdout, then you will need to make
> sure that it's stdout is not the terminal that you're running from.
> One way to do that is to fork, redirect the child's stdout
> somewhere else, and then have the child exec the process
> you are trying to run. However, if you intend for this other
> process to be a daemon, it shouldn't be writing to stdout.
> The general process of implementing a daemon usually
> involves closing stdout and stdin, then forking twice and
> letting the middle process terminate so that the daemon is
> adopted by init. If your process is writing to stdout, I would
> argue that it is NOT a daemon.
>

So lets call it a daemon in debug mode when it interacts with stdout. The
reason for doing so is to catch application specific problems faster then by
monitoring logfiles.
See other post for the code.

-- Henrik


Henrik Goldman

2006-10-29, 7:17 am

Here is my revised code which seems to work quite ok on all unix platforms
so far (linux, solaris, hp-ux, macosx and aix):

bool CProcess::Start()

{

if ((m_fp = popen((char *) m_strCommand.c_str(), "r")) == NULL)

return false;


// Wait on unix for the process to startup

sleep(5);

m_bStarted = true;

}

void CProcess::Stop()

{

FILE *fp;

if (m_bStarted)

{

if ((fp = fopen("/var/tmp/mypid.lock", "r")) != NULL)

{

fscanf(fp, "%d", &m_nPID);

kill(m_nPID, SIGTERM);

fclose(fp);

}

pclose(m_fp);

m_bStarted = false;

}

}



What I do differently now is to send a SIGTERM and then get the pid of the
daemon through a hardcoded pid file. This way I ensure I get the right pid.

I wait 5 seconds at startup to ensure that the application stabilizes so
it'll be ready for taking requests. Previously I had communication problems
due to the fact that the daemon wasn't ready yet.

The only thing I observed is that pclose() can make the daemon die with
SIGPIPE or SIGALRM on solaris. Other then that it seems to work ok. I fixed
the solaris problems by catching those signals in the daemon error handler
and performing a graceful shutdown.

-- Henrik


Jens Thoms Toerring

2006-10-29, 7:17 am

Henrik Goldman <henrik_goldman@mail.tele.dk> wrote:
> However I noticed that on solaris 8 on sparc it might backfire a SIG_ALRM
> and on Solaris 10 x86 it might kick a SIG_PIPE.


Who's receiving a signal? And there's nowhere a SIGALRM could come from
unless you send it yourself (perhaps indirectly by setting a timer),

> For now I am catching those in my daemon since the parent process (the
> process which started the daemon) might display the error on console which I
> do not want.


Your "daemon" of course still can write to standard error, and that's
where it sends error messages to. If you want to keep it from messing
up your console have it close its standard error or redirect standard
error to e.g. /dev/null.

[vbcol=seagreen]
> Yeah thats what I did. So far I only saw SIG_PIPE and SIG_ALRM on Solaris
> but I don't know which other platforms works like this too. I'm compiling
> for linux, solaris, hp-ux, macosx and aix but only solaris behaved like you
> described. So far I'm just catching the signals on solaris only for this
> particular reason.


Sorry, but then there must be something going wrong in your program.
You definitely should get a SIGPIPE on all platforms, I tested the
following under Linux and IRIX (that's the two platforms I have easy
access to at the moment, but others should behave in exactly the same
way). There are two programs I cobbled together for testing, named A
(the program that forks and have its child A' starts the "daemon")
and B (the "daemon"):

--------- A.c ----------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

pid_t child_stuff( void )
{
char buf[ 80 ];
FILE *fp;
pid_t ch = fork( );

if ( ch == -1 )
{
perror( "Fork failed" );
exit( EXIT_FAILURE );
}
else if ( ch > 0 )
{
sleep( 3 );
return ch;
}

if ( ( fp = popen( "./B", "r" ) ) == NULL )
{
perror( "popen() failed" );
exit( EXIT_FAILURE );
}

while ( fgets( buf, sizeof buf, fp ) != NULL )
fprintf( stdout, "A': B sent message: %s", buf );

pclose( fp );
exit( EXIT_SUCCESS );
}

int main( void )
{
pid_t ch = child_stuff( );
kill( ch, SIGTERM );
return EXIT_SUCCESS;
}

--------- B.c ----------------------------------------------------

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

void sig_handler( int signo )
{
fprintf( stderr, "B: Got SIGPIPE\n" );
exit( EXIT_SUCCESS );
}

int main( void )
{
struct sigaction sact;

sact.sa_handler = sig_handler;
sigemptyset( &sact.sa_mask );
sact.sa_flags = 0;
if ( sigaction( SIGPIPE, &sact, NULL ) == -1 )
{
fprintf( stderr, "sigaction failed\n" );
exit( EXIT_FAILURE );
}

setbuf( stdout, NULL );

while ( 1 )
{
fprintf( stderr, "B: Sending message to A'\n" );
fprintf( stdout, "Message for A' from B\n" );
sleep( 1 );
}

return EXIT_SUCCESS;
}

------------------------------------------------------------------

And here's a typical invocation (no difference between Linux or
IRIX and no other signals like SIGALRM):

jens@john:~/TESTS> ./A
B: Sending message to A'
A': B sent message: Message for A' from B
B: Sending message to A'
A': B sent message: Message for A' from B
B: Sending message to A'
A': B sent message: Message for A' from B
jens@john:~/TESTS> B: Sending message to A'
B: Got SIGPIPE

As you can see, A' receives 3 messages from A' (I have B send one
message per second and A kills A' after 3 seconds), and then B
tries to send another one (by that time A already has quit, so
I got back to the command prompt), and it receives a SIGPIPE signal
since it can't write to the pipe to A' anymore. If you use standard
C functions like fprintf() it's of course important to "unbuffer"
them (that's what the setbuf() call is needed for, but setlinebuf()
will also do in this case since all lines end with a '\n), otherwise
the output would only get written to the internal buffers of these
functions until they are full (you are not writing to a terminal but
something that looks to them like a file), and the other side won't
get the messages immediately and the "daemon" would only detect that
there's no standard output anymore when those internal buffers are
full and need to be flushed.
Regards, Jens
--
\ Jens Thoms Toerring ___ jt@toerring.de
\__________________________ http://toerring.de
Henrik Goldman

2006-10-30, 1:19 pm

> Who's receiving a signal? And there's nowhere a SIGALRM could come from
> unless you send it yourself (perhaps indirectly by setting a timer),


To the best of my knowledge I have no timers.
What happens is that when I call kill(daemon_pid, SIGKILL) on the pid of the
daemon I can see from it's log file that it shuts down gracefully. However
when pclose() is called I get "Alarm" or "pipe" error messages on stdout.
When I specifically catch those in the daemon I get nothing.
So it seems that when pclose() is called it's the interaction between
between pclose and the daemon.
I cannot tell what happens inside pclose() however it's not the parent
process which prints this so it can only be the child (the daemon).

> Your "daemon" of course still can write to standard error, and that's
> where it sends error messages to. If you want to keep it from messing
> up your console have it close its standard error or redirect standard
> error to e.g. /dev/null.


Right. However it's not something which is a problem on normal daily basis
as the software has other ways to disable console output.

>
> Sorry, but then there must be something going wrong in your program.
> You definitely should get a SIGPIPE on all platforms, I tested the
> following under Linux and IRIX (that's the two platforms I have easy
> access to at the moment, but others should behave in exactly the same
> way). There are two programs I cobbled together for testing, named A
> (the program that forks and have its child A' starts the "daemon")
> and B (the "daemon"):


I tried your app on both linux and solaris and got same result.

> As you can see, A' receives 3 messages from A' (I have B send one
> message per second and A kills A' after 3 seconds), and then B
> tries to send another one (by that time A already has quit, so
> I got back to the command prompt), and it receives a SIGPIPE signal
> since it can't write to the pipe to A' anymore. If you use standard
> C functions like fprintf() it's of course important to "unbuffer"
> them (that's what the setbuf() call is needed for, but setlinebuf()
> will also do in this case since all lines end with a '\n), otherwise
> the output would only get written to the internal buffers of these
> functions until they are full (you are not writing to a terminal but
> something that looks to them like a file), and the other side won't
> get the messages immediately and the "daemon" would only detect that
> there's no standard output anymore when those internal buffers are
> full and need to be flushed.


Yes I use fprintf() and standard printf() in several places. It's not really
a problem as such. As long as those signals are caught and the software
doesn't crash then all should be fine.
The only thing that buggers me is that the same codes gives sort of random
behavior on each platform. E.g. Solaris sparc gives SIGALRM and solaris x86
gives SIGPIPE. Linux behaves quite good though.
The daemon is quite complex though and theres like 6-7 active threads which
doesn't make it easy to understand what happens at which times.
Since it's the first time I see this in the last half year since the daemon
was programmed I don't think it's something people will see on a daily
basis. I'm just running this stuff as a part of an automated test suite.
I'm tending to believe that it's the way the io libraries differ on the
specific platforms.

-- Henrik


Sponsored Links






Free braindumps | Software forum | Database administration forum

Copyright 2003 - 2008 webservertalk.com