Ejemplo n.º 1
0
int
main(int argc, char *argv[])
{
	int quiet = 0;
	char input[BUFSIZ];
	int r = 0;

	// Check for '-q' option: be quiet -- print no prompts
	if (argc > 1 && strcmp(argv[1], "-q") == 0)
		quiet = 1;
	signal(SIGCHLD,signal_handler);
	while (!feof(stdin)) {
		parsestate_t parsestate;
		command_t *cmdlist;
		// Print the prompt
		if (!quiet) {
			printf("prog1$ ");
			fflush(stdout);
		}

		// Read a string, checking for error or EOF
		if (fgets(input, BUFSIZ, stdin) == NULL) {
			if (ferror(stdin))
				// This function prints a description of the
				// error, preceded by 'cs111_fall07: '.
				perror("prog1");
			break;
		}

		// TODO: invoke some function(s) in cmdline.c for parsing the read string.
		parse_init(&parsestate,input);
		cmdlist=command_line_parse(&parsestate,0);

		if (!cmdlist) {
			printf("Syntax error\n");
			continue;
		}

		// print the command list
		if (!quiet) {
			// TODO: invoke some function(s) in cmdline.c for printing out the command for debugging.
			command_print(cmdlist,1);
			// why do we need to do this?
			fflush(stdout);
		}

		// and run it!
		int not_impotant=0;
		waitpid(-1,&not_impotant,WNOHANG);//kill zombies
		if (cmdlist)
			command_line_exec(cmdlist);
		if (cmdlist)
			command_free(cmdlist);

	}

	return 0;
}
Ejemplo n.º 2
0
/* command_exec(cmd, pass_pipefd)
 *
 *   Execute the single command specified in the 'cmd' command structure.
 *
 *   The 'pass_pipefd' argument is used for pipes.
 *   On input, '*pass_pipefd' is the file descriptor used to read the
 *   previous command's output.  That is, it's the read end of the previous
 *   pipe.  It equals STDIN_FILENO if there was no previous pipe.
 *   On output, command_exec should set '*pass_pipefd' to the file descriptor
 *   used for reading from THIS command's pipe.
 *   If this command didn't have a pipe -- that is, if cmd->commandop != PIPE
 *   -- then it should set '*pass_pipefd = STDIN_FILENO'.
 *
 *   Returns the process ID of the forked child, or < 0 if some system call
 *   fails.
 *
 *   You must also handle the internal commands "cd" and "exit".
 *   These are special because they must execute in the shell process, rather
 *   than a child.  (Why?)
 *
 *   However, these special commands still have a status!
 *   For example, "cd DIR" should return status 0 if we successfully change
 *   to the DIR directory, and status 1 otherwise.
 *   Thus, "cd /tmp && echo /tmp exists" should print "/tmp exists" to stdout
 *   iff the /tmp directory exists.
 *   Not only this, but redirections should work too!
 *   For example, "cd /tmp > foo" should create an empty file named 'foo';
 *   and "cd /tmp 2> foo" should print any error messages to 'foo'.
 *
 *   How can you return a status, and do redirections, for a command executed
 *   in the parent shell?
 *   Hint: It is easiest if you fork a child ANYWAY!
 *   You should divide functionality between the parent and the child.
 *   Some functions will be executed in each process.
 */
