You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
195 lines
5.3 KiB
C
195 lines
5.3 KiB
C
// gcc -I/usr/include/ruby-3.0.0/x86_64-linux -I/usr/include/ruby-3.0.0/ruby/backward -I/usr/include/ruby-3.0.0 -shared -fPIC -o linux_rb.so linux_rb.c
|
|
|
|
#include <ruby.h>
|
|
|
|
#include <linux/sched.h> /* Definition of struct clone_args */
|
|
#include <sched.h> /* Definition of CLONE_* constants */
|
|
#include <sys/syscall.h> /* Definition of SYS_* constants */
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/ip.h>
|
|
#include <net/if.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
static volatile _Thread_local unsigned long long clone_flags =
|
|
CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID;
|
|
static pid_t fork_hijack()
|
|
{
|
|
struct clone_args ca = {
|
|
.flags = clone_flags,
|
|
.exit_signal = SIGCHLD,
|
|
};
|
|
clone_flags = CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID;
|
|
return syscall(SYS_clone3, &ca, sizeof(ca));
|
|
}
|
|
|
|
static void hijack_fork()
|
|
{
|
|
char* ptr = (char*) &_Fork;
|
|
void* page = (void*) (((uintptr_t) ptr) & ~4095);
|
|
mprotect(page, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
|
|
ptr[0] = 0xe9;
|
|
*(uint32_t*)(ptr+1) = (uintptr_t) &fork_hijack - (uintptr_t) (ptr+5);
|
|
mprotect(page, 4096, PROT_READ | PROT_EXEC);
|
|
}
|
|
|
|
static VALUE rb_clone(VALUE obj, VALUE flags)
|
|
{
|
|
unsigned long long old_flags = flags;
|
|
clone_flags = RB_NUM2LONG(flags);
|
|
pid_t pid = NUM2PIDT(rb_funcall(rb_mProcess, rb_intern("_fork"), 0));
|
|
|
|
switch (pid)
|
|
{
|
|
case 0:
|
|
if (rb_block_given_p())
|
|
{
|
|
int status;
|
|
rb_protect(rb_yield, Qundef, &status);
|
|
ruby_stop(status);
|
|
}
|
|
return Qnil;
|
|
|
|
case -1:
|
|
rb_sys_fail("clone3");
|
|
return Qnil;
|
|
|
|
default:
|
|
return PIDT2NUM(pid);
|
|
}
|
|
}
|
|
|
|
static VALUE rb_unshare(VALUE self, VALUE flags)
|
|
{
|
|
if (unshare(RB_NUM2INT(flags)) != 0)
|
|
rb_sys_fail("unshare");
|
|
return Qnil;
|
|
}
|
|
|
|
#define MAYBE_CSTR(x) ((x) == Qnil ? NULL : StringValueCStr(x))
|
|
static VALUE rb_mount(int argc, VALUE* argv, VALUE self)
|
|
{
|
|
VALUE src, tgt, fstype, flags, opts;
|
|
rb_scan_args(argc, argv, "32", &src, &tgt, &fstype, &flags, &opts);
|
|
if (mount(MAYBE_CSTR(src), MAYBE_CSTR(tgt), MAYBE_CSTR(fstype),
|
|
flags == Qnil ? 0 : RB_NUM2ULONG(flags), MAYBE_CSTR(opts)) != 0)
|
|
rb_sys_fail("mount");
|
|
return Qnil;
|
|
}
|
|
|
|
static VALUE rb_umount(VALUE self, VALUE path, VALUE flags)
|
|
{
|
|
if (umount2(StringValueCStr(path), RB_NUM2INT(flags)) != 0)
|
|
rb_sys_fail("umount2");
|
|
return Qnil;
|
|
}
|
|
|
|
static VALUE rb_gethostname(VALUE self)
|
|
{
|
|
char buf[HOST_NAME_MAX+1];
|
|
if (gethostname(buf, HOST_NAME_MAX) != 0)
|
|
rb_sys_fail("gethostname");
|
|
buf[HOST_NAME_MAX] = '\0';
|
|
return rb_str_new_cstr(buf);
|
|
}
|
|
|
|
static VALUE rb_sethostname(VALUE self, VALUE name)
|
|
{
|
|
Check_Type(name, RUBY_T_STRING);
|
|
if (sethostname(RSTRING_PTR(name), RSTRING_LEN(name)) != 0)
|
|
rb_sys_fail("sethostname");
|
|
return Qnil;
|
|
}
|
|
|
|
static VALUE rb_getdomainname(VALUE self)
|
|
{
|
|
char buf[65];
|
|
if (getdomainname(buf, 64) != 0)
|
|
rb_sys_fail("getdomainname");
|
|
buf[64] = '\0';
|
|
return rb_str_new_cstr(buf);
|
|
}
|
|
|
|
static VALUE rb_setdomainname(VALUE self, VALUE name)
|
|
{
|
|
Check_Type(name, RUBY_T_STRING);
|
|
if (setdomainname(RSTRING_PTR(name), RSTRING_LEN(name)) != 0)
|
|
rb_sys_fail("setdomainname");
|
|
return Qnil;
|
|
}
|
|
|
|
static VALUE rb_up_lo(VALUE self)
|
|
{
|
|
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
|
if (fd == -1) rb_sys_fail("socket");
|
|
|
|
struct ifreq req;
|
|
memset(&req, 0, sizeof(req));
|
|
strncpy(req.ifr_name, "lo", IFNAMSIZ);
|
|
req.ifr_flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING;
|
|
int res = ioctl(fd, SIOCSIFFLAGS, &req);
|
|
int errsav = errno;
|
|
close(fd);
|
|
|
|
if (res != 0)
|
|
{
|
|
errno = errsav;
|
|
rb_sys_fail("ioctl");
|
|
}
|
|
return Qnil;
|
|
}
|
|
|
|
static VALUE rb_pivot_root(VALUE self, VALUE new, VALUE old)
|
|
{
|
|
if (syscall(SYS_pivot_root, StringValueCStr(new), StringValueCStr(old)) != 0)
|
|
rb_sys_fail("pivot_root");
|
|
return Qnil;
|
|
}
|
|
|
|
void Init_linux_rb(void)
|
|
{
|
|
hijack_fork();
|
|
|
|
VALUE mod = rb_define_module("Linux");
|
|
#define DEF(name, fun, arity) \
|
|
rb_define_singleton_method(mod, name, fun, arity); \
|
|
rb_define_module_function(mod, name, fun, arity)
|
|
|
|
DEF("clone", rb_clone, 1);
|
|
DEF("unshare", rb_unshare, 1);
|
|
DEF("mount", rb_mount, -1);
|
|
DEF("umount", rb_umount, 2);
|
|
DEF("up_lo", rb_up_lo, 0);
|
|
DEF("hostname", rb_gethostname, 0);
|
|
DEF("hostname=", rb_sethostname, 1);
|
|
DEF("domainname", rb_getdomainname, 0);
|
|
DEF("domainname=", rb_setdomainname, 1);
|
|
DEF("pivot_root", rb_pivot_root, 2);
|
|
#undef DEF
|
|
|
|
#define C(x) rb_define_const(mod, #x, RB_LONG2NUM(x))
|
|
C(CLONE_VM); C(CLONE_FS); C(CLONE_FILES); C(CLONE_SIGHAND); C(CLONE_PIDFD);
|
|
C(CLONE_PTRACE); C(CLONE_VFORK); C(CLONE_PARENT); C(CLONE_THREAD);
|
|
C(CLONE_NEWNS); C(CLONE_SYSVSEM); C(CLONE_SETTLS); C(CLONE_PARENT_SETTID);
|
|
C(CLONE_CHILD_CLEARTID); C(CLONE_DETACHED); C(CLONE_UNTRACED);
|
|
C(CLONE_CHILD_SETTID); C(CLONE_NEWCGROUP); C(CLONE_NEWUTS); C(CLONE_NEWIPC);
|
|
C(CLONE_NEWUSER); C(CLONE_NEWPID); C(CLONE_NEWNET); C(CLONE_IO);
|
|
|
|
C(MS_RDONLY); C(MS_NOSUID); C(MS_NODEV); C(MS_NOEXEC); C(MS_SYNCHRONOUS);
|
|
C(MS_REMOUNT); C(MS_MANDLOCK); C(MS_DIRSYNC);
|
|
#ifdef MS_NOSYMFOLLOW
|
|
C(MS_NOSYMFOLLOW);
|
|
#endif
|
|
C(MS_NOATIME); C(MS_NODIRATIME); C(MS_BIND); C(MS_MOVE); C(MS_REC);
|
|
C(MS_SILENT); C(MS_POSIXACL); C(MS_UNBINDABLE); C(MS_PRIVATE); C(MS_SLAVE);
|
|
C(MS_SHARED); C(MS_RELATIME); C(MS_KERNMOUNT); C(MS_I_VERSION);
|
|
C(MS_STRICTATIME); C(MS_LAZYTIME); C(MS_ACTIVE); C(MS_NOUSER);
|
|
|
|
C(MNT_FORCE); C(MNT_DETACH); C(MNT_EXPIRE); C(UMOUNT_NOFOLLOW);
|
|
#undef C
|
|
}
|