I'm writing a small program with system calls which parses an input file that contains commands and its parameters in each line, "benchmarks" them by outputting their elapsed time and waits for them to run one by one or executes them in parallel (basically, whether to wait() the spawned children or not in a loop)
The problem is that I'm required to use gets()
, which I understand is deprecated and extremely dangerous, but still is going to be a requirement for my CS exams (sigh).
The way I'm doing this is by parsing each line with gets()
and strtok()
with the file redirected to stdin
.
This seems to work fine if the last parameter is p
, that is, if the children are to be run in parallel by not doing a wait()
on each loop of parsing a new line and spawning the children.
BUT, if the last parameter is s
(or anything else), gets()
seems to strangely read duplicate lines from the file, and it will spawn twice the children. I have absolutely no clue on why and how is this happening.
As an example, if the input file contains the stuff below and the last parameter is p
:
Input file:
ls -l
ps
ps -lu user
Output file:
Comm 1: ls
Elapsed time: 0000.001824
Comm 2: ps
Elapsed time: 0000.014160
Comm 3: ps
Elapsed time: 0000.015795
But, if the input file contains the same thing and the last parameter is s
:
Input file:
ls -l
ps
ps -lu user
Output file:
Comm 1: ls
Elapsed time: 0000.001612
Comm 2: ps
Elapsed time: 0000.008564
Comm 3: ps
Elapsed time: 0000.009006
Comm 4: ps
Elapsed time: 0000.007379
Comm 5: ps
Elapsed time: 0000.008407
Comm 6: ps
Elapsed time: 0000.008975
// ./prog inputFile logFile p
// inputFile: file with a series of lines of max. 200 chars which consist of:
// command p1 p2 ... pn (between 0 and 30 parameters)
// logFile: where the times are going to be logged
// For each command parsed and executed:
// command <i>: <name of the program executed>
// Elapsed time: xxxx.yyyyyy
// p is either "s" or "p"
// If it's "s", the commands must be executed in series
// If it's "p", the commands must be executed in parallel
#include <unistd.h> // exec, read
#include <fcntl.h> // open
#include <stdio.h> // printf
#include <stdlib.h> // exit
#include <string.h> // strtok
#include <time.h> // timeval
#include <sys/wait.h>
int main(int argc, char *argv[]) {
if (argc < 4) {
printf("Usage: ./prog inputFile logFile p\n"); exit(0);
}
int fdCom = open(argv[1], O_RDONLY); // opens the input file with the instructions in it
int fdLog = creat(argv[2], 0600); // creates the logFile
int fd[2]; pipe(fd); // pipe son->father
close(0);dup(fdCom); // place the inputFile into stdin in order to use gets() on it
close(fdCom);
int lineNumber = 0;
char buf[200]; // max length of a line
char *com[21]; // command + max number of parameter
while (gets(buf) != NULL) { // While there's still lines left in the input file...
lineNumber++;
int i = 0;
com[i++] = strtok(buf, " "); // Parse the command
while ((com[i] = strtok(NULL, " ")) != NULL) { // And its parameters
i++;
}
printf("Leftover buf is: %s\n", buf);
printf("The first three parsed parts are:%s %s %s\n", com[0], com[1], com[2]);
if (fork() == 0) { // child
if (fork() == 0) { // create another child in charge of running the current command
close(fd[0]);close(fd[1]);
execvp(com[0], com);
} else { // wait for it to finish
close(1); dup(fd[1]); // stdout to pipe output
close(fd[0]);close(fd[1]);
struct timeval initialTime, elapsedTime;
gettimeofday(&initialTime, NULL);
wait(NULL);
gettimeofday(&elapsedTime, NULL);
elapsedTime.tv_sec = elapsedTime.tv_sec - initialTime.tv_sec;
elapsedTime.tv_usec = elapsedTime.tv_usec - initialTime.tv_usec ;
if(elapsedTime.tv_usec < 0){
elapsedTime.tv_usec = elapsedTime.tv_usec + 100000;
elapsedTime.tv_sec--;
}
printf("Comm %d: %s\nElapsed time: %04d.%06d\n",lineNumber, com[0], elapsedTime.tv_sec, elapsedTime.tv_usec); // Va por pipe a padre
fprintf(stderr, "Comm %d: %s\nElapsed time: %04d.%06d\n",lineNumber, com[0], elapsedTime.tv_sec, elapsedTime.tv_usec); // Va por pipe a padre
exit(0);
}
} else { // Father
if (argv[3][0] == 's') { // If they indicated "s" as the last parameter, we have to wait for the current program until we execute another command
printf("Waiting...\n");
wait(NULL);
} // Else, continue launching new ones
}
}
close(fd[1]); // Close the pipe's output
int readBytes;
while((readBytes = read(fd[0], buf, sizeof(buf))) > 0) { // Writes everything that comes from the pipe into the logFile
write(fdLog, buf, readBytes);
}
close(fd[0]); // Close the pipe's output
// once everything is fixed, handle waiting for children here in case it was a parallel run
exit(0);
}