Giving control to shell from a C code? - c

How can I execute shell from a C code?
My shell is placed in /bin/sh
Following didn't seem to work for me
system("/bin/sh");
exec("/bin/sh");

Maybe you need to tell the shell it should be interactive:
system("/bin/sh -i");
However, I believe that your original system() call should have produced a shell prompt too.
Both notations (with and without the '-i') in this program give me a shell prompt (return to the previous shell by typing 'exit' and RETURN or Control-D):
#include <stdlib.h>
int main(void)
{
system("/bin/sh -i");
return 0;
}

This program works as expected for me:
int main()
{
int ret = system("/bin/sh");
printf ("Shell returned %d\n", ret);
return 0;
}
using -i causes some sort of redirection issue and everything hangs as soon as I type a command that produces output.
There are important differences between system() and exec(). system() is effectively the same as /bin/sh -c yourCommand on the command line, so the system("/bin/sh") is the same as
/bin/sh -c /bin/sh
This is why it is rarely used, because the command you want is executed by first starting an unnecessary shell process.
exec() causes the entire process image to be replaced by the command specified so if I had written:
int main()
{
int ret = exec("/bin/sh");
printf ("Shell returned %d\n", ret);
return 0;
}
The printf() and everything after it would never have been executed because the whole process transforms into an instance of /bin/sh. The proper way to run a child command is to fork and then exec in the child and wait in the parent.

Related

Pipes connected to stdio print output after new terminal prompt

I'm having trouble understanding how to do basic piping in C. I looked at a couple other questions on this topic, and either they were for subtly different issues, or I'm so far off the mark on this subject I couldn't understand why the answers are good for my problem.
This program below is just a simple test I made, where I'm trying to get behaviour equivalent to typing "ls | grep a" into my shell. (I have a homework assignment where I have to build a shell that can handle piping, but this is my first step towards understanding pipes to even attempt the homework). I get the correct output, but the terminal prompt ends up appearing before the output, making it look like it did not properly terminate. Since this is connected to a shell homework, I'm worried that will impact the grade (and it just feels wrong to let it look like that anyway). Any advice?
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
int main()
{
int fdpipe[2];
pipe(fdpipe);
int f1 = fork();
if(f1 == 0)
{
close(fdpipe[1]);
dup2(fdpipe[0],0);
close(fdpipe[0]);
execlp("/bin/grep","grep","a",NULL);
}
else
{
close(fdpipe[0]);
dup2(fdpipe[1],1);
close(fdpipe[1]);
execlp("/bin/ls","ls",NULL);
wait(NULL);
}
return 0;
}
Here's an example of my terminal output.
1067: ls
a.out test.c test.cc
NathanE: ~/Desktop/playground
1068: ./a.out
NathanE: ~/Desktop/playground
1069: a.out
(The beginning of this line is where my cursor is)
What I'm expecting would be:
1067: ls
a.out test.c test.cc
NathanE: ~/Desktop/playground
1068: ./a.out
a.out
NathanE: ~/Desktop/playground
1069: (my cursor would go here)
The child process runs grep, while the parent replaces itself with ls. The wait(NULL) does nothing, because successful exec*() never return.
Because the control returns to the shell immediately after ls completes, the shell can display the next prompt before grep completes.
There are two approaches you can use to avoid this:
fork() both child processes, and wait() for them
Replace the process itself with the last process in the pipe chain
Either will ensure that control is returned to the shell only after the last process in the pipe chain completes.

How can I use execl() function or other kinds of exec() functions to perform echo command?

I have a question, I used the following program(fork() functions) to create a child process to overload another program using execl(). If I want to use echo command in the execl() functions or other kinds of exec() functions ,what should I do? I use following program, but it failed! the terminal gave me a warning:echo: cannot access hello world!: No such file or directory
this is parent!
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{ pid_t childpid;
childpid=fork();
if(childpid==-1){
perror("failed to fork");
return 1;
}
if(childpid==0)
{
execl("/bin/ls","echo","hello world!",NULL);
perror("child failed to exec");
return 1;
}
if(childpid!=wait(NULL)){
perror("parent failed to wait due to signal or error");
return 1;
}
sleep(5);
printf("this is parent!\n");
return 0;
}
The command you are executing is:
/bin/ls "echo" "hello world!"
What you probably want to execute is (assuming you are using bash):
/bin/bash -c 'echo "hello world!"'
So use:
execl("/bin/bash","-c","echo \"hello world!\"",NULL);
And you probably want to check for errors before using perror.
Edit:
As per EOF's reccomendation you should probably use system call rather than exec. It handles creating the subprocess for you, and allows you to focus on the task at hand (namely invoking a shell command). The possible disadvantage is that it waits for the subprocess to die before continuing, but that's most likely not an issue (not in this case, anyway). It also ignores certain signals (SIGINT and SIGQUIT) which might not be desirable, depending on how you design your program.

