0

I would like to use fork from my main program to make a process instance of other program I wrote. Here is example of what I am trying to do:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>

    int main(int argc, char *argv[]){
    char *ar[] = {"./myfile",NULL};

    switch (fork()) {
        case -1:
            printf("Problem.\n");
            break;
        case 0:
            printf("Everything ok...\n");
            execv("./myfile",ar);
            printf("err\n");
            exit(1);
        default: 
            sleep(1);
    }
    return 0;
}

This is my main program. Program myfile looks like this:

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

int main(int argc, char *argv[]){
    printf("Hi!\n");
    return 0;
}

This is output I am expecting:

Everything ok...
Hi!

But I am only getting:

Everything ok...

What I am doing wrong? execv takes two parameters, but the second one in my case is empty (or NULL?). I tried to add line

char *argv[] = NULL;

to my main program, but I got error because I can't do that in C.

  • `char *argv[] = NULL;` --> `char *argv[] = {NULL};` – David Ranieri Mar 25 '20 at 20:43
  • 2
    `execv(...); exit(EXIT_SUCCESS)` is odd. `execv` only returns if it fails, so it is more usual to write `exit(EXIT_FAILURE)`. In addition, if you add `perror("execve")`, you might just see why it's failing. – William Pursell Mar 25 '20 at 20:45
  • 2
    "But I am only getting". Are you sure? That code should not even compile: `error: too few arguments to function execv` – kaylum Mar 25 '20 at 20:45
  • 1
    Please include in your question the entire source file, including any `#include` directives (see [mre]). Please also include any diagnostic messages you got from your compiler; you should have gotten at least a warning. – Keith Thompson Mar 25 '20 at 20:47
  • 1
    `filename` is not an *array of pointers* with the first pointer after the last user-defined element set `NULL` as a *Sentinel NULL*. You invoke *Undefined Behavior* by passing an incompatible pointer type to `execv`. – David C. Rankin Mar 25 '20 at 20:52
  • @KeithThompson edited, tnx – stickynotes3 Mar 25 '20 at 20:53
  • @kaylum I can compile it with few warnings. I am doing that on Ubuntu. – stickynotes3 Mar 25 '20 at 20:55
  • 2
    @stickynotes3 That's not good. It means you have probably not included all the right headers.Add all the required headers. For `execv` it is: `#include `. Then you will get a compile error. Which is actually good as it will more clearly point you to what needs to be fixed. – kaylum Mar 25 '20 at 20:57
  • 1
    @stickynotes3: "Few" warnings is too many. Only "none" is acceptable. – Nate Eldredge Mar 25 '20 at 20:58
  • 1
    `execv(filename);` is wrong. [The prototype is `int execv(const char *path, char *const argv[]);`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html) – Andrew Henle Mar 25 '20 at 21:01
  • @NateEldredge I am getting warnings only for implicit declarations – stickynotes3 Mar 25 '20 at 21:01
  • 1
    @stickynotes3: Indeed. And implicit declaration warnings are **very bad**, usually indicating a missing header, and need to be fixed before proceeding. – Nate Eldredge Mar 25 '20 at 21:02
  • @DavidRanieri after making your suggested correction, the main program runs without any output – stickynotes3 Mar 25 '20 at 21:03
  • @NateEldredge ok, I added #include and got passing argument 2 of ‘execv’ from incompatible pointer type [-Wincompatible-pointer-types]; second arg is *a = {NULL}; – stickynotes3 Mar 25 '20 at 21:07
  • @stickynotes3 - note, traditionally, you simply fill an array of pointers to char with the first element the program name you wish to invoke. That way you simply call `execv (yourarray[0], yourarray);` where the elements could be `char *yourarray[] = { "/bin/ls", "-al", "somedir", NULL };` (thanks rici) – David C. Rankin Mar 25 '20 at 21:27
  • regarding: `printf("Problem.\n");` Error messages should be output to `stderr`, not `stdout` and when the error indication is from a C library function should also output the text reason the system thinks the error occurred. a simple way to do this is: `perror( "fork failed" );` – user3629249 Mar 26 '20 at 19:14
  • regarding: `default: sleep(1);` This is not a reliable method. Suggest: `default: wait( NULL );` so the execution in the parent process actually waits for the child process to complete. Note: `wait()` needs the statements: `#include ` and `#include ` – user3629249 Mar 26 '20 at 19:21

