Pipeline system with string - c

I am working on pipeline system with string in C. For example I write this line to console ./pipeline LOWERCASE REVWORD SQUASHWS < stringFile.txt. It should be any like this P0 -> P1 -> P2 -> ...-> Pn. P0 load string from file stringFile.txt and send it to P1.. I can work with pipe(send and read), but I dont know how to work with N processes. It should be any like this. Can you give me any advice or example? Thank you
while(is_something_to_read) {
load_from_previous_process();
do_process(); // for example LOWERCASE
send_to_next_process();
}

Patrik,
I wrote a program that simulate a shell that will spawn a given amount of children and each process will then communicate with another process. Since the number of processes can vary I decided to use a 2D array for the pipes. In the below code NUM_PROCS refers to the amount of processes that will be running (including the parent).
I declare it
int pipes[NUM_PROCS][2];
After this, I create the pipes
for(i = 0; i < NUM_PROCS; i++)
{
if((pipe(pipes[i])) < 0)
{
perror("Failed to open pipe");
}
}
This is a shell program that i wrote for practise.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#define MAXARGS 256
char ** getargs(char * cmd) {
char** argsarray;
int nargs = 0;
int nlen = strlen(cmd);
int i = 0;
argsarray = (char**) malloc(sizeof(char*) * MAXARGS);
argsarray[0] = strtok(cmd," ");
i = 0;
while (argsarray[i] != NULL){
i++;
argsarray[i] = strtok(NULL," ");
}
return argsarray;
}
int main(void){
pid_t childpid;
int fd[256][2];
char cmd[256];
char * sepCmd[256];
char * pch;
printf("Please enter a command sequence: \n");
gets(cmd);
printf("You have entered: %s ....%d\n", cmd,strlen(cmd));
printf("Attempting to split up command: \n");
pch = strtok (cmd, "|");
int count = 0;
while (pch != NULL && count < 256) {
printf("%s\n", pch);
sepCmd[count] = pch;
printf("The value in this array value is: %s\n", sepCmd[count]);
pch = strtok (NULL, "|");
count++;
}
char ** argue;
int k;
/* Block that deals with the first command given by the user */
k = 0;
pipe(fd[k]);
if(!fork()) {
dup2(fd[k][1], STDOUT_FILENO);
close(fd[k][0]);
argue = getargs(sepCmd[k]);
execvp(argue[0], argue);
perror(argue[0]);
exit(0);
}
/*Loop that will control all other comands except the last*/
for(k = 1; k <= count - 2; k++) {
close(fd[k-1][1]);
pipe(fd[k]);
if(!fork()) {
close(fd[k][0]);
dup2(fd[k-1][0], STDIN_FILENO);
dup2(fd[k][1], STDOUT_FILENO);
argue = getargs(sepCmd[k]);
execvp(argue[0], argue);
perror(argue[0]);
exit(0);
}
}
/*Block that will take care of the last command in the sequence*/
k = count - 1;
close(fd[k-1][1]);
if(!fork()) {
dup2(fd[k-1][0], STDIN_FILENO);
argue = getargs(sepCmd[k]);
execvp(argue[0], argue);
perror(argue[0]);
exit(0);
}
while(waitpid(-1, NULL, 0) != -1);
}

Related

Using pipes to communicate between two programs

