ps command linux vs unix different behavior in c program - c

I have a simple c program that executes 'ps' and pipes it to 'grep', basically 'ps | grep x'.
the code goes more or less something like this:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
int pipefd[2];
int pid;
pipe(pipefd);
pid=fork();
if (pid == 0){
close(pipefd[1]);
dup2(pipefd[0], 0);
close(pipefd[0]);
execlp("grep", "grep", "b", (char *) 0);
}
else{
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ps", "ps", (char *) 0);
}
exit(0);
}
The problem that i have is that when i run this on unix (Solaris) is works perfect, but when i run this on (Debian) it executes properly but gives me an error message.
error message:
Signal 17 (CHLD) caught by ps (procps-ng version 3.3.3).
ps:display.c:59: please report this bug
I have try the same program running different commands like 'ls' and 'grep' with no problem on either os. What makes 'ps' different?
EDIT:
added the included libraries to the code.

When your program calls fork, it creates a parent process and a child process. In the child process fork returns 0 and in the parent it returns 1. Whenever a child process terminates, a SIGCHLD signal is sent to the parent process.
Now, in your case you call execlp in both the parent and child process, which replaces the running process image but does not change the relationship. This means that ps is your parent process and grep is your child process. Normally this would not matter, as programs ignore SIGCHLD by default, but ps catches all unknown signals and quits with the message you see there. You can see the relevant function in the source code for ps (or rather procps).

Related

fork() and execvp() unexpected outcome when used with sudo

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;
}

C program cannot use pipe to execute the "more" command in "execlp" in order to view program's output

