/************************************************ object.c - $Author$ $Date$ created at: Thu Jul 15 12:01:24 JST 1993 Copyright (C) 1993-1999 Yukihiro Matsumoto ************************************************/ #include "ruby.h" #include "st.h" #include VALUE rb_mKernel; VALUE rb_cObject; VALUE rb_cModule; VALUE rb_cClass; VALUE rb_cData; VALUE rb_cNilClass; VALUE rb_cTrueClass; VALUE rb_cFalseClass; VALUE rb_f_sprintf(); VALUE rb_obj_alloc(); static ID eq, eql; static ID inspect; VALUE rb_equal(obj1, obj2) VALUE obj1, obj2; { VALUE result; if (obj1 == obj2) return Qtrue; result = rb_funcall(obj1, eq, 1, obj2); if (result == Qfalse || NIL_P(result)) return Qfalse; return Qtrue; } int rb_eql(obj1, obj2) VALUE obj1, obj2; { return rb_funcall(obj1, eql, 1, obj2) == Qtrue; } static VALUE rb_obj_equal(obj1, obj2) VALUE obj1, obj2; { if (obj1 == obj2) return Qtrue; return Qfalse; } static VALUE rb_any_to_a(obj) VALUE obj; { return rb_ary_new3(1, obj); } static VALUE rb_obj_hash(obj) VALUE obj; { return (long)obj|FIXNUM_FLAG; } VALUE rb_obj_id(obj) VALUE obj; { if (rb_special_const_p(obj)) { return INT2NUM((long)obj); } return (long)obj|FIXNUM_FLAG; } static VALUE rb_obj_type(obj) VALUE obj; { VALUE cl = CLASS_OF(obj); while (FL_TEST(cl, FL_SINGLETON) || TYPE(cl) == T_ICLASS) { cl = RCLASS(cl)->super; } return cl; } VALUE rb_obj_clone(obj) VALUE obj; { VALUE clone; if (TYPE(obj) != T_OBJECT) { rb_raise(rb_eTypeError, "can't clone %s", rb_class2name(CLASS_OF(obj))); } clone = rb_obj_alloc(RBASIC(obj)->klass); CLONESETUP(clone,obj); if (ROBJECT(obj)->iv_tbl) { ROBJECT(clone)->iv_tbl = st_copy(ROBJECT(obj)->iv_tbl); RBASIC(clone)->klass = rb_singleton_class_clone(RBASIC(obj)->klass); RBASIC(clone)->flags = RBASIC(obj)->flags; } return clone; } static VALUE rb_obj_dup(obj) VALUE obj; { return rb_funcall(obj, rb_intern("clone"), 0, 0); } VALUE rb_any_to_s(obj) VALUE obj; { char *s; char *cname = rb_class2name(CLASS_OF(obj)); VALUE str; s = ALLOCA_N(char, strlen(cname)+6+16+1); /* 6:tags 16:addr 1:eos */ sprintf(s, "#<%s:0x%lx>", cname, obj); str = rb_str_new2(s); if (OBJ_TAINTED(obj)) OBJ_TAINT(str); return str; } VALUE rb_inspect(obj) VALUE obj; { return rb_obj_as_string(rb_funcall(obj, inspect, 0, 0)); } static int inspect_i(id, value, str) ID id; VALUE value; VALUE str; { VALUE str2; char *ivname; /* need not to show internal data */ if (CLASS_OF(value) == 0) return ST_CONTINUE; if (!rb_is_instance_id(id)) return ST_CONTINUE; if (RSTRING(str)->ptr[0] == '-') { RSTRING(str)->ptr[0] = '#'; rb_str_cat(str, ": ", 2); } else { rb_str_cat(str, ", ", 2); } ivname = rb_id2name(id); rb_str_cat(str, ivname, strlen(ivname)); rb_str_cat(str, "=", 1); str2 = rb_inspect(value); rb_str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len); return ST_CONTINUE; } static VALUE inspect_obj(obj, str) VALUE obj, str; { st_foreach(ROBJECT(obj)->iv_tbl, inspect_i, str); rb_str_cat(str, ">", 1); return str; } static VALUE rb_obj_inspect(obj) VALUE obj; { if (TYPE(obj) == T_OBJECT && ROBJECT(obj)->iv_tbl && ROBJECT(obj)->iv_tbl->num_entries > 0) { VALUE str; char *b; b = rb_class2name(CLASS_OF(obj)); if (rb_inspecting_p(obj)) { char *buf = ALLOCA_N(char, strlen(b)+8); sprintf(buf, "#<%s:...>", b); return rb_str_new2(buf); } str = rb_str_new2("-<"); rb_str_cat(str, b, strlen(b)); return rb_protect_inspect(inspect_obj, obj, str); } return rb_funcall(obj, rb_intern("to_s"), 0, 0); } VALUE rb_obj_is_instance_of(obj, c) VALUE obj, c; { VALUE cl; switch (TYPE(c)) { case T_MODULE: case T_CLASS: break; case T_NIL: if (NIL_P(obj)) return Qtrue; return Qfalse; case T_FALSE: if (obj) return Qfalse; return Qtrue; case T_TRUE: if (obj) return Qtrue; return Qfalse; default: rb_raise(rb_eTypeError, "class or module required"); } cl = CLASS_OF(obj); while (FL_TEST(cl, FL_SINGLETON) || TYPE(cl) == T_ICLASS) { cl = RCLASS(cl)->super; } if (c == cl) return Qtrue; return Qfalse; } VALUE rb_obj_is_kind_of(obj, c) VALUE obj, c; { VALUE cl = CLASS_OF(obj); switch (TYPE(c)) { case T_MODULE: case T_CLASS: break; default: rb_raise(rb_eTypeError, "class or module required"); } while (cl) { if (cl == c || RCLASS(cl)->m_tbl == RCLASS(c)->m_tbl) return Qtrue; cl = RCLASS(cl)->super; } return Qfalse; } static VALUE rb_obj_dummy(obj) VALUE obj; { return Qnil; } VALUE rb_obj_tainted(obj) VALUE obj; { if (OBJ_TAINTED(obj)) return Qtrue; return Qfalse; } VALUE rb_obj_taint(obj) VALUE obj; { OBJ_TAINT(obj); return obj; } VALUE rb_obj_untaint(obj) VALUE obj; { rb_secure(3); FL_UNSET(obj, FL_TAINT); return obj; } static VALUE nil_to_i(obj) VALUE obj; { return INT2FIX(0); } static VALUE nil_to_s(obj) VALUE obj; { return rb_str_new2(""); } static VALUE nil_to_a(obj) VALUE obj; { return rb_ary_new2(0); } static VALUE nil_inspect(obj) VALUE obj; { return rb_str_new2("nil"); } static VALUE nil_type(obj) VALUE obj; { return rb_cNilClass; } #ifdef NIL_PLUS static VALUE nil_plus(x, y) VALUE x, y; { switch (TYPE(y)) { case T_NIL: case T_FIXNUM: case T_FLOAT: case T_BIGNUM: case T_STRING: case T_ARRAY: return y; default: rb_raise(rb_eTypeError, "tried to add %s(%s) to nil", STR2CSTR(rb_inspect(y)), rb_class2name(CLASS_OF(y))); } /* not reached */ } #endif static VALUE main_to_s(obj) VALUE obj; { return rb_str_new2("main"); } static VALUE true_to_s(obj) VALUE obj; { return rb_str_new2("true"); } static VALUE true_type(obj) VALUE obj; { return rb_cTrueClass; } static VALUE true_and(obj, obj2) VALUE obj, obj2; { return RTEST(obj2)?Qtrue:Qfalse; } static VALUE true_or(obj, obj2) VALUE obj, obj2; { return Qtrue; } static VALUE true_xor(obj, obj2) VALUE obj, obj2; { return RTEST(obj2)?Qfalse:Qtrue; } static VALUE false_to_s(obj) VALUE obj; { return rb_str_new2("false"); } static VALUE false_type(obj) VALUE obj; { return rb_cFalseClass; } static VALUE false_and(obj, obj2) VALUE obj, obj2; { return Qfalse; } static VALUE false_or(obj, obj2) VALUE obj, obj2; { return RTEST(obj2)?Qtrue:Qfalse; } static VALUE false_xor(obj, obj2) VALUE obj, obj2; { return RTEST(obj2)?Qtrue:Qfalse; } static VALUE rb_true(obj) VALUE obj; { return Qtrue; } static VALUE rb_false(obj) VALUE obj; { return Qfalse; } VALUE rb_obj_alloc(klass) VALUE klass; { NEWOBJ(obj, struct RObject); OBJSETUP(obj, klass, T_OBJECT); obj->iv_tbl = 0; return (VALUE)obj; } static VALUE rb_mod_clone(module) VALUE module; { NEWOBJ(clone, struct RClass); CLONESETUP(clone, module); clone->super = RCLASS(module)->super; clone->iv_tbl = 0; clone->m_tbl = 0; /* avoid GC crashing */ if (RCLASS(module)->iv_tbl) { clone->iv_tbl = st_copy(RCLASS(module)->iv_tbl); } if (RCLASS(module)->m_tbl) { clone->m_tbl = st_copy(RCLASS(module)->m_tbl); } return (VALUE)clone; } static VALUE rb_mod_to_s(klass) VALUE klass; { return rb_str_dup(rb_class_path(klass)); } static VALUE rb_mod_eqq(mod, arg) VALUE mod, arg; { return rb_obj_is_kind_of(arg, mod); } static VALUE rb_mod_le(mod, arg) VALUE mod, arg; { switch (TYPE(arg)) { case T_MODULE: case T_CLASS: break; default: rb_raise(rb_eTypeError, "compared with non class/module"); } while (mod) { if (RCLASS(mod)->m_tbl == RCLASS(arg)->m_tbl) return Qtrue; mod = RCLASS(mod)->super; } return Qfalse; } static VALUE rb_mod_lt(mod, arg) VALUE mod, arg; { if (mod == arg) return Qfalse; return rb_mod_le(mod, arg); } static VALUE rb_mod_ge(mod, arg) VALUE mod, arg; { switch (TYPE(arg)) { case T_MODULE: case T_CLASS: break; default: rb_raise(rb_eTypeError, "compared with non class/module"); } return rb_mod_le(arg, mod); } static VALUE rb_mod_gt(mod, arg) VALUE mod, arg; { if (mod == arg) return Qfalse; return rb_mod_ge(mod, arg); } static VALUE rb_mod_cmp(mod, arg) VALUE mod, arg; { if (mod == arg) return INT2FIX(0); switch (TYPE(arg)) { case T_MODULE: case T_CLASS: break; default: rb_raise(rb_eTypeError, "<=> requires Class or Module (%s given)", rb_class2name(CLASS_OF(arg))); break; } if (rb_mod_le(mod, arg)) { return INT2FIX(-1); } return INT2FIX(1); } static VALUE rb_module_s_new(klass) { VALUE mod = rb_module_new(); RBASIC(mod)->klass = klass; return mod; } static VALUE rb_class_s_new(argc, argv) int argc; VALUE *argv; { VALUE super, klass; if (rb_scan_args(argc, argv, "01", &super) == 0) { super = rb_cObject; } Check_Type(super, T_CLASS); if (FL_TEST(super, FL_SINGLETON)) { rb_raise(rb_eTypeError, "can't make subclass of virtual class"); } klass = rb_class_new(super); /* make metaclass */ RBASIC(klass)->klass = rb_singleton_class_new(RBASIC(super)->klass); rb_singleton_class_attached(RBASIC(klass)->klass, klass); return klass; } static VALUE rb_class_s_inherited() { rb_raise(rb_eTypeError, "can't make subclass of Class"); } static VALUE rb_class_superclass(klass) VALUE klass; { VALUE super = RCLASS(klass)->super; while (TYPE(super) == T_ICLASS) { super = RCLASS(super)->super; } if (!super) { return Qnil; } return super; } ID rb_to_id(name) VALUE name; { ID id; if (TYPE(name) == T_STRING) { return rb_intern(RSTRING(name)->ptr); } id = NUM2UINT(name); if (!rb_id2name(id)) { rb_raise(rb_eArgError, "%d is not a symbol", id); } return id; } static VALUE rb_mod_attr(argc, argv, klass) int argc; VALUE *argv; VALUE klass; { VALUE name, pub; rb_scan_args(argc, argv, "11", &name, &pub); rb_attr(klass, rb_to_id(name), 1, RTEST(pub), Qtrue); return Qnil; } static VALUE rb_mod_attr_reader(argc, argv, klass) int argc; VALUE *argv; VALUE klass; { int i; for (i=0; ival, rb_intern(arg->s), 0); } static VALUE fail_to_type(arg) struct arg_to *arg; { rb_raise(rb_eTypeError, "failed to convert %s into %s", NIL_P(arg->val) ? "nil" : arg->val == Qtrue ? "true" : arg->val == Qfalse ? "false" : rb_class2name(CLASS_OF(arg->val)), arg->s); } VALUE rb_convert_type(val, type, tname, method) VALUE val; int type; const char *tname, *method; { struct arg_to arg1, arg2; if (TYPE(val) == type) return val; arg1.val = arg2.val = val; arg1.s = method; arg2.s = tname; val = rb_rescue(to_type, (VALUE)&arg1, fail_to_type, (VALUE)&arg2); Check_Type(val, type); return val; } VALUE rb_Integer(val) VALUE val; { struct arg_to arg1, arg2; switch (TYPE(val)) { case T_FLOAT: if (RFLOAT(val)->value <= (double)FIXNUM_MAX && RFLOAT(val)->value >= (double)FIXNUM_MIN) { break; } return rb_dbl2big(RFLOAT(val)->value); case T_BIGNUM: return val; case T_STRING: return rb_str2inum(RSTRING(val)->ptr, 0); case T_NIL: return INT2FIX(0); default: break; } arg1.val = arg2.val = val; arg1.s = "to_i"; arg2.s = "Integer"; val = rb_rescue(to_type, (VALUE)&arg1, fail_to_type, (VALUE)&arg2); if (!rb_obj_is_kind_of(val, rb_cInteger)) { rb_raise(rb_eTypeError, "to_i should return Integer"); } return val; } static VALUE rb_f_integer(obj, arg) VALUE obj, arg; { return rb_Integer(arg); } double rb_big2dbl _((VALUE)); VALUE rb_Float(val) VALUE val; { switch (TYPE(val)) { case T_FIXNUM: return rb_float_new((double)FIX2LONG(val)); case T_FLOAT: return val; case T_BIGNUM: return rb_float_new(rb_big2dbl(val)); case T_NIL: return rb_float_new(0.0); default: return rb_convert_type(val, T_FLOAT, "Float", "to_f"); } } static VALUE rb_f_float(obj, arg) VALUE obj, arg; { return rb_Float(arg); } double rb_num2dbl(val) VALUE val; { switch (TYPE(val)) { case T_FLOAT: return RFLOAT(val)->value; case T_STRING: rb_raise(rb_eTypeError, "no implicit conversion from String"); break; case T_NIL: rb_raise(rb_eTypeError, "no implicit conversion from nil"); break; default: break; } return RFLOAT(rb_Float(val))->value; } char* rb_str2cstr(str, len) VALUE str; int *len; { if (TYPE(str) != T_STRING) { str = rb_str_to_str(str); } if (len) *len = RSTRING(str)->len; return RSTRING(str)->ptr; } VALUE rb_String(val) VALUE val; { return rb_convert_type(val, T_STRING, "String", "to_s"); } static VALUE rb_f_string(obj, arg) VALUE obj, arg; { return rb_String(arg); } VALUE rb_Array(val) VALUE val; { if (TYPE(val) == T_ARRAY) return val; val = rb_funcall(val, rb_intern("to_a"), 0); if (TYPE(val) != T_ARRAY) { rb_raise(rb_eTypeError, "`to_a' did not return Array"); } return val; } static VALUE rb_f_array(obj, arg) VALUE obj, arg; { return rb_Array(arg); } static VALUE boot_defclass(name, super) char *name; VALUE super; { extern st_table *rb_class_tbl; VALUE obj = rb_class_new(super); ID id = rb_intern(name); rb_name_class(obj, id); st_add_direct(rb_class_tbl, id, obj); return obj; } VALUE ruby_top_self; void Init_Object() { VALUE metaclass; rb_cObject = boot_defclass("Object", 0); rb_cModule = boot_defclass("Module", rb_cObject); rb_cClass = boot_defclass("Class", rb_cModule); metaclass = RBASIC(rb_cObject)->klass = rb_singleton_class_new(rb_cClass); rb_singleton_class_attached(metaclass, rb_cObject); metaclass = RBASIC(rb_cModule)->klass = rb_singleton_class_new(metaclass); rb_singleton_class_attached(metaclass, rb_cModule); metaclass = RBASIC(rb_cClass)->klass = rb_singleton_class_new(metaclass); rb_singleton_class_attached(metaclass, rb_cClass); rb_mKernel = rb_define_module("Kernel"); rb_include_module(rb_cObject, rb_mKernel); rb_define_private_method(rb_cObject, "initialize", rb_obj_dummy, -1); rb_define_private_method(rb_cClass, "inherited", rb_obj_dummy, 1); /* * Ruby's Class Hierarchy Chart * * +------------------+ * | | * Object---->(Object) | * ^ ^ ^ ^ | * | | | | | * | | +-----+ +---------+ | * | | | | | * | +-----------+ | | * | | | | | * +------+ | Module--->(Module) | * | | ^ ^ | * OtherClass-->(OtherClass) | | | * | | | * Class---->(Class) | * ^ | * | | * +----------------+ * * + All metaclasses are instances of the class `Class'. */ rb_define_method(rb_mKernel, "nil?", rb_false, 0); rb_define_method(rb_mKernel, "==", rb_obj_equal, 1); rb_define_alias(rb_mKernel, "equal?", "=="); rb_define_alias(rb_mKernel, "===", "=="); rb_define_method(rb_mKernel, "=~", rb_false, 1); rb_define_method(rb_mKernel, "eql?", rb_obj_equal, 1); rb_define_method(rb_mKernel, "hash", rb_obj_hash, 0); rb_define_method(rb_mKernel, "id", rb_obj_id, 0); rb_define_method(rb_mKernel, "__id__", rb_obj_id, 0); rb_define_method(rb_mKernel, "type", rb_obj_type, 0); rb_define_method(rb_mKernel, "clone", rb_obj_clone, 0); rb_define_method(rb_mKernel, "dup", rb_obj_dup, 0); rb_define_method(rb_mKernel, "taint", rb_obj_taint, 0); rb_define_method(rb_mKernel, "tainted?", rb_obj_tainted, 0); rb_define_method(rb_mKernel, "untaint", rb_obj_untaint, 0); rb_define_method(rb_mKernel, "to_a", rb_any_to_a, 0); rb_define_method(rb_mKernel, "to_s", rb_any_to_s, 0); rb_define_method(rb_mKernel, "inspect", rb_obj_inspect, 0); rb_define_method(rb_mKernel, "methods", rb_obj_methods, 0); rb_define_method(rb_mKernel, "public_methods", rb_obj_methods, 0); rb_define_method(rb_mKernel, "singleton_methods", rb_obj_singleton_methods, 0); rb_define_method(rb_mKernel, "protected_methods", rb_obj_protected_methods, 0); rb_define_method(rb_mKernel, "private_methods", rb_obj_private_methods, 0); rb_define_method(rb_mKernel, "instance_variables", rb_obj_instance_variables, 0); rb_define_private_method(rb_mKernel, "remove_instance_variable", rb_obj_remove_instance_variable, 0); rb_define_method(rb_mKernel, "instance_of?", rb_obj_is_instance_of, 1); rb_define_method(rb_mKernel, "kind_of?", rb_obj_is_kind_of, 1); rb_define_method(rb_mKernel, "is_a?", rb_obj_is_kind_of, 1); rb_define_global_function("singleton_method_added", rb_obj_dummy, 1); rb_define_global_function("sprintf", rb_f_sprintf, -1); rb_define_global_function("format", rb_f_sprintf, -1); rb_define_global_function("Integer", rb_f_integer, 1); rb_define_global_function("Float", rb_f_float, 1); rb_define_global_function("String", rb_f_string, 1); rb_define_global_function("Array", rb_f_array, 1); rb_cNilClass = rb_define_class("NilClass", rb_cObject); rb_define_method(rb_cNilClass, "type", nil_type, 0); rb_define_method(rb_cNilClass, "to_i", nil_to_i, 0); rb_define_method(rb_cNilClass, "to_s", nil_to_s, 0); rb_define_method(rb_cNilClass, "to_a", nil_to_a, 0); rb_define_method(rb_cNilClass, "inspect", nil_inspect, 0); rb_define_method(rb_cNilClass, "&", false_and, 1); rb_define_method(rb_cNilClass, "|", false_or, 1); rb_define_method(rb_cNilClass, "^", false_xor, 1); rb_define_method(rb_cNilClass, "nil?", rb_true, 0); rb_undef_method(CLASS_OF(rb_cNilClass), "new"); rb_define_global_const("NIL", Qnil); rb_define_method(rb_cModule, "===", rb_mod_eqq, 1); rb_define_method(rb_cModule, "<=>", rb_mod_cmp, 1); rb_define_method(rb_cModule, "<", rb_mod_lt, 1); rb_define_method(rb_cModule, "<=", rb_mod_le, 1); rb_define_method(rb_cModule, ">", rb_mod_gt, 1); rb_define_method(rb_cModule, ">=", rb_mod_ge, 1); rb_define_method(rb_cModule, "clone", rb_mod_clone, 0); rb_define_method(rb_cModule, "to_s", rb_mod_to_s, 0); rb_define_method(rb_cModule, "included_modules", rb_mod_included_modules, 0); rb_define_method(rb_cModule, "name", rb_mod_name, 0); rb_define_method(rb_cModule, "ancestors", rb_mod_ancestors, 0); rb_define_private_method(rb_cModule, "attr", rb_mod_attr, -1); rb_define_private_method(rb_cModule, "attr_reader", rb_mod_attr_reader, -1); rb_define_private_method(rb_cModule, "attr_writer", rb_mod_attr_writer, -1); rb_define_private_method(rb_cModule, "attr_accessor", rb_mod_attr_accessor, -1); rb_define_singleton_method(rb_cModule, "new", rb_module_s_new, 0); rb_define_method(rb_cModule, "instance_methods", rb_class_instance_methods, -1); rb_define_method(rb_cModule, "public_instance_methods", rb_class_instance_methods, -1); rb_define_method(rb_cModule, "protected_instance_methods", rb_class_protected_instance_methods, -1); rb_define_method(rb_cModule, "private_instance_methods", rb_class_private_instance_methods, -1); rb_define_method(rb_cModule, "shared_variable", rb_mod_shvars, 0); rb_define_method(rb_cModule, "shared_variable_get", rb_mod_shvar_get, 1); rb_define_method(rb_cModule, "shared_variable_set", rb_mod_shvar_set, 2); rb_define_method(rb_cModule, "shared_variable_defined?", rb_mod_shvar_defined, 1); rb_define_private_method(rb_cModule, "remove_shared_variable", rb_mod_remove_shvar, 1); /* to be remove at 1.6*/ rb_define_method(rb_cModule, "constants", rb_mod_shvars, 0); rb_define_method(rb_cModule, "const_get", rb_mod_shvar_get, 1); rb_define_method(rb_cModule, "const_set", rb_mod_shvar_set, 2); rb_define_method(rb_cModule, "const_defined?", rb_mod_shvar_defined, 1); rb_define_private_method(rb_cModule, "remove_const", rb_mod_remove_shvar, 1); rb_define_private_method(rb_cModule, "method_added", rb_obj_dummy, 1); rb_define_method(rb_cClass, "new", rb_class_new_instance, -1); rb_define_method(rb_cClass, "superclass", rb_class_superclass, 0); rb_define_singleton_method(rb_cClass, "new", rb_class_s_new, -1); rb_undef_method(rb_cClass, "extend_object"); rb_undef_method(rb_cClass, "append_features"); rb_define_singleton_method(rb_cClass, "inherited", rb_class_s_inherited, 1); rb_cData = rb_define_class("Data", rb_cObject); rb_undef_method(CLASS_OF(rb_cData), "new"); ruby_top_self = rb_obj_alloc(rb_cObject); rb_global_variable(&ruby_top_self); rb_define_singleton_method(ruby_top_self, "to_s", main_to_s, 0); rb_cTrueClass = rb_define_class("TrueClass", rb_cObject); rb_define_method(rb_cTrueClass, "to_s", true_to_s, 0); rb_define_method(rb_cTrueClass, "type", true_type, 0); rb_define_method(rb_cTrueClass, "&", true_and, 1); rb_define_method(rb_cTrueClass, "|", true_or, 1); rb_define_method(rb_cTrueClass, "^", true_xor, 1); rb_undef_method(CLASS_OF(rb_cTrueClass), "new"); rb_define_global_const("TRUE", Qtrue); rb_cFalseClass = rb_define_class("FalseClass", rb_cObject); rb_define_method(rb_cFalseClass, "to_s", false_to_s, 0); rb_define_method(rb_cFalseClass, "type", false_type, 0); rb_define_method(rb_cFalseClass, "&", false_and, 1); rb_define_method(rb_cFalseClass, "|", false_or, 1); rb_define_method(rb_cFalseClass, "^", false_xor, 1); rb_undef_method(CLASS_OF(rb_cFalseClass), "new"); rb_define_global_const("FALSE", Qfalse); eq = rb_intern("=="); eql = rb_intern("eql?"); inspect = rb_intern("inspect"); } > 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
/*
    ELAPI

    Implementation of the ELAPI event interface.

    Copyright (C) Dmitri Pal <dpal@redhat.com> 2009

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#define _GNU_SOURCE
#include <sys/types.h>  /* for getpid() */
#include <unistd.h>     /* for getpid() */
#include <stdlib.h>     /* for realloc() */
#include <syslog.h>     /* for contants releted to severity */
#include <unistd.h>     /* for gethostname() */
#include <errno.h>      /* for errors */
#include <string.h>     /* for memset() and other */
#include <netdb.h>      /* for gethostbyname() */
#include <sys/socket.h> /* for inet_ntop() */
#include <arpa/inet.h>  /* for inet_ntop() */
#include <ctype.h>      /* for isspace() */
#include <stdarg.h>     /* for va_arg() */
#include <string.h>     /* for strndup() */