I need the main prog to get two strings from the user and an argument for the other program, call fork() and then in child process I need to write the strings into pipe and send them to the other program which returns an int which I want to pass to parent so I'm trying to use another pipe for it but every time it stops right after inserting the strings.
So the main program: (EDITED)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/wait.h>
#define LINELEN (80)
char *mygets(char *buf, int len);
int mygeti();
int main(int argc, char *argv[])
{
char *cmpstr[] = {"lexcmp", "lencmp"};
int veclen = sizeof(cmpstr)/sizeof(char *);
char str1[LINELEN + 1];
char str2[LINELEN + 1];
int index;
int pid[2];
int pfd[4][2];
for(int i = 0; i < 4; i++)
{
if(pipe(pfd[i]) < 0)
{
perror("pipe");
return -2;
}
}
pid[0] = fork();
if(pid[0] == 0) // child a
{
close(pfd[0][1]);
close(pfd[2][0]);
dup2(pfd[0][0], STDIN_FILENO);
dup2(pfd[2][1], STDOUT_FILENO);
char *myargs[3];
myargs[0] = "./loopcmp";
myargs[1] = "lexcmp";
myargs[2] = NULL;
if(execvp(myargs[0], myargs) == -1)
{
perror("exec");
return -2;
}
close(pfd[0][0]);
close(pfd[2][1]);
}
else
{
pid[1] = fork();
if(pid[1] == 0) //child b
{
close(pfd[1][1]);
close(pfd[3][0]);
dup2(pfd[1][0], STDIN_FILENO);
dup2(pfd[3][1], STDOUT_FILENO);
char *myargs[3];
myargs[0] = "./loopcmp";
myargs[1] = "lencmp";
myargs[2] = NULL;
if(execvp(myargs[0], myargs) == -1)
{
perror("exec");
return -2;
}
close(pfd[1][0]);
close(pfd[3][1]);
}
else // parent
{
while (1)
{
printf("Please enter first string:\n");
if (mygets(str1, LINELEN) == NULL)
break;
printf("Please enter second string:\n");
if (mygets(str2, LINELEN) == NULL)
break;
do {
printf("Please choose:\n");
for (int i=0 ; i < veclen ; i++)
printf("%d - %s\n", i, cmpstr[i]);
index = mygeti();
} while ((index < 0) || (index >= veclen));
close(pfd[index][0]);
if(write(pfd[index][1], str1, strlen(str1)) == -1)
{
perror("writeToPipe");
return -2;
}
if(write(pfd[index][1], str2, strlen(str2)) == -1)
{
perror("writeToPipe");
return -2;
}
if(index == 0)
{
close(pfd[2][1]);
char rbuf[1];
while(read(pfd[2][0], &rbuf, 1) > 0)
{
write(STDOUT_FILENO, &rbuf, 1);
}
}
if(index == 1)
{
close(pfd[3][1]);
char rbuf[1];
while(read(pfd[3][0], &rbuf, 1) > 0)
{
write(STDOUT_FILENO, &rbuf, 1);
}
}
}
}
}
return 0;
}
char *mygets(char *buf, int len)
{
char *retval;
retval = fgets(buf, len, stdin);
buf[len] = '\0';
if (buf[strlen(buf) - 1] == 10) /* trim \r */
buf[strlen(buf) - 1] = '\0';
else if (retval)
while (getchar() != '\n'); /* get to eol */
return retval;
}
int mygeti()
{
int ch;
int retval=0;
while(isspace(ch=getchar()));
while(isdigit(ch))
{
retval = retval * 10 + ch - '0';
ch = getchar();
}
while (ch != '\n')
ch = getchar();
return retval;
}
The other program - loopcmp: (Here I shouldn't change anything)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LINELEN (80)
int lencmp(const char *str1, const char *str2);
int lexcmp(const char *str1, const char *str2);
char *mygets(char *buf, int len);
int main(int argc, char *argv[])
{
int(*cmpfunc)(const char *, const char *) = NULL;
char str1[LINELEN + 1];
char str2[LINELEN + 1];
if (argc != 2)
return -1;
if (!strcmp(argv[1], "lexcmp"))
cmpfunc = lexcmp;
else if (!strcmp(argv[1], "lencmp"))
cmpfunc = lencmp;
else
return -1;
while (1)
{
if (mygets(str1, LINELEN) == NULL)
break;
if (mygets(str2, LINELEN) == NULL)
break;
printf("%d\n", cmpfunc(str1, str2));
fflush(stdout);
}
return 0;
}
int lencmp(const char *str1, const char *str2)
{
int val;
val = strlen(str1) - strlen(str2);
if (val < 0)
return 1;
if (val > 0)
return 2;
return 0;
}
int lexcmp(const char *str1, const char *str2)
{
int val;
val = strcmp(str1, str2);
if (val < 0)
return 1;
if (val > 0)
return 2;
return 0;
}
char *mygets(char *buf, int len)
{
char *retval;
retval = fgets(buf, len, stdin);
buf[len] = '\0';
if (buf[strlen(buf) - 1] == 10) /* trim \r */
buf[strlen(buf) - 1] = '\0';
else if (retval) while (getchar() != '\n'); /* get to eol */
return retval;
}
This is what I get:
Picture
and what I actually need it to print the interger returned from the exec of the child and then start again and get new two strings and so on till the user exits. what am I doing wrong? I can only modify the main program (the first one)
The first thing to do is ensure you are closing all unnecessary file descriptors in each process.
This means anything relating to the lexcmp child process should be closed in the lencmp child process, and vice versa. The parent needs to close the read ends of both "TO" pipes, and the write end of both "FROM" pipes.
Each of these closures should happen exactly once, where appropriate.
As is, in the parent, you are calling close(pfd[index][0]);, close(pfd[2][1]);, and close(pfd[3][1]); in a loop.
After calling dup2, you should immediately close the first argument (the original pipe end). As is, in the the children, you are attempting to close them after execvp is called, which leads into the next issue...
If execvp succeeds, it NEVER returns, as it will completely replace the process image. Anything expected to run after it is really operating in a failure state. So
if(execvp(myargs[0], myargs) == -1)
{
perror("exec");
return -2;
}
could be written as
execvp(myargs[0], myargs)
perror("exec");
return -2;
to the same effect.
Aside: the large if .. else if .. else structure of main is a bit hard to read, and not needed since the body of each if statement results in the child processes being replaced, or exiting on error.
The next issues have to do with deadlocking, which most typically occurs when two intercommunicating processes attempt blocking reads from one another at the same time.
Your child processes expect input in a very specific way: 2 lines at a time, creating a pair of strings. The two write calls, in the form of,
write(pfd[index][1], strX, strlen(strX))
do not write any newlines, thus the children wait forever, never to send any data back, and the parent will wait forever, never receiving any data.
Aside: mygets is severely flawed, in a few ways, including being unable to detect EOF or I/O failures (this function is a SIGSEGV in waiting). One of the more obnoxious failings is that the comment here
if (buf[strlen(buf) - 1] == 10) /* trim \r */
is just plain wrong. ASCII decimal 10 is '\n', the line feed, or newline character. '\r', or carriage return, would be decimal 13. This is why using character constants 'A' instead of integer constants 65 is highly encouraged.
The side effect here, generally speaking, is your strings are stripped of a trailing newline character.
The second deadlock occurs when you go to read the child process' response.
Firstly, this example
char rbuf[1];
while(read(pfd[N][0], &rbuf, 1) > 0)
{
write(STDOUT_FILENO, &rbuf, 1);
}
is malformed. Either remove the & operators, OR change char rbuf[1]; to char rbuf;. Fixing this, and the newline problem from above, will result in the parent process reading data back from the child.
The problem then becomes that a while (read(...) > 0) loop will continuously block execution of the calling process, waiting for more data to be available.
This means another deadlock when the child process has already moved on to trying to read another pair of lines from the parent process.
A simple solution is to attempt a single, reasonably large read in the parent, and rely on the behaviour of fflush(stdout); in the child to flush the pipe to the parent.
Here is a functional -ish example, with minimal changes made. This program still has some problems, such as: the parent process generally has no idea of the status of the child processes, and relying signal propagation (^C) from the terminal to end the process tree gracefully, since loopcmp does not handle EOF (should really discuss this with whoever wrote loopcmp.c / mygets).
Additionally, mygeti is flawed as well, as an invalid input cannot be distinguished from a valid input of 0. It also does not handle EOF, or prevent signed integer overflow.
Some more robust abstraction (functions and structures) around creating child processes would help a lot to clean this up further.
This should help you progress, though.
#define _POSIX_C_SOURCE 200809L
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define LINELEN (80)
char *mygets(char *buf, int len);
int mygeti();
void close_pipe(int fd[2])
{
close(fd[0]);
close(fd[1]);
}
int main(void)
{
char *cmpstr[] = {"lexcmp", "lencmp"};
int veclen = sizeof(cmpstr)/sizeof(char *);
char str1[LINELEN + 1];
char str2[LINELEN + 1];
int index;
int pid[2];
int pfd[4][2];
/* pfd[0] is TO lexcmp
* pfd[1] is TO lencmp
* pfd[2] is FROM lexcmp
* pfd[3] is FROM lencmp
*/
for(int i = 0; i < 4; i++)
if(pipe(pfd[i]) < 0) {
perror("pipe");
return -2;
}
pid[0] = fork();
if (pid[0] == 0) {
/* child lexcmp */
close_pipe(pfd[1]);
close_pipe(pfd[3]);
close(pfd[0][1]);
close(pfd[2][0]);
dup2(pfd[0][0], STDIN_FILENO);
dup2(pfd[2][1], STDOUT_FILENO);
close(pfd[0][0]);
close(pfd[2][1]);
char *args[] = { "./loopcmp", "lexcmp", NULL };
execvp(*args, args);
perror("exec");
return -2; /* This only returns from the child */
}
pid[1] = fork();
if (pid[1] == 0) {
/* child lencmp */
close_pipe(pfd[0]);
close_pipe(pfd[2]);
close(pfd[1][1]);
close(pfd[3][0]);
dup2(pfd[1][0], STDIN_FILENO);
dup2(pfd[3][1], STDOUT_FILENO);
close(pfd[1][0]);
close(pfd[3][1]);
char *args[] = { "./loopcmp", "lencmp", NULL };
execvp(*args, args);
perror("exec");
return -2; /* This only returns from the child */
}
/* parent */
close(pfd[0][0]);
close(pfd[1][0]);
close(pfd[2][1]);
close(pfd[3][1]);
while (1) {
printf("Please enter first string: ");
if (mygets(str1, LINELEN) == NULL)
break;
printf("Please enter second string: ");
if (mygets(str2, LINELEN) == NULL)
break;
do {
printf("Please choose (");
for (int i=0 ; i < veclen ; i++)
printf(" [%d] %s", i, cmpstr[i]);
printf(" ): ");
index = mygeti();
} while ((index < 0) || (index >= veclen));
if (0 >= dprintf(pfd[index][1], "%s\n%s\n", str1, str2)) {
fprintf(stderr, "Failed to write to child %d\n", index);
perror("dprintf");
return -2;
}
char buf[64];
ssize_t bytes = read(pfd[index + 2][0], buf, sizeof buf - 1);
if (-1 == bytes) {
perror("read from child");
return -2;
}
buf[bytes] = 0;
printf("Result: %s", buf);
}
}
char *mygets(char *buf, int len)
{
char *retval;
retval = fgets(buf, len, stdin);
buf[len] = '\0';
if (buf[strlen(buf) - 1] == 10) /* trim \r */
buf[strlen(buf) - 1] = '\0';
else if (retval)
while (getchar() != '\n'); /* get to eol */
return retval;
}
int mygeti()
{
int ch;
int retval=0;
while(isspace(ch=getchar()));
while(isdigit(ch))
{
retval = retval * 10 + ch - '0';
ch = getchar();
}
while (ch != '\n')
ch = getchar();
return retval;
}
Note the use of dprintf. If not available for whatever reason, just make sure to write a single newline after each string.
Final aside: with the way fgets works, the + 1 to the string buffer sizes are rather meaningless (although they are indirectly required here due to mygets performing its own, poorly designed buf[len] = '\0'). fgets writes at most len - 1 non-null bytes, always leaving room for the null terminating byte, which it places.

Memory Allocation of char** array in C [duplicate]

This question already has answers here:
C 2D array Memory allocation
(2 answers)
Closed 1 year ago.
I am writing a basic shell program in C for an assignment. I am currently having troubles allocating the memory that will be used to store the command line arguments that will be passed into the program to be parsed.
I am getting a segmentation fault if the input size is greater than or equal to 4 (i.e. "this is a test" will produce a segmentation fault upon program terminations whereas "this is a" will not).
I imagine my issue lies within how I was allocating the memory but the program is capturing each token that I enter and printing them to the screen.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
char cap[] = "";
char* cmd;
const int MAX_ARGS = 16;
const int MAX_ARG_SIZE = 256;
char** arglst = (char**)malloc(MAX_ARGS * sizeof(char*));
for(int i = 0; i < MAX_ARGS; i++)
{
arglst[i] = (char*)malloc(MAX_ARG_SIZE * sizeof(char));
}
pid_t cpid;
//implement ls, cd, mkdir, chdir, rm, rmdir
while(1)
{
printf("Enter a command: ");
scanf("%[^\n]s", cap);
int index = 0;
cmd = strtok(cap, " ");
while( cmd != NULL )
{
strcpy(arglst[index], cmd);
cmd = strtok(NULL, " ");
index++;
}
for(int i = 0; i < index; i++)
{
printf("%s\n", arglst[i]);
}
printf("%d\n", index);
/*
if(strcmp(cap, "quit") == 0) exit(EXIT_SUCCESS);
if( (cpid = fork()) == -1) perror("fork()");
else if(cpid == 0)
{
if( execvp(cmd, arglst) == -1 )
{
errorp("cmd error");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
else
{
cpid = wait(NULL);
strcpy(cmd, "/bin/");
}
*/
for(int i = 0; i < index; i++)
{
free(arglst[i]);
}
free(arglst);
return 0;
}
}
There are a number of bugs.
This won't compile (e.g. errorp instead of perror).
cap is too small to contain a line. Better to use (e.g.) char cap[1000];
Doing a preallocate of each arglst[i] once before the main loop is problematic. One of the cells has to get a NULL value so it works with execvp. However, doing so would cause a memory leak. The solution is to use strdup inside the strtok loop so that cells are only allocated when needed.
Also, because arglst[i] is set only once during initialization, doing a loop with free near the bottom causes UB [accessing the buffer after being freed]. This is fixed with the use of strdup below.
The commented out code references variables (e.g. cmd and cap) that should not be relied upon. At that point, cmd will be NULL, causing a segfault.
The return 0; is placed incorrectly. Only one iteration (and thus only one command) will be executed.
The final free of arglst (e.g. free(arglst)) is done inside the outer loop, so referencing it on the second iteration is UB.
There are a few more issues [annotated below]
Here's a refactored version. It fixes the bugs and is heavily annotated.
I've used the preprocessor to show old/original code vs new/fixed code:
#if 0
// old code
#else
// new code
#endif
Likewise, using #if 1 for purely new code.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#if 1
#include <sys/wait.h>
#endif
int
main(void)
{
// NOTE/BUG: this must be large enough to contain a command line
#if 0
char cap[] = "";
#else
char cap[1000];
#endif
char *cmd;
const int MAX_ARGS = 16;
char **arglst = malloc(MAX_ARGS * sizeof(*arglst));
// NOTE/BUG: because we need to add a NULL terminator, don't preallocate the
// elements -- we'll leak memory
#if 0
const int MAX_ARG_SIZE = 256;
for (int i = 0; i < MAX_ARGS; i++) {
arglst[i] = (char *) malloc(MAX_ARG_SIZE * sizeof(char));
}
#endif
pid_t cpid;
// implement ls, cd, mkdir, chdir, rm, rmdir
while (1) {
printf("Enter a command: ");
// NOTE/BUG: this didn't work too well
#if 0
scanf("%[^\n]s", cap);
#else
fgets(cap,sizeof(cap),stdin);
cap[strcspn(cap,"\n")] = 0;
#endif
int index = 0;
cmd = strtok(cap, " ");
while (cmd != NULL) {
// NOTE/BUG: we should strdup dynamically rather than preallocate -- otherwise,
// we leak memory when we set the necessary NULL pointer below
#if 0
strcpy(arglst[index], cmd);
#else
arglst[index] = strdup(cmd);
#endif
cmd = strtok(NULL, " ");
index++;
}
// NOTE/FIX: we have to add a NULL terminator before passing to execvp
#if 1
arglst[index] = NULL;
#endif
for (int i = 0; i < index; i++) {
printf("%s\n", arglst[i]);
}
printf("%d\n", index);
// NOTE/BUG: we can't [shouldn't] rely on cap here
#if 0
if (strcmp(cap, "quit") == 0)
exit(EXIT_SUCCESS);
#else
if (strcmp(arglst[0], "quit") == 0)
exit(EXIT_SUCCESS);
#endif
if ((cpid = fork()) == -1)
perror("fork()");
else if (cpid == 0) {
// NOTE/BUG: cmd will be NULL here
#if 0
if (execvp(cmd, arglst) == -1) {
errorp("cmd error");
exit(EXIT_FAILURE);
}
#else
if (execvp(arglst[0], arglst) == -1) {
perror("cmd error");
exit(EXIT_FAILURE);
}
#endif
// NOTE/BUG: this will never be executed
#if 0
exit(EXIT_SUCCESS);
#endif
}
else {
cpid = wait(NULL);
// NOTE/BUG -- cmd is NULL and this serves no purpose
#if 0
strcpy(cmd, "/bin/");
#endif
}
// NOTE/BUG: in the _old_ code that did a single preallocate of these cells
// _before_ the loop, freeing them here is wrong -- they would never be
// reallocated because -- the fix using strdup alleviates the issue
for (int i = 0; i < index; i++) {
free(arglst[i]);
}
// NOTE/BUG: freeing this is wrong because we do the allocation only _once_
// above the outer loop
#if 0
free(arglst);
#endif
// NOTE/BUG -- this should be placed at the end to allow multiple commands --
// here it stops after the first command is input
#if 0
return 0;
#endif
}
// NOTE/FIX: correct placement for the above
#if 1
free(arglst);
return 0;
#endif
}
Here's that version cleaned up so that only the fixed code remains:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
int
main(void)
{
char cap[1000];
char *cmd;
const int MAX_ARGS = 16;
char **arglst = malloc(MAX_ARGS * sizeof(*arglst));
pid_t cpid;
// implement ls, cd, mkdir, chdir, rm, rmdir
while (1) {
printf("Enter a command: ");
fgets(cap,sizeof(cap),stdin);
cap[strcspn(cap,"\n")] = 0;
int index = 0;
cmd = strtok(cap, " ");
while (cmd != NULL) {
arglst[index] = strdup(cmd);
cmd = strtok(NULL, " ");
index++;
}
arglst[index] = NULL;
for (int i = 0; i < index; i++) {
printf("%s\n", arglst[i]);
}
printf("%d\n", index);
if (strcmp(arglst[0], "quit") == 0)
exit(EXIT_SUCCESS);
if ((cpid = fork()) == -1)
perror("fork()");
else if (cpid == 0) {
if (execvp(arglst[0], arglst) == -1) {
perror("cmd error");
exit(EXIT_FAILURE);
}
}
else {
cpid = wait(NULL);
}
for (int i = 0; i < index; i++) {
free(arglst[i]);
}
}
free(arglst);
return 0;
}
Note that the above does not check for the number of actual arguments exceeding MAX_ARGS.
While we could add that check, a better way is to use realloc on arglst to dynamically increase it, so an arbitrary limit on the number of arguments isn't needed
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
int
main(void)
{
char cap[1000];
char *cmd;
char **arglst = NULL;
int argmax = 0;
pid_t cpid;
// implement ls, cd, mkdir, chdir, rm, rmdir
while (1) {
printf("Enter a command: ");
fgets(cap,sizeof(cap),stdin);
cap[strcspn(cap,"\n")] = 0;
int index = 0;
cmd = strtok(cap, " ");
while (cmd != NULL) {
if (index >= argmax) {
argmax += 10;
arglst = realloc(arglst,sizeof(*arglst) * (argmax + 1));
}
arglst[index] = strdup(cmd);
cmd = strtok(NULL, " ");
index++;
}
arglst[index] = NULL;
for (int i = 0; i < index; i++) {
printf("%s\n", arglst[i]);
}
printf("%d\n", index);
if (strcmp(arglst[0], "quit") == 0)
exit(EXIT_SUCCESS);
if ((cpid = fork()) == -1)
perror("fork()");
else if (cpid == 0) {
if (execvp(arglst[0], arglst) == -1) {
perror("cmd error");
exit(EXIT_FAILURE);
}
}
else {
cpid = wait(NULL);
}
for (int i = 0; i < index; i++) {
free(arglst[i]);
}
}
free(arglst);
return 0;
}
The original code used malloc and/or strdup on the individual elements of arglst (e.g. arglst[i]).
This makes the code general enough to be used in more complex scenarios. But, as the code is written, the malloc/strdup for the individual elements really isn't necessary.
This is because the cells are fully used [up] at the bottom of the main loop, so we don't need to save them.
We can reuse the cap buffer space on each loop iteration because we do not need any tokens to live on iteration to iteration.
We can simply store the return value from strtok and simplify the code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
int
main(void)
{
char cap[1000];
char *cmd;
char **arglst = NULL;
int argmax = 0;
pid_t cpid;
// implement ls, cd, mkdir, chdir, rm, rmdir
while (1) {
printf("Enter a command: ");
fgets(cap,sizeof(cap),stdin);
cap[strcspn(cap,"\n")] = 0;
int index = 0;
cmd = strtok(cap, " ");
while (cmd != NULL) {
if (index >= argmax) {
argmax += 10;
arglst = realloc(arglst,sizeof(*arglst) * (argmax + 1));
}
arglst[index] = cmd;
cmd = strtok(NULL, " ");
index++;
}
arglst[index] = NULL;
for (int i = 0; i < index; i++) {
printf("%s\n", arglst[i]);
}
printf("%d\n", index);
if (strcmp(arglst[0], "quit") == 0)
exit(EXIT_SUCCESS);
if ((cpid = fork()) == -1)
perror("fork()");
else if (cpid == 0) {
if (execvp(arglst[0], arglst) == -1) {
perror("cmd error");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
else {
cpid = wait(NULL);
}
}
free(arglst);
return 0;
}

C: How do I pass a value from child process to parent process via pipes before execv?

Before I start, I just want to say that this is for a school assignment of mine. I'm really close to finishing, except well, since I'm here, obvious to say, I'm stuck on a problem :(.
First of I'll explain what my assignment wants:
The assignment wants me to create a command-line program in C that allows the user to type in N number of /bin/ commands (E.g. > /bin/ps /bin/ls "/bin/which gcc"). Once the user enters the command and hits the enter key, the parent process (the parent process is the program) will create N child processes (i.e. no. of /bin/ commands entered = no. of child processes parent process will create). Each child will run one of the N commands. All the children will be running concurrently, with the parent waiting for each child to terminate.
Once a child terminates, the parent will print whether the command executed successfully or not (E.g. "Command /bin/ps has completed successfully" or "Command /bin/ps has not completed successfully") and once all children have been terminated, the parent will print "All done, bye!"
The issue:
So I've managed to get my child processes to run concurrently, the only issue is that I'm not sure how to pipe the value of the command (like /bin/ps or /bin/which gcc) from the child process to the parent process to print out the success or not message. I've tried putting the write pipe above my execv which allows me to pipe what I want but the execv won't output anything and I can't put my pipe code below my execv because in that case, then while my execv output will show, my pipe won't. I did think that it might be due to close(1) but commenting that out didn't change the result.
So what I am trying to achieve is something like this:
> /bin/ls "/bin/which gcc" /bin/domainname /bin/fake_command
Output:
/usr/bin/gcc
localdomain
Command /bin/which gcc has completed successfully
Command /bin/domainname has completed successfully
a.txt b.c
Command /bin/ls has completed successfully
Command /bin/fake_command has not completed successfully
All done, bye!
>
But right now, I'm getting:
> /bin/ls "/bin/which gcc" /bin/domainname /bin/fake_command
Output:
Command /bin/which gcc has completed successfully
Command /bin/domainname has completed successfully
Command /bin/ls has completed successfully
Command /bin/fake_command has not completed successfully
>
As you can see, my execv output for the /bin/ commands aren't shown in the output.
I've tried searching SO for people who faced this similar issue as me but none of their solutions managed to work for me which is why I'm asking here. If there's anything you're not clear about, please let me know and I will try my best to explain.
The code:
q1.c
#include "q1.h"
int main(int argc, char *argv[])
{
int
child_status,
pipe_array[2];
pid_t child;
char *success_or_fail;
char *msg_buffer = malloc(CHAR_MAX);
if (msg_buffer == NULL)
{
return -1;
}
struct timespec tw = {.tv_sec = 0, .tv_nsec = 10000000L};
Tuple *process_tuple;
size_t tuple_size = sizeof(*process_tuple) + sizeof(pid_t) + sizeof(char *);
process_tuple = calloc(argc, tuple_size);
if (process_tuple == NULL)
{
return -1;
}
// if (pipe(pipe_array) == -1)
// {
// perror("pipe: ");
// return -1;
// }
for (int j = 1; j < argc; j++)
{
child = fork();
if (child == 0)
{
int
executed,
num_of_words,
num_of_chars;
char
string[strlen(argv[j]) + 1],
*backup = argv[j];
snprintf(string, sizeof(string), "%s", argv[j]);
num_of_chars = get_num_of_chars(string);
num_of_words = get_num_of_words(string);
char *command[num_of_chars + 1];
preparing_the_command(num_of_words, string, command);
// close(pipe_array[0]);
// close(1);
// dup2(pipe_array[1], STDOUT_FILENO);
// write(pipe_array[1], backup, sizeof(backup));
process_tuple[j - 1].pid = getpid();
process_tuple[j - 1].command = backup;
printf(" %i-PID -> %i\n %i-Command -> %s\n\n", process_tuple[j - 1].pid, process_tuple[j - 1].pid, process_tuple[j - 1].pid, process_tuple[j - 1].command);
executed = execv(command[0], command);
nanosleep(&tw, 0);
if (executed == -1)
{
exit(EXIT_FAILURE);
}
else
{
exit(EXIT_SUCCESS);
}
}
else if (child == -1)
{
perror("fork() failed: ");
exit(EXIT_FAILURE);
}
}
printf(" PID -> %i\n Command -> %s\n\n", process_tuple[0].pid, process_tuple[0].command);
// while ((child = waitpid(-1, &child_status, 0)) != -1)
// {
// for (int o = 0; o < argc; o++)
// {
// printf(" PID -> %i\n Command -> %s\n\n", process_tuple[o].pid, process_tuple[o].command);
// }
// close(0);
// close(pipe_array[1]);
//
// dup2(pipe_array[0], STDIN_FILENO);
// char *recipient;
//
// read(pipe_array[0], recipient, sizeof(recipient));
// if (!(WIFEXITED(child_status) && (WEXITSTATUS(child_status) == 0)))
// {
// success_or_fail = "not completed successfully";
// }
// else
// {
// success_or_fail = "completed successfully";
// }
// snprintf(msg_buffer, CHAR_MAX, "Command %s has %s\n", recipient, success_or_fail);
// fputs(msg_buffer, stdout);
// }
fputs("All done, bye!\n", stdout);
free(msg_buffer);
return 0;
}
int get_num_of_chars(const char string[])
{
int
i = 0,
num_of_chars = 0;
while (string[i++] != '\0')
{
if (string[i] != ' ' && string[i] != '\t')
{
num_of_chars++;
}
}
return num_of_chars;
}
int get_num_of_words(const char string[])
{
int
i = 0,
num_of_words = 0;
bool is_not_separator = false;
while (string[i++] != '\0')
{
if (string[i] == ' ' || string[i] == '\t')
{
is_not_separator = false;
}
else if (!is_not_separator)
{
is_not_separator = true;
num_of_words++;
}
}
return num_of_words;
}
void preparing_the_command(int num_of_words, char string[], char *command[])
{
char *token;
for (int j = 0; j < num_of_words && (token = strtok_r(string, " ", &string)); j++)
{
command[j] = token;
}
command[num_of_words] = (void *) NULL;
}
q1.h
#ifndef ASSIGNMENT2Q1_Q1_H
#define ASSIGNMENT2Q1_Q1_H
/***************
** LIBRARIES **
***************/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>
/*************
** STRUCTS **
*************/
typedef struct
{
pid_t pid;
char *command;
} Tuple;
/*************************
** FUNCTION PROTOTYPES **
*************************/
int get_num_of_chars(const char string[]);
int get_num_of_words(const char string[]);
void preparing_the_command(int num_of_words, char string[], char *command[]);
#endif //ASSIGNMENT2Q1_Q1_H

Weird behaviour of C program in MacOS

I've been writing a shell program in C. The program is working as expected in Linux (Ubuntu 16.04) but I'm getting unexpected output in MacOS (10.14.2 Mojave).
/* A shell program.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
void input(char* argv[]);
void print_arr(char *argv[]); // For debugging
int
main(void)
{
while (1)
{
pid_t pid;
char *argv[100];
// Display shell prompt
write(1, "(ash) $ ", 8);
// Take user input
input(argv);
// print_arr(argv); // DEBUG STATEMENT
if (argv[0] != NULL)
{
// Exit if exit command is entered
if (strcmp(argv[0], "exit") == 0)
{
exit(0);
}
// Create child process
if ((pid = fork()) > 0)
{
wait(NULL);
}
else if (pid == 0)
{
// print_arr(argv); // DEBUG STATEMENT
execvp(argv[0], argv);
printf("%s: Command not found\n", argv[0]);
exit(0);
}
else
{
printf("Fork Error!\n");
}
}
}
}
/* Takes input from user and splits it in
tokens into argv. The last element in
argv will always be NULL. */
void
input(char* argv[])
{
const int BUF_SIZE = 1024;
char buf[BUF_SIZE];
int i;
buf[0] = '\0';
fgets((void*) buf, BUF_SIZE, stdin);
i = 0;
argv[i] = strtok(buf, " \n\0");
while (argv[i] != NULL)
{
argv[++i] = strtok(NULL, " \n\0");
}
}
/* Print argv for debugging */
void
print_arr(char *argv[])
{
int i = 0;
while (argv[i] != NULL)
{
printf("%d: %s\n", i, argv[i]);
++i;
}
}
In Linux:
(ash) $ ls
// files and folders are listed
In MacOS (with debug statements):
(ash) $ ls
0: p?M??
0: ??M??
: Command not found
(ash) $ ls
0: ls
0: ??M??
: Command not found
(ash) $ ls
0: ls
0: ??M??
I don't understand that why are the contents of char* argv[] getting modified across fork()?
I've also tried it in the default clang compiler and brew's gcc-4.9, the results are same.
When a program behaves different for no good reason, that's a VERY good sign of undefined behavior. And it is also the reason here.
The array buf is local to the function input and ceases to exist when the function exits.
One way of solving this is to declare buf in main and pass it to input. You will also need the size of the buffer for fgets.
void
input(char * argv[], char * buf, size_t size)
{
buf[0] = '\0';
fgets(buf, sizeof buf, stdin);
argv[0] = strtok(buf, " \n\0");
for(int i=0; argv[i] != NULL; i++) argv[i+1] = strtok(NULL, " \n\0");
}
Another solution (although I suspect many will frown upon it) is to declare buf as static, but then you would need to change BUF_SIZE to a #define or a hard coded value, since you cannot have a static VLA.
#define BUF_SIZE 1024
void
input(char * argv[])
{
static char buf[BUF_SIZE];
buf[0] = '\0';
fgets(buf, sizeof buf, stdin);
argv[0] = strtok(buf, " \n\0");
for(int i=0; argv[i] != NULL; i++) argv[i+1] = strtok(NULL, " \n\0");
}
I removed the cast to void* since it's completely unnecessary. I also changed the while loop to a for loop to make the loop variable local to the loop.

Find a substring within a string using processes

I have a large file (around 1,000,000 characters) in the format "AATACGTAGCTA" and a subsequent file, such as "CGTATC" (10,240 characters). I want to find the largest match of the subsequence within the main sequence. A full, 100% subsequence match may not exist, this is not guaranteed. For the sake of a smaller example, the above would output: Longest match is 4/6 starting at position 5.
I'm working on my C basics, and would like to implement it like so:
The user chooses how many processes they would like to split the work
into.
Each process does 1/nth of the work and updates the shared memory
values located in the struct.
The longest match (it may not be all characters) is reflected in the
struct, as well as it's starting position, and how many
characters were matched. See output below.
Code
#define _GNU_SOURCE
#include <limits.h>
#include <stdio.h>
#include <errno.h>
#include <semaphore.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/shm.h>
typedef struct memoryNeeded {
int start_pos, total_correct;
char sequence[1038336];
char subsequence[10240];
sem_t *sem;
} memoryNeeded;
// Used to check all arguments for validity
int checkArguments(char* p, int argc) {
char *prcs;
errno = 0;
int num;
long conv = strtol(p, &prcs, 10);
if (errno!= 0 || *prcs != '\0' || conv > INT_MAX || conv > 50) {
puts("Please input a valid integer for number of processes. (1-50)");
exit(1);
} else {
num = conv;
if (argc != 4) {
puts("\nPlease input the correct amount of command line arguments (4) in"
"the format: \n./DNA (processes) (sequence) (subsequence)\n");
exit(1);
} else
printf("Looking for string using %d processes...\n", num);
return(num);
}
}
int main (int argc, char* argv[]) {
int processes = checkArguments(argv[1], argc);
key_t shmkey;
int procNumber, shmid, pid;
FILE *sequence;
FILE *subsequence;
char *buf1, *buf2;
// Create shared memory
size_t region_size = sizeof(memoryNeeded);
shmkey = ftok("ckozeny", 5);
shmid = shmget(shmkey, region_size, 0644 | IPC_CREAT);
if (shmid < 0) {
perror("shmget\n");
exit(1);
}
// Create structure in shared memory, attach memory and open semaphore
memoryNeeded *mn;
mn = (memoryNeeded *)shmat(shmid, NULL, 0);
mn->sem = sem_open("sem", O_CREAT | O_EXCL, 0644, 1);
sequence = fopen(argv[2], "r");
subsequence = fopen(argv[3], "r");
// Get file sizes
fseek(sequence, 0L, SEEK_END);
int sz1 = ftell(sequence);
rewind(sequence);
fseek(subsequence, 0L, SEEK_END);
int sz2 = ftell(subsequence);
rewind(subsequence);
// Read files into 2 buffers, which are put into struct mn
buf1 = malloc(sz1);
buf2 = malloc(sz2);
if (sz1 != fread(buf1, 1, sz1, sequence)) {
free(buf1);
}
if (sz2 != fread(buf2, 1, sz2, subsequence)) {
free(buf2);
}
// Initialize struct with necessary values
mn->start_pos = 0;
mn->total_correct = 0;
strncpy(mn->sequence, buf1, sz1);
strncpy(mn->subsequence, buf2, sz2);
fclose(sequence);
fclose(subsequence);
// Begin n forks
for (procNumber = 0; procNumber < processes; procNumber++) {
pid = fork();
if (pid < 0) {
sem_unlink("sem");
sem_close(mn->sem);
printf ("Fork error.\n");
} else if (pid == 0)
break;
}
if (pid != 0) {
while ((pid = waitpid (-1, NULL, 0))){
if (errno == ECHILD)
break;
}
printf("Best match is at position %d with %d/10240 correct.", mn->start_pos, mn->total_correct);
printf ("\nParent: All children have exited.\n");
sem_unlink("sem");
sem_close(mn->sem);
shmdt(mn);
shmctl(shmid, IPC_RMID, 0);
exit(0);
} else {
// this child process will do its 1/nth of the work
sem_wait(mn->sem);
printf ("Child(%d) is in critical section.\n", procNumber);
sleep(1);
int i = 0;
int longest, count = 0;
for (i = 0; i < sz1; i += processes) {
for (int j = 0; j < sz2; j += processes) {
count = 0;
while (mn->sequence[i+j] == mn->subsequence[j]) {
count++;
j++;
}
if (count > longest) {
longest = count;
}
}
}
// If local match is longer than that of the struct, update and unlock
if (longest > mn->total_correct) {
mn->total_correct = count;
mn->start_pos = (i - count);
sem_post(mn->sem);
} else
// If not - unlock and let next process go
sem_post(mn->sem);
exit(0);
}
return 1;
}
The current child code is more or less "pseudocode". I've put it together how it makes sense in my head. (I'm aware this may not be correct or function as intended.) My question is in regard to the child code algorithm near the bottom.
How do I implement this so each child does 1/nth of the work, and finds the longest match, even though it may not match 100%?
Final output would be:
./DNA 6 sequence1 subsequence1
Looking for string using 6 processes...
Best match is at position 123456 with 9876/10240 correct.
Thanks.

Resources