thanks in advance for any help.
I am trying to replicate the behavior of the shell command ls -1 /usr/include | more using a C program.
I wrote this code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
int page[2]; // page is the name of my pipe
pipe(page);
switch (fork()){
case -1:
exit(1);
break;
case 0:;
close(page[1]);
dup2(page[0], 0);
close(page[0]);
execlp("more", "more", NULL);
default:
close(page[0]);
dup2(page[1], 1);
close(page[1]);
execlp("ls", "ls", "-1", "/usr/include", NULL);
break;
}
}
But it only prints one page (as more would do) and causes some weird behavior that blocks my terminal (forcing me to use reset to set it back to normal).
Your original code creates a kind of a race condition between the parent and the child processes, and the shell which started your program. The ls process ends before more can read all the data from the pipe, and since in your program the parent process is replaced by the ls process, when that ls process ends (after writing all its output into the pipe buffer) it exits, and in doing so closes the pipe and gives control back to the shell which will immediately get ready to read another command.
So, initially both more and the shell may be reading from the same TTY device (it's reading from its STDERR descriptor, still attached to your TTY), and then once more eventually gets some input it will try to read from the pipe again (its STDIN) and it will get an end-of-file (the pipe has been closed on the write end by the exit of ls), and so more will now also exit (without printing any more output). There's also a possible race between the more process and the shell as to which (re)sets the TTY driver modes and when.
An alternative implementation of your program is for the original parent process to start two child processes, one for more and one for ls, and then to wait for both processes to terminate, but this will of course require more system resources.
I just realized the roles of the parent and the child process were being mixed up. The one to run the more command should be the parent. Since more is an interactive command, the terminal will respond better to it as the parent (I'm guessing).
So to solve my issue, I switched the roles of the parent and the child.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
int page[2]; // page is the name of my pipe
pipe(page);
switch (fork()){
case -1:
exit(1);
break;
case 0:;
close(page[0]);
dup2(page[1], 1);
close(page[1]);
execlp("ls", "ls", "-1", "/usr/include", NULL);
break;
default:
close(page[1]);
dup2(page[0], 0);
close(page[0]);
execlp("more", "more", NULL);
}
}
WHY DOES THIS SOLVES THE PROBLEM? (I still do not clearly understand why it worked!)

error in executing system function with STDOUT_FILENO closed

I have an strange issue. I am not very good in C language. I am trying to create a daemon to execute my bash script based service in Linux. To make it easy I have made the code simple. Below is my code.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
int main(int argc, char* argv[])
{
pid_t process_id = 0;
pid_t sid = 0;
process_id = fork();
if (process_id < 0)
{
printf("fork failed!\n");
exit(1);
}
if (process_id > 0)
{
printf("daemon creatd with process id %d \n", process_id);
exit(0);
}
umask(0);
sid = setsid();
if(sid < 0)
{
fprintf(stderr, "Return error\n");
exit(1);
}
chdir("/");
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
int status = system("ls");
openlog("slog", LOG_PID|LOG_CONS, LOG_USER);
syslog(LOG_INFO, "Returned status is %d", status);
closelog();
return (0);
}
As you can see, the above program will execute the system function to execute the ls command and output the exit code to system log.
After I compile the program and run, in the logs I can see the status code is 512. But If I comment out the following line,
close(STDOUT_FILENO);
then it works perfect and I can see in the log the status code is 0,
What I might be doing wrong?
UPDATE
My program is pretty big and I am not using ls in real environment. i made the program simple to reproduce the issue what I am facing. Also, to see the status of program, I am not looking at the output but the status code in the syslog.
In case it is not clear from other comments and answers, ls writes its output to stdout. If it can not write to stdout it terminates and (apparently) sets an status code of 512 (non-zero in any case).
The child process will inherit stdout from the parent, so if you do not close stdout in the parent, the child will have a stdout to write to and the command will succeed. If you close stdout, then the child has nowhere to write its output and the error occurs.
So, although ls is just an example for this question, your actual child process is writing to stdout which results in the same problem.
The simplest way around this, and assuming that you do not want the child's output, is to redirect the child's stdout on the command line:
int status = system("ls >/dev/null 2>&1");
This will redirect stdout and stderr to /dev/null, effectively throwing away the child's output while still giving it somewhere to write to.
Your daemon creation looks fine. But daemon processes by convention do not have a controlling terminal which you accomplish by closing all the standard file descriptors and call setsid() to create a new session to make the daemon a session leader. So, you can't make the daemon produce any output on the stdout stream. It obviously works if you don't close the stdout stream.
So, what you are doing is trying to write something to a tty from a terminal. So, you either don't need a daemon for this purpose or you need to write to a different file (instead of stdout).

Using processes to run commands with pipes

I'm still new to processes,pipes and dup2, therefore I'd like someone to help me figure out what's wrong with a program I've created. This program is supposed to run ls | wc. So far the output I get is :
wc : standard input : Bad file descriptor
0 0 0
ls : write error : Bad file descriptor
After I get this output, the terminal still accepts inputs. It's like wc is still running, although if I put commands like ls first(without any other input before) it runs them and shuts down. I tried running ps before/after and while the program was still running and it didn't show any process being open aside from bash and ps. (I'm running this program in Linux terminal)
Here's my code :
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<string.h>
#include<sys/wait.h>
#include<errno.h>
int main(int argc, char* argv[]){
pid_t pid;
int fd[2];
char com1[1024] = ("ls");
char com2[1024] = ("wc");
pipe(fd);
pid = fork();
if(pid == 0){
open(fd[1]);
dup2(fd[0],STDOUT_FILENO);
close(fd[0]);
execlp(com1, com1, NULL);
}
else {
pid = fork();
if (pid == 0){
open(fd[0]);
dup2(fd[1],STDIN_FILENO);
close(fd[1]);
execlp(com2, com2, NULL);
}
}
return 0;
}
Bear in mind that I know some if commands for checking are required(like if(pid<0)exit(0);) but I tried to simplify my code as much as possible in order to see if there's any mistake due to carelessness.
Thank you in advance!
According to the pipe manual page:
pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe.
Now take this line from the first child, the process that calls the ls command:
dup2(fd[0],STDOUT_FILENO);
Here you duplicate the read end of the pipe to STDOUT_FILENO, i.e. where output is written. If you stop and think a little about it, how would you write to a read-only file-descriptor like fd[0]?
Same with the other child process, where you make the write end of the pipe standard input.
The solution is simple: Swap places of the descriptors you duplicate. Use fd[1] for the first child process, and fd[0] for the second child process.
In the first process where you call the ls command:
dup2(fd[1],STDOUT_FILENO);
close(fd[1]);
execlp(com1, com1, NULL);
And in the second child process where you call the wc command:
dup2(fd[0],STDIN_FILENO);
close(fd[0]);
execlp(com2, com2, NULL);

Finding pid of background process from c prog

I am writing a program for creating a shell which handles basic functionalities such as executing basic commands,piping,redirection,executing background process.However i am not being able to kill a background process, i need to know the pid() of the background process so that i can send a kill call along with the pid.Any idea how to get the pid() of a background process from a c pogram?
For running the commands I am taking input from commandline into an array,parsing it and putting the command in arr[0] and the subsequent arguments in the subsequent indexes,i am taking the PATH of the system into another array and storing them as strings by using strtok and delim option as :,after this i am concatenating the path with the command,and then doing an execv().
I am stuck with this part where i have to kill a background process.Any suggestion would be extremely helpful.
Thanks in advance.
fork returns the PID of the child in the parent process, store it someplace and then use it to kill?
You should do something like this:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
pid_t pID = fork();
if (pID == 0) {
execl("/bin/ls", "/bin/ls", "-r", "-t", "-l", (char *) 0);
} else {
waitpid(pID, NULL, 0); // wait for child process
}
}
You can call getpid() (in the childprosses), or the pid of the child is returned to the parent when calling fork()

Resources