#include "elapi_priv.h"
#include "elapi_event.h"
#include "elapi_net.h"
#include "trace.h"
#include "config.h"

#include "collection_tools.h"

/* Internal return states from key processing */
#define E_LIST_EMPTY       0
#define E_LIST_ERROR       1
#define E_LIST_LAST        2
#define E_LIST_ADD         3
#define E_LIST_REMOVE      4

#define LOCALHOSTDOMAIN "localhost.localdomain"
#define LOCALHOST       "localhost"
#define LOCALADDRESS    "127.0.0.1"
#define LOCALADDRESSV6  "::1"

const char *undefined = "undefined";
const char *str_yes = "yes";
const char *str_no = "no";
const char *str_true = "true";
const char *str_false = "false";


/* Function to add host identity information to the template */
static int add_host_identity(struct collection_item *tpl, unsigned base)
{
    char hostname[NI_MAXHOST + 1];
    int error = EOK;
    int gai_ret_host = 0;
    int gai_ret_addr = 0;
    char host[NI_MAXHOST];
    char address[NI_MAXHOST];
    char *hnm, *haddr;
    ELAPI_ADDRLIST ifaddr;
    ELAPI_ADDRITEM ifa;
    struct sockaddr *addr;
    int family;
    int set_hostname = 0;
    int set_ip = 0;
    int used_this_ip = 0;

    TRACE_FLOW_STRING("add_host_identity", "Entry");

    /* The goal here to collect information about the host.
     * there is no need to actually use it for establishing
     * any connections.
     * It is a best effort attempt.
     */

    /* If we are not asked for hostname then say we already have it */
    if (!(base & E_HAVE_HOSTNAME)) set_hostname = 1;
    /* If we are not asked for ip then say we already have it */
    if (!(base & E_HAVE_HOSTIP)) set_ip = 1;

    if (ELAPI_GET_ADDRLIST(&ifaddr) == EOK) {

        TRACE_FLOW_STRING("getifaddrs", "Ok");

        /* Walk through linked list, maintaining head pointer so we
            can free list later */
        ELAPI_GET_FIRST_ADDR_ITEM(ifaddr, ifa);

        while (ifa != NULL) {

            TRACE_FLOW_STRING("Top of the loop", "");

            used_this_ip = 0;

            ELAPI_GET_ADDR(ifa, addr);
            if (!addr) {
                ELAPI_GET_NEXT_ADDR_ITEM(ifaddr, ifa);
                continue;
            }

            family = addr->sa_family;

            TRACE_FLOW_NUMBER("Family", family);

            /* For an AF_INET* interface address, display the address */
            if (family == AF_INET || family == AF_INET6) {

                TRACE_FLOW_NUMBER("Got right family", family);

                /* getnameinfo function claims that it returns NULL
                 * terminated strings. Well...
                 * We will trust it here and not clear memory using memset.
                 */

                gai_ret_host = getnameinfo(addr,
                                           (family == AF_INET) ? sizeof(struct sockaddr_in) :
                                                                 sizeof(struct sockaddr_in6),
                                           host,
                                           NI_MAXHOST,
                                           NULL,
                                           0,
                                           0 /* Gets host name */);

                gai_ret_addr = getnameinfo(addr,
                                           (family == AF_INET) ? sizeof(struct sockaddr_in) :
                                                                 sizeof(struct sockaddr_in6),
                                           address,
                                           NI_MAXHOST,
                                           NULL,
                                           0,
                                           NI_NUMERICHOST /* Gets address as string */);

                TRACE_INFO_STRING("Resolved host:", host);
                TRACE_INFO_STRING("Resolved address:", address);
                /* If we have not set host name set it */
                if(!set_hostname) {

                    TRACE_FLOW_STRING("Host name is not set", "");

                    hnm = NULL;
                    /* Use host name returned by gethostname() as main host name */
                    if (gethostname(hostname, NI_MAXHOST) == EOK) {
                        /* Make sure hostname is NULL terminated */
                        hostname[NI_MAXHOST] = '\0';
                        hnm = hostname;
                    }
                    else {
                        /* Were we able to get a host name ? */
                        if (gai_ret_host == EOK) {
                            TRACE_INFO_STRING("getnameinfo returned:", host);
                            hnm = host;
                        }
                    }

                    /* Do we have a host meaningful host name? */
                    if ((hnm) &&
                        ((strcasecmp(hnm, LOCALHOST) == 0 ) ||
                         (strcasecmp(hnm, LOCALHOSTDOMAIN) == 0 ) ||
                         (strcasecmp(hnm, address) == 0))) hnm = NULL;

                    /* If host name is not NULL it would work for us */
                    if (hnm) {
                        TRACE_INFO_STRING("Adding host name:", hnm);
                        error = col_add_str_property(tpl, NULL, E_HOSTNAME, hnm, 0);
                        if (error) {
                            TRACE_ERROR_NUMBER("Failed to add host name. Error", error);
                            ELAPI_ADDR_LIST_CLEANUP(ifaddr);
                            return error;
                        }
                        /* Done with the name */
                        set_hostname = 1;
                    }
                }

                /* If we have not set processed ip address do it */
                if(!set_ip) {

                    TRACE_FLOW_STRING("Address is not set", "");

                    haddr = NULL;
                    if (gai_ret_addr == EOK) {
                        TRACE_INFO_STRING("getnameinfo returned:", address);
                        if ((strcasecmp(address, LOCALADDRESS) != 0 ) &&
                            (strcasecmp(address, LOCALADDRESSV6) != 0 )) {
                            TRACE_INFO_STRING("Not an unhelpful address", "");
                            haddr = address;
                        }
                    }

                    if (haddr) {
                        TRACE_INFO_STRING("Adding host address:", haddr);
                        error = col_add_str_property(tpl, NULL, E_HOSTIP, haddr, 0);
                        if (error) {
                            TRACE_ERROR_NUMBER("Failed to add host name. Error", error);
                            ELAPI_ADDR_LIST_CLEANUP(ifaddr);
                            return error;
                        }
                        set_ip = 1;
                        used_this_ip = 1;
                    }
                }

                /* If we have a name and we are told to deal with alias names */
                if ((set_hostname) && (base & E_HAVE_HOSTALIAS)) {

                    TRACE_INFO_NUMBER("gai_ret_host:", gai_ret_host);
                    TRACE_INFO_STRING("host:", host);
                    TRACE_INFO_STRING("address:", address);
                    TRACE_INFO_STRING("they are:", ((strcasecmp(host, address) != 0) ? "different" : "same"));

                    /* Do we have a host meaningful host name? */
                    if ((gai_ret_host != EOK) ||
                        ((gai_ret_host == EOK) &&
                         ((strcasecmp(host, LOCALHOST) == 0 ) ||
                          (strcasecmp(host, LOCALHOSTDOMAIN) == 0 ) ||
                          (strcasecmp(host, address) == 0)))) hnm = NULL;
                    else hnm = host;

                    if (hnm) {
                        TRACE_INFO_STRING("Adding alias host name:", hnm);
                        error = col_add_str_property(tpl, NULL, E_HOSTALIAS, hnm, 0);
                        if (error) {
                            TRACE_ERROR_NUMBER("Failed to add host name. Error", error);
                            ELAPI_ADDR_LIST_CLEANUP(ifaddr);
                            return error;
                        }
                    }
                }

                /* If we got then main IP and we are told to deal with opther IPs */
                if ((set_ip) && (base & E_HAVE_HOSTIPS) && (!used_this_ip)) {

                    TRACE_INFO_STRING("Considering address:", address);

                    /* Do we have a host meaningful IP */
                    if ((gai_ret_addr != EOK) ||
                        ((gai_ret_addr == EOK) &&
                         ((strcasecmp(address, LOCALADDRESS) == 0 ) ||
                          (strcasecmp(address, LOCALADDRESSV6) == 0 )))) haddr = NULL;
                    else haddr = address;

                    if (haddr) {
                        TRACE_INFO_STRING("Adding alias host IP:", haddr);
                        error = col_add_str_property(tpl, NULL, E_HOSTIPS, haddr, 0);
                        if (error) {
                            TRACE_ERROR_NUMBER("Failed to add host name. Error", error);
                            ELAPI_ADDR_LIST_CLEANUP(ifaddr);
                            return error;
                        }
                    }
                }
            }
            TRACE_INFO_STRING("Moved to next", "");
            ELAPI_GET_NEXT_ADDR_ITEM(ifaddr, ifa);
            TRACE_INFO_NUMBER("Moved to", ifa);
        }

        ELAPI_ADDR_LIST_CLEANUP(ifaddr);
    }

    /* Make sure that we really have the name after all */
    if (!set_hostname) {
        TRACE_INFO_STRING("No host name using default:", undefined);
        error = col_add_str_property(tpl, NULL, E_HOSTNAME, undefined, 0);
        if (error) {
            TRACE_ERROR_NUMBER("Failed to add host name. Error", error);
            return error;
        }
    }

    /* Make sure that we really have the IP after all */
    if (!set_ip) {
        TRACE_INFO_STRING("No host name using default:", undefined);
        error = col_add_str_property(tpl, NULL, E_HOSTIP, undefined, 0);
        if (error) {
            TRACE_ERROR_NUMBER("Failed to add host name. Error", error);
            return error;
        }
    }

    TRACE_FLOW_STRING("add_host_identity", "Exit");
    return error;
}

