1

I am trying to call an external program from within my C program. I understand that calling system() is a bad habit. I've been reading up on how to use execl() or execv() to replace system(), but due to my limited brain capacity, I would like to ask for some examples and guidance.

If I was going to run

    int return_val = system("/usr/bin/someprogram arg1 arg2 arg3");

then is this what I should do instead?

    if ((pid = `fork()`) == -1)
       perror("fork error");
    else if (pid == 0) {
       execl("/usr/bin/someprogram",  "arg1", "arg2", "arg3", NULL);
       perror("Return not expected: execl error");
    }

I found a similar question on StackOverflow about replacing system() with execl(), but the answer (3 votes) did not talk about fork(). How to use execl as replacement for system Do I need fork() if I am waiting for the child process to finish so my own program can continue?

I know there is also execv() but my limited-capacity brain would prefer execl() unless someone tells me there is a disadvantage to that. Most examples on the web use execv instead of execl, probably because when I search for execl, Google keeps telling me about MS Excel functions.


I'll describe my use case but, in case I need it for other circumstances, would prefer general answers to my question over answers specific to my circumstances like, "Why are you even using $PROGRAM1 ? You should download $PROGRAM2 instead!"

I'm cobbling together a PyQt application for my child's Kubuntu system to limit Internet access to certain sites (youtube) to a certain number of hours per day. He can voluntarily turn off his own Internet access to save up access time for later; and when he turns it back on, my app will set the iptables rules according to how much time is left. Ideally I'd call "iptables" from within my Python app, but iptables needs to be executed as root (I don't want to sudo for a password or set up a passwordless sudo user account), and Python scripts aren't allowed to run as setuid root. Hence I'm writing a C program whose sole function is to run as setuid root and pass (sanitized) parameters to iptables:

    int return_val = system("iptables -A Parental_Timeblock -m time --weekdays Su,Mo,Tu,We,Th,Fr,Sa --timestart <sanitized argv params> --timestop <more sanitized argv params> -j DROP")

Haven't done C programming for decades. Would appreciate some hand-holding regarding the execl() calls. Thanks.

kwantum
  • 59
  • 6
  • 1
    Congratulations, you have found the only resource in the world that doesn't mention `fork` in this context. Perhaps they don't need `fork` because they do not continue their original process. You do need `fork`. – n. m. could be an AI Jul 29 '20 at 10:59
  • ... and dont forget to _exit() after the failed exec() – wildplasser Jul 29 '20 at 11:03
  • You can use `execl` directly if you're okay with the original process not existing any more. If you just call `execl`, it overwrites your process with the iptables process. If you want to make a different iptables process, you have to make a new process and then overwrite that one. But in your case you might be okay with just overwriting the original process ? – user253751 Jul 29 '20 at 11:15
  • 1
    You shouldn't use `exec` just because "`system` is a bad habit". There are reasons for not using `system` - `exec` is more efficient; doesn't do unwanted parsing on command line arguments; and possibly more. If none of the reasons apply to your use-case, just use `system`. See [here](https://stackoverflow.com/q/1697440/509868) for more info. BTW `system` is cross-platform, unlike `fork`. – anatolyg Jul 29 '20 at 12:28
  • system is implemented using fork + exec. It isn't bad to use system, its bad to be calling external processes when you could do the equivalent in your code. – stark Jul 29 '20 at 14:03

1 Answers1

1

Do I need fork() if I am waiting for the child process to finish so my own program can continue?

Without fork() your program will, like, not continue after execl, so you wouldn't be able to "wait". So you do need fork() if you want to have a "child" and a "parent" process. Otherwise it's the same process, that is replaced by a new executable.

Hence I'm writing a C program whose sole function is to run as setuid root and pass (sanitized) parameters to iptables:

Cool, so you do not need fork() - you do not need a parent process. Just call execl and become iptables.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • Thank you! Sounds like in this case I don't need `fork()`. In the generic case where I _am_ waiting for the process to finish, is my code above the correct general recipe to replace `spawnl()`, which I read all about and was all ready to implement until I found out it was Windows-only? Can I just paste that like a template into my future C programs (which I will hopefully have occasion to write)? Or is there some `_exit()` code that I need, as mentioned by wildplasser in the comment above? – kwantum Jul 29 '20 at 14:57
  • `correct general recipe` I do not think I believe in "correct general recipies". A 5min google search: https://comp.unix.programmer.narkive.com/qy7REFrB/qnx-spawnl-port-to-linux with example on how to replace `spawnl` with `fork+exec+wait` or with `posix_spawnp`. `Or is there some _exit() code that I need, as mentioned by wildplasser in the comment above?` Wildplasser was mentioning, that your code continues execution after failed attempt to `execl`, most probably you do not want to do that, you most probably want to terminate child execution. – KamilCuk Jul 29 '20 at 15:16