piping to interactive programs hang?

Hi, I am doing a homework assignment that creates a shell to be used in a Unix environment. One of the requirements is that the shell accept piped commands. I have set this up as a primer to accept only ONE pipe in the code below. I am able to get correct output from the piped commands (such as "ls | less") But once the command completes the keyboard control is lost. I have to use ctrl+c to terminate the program. Does any one know why?

- Will
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#define _BSD_SOURCE
#include <cstdlib>
#include <string>
#include <vector>
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <cstring>
using namespace std;
int runCmd(const vector< vector <string> >&, vector<struct program_time>&);

struct program_time {
	char *name;
	struct timeval utime;
	struct timeval stime;
};


int main (int argc, char **argv) {
	int errStatus;
	string input;
	vector<vector < vector <string> > >commands; //vector of tokenized input from user line -> pipe delimited -> word tokenized
	vector<struct program_time> program_times; 
	
	do {
		fflush(stdout);
		fflush(stderr);
		int saveStdOut=dup(1);
		cout << "statsh> ";
		dup2(saveStdOut, 1);
      	 close(saveStdOut); // credit for this line goes to Foonly below ;-)
		fflush(stdout);
		getline (cin,input);
		//cout <<input<<endl;
			if (input.compare("exit") !=0) {
				if (input.compare("stats") !=0) {  //start tokenizing
					/*Create a single vector of strings that is delimited by the pipe symbol
					 * Then run a separate tokenizer on these strings usingin "  " as a delimited */
					vector < vector <string> > pipe_delimited;
					vector<string> temp_pipe_delimited;
					vector<string> space_delimited;
					char *pipeToken= strtok(const_cast<char *>(input.c_str()), "|");
					while (pipeToken != NULL) {
						temp_pipe_delimited.push_back(pipeToken);				 	
						cout << pipeToken << endl;
					pipeToken= strtok(NULL, "|");
					}
								
					for (vector<string>::iterator it = temp_pipe_delimited.begin(); it != temp_pipe_delimited.end(); ++it) {
					char *token = strtok(const_cast<char  *>(it->c_str()), " "); 		
						while (token != NULL) {
					 		space_delimited.push_back(token);
					 		token = strtok(NULL, " "); 
				 		}
				 		pipe_delimited.push_back(space_delimited);
				 		//Test Run Comand
				 		//errStatus= runCmd(space_delimited, program_times);
				 		//Run command succeeds on each pipe
				 		space_delimited.clear();					
					}
					
					errStatus= runCmd(pipe_delimited, program_times);
					commands.push_back(pipe_delimited);
				}
				else { //run stats
					
					cout << "\nStat Shell\n";
					for (vector<struct program_time>::iterator it = program_times.begin(); it!=program_times.end();++it) {
						cout <<"--------\n " <<"Name: " << it->name <<"\n  Sys Time: "<< it-> stime.tv_sec << "." << it->stime.tv_usec<<"\n User Time: " <<  it->utime.tv_sec<<"." << it->utime.tv_usec <<endl;
												
					}
					cout <<endl;
				}
			} 
		
	} while ((input.compare("exit") !=0));
	/* Debug out of the commands entered by the user with pipes */
		for (vector< vector < vector <string> > >::iterator it= commands.begin(); it != commands.end(); ++it) {
			
			for (vector < vector <string> >::iterator piped_it= it->begin(); piped_it != it->end(); ++piped_it) {
				
				for (vector<string>::iterator spaced_it= piped_it->begin(); spaced_it != piped_it->end(); ++spaced_it) {
					cout << *spaced_it << " ";
				}
				cout << " | "; 
			}
			cout << endl;
		}
return 0;
}
	// in: list2 of list1 of strings
					//list1 of strings is space delimited string between pipes
					//list2 is list of commands spearated by pipes  
	int runCmd(const vector<vector< string> > &cmdLine, vector<struct program_time> &_program_times ) {
		//char *argv[100]; //assume max size 
		int args;
		char *argv[255]; //hardcoded 255 token max //char *argv[args + 1];
		int longestLength = 40; //assumed a token is at most 40 characters long
		int forkNum= (cmdLine.size());
		pid_t child_pid;
		int child_status;
		int **fdArray; //pointer to array of int
		
		//Set up corrent number of pipes
		fdArray = new int*[2]; //allocate memory for new pipe
		for (unsigned int i=0; i<(cmdLine.size()-1); ++i) {
		 
		fdArray[i]= new int[2];
		pipe(fdArray[i]);
		cout << fdArray[i][0] <<"," << fdArray[i][1] <<endl; 
		}

		cout << forkNum << " command to process.\n";
		for (int i =0; i < forkNum; ++i) { //perform fork and exec from 0 to (n) processes  
			cout <<"inside loop\n";			
			child_pid= fork();
			args=cmdLine[i].size();
			//Get the character array from the vector here 
			for (int c_size = 0; c_size < args; ++c_size){ //c_size = single command size
				argv[c_size] = (char *)malloc((sizeof(char) * longestLength + 1));
				strcpy(argv[c_size], cmdLine.at(i).at(c_size).c_str());
				//cout << argv[c_size] << endl;
				//cout<<argv[i]<<endl; //print each element 
			}
			
			*(argv + args)=NULL; //null terminate this string of tokens for the exec call 
			
			
			
			if (child_pid >0 ) { //Process is  a parent
				
				 cout <<"Child PID is "<<child_pid<<endl;
			     cout <<"child status is "<< child_status<<endl;
				/*this is commented out because we want to wait for all process to finish outside the fork loop
				  so we don't disturb interactive piped process */			 
				//wait4(-1,&child_status,0,NULL);
			     
			 } 
			 if (child_pid == 0){
			 	cout <<"In Child - " << cmdLine.at(i).at(0) << endl;
			 	if ((i==0) && (cmdLine.size() >  1)) { //if first command and there is at least one pipe
			 		close(fdArray[0][0]); //close reading file descriptor of first pipe
			 		dup2(fdArray[0][1],1); //dup writing pipe to stdout
					close(fdArray[0][1]);
			 		
			 	}
			 	
			 	if (i== (forkNum-1) && (cmdLine.size() >  1) ) { //if last command in pipe
			 		
			 		dup2(fdArray[i-1][0],0);  //duplicate reading pipe to standard input 
			 		close(fdArray[1][0]);
			 		close(fdArray[0][1]);
			 		
			 	}
				
			 	
			 	execvp(cmdLine.at(i).at(0).c_str(), argv);
			    
				//Invalid execs proceed to here 
			    cerr << "Unknown command!\n";
			    fflush(stderr);
			    fflush(stdout);
			    exit(0);
			}
			else if (child_pid < 0){ //some fork error
				cerr <<"Error!\n";
			}
				
			for (int c_size=0;c_size < args;++c_size) { //free argument array for each piped process 
				free(argv[c_size]);
				}
				
			for (unsigned int proc_num=0; proc_num<cmdLine.size();++proc_num) { //wait for all spawned processes in pipe before continuing
			   	//while 
			   	wait4(-1,&child_status,0,NULL);// {}
			   	cout <<".";
			}
						
		}

		for (unsigned int i=0; i<(cmdLine.size()-1); ++i) { //delete each pipe after they have all been accumulated
		close(fdArray[i][0]); //catch all opened pipes
		close(fdArray[i][1]);
		delete fdArray[i];
		}
	
			   
	 return 0;
	}
Topic archived. No new replies allowed.