diff --git a/direct/src/plugin/p3dAuthSession.I b/direct/src/plugin/p3dAuthSession.I new file mode 100644 index 0000000000..e36c737988 --- /dev/null +++ b/direct/src/plugin/p3dAuthSession.I @@ -0,0 +1,14 @@ +// Filename: p3dAuthSession.I +// Created by: drose (17Sep09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + diff --git a/direct/src/plugin/p3dAuthSession.cxx b/direct/src/plugin/p3dAuthSession.cxx new file mode 100644 index 0000000000..35fed20313 --- /dev/null +++ b/direct/src/plugin/p3dAuthSession.cxx @@ -0,0 +1,404 @@ +// Filename: P3DAuthSession.cxx +// Created by: drose (17Sep09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "p3dAuthSession.h" +#include "p3dInstance.h" +#include "p3dInstanceManager.h" +#include "p3dMultifileReader.h" +#include "p3d_plugin_config.h" +#include "mkdir_complete.h" + +#include + +#ifndef _WIN32 +#include +#include +#include +#include +#include +#endif + +//////////////////////////////////////////////////////////////////// +// Function: P3DAuthSession::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +P3DAuthSession:: +P3DAuthSession(P3DInstance *inst) : + _inst(inst) +{ + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + + _p3dcert_started = false; + _p3dcert_running = false; + _started_wait_thread = false; + INIT_THREAD(_wait_thread); + + // Write the cert chain to a temporary file. + _cert_filename = new P3DTemporaryFile(".crt"); + string filename = _cert_filename->get_filename(); + FILE *fp = fopen(filename.c_str(), "w"); + if (fp == NULL) { + nout << "Couldn't write temporary file\n"; + return; + } + + if (inst->_mf_reader.get_num_signatures() > 0) { + const P3DMultifileReader::CertChain &cert_chain = + inst->_mf_reader.get_signature(0); + + if (cert_chain.size() > 0) { + // Save the cert_dir, this is where the p3dcert program will + // need to write the cert when it is approved. + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + _cert_dir = inst_mgr->get_cert_dir(cert_chain[0]._cert); + } + + P3DMultifileReader::CertChain::const_iterator ci; + for (ci = cert_chain.begin(); ci != cert_chain.end(); ++ci) { + X509 *c = (*ci)._cert; + PEM_write_X509(fp, c); + } + } + fclose(fp); + + start_p3dcert(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: P3DAuthSession::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +P3DAuthSession:: +~P3DAuthSession() { + shutdown(false); + + if (_cert_filename != NULL) { + delete _cert_filename; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DAuthSession::shutdown +// Access: Public +// Description: Terminates the session by killing the subprocess. +//////////////////////////////////////////////////////////////////// +void P3DAuthSession:: +shutdown(bool send_message) { + if (!send_message) { + // If we're not to send the instance the shutdown message as a + // result of this, then clear the _inst pointer now. + _inst = NULL; + } + + if (_p3dcert_started) { + nout << "Killing p3dcert process\n"; +#ifdef _WIN32 + TerminateProcess(_p3dcert_handle, 2); + CloseHandle(_p3dcert_handle); + +#else // _WIN32 + kill(_p3dcert_pid, SIGKILL); +#endif // _WIN32 + + _p3dcert_running = false; + _p3dcert_started = false; + } + + // Now that the process has stopped, the thread should stop itself + // quickly too. + join_wait_thread(); + + // We're no longer bound to any particular instance. + _inst = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DAuthSession::start_p3dcert +// Access: Private +// Description: Starts the p3dcert program running in a child +// process. +//////////////////////////////////////////////////////////////////// +void P3DAuthSession:: +start_p3dcert() { + if (_p3dcert_started) { + // Already started. + return; + } + + if (_inst->_p3dcert_package == NULL) { + nout << "Couldn't start Python: no p3dcert package.\n"; + return; + } + + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + + _start_dir = inst_mgr->get_root_dir() + "/start"; + + string root_dir = _inst->_p3dcert_package->get_package_dir(); + mkdir_complete(_start_dir, nout); + + _p3dcert_exe = root_dir + "/p3dcert"; +#ifdef _WIN32 + _p3dcert_exe += ".exe"; +#endif + + // Populate the new process' environment. + _env = string(); + + // These are the enviroment variables we forward from the current + // environment, if they are set. + const char *keep[] = { + "TMP", "TEMP", "HOME", "USER", +#ifdef _WIN32 + "SYSTEMROOT", "USERPROFILE", "COMSPEC", +#endif +#ifdef HAVE_X11 + "DISPLAY", +#endif + NULL + }; + + for (int ki = 0; keep[ki] != NULL; ++ki) { + char *value = getenv(keep[ki]); + if (value != NULL) { + _env += keep[ki]; + _env += "="; + _env += value; + _env += '\0'; + } + } + + // Define some new environment variables. + _env += "PATH="; + _env += root_dir; + _env += '\0'; + + _env += "LD_LIBRARY_PATH="; + _env += root_dir; + _env += '\0'; + + _env += "DYLD_LIBRARY_PATH="; + _env += root_dir; + _env += '\0'; + + _env += "P3DCERT_ROOT="; + _env += root_dir; + _env += '\0'; + + nout << "Attempting to start p3dcert from " << _p3dcert_exe << "\n"; + + bool started_p3dcert = false; +#ifdef _WIN32 + _p3dcert_handle = win_create_process(); + started_p3dcert = (_p3dcert_handle != INVALID_HANDLE_VALUE); +#else + _p3dcert_pid = posix_create_process(); + started_p3dcert = (_p3dcert_pid > 0); +#endif + if (!started_p3dcert) { + nout << "Failed to create process.\n"; + return; + } + + _p3dcert_started = true; + _p3dcert_running = true; + + spawn_wait_thread(); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DAuthSession::spawn_wait_thread +// Access: Private +// Description: Starts the wait thread. This thread is responsible +// for waiting for the process to finish, and notifying +// the instance when it does. +//////////////////////////////////////////////////////////////////// +void P3DAuthSession:: +spawn_wait_thread() { + SPAWN_THREAD(_wait_thread, wt_thread_run, this); + _started_wait_thread = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DAuthSession::join_wait_thread +// Access: Private +// Description: Waits for the wait thread to stop. +//////////////////////////////////////////////////////////////////// +void P3DAuthSession:: +join_wait_thread() { + if (!_started_wait_thread) { + return; + } + + JOIN_THREAD(_wait_thread); + _started_wait_thread = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DAuthSession::wt_thread_run +// Access: Private +// Description: The main function for the wait thread. +//////////////////////////////////////////////////////////////////// +void P3DAuthSession:: +wt_thread_run() { + // All we do here is wait for the process to terminate. + nout << "wt_thread_run in " << this << " beginning\n"; +#ifdef _WIN32 + DWORD result = WaitForSingleObject(_p3dcert_handle, INFINITE); + if (result != 0) { + nout << "Wait for process failed: " << GetLastError() << "\n"; + } + nout << "p3dcert process has successfully stopped.\n"; +#else + int status; + pid_t result = waitpid(_p3dcert_pid, &status, WNOHANG); + if (result == -1) { + perror("waitpid"); + break; + } + + nout << "p3dcert process has successfully stopped.\n"; + if (WIFEXITED(status)) { + nout << " exited normally, status = " + << WEXITSTATUS(status) << "\n"; + } else if (WIFSIGNALED(status)) { + nout << " signalled by " << WTERMSIG(status) << ", core = " + << WCOREDUMP(status) << "\n"; + } else if (WIFSTOPPED(status)) { + nout << " stopped by " << WSTOPSIG(status) << "\n"; + } +#endif // _WIN32 + + // Notify the instance that we're done. + P3DInstance *inst = _inst; + if (inst != NULL) { + inst->auth_finished_sub_thread(); + } + + nout << "Exiting wt_thread_run in " << this << "\n"; +} + +#ifdef _WIN32 +//////////////////////////////////////////////////////////////////// +// Function: P3DAuthSession::win_create_process +// Access: Private +// Description: Creates a sub-process to run _p3dcert_exe, with +// the appropriate command-line arguments, and the +// environment string defined in _env. +// +// Returns the handle to the created process on success, +// or INVALID_HANDLE_VALUE on falure. +//////////////////////////////////////////////////////////////////// +HANDLE P3DAuthSession:: +win_create_process() { + // Make sure we see an error dialog if there is a missing DLL. + SetErrorMode(0); + + STARTUPINFO startup_info; + ZeroMemory(&startup_info, sizeof(STARTUPINFO)); + startup_info.cb = sizeof(startup_info); + + // Make sure the initial window is *shown* for this graphical app. + startup_info.wShowWindow = SW_SHOW; + startup_info.dwFlags |= STARTF_USESHOWWINDOW; + + const char *start_dir_cstr = _start_dir.c_str(); + + // Construct the command-line string, containing the quoted + // command-line arguments. + ostringstream stream; + stream << "\"" << _p3dcert_exe << "\" \"" + << _cert_filename->get_filename() << "\" \"" + << _cert_dir << "\""; + + // I'm not sure why CreateProcess wants a non-const char pointer for + // its command-line string, but I'm not taking chances. It gets a + // non-const char array that it can modify. + string command_line_str = stream.str(); + nout << "command is: " << command_line_str << "\n"; + char *command_line = new char[command_line_str.size() + 1]; + strcpy(command_line, command_line_str.c_str()); + + PROCESS_INFORMATION process_info; + BOOL result = CreateProcess + (_p3dcert_exe.c_str(), command_line, NULL, NULL, TRUE, 0, + (void *)_env.c_str(), start_dir_cstr, + &startup_info, &process_info); + bool started_program = (result != 0); + + delete[] command_line; + + if (!started_program) { + return INVALID_HANDLE_VALUE; + } + + CloseHandle(process_info.hThread); + return process_info.hProcess; +} +#endif // _WIN32 + + +#ifndef _WIN32 +//////////////////////////////////////////////////////////////////// +// Function: P3DAuthSession::posix_create_process +// Access: Private +// Description: Creates a sub-process to run _p3dcert_exe, with +// the appropriate command-line arguments, and the +// environment string defined in _env. +// +// Returns the pid of the created process on success, or +// -1 on falure. +//////////////////////////////////////////////////////////////////// +int P3DAuthSession:: +posix_create_process() { + // Fork and exec. + pid_t child = fork(); + if (child < 0) { + perror("fork"); + return -1; + } + + if (child == 0) { + // Here we are in the child process. + + if (chdir(_start_dir.c_str()) < 0) { + nout << "Could not chdir to " << _start_dir << "\n"; + // This is a warning, not an error. We don't actually care + // that much about the starting directory. + } + + // build up an array of char strings for the environment. + vector ptrs; + size_t p = 0; + size_t zero = _env.find('\0', p); + while (zero != string::npos) { + ptrs.push_back(_env.data() + p); + p = zero + 1; + zero = _env.find('\0', p); + } + ptrs.push_back((char *)NULL); + + execle(_p3dcert_exe.c_str(), _p3dcert_exe.c_str(), + _cert_filename->get_filename().c_str(), _cert_dir.c_str(), + (char *)0, &ptrs[0]); + nout << "Failed to exec " << _p3dcert_exe << "\n"; + _exit(1); + } + + return child; +} +#endif // _WIN32 diff --git a/direct/src/plugin/p3dAuthSession.h b/direct/src/plugin/p3dAuthSession.h new file mode 100644 index 0000000000..b583155b7d --- /dev/null +++ b/direct/src/plugin/p3dAuthSession.h @@ -0,0 +1,83 @@ +// Filename: P3DAuthSession.h +// Created by: drose (17Sep09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef P3DAUTHSESSION_H +#define P3DAUTHSESSION_H + +#include "p3d_plugin_common.h" +#include "p3dPackage.h" +#include "get_tinyxml.h" +#include "p3dTemporaryFile.h" +#include "p3dReferenceCount.h" + +class P3DInstance; + +//////////////////////////////////////////////////////////////////// +// Class : P3DAuthSession +// Description : This is an instance of a p3dcert program running in a +// subprocess. There's no communication with the +// process, or none of that complicated stuff the +// P3DSession has to do; all we do here is fire off the +// process, then wait for it to exit. +//////////////////////////////////////////////////////////////////// +class P3DAuthSession : public P3DReferenceCount { +public: + P3DAuthSession(P3DInstance *inst); + ~P3DAuthSession(); + + void shutdown(bool send_message); + +private: + void start_p3dcert(); + + void spawn_wait_thread(); + void join_wait_thread(); + +private: + // These methods run only within the read thread. + THREAD_CALLBACK_DECLARATION(P3DAuthSession, wt_thread_run); + void wt_thread_run(); + +#ifdef _WIN32 + HANDLE win_create_process(); +#else + int posix_create_process(); +#endif + +private: + P3DInstance *_inst; + string _start_dir; + + // This information is passed to create_process(). + P3DTemporaryFile *_cert_filename; + string _cert_dir; + string _p3dcert_exe; + string _env; + +#ifdef _WIN32 + HANDLE _p3dcert_handle; +#else + int _p3dcert_pid; +#endif + bool _p3dcert_started; + bool _p3dcert_running; + + // The remaining members are manipulated by or for the read thread. + bool _started_wait_thread; + THREAD _wait_thread; +}; + +#include "P3DAuthSession.I" + +#endif