I am getting incorrect output from my forks - c++

I'm attempting to create a program that creates 2 child processes, and 4 pipes (I know this isn't ideal, but the spec for this specific assignment requires it). While it correctly sorts two of the 5 command line argument integers, the rest are just spat out as what I believe are uninitialized integers, I.E. 7 is printed out as 33234951.
I'm pretty new to pipes, and it's been a little hard to wrap my head around, so I believe this issue has to do with this and not some arbitrary error in code.
I was able to successfully get this done using only 1 parent and child, but as soon as I tried to implement multiple, things got dicey.
I have a lot of unused includes just from messing around with things in attempt to solve the problem.
#include <bits/stdc++.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char **argv) {
printf("Starting\n");
pid_t pid;
pid_t pid2;
int mypipe0[2];
int mypipe1[2];
int mypipe2[2];
int mypipe3[2];
pipe(mypipe0);
pipe(mypipe1);
pipe(mypipe2);
pipe(mypipe3);
/* Create the child process. */
pid = fork();
std::cout << "Fork " << pid << std::endl;
// Child: Sorts Array
if (pid == 0) {
printf("pid == (pid_t) 0 p2\n");
/* This is the child process.
Close other end first. */
close(mypipe0[1]);
char valuesArray[5];
for (int a = 0; a < 5; a++)
read(mypipe0[0], &valuesArray[a], sizeof(char));
printf("finish reading mypipe0");
std::sort(valuesArray, valuesArray + 5);
printf("sorted");
close(mypipe1[0]);
close(mypipe2[0]);
for (int a = 0; a < 5; a++) {
write(mypipe1[1], &valuesArray[a], sizeof(char));
write(mypipe2[1], &valuesArray[a], sizeof(char));
}
close(mypipe1[1]);
close(mypipe2[1]);
exit(0);
}
else if (pid > 1) {
std::cout << "pid == (pid_t) 1" << std::endl;
/* This is the parent process.
Close other end first. */
close(mypipe0[0]); // Closes reading
int valuesArray[5];
valuesArray[0] = atoi(argv[1]);
valuesArray[1] = atoi(argv[2]);
valuesArray[2] = atoi(argv[3]);
valuesArray[3] = atoi(argv[4]);
valuesArray[4] = atoi(argv[5]);
printf("Argv init");
for (int a = 0; a < 5; ++a)
write(mypipe0[1], &valuesArray[a], sizeof(char));
printf("wrote to pipe 1");
close(mypipe0[1]);
wait(NULL);
close(mypipe1[1]); // Closes writing
// char outputArray[6];
int sortedArray[5];
for (int a = 0; a < 5; ++a)
read(mypipe1[0], &sortedArray[a], sizeof(char));
// Printing Array]
for (int a = 1; a < 5; ++a)
printf(", %d", sortedArray[a]);
printf("]");
// wait(NULL);
// close(mypipe2[1]); // Closes writing
// int median;
// read(mypipe1[0], median, sizeof(charian));
exit(0);
}
else {
pid2 = fork();
// Other child
if (pid2 == 0) {
printf("pid == (pid_t) 0\n");
/* This is the child process.
Close other end first. */
close(mypipe0[1]);
char valuesArray[5];
for (int a = 0; a < 5; a++)
read(mypipe0[0], &valuesArray[a], sizeof(char));
printf("finish reading mypipe0");
std::sort(valuesArray, valuesArray + 5);
printf("sorted");
close(mypipe1[0]);
close(mypipe2[0]);
for (int a = 0; a < 5; a++) {
write(mypipe1[1], &valuesArray[a], sizeof(char));
write(mypipe2[1], &valuesArray[a], sizeof(char));
}
close(mypipe1[1]);
close(mypipe2[1]);
exit(0);
}
}
}
I expect the output to be 2 4 5 6 7 from the given command line arguments of 4 2 5 6 7. Instead, I get [1, 28932, 5, 6, -14276913]

