/** * 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." * * @file load_plugin.cxx * @author drose * @date 2009-06-19 */ #include "load_plugin.h" #include "p3d_plugin_config.h" #include "is_pathsep.h" #include "wstring_encode.h" #include #include #ifndef _WIN32 #include #endif using std::string; #ifdef _WIN32 static const string dll_ext = ".dll"; #elif defined(__APPLE__) static const string dll_ext = ".dylib"; #else static const string dll_ext = ".so"; #endif static const string default_plugin_filename = "p3d_plugin"; P3D_initialize_func *P3D_initialize_ptr; P3D_finalize_func *P3D_finalize_ptr; P3D_set_plugin_version_func *P3D_set_plugin_version_ptr; P3D_set_super_mirror_func *P3D_set_super_mirror_ptr; P3D_new_instance_func *P3D_new_instance_ptr; P3D_instance_start_func *P3D_instance_start_ptr; P3D_instance_start_stream_func *P3D_instance_start_stream_ptr; P3D_instance_finish_func *P3D_instance_finish_ptr; P3D_instance_setup_window_func *P3D_instance_setup_window_ptr; P3D_object_get_type_func *P3D_object_get_type_ptr; P3D_object_get_bool_func *P3D_object_get_bool_ptr; P3D_object_get_int_func *P3D_object_get_int_ptr; P3D_object_get_float_func *P3D_object_get_float_ptr; P3D_object_get_string_func *P3D_object_get_string_ptr; P3D_object_get_repr_func *P3D_object_get_repr_ptr; P3D_object_get_property_func *P3D_object_get_property_ptr; P3D_object_set_property_func *P3D_object_set_property_ptr; P3D_object_has_method_func *P3D_object_has_method_ptr; P3D_object_call_func *P3D_object_call_ptr; P3D_object_eval_func *P3D_object_eval_ptr; P3D_object_incref_func *P3D_object_incref_ptr; P3D_object_decref_func *P3D_object_decref_ptr; P3D_make_class_definition_func *P3D_make_class_definition_ptr; P3D_new_undefined_object_func *P3D_new_undefined_object_ptr; P3D_new_none_object_func *P3D_new_none_object_ptr; P3D_new_bool_object_func *P3D_new_bool_object_ptr; P3D_new_int_object_func *P3D_new_int_object_ptr; P3D_new_float_object_func *P3D_new_float_object_ptr; P3D_new_string_object_func *P3D_new_string_object_ptr; P3D_instance_get_panda_script_object_func *P3D_instance_get_panda_script_object_ptr; P3D_instance_set_browser_script_object_func *P3D_instance_set_browser_script_object_ptr; P3D_instance_get_request_func *P3D_instance_get_request_ptr; P3D_check_request_func *P3D_check_request_ptr; P3D_request_finish_func *P3D_request_finish_ptr; P3D_instance_feed_url_stream_func *P3D_instance_feed_url_stream_ptr; P3D_instance_handle_event_func *P3D_instance_handle_event_ptr; #ifdef _WIN32 static HMODULE module = nullptr; #else static void *module = nullptr; #endif static bool plugin_loaded = false; static bool dso_needs_unload = false; /** * Returns the default plugin filename, without any directory path (but * including the extension appropriate to this platform). */ string get_plugin_basename() { return default_plugin_filename + dll_ext; } /** * Returns the position in the string of the dot before the filename * extension; that is, the position of the rightmost dot that is right of the * rightmost slash (or backslash, on Windows). Returns string::npos if there * is no extension. */ static size_t find_extension_dot(const string &filename) { size_t p = filename.length(); while (p > 0 && !is_pathsep(filename[p - 1])) { --p; if (filename[p] == '.') { return p; } } return string::npos; } // Forward reference for function defined below. static void unload_dso(); /** * Loads the plugin and assigns all of the function pointers. Returns true on * success, false on failure. If load_plugin() has already been called * successfully, this returns true immediately, without parsing any * parameters. * * If p3d_plugin_filename is empty, the module is assumed to be already loaded * (or statically linked in), and the symbols are located within the current * address space. */ bool load_plugin(const string &p3d_plugin_filename, const string &contents_filename, const string &host_url, P3D_verify_contents verify_contents, const string &platform, const string &log_directory, const string &log_basename, bool trusted_environment, bool console_environment, const string &root_dir, const string &host_dir, const string &start_dir, std::ostream &logfile) { if (plugin_loaded) { return true; } string filename = p3d_plugin_filename; #ifdef _WIN32 assert(module == nullptr); if (filename.empty()) { // If no filename is supplied, look within our existing address space. module = GetModuleHandle(nullptr); dso_needs_unload = false; } else { // If a filename is supplied, attempt to load it as a dynamic library. // On Windows, the filename passed to LoadLibrary() must have an // extension, or a default ".DLL" will be implicitly added. If the file // actually has no extension, we must add "." to avoid this. // Check whether the filename has an extension. size_t extension_dot = find_extension_dot(filename); if (extension_dot == string::npos) { // No extension. filename += "."; } SetErrorMode(0); std::wstring filename_w; if (string_to_wstring(filename_w, filename)) { module = LoadLibraryW(filename_w.c_str()); } dso_needs_unload = true; } if (module == nullptr) { // Couldn't load the DLL. logfile << "Couldn't load " << filename << ", error = " << GetLastError() << "\n"; return false; } #define get_func GetProcAddress #else // _WIN32 // Posix case. assert(module == nullptr); if (filename.empty()) { module = dlopen(nullptr, RTLD_LAZY | RTLD_LOCAL); } else { module = dlopen(filename.c_str(), RTLD_LAZY | RTLD_LOCAL); } if (module == nullptr) { // Couldn't load the .so. const char *message = dlerror(); if (message == nullptr) { message = "No error"; } logfile << "Couldn't load " << filename << ": " << message << "\n"; return false; } dso_needs_unload = true; #define get_func dlsym #endif // _WIN32 // Now get all of the function pointers. P3D_initialize_ptr = (P3D_initialize_func *)get_func(module, "P3D_initialize"); P3D_finalize_ptr = (P3D_finalize_func *)get_func(module, "P3D_finalize"); P3D_set_plugin_version_ptr = (P3D_set_plugin_version_func *)get_func(module, "P3D_set_plugin_version"); P3D_set_super_mirror_ptr = (P3D_set_super_mirror_func *)get_func(module, "P3D_set_super_mirror"); P3D_new_instance_ptr = (P3D_new_instance_func *)get_func(module, "P3D_new_instance"); P3D_instance_start_ptr = (P3D_instance_start_func *)get_func(module, "P3D_instance_start"); P3D_instance_start_stream_ptr = (P3D_instance_start_stream_func *)get_func(module, "P3D_instance_start_stream"); P3D_instance_finish_ptr = (P3D_instance_finish_func *)get_func(module, "P3D_instance_finish"); P3D_instance_setup_window_ptr = (P3D_instance_setup_window_func *)get_func(module, "P3D_instance_setup_window"); P3D_object_get_type_ptr = (P3D_object_get_type_func *)get_func(module, "P3D_object_get_type"); P3D_object_get_bool_ptr = (P3D_object_get_bool_func *)get_func(module, "P3D_object_get_bool"); P3D_object_get_int_ptr = (P3D_object_get_int_func *)get_func(module, "P3D_object_get_int"); P3D_object_get_float_ptr = (P3D_object_get_float_func *)get_func(module, "P3D_object_get_float"); P3D_object_get_string_ptr = (P3D_object_get_string_func *)get_func(module, "P3D_object_get_string"); P3D_object_get_repr_ptr = (P3D_object_get_repr_func *)get_func(module, "P3D_object_get_repr"); P3D_object_get_property_ptr = (P3D_object_get_property_func *)get_func(module, "P3D_object_get_property"); P3D_object_set_property_ptr = (P3D_object_set_property_func *)get_func(module, "P3D_object_set_property"); P3D_object_has_method_ptr = (P3D_object_has_method_func *)get_func(module, "P3D_object_has_method"); P3D_object_call_ptr = (P3D_object_call_func *)get_func(module, "P3D_object_call"); P3D_object_eval_ptr = (P3D_object_eval_func *)get_func(module, "P3D_object_eval"); P3D_object_incref_ptr = (P3D_object_incref_func *)get_func(module, "P3D_object_incref"); P3D_object_decref_ptr = (P3D_object_decref_func *)get_func(module, "P3D_object_decref"); P3D_make_class_definition_ptr = (P3D_make_class_definition_func *)get_func(module, "P3D_make_class_definition"); P3D_new_undefined_object_ptr = (P3D_new_undefined_object_func *)get_func(module, "P3D_new_undefined_object"); P3D_new_none_object_ptr = (P3D_new_none_object_func *)get_func(module, "P3D_new_none_object"); P3D_new_bool_object_ptr = (P3D_new_bool_object_func *)get_func(module, "P3D_new_bool_object"); P3D_new_int_object_ptr = (P3D_new_int_object_func *)get_func(module, "P3D_new_int_object"); P3D_new_float_object_ptr = (P3D_new_float_object_func *)get_func(module, "P3D_new_float_object"); P3D_new_string_object_ptr = (P3D_new_string_object_func *)get_func(module, "P3D_new_string_object"); P3D_instance_get_panda_script_object_ptr = (P3D_instance_get_panda_script_object_func *)get_func(module, "P3D_instance_get_panda_script_object"); P3D_instance_set_browser_script_object_ptr = (P3D_instance_set_browser_script_object_func *)get_func(module, "P3D_instance_set_browser_script_object"); P3D_instance_get_request_ptr = (P3D_instance_get_request_func *)get_func(module, "P3D_instance_get_request"); P3D_check_request_ptr = (P3D_check_request_func *)get_func(module, "P3D_check_request"); P3D_request_finish_ptr = (P3D_request_finish_func *)get_func(module, "P3D_request_finish"); P3D_instance_feed_url_stream_ptr = (P3D_instance_feed_url_stream_func *)get_func(module, "P3D_instance_feed_url_stream"); P3D_instance_handle_event_ptr = (P3D_instance_handle_event_func *)get_func(module, "P3D_instance_handle_event"); #undef get_func // Successfully loaded. plugin_loaded = true; if (!init_plugin(contents_filename, host_url, verify_contents, platform, log_directory, log_basename, trusted_environment, console_environment, root_dir, host_dir, start_dir, logfile)) { unload_dso(); return false; } return true; } /** * Ensures all the required function pointers have been set, and then calls * P3D_initialize() on the recently-loaded plugin. Returns true on success, * false on failure. * * It is not necessary to call this after calling load_plugin(); it is called * implicitly. */ bool init_plugin(const string &contents_filename, const string &host_url, P3D_verify_contents verify_contents, const string &platform, const string &log_directory, const string &log_basename, bool trusted_environment, bool console_environment, const string &root_dir, const string &host_dir, const string &start_dir, std::ostream &logfile) { // Ensure that all of the function pointers have been found. if (P3D_initialize_ptr == nullptr || P3D_finalize_ptr == nullptr || P3D_set_plugin_version_ptr == nullptr || P3D_set_super_mirror_ptr == nullptr || P3D_new_instance_ptr == nullptr || P3D_instance_start_ptr == nullptr || P3D_instance_start_stream_ptr == nullptr || P3D_instance_finish_ptr == nullptr || P3D_instance_setup_window_ptr == nullptr || P3D_object_get_type_ptr == nullptr || P3D_object_get_bool_ptr == nullptr || P3D_object_get_int_ptr == nullptr || P3D_object_get_float_ptr == nullptr || P3D_object_get_string_ptr == nullptr || P3D_object_get_repr_ptr == nullptr || P3D_object_get_property_ptr == nullptr || P3D_object_set_property_ptr == nullptr || P3D_object_has_method_ptr == nullptr || P3D_object_call_ptr == nullptr || P3D_object_eval_ptr == nullptr || P3D_object_incref_ptr == nullptr || P3D_object_decref_ptr == nullptr || P3D_make_class_definition_ptr == nullptr || P3D_new_undefined_object_ptr == nullptr || P3D_new_none_object_ptr == nullptr || P3D_new_bool_object_ptr == nullptr || P3D_new_int_object_ptr == nullptr || P3D_new_float_object_ptr == nullptr || P3D_new_string_object_ptr == nullptr || P3D_instance_get_panda_script_object_ptr == nullptr || P3D_instance_set_browser_script_object_ptr == nullptr || P3D_instance_get_request_ptr == nullptr || P3D_check_request_ptr == nullptr || P3D_request_finish_ptr == nullptr || P3D_instance_feed_url_stream_ptr == nullptr || P3D_instance_handle_event_ptr == nullptr) { logfile << "Some function pointers not found:" << "\nP3D_initialize_ptr = " << P3D_initialize_ptr << "\nP3D_finalize_ptr = " << P3D_finalize_ptr << "\nP3D_set_plugin_version_ptr = " << P3D_set_plugin_version_ptr << "\nP3D_set_super_mirror_ptr = " << P3D_set_super_mirror_ptr << "\nP3D_new_instance_ptr = " << P3D_new_instance_ptr << "\nP3D_instance_start_ptr = " << P3D_instance_start_ptr << "\nP3D_instance_start_stream_ptr = " << P3D_instance_start_stream_ptr << "\nP3D_instance_finish_ptr = " << P3D_instance_finish_ptr << "\nP3D_instance_setup_window_ptr = " << P3D_instance_setup_window_ptr << "\nP3D_object_get_type_ptr = " << P3D_object_get_type_ptr << "\nP3D_object_get_bool_ptr = " << P3D_object_get_bool_ptr << "\nP3D_object_get_int_ptr = " << P3D_object_get_int_ptr << "\nP3D_object_get_float_ptr = " << P3D_object_get_float_ptr << "\nP3D_object_get_string_ptr = " << P3D_object_get_string_ptr << "\nP3D_object_get_repr_ptr = " << P3D_object_get_repr_ptr << "\nP3D_object_get_property_ptr = " << P3D_object_get_property_ptr << "\nP3D_object_set_property_ptr = " << P3D_object_set_property_ptr << "\nP3D_object_has_method_ptr = " << P3D_object_has_method_ptr << "\nP3D_object_call_ptr = " << P3D_object_call_ptr << "\nP3D_object_eval_ptr = " << P3D_object_eval_ptr << "\nP3D_object_incref_ptr = " << P3D_object_incref_ptr << "\nP3D_object_decref_ptr = " << P3D_object_decref_ptr << "\nP3D_make_class_definition_ptr = " << P3D_make_class_definition_ptr << "\nP3D_new_undefined_object_ptr = " << P3D_new_undefined_object_ptr << "\nP3D_new_none_object_ptr = " << P3D_new_none_object_ptr << "\nP3D_new_bool_object_ptr = " << P3D_new_bool_object_ptr << "\nP3D_new_int_object_ptr = " << P3D_new_int_object_ptr << "\nP3D_new_float_object_ptr = " << P3D_new_float_object_ptr << "\nP3D_new_string_object_ptr = " << P3D_new_string_object_ptr << "\nP3D_instance_get_panda_script_object_ptr = " << P3D_instance_get_panda_script_object_ptr << "\nP3D_instance_set_browser_script_object_ptr = " << P3D_instance_set_browser_script_object_ptr << "\nP3D_instance_get_request_ptr = " << P3D_instance_get_request_ptr << "\nP3D_check_request_ptr = " << P3D_check_request_ptr << "\nP3D_request_finish_ptr = " << P3D_request_finish_ptr << "\nP3D_instance_feed_url_stream_ptr = " << P3D_instance_feed_url_stream_ptr << "\nP3D_instance_handle_event_ptr = " << P3D_instance_handle_event_ptr << "\n"; return false; } // A bit of extra hand-hacked compatibility for using newer plug-ins with an // older version of the core API. int api_version = P3D_API_VERSION; if (api_version == 17 && start_dir.empty()) { api_version = 16; if (host_dir.empty()) { api_version = 15; } } if (!P3D_initialize_ptr(api_version, contents_filename.c_str(), host_url.c_str(), verify_contents, platform.c_str(), log_directory.c_str(), log_basename.c_str(), trusted_environment, console_environment, root_dir.c_str(), host_dir.c_str(), start_dir.c_str())) { // Oops, failure to initialize. logfile << "Failed to initialize plugin (passed API version " << api_version << ")\n"; return false; } return true; } /** * Calls finalize, then removes the plugin from memory space and clears all of * the pointers. */ void unload_plugin(std::ostream &logfile) { if (!plugin_loaded) { return; } P3D_finalize_ptr(); unload_dso(); } /** * Removes the plugin from memory space and clears all of the pointers. This * is only intended to be called by load_plugin(), above, in the specific case * that the plugin loaded but could not successfully initialize itself. All * user code should call unload_plugin(), above, which first calls * P3D_finalize(). */ static void unload_dso() { if (dso_needs_unload) { assert(module != nullptr); #ifdef _WIN32 FreeLibrary(module); #else dlclose(module); #endif module = nullptr; dso_needs_unload = false; } P3D_initialize_ptr = nullptr; P3D_finalize_ptr = nullptr; P3D_set_plugin_version_ptr = nullptr; P3D_set_super_mirror_ptr = nullptr; P3D_new_instance_ptr = nullptr; P3D_instance_start_ptr = nullptr; P3D_instance_start_stream_ptr = nullptr; P3D_instance_finish_ptr = nullptr; P3D_instance_setup_window_ptr = nullptr; P3D_object_get_type_ptr = nullptr; P3D_object_get_bool_ptr = nullptr; P3D_object_get_int_ptr = nullptr; P3D_object_get_float_ptr = nullptr; P3D_object_get_string_ptr = nullptr; P3D_object_get_repr_ptr = nullptr; P3D_object_get_property_ptr = nullptr; P3D_object_set_property_ptr = nullptr; P3D_object_has_method_ptr = nullptr; P3D_object_call_ptr = nullptr; P3D_object_eval_ptr = nullptr; P3D_object_incref_ptr = nullptr; P3D_object_decref_ptr = nullptr; P3D_make_class_definition_ptr = nullptr; P3D_new_undefined_object_ptr = nullptr; P3D_new_none_object_ptr = nullptr; P3D_new_bool_object_ptr = nullptr; P3D_new_int_object_ptr = nullptr; P3D_new_float_object_ptr = nullptr; P3D_new_string_object_ptr = nullptr; P3D_instance_get_panda_script_object_ptr = nullptr; P3D_instance_set_browser_script_object_ptr = nullptr; P3D_instance_get_request_ptr = nullptr; P3D_check_request_ptr = nullptr; P3D_request_finish_ptr = nullptr; P3D_instance_feed_url_stream_ptr = nullptr; P3D_instance_handle_event_ptr = nullptr; plugin_loaded = false; } /** * Returns true if the plugin has been loaded successfully by a previous call * to load_plugin(), false otherwise. */ bool is_plugin_loaded() { return plugin_loaded; }