Killing process that has been created with popen2 - c

I'm using the function popen2 (that has been recommended elsewhere on stackoverflow) to programatically create a process that has to be killed again after some time. popen2 returns a PID and I thought that this PID could be used to kill the process. It doesn't work this way, though. In order to kill it, I have to increment the returned PID, which I don't understand (see code below)
Another problem might arise when various threads are doing this in parallel. In that case the PIDs might differ by more than one due to race conditions, I think.
So my question is this: How can I reliably identify the PID of the process created by popen2 in a multi-threaded scenario?
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#define READ 0
#define WRITE 1
pid_t popen2(const char *command, int *infp, int *outfp) {
int p_stdin[2], p_stdout[2];
pid_t pid;
if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
return -1;
pid = fork();
if (pid < 0)
return pid;
else if (pid == 0)
{
close(p_stdin[WRITE]);
dup2(p_stdin[READ], READ);
close(p_stdout[READ]);
dup2(p_stdout[WRITE], WRITE);
execl("/bin/sh", "sh", "-c", command, NULL);
perror("execl");
exit(1);
}
if (infp == NULL)
close(p_stdin[WRITE]);
else
*infp = p_stdin[WRITE];
if (outfp == NULL)
close(p_stdout[READ]);
else
*outfp = p_stdout[READ];
return pid;
}
main() {
pid_t pid;
// Create process
pid = popen2("crafty", &in, &out);
sleep(5);
// Why doesn't kill(pid, SIGKILL) work?
kill(pid+1, SIGKILL);
while (1);
}

I think I got it.
execl("/bin/sh", "sh", "-c", command, NULL);
runs sh and popen2 returns it's pid. When you call kill it kills sh, but does not touch it's child process command. It is actually a fluke that killing the next pid kills command. This will not always work and is just up to race conditions.
If you want to be able to kill your target process then you will have to start that directly.
Warning (untested code):
pid_t popen2(const char **command, int *infp, int *outfp) {
int p_stdin[2], p_stdout[2];
pid_t pid;
if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
return -1;
pid = fork();
if (pid < 0)
return pid;
else if (pid == 0)
{
close(p_stdin[WRITE]);
dup2(p_stdin[READ], READ);
close(p_stdout[READ]);
dup2(p_stdout[WRITE], WRITE);
execvp(*command, command);
perror("execvp");
exit(1);
}
if (infp == NULL)
close(p_stdin[WRITE]);
else
*infp = p_stdin[WRITE];
if (outfp == NULL)
close(p_stdout[READ]);
else
*outfp = p_stdout[READ];
return pid;
}
and pass command in the form of
char *command[] = {"program", "arg1", "arg2", ..., NULL};
in your particular example:
char *command[] = {"crafty", NULL};

You can use 'exec' command of shell to avoid pending process. Also: popen2 shall close the writing end of unused pipes, otherwise the pipe remains open. If one of pointers (infp, outpf) is NULL, it is useless to create and immediately close the pipe. Here is the version of popen2 I use in my project:
pid_t popen2(char *command, int *in_fd, int *out_fd) {
int pin[2], pout[2];
pid_t pid;
char cmd[strlen(command)+10];
if (out_fd != NULL) {
if (pipe(pin) != 0) return(-1);
}
if (in_fd != NULL) {
if (pipe(pout) != 0) {
if (out_fd != NULL) {
close(pin[0]);
close(pin[1]);
}
return(-1);
}
}
pid = fork();
if (pid < 0) {
if (out_fd != NULL) {
close(pin[0]);
close(pin[1]);
}
if (in_fd != NULL) {
close(pout[0]);
close(pout[1]);
}
return pid;
}
if (pid==0) {
if (out_fd != NULL) {
close(pin[1]);
dup2(pin[0], 0);
}
if (in_fd != NULL) {
close(pout[0]);
dup2(pout[1], 1);
}
// Exec makes possible to kill this process
sprintf(cmd, "exec %s", command);
execlp("sh", "sh", "-c", cmd, NULL);
fprintf(stderr, "%s:%d: Exec failed in popen2. ", __FILE__, __LINE__);
perror("Error:");
exit(1);
}
if (in_fd != NULL) {
close(pout[1]);
*in_fd = pout[0];
}
if (out_fd != NULL) {
close(pin[0]);
*out_fd = pin[1];
}
return pid;
}

