Example #1
0
int main()
{
	comm_channel comm[MAX_TAB];	//Router <-> URL-RENDERING process communication channel array
	//Controller <-> Router communication channel is now comm[0]
	
	pid_t contPid, tabspid[MAX_TAB]; //for forking the CONTROLLER and URL_RENDERING processes
	
	int controllerDead = 1;
	int r,n;				//used for checking if the read returns anything useful
	int tab_count = 0;  //total # of tabs
	int tab_index = 0;  //current available index

	//pipes for bi-directions communication with the CONTROLLER process
	pipe(comm[0].parent_to_child_fd);
	pipe(comm[0].child_to_parent_fd);
	
	//make these pipes non-blocking
	int flags;
	flags = fcntl(comm[0].parent_to_child_fd[0], F_GETFL, 0);
	fcntl(comm[0].parent_to_child_fd[0] , F_SETFL, flags | O_NONBLOCK);
	flags = fcntl(comm[0].child_to_parent_fd[0], F_GETFL, 0);
	fcntl(comm[0].child_to_parent_fd[0] , F_SETFL, flags | O_NONBLOCK);
	
	//fork for the CONTROLLER
	contPid = fork();
	controllerDead = 0;
	
	if(contPid == 0)
	{

		run_control(comm[0]);

		//uses callback functions to respond to reodr.quests from the ROUTER
		process_all_gtk_events();

		//exits with the return status of 0 for success when the user closed the CONTROLLER window
		exit(0);
	}
	
	tab_count++;
	tab_index++; //tab 0 is the controller

	while(!controllerDead) // while the controller is still running
	{
		child_req_to_parent request;
		
		//do a non blocking read to check for messages from the children processes
		for(n = 0; n < tab_index; n++)
		{
			r = read(comm[n].child_to_parent_fd[0], &request, sizeof(child_req_to_parent));
			
			if(r == -1)
			{
				//just keep going, ignore the error message
			}
			else		//when the read returns some data
			{
		
				if(request.type == CREATE_TAB) // Create-tab req read
				{
					//creates non blocking pipes for Router <-> URL-RENDERING process communication
					if(tab_index >= MAX_TAB)
					{
						perror("Too many tabs");
						break;
					}
					// Set the pipe
					pipe(comm[tab_index].parent_to_child_fd);
					pipe(comm[tab_index].child_to_parent_fd);
			
					// Set flags for non-blocking pipe reads
					flags = fcntl(comm[tab_index].parent_to_child_fd[0], F_GETFL, 0);
					fcntl(comm[tab_index].parent_to_child_fd[0] , F_SETFL, flags | O_NONBLOCK);
					flags = fcntl(comm[tab_index].child_to_parent_fd[0], F_GETFL, 0);
					fcntl(comm[tab_index].child_to_parent_fd[0] , F_SETFL, flags | O_NONBLOCK);
				

					//the ROUTER forks() a URL_RENDERING process (this happens when the user clicks the 'new tab' button in the controller window)
					tabspid[tab_index] = fork();
					if(tabspid[tab_index] == -1)
					{
						perror("Fork failed :(");
						//fork failed
					}
					if(tabspid[tab_index] == 0) // Execute the browser
					{
						run_url_browser(tab_index, comm[tab_index]);
						exit(0);
					}
					// Increment the current tab and the max count
					tab_index++;
					tab_count++;
				}
				else if(request.type == NEW_URI_ENTERED) // URL rendering
				{
					int uri_index = request.req.uri_req.render_in_tab;
					
					// Request declarations
					child_req_to_parent new_uri_child;
					child_request c_req;
					new_uri_req new_uri;
					// Request field assignment
					new_uri.render_in_tab = uri_index;
					strcpy(new_uri.uri, request.req.uri_req.uri);
					c_req.uri_req = new_uri;
					new_uri_child.req = c_req;
					new_uri_child.type = NEW_URI_ENTERED;
				
					if(uri_index >= tab_index || uri_index < 0) //Error handling (is tab alive?)
					{
						perror("No such tab!");
					}
					else
					{
						write(comm[uri_index].parent_to_child_fd[1], &new_uri_child, sizeof(child_req_to_parent)); // Write request to URL proc
					}
				}
				else if(request.type == TAB_KILLED)
				{

					//ROUTER closes the file descriptors of the correspoding pipe that was between the ROUTER and the tab that is now closed
					int killed_index = request.req.killed_req.tab_index;

					if( killed_index == 0) // Controller is killed
					{
						// Request declaration
						child_req_to_parent new_req;
						tab_killed_req killed_req;
						child_request child_req;
						// Request field assignment
						child_req.killed_req = killed_req;
						new_req.type = TAB_KILLED;
						new_req.req = child_req;
						
						int j;
						for(j = 0;j < tab_index; j++ ) // Handle children
						{
							new_req.req.killed_req.tab_index = j;
							write(comm[j].parent_to_child_fd[1], &new_req, sizeof(child_req_to_parent)); // Send tab-kill req to all children
							kill(tabspid[j], SIGKILL); //Euthanize children
						}
						controllerDead = 1;
					}else{
						//
					}
					
					//Close the pipe & decrement tab count
					close(comm[killed_index].child_to_parent_fd[0]);
					close(comm[killed_index].parent_to_child_fd[0]);
					close(comm[killed_index].child_to_parent_fd[1]);
					close(comm[killed_index].parent_to_child_fd[1]);
					
					kill(tabspid[killed_index], SIGKILL);
					tab_count--;
					
					
					if( tab_count == 0 )
					{
						return 0;
					}
				}
			}
		}
		//use usleep between reads so we dont slow down the CPU
		usleep(10000); 

	}
	//ROUTER exits and returns 0 for success (when CONTROLLER and all URL_REDERING processes are finished
	kill(0, SIGKILL);
	exit(0);
}
/*
* Name:                 main
* Input arguments:      none
* Output arguments:     none
* Function:             This function creates the router and handles all child processes,
* 						terminating when the controller is killed by the user.
*/
int main()
{
	pid_t childpid;
	int i,j,k,l,newTabIndex,tailIndex = 0;
	size_t bytesWritten,bytesRead;
	comm = (comm_channel*)calloc (MAX_TAB, sizeof(comm_channel));

	// Create pipes for controller and set to nonblocking read
	if(createPipesAndSetToNonBlock(0) < 0){
		fprintf(stderr, "main@268: Failed to create pipes for controller\n");
		exit(EXIT_FAILURE);
	}
	// Fork to create controller
	if((childpid = fork()) == 0){
		run_control();
	}else{
		
		tailIndex++;		
		child_req_to_parent request;
	
		while(1){
			// Router polls for requests from children (controller + url browsers)
			for(i = 0; i < tailIndex; i++){
				// only read from active processes (indicating pipe is open)
				if(!comm[i].isOpen)	
					continue;
				usleep(1000);

				// Uses non-blocking read 
				bytesRead = read(comm[i].child_to_parent_fd[0], &request, 
					sizeof(child_req_to_parent));

				// continue working with other tabs even if one of them had crashed 
				if( bytesRead == -1 && errno != EAGAIN){
					perror("main@293: Router fails to read tab's request");
					continue;				
				}

				// Router reads a request, act accordingly
				if( bytesRead!= -1){
					switch(request.type){
						case CREATE_TAB:
							// set the new tab index to an appropriate number
							newTabIndex = 0;
							for( l = 1; l < tailIndex; l++){
								if(comm[l].isOpen == false){
									newTabIndex = l;
									break;
								}
							}
							// didn't find an unused slot
							if(!newTabIndex){	
								if(tailIndex <= MAX_TAB)
									newTabIndex = tailIndex++;
								else{
									fprintf(stderr, "main@315: Reached maximum number of tabs\n");
									break;
								}
							}
							// set up pipes for the new tab
							if(createPipesAndSetToNonBlock(newTabIndex) < 0){
								fprintf(stderr, "main@321: Failed to create pipes for new tab\n");
								break;
							}
							// fork to create new tab
							if((childpid = fork()) == 0){
								run_url_browser(newTabIndex);
							}
							break;

						case NEW_URI_ENTERED:
							if(!request.req.uri_req.render_in_tab){
								fprintf(stderr, "main@331: Tab index unspecified\n");
								break;							
							}
							// No url rendering browser existd
							if(tailIndex <= 1){
								fprintf(stderr, "main@336: Create an url window to render the page\n");
								break;
							}
							if(!comm[request.req.uri_req.render_in_tab].isOpen){
								fprintf(stderr, "main@340: The specified tab isn't open\n");
								break;
							}
							if((bytesWritten = write(comm[request.req.uri_req.render_in_tab].parent_to_child_fd[1], 
								&request, sizeof(child_req_to_parent))) == -1){
								perror("main@344: Failed to write url");
							}
							break;

						case TAB_KILLED: //Close file descriptors of corresponding tab's pipes.
							j = request.req.killed_req.tab_index;
							if (j > 0){	// close relevant pipes, kill the process
								if(comm[j].isOpen){
									if(kill_process(j) < 0){
										fprintf(stderr, "main@354: Failed to send kill process request\n");
										fprintf(stderr, "main@354: Cannot kill tab %d process\n",j);
										break;
									}
									if(closePipes(j) < 0){
										fprintf(stderr, "main@359: Tab %d process is killed successfully\n",j);
										fprintf(stderr, "main@359: But failed to close its pipes\n");
										fprintf(stderr, "main@359: OS will close these pipes on program exit\n");
										break;
									}	
									if(j == tailIndex - 1)
										tailIndex--;

								}else{
									fprintf(stderr, "main@368: Tab %d is invalid process to kill\n",j);
									break;
								}
							}else{	
								// Killing controller -> close all tabs  				
								for (k = tailIndex - 1; k > 0; k--){
									if(!comm[k].isOpen)
										continue;
									if(kill_process(k) < 0){
										fprintf(stderr, "main@377: Failed to send kill process request\n");
										fprintf(stderr, "main@377: Cannot kill tab %d process\n",k);
										break;
									}
									if(closePipes(k) < 0){
										fprintf(stderr, "main@382: Tab %d process is killed successfully\n",k);
										fprintf(stderr, "main@382: But failed to close its pipes\n");
										fprintf(stderr, "main@382: OS will close these pipes on program exit\n");
										break; 					
									}	
								}
								closePipes(0);
								exit(EXIT_SUCCESS);
							}	
							break;
						default:
							fprintf(stderr, "main@393: Controller received invalid request\n");
							break;
					}	
				}
			}
		}
	}
	return 0 ;
}