How to give filenames to a parent procces using FIFO(in C)? - c

I have 2 processes, one reads from the input a filename, this filename is then given to the child process.The child process determinates in which directories the given file exits and the child procces gives all the directory names back to the parent who then prints these directory names.I need to do this using FIFO(named pipes).I got stuck at the part where the child prcess gives the parent the directory names.Could someone please help?
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <errno.h>
#include <dirent.h>
extern int errno;
#define FIFO "/tmp/fifo0002.1"
//searches for the direrctories
void listdir( const char *dir, const char *filename)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
char *subdir;
if((dp = opendir(dir)) == NULL)
{
fprintf(stderr,"cannot open directory: %s\n", dir);
return;
}
while((entry = readdir(dp)) != NULL)
{
if(lstat(entry->d_name, &statbuf) == 0)
{
if(statbuf.st_mode & S_IFDIR)
{
/* Found a directory, but ignore . and .. */
if(strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0)
continue;
// allocate space for parent directory, "/", subdir, plus NULL terminator
subdir = malloc(strlen(dir) + strlen(entry->d_name) + 2);
// Concatenate directory name
strcpy(subdir, dir);
strcat(subdir, "/");
strcat(subdir, entry->d_name);
/* Recurse at a new indent level */
listdir(subdir,filename);
free(subdir);
}
else
{
if(statbuf.st_mode & S_IFREG ){
if (strcmp(entry->d_name,filename)==0){
printf("%s\n",dir);}}
}
}
}
closedir(dp);
}
int main (void)
{ int r_fifo, w_fifo, r1_fifo, w1_fifo;
char filename[100];
printf("enter filename:");
scanf("%s",filename);
char buf[100];
char buf2[100];
pid_t pid;
if ((mkfifo (FIFO, S_IRUSR | S_IWUSR)) == -1) {
/* FIFO exists */
if(errno == EEXIST)
perror ("mkfifo()");
else {
perror("mkfifo()");
exit (EXIT_FAILURE);
} }
pid = fork ();
if (pid == -1)
{ perror ("fork()");
exit (EXIT_FAILURE);
}
else if (pid > 0) {
/*parent procces */
if ((r_fifo = open (FIFO, O_RDONLY)) < 0) {
perror ("open()");
exit (EXIT_FAILURE); }
write (w_fifo, filename, strlen (filename));
/*wait for child */
while (wait (NULL) != pid);
/*read from FIFO -> the directory names
read (r_fifo, buf, strlen (filename));
buf2[strlen(buf2)] = '\0';
printf(" directories:\n %s\n",buf2);*/
}
else {
/*child procces */
if ((w_fifo = open (FIFO, O_WRONLY)) < 0) {
perror ("open()");
exit (EXIT_FAILURE); }
/*Read from FIFO filename */
read (r_fifo, buf, strlen (buf));
buf[strlen(filename)] = '\0';
//printf("%s\n",buf);
listdir("/home/folder1",filename);
//close (w_fifo); /* EOF */
exit (EXIT_SUCCESS); }
return EXIT_SUCCESS;}

The parent process opens the pipe for reading and stores the descriptor in r_fifo. Then it immediately tries to write to the pipe, using the uninitialized w_fifo descriptor.
The child process does the opposite, open the pipe for writing (assigning to w_fifo) then attempts to read using the uninitialized r_fifo variable.
The parent process should open the pipe for writing, and the child process should open for reading. And both needs to use the correct and initialized variables.
There are also many other problems, like you using the strlen function on uninitialized arrays, which will not get the size of the arrays. And in the client you read into buf but use strlen(filename) to set the terminator. And you never initialize filename At all and still pass it to the listdir function.
And you really need to check what read and write returns.

Related

Redirect stdin from exec to reading pipe