Related

C - Notify When A Background Process Is Finished

I am creating a small shell program, one functionality of my shell is to be able to run commands in the background.
If a job is run in the background, then a small message is shown notifying the user of the pid.
Furthermore, I want to also be able to notify the user of when the background job is done
This is my code so far:
int execArgs(char *argv[], int bg)
{
int pid ;
int child_info = -1;
if(argv[1] == "exit"){
printf("%s\n", argv[0]);
}
if ( argv[0] == NULL ) /* nothing succeeds */
return 0;
if ( (pid = fork()) == -1 )
perror("fork");
else if ( pid == 0 ){
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
execvp(argv[0], argv);
perror("cannot execute command");
exit(1);
}
else {
if(bg == 0){
if ( wait(&child_info) == -1 )
perror("wait");
}
else
printf("The Process PID:%d has started\n", pid);
}
return child_info;
}
I know the answer lies in something to do with the
pid_t return_pid = waitpid(process_id, &status, WNOHANG);
but I don't understand how to use it completely, what does 'status' mean?
I tried implementing it like this:
int execArgs(char *argv[], int bg)
{
int pid ;
int child_info = -1;
if(argv[1] == "exit"){
printf("%s\n", argv[0]);
}
if ( argv[0] == NULL ) /* nothing succeeds */
return 0;
if ( (pid = fork()) == -1 )
perror("fork");
else if ( pid == 0 ){
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
execvp(argv[0], argv);
perror("cannot execute command");
exit(1);
}
else {
if(bg == 0){
if ( wait(&child_info) == -1 )
perror("wait");
}
else
printf("The Process PID:%d has started\n", pid);
int status;
pid_t return_pid = waitpid(pid, &status, WNOHANG); /* WNOHANG def'd in wait.h */
if (return_pid == -1) {
/* error */
} else if (return_pid == 0) {
/* child is still running */
} else if (return_pid == pid) {
printf("The process PID:%d has finished\n", pid);
}
}
return child_info;
}
but it doesn't work. I have read the man pages but they are a bit cumbersome and I don't understand them.
Apologies if the answer is elsewhere on StackOverFlow, I tried searching as thoroughly as I could.

Create background process in C using sigset()

I am making my own shell in C. This is my basic code for running a process:
pid_t childpid;
int status;
int ret = 0;
if (strcmp(line[0], "exit") == 0) {
return;
}
childpid = fork();
if (childpid >= 0) {
if (childpid == 0) {
ret = execvp(line[0], &line[0]);
if (ret == -1) {
printf("ERROR\n");
exit(0);
}
exit(ret);
} else {
waitpid(childpid, &status, 0);
ret = WEXITSTATUS(status);
}
} else {
perror("fork");
exit(-1);
}
Where line is a "string array" holding the commands to run.
Now say I want to run a background process - I have to use sigset. A parent doesn't wait for a backgrounded child process, right? How do I handle this without the child becoming a background process?

Piping in a while loop C

