C - fork() & execl() & loop + "half pyramid output" - c

I have a troubles with the following. Let's say, I have a two programs, one is "input.c" and second is "output.c". Output is a simple one and looks like this (I will paste only the most important passage).
outputbin.c
//
char buffer[512];
strncpy(buffer, argv[1], sizeof(buffer));
printf("Your output is: %s\n", buffer);
//
And this is the main passage from my input.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
pid_t pid;
char *charchar = "\x41";
int status;
char *outputbin;
int i, j, iterations;
if(argc < 2)
{
fprintf(stderr, "Usage: %s <iterations> <outputbin>\n", argv[0]);
exit(0);
}
iterations = atoi(argv[1]);
outputbin = argv[2];
pid = fork();
if(pid != 0)
{
waitpid(-1, &status, 0);
}
if(pid == 0)
{
for(i=0; i < iterations; ++i)
{
for(j = 0; j <= i; ++j)
{
printf("%s", charchar);
//execl(outputbin, outputbin, charchar, NULL);
}
printf("\n");
}
}
return 0;
}
When I compile this program using gcc and do this (without argv[2]):
./input 10
I get this:
A
AA
AAA
AAAA
AAAAA
AAAAAA
AAAAAAA
AAAAAAAA
AAAAAAAAA
AAAAAAAAAA
It's okay, but only till I remove this piece of code - "printf("%s", input);" and uncomment "execl", so:
for(j = 0; j <= i; ++j)
{
execl(output, output, input, NULL);
}
And run: ./a.out 10 ./outputbin
I get only this - Your output is: A
Only first char and that's all. How to let is execute whole "half pyramid" of strings line by line? No matter what I tried, everything end with the same result.

Assuming it executes successfully, execl does not return - it replaces the program running in the process with another program. So when calling:
for(j = 0; j <= i; ++j)
{
execl(output, output, input, NULL);
}
The loop will only iterate one time - after execl is called, outputbin will start executing. If you want to keep the loop logic, you should move it to outputbin.c so that it'll be executed there.

Related

Taking an exact number of bytes from input in C and synchronization between processes

