When OSv kernel is built to hide most symbols but glibc ones, the
OSv applications like httpserver monitoring API can not function
corretly as they rely on number of internal C++ API.
This patch modifies httpserver monitoring API to stop using kernel
internal C++ API. It does so by replacing some of the calls to internal C++
symbols with new module C-style API symbols: for example, sched::with_all_threads()
with new osv_get_all_threads(). In other scenarios, we fall back to
standard glibc API: for example osv::current_mounts() is replaced with
getmntent_r() and related functions.
Finally, we link httpserver monitoring app with core/options.cc and
thus remove need to have those symbols exposed by the kernel.
modules/httpserver-api/api/fs.cc | 42 ++++++++++----
modules/httpserver-api/api/hardware.cc | 23 +++++---
modules/httpserver-api/api/network.cc | 19 ++++--
modules/httpserver-api/api/os.cc | 67 +++++++++++++---------
modules/httpserver-api/global_server.cc | 34 +++++++----
modules/httpserver-api/global_server.hh | 5 ++
modules/httpserver-api/openssl-init.cc | 15 +++--
modules/httpserver-api/ssl_server.cc | 9 ++-
modules/httpserver-monitoring-api/Makefile | 6 +-
9 files changed, 150 insertions(+), 70 deletions(-)
diff --git a/modules/httpserver-api/api/fs.cc b/modules/httpserver-api/api/fs.cc
index 94eec77b..52e58c39 100644
--- a/modules/httpserver-api/api/fs.cc
+++ b/modules/httpserver-api/api/fs.cc
@@ -6,12 +6,12 @@
*/
#include "fs.hh"
-#include "osv/mount.h"
#include "json/formatter.hh"
#include "autogen/fs.json.hh"
#include <string>
#include <vector>
#include <sys/statvfs.h>
+#include <mntent.h>
namespace httpserver {
@@ -23,9 +23,9 @@ using namespace std;
using namespace json;
using namespace fs_json;
-static void fill_dfstat(DFStat& dfstat, const osv::mount_desc& mount, const struct statvfs& st) {
- dfstat.filesystem = mount.special;
- dfstat.mount = mount.path;
+static void fill_dfstat(DFStat& dfstat, mntent* mount, const struct statvfs& st) {
+ dfstat.filesystem = mount->mnt_fsname;
+ dfstat.mount = mount->mnt_dir;
dfstat.btotal = st.f_blocks;
dfstat.bfree = st.f_bfree;
dfstat.ftotal = st.f_files;
@@ -46,21 +46,31 @@ void init(routes& routes) {
getDFStats.set_handler("json",
[](const_req req)
{
- using namespace osv;
const std::string onemount =
req.param.at("mount");
struct statvfs st;
httpserver::json::DFStat dfstat;
vector<httpserver::json::DFStat> dfstats;
- for (mount_desc mount : osv::current_mounts()) {
- if ((mount.type == "zfs" || mount.type == "rofs") && (onemount == "" || onemount == mount.path)) {
- if (statvfs(mount.path.c_str(),&st) != 0) {
+ FILE *mounts_fp = setmntent("/proc/mounts", "r");
+ if (!mounts_fp) {
+ throw server_error_exception("failed to get mounts information");
+ }
+
+ struct mntent* mount;
+ mntent mnt;
+ char strings[4096];
+ while ((mount = getmntent_r(mounts_fp, &mnt, strings, sizeof(strings)))) {
+ std::string fstype(mount->mnt_type);
+ if ((fstype == "zfs" || fstype == "rofs") && (onemount == "" || onemount == mount->mnt_dir)) {
+ if (statvfs(mount->mnt_dir,&st) != 0) {
+ endmntent(mounts_fp);
throw not_found_exception("mount does not exist");
}
fill_dfstat(dfstat, mount, st);
dfstats.push_back(dfstat);
}
};
+ endmntent(mounts_fp);
// checking if a specific file system was requested and if we found it
if (onemount != "" && dfstats.size() == 0) {
@@ -76,14 +86,24 @@ void init(routes& routes) {
httpserver::json::DFStat dfstat;
vector<httpserver::json::DFStat> res;
- for (osv::mount_desc mount : osv::current_mounts()) {
- if (mount.type == "zfs" || mount.type == "rofs") {
- if (statvfs(mount.path.c_str(),&st) == 0) {
+ FILE *mounts_fp = setmntent("/proc/mounts", "r");
+ if (!mounts_fp) {
+ throw server_error_exception("failed to get mounts information");
+ }
+
+ struct mntent* mount;
+ mntent mnt;
+ char strings[4096];
+ while ((mount = getmntent_r(mounts_fp, &mnt, strings, sizeof(strings)))) {
+ std::string fstype(mount->mnt_type);
+ if (fstype == "zfs" || fstype == "rofs") {
+ if (statvfs(mount->mnt_dir,&st) == 0) {
fill_dfstat(dfstat, mount, st);
res.push_back(dfstat);
}
}
}
+ endmntent(mounts_fp);
return res;
});
diff --git a/modules/httpserver-api/api/hardware.cc b/modules/httpserver-api/api/hardware.cc
index f023e394..fe069913 100644
--- a/modules/httpserver-api/api/hardware.cc
+++ b/modules/httpserver-api/api/hardware.cc
@@ -10,9 +10,8 @@
#include "autogen/hardware.json.hh"
#include "processor.hh"
#include "cpuid.hh"
-#include <osv/sched.hh>
-#include <osv/firmware.hh>
-#include <osv/hypervisor.hh>
+#include <osv/osv_c_wrappers.h>
+#include <sys/sysinfo.h>
namespace httpserver {
@@ -30,26 +29,36 @@ extern "C" void httpserver_plugin_register_routes(httpserver::routes* routes) {
}
#endif
+static std::string from_c_string(char *c_str) {
+ if (c_str) {
+ std::string str(c_str);
+ free(c_str);
+ return str;
+ } else {
+ return std::string();
+ }
+}
+
void init(routes& routes)
{
hardware_json_init_path("Hardware management API");
processorFeatures.set_handler([](const_req req)
{
- return processor::features_str();
+ return from_c_string(osv_processor_features());
});
processorCount.set_handler([](const_req req)
{
- return sched::cpus.size();
+ return get_nprocs();
});
firmware_vendor.set_handler([](const_req) {
- return osv::firmware_vendor();
+ return from_c_string(osv_firmware_vendor());
});
hypervisor_name.set_handler([](const_req) {
- return osv::hypervisor_name();
+ return from_c_string(osv_hypervisor_name());
});
}
diff --git a/modules/httpserver-api/api/network.cc b/modules/httpserver-api/api/network.cc
index 7d31faf8..595faf4c 100644
--- a/modules/httpserver-api/api/network.cc
+++ b/modules/httpserver-api/api/network.cc
@@ -10,7 +10,7 @@
#include "../libtools/network_interface.hh"
#include "exception.hh"
#include <vector>
-#include <osv/clock.hh>
+#include <time.h>
namespace httpserver {
@@ -57,13 +57,16 @@ void init(routes& routes)
network_json_init_path("Hardware management API");
network_json::listIfconfig.set_handler([](const_req req) {
vector<Interface> res;
- auto time = duration_cast<microseconds>
- (osv::clock::uptime::now().time_since_epoch()).count();
+ timespec time;
+ if (clock_gettime(CLOCK_BOOTTIME, &time)) {
+ return res;
+ }
+ auto time_mc = time.tv_sec * 1000000 + time.tv_nsec / 1000;
for (unsigned int i = 0; i <= number_of_interfaces(); i++) {
auto* ifp = get_interface_by_index(i);
if (ifp != nullptr) {
- res.push_back(get_interface(get_interface_name(ifp), ifp, time));
+ res.push_back(get_interface(get_interface_name(ifp), ifp, time_mc));
}
}
return res;
@@ -76,8 +79,12 @@ void init(routes& routes)
if (ifp == nullptr) {
throw not_found_exception(string("Interface ") + name + " not found");
}
- auto time = duration_cast<microseconds>(osv::clock::uptime::now().time_since_epoch()).count();
- return get_interface(name, ifp, time);
+ timespec time;
+ if (clock_gettime(CLOCK_BOOTTIME, &time)) {
+ throw not_found_exception("Failed to get time");
+ }
+ auto time_mc = time.tv_sec * 1000000 + time.tv_nsec / 1000;
+ return get_interface(name, ifp, time_mc);
});
network_json::getRoute.set_handler([](const_req req) {
diff --git a/modules/httpserver-api/api/os.cc b/modules/httpserver-api/api/os.cc
index 036f9162..38a662a7 100644
--- a/modules/httpserver-api/api/os.cc
+++ b/modules/httpserver-api/api/os.cc
@@ -6,23 +6,20 @@
*/
#include <sys/utsname.h>
+#include <sys/time.h>
#include "os.hh"
-#include "osv/version.hh"
#include "json/formatter.hh"
#include "autogen/os.json.hh"
#include <sys/sysinfo.h>
#include <time.h>
#include <osv/shutdown.hh>
#include <osv/power.hh>
-#include <osv/debug.hh>
-#include <osv/sched.hh>
#include <api/unistd.h>
#include <osv/commands.hh>
+#include <osv/osv_c_wrappers.h>
#include <algorithm>
#include "../java-base/balloon/balloon_api.hh"
-extern char debug_buffer[DEBUG_BUFFER_SIZE];
-
namespace httpserver {
namespace api {
@@ -39,6 +36,16 @@ extern "C" void httpserver_plugin_register_routes(httpserver::routes* routes) {
}
#endif
+static std::string from_c_string(char *c_str) {
+ if (c_str) {
+ std::string str(c_str);
+ free(c_str);
+ return str;
+ } else {
+ return std::string();
+ }
+}
+
void init(routes& routes)
{
os_json_init_path("OS core API");
@@ -48,7 +55,7 @@ void init(routes& routes)
});
os_version.set_handler([](const_req req) {
- return osv::version();
+ return from_c_string(osv_version());
});
os_vendor.set_handler([](const_req req) {
@@ -103,7 +110,7 @@ void init(routes& routes)
#endif
os_dmesg.set_handler([](const_req req) {
- return debug_buffer;
+ return osv_debug_buffer();
});
os_get_hostname.set_handler([](const_req req)
@@ -122,31 +129,39 @@ void init(routes& routes)
#endif
os_threads.set_handler([](const_req req) {
- using namespace std::chrono;
httpserver::json::Threads threads;
- threads.time_ms = duration_cast<milliseconds>
- (osv::clock::wall::now().time_since_epoch()).count();
+ timeval timeofday;
+ if (gettimeofday(&timeofday, nullptr)) {
+ return threads;
+ }
+ threads.time_ms = timeofday.tv_sec * 1000 + timeofday.tv_usec / 1000;
httpserver::json::Thread thread;
- sched::with_all_threads([&](sched::thread &t) {
-
thread.id =
t.id();
- thread.status = t.get_status();
- auto tcpu = t.tcpu();
- thread.cpu = tcpu ? tcpu->id : -1;
- thread.cpu_ms = duration_cast<milliseconds>(t.thread_clock()).count();
- thread.switches = t.stat_switches.get();
- thread.migrations = t.stat_migrations.get();
- thread.preemptions = t.stat_preemptions.get();
-
thread.name =
t.name();
- thread.priority = t.priority();
- thread.stack_size = t.get_stack_info().size;
- thread.status = t.get_status();
- threads.list.push(thread);
- });
+ osv_thread *osv_threads;
+ size_t threads_num;
+ if (!osv_get_all_threads(&osv_threads, &threads_num)) {
+ for (size_t i = 0; i < threads_num; i++) {
+ auto &t = osv_threads[i];
+
thread.id =
t.id;
+ thread.status = t.status;
+ thread.cpu = t.cpu_id;
+ thread.cpu_ms = t.cpu_ms;
+ thread.switches = t.switches;
+ thread.migrations = t.migrations;
+ thread.preemptions = t.preemptions;
+
thread.name =
t.name;
+ free(
t.name);
+ thread.priority = t.priority;
+ thread.stack_size = t.stack_size;
+ thread.status = t.status;
+ threads.list.push(thread);
+ }
+ free(osv_threads);
+ }
return threads;
});
os_get_cmdline.set_handler([](const_req req) {
- return osv::getcmdline();
+ return from_c_string(osv_cmdline());
});
#if !defined(MONITORING)
diff --git a/modules/httpserver-api/global_server.cc b/modules/httpserver-api/global_server.cc
index 5cf1c62e..649e62cf 100644
--- a/modules/httpserver-api/global_server.cc
+++ b/modules/httpserver-api/global_server.cc
@@ -8,15 +8,14 @@
#include "global_server.hh"
#include "path_holder.hh"
#include <iostream>
-#include <osv/app.hh>
#include <fstream>
#include <dlfcn.h>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include "json/api_docs.hh"
-#include <osv/debug.h>
#include "transformers.hh"
#include <osv/options.hh>
+#include <osv/osv_c_wrappers.h>
#if !defined(MONITORING)
#include "yaml-cpp/yaml.h"
#else
@@ -41,6 +40,13 @@ global_server& global_server::get()
return *instance;
}
+void global_server::termination_handler() {
+ get().s->close();
+ for( auto plugin : get().plugins) {
+ dlclose(plugin);
+ }
+}
+
bool global_server::run(std::map<std::string,std::vector<std::string>>& _config)
{
if (get().s != nullptr) {
@@ -107,12 +113,7 @@ bool global_server::run(std::map<std::string,std::vector<std::string>>& _config)
auto port = get().config["port"][0];
get().s = new http::server::server(get().config, &get()._routes);
- osv::this_application::on_termination_request([&] {
- get().s->close();
- for( auto plugin : get().plugins) {
- dlclose(plugin);
- }
- });
+ osv_current_app_on_termination_request(termination_handler);
std::cout << "Rest API server running on port " << port << std::endl;
get().s->run();
@@ -121,6 +122,7 @@ bool global_server::run(std::map<std::string,std::vector<std::string>>& _config)
#if !defined(MONITORING)
void global_server::setup_file_mappings(const YAML::Node& file_mappings_node) {
+ auto debug_enabled = osv_debug_enabled();
for (auto node : file_mappings_node) {
const YAML::Node path = node["path"];
if (path && node["directory"]) {
@@ -130,7 +132,9 @@ void global_server::setup_file_mappings(const YAML::Node& file_mappings_node) {
new directory_handler(directory, new content_replace(
content_replace_node.as<std::string>())) :
new directory_handler(directory);
_routes.add(GET, url(
path.as<std::string>()).remainder("path"), handler);
- debug("httpserver: setup directory mapping: [%s] -> [%s]\n",
path.as<std::string>().c_str(), directory.c_str());
+ if (debug_enabled) {
+ std::cout << "httpserver: setup directory mapping: [" <<
path.as<std::string>() << "] -> [" << directory << "]" << std::endl;
+ }
}
else if (path && node["file"]) {
const std::string file = node["file"].as<std::string>();
@@ -142,7 +146,9 @@ void global_server::setup_file_mappings(const YAML::Node& file_mappings_node) {
else {
_routes.add(GET, url(
path.as<std::string>()).remainder("path"), handler);
}
- debug("httpserver: setup file mapping: [%s] -> [%s]\n",
path.as<std::string>().c_str(), file.c_str());
+ if (debug_enabled) {
+ std::cout << "httpserver: setup file mapping: [" <<
path.as<std::string>() << "] -> [" << file << "]" << std::endl;
+ }
}
}
}
@@ -165,7 +171,9 @@ void global_server::setup_redirects(const YAML::Node& redirects_node) {
return "";
});
_routes.put(GET, path, redirect);
- debug("httpserver: setup redirect: [%s] -> [%s]\n", path.c_str(), target_path.c_str());
+ if (osv_debug_enabled()) {
+ std::cout << "httpserver: setup redirect: [" << path << "] -> [" << target_path << "]" << std::endl;
+ }
}
}
}
@@ -242,6 +250,8 @@ void global_server::load_plugin(const std::string& path)
}
plugins.push_back(plugin);
httpserver_plugin_register_routes(&_routes);
- debug("httpserver: loaded plugin from path: %s\n",path.c_str());
+ if (osv_debug_enabled()) {
+ std::cout << "httpserver: loaded plugin from path: " << path << std::endl;
+ }
}
}
diff --git a/modules/httpserver-api/global_server.hh b/modules/httpserver-api/global_server.hh
index e70b7854..587860fb 100644
--- a/modules/httpserver-api/global_server.hh
+++ b/modules/httpserver-api/global_server.hh
@@ -29,6 +29,11 @@ public:
*/
static global_server& get();
+ /**
+ * cleanup routine: shutdown server and close all plugins
+ */
+ static void termination_handler();
+
/**
* get the route object
* @return a reference to the route object
diff --git a/modules/httpserver-api/openssl-init.cc b/modules/httpserver-api/openssl-init.cc
index 8630d019..a1bed2bd 100644
--- a/modules/httpserver-api/openssl-init.cc
+++ b/modules/httpserver-api/openssl-init.cc
@@ -8,7 +8,7 @@
#include <iostream>
#include <fstream>
#include <mutex>
-#include <osv/debug.hh>
+#include <osv/osv_c_wrappers.h>
#include <assert.h>
#include <openssl/rand.h>
@@ -16,9 +16,16 @@
#include "openssl-init.hh"
+static void debug_line(const char *line) {
+ auto debug_enabled = osv_debug_enabled();
+ if (debug_enabled) {
+ std::cout << line << std::endl;
+ }
+}
+
static void seed_openssl()
{
- debug("Seeding OpenSSL...\n");
+ debug_line("Seeding OpenSSL...");
for (;;) {
char buf[32];
@@ -39,10 +46,10 @@ static void seed_openssl()
// fedora's openssl-1.0.1h-5.fc20 needs 48 bytes instead
// of 32, which is what is required by its upstream version
// (tag OpenSSL_1_0_1h). In general, we should make no assumptions.
- debug("Still not seeded, retrying\n");
+ debug_line("Still not seeded, retrying");
}
- debug("OpenSSL seeding done.\n");
+ debug_line("OpenSSL seeding done.");
}
void ensure_openssl_initialized()
diff --git a/modules/httpserver-api/ssl_server.cc b/modules/httpserver-api/ssl_server.cc
index a4f8ada2..a064bd6b 100644
--- a/modules/httpserver-api/ssl_server.cc
+++ b/modules/httpserver-api/ssl_server.cc
@@ -5,11 +5,12 @@
* BSD license as described in the LICENSE file in the top-level directory.
*/
-#include <osv/debug.hh>
+#include <osv/osv_c_wrappers.h>
#include "transport.hh"
#include "ssl_server.hh"
#include <openssl/ssl.h>
+#include <iostream>
namespace http {
@@ -83,8 +84,10 @@ void ssl_acceptor::do_accept(callback_t callback)
[this, socket, callback] (boost::system::error_code ec) {
if (ec) {
auto remote = socket->lowest_layer().remote_endpoint();
- debug("handshake with " + remote.address().to_string()
- + " failed: " + ec.message() + "\n");
+ if (osv_debug_enabled()) {
+ std::cout << "handshake with " << remote.address().to_string()
+ << " failed: " << ec.message() << std::endl;
+ }
}
if (!_tcp_acceptor.is_open()) {
diff --git a/modules/httpserver-monitoring-api/Makefile b/modules/httpserver-monitoring-api/Makefile
index e26fc06b..8ac415b7 100644
--- a/modules/httpserver-monitoring-api/Makefile
+++ b/modules/httpserver-monitoring-api/Makefile
@@ -45,7 +45,7 @@ all: $(module_out)/lib$(TARGET).so
$(src)/scripts/manifest_from_host.sh $(module_out)/lib$(TARGET).so > usr.manifest; \
fi
-$(module_out)/lib$(TARGET).so: $(JSON_OBJ_FILES) $(OBJ_FILES)
+$(module_out)/lib$(TARGET).so: $(JSON_OBJ_FILES) $(OBJ_FILES) $(module_out)/options.o
$(call quiet, $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(STATIC_LIBS) -o $@ $^ $(DYN_LIBS), LINK $@)
ifneq ($(MAKECMDGOALS),clean)
@@ -64,6 +64,10 @@ $(JSON_OBJ_FILES): $(module_out)/autogen/%.o: autogen/%.cc
$(call very-quiet, mkdir -p $(module_out)/autogen)
$(call quiet, $(CXX) $(CXXFLAGS) -c -MMD -o $@ $<, CXX $@)
+$(module_out)/options.o: $(src)/core/options.cc
+ $(call very-quiet, mkdir -p $(module_out))
+ $(call quiet, $(CXX) $(CXXFLAGS) -c -MMD -o $@ $<, CXX $@)
+
clean:
$(call quiet, $(RM) -f $(TARGET), CLEAN)
$(call very-quiet, $(RM) -rf $(module_out))
--
2.31.1