How to execute multiple commands from argv with an specific delimiter - c

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.

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.

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...

Related

Create N processes in Linux and create a file in the current directory for every process

My homework is to execute a C program from Linux shell with N parameters: N file names.
I have to create N processes and for each process I have to create a .bak file with the file name specified in the parameters.
The professor suggests we use the cp command but I don't know how to proceed.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <limits.h>
typedef char stringa[80];
typedef stringa strvett[DIM];
void usage(char* prog_name) {
fprintf(stderr,"Usage:\n\t%s: file1 file2 ... fileN dir_dest.\n", prog_name);
}
void main(int argc, char *argv[]) {
int i = 0, j = 0, status, N = argc - 2, n_children = N;
int[N] pid;
char[N]* files;
char[PATH_MAX - 1] wd;
char term = '0';
if (strcmp(argv[argc - 1], getcwd(wd, sizeof(wd)))) {
fprintf(stderr, "Invalid directory.\n");
usage(argv[0]);
exit(EXIT_FAILURE);
}
for (i = 0; i < N; i++) {
char[strlen(argv[i + 1])] files[i];
strcpy(files[i], argv[i + 1]);
}
for (j = 0; j < n_children; j++ ) {
pid[j] = fork();
if (pid[j] == 0) { // Executed by child
if (term == '1') exit(0);
else {
term = '1';
execl("/bin/ls", "ls", "-l", (char*)0);
// cp command???
}
}
else {
if (pid[j] > 0) printf("%d: child created with PID %d\n", getpid(), pid[j]); // Executed by parent
else {
perror("Fork error");
exit(1);
}
}
}
}
The below program forks child processes and each process create a .bak file, using cp command, with the same name as a file name specified in the argument list passed to program. Since, the last argument is dir_dest, I am assuming that it is the destination directory where the child processes are suppose to create .bak files. Follow the inline comments of program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define FL_EXTN ".bak"
#define PLEN 256
#define NLEN 64
void usage(const char* prog_name) {
printf("Usage:\n\t%s: file1 file2 ... fileN dir_dest.\n", prog_name);
}
int main(int argc, char *argv[]) {
char arg1[NLEN];
char dir_dest[PLEN];
// adding check for argc < 3, as it need minimum 2 arguments
// a file_name and dir_dest
if (argc < 3) {
usage(argv[0]);
exit(EXIT_FAILURE);
}
// add the command line argument validations
// for e.g. dir_dest is valid or not
strcpy (dir_dest, argv[argc - 1]);
// run loop for argc - 2 time as the first argument is process name and
// last is dir_dest, rest are files to be copy with .bak extension
for (int j = 0; j < argc - 2; j++) {
// leaving it up to you to add a check whether file argv[j+1]
// exits or not. Below code is based on assumption that files exist
strcpy (arg1, argv[j+1]);
// fork child process
pid_t child_pid = fork();
if (child_pid == 0) {
// in child process
char *cmdpath = "/bin/cp";
char *cmd = "cp";
char arg2[PLEN];
// prepare the .bak file absolute path using dir_dest
snprintf (arg2, PLEN, "%s/%s%s", dir_dest, arg1, FL_EXTN);
// replace child process with cp
execl(cmdpath, cmd, arg1, arg2, NULL);
// get here only if execl fails
perror("execl failed");
// exit child process
exit(1);
} else {
// in parent process
if (child_pid > 0) {
printf("%d: child created with PID %d\n", getpid(), child_pid);
} else {
perror("Fork error");
exit(1);
}
}
}
return 0;
}
Additional:
Using void as return type of main function is not as per standards. The return type of main function should be int.

Why does the execlp in child not return anything into stdout?