Basically I'm programming my own xargs implementation for practicing. The main difference with the original xargs is that what I do is to buffer the first 4 lines I get from stdin in the parent process and write it in the pipe I created, so it processes 4 lines at a time instead of each line. Then, in the child process I redirect stdin to the reading pipe so when I call exec it should receive the arguments. After that, when child ends, the parent should do this again until all stdin was read.
So let's say I do cat directories.txt | ./my_xargs ls where directories is a file that has:
/var/
/opt/
/dev/
I should get the result of running ls /var/ /opt/ /dev/. But instead I'm getting as if I've run just ls without parameters.
I tried several things:
Writing a file and redirecting that file descriptor to stdin. Didn't work.
I already checked the pipe is written correctly debugging and also printing the result of reading the pipe instead of calling exec.
closing stdin and opening a new file and write what I read in the child to that file. When I debug I can even see that that file descriptor is 0.
removing the line close(pd[0]); didn't work either.
#ifndef NARGS
#define NARGS 4
#endif
#define LINE_SIZE 1024
#define PATH_MAX 1024
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
typedef enum { false, true } bool;
int
main(int argc, char *argv[])
{
bool eof = false;
int pd[2];
size_t len = 0;
while (!eof) {
if (pipe(pd) == -1) {
fprintf(stderr, "Error creating pipe\n");
}
pid_t pid;
pid = fork();
if (pid == -1) {
fprintf(stderr, "Fork error\n");
}
if (pid == 0) { // child
close(pd[1]); // child doesn't write
dup2(pd[0], 0); // changing reading pipe por stdin
close(pd[0]);
execvp(argv[1], argv + 1);
perror("Exec failed.\n");
} else {
close(pd[0]); // parent doesn't read
char total_params[PATH_MAX] = "";
char* newLine = NULL;
int i = 0;
while (i < NARGS && !eof) {
// Reading stdin
eof = getline(&newLine, &len, stdin) < 1;
// Removing '\n'
if ( i+1 < NARGS) {
newLine[strcspn(newLine, "\n")] = ' ';
} else {
newLine[strcspn(newLine, "\n")] = '\0';
}
strcat(total_params, newLine);
i++;
}
free(newLine);
write(pd[1], &total_params, strlen(total_params));
close(pd[1]);
wait(NULL);
}
}
return 0;
}

Not able to read multiple struct type messages from C pipe

