-
Notifications
You must be signed in to change notification settings - Fork 0
/
execute.c
155 lines (140 loc) · 4.24 KB
/
execute.c
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
/**
* Author: Samuel Marsh
* Date: 30/10/2015
*/
#include "execute.h"
/**
* Executes the shell script residing at the given file path. Prints an error
* message and exits on failure. Called from a child process.
*
* @param path the path to the shell script
*/
void execute_script(char *path)
{
//set stdin to read from the file
//the shell should no longer be interactive - calling run_mysh() below will
//fix this by re-checking stdin (isatty)
redirect_io_stream(fileno(stdin), path, "r");
//re-run the program... this will reset all internal variables.
exit(run_mysh());
}
/**
* Attempts to execute an external program from a file in a child process.
* If invoking the process fails, the contents of the file are interpreted as
* commands (a shell script).
*
* @param command the command tree, with root node of type N_COMMAND
* @param path the full path to the file
* @param argv the arguments to be passed to the program
* @return the exit status of the program
*/
int execute_external_command(CMDTREE *command, char *path, char **argv)
{
int child_pid;
switch (child_pid = fork())
{
case FORK_FAILURE:
MYSH_PERROR("execute_command");
return EXIT_FAILURE;
case FORK_CHILD:
//check and enable I/O redirection if required
set_redirection(command);
if (access(path, X_OK) == 0)
{
//attempt to invoke the program
execv(path, argv);
//failed to access for execution for whatever reason - try interpreting as script
execute_script(path);
//should never reach here - will have errored and exited before this
//point anyway if something went wrong
fprintf(stderr, "%s: %s: Failed to execute shell script\n", argv0, argv[0]);
exit(EXIT_FAILURE);
}
else
{
//couldn't run, couldn't read
fprintf(stderr, "%s: %s: Permission denied\n", argv0, argv[0]);
exit(EXIT_FAILURE);
}
break;
default:
{
//in the parent process, just wait for the child to finish execution
int exit_status;
waitpid(child_pid, &exit_status, 0);
exit_status = WEXITSTATUS(exit_status);
return exit_status;
}
}
fprintf(stderr, "%s: internal error: should never get here\n", argv0);
exit(EXIT_FAILURE);
}
/**
* Executes a command node, which could represent an internal (cd, time) or
* external (e.g. /bin/ls) command.
* @param t the command tree, with root node of type N_COMMAND
* @return the exit status of the command
*/
int execute_generic_command(CMDTREE *t, int c_argc, char **c_argv)
{
int exit_status;
if (execute_internal_command(t, c_argc, c_argv, &exit_status))
{
return exit_status;
}
//reached here - user didn't request an internal command, so search for an
//external one
char *file_path = locate_file(c_argv[0], PATH);
if (file_path == NULL)
{
fprintf(stderr, "%s: %s: command not found...\n", argv0, c_argv[0]);
return EXIT_FAILURE;
}
int result = execute_external_command(t, file_path, c_argv);
free(file_path);
return result;
}
/**
* The 'main' execution function - traverses and interprets a command tree
* and executes each of the nodes based on their type.
*
* @param t the command tree to execute
* @return the exit status of the command tree after (attempted) execution
*/
int execute_cmdtree(CMDTREE *t)
{
if (t == NULL)
{
fprintf(stderr, "%s: internal error: null command tree\n", argv0);
return (last_exit_status = EXIT_FAILURE);
}
switch (t->type)
{
case N_AND:
last_exit_status = execute_and(t);
break;
case N_BACKGROUND:
last_exit_status = execute_background(t);
break;
case N_OR:
last_exit_status = execute_or(t);
break;
case N_SEMICOLON:
last_exit_status = execute_semicolon(t);
break;
case N_PIPE:
last_exit_status = execute_pipe(t);
break;
case N_SUBSHELL:
last_exit_status = execute_subshell(t);
break;
case N_COMMAND:
last_exit_status = execute_generic_command(t, t->argc, t->argv);
break;
default:
fprintf(stderr, "%s: internal error: unknown node type\n", argv0);
last_exit_status = EXIT_FAILURE;
break;
}
return last_exit_status;
}