/* Add base elements to template collection */
static int add_base_elements(struct collection_item *tpl, unsigned base)
{
    int error = EOK;
    unsigned pass_base;

    TRACE_FLOW_STRING("add_base_elements", "Entry");

    /* Populate the template using base */
    if (base & E_HAVE_TIMESTAMP) {
        /* Value is the format string for strftime() */
        error = col_add_str_property(tpl, NULL, E_TIMESTAMP, E_TIMESTAMP_FORMAT, sizeof(E_TIMESTAMP_FORMAT));
        if (error) {
            TRACE_ERROR_NUMBER("Failed to add timestamp. Error", error);
            return error;
        }
    }

    if (base & E_HAVE_UTCTIME) {
        /* Value does not matter */
        error = col_add_int_property(tpl, NULL, E_UTCTIME, 0);
        if (error) {
            TRACE_ERROR_NUMBER("Failed to add UTC time. Error", error);
            return error;
        }
    }

    if (base & E_HAVE_OFFSET) {
        /* Value does not matter */
        error = col_add_int_property(tpl, NULL, E_OFFSET, 0);
        if (error) {
            TRACE_ERROR_NUMBER("Failed to add UTC time. Error", error);
            return error;
        }
    }

    if (base & E_HAVE_PID) {
        /* Value is the current pid */
        error = col_add_long_property(tpl, NULL, E_PID, (long)getpid());
        if (error) {
            TRACE_ERROR_NUMBER("Failed to add pid. Error", error);
            return error;
        }
    }

    if (base & E_HAVE_APPNAME) {
        /* Value does not matter */
        error = col_add_str_property(tpl, NULL, E_APPNAME, "", 1);
        if (error) {
            TRACE_ERROR_NUMBER("Failed to add application name. Error", error);
            return error;
        }
    }

    if (base & E_HAVE_SEVERITY) {
        /* Value is the default severity */
        error = col_add_int_property(tpl, NULL, E_SEVERITY, LOG_USER | LOG_INFO);
        if (error) {
            TRACE_ERROR_NUMBER("Failed to add pid. Error", error);
            return error;
        }
    }

    /* If we need to add aliases or other IPs call the function */
    if ((base & E_HAVE_HOSTNAME) ||
        (base & E_HAVE_HOSTIP) ||
        (base & E_HAVE_HOSTALIAS) ||
        (base & E_HAVE_HOSTIPS))  {

        pass_base = base;

        /* make sure we have extensions on top of the basic data */
        if ((base & E_HAVE_HOSTALIAS) && (!(base & E_HAVE_HOSTNAME))) pass_base |= E_HAVE_HOSTNAME;
        if ((base & E_HAVE_HOSTIPS) && (!(base & E_HAVE_HOSTIP))) pass_base |= E_HAVE_HOSTIP;

        error = add_host_identity(tpl, pass_base);
        if (error) {
            TRACE_ERROR_NUMBER("Failed to add host identity. Error", error);
            return error;
        }
    }

    TRACE_FLOW_STRING("add_base_elements", "Exit");
    return error;
}


