Interrupt a process calling the function popen - c

I need to implement a child process that will execute a file and send the execution result, the 2 process will communicate with a shared memory segment.
My problem is that i want to kill the child process calling popen after 10 seconds but the function popen ignores signals.
Here is my code (shared memory segment not included) :
void kill_child(int sig)
{
kill(child_pid,SIGKILL);
printf("processus killed \n");
}
/*code....*/
signal(SIGALRM,(void (*)(int))kill_child);
if(fork()==0){
res.buffer=true;
FILE * fd;
char cmd[BUFFER_SIZE],output[BUFFER_SIZE];
strcpy(cmd,"./");
strcat(cmd,res.filepath);
system(cmd);
if((fd=popen(cmd,"r"))== NULL)
exit(1);
else
res.status=200;
strcpy(output,"");
while(fgets(buf,sizeof(buf)-1,fd))
strcat(output,buf);
if(pclose(fd))
exit(1);
strcat(res.customHTML,output);
res.buffer=true;
int err = sendResponse(res,args->client_fd);
if (err < 0) on_error("failed!\r\n");
exit(0);
}
else{
int status;
alarm(10);
waitpid(-1,&status,0);
printf("status %d _n);
}
How can make the child process interruptible?
thanks

First off, you need to actually store the child PID into child_pid. It's returned from fork for the parent process so changing your fork call to
child_pid = fork();
if(child_pid == 0)
{
...
otherwise your call to kill is being passed a random value. Luckily it seems to be defaulting to 0, which kill takes to mean kill all processes in the same process group so your child process is being killed.
Secondly, rather than calling popen() call the executable yourself with (for example) execvp() and have the parent read the output using a pipe you create yourself...
int fds[2];
pipe(fds);
child_pid = fork();
if(child_pid == 0)
{
char *cmd[]={"mycmd",NULL};
/* Replace stdout with the output of the pipe and close the original */
dup2(fds[1],1);
close(fds[0]);
close(fds[1]);
execvp(cmd[0],cmd);
}
else
{
close(fds[1]);
alarm(10);
while(...)
{
read(fds[0],....);
if(waitpid(child_pid,&status,WNOHANG))
{
....
}
}
}
This way you've only got the one child process which is running your executable and you've got visibility on when and how it exits.

Related

Bug in forking and running processes

So I've been trying to fork a process two times, first fork is to let the main process continue working and the second is to let the child capture the output of the execution of the grand child process. The code is as follow:
void execute_run(char **parameters) {
task_t* task = create_task();
tasks[task_number] = *task;
int pipe_stdout[2];
ASSERT_SYS_OK(pipe(pipe_stdout)); // creating a pipe for stdout
pid_t pid = fork();
ASSERT_SYS_OK(pid);
if (pid == 0) {
// child process
char buffer[MAXLENGTH_OUTPUT];
pid = fork();
ASSERT_SYS_OK(pid);
if (pid == 0) {
// this process will redirect stdout to buffer
const char* program_name = parameters[0];
char** program_args = &parameters[1];
ASSERT_SYS_OK(close(pipe_stdout[0]));
ASSERT_SYS_OK(dup2(pipe_stdout[1], STDOUT_FILENO));
// redirecting stdout to pipe
ASSERT_SYS_OK(close(pipe_stdout[1]));
ASSERT_SYS_OK(execvp(program_name, program_args));
}
else {
ASSERT_SYS_OK(close(pipe_stdout[1]));
int status;
do
{
// this loop runs forever
} while (waitpid(pid, &status, WNOHANG) == 0);
ASSERT_SYS_OK(close(pipe_stdout[0]));
// this is never printed
fprintf(stderr, "Child process finished with status %d\n", status);
}
} else {
// parent process won't use any of the pipe ends
ASSERT_SYS_OK(close(pipe_stdout[0]));
ASSERT_SYS_OK(close(pipe_stdout[1]));
}
}
char** parameters are ['cat', 'in.txt'] in the example i'm trying to run (and file exists).
Thanks in advance!
I've tried debugging it and running with different commands like 'ls' for which it works fine, nevertheless I can't figure out why with 'cat' it doesn't work

How to send data from parent process to child process and signal

I got two processes run in parallel. I want that the parent will send some char to the child. I want to use pipe() and write from the parent and send signal to the child, then check from child if signal sent and read the char at the child process. How can i do that?
int run() {
pid_t pid;
int filds[2];
pipe(filds);
char *args[150] = {"./draw.out", NULL}; // child will run executable.
char buff = '\0';
if ((pid = fork()) < 0) { // fork a child process/
printf("*** ERROR: forking child process failed\n");
exit(1);
} else if (pid == 0) {
execvp(args[0], args); // run program from the child process.
} else { // for the parent
char btnPressed = getch();
while (btnPressed != 'q'){
btnPressed = getch(); // gets the char
write(filds[1],buff, BUFF_SIZE); //write to the pipe.
// how do i send safe signal to child?
}
}
}
First of all, you cannot use execvp and pipe together. Since execvp replaces the child process with another executable. It means that the parent and child process don't share the pipe anymore because the new process created by execvp has a different stack.
You can pass the data on shared memory and send a signal to the child. After the child reads the data, it can also send a signal to indicate that I got the data.

Shell prompt returns before the child process complets via putty

Please review the following code example:
int main() {
pid_t childpid;
char buf[100] = {0};
if((childpid = fork()) == -1)
{
perror("fork");
exit(1);
}
if(childpid == 0) {
sprintf(buf,"child process id: %d\n",getpid());
write(1,buf,strlen(buf));
}
else {
sprintf(buf,"parent process id: %d\n",getpid());
write(1,buf,strlen(buf));
// fix here
wait(&childpid);
}
return 0;
}
When run directly on terminal on Linux machine, the output as expected:
[user#192 ~]$ ./test
parent process id: 28788
child process id: 28789
On another hand running the same via Putty brings:
parent process id: 28978
[user#192 ~]$ child process id: 28979
Thanks everybody for the suggestions. Adding a wait call brings prompt after child finishes.
I hope that it is a good idea to consider in a same question another case where the output differs as well, but independently of the wait() call.
This is dup() call implementation:
int main() {
pid_t childpid;
char string[] = "c\nb\na";
if((childpid = fork()) == -1)
{
perror("fork");
exit(1);
}
if(childpid == 0) {
close(0); // close STDIN
close(fd[1]); // close write end of a pipe
dup(*fd); // duplicate read end of the pipe to STDIN
execlp("sort","sort",NULL); // run sort(1) command
}
else {
close(*fd); // close read end of a pipe
write(fd[1],string,strlen(string));
}
return 0;
}
And again to different outputs, running program directly from the terminal,
gives:
[user#192 pipe]$ ./dup
a
b
c
[user#192 pipe]$
and via putty connection:
root#debian-512mb-ams2-01:~/C/inner/pipe# a
b
c
In the second example prompt never come back :(
What it can be?)
You need to wait() for the child process:
if(childpid == 0) {
sprintf(buf,"child process id: %d\n",getpid());
write(1,buf,sizeof(buf));
}
else {
sprintf(buf,"parent process id: %d\n",getpid());
write(1,buf,sizeof(buf));
wait(childpid); // <---
}
return 0;
All the child processes created inside a main process dies prematurely when the main process exit or dies. Hence before exit from the main process we need to have tell the operating system that please complete all the child processes before exit from the main process. For that you have to add wait() system call with a int value or NULL . For testing purpose you can use a sleep() statement as well.
write(1,buf,sizeof(buf));
wait(NULL); // just for testing sleep(5); sleep for 5 second to complete the child process
I think it may be related to latency in finishing processes (they are running independently).
Putty just show you prompt after core process is finished but it is not deterministic in your case.

Setting parent to child process group with setpgid fails

I'm trying to change the pgrp of the processes to that of the child's so i can setsid on the parent process. The only thing is I keep getting an EPERM error code. Both processes have the same session group, according to htop.
I'm basing this off of this blog post, so I can change which terminal output gets directed to.
void sig_exit(int signum)
{
_Exit(0);
}
pid_t change_process_group()
{
pid_t child_pid;
if ((child_pid = fork()) < 0)
{
perror("fork failed while attaching to term");
exit(1);
}
if (child_pid == 0)
{
pid_t parent = getppid();
setpgid(0, getpid());
signal(SIGUSR1, sig_exit); // wait till parent tells child to exit
//sleep(5);
//kill(parent, SIGUSR2);
pause();
printf("Shouldn't reach this\n");
}
//sleep(5);
//signal(SIGUSR2, sig_wait);
//pause();
int parent_pid = getpid();
int code = setpgid(parent_pid, child_pid); // need child process group
printf("%s\n", strerror(errno));
setsid();
return child_pid;
}
main()
{
pid_t child = change_process_group();
kill(child, SIGUSR1);
}
The commented out lines were from me thinking the processes might not be executing in the correct order, but those don't appear to fix the problem.
How may I correctly use setpgid to change the pgrp of the parent process to the child's?
This is a race condition and it works if you uncomment the sleep(5) line in the parent. When you call setpgid(parent_pid, child_pid), the child_pid process group must exist. It isn't enough that there exists a process with the PID child_pid: setpgid needs an existing process group unless the process is putting itself into its own group. If setpgid(parent_pid, child_pid) in the parent runs after setpgid(0, getpid()) in the child, it works.
Sleeping is both inefficient and fragile, so instead of that the parent should wait for a notification from the child. Signals are fragile because there aren't many different signals and they could come from anywhere. A good way to communicate between related processes is a pipe. Since all you need here is a one-time notification, you can set up a pipe and read from it in the parent (with the write end closed in the parent). The parent will wait until the child writes to the pipe or closes it. In the child, just close the write end of the pipe when you've finished the preparations. The parent's read call (or select if you need to do other things at the same time) will return.
Proof-of-concept code:
pid_t change_process_group()
{
pid_t child_pid;
int child_ready_pipe[2];
if (pipe(child_ready_pipe) < 0)
{
perror("pipe");
exit(1);
}
if ((child_pid = fork()) < 0)
{
perror("fork failed while attaching to term");
exit(1);
}
if (child_pid == 0)
{
close(child_ready_pipe[0]);
sleep(1); // mimic slow start of the child
if (setpgid(0, 0))
perror("child setpgid to create group");
close(child_ready_pipe[1]);
signal(SIGUSR1, sig_exit); // wait till parent tells child to exit
pause();
printf("Shouldn't reach this\n");
}
close(child_ready_pipe[1]);
int parent_pid = getpid();
char ignored;
read(child_ready_pipe[0], &ignored, 1);
close(child_ready_pipe[0]);
if (setpgid(parent_pid, child_pid) < 0) // need child process group
perror("parent setpgid");
if (setsid() < 0)
perror("parent setsid");
return child_pid;
}

whether a function returned in the child process can be captured in the parent process

I'm currently implementing the && function in a shell using C. For example, if we input cmd1 && cmd2, then cmd2 executes only when cmd1 exits successfully. I'm thinking about:
int main() {
int i;
char **args;
while(1) {
printf("yongfeng's shell:~$ ");
args = get_line();
if (strcmp(args[0], "exit") == 0) exit(0); /* if it's built-in command exit, exit the shell */
if('&&') parse_out_two_commands: cmd1, cmd2;
if (execute(cmd1) != -1) /* if cmd1 successfully executed */
execute(cmd2); /* then execute the second cmd */
}
}
int execute(char **args){
int pid;
int status; /* location to store the termination status of the terminated process */
char **cmd; /* pure command without special charactors */
if(pid=fork() < 0){ //fork a child process, if pid<0, fork fails
perror("Error: forking failed");
return -1;
}
/* child */
else if(pid==0){ /* child process, in which command is going to be executed */
cmd = parse_out(args);
/* codes handleing I/O redirection */
if(execvp(*cmd, cmd) < 0){ /* execute command */
perror("execution error");
return -1;
}
return 0;
}
/* parent */
else{ /* parent process is going to wait for child or not, depends on whether there's '&' at the end of the command */
if(strcmp(args[sizeof(args)],'&') == 0){
/* handle signals */
}
else if (pid = waitpid(pid, &status, 0) == -1) perror("wait error");
}
}
So I'm using another function int execute(char ** args) to do the actual work. Its return type is int because I wan to know whether the command exits successfully. But I'm not sure here whether the parent process can get the return value from the child since they're two different processes.
Or should I decide whether to execute the second command in the child process, by forking another process to run it? Thanks a lot.
Change:
if(pid=fork() < 0){ //fork a child process, if pid<0, fork fails
to:
if((pid=fork()) < 0){ //fork a child process, if pid<0, fork fails
You're setting pid to the result of fork() < 0, not setting it to the PID of the child. So unless there's an error in fork(), this sets pid to 0 in both the parent and child, so they both think they're the child.
Regarding the return value of the execute() function: It will return in both the parent and child. In each process, it will return whatever was specified in the return statement in the corresponding branch of the if in execute(). Note that it execve() is successful, the child never returns, because it's no longer running this program, it's running the program that was exec'ed.
If the child wants to send success or failure information to the parent, it does this using its exit status, by calling exit(0) to indicate success, and exit(some-nonzero-value) to indicate failure. The parent can get the exit status using waitpid, and then return a success or failure indication from execute().

Resources