The else branch is reached when there is a failure in the first fork call.
Your current code structure is,
pid = fork();
if (pid == 0)
{
runFirstChild();
}
else if (pid > 1)
{
runParent();
}
else // This branch is not taken unless there is a failure
{
pid2 = fork();
if (pid2 == 0)
{
runSecondChild();
}
}
You should instead structure it like so,
pid = fork();
if (pid == 0)
{
runFirstChild();
}
else if (pid > 1)
{
pid2 = fork(); // Fork the second child in the parent process
if (pid2 == 0)
{
runSecondChild();
}
else if (pid2 > 1)
{
runParent();
}
}

Related

c++ Process hangs when piping between two child processes

I am trying to pipe data from one child process to another. When I run this, it hangs. If I don't make it wait for the first child process, it goes back to the top of the loop prompting for commands without giving the expected output, and when I prompt it to quit, it dumps all of the output I was expecting. I had it working with just one child process, but then the second execvp killed the parent process, and I didn't get back to the top of the loop prompting for more commands.
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
using namespace std;
int main(int argc, const char * argv[]) {
bool quit=0;
char quitArray[] = "quit";
int pipeReturnValue, fork1ReturnValue, fork2ReturnValue, pipefd[2], checkForQuit;
//Enter a loop where each iteration prompts for two single-line inputs
while (!quit) {
//Get command 1
char command1[128];
printf("Enter command 1: ");
fgets(command1,128,stdin);
command1[strlen(command1) -1] = 0;
//Exit if user enters quit
checkForQuit = strncmp(command1, quitArray, 4);
if (checkForQuit == 0) {
exit(0);
}
//Get command 2
char command2[128];
printf("Enter command 2: ");
fgets(command2,128,stdin);
command2[strlen(command2) -1] = 0;
//Exit if user enters quit
checkForQuit = strncmp(command2, quitArray, 4);
if (checkForQuit == 0) {
exit(0);
}
//Open pipe
pipeReturnValue = pipe(pipefd);
if (pipeReturnValue < 0) {
perror("Pipe failed");
exit(1);
}
//Fork 1
fork1ReturnValue = fork();
if(fork1ReturnValue < 0) {
perror("Fork failed");
exit(1);
}
else if (fork1ReturnValue == 0) {
//Fork 2
fork2ReturnValue = fork();
if (fork2ReturnValue < 0) {
perror("Fork 2 failed");
}
else if (fork2ReturnValue == 0) {
//close read end of pipe
close(pipefd[0]);
//parse command 1 arguments
//store tokens in array
char *arguments[6] = {};
arguments[0] = strtok(command1, " ");
int tokenCounter = 0;
while (arguments[tokenCounter] != NULL) {
tokenCounter++;
arguments[tokenCounter] = strtok(NULL, " ");
}
//dup stdo to pipe
dup2(pipefd[1], 1);
//execute arguments
execvp(arguments[0], arguments);
}
else {
wait(&fork2ReturnValue);
//close write end of pipe
close(pipefd[1]);
//parse command 2 arguments
//store tokens in array
char *arguments[6] = {};
arguments[0] = strtok(command2, " ");
int tokenCounter = 0;
while (arguments[tokenCounter] != NULL) {
tokenCounter++;
arguments[tokenCounter] = strtok(NULL, " ");
}
//dup stdin to pipe
dup2(pipefd[0], 0);
//exec
execvp(arguments[0], arguments);
}
}
else {
wait(&fork1ReturnValue);
}
}
return 0;
}
I finally figured it out. I needed to open the pipe after the first fork rather than before.
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
using namespace std;
int main(int argc, const char * argv[]) {
bool quit=0;
char command1[128],
command2[128],
quitArray[] = "quit";
int pipeReturnValue,
fork1ReturnValue,
fork2ReturnValue,
checkForQuit,
pipefd[2];
//Loop where each iteration prompts for two single-line inputs
while (!quit) {
//Get command 1
printf("Enter command 1: ");
fgets(command1,128,stdin);
command1[strlen(command1) -1] = 0;
//Exit if user enters quit
checkForQuit = strncmp(command1, quitArray, 4);
if (checkForQuit == 0) {
quit = 1;
exit(0);
}
//Get command 2 and trim trailing new line character
printf("Enter command 2: ");
fgets(command2,128,stdin);
command2[strlen(command2) -1] = 0;
//Exit if user enters quit
checkForQuit = strncmp(command2, quitArray, 4);
if (checkForQuit == 0) {
quit = 1;
exit(0);
}
//Fork to create 1st child process, return error if fork fails
fork1ReturnValue = fork();
if(fork1ReturnValue < 0) {
perror("Fork 1 failed");
exit(1);
}
//Open pipe, return error if fork fails
pipeReturnValue = pipe(pipefd);
if (pipeReturnValue < 0) {
perror("Pipe failed");
exit(1);
}
//First child process
else if (fork1ReturnValue == 0) {
//Fork to create 2nd child process, return error if fork fails
fork2ReturnValue = fork();
if (fork2ReturnValue < 0) {
perror("Fork 2 failed");
}
//Second child process
else if (fork2ReturnValue == 0) {
//close read end of pipe
close(pipefd[0]);
//Parse command 1 arguments, store tokens in an array
char *arguments[6] = {};
arguments[0] = strtok(command1, " ");
int tokenCounter = 0;
while (arguments[tokenCounter] != NULL) {
tokenCounter++;
arguments[tokenCounter] = strtok(NULL, " ");
}
//Dup standard output to write side of pipe
dup2(pipefd[1], 1);
//Execute arguments from command 1
execvp(arguments[0], arguments);
}
//First child code continued
else {
//Wait for child 2 to to terminate
wait(&fork2ReturnValue);
//Close write end of pipe
close(pipefd[1]);
//Parse command 2 arguments, store tokens in array
char *arguments[6] = {};
arguments[0] = strtok(command2, " ");
int tokenCounter = 0;
while (arguments[tokenCounter] != NULL) {
tokenCounter++;
arguments[tokenCounter] = strtok(NULL, " ");
}
//dup standard input to read side of pipe
dup2(pipefd[0], 0);
//Execute arguments from command 2
execvp(arguments[0], arguments);
}
}
//Parent process continued
else {
//Wait for child 1 to terminate
wait(&fork1ReturnValue);
}
//return to top of loop
}
return 0;
}

