Merge remote-tracking branch 'origin/master'
[minishell-2.git] / Sources / opsys.cpp
blob9307fb0916c7fd730bd2f01b3ed697641c03fd6f
1 /*
2 * miniSHELL-2
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
23 * SOFTWARE.
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
33 using std::cin;
34 using std::cout;
35 using std::cerr;
37 OpSys OS; // Global OpSys variable. Should be accessible to all modules.
38 bool exit_program = false;
40 void signal_handler(int s)
42 if (s==SIGINT)
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.
48 if (s==SIGHUP)
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";
76 return;
78 this->cwd = get_cwd();
79 cwd_changed = true;
80 return;
83 string cmd;
85 for(unsigned short int i = 1; i < command.size(); i++) // TODO: unneeded?
86 cmd += command[i];
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";
93 else
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);
108 out.shrink_to_fit();
109 return out;
112 short OpSys::piped_command(vector<string> tokens, int pipe_count)
114 if(is_verbose)
116 cerr << "Running piped_command with ";
117 cerr << "tokens: [";
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++)
130 if (n_commands > 10)
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";
134 return -1;
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.
142 break;
144 else
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++]);
151 // Creating pipe
152 if(i!=n_commands-1)
154 if(pipe(file_descriptor_array[i])<0)
156 cerr << "Error on pipe initialization.\n";
157 return -1;
160 // Forking
161 pid_t pid = fork();
162 if(pid==0)
163 { // Child process
164 if(i!=n_commands-1)
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]);
170 if(i!=0)
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]);
176 if(is_verbose)
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";
185 return -1;
187 else {
188 if(i!=0)
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.
197 return 1;
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)
205 string command_str;
206 for(string command_part : command)
207 command_str += command_part + ' ';
209 printf("%-20hu\t%s\n", (unsigned short) i++, command_str.c_str());
211 fflush(stdout);
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);
221 return cwd_changed;
222 } else if (!tokens[0].compare("pwd"))
224 cout << OS.cwd << '\n';
225 return 1;
226 } else if (!tokens[0].compare("help"))
228 cout << "Insert help text here.\n";
229 return 1;
230 } else if (!tokens[0].compare("history"))
232 OS.show_history();
233 return 1;
235 else if (!tokens[0].compare("quit"))
237 std::cerr << "Exiting.\n";
238 exit_program = true;
239 return 1;
243 /* Running an external command */
244 int status;
245 if(is_verbose)
247 cerr << "execvp(\"" << tokens[0] << "\",\"";
248 for(unsigned int i = 0; i < tokens.size()-1; i++)
249 cerr << tokens[i] << ' ';
250 cerr << tokens.back();
251 cerr << "\");\n";
254 pid_t pid = fork();
255 if (pid == 0)
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";
261 return -1;
263 else if (pid < 0)
265 cerr << "Couldn't fork a process.";
266 return -1;
269 // Block the parent process until the child process finishes.
270 waitpid(-1, &status, WUNTRACED);
271 return 1;
274 OpSys::OpSys()
276 cwd = get_cwd();
277 if(cwd.empty())
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
281 struct passwd pwent;
282 struct passwd *pwent_ptr;
283 char buffer[1024];
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");
290 if(home.empty())
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;