So the brief summary is- I have a program which recursively searches for a file with a certain extension. Each time it finds one, it makes a copy, makes some changes to the copy, creates a patch (by using diff and execvp()) and delete the original file.
The problem I'm having is, after several hundred files, fork() returns with "Resource temporarily unavailable". I added a counter to see how many processes are still running when this failure happens, and it looks like none have been closed- the number of processes open is always the same as the number of files processed.
Now, I was under the impression that the flow should go something like this-
fork();//creates a child process
dostuff();//in the child process
_exit(1);//return control to the parent
but things appear not to be so simple. Perhaps somebody here may spot something obvious I'm missing in the code.
I have posted the 'cleanup' function which is responsible for the forking & patching- the rest is split over several files so hopefully this is enough.
(argument "name" is the original filename, and "newname" is the modified copy.)
void cleanup (char * name, char * newname)
{
if (pf)
{
pid_t patch_pid;
char * const diffargs[5] = {thisdiff, "-u", newname, name, NULL};
char * patchname = malloc(strlen(name) + 6);
strcpy(patchname, name);
strcat(patchname, ".patch");
if((patch_pid = fork()) < 0 )
{
printf("fork failed.\n%s\nfilecount: %ld\nopen forks: %d\n", strerror(errno), filecount, pcount);
exit(-1);
}
pcount++;
if (patch_pid == 0)
{
FILE *pfp;
if ((pfp = fopen(patchname, "w")) == NULL)
{
printf("Error opening file \"%s\" for writing.\n%s\n", patchname, strerror(errno));
exit(-1);
}
dup2(fileno(pfp), STDOUT_FILENO);
fclose(pfp);
execvp(diffargs[0], diffargs);
free(patchname);
if (remove(name) != 0)
{
printf("Error removing file %s\n%s\n", name, strerror(errno));
exit(-1);
}
if (rename(newname, name) != 0)
{
printf("Error renaming file %s\n%s\n", newname, strerror(errno));
exit(-1);
}
pcount--;
_exit(1);
}
}
else if (!df && !xf)
{
if (remove(name) != 0)
{
printf("Error removing file %s\n%s\n", name, strerror(errno));
exit(-1);
}
if (rename(newname, name) != 0)
{
printf("Error renaming file %s\n%s\n", newname, strerror(errno));
exit(-1);
}
}
}
Two suggestions:
Understand that exec*() replaces your process (if successful). Any code following it is unreachable.
Reap the exit status of exited processes with one of the wait*() functions; possibly in a signal handler for SIGCHLD.
Bonus suggestion: Read W. Richard Stevens' Advanced Programming in the Unix Environment; it's the Bible for tasks like these.
You probably want an else clause after the fork to handle the parent process as well. Something like the following might work;
void cleanup (char * name, char * newname)
{
if (pf)
{
/* SNIP - unchanged */
if (patch_pid == 0) /* child */
{
FILE *pfp;
if ((pfp = fopen(patchname, "w")) == NULL)
{
printf("Error opening file \"%s\" for writing.\n%s\n",
patchname, strerror(errno));
exit(EXIT_FAILURE);
}
dup2(fileno(pfp), STDOUT_FILENO);
fclose(pfp);
execvp(diffargs[0], diffargs);
perror("execvp");
exit(EXIT_FAILURE);
}
else /* parent */
{
pid_t rc;
int stat;
free(patchname);
rc = waitpid(patch_pid, &stat, 0);
if (rc < 0)
{
perror("waitpid");
/* do something appropriate here */
}
else
{
/* if you care about your children */
if (WIFEXITED(stat))
{
printf("%d exited with status %d\n",
(int)rc, WEXITSTATUS(stat));
}
else if (WIFSIGNALED(stat))
{
printf("%d terminated because of signal %d\n",
(int)rc, WTERMSIG(stat));
}
else if (WIFSTOPPED(stat))
{
printf("%d was STOPPED with signal %d\n",
(int)rc, WSTOPSIG(stat));
}
}
pcount--;
if (remove(name) != 0)
{
printf("Error removing file %s\n%s\n", name, strerror(errno));
exit(EXIT_FAILURE);
}
if (rename(newname, name) != 0)
{
printf("Error renaming file %s\n%s\n", newname, strerror(errno));
exit(EXIT_FAILURE);
}
}
}
}
I also high recommend reading Advanced Programming in the UNIX Environment by the late W. Richard Stevens. Actually, you should add UNIX Network Programming volume 1 & 2 to your list as well ;)
The other thing that I noticed is that you have a memory management error related to patchname. The malloc call isn't accounting for the extra NUL character at the end of the string.
Related
I have a problem with dup2 syscall.
I added a while loop(a part of my program) that running over a students directory,
each student has a "c" file that I compile to an "exe" file. the students' programs scan from
testInput file (using dup2 to take the keyboard) 2 numbers, add them and then the answers is written down to programOutPut file.
afterward, I compare the 2 programs with a comparison program I wrote and it helps me with WEXITSTATUS to know whether a student succeeded in the test.
The problem is that I try to write the grade sheet into the file and then print it also to the screen.
somehow it only appears on the file or the screen but not both.
while (myDirent = readdir(dir)) {
if (strcmp(myDirent->d_name, ".") == 0 || strcmp(myDirent->d_name, "..") == 0)
continue;
if ((pid = fork()) == 0) {
status=execlp("gcc", "gcc", "-o", mainPath, cPath, NULL); //compiling students code to main.out path
if (status == -1) {
fprintf(stderr, "gcc Exec failed\n");
exit(-1);
}
}
wait(&status);
fdin = open(testInputPath, O_RDONLY); //test input file I wrote to compare with student's output
if(fdin==-1){
fprintf(stderr, "Error opening test input file\n");
exit(-1);
}
fdout = open(programOutputPath, O_WRONLY | O_CREAT | O_TRUNC,0777); //opening file for each student
if(fdout==-1){
fprintf(stderr, "Error opening Student's program output file\n");
exit(-1);
}
if ((pid = fork()) == 0) {
dup2(fdin, 0);
dup2(fdout, 1);
status= execlp(mainPath,mainPath, NULL);
if (status == -1) {
fprintf(stderr, "Student's main Exec failed\n");
exit(-1);
}
}
wait(&status);
fdresults = open("results.txt", O_WRONLY | O_CREAT | O_APPEND, 0777); //grades sheet
if (fdresults == -1) {
fprintf(stderr, "Error opening results.csv file\n");
exit(-1);
}
if ((pid = fork()) == 0) {
status= execlp("./comp.out", "./comp.out", programOutputPath, expectedOutputPath, NULL); //compare program I wrote that simply compare 2 files and return value to status
if (status == -1) {
fprintf(stderr, "Compare Exec failed\n");
exit(-1);
}
}
wait(&status);
**dup2(fdresults, 1); //trying to write to the file the grades
printf("%s,%d\n", myDirent->d_name,WEXITSTATUS(status));
dup2(fdscreen, 1); // trying to return to stdout unsuccessfuly**
}//end of while loop
First, do not open a file that is already open (source). Right now, you are opening testInputPath and programOutputPath during each iteration of the the loop without closing them. This can lead to undefined behavior.
It's unclear where fdscreen came from. If you do something similar to this answer, then you can achieve what you're looking for using dup(2). You should flush the corresponding userspace file buffer using fflush(3) before calling dup2.
A Better Solution
A much better solution is to open results.txt outside the loop, and then use dprintf(3) to write directly to the file. This is more elegant, and avoids having to change stdout. So, you can easily replace the last three lines of the loop with
dprintf(fdresults, "%s,%d\n", myDirent->d_name, WEXITSTATUS(status));
Don't forget to close fdresults after the loop is complete. In general, each open(2) should have a corresponding close(2). Using this approach is strongly encouraged over changing stdout every iteration of the loop.
I have a program that forks a child and want it to communicate with its parent. However, I seem to get an error when closing the write end in the child.
The program stops inside the child and in the if (close(pfd1[1]) == -1)
Apparently it fails when the child wants to close the write end. Why?
/* Note: working under the assumption that the messages are of equal length */
int main(int argc, char * argv[])
{
int pfd1[2];
char buf[BUF_SIZE];
//checks pipefd1
if (pipe(pfd1) == -1)
{
printf("Error opening pipe 1!\n");
exit(1);
}
printf("Pipe opened with success. Forking ...\n");
// child 1
switch (fork())
{
case -1:
printf("Error forking child 1!\n");
exit(1);
case 0:
printf("\nChild 1 executing...\n");
/* close writing end of first pipe */
if (close(pfd1[1]) == -1)
{
printf("Error closing writing end of pipe 1.\n");
_exit(1);
}
/* read from pipe 1 */
if (read(pfd1[0], buf, 2000))
{
printf("Error reading to pipe 1.\n");
_exit(1);
}
/* close reading end of first pipe */
if (close(pfd1[1]) == -1)
{
printf("Error closing writing end of pipe 1.\n");
_exit(1);
}
printf("Message received child ONE: %s", buf);
printf("Exiting child 1...\n");
_exit(0);
default: //parent breaks just out
break;
}
printf("inside parent\n");
int child = 1;
char *message = "Hey child1, this is your parent speaking";
if(child == 1)
{
//close read end of pipe
if(close(pfd1[0]) == -1)
{
printf("Error closing reading end of the pipe.\n");
exit(EXIT_FAILURE);
}
printf("Parent closed read end of pipe1\n");
//read end is closed, now write to child
if(write(pfd1[1], message, strlen(message)))
{
printf("Error writing to the pipe.");
_exit(EXIT_FAILURE);
}
printf("Writing to child1 succeeded\n");
}
if (wait(NULL) == -1)
{
printf("Error waiting.\n");
exit(EXIT_FAILURE);
}
if (wait(NULL) == -1)
{
printf("Error waiting.\n");
exit(EXIT_FAILURE);
}
printf("Parent finishing.\n");
exit(EXIT_SUCCESS);
}
First of all, in the child's case you attempt to close the writing end of the pipe twice. I guess the second call to close(2) was meant to close the reading end, as mentioned in the comment above it:
/* close reading end of first pipe */
if (close(pfd1[0]) == -1)
{
printf("Error closing writing end of pipe 1.\n");
_exit(1);
}
Besides that, note that both read(2) and write(2) return the number of bytes that were actually read or written; in the case of error the return value is -1, so your error-checking conditions there should be fixed too, to something like:
/* read from pipe 1 */
if (read(pfd1[0], buf, 2000) < 0) {
printf("Error reading to pipe 1.\n");
_exit(1);
}
and
//read end is closed, now write to child
if(write(pfd1[1], message, strlen(message)) < 0) {
printf("Error writing to the pipe.");
_exit(EXIT_FAILURE);
}
On the principle of teaching to fish, a good technique to diagnose problems like this is to check what the error was and print a more informative message. Here is a technique I frequently put into a header file and use:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* This declaration and macro would really go into a header file: */
void fatal_error_helper( const char* msg, const char* sourcefile, int lineno, const char* syserr );
#define fatal_system_error(m) \
fatal_error_helper( (m), __FILE__, __LINE__, strerror(errno) )
/* This function definition would really go into a .c file: */
void fatal_error_helper( const char* const msg,
const char* const sourcefile,
const int lineno,
const char * const syserr )
{
fflush(stdout); /* Don't cross the streams! */
fprintf( stderr,
"%s at %s:%d: %s. Program terminated.\n",
msg, sourcefile, lineno, syserr
);
exit(EXIT_FAILURE);
}
/* Test driver: */
FILE* fails_to_open_file( const char* filename )
/* Returns a FILE* to an open file. If the operation fails, prints an
* error message and terminates the program.
*/
{
/* Do this in general before calling the function whose error value
* you check. Otherwise, you might report the wrong error message
* from an earlier call and really confuse someone.
*/
errno = 0;
FILE* result = NULL;
result = fopen(filename, ""); /* Fails. */
if (!result)
fatal_system_error("Failed to open file");
return result;
}
int main(void)
{
fails_to_open_file("nonexistent.file");
return EXIT_SUCCESS;
}
This gives an error message such as: Failed to open file at prog.c:26: Invalid argument. Program terminated.
I have 2 processes (an 'ls' process and a 'grep'). I'm using pipe to communicate between both of them. But the grep process is unable to read from the pipe. Could you help me figure out why so?
Here is my code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int pipe_fd[2];
int main()
{
pid_t p1,p2;
char *prog1_argv[4];
char *prog2_argv[2];
/* Build argument list */
prog1_argv[0] = "ls";
prog1_argv[1] = "-l";
prog1_argv[2] = "/";
prog1_argv[3] = NULL;
prog2_argv[0] = "grep";
prog2_argv[1] = "s";
prog2_argv[1] = NULL;
if (pipe(pipe_fd) < 0)
{
printf ("pipe failed");
}
p1 = fork();
if(p1 == 0)
{
printf("in child\n");
close(pipe_fd[0]);
if(dup2(pipe_fd[1],1)<0)
{
printf("dup failed:%d\n",errno);
}
close(pipe_fd[1]);
if(execvp (prog1_argv[0], prog1_argv)<0)
printf("exec failed");
}
if(p1>0)
{
printf("im in parent\n");
waitpid(p1,NULL,0);
printf("parent: child exited. Now test the pipe\n");
close(pipe_fd[1]);
if(dup2(pipe_fd[0],0)<0)
{
printf("dup failed:%d\n",errno);
}
close(pipe_fd[0]);
if(execvp (prog2_argv[0], prog2_argv)<0)
printf("exec failed");
}
}
Fundamentally, you should not be waiting for the ls to die before running the grep.
The ls command might generate so much data that it can't all be stored in the pipe, so the ls command will block until the other process reads from the pipe, but the other process is waiting for ls to complete before it tries to read anything from the pipe. This is a deadlock.
Also, by waiting like that, you enforce serial execution, which throws away the benefits of multiple cores.
There are a number of minor improvements you should make. There are various points at which you report errors. Errors should be reported on the standard error stream (stderr), not on stdout. You should also ensure the program does not continue after at least some of those errors.
You don't have to test the return value from any of the exec*() system calls. If the function returns, it failed. And again, you should ensure that the process exits after that. In this program, it doesn't matter that the child continues; in many programs, not exiting would lead to chaos (two processes trying to read standard input at the same time, for example).
There's no need for pipe_fd to be a global variable. Do make sure all your messages end with a newline, please. You didn't include <sys/wait.h> so you were working without a prototype in scope for the waitpid() function — that's generally a bad idea. You should set your compiler to fussy so it demands that every function has a prototype in scope before it is used or defined. You can initialize the argument lists in the definitions:
char *prog1_argv[] = { "ls", "-l", "/", NULL };
char *prog2_argv[] = { "grep", "s", NULL };
This has the crucial beneficial side-effect of not zapping prog_argv2[1] with a NULL pointer (as noted by Matthias in his answer. I also removed the sizes of the arrays; the second one was dimensioned at 2 and needed to be 3, but when you initialize like this, the compiler does the counting.
One thing you did correctly that was important to do correctly is ensure that the pipe file descriptors were all closed.
This works correctly for me:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main(void)
{
pid_t p1;
int pipe_fd[2];
char *prog1_argv[] = { "ls", "-l", "/", NULL };
char *prog2_argv[] = { "grep", "s", 0 };
if (pipe(pipe_fd) < 0)
{
fprintf(stderr, "pipe failed:%d\n", errno);
exit(1);
}
p1 = fork();
if (p1 == 0)
{
printf("In child\n");
close(pipe_fd[0]);
if (dup2(pipe_fd[1], 1) < 0)
{
fprintf(stderr, "dup failed:%d\n", errno);
exit(1);
}
close(pipe_fd[1]);
execvp(prog1_argv[0], prog1_argv);
fprintf(stderr, "exec failed:%d\n", errno);
exit(1);
}
if (p1 > 0)
{
printf("In parent\n");
close(pipe_fd[1]);
if (dup2(pipe_fd[0], 0) < 0)
{
fprintf(stderr, "dup failed:%d\n", errno);
exit(1);
}
close(pipe_fd[0]);
execvp(prog2_argv[0], prog2_argv);
fprintf(stderr, "exec failed:%d\n", errno);
exit(1);
}
fprintf(stderr, "Fork failed:%d\n", errno);
return(1);
}
You override your grep's argument. Try:
int main()
{
pid_t p1,p2;
char *prog1_argv[4];
char *prog2_argv[3];
/* Build argument list */
prog1_argv[0] = "ls";
prog1_argv[1] = "-l";
prog1_argv[2] = "/";
prog1_argv[3] = NULL;
prog2_argv[0] = "grep";
prog2_argv[1] = "s";
prog2_argv[2] = NULL;
// ...
Hi I need a little help with parallel download program.
Currently, it is downloading the same file in parallel instead of downloading multiple files at the same time.
Something is wrong with the fork and fgets, not sure how to fix them. Thank you.
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
FILE *file; /*declare the file pointer*/
#define LINE_MAX 1000
char line [LINE_MAX];
//Parent process
int main()
{
pid_t pid;
file= fopen ("urls.txt", "rt"); /*open file and read it*/
if(!file)
{
perror("fopen");
exit(-1);
}
int numberOfChildren = 0;
while (!feof (file)) {
memset (line,'\0',1000);
char *urlPtr;
while (!feof (file))
{
urlPtr= fgets (line,LINE_MAX, file);
if(urlPtr)
{
int lineLen = strlen(urlPtr);
urlPtr[lineLen-1] = '\0';
pid = fork();
++numberOfChildren;
if (pid == 0) { /* child process */
execlp("/usr/bin/wget", "wget", urlPtr, NULL);
}
else if (pid < 0) { /* error occurred */
fprintf(stderr, "Fork Failed");
exit(-1);
}
}
}
while (numberOfChildren>0) { /* parent process */
/* parent will wait for the child to complete */
wait (NULL);
--numberOfChildren;
printf ("Child Complete");
}
}
fclose (file); /*close file command*/
return 0;
}
You have the fork() check outside the URL reading loop. You first read lots of URLs and spawn a lot of children, and then do the pid check. Try
while (!feof (file))
{
urlPtr= fgets (line,LINE_MAX, file);
pid = fork();
if (pid == 0) { /* child process */
execlp("/usr/bin/wget", "wget", urlPtr, NULL);
}
else if (pid < 0) { /* error occurred */
fprintf(stderr, "Fork Failed");
exit(-1);
}
++numberOfChildren;
}
You should put a diagnostic print and exit after the execlp() (but in the child code after the if). You should probably also close the input file before you execute wget; the program doesn't need it open. No huge harm done this time, but it's good to be tidy. Your parent probably shouldn't exit just because one child failed to fork(); you have other children, in general, that you should wait for. You might stop processing the file at that point, though. And you should definitely forget about feof(); use while (fgets(line, sizeof(line), file) != 0), though that means you don't need urlPtr. The memset() is superfluous; fgets() initializes the string correctly.
Adaptation of code in question
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
FILE *file; /*declare the file pointer*/
#define LINE_MAX 1000
char line [LINE_MAX];
//Parent process
int main(void)
{
pid_t pid;
file = fopen("urls.txt", "rt"); /*open file and read it*/
if (!file)
{
perror("fopen");
exit(-1);
}
int numberOfChildren = 0;
memset(line,'\0',1000);
char *urlPtr;
while (!feof(file))
{
urlPtr= fgets(line, sizeof(line), file);
if (urlPtr)
{
int lineLen = strlen(urlPtr);
urlPtr[lineLen-1] = '\0';
pid = fork();
++numberOfChildren;
if (pid == 0)
{ /* child process */
execlp("/usr/bin/wget", "wget", urlPtr, NULL);
fprintf(stderr, "%d: wget failed\n", (int)getpid());
exit(1);
}
else if (pid < 0)
{ /* error occurred */
fprintf(stderr, "Fork Failed\n");
exit(-1);
}
else
printf("%d: %s\n", (int)pid, urlPtr);
}
}
/* JL: Moved block of code */
while (numberOfChildren>0)
{ /* parent process */
/* parent will wait for the child to complete */
int status;
int corpse = wait(&status);
--numberOfChildren;
printf("Child %d Complete (0x%04X)\n", corpse, status);
}
fclose(file); /*close file command*/
return 0;
}
Note that a while (!feof(file)) loop has been removed, but there is more unnecessary code that could go. Given data file
ftp://ftp.iana.org/tz/releases/tzcode2012f.tar.gz
ftp://ftp.iana.org/tz/releases/tzdata2012f.tar.gz
The code above works fetching the two files in parallel.
Alternative code
I like to use functions, even for relatively short stretches of code that are used once. Hence the be_childish() function added below. The error reporting is a bit tedious to write out, but that is no excuse for not doing it.
I briefly introduced a minimal function that does error reporting, based on an elaborate library of my own, but it would only be used twice in this code (for the file open error and after execlp() returns, which always and unconditionally indicates failure), but decided to leave it out. I have functions such as err_setarg0(), err_error(), err_remark() and err_usage() and using those would reduce each error report to a single line (and some more complex functions that could be told to include the PID automatically, etc). To me, it is worth having such a library as it makes error checking much, much simpler and therefore less painful and less likely to be skimped on.
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
static void be_childish(const char *urlPtr)
{
const char *wget = "/usr/bin/wget";
char *nl = strchr(urlPtr, '\n');
if (nl != 0)
*nl = '\0';
printf("%d: %s\n", (int)getpid(), urlPtr);
execlp(wget, "wget", urlPtr, NULL);
fprintf(stderr, "%d: Failed to execute %s\n", (int)getpid(), wget);
exit(EXIT_FAILURE);
}
int main(int argc, char **argv)
{
FILE *file;
char line [1024];
pid_t pid;
const char *name = "urls.txt";
int rc = EXIT_SUCCESS;
if (argc == 2)
name = argv[1];
else if (argc > 2)
{
fprintf(stderr, "Usage: %s [filename]\n", argv[0]);
exit(EXIT_FAILURE);
}
file = fopen(name, "rt"); /* Undefined behaviour per POSIX */
int numberOfChildren = 0;
if (file == 0)
{
fprintf(stderr, "Failed to open file %s\n", name);
exit(EXIT_FAILURE);
}
while (fgets(line, sizeof(line), file) != 0)
{
if ((pid = fork()) == 0)
{
fclose(file);
be_childish(line);
}
else if (pid < 0)
{
fprintf(stderr, "Fork Failed");
rc = EXIT_FAILURE;
break;
}
++numberOfChildren;
}
fclose(file);
/* Parent waits for the children to complete */
while (numberOfChildren > 0)
{
int status;
const char *result = "OK";
pid = wait(&status);
--numberOfChildren;
if (status != 0)
{
result = "Failed";
rc = EXIT_FAILURE;
}
printf("Child %d %s\n", pid, result);
}
return rc;
}
Note that the code takes a file name on the command line, defaulting to your "urls.txt". The "rt" open mode is not a POSIX or standard C mode; it will likely work, but "r" is sufficient to open a text file on all systems ("rb" to open a binary file works on all systems too, and is POSIX and standard C compliant). It reports which child process is processing each file listed. It reports the status (success or failure) of each child; it's own exit status is only success if all the children were successful.
You could probably control the verboseness from the command line. You might also want to keep a record of which child was processing each file so that you could report on files successfully downloaded, rather than on the processes which the user doesn't care about, really. That complicates the processing since you need to make a copy of each URL as you read it.
Note that you do need to trim the newlines off the end of the string (URL) before passing it to wget.
This code now tested (after adding the newline amendment), and it produced two files. The screen display is a bit of a mess; that's because each copy of wget thinks it is the sole user:
80334: ftp://ftp.iana.org/tz/releases/tzcode2012f.tar.gz
80335: ftp://ftp.iana.org/tz/releases/tzdata2012f.tar.gz
--2012-09-23 19:19:44-- ftp://ftp.iana.org/tz/releases/tzcode2012f.tar.gz
=> “tzcode2012f.tar.gz”
Resolving ftp.iana.org... --2012-09-23 19:19:44-- ftp://ftp.iana.org/tz/releases/tzdata2012f.tar.gz
=> “tzdata2012f.tar.gz”
Resolving ftp.iana.org... 192.0.32.8192.0.32.8, , 2620:0:2d0:200::82620:0:2d0:200::8
Connecting to ftp.iana.org|192.0.32.8|:21... Connecting to ftp.iana.org|192.0.32.8|:21... connected.
Logging in as anonymous ... connected.
Logging in as anonymous ... Logged in!
==> SYST ... Logged in!
==> SYST ... done. ==> PWD ... done. ==> PWD ... done.
==> TYPE I ... done.
==> TYPE I ... done. ==> CWD (1) /tz/releases ... done. ==> CWD (1) /tz/releases ... done.
==> SIZE tzdata2012f.tar.gz ... done.
==> SIZE tzcode2012f.tar.gz ... 206404
==> PASV ... 135543
==> PASV ... done. ==> RETR tzdata2012f.tar.gz ... done. ==> RETR tzcode2012f.tar.gz ... done.
Length: 206404 (202K) (unauthoritative)
0% [ ] 0 --.-K/s done.
Length: 135543 (132K) (unauthoritative)
100%[==============================================================================>] 135,543 72.7K/s in 1.8s
100%[==============================================================================>] 206,404 81.4K/s in 2.5s
2012-09-23 19:19:48 (72.7 KB/s) - “tzcode2012f.tar.gz” saved [135543]
Child 80334 OK
2012-09-23 19:19:48 (81.4 KB/s) - “tzdata2012f.tar.gz” saved [206404]
Child 80335 OK
I'm using the execl function to run a Linux process from C. When I do, for example:
int cmd_quem() {
int result;
result = fork();
if(result < 0) {
exit(-1);
}
if (result == 0) {
execl("/usr/bin/who", "who", NULL);
sleep(4); //checking if father is being polite
exit(1);
}
else {
// father's time
wait();
}
return 0;
}
I get on the console the result of doing "who" on the terminal. What I'd like to know is if there is any function to "catch" the output result from a command. What I mean is, if there is anyway to catch this:
feuplive tty5 2009-11-21 18:20
Which is one of the lines resulting from the who command.
To do this, you need to open a pipe. You then replace the child's stdout with the writing end of the pipe, and read from the reading end of the pipe in the parent. Like this modified version of your code:
int cmd_quem(void) {
int result;
int pipefd[2];
FILE *cmd_output;
char buf[1024];
int status;
result = pipe(pipefd);
if (result < 0) {
perror("pipe");
exit(-1);
}
result = fork();
if(result < 0) {
exit(-1);
}
if (result == 0) {
dup2(pipefd[1], STDOUT_FILENO); /* Duplicate writing end to stdout */
close(pipefd[0]);
close(pipefd[1]);
execl("/usr/bin/who", "who", NULL);
_exit(1);
}
/* Parent process */
close(pipefd[1]); /* Close writing end of pipe */
cmd_output = fdopen(pipefd[0], "r");
if (fgets(buf, sizeof buf, cmd_output)) {
printf("Data from who command: %s\n", buf);
} else {
printf("No data received.\n");
}
wait(&status);
printf("Child exit status = %d\n", status);
return 0;
}
First, execl does not return unless there's a problem like the executable is not found. That sleep(4) is probably never executed.
As for redirecting and getting the output, check out the Unix Programming FAQ. Look for spawn_background_command.
The exec() family of functions creates a new process image from a regular, executable file. This file is either an executable object file, or an interpreter script. There is no return from a successful call to an exec() function, because the calling process is functionally replaced by the new process.
So any code after exec() is never executed unless it is failed.
If you want to capture output of a shell command you need popen.