3 Answers3

1

execv takes two arguments, and the second one, according to the man page, needs to be:

an array of pointers to null-terminated strings that represent the argument list available to the new program. The first argument, by convention, should point to the filename associated with the file being executed. The array of pointers must be terminated by a null pointer.

So you should have something like:

char *argv_for_program[] = { filename, NULL };
execv(filename, argv_for_program);

Some other notes:

  • Your printf immediately preceding the execv doesn't end in a newline. Since stdout is typically line buffered, the text "Everything ok..." will not be printed until the buffer is flushed. And the execv replaces your program with ./myfile, without flushing buffers, so the "Everything ok..." message is lost. To fix this, print "Everything ok...\n" instead, or call fflush(stdout) before execv.

  • execv only returns if you failed to execute the program. In this case, it's probably not desirable to exit(EXIT_SUCCESS); it'd be better to print an appropriate error message (e.g. with perror) and exit with a failure code.

  • As noted in comments, you need to #include <unistd.h>.

Nate Eldredge
  • 24,174
  • 2
  • 31
  • 43
  • Ok, but what if I don't have arguments for my program? Don't know what to put in variable argv_for_program in execv(filename, argv_for_program); As you can see, my program only prints Hi – stickynotes3 Mar 25 '20 at 21:16
  • @stickynotes3: The `argv` array is allowed to only have one useful element, which is the name of the program. But it's not a good idea for it to have none, because the first element, as Nate says, is expected to be the name of the program. (In theory, an `argv` array consisting only of NULL is legal. But it will usually get you into trouble because most programs want to know what their name is.) – rici Mar 25 '20 at 21:18
  • @rici In other words, you want to say that my execv should look like this according to Nate: execv(filename, argv_for_program[0])? – stickynotes3 Mar 25 '20 at 21:20
  • @stickynotes3: No I want to say that Nate got it right. `argv_for_program[0]` is the address of a string (i.e. a `char*`), not the address of a array of strings. – rici Mar 25 '20 at 21:23
  • @stickynotes3: My example is exactly for the case that you don't want to pass any arguments. The `filename` entry in the array is for its `argv[0]` entry, which the C library usually expects to have even if you don't look at it in your program. If you did want to pass arguments, they would be additional entries in the array: `char *argv_for_program = { filename, "first-argument", "second-argument", NULL };` – Nate Eldredge Mar 25 '20 at 21:24
  • @NateEldredge done exactly how you said, got no warnings and no output – stickynotes3 Mar 25 '20 at 21:29
  • 1
    Can you post the exact code you're now using? When I try it, I get "Hi!" – Nate Eldredge Mar 25 '20 at 21:31
  • @stickynotes3: `xecv` isn't a function, and you still don't have `unistd.h`. Please copy and paste your exact new code instead of trying to manually apply the changes. – Nate Eldredge Mar 25 '20 at 21:50
  • @NateEldredge ok, sorry, now it's the same as mine – stickynotes3 Mar 25 '20 at 22:33
  • @stickynotes3: The current version of your code works correctly when I run it. – Nate Eldredge Mar 25 '20 at 22:36
  • @NateEldredge ok, stupid question, but did I running it corectly? cc main.c -o main and after that ./main? – stickynotes3 Mar 25 '20 at 22:39
  • @stickynotes3: Yes, and likewise `cc myfile.c -o myfile`. You might like to verify that running `./myfile` from the command line works, and to add some additional printfs (with `\n`!) to `main.c` to verify that it is actually getting run – Nate Eldredge Mar 25 '20 at 22:45
  • @NateEldredge if I compile myfile and run it sepeartely, works fine, but when I compile main it only prints Everything is ok... without printing from myfile – stickynotes3 Mar 25 '20 at 22:51
  • @stickynotes3: And you're certain that `main.c` is exactly the file you pasted above (you don't have an old version laying around or something)? What OS are you on, and what sort of environment are you running this in? – Nate Eldredge Mar 25 '20 at 22:54
  • @NateEldredge Ubuntu 16.04 in VMware Workstation player. I am using gedit to code and terminal for compile and run. – stickynotes3 Mar 25 '20 at 23:00
  • And you saved your file before compiling? – Nate Eldredge Mar 25 '20 at 23:01
  • @NateEldredge yes – stickynotes3 Mar 25 '20 at 23:07
