Skip to content

Instantly share code, notes, and snippets.

@NotKit
Last active December 13, 2022 20:40
Show Gist options
  • Save NotKit/2f68f5fb6be4af46ae85779cb0b1fe1f to your computer and use it in GitHub Desktop.
Save NotKit/2f68f5fb6be4af46ae85779cb0b1fe1f to your computer and use it in GitHub Desktop.
LD_PRELOAD workaround to create devnodes by udev on devices without CONFIG_DEVTMPFS in kernel
#define _GNU_SOURCE
#include <errno.h>
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/stat.h>
#include <libgen.h>
#include <systemd/sd-device.h>
// FIXME: hooking sd_device_get_devname should work better than hooking readlinkat,
// however libsystemd is linked statically to systemd-udevd on Ubuntu 20.04
#define HOOK_LIBSYSTEMD 0
// compile with:
// gcc -shared -fPIC -ldl udevd-nodevtmpfs.c -o udevd-nodevtmpfs.so
typedef int (*sd_device_get_devname_t)(sd_device *device, const char **devname);
typedef ssize_t (*readlinkat_t)(int dirfd, const char *pathname, char *buf, size_t bufsiz);
// From https://stackoverflow.com/a/20634902
static int mkpath(char *dir, mode_t mode) {
struct stat sb;
if (!dir) {
errno = EINVAL;
return 1;
}
if (stat(dir, &sb) == 0)
return 0;
mkpath(dirname(strdupa(dir)), mode);
return mkdir(dir, mode);
}
static void make_device(sd_device *device, const char *devname) {
const char *subsystem = NULL;
dev_t devnum;
int r = sd_device_get_subsystem(device, &subsystem);
if (r >= 0 && sd_device_get_devnum(device, &devnum) >= 0) {
int mode = 0660;
mode |= (strcmp(subsystem, "block") == 0) ? S_IFBLK : S_IFCHR;
mkpath(dirname(strdupa(devname)), 0755);
fprintf(stderr, "mknod(%s, %o, %d:%d)\n", devname, mode, major(devnum), minor(devnum));
mknod(devname, mode, devnum);
}
}
#if !HOOK_LIBSYSTEMD
static void ensure_device_from_syspath(const char* syspath) {
sd_device *device = NULL;
int r;
r = sd_device_new_from_syspath(&device, syspath);
if (r < 0)
return;
const char* devname = NULL;
r = sd_device_get_devname(device, &devname);
if (r >= 0 && access(devname, F_OK) != 0) {
make_device(device, devname);
}
}
static bool prefix(const char *pre, const char *str) {
return strncmp(pre, str, strlen(pre)) == 0;
}
ssize_t readlinkat(int dirfd, const char *pathname,
char *buf, size_t bufsiz) {
static readlinkat_t real_readlinkat = NULL;
static __thread bool processing_device = false;
if (!real_readlinkat) {
real_readlinkat = dlsym(RTLD_NEXT, "readlinkat");
}
fprintf(stderr, "readlink(%s)\n", pathname);
if (!processing_device && prefix("/sys/devices", pathname)) {
processing_device = true;
ensure_device_from_syspath(dirname(strdupa(pathname)));
processing_device = false;
}
return real_readlinkat(dirfd, pathname, buf, bufsiz);
}
#endif
#if HOOK_LIBSYSTEMD
int sd_device_get_devname(sd_device *device, const char **devname) {
static sd_device_get_devname_t device_get_devname = NULL;
fprintf(stderr, "sd_device_get_devname(%p, ", device);
if (!device_get_devname) {
device_get_devname = dlsym(RTLD_NEXT, "sd_device_get_devname");
}
int ret = device_get_devname(device, devname);
if (ret < 0)
return ret;
fprintf(stderr, "%s) = %d\n", *devname, ret);
if (access(*devname, F_OK) != 0) {
make_device(device, *devname);
}
return ret;
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment