]> git.tdb.fi Git - gldbg.git/blobdiff - source/process.cpp
Make gldbg interactive
[gldbg.git] / source / process.cpp
diff --git a/source/process.cpp b/source/process.cpp
new file mode 100644 (file)
index 0000000..ebc0b95
--- /dev/null
@@ -0,0 +1,111 @@
+/* $Id$
+
+This file is part of gldbg
+Copyright © 2009  Mikko Rasa, Mikkosoft Productions
+Distributed under the GPL
+*/
+
+#include <cstdlib>
+#include <cerrno>
+#include <cstring>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <msp/core/except.h>
+#include "process.h"
+
+using namespace std;
+using namespace Msp;
+
+Process::Process(const vector<string> &a):
+       args(a),
+       pid(-1),
+       state(INACTIVE)
+{ }
+
+void Process::setenv(const string &key, const string &value)
+{
+       env[key] = value;
+}
+
+void Process::launch()
+{
+       if(state!=INACTIVE)
+               throw InvalidState("Program is already running");
+
+       pid = fork();
+       if(pid==0)
+       {
+               for(map<string, string>::const_iterator i=env.begin(); i!=env.end(); ++i)
+                       ::setenv(i->first.c_str(), i->second.c_str(), true);
+               std::vector<char *> argv(args.size()+1);
+               for(unsigned i=0; i<args.size(); ++i)
+                       argv[i] = strdup(args[i].c_str());
+               argv[args.size()] = 0;
+               ::ptrace(PTRACE_TRACEME, 0, 0, 0);
+               execvp(argv[0], &argv[0]);
+               ::exit(255);
+       }
+       else if(pid>0)
+               state = STARTING;
+       else
+               throw SystemError("Could not launch process", errno);
+}
+
+int Process::check()
+{
+       int status;
+       int ret = waitpid(pid, &status, WNOHANG);
+       if(ret==pid)
+       {
+               if(WIFEXITED(status))
+               {
+                       int code = WEXITSTATUS(status);
+                       state = INACTIVE;
+                       return 0x100|code;
+               }
+               else if(WIFSIGNALED(status))
+               {
+                       state = INACTIVE;
+                       return 0x200|WTERMSIG(status);
+               }
+               else if(WIFSTOPPED(status))
+               {
+                       int sig = WSTOPSIG(status);
+                       if(sig==SIGTRAP && state==STARTING)
+                       {
+                               ptrace(PTRACE_CONT, 0, 0);
+                               state = RUNNING;
+                       }
+                       else
+                       {
+                               state = STOPPED;
+                               return 0x300|sig;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+void Process::resume(int sig)
+{
+       if(state!=STOPPED)
+               throw InvalidState("Program is not stopped");
+       ptrace(PTRACE_CONT, 0, (void *)sig);
+       state = RUNNING;
+}
+
+void Process::kill()
+{
+       if(state==INACTIVE)
+               throw InvalidState("Program is not running");
+       ptrace(PTRACE_KILL, 0, 0);
+}
+
+long Process::ptrace(int req, void *addr, void *data)
+{
+       int ret = ::ptrace((__ptrace_request)req, pid, addr, data);
+       if(ret==-1 && ((req!=PTRACE_PEEKTEXT && req!=PTRACE_PEEKDATA && req!=PTRACE_PEEKUSER) || errno))
+               throw SystemError("ptrace error", errno);
+       return ret;
+}