How to re-develop the sys-function "system()" cleanly? - c

I have to develop my own C function system. To do that, I use the call-system fork to create a child process and it has to execute the command given to system, calling exec.
What I've wrote seems to work fine (it compiles and executes without any error).
The problem concerns my function system's return (called mySystem in my code). For example, in my child process, if I give an nonexistent shell to exec (the latter returns -1), my child process stops with an exit code of -1 because I told it to do that. But : my parent process, which retrieves this exit code thanks to a wait(&status), returns... 255 and not -1 !
I don't understand why. I paid attention to use the macro WEXISTATUS in my mySystem's return.
Could you help me to know why my parent process doesn't return -1 (the exit code of its child process) please ? Thank you in advance.
My src (there are a lot of comments) :
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
pid_t pid;
int mySystem(char*);
int main(int argc, char* argv[]) {
int result = mySystem("ls");
fprintf(stdout, "%i", result);
return 0;
}
int mySystem(char* command) {
pid = fork();
if(pid == -1) {
perror("Fork");
return -1; // An error occurred => return -1
} else if (pid == 0) { // The child process will do the following
execl("BLABLABLABLA MAUVAIS SHELL BLABLABLAA", "sh", "-c", command, NULL); // If this call doesn't fail, the following lines are not read
perror("Exec"); // If (and only if) execl couldn't be called (bad shell's path, etc.)...
exit(-1); // ..., we stop the child process and this one has an exit code equaled to -1
}
/*
* NOW, the child process ended because... :
* 1. Either because of our "exit(-1)" after the "perror" (our source-code)
* 2. OR because of an "exit(-1") of the command passed into the execl (source-code of the execl's command)
* 3. OR because of the "exit(0)" of the command passed into the execl (source-code of the execl's command)
*/
// The parent process will execute the following lines (child process ended)
int status = -1;
if(wait(&status) == -1) { // We store into the var 'status' the exit code of the child process : -1 or 0
perror("Wait"); // Note that because we have only one process child, we don't need to do : while(wait(&status) > 0) {;;}
return -1;
}
return WEXITSTATUS(status); // Our function mySystem returns this exit code
}

Look at the following picture:
Where each of the 2 blocks is 8 bits (so total 16 bits).
Now you pass exit(-1) which in binary using 8 bits is: 11111111 (the two-complement) that's why you get 255 using WEXITSTATUS(status).
Another example to be clear: let's suppose to call exit(-6), -6 in binary two-complement is 11111010 that correspond to 250, if you make your program run you will see 250 printed on stdout.

Related

exit(1) does not give me 1 as an exit value?

Why when i execute this code inside a function in c
int Pandoc(char *file)
{
//printf("Pandoc is trying to convert the file...\n");
// Forking
pid_t pid;
pid = fork();
if (pid == -1)
{
perror("Fork Error");
}
// child process because return value zero
else if (pid == 0)
{
//printf("Hello from Child!\n");
// Pandoc will run here.
//calling pandoc
// argv array for: ls -l
// Just like in main, the argv array must be NULL terminated.
// try to run ./a.out -x -y, it will work
char *output = replaceWord(file, ".md", ".html");
//checking if the file exists
char *ls_args[] = {"pandoc", file, "-o", output, NULL};
// ^
// use the name ls
// rather than the
// path to /bin/ls
// Little explaination
// The primary difference between execv and execvp is that with execv you have to provide the full path to the binary file (i.e., the program).
// With execvp, you do not need to specify the full path because execvp will search the local environment variable PATH for the executable.
if(file_exist(output)){execvp(ls_args[0], ls_args);}
else
{
//Error Handeler
fprintf(stdout, "pandoc should failed with exit 42\n");
exit(42);
printf( "hello\n");
}
}
return 0;
}
I get 0 as a returned value?
EDIT:
Edit:
So here i changed the return value of the main to 5.
The exit value for my function above to 42 (idk why tho)
It gives me 5 as an output.. no idea whats hapening.
I shouldve mentionned that i use fork() in my code.. Maybe its the reason.
I think that my exit shut off the child process but the main continiues.. so thats why its giving me the returned value inside of my main and not the exit one.
Your child process exits with an exotic value, but your main process always exits with 0, and that's what determines $?.
If you want $? to be the exit value of the child process, you'll have to wait() for it, retrieve the child's exit code, and then exit your main process with it.