/* Internal untility function to tokenize a string */
static int interpret_key(char *key,
                         int *type,
                         char **property,
                         int *prop_len,
                         int *has_len,
                         int *bool_type)
{
    int adjust_by = 0;
    char *start = NULL;
    char *cursor = NULL;
    char *end = NULL;
    int ret = E_LIST_EMPTY;

    TRACE_FLOW_STRING("interpret_key", "Entry");

    TRACE_INFO_STRING("Key", key);

    /* Initialize passed in data */
    *has_len = 0;
    *property = NULL;
    *type = COL_TYPE_STRING;

    cursor = key;

    while (isspace(*cursor)) cursor++;

    /* End of string - we are done */
    if (*cursor == '\0') {
        TRACE_ERROR_STRING("Empty key - end of processing!", "");
        return E_LIST_EMPTY;
    }

    /* This is the beginning of the formatted token */
    if (*cursor == '-') {

        /* This is a remove attribute case */

        cursor++;
        /* Skip spaces if any */
        while (isspace(*cursor)) cursor++;

        /* Mark the start of the actual property */
        start = cursor;

        /* Now we need to extract the name of the property */
        /* We will not be nice here - the add_property will validate if the name is ok */
        while ((*cursor != '\0') && (!isspace(*cursor))) cursor++;

        /* End of string - we are done */
        if (cursor == start) {
            TRACE_ERROR_STRING("Invalid key - end of processing!", "");
            return E_LIST_EMPTY;
        }

        *prop_len = cursor - start;
        *property = start;
        TRACE_INFO_STRING("We are told to remove the property!", *property);
        ret = E_LIST_REMOVE;
    }
    else if (*cursor == '%') {

        /* We got a full key with format string */

        cursor++;
        if ((*cursor == '*') && (*(cursor+1) == 's') && (*(cursor+2) == '(')) {
            *type = COL_TYPE_STRING;
            *has_len = 1;
            adjust_by = 3;
        }
        else if ((*cursor == 's') && (*(cursor+1) == '(')) {
            *type = COL_TYPE_STRING;
            adjust_by = 2;
        }
        else if (((*cursor == 'i')||(*cursor == 'd')) && (*(cursor+1) == '(')) {
            *type = COL_TYPE_INTEGER;
            adjust_by = 2;
        }
        else if ((*cursor == 'u') && (*(cursor+1) == '(')) {
            *type = COL_TYPE_UNSIGNED;
            adjust_by = 2;
        }
        else if ((*cursor == 'l') && ((*(cursor+1) == 'i')||(*(cursor+1) == 'd')) && (*(cursor+2) == '(')) {
            *type = COL_TYPE_LONG;
            adjust_by = 3;
        }
        else if ((*cursor == 'l') && (*(cursor+1) == 'u') && (*(cursor+2) == '(')) {
            *type = COL_TYPE_ULONG;
            adjust_by = 3;
        }
        else if (((*cursor == 'f')||(*cursor == 'e')) && (*(cursor+1) == '(')) {
            *type = COL_TYPE_DOUBLE;
            adjust_by = 2;
        }
        else if (((*cursor == 's') || (*cursor == 'd')) && (*(cursor+1) == 'b') && (*(cursor+2) == '(')) {
            *type = COL_TYPE_BOOL;
            adjust_by = 3;
            if (*cursor == 's') *bool_type = 1;
            else *bool_type = 0;
        }
        else if ((*cursor == 'n') && (*(cursor+1) == '(')) {
            *type = COL_TYPE_BINARY;
            adjust_by = 2;
        }
        else {
            TRACE_ERROR_STRING("Invalid key - end of processing!", key);
            return E_LIST_ERROR;
        }

        cursor += adjust_by;

        /* Skip spaces if any */
        while (isspace(*cursor)) cursor++;

        start = cursor;

        /* Now we need to extract the name of the property */
        /* We will not be nice here - the add_property will validate if the name is ok */
        while ((*cursor != '\0') && (*cursor != ')') && (!isspace(*cursor))) cursor++;

        /* End of string - we are done */
        if ((*cursor == '\0') || (cursor == start)) {
            TRACE_ERROR_STRING("Invalid key - end of processing!", "");
            return E_LIST_EMPTY;
        }

        end = cursor;

        /* Skip spaces if any */
        while (isspace(*cursor)) cursor++;

        /* Check that end of the string is in proper format */
        if ((*cursor != ')') && (*(cursor + 1) != '\0')) {
            TRACE_ERROR_STRING("Invalid key - missing ')' .", key);
            return E_LIST_ERROR;
        }

        *property = start;
        *prop_len = end - start;

        TRACE_INFO_STRING("Property:", *property);
        TRACE_INFO_NUMBER("Property len:", *prop_len);
        ret = E_LIST_ADD;
    }
    else {
        /* Just got a key */
        /* Mark the start of the actual property */
        start = cursor;

        /* Now we need to extract the name of the property */
        /* We will not be nice here - the add_property will validate if the name is ok */
        while ((*cursor != '\0') && (!isspace(*cursor))) cursor++;

        /* End of string - we are done */
        if (cursor == start) {
            TRACE_ERROR_STRING("Invalid key - end of processing!", "");
            return E_LIST_EMPTY;
        }

        *prop_len = cursor - start;
        *property = start;
        TRACE_INFO_STRING("We are told to add/update the property (or last)!", *property);

        if(strncmp(*property, E_EOARG, *prop_len) == 0) ret = E_LIST_LAST;
        else ret = E_LIST_ADD;
    }

    TRACE_INFO_STRING("Returning Property:",*property);
    TRACE_INFO_NUMBER("Returning Property len:", *prop_len);
    TRACE_INFO_NUMBER("Returning Type:", *type);
    TRACE_INFO_NUMBER("Returning Has length:", *has_len);


    TRACE_FLOW_STRING("interpret_key", "Exit");

    return ret;
}