Im working on a shell that can handle pipes. the problem im runing into now is that when a pipe is found after both are done executing my shell exits. This is some sample code ive been working on to try and figure
int main(int argc, char** argv)
{
int fd[2];
pid_t pid;
pid_t pid2;
int childSt;
int hell;
struct arguments args;
char line[1024];
while (fgets(line, 1024, stdin)) {
line[strlen(line)-1] = '\0';
////////////////////////////////////////////////////////////// /
args = finArgs(line);
pipe(fd);
pid = fork();
if (pid > 0) // PARENT
{
pid2 = fork();
if(pid2 == 0){
close(fd[1]); //close write end
if (fd[0] != STDIN_FILENO)
{
if ((dup2(fd[0], STDIN_FILENO)) != STDIN_FILENO)
{
printf("dup2 error to stdin\n");
close(fd[0]);
}
}
if (execvp(args.pipeArgs[0], args.pipeArgs) < 0)
printf("parent error \n");
}
else {
wait(&childSt);
}
}
else // CHILD
{
close(fd[0]);
if (fd[1] != STDOUT_FILENO)
{
if ((dup2(fd[1], STDOUT_FILENO)) != STDOUT_FILENO)
{
printf("dup2 error to stdout\n");
close(fd[1]);
}
}
if (execvp(args.args[0], args.args) < 0)
printf("child error \n");
}
}
return(0);
}
here is some sample output
ls | wc
14 14 123
this is what i want but then the program exits.

I'm building a small shell. How do I set the standard in- and output of two processes to a pipe, so they can communicate?

