3 * https://github.com/vrmiguel/minishell-2
5 * Copyright (c) 2020 Vinícius R. Miguel <vinicius.miguel at unifesp.br>
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 #include "Headers/opsys.h"
28 #include <sys/param.h> // MAXPATHLEN
29 #include <sys/wait.h> // waitpid
30 #include <unistd.h> // getcwd, fork, chdir, geuid, gethostname, execvp
31 #include <pwd.h> // struct passwd, getpwuid_r
37 OpSys OS
; // Global OpSys variable. Should be accessible to all modules.
38 bool exit_program
= false;
40 void signal_handler(int s
)
44 cout
<< "\nSIGINT (Ctrl+C) received. Code " << s
<< ". Exiting.\n";
45 exit_program
= true; // We'll do this instead of an exit(0) in order to allow for cleanup.
50 cout
<< "\nSIGHUP received. Code " << s
<< ". Exiting.\n";
51 exit_program
= true; // We'll do this instead of an exit(0) in order to allow for cleanup.
55 SignalHandler::SignalHandler()
57 signal_action
.sa_handler
= signal_handler
;
58 sigemptyset(&signal_action
.sa_mask
);
59 signal_action
.sa_flags
= 0;
60 sigaction(SIGINT
, &signal_action
, NULL
);
63 string
OpSys::get_cwd()
65 char temp
[MAXPATHLEN
];
66 return getcwd(temp
, sizeof(temp
)) ? std::string( temp
) : std::string("");
69 void OpSys::change_dir(vector
<string
> command
)
71 if (command
.size() == 1 || !command
[1].compare("~") || !command
[1].compare("$HOME"))
72 { // User wants to cd into $HOME/~.
73 if(chdir(home_dir
.c_str()))
75 cerr
<< "minish2: cd: " << command
[1] << ": Arquivo ou diretório não encontrado.\n";
78 this->cwd
= get_cwd();
85 for(unsigned short int i
= 1; i
< command
.size(); i
++) // TODO: unneeded?
88 if (chdir(cmd
.c_str()))
90 // Couldn't get to change dirs
91 cerr
<< "minish2: cd: " << cmd
<< ": Arquivo ou diretório não encontrado.\n";
95 // chdir worked. and tell Prompt to act accordingly.
96 this->cwd
= get_cwd(); // Update the OpSys OS's current cwd
97 cwd_changed
= true; // Signals a cwd change to Prompt.
101 vector
<const char*> make_argv(vector
<string
>const& in
)
103 vector
<const char*> out
;
104 out
.reserve(in
.size() + 1);
105 for (const auto& s
: in
)
106 out
.push_back(s
.data());
107 out
.push_back(nullptr);
112 short OpSys::piped_command(vector
<string
> tokens
, int pipe_count
)
116 cerr
<< "Running piped_command with ";
118 for(unsigned short int k
= 0; k
< tokens
.size()-1; k
++)
119 cerr
<< "\"" << tokens
[k
] << "\", ";
120 cerr
<< "\"" << tokens
.back() << "\"]\n";
123 int n_commands
= pipe_count
+ 1;
124 int file_descriptor_array
[10][2];
125 unsigned short i
, j
=0;
126 string pipe_str
= "|";
128 for(i
=0; i
< n_commands
; i
++)
131 { /* This is an arbitrary decision. You can allow for more pipes by
132 altering this `if` and the size of file_descriptor_array. */
133 cerr
<< "More pipes than supported.\n";
136 vector
<string
> aux_cmd
;
137 for(;j
<tokens
.size();)
139 if (!tokens
[j
].compare(pipe_str
))
140 { // We've hit a pipe character.
141 j
++; // Skip a position for the next iteration.
145 { /* Current token is not a pipe character
146 * so let's add it to our auxiliary command vector */
147 aux_cmd
.push_back(tokens
[j
++]);
154 if(pipe(file_descriptor_array
[i
])<0)
156 cerr
<< "Error on pipe initialization.\n";
166 dup2(file_descriptor_array
[i
][WRITE_END
],STDOUT_FILENO
);
167 close(file_descriptor_array
[i
][READ_END
]);
168 close(file_descriptor_array
[i
][WRITE_END
]);
172 dup2(file_descriptor_array
[i
-1][READ_END
],STDIN_FILENO
);
173 close(file_descriptor_array
[i
-1][WRITE_END
]);
174 close(file_descriptor_array
[i
-1][READ_END
]);
178 cerr
<< "piped_command is going to execvp aux_cmd: [";
179 for(unsigned short int k
= 0; k
< aux_cmd
.size()-1; k
++)
180 cerr
<< "\"" << aux_cmd
[k
] << "\", ";
181 cerr
<< "\"" << aux_cmd
.back() << "\"]\n";
183 execvp(aux_cmd
[0].c_str(), const_cast<char* const *>(make_argv(aux_cmd
).data())); // TODO: use OS.simple_command here
184 cerr
<< aux_cmd
[0] << ": Command not found.\n";
189 { // Close the file descriptors previously used
190 close(file_descriptor_array
[i
- 1][READ_END
]);
191 close(file_descriptor_array
[i
- 1][WRITE_END
]);
195 for(i
=0; i
<n_commands
; i
++)
196 wait(NULL
); // Wait for all commands to finish.
200 void OpSys::show_history() // TODO: save (and read) history on a file? use Bourne Again's history??
202 unsigned short i
= 0;
203 for(vector
<string
> command
: history
)
206 for(string command_part
: command
)
207 command_str
+= command_part
+ ' ';
209 printf("%-20hu\t%s\n", (unsigned short) i
++, command_str
.c_str());
214 short OpSys::simple_command(vector
<string
> tokens
)
217 /* Checking for internal minishell functions */
218 if(!tokens
[0].compare("cd"))
220 OS
.change_dir(tokens
);
222 } else if (!tokens
[0].compare("pwd"))
224 cout
<< OS
.cwd
<< '\n';
226 } else if (!tokens
[0].compare("help"))
228 cout
<< "Insert help text here.\n";
230 } else if (!tokens
[0].compare("history"))
235 else if (!tokens
[0].compare("quit"))
237 std::cerr
<< "Exiting.\n";
243 /* Running an external command */
247 cerr
<< "execvp(\"" << tokens
[0] << "\",\"";
248 for(unsigned int i
= 0; i
< tokens
.size()-1; i
++)
249 cerr
<< tokens
[i
] << ' ';
250 cerr
<< tokens
.back();
257 // Child process is going to execvp the given command
258 execvp(tokens
[0].c_str(), const_cast<char* const *>(make_argv(tokens
).data()));
259 // The following lines will only run if execvp doesn't work.
260 cerr
<< tokens
[0] << ": command not found.\n";
265 cerr
<< "Couldn't fork a process.";
269 // Block the parent process until the child process finishes.
270 waitpid(-1, &status
, WUNTRACED
);
278 cerr
<< RED_ANSI
<< "Could not get the current working directory.\n" << RESET_ANSI
;
280 uid_t uid
= geteuid(); // Gets the effective ID of the user that started miniSHELL
282 struct passwd
*pwent_ptr
;
285 // Looks for the UDI on the password databank and saves the result on pwent
286 getpwuid_r(uid
, &pwent
, buffer
, sizeof buffer
, &pwent_ptr
);
287 username
= pwent
.pw_name
; // Saves username
289 string home
= getenv("HOME");
291 cerr
<< "Couldn't obtain $HOME.\n";
293 this->home_dir
= home
;
295 char hostname_buffer
[64];
296 gethostname(hostname_buffer
, 64);
297 this->hostname
= hostname_buffer
;