How to execute multiple processes in C?

I need to create a program that takes input from the user and executes it just like it does in the terminal. I am using the execvp() function for this purpose. The requirement of the program is to keep taking input from the user unless the quit call is encountered. The problem here is that the current program is replaced after the execvp() call. So, using a goto is not an option either. I found this Fork–exec article but it doesn't tell how to create an indefinite number of processes. Here is my code:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void main() {
char *args[4];
char inputCommand[100];
fgets (inputCommand, 100, stdin);
printf ("Splitting string \"%s\" into tokens:\n",inputCommand);
/* Perfrom string tokenization here */
execvp(args[0], args);
}
fork() can be called an indefinite number of times; as long as the return value indicates that you're the parent process, you can continue to operate as usual and call it again.
Thus, you can have a loop within which you fork, call an execvp() if you're the child process, and continue to the next iteration if you're the parent.
Execvp replaces the current process image with the command you run. So it cancels your C program. To produce the desired effect, you should fork before execvp. It would look something like this:
int status = 0;
pid_t pid = fork();
if(pid > 0) {
waitpid(pid, &status, 0);
// is parent
} else if(pid == 0) {
execvp(*args, args);
// is child
} else {
// fork didn't work
}

C - using exec() instead of system()

In the following code:
int main ( int argc, char *argv[] ) {
int i, pid, status;
for(i = 0; i < atoi(argv[1]); i++) {
pid = fork();
if(pid < 0) {
printf("Error occured");
exit(1);
} else if (pid == 0) {
status = system("./frun test.txt 1");
if (status != 0)
printf("ERROR, exited with %d\n", status);
else
printf("SUCCESS, exited with %d\n", status); // 0
exit(0);
} else {
wait(NULL);
}
}
return 0;
}
I am using system() to run a program called 'frun'
My question is - how would I change the code to use one of the exec() family functions instead?
My main problem is that I need to get the exit code of the program and I can't find a way to do it with exec(), while system() just returns the exit status.
Read the manual pages for wait and exec.
You wait on the process created from forking and execing (recall that exec replaces the current process, so you must fork to get its exit code). This is from the man page for wait:
Regardless of its value, this information may be interpreted using the following
macros, which are defined in <sys/wait.h> and evaluate to integral expressions; the
stat_val argument is the integer value pointed to by stat_loc.
WIFEXITED(stat_val)
Evaluates to a non-zero value if status was returned for a child process that
terminated normally.
WEXITSTATUS(stat_val)
If the value of WIFEXITED(stat_val) is non-zero, this macro evaluates to the
low-order 8 bits of the status argument that the child process passed to _exit()
or exit(), or the value the child process returned from main().
WIFSIGNALED(stat_val)
Evaluates to a non-zero value if status was returned for a child process that
terminated due to the receipt of a signal that was not caught (see <signal.h>).
WTERMSIG(stat_val)
If the value of WIFSIGNALED(stat_val) is non-zero, this macro evaluates to the
number of the signal that caused the termination of the child process.
WIFSTOPPED(stat_val)
Evaluates to a non-zero value if status was returned for a child process that is
currently stopped.
WSTOPSIG(stat_val)
If the value of WIFSTOPPED(stat_val) is non-zero, this macro evaluates to the
number of the signal that caused the child process to stop.
WIFCONTINUED(stat_val)
Evaluates to a non-zero value if status was returned for a child process that
has continued from a job control stop.
In your case you'd probably want to use WEXITSTATUS(stat_val) to get the 8 bit exit code.
You call waitpid(pid, stat_loc, options) where you'd pass the pid returned from fork(), a pointer to a local int (where the status is stored), and your options flags (or 0). You would do this is in the branch where pid != 0, since this is the original process. The original process calling fork() (where pid == 0) would call exec and thus be replaced by the exec'd command.
Here is a pseudo-example that you can adapt to your code:
pid_t pid;
int status;
pid = fork();
if (pid == 0)
{
exec(/*look up the different functions in the exec family and how to use them*/);
}
else if (pid < 0)
{
//uh oh
}
else
{
waitpid(pid, &status, 0);
printf("Exited with %d\n", WEXITSTATUS(status));
}
Though you should check the result of waitpid.

Explain why fork causes the IF condition to behave differently.