0

Without a terminating newline, the output from either of your programs is not guaranteed to appear as you expect. Always output a terminating '\n'.

mlp
  • 799
  • 7
  • 20
0

If you want to provide the arguments through argv[] (as you indicate in your comments), that makes things very simple to pass to execv so long as you provide the Full-Path to the program to run as the first argument and any argument to pass to that program thereafter. argv is already an array of pointers with the first pointer after the last user-supplied argument set to NULL. You just start indexing at argv[1] (argv[0] is always the current program being run, argv[1] the first user-supplied argument) You can do something as simple as:

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

int main (int argc, char **argv) {

    if (argc < 2) {
        fprintf(stderr, "error: insufficient input.\n"
                        "usage: %s full-path-prog [arg1, arg2, ...]\n", argv[0]);
        return 1;
    }

    execv (argv[1], &argv[1]);      /* call execv with argv[] */
}

(note: on success, execv does not return, the new process replaces the current one, on failure, you should actually call _exit(); to avoid corner-case UB if you have set atexit() functions or destructors -- not relevant here)

So long as you provide a valid utility name and the arguments (not including any redirection, etc..) the code will simply pass the utility to execv and it will be executed with any additional arguments provided, e.g.

Example Use/Output

Providing the full path to the program and any arguments, e.g.

$ ./bin/execv_simple /bin/ls -al /home/david/tmp
total 1163032
drwxr-xr-x 44 david david         20480 Mar 23 13:35  .
drwxr-xr-x 68 david david          4096 Mar 25 02:36  ..
drwxr-xr-x  2 david david          4096 Nov  7 22:30  .qt
-rw-r--r--  1 david david       4534793 Nov  4 19:31  .xsession-errors
...

$ ./bin/execv_simple /usr/bin/df -h
Filesystem     1K-blocks     Used Available Use% Mounted on
devtmpfs         4043708        0   4043708   0% /dev
tmpfs            4055092    11964   4043128   1% /dev/shm
tmpfs            4055092     1716   4053376   1% /run
tmpfs            4055092        0   4055092   0% /sys/fs/cgroup
/dev/sdb2       41156156 25400864  13641604  66% /
tmpfs            4055092       60   4055032   1% /tmp
/dev/sdb3      437217592 43731284 392581260  11% /home
tmpfs             811016        8    811008   1% /run/user/1000

$ ./bin/execv_simple /usr/bin/free
              total        used        free      shared  buff/cache   available
Mem:        8110188     2147572     4048728       53432     1913888     5594460
Swap:       2103292           0     2103292

(note: you can use a command substitution with type -p or which to provide the full program name, e.g. ./bin/execv_simple $(type -p free) would fill in the full path information for you)

No arguments provided:

$ ./bin/execv_simple
error: insufficient input.
usage: ./bin/execv_simple full-path-prog [arg1, arg2, ...]

Look things over and let me know if you have further questions.

David C. Rankin
  • 69,681
  • 6
  • 44
  • 72