I have a program that reads in line by line from a text file. Each line has the layout
command arg1 arg2 arg3
and I have read it in so that I have 2 arrays, 1 which contains the string and another which points to each string value. eg
char read_in_line[128]
char* command[100]
and so:
command[0] = command arg1 arg2 arg3
command[1] = command arg1
etc.
I then have this command array as an input to a function that uses fork and pipes. The following is a snippet of this function and note it is in a while loop which will continue while *cmd != NULL
void piping(char* cmd[100]{
else if(pid == 0){
//child does not need to read
close(thepipe[0]);
dup2(thepipe[1],1);
close(thepipe[1]);
execlp(*cmd,*cmd,NULL);
However, this does not return anything. My C program compiles without showing any errors, however in my stdout I can not see the execution of any of the commands i sent into the function.
EDIT:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define BUFFERSIZE 128
#define oops(m,x) {perror(m); exit(x);}
void piping(char **cmd[BUFFERSIZE]){
pid_t pid;
int thepipe[2];
int in = 0;
//while there are still commands
while (*cmd != NULL){
pipe(thepipe);
//fork error case
if((pid = fork()) < 0)
oops("cannot fork",1);
//child
if(pid == 0){
//child does not need to read
close(thepipe[0]);
if(dup2(thepipe[1],1)== -1)
oops("Error redirecting stdout",2);
//duplication succesful can now close thepipe[1]
close(thepipe[1]);
//execute the command
execvp(*cmd[0], *cmd);
exit(-1);
}
else{
//parent does not write to pipe
close(thepipe[1]);
//setting up parent input to read from the pipe
dup2(thepipe[0],0);
close(thepipe[0]);
//wait until child finishes
wait(NULL);
cmd++;
}
}
}
int main(int argc, char* argv[]){
char **command[BUFFERSIZE];
char read_in_line[BUFFERSIZE];
int i = 0;
int counter =0;
int counter2 =0;
//reading in line by line until end of file is reached
FILE* fp = fopen("test.txt","r");
while( fgets(read_in_line, BUFFERSIZE, fp) != NULL ){
int j = 0;
//setting up memory for arguments given that we know there is a max
//of 10 arguments per line
char **arguments = (char**) calloc(16, sizeof(char*));
command[i] = arguments;
//Will break up the line read in when a newline is argument resulting in one
//string containing the commands and arguments
//this string will then be broken up every time a space is met so that
//commands and arguments can be seperated, and saved to command[i][j]
char *t = strtok(read_in_line, "\n");
char *argument = strtok(t, " ");
command[i][j] = strdup(argument);
while(argument != NULL){
argument =strtok(NULL, " ");
if(argument != NULL){
command[i][++j] = strdup(argument);
}
}
i++;
}
piping(command);
return (0);
}
The program below works as expected:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
int main(void)
{
int rc;
rc=execlp("/bin/date", "deet", (char*) NULL);
printf("Rc=%d,%d(%s)\n", rc, errno, strerror(errno));
return 0;
}
Next step: add some arguments. (next step: fix the plumbing)
rc=execlp("/bin/ls", "ls", "-li", (char*) NULL);

Shell in C with multiple commands, segmentation fault(core dumped)

I am trying to write a basic shell in C for ubuntu. I want to execute several commands in one line , seperated with semi colon. I m trying to run it but i get the message "Segmentation fault (core dumped)"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#ifdef _WIN32
#include <windows.h>
#define chdir _chdir
#else
#include <unistd.h>
#endif
#define MAX_LENGTH 512
int main(int argc, char *argv[]) {
char *cmd;
char line[MAX_LENGTH];
char *token[20];
int i=0;
int j=0;
int k=0;
char* args[20];
while (1) {
printf("8334>");
if (!fgets(line, MAX_LENGTH, stdin)) break;
while ((cmd = strtok(line, ";")) != NULL)
{
printf("<<%s>>\n", cmd);
strcpy(token[i], cmd);
i+=1;
cmd = NULL;
}
for(j=0;j<i;j++){
k=0;
while ((cmd = strtok(token[j], " ")) != NULL)
{
printf("<<%s>>\n", cmd);
strcpy(args[k],cmd);
k+=1;
cmd = NULL;
}
args[k]=NULL;
pid_t pid;
int status;
if ((pid = fork()) < 0) { /* fork a child process */
printf("*** ERROR: forking child process failed\n");
exit(1);
}
else if (pid == 0) { /* for the child process: */
if (execvp(args[0], args) < 0) { /* execute the command */
printf("*** ERROR: exec failed\n");
exit(1);
}
}
else { /* for the parent: */
while (wait(&status) != pid) /* wait for completion */
;
}
for(int l=0;l<20;l++)
args[l]=NULL;
}
}return 0;
}
The main thing that I see is that you do not reserve space for copying strings to args[i] and token[i]; you are reserving space for an array of pointers, but not for the content to which the pointers shall point:
char *token[20]; // reserves place for 20 pointers
...
strcpy(token[i], cmd); // token[i] has not been initialized...
The probably easiest way to overcome this is to write
token[i] = strdup(cmd);
instead of strcpy; note that strdup automatically reserves enough memory for holding the copy, whereas strcpy expects that this space has already been reserved before.
Anyway, do not forget to free this memory once not used any more, and do not forget to do this also for args.
There might be other issues in the code as well; for example, when using strtok, only the first call will pass the line to split, whereas all consecutive calls should pass NULL then (see doku). But I think this is then at the level where you and your debugger can get ahead :-)
the root of the problem is this line:
strcpy(token[i], cmd);
This is trying to copy that string to an entry in char *token[20] But, that is just an array of pointers, not an array of pointers to strings.
perhaps you could use:
token[i] = strdup( cmd );
Of course, the code would then need to check (!=NULL) token[i] to assure the call to strdup() was successful AND after using a specific command, pass that pointer to free()
similar considerations apply to the char *args[20]; usage

C - creating process tree using pipes, select, fork, and execl

I'm trying to write the following, 2 part program. In one file ("root.c"), I read in a random string of 1's and 0's. I then split the resulting string in half, and send each half to its own process through fork(). Each child process uses execl() to run the second program ("bit_count.c").
Within bit_count.c, it:
a) checks if the length of the (half)string is 2 or less. If yes, it returns the number of
1's and 0's to it's parent process.
b) if not, it begins to recursively split the string in half, and sending each half to its own new process (replicating the procedure in root.c). This creates a binary process tree, until all pieces of the string are 2 characters long or less.
c) the count results of the left and right children are aggregated by the parent, and returned to its parent, until returning to the root process, which aggregates the highest two children, and outputs it to the user.
My problem with this project is returning the 2-character counts to the parent. My idea right now is to direct the parent's left and right read pipes to stdin with dup2(), and just to print to stdout with fprintf from the children. The select() function of the parent should catch the returning output, right?
My second problem is the format of the output. if the counts are in ints, what is the best way to return that using select() in this case? I've attached my code below, just be warned that it may be a mess - I'm rusty with C code and this is my first exposure to select() and execl().
root.c:
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char* argv[]) {
if (argc != 2) {
perror("input file name");
printf("%d", argc);
exit(1);
}
FILE* fp;
if((fp = fopen(argv[1], "r")) == NULL) {
perror("open file");
}
fseek(fp, 0, SEEK_END);
long fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
char *bits = malloc(fsize+1);
fread(bits, fsize, 1, fp);
fclose(fp);
char *left_half = malloc( fsize/2 + 1 );
char *right_half;
if (fsize%2) right_half = malloc( fsize/2 + 2 );
else right_half = malloc( fsize/2 + 1 );
if (!left_half || !right_half) perror("array split");
memcpy(left_half, bits, fsize/2);
if (fsize%2) memcpy(right_half, bits + fsize/2, fsize/2 + 1);
else memcpy(right_half, bits + fsize/2, fsize/2);
int fd_left[2], fd_right[2];
int zero, one;
int *left_res, *right_res;
pid_t left, right;
struct timeval tv;
fd_set readfds;
tv.tv_sec = 2;
tv.tv_usec = 500000;
if ((pipe(fd_left) == -1) || (pipe(fd_right) == -1)){
perror("Create pipe error");
exit(1);
}
FD_ZERO(&readfds);
FD_SET(fd_left[0], &readfds);
FD_SET(fd_right[0], &readfds);
if ((left=fork()) == 0) {
close(fd_left[0]);
execl("./bit_count", "bit_count", left_half, NULL);
perror("initiating recursion");
exit(1);
}
else if(left > 0) {
if ((right = fork())==0) {
close(fd_right[0]);
execl("./bit_count", "bit_count", right_half, NULL);
perror("initiating recursion");
exit(1);
}
else if (right > 0) {
close(fd_right[1]);
close(fd_left[1]);
char *left;
char *right;
dup2(fd_left[0], 0);
dup2(fd_right[0], 0);
int ret = select(2, &readfds, NULL, NULL, &tv);
read(fd_left[0], &left_res, 1);
read(fd_right[0], &right_res, 1);
printf("Back in root process!\n");
}
}
zero = (*right_res + *left_res);
one = (*(left_res+sizeof(int)) + *(right_res+sizeof(int)));
printf("%s had %d zeroes and %d ones\n", argv[1], zero, one);
return 0;
}
bit_count.c (only relevant part):
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
if (argc != 2) {
perror("sent bit string");
printf("%d", argc);
exit(1);
}
char *bit_string = argv[1];
int size = strlen(bit_string);
int counts[2];
counts[0] = 0;
counts[1] = 0;
if (!(size > 2)) {
int i=0;
for(; i < size; i++) {
if (bit_string[i]=='1') ++counts[1];
else ++counts[0];
}
fprintf(stdout, "%p", &counts);
fflush(stdout);
return 0;
}
}
My idea right now is to direct the parent's left and right read pipes to stdin with dup2(), and just to print to stdout with fprintf from the children. The select() function of the parent should catch the returning output, right?
No. You need to dup2(fd[1], STDOUT_FILENO) in the child before calling execl(). How else is bit_count supposed to know about the pipe? Then in the parent you can just read from fd[0]. To make things easier, you could just make bit_count a function, and call it directly in the child without using execl(). Then you could just write to fd[1] (if you made it global, or passed the value to the bit_count function) from the children.
My second problem is the format of the output. if the counts are in ints, what is the best way to return that using select() in this case?
You could use write(STDOUT_FILENO, &counts, 2*sizeof(int)) to write the ints directly to the pipe, rather than formatting them as a string. This way the parent does not need to convert them back to ints.

