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).
Implement concat class for simple formatting strings.
Some options allow significantly reduce executable size.
Static part of libstdc++ now is only 130~150 Kb.
  • Loading branch information
Nekotekina committed Dec 26, 2024
1 parent 7b12bdc commit 5b5aa2d
Show file tree
Hide file tree
Showing 13 changed files with 172 additions and 78 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
1 change: 1 addition & 0 deletions keyd.service.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Documentation=man:keyd(1)
Type=simple
ExecStart=@PREFIX@/bin/keyd
KillMode=process
Environment="GLIBCXX_TUNABLES=glibcxx.eh_pool.obj_count=2:glibcxx.eh_pool.obj_size=4"

[Install]
WantedBy=multi-user.target
49 changes: 24 additions & 25 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,33 @@ 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), 4096, [&] {
err("failed to open %s", path);
return {};
}
perror("open");
});

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

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 +1089,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
47 changes: 23 additions & 24 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 @@ -183,7 +182,6 @@ static void on_layer_change(const struct keyboard *kbd, struct layer *layer, uin
static void load_configs()
{
DIR *dh = opendir(CONFIG_DIR);
struct dirent *dirent;

if (!dh) {
perror("opendir");
Expand All @@ -192,22 +190,18 @@ static void load_configs()

configs.reset();

while ((dirent = readdir(dh))) {
char path[1024];
int len;

while (struct dirent* dirent = readdir(dh)) {
if (dirent->d_type == DT_DIR)
continue;

len = snprintf(path, sizeof path, "%s/%s", CONFIG_DIR, dirent->d_name);

if (len >= 5 && !strcmp(path + len - 5, ".conf")) {
auto name = concat(CONFIG_DIR "/", dirent->d_name);
if (name.ends_with(".conf")) {
auto ent = std::make_unique<config_ent>();

keyd_log("CONFIG: parsing b{%s}\n", path);
keyd_log("CONFIG: parsing b{%s}\n", name.c_str());

auto kbd = std::make_unique<keyboard>();
if (!config_parse(&kbd->config, path)) {
if (!config_parse(&kbd->config, name.c_str())) {
kbd->output = {
.send_key = send_key,
.on_layer_change = on_layer_change,
Expand All @@ -220,7 +214,7 @@ static void load_configs()
ent->next = std::move(configs);
configs = std::move(ent);
} else {
keyd_log("DEVICE: y{WARNING} failed to parse %s\n", path);
keyd_log("DEVICE: y{WARNING} failed to parse %s\n", name.c_str());
}

}
Expand Down Expand Up @@ -266,7 +260,7 @@ static void manage_device(struct device *dev)

if ((ent = lookup_config_ent(dev->id, flags))) {
if (device_grab(dev)) {
keyd_log("DEVICE: y{WARNING} Failed to grab %s\n", dev->path);
keyd_log("DEVICE: y{WARNING} Failed to grab /dev/input/%u\n", dev->num);
dev->data = NULL;
return;
}
Expand All @@ -287,7 +281,6 @@ static void manage_device(struct device *dev)
static void reload(std::shared_ptr<env_pack> env)
{
restore_leds();
configs.reset();
load_configs();

for (size_t i = 0; i < device_table_sz; i++)
Expand All @@ -307,12 +300,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), 4096, []{
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 +511,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(concat("/proc/", 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 +537,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 Expand Up @@ -692,11 +687,15 @@ static int event_handler(struct event *ev)
return timeout;
}

#ifndef VERSION
#define VERSION "unknown"
#endif

int run_daemon(int, char *[])
{
ipcfd = ipc_create_server(/* SOCKET_PATH */);
ipcfd = ipc_create_server();
if (ipcfd < 0)
die("failed to create %s (another instance already running?)", SOCKET_PATH);
die("failed to create socket (another instance already running?)");

vkbd = vkbd_init(VKBD_NAME);

Expand Down
28 changes: 12 additions & 16 deletions src/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ uint32_t generate_uid(uint32_t num_keys, uint8_t absmask, uint8_t relmask, const
return hash;
}

static int device_init(const char *path, struct device *dev)
static int device_init(struct device *dev)
{
int fd;
int capabilities;
Expand All @@ -136,15 +136,16 @@ static int device_init(const char *path, struct device *dev)
uint8_t absmask;
struct input_absinfo absinfo;

if ((fd = open(path, O_RDWR | O_NONBLOCK | O_CLOEXEC, 0600)) < 0) {
keyd_log("failed to open %s\n", path);
auto path = concat("/dev/input/event", dev->num);
if ((fd = open(path.c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC, 0600)) < 0) {
keyd_log("failed to open %s\n", path.c_str());
return -1;
}

capabilities = resolve_device_capabilities(fd, &num_keys, &relmask, &absmask);

if (ioctl(fd, EVIOCGNAME(sizeof(dev->name)), dev->name) == -1) {
keyd_log("ERROR: could not fetch device name of %s\n", dev->path);
if (ioctl(fd, EVIOCGNAME(sizeof(dev->name) - 1), dev->name) == -1) {
keyd_log("ERROR: could not fetch device name of /dev/input/event%u\n", dev->num);
return -1;
}

Expand All @@ -166,7 +167,7 @@ static int device_init(const char *path, struct device *dev)
dev->_maxy = absinfo.maximum;
}

dbg2("capabilities of %s (%s): %x", path, dev->name, capabilities);
dbg2("capabilities of %s (%s): %x", path.c_str(), dev->name, capabilities);

if (capabilities) {
struct input_id info;
Expand All @@ -176,9 +177,6 @@ static int device_init(const char *path, struct device *dev)
return -1;
}

strncpy(dev->path, path, sizeof(dev->path)-1);
dev->path[sizeof(dev->path)-1] = 0;

/*
* Attempt to generate a reproducible unique identifier for each device.
* The product and vendor ids are insufficient to identify some devices since
Expand Down Expand Up @@ -206,14 +204,13 @@ static int device_init(const char *path, struct device *dev)

struct device_worker {
pthread_t tid;
char path[1024];
struct device dev;
};

static void *device_scan_worker(void *arg)
{
struct device_worker *w = (struct device_worker *)arg;
if (device_init(w->path, &w->dev) < 0)
if (device_init(&w->dev) < 0)
return NULL;

return &w->dev;
Expand All @@ -237,7 +234,8 @@ int device_scan(struct device devices[MAX_DEVICES])
assert(n < MAX_DEVICES);
struct device_worker *w = &workers[n++];

snprintf(w->path, sizeof(w->path), "/dev/input/%s", ent->d_name);
w->dev = {};
w->dev.num = atoi(ent->d_name + 5);
pthread_create(&w->tid, NULL, device_scan_worker, w);
}
}
Expand Down Expand Up @@ -292,7 +290,6 @@ int devmon_read_device(int fd, struct device *dev)
static char *ptr = buf;

while (1) {
char path[1024];
struct inotify_event *ev;

if (ptr >= (buf+buf_sz)) {
Expand All @@ -310,9 +307,8 @@ int devmon_read_device(int fd, struct device *dev)
if (strncmp(ev->name, "event", 5))
continue;

snprintf(path, sizeof path, "/dev/input/%s", ev->name);

if (!device_init(path, dev))
dev->num = atoi(ev->name + 5);
if (!device_init(dev))
return 0;
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ struct device {
uint8_t capabilities;
uint8_t is_virtual;

uint8_t led_state[LED_CNT];
char id[25];
uint32_t num;
char name[96];

char id[64];
char name[64];
char path[256];
uint8_t led_state[LED_CNT];

/* Internal. */
uint32_t _maxx;
Expand Down
4 changes: 4 additions & 0 deletions src/ipc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

/* TODO (maybe): settle on an API and publish the protocol. */

#ifndef SOCKET_PATH
#define SOCKET_PATH "/var/run/keyd.socket"
#endif

static void chgid()
{
struct group *g = getgrnam("keyd");
Expand Down
4 changes: 4 additions & 0 deletions src/keyd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ static int ipc_exec(enum ipc_msg_type_e type, const char *data, size_t sz, uint3
return msg.type == IPC_FAIL;
}

#ifndef VERSION
#define VERSION "unknown"
#endif

static int version(int, char *[])
{
printf("keyd " VERSION "\n");
Expand Down
Loading

0 comments on commit 5b5aa2d

Please sign in to comment.