Linux - Shell does not redirect outputs

I created a simple shell in Linux using fork() and execvp(). It works fine with cat, ls etc. but when I try to redirect its output like ./hello.o > output.txt it doesn't work.
I am guessing I didn't provide the write path to look for the definitions. My shell is currently searching on /bin/ path where most of the commands are stored.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define ARG_SIZE 100 // MAX LENGTH FOR ARGUMENTS
#define PATH "/bin/" // PATH FOR ARGUMENTS
int main() {
char inputLine[BUFSIZ];
char *argv[ARG_SIZE];
// for path + argv
char programPath[200];
while (1) {
printf("myshell> ");
// check if ctrl + D is pressed
if (fgets(inputLine, BUFSIZ, stdin) == NULL)
break;
inputLine[strlen(inputLine) - 1] = '\0';
// check if exit is typed
if (strcmp(inputLine, "exit") == 0)
break;
int i = 0;
argv[0] = strtok(inputLine, " \n");
for (i = 0; argv[i] && i < ARG_SIZE-1; ++i)
argv[++i] = strtok(NULL, " \n");
// create a fork call
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
}
// parent
if (pid != 0) {
wait();
// child
} else {
strcat(programPath, argv[0]);
// will not return unless it fails
execvp(programPath, argv);
perror("execvp");
exit(1);
}
}
return 0;
}

IPC_RMID not work on linux with C++

I'm trying to solve my school project in C++. I have to create 15 processes and they have to run in order what means that processes run in this order 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 0. It work but when I try to remove semaphore from the memory I am getting error from semctl. On the end I use "semctl(semid, 0, IPC_RMID, 0" but I get error 22 which means EINVAL but it doesn't make sense and I try to remove semaphore from parrent process so I should have privileges to do that.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
#include <sys/wait.h>
union semun {
int val;
struct semid_ds *buf;
ushort *array;
};
struct sembuf sops[1];
int semid;
int wait_sem(int index, int pid){
fprintf(stderr, "\n------- Proces %d do operation wait (-1) on semaphore %d\n",pid, index);
sops[0].sem_num = index;
sops[0].sem_op = -1;
sops[0].sem_flg = 0 ;
if (semop(semid, sops, 1)<0){
perror("semop fail wait");
return 1;
}
else
return 0;
}
int signal_sem(int index, int pid){
fprintf(stderr, "\n++++++ Proces %d vykonava operaciu signal (1) na semafore %d\n",pid,index);
sops[0].sem_num = index;
sops[0].sem_op = 1;
sops[0].sem_flg = 0;
if (semop(semid, sops, 1)<0){
perror("semop fail signal");
return 1;
}
else
return 0;
}
void createSem(key_t paKey, int paSemFlg, int paNsems)
{
printf ("uid=%d euid=%d\n", (int) getuid (), (int) geteuid ());
(semid = semget(paKey, paNsems, paSemFlg));
for (int i = 0; i < paNsems; ++i) {
semctl(semid, i, SETVAL, 0);
}
}
void kic()
{
printf("\naaaaaaaaaaaaaa\n");
}
int main() {
key_t key = 1234;
int semflg = IPC_CREAT | 0666;
int nsems = 15;
int semid;
fprintf(stderr, "%d=", sops);
createSem(IPC_PRIVATE, semflg, nsems);
if (semid == -1) {
perror("semget: semget failed");
return 1;
}
else
fprintf(stderr, "semget: semget sucess: semid = %d, parrent pid %d\n", semid, getpid());
int PROCESS_ID = 0;
pid_t PID;
for (int i = 1; i < nsems; i++) {
PID = fork();
if(PID == 0)
{
PROCESS_ID = i;
break;
}
}
if(PID == -1)
{
printf("\nPID ERROR");
}
if(PID != 0) //parrent
{
printf("\n\nparrent with ID %d", PROCESS_ID);
signal_sem(PROCESS_ID+1, PROCESS_ID);
wait_sem(PROCESS_ID, PROCESS_ID);
printf ("uid=%d euid=%d\n", (int) getuid (), (int) geteuid ());
printf("\nEND %d\n", getpid());
int s;
wait(&s);
if((semctl(semid, 0, IPC_RMID, 0))==-1)
{
int a = errno;
printf("\nERROR IPC_RMID %d\n", a);
}
}
if(PID == 0)//child
{
if(wait_sem(PROCESS_ID, PROCESS_ID) == 0){
printf("\nI am child with ID %d", PROCESS_ID);
int ID_NEXT_PROCESS = 1+PROCESS_ID;
if(ID_NEXT_PROCESS == nsems)
ID_NEXT_PROCESS = 0;
signal_sem(ID_NEXT_PROCESS, PROCESS_ID);
return 0;
}
}
return 0;
}
You have two semids. One in global scope, another local to main (which shadows global, you should see a warning). createSem only knows about global one, and initializes it. semctl is called directly by main, and is passed the local one, which is garbage.

Implementing a Pipe in C++

I am having trouble implementing my pipe. It reads Unix terminal commands from a text file for commands such as "ls|wc" where it opens a pipe so that the output of ls can be used for wc.
I have implemented it to parse program names ("ls", "wc") and store them in two separate arrays (arguments and arguments2), fork a child process, then have that child fork another child process, then the second child process calls the execvp() command and passes the first program to be executed.
The output from ("ls") is then written to the pipe by changing the standard output. The former child process then execvp()'s the other process ("wc") and reads from the pipe by changing the standard input.
However, wc loops indefinitely and does not seem to count the number of words from ls. Ls executes in a directory where there are words to be counted.
Any tips? Thank you so much and sorry for the long explanation.
Here is a simpler example: It forks, creates a pipe, and implements "ls" and writes its output to the pipe, goes back to the parent and reads from the pipe the output of "ls". It still seems to be reading forever or not working right.
//
// main.cpp
// Pipe_Test
//
// Created by Dillon Sheffield on 9/28/15.
// Copyright © 2015 Dillon Sheffield. All rights reserved.
//
#include <iostream>
using namespace std;
int main()
{
char* arguments[2];
char* programArguments[1];
int fd[2];
arguments[0] = new char[2];
arguments[1] = new char[2];
programArguments[0] = new char[1];
programArguments[0][0] = '\0';
string ls = "ls";
string wc = "wc";
for (int i = 0; i < 1; i++) {
arguments[0] = &ls.at(i);
arguments[1] = &wc.at(i);
}
pid_t pid = fork();
pipe(fd);
if (pid < 0) {
perror("Failed.\n");
} else if (pid == 0) {
dup2(fd[1], STDOUT_FILENO);
execvp(arguments[0], programArguments);
}
wait(NULL);
dup2(fd[0], STDIN_FILENO);
execvp(arguments[1], programArguments);
close(0);
close(1);
return 0;
}
Here is my original code:
//
// main.cpp
// homework2
//
// Created by Dillon Sheffield on 9/19/15.
// Copyright © 2015 Dillon Sheffield. All rights reserved.
//
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
// Global Variable(s)
const short inputLineSize = 10; // Size of programName, arguments, and argument name.
char *arguments[inputLineSize];
char *arguments2[inputLineSize];
ifstream inputFile;
char* input;
void readLine()
{
// Create new char array(s)
input = new char[inputLineSize];
// Initialize the char array(s)
for (int i = 0; i < inputLineSize; i++)
{
input[i] = '\0';
}
// Read a line and skip tabs, spaces, and new line characters
for (int i = 0; !inputFile.eof() && inputFile.peek() != '\n'; i++)
{
while (inputFile.peek() == '\n' || inputFile.peek() == '\t' || inputFile.peek() == ' ') inputFile.get();
inputFile.get(input[i]);
}
// If the file is multi-spaced, keep reading new line char(s) to clear them
while (inputFile.peek() == '\n') inputFile.get();
}
void parseTokens()
{
//----------Parse the read line into tokens--------------------------------------------//
// Get the program name
for (int i = 0; i < inputLineSize; i++)
{
arguments[i] = new char[inputLineSize];
for (int j = 0; j < inputLineSize; j++)
arguments[i][j] = '\0';
}
int i = 0;
int j = 0;
while (input[i] != '\0' && input[i] != '-' && input[i] != '|')
{
arguments[j][i] = input[i];
i++;
}
// Tokenize arguments if supplied
j++;
int k;
while (input[i] == '-')
{
k = 0;
arguments[j][k] = input[i];
i++;
k++;
while (input[i] != '-' && input[i] != '\0')
{
arguments[j][k] = input[i];
i++;
k++;
}
j++;
}
// Delete unused arguments
while (j < inputLineSize)
{
delete arguments[j];
arguments[j] = NULL;
j++;
}
// Check if the pipe command '|' is supplied
if (input[i] == '|')
{
i++;
// Get the other program name
for (int x = 0; x < inputLineSize; x++)
{
arguments2[x] = new char[inputLineSize];
for (int y = 0; y < inputLineSize; y++)
arguments2[x][y] = '\0';
}
int x = 0;
int j = 0;
while (input[i] != '\0' && input[i] != '-' && input[i] != '|')
{
arguments2[j][x] = input[i];
i++;
x++;
}
// Tokenize arguments if supplied
j++;
int k;
while (input[i] == '-')
{
k = 0;
arguments2[j][k] = input[i];
i++;
k++;
while (input[i] != '-' && input[i] != '\0')
{
arguments2[j][k] = input[i];
i++;
k++;
}
j++;
}
// Delete unused arguments
while (j < inputLineSize)
{
delete arguments2[j];
arguments2[j] = NULL;
j++;
}
}
}
int main()
{
// Variable(s)
pid_t pid;
pid_t pid2;
int fd[2];
//--Open the file named "input"-------------------------------------------------------//
inputFile.open("input", ios::in);
// Check if opening the file was successful
if (inputFile.is_open())
{
// Read until the file has reached the end
while (!inputFile.eof())
{
// Read a line and parse tokens
readLine();
parseTokens();
//----------Now create a new process with parsed Program Name and Arguments-----------//
// Create a pipe
pipe(fd);
// Fork
pid = fork();
if (pid < 0)
{
perror("Fork failed.\n");
return -2;
}
else if (pid == 0)
{
// Fork again
pid2 = fork();
if (pid2 < 0)
{
perror("Fork failed.\n");
return -2;
}
else if (pid2 == 0)
{
// Change standard output
if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) perror("dup2 error to stdout.\n");
// Execute the given program
execvp(arguments[0], arguments);
}
// Change the standard input to the pipe
if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) perror("dup2 error to stdin.\n");
int returnValue = execvp(arguments2[0], arguments2);
if (returnValue == -1) perror("Error has occurred.\n");
// Close the pipe and exit
close(fd[0]);
close(fd[1]);
exit(0);
}
// Wait for the child so it doesn't become a Zombie
wait(NULL);
//----------Clean up-----------------------------------------------------------------//
delete input;
input = NULL;
int i = 0;
while (arguments[i] != NULL)
{
delete arguments[i];
arguments[i] = NULL;
i++;
}
i = 0;
}
}
else perror("Cannot open file.\n");
inputFile.close();
return 0;
}
You first execute a pipe(), then you execute a fork(), and the child process, with some additional work, executes your commands.
The problem here is that the pipe()d file descriptors, both of them, remain open in the original parent process. Both the read-side of the pipe, and, more importantly, the write-side of the pipe. These file descriptors do, from all appearances, get correctly set up for your child processes, but because the file descriptors also remain open in the parent process, the pipe never closes, even after the process that writes to the write side of the pipe terminates.
Since the write side of the pipe remains open, in the parent process, the child process that's reading the read side of the pipe will continue reading. Forever.
What you need to do is that after the fork, in the parent process, close both the read and the write side of the pipe. Once the initial process gets forked, the parent process does not need the pipe, and its open file descriptors get in the way.

How to share an array between forks?

I am writing this code, which basically takes an argument specifying how many child threads I want, forks to get them, and then prints all the pids which are stored in an array.
This would be fine if only the parent would need the PIDs, but I also need the child to get their IDS (pcid). I copy and pasted some code from the net (which I didn't really understand), so I'm not sure why it's not working.
I get a segmentation error after the first PID prints.
What's wrong here?
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/shm.h>
#include <sys/ipc.h>
int main(int argc, char *argv[])
{
if(argc < 2) {
printf("ERROR: No arguments fed.\n");
exit(-1);
}
int amount = atoi(argv[1]);
int i;
int pid = 1;
int pcid = 0;
key_t key;
int shmid;
int *arr[amount];
key = ftok("thread1.c",'R');
shmid = shmget(key, 1024, 0644 | IPC_CREAT);
for(i = 0; i < amount; i++)
{
if(pid != 0)
{
pid = fork();
}
*arr = shmat(shmid, (void *) 0, 0);
if(pid != 0)
{
*arr[i] = pid;
}
else
{
pcid = *arr[i];
break;
}
}
if(pid != 0)
{
printf("Printing PID Array:\n");
for(i =0; i < amount; i++)
{
printf("%d\n", *arr[i]);
}
}
else
{
printf("My PID: %d\n",pcid);
}
}
you are using an array of pointers. And in line *arr = shmat(shmid, (void *) 0, 0) you assigned the shared memory access point to the first element of array. Now when you are using *arr[i] = pid it will go to the array i+1 element where an unknown address stays and you try to put a value there. so you got segmentation fault.