LinuxQuestions.org
Review your favorite Linux distribution.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices

Reply
 
Search this Thread
Old 06-05-2012, 07:01 PM   #1
Wolf-Ekkehard
LQ Newbie
 
Registered: Oct 2006
Location: California
Distribution: Kubuntu 12.04
Posts: 11

Rep: Reputation: 1
system() call produces wrong exit status in daemon


I am running a daemon (written in C/C++) from which I call a script (Python or Bash - same result) using system() like so:
Code:
int rc;
daemonize();
rc = system( "./myScript" );
if ( rc != -1 ) {
    rc = WEXITSTATUS( rc );
    if ( rc != 0 ) {
        syslog( LOG_DEBUG, "rc = %d (expected to be != -1)", rc );
    }
} else {
    syslog( LOG_DEBUG, "error: %s", strerror( errno ) );
}
cleanupAndExit( rc );
The daemon does its thing correctly, and the script does its thing correctly, except that the exit stauts returned by system() is not what I expect it to be: It remains stubbornly -1, indicating a failure in its fork() (the error message is "No child processes"). This is weird, because whichever script I call it runs fine and produces the expected results, so fork() must have created a child process for them somehow.

The daemon successfully forked off a child, exited the parent and got a new pid via setsid(). It also created its own lock file. It closed all file handles and reopened stdin, stdout, and stderr, redirecting them to /dev/null. But neither of the scripts use any of those standard IO channels - they just create a new log file in a writable directory, and I verified that this log file gets created and has correct content.

If I skip the daemonizing, the program works as expected.

I was under the impression that system() can be used in a daemon. If not, I'd like to be educated and also learn how else I would call a script and get its return code in a daemon. Any and all help will be highly appreciated.

I am using Kubuntu 12.04 64-bit with the 3.2.0-24-generic kernel.
 
Old 06-05-2012, 07:18 PM   #2
kbp
Senior Member
 
Registered: Aug 2009
Posts: 3,758

Rep: Reputation: 643Reputation: 643Reputation: 643Reputation: 643Reputation: 643Reputation: 643
You can never guarantee your working directory so I would change
Code:
rc = system( "./myScript" );
.. to use the full path to the script for a start.
 
Old 06-05-2012, 09:19 PM   #3
Wolf-Ekkehard
LQ Newbie
 
Registered: Oct 2006
Location: California
Distribution: Kubuntu 12.04
Posts: 11

Original Poster
Rep: Reputation: 1
Thanks kbp!

Did that - same result :-(

Even though you're right, of course - I shoudl have given full path also in this little test case, but the script got executed, so it must have been found, and the consistency of the (bad) result is explainable.
 
Old 06-06-2012, 12:57 PM   #4
Reuti
Senior Member
 
Registered: Dec 2004
Location: Marburg, Germany
Distribution: openSUSE 11.4
Posts: 1,319

Rep: Reputation: 252Reputation: 252Reputation: 252
Why are you testing against -1 after the call and not 0?
 
Old 06-06-2012, 01:17 PM   #5
em31amit
Member
 
Registered: Apr 2012
Location: /root
Distribution: Ubuntu, Redhat, Fedora, CentOS
Posts: 190

Rep: Reputation: 55
how about using "exit 0" as a return statement into script, are you using this ?
 
Old 06-06-2012, 04:29 PM   #6
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942
Quote:
Originally Posted by Wolf-Ekkehard View Post
how else I would call a script and get its return code in a daemon.
Here is an example:
Code:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    pid_t   child_pid, p;
    int     child_status;

    if (argc < 2)
        return 0;

    child_pid = fork();
    if (child_pid == (pid_t)0) {

        /* This is the child process. Execute whatever was specified on the command line.
         * Note: This does NOT use a shell. If you want one, use one explicitly.
        */
        execv(argv[1], argv + 1);

        /* Oops, could not execute the program. Exit with exit status.. say, 126. */
        exit(126);

    } else
    if (child_pid == (pid_t)-1) {
        const char *const error = strerror(errno);

        /* Cannot fork(). Error string in error. */
        fprintf(stderr, "Cannot create a child process: %s.\n", error);
        exit(1);
    }

    /*
     * Do whatever you want while the other one is executing.
    */

    /* Okay, now we want to wait until the other exits.
     * Signal delivery may interrupt the wait, so retry in a loop.
    */
    do {
        p = waitpid(child_pid, &child_status, 0);
    } while (p == (pid_t)-1 && errno == EINTR);
    if (p == (pid_t)-1) {
        const char *const error = strerror(errno);

        fprintf(stderr, "Error waiting for child to exit: %s.\n", error);
        exit(1);
    }

    /* Describe how the other process exited.
    */
    if (WIFEXITED(child_status)) {
        if (WEXITSTATUS(child_status))
            printf("%s failed with exit status %d.\n", argv[1], WEXITSTATUS(child_status));
        else
            printf("%s exited successfully.\n", argv[1]);
    } else
    if (WIFSIGNALED(child_status))
        printf("%s died from signal %s.\n", argv[1], strsignal(WTERMSIG(child_status)));
    else
        printf("%s died from unknown causes.\n", argv[1]);

    return 0;
}
The fork() creates a duplicate of the current process. They are independent, except they share open files and some other similar resources. The newly created process will see child_pid == 0, the other will see the PID of the newly created process instead. All other variables will have the same values (but changes will NOT be reflected in the other process; the values were just copied).

