linux_rb.c (5425B)
1 // 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 2 3 #include <ruby.h> 4 5 #include <linux/sched.h> /* Definition of struct clone_args */ 6 #include <sched.h> /* Definition of CLONE_* constants */ 7 #include <sys/syscall.h> /* Definition of SYS_* constants */ 8 #include <unistd.h> 9 #include <signal.h> 10 #include <errno.h> 11 #include <sys/mount.h> 12 #include <sys/socket.h> 13 #include <netinet/ip.h> 14 #include <net/if.h> 15 16 #include <sys/mman.h> 17 18 static volatile _Thread_local unsigned long long clone_flags = 19 CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID; 20 static pid_t fork_hijack() 21 { 22 struct clone_args ca = { 23 .flags = clone_flags, 24 .exit_signal = SIGCHLD, 25 }; 26 clone_flags = CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID; 27 return syscall(SYS_clone3, &ca, sizeof(ca)); 28 } 29 30 static void hijack_fork() 31 { 32 char* ptr = (char*) &_Fork; 33 void* page = (void*) (((uintptr_t) ptr) & ~4095); 34 mprotect(page, 4096, PROT_READ | PROT_WRITE | PROT_EXEC); 35 ptr[0] = 0xe9; 36 *(uint32_t*)(ptr+1) = (uintptr_t) &fork_hijack - (uintptr_t) (ptr+5); 37 mprotect(page, 4096, PROT_READ | PROT_EXEC); 38 } 39 40 static VALUE rb_clone(VALUE obj, VALUE flags) 41 { 42 unsigned long long old_flags = flags; 43 clone_flags = RB_NUM2LONG(flags); 44 pid_t pid = NUM2PIDT(rb_funcall(rb_mProcess, rb_intern("_fork"), 0)); 45 46 switch (pid) 47 { 48 case 0: 49 if (rb_block_given_p()) 50 { 51 int status; 52 rb_protect(rb_yield, Qundef, &status); 53 ruby_stop(status); 54 } 55 return Qnil; 56 57 case -1: 58 rb_sys_fail("clone3"); 59 return Qnil; 60 61 default: 62 return PIDT2NUM(pid); 63 } 64 } 65 66 static VALUE rb_unshare(VALUE self, VALUE flags) 67 { 68 if (unshare(RB_NUM2INT(flags)) != 0) 69 rb_sys_fail("unshare"); 70 return Qnil; 71 } 72 73 #define MAYBE_CSTR(x) ((x) == Qnil ? NULL : StringValueCStr(x)) 74 static VALUE rb_mount(int argc, VALUE* argv, VALUE self) 75 { 76 VALUE src, tgt, fstype, flags, opts; 77 rb_scan_args(argc, argv, "32", &src, &tgt, &fstype, &flags, &opts); 78 if (mount(MAYBE_CSTR(src), MAYBE_CSTR(tgt), MAYBE_CSTR(fstype), 79 flags == Qnil ? 0 : RB_NUM2ULONG(flags), MAYBE_CSTR(opts)) != 0) 80 rb_sys_fail("mount"); 81 return Qnil; 82 } 83 84 static VALUE rb_umount(VALUE self, VALUE path, VALUE flags) 85 { 86 if (umount2(StringValueCStr(path), RB_NUM2INT(flags)) != 0) 87 rb_sys_fail("umount2"); 88 return Qnil; 89 } 90 91 static VALUE rb_gethostname(VALUE self) 92 { 93 char buf[HOST_NAME_MAX+1]; 94 if (gethostname(buf, HOST_NAME_MAX) != 0) 95 rb_sys_fail("gethostname"); 96 buf[HOST_NAME_MAX] = '\0'; 97 return rb_str_new_cstr(buf); 98 } 99 100 static VALUE rb_sethostname(VALUE self, VALUE name) 101 { 102 Check_Type(name, RUBY_T_STRING); 103 if (sethostname(RSTRING_PTR(name), RSTRING_LEN(name)) != 0) 104 rb_sys_fail("sethostname"); 105 return Qnil; 106 } 107 108 static VALUE rb_getdomainname(VALUE self) 109 { 110 char buf[65]; 111 if (getdomainname(buf, 64) != 0) 112 rb_sys_fail("getdomainname"); 113 buf[64] = '\0'; 114 return rb_str_new_cstr(buf); 115 } 116 117 static VALUE rb_setdomainname(VALUE self, VALUE name) 118 { 119 Check_Type(name, RUBY_T_STRING); 120 if (setdomainname(RSTRING_PTR(name), RSTRING_LEN(name)) != 0) 121 rb_sys_fail("setdomainname"); 122 return Qnil; 123 } 124 125 static VALUE rb_up_lo(VALUE self) 126 { 127 int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); 128 if (fd == -1) rb_sys_fail("socket"); 129 130 struct ifreq req; 131 memset(&req, 0, sizeof(req)); 132 strncpy(req.ifr_name, "lo", IFNAMSIZ); 133 req.ifr_flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING; 134 int res = ioctl(fd, SIOCSIFFLAGS, &req); 135 int errsav = errno; 136 close(fd); 137 138 if (res != 0) 139 { 140 errno = errsav; 141 rb_sys_fail("ioctl"); 142 } 143 return Qnil; 144 } 145 146 static VALUE rb_pivot_root(VALUE self, VALUE new, VALUE old) 147 { 148 if (syscall(SYS_pivot_root, StringValueCStr(new), StringValueCStr(old)) != 0) 149 rb_sys_fail("pivot_root"); 150 return Qnil; 151 } 152 153 void Init_linux_rb(void) 154 { 155 hijack_fork(); 156 157 VALUE mod = rb_define_module("Linux"); 158 #define DEF(name, fun, arity) \ 159 rb_define_singleton_method(mod, name, fun, arity); \ 160 rb_define_module_function(mod, name, fun, arity) 161 162 DEF("clone", rb_clone, 1); 163 DEF("unshare", rb_unshare, 1); 164 DEF("mount", rb_mount, -1); 165 DEF("umount", rb_umount, 2); 166 DEF("up_lo", rb_up_lo, 0); 167 DEF("hostname", rb_gethostname, 0); 168 DEF("hostname=", rb_sethostname, 1); 169 DEF("domainname", rb_getdomainname, 0); 170 DEF("domainname=", rb_setdomainname, 1); 171 DEF("pivot_root", rb_pivot_root, 2); 172 #undef DEF 173 174 #define C(x) rb_define_const(mod, #x, RB_LONG2NUM(x)) 175 C(CLONE_VM); C(CLONE_FS); C(CLONE_FILES); C(CLONE_SIGHAND); C(CLONE_PIDFD); 176 C(CLONE_PTRACE); C(CLONE_VFORK); C(CLONE_PARENT); C(CLONE_THREAD); 177 C(CLONE_NEWNS); C(CLONE_SYSVSEM); C(CLONE_SETTLS); C(CLONE_PARENT_SETTID); 178 C(CLONE_CHILD_CLEARTID); C(CLONE_DETACHED); C(CLONE_UNTRACED); 179 C(CLONE_CHILD_SETTID); C(CLONE_NEWCGROUP); C(CLONE_NEWUTS); C(CLONE_NEWIPC); 180 C(CLONE_NEWUSER); C(CLONE_NEWPID); C(CLONE_NEWNET); C(CLONE_IO); 181 182 C(MS_RDONLY); C(MS_NOSUID); C(MS_NODEV); C(MS_NOEXEC); C(MS_SYNCHRONOUS); 183 C(MS_REMOUNT); C(MS_MANDLOCK); C(MS_DIRSYNC); 184 #ifdef MS_NOSYMFOLLOW 185 C(MS_NOSYMFOLLOW); 186 #endif 187 C(MS_NOATIME); C(MS_NODIRATIME); C(MS_BIND); C(MS_MOVE); C(MS_REC); 188 C(MS_SILENT); C(MS_POSIXACL); C(MS_UNBINDABLE); C(MS_PRIVATE); C(MS_SLAVE); 189 C(MS_SHARED); C(MS_RELATIME); C(MS_KERNMOUNT); C(MS_I_VERSION); 190 C(MS_STRICTATIME); C(MS_LAZYTIME); C(MS_ACTIVE); C(MS_NOUSER); 191 192 C(MNT_FORCE); C(MNT_DETACH); C(MNT_EXPIRE); C(UMOUNT_NOFOLLOW); 193 #undef C 194 }