I'm writing a shell as a hobby and more specifically because I'm bored but passionate. My shell works great, I even implemented pipelines. But there is one thing that keeps my shell crashing or entering in a for loop and it's only happening when I run bash through my shell.
So I'm in trouble when I issue this command bash -ic <some_command>. If my shell is the default one and I launch an instance and I issue this command above, my shell gets in an infinite loop. Whereas if the default shell is bash and I launch an instance then I run my shell through the first bash prompt and THEN run bash -ic <some_command> through my shell, it gets stopped and I'm back to bash.
Here is a minimal example of the main issue:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void loop_pipe(char ***cmd)
{
pid_t pid, wpid;
int status;
pid = fork();
if (pid == 0)
{
// Child process
if (execvp((*cmd)[0],*cmd) == -1)
perror("Command error");
exit(EXIT_FAILURE);
}
else if (pid < 0)
{
// Error forking
perror("Fork error");
}
else
{
// Parent process
do {
wpid = waitpid(pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
}
int main()
{
char *ls[] = {"bash", "-ic", "uname", NULL};
char **cmd[] = {ls, NULL};
while (1)
{
loop_pipe(cmd);
}
}
The problem here is that after running the command, the process gets stopped so the output is this:
./a.out
Linux
[4]+ Stopped ./a.out
I really don't now what's causing this, but it has to do with bash conflicting with my shell. I also tried to ignore SIGINT and SIGSTOP But it still gets stopped (by bash i guess ?) If someone could help the situation that would be great.
Because my project has more than one source file, I link it not sure if it's right way to do it.
Related
So when i invoke this program without sudo. It works fine.
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char** argv)
{
if(fork() == 0) execvp(argv[1], &argv[1]);
// else wait(NULL);
}
But with sudo (when i need to input my password) it gives an odd output:
pasha#skynet:~$ sudo ./a.out bash
[sudo] password for pasha:
pasha#skynet:~$ root#skynet:~#
Then on any input the terminal terminates. Further it only happens on a newly spawned terminal. And when the parent waits for the child the problem with sudo disappears.
Can someone explain why ?
why this happens
You are forking your process, so there are two processes now.
One process is the parent, the process run by your shell, like shell -> fork() -> exec(sudo) -> exec(./a.out). The parent terminates, because fork returns with nonzero and then main() reaches closing }. main by default returns 0. So shell sees that your program terminated with exit status 0. And your shell greets you with a new pasha#skynet:~$ prompt line after your program is done.
The other process is the child, run from your program where fork returned zero, like shell -> fork() -> exec(sudo) -> exec(./a.out) -> fork() -> exec(bash). The child process is bash, it prints root#skynet:~# (it was run after sudo) and waits for input.
Those two processes are running at the same time - ie. your shell (from which you executed sudo ./a.out) and the newly bash run from your program. Both those programs try to read and write to the same input and output at the same time.
The child process, ie. bash, needs to have exclusive control over the input in the terminal. So the child process bash executes tcsetpgrp. But your shell is that one that is controlling your terminal, not the child process. So the child process either receives signal SIGTTOU or maybe SIGTTIN upon trying to read from the input. Then the child bash executed the default handler for the signals - it terminates.
Running sudo bash & from your shell would cause a similar problem to the one that your program causes.
Your program is correct; try it out with "ls" instead of "bash",
$ ./a.out ls -al /tmp
The reason why it does not seem to work with bash is that bash
expect the process to be the group leader of the terminal foreground
process
group, which
it isn't.
That said, while the program is correct, it's severe lack of error
handling is offending :-). For example, when calling a program that
does not exist, execvp() returns with an error (as opposed to not
returning at all) which is ignored. With the effect that ... well
... you can only guess if it worked.
$ ./a.out frobozzzzz
$ # (hm)
Here's my incarnation of it. Longer. Handling errors. Seeing how it
went after child terminated.
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(int argc, char** argv)
{
int status;
pid_t pid, terminated;
pid = fork();
if (pid == -1 /*unlikely*/) {
perror("fork()");
exit(EXIT_FAILURE);
}
if (pid == 0 /*child*/) {
if (execvp(argv[1], &argv[1]) != 0) { // when argv[1] is no
// progrm in path
perror("execvp()");
exit(EXIT_FAILURE);
}
else
assert(!"not getting here because successful exec() never returns");
}
// optional: wait for child to terminate, and print diagnostics
terminated = waitpid(pid, &status, 0);
if (terminated == -1) {
perror("waitpid()");
exit(EXIT_FAILURE);
}
if (terminated == pid) { // how come those not be equal?
if (WIFEXITED(status))
fprintf(stderr, "child terminated with exit status %d\n", WEXITSTATUS(status));
else if (WIFSIGNALED(status))
fprintf(stderr, "child terminated by %d\n", WTERMSIG(status));
else
fprintf(stderr, "see \"man waidpid\" for what that could be\n");
}
return 0;
}
I'm currently trying to experiment with signals in C by using them to control a child process created with the fork() method. Essentially, I have a child process running the "yes" command from the linux terminal (this command just prints "y" and a newline until it is terminated). I want to be able to pause/resume this process with CTRL-Z. This is what i've got right now:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
pid_t CHILD_PROCESS;
pid_t PARENT_PROCESS;
int isPaused;
void pause_handler(int signo){
if(!isPaused){
printf("Ctrl-Z pressed. Pausing child.\n");
isPaused = 1;
kill(CHILD_PROCESS,SIGSTOP);
}
else if(isPaused){
printf("\nCtrl-Z pressed. Resuming child.\n");
kill(CHILD_PROCESS,SIGCONT);
isPaused = 0;
}
}
int main(int argc, char** argv){
pid_t pid;
PARENT_PROCESS = getpid();
pid = fork();
if(pid == 0){
system("yes");
}
isPaused = 0;
if(pid > 0){
signal(SIGTSTP, SIG_IGN);
signal(SIGSTOP, SIG_IGN);
CHILD_PROCESS = pid;
while(1){
if(signal(SIGTSTP,pause_handler) == SIG_ERR){
printf("Signal Failure");
}
}
}
}
When I run this, I can get "Ctrl-Z pressed. Pausing child." to print to console by pressing CTRL-Z, and I can get "Ctrl-Z pressed. Resuming child." to print to the console by pressing CTRL-Z again. However, it doesn't actually resume printing "y" over and over again. Any ideas as to why the child process isn't resuming?
As it turns out, system has an implicit fork call within it, so the PID that gets stored in CHILD_PROCESS ends up not actually being the child process, and instead an intermediate one.
From man 3 system:
The system() library function uses fork(2) to create a child process
that executes the shell command specified in command using execl(3) as
follows:
execl("/bin/sh", "sh", "-c", command, (char *) 0);
system() returns after the command has been completed.
So, if we replace the system("yes") call with execl("/bin/sh", "sh", "-c", "yes", NULL), then we avoid this extra fork and the program functions as desired.
The only other issue is that, by I comment I found on this post, using printf within a signal handler is undefined behavior. Not an issue to worry about here, but something to keep in mind for future code!
I'm currently trying to run an Internet browser (Firefox) as a child process in a C program and perform actions on it. At first I'd like to get child's pid and kill it with parent.
After some researches I've chosen to use Fork/exec to create a child process.
But when I execute my code, the two programs doesn't run simultaneously.
After I opened my browser, I can't do anything before closing it.
My code looks like that
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
//version fork
pid_t pid;
char *parmList[] = {"firefox", "google.com", NULL};
int a;
printf("test001");
if ((pid = fork()) == -1)
perror("fork failed");
if (pid == 0) {
printf("test002");
a = execvp("/usr/bin/firefox", parmList);
printf("test003");
}
else {
waitpid(pid, 0, 0);
printf("test004");
}
return 0;
}
For now I got navigator running with some errors due to Firefox.
And I get this kind of output :
root#debian:/home/user/Documents/Projet/git_sources/ProjetSIDA# ./a.out
(process:3858): GLib-CRITICAL **: g_slice_set_config: assertion 'sys_page_size == 0' failed
Gtk-Message: Failed to load module "canberra-gtk-module"
console.error:
[CustomizableUI]
Custom widget with id loop-button does not return a valid node
console.error:
[CustomizableUI]
Custom widget with id loop-button does not return a valid node
And this last line when I close iceweasel:
test001test004root#debian:/home/user/Documents/Projet/git_sources/ProjetSIDA#
You are not able to do anything with your command prompt when your browser is on is because this line of code.
waitpid(pid, 0, 0);
If you comment it out, you can open the browser and get your command prompt back to accept input again.
The reason is your are asking your main process(a.out in your case) to wait for the browser process to change its state with waitpid(pid, 0, 0);.
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.
I've been working on my school project and I've been stuck on this step for a few days now. Any kind of help would be highly appreciated!
What I've tried so far:
Compiling the script. It compiles correctly and I'm able to run it by typing ./process.o, however I cannot make it so when I kill it, it restarts. I've been googling and trying various things but nothing seems to work, it always kills the process but doesn't restart it.
kill -SIGKILL (PID)
kill 2 (PID)
kill 1 (PID)
kill -HUP 3155
Various other commands that only killed it, nothing seems to work. Do I have to modify the code or something? I'm deeply confused.
Here's what I have to do:
Make a new file with C. Save it with name process.c (Did this)
#include <stdio.h>
#include <unistd.h>
int main() {
printf("Creating a background process..\n");
pid_t pid = fork();
if (pid > 0) return 0; /* Host process ends */
if (pid < 0) return -1; /* Forking didn't work */
while(1) { } /* While loop */
return 0;
}
Compile the following code to working program called process.o and start the process. (Did this, works to this point)
Use a kill command which restarts the process.o (Killing the process works, but it doesn't restart it)
You need to keep the parent process running to monitor the child process. If the parent detects that the child is no longer running, it can restart it.
The parent can use the wait system call to detect when the child exits.
while (1) {
pid_t pid = fork();
if (pid < 0) {
return -1;
} else if (pid > 0) {
// parent waits for child to finish
// when it does, it goes back to the top of the loop and forks again
wait(NULL);
} else {
// child process
while (1);
}
}