Question

I cannot post my actual code due to work copyright, so I will try to show my problem with simple example code.

I have a C extension whose simplified version looks like:

#include <ruby.h>
#include <termios.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

VALUE test(VALUE self, VALUE string);
void Init_module_name() {
     module_name = rb_define_module("Modulename");
     c_modulename = rb_define_class_under(modulename, "Class", rb_cObject);
     rb_define_method(c_modulename, "test", test, 1);

     e_ModuleNameError = rb_define_class_under(modulename, "Error", rb_eStandardError);
}

VALUE test(VALUE self, VALUE string) {
    char *c_string = StringValueCStr(string);
    int fd = open(c_string, O_RDWR | O_NOCTTY | O_NONBLOCK);

    if (fd == -1) {
       rb_raise(e_ModuleNameError, "Failed to open file");
    }
    if (!isatty(fd)) {
       rb_raise(e_ModuleNameError, "File is not a tty");
    }

    struct termios config;
    int termios_ret = init_termios(config, fd)
    if (termios_ret != OK) { // OK defined by enum in modulename's header
        close(fd);
        rb_raise(e_ModuleNameError, "Termios init failed.");
    }

    int success = write(fd, "I'm a string", str_length);
    if (success < str_length) {
        close(fd);
        rb_raise(e_ModuleNameError, "Failed to write to file.");
    }

    close(fd);
    return rb_str_new2("Success");
}

Then, the ruby code that requires this looks like:

require 'modulename'

class ModuleName
  attr_acessor :file

  def initialize(file)
    @file = file
    @object = Modulename::Class.new
  end

  def test
    @object.test @file
  end
end

Which is then called in my production project like:

require "modulename_ruby_file"

x = ModuleName "/dev/pts/1"
x.test

Here is the interesting thing. When I run this code in production, the return value from x.test above is false (As in literally the value false, not a string). Also, the write to the file never happens. However, if I do it in some simplified test code it returns the string "Success" just like expected and the write is indeed completed.

Does anyone know of any situation that would cause this function not to execute the write, and return false? I already tried putting a rescue around it in case it was throwing one of the rb_raises, but it doesn't appear to be.

Me and 3 other members of my team have looked at this all afternoon and have not found an answer.

Was it helpful?

Solution

Finally figured this out, and it was very similar to what @NeilSlater was saying in the comments on the question.

We added a TON of debugging to the C code and had it write to a log file, and figured out that the C function (test in my example) was literally never being called. So, we looked at the symbol table for the .so and the assembly code gcc was generating and both looked fine. Finally we just said, "Let's change the name of the function and see if that helps" and... it worked. The actual function was named logout, and changing it to project_name_logout worked, so apparently there was some kind of namespace collision. So, much like @NeilSlater said, it had something to do with the environment!

So, for anyone else finding this on google: Add a "namespace" to your C code by prefixing all functions with your project name and you should be able to avoid this issue. [One of the other members mentioned, after the fact, that this was a good practice in C anyway.]

Note: We did not take the time to track down what was colliding with logout.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top