I found this sample via a search.
At first glance I thought only the first fork() would be executed based on the IF condition, but actually both fork() calls get executed.
If the fork() command is changed to a simple boolean comparison like (x==10) then the IF conditions behave as expected.
What is it about the fork() that causes the IF condition to behave differently?
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
pid_t whichone, first, second;
int howmany;
int status;
int x = 0
if ((first=fork())==0) /* Parent spawns 1st child */
{
printf("Hiya, I am the first child nd my id is %d\n", getpid());
sleep(10);
exit(0);
}
else if (first == -1)
{
perror("1st fork: something went bananas\n");
exit(1);
}
else if ((second=fork())==0) /* Parent spawns 2nd child */
{
printf("Hiya, I am the second child and my id is %d\n", getpid());
sleep(15); /* Sleep 15 sec, then exit */
exit(0);
}
else if (second == -1)
{
perror("2nd fork: something went bananas\n");
exit(1);
}
printf("This is the parent\n");
howmany=0;
while (howmany < 2) /* Wait twice */
{
whichone=wait(&status);
howmany++;
if (whichone==first)
printf("First child exited ");
else
printf("Second child exited ");
if ((status & 0xffff)==0)
printf("correctly\n");
else
printf("uncorrectly\n");
}
return 0;
Here is the output when executed. Notice that both fork() calls are processed.
> runtThis
Hiya, I am the first child, and my id is 31204
Hiya, I am the second child, and my id is 31205
This is the parent
First child exited correctly
Second child exited correctly
The first if detects if its running in the child. If it isn't, but fork() didn't fail (i.e., it didn't return -1), it's in the original process, which can then continue and call fork() again.
Not sure what the confusion is about.
The fork() function duplicates the current process and then returns in both processes. So, effectively, the fork() function returns twice.
In the new process, fork() returns with the value 0, which causes the relevant branch that prints "I am the child" to be taken.
In the original process, fork() returns a non-zero value, indicating either failure (if it is negative) or the process id of the new process (if it is positive). As this value is non-zero, the branch is skipped to the next else if and that test is performed until one succeeds.
In the parent process, none of the if/else if tests will succeed, but each of the fork calls causes a new process to be created in which the corresponding test succeeds and that creates the effects you noticed.

Creating a child process on Unix systems?

I'm trying to create a child process in another process. I am writing both the programs in C language. First I write a dummy process which will be the child process. What it is doing is only to write a string on the screen. It works well on its own. Then I write another program which will be the parent process. However, I can't make it happen. I'm trying to use fork and execl functions together, but I fail. I also want the child process does not terminate until the parent process terminates.
How should I write the parent process?
Thanks.
Here is the code for the child process:
#include <stdio.h>
int main(void) {
while(1) {
printf("*");
sleep(1);
}
}
And here is the parent process:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) {
if (fork()) {
while(1) {
printf("-\n");
sleep(5);
}
} else {
execl("./", "dummy", (char *)0);
}
}
The fork() system call may return three different statuses: failure (<0), parent process (>0) or child process (==0). You must test the return value properly.
int pid = fork();
if (pid < 0) {
/* handle error */
perror("fork");
exit(1);
} else if (pid > 0) {
/* parent code */
} else {
/* child code */
}
Your execl() system call is wrong. The first argument is the path to the program you want to execute, "./" is not valid, it should be something like "./dummy" at least. The next argument is by convention the command name (argv[0] in the executed program), which may be a repetition of the first argument. So:
execl("./dummy", "dummy", NULL);
Also, note that the printf("*") statement in the child program will probably buffer and you won't see anything on the terminal. You must either add a "\n" to the end or call fflush(stdout) to flush the standard output.
Basic use of fork in C
int PID = fork();
if( PID < 0 ) {
//fail
return PID;
}
else if( !PID ) {
//child process
return exec( prog, args );
}
else {
//parent process
return 0;
}
There is no way to force the child process to "not terminate" when it's done (you'll still be able in the parent to wait for it to get info on how it terminated, but that's about it). Apart from that, any of the many examples of fork/exec on the web, such as this one, should work -- why don't you try it and see if it performs as you wish (in which case you'll just need to change whatever you were doing differently in your own attempt). If it doesn't work as desired (except for the impossibility per the first sentence in this A;-), please edit your to add copious detail about how the code behaves differently than you expect it to.

Resources