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.
libshit/tools/linux_rb.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
}