1

I have a program that runs indefinitely. For testing purposes I have made a wrapper program that kills the other after a specified amount of time (specified via command line/terminal args). The program being forked requires that it is passed two folders with the same name (I have no control over this), so I simply pass it the same arg twice as can be seen here:

pid_t pid = fork();
if(pid == 0)
{
    //build the execution string
    char* test[2];
    test[0] = argv[2];
    test[1] = argv[2];
    test[2] = NULL;
    cout << "test[0] is " << test[0] << endl;
    cout << "test[1] is " << test[1] << endl;
    cout << "argv[1] is " << argv[1] << endl;
    execvp(argv[1],test);
}

The problem is that the program being passed in argv[1] keeps segmentation faulting. If I call the by itself via the terminal it runs with no problems. I am passing the same folder in both cases. Can anyone tell me why it isn't working for execvp?

I should mention a co-worker ran it on his computer as well, and it will stand up fine the first time, but each time after that, it will seg fault.

edit: I have added a null term to test, however, this has not fixed the issue.

The command's form is exactly:

<executable> <wrapped prog> <folder> <duration>

In relative paths it's:

Intel/debug/Tester.exe <program> test 10
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Eric
  • 452
  • 1
  • 5
  • 18

5 Answers5

2

Array passed as arguments should be null-terminated. For example:

char *test[3]={0};
...
gandgandi
  • 330
  • 1
  • 8
  • 1
    For reference; http://linux.die.net/man/3/execvp "The ... execvp()... functions... The array of pointers must be terminated by a NULL pointer." – Niall Jul 27 '15 at 14:14
  • I just ran the program again, and it didn't fix the problem (it did get farther though). – Eric Jul 27 '15 at 14:25
  • Try concatenating command you want to call with `/bin/bash` (or whatever you bash binary is sitting) string so it will look like: `/bin/bash command`. – gandgandi Jul 27 '15 at 14:45
2

If the length of the array is static, you might be better off with

execlp

execlp(argv[1], argv[1], argv[2], argv[2], (char*)0);

As for execvp, the array should start with the name of the executable and end with NULL.

execvp

char* args[] = { argv[1], argv[2], argv[2], NULL };
execvp(argv[1], args);

runWithTimeout

In any case, if all you want is a simple wrapper that runs a single child with a timeout, then your program could be very simple and general if only you'd be willing to start with the timeout argument:

/*runWithTimeout.c
  compile with: make runWithTimeout
  run with: ./runWithTimeout seconds program arguments...
*/
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>

int main(int argc, char** argv)
{
  assert(argc >= 1+2);
  int pid, status = 1;
  if((pid = fork()) == 0) {
    alarm(atoi(argv[1]));
    execvp(argv[2], argv + 2); 
    /*^the child ends here if execvp succeeds,
    otherwise fall-through and return the default error status of 1
    (once in child (which has no one to wait on) and 
    then in the parent (which gets the status from the child))*/
    perror("Couldn't exec");
  }else if(pid < 0){ perror("Couldn't fork"); };
  wait(&status);
  return status;
}
Community
  • 1
  • 1
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • For debug purposes I have a for loop that prints all the args passed. They all appear properly, and argc has a size of 4: 1) Itself 2) the program to call 3) the folder to pass 4) the max time to run – Eric Jul 27 '15 at 14:27
  • I just tried the execlp and got the same results as execvp. – Eric Jul 27 '15 at 14:30
  • That's the point of it. Any way, I added a whole functioning example. – Petr Skocik Jul 27 '15 at 15:31
  • 1
    Note that `execlp()` needs a `(char *)0` argument as its last argument. – Jonathan Leffler Jul 28 '15 at 16:08
  • Note that while the `perror()` is good, you also need some form of `exit()` after the `execvp()` fails. – Jonathan Leffler Jul 28 '15 at 16:11
  • @JonathanLeffler It's kind of hacky but this uses the same exit as the parent--the `wait` should will fail in the child in which case status remains 1 which is what the parent gets via its `wait` call. Thanks for the `execlp` comment though. I naively thought the `l` versions didn't need the null terminator. – Petr Skocik Jul 28 '15 at 16:14
  • 1
    Yes, I see what you mean. I'm not keen on it, but it does work sanely with this code. However, as a general rule, you shouldn't let the child out of its statement block — there are exceptions, of course, but most often, the early exit is appropriate. (There are those who'd want to use one of `_exit()`, `_Exit()` or `quick_exit()` instead of regular `exit()`; I normally just use `exit()` with a non-zero status.) – Jonathan Leffler Jul 28 '15 at 16:44
1

You can turn on core dumps ( make sure to shut them off when done ) ulimit -c unlimited . Run it before you run your main process. ( I would be leary of running it in the fork though you probably can. )

When your program crashes this will produce a core dump which you can examine with gdb.

For help with core files, you can just google them.

Other then that. You can make a script which launches your file. You can use the script to log stuff.

TLOlczyk
  • 443
  • 3
  • 11
1

You want:

char* test[3];
test[0] = argv[2];
test[1] = argv[2];
test[2] = NULL;

You need a NULL parameter to mark the end of the parameter list.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
1

Given the specification:

The command's form is exactly:

<executable> <wrapped prog> <folder> <duration>

In relative paths it's:

Intel/debug/Tester.exe <program> test 10

and also:

The program being forked requires that it is passed two folders with the same name…

then, assuming you've checked that the wrapper is passed 4 arguments, the code you need is:

pid_t pid = fork();
if (pid == 0)
{
    //build the execution string
    char  *test[4];      // Note the size!
    test[0] = argv[1];   // Program name: argv[0] in exec'd process
    test[1] = argv[2];   // Directory name: argv[1] …
    test[2] = argv[2];   // Directory name: argv[2] …
    test[3] = NULL;      // Null terminator
    cout << "test[0] is " << test[0] << endl;
    cout << "test[1] is " << test[1] << endl;
    cout << "test[2] is " << test[2] << endl;
    execvp(test[0], test);
    cerr << "Failed to exec '" << test[0] << "': " << strerror(errno) << endl;
    exit(1);  // Or throw an exception, or …
}

There is seldom (but not never) a reason to invoke execvp() other than using the idiom execvp(argv[0], argv) for the array of arguments in argv.

Note that this code ensures that the control flow doesn't escape from the statement block that is supposed to represent the child. Having the child process continue afterwards, usually in effect thinking it is a parent process, leads to confusion. Always make sure the child execs or exits. (That's a rhetorical over-statement — yes; but there's a large chunk of truth behind the idea too.) Also, since this is C++, you may need to consider How to end C++ code?. That complicates life. The crucial thing is that if the child process fails to exec, it does not continue as if it was a parent process.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • My parent process is handling the timing and is guaranteed to end after the child so I don't need to worry about that. The only thing args the child needs are argv[1] and argv[2]. The How to end code is a good thing for me to keep in mind though. – Eric Jul 28 '15 at 17:48