more robust temp file and log handling

This commit is contained in:
David Rose 2009-08-20 00:24:00 +00:00
parent 19e866c803
commit 5289285624
32 changed files with 614 additions and 148 deletions

View File

@ -45,6 +45,7 @@
p3dSession.h p3dSession.I \
p3dSplashWindow.h p3dSplashWindow.I \
p3dStringObject.h \
p3dTemporaryFile.h p3dTemporaryFile.I \
p3dToplevelObject.h \
p3dUndefinedObject.h \
p3dWinSplashWindow.h p3dWinSplashWindow.I \
@ -74,6 +75,7 @@
p3dSession.cxx \
p3dSplashWindow.cxx \
p3dStringObject.cxx \
p3dTemporaryFile.cxx \
p3dToplevelObject.cxx \
p3dUndefinedObject.cxx \
p3dWinSplashWindow.cxx \

View File

@ -124,7 +124,8 @@ static void unload_dso();
////////////////////////////////////////////////////////////////////
bool
load_plugin(const string &p3d_plugin_filename, const string &contents_filename,
const string &download_url, const string &platform) {
const string &download_url, const string &platform,
const string &log_directory, const string &log_basename) {
string filename = p3d_plugin_filename;
if (filename.empty()) {
// Look for the plugin along the path.
@ -294,7 +295,8 @@ load_plugin(const string &p3d_plugin_filename, const string &contents_filename,
plugin_loaded = true;
if (!P3D_initialize(P3D_API_VERSION, contents_filename.c_str(),
download_url.c_str(), platform.c_str())) {
download_url.c_str(), platform.c_str(),
log_directory.c_str(), log_basename.c_str())) {
// Oops, failure to initialize.
cerr << "Failed to initialize plugin (wrong API version?)\n";
unload_plugin();

View File

@ -60,7 +60,8 @@ extern P3D_instance_handle_event_func *P3D_instance_handle_event;
string get_plugin_basename();
bool
load_plugin(const string &p3d_plugin_filename, const string &contents_filename,
const string &download_url, const string &platform);
const string &download_url, const string &platform,
const string &log_directory, const string &log_basename);
void unload_plugin();
bool is_plugin_loaded();

View File

@ -122,6 +122,19 @@ lookup_token(const string &keyword) const {
return string();
}
////////////////////////////////////////////////////////////////////
// Function: P3DFileParams::lookup_token_int
// Access: Public
// Description: Returns the integer value associated with the first
// appearance of the named token, or zero if the
// token does not appear or is not an integer.
////////////////////////////////////////////////////////////////////
int P3DFileParams::
lookup_token_int(const string &keyword) const {
string value = lookup_token(keyword);
return atoi(value.c_str());
}
////////////////////////////////////////////////////////////////////
// Function: P3DFileParams::has_token
// Access: Public

View File

@ -36,6 +36,7 @@ public:
inline const string &get_p3d_filename() const;
string lookup_token(const string &keyword) const;
int lookup_token_int(const string &keyword) const;
bool has_token(const string &keyword) const;
TiXmlElement *make_xml();

View File

@ -25,6 +25,7 @@
#include "p3dToplevelObject.h"
#include "p3dUndefinedObject.h"
#include "p3dMultifileReader.h"
#include "p3dTemporaryFile.h"
#include <sstream>
#include <algorithm>
@ -59,6 +60,8 @@ P3DInstance(P3D_request_ready_func *func,
_panda_script_object = new P3DToplevelObject;
_user_data = user_data;
_request_pending = false;
_temp_p3d_filename = NULL;
_temp_splash_image = NULL;
_got_fparams = false;
_got_wparams = false;
@ -68,6 +71,7 @@ P3DInstance(P3D_request_ready_func *func,
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
_instance_id = inst_mgr->get_unique_id();
_full_disk_access = false;
_hidden = false;
_session = NULL;
_panda3d = NULL;
_splash_window = NULL;
@ -122,6 +126,16 @@ P3DInstance::
_splash_window = NULL;
}
if (_temp_p3d_filename != NULL) {
delete _temp_p3d_filename;
_temp_p3d_filename = NULL;
}
if (_temp_splash_image != NULL) {
delete _temp_splash_image;
_temp_splash_image = NULL;
}
#ifdef __APPLE__
if (_frame_timer != NULL) {
CFRunLoopTimerInvalidate(_frame_timer);
@ -167,9 +181,8 @@ void P3DInstance::
set_p3d_url(const string &p3d_url) {
nout << "set_p3d_url(" << p3d_url << ")\n";
// Make a temporary file to receive the instance data.
char *name = tempnam(NULL, "p3d_");
string filename = name;
free(name);
assert(_temp_p3d_filename == NULL);
_temp_p3d_filename = new P3DTemporaryFile(".p3d");
// Mark the time we started downloading, so we'll know when to set
// the install label.
@ -183,7 +196,7 @@ set_p3d_url(const string &p3d_url) {
// Start downloading the data.
InstanceDownload *download = new InstanceDownload(this);
download->set_url(p3d_url);
download->set_filename(filename);
download->set_filename(_temp_p3d_filename->get_filename());
_panda_script_object->set_string_property("status", "downloading_instance");
start_download(download);
@ -243,10 +256,15 @@ set_p3d_filename(const string &p3d_filename) {
////////////////////////////////////////////////////////////////////
void P3DInstance::
set_wparams(const P3DWindowParams &wparams) {
nout << "set_wparams, _session = " << _session << "\n";
_got_wparams = true;
_wparams = wparams;
nout << "set window_type = " << _wparams.get_window_type() << "\n";
if (_hidden || _wparams.get_win_width() == 0 || _wparams.get_win_height() == 0) {
// If we're a hidden app, or if the window has no size, then it is
// really a hidden window, regardless of what type it claims to
// be.
_wparams.set_window_type(P3D_WT_hidden);
}
if (_wparams.get_window_type() != P3D_WT_hidden) {
// Update or create the splash window.
@ -854,6 +872,15 @@ scan_app_desc_file(TiXmlDocument *doc) {
_full_disk_access = (full_disk_access != 0);
}
int hidden = 0;
if (xpackage->QueryIntAttribute("hidden", &hidden) == TIXML_SUCCESS) {
_hidden = (hidden != 0);
}
if (_hidden && _got_wparams) {
_wparams.set_window_type(P3D_WT_hidden);
}
TiXmlElement *xrequires = xpackage->FirstChildElement("requires");
while (xrequires != NULL) {
const char *name = xrequires->Attribute("name");
@ -1015,6 +1042,11 @@ handle_notify_request(const string &message) {
_splash_window = NULL;
}
if (_temp_splash_image != NULL) {
delete _temp_splash_image;
_temp_splash_image = NULL;
}
_panda_script_object->set_string_property("status", "open");
#ifdef __APPLE__
@ -1112,7 +1144,6 @@ handle_script_request(const string &operation, P3D_object *object,
char *buffer = new char[size + 1];
P3D_OBJECT_GET_STRING(value, buffer, size + 1);
result = P3D_OBJECT_EVAL(object, buffer);
logfile << " eval " << *object << ": " << buffer << ", result = " << result << "\n";
delete[] buffer;
if (result != NULL) {
@ -1157,14 +1188,13 @@ make_splash_window() {
}
// Make a temporary file to receive the splash image.
char *name = tempnam(NULL, "p3d_");
string filename = name;
free(name);
assert(_temp_splash_image == NULL);
_temp_splash_image = new P3DTemporaryFile(".jpg");
// Start downloading the requested splash image.
SplashDownload *download = new SplashDownload(this);
download->set_url(splash_image_url);
download->set_filename(filename);
download->set_filename(_temp_splash_image->get_filename());
start_download(download);
}
@ -1202,6 +1232,8 @@ report_package_info_ready(P3DPackage *package) {
_splash_window->set_install_progress(0.0);
}
_panda_script_object->set_string_property("status", "downloading");
_panda_script_object->set_int_property("numDownloadingPackages", _downloading_packages.size());
_panda_script_object->set_int_property("totalDownloadSize", _total_download_size);
send_notify("ondownloadbegin");
start_next_download();
@ -1232,6 +1264,8 @@ start_next_download() {
}
_panda_script_object->set_string_property("downloadPackageName", package->get_package_name());
_panda_script_object->set_string_property("downloadPackageDisplayName", name);
_panda_script_object->set_int_property("downloadPackageNumber", _download_package_index + 1);
_panda_script_object->set_int_property("downloadPackageSize", package->get_download_size());
set_install_label("Installing " + name);
nout << "Downloading " << package->get_package_name()
@ -1241,6 +1275,7 @@ start_next_download() {
<< " bytes.\n";
package->activate_download();
send_notify("ondownloadnext");
return;
}
@ -1253,10 +1288,12 @@ start_next_download() {
_downloading_packages.clear();
if (get_packages_ready()) {
_panda_script_object->set_bool_property("downloadComplete", true);
_panda_script_object->set_string_property("status", "starting");
send_notify("ondownloadcomplete");
if (!_panda_script_object->get_bool_property("downloadComplete")) {
_panda_script_object->set_bool_property("downloadComplete", true);
_panda_script_object->set_string_property("status", "starting");
send_notify("ondownloadcomplete");
}
// Notify the session also.
if (_session != NULL) {
_session->report_packages_done(this, true);

View File

@ -40,6 +40,7 @@ class P3DDownload;
class P3DPackage;
class P3DObject;
class P3DToplevelObject;
class P3DTemporaryFile;
////////////////////////////////////////////////////////////////////
// Class : P3DInstance
@ -145,6 +146,9 @@ private:
P3D_object *_browser_script_object;
P3DToplevelObject *_panda_script_object;
P3DTemporaryFile *_temp_p3d_filename;
P3DTemporaryFile *_temp_splash_image;
bool _got_fparams;
P3DFileParams _fparams;
@ -156,6 +160,7 @@ private:
string _python_version;
string _log_basename;
bool _full_disk_access;
bool _hidden;
// Not ref-counted: session is the parent.
P3DSession *_session;

View File

@ -60,6 +60,20 @@ get_platform() const {
return _platform;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::get_log_directory
// Access: Public
// Description: Returns the pathname of the directory into which all
// log files should be written. This filename will end
// with a slash or backslash, as appropriate, so that
// logfile pathnames may be made by concatenting
// directly with this string.
////////////////////////////////////////////////////////////////////
inline const string &P3DInstanceManager::
get_log_directory() const {
return _log_directory;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::has_contents_file
// Access: Public

View File

@ -24,6 +24,7 @@
#include "find_root_dir.h"
#include "fileSpec.h"
#include "get_tinyxml.h"
#include "mkdir_complete.h"
// We can include this header file to get the DTOOL_PLATFORM
// definition, even though we don't link with dtool.
@ -32,9 +33,12 @@
#ifdef _WIN32
#include <shlobj.h>
#else
//#include <sys/stat.h>
#include <sys/stat.h>
#endif
static ofstream logfile;
ostream *nout_stream = &logfile;
P3DInstanceManager *P3DInstanceManager::_global_ptr;
////////////////////////////////////////////////////////////////////
@ -45,6 +49,7 @@ P3DInstanceManager *P3DInstanceManager::_global_ptr;
P3DInstanceManager::
P3DInstanceManager() {
_is_initialized = false;
_next_temp_filename_counter = 0;
_unique_id = 0;
_xcontents = NULL;
@ -105,6 +110,15 @@ P3DInstanceManager::
assert(_instances.empty());
assert(_sessions.empty());
// Delete any remaining temporary files.
TempFilenames::iterator ti;
for (ti = _temp_filenames.begin(); ti != _temp_filenames.end(); ++ti) {
const string &filename = (*ti);
nout << "Removing delinquent temp file " << filename << "\n";
unlink(filename.c_str());
}
_temp_filenames.clear();
nout << "counts: " << _undefined_object->_ref_count
<< " " << _none_object->_ref_count
<< " " << _true_object->_ref_count
@ -138,23 +152,104 @@ P3DInstanceManager::
////////////////////////////////////////////////////////////////////
bool P3DInstanceManager::
initialize(const string &contents_filename, const string &download_url,
const string &platform) {
const string &platform, const string &log_directory,
const string &log_basename) {
_root_dir = find_root_dir();
_download_url = download_url;
#ifdef P3D_PLUGIN_DOWNLOAD
if (_download_url.empty()) {
_download_url = P3D_PLUGIN_DOWNLOAD;
}
#endif
_platform = platform;
if (_platform.empty()) {
_platform = DTOOL_PLATFORM;
}
_log_directory = log_directory;
#ifdef P3D_PLUGIN_LOG_DIRECTORY
if (_log_directory.empty()) {
_log_directory = P3D_PLUGIN_LOG_DIRECTORY;
}
#endif
// Determine the temporary directory.
#ifdef _WIN32
size_t needs_size_1 = GetTempPath(0, NULL);
char *buffer_1 = new char[needs_size_1];
if (GetTempPath(needs_size_1, buffer_1) != 0) {
_temp_directory = buffer_1;
}
delete[] buffer_1;
static const size_t buffer_size = 4096;
char buffer[buffer_size];
if (GetTempPath(buffer_size, buffer) != 0) {
_temp_directory = buffer;
}
// Also insist that the temp directory is fully specified.
size_t needs_size_2 = GetFullPathName(_temp_directory.c_str(), 0, NULL, NULL);
char *buffer_2 = new char[needs_size_2];
if (GetFullPathName(_temp_directory.c_str(), needs_size_2, buffer_2, NULL) != 0) {
_temp_directory = buffer_2;
}
delete[] buffer_2;
// Also make sure the directory actually exists.
mkdir_complete(_temp_directory, nout);
#else
_temp_directory = "/tmp/";
#endif // _WIN32
// If the log directory is still empty, use the temp directory.
if (_log_directory.empty()) {
_log_directory = _temp_directory;
}
_log_basename = log_basename;
#ifdef P3D_PLUGIN_LOG_BASENAME2
if (_log_basename.empty()) {
_log_basename = P3D_PLUGIN_LOG_BASENAME2;
}
#endif
// Ensure that the download URL ends with a slash.
if (!_download_url.empty() && _download_url[_download_url.size() - 1] != '/') {
_download_url += "/";
}
// Ensure that the temp directory ends with a slash.
if (!_temp_directory.empty() && _temp_directory[_temp_directory.size() - 1] != '/') {
#ifdef _WIN32
if (_temp_directory[_temp_directory.size() - 1] != '\\')
#endif
_temp_directory += "/";
}
// Ensure that the log directory ends with a slash.
if (!_log_directory.empty() && _log_directory[_log_directory.size() - 1] != '/') {
#ifdef _WIN32
if (_log_directory[_log_directory.size() - 1] != '\\')
#endif
_log_directory += "/";
}
// Construct the logfile pathname.
if (!_log_basename.empty()) {
_log_pathname = _log_directory;
_log_pathname += _log_basename;
_log_pathname += ".log";
logfile.open(_log_pathname.c_str(), ios::out | ios::trunc);
if (logfile) {
logfile.setf(ios::unitbuf);
nout_stream = &logfile;
}
}
nout << "_root_dir = " << _root_dir
<< ", contents = " << contents_filename
<< ", download = " << _download_url
@ -497,6 +592,82 @@ make_class_definition() const {
return new_class;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::make_temp_filename
// Access: Public
// Description: Constructs a new, unique temporary filename with the
// indicated extension. You should use the
// P3DTemporaryFilename interface instead of calling
// this method directly.
////////////////////////////////////////////////////////////////////
string P3DInstanceManager::
make_temp_filename(const string &extension) {
string result;
bool exists;
do {
int tid;
#ifdef _WIN32
tid = GetCurrentProcessId();
#else
tid = getpid();
#endif
if (tid == 0) {
tid = 1;
}
int hash = ((clock() + _next_temp_filename_counter) * ((time(NULL) * tid) >> 8)) & 0xffffff;
++_next_temp_filename_counter;
char hex_code[10];
sprintf(hex_code, "%06x", hash);
result = _temp_directory;
result += "p3d_";
result += hex_code;
result += extension;
exists = false;
if (_temp_filenames.find(result) != _temp_filenames.end()) {
// We've previously allocated this file.
exists = true;
} else {
// Check if the file exists on disk.
#ifdef _WIN32
DWORD results = GetFileAttributes(result.c_str());
if (results != -1) {
exists = true;
}
#else // _WIN32
struct stat this_buf;
if (stat(result.c_str(), &this_buf) == 0) {
exists = true;
}
#endif
}
} while (exists);
nout << "make_temp_filename: " << result << "\n";
return result;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::release_temp_filename
// Access: Public
// Description: Releases a temporary filename assigned earlier via
// make_temp_filename(). If the file exists, it will be
// removed. You should use the P3DTemporaryFilename
// interface instead of calling this method directly.
////////////////////////////////////////////////////////////////////
void P3DInstanceManager::
release_temp_filename(const string &filename) {
nout << "release_temp_filename: " << filename << "\n";
_temp_filenames.erase(filename);
unlink(filename.c_str());
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::get_global_ptr
// Access: Public, Static

View File

@ -45,13 +45,16 @@ private:
public:
bool initialize(const string &contents_filename,
const string &download_url,
const string &platform);
const string &platform,
const string &log_directory,
const string &log_basename);
inline bool is_initialized() const;
inline const string &get_root_dir() const;
inline const string &get_download_url() const;
inline const string &get_platform() const;
inline const string &get_log_directory() const;
inline bool has_contents_file() const;
bool read_contents_file(const string &contents_filename);
@ -88,6 +91,9 @@ public:
inline P3D_object *new_none_object();
inline P3D_object *new_bool_object(bool value);
string make_temp_filename(const string &extension);
void release_temp_filename(const string &filename);
static P3DInstanceManager *get_global_ptr();
static void delete_global_ptr();
@ -106,6 +112,10 @@ private:
string _root_dir;
string _download_url;
string _platform;
string _log_directory;
string _log_basename;
string _log_pathname;
string _temp_directory;
TiXmlElement *_xcontents;
@ -123,6 +133,10 @@ private:
typedef map<string, P3DPackage *> Packages;
Packages _packages;
typedef set<string> TempFilenames;
TempFilenames _temp_filenames;
int _next_temp_filename_counter;
int _unique_id;
// This condition var is waited on the main thread and signaled in a

View File

@ -16,6 +16,7 @@
#include "p3dInstanceManager.h"
#include "p3dInstance.h"
#include "p3dMultifileReader.h"
#include "p3dTemporaryFile.h"
#include "mkdir_complete.h"
#include "zlib.h"
@ -53,6 +54,8 @@ P3DPackage(const string &package_name,
_package_fullname += string("_") + _package_version;
_package_dir += string("/") + _package_version;
_temp_contents_file = NULL;
_info_ready = false;
_download_size = 0;
_allow_data_download = false;
@ -85,6 +88,7 @@ P3DPackage::
_active_download = NULL;
}
assert(_temp_contents_file == NULL);
assert(_instances.empty());
}
@ -179,10 +183,10 @@ download_contents_file() {
// Download contents.xml to a temporary filename first, in case
// multiple packages are downloading it simultaneously.
_contents_file_pathname = tempnam(NULL, "p3d_");
assert(_temp_contents_file == NULL);
_temp_contents_file = new P3DTemporaryFile(".xml");
cerr << "starting contents download\n";
start_download(DT_contents_file, url, _contents_file_pathname, false);
start_download(DT_contents_file, url, _temp_contents_file->get_filename(), false);
}
////////////////////////////////////////////////////////////////////
@ -193,20 +197,18 @@ download_contents_file() {
void P3DPackage::
contents_file_download_finished(bool success) {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
cerr << "done contents download: " << success
<< ", has_contents = " << inst_mgr->has_contents_file()
<< "\n";
if (!inst_mgr->has_contents_file()) {
if (!success || !inst_mgr->read_contents_file(_contents_file_pathname)) {
nout << "Couldn't read " << _contents_file_pathname << "\n";
if (!success || !inst_mgr->read_contents_file(_temp_contents_file->get_filename())) {
nout << "Couldn't read " << *_temp_contents_file << "\n";
// Maybe we can read an already-downloaded contents.xml file.
string standard_filename = inst_mgr->get_root_dir() + "/contents.xml";
if (!inst_mgr->read_contents_file(standard_filename)) {
// Couldn't even read that. Fail.
report_done(false);
unlink(_contents_file_pathname.c_str());
delete _temp_contents_file;
_temp_contents_file = NULL;
return;
}
}
@ -214,7 +216,8 @@ contents_file_download_finished(bool success) {
// The file is correctly installed by now; we can remove the
// temporary file.
unlink(_contents_file_pathname.c_str());
delete _temp_contents_file;
_temp_contents_file = NULL;
download_desc_file();
}

View File

@ -21,6 +21,7 @@
#include "get_tinyxml.h"
class P3DInstance;
class P3DTemporaryFile;
////////////////////////////////////////////////////////////////////
// Class : P3DPackage
@ -107,7 +108,7 @@ private:
string _package_fullname;
string _package_dir;
string _contents_file_pathname;
P3DTemporaryFile *_temp_contents_file;
string _desc_file_basename;
string _desc_file_pathname;

View File

@ -978,7 +978,7 @@ set_p3d_filename(P3DCInstance *inst, TiXmlElement *xfparams) {
xarg = xarg->NextSiblingElement("arg");
}
PyObject *result = PyObject_CallMethod
(_runner, (char *)"setP3DFilename", (char *)"sOOi", p3d_filename.c_str(),
token_list, arg_list, inst->get_instance_id());

View File

@ -650,6 +650,8 @@ start_p3dpython(P3DInstance *inst) {
return;
}
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
_python_root_dir = inst->_panda3d->get_package_dir();
// We'll be changing the directory to the standard start directory
@ -699,7 +701,7 @@ start_p3dpython(P3DInstance *inst) {
const char *keep[] = {
"TMP", "TEMP", "HOME", "USER",
#ifdef _WIN32
"SYSTEMROOT", "USERPROFILE", "COMSPEC",
"SYSTEMROOT", "USERPROFILE", "COMSPEC", "PANDA_ROOT",
#endif
NULL
};
@ -766,7 +768,17 @@ start_p3dpython(P3DInstance *inst) {
log_basename = inst->get_fparams().lookup_token("log_basename");
}
// However, it is always written into the temp directory only; the
bool console_output = (inst->get_fparams().lookup_token_int("console_output") != 0);
#ifdef P3D_PLUGIN_LOG_BASENAME3
if (log_basename.empty()) {
// No log_basename specified for the app; use the compiled-in
// default.
log_basename = P3D_PLUGIN_LOG_BASENAME3;
}
#endif
// However, it is always written into the log directory only; the
// user may not override the log file to put it anywhere else.
size_t slash = log_basename.rfind('/');
if (slash != string::npos) {
@ -779,32 +791,24 @@ start_p3dpython(P3DInstance *inst) {
}
#endif // _WIN32
if (!log_basename.empty()) {
#ifdef _WIN32
static const size_t buffer_size = 4096;
char buffer[buffer_size];
if (GetTempPath(buffer_size, buffer) != 0) {
_output_filename = buffer;
}
#else
_output_filename = "/tmp/";
#endif // _WIN32
_output_filename += log_basename;
if (!console_output && !log_basename.empty()) {
_log_pathname = inst_mgr->get_log_directory();
_log_pathname += log_basename;
// We always tack on the extension ".log", to make it even more
// difficult to overwrite a system file.
_output_filename += ".log";
_log_pathname += ".log";
}
nout << "Attempting to start python from " << p3dpython << "\n";
#ifdef _WIN32
_p3dpython_handle = win_create_process
(p3dpython, start_dir, env, _output_filename,
(p3dpython, start_dir, env, _log_pathname,
_pipe_read, _pipe_write);
bool started_p3dpython = (_p3dpython_handle != INVALID_HANDLE_VALUE);
#else
_p3dpython_pid = posix_create_process
(p3dpython, start_dir, env, _output_filename,
(p3dpython, start_dir, env, _log_pathname,
_pipe_read, _pipe_write);
bool started_p3dpython = (_p3dpython_pid > 0);
#endif
@ -976,7 +980,7 @@ rt_terminate() {
// Access: Private, Static
// Description: Creates a sub-process to run the named program
// executable, with the indicated environment string.
// Standard error is logged to output_filename, if that
// Standard error is logged to log_pathname, if that
// string is nonempty.
//
// Opens the two HandleStreams as the read and write
@ -988,7 +992,7 @@ rt_terminate() {
////////////////////////////////////////////////////////////////////
HANDLE P3DSession::
win_create_process(const string &program, const string &start_dir,
const string &env, const string &output_filename,
const string &env, const string &log_pathname,
HandleStream &pipe_read, HandleStream &pipe_write) {
// Create a bi-directional pipe to communicate with the sub-process.
@ -1013,19 +1017,19 @@ win_create_process(const string &program, const string &start_dir,
}
HANDLE error_handle = GetStdHandle(STD_ERROR_HANDLE);
bool got_output_filename = !output_filename.empty();
if (got_output_filename) {
bool got_log_pathname = !log_pathname.empty();
if (got_log_pathname) {
// Open the named file for output and redirect the child's stderr
// into it.
HANDLE handle = CreateFile
(output_filename.c_str(), GENERIC_WRITE,
(log_pathname.c_str(), GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, CREATE_ALWAYS, 0, NULL);
if (handle != INVALID_HANDLE_VALUE) {
error_handle = handle;
SetHandleInformation(error_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
} else {
nout << "Unable to open " << output_filename << "\n";
nout << "Unable to open " << log_pathname << "\n";
}
}
@ -1063,7 +1067,7 @@ win_create_process(const string &program, const string &start_dir,
// Close the pipe handles that are now owned by the child.
CloseHandle(w_from);
CloseHandle(r_to);
if (got_output_filename) {
if (got_log_pathname) {
CloseHandle(error_handle);
}
@ -1099,7 +1103,7 @@ win_create_process(const string &program, const string &start_dir,
////////////////////////////////////////////////////////////////////
int P3DSession::
posix_create_process(const string &program, const string &start_dir,
const string &env, const string &output_filename,
const string &env, const string &log_pathname,
HandleStream &pipe_read, HandleStream &pipe_write) {
// Create a bi-directional pipe to communicate with the sub-process.
int to_fd[2];
@ -1124,14 +1128,14 @@ posix_create_process(const string &program, const string &start_dir,
if (child == 0) {
// Here we are in the child process.
bool got_output_filename = !output_filename.empty();
if (got_output_filename) {
bool got_log_pathname = !log_pathname.empty();
if (got_log_pathname) {
// Open the named file for output and redirect the child's stderr
// into it.
int logfile_fd = open(output_filename.c_str(),
int logfile_fd = open(log_pathname.c_str(),
O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (logfile_fd < 0) {
nout << "Unable to open " << output_filename << "\n";
nout << "Unable to open " << log_pathname << "\n";
} else {
dup2(logfile_fd, STDERR_FILENO);
close(logfile_fd);

View File

@ -90,7 +90,7 @@ private:
int _session_id;
string _session_key;
string _python_version;
string _output_filename;
string _log_pathname;
string _python_root_dir;
string _start_dir;

View File

@ -0,0 +1,24 @@
// Filename: p3dTemporaryFile.I
// Created by: drose (19Aug09)
//
////////////////////////////////////////////////////////////////////
//
// 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."
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: P3DTemporaryFile::get_filename
// Access: Public
// Description: Returns the temporary filename.
////////////////////////////////////////////////////////////////////
inline const string &P3DTemporaryFile::
get_filename() const {
return _filename;
}

View File

@ -0,0 +1,38 @@
// Filename: p3dTemporaryFile.cxx
// Created by: drose (19Aug09)
//
////////////////////////////////////////////////////////////////////
//
// 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 "p3dTemporaryFile.h"
#include "p3dInstanceManager.h"
////////////////////////////////////////////////////////////////////
// Function: P3DTemporaryFile::Constructor
// Access: Public
// Description: Constructs a new, unique temporary filename.
////////////////////////////////////////////////////////////////////
P3DTemporaryFile::
P3DTemporaryFile(const string &extension) {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
_filename = inst_mgr->make_temp_filename(extension);
}
////////////////////////////////////////////////////////////////////
// Function: P3DTemporaryFile::Destructor
// Access: Public
// Description: Deletes the temporary file, if it exists.
////////////////////////////////////////////////////////////////////
P3DTemporaryFile::
~P3DTemporaryFile() {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
inst_mgr->release_temp_filename(_filename);
}

View File

@ -0,0 +1,47 @@
// Filename: p3dTemporaryFile.h
// Created by: drose (19Aug09)
//
////////////////////////////////////////////////////////////////////
//
// 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 P3DTEMPORARYFILE_H
#define P3DTEMPORARYFILE_H
#include "p3d_plugin_common.h"
////////////////////////////////////////////////////////////////////
// Class : P3DTemporaryFile
// Description : This represents a temporary filename for some
// transitory purpose. This returns a filename which is
// guaranteed to be unique at the time the constructor
// was called.
//
// The file on disk, if it exists, will automatically be
// deleted when the destructor is called.
////////////////////////////////////////////////////////////////////
class P3DTemporaryFile {
public:
P3DTemporaryFile(const string &extension);
~P3DTemporaryFile();
inline const string &get_filename() const;
private:
string _filename;
};
inline ostream &operator << (ostream &out, P3DTemporaryFile &tfile) {
return out << tfile.get_filename();
}
#include "p3dTemporaryFile.I"
#endif

View File

@ -17,13 +17,23 @@
// Function: P3DWindowParams::get_window_type
// Access: Public
// Description: Returns the window_type that was passed to the
// constructor.
// constructor, or to set_window_type().
////////////////////////////////////////////////////////////////////
inline P3D_window_type P3DWindowParams::
get_window_type() const {
return _window_type;
}
////////////////////////////////////////////////////////////////////
// Function: P3DWindowParams::set_window_type
// Access: Public
// Description: Changes the window_type.
////////////////////////////////////////////////////////////////////
inline void P3DWindowParams::
set_window_type(P3D_window_type window_type) {
_window_type = window_type;
}
////////////////////////////////////////////////////////////////////
// Function: P3DWindowParams::get_window_x
// Access: Public

View File

@ -35,6 +35,8 @@ public:
void operator = (const P3DWindowParams &other);
inline P3D_window_type get_window_type() const;
inline void set_window_type(P3D_window_type window_type);
inline int get_win_x() const;
inline int get_win_y() const;
inline int get_win_width() const;

View File

@ -32,14 +32,10 @@
bool initialized_lock = false;
LOCK _api_lock;
ofstream logfile;
string plugin_output_filename;
ostream *nout_stream;
bool
P3D_initialize(int api_version, const char *contents_filename,
const char *download_url, const char *platform) {
const char *download_url, const char *platform,
const char *log_directory, const char *log_basename) {
if (api_version != P3D_API_VERSION) {
// Can't accept an incompatible version.
return false;
@ -63,36 +59,17 @@ P3D_initialize(int api_version, const char *contents_filename,
platform = "";
}
#ifdef P3D_PLUGIN_LOGFILE2
string logfilename = P3D_PLUGIN_LOGFILE2;
#else
string logfilename;
#endif // P3D_PLUGIN_LOGFILE2
if (logfilename.empty()) {
#ifdef _WIN32
static const size_t buffer_size = 4096;
char buffer[buffer_size];
if (GetTempPath(buffer_size, buffer) != 0) {
logfilename = buffer;
logfilename += "panda3d.2.log";
}
#else
logfilename = "/tmp/panda3d.2.log";
#endif // _WIN32
if (log_directory == NULL) {
log_directory = "";
}
cerr << "logfile: " << logfilename << "\n";
nout_stream = &cerr;
logfile.open(logfilename.c_str(), ios::out | ios::trunc);
if (logfile) {
logfile.setf(ios::unitbuf);
nout_stream = &logfile;
if (log_basename == NULL) {
log_basename = "";
}
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
bool result = inst_mgr->initialize(contents_filename, download_url, platform);
bool result = inst_mgr->initialize(contents_filename, download_url,
platform, log_directory, log_basename);
RELEASE_LOCK(_api_lock);
return result;
}

View File

@ -101,13 +101,26 @@ extern "C" {
If platform is not NULL or empty, it specifies the current platform
string; otherwise, the compiled-in default is used.
If log_directory is not NULL or empty, it specifies the directory
into which all log files will be written; otherwise, the
compiled-in default will be used, or the system temp directory if
no default is compiled in.
If log_basename is not NULL or empty, it specifies the filename in
log_directory to which the core API's logfile output will be
written. Otherwise, the compiled-in default is used; if there is
no compiled-in default, no logfile output will be generated by the
core API. Note that the individual instances also have their own
log_basename values.
This function returns true if the core API is valid and uses a
compatible API, false otherwise. If it returns false, the host
should not call any more functions in this API, and should
immediately unload the DLL and (if possible) download a new one. */
typedef bool
P3D_initialize_func(int api_version, const char *contents_filename,
const char *download_url, const char *platform);
const char *download_url, const char *platform,
const char *log_directory, const char *log_basename);
/* This function should be called to unload the core API. It will
release all internally-allocated memory and return the core API to

View File

@ -38,8 +38,7 @@
using namespace std;
// Appears in p3d_plugin.cxx.
extern string plugin_output_filename;
// Appears in p3dInstanceManager.cxx.
extern ostream *nout_stream;
#define nout (*nout_stream)

View File

@ -20,6 +20,7 @@
#include "p3dSession.cxx"
#include "p3dSplashWindow.cxx"
#include "p3dStringObject.cxx"
#include "p3dTemporaryFile.cxx"
#include "p3dToplevelObject.cxx"
#include "p3dUndefinedObject.cxx"
#include "p3dWinSplashWindow.cxx"

View File

@ -13,8 +13,10 @@
/* The filename(s) to generate output to when the plugin is running.
For debugging purposes only. */
#$[]define P3D_PLUGIN_LOGFILE1 "$[subst \,\\,$[osfilename $[P3D_PLUGIN_LOGFILE1]]]"
#$[]define P3D_PLUGIN_LOGFILE2 "$[subst \,\\,$[osfilename $[P3D_PLUGIN_LOGFILE2]]]"
#$[]define P3D_PLUGIN_LOG_DIRECTORY "$[subst \,\\,$[osfilename $[P3D_PLUGIN_LOG_DIRECTORY]]]"
#$[]define P3D_PLUGIN_LOG_BASENAME1 "$[P3D_PLUGIN_LOG_BASENAME1]"
#$[]define P3D_PLUGIN_LOG_BASENAME2 "$[P3D_PLUGIN_LOG_BASENAME2]"
#$[]define P3D_PLUGIN_LOG_BASENAME3 "$[P3D_PLUGIN_LOG_BASENAME3]"
/* For development only: the location at which p3dpython.exe can be
found. Empty string for the default. */

View File

@ -928,7 +928,7 @@ do_load_plugin() {
#endif // P3D_PLUGIN_P3D_PLUGIN
nout << "Attempting to load core API from " << pathname << "\n";
if (!load_plugin(pathname, "", "", "")) {
if (!load_plugin(pathname, "", "", "", "", "")) {
nout << "Unable to launch core API in " << pathname << "\n";
return;
}

View File

@ -30,26 +30,44 @@ static bool logfile_is_open = false;
static void
open_logfile() {
if (!logfile_is_open) {
#ifdef P3D_PLUGIN_LOGFILE1
string logfilename = P3D_PLUGIN_LOGFILE1;
#else
string logfilename;
#endif // P3D_PLUGIN_LOGFILE1
// Note that this logfile name may not be specified at runtime. It
// must be compiled in if it is specified at all.
if (logfilename.empty()) {
string log_basename;
#ifdef P3D_PLUGIN_LOG_BASENAME1
log_basename = P3D_PLUGIN_LOG_BASENAME1;
#endif
if (!log_basename.empty()) {
// Get the log directory.
string log_directory;
#ifdef P3D_PLUGIN_LOG_DIRECTORY
log_directory = P3D_PLUGIN_LOG_DIRECTORY;
#endif
if (log_directory.empty()) {
#ifdef _WIN32
static const size_t buffer_size = 4096;
char buffer[buffer_size];
if (GetTempPath(buffer_size, buffer) != 0) {
logfilename = buffer;
logfilename += "panda3d.1.log";
}
static const size_t buffer_size = 4096;
char buffer[buffer_size];
if (GetTempPath(buffer_size, buffer) != 0) {
log_directory = buffer;
}
#else
logfilename = "/tmp/panda3d.1.log";
log_directory = "/tmp/";
#endif // _WIN32
}
// Construct the full logfile pathname.
string log_pathname = log_directory;
log_pathname += log_basename;
log_pathname += ".log";
logfile.open(log_pathname.c_str());
logfile.setf(ios::unitbuf);
}
logfile.open(logfilename.c_str());
logfile.setf(ios::unitbuf);
// If we didn't have a logfile name compiled in, we throw away log
// output by the simple expedient of never actually opening the
// ofstream.
logfile_is_open = true;
}
}

View File

@ -44,6 +44,7 @@
Panda3D::
Panda3D() {
_root_dir = find_root_dir();
_reporting_download = false;
}
////////////////////////////////////////////////////////////////////
@ -60,7 +61,7 @@ run(int argc, char *argv[]) {
// We prefix a "+" sign to tell gnu getopt not to parse options
// following the first not-option parameter. (These will be passed
// into the sub-process.)
const char *optstr = "+mu:p:ft:s:o:h";
const char *optstr = "+mu:p:ft:s:o:l:h";
bool allow_multiple = false;
string download_url = P3D_PLUGIN_DOWNLOAD;
@ -69,7 +70,7 @@ run(int argc, char *argv[]) {
P3D_window_type window_type = P3D_WT_toplevel;
int win_x = 0, win_y = 0;
int win_width = 0, win_height = 0;
int win_width = 640, win_height = 480;
int flag = getopt(argc, argv, optstr);
@ -120,6 +121,11 @@ run(int argc, char *argv[]) {
}
break;
case 'l':
_log_dirname = Filename::from_os_specific(optarg).to_os_specific();
_log_basename = "panda3d";
break;
case 'h':
case '?':
case '+':
@ -321,7 +327,6 @@ get_plugin(const string &download_url, const string &this_platform, bool force_d
// Couldn't read it, so go get it.
string url = download_url;
url += "contents.xml";
cerr << "Getting URL " << url << "\n";
HTTPClient *http = HTTPClient::get_global_ptr();
PT(HTTPChannel) channel = http->get_document(url);
@ -436,7 +441,8 @@ get_core_api(const Filename &contents_filename, const string &download_url,
#endif // P3D_PLUGIN_P3D_PLUGIN
if (!load_plugin(pathname, contents_filename.to_os_specific(),
download_url, this_platform)) {
download_url, this_platform, _log_dirname,
_log_basename)) {
cerr << "Unable to launch core API in " << pathname << "\n" << flush;
return false;
}
@ -481,7 +487,6 @@ handle_request(P3D_request *request) {
switch (request->_request_type) {
case P3D_RT_stop:
cerr << "Got P3D_RT_stop\n";
delete_instance(request->_instance);
#ifdef _WIN32
// Post a silly message to spin the event loop.
@ -491,8 +496,6 @@ handle_request(P3D_request *request) {
break;
case P3D_RT_get_url:
cerr << "Got P3D_RT_get_url: " << request->_request._get_url._url
<< "\n";
{
int unique_id = request->_request._get_url._unique_id;
const string &url = request->_request._get_url._url;
@ -504,8 +507,6 @@ handle_request(P3D_request *request) {
break;
case P3D_RT_post_url:
cerr << "Got P3D_RT_post_url: " << request->_request._post_url._url
<< "\n";
{
int unique_id = request->_request._post_url._unique_id;
const string &url = request->_request._post_url._url;
@ -519,9 +520,15 @@ handle_request(P3D_request *request) {
break;
case P3D_RT_notify:
cerr << "Got P3D_RT_notify: " << request->_request._notify._message
<< "\n";
// Ignore notifications.
{
if (strcmp(request->_request._notify._message, "ondownloadnext") == 0) {
// Tell the user we're downloading a package.
report_downloading_package(request->_instance);
} else if (strcmp(request->_request._notify._message, "ondownloadcomplete") == 0) {
// Tell the user we're done downloading.
report_download_complete(request->_instance);
}
}
break;
default:
@ -624,6 +631,30 @@ create_instance(const string &p3d, P3D_window_type window_type,
is_local = true;
}
// Build up the token list.
pvector<P3D_token> tokens;
string log_basename;
if (!_log_dirname.empty()) {
// Generate output to a logfile.
log_basename = p3d_filename.get_basename_wo_extension();
P3D_token token;
token._keyword = "log_basename";
token._value = log_basename.c_str();
tokens.push_back(token);
} else {
// Send output to the console.
P3D_token token;
token._keyword = "console_output";
token._value = "1";
tokens.push_back(token);
}
P3D_token *tokens_p;
size_t num_tokens = tokens.size();
if (!tokens.empty()) {
tokens_p = &tokens[0];
}
// Build up the argument list, beginning with the p3d_filename.
pvector<const char *> argv;
argv.push_back(os_p3d_filename.c_str());
@ -631,13 +662,15 @@ create_instance(const string &p3d, P3D_window_type window_type,
argv.push_back(args[i]);
}
P3D_instance *inst = P3D_new_instance(NULL, NULL, 0,
P3D_instance *inst = P3D_new_instance(NULL, tokens_p, num_tokens,
argv.size(), &argv[0], NULL);
if (inst != NULL) {
// We call start() first, to give the core API a chance to notice
// the "hidden" attrib before we set the window parameters.
P3D_instance_start(inst, is_local, os_p3d_filename.c_str());
P3D_instance_setup_window
(inst, window_type, win_x, win_y, win_width, win_height, parent_window);
P3D_instance_start(inst, is_local, os_p3d_filename.c_str());
}
return inst;
@ -682,10 +715,10 @@ usage() {
<< " panda3d -m [opts] file_a.p3d file_b.p3d [file_c.p3d ...]\n\n"
<< "This program is used to execute a Panda3D application bundle stored\n"
<< "in a .p3d file. In the first form, without a -m option, it\n"
<< "in a .p3d file. In the first form, without the -m option, it\n"
<< "executes one application; remaining arguments following the\n"
<< "application name are passed into the application. In the second\n"
<< "form, with a -m option, it can execute multiple applications\n"
<< "form, with the -m option, it can execute multiple applications\n"
<< "simultaneously, though in this form arguments cannot be passed into\n"
<< "the applications.\n\n"
@ -708,6 +741,11 @@ usage() {
<< " Specify the position (origin) of the graphic window on the\n"
<< " screen, or on the parent window.\n\n"
<< " -l log_dirname\n"
<< " Specify the full path to the directory in which log files are\n"
<< " to be written. If this is not specified, the default is to send\n"
<< " the application output to the console.\n\n"
<< " -f\n"
<< " Force a HTTP contact to the Panda3D download server, to check\n"
<< " if a new version is available. Normally, this is done only\n"
@ -743,6 +781,44 @@ parse_int_pair(char *arg, int &x, int &y) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: Panda3D::report_downloading_package
// Access: Private
// Description: Tells the user we have to download a package.
////////////////////////////////////////////////////////////////////
void Panda3D::
report_downloading_package(P3D_instance *instance) {
P3D_object *obj = P3D_instance_get_panda_script_object(instance);
P3D_object *display_name = P3D_object_get_property(obj, "downloadPackageDisplayName");
if (display_name == NULL) {
cerr << "no name: " << obj << "\n";
return;
}
int name_length = P3D_object_get_string(display_name, NULL, 0);
char *name = new char[name_length + 1];
P3D_object_get_string(display_name, name, name_length + 1);
cerr << "Downloading " << name << "\n";
delete[] name;
P3D_object_decref(display_name);
_reporting_download = true;
}
////////////////////////////////////////////////////////////////////
// Function: Panda3D::report_download_complete
// Access: Private
// Description: Tells the user we're done downloading packages
////////////////////////////////////////////////////////////////////
void Panda3D::
report_download_complete(P3D_instance *instance) {
if (_reporting_download) {
cerr << "Download complete.\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: Panda3D::URLGetter::Constructor
// Access: Public
@ -758,8 +834,6 @@ URLGetter(P3D_instance *instance, int unique_id,
{
HTTPClient *http = HTTPClient::get_global_ptr();
cerr << "Getting URL " << _url << "\n";
_channel = http->make_channel(false);
if (_post_data.empty()) {
_channel->begin_get_document(_url);
@ -815,8 +889,6 @@ run() {
status = P3D_RC_generic_error;
}
cerr << "Error getting URL " << _url << "\n";
} else {
cerr << "Done getting URL " << _url << ", got " << _bytes_sent << " bytes\n";
}
P3D_instance_feed_url_stream

View File

@ -65,9 +65,15 @@ private:
void usage();
bool parse_int_pair(char *arg, int &x, int &y);
void report_downloading_package(P3D_instance *instance);
void report_download_complete(P3D_instance *instance);
private:
string _root_dir;
string _log_dirname;
string _log_basename;
FileSpec _core_api_dll;
bool _reporting_download;
typedef pset<P3D_instance *> Instances;
Instances _instances;

View File

@ -84,7 +84,7 @@ def makePackedApp(args):
elif option == '-h':
print __doc__
sys.exit(1)
if not args:
raise ArgumentError, "No destination app specified. Use:\npackp3d.py app.p3d"
@ -133,10 +133,6 @@ def main(appRunner):
""" This function is called when this module is invoked as
packp3d.p3d. """
print "args = %s" % (appRunner.argv,)
vfs = VirtualFileSystem.getGlobalPtr()
print "cwd = %s, %s" % (vfs.getCwd(), ExecutionEnvironment.getCwd())
print "sys.path = %s" % (sys.path,)
try:
makePackedApp(appRunner.argv[1:])
except ArgumentError, e:
@ -145,7 +141,6 @@ def main(appRunner):
sys.exit(0)
if __name__ == '__main__':
print "sys.path = %s" % (sys.path,)
try:
makePackedApp(sys.argv[1:])
except ArgumentError, e:

View File

@ -244,7 +244,6 @@ class AppRunner(DirectObject):
same page with the Panda3D plugin. """
self.dom = dom
print "setBrowserScriptObject(%s)" % (dom)
# Now evaluate any deferred expressions.
for expression in self.deferredEvals:
@ -334,11 +333,6 @@ class AppRunner(DirectObject):
settings, for future windows; or applies them directly to the
main window if the window has already been opened. """
print "session %s, nextScriptId = %s" % (self.sessionId, self.nextScriptId)
print "setupWindow %s, %s, %s, %s, %s, %s, %s" % (windowType, x, y, width, height, parent, subprocessWindow)
if self.started and base.win:
# If we've already got a window, this must be a
# resize/reposition request.

View File

@ -248,7 +248,7 @@ open = file
def listdir(path):
""" Implements os.listdir over vfs. """
files = []
dirlist = _vfs.scanDirectory(path)
dirlist = _vfs.scanDirectory(pm.Filename.fromOsSpecific(path))
if dirlist is None:
message = 'No such file or directory: %s' % (path)
raise OSError, message