Hi i need to take only 5 bytes from stdin, i've tried this but i have problem while executing it since it keeps asking me for input and at the end the string contained in buffer is wrong.
Also i'd like to know how to synchronize N processes while the parent is sleeping.
buffers[] is an array of buffers.
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define fflush(stdin) while (getchar() != '\n')
char **filenames;
int *files;
char **buffers;
int n_proc;
int main(int argc, char **argv) {
long i;
pid_t pid;
int status;
if(argc < 2) {
puts("Usage error: prog file1 ... fileN.\n");
exit(1);
}
filenames = argv + 1;
n_proc = argc - 1;
puts("Bef malloc buff.\n");
if((buffers = malloc(sizeof(char *) * n_proc)) == NULL) {
puts("Buffers' malloc error.\n");
exit(1);
}
if((files = malloc(sizeof(int) * n_proc)) == NULL) {
puts("Files' malloc error.\n");
exit(1);
}
puts("After malloc buff.\n");
for(i = 0; i < n_proc; i++) {
if((files[i] = open(filenames[i], O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) {
printf("Error while opening file %ld.\n", i);
exit(1);
}
}
puts("After file open.\n");
for(i = 0; i < n_proc; i++) {
if((buffers[i] = (char *) mmap(NULL, 1028, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0)) == NULL) {
printf("Error in mapping buffer %ld.\n", i);
exit(1);
}
}
puts("After mapping.\n");
i = 0;
while(i < n_proc) {
printf("Fork %ld started.\n", i);
pid = fork();
if(pid < 0) {
printf("Error while forking %ld.\n", i);
exit(1);
} else if(pid == 0) {
puts("Please insert an input of max 5 characters.\n");
printf("Son %ld.\n", i);
fflush(stdout);
fgets(buffers[i], 6, stdin);
buffers[i][strcspn(buffers[i], "\n")] = 0;
//int j;
//for(j = 0; j < 5; j++)
//buffers[i][j] = getchar();
//printf("Buff has %s inside.\n", buff);
//fflush(stdout);
fflush(stdin);
//strcpy(buffers[i], buff);
printf("Buffer %d has string %s inside.\n", i, buffers[i]);
fflush(stdout);
write(files[i], buffers[i], 6);
} else {
printf("Parent %ld.\n", i);
wait(&status);
}
i++;
}
}
This is only a prototype of the code, since there's still synchronization needed and signal handling
Code requires when to write on command line N files and creating N processes that each take 5 bytes from stdin and put in their own file.
As an example if i try with
./a.out hello.txt hello1.txt
Bef malloc buff.
After malloc buff.
After file open.
After mapping.
Fork 0 started.
Parent 0.
Please insert an input of max 5 characters.
Son 0.
Hello
Hello
Buffer 0 has string Hello inside.
Hello
Fork 1 started.
Parent 1.
Please insert an input of max 5 characters.
Son 1.
Hello
Hello
Buffer 1 has string Hello inside.
Hello
Fork 1 started.
Parent 1.
Please insert an input of max 5 characters.
Son 1.
As you can see it doesn't take the input and keeps asking for it, same problem with the getchar().
Note that in case stdin is associated with a terminal, there may also be input buffering in the terminal driver, entirely unrelated to stdio buffering. (Indeed, normally terminal input is line buffered in the kernel.) This kernel input handling can be modified using calls like tcsetattr(3); (stdin(3) man page)
If you give it the input "12345\n":
#include <stdio.h>
int main(void) {
char buffers[1][5];
unsigned i = 0;
for(unsigned j = 0; j < 5; j++)
buffers[i][j] = getchar();
printf("%.5s", buffers[i]);
// read the newline. You may need to discard others.
int ch = getchar();
if(ch == '\n')
printf("\n");
return 0;
}
it will print:
12345

Why this code with fork() generates Runtime Error in ejudge?

Why this code may generate Runtime Error in ejudge? This program counts the number of words from stdin input. Words can be separated by any amount of ' ' and '\n'.
It seems like fork() can cause a problem but I am not sure why I don't get the same error on my computer.
ejudge uses gcc - Plain C, 64 bit, using -std=c11 or -std=gnu11
The task:
On the standard input stream a text string is given which consists of
words (a sequence of non-space characters), between which there can be
any number of whitespace characters, including line feeds.
You need to calculate the number of words if you know there are not
more than 255, and output this value to the standard output stream.
Use creating new processes so that each process reads not more than
one word, e.g. using scanf("%s", ...).
You can only output the result from the process which was started
first (i.e. from the original program).
The resulting program must return with return code 0.
The size of each word does not exceed 4096 bytes.
My code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#define DBG(args...) fprintf(stderr, args)
//#define DBG(args...)
int main(int argc, char* argv[])
{
int status;
pid_t pid;
pid_t first_child;
for (int i = 0; i < 256; ++i) {
pid = fork();
if (pid == 0) { // child continue reading
char str[4097];
if (scanf("%s", str) != EOF)
continue;
exit(1);
} else {
if (i == 1) {
first_child = pid;
}
if (wait(&status) == first_child) {
break;
} else {
exit(WEXITSTATUS(status) + 1);
}
}
}
fprintf(stdout, "%i\n", WEXITSTATUS(status));
fflush(stdout);
fclose(stdout);
return 0;
}
Rewrote the algorithm and it worked!
In the first version, many unnecessary forks were made. For example, if 6 were intended it created 12.
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
// #define DBG(args...) fprintf(stderr, args)
#define DBG(args...)
int main(int argc, char* argv[])
{
int status;
pid_t first_pid;
pid_t pid = fork();
if (pid != 0) {
wait(&status);
printf("%i\n", WEXITSTATUS(status));
return 0;
}
for (int i = 0; i < 256; ++i) {
char str[4097];
if (scanf("%s", str) == EOF) {
DBG("PID %i\n", pid);
exit(0);
}
pid = fork();
if (pid != 0)
break;
}
DBG("PID %i waiting\n", pid);
wait(&status);
exit(WEXITSTATUS(status) + 1);
}

How do I get keyboard inputs from the user using C and linux?

I have to make a short program that, creates two child processes, each one accepting an integer from the keyboard and writes them to a pipe, from where the parent process summarizes them and displays the total on the screen.
I've written one with scanf(), but it freezes up and it doesn't give me the sum. How do I make it work with scanf or any other way if possible?
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int pipe(int pd[2]);
int main(int argc, char *argv[])
{
int pd[2], sum=0, num=0;
if(pipe(pd) == -1)
for(int i = 0; i < 2; i++)
{
if(fork() == 0)
{
scanf("%d", num);
if(write(pd[1], &num, sizeof(int)) == -1)
printf("Error: Write()");
}
}
for(int j = 0; j < 2; j++)
{
wait(NULL);
if(read(pd[0], &num, sizeof(int)) == -1)
printf("Error: Read()");
sum += num;
}
printf("Total: %d\n", sum);
}
Lots of problems here:
You have if(pipe(pd) == -1), and I assume you meant to have an error handler as the "then" clause for it, but you don't, so children will only spawn if the pipe fails, which is basically the opposite of what you want.
You have scanf("%d", num);. You need &num since num isn't already a pointer.
You need to return or exit from the child processes, or they'll fall into the next loop and consume the output.
With just those things fixed, it's enough to make it work:
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int pipe(int pd[2]);
int main(int argc, char *argv[])
{
int pd[2], sum=0, num=0;
if(pipe(pd) == -1)
{
perror("pipe");
return 1;
}
for(int i = 0; i < 2; i++)
{
if(fork() == 0)
{
scanf("%d", &num);
if(write(pd[1], &num, sizeof(int)) == -1)
printf("Error: Write()");
return 0;
}
}
for(int j = 0; j < 2; j++)
{
wait(NULL);
if(read(pd[0], &num, sizeof(int)) == -1)
printf("Error: Read()");
sum += num;
}
printf("Total: %d\n", sum);
}
There's a few other things you should fix too, but they're not complete show-stoppers. Here's what jumped out at me:
You don't need to declare your own pipe prototype. The one from unistd.h is fine.
You should handle the case where fork or scanf fail.
You should handle partial reads and writes.
You should close the read end of the pipe in the children and the write end of the pipe in the parent after forking.
You should consider using a lock to control reading input, so that it doesn't depend on TTY line buffering to work reliably.

C - Creating child processes

How do i create a number of child processes given from command line ?
Something like this , where n is given from command line :
for (i = 0; i < n; i++) {
pids[i] = fork();
}
No, this will not work because then the child processes will create more children and this will not be what you wanted. For a better idea of how that happens, go take a look at fork() branches more than expected?. So you have to break out of the loop if the current process is a child like so:
for (i = 0; i < n; i++) {
if (!(pid[i] = fork()))
break;
}
In order to see this in action, lets look at minimally complete example
file.c:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int i, n = atoi(argv[1]);
pid_t *pid = calloc(n, sizeof *pid);
for (i = 0; i < n; i++)
if (!(pid[i] = fork()))
break;
puts("hello world");
return 0;
}
Then compile and run it
$ gcc -o file file.c
$ ./file 3
hello world
hello world
hello world
hello world
Note that there are 4 messages because there 3 children plus the parent process.

Learning Pipes and Processes

I'm trying to get a better understanding of pipes and processes. I want to implement multiple chained pipes like cat test.txt | sort | uniq -c. I started my code with the cat test.txt, but it isn't working. It compiles, but when I provide a file name in the command line, for example, ./hwk ./test.txt. Nothing returns. Can someone take a look and give me some hints? I want to use loops because I want to be able to add more pipes. I know there's a lot of issues in my code, so I hope someone can give me some guidance on this topic. Thanks.
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#define SIZE 1024
int main (int argc, char **argv)
{
int num_pipe = 1;
int commands = num_pipe + 1; //number of commands is one more than the number of pipes
int fds[num_pipe * 2];
int status;
pid_t pid;
char *str_ptr;
//Pass Command
char *arrayOfCommands[] = {"cat", NULL};
//Setting up pipes
int i;
for (i = 0; i < num_pipe; i++){
if(pipe(fds + i * 2) == -1) {
perror("Error creating pipes");
exit(1);
}
}
int j = 0;
for (i = 0; i < commands - 1; ++i) {
pid = fork();
if (pid == 0) {
if (i < commands) {
if (dup2(fds[j+1], 1) < 0) {
perror("dup2 error");
exit(EXIT_FAILURE);
}
}
if (j != 0) {
if(dup2(fds[j-2], 0) < 0) {
perror("dup2 error");
exit(EXIT_FAILURE);
}
}
for (i = 0; i < 2*num_pipe; i++) {
close(fds[i]);
}
if (execvp(arrayOfCommands[0], arrayOfCommands) < 0) {
perror("Array error");
exit(EXIT_FAILURE);
}
}
else if (pid < 0){
perror("Error");
exit(EXIT_FAILURE);
}
j += 2;
}
for (i = 0; i < 2 * num_pipe; i++){
close(fds[i]);
}
for (i = 0; i < num_pipe + 1; i++) {
wait(&status);
}
return 0;
}
I called this mainly minor adaptation of your program p3.c, compiling it to produce p3. Since there's only one command (cat) being invoked, I juggled things so that it will work correctly. When run as ./p3 p3.c, it prints out the content of the source code.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
static void err_exit(const char *str);
int main (int argc, char **argv)
{
int num_pipe = 0; // Just cat - no pipes
int commands = num_pipe + 1; // Number of commands is one more than the number of pipes
int fds[num_pipe * 2 + 1]; // Avoid size 0 array
char *arrayOfCommands[3] = { "cat", NULL, NULL};
if (argc != 2)
err_exit("Missing filename argument");
arrayOfCommands[1] = argv[1];
for (int i = 0; i < num_pipe; i++)
{
if (pipe(fds + i * 2) == -1)
err_exit("Error creating pipes");
}
int j = 0;
for (int i = 0; i < commands; ++i)
{
pid_t pid = fork();
if (pid == 0)
{
printf("%d: %s %s\n", (int)getpid(), arrayOfCommands[0], arrayOfCommands[1]);
fflush(stdout);
if (i < commands-1 && dup2(fds[j+1], 1) < 0)
err_exit("dup2 error");
if (j != 0 && dup2(fds[j-2], 0) < 0)
err_exit("dup2 error");
for (i = 0; i < 2*num_pipe; i++)
close(fds[i]);
execvp(arrayOfCommands[0], arrayOfCommands);
err_exit("Array error");
}
else if (pid < 0)
err_exit("Error");
j += 2;
}
for (int i = 0; i < 2 * num_pipe; i++)
close(fds[i]);
for (int i = 0; i < num_pipe + 1; i++)
{
int status;
pid_t pid = wait(&status);
printf("PID %d exited 0x%.4X\n", (int)pid, status);
}
return 0;
}
static void err_exit(const char *str)
{
perror(str);
exit(EXIT_FAILURE);
}
Check that works for you. Then you'll need to work out how you're going to create a second command. Your arrayOfCommands isn't going to help directly. You'll need another array of strings in some shape or form.
An extension to run cat file | rev. The changes are really quite minor. I created a_cat to handle the cat command, a_rev for the rev command, and a_cmds as the array of commands. It was also necessary to fix a loop on i to a loop on k.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
static void err_exit(const char *str);
int main (int argc, char **argv)
{
int num_pipe = 1;
int commands = num_pipe + 1; //number of commands is one more than the number of pipes
int fds[num_pipe * 2 + 1]; // Avoid size 0 array
char *a_cat[3] = { "cat", NULL, NULL};
char *a_rev[2] = { "rev", NULL};
char **a_cmds[] = { a_cat, a_rev };
if (argc != 2)
err_exit("Missing filename argument");
a_cat[1] = argv[1];
for (int i = 0; i < num_pipe; i++)
{
if (pipe(fds + i * 2) == -1)
err_exit("Error creating pipes");
}
int j = 0;
for (int i = 0; i < commands; ++i)
{
pid_t pid = fork();
if (pid == 0)
{
printf("%d: %s\n", (int)getpid(), a_cmds[i][0]);
fflush(stdout);
if (i < commands-1 && dup2(fds[j+1], 1) < 0)
err_exit("dup2 error");
if (j != 0 && dup2(fds[j-2], 0) < 0)
err_exit("dup2 error");
for (int k = 0; k < 2*num_pipe; k++)
close(fds[k]);
execvp(a_cmds[i][0], a_cmds[i]);
err_exit("Array error");
}
else if (pid < 0)
err_exit("Error");
j += 2;
}
for (int i = 0; i < 2 * num_pipe; i++)
close(fds[i]);
for (int i = 0; i < num_pipe + 1; i++)
{
int status;
pid_t pid = wait(&status);
printf("PID %d exited 0x%.4X\n", (int)pid, status);
}
return 0;
}
static void err_exit(const char *str)
{
perror(str);
exit(EXIT_FAILURE);
}
You aren't passing your program's command-line arguments through to the "cat" child process. You initialize arrayOfCommands like so -> char *arrayOfCommands[] = {"cat", NULL}; <- then you pass it as-is to the execvp() function as the second argument.
Okay your first problem is that in the line:
execvp(arrayOfCommands[0], arrayOfCommands);
you are using arrayOfCommands but I am not sure how you're populating arrayOfCommands for the case where the text file is not being displayed. I mean are you setting arrayOfCommands like the following earlier in the code:
char *arrayOfCommands[] = {"cat", "./test.txt", NULL};
If I understand you correctly your program is called hwk and for whatever reason you think ./hwk ./test.txt should be parsed but that means you should be parsing argv.
Okay now that that's out of the way let's look at the bigger problem of how you are setting things up.
So when a shell parses out pipes it does there's quite a bit going on. Consider the following:
foo fooparam1 fooparam2 | bar barparam1 | baz bazparam1 bazparam2
The shell uses recursion to solve the problem:
foo fooparam1 fooparam2 | ( bar barparam1 | baz bazparam1 bazparam2 )
So it would look SOMETHING like:
spawn_sub_pipes(const char *str) {
char *cmd = strtok(str, "|");
char *rest = strtok(NULL, "|");
int fds[2];
pipe(fds[]);
int pid = fork();
if ( pid < 0 ) {
perror("pipe error");
exit(-1);
}
if ( pid ) { /* parent is the writer */
close(fds[0]); /* close reading pipe */
dup2(fds[1], 1); /* we attach stdout to the pipe */
}
if ( pid == 0 ) {
close(fds[1]);
dup2(fds[0], 0); /* attach the pipe to stdin */
if ( rest ) { /* fork next children */
spawn_sub_pipes(rest);
}
execvpe(cmd);
}
}
IMPORTANT NOTE
I have just written the above code out without testing it. Get the idea from it but don't use it verbatim.

Resources