execvp and fork not working as expected

I have the following code:
for (loop=0;loop<2;loop++)
{
child_pid = fork();
if (child_pid == 0)
{
rc = execvp ("/usr/local/some_program", arguments);
}
}
I change the arguments passed to /usr/local/some_program based on the value of loop. I want the some_program to parallel-ly execute two instances. But i see that i first execute some_program once and only after it finishes that i get execute the second instance of some_program.
I not able to get them to run parallelly and independently.
Instead of fork, should i use pthreads?
The some_program that i am trying to execute is completely unrelated to the parent process.
Thanks
"I not able to get them to run parallelly and independently." - No. Both process will be executed in parallel. To check that create two programs like this-
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
printf("Pid= %d\n", getpid());
while(1);
}
Compile those 2 program's and make two executable files. Pass these 2 exe file names via command line arguments to main program and run that using system command.But your main program should be live.
for (loop=0;loop<2;loop++)
{
child_pid = fork();
if (child_pid == 0)
{
system(argv[loop+1]);
exit(0); // if you don't use exit your second exe file will be executed twice.
}
}
while(1);
Now type ps command in terminal and find the TTY name and run the main program. Open a new terminal and type
ps -e | grep pts/X // pts/X is main program running terminal name. it may pts/0 or pts/1 ...
Through this you can come to know that the two processes running parallelly or not.

Trouble calling the Unix command kill(int PID) through c program

I am writing a program in C in one of my systems classes. We write c code and run it in a unix environment. I have looked all over the internet, but can't seem to find any way to make the kill(int PID) command work. The code will compile and run fine, but if I use the
ps -u username
command in a unix command prompt (after execution has completed of course,) it says that the all of the processes I tried to kill in my c code are still running. I can kill them from the unix command prompt by manually entering their PIDs, but for the life of me, I cannot figure out how to do it inside of my program.
In this particular program, I am trying to kill process CC, which is a process that just infinitely calls usleep(100); until terminated.
I tried using kill(C3, -9); and variations of execlp("kill", "kill", C3, (char *)0); but still no luck. Does anyone have any idea what I am doing wrong here? My only guess is that the kill command is being passed the wrong PID parameter, but if that's the case, I have no idea how I would get the correct one.
EDIT: Also, the kill command returns a value of zero, which I believe means that it "succeeded" in executing the command.
EDIT: Just noticed that the solution to my problem was in the instructions for the assignment all along. Yup. I'm stupid.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int args, char* argv[])
{
//
//Step 7
//
//Create process C3
int C3=fork();
if (C3==0)
{
execlp("CC", "CC", (char *)0);
}
else
{
usleep(500000);
//
//Step 8
//
int ps=fork();
if (ps==0)
{
execlp("ps", "ps", "-u", "cooley", (char *)0);
}
else
{
wait(NULL);
kill(C3);
}
}
exit(0);
}
You're calling the kill system call with only one argument, when it takes two. This leads to undefined behavior since then the second argument can by anything. You should get a warning about this when compiling.
The second argument should be a value from <signal.h> (see the signal(7) manual page).

Echo problems with execv() after fork()

AFAICS, the child process inherits stdout/stdin from the parent process on fork(). This leaves me wondering why the following code does NOT work:
int main(int argc, char *argv[])
{
char *earg[] = {"echo", "Hello", NULL};
if(fork() == 0) {
printf("running echo...\n");
execv("echo", earg);
printf("done!\n");
exit(0);
} else {
sleep(2);
}
return 0;
}
When running this little program, the two printf() calls appear just fine on the console. But the call to echo somehow gets lost! The output on the console is just:
running echo...
done!
Can somebody explain to me why the echo output doesn't appear on the console? And how I can fix this?
Since your printf("done") gets invoked your execv() clearly failed. All exec() functions do only return if an error occurred. Evaluating errno should help you to find out why it failed.
try using the whole path to echo:
execv("/bin/echo", earg);
EDIT: if you want to print done as soon as the child exited you should add a wait(NULL) call to your parent. see the manpage of wait() for more information and an example how to use it.
execv will not search for echo command in the PATH, so it fails, and it prints out "done" (which should not happen if execv is successful). You must supply the full path for execv to work
You may want to use execvp instead. It will search for the echo command in the PATH variable.

Resources