I'm trying to develop a program that will initiate 2 child processes to run and these child processes will read all the files in one directory and pass the information to another child to create those files in another directory.
Below is my code for the same. But I'm not able to read messages from the pipe. If I pass a hardcoded string object, I'm able to read that from the pipe. But not the struct object I'm trying to pass.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> //Header file for sleep(). man 3 sleep for details.
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
#define BUF_SIZE 1024
// Struct to store the file name and content
struct FileDetails{
char *file_name;
char *file_content;
};
// Helper function to create file at a given path and with given content.
void * createFile(struct FileDetails fileDetail){
int status;
FILE *fptr;
fptr = fopen(fileDetail.file_name,"a");
if(fptr == NULL)
{
perror("Error!");
return NULL;
}
fprintf(fptr,"%s",fileDetail.file_content);
fclose(fptr);
return NULL;
}
//Helper function used to copy two strings into one and return new string
char* strAppend(char* str1, char* str2){
char * new_str ;
if((new_str = malloc(strlen(str1)+strlen(str2)+1)) != NULL){
new_str[0] = '\0'; // ensures the memory is an empty string
strcat(new_str,str1);
strcat(new_str,str2);
} else {
printf("%s","malloc failed!\n");
// exit?
}
return new_str;
}
// Each child will execute this helper funtion. It takes one argument which is the location of the directory to read all files from and send data to other child.
// Second argument is the pipe where we write
void *threadUtility(void *vargp, int* fd1)
{
// Store directory pointer
DIR* directoryPtr;
// Store file pointer
FILE *entry_file;
struct dirent *current_file;
int i;
char* directory = (char *)vargp;
printf("Process started for directory: %s\n", directory);
directoryPtr = opendir(directory);
if(directoryPtr==NULL) {
printf("Error! Unable to read directory: %s\n", directory);
exit(1);
}
close(fd1[0]); // Close reading end of first pipe
while((current_file=readdir(directoryPtr)) != NULL) {
if (!strcmp (current_file->d_name, "."))
continue;
if (!strcmp (current_file->d_name, ".."))
continue;
struct FileDetails fileDetail;
fileDetail.file_name = malloc(strlen(current_file->d_name) + 1);
fileDetail.file_name = current_file->d_name;
// Appending directory path to file name to read contents
char * new_str = strAppend(directory, current_file->d_name);
//printf("FileName FQN: %s\n", fileDetail.file_name);
entry_file = fopen(new_str, "r");
struct stat sb;
stat(new_str, &sb);
char *file_contents = malloc(sb.st_size);
fileDetail.file_content = malloc(sb.st_size);
if (entry_file != NULL) {
//Looping through each line of file to read all lines.
while (fscanf(entry_file, "%[^\n] ", file_contents) != EOF) {
fileDetail.file_content = strAppend(fileDetail.file_content, file_contents) ;
fileDetail.file_content = strAppend(fileDetail.file_content, "/n") ;
}
} else{
perror("Failed: ");
exit(1);
}
fclose(entry_file);
//printf("Writing to pipe: %s\n", fileDetail.file_name);
if (write(fd1[1], &fileDetail, sizeof(struct FileDetails)) < 0) {
printf("error writing");
}
}
close(fd1[1]);
closedir(directoryPtr);
printf("Process ended for directory: %s\n", directory);
return NULL;
}
// This program takes two command line argument which are direectory paths of two location.
int main( int argc, char *argv[] )
{
// If 2 paths not provided the program will terminate
if( argc != 3 ) {
printf("Provide two directory paths: %i\n", argc);
exit(1);
}
//Pipes for communication
int fd1[2]; // Used to store two ends of first pipe
int fd2[2]; // Used to store two ends of second pipe
//Create pipes
if (pipe(fd1)==-1)
{
fprintf(stderr, "Pipe Failed" );
return 1;
}
if (pipe(fd2)==-1)
{
fprintf(stderr, "Pipe Failed" );
return 1;
}
// Process ids for two child processes
pid_t dir_one_child_process, dir_two_child_process;
dir_one_child_process = fork();
if (dir_one_child_process == 0) {
/* Child One Process */
threadUtility(strAppend(argv[1],"/"), fd1);
wait(NULL);
// Reading messages sent by 2nd child
close(fd2[1]); // Close writing end of second pip
// Read string from child, print it and close
// reading end.
struct FileDetails readForSecond;
int n_bytes;
while (read(fd2[0], &readForSecond, sizeof(&readForSecond)) > 0) {
printf("From Child One: %s\n", readForSecond.file_name);
// Here you have to call the createFile function in order to create the file once you get the FileDetail object
//I'm stuck here
//sleep(1);
}
printf("From Child Two: %s\n", readForSecond.file_name);
close(fd2[0]);
} else {
dir_two_child_process = fork();
if (dir_two_child_process == 0) {
/* Child Two Process */
close(fd1[1]); // Close writing end of first pipe
struct FileDetails readForFirst;
int n_bytes;
while (read(fd1[0], &readForFirst, sizeof(&readForFirst)) > 0) {
printf("From Child One file_name: %s\n", readForFirst.file_name);
// Here you have to call the createFile function in order to create the file once you get the FileDetail object
//I'm stuck here
//sleep(1);
}
printf("reading ends:");
// Close both reading ends
close(fd1[0]);
threadUtility(strAppend(argv[2],"/"), fd2);
} else {
/* Parent Code waiting for two childs to end*/
waitpid(dir_one_child_process, NULL, 0);
waitpid(dir_two_child_process, NULL, 0);
printf("Main Function Ends. Exiting\n");
}
}
exit(0);
}
Here it one bug:
write(fd1[1], &fileDetail, sizeof(struct FileDetails))
sizeof(struct FileDetails) is not the size of the data that you have stored using the pointers in fileDetail. Since struct FileDetails is
struct FileDetails{
char *file_name;
char *file_content;
};
it's simply the size of two pointers (which is typically 2x4 or 2x8).
You can check the size using:
printf("%zu\n", sizeof(struct FileDetails));
So it looks as-if you sending two pointer values instead of the content of the files. That's not what you want.
Further, this is wrong:
read(fd2[0], &readForSecond, sizeof(&readForSecond))
^^^^^^^^^^^^^^^^^^^^^
This is size-of a single pointer