static pid_t
command_exec(command_t *cmd, int *pass_pipefd)
{
	pid_t pid = -1;		// process ID for child
	int pipefd[2];		// file descriptors for this process's pipe
    qcommand_t *q_com;

	/* EXERCISE: Complete this function!
	 * We've written some of the skeleton for you, but feel free to
	 * change it.
	 */

	// Create a pipe, if this command is the left-hand side of a pipe.
	// Return -1 if the pipe fails.
	if (cmd->controlop == CMD_PIPE) {
		/* Your code here. */
		if (pipe(pipefd) == -1)
			return -1;
	}
    
    if (cmd->argv[0] != NULL && strcmp(cmd->argv[0], "q") == 0) {
        if (MKQ == NULL || strcmp(cmd->argv[1], MKQ->name))
            return -1;
        q_com = qcommand_alloc();
        pipe(q_com->pipe);
    }
    
	pid = fork();
    if (pid == -1) {
        return -1; //or error
    }

	if (pid == 0) {
		int fd;
		dup2(*pass_pipefd, 0);
        
        //used
		
        if (cmd->redirect_filename[0]) {
			fd = open(cmd->redirect_filename[0], O_RDONLY);
			dup2(fd, 0);
			close(fd);
		}

        if (cmd->controlop == CMD_PIPE) {
            dup2(pipefd[1], 1);
			close(pipefd[0]);
        } else if (cmd->redirect_filename[1]) {
            *pass_pipefd = STDIN_FILENO;
			fd = open(cmd->redirect_filename[1], O_TRUNC|O_CREAT|O_WRONLY, 0666);
			dup2(fd, 1);
			close(fd);	
		}
        else {
            *pass_pipefd = STDIN_FILENO;
            dup2(STDOUT_FILENO, 1);
        }

		if (cmd->redirect_filename[2]) {
			fd = open(cmd->redirect_filename[2], O_CREAT|O_WRONLY, 0666);
			dup2(fd, 2);
			close(fd);
		}
		
		if (cmd->subshell) {
            //printf("subshell?\n");
			int exit_status = command_line_exec(cmd->subshell);
			exit(exit_status ? EXIT_FAILURE : EXIT_SUCCESS);
		} else if (strcmp(cmd->argv[0], "cd") == 0) {
			if (cmd->argv[1]) {
				int fd = open(cmd->argv[1], O_RDONLY);
				if (fd != -1)
					close(fd);
				else {
					exit(EXIT_FAILURE);
				}
				exit(EXIT_SUCCESS);
			}
        } else if (strcmp(cmd->argv[0], "q") == 0) {
            //char a;
            //printf("To be Q'd\n");
            read(q_com->pipe[0], NULL, 1);
            execvp(cmd->argv[2], &cmd->argv[2]);
		} else {
			execvp(cmd->argv[0], &cmd->argv[0]);
		}
	} 
    else {
        if (cmd->argv[0]) {
			if (strcmp(cmd->argv[0], "cd") == 0) {
				chdir(cmd->argv[1] ? cmd->argv[1] : getenv("HOME"));
			} else if (strcmp(cmd->argv[0], "exit") == 0) {
				exit(0);
			} else if (strcmp(cmd->argv[0], "q") == 0) {
                q_com->pid = pid;
                q_com->cmd = cmd;//not necessarily, but useful for debugging purposes
                add_command(q_com);
            }
		}
        
        if (*pass_pipefd != STDIN_FILENO) {
            close(*pass_pipefd);
			//close(pipefd[1]);
		}
        if (cmd->controlop == CMD_PIPE) {
            *pass_pipefd = pipefd[0];
			close(pipefd[1]);
        } else
            *pass_pipefd = STDIN_FILENO;
        
		

	}
	
	
	
	// Fork the child and execute the command in that child.
	// You will handle all redirections by manipulating file descriptors.
	//
	// This section is fairly long.  It is probably best to implement this
	// part in stages, checking it after each step.  For instance, first
	// implement just the fork and the execute in the child.  This should
	// allow you to execute simple commands like 'ls'.  Then add support
	// for redirections: commands like 'ls > foo' and 'cat < foo'.  Then
	// add parentheses, then pipes, and finally the internal commands
	// 'cd' and 'exit'.
	//
	// In the child, you should:
	//    1. Set up stdout to point to this command's pipe, if necessary.
	//    2. Set up stdin to point to the PREVIOUS command's pipe (that
	//       is, *pass_pipefd), if appropriate.
	//    3. Close some file descriptors.  Hint: Consider the read end
	//       of this process's pipe.
	//    4. Set up redirections.
	//       Hint: For output redirections (stdout and stderr), the 'mode'
	//       argument of open() should be set to 0666.
	//    5. Execute the command.
	//       There are some special cases:
	//       a. Parentheses.  Execute cmd->subshell.  (How?)
	//       b. A null command (no subshell, no arguments).
	//          Exit with status 0.
	//       c. "exit".
	//       d. "cd".
	//
	// In the parent, you should:
	//    1. Close some file descriptors.  Hint: Consider the write end
	//       of this command's pipe, and one other fd as well.
	//    2. Handle the special "exit" and "cd" commands.
	//    3. Set *pass_pipefd as appropriate.
	//
	// "cd" error note:
	// 	- Upon syntax errors: Display the message
	//	  "cd: Syntax error on bad number of arguments"
	// 	- Upon system call errors: Call perror("cd")
	//
	// "cd" Hints:
	//    For the "cd" command, you should change directories AFTER
	//    the fork(), not before it.  Why?
	//    Design some tests with 'bash' that will tell you the answer.
	//    For example, try "cd /tmp ; cd $HOME > foo".  In which directory
	//    does foo appear, /tmp or $HOME?  If you chdir() BEFORE the fork,
	//    in which directory would foo appear, /tmp or $HOME?
	//
	//    EXTRA CREDIT: Our "cd" solution changes the
	//    directory both in the parent process and in the child process.
	//    This introduces a potential race condition.
	//    Explain what that race condition is, and fix it.
	//    Hint: Investigate fchdir().

	/* Your code here. */

	// return the child process ID
	return pid;
}
Ejemplo n.º 3
0
/* command_exec(cmd, pass_pipefd)
 *
 *   Execute the single command specified in the 'cmd' command structure.
 *
 *   The 'pass_pipefd' argument is used for pipes.
 *   On input, '*pass_pipefd' is the file descriptor used to read the
 *   previous command's output.  That is, it's the read end of the previous
 *   pipe.  It equals STDIN_FILENO if there was no previous pipe.
 *   On output, command_exec should set '*pass_pipefd' to the file descriptor
 *   used for reading from THIS command's pipe.
 *   If this command didn't have a pipe -- that is, if cmd->commandop != PIPE
 *   -- then it should set '*pass_pipefd = STDIN_FILENO'.
 *
 *   Returns the process ID of the forked child, or < 0 if some system call
 *   fails.
 *
 *   You must also handle the internal commands "cd" and "exit".
 *   These are special because they must execute in the shell process, rather
 *   than a child.  (Why?)
 *
 *   However, these special commands still have a status!
 *   For example, "cd DIR" should return status 0 if we successfully change
 *   to the DIR directory, and status 1 otherwise.
 *   Thus, "cd /tmp && echo /tmp exists" should print "/tmp exists" to stdout
 *   iff the /tmp directory exists.
 *   Not only this, but redirections should work too!
 *   For example, "cd /tmp > foo" should create an empty file named 'foo';
 *   and "cd /tmp 2> foo" should print any error messages to 'foo'.
 *
 *   How can you return a status, and do redirections, for a command executed
 *   in the parent shell?
 *   Hint: It is easiest if you fork a child ANYWAY!
 *   You should divide functionality between the parent and the child.
 *   Some functions will be executed in each process.
 */
