#include <signal.h>
#include <sys/types.h>
#include <errno.h>
#include <utmp.h>
#include <ruby.h>
#include <stdio.h>

// Module and classes.
static VALUE mWtmp;
static VALUE cList;
static VALUE cEntry;

static int
_alive (pid_t pid)
{
    if (pid <= 0) { return 0; }

    // Straight from last.c
    if (kill (pid, 0) != 0 && errno == ESRCH)
	return 0;
    else
	return 1;
}

static VALUE
mWtmp_alive (VALUE self, VALUE arg)
{
    Check_Type (arg, T_FIXNUM);
    pid_t pid = FIX2INT (arg);

    return _alive (pid) ? Qtrue : Qfalse;
}

static VALUE
cEntry_initialize (VALUE self)
{
    rb_hash_aset (self,
		  ID2SYM(rb_intern("pid")),
		  INT2FIX(-1));
    return self;
}

static VALUE
cEntry_alive (VALUE self)
{
    pid_t pid = FIX2INT (rb_hash_aref (self, ID2SYM(rb_intern("pid"))));

    return _alive (pid) ? Qtrue : Qfalse;
}

// Foreach utmp structure in /var/log/wtmp {
//     Create Entry object
//     Push into array
// }
static VALUE
cList_initialize (VALUE self)
{
    struct utmp *result;
    utmpname (_PATH_WTMP);
    setutent ();

    while ((result = getutent ()) != NULL) {
        VALUE entry = rb_class_new_instance (0, 0, cEntry);

        rb_hash_aset (entry,
                      ID2SYM(rb_intern("name")),
                      rb_str_new2(result->ut_name));

        rb_hash_aset (entry,
                      ID2SYM(rb_intern("pid")),
                      INT2FIX(result->ut_pid));

        rb_hash_aset (entry,
                      ID2SYM(rb_intern("time")),
                      INT2NUM(result->ut_time));

        rb_hash_aset (entry,
                      ID2SYM(rb_intern("type")),
                      INT2FIX(result->ut_type));

        rb_ary_push (self, entry);
    }

    free (result);
    return (self);
}

// Define module, classes and methods.
void
Init_wtmplist ()
{
    mWtmp = rb_define_module("Wtmp");
    cList = rb_define_class_under (mWtmp, "List", rb_cArray);
    cEntry = rb_define_class_under (mWtmp, "Entry", rb_cHash);

    rb_define_module_function (mWtmp, "alive?", mWtmp_alive, 1);
    rb_define_method (cList, "initialize", cList_initialize, 0);
    rb_define_method (cEntry, "initialize", cEntry_initialize, 0);
    rb_define_method (cEntry, "alive?", cEntry_alive, 0);
}
