-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcommand_exec.cc
More file actions
executable file
·172 lines (147 loc) · 4.54 KB
/
command_exec.cc
File metadata and controls
executable file
·172 lines (147 loc) · 4.54 KB
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
#include "command_exec.h"
extern pid_t shell_pgid;
extern struct termios shell_tmodes;
extern int shell_terminal;
extern int shell_is_interactive;
extern Job* first_job;
void exec_command(Job *job)
{
auto c = job->commands.front(); // if c is not external command, then command_list only has one element.
c.check_type();
switch(c.type){
case EXIT :
exit(0);
break;
case CD :
{
std::string dir_name = c.parameters.front(); // cd always has one parameter_len
int cd_status = chdir(dir_name.c_str());
if (cd_status < 0)
perror("cd");
break;
}
case JOBS :
print_job_info();
break;
case FG :
if (!first_job) // no job
fprintf(stderr, "fg: no job can be put into foreground\n");
else if (c.parameter_len() == 0) // no specified job, fg the default one
foreground_continue_job(first_job);
else{
std::string pgid_str = c.parameters.front();
pid_t pgid = atoi(pgid_str.c_str());
Job *job = find_job(pgid);
if (job)
foreground_continue_job(job);
else
fprintf(stderr, "fg: no such job can be put into foreground\n");
}
break;
case BG :
if (!first_job) // no job
fprintf(stderr, "bg: no such job\n");
else if (c.parameter_len() == 0) // no specified job, bg the default one
background_continue_job(first_job);
else{
std::string pgid_str = c.parameters.front();
pid_t pgid = atoi(pgid_str.c_str());
Job *job = find_job(pgid);
if (job)
background_continue_job(job);
else
fprintf(stderr, "bg: no such job\n");
}
break;
case EMPTY: //empty, just skip
break;
default : // execute external commands (the true job)
exec_job(job);
}
}
/* This function is kind of complicated. And can be restructured later. */
void exec_job(Job *job)
{
int number_of_pipes = (job->commands.size() - 1);
int fds[2*number_of_pipes];
job->next = first_job; // insert the job at the head of the list
first_job = job;
// create all pipes for children processes
for (int i = 0; i < number_of_pipes; i++){
if ( pipe(fds + i*2) < 0){
perror("pipe");
exit(EXIT_FAILURE);
}
}
unsigned int i = 0;
for (auto iter = job->commands.begin(); iter != job->commands.end(); iter++, i++){
pid_t pid = fork();
if (pid == 0){
/* child */
if (i != 0){ // not the first command in the list
close(STDIN_FILENO);
dup2(fds[(i-1)*2], STDIN_FILENO);
}
if (i != job->commands.size() -1 ){ // not the last command in the list
close(STDOUT_FILENO);
dup2(fds[i*2+1], STDOUT_FILENO);
}
for(int i = 0; i < 2*number_of_pipes; i++){ // close all pipes in child
close(fds[i]);
}
/* set up the args for exec */
char *args[ 2 + iter->parameter_len()]; // one for command name, one for NULL
args[0] = (char*)iter->name.c_str();
size_t index = 1;
for(auto iter2 = iter->parameters.begin();
iter2!=iter->parameters.end();
iter2++, index++)
{
args[index] = (char*)(iter2->c_str());
}
args[index] = NULL; // the last one should be NULL
if (shell_is_interactive){
pid_t pid = getpid();
if (job->pgid == 0) // set the first process's pid as the group's pgid
job->pgid = pid;
setpgid(pid, job->pgid); // put the process specified by pid into the group specified by pgid
if (!job->is_bg)
tcsetpgrp(shell_terminal, pid);
// the child would inherit the signal control of parent, need to reset it
// to default or it would ignore all the signals
signal (SIGINT, SIG_DFL);
signal (SIGQUIT, SIG_DFL);
signal (SIGTSTP, SIG_DFL);
signal (SIGTTIN, SIG_DFL);
signal (SIGTTOU, SIG_DFL);
signal (SIGCHLD, SIG_DFL);
}
if (execvp(iter->name.c_str(), args) < 0 ){
perror(iter->name.c_str());
exit(EXIT_FAILURE);
}
}
else if (pid < 0){
perror("fork");
}
else{
/* parent */
iter->pid = pid; // set the pid for each forked processes in parent process
if (shell_is_interactive){
if (job->pgid == 0)
job->pgid = pid;
setpgid(pid, job->pgid); // shell also put the process specified by pid into the correct group
}
}
}
for(int i = 0; i < 2*number_of_pipes; i++){ // close all pipes in parent
close(fds[i]);
}
// if the shell is not in foreground, can only wait for job and cannot put it into
// foreground or background since the shell has no control to the terminal.
if (!shell_is_interactive)
wait_for_job(job);
else if (!job->is_bg)
put_job_foreground(job);
// do nothing for background job. We just don't wait this job and continue the parent.
}