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).
Related
I am trying to do the equivalent of the bash command ls>foo.txt in C.
The code bellow redirects the output to a variable.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(){
int pfds[2];
char buf[30];
pipe(pfds);
if (!fork()) {
close(pfds[0]);
//close(1);//Close stdout
//dup(pfds[1]);
//execlp("ls", "ls", NULL);
write(pfds[1], "test", 5); //Writing in the pipe
exit(0);
} else {
close(pfds[1]);
read(pfds[0], buf, 5); //Read from pipe
wait(NULL);
}
return 0;
}
The comments lines refer to those operations that I believe that are required for the redirection.
What should I change to redirect the output of ls to foo.txt?
While dealing with redirecting output to a file you may use freopen().
Assuming you are trying to redirect your stdout to a file 'output.txt' then you can write-
freopen("output.txt", "a+", stdout);
Here "a+" for append mode. If the file exists then the file open in append mode. Otherwise a new file is created.
After reopening the stdout with freopen() all output statement (printf, putchar) are redirected to the 'output.txt'. So after that any printf() statement will redirect it's output to the 'output.txt' file.
If you want to resume printf()'s default behavior again (that is printing in terminal/command prompt) then you have to reassign stdout again using the following code-
freopen("/dev/tty", "w", stdout); /*for gcc, ubuntu*/
Or -
freopen("CON", "w", stdout); /*Mingw C++; Windows*/
However similar technique works for 'stdin'.
What your code essentially does is that you open a pipe, then fork the process and in the child process (in commented code) close stdout, duplicate the pipe to stdout and execute and ls command, and then (in non-commented code) write 4 bytes to the pipe. In the parent process, you read data from the pipe and wait for the completion of the child process.
Now you want to redirect stdout to a file. You can do that by opening a file using the open() system call and then duplicating that file descriptor to stdout. Something like (I haven't tested this so beware of bugs in the code):
int filefd = open("foo.txt", O_WRONLY|O_CREAT, 0666);
if (!fork()) {
close(1);//Close stdout
dup(filefd);
execlp("ls", "ls", NULL);
} else {
close(filefd);
wait(NULL);
}
return 0;
However, you can also use the freopen as suggested by the other answer.
However, I have several concerns of your code and of my modified code:
The pipe() and open() system calls can fail. You should always check for system call failure.
The fork() system call can fail. Ditto.
dup2() can be used instead of dup(); otherwise the code will fail if stdin is not open as it duplicates to the first available file descriptor.
The execlp() system call can fail. Ditto.
I think wait() can be interrupted by a signal (EINTR). It's recommended to wrap it around a wrapper that retries the system call if it's aborted by a signal (errno == EINTR).
I am trying to do the equivalent of the bash command ls>foo.txt in C.
The code bellow redirects the output to a variable.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(){
int pfds[2];
char buf[30];
pipe(pfds);
if (!fork()) {
close(pfds[0]);
//close(1);//Close stdout
//dup(pfds[1]);
//execlp("ls", "ls", NULL);
write(pfds[1], "test", 5); //Writing in the pipe
exit(0);
} else {
close(pfds[1]);
read(pfds[0], buf, 5); //Read from pipe
wait(NULL);
}
return 0;
}
The comments lines refer to those operations that I believe that are required for the redirection.
What should I change to redirect the output of ls to foo.txt?
While dealing with redirecting output to a file you may use freopen().
Assuming you are trying to redirect your stdout to a file 'output.txt' then you can write-
freopen("output.txt", "a+", stdout);
Here "a+" for append mode. If the file exists then the file open in append mode. Otherwise a new file is created.
After reopening the stdout with freopen() all output statement (printf, putchar) are redirected to the 'output.txt'. So after that any printf() statement will redirect it's output to the 'output.txt' file.
If you want to resume printf()'s default behavior again (that is printing in terminal/command prompt) then you have to reassign stdout again using the following code-
freopen("/dev/tty", "w", stdout); /*for gcc, ubuntu*/
Or -
freopen("CON", "w", stdout); /*Mingw C++; Windows*/
However similar technique works for 'stdin'.
What your code essentially does is that you open a pipe, then fork the process and in the child process (in commented code) close stdout, duplicate the pipe to stdout and execute and ls command, and then (in non-commented code) write 4 bytes to the pipe. In the parent process, you read data from the pipe and wait for the completion of the child process.
Now you want to redirect stdout to a file. You can do that by opening a file using the open() system call and then duplicating that file descriptor to stdout. Something like (I haven't tested this so beware of bugs in the code):
int filefd = open("foo.txt", O_WRONLY|O_CREAT, 0666);
if (!fork()) {
close(1);//Close stdout
dup(filefd);
execlp("ls", "ls", NULL);
} else {
close(filefd);
wait(NULL);
}
return 0;
However, you can also use the freopen as suggested by the other answer.
However, I have several concerns of your code and of my modified code:
The pipe() and open() system calls can fail. You should always check for system call failure.
The fork() system call can fail. Ditto.
dup2() can be used instead of dup(); otherwise the code will fail if stdin is not open as it duplicates to the first available file descriptor.
The execlp() system call can fail. Ditto.
I think wait() can be interrupted by a signal (EINTR). It's recommended to wrap it around a wrapper that retries the system call if it's aborted by a signal (errno == EINTR).
My task is to write a C program that executes the command "ls -l /bin/?? | grep rwxr-xr-x | sort". There are 3 child processes where each of them executes one of the commands separately and sends the result through a pipe to the next child process. I'm using a Swedish modified verision of debian so the error message is in Swedish, but i'll translate the error i get, it's something along the lines of: sort: failed to status -: unknown fileidentifier.
Maybe it's my pipes that do not work as intended, I'm not too sure about the close() commands. I'm pretty sure the error comes from the pipes. Would be grateful if someone could run the program and get the english error message.
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
int main()
{
int ret;
int fds1[2], fds2[2], fds3[2];
char buf[20];
pid_t pid;
///initiating pipes
ret=pipe(fds1);
if(ret == -1){
perror("could not pipe");
exit(1);
}
ret=pipe(fds2);
if( ret == -1){
perror("could not pipe");
exit(1);
}
ret=pipe(fds3);
if (ret == -1){
perror("could not pipe");
exit(1);
}
pid=fork();
if(pid==-1){
fprintf(stderr,"fork failed");
exit(0);
}
if(pid==0){
///CHILD 1
close(1);
dup(fds1[1]);
close(fds1[0]);
close(fds1[1]);
close(0);
execlp("/bin/sh","bin/sh", "ls-l /bin/??", (char *)NULL);
}
else{
wait(0);
}
pid=fork();
if(pid==-1){
fprintf(stderr,"fork failed");
exit(0);
}
if(pid==0){
close(0);
dup(fds1[0]);
close(fds1[0]);
close(fds1[1]);
close(1);
dup(fds2[1]);
close(fds2[0]);
close(fds2[1]);
execlp("/usr/share/grep/", "grep", "rwxr-xr-x", NULL);
}
else{
wait(0);
}
close(fds1[0]);
close(fds1[1]);
pid=fork();
if(pid==-1){
fprintf(stderr,"fork failed");
exit(0);
}
if(pid==0){
close(0);
dup(fds2[0]);
close(fds2[0]);
close(fds2[1]);
execlp("sort", "sort", NULL);
}
else{
wait(0);
}
close(fds2[0]);
close(fds2[1]);
}
Your code has several problems, but before I discuss them, let me introduce you to a flavor of one of my favorite preprocessor macros:
#define DO_OR_DIE(x, s) do { \
if ((x) < 0) { \
perror(s); \
exit(1); \
} \
} while (0)
Using this macro where it is applicable can clarify your code by replacing all the boilerplate error checking. For example, this:
ret=pipe(fds1);
if(ret == -1){
perror("could not pipe");
exit(1);
}
becomes just
DO_OR_DIE(pipe(fds1), "pipe");
That makes it a lot easier to see and focus on the key parts of the code, and it's easier to type, too. As a result, it also reduces the temptation to skip error checks, such as those for your calls to dup().
Now, as to your code. For me, it exhibits not just the one misbehavior you now describe in your question, but three:
It emits an error message "bin/sh: ls-l /bin/??: No such file or directory".
It emits the error message you describe, "sort: stat failed: -: Bad file descriptor".
It does not terminate.
The first error message pertains to multiple problems in the arguments to your first execlp() call. If you want to launch a shell and specify a command for it to run, as opposed to a file from which to read commands, then you must pass the -c option to it. Additionally, you've omitted mandatory whitespace between the ls and its arguments. It looks like you want this:
execlp("/bin/sh","sh", "-c", "ls -l /bin/??", (char *)NULL);
Setting aside the second problem for the moment, let's turn to the failure to terminate. You have several problems in this area, falling into these categories:
Holding pipe ends open where you should ensure them closed
Calling wait() at the wrong points
When you set up a pipe between two processes, you generally want to make sure that there are no open file descriptors on either end of the pipe other than one on the write end held by one process, and one on the read end held by the other process. Each end should be open exactly once, in exactly one process. Since the processes being connected invariably inherit these file descriptors from their parent, it is essential that the parent close its copies (except that the parent will want to keep one open in the event that it itself is one of the communicating processes).
The process on the read end of a pipe will not see EOF on that pipe until all open file descriptors on the write end are closed. Child processes running programs such as grep and sort that read their input to its end will hang indefinitely if the write end of the pipe is not completely closed.
That can be a particularly perverse problem when the child reading the pipe also has a copy of the write end of that pipe, unused, or if one of its siblings does.
Additionally, the whole point of a pipeline is that the processes involved run concurrently. If you wait() after starting one before starting the next, then at minimum you prevent such concurrency. Worse, however, that can also cause your program to hang, because a pipe has finite buffer capacity. If the child is writing output to a pipe, but no one is reading it, then the pipe's buffer can fill to capacity, at which point the child blocks. If the parent is waiting for the child to finish before launching the process that will drain the pipe, then you have a deadlock. Therefore, you should start all the processes in the pipeline first, then wait for them all.
Having fixed such problems in your code, I find that the program emits a different error for me:
execlp: No such file or directory
(The specifics of this message derive from the nature of my fixes.) This should be especially concerning, because if execlp() fails then it returns in the process in which it was called. In your cases, control will then fall right out of your if statement, into the code intended only for the parent to execute. For this reason, it is essential to handle errors from execlp(). At minimum, add a call to exit() or _Exit() immediately after.
But what's failing? Well, it's the grep this time. Note that you specify the command to execute as "/usr/share/grep/" -- that trailing / is erroneous, and the path itself is suspect. On my system, the correct path is /usr/bin/grep, but since we're using execlp, which resolves the executable in the path, we might as well omit the path altogether:
execlp("grep", "grep", "rwxr-xr-x", (char *) NULL);
Et voilĂ ! After making that correction as well, your program runs for me.
Additional advice: do not use dup() when you care what file descriptor number you want the duplicate to have, such as when you're trying to dup onto one of the standard streams. Use dup2() for that, which has the additional advantage that you don't need to close the specified file descriptor first.
I am trying to do the equivalent of the bash command ls>foo.txt in C.
The code bellow redirects the output to a variable.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(){
int pfds[2];
char buf[30];
pipe(pfds);
if (!fork()) {
close(pfds[0]);
//close(1);//Close stdout
//dup(pfds[1]);
//execlp("ls", "ls", NULL);
write(pfds[1], "test", 5); //Writing in the pipe
exit(0);
} else {
close(pfds[1]);
read(pfds[0], buf, 5); //Read from pipe
wait(NULL);
}
return 0;
}
The comments lines refer to those operations that I believe that are required for the redirection.
What should I change to redirect the output of ls to foo.txt?
While dealing with redirecting output to a file you may use freopen().
Assuming you are trying to redirect your stdout to a file 'output.txt' then you can write-
freopen("output.txt", "a+", stdout);
Here "a+" for append mode. If the file exists then the file open in append mode. Otherwise a new file is created.
After reopening the stdout with freopen() all output statement (printf, putchar) are redirected to the 'output.txt'. So after that any printf() statement will redirect it's output to the 'output.txt' file.
If you want to resume printf()'s default behavior again (that is printing in terminal/command prompt) then you have to reassign stdout again using the following code-
freopen("/dev/tty", "w", stdout); /*for gcc, ubuntu*/
Or -
freopen("CON", "w", stdout); /*Mingw C++; Windows*/
However similar technique works for 'stdin'.
What your code essentially does is that you open a pipe, then fork the process and in the child process (in commented code) close stdout, duplicate the pipe to stdout and execute and ls command, and then (in non-commented code) write 4 bytes to the pipe. In the parent process, you read data from the pipe and wait for the completion of the child process.
Now you want to redirect stdout to a file. You can do that by opening a file using the open() system call and then duplicating that file descriptor to stdout. Something like (I haven't tested this so beware of bugs in the code):
int filefd = open("foo.txt", O_WRONLY|O_CREAT, 0666);
if (!fork()) {
close(1);//Close stdout
dup(filefd);
execlp("ls", "ls", NULL);
} else {
close(filefd);
wait(NULL);
}
return 0;
However, you can also use the freopen as suggested by the other answer.
However, I have several concerns of your code and of my modified code:
The pipe() and open() system calls can fail. You should always check for system call failure.
The fork() system call can fail. Ditto.
dup2() can be used instead of dup(); otherwise the code will fail if stdin is not open as it duplicates to the first available file descriptor.
The execlp() system call can fail. Ditto.
I think wait() can be interrupted by a signal (EINTR). It's recommended to wrap it around a wrapper that retries the system call if it's aborted by a signal (errno == EINTR).
I m learning interprocess communication ...this is the code that bugs me
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int pfds[2];
pipe(pfds);
if (!fork())
{
printf("I m the child process\n");
close(1); /* close normal stdout */
dup(pfds[1]); /* make stdout same as pfds[1] */
close(pfds[0]); /* we don't need this */
execlp("ls", "ls", NULL);
}
else
{
printf("I m the parent process\n");
close(0); /* close normal stdin */
dup(pfds[0]); /* make stdin same as pfds[0] */
close(pfds[1]); /* we don't need this */
execlp("wc", "wc", "-l", NULL);
}
return 0;
}
These are some of the questions :-
1) I agree that after the execlp() , nothing gets executed , but my printf() statements are before the execlp() , then still why they does not get executed ??
2)The program acts as a pipe command in linux
hence it executes like "ls | wc -l" , but how does the system knows to execute the program like "ls | wc -l" and not "wc -l | ls" .. ??
3) I think the 2) question is because i have closed the stdout and used it as my pfds[1] and closed the stdin and used as my pads[0]..but what if the one threads gets exited before the other .. ??
4) (i m using Xcode as well as gcc) , when run the above program in gcc , it works well , but when run in Xcode it shows a "SIGTRAP" and returns "1" in the console
PLZ HELP ...
PS : It would be better if someone tell me how to see execution of the seperate threads in any general problem !!
thank you !!
Your printf is being executed. However, it only gets flushed after you redirect stdout, so its output is not shown on the terminal. Try fprintf(stderr, ...) if you want to see it on stderr, or fflush(stdout) before you start messing with file descriptors.
You attach the writing end of the pipe to the stdout of the ls process, and the reading end to stdin on wc, so there's no confusion.
I'm not sure what your concern is here. The ls process exits once it finishes listing the directory, and wc exits once it runs out of input and prints the number of lines. The pipe gets cleaned up automatically once both processes which hold an endpoint have closed it (by exiting).
Hard to say. What does the debugger show? The Xcode debugging environment is primarily aimed at developing and debugging desktop/mobile applications, though, so it's probably just a fluke of some sort.