create a new process to execute ls command

I want to write a program which will create a new process and in that child process, it should execute the command: ls. In the meanwhile, the parent should wait for the child to die. However, my code does not work.
Please help me thank you very much!
int main()
{
char * read;
size_t size;
getline(&read , &size , stdin);
read[strlen(read)-1] = '\0';
printf("%s\n" , read);
int status;
pid_t f;
if((f = fork()) == 0)
{
execvp(read , &read);
exit(0);
}
else
{
wait(&status);
}
}
From man execvp:
The execv(), execvp(), and execvpe() functions provide an array of pointers to null-terminated strings that represent the argument list available to the new program. The first argument, by convention, should point to the filename associated with the file being executed. The array of pointers must be terminated by a NULL pointer.
You need to use an array of char* and set the last element to NULL.
I am unsure what the getline() is reading but I guess it is the directory to be lsd. The first argument to execvp() should be ls and the second argument the array of char*.
Consider the following:
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
char *input_arg[2];
char *input_str = NULL;
size_t input_len = 0;
char **args;
ssize_t len;
size_t n;
pid_t child, p;
int status;
if (argc < 2) {
/* No command line parameters. Read command from stdin. */
len = getline(&input_str, &input_len, stdin);
/* Find length excluding the newline at end. */
if (len > (ssize_t)0)
n = strcspn(input_str, "\r\n");
else
n = 0;
if (n > (size_t)0) {
/* Terminate input command before the newline. */
input_str[n] = '\0';
} else {
fprintf(stderr, "No input, no command.\n");
return 1;
}
input_arg[0] = input_str;
input_arg[1] = NULL;
args = input_arg;
} else {
/* Use command line parameters */
argv[argc] = NULL;
args = argv + 1;
}
child = fork();
if (child == (pid_t)-1) {
fprintf(stderr, "Cannot fork: %s.\n", strerror(errno));
return 1;
}
if (!child) {
/* This is the child process. */
errno = ENOENT;
execvp(args[0], args);
fprintf(stderr, "%s: %s.\n", args[0], strerror(errno));
exit(127);
}
do {
p = waitpid(child, &status, 0);
} while (p == (pid_t)-1 && errno == EINTR);
if (p == (pid_t)-1) {
fprintf(stderr, "Lost child process: %s.\n", strerror(errno));
return 127;
}
if (p != child) {
fprintf(stderr, "waitpid() library bug occurred.\n");
return 127;
}
if (WIFEXITED(status)) {
if (!WEXITSTATUS(status))
fprintf(stderr, "Command successful.\n");
else
fprintf(stderr, "Command failed with exit status %d.\n", WEXITSTATUS(status));
return WEXITSTATUS(status);
}
if (WIFSIGNALED(status)) {
fprintf(stderr, "Command died by signal %s.\n", strsignal(WTERMSIG(status)));
return 126;
}
fprintf(stderr, "Command died from unknown causes.\n");
return 125;
}
The above uses the command line parameters if specified, otherwise it reads one from the standard input. Because the standard input is not tokenized, you can only supply the command name, no parameters. If you enlarge the input_arg[] array into
char *input_arg[4];
and modify the assignment into
input_arg[0] = "/bin/sh";
input_arg[1] = "-c";
input_arg[2] = input_str;
input_arg[3] = NULL;
args = input_arg;
then the input string will be processed using the /bin/sh shell, just like popen() does.
You can also use len = getdelim(&input_str, &input_len, '\0', stdin); and remove the input_str[n] = '\0'; assignment to allow multiline input; the shell should handle those fine, as long as it is short enough to fit in the command line argument buffer (maximum length depends on your OS).
The rules how shells split input into separate commands and parameters are rather complex, and you should not try to emulate them. Instead, find a simple way for the user to specify the parameters separately (like the command-line parameter case), or use the shell to do it for you. If you don't do any splitting, you will probably need to remove the newline at the end of the input line.
The point to note is that for execvp(file, args), args[0] is the name the application sees (as $0 or argv[0]), and args[1] is the first parameter. Each parameter is terminated by NUL (\0) just like strings are normally in C, and the args pointer array must end with a NULL pointer. If there are no parameters, then args[1] == NULL.
why dont you just use system command...
#include <stdio.h>
#include <stdlib.h>
int main ()
{
int i;
printf ("Executing command ls...\n");
i=system ("ls");
printf ("The value returned was: %d.\n",i);
return 0;
}
Update:
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void main(void)
{
pid_t pid;
pid = fork();
if (pid == 0) // this is child process
{
int i;
printf ("Executing command ls...\n");
i=system ("ls");
printf ("The value returned was: %d.\n",i);
}
else // this is paraent process
{
int status=0
wait(&status);
printf ("Child process is returned with: %d.\n",status);
}
}

Resources