File reversing copy in c

I am trying to finish a program that can fork function a child process, and parent can get the input file (under same directory) , reverse the content of this file, then using pipe function pass to the child process. Child will read the message from pipe and generate an output file. I have finished fork, create pipe and reverse function. However I got stucked on write it to the pipe. I know there must some type confusion when i try to pass the parameter into the write function, Any hits would be appreciated.
Here is the Code I have so far:
#include <stdio.h>
#include <stdlib.h> //exit
#include <string.h>
#include <sys/types.h> //pid_t
#define READ_END 0
#define WRITE_END 1
int main(int argc, char *argv[]){
long loc;
FILE *in, *out;
char ch;
if (argc != 3)
{
printf("Usage %s message\n", argv[0]);
exit(EXIT_FAILURE);
}
int pipefd[2];
int pipe_return = pipe(pipefd);
if((in = fopen(argv[1], "rb")) == NULL) {
printf("Cannot open input file.\n");
exit(1);
}
if((out = fopen(argv[2], "wb"))==NULL) {
printf("Cannot open output file.\n");
exit(1);
}
if(pipe_return == -1)
{
printf("Unable to create pipe\n");
exit(EXIT_FAILURE);
}
pid_t return_from_fork = fork();
if (return_from_fork == -1)
{
printf("Unable to fork\n");
exit(EXIT_FAILURE);
}
else if (return_from_fork == 0) //this is a child
{
char msg;
close(pipefd[WRITE_END]);
int read_return = read(pipefd[READ_END], &msg, 1);
printf("read return:%d\n", read_return);
while(read_return > 0){
fputc(ch, out);
printf("%c",msg);
read_return = read(pipefd[READ_END], &msg, 1);
}
printf("child ends\n");
close(pipefd[READ_END]);
exit(EXIT_SUCCESS);
}
else if (return_from_fork > 0)
{
close(pipefd[READ_END]);
printf("this is parent\n");
fseek(in, 0L, SEEK_END);
loc = ftell(in);
while(loc >= 0L){
fseek(in, loc, SEEK_SET);
ch = fgetc(in);
printf("%c",ch);
int write_r = write(pipefd[WRITE_END], ch, 1);//here is the problem the printf() return -1
printf("%d",write_r);
loc--;
}
printf("\n");
close(pipefd[WRITE_END]);
wait(NULL);
printf("file successful generated.\n");
fcloseall();
exit(EXIT_SUCCESS);
}
}
And Here is the compile result:
zzz#ubuntu:~/Desktop/test$ gcc filereversecopy.c -o run
zzz#ubuntu:~/Desktop/test$ ./run src.txt out.txt
this is parent
�-1
-1e-1c-1n-1e-1t-1n-1e-1s-1 -1a-1 -1s-1i-1 -1s-1i-1h-1T-1
read return:0
child ends
file successful generated.
zzz#ubuntu:~/Desktop/test$
On the line you say is problem you are passing ch to write, and ch is type char. I'm sure you mean &ch instead. I bet if you change that write will return 1 instead of -1.
Also, you seek to the end to start reading, but when you seek to the end you are pointing at EOF. You need to start reading at the position before EOF. So after "fseek(in, 0L, SEEK_END); loc = ftell(in);" adding "loc--; fseek(in, loc, SEEK_SET);" makes it work.

