224 lines
6.6 KiB
C++
224 lines
6.6 KiB
C++
/**
|
|
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.
|
|
*/
|
|
|
|
#include "shell.h"
|
|
namespace mshconsole {
|
|
const std::string& Shell::getPs() const{
|
|
return ps;
|
|
}
|
|
|
|
void Shell::setPs(const std::string &value){
|
|
ps = value;
|
|
}
|
|
|
|
const std::string& Shell::getName() const{
|
|
return name;
|
|
}
|
|
|
|
void Shell::setName(const std::string &value){
|
|
name = value;
|
|
}
|
|
|
|
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){
|
|
shellSetup = s;
|
|
name = nam;
|
|
this->ps = ps;
|
|
shellPostSetup = pss;
|
|
}
|
|
|
|
int Shell::launch(){
|
|
//Forbid launching shell if it is already launched in other threads.
|
|
static bool notLoop=false;
|
|
if(notLoop) return -1;
|
|
notLoop = true;
|
|
|
|
//Launch the shell setup.
|
|
if(shellSetup!=0) (*shellSetup)(this);
|
|
|
|
//Run the shell loop.
|
|
int exitCode;
|
|
|
|
try{
|
|
std::string* line=nullptr;
|
|
struct Params p;
|
|
|
|
while(1){
|
|
delete line;
|
|
outputStream << ps << " "; //Print the prompt.
|
|
|
|
//Try reading the line. if SIGINT is caught, print another time the prompt and retry.
|
|
try{
|
|
line = readLine();
|
|
}
|
|
catch (IsUndoingLineException){
|
|
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;
|
|
}
|
|
|
|
//Execute the command.
|
|
executeCmd(p);
|
|
};
|
|
} catch(CommandExecutor::ExitException c) {
|
|
exitCode=c.getCode();
|
|
}
|
|
|
|
//Launch postSetup().
|
|
if(shellPostSetup!=0) shellPostSetup(this);
|
|
|
|
notLoop = false;
|
|
return exitCode;
|
|
}
|
|
|
|
int Shell::launchCmd(const struct Params& p){
|
|
int status;
|
|
|
|
pid_t pid = fork();
|
|
if (pid == 0) {
|
|
//child process
|
|
|
|
//execute threadCommand
|
|
int a;
|
|
|
|
try {
|
|
a=cmds.launch(p, true);
|
|
std::exit(EXIT_SUCCESS);
|
|
}
|
|
catch (CommandNotFoundException){}
|
|
|
|
//execute bash command or program
|
|
if((a=execvp(p.argv[0], p.argv)) == -1) {
|
|
outputStream << name << ": execution failed. Errno is set to: "<<strerror(errno)<<"\n";
|
|
std::exit(EXIT_FAILURE);
|
|
}
|
|
else std::exit(EXIT_SUCCESS);
|
|
}
|
|
else if (pid < 0) {
|
|
// Error forking
|
|
errorStream << name <<": error in process forking.\n";
|
|
}
|
|
else {
|
|
// Parent process
|
|
do {
|
|
//wait until child finished
|
|
waitpid(pid, &status, WUNTRACED);
|
|
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int Shell::executeCmd(const struct Params& p){
|
|
if (!p.argc) return 1; //Return if the line is empty.
|
|
|
|
try {
|
|
//Search in non-thread commands.
|
|
return cmds.launch(p, false);
|
|
}
|
|
catch(CommandNotFoundException) {}
|
|
|
|
//Execute the threadCommand or the executable.
|
|
return launchCmd(p);
|
|
}
|
|
|
|
inline int Shell::executeCmd(const std::string &args){
|
|
return executeCmd(splitLine(&args));
|
|
}
|
|
|
|
void Shell::SIGINTHandler(int,siginfo_t*, void *){
|
|
SIGINTRaised = true;
|
|
}
|
|
|
|
void Shell::setShellSetup(void (*s)(Shell *)){
|
|
shellSetup=s;
|
|
}
|
|
|
|
void (*Shell::getShellSetup())(Shell*) const{
|
|
return shellSetup;
|
|
}
|
|
|
|
void Shell::setShellPostSetup(void (*s)(Shell *)){
|
|
shellPostSetup=s;
|
|
}
|
|
|
|
void (*Shell::getShellPostSetup())(Shell*) const{
|
|
return shellPostSetup;
|
|
}
|
|
|
|
|
|
struct sigaction* setHandler(unsigned signal, void (*funcptr)(int, siginfo_t*, void*)){
|
|
struct sigaction* before = new struct sigaction();
|
|
struct sigaction sa;
|
|
sa.sa_sigaction = funcptr;
|
|
sa.sa_flags = 0;
|
|
sigaction(signal, &sa, before);
|
|
}
|
|
|
|
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
|
|
|
|
if(SIGINTRaised){
|
|
SIGINTRaised=false;
|
|
throw IsUndoingLineException();
|
|
}
|
|
|
|
restoreHandler(SIGINT, beforeSIGINT);
|
|
return buffer;
|
|
}
|
|
|
|
struct Params Shell::splitLine(const std::string* line){
|
|
struct Params p;
|
|
stringToArgcArgv(*line, &(p.argc), &(p.argv));
|
|
return p;
|
|
}
|
|
|
|
bool Shell::SIGINTRaised = false;
|
|
|
|
void Shell::addCmd(Command* cmd, bool isthread){
|
|
cmds.add(cmd, isthread);
|
|
}
|
|
|
|
size_t Shell::howManyCmds() const{
|
|
return cmds.howMany();
|
|
}
|
|
}
|