static pid_t
command_exec(command_t *cmd, int *pass_pipefd)
{
	pid_t pid = -1;		// process ID for child
	int pipefd[2];		// file descriptors for this process's pipe
	int status;
	/* EXERCISE: Complete this function!
	 * We've written some of the skeleton for you, but feel free to
	 * change it.
	 */

	// Create a pipe, if this command is the left-hand side of a pipe.
	// Return -1 if the pipe fails.
		
		/* Your code here. */
	   
	 if(cmd->subshell && cmd->argv[0] && !(strcmp(cmd->argv[0],"") == 0)){
	 	goto error;
	 }

	if (cmd->controlop == CMD_PIPE) {	
		if(pipe(pipefd)){
			printf("Error creating pipe\n");
			return -1;
		}
	}


	// Fork the child and execute the command in that child.
	// You will handle all redirections by manipulating file descriptors.
	//
	// This section is fairly long.  It is probably best to implement this
	// part in stages, checking it after each step.  For instance, first
	// implement just the fork and the execute in the child.  This should
	// allow you to execute simple commands like 'ls'.  Then add support
	// for redirections: commands like 'ls > foo' and 'cat < foo'.  Then
	// add parentheses, then pipes, and finally the internal commands
	// 'cd' and 'exit'.
	//
	// In the child, you should:
	//    1. Set up stdout to point to this command's pipe, if necessary.
	//    2. Set up stdin to point to the PREVIOUS command's pipe (that
	//       is, *pass_pipefd), if appropriate.
	//    3. Close some file descriptors.  Hint: Consider the read end
	//       of this process's pipe.
	//    4. Set up redirections.
	//       Hint: For output redirections (stdout and stderr), the 'mode'
	//       argument of open() should be set to 0666.
	//    5. Execute the command.
	//       There are some special cases:
	//       a. Parentheses.  Execute cmd->subshell.  (How?)
	//       b. A null command (no subshell, no arguments).
	//          Exit with status 0.
	//       c. "exit".
	//       d. "cd".
	//
	// In the parent, you should:
	//    1. Close some file descriptors.  Hint: Consider the write end
	//       of this command's pipe, and one other fd as well.
	//    2. Handle the special "exit" and "cd" commands.
	//    3. Set *pass_pipefd as appropriate.
	//
	// "cd" error note:
	// 	- Upon syntax errors: Display the message
	//	  "cd: Syntax error on bad number of arguments"
	// 	- Upon system call errors: Call perror("cd")
	//
	// "cd" Hints:
	//    For the "cd" command, you should change directories AFTER
	//    the fork(), not before it.  Why?
	//    Design some tests with 'bash' that will tell you the answer.
	//    For example, try "cd /tmp ; cd $HOME > foo".  In which directory
	//    does foo appear, /tmp or $HOME?  If you chdir() BEFORE the fork,
	//    in which directory would foo appear, /tmp or $HOME?
	//

	if(!cmd->subshell && strcmp(cmd->argv[0],"exit") == 0){
		// execute exit
		exit(0);
	}

	if(!cmd->subshell && (strcmp(cmd->argv[0],"") == 0))
		return -1;
	
	//printf("Spawning new process\n");

	// Handle reaping of zombie processes automatically
	// struct sigaction sigact;
	
	// sigact.sa_handler = SIG_IGN;

	// sigemptyset(&sigact.sa_mask);

	// sigact.sa_flags = 0;

	// if (sigaction(SIGCHLD, &sigact, 0) == -1) {
	// 	printf("Triggered\n");
	// 	perror(0);
	//   	exit(1);
	// }

	// if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
	//   perror(0);
	//   exit(1);
	// }	

	/* Your code here. */
	pid = fork();
	
	// parent process
	if (pid > 0){
		if(cmd->controlop == CMD_PIPE) {
			*pass_pipefd = pipefd[0];
			close(pipefd[1]);
		}
		if(cmd->argv[0]){
			if(strcmp(cmd->argv[0],"cd") == 0){
				if(!cmd->argv[1]){
					if(chdir(getenv("HOME")) != 0){
						int lsts;
						waitpid(pid, &lsts, WNOHANG);
						perror("cd");
						return -1;
					}
					return 0;
				}
				else if ((cmd->argv[1])[0] == '~'){
					cmd->argv[1] = strcat(getenv("HOME"),(cmd->argv[1])+1);	
				}
				if(chdir(cmd->argv[1]) != 0){
					int lsts;
					waitpid(pid, &lsts, WNOHANG);
					perror("cd");
					return -1;
				}
			}
			else if(strcmp(cmd->argv[0],"jobs") == 0){
				if(jobindex) {
					printf("PID    PPID   STATUS\n");
					int removelist[100];
					int j, k = 0; 
					for(j = 0 ; j<jobindex;j++){
						char *sts;
						int stspid;
						// printf("Current Status: %d\n",waitpid(joblist[j]->pid,&stspid,WNOHANG));
						joblist[j]->status = waitpid(joblist[j]->pid,&stspid,WNOHANG) ? DONE : RUNNING ;
						switch(joblist[j]->status){
							default: printf("Error Setting value\n");
									break;
							case RUNNING: sts = "RUNNING";
											break;
							case DONE: sts = "DONE";
											joblist[j]->printstatus = 1;
											removelist[k++] = j;
											break;
							case KILLED: sts = "KILLED";
											break;
						}
						// print the status of background jobs
						printf("%d  %d  %s\n",joblist[j]->pid,joblist[j]->ppid,sts);
					}
					int l, m = 0;
					for(l = 0 ;l<k;l++){
						job_free(removelist[l]-m++);
					}
				}
			}
		} 
		return pid;
	}
	// child process
	else if (pid == 0){
		FILE *fp;

		dup2(*pass_pipefd, STDIN_FILENO);

		if(cmd->redirect_filename[0]){
			fp = fopen(cmd->redirect_filename[0], "r");
			dup2(fileno(fp),0);
		}
		if(cmd->redirect_filename[1]){
			fp =fopen(cmd->redirect_filename[1], "w");
			dup2(fileno(fp), 1);
		}
		if(cmd->redirect_filename[2]){
			//close(2);
			fp = fopen(cmd->redirect_filename[2], "w");
			dup2(fileno(fp), 2);
		}

		if(cmd->controlop == CMD_PIPE) {

				dup2(pipefd[1],STDOUT_FILENO);
				close(pipefd[1]);
		}

		if(cmd->subshell){
			//printf("Entered inside: %s\n",(cmd->subshell)->argv[0]);

				status = command_line_exec(cmd->subshell);
				//printf("Status: %d\n",status);
				// Return teh status of the last executed command
				if(status)
					kill(getpid(),SIGKILL);
				//printf("Reached exit error\n");
				exit(status);
		}
		

		// printf("In child process [0]:%s, %s\n",cmd->argv[0], cmd->redirect_filename[1]);
		if(strcmp(cmd->argv[0],"cd") == 0){
			// Exit
			exit(0);
		}
		if(strcmp(cmd->argv[0],"jobs") == 0){
			// Exit
			exit(0);
		}

		//handle wildcard entries
		int z,m=0;
		char *local_cmd[512];
		local_cmd[m++] = cmd->argv[0];
		for(z = 1; z < 512 ;z++){
			//printf("Reached %d\n",z );
			local_cmd[m++] = cmd->argv[z]?cmd->argv[z]:0;
			//printf("Reached %d\n",z );
			if(!cmd->argv[z])
				break;
			// if((strcmp(local_cmd[0],"ls") == 0) && (strstr(cmd->argv[z],"*") != NULL)) {
			if(strstr(cmd->argv[z],"*") != NULL) {	
				m--;
				const char *pattern = cmd->argv[z];
				glob_t results;
				// glob function : glob(const char *pattern, int flags, int (*errfunc) (const char *epath, int eerrno), glob_t *pglob);
	       		if(glob(pattern,0,glob_error, &results) != 0)
	       			exit(1);
	       		unsigned int k;
	       		for (k = 0; k < results.gl_pathc; k++){
	       			//printf("%s\n", results.gl_pathv[k]);
	       			local_cmd[m++] = results.gl_pathv[k];
	       		}
	       		break;
	       		globfree(& results);
			}
		}
		local_cmd[m] = 0;
		
		execvp(local_cmd[0],local_cmd);

		// execvp(cmd->argv[0],cmd->argv);

		return -1;
	}

	error:
		printf("Error\n");
		return -1;
}