Skip to content

Commit

Permalink
[C++] Don't use iostream, use static libstdc++
Browse files Browse the repository at this point in the history
Implement file_reader class (simple fd->container).
Some options allow significantly reduce executable size.
Static part of libstdc++ now is only 130~150 Kb.
  • Loading branch information
Nekotekina committed Dec 25, 2024
1 parent 7b12bdc commit 14ee0f1
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 38 deletions.
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ CXXFLAGS:=-DVERSION=\"v$(VERSION)\ \($(COMMIT)\)\" \
-D_FORTIFY_SOURCE=2 \
-D_DEFAULT_SOURCE \
-Werror=format-security \
-fdata-sections -ffunction-sections \
-static-libgcc -static-libstdc++\
$(CXXFLAGS)

platform=$(shell uname -s)
Expand All @@ -53,7 +55,7 @@ endif
all: compose man
mkdir -p bin
cp scripts/keyd-application-mapper bin/
$(CXX) $(CXXFLAGS) -O3 $(COMPAT_FILES) src/*.cpp src/vkbd/$(VKBD).cpp -lpthread -o bin/keyd $(LDFLAGS)
$(CXX) $(CXXFLAGS) -O3 $(COMPAT_FILES) src/*.cpp src/vkbd/$(VKBD).cpp -lpthread -Wl,--gc-sections -o bin/keyd $(LDFLAGS)
debug:
CFLAGS="-g -fsanitize=address -Wunused" $(MAKE)
compose:
Expand Down
49 changes: 25 additions & 24 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@
#include <limits>
#include <string>
#include <string_view>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <numeric>

Expand Down Expand Up @@ -186,40 +184,35 @@ static std::string resolve_include_path(const char *path, std::string_view inclu
return resolved_path;
}

static std::string read_file(const char *path)
static std::string read_file(const char *path, size_t recursion_depth = 0)
{
constexpr std::string_view include_prefix = "include ";
std::string buf;

std::string buf, line;

std::ifstream file(path);
if (!file.is_open()) {
std::string file = file_reader(open(path, O_RDONLY), 65536, [&] {
err("failed to open %s", path);
return {};
}
perror("open");
});

while (std::getline(file, line)) {
if (line.starts_with(include_prefix)) {
for (auto line : split_char<'\n'>(file)) {
if (line.starts_with("include ") || line.starts_with("include\t")) {
std::string_view include_path = line;
include_path.remove_prefix(include_prefix.size());
while (include_path.starts_with(' '))
include_path.remove_prefix(1);
include_path.remove_prefix(8);
keyd_log("include %.*s\n", (int)include_path.size(), include_path.data());

auto resolved_path = resolve_include_path(path, include_path);
std::string resolved_path = resolve_include_path(path, include_path);
if (resolved_path.empty()) {
warn("failed to resolve include path: %s", include_path.data());
warn("failed to resolve include path: %.*s", (int)include_path.size(), include_path.data());
continue;
}

std::ifstream file(resolved_path);
if (!file.is_open()) {
warn("failed to %s", line.c_str());
perror("open");
} else {
buf.append(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
if (recursion_depth >= 10) {
warn("include depth too big or cyclic: %.*s", (int)include_path.size(), include_path.data());
continue;
}

buf += read_file(resolved_path.c_str(), recursion_depth + 1);
} else {
buf += line;
buf.append(line);
buf += '\n';
}
}
Expand Down Expand Up @@ -1098,3 +1091,11 @@ void config_backup::restore(struct config& cfg)
cfg.macros.resize(macro_count);
cfg.commands.resize(cmd_count);
}

config::~config()
{
}

config_backup::~config_backup()
{
}
2 changes: 2 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ struct config {
config() = default;
config(const config&) = delete;
config& operator=(const config&) = delete;
~config();
};

struct config_backup {
Expand All @@ -199,6 +200,7 @@ struct config_backup {
std::vector<layer_backup> layers;

explicit config_backup(const struct config& cfg);
~config_backup();

void restore(struct config& cfg);
};
Expand Down
19 changes: 10 additions & 9 deletions src/daemon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#include "log.h"
#include <memory>
#include <utility>
#include <fstream>

#ifndef CONFIG_DIR
#define CONFIG_DIR ""
Expand Down Expand Up @@ -307,12 +306,13 @@ static void reload(std::shared_ptr<env_pack> env)
else
buf.clear();
buf += "keyd/bindings.conf";
std::ifstream file(buf, std::ios::binary);
if (!file.is_open()) {
buf = std::string(file_reader(open(buf.c_str(), O_RDONLY), 65536, []{
keyd_log("Unable to open %s\n", buf.c_str());
perror("open");
}));

if (buf.empty())
return;
}
buf.assign(std::istreambuf_iterator(file), std::istreambuf_iterator<char>());

for (auto ent = configs.get(); ent; ent = ent->next.get()) {
ent->kbd->config.cfg_use_uid = env->uid;
Expand Down Expand Up @@ -517,10 +517,10 @@ static void handle_client(int con)
if (getuid() < 1000)
{
// Copy initial environment variables from caller process
std::ifstream envf(("/proc/" + std::to_string(cred.pid) + "/environ").c_str(), std::ios::binary);
if (envf.is_open()) {
std::vector<char> buf;
buf.assign(std::istreambuf_iterator<char>(envf), std::istreambuf_iterator<char>());
std::vector<char> buf = file_reader(open(("/proc/" + std::to_string(cred.pid) + "/environ").c_str(), O_RDONLY), 8192, [] {
perror("environ");
});
if (!buf.empty()) {
if (prev && prev->buf == buf) {
// Share previous environment variables
ephemeral_config.env = prev;
Expand All @@ -543,6 +543,7 @@ static void handle_client(int con)

size_t msg_count = 0;
while (handle_message(con, &ephemeral_config, prev ? prev : std::make_shared<env_pack>())) {
ephemeral_config.commands.clear();
msg_count++;
}
dbg2("%zu messages processed", msg_count);
Expand Down
51 changes: 51 additions & 0 deletions src/keyd.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,55 @@ int ipc_connect();
extern struct device device_table[MAX_DEVICES];
extern size_t device_table_sz;

// One-time file reader
struct file_reader
{
explicit file_reader(int fd, unsigned reserve, auto on_fail)
: fd(fd)
, reserve(reserve)
{
if (fd < 0) {
on_fail();
}
}

file_reader(const file_reader&) = delete;
file_reader& operator=(const file_reader&) = delete;

// Read full file
template <typename T, typename V = typename T::value_type>
operator T()
{
T result;
if constexpr (requires { result.reserve(reserve); })
result.reserve(reserve);
size_t rd = 0;
V buf[4096];
while ((rd = read(this->fd, buf, sizeof(buf))) <= sizeof(buf)) {
if (rd == 0)
break;
// Unimplemented, but won't happen for single-byte types
if (rd % sizeof(V))
throw buf[0];
result.insert(result.end(), buf, buf + rd / sizeof(V));
}
return result;
}

void reset()
{
if (lseek(fd, 0, SEEK_SET) < 0)
perror("file_reader::lseek");
}

~file_reader()
{
if (fd >= 0)
close(fd);
}
private:
int fd;
unsigned reserve;
};

#endif
4 changes: 0 additions & 4 deletions src/macro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ int macro_parse(std::string_view s, macro& macro, struct config* config)
err("incomplete macro command found");
return -1;
}
if (is_cmd && !config) {
err("commands are not allowed in this context");
return -1;
}
if (is_cmd && config->commands.size() > std::numeric_limits<decltype(descriptor_arg::idx)>::max()) {
err("max commands exceeded");
return -1;
Expand Down

0 comments on commit 14ee0f1

Please sign in to comment.