I'm trying to implement a very small shell of my own. I have to be able to handle pipes, like
ls -l | wc -l
but only for two programs at a time. Right now, I have this:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFFER_SIZE 256
#define NO_PARAMS 32
void split_string(char **params, char *string){
char *arg;
int i;
arg = strtok(string, " ");
params[0] = arg;
i = 1;
while(arg != NULL){
arg = strtok(NULL, " ");
params[i] = arg;
i++;
}
}
int main(int argc, char **argv){
char string[BUFFER_SIZE];
char *prog1, *prog2;
int i, err;
int fd[2];
pid_t pid1, pid2;
size_t buffer = BUFFER_SIZE;
char *params1[NO_PARAMS], *params2[NO_PARAMS];
int pipe_exists = 0;
memset(string,0,buffer);
while(1){
/*Read command*/
fgets(string, BUFFER_SIZE-1, stdin);
if(string == NULL){
perror("Error reading input:\n");
exit(1);
}
/*replace linefeed character with end of line character*/
for(i=0;i<BUFFER_SIZE;i++){
if(string[i] == 10){
string[i] = 0;
}
}
/*check if command is "exit"*/
if(strcmp(string,"exit") == 0){
return 0;
}
/*split command into different program calls*/
prog1 = strtok(string, "|");
prog2 = strtok(NULL,"\0");
if(prog2 != NULL){
pipe_exists = 1;
printf("PIPE!\n");
err = pipe(fd);
if(err<0){
perror("Error creating pipe:\n");
exit(1);
}
}
/*split string into arguments*/
split_string(params1, prog1);
if(pipe_exists){
split_string(params2, prog2);
}
/*fork child process*/
pid1 = fork();
if(pid1==0){ /*child 1*/
if(pipe_exists){
close(fd[0]); /*close read-end*/
err = dup2(fd[1], 1);
if(err<0){
perror("Error with dup in child 1!\n");
exit(1);
}
}
execvp(params1[0],params1);
perror("Error calling exec()!\n");
exit(1);
}else{ /*parent*/
if(pipe_exists){
pid2 = fork();
if(pid2==0){ /*child 2*/
close(fd[1]); /*close pipe write-end*/
err = dup2(fd[0], 0);
if(err<0){
perror("Error with dup in child 2!\n");
exit(1);
}
execvp(params2[0],params2);
perror("Error calling exec()!\n");
exit(1);
}else{ /*parent with 2 children*/
waitpid(pid1,0,0);
waitpid(pid2,0,0);
}
}else{ /*parent with 1 child*/
waitpid(pid1,0,0);
}
}
}
}
Right now, it'll handle single commands fine, but when I input something like the command above, nothing happens!
Thanks!
Oh! I've already figured it out. I had to close the pipe in the parent program as well :)
To start with, you should loop as long as you find the pipe character. Then you need to create a pipe for each "piping".
Real shells usually forks and exec itself for each command in the pipeline. This is so it should be able to handle internal commands.
There are 3 main parts in a command with pipes.
The begining, that takes stdin and pipes its output something |
The middle, optionnal or repeated at will with two pipes | something |
The end, that outputs to stdout | something
Then use three functions, one for each of those:
#define PIPE_INPUT 0
#define PIPE_OUTPUT 1
execute_pipe_start(t_cmdlist *commands)
{
int pid;
int fd[2];
if (!commands)
return;
if (commands->next)
{
if (pipe(fd) < 0)
{
perror("pipe failed");
exit(1);
}
pid = fork();
if (!pid)
{
close(fd[PIPE_INPUT]);
if (dup2(fd[PIPE_OUTPUT, 1) < 0)
{
perror("dup2 failed");
exit(1);
}
parse_and_exec_cmd(commands->cmd);
}
else
{
waitpid(...); //what you put here is a bit tricky because
//some shells like tcsh will execute all
//commands at the same time (try cat | cat | cat | cat)
}
if (commands->next->next != null) //If you have 2 commands in line there is a middle
execute_pipe_middle(commands->next, fd);
else // no middle
execute_pipe_end(commands->next, fd);
}
else
parse_and_exec_cmd(commands->cmd);
}
execute_pipe_middle(t_cmdlist *commands, int fd_before[2])
{
int pid;
int fd_after[2];
if (pipe(fd_after) < 0)
{
perror("pipe failed");
exit(1);
}
pid = fork();
if (!pid)
{
close(fd_before[PIPE_OUTPUT]);
close(fd_after[PIPE_INPUT]);
if (dup2(fd_after[PIPE_OUTPUT, 1) < 0)
{
perror("dup2 failed");
exit(1);
}
if (dup2(fd_before[PIPE_INPUT, 0) < 0)
{
perror("dup2 failed");
exit(1);
}
parse_and_exec_cmd(commands->cmd);
}
else
waitpid(...);
if (commands->next->next != null) //More than two following commands : a middle again
execute_pipe_middle(commands->next, fd_after);
else // No more repetition
execute_pipe_end(commands->next, fd_after);
}
execute_pipe_end(t_cmdlist *commands, int fd_before[2])
{
int pid;
if (!commands)
return;
if (commands->next)
{
pid = fork();
if (!pid)
{
close(fd_before[PIPE_OUTPUT]);
if (dup2(fd_before[PIPE_INPUT, 0) < 0)
{
perror("dup2 failed");
exit(1);
}
parse_and_exec_cmd(commands->cmd);
}
else
waitpid(...);
}
}

forking, waitpid problems in c

For some reason this code executes the parental commands immediately, terminating my semaphores and screwing up my flow control of other programs. Can anyone tell me why the waitpid() isnt working?
//Create child processes
pid = fork();
if(pid < 0){
fprintf(stderr, "Fork Failed.\n");
exit(1);
return;
}else if(pid==0){
if(execl("/home/tropix/hw11-2","/home/tropix/hw11-2",semarg,pipe_to_p3,pipe_to_p4,(char*)0)){
fprintf(stderr, "File Exexecution of hw11-2 failed.\n");
exit(1);
}
} else {
pid = fork();
if(pid < 0){
fprintf(stderr, "Fork Failed.\n");
exit(1);
return;
} else if(pid==0){
if(execl("/home/tropix/hw11-3","/home/tropix/hw11-3",shmarg,semarg,pipe_from_p2,pipe_to_p5_1, (char*)0)){
fprintf(stderr, "File Execution of hw11-3 failed.\n");
exit(1);
}
} else {
pid = fork();
if(pid < 0){
fprintf(stderr, "Fork Failed.\n");
exit(1);
return;
} else if (pid == 0){
if(execl("/home/tropix/hw11-4","/home/tropix/hw11-4",shmarg,semarg,pipe_from_p2_2,pipe_to_p5_2, (char*)0)){
fprintf(stderr, "File Execution of hw11-4 failed.\n");
exit(1);
}
} else {
pid = fork();
if(pid < 0){
fprintf(stderr, "Fork Failed.\n");
exit(1);
return;
} else if (pid == 0){
if(execl("/home/tropix/hw11-5","/home/tropix/hw11-5",semarg,pipe_from_p3,pipe_from_p4,(char*)0)){
fprintf(stderr, "File Execution of hw11-5 failed.\n");
exit(1);
}
} else if (pid > 0) {
}
}
}
//Closing Pipes
close(pipe1[1]);
close(pipe2[1]);
close(pipe3[1]);
close(pipe4[1]);
close(pipe1[0]);
close(pipe2[0]);
close(pipe3[0]);
close(pipe4[0]);
//Wait for child process completetion
waitpid(pid,NULL,0);
printf("Child Processes Complete.\n");
//Terminate Semaphores
semctl(sem_id,0,IPC_RMID);
//Terminate Shared Memory Segement
shmctl(shmid, IPC_RMID, NULL);
}
}
Thanks!
EDIT: Ok, I replaced waitpid with:
while (pid = waitpid(-1, NULL, 0)) {
if (errno == ECHILD) {
break;
}
}
and that got me part of the way there. It isnt executing the parental controls immediately, but it seems to never execute now. As far as the pipe issue you talked about, program 1 (this one) is supposed to terminate all IPC elements, including the pipes. If there is a better way, I would love to hear it.
Thanks #Jonathan
You only wait for one process to complete - not for all processes to complete. That is probably one problem. Fix with a loop on waitpid() until it returns 'no more kids'.
The structure of the code leaves something to be desired - it is a rabbit's warren of nested if's; ick!
I worry that you are not closing enough pipes before the other commands are executed. You may be OK if the commands do not depend on detecting EOF on a pipe; otherwise, you are in for a long wait.
You need a function like:
#include <stdarg.h>
static void err_exit(const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(EXIT_FAILURE);
}
This simplifies your error handling. You can also do things like automatically add the PID that is dying, or the error that triggered the exit, if you wish.
We can also create a function to run another command:
static pid_t run_command(const char *cmd, const char *shmarg, const char *semarg,
const char *fdarg1, const char *fdarg2)
{
pid_t pid = fork();
if (pid < 0)
err_exit("Failed to fork\n");
else if (pid == 0)
{
execl(cmd, cmd, shmarg, semarg, fdarg1, fdarg2, (char *)0);
err_exit("Failed to exec %s\n", cmd);
}
return pid;
}
With those in place, we can look to reduce your code to this...
// Create child processes
pid_t pid1 = run_command("/home/tropix/hw11-2", semarg, pipe_to_p3, pipe_to_p4);
pid_t pid2 = run_command("/home/tropix/hw11-3", shmarg, semarg, pipe_from_p2, pipe_to_p5_1);
pid_t pid3 = run_command("/home/tropix/hw11-4", shmarg, semarg, pipe_from_p2_2, pipe_to_p5_2);
pid_t pid4 = run_command("/home/tropix/hw11-5", semarg, pipe_from_p3, pipe_from_p4);
Hmmm...some of these have the shmarg and some don't - is that inconsistency intentional or accidental? We'll assume intentional, so we need two versions of 'run_command()':
static pid_t run_cmd4(const char *cmd, const char *shmarg, const char *semarg,
const char *fdarg1, const char *fdarg2)
{
pid_t pid = fork();
if (pid < 0)
err_exit("Failed to fork\n");
else if (pid == 0)
{
execl(cmd, cmd, shmarg, semarg, fdarg1, fdarg2, (char *)0);
err_exit("Failed to exec %s\n", cmd);
}
return pid;
}
static pid_t run_cmd3(const char *cmd, const char *semarg,
const char *fdarg1, const char *fdarg2)
{
pid_t pid = fork();
if (pid < 0)
err_exit("Failed to fork\n");
else if (pid == 0)
{
execl(cmd, cmd, semarg, fdarg1, fdarg2, (char *)0);
err_exit("Failed to exec %s\n", cmd);
}
return pid;
}
And then:
// Create child processes
pid_t pid1 = run_cmd3("/home/tropix/hw11-2", semarg, pipe_to_p3, pipe_to_p4);
pid_t pid2 = run_cmd4("/home/tropix/hw11-3", shmarg, semarg, pipe_from_p2, pipe_to_p5_1);
pid_t pid3 = run_cmd4("/home/tropix/hw11-4", shmarg, semarg, pipe_from_p2_2, pipe_to_p5_2);
pid_t pid4 = run_cmd3("/home/tropix/hw11-5", semarg, pipe_from_p3, pipe_from_p4);
If it was my code, the names of the variables would be more uniform - and probably in arrays:
// Create child processes
pid_t pid1 = run_cmd3("/home/tropix/hw11-2", semarg, pipearg[0], pipearg[1]);
pid_t pid2 = run_cmd4("/home/tropix/hw11-3", shmarg, semarg, pipearg[2], pipearg[3]);
pid_t pid3 = run_cmd4("/home/tropix/hw11-4", shmarg, semarg, pipearg[4], pipearg[5]);
pid_t pid4 = run_cmd3("/home/tropix/hw11-5", semarg, pipearg[6], pipearg[7]);
Then, finally, you have the code:
// Closing Pipes
close(pipe1[1]);
close(pipe2[1]);
close(pipe3[1]);
close(pipe4[1]);
close(pipe1[0]);
close(pipe2[0]);
close(pipe3[0]);
close(pipe4[0]);
//Wait for child process completion
while (waitpid(0, NULL, 0) != 0)
;
printf("Child Processes Complete.\n");
// Remove Semaphores and Shared Memory
semctl(sem_id,0,IPC_RMID);
shmctl(shmid, IPC_RMID, NULL);
I am deeply suspicious that the run_cmdX() functions also need to close a large selection of the pipes - at least every descriptor of the pipes not intended for communication with their sub-process.
Organizing that cleanly is trickier - but can be done with care. I'd probably create the pipes in a single array:
if (pipe(&pipes[0]) != 0 || pipe(&pipes[2]) != 0 ||
pipe(&pipes[4]) != 0 || pipe(&pipes[6]) != 0)
err_exit("Failed to create a pipe\n");
Then I'd create a function:
void pipe_closer(int *pipes, int close_mask)
{
for (i = 0; i < 8; i++)
{
if ((mask & (1 << i)) != 0)
close(pipes[i]);
}
}
Then it can be called to close the unneeded pipes:
pipe_closer(pipes, 0xFF); // Close them all - main()
pipe_closer(pipes, 0xFC); // All except 0, 1
pipe_closer(pipes, 0xF3); // All except 2, 3
pipe_closer(pipes, 0xCF); // All except 4, 5
pipe_closer(pipes, 0x3F); // All except 6, 7
You just have to arrange for the right mask to be passed with each of the run_cmdN() functions, and the correct calls to be made. If the pipes array is not global, that will need to be passed too. I'd also look at how to encode the data tidily so that the calls to run_cmdN() are as regular and symmetric as possible.
Kernighan & Plauger's "The Elements of Programming Style" (2nd Edn, 1978; hard to find, I suspect) contains many magnificent quotes. The immediately apposite one is (bold emphasis added, italics in original):
[T]he subroutine call permits us to summarize the irregularities in the argument list where we can quickly see what is going on.
The subroutine itself summarizes the regularities of the code, so repeated patterns need not be used.
This can be viewed as part of the DRY (Don't Repeat Yourself) principle of programming. The err_exit() function call encapsulates three or four lines of code - a print and an exit plus the braces, depending on your preferred layout. The run_command() functions are a prime case of DRY. The proposed pipe_closer()is yet another.

Resources