This question already has answers here:
How to bring a child process running in the background to the foreground
(3 answers)
Closed 9 years ago.
I write similarity of the command bash interpreter.
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <wait.h>
#include "shell.h"
#include <string.h>
char *infile;
char *outfile;
char *appfile;
struct command cmds[MAXCMDS];
char bkgrnd;
int k = 0;
static void my_wait(pid_t pid, pid_t bkgrnds[]);
int main(int argc,char *argv[])
{
int i;
char line[1024]; /* allow large command lines */
int ncmds;
char prompt[50]; /* shell prompt */
pid_t bkgrnds[1024]; /* for fg */
/* PLACE SIGNAL CODE HERE */
signal(SIGINT, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
sprintf(prompt,"[%s] ",argv[0]);
while (promptline(prompt, line, sizeof(line)) > 0)
{ /* l eof */
if (0 >= (ncmds = parseline(line)))
{
continue; /* read next line */
}
#ifdef DEBUG
{
int i, j;
for (i = 0; i < ncmds; i++)
{
for (j = 0; NULL != cmds[i].cmdargs[j]; j++)
{
fprintf(stderr, "cmd[%d].cmdargs[%d] = %s\n", i, j, cmds[i].cmdargs[j]);
}
fprintf(stderr, "cmds[%d].cmdflag = %o\n", i, [i].cmdflag);
}
}
#endif
{
int previous_pipe_output = -1;
int pipeline_start = 0;
pid_t pids[MAXCMDS];
fprintf(stderr, "%d %s\n", ncmds, cmds[0].cmdargs[0]);
if (1 == ncmds && 0 == strcmp(cmds[0].cmdargs[0], "fg"))
{
if (0 == k)
{
fprintf(stderr, "k == 0\n");
continue;
}
int num = -1;
signal(SIGTTOU, SIG_IGN);
if (0 == cmds[0].cmdargs[1])
{
num = k - 1;
}
else
{
num = atoi(cmds[0].cmdargs[1]);
}
fprintf(stderr, "%d\n", bkgrnds[num]);
if(-1 == tcsetpgrp(STDIN_FILENO, bkgrnds[num]))
{
perror("Couldn't set terminal foreground process group");
return EXIT_FAILURE;
}
signal(SIGTTOU, SIG_DFL);
my_wait(bkgrnds[num], bkgrnds);
signal(SIGTTOU, SIG_IGN);
if (-1 == tcsetpgrp(STDIN_FILENO, getpgrp()))
{
perror("Couldn't set terminal foreground process group");
return EXIT_FAILURE;
}
signal(SIGTTOU, SIG_DFL);
continue;
}
for (i = 0; i < ncmds; i++)
{
int pipefd[2] = {-1, -1};
if(cmds[i].cmdflag & OUTPIP)
{
if(-1 == pipe(pipefd))
{
perror("Couldn't create pipe");
return EXIT_FAILURE;
}
}
{
/* FORK AND EXECUTE */
pids[i] = fork();
if (0 == pids[i])
{
signal(SIGINT, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
if(-1 == setpgid(0, (cmds[i].cmdflag & INPIP)? pids[pipeline_start]:0))
{
perror("Couldn't set process group ID");
return EXIT_FAILURE;
}
if (!bkgrnd && !(cmds[i].cmdflag & INPIP))
{
signal(SIGTTOU, SIG_IGN);
if(-1 == tcsetpgrp(STDIN_FILENO, getpgrp()))
{
perror("Couldn't set terminal foreground process group");
return EXIT_FAILURE;
}
signal(SIGTTOU, SIG_DFL);
}
//....
execvp(cmds[i].cmdargs[0], cmds[i].cmdargs);
perror("Couldn't execute command");
return EXIT_FAILURE;
}
else if (-1 != pids[i])
{
if (!bkgrnd)
{
if (!(cmds[i].cmdflag & OUTPIP))
{
{
int j = 0;
for (j = pipeline_start; j <= i; j++)
{
my_wait(pids[j], bkgrnds);
}
}
signal(SIGTTOU, SIG_IGN);
if (-1 == tcsetpgrp(STDIN_FILENO, getpgrp()))
{
perror("Couldn't set terminal foreground process group");
return EXIT_FAILURE;
}
signal(SIGTTOU, SIG_DFL);
}
}
else
{
printf("Background process ID: %ld\n", (long)pids[i]);
}
}
else
{
perror("Couldn't create process");
}
}
}
}
} /* close while */
return EXIT_SUCCESS;
}
static void my_wait(pid_t pid, pid_t bkgrnds[])
{
int status = 0;
if (-1 != waitpid(pid, &status, WUNTRACED))
{
if (WIFSTOPPED(status) && (SIGTSTP == WSTOPSIG(status)))
{
bkgrnds[k++] = pid;
//printf("%d %d\n", pid, k);
kill(pid, SIGCONT);
bkgrnd = 1;
printf("Background process ID: %ld\n", (long)pid);
}
}
else
{
perror("Couldn't wait for child process termination");
}
}
Now I want to implement a command fg.
if (1 == ncmds && 0 == strcmp(cmds[0].cmdargs[0], "fg"))
{
if (0 == k)
{
fprintf(stderr, "k == 0\n");
continue;
}
int num = -1;
signal(SIGTTOU, SIG_IGN);
if (0 == cmds[0].cmdargs[1])
{
num = k - 1;
}
else
{
num = atoi(cmds[0].cmdargs[1]);
}
fprintf(stderr, "%d\n", bkgrnds[num]);
if(-1 == tcsetpgrp(STDIN_FILENO, bkgrnds[num]))
{
perror("Couldn't set terminal foreground process group");
return EXIT_FAILURE;
}
signal(SIGTTOU, SIG_DFL);
my_wait(bkgrnds[num], bkgrnds);
signal(SIGTTOU, SIG_IGN);
if (-1 == tcsetpgrp(STDIN_FILENO, getpgrp()))
{
perror("Couldn't set terminal foreground process group");
return EXIT_FAILURE;
}
signal(SIGTTOU, SIG_DFL);
continue;
}
Question : PID I know how do I bring to the foreground?
That is if I import
cat
CTRL + Z
fg
<- here shall continue cat
Thanks in advance.
Sorry. I just forgot to send SIGCONT process and everything. Added and working. kill(bkgrnds[num], SIGCONT);
Related
I am attempting to send SIGUSR1 to my child process, child_b, after my parent process waits. However, my handler for sigusr1 is not executing and printing my message "pong quitting" at the end of the output.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>
int fd[2][2];
void sig_handler(int signum) {
if (signum == SIGUSR1) {
printf("pong quitting\n");
exit(1);
}
}
void f1() {
int x = 0;
close(fd[0][0]);
close(fd[1][1]);
while (x <= 100) {
printf("ping: %d\n", x);
x++;
if (write(fd[0][1], &x, sizeof(int)) < 0) {
printf("Error writing f1\n");
}
if (read(fd[1][0], &x, sizeof(int)) < 0) {
printf("Error reading f1\n");
}
}
close(fd[0][1]);
close(fd[1][0]);
}
void f2() {
int x = 0;
close(fd[0][1]);
close(fd[1][0]);
while (x <= 100) {
if (read(fd[0][0], &x, sizeof(int)) < 0) {
printf("Error reading f2\n");
}
if (x <= 100)
printf("pong: %d\n", x);
x++;
if (write(fd[1][1], &x, sizeof(int)) < 0) {
printf("Error writing f2\n");
}
}
close(fd[0][0]);
close(fd[1][1]);
}
int main(void) {
for (int i = 0; i < 2; i++) {
if (pipe(fd[i]) < 0) {
printf("Error opening pipe.\n");
return 1;
}
}
pid_t child_a = fork();
if (child_a < 0) {
printf("Error forking child_a.\n");
return 2;
}
if (child_a == 0) {
f1();
} else {
pid_t child_b = fork();
if (child_b < 0) {
printf("Error forking child_b.\n");
return 3;
}
if (child_b == 0) {
signal(SIGUSR1, sig_handler);
f2();
}
else {
waitpid(child_a, NULL, 0);
waitpid(child_b, NULL, 0);
kill(child_b, SIGUSR1);
}
}
return 0;
}
I've tried sleeping both the parent and child, as well as rearranging the order of wait and kill; however, I can not get the signal handler to execute. Any help would be appreciated.
Killing after wait is just fundamentally wrong: the program is dead and gone, you'd either be getting ESCHR or kill an innocent unrelated process.
If you move the wait before kill, then the problems become child_b finishing two quickly (try running it under strace -f and see child_b is already a zombie by the time the kill runs). Adding some sleep(a_lot); or pause(); at the end of child_b would help there.
if (child_b == 0) {
signal(SIGUSR1, sig_handler);
f2();
pause(); //+
}
else {
waitpid(child_a, NULL, 0);
kill(child_b, SIGUSR1); //^
waitpid(child_b, NULL, 0); //v
}
For added async-signal safety, you could also change the handler to be async-signal-safe:
void sig_handler(int signum) {
char m[]="pong quitting\n";
write(1,m,sizeof(m)-1);
_exit(1);
}
I've written a basic shell in C, and I'm trying to catch a SIGTSTP signal from a child process. To do this, I've set up a handler for SIGCHLD, but the signal number being returned is 20, when it should be 24.
I have my SIGCHLD handler:
signal(SIGCHLD, trapChld);
void trapChld(int signo) {
printf("%d", signo);
}
This prints signal 20 when kill -SIGTSTP child_pid is run. Why might this be happening?
Here's my full code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
int statusCode;
int foregroundMode = 0;
int bg = 0;
int bgPsArray[20];
int bgPsCount = 0;
int i;
char line[256];
pid_t popBgProcess() {
int size = sizeof(bgPsArray)/sizeof(bgPsArray[0]);
if (size > 0) {
return bgPsArray[size+1];
} else {
return 0;
}
}
void trapInterrupt(int _) {
int childStatus;
pid_t child;
while ((child = popBgProcess())) {
if(child != getpid()) {
kill(child, SIGKILL);
waitpid(child, &childStatus, 0);
}
}
}
void trapChld(int signo) {
printf("%d", signo);
if(signo == 24) {
if(foregroundMode == 0) {
write(1, "Entering foreground-only mode (& is now ignored)\n", 49);
write(1, ": ", 2);
fflush(stdout);
foregroundMode = 1;
} else {
write(1, "Exiting foreground-only mode\n", 29);
write(1, ": ", 2);
fflush(stdout);
foregroundMode = 0;
}
}
}
int getCommand() {
printf(": ");
fflush(stdout);
if(fgets(line, sizeof(line), stdin) != NULL) {
char *position = strchr(line, '\n');
*position = '\0'; // Replace '\n' with '\0'
if(foregroundMode == 1) { // Foreground mode on
if((position = strchr(line, '&')) != NULL) {
*position = '\0'; // Replace '&' with '\0'
}
bg = 0; // Ignore '&' so do not create background process
} else { // Foreground mode off
if((position = strchr(line, '&')) != NULL) {
*position = '\0'; // Replace '&' with '\0'
bg = 1; // Is a background process
} else {
bg = 0;
}
}
} else { // If input is null
return 0;
}
return 1;
}
void checkProcessCompletion() {
int status;
for(i=0; i<bgPsCount; i++) {
if(waitpid(bgPsArray[i], &status, WNOHANG) > 0) {
if(WIFEXITED(status)) { // If exit
printf("Background PID %d is done: exit value %d\n", bgPsArray[i], WEXITSTATUS(status));
fflush(stdout);
} else if(WIFSIGNALED(status)) { // If signal
printf("Background PID %d is done: terminated by signal %d\n", bgPsArray[i], WTERMSIG(status));
fflush(stdout);
}
}
}
}
int runCommand(int cmd) {
if(cmd == 0) { // Return if there was no command
return 0;
} else if(strcmp(line, "exit") == 0) {
exit(0);
} else if(strstr(line, "#")) { // Comment input (do nothing)
} else if(strcmp(line, "status") == 0) {
printf("exit value %d\n", statusCode);
fflush(stdout);
}
else if(strncmp("cd", line, strlen("cd")) == 0) {
if(line[2] == ' ') { // If space after 'cd' expect directory
char cwd[1024];
getcwd(cwd, sizeof(cwd));
char *path = strstr(line, " ");
if(path) {
path += 1;
char *value;
value = malloc(strlen(path));
memcpy(value, path, strlen(path));
*(value + strlen(path)) = 0;
sprintf(cwd, "%s/%s", cwd, value); // Directory to change to
free(value);
}
chdir(cwd); // cd to new directory
} else { // cd with no argument
char *home = getenv("HOME");
chdir(home); // cd to HOME directory
}
}
else { // System commands
pid_t pid, ppid;
int status;
char *command;
char *args[256];
int argCount;
command = strtok(line, " ");
// Create args array for execvp
args[0] = command;
argCount = 1;
args[argCount] = strtok(NULL, " ");
while(args[argCount] != NULL) { // Add arguments to array
argCount++;
args[argCount] = strtok(NULL, " ");
}
if((pid = fork()) < 0) { // Fork fails
perror("fork");
fflush(stdout);
exit(1);
}
if(pid == 0) { // Child process
for(i=0; i<argCount; i++) {
if(strcmp(args[i], "<") == 0) { // Redirecting input
if(access(args[i+1], R_OK) == -1) { // File is unreadable
perror("access");
fflush(stdout);
} else { // File is readable
int file = open(args[i+1], O_RDONLY, 0);
dup2(file, STDIN_FILENO);
close(file);
execvp(command, &command);
}
}
else if(strcmp(args[i], ">") == 0) { // Redirecting output
int file = creat(args[i+1], 7777);
dup2(file, STDOUT_FILENO);
close(file);
execvp(command, args);
} else { // No redirection
execvp(command, args);
}
}
perror("execvp"); // Error for execvp
exit(1);
} else { // Parent process
if (bg == 1) { // Background process
int status;
int process;
printf("Background PID: %d\n", pid);
fflush(stdout);
bgPsArray[bgPsCount] = pid; // Add process to background process array
bgPsCount++;
process = waitpid(pid, &status, WNOHANG);
} else { // Foreground process
int status;
waitpid(pid, &status, 0); // Wait on the process
if(WIFEXITED(status)) {
statusCode = WEXITSTATUS(status);
}
}
}
}
return 1;
}
int main(int argc, char *argv[], char *envp[]) {
// Creating 'junk' manually is necessary because output redirection is broken,
// and a large portion of the grading script is depedent upon it's existence.
FILE *fp = fopen("junk", "ab+");
const char *text;
fprintf(fp, "Junk in junkfile\n");
fclose(fp);
signal(SIGINT, trapInterrupt);
signal(SIGCHLD, trapChld);
while(1) {
checkProcessCompletion(); //Check the processes
int cmd = getCommand(); // Get command from user
int result = runCommand(cmd);
if (result == 0) {
break;
}
}
return 0;
}
You haven't told us what platform you're running on, so this is just a guess, but perhaps it's because that platform defines SIGTSTP as 20?
Linux does, for example:
$ grep SIGTSTP /usr/include/asm/signal.h
#define SIGTSTP 20
A better question is why do you think it should be 24? On AIX it's 18. On HP-UX it's 25. Various Cygwin headers define it as 8, 18, or 24 (because the Cygwin headers come from glib and are full of platform-specific conditional-compilation shenanigans); 18 is the actual value used at runtime.
On Solaris, now, it happens to be 24. I believe Solaris 2 inherited that from SVR4, and subsequent Solaris releases kept it. But the signal numbers are not standardized by any of the applicable specifications (SUS and its ancestors, such as POSIX and XPG3).
Don't assume the signal values are fixed. That's why you have signal.h.
Oh, and sigaction(2) is preferable to signal(2) on platforms that support it, which is most of them.
I've written a shell in C that does has some basic functionality. I've implemented a 'foreground-only mode' in my program which is toggled with SIGTSTP or CTRL-Z. However, I am able to trap the CTRL-Z signal, but not the SIGTSTP signal. How is this possible? Isn't CTRL-Z the same thing as SIGTSTP? Shouldn't trapping one trap the other?
I trap the signal (I realize signal() is deprecated, but I have tried sigaction() as well, and the problem persists.):
signal(SIGTSTP, trapTstp);
And handle it with this function:
void trapTstp() {
if(foregroundMode == 0) {
write(1, "Entering foreground-only mode (& is now ignored)\n", 49);
write(1, ": ", 2);
fflush(stdout);
foregroundMode = 1;
} else {
write(1, "Exiting foreground-only mode\n", 29);
write(1, ": ", 2);
fflush(stdout);
foregroundMode = 0;
}
}
If I run my program and hit CTRL-Z, I can successfully toggle in and out of foreground-only mode. However, I cannot trap SIGTSTP. For example, if I run sleep 100, get the PID for it, and run kill -SIGTSTP sleep_pid, the sleep 100 process gets killed early with the output Terminated: 15 indicating it was in fact killed, and thus the SIGTSTP signal was not trapped.
Here is my full shell program:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
int statusCode;
int foregroundMode = 0;
int bg = 0;
int bgPsArray[20];
int bgPsCount = 0;
int i;
char line[256];
pid_t popBgProcess() {
int size = sizeof(bgPsArray)/sizeof(bgPsArray[0]);
if (size > 0) {
return bgPsArray[size+1];
} else {
return 0;
}
}
void trapInterrupt(int _) {
int childStatus;
pid_t child;
while ((child = popBgProcess())) {
if(child != getpid()) {
kill(child, SIGKILL);
waitpid(child, &childStatus, 0);
}
}
}
void trapTstp() {
if(foregroundMode == 0) {
write(1, "Entering foreground-only mode (& is now ignored)\n", 49);
write(1, ": ", 2);
fflush(stdout);
foregroundMode = 1;
} else {
write(1, "Exiting foreground-only mode\n", 29);
write(1, ": ", 2);
fflush(stdout);
foregroundMode = 0;
}
}
int getCommand() {
printf(": ");
fflush(stdout);
if(fgets(line, sizeof(line), stdin) != NULL) {
char *position = strchr(line, '\n');
*position = '\0'; // Replace '\n' with '\0'
if(foregroundMode == 1) { // Foreground mode on
if((position = strchr(line, '&')) != NULL) {
*position = '\0'; // Replace '&' with '\0'
}
bg = 0; // Ignore '&' so do not create background process
} else { // Foreground mode off
if((position = strchr(line, '&')) != NULL) {
*position = '\0'; // Replace '&' with '\0'
bg = 1; // Is a background process
} else {
bg = 0;
}
}
} else { // If input is null
return 0;
}
return 1;
}
void checkProcessCompletion() {
int status;
for(i=0; i<bgPsCount; i++) {
if(waitpid(bgPsArray[i], &status, WNOHANG) > 0) {
if(WIFEXITED(status)) { // If exit
printf("Background PID %d is done: exit value %d\n", bgPsArray[i], WEXITSTATUS(status));
fflush(stdout);
} else if(WIFSIGNALED(status)) { // If signal
printf("Background PID %d is done: terminated by signal %d\n", bgPsArray[i], WTERMSIG(status));
fflush(stdout);
}
}
}
}
int runCommand(int cmd) {
if(cmd == 0) { // Return if there was no command
return 0;
} else if(strcmp(line, "exit") == 0) {
exit(0);
} else if(strstr(line, "#")) { // Comment input (do nothing)
} else if(strcmp(line, "status") == 0) {
printf("exit value %d\n", statusCode);
fflush(stdout);
}
else if(strncmp("cd", line, strlen("cd")) == 0) {
if(line[2] == ' ') { // If space after 'cd' expect directory
char cwd[1024];
getcwd(cwd, sizeof(cwd));
char *path = strstr(line, " ");
if(path) {
path += 1;
char *value;
value = malloc(strlen(path));
memcpy(value, path, strlen(path));
*(value + strlen(path)) = 0;
sprintf(cwd, "%s/%s", cwd, value); // Directory to change to
free(value);
}
chdir(cwd); // cd to new directory
} else { // cd with no argument
char *home = getenv("HOME");
chdir(home); // cd to HOME directory
}
}
else { // System commands
pid_t pid, ppid;
int status;
char *command;
char *args[256];
int argCount;
command = strtok(line, " ");
// Create args array for execvp
args[0] = command;
argCount = 1;
args[argCount] = strtok(NULL, " ");
while(args[argCount] != NULL) { // Add arguments to array
argCount++;
args[argCount] = strtok(NULL, " ");
}
if((pid = fork()) < 0) { // Fork fails
perror("fork");
fflush(stdout);
exit(1);
}
if(pid == 0) { // Child process
for(i=0; i<argCount; i++) {
if(strcmp(args[i], "<") == 0) { // Redirecting input
if(access(args[i+1], R_OK) == -1) { // File is unreadable
perror("access");
fflush(stdout);
} else { // File is readable
int file = open(args[i+1], O_RDONLY, 0);
dup2(file, STDIN_FILENO);
close(file);
execvp(command, &command);
}
}
else if(strcmp(args[i], ">") == 0) { // Redirecting output
int file = creat(args[i+1], 7777);
dup2(file, STDOUT_FILENO);
close(file);
execvp(command, args);
} else { // No redirection
execvp(command, args);
}
}
perror("execvp"); // Error for execvp
exit(1);
} else { // Parent process
if (bg == 1) { // Background process
int status;
int process;
printf("Background PID: %d\n", pid);
fflush(stdout);
bgPsArray[bgPsCount] = pid; // Add process to background process array
bgPsCount++;
process = waitpid(pid, &status, WNOHANG);
} else { // Foreground process
int status;
waitpid(pid, &status, 0); // Wait on the process
if(WIFEXITED(status)) {
statusCode = WEXITSTATUS(status);
}
}
}
}
return 1;
}
int main(int argc, char *argv[], char *envp[]) {
// Creating 'junk' manually is necessary because output redirection is broken,
// and a large portion of the grading script is depedent upon it's existence.
FILE *fp = fopen("junk", "ab+");
const char *text;
fprintf(fp, "Junk in junkfile\n");
fclose(fp);
signal(SIGINT, trapInterrupt);
signal(SIGTSTP, trapTstp);
while(1) {
checkProcessCompletion(); //Check the processes
int cmd = getCommand(); // Get command from user
int result = runCommand(cmd);
if (result == 0) {
break;
}
}
return 0;
}
First you must not trap SIGTSTP for the shell itself (it should ignore it), only for its child. Second if you really want to write a job controller shell, you need to manage children with the help of process group and correctly set the foreground group. Writing a shell that behave correctly with job control is a heavy task. Read POSIX standard about shells, groups, sessions, terminal control.
About your current problem. If your sub process does an exec then each handled signal is reset to its default behavior. This is because exec recovers the old code with the new one, so the previously set handler is no more available. Now you must let the child behave normally against TSTP and just let the parent track its status either with a synchronous call to wait/waitpid or with the asynchronous help of SIGCHLD. When the child stops or terminates, the parent is able to see it in the returned status (WIFEXITED, WIFSIGNALED, WIFSTOPPED).
I am writing program in C on Linux which has to fork 2 children.
First child will send two random numbers over pipe to the second child. It will listen for SIGUSR1 signal and will then terminate.
The second child will duplicate(dup2) pipe input as STDIN and file fp as STDOUT. It will then execl program which will print out some data according to its input and end.
My problem is, that the execl'd program will never terminate and I don't know why. Any help or tips will be appreciated.
main.c (parent):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
const int BUFFER_SIZE = 30;
int pipefd[2] = {0,0};
int parent_pid = 0;
int first_pid = 0;
int second_pid = 0;
int sleep_time = 5;
int debug = 0;
FILE *fp;
void parent_func() {
int wstatus = 0;
sleep(sleep_time);
kill(first_pid, SIGUSR1);
wait(&wstatus);
waitpid(second_pid, &wstatus, 0);
}
static void sigusr1_handler(int sig) {
if (sig == SIGUSR1) {
fputs("TERMINATED", stderr);
close(pipefd[1]);
exit(0);
}
}
void first_func() {
struct sigaction act;
char buffer[BUFFER_SIZE];
close(pipefd[0]);
memset(&act, '\0', sizeof(act)); // clear the sigaction struct
act.sa_handler = &sigusr1_handler; // sets function to run on signal
if (sigaction(SIGUSR1, &act, NULL) < 0) { // assign sigaction
fputs("cannot assign sigaction - exiting...", stderr);
exit(1);
}
while (1) {
sprintf(buffer, "%d %d\n", rand(), rand());
write(pipefd[1], buffer, strlen(buffer));
puts(buffer);
sleep(1);
}
}
void second_func() {
close(pipefd[1]);
fp = fopen("out.txt", "w");
char buf[30];
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
//dup2(fileno(fp), STDOUT_FILENO);
execl("./test", "", NULL);
perror("Error");
}
int main(int argc, char *argv[]) {
int fork_val = 0;
parent_pid = getpid();
if (pipe(pipefd)) {
fputs("cannot create pipe - exiting...", stderr);
return 1;
}
if (debug) {
sleep_time *= 10;
}
if ((fork_val = fork()) == -1) {
fputs("cannot fork process - exiting...", stderr);
return 1;
} else if (fork_val == 0) {
first_func();
} else {
first_pid = fork_val;
if ((fork_val = fork()) == -1) {
fputs("cannot fork process - exiting...", stderr);
return 1;
} else if (fork_val == 0) {
second_func();
} else {
second_pid = fork_val;
parent_func();
}
}
fclose(fp);
exit(0);
}
test.c (the execl'd file):
#include "nd.h"
#include "nsd.h"
#include <stdio.h>
#include <stdlib.h>
int main() {
int num1 = 0;
int num2 = 0;
char buffer[100];
while (fgets(buffer, 100, stdin) != NULL) {
if (sscanf(buffer, "%d %d", &num1, &num2) == 2) {
(num1 < 0) ? num1 = (num1 * -1) : num1;
(num2 < 0) ? num2 = (num2 * -1) : num2;
if (num1 == 1 || num2 == 1) {
puts("1");
} else if (num1 == num2) {
if (nd(num1) == 1) {
puts("prime");
} else {
printf("%d\n", num1);
}
} else if (nd(num1) == 1 && nd(num2) == 1) {
puts("prime");
} else {
printf("%d\n", nsd(num1, num2));
}
} else {
fputs("error\n", stderr);
}
}
fputs("DONE", stderr);
exit(0);
}
To be able to detect an end of file from a pipe you need to read from a empty pipe with no writer (no process with an open for writing descriptor).
As your writer (first_func()) never closes its descriptor and always writes something in a never ending loop the reader will either wait for some data or read some data.
Be also careful about closing non useful descriptors, if not you may encounter some problems with pipes, such has a single process that is a reader and a writer, so being unable to detect the end of file...
I have a problem with pipes. My program is a Shell program in C. I want to execute for example ls | wc, but what I get after running is:
ls: cannot access |: no such file or directory ls: cannot access wc: no such file or directory.
What am I doing wrong?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#define MAX_CMD_LENGTH 100
#define MAX_NUM_PARAMS 10
int parsecmd(char* cmd, char** params) { //split cmd into array of params
int i,n=-1;
for(i=0; i<MAX_NUM_PARAMS; i++) {
params[i] = strsep(&cmd, " ");
n++;
if(params[i] == NULL) break;
}
return(n);
};
int executecmd(char** params) {
pid_t pid = fork(); //fork process
if (pid == -1) { //error
char *error = strerror(errno);
printf("error fork!!\n");
return 1;
} else if (pid == 0) { // child process
execvp(params[0], params); //exec cmd
char *error = strerror(errno);
printf("unknown command\n");
return 0;
} else { // parent process
int childstatus;
waitpid(pid, &childstatus, 0);
return 1;
}
};
int execpipe (char ** argv1, char ** argv2) {
int fds[2];
pipe(fds);
int i;
pid_t pid = fork();
for (i=0; i<2; i++) {
if (pid == -1) { //error
char *error = strerror(errno);
printf("error fork!!\n");
return 1;
} else
if (pid == 0) {
if(i ==0){
close(fds[1]);
dup2(fds[0], 0);
close(fds[0]);
execvp(argv1[0], argv1);
char *error = strerror(errno);
printf("unknown command\n");
return 0;
} else if(i == 1) {
close(fds[0]);
dup2(fds[1], 1);
close(fds[1]);
execvp(argv2[0], argv2);
char *error = strerror(errno);
printf("unknown command\n");
return 0;
}
} else { // parent process
int childstatus;
waitpid(pid, &childstatus, 0);
return 1;
}
} // end for
};
int main() {
char cmd[MAX_CMD_LENGTH+1];
char * params[MAX_NUM_PARAMS+1];
char * argv1[MAX_NUM_PARAMS+1];
char * argv2[MAX_NUM_PARAMS+1];
int k, y, x;
int f = 1;
while(1) {
printf("$"); //prompt
if(fgets(cmd, sizeof(cmd), stdin) == NULL) break; //read command, ctrl+D exit
if(cmd[strlen(cmd)-1] == '\n') { //remove newline char
cmd[strlen(cmd)-1] = '\0';
}
int j=parsecmd(cmd, params); //split cmd into array of params
if (strcmp(params[0], "exit") == 0) break; //exit
for (k=0; k <j; k++) { //elegxos gia uparksi pipes
if (strcmp(params[k], "|") == 0) {
f = 0; y = k;
printf("pipe found\n");
}
}
if (f==0) {
for (x=0; x<k; x++) {
argv1[x]=params[x];
}
int z = 0;
for (x=k+1; x< j; x++) {
argv2[z]=params[x];
z++;
}
if (execpipe(argv1, argv2) == 0) break;
} else if (f==1) {
if (executecmd(params) == 0) break;
}
} // end while
return 0;
}
Updated your code with following corrections.
Removed for() loop that iterated two times after fork() call.
Removed incorrect close of pipe FDs after dup2 calls for both parent and child processes.
Aligned the command that needed to be run as per the file descriptors that were duplicated in dup2() calls for parent and child. Basically I needed to swap execvp(argv2[0], argv2) and execvp(argv1[0], argv1) calls.
Added a break; statement in the for loop that searched for pipe character.
The updated code is as below.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAX_CMD_LENGTH 100
#define MAX_NUM_PARAMS 10
int parsecmd(char* cmd, char** params) { //split cmd into array of params
int i,n=-1;
for(i=0; i<MAX_NUM_PARAMS; i++) {
params[i] = strsep(&cmd, " ");
n++;
if(params[i] == NULL) break;
}
return(n);
};
int executecmd(char** params) {
pid_t pid = fork(); //fork process
if (pid == -1) { //error
char *error = strerror(errno);
printf("error fork!!\n");
return 1;
} else if (pid == 0) { // child process
execvp(params[0], params); //exec cmd
char *error = strerror(errno);
printf("unknown command\n");
return 0;
} else { // parent process
int childstatus;
waitpid(pid, &childstatus, 0);
return 1;
}
};
int execpipe (char ** argv1, char ** argv2) {
int fds[2];
pipe(fds);
int i;
pid_t pid = fork();
if (pid == -1) { //error
char *error = strerror(errno);
printf("error fork!!\n");
return 1;
}
if (pid == 0) { // child process
close(fds[1]);
dup2(fds[0], 0);
//close(fds[0]);
execvp(argv2[0], argv2); // run command AFTER pipe character in userinput
char *error = strerror(errno);
printf("unknown command\n");
return 0;
} else { // parent process
close(fds[0]);
dup2(fds[1], 1);
//close(fds[1]);
execvp(argv1[0], argv1); // run command BEFORE pipe character in userinput
char *error = strerror(errno);
printf("unknown command\n");
return 0;
}
};
int main() {
char cmd[MAX_CMD_LENGTH+1];
char * params[MAX_NUM_PARAMS+1];
char * argv1[MAX_NUM_PARAMS+1] = {0};
char * argv2[MAX_NUM_PARAMS+1] = {0};
int k, y, x;
int f = 1;
while(1) {
printf("$"); //prompt
if(fgets(cmd, sizeof(cmd), stdin) == NULL) break; //read command, ctrl+D exit
if(cmd[strlen(cmd)-1] == '\n') { //remove newline char
cmd[strlen(cmd)-1] = '\0';
}
int j=parsecmd(cmd, params); //split cmd into array of params
if (strcmp(params[0], "exit") == 0) break; //exit
for (k=0; k <j; k++) { //elegxos gia uparksi pipes
if (strcmp(params[k], "|") == 0) {
f = 0; y = k;
printf("pipe found\n");
break;
}
}
if (f==0) {
for (x=0; x<k; x++) {
argv1[x]=params[x];
}
int z = 0;
for (x=k+1; x< j; x++) {
argv2[z]=params[x];
z++;
}
if (execpipe(argv1, argv2) == 0) break;
} else if (f==1) {
if (executecmd(params) == 0) break;
}
} // end while
return 0;
}
If you are interested only in changes I made, here is the diff between your code and the above updated code:
--- original.c
+++ updated.c
## -4,6 +4,7 ##
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
+#include <sys/wait.h>
#define MAX_CMD_LENGTH 100
## -43,44 +44,36 ##
pipe(fds);
int i;
pid_t pid = fork();
- for (i=0; i<2; i++) {
if (pid == -1) { //error
char *error = strerror(errno);
printf("error fork!!\n");
return 1;
- } else
- if (pid == 0) {
- if(i ==0){
+ }
+ if (pid == 0) { // child process
close(fds[1]);
dup2(fds[0], 0);
- close(fds[0]);
- execvp(argv1[0], argv1);
+ //close(fds[0]);
+ execvp(argv2[0], argv2); // run command AFTER pipe character in userinput
char *error = strerror(errno);
printf("unknown command\n");
return 0;
- } else if(i == 1) {
+ } else { // parent process
close(fds[0]);
dup2(fds[1], 1);
- close(fds[1]);
- execvp(argv2[0], argv2);
+ //close(fds[1]);
+ execvp(argv1[0], argv1); // run command BEFORE pipe character in userinput
char *error = strerror(errno);
printf("unknown command\n");
return 0;
}
- } else { // parent process
- int childstatus;
- waitpid(pid, &childstatus, 0);
- return 1;
- }
- } // end for
};
int main() {
char cmd[MAX_CMD_LENGTH+1];
char * params[MAX_NUM_PARAMS+1];
- char * argv1[MAX_NUM_PARAMS+1];
- char * argv2[MAX_NUM_PARAMS+1];
+ char * argv1[MAX_NUM_PARAMS+1] = {0};
+ char * argv2[MAX_NUM_PARAMS+1] = {0};
int k, y, x;
int f = 1;
while(1) {
## -95,6 +88,7 ##
if (strcmp(params[k], "|") == 0) {
f = 0; y = k;
printf("pipe found\n");
+ break;
}
}
if (f==0) {
execv* procedure doesn't interpret shell script string. It merely starts an executable file and passes an array of arguments to it. Thus, it cannot organize a pipeline.
If you need "normal" shell command execution, you may want to use system(char*) procedure instead of execvp.
Otherwise, if you need to do the pipes yourself, you may want to parse the string with '|' special characters and use pipe(), fork() and I/O redirection. Like here How to run a command using pipe?