Simple shell with indirect input

I am writing a simple code to implement the indirect input function for a unix/linux shell.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
extern void error(char* message);
void
cisshRedirectedInput(char* command[], char* inputFile)
{
//Try to implement the RedirectInput from here
pid_t pid;
int status;
int fd;
//For the child process
if ((pid=fork())==0)
{
//Try to input files, failing on an error
fd=open(inputFile,O_RDONLY);//To read input file
if(fd < 0)
{
error("sampleSh: error opening standard input file");
exit(1);
}
//use dup() to copy file
close(1);
if(dup(fd) < 0)
{
error("sampleSh: error duplicating standard input");
perror("dup()");
exit(1);
}
//Close file and exec()
close(fd);
execvp(command[0], command);
//If failure in any case
error("sampleSh: failure to execute command");
exit(1);
}
else
{
/* This is the parent process.
* Wait for the child to terminate.
*/
if(wait(&status) < 0)
{
error("sampleSh: error waiting for child.");
perror("wait");
}
if(status != 0)
error("sampleSh: command exited with nonzero error status.");
}
}
However, after compilation (no error reported), but when I try (fileList created already)
sort -r <fileList
The shell just stuck there without giving me answer, what is the problem please?
The standard input file descriptor is 0 (or STDIN_FILENO), not 1 (or STDOUT_FILENO).
Either use:
int fd = open(inputFile, O_RDONLY);
if (fd < 0) …
close(0);
if (dup(fd) < 0) …
close(fd);
Or:
int fd = open(inputFile, O_RDONLY);
if (fd < 0) …
if (dup2(fd, 0) < 0) …
close(fd);
It is good that your code does the close(fd) after duplicating to a standard I/O descriptor — that is almost always correct. It's also good that you are checking that the key system calls succeed. (There isn't much you can do if close() fails.)
This simple modification of your code (key change: use close(0); instead of close(1);) works for me. Did you null terminate your argument list?
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
static inline void error(char *message)
{
fprintf(stderr, "%s\n", message);
}
void
cisshRedirectedInput(char *command[], char *inputFile);
void
cisshRedirectedInput(char *command[], char *inputFile)
{
// Try to implement the RedirectInput from here
pid_t pid;
int status;
int fd;
// For the child process
if ((pid = fork()) == 0)
{
// Try to input files, failing on an error
fd = open(inputFile, O_RDONLY); // To read input file
if (fd < 0)
{
error("sampleSh: error opening standard input file");
exit(1);
}
// use dup() to copy file
close(0);
if (dup(fd) < 0)
{
error("sampleSh: error duplicating standard input");
perror("dup()");
exit(1);
}
// Close file and exec()
close(fd);
execvp(command[0], command);
// If failure in any case
error("sampleSh: failure to execute command");
exit(1);
}
else
{
/* This is the parent process.
* Wait for the child to terminate.
*/
if (wait(&status) < 0)
{
error("sampleSh: error waiting for child.");
perror("wait");
}
if (status != 0)
error("sampleSh: command exited with nonzero error status.");
}
}
int main(void)
{
char *args[] = { "sort", "-r", 0 };
cisshRedirectedInput(args, "fileList");
return 0;
}
Input file:
bash-assoc-arrays.sh
cissh.c
fileList
kwargs.py
makefile
posixver.h
rangeinc.c
select.c
spc.py
testcsv.py
uncrustify.bug
yield.py
Output:
yield.py
uncrustify.bug
testcsv.py
spc.py
select.c
rangeinc.c
posixver.h
makefile
kwargs.py
fileList
cissh.c
bash-assoc-arrays.sh

parallel download

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

Resources