Recently I had an assignment where I had to make a program that takes from command line two different commands, separated with a ' ', for example:
ps -lu myUsername ls -la
The objective of the program was to run any two orders simultaneously, with any number of parameters per order, by using fork()
and exec()
. This was my solution to this problem:
Note: the original problem was meant to be run on a machine with Solaris 5.10 and c89 or c99 standard (gcc 3.4.3)
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <strings.h>
int main (int argc, char* argv[]) {
char delimiter = ' ';
char* auxp; //auxiliar pointer
int i = 1;
int position;
while(i < argc){
if (strcmp(" ", argv[i]) == 0) {
argv[i] = NULL;
position = i;
}
i ;
}
if (fork() == 0) {
execvp(argv[1], &argv[1]);
exit(1);
}
if (fork() == 0) {
execvp(argv[position 1], &argv[position 1]);
exit(1);
}
wait(NULL);
wait(NULL);
exit(0);
}
This was enough for the assignment but I wanted to make it work with N arguments instead of only 2. I can't reach a systematic way to find the all the addresses. Any help is appreciated, thanks in advice.
CodePudding user response:
Just move the forking into the loop:
int main (int argc, char* argv[])
{
char** start = argv;
unsigned int n = 0;
for(; *argv; argv) // profiting from argv being null terminated, too...
{
if (strcmp(" ", *argv) == 0)
{
*argv = NULL;
if (fork() == 0)
{
execvp(*start, start);
exit(1);
}
start = argv 1;
n; // but need to count how many times we actually forked!
}
}
while(n--)
{
wait(NULL);
}
exit(0);
}
OK, I modified iterating a bit, too – pointers are just so much nicer (personal oppinion...).
Note: This is untested code, if you find a bug please fix yourself...
CodePudding user response:
For the general case of N commands, need to keep track of where each command starts and ends, and fork a new child whenever the end of a command is found. That could be at a
separator argument, or at the end of the original argument list.
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char* argv[]) {
/*
* Note: argc could be in range 0 to INT_MAX.
* Using unsigned int values to avoid arithmetic overflow.
*/
unsigned int start = 1;
unsigned int end = 1;
unsigned int child_count = 0;
/* Note: argv[argc] is a terminating null pointer. */
while(end <= (unsigned int)argc){
if(end == (unsigned int)argc || strcmp(" ", argv[end]) == 0){
/* Reached the terminating null pointer or a command separator. */
argv[end] = NULL;
if(start != end){
/*
* Command is not empty.
* Fork a child process to execute the command.
*/
pid_t child = fork();
if(child > 0){
/* Parent forked child successfully. */
child_count ;
}else if(child == 0){
/* This is the child process. Execute command. */
execvp(argv[start], &argv[start]);
exit(1);
}
}
/* Next command starts after this one. */
start = end 1;
}
/* Looking for terminating null pointer or command separator. */
end ;
}
/* Wait for the child processes to terminate. */
while(child_count){
wait(NULL);
child_count--;
}
exit(0);
}
Note: the argv[end] = NULL;
line could be moved into the if(child == 0){
}
block (but before the call to execvp
) to leave the parent's original argument list intact.