I am writing a Ruby method that calls Linux's utsname.h.
extconf.rb:
#!/usr/bin/env ruby
require 'mkmf'
abort('Not a linux') unless have_const('linux') || RbConfig::CONFIG['arch'].to_s[/linux/]
abort('No utsname.h') unless have_header('sys/utsname.h')
create_makefile 'utsname'
Here's the C code, (which isn't ideal, but I am still following the bad practice to learn why the values are getting corrupted inside irb):
#include <sys/utsname.h>
#include "ruby.h"
static struct utsname buf ;
static VALUE machine, nodename ;
void init() {
short status = uname(&buf) ;
if (status > -1) {
machine = rb_str_new_cstr(buf.machine) ;
nodename = rb_str_new_cstr(buf.nodename) ;
} else {
machine = rb_str_new_cstr("") ;
nodename = rb_str_new_cstr("") ;
}
}
static VALUE getMachine(VALUE obj) {
return machine ;
}
static VALUE getNodename(VALUE obj) {
return nodename ;
}
int Init_utsname() {
init() ;
rb_define_global_function("machine", getMachine, 0) ;
rb_define_global_function("nodename", getNodename, 0) ;
}
Now after compiling and running in ruby interpreter, without irb:
$ ruby extconf.rb && make && ruby -r ./utsname.so -e "p [machine, nodename, machine, machine, nodename]"
checking for linux... yes
checking for sys/utsname.h... yes
creating Makefile
linking shared-object utsname.so
["x86_64", "archlinux", "x86_64", "x86_64", "archlinux"]
But when I am running that same thing inside irb:
$ irb
irb(main):001:0> require_relative File.join(__dir__, 'utsname.so')
=> true
irb(main):002:0> p [machine, nodename, machine, machine, nodename]
["x86_64", "archlinux", "x86_64", "x86_64", "archlinux"]
=> ["x86_64", "archlinux", "x86_64", "x86_64", "archlinux"]
irb(main):003:0> a = ''.match //
=> #<MatchData "">
irb(main):004:0> p [machine, nodename, machine, machine, nodename]
[[], ["irb(main):003:0> "], [], [], ["irb(main):003:0> "]]
=> [[], ["irb(main):003:0> "], [], [], ["irb(main):003:0> "]]
That happens randomly Sometimes the nodename is a string, sometimes, it's a matchdata, sometimes, it's something else, and sometimes I get the whole Ruby interpreter to crash (segmentation fault).
Now if I modify my C code, make the machine and nodename a local variable and that works reliably:
#include <sys/utsname.h>
#include "ruby.h"
static struct utsname buf ;
static short status ;
void init() {
status = uname(&buf) ;
}
static VALUE getMachine(VALUE obj) {
VALUE machine = status < 0 ? rb_str_new_cstr("") : rb_str_new_cstr(buf.machine) ;
return machine ;
}
static VALUE getNodename(VALUE obj) {
VALUE nodename = status < 0 ? rb_str_new_cstr("") : rb_str_new_cstr(buf.nodename) ;
return nodename ;
}
int Init_utsname() {
init() ;
rb_define_global_function("machine", getMachine, 0) ;
rb_define_global_function("nodename", getNodename, 0) ;
}
So what's going on? Why the first C code is buggy?