/* Make sure that the right string is given for bool value */
static int convert_bool(char *data_str, unsigned char *data_bool)
{
    TRACE_FLOW_STRING("convert_bool", "Called");
    TRACE_INFO_STRING("Data", data_str);

    if ((strcasecmp(data_str, str_true) == 0) ||
        (strcasecmp(data_str, str_yes) == 0)) {
        TRACE_INFO_STRING("Matched TRUE", "");
        *data_bool = '\1';
        return 1;
    }
    if ((strcasecmp(data_str, str_false) == 0) ||
        (strcasecmp(data_str, str_no) == 0)) {
        TRACE_INFO_STRING("Matched FALSE", "");
        *data_bool = '\0';
        return 1;
    }
    TRACE_INFO_STRING("Matched NOTHING", "");
    return 0;
}


/* Process argument list */
/* Update collection based on the passed in arguments */
static int process_arg_list(struct collection_item *col,
                            va_list args)
{
    int error = EOK;
    char *arg = NULL;
    char *propcopy = NULL;
    int ret = 0;
    int type = 0;
    char *property = NULL;
    int prop_len = 0;
    int has_len = 0;
    int bool_type = 0;
    char *data_str = NULL;
    int data_int = 0;
    unsigned int data_uint = 0;
    long data_long = 0;
    unsigned long data_ulong = 0;
    void *data_bin = NULL;
    double data_dbl = 0.;
    int length = 0;
    void *data = NULL;
    unsigned char data_bool = '\0';

    TRACE_FLOW_STRING("process_arg_list", "Entry.");

    /* We will break from the loop when we find the last item */
    while (1) {

        /* Get next key */
        arg = va_arg(args, char *);

        if (arg == NULL) {
            TRACE_ERROR_STRING("Invalid NULL argument.", "Key can't be NULL");
            return EINVAL;
        }

        /* Interpret the key.
         * It can be just " key ",
         * it can be " - key ",
         * or it can be a formatted string
         * something like " %*s( key )".
         * Function will deal with all cases.
         * Passed in variables initialized and updated inside
         */
        ret = interpret_key(arg,
                            &type,
                            &property,
                            &prop_len,
                            &has_len,
                            &bool_type);

        if (ret == E_LIST_LAST) {
            TRACE_INFO_STRING("Process found last key", arg);
            break;
        }

        if ((ret == E_LIST_ADD) || (ret == E_LIST_REMOVE)) {
            /* We need to create a dup of the string */
            propcopy = malloc(prop_len + 1);
            if (propcopy == NULL) {
                TRACE_ERROR_STRING("Failed to allocate property", arg);
                return ENOMEM;
            }

            /* Copy property */
            memcpy(propcopy, property, prop_len);
            propcopy[prop_len] = '\0';

            TRACE_INFO_STRING("Processing property", propcopy);

            /* Are we supposed to add? */
            if (ret == E_LIST_ADD) {


                /* NOTE: We are not going to check if the key value pairs
                 * are consistent.
                 * It can be made a bit more bullet proof by adding
                 * significant complexity to the code but I do not
                 * think it makes much sense to do so.
                 * There is no way to prevent the argument mismatch
                 * issues 100%. Printf can crash if aguments are
                 * missed or bad, so do we...
                 */

                /* Get data */
                switch(type) {

                case COL_TYPE_STRING:   data_str = va_arg(args, char *);
                                        data = (void *)data_str;
                                        if (has_len) length = va_arg(args, int);
                                        else length = strlen(data_str) + 1;
                                        TRACE_INFO_STRING("Adding string:", data_str);
                                        TRACE_INFO_NUMBER("Length:",length);
                                        break;

                case COL_TYPE_BINARY:   data_bin = va_arg(args, void *);
                                        data = (void *)data_bin;
                                        length = va_arg(args, int);
                                        break;

                case COL_TYPE_INTEGER:  data_int = va_arg(args, int);
                                        data = (void *)(&data_int);
                                        length = sizeof(int);
                                        break;

                case COL_TYPE_UNSIGNED: data_uint = va_arg(args, unsigned int);
                                        data = (void *)(&data_uint);
                                        length = sizeof(unsigned int);
                                        break;

                case COL_TYPE_LONG:     data_long = va_arg(args, long);
                                        data = (void *)(&data_long);
                                        length = sizeof(long);
                                        break;

                case COL_TYPE_ULONG:    data_ulong = va_arg(args, unsigned long);
                                        data = (void *)(&data_ulong);
                                        length = sizeof(unsigned long);
                                        break;

                case COL_TYPE_DOUBLE:   data_dbl = va_arg(args, double);
                                        data = (void *)(&data_dbl);
                                        length = sizeof(double);
                                        break;

                case COL_TYPE_BOOL:     if (bool_type) {
                                            /* It is a string */
                                            data_str = va_arg(args,char *);
                                            /* Check if it is a valid str */
                                            if (!(convert_bool(data_str, &data_bool))) {
                                                TRACE_ERROR_STRING("Failed to to convert bool value", data_str);
                                                free(propcopy);
                                                return EINVAL;
                                            }
                                        }
                                        else {
                                            /* It is an int */
                                            data_int = va_arg(args, int);
                                            if (data_int) data_bool = 1;
                                            else data_bool = 0;
                                        }

                                        data = (void *)(&data_bool);
                                        length = sizeof(unsigned char);
                                        break;

                default:
                                        TRACE_ERROR_STRING("Invalid or unknown type", propcopy);
                                        free(propcopy);
                                        return EINVAL;
                }

                /* Insert or update */
                error = col_insert_property_with_ref(col,
                                                     NULL,
                                                     COL_DSP_END,
                                                     NULL,
                                                     0,
                                                     COL_INSERT_DUPOVER,
                                                     propcopy,
                                                     type,
                                                     data,
                                                     length,
                                                     NULL);
                if (error) {
                    TRACE_ERROR_STRING("Error inserting property", property);
                    free(propcopy);
                    return error;
                }
            }
            else {
                /* Remove case */
                while (error != ENOENT) {
                    error = col_remove_item(col,
                                            NULL,
                                            COL_DSP_FIRSTDUP,
                                            propcopy,
                                            0,
                                            COL_TYPE_ANY);
                    if ((error) && (error != ENOENT)) {
                        TRACE_ERROR_STRING("Error deleting property", propcopy);
                        free(propcopy);
                        return error;
                    }
                }
                error = EOK;
            }
            free(propcopy);
        }
        else {
            /* Errors related to key interpretation are handled here */
            TRACE_ERROR_STRING("Invalid arg", arg);
            return EINVAL;
        }
    } /* end of arg processing loop */

    TRACE_FLOW_STRING("process_arg_list", "Exit");
    return error;
}