The execv() function takes two parameters: the first is the path to the script or program to be executed, and the second is an array of pointers to strings. The first element in the array corresponds to argv[0] for the executed program, i.e. should be the program name. The program above just executes whatever is specified as an argument. It does not search for the program in PATH; if you want it to, just use execvp(argv[1], argv + 1); instead. You could also use e.g. execl("/path/to/script", "scriptname", NULL); with optional parameters to the script listed as separate strings before the NULL.

There is an additional quirk when used in a daemon: you should provide sensible standard input, output, and error streams. The simplest option is to open them to /dev/null. Assuming your daemonize() function is typical, then it should do that already. If you do not trust the function does that, then you need a helper function,
Code:
/* Redirect the specified descriptor to/from the device/file.
 * Returns 0 if successful (with errno unmodified), errno if error.
*/
int redirect(const int descriptor, const char *const device, const int flags)
{
    int fd, result, saved_errno;

    /* Check for invalid parameters. */
    if (descriptor == -1 || !device || !*device)
        return errno = EINVAL;

    saved_errno = errno;

    /* Open the device. */
    do {
        fd = open(device, flags);
    } while (fd == -1 && errno == EINTR);
    if (fd == -1)
        return errno;

    /* Did we get lucky, and got the desired descriptor? */
    if (fd == descriptor) {
        errno = saved_errno;
        return 0;
    }

    /* Duplicate to the desired descriptor. */
    do {
        result = dup2(fd, descriptor);
    } while (result == -1 && errno == EINTR);
    if (result == -1) {
        saved_errno = errno;
        do {
            result = close(fd);
        } while (result == -1 && errno == EINTR);
        return errno = saved_errno;
    }

    /* Close the unneeded descriptor. */
    do {
        result = close(fd);
    } while (result == -1 && errno == EINTR);
    /* This time we ignore that exit status;
     * the redirection is done, and this was just
     * a temporary descriptor.
    */

    /* Return success. */
    errno = saved_errno;
    return 0;
}
so that you can do the redirections easily, by adding
Code:
        /* Redirect standard input, output and error to /dev/null.
         * If an error occurs, abort with exit status.. say, 125. */
        if (redirect(STDIN_FILENO,  "/dev/null", O_RDONLY)) exit(125);
        if (redirect(STDOUT_FILENO, "/dev/null", O_WRONLY)) exit(125);
        if (redirect(STDERR_FILENO, "/dev/null", O_WRONLY)) exit(125);
just before the execv() call.

The main difference between this approach and the system() library function is that this executes the other program directly, whereas system() always uses a shell. Basically, system() does something like the above, with execv("/bin/sh", "sh", "-c", "the-command-string", NULL); .

Most /bin/sh do not like to be executed without standard streams, and I suspect your daemonize() just closes the descriptors instead of opening them to e.g. /dev/null.
 
1 members found this post helpful.
Old 06-06-2012, 05:00 PM   #7
bigearsbilly
Senior Member
 
Registered: Mar 2004
Location: england
Distribution: FreeBSD, Debian, Mint, Puppy
Posts: 3,276

Rep: Reputation: 170Reputation: 170
does the daemon(3) function exist on your system?
might be worth using.
 
1 members found this post helpful.
Old 06-06-2012, 08:59 PM   #8
Wolf-Ekkehard
LQ Newbie
 
Registered: Oct 2006
Location: California
Distribution: Kubuntu 12.04
Posts: 11

Original Poster
Rep: Reputation: 1
Waw - I am impressed. This is the most active (and helpful!!) forum I ever used.

First, to answer Reuti, I test for -1 because -1 is a special case in which the return code does not reflect the return code of the script at all (which may also be different from 0), but it reflects the failed fork, and there is no way to get to the return code of the script. If the return code is different from -1, it should reflect my script's return code (0 or different from 0 in case of an error in the script) and I can extract the return code that my script exited with using the WEXITSTATUS macro.

Secondly, to answer em31amit, yes, I am using exit in the bash script and sys.exit() in the Python script.

Thirdly, Nominal Animal, this is extremely helpful - thanks! Wrapping a shell around the script, as system() does, is indeed unnecessary, and I should be following the method you propose - just to avoid things that are not really necessary. And no, I do close all the file descriptors, but I re-open stdin, stdout, and stderr and point them all to /dev/null.

Finally, thanks a bunch bigearsbilly! My system does indeed have a daemon function, and using this avoids all of my problems. It is the first daemon I write, and so I was looking for tutorials on the web, which were didactically great, but not very effective, as they did not mention the built-in library function.

So for today, I have a "quick fix" by just using daemon(), but for the next couple of days, I will try to execute the scripts directly without the intermediate shell.

Again, thanks a bunch for all you help!!!!!
 
  


Reply

Tags
daemon, return, system


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] Exit status of system() call shankar.489 Linux - Newbie 2 03-18-2011 12:54 AM
how can a daemon get notified when a system call is made linuxdoniv Programming 6 07-16-2008 08:12 AM
PPP Daemon died ... Exit status: 1 windus Linux - Newbie 11 02-03-2008 10:29 PM
Exit status of system command?? rmvinodh123 Programming 4 05-04-2007 11:15 AM
kppp daemon died unexpectedly exit status 4 steelrose Linux - Networking 1 04-03-2002 04:00 AM


All times are GMT -5. The time now is 07:06 PM.

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration