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.
Related
I've been stuck on an issue with my program and just hoping for any help at this point :(
or guidance towards the right direction. In my code, I'm implenting a mini shell in c++ where the user can pipe 2 or more processes together, yet an issue keeps coming up whenever I execute it. Only the first and last commands actually execute so say I run:
cat b.txt | sort | tail -2
only cat b.txt and tail -2 would execute.
Here is my attempt at the whole program, also referenced to this which helped me tremendously with the setup.
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <sys/utsname.h>
#include <unistd.h>
using namespace std;
//this variable will take in the line of input submitted by the user
char buf[1024];
//PIDs for the two child processes
pid_t pid[300];
//these will be use to check the status of each child in the parent process
int status;
int status2;
int pid_num = 1;
//initializes the pipe
int pipeA[2] = {-1,-1};
int g = 0;
void first_command(int pipeA[], char * command[], bool pipeExists){
if(pipeExists){
dup2(pipeA[1], 1);
close(pipeA[0]);
}
// this will run command[0] as the file to execute, and command as the arg
execvp(command[0], command);
printf("Can not execute FIRST command, please enter a valid command \n");
exit(127);
}
void other_command(int pipeA[], char * command0[], int index){
dup2(pipeA[0], 0);
close(pipeA[1]);
execvp(command0[0], command0);
printf("Can not execute SECOND command, please enter a valid command\n");
exit(127);
}
void main_func() {
//stay inside the loop and keep asking the user for input until the user quits the program
while (fgets(buf,1024,stdin) != NULL){
//initialize a boolean to check if user wants to pipe something, set to false by default until we check with user
bool pipeExists = false;
//initialize this arrays to NULL so anything that store in them gets cleared out.
//these arrays will hold the commands that the user wants to carry out.
char * command[1024] = {NULL, NULL, NULL};
char *command0[1024] = {NULL, NULL, NULL};
char *command1[] = {NULL, NULL, NULL};
char *command2[] = {NULL, NULL, NULL};
char *command3[] = {NULL, NULL, NULL};
char ** my_commands[] = {
command0,
command1,
command2,
command3,
NULL
};
//Important to delete mark the last byte as 0 in our input
buf[strlen(buf) -1] = 0;
//initialize this number to zero to start save the tokens at this index
int index = 0;
//a char * to hold the token saved by strtok
char * ptr;
ptr = strtok(buf, " \"");
//Loop through 'buf' and save tokens accordingly
while(ptr != NULL){
// if the user types exit at any moment, the program will exit gracefully and terminate
if(strcmp( ptr, "exit" ) == 0){
exit(0);
}
//if ptr is equal to | user wants to pipe something and we change pipeExists to true
if(strcmp( ptr, "|" ) == 0){
pipeExists = true;
index= 0;
ptr = strtok(NULL, " ");
}
//enter here while user doesnt want to user pipes
if(!pipeExists){
command[index] = ptr;
ptr = strtok(NULL, " ");
index++;
}
//enter here if user want to use pipes
if(pipeExists){
command0[index] = ptr;
ptr = strtok(NULL, " ");
index++;
}
g++;
printf("%s %i\n", ptr, g);
}
for (int s = 0; my_commands[s] != NULL; s++) {
cout << command0[s] << " \n" << endl;
}
//if pipes exists then initialize it
if(pipeExists){
pipe(pipeA);
}
//create first child
if ((pid[0] = fork()) == 0) {
//pass in the pipe, commands and pipe to function to execute
first_command(pipeA, command, pipeExists);
}
else if(pid[0] < 0){
//error with child
cerr<<"error forking first child"<<endl;
}
// if pipe exists create a second process to execute the second part of the command
if(pipeExists){
for(int f = 0; my_commands[f] != NULL; f++) {
//create second child
if ((pid[f] = fork()) == 0) {
other_command(pipeA, command0, index);
}
else if(pid[f] < 0){
//error with second child
cerr<<"error forking child "<< pid_num << endl;
}
}
pid_num++;
}
//if the pipe was created then we close its ends
if(pipeExists){
for(int z = 0; z < pid_num; z++) {
close(pipeA[z]);
}
}
//wait for the first child that ALWAYS executes
if ( (pid[0] = waitpid(pid[0], &status, 0)) < 0)
cerr<<"error waiting for first child"<<endl;
//wait for the second child but only if user wanted to created to use piping
if(pipeExists){
for(int j = 1; j < pid_num; j++) {
if ( (pid[j] = waitpid(pid[j], &status2, 0)) < 0){
printf("Status: %d", pid[j]);
cerr<<"error waiting for child " << j <<endl;
}
}
}
pid_num = 1;
}//endwhile
}
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();
}
}
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;
}
Hey folks I am making a batch command function in a shell that reads from a txt file and pipes it to a child to exec.
I'm having an issue with exec. I suspect it is something with the null terminator. If I execl with an L and an explicit (char*)NULL the exec runs. If I execvp(argIn[0],argIn) nothing runs and returns a -1. If I execvp with an explicit (char*)NULL I get an error cannot convert char* to char* constant*. I read somewhere that it might be the g++ compiler giving me the error but the gcc compiler wouldn't give the error. Right now it won't compile with gcc though so I'm not sure if that's true. But it shouldn't need the explicit terminator anyway. I'm not sure if the '\0' I have stored is being passed to the exec right. It checks out when I pass it to other functions though so maybe that's not the solution.
Second, my for loop won't exec more than once which I think is more to do with the first solution. I can get the execl to fork with an index but I can't increment the index to point to the right token the next time through because the child should be wiping out my index right?
Anyway it's been 3 weeks of digging to figure out what's wrong. I failed the assignment. Probably going to fail the class. I don't know what else to try. So any help I would appreciate.
My question is why would the exec function not execute the program? I'm passing execvp(program name, program name, option, option, '\0') and not getting a result.
or
execl(program name, program name[index], option[index+1], option[index+1], (char*)NULL) and getting a result. They both seem to be following the parameters but only one is giving me a result.
#include<string.h>
#include<iostream>
#include<ctype.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
using namespace std;
int makearg(char s[], char**args[]);
int main(int argc, char *argv[])
{
char **argIn;
int argCount;
int pos = 0;
char str[500];
pid_t pid = fork();
for(int i = 0; i < 4; i++)
{
if(pid == 0)
{
while(fgets(str, 500, stdin) != NULL)
{
cout << "String loaded: " << str;
argCount = makearg(str, &argIn);
execvp(argIn[0],argIn); //nothing exec
// execl(argIn[0],argIn[0],argIn[1],argIn[2],(char*)NULL); //exec but requires index.
else if(pid < 0)
{
perror("fork() error");
exit(-1);
}
else if(pid > 0)
{
cout << "Parent waiting" << endl;
wait(NULL);
}
}
return 0;
}
int makearg(char s[], char**args[])
{
int counter = 1;
int tokenLen = 1;
int i = 0;
int j = 0;
int k = 0;
char arg1[50];
char * arg2;
strcpy(arg1, s);
//Count white space.
while (arg1[j] != '\0')
{
if (arg1[j] == ' ' || arg1[j] == '\0' || arg1[j] == '\n')
{
counter++;
}
j++;
}
//Allocate the number of rows to be pointed to.
args[0] = (char**) malloc(counter + 1);
if(args[0] == NULL)
exit(1);
//Allocate the size of the c string arrays
j = 0;
while(arg1[j] != '\0')
{
if (arg1[j] == ' ' || arg1[j] == '\0' || arg1[j] == '\n')
{
(*args)[i] = (char*)(malloc(tokenLen));
if((*args)[i] == NULL)
exit(1);
tokenLen = 0;
i++;
}
j++;
tokenLen++;
}
(*args)[i] = (char*)(malloc(tokenLen));
if ((*args)[i] == NULL)
exit(1);
//reset values
i = 0;
j = 0;
//Set arg2 to point to args row head. Transfer values from arg1 to arg2.
arg2 = ((*args)[i]);
while(arg1[j] != '\0')
{
if (arg1[j] != ' ' && arg1[j] != '\0' && arg1[j] != '\n')
{
arg2[k] = arg1[j];
k++;
}
else
{
arg2[k] = '\0';
i++;
k = 0;
arg2 = ((*args)[i]);
}
j++;
}
arg2[k] = '\0';
if (counter < 1)
{
return -1;
}
return counter;
}
I took your posted code, updated it to fix build errors and ran. I executed the simple command "ls" but I got the message
String loaded: ls
ls: cannot access '': No such file or directory
That indicated to me that makearg is not working correctly. Then, I added a function to help with diagnosing the problem.
void printArguments(char **args)
{
for ( int j = 0; args[j] != NULL; ++j )
{
printf("args[%d]: %s\n", j, args[j]);
}
}
and added a call to it from main, right after the call to makearg.
argCount = makearg(str, &argIn);
printArguments(argIn);
I got the output:
String loaded: ls
args[0]: ls
args[1]:
ls: cannot access '': No such file or directory
That indicated to me that makearg was not dealing with the end of the line correctly. It creates an empty argument.
I added couple of functions to trim whitespaces from the left and from the right. After that, the child process was able to execute "ls" correctly.
Here's the updated program.
#include<string.h>
#include<iostream>
#include<ctype.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
using namespace std;
int makearg(char s[], char**args[]);
void printArguments(char **args)
{
for ( int j = 0; args[j] != NULL; ++j )
{
printf("args[%d]: %s\n", j, args[j]);
}
}
int main(int argc, char *argv[])
{
char **argIn;
int argCount;
char str[500];
pid_t pid = fork();
for(int i = 0; i < 4; i++)
{
if(pid == 0)
{
while(fgets(str, 500, stdin) != NULL)
{
cout << "String loaded: " << str;
argCount = makearg(str, &argIn);
printArguments(argIn);
execvp(argIn[0],argIn); //nothing exec
}
}
else if(pid < 0)
{
perror("fork() error");
exit(-1);
}
else if(pid > 0)
{
cout << "Parent waiting" << endl;
wait(NULL);
}
}
return 0;
}
void trimWhiteSpacesLeft(char s[])
{
int i = 0;
for ( ; isspace(s[i]); ++i );
if ( i == 0 )
{
return;
}
int j = 0;
for (; s[i] != '\0'; ++j, ++i )
{
s[j] = s[i];
}
s[j] = '\0';
}
void trimWhiteSpacesRight(char s[])
{
int len = strlen(s);
int i = len-1;
for ( ; i >= 0; --i )
{
if ( !isspace(s[i]) )
{
break;
}
}
s[i+1] = '\0';
}
int makearg(char s[], char**args[])
{
int counter = 1;
int tokenLen = 1;
int i = 0;
int j = 0;
int k = 0;
char arg1[50];
char * arg2;
strcpy(arg1, s);
// Trim whitespaces from both ends.
trimWhiteSpacesLeft(arg1);
trimWhiteSpacesRight(arg1);
//Count white space.
while (arg1[j] != '\0')
{
if (arg1[j] == ' ' || arg1[j] == '\0' )
{
counter++;
}
j++;
}
//Allocate the number of rows to be pointed to.
args[0] = (char**) malloc(counter + 1);
if(args[0] == NULL)
exit(1);
//Allocate the size of the c string arrays
j = 0;
while(arg1[j] != '\0')
{
if (arg1[j] == ' ' || arg1[j] == '\0' || arg1[j] == '\n')
{
(*args)[i] = (char*)(malloc(tokenLen));
if((*args)[i] == NULL)
exit(1);
tokenLen = 0;
i++;
}
j++;
tokenLen++;
}
(*args)[i] = (char*)(malloc(tokenLen));
if ((*args)[i] == NULL)
exit(1);
//reset values
i = 0;
j = 0;
//Set arg2 to point to args row head. Transfer values from arg1 to arg2.
arg2 = ((*args)[i]);
while(arg1[j] != '\0')
{
if (arg1[j] != ' ' && arg1[j] != '\0' && arg1[j] != '\n')
{
arg2[k] = arg1[j];
k++;
}
else
{
arg2[k] = '\0';
i++;
k = 0;
arg2 = ((*args)[i]);
}
j++;
}
arg2[k] = '\0';
if (counter < 1)
{
return -1;
}
return counter;
}
I am writing on a shell and want to implement the signals SIGSTP and SIGINT.
When the user starts a new process and presses CTRL+C it should send a SIGINT to the process and when CTRL+Z is pressed the process should get the SIGSTP signal.
Here is my code so far:
string inputNew = input;
vector<char*> arguments;
size_t lastChar = 0;
for(int i = 0; i < input.size(); i++)
{
char& c = input[i];
if((c == ' ' || c == '\t'||c == '\n') && lastChar != i)
{
c = '\0';
arguments.push_back(&input[0] + lastChar);
lastChar = i+1;
}
}
bool checkIfBackground(string & input)
{
size_t lastChar = input.size() - 1;
if(input[lastChar] == '&')
{
return true;
}
return false;
}
if((pid = fork()) < 0) {
exit(1);
} else if(pid == 0) {
execvp(arguments[0], &arguments[0]);
exit(1);
} else if(checkIfBackground(inputNew) == false) {
int status;
pid_t pid_r;
if(waitpid(pid, &status, 0) < 0) {
cout << "PID not known!";
}
} else {
cout << "Prozess is waiting in the background." << endl;
}
I have no idea how to implement the SIGSTP and SIGINT signals inside my code.
See the sigaction(2) manual pages. It explains how to set up an implement a signal handler.
Note that a signal handler is asynchronous. This has a number of implications. Read the manual page, and spend some time in Google.