/*****************************************************************************/
/* Create event template */
int elapi_create_event_tplt_with_vargs(struct collection_item **tpl,
                                       unsigned base,
                                       va_list args)
{
    int error = EOK;
    struct collection_item *new_tpl = NULL;

    TRACE_FLOW_STRING("elapi_create_event_tplt_with_vargs", "Entry");

    if (tpl == NULL ) {
        TRACE_ERROR_STRING("Template storage must be provided", "");
        return EINVAL;
    }

    *tpl = NULL;

    /* Create collection */
    error = col_create_collection(&new_tpl, E_TEMPLATE_NAME, COL_CLASS_ELAPI_TEMPLATE);
    if (error) {
        TRACE_ERROR_NUMBER("Failed to create collection. Error", error);
        return error;
    }

    /* Add elements using base mask */
    error = add_base_elements(new_tpl, base);
    if (error) {
        TRACE_ERROR_NUMBER("Failed to add base elements. Error", error);
        col_destroy_collection(new_tpl);
        return error;
    }

    /* Process variable argument list */
    error = process_arg_list(new_tpl, args);

    if (error) {
        TRACE_ERROR_NUMBER("Failed to process argument list. Error", error);
        col_destroy_collection(new_tpl);
        return error;
    }

    *tpl = new_tpl;

    TRACE_FLOW_STRING("elapi_create_event_tplt_with_vargs", "Exit");
    return error;
}


/* Create event template */
int elapi_create_event_tplt(struct collection_item **tpl,
                            unsigned base, ...)
{
    int error = EOK;
    va_list args;

    TRACE_FLOW_STRING("elapi_create_event_tplt", "Entry");

    /* Process varible arguments */
    va_start(args, base);

    /* Create template using arguments  */
    error = elapi_create_event_tplt_with_vargs(tpl,
                                               base,
                                               args);

    va_end(args);

    TRACE_FLOW_STRING("elapi_create_event_tplt", "Exit");
    return error;
}

/* Function to destroy event template */
void elapi_destroy_event_tplt(struct collection_item *tpl)
{
    TRACE_FLOW_STRING("elapi_destroy_event_tplt", "Entry");

    col_destroy_collection(tpl);

    TRACE_FLOW_STRING("elapi_destroy_event_tplt", "Exit");
}


/* Create event from template, colection and arguments */
int elapi_create_event_with_vargs(struct collection_item **event,
                                  struct collection_item *tpl,
                                  struct collection_item *collection,
                                  int mode, va_list args)
{
    int error = EOK;