2016-04-23 11:03:53 +00:00
|
|
|
/**
|
|
|
|
MIT License
|
|
|
|
|
|
|
|
Copyright (c) 2016 Claudio Maggioni
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
|
|
copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
2016-03-16 20:15:24 +00:00
|
|
|
#include "shell.h"
|
2016-03-18 17:48:16 +00:00
|
|
|
namespace mshconsole {
|
2016-04-23 11:03:53 +00:00
|
|
|
const std::string& Shell::getPs() const{
|
2016-03-18 17:48:16 +00:00
|
|
|
return ps;
|
|
|
|
}
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-04-23 11:03:53 +00:00
|
|
|
void Shell::setPs(const std::string &value){
|
2016-03-18 17:48:16 +00:00
|
|
|
ps = value;
|
|
|
|
}
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-04-23 11:03:53 +00:00
|
|
|
const std::string& Shell::getName() const{
|
2016-03-18 17:48:16 +00:00
|
|
|
return name;
|
|
|
|
}
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-04-23 11:03:53 +00:00
|
|
|
void Shell::setName(const std::string &value){
|
2016-03-18 17:48:16 +00:00
|
|
|
name = value;
|
|
|
|
}
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-04-23 11:03:53 +00:00
|
|
|
Shell::Shell(std::string nam, std::string ps, std::istream &i, std::ostream &o, std::ostream &e,
|
|
|
|
void (*s)(Shell*), void (*pss)(Shell*)) : cmds(this), inputStream(i),
|
|
|
|
outputStream(o), errorStream(e){
|
2016-03-18 17:48:16 +00:00
|
|
|
shellSetup = s;
|
2016-04-23 11:03:53 +00:00
|
|
|
name = nam;
|
2016-03-18 17:48:16 +00:00
|
|
|
this->ps = ps;
|
|
|
|
shellPostSetup = pss;
|
|
|
|
}
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-04-02 14:28:03 +00:00
|
|
|
int Shell::launch(){
|
2016-04-23 11:03:53 +00:00
|
|
|
//Forbid launching shell if it is already launched in other threads.
|
|
|
|
static bool notLoop=false;
|
|
|
|
if(notLoop) return -1;
|
|
|
|
notLoop = true;
|
2016-03-18 17:48:16 +00:00
|
|
|
|
2016-04-23 11:03:53 +00:00
|
|
|
//Launch the shell setup.
|
|
|
|
if(shellSetup!=0) (*shellSetup)(this);
|
2016-03-18 17:48:16 +00:00
|
|
|
|
2016-04-23 11:03:53 +00:00
|
|
|
//Run the shell loop.
|
2016-04-02 14:28:03 +00:00
|
|
|
int exitCode;
|
2016-05-01 14:32:41 +00:00
|
|
|
std::string* line=nullptr;
|
2016-05-21 16:25:20 +00:00
|
|
|
std::vector<std::string> p;
|
2016-04-23 11:03:53 +00:00
|
|
|
|
2016-04-02 14:28:03 +00:00
|
|
|
try{
|
2016-04-23 11:03:53 +00:00
|
|
|
while(1){
|
|
|
|
delete line;
|
|
|
|
outputStream << ps << " "; //Print the prompt.
|
|
|
|
|
|
|
|
//Try reading the line. if SIGINT is caught, print another time the prompt and retry.
|
2016-03-18 17:48:16 +00:00
|
|
|
try{
|
2016-04-23 11:03:53 +00:00
|
|
|
line = readLine();
|
2016-03-18 17:48:16 +00:00
|
|
|
}
|
|
|
|
catch (IsUndoingLineException){
|
2016-04-23 11:03:53 +00:00
|
|
|
outputStream << "\n";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Try line conversion from string to argc and argv. stringToArgcArgv() throws a std::runtime_error
|
|
|
|
//exception if the string has unproper quotes.
|
|
|
|
try{
|
|
|
|
p = splitLine(line);
|
|
|
|
}
|
|
|
|
catch(std::runtime_error e){
|
|
|
|
errorStream << name << ": error in line parsing\n";
|
|
|
|
errorStream << e.what() << "\n";
|
|
|
|
continue;
|
2016-03-18 17:48:16 +00:00
|
|
|
}
|
2016-04-23 11:03:53 +00:00
|
|
|
|
2016-05-21 16:25:20 +00:00
|
|
|
//Execute the pipe.
|
|
|
|
executePipe(p);
|
2016-04-23 11:03:53 +00:00
|
|
|
};
|
2016-04-02 14:28:03 +00:00
|
|
|
} catch(CommandExecutor::ExitException c) {
|
2016-05-01 14:32:41 +00:00
|
|
|
delete line;
|
2016-04-23 11:03:53 +00:00
|
|
|
exitCode=c.getCode();
|
2016-04-02 14:28:03 +00:00
|
|
|
}
|
2016-03-18 17:48:16 +00:00
|
|
|
|
2016-04-23 11:03:53 +00:00
|
|
|
//Launch postSetup().
|
|
|
|
if(shellPostSetup!=0) shellPostSetup(this);
|
|
|
|
|
|
|
|
notLoop = false;
|
2016-04-02 14:28:03 +00:00
|
|
|
return exitCode;
|
2016-03-18 17:48:16 +00:00
|
|
|
}
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-05-21 16:25:20 +00:00
|
|
|
int Shell::executePipe(std::vector<std::string>& argv)
|
|
|
|
{
|
|
|
|
int newPipe[2], oldPipe[2], status;
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
std::vector<std::vector<std::string>> argvs;
|
|
|
|
|
|
|
|
size_t c=0, i=0;
|
|
|
|
for(; i<argv.size(); i++){
|
|
|
|
if(argv[i]=="|"){ //Pipe reached
|
|
|
|
if(c!=i){
|
|
|
|
//Write the command in the pipe std::vector
|
|
|
|
argvs.push_back(std::vector<std::string>(argv.begin()+c, argv.begin()+i));
|
|
|
|
}
|
|
|
|
c=i+1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(c!=i){
|
|
|
|
//Write the last command in the pipe std::vector
|
|
|
|
argvs.push_back(std::vector<std::string>(argv.begin()+c, argv.end()));
|
|
|
|
}
|
|
|
|
if(argvs.size()==1){
|
|
|
|
struct Params p;
|
|
|
|
vectorToArgcArgv(argvs[0], &p.argc, &p.argv);
|
|
|
|
return executeCmd(p,false);
|
|
|
|
}
|
|
|
|
for(size_t i=0; i<argvs.size(); i++){
|
|
|
|
if(cmds.found(std::string(argvs[i][0]),false)){
|
|
|
|
errorStream << name << ": non-thread command " << argv[0] << " can't be launched from a pipe" << std::endl;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for(size_t i=0; i<argvs.size(); i++) /* For each command */
|
|
|
|
{
|
|
|
|
struct Params p;
|
|
|
|
vectorToArgcArgv(argvs[i], &p.argc, &p.argv);
|
|
|
|
|
|
|
|
/* If there still are commands to be executed */
|
|
|
|
if(i < argvs.size()-1)
|
|
|
|
{
|
|
|
|
pipe(newPipe); /* just create a pipe */
|
|
|
|
}
|
|
|
|
|
|
|
|
pid = fork();
|
|
|
|
|
|
|
|
if(pid == 0) /* Child */
|
|
|
|
{
|
|
|
|
/* If there is a previous command */
|
|
|
|
if(i > 0)
|
|
|
|
{
|
|
|
|
close(oldPipe[1]);
|
|
|
|
dup2(oldPipe[0], 0); //redirect stdin to input of the pipe
|
|
|
|
close(oldPipe[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If there still are commands to be executed */
|
|
|
|
if(i < argvs.size()-1)
|
|
|
|
{
|
|
|
|
close(newPipe[0]);
|
|
|
|
dup2(newPipe[1], 1); //redirect stdout to output of the pipe
|
|
|
|
close(newPipe[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
//Execute
|
|
|
|
exit(executeCmd(p,true));
|
|
|
|
}
|
|
|
|
else if (pid < 0) {
|
|
|
|
// Error forking
|
|
|
|
errorStream << name <<": error in process forking.\n";
|
|
|
|
}
|
|
|
|
else /* Father */
|
|
|
|
{
|
|
|
|
/* If there is a previous command */
|
|
|
|
if(i > 0)
|
|
|
|
{
|
|
|
|
close(oldPipe[0]);
|
|
|
|
close(oldPipe[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* do we have a next command? */
|
|
|
|
if(i < argvs.size()-1)
|
|
|
|
{
|
|
|
|
oldPipe[0] = newPipe[0];
|
|
|
|
oldPipe[1] = newPipe[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* wait for last command process? */
|
|
|
|
if(i == argvs.size()-1)
|
|
|
|
{
|
|
|
|
waitpid(pid, &status, 0);
|
|
|
|
}
|
|
|
|
}
|
2016-05-29 14:25:03 +00:00
|
|
|
deleteParams(p);
|
2016-05-21 16:25:20 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-04-23 11:03:53 +00:00
|
|
|
int Shell::launchCmd(const struct Params& p){
|
2016-03-18 17:48:16 +00:00
|
|
|
int status;
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-03-18 17:48:16 +00:00
|
|
|
pid_t pid = fork();
|
|
|
|
if (pid == 0) {
|
|
|
|
//child process
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-03-18 17:48:16 +00:00
|
|
|
//execute threadCommand
|
|
|
|
int a;
|
2016-04-23 11:03:53 +00:00
|
|
|
|
2016-03-18 17:48:16 +00:00
|
|
|
try {
|
2016-04-16 17:21:39 +00:00
|
|
|
a=cmds.launch(p, true);
|
2016-04-23 11:03:53 +00:00
|
|
|
std::exit(EXIT_SUCCESS);
|
2016-03-16 20:15:24 +00:00
|
|
|
}
|
2016-04-23 11:03:53 +00:00
|
|
|
catch (CommandNotFoundException){}
|
|
|
|
|
|
|
|
//execute bash command or program
|
2016-05-01 14:32:41 +00:00
|
|
|
a=execvp(p.argv[0], p.argv);
|
|
|
|
|
|
|
|
if(a == -1) {
|
2016-04-23 11:03:53 +00:00
|
|
|
outputStream << name << ": execution failed. Errno is set to: "<<strerror(errno)<<"\n";
|
|
|
|
std::exit(EXIT_FAILURE);
|
2016-03-16 20:15:24 +00:00
|
|
|
}
|
2016-04-23 11:03:53 +00:00
|
|
|
else std::exit(EXIT_SUCCESS);
|
2016-03-18 17:48:16 +00:00
|
|
|
}
|
|
|
|
else if (pid < 0) {
|
|
|
|
// Error forking
|
2016-04-23 11:03:53 +00:00
|
|
|
errorStream << name <<": error in process forking.\n";
|
2016-03-18 17:48:16 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Parent process
|
|
|
|
do {
|
|
|
|
//wait until child finished
|
|
|
|
waitpid(pid, &status, WUNTRACED);
|
|
|
|
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
|
2016-03-16 20:15:24 +00:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
2016-03-18 17:48:16 +00:00
|
|
|
|
2016-05-21 16:25:20 +00:00
|
|
|
int Shell::executeCmd(const struct Params& p, bool isPipe){
|
2016-04-23 11:03:53 +00:00
|
|
|
if (!p.argc) return 1; //Return if the line is empty.
|
|
|
|
|
2016-05-21 16:25:20 +00:00
|
|
|
if(isPipe){
|
|
|
|
if(cmds.found(p.argv[0],false)){
|
|
|
|
errorStream << name << ": non-thread command " << p.argv[0] << " can't be launched from a pipe" << std::endl;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
try {
|
|
|
|
//Search in non-thread commands.
|
|
|
|
return cmds.launch(p, false);
|
|
|
|
}
|
|
|
|
catch(CommandNotFoundException) {}
|
2016-03-18 17:48:16 +00:00
|
|
|
}
|
2016-04-23 11:03:53 +00:00
|
|
|
//Execute the threadCommand or the executable.
|
|
|
|
return launchCmd(p);
|
2016-03-16 20:15:24 +00:00
|
|
|
}
|
|
|
|
|
2016-03-18 17:48:16 +00:00
|
|
|
inline int Shell::executeCmd(const std::string &args){
|
2016-05-21 16:25:20 +00:00
|
|
|
std::vector<std::string>a =splitLine(&args);
|
|
|
|
return executePipe(a);
|
2016-03-18 17:48:16 +00:00
|
|
|
}
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-04-23 11:03:53 +00:00
|
|
|
void Shell::SIGINTHandler(int,siginfo_t*, void *){
|
|
|
|
SIGINTRaised = true;
|
2016-03-18 17:48:16 +00:00
|
|
|
}
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-03-18 17:48:16 +00:00
|
|
|
void Shell::setShellSetup(void (*s)(Shell *)){
|
|
|
|
shellSetup=s;
|
|
|
|
}
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-04-23 11:03:53 +00:00
|
|
|
void (*Shell::getShellSetup())(Shell*) const{
|
2016-03-18 17:48:16 +00:00
|
|
|
return shellSetup;
|
|
|
|
}
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-03-18 17:48:16 +00:00
|
|
|
void Shell::setShellPostSetup(void (*s)(Shell *)){
|
|
|
|
shellPostSetup=s;
|
|
|
|
}
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-04-23 11:03:53 +00:00
|
|
|
void (*Shell::getShellPostSetup())(Shell*) const{
|
2016-03-18 17:48:16 +00:00
|
|
|
return shellPostSetup;
|
|
|
|
}
|
2016-03-16 20:15:24 +00:00
|
|
|
|
|
|
|
|
2016-04-23 11:03:53 +00:00
|
|
|
struct sigaction* setHandler(unsigned signal, void (*funcptr)(int, siginfo_t*, void*)){
|
|
|
|
struct sigaction* before = new struct sigaction();
|
|
|
|
struct sigaction sa;
|
2016-05-01 14:32:41 +00:00
|
|
|
sigset_t mask;
|
|
|
|
sigemptyset(&mask);
|
|
|
|
sigaddset(&mask, SIGTERM);
|
|
|
|
sa.sa_mask=mask;
|
2016-04-23 11:03:53 +00:00
|
|
|
sa.sa_sigaction = funcptr;
|
2016-05-01 14:32:41 +00:00
|
|
|
sa.sa_flags = SA_RESTART;
|
2016-04-23 11:03:53 +00:00
|
|
|
sigaction(signal, &sa, before);
|
2016-05-01 14:32:41 +00:00
|
|
|
return before;
|
2016-03-18 17:48:16 +00:00
|
|
|
}
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-04-23 11:03:53 +00:00
|
|
|
void restoreHandler(unsigned signal, struct sigaction* before){
|
|
|
|
sigaction(signal, before, NULL);
|
|
|
|
delete before;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string* Shell::readLine(){
|
|
|
|
std::string* buffer = new std::string();
|
|
|
|
struct sigaction* beforeSIGINT = setHandler(SIGINT,SIGINTHandler);
|
|
|
|
getline(inputStream,*buffer); //Get line of input TODO: handle directional keys
|
|
|
|
inputStream.clear(); //Clear flags. This is needed for the detection of SIGINT
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-04-23 11:03:53 +00:00
|
|
|
if(SIGINTRaised){
|
|
|
|
SIGINTRaised=false;
|
2016-03-18 17:48:16 +00:00
|
|
|
throw IsUndoingLineException();
|
|
|
|
}
|
2016-04-23 11:03:53 +00:00
|
|
|
|
|
|
|
restoreHandler(SIGINT, beforeSIGINT);
|
2016-03-18 17:48:16 +00:00
|
|
|
return buffer;
|
2016-03-16 20:15:24 +00:00
|
|
|
}
|
|
|
|
|
2016-05-21 16:25:20 +00:00
|
|
|
std::vector<std::string> Shell::splitLine(const std::string* line){
|
|
|
|
return parseStringToVector(*line);
|
2016-03-18 17:48:16 +00:00
|
|
|
}
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-05-01 14:32:41 +00:00
|
|
|
void Shell::deleteParams(struct Params p){
|
|
|
|
freeString(p.argc,p.argv);
|
|
|
|
}
|
|
|
|
|
2016-04-23 11:03:53 +00:00
|
|
|
bool Shell::SIGINTRaised = false;
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-03-18 17:48:16 +00:00
|
|
|
void Shell::addCmd(Command* cmd, bool isthread){
|
|
|
|
cmds.add(cmd, isthread);
|
|
|
|
}
|
2016-03-16 20:15:24 +00:00
|
|
|
|
2016-03-18 17:48:16 +00:00
|
|
|
size_t Shell::howManyCmds() const{
|
|
|
|
return cmds.howMany();
|
|
|
|
}
|
2016-03-16 20:15:24 +00:00
|
|
|
}
|