Last active
December 13, 2022 20:40
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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