rbroccoli 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MIT-LICENSE +21 -0
- data/README +40 -0
- data/data/bro/callback-typemaps.yml +1010 -0
- data/data/bro/record-typemaps.yml +332 -0
- data/ext/broccoli_ext/autogen.sh +3 -0
- data/ext/broccoli_ext/broccoli.i +463 -0
- data/ext/broccoli_ext/broccoli_wrap.c +4213 -0
- data/ext/broccoli_ext/extconf.rb +15 -0
- data/ext/broccoli_ext/post-clean.rb +3 -0
- data/ext/broccoli_ext/pre-config.rb +5 -0
- data/ext/broccoli_ext/test/broconftest.rb +12 -0
- data/ext/broccoli_ext/test/test.rb +174 -0
- data/lib/Bro/connection.rb +73 -0
- data/lib/Bro/event.rb +34 -0
- data/lib/Bro/record.rb +60 -0
- data/lib/Bro/typemap.rb +158 -0
- data/lib/bro.rb +105 -0
- metadata +64 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
if ex = find_executable("broccoli-config")
|
4
|
+
$CFLAGS << " " + `#{ex} --cflags`.chomp
|
5
|
+
$LDFLAGS << " " + `#{ex} --libs`.chomp
|
6
|
+
else
|
7
|
+
puts "You need to have broccoli-config in your path!"
|
8
|
+
exit(-1)
|
9
|
+
end
|
10
|
+
|
11
|
+
if have_header("broccoli.h") and
|
12
|
+
have_library("broccoli") and
|
13
|
+
have_library("ssl")
|
14
|
+
create_makefile('broccoli_ext')
|
15
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# Please don't try to read too much into this file. It's mostly my
|
2
|
+
# internal test file while I'm building the C binding.
|
3
|
+
#
|
4
|
+
# Check out the examples directory for better examples that use the ruby
|
5
|
+
# library I've built overtop the C bindings.
|
6
|
+
|
7
|
+
require './broccoli_ext'
|
8
|
+
include Broccoli_ext
|
9
|
+
Broccoli_ext::bro_debug_calltrace=false
|
10
|
+
Broccoli_ext::bro_debug_messages=false
|
11
|
+
|
12
|
+
STDOUT.sync = true
|
13
|
+
|
14
|
+
#a= Broccoli_ext::BroString.new
|
15
|
+
#a.str_val="asdf"
|
16
|
+
|
17
|
+
host_str = "127.0.0.1:12345"
|
18
|
+
|
19
|
+
bc = bro_conn_new_str(host_str, BRO_CFLAG_NONE)
|
20
|
+
puts "Connection? #{bc}"
|
21
|
+
#puts " Connected" unless bro_conn_connect(bc).zero?
|
22
|
+
|
23
|
+
###
|
24
|
+
# Test BroString Creation
|
25
|
+
###
|
26
|
+
|
27
|
+
module SWIG
|
28
|
+
class TYPE_p_bro_conn
|
29
|
+
def method_missing(meth, *args)
|
30
|
+
return bro_conn_data_get(self, meth.id2name)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class TYPE_p_bro_record
|
35
|
+
# I need to build a full record typemapper to deal with this correctly.
|
36
|
+
|
37
|
+
def method_missing(meth, *args)
|
38
|
+
#return bro_record_get_named_val(self, meth.id2name, BRO_TYPE_STRING)
|
39
|
+
end
|
40
|
+
|
41
|
+
def [](position)
|
42
|
+
#return bro_record_get_nth_val(self, position, BRO_TYPE_STRING)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
###
|
50
|
+
# Test Record Creation
|
51
|
+
###
|
52
|
+
#rec = bro_record_new()
|
53
|
+
#puts "Ruby: Inserting data into the record"
|
54
|
+
##time = bro_util_current_time()
|
55
|
+
##puts "Ruby: Current time: #{time}"
|
56
|
+
#bro_record_add_val(rec, "seq", BRO_TYPE_IPADDR, 213054988)
|
57
|
+
#puts "Ruby: Getting the data back out"
|
58
|
+
##puts bro_record_get_named_val(rec, "seq", BRO_TYPE_COUNT);
|
59
|
+
#puts " " + bro_record_get_nth_val(rec, 0, BRO_TYPE_IPADDR).to_s
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
###
|
64
|
+
# Test Callback creation
|
65
|
+
###
|
66
|
+
|
67
|
+
# Ideal :)
|
68
|
+
#bro_ruby_typemap_new("pong", [8,19,0])
|
69
|
+
#build_typemap("pong", [:conn,:record])
|
70
|
+
|
71
|
+
#while 1
|
72
|
+
# ev = bro_event_new("ping")
|
73
|
+
# bro_event_free(ev)
|
74
|
+
# #GC.start
|
75
|
+
#end
|
76
|
+
|
77
|
+
bro_pong_record = Proc.new do |conn, rec|
|
78
|
+
now = bro_util_current_time()
|
79
|
+
puts "Pong_record callback"
|
80
|
+
puts rec
|
81
|
+
|
82
|
+
seq = bro_record_get_nth_val(rec, 0, BRO_TYPE_COUNT)
|
83
|
+
src_time = bro_record_get_nth_val(rec, 1, BRO_TYPE_TIME)
|
84
|
+
dst_time = bro_record_get_nth_val(rec, 2, BRO_TYPE_TIME)
|
85
|
+
|
86
|
+
puts "pong event from #{host_str}: seq=#{seq}, time=#{dst_time-src_time}/#{now-src_time} s"
|
87
|
+
end
|
88
|
+
|
89
|
+
bro_pong = Proc.new do |conn, src_time, dst_time, seq|
|
90
|
+
puts "Pong callback!"
|
91
|
+
now = bro_util_current_time()
|
92
|
+
puts "pong event from #{host_str}: seq=#{seq}, time=#{dst_time-src_time}/#{now-src_time} s"
|
93
|
+
end
|
94
|
+
|
95
|
+
new_connection = Proc.new do |conn|
|
96
|
+
puts "Saw a connection!"
|
97
|
+
end
|
98
|
+
|
99
|
+
dns_request = Proc.new do |conn, msg, query, qtype, qclass|
|
100
|
+
#$count = $count+1
|
101
|
+
#puts "msg: #{msg}"
|
102
|
+
#puts "query: #{query}"
|
103
|
+
#puts "qtype: #{qtype}"
|
104
|
+
#puts "qclass: #{qclass}"
|
105
|
+
#puts "service: #{conn.blah}"
|
106
|
+
#puts "Query output class: #{query.class}"
|
107
|
+
#answers = bro_record_get_nth_val(msg, 11, BRO_TYPE_COUNT).to_s
|
108
|
+
#puts "Number of dns answers: #{answers}"
|
109
|
+
#puts "Query: #{query} - Query type: #{qtype} - Query class: #{qclass}"
|
110
|
+
end
|
111
|
+
|
112
|
+
#puts "Registering callback..."
|
113
|
+
#bro_event_registry_add(bc, "dns_A_reply", dns_reply)
|
114
|
+
|
115
|
+
bro_event_registry_add(bc, "dns_request", ["dns_request", [19, 19, 8, 3, 3], dns_request])
|
116
|
+
|
117
|
+
#bro_event_registry_add(bc, "pong", ["pong", {"pong", [19]}, bro_pong_record])
|
118
|
+
#bro_event_registry_add(bc, "pong", ["pong", {"pong", [6,6,3]}, bro_pong])
|
119
|
+
|
120
|
+
#bro_event_registry_add(bc, "wootback", [[8], wootback])
|
121
|
+
|
122
|
+
#bro_event_registry_add(bc, "new_connection", ["new_connection", {"new_connection", [19]}, new_connection])
|
123
|
+
#bro_event_registry_add(bc, "return_memory", return_memory)
|
124
|
+
|
125
|
+
#puts "Done Registering callback..."
|
126
|
+
puts "Connected" if bro_conn_connect(bc)
|
127
|
+
|
128
|
+
while(1)
|
129
|
+
#puts "Checking input"
|
130
|
+
$count = 0
|
131
|
+
bro_conn_process_input(bc)
|
132
|
+
puts "*" * ($count/2)
|
133
|
+
|
134
|
+
sleep 0.5
|
135
|
+
|
136
|
+
GC.start
|
137
|
+
end
|
138
|
+
exit
|
139
|
+
|
140
|
+
###
|
141
|
+
# Testing record creation and event sending
|
142
|
+
###
|
143
|
+
record = false
|
144
|
+
(1..100).each do |seq|
|
145
|
+
bro_conn_process_input(bc)
|
146
|
+
#puts "Creating event"
|
147
|
+
ev = bro_event_new("ping")
|
148
|
+
timestamp = bro_util_current_time()
|
149
|
+
if(record)
|
150
|
+
rec = bro_record_new()
|
151
|
+
bro_record_add_val(rec, "seq", BRO_TYPE_COUNT, seq)
|
152
|
+
bro_record_add_val(rec, "src_time", BRO_TYPE_TIME, timestamp)
|
153
|
+
bro_event_add_val(ev, BRO_TYPE_RECORD, rec)
|
154
|
+
else
|
155
|
+
bro_event_add_val(ev, BRO_TYPE_TIME, timestamp)
|
156
|
+
bro_event_add_val(ev, BRO_TYPE_COUNT, seq)
|
157
|
+
end
|
158
|
+
|
159
|
+
puts "Sending ping..."
|
160
|
+
bro_event_send(bc, ev)
|
161
|
+
# May not need to call this anymore either
|
162
|
+
#bro_event_free(ev)
|
163
|
+
sleep 1
|
164
|
+
#GC.start
|
165
|
+
end
|
166
|
+
|
167
|
+
#while(1) do
|
168
|
+
# ev = bro_event_new "show_memory"
|
169
|
+
# puts "Sending event"
|
170
|
+
# puts bro_event_send(bc, ev)
|
171
|
+
# sleep 1
|
172
|
+
# puts "Processing input..."
|
173
|
+
# puts bro_conn_process_input(bc)
|
174
|
+
#end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'broccoli_ext'
|
2
|
+
|
3
|
+
module Bro
|
4
|
+
class Connection
|
5
|
+
include Broccoli_ext
|
6
|
+
|
7
|
+
def initialize(hp, flags=nil)
|
8
|
+
flags = (BRO_CFLAG_RECONNECT | BRO_CFLAG_ALWAYS_QUEUE) if flags.nil?
|
9
|
+
@bc = bro_conn_new_str(hp, flags)
|
10
|
+
@io_object = nil
|
11
|
+
@event_blocks = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def delete
|
15
|
+
bro_conn_delete(@bc)
|
16
|
+
end
|
17
|
+
|
18
|
+
def connect
|
19
|
+
bro_conn_connect(@bc)
|
20
|
+
end
|
21
|
+
|
22
|
+
def connected?
|
23
|
+
bro_conn_alive?(@bc)
|
24
|
+
end
|
25
|
+
|
26
|
+
def process_input
|
27
|
+
bro_conn_process_input(@bc)
|
28
|
+
end
|
29
|
+
|
30
|
+
def queue_length
|
31
|
+
bro_event_queue_length(@bc)
|
32
|
+
end
|
33
|
+
|
34
|
+
def flush
|
35
|
+
bro_event_queue_flush(@bc)
|
36
|
+
end
|
37
|
+
|
38
|
+
def send(event)
|
39
|
+
# .ev is the real event pointer
|
40
|
+
bro_event_send(@bc, event.ev)
|
41
|
+
end
|
42
|
+
|
43
|
+
def wait
|
44
|
+
unless @io_object
|
45
|
+
fd = bro_conn_get_fd(@bc)
|
46
|
+
return false if fd < 0
|
47
|
+
@io_object = IO.new(fd)
|
48
|
+
end
|
49
|
+
# block until there is data
|
50
|
+
select([@io_object])
|
51
|
+
end
|
52
|
+
|
53
|
+
def event_handler(event, mapping=nil, &block)
|
54
|
+
# This lets me pass the proc object and the event name into the callback
|
55
|
+
# It needs to be done so that the typemapper can map types before
|
56
|
+
# calling the proc object.
|
57
|
+
# TODO: maybe make this less hacky?
|
58
|
+
Bro::Typemap.define_event(event, mapping) unless mapping.nil?
|
59
|
+
|
60
|
+
typemap = Bro::Typemap.get(:callback, event)
|
61
|
+
|
62
|
+
event_block = [event, typemap, block]
|
63
|
+
@event_blocks << event_block
|
64
|
+
|
65
|
+
bro_event_registry_add(@bc, event, event_block)
|
66
|
+
|
67
|
+
# Re-request all events if we're already connected.
|
68
|
+
bro_event_registry_request(@bc) if connected?
|
69
|
+
end
|
70
|
+
alias :event_handler_for :event_handler
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
data/lib/Bro/event.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module Bro
|
2
|
+
|
3
|
+
class Event
|
4
|
+
include Broccoli_ext
|
5
|
+
attr_reader :ev
|
6
|
+
|
7
|
+
def initialize(name)
|
8
|
+
@event_name = name
|
9
|
+
@ev = bro_event_new(name)
|
10
|
+
@current_val = 0
|
11
|
+
ObjectSpace.define_finalizer(self, Event.create_finalizer(@ev))
|
12
|
+
end
|
13
|
+
|
14
|
+
def insert(value)
|
15
|
+
name_type, int_type = Bro::Typemap.get(:callback, @event_name, @current_val)
|
16
|
+
#puts "(#{@ev}, #{int_type}, #{value})"
|
17
|
+
# TODO: Yikes! more hacks to get this thing working
|
18
|
+
value = value.rec if int_type == Broccoli_ext::BRO_TYPE_RECORD
|
19
|
+
bro_event_add_val(@ev, int_type, value)
|
20
|
+
@current_val += 1
|
21
|
+
end
|
22
|
+
|
23
|
+
def free
|
24
|
+
bro_event_free(@ev)
|
25
|
+
end
|
26
|
+
|
27
|
+
# When the garbage collector comes around, make sure the C structure
|
28
|
+
# is freed.
|
29
|
+
def self.create_finalizer(event)
|
30
|
+
proc { bro_event_free(event) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/lib/Bro/record.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module Bro
|
2
|
+
|
3
|
+
class Record
|
4
|
+
include Broccoli_ext
|
5
|
+
attr_accessor :rec
|
6
|
+
|
7
|
+
def initialize(type=nil)
|
8
|
+
@type = type
|
9
|
+
@rec = bro_record_new()
|
10
|
+
if type
|
11
|
+
@rec.record_type = type
|
12
|
+
else
|
13
|
+
# Generate a "unique" name for this record since no type was given
|
14
|
+
# It allows the record to read back it's own values by storing types
|
15
|
+
# as it is created.
|
16
|
+
@rec.record_type = rand().to_s
|
17
|
+
# TODO: abstract into typemapper
|
18
|
+
$bro_global_typemap[:record][@rec.record_type] = {}
|
19
|
+
end
|
20
|
+
@current_val = 0
|
21
|
+
ObjectSpace.define_finalizer(self, Record.create_finalizer(@rec))
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(meth)
|
25
|
+
@rec.send(meth)
|
26
|
+
end
|
27
|
+
|
28
|
+
def insert(name, value, value_type=nil)
|
29
|
+
if value_type.nil? and @rec.record_type.nil?
|
30
|
+
raise "No type inferred.. type must be given"
|
31
|
+
end
|
32
|
+
if not value_type
|
33
|
+
name_type, int_type = Bro::Typemap.get(:record, @type, name)
|
34
|
+
else
|
35
|
+
int_type = Bro::Typemap.get_int_type(value_type)
|
36
|
+
# TODO: needs to be abstracted into the typemapper
|
37
|
+
$bro_global_typemap[:record][@rec.record_type][name.to_sym] = value_type
|
38
|
+
end
|
39
|
+
#puts "(#{@rec}, #{name.to_s}, #{int_type}, #{value})"
|
40
|
+
Broccoli_ext::bro_record_add_val(@rec, name.to_s, int_type, value)
|
41
|
+
@current_val += 1
|
42
|
+
end
|
43
|
+
|
44
|
+
def []=(key, val, type)
|
45
|
+
int_type = Bro::Typemap.get_int_type(type)
|
46
|
+
Broccoli_ext::bro_record_set_nth_val(@rec, key, int_type, val)
|
47
|
+
end
|
48
|
+
|
49
|
+
def free
|
50
|
+
bro_record_free(@rec)
|
51
|
+
end
|
52
|
+
|
53
|
+
# When the garbage collector comes around,
|
54
|
+
# make sure the C structure is freed.
|
55
|
+
def self.create_finalizer(record)
|
56
|
+
proc { bro_record_free(record) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
data/lib/Bro/typemap.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module Bro
|
5
|
+
module Typemap
|
6
|
+
|
7
|
+
DATA_TYPEMAP = {:bool => 1,
|
8
|
+
:int => 2,
|
9
|
+
:count => 3,
|
10
|
+
:counter => 4,
|
11
|
+
:double => 5,
|
12
|
+
:time => 6,
|
13
|
+
:interval => 7,
|
14
|
+
:string => 8,
|
15
|
+
:pattern => 9,
|
16
|
+
:enum => 10,
|
17
|
+
:timer => 11,
|
18
|
+
:port => 12,
|
19
|
+
:addr => 13,
|
20
|
+
:net => 14,
|
21
|
+
:subnet => 15,
|
22
|
+
:record => 19
|
23
|
+
}
|
24
|
+
|
25
|
+
|
26
|
+
def Typemap.get_int_type(type)
|
27
|
+
type_code = DATA_TYPEMAP[type]
|
28
|
+
if type_code.nil?
|
29
|
+
Broccoli_ext::BRO_TYPE_RECORD
|
30
|
+
else
|
31
|
+
type_code
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# TODO: this is an utter mess
|
36
|
+
def Typemap.get(typemap, name, key=nil)
|
37
|
+
if key
|
38
|
+
if typemap == :record and key.class == Fixnum
|
39
|
+
# this is especially bad
|
40
|
+
foo = $bro_global_typemap[typemap][name].to_a[key][1]
|
41
|
+
elsif typemap == :callback and key.class == Fixnum
|
42
|
+
foo = $bro_global_typemap[typemap][name][key]
|
43
|
+
else
|
44
|
+
foo = $bro_global_typemap[typemap][name.to_sym][key.to_sym]
|
45
|
+
end
|
46
|
+
return [foo, get_int_type(foo)]
|
47
|
+
else
|
48
|
+
foo = $bro_global_typemap[typemap][name]
|
49
|
+
return foo.inject([]) { |out, t| out << get_int_type(t) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def Typemap.insert(typemap_type, name, map)
|
54
|
+
$bro_global_typemap[typemap_type][name] = map
|
55
|
+
end
|
56
|
+
|
57
|
+
def Typemap.define_event(event, mapping)
|
58
|
+
map = mapping.collect { |m| m.to_sym }
|
59
|
+
Bro::Typemap.insert(:callback, event, map)
|
60
|
+
end
|
61
|
+
|
62
|
+
def Typemap.define_record(record_name, mapping)
|
63
|
+
new_mapping = {}
|
64
|
+
mapping.each { |k,v| new_mapping[k.to_sym] = v.to_sym}
|
65
|
+
Bro::Typemap.insert(:record, record_name.to_sym, new_mapping)
|
66
|
+
end
|
67
|
+
|
68
|
+
def Typemap.load
|
69
|
+
$bro_global_typemap = {}
|
70
|
+
if __FILE__.match(File.join("gems", "rbroccoli"))
|
71
|
+
# Gem installed
|
72
|
+
dir = File.join(File.dirname(__FILE__), '..', '..', 'data', 'bro')
|
73
|
+
else
|
74
|
+
# Native install
|
75
|
+
$:.each do |path|
|
76
|
+
if m = path.match(File.join("lib", "ruby"))
|
77
|
+
# Supporting native install
|
78
|
+
tmp_dir = File.join(m.pre_match, "share", "bro")
|
79
|
+
if File.exists?(tmp_dir)
|
80
|
+
dir = tmp_dir
|
81
|
+
break
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
if !dir
|
88
|
+
# Check if it isn't installed
|
89
|
+
dir = File.join(File.dirname(__FILE__), '..', '..', 'data', 'bro')
|
90
|
+
end
|
91
|
+
$bro_global_typemap[:callback] = YAML.load_file("#{dir}/callback-typemaps.yml")
|
92
|
+
$bro_global_typemap[:record] = YAML.load_file("#{dir}/record-typemaps.yml")
|
93
|
+
end
|
94
|
+
|
95
|
+
# This needs the event.bif.bro file to generate its callback
|
96
|
+
# typemappings for the default bro events.
|
97
|
+
# Distributions will have these files prebuilt for the appropriate
|
98
|
+
# version of Bro.
|
99
|
+
def Typemap.generate_callback_file(input_filename, output_filename)
|
100
|
+
f = open(input_filename)
|
101
|
+
|
102
|
+
output = {}
|
103
|
+
f.each_line do |line|
|
104
|
+
match = line.match(/^global\s+(.*?):\s+event\((.*)\)/)
|
105
|
+
if match
|
106
|
+
types = []
|
107
|
+
event = match[1]
|
108
|
+
match[2].split(", ").each { |i| types << i.split(": ")[1].to_sym }
|
109
|
+
output[event] = types
|
110
|
+
end
|
111
|
+
end
|
112
|
+
yaml = open(output_filename, "w")
|
113
|
+
yaml.puts "# This file was autogenerated on #{Time.now}"
|
114
|
+
yaml.puts "# Please do not edit!"
|
115
|
+
yaml.puts output.to_yaml
|
116
|
+
end
|
117
|
+
|
118
|
+
# This will generate a typemapper for records from the
|
119
|
+
# bro.init file.
|
120
|
+
def Typemap.generate_record_file(input_filename, output_filename)
|
121
|
+
f = open(input_filename)
|
122
|
+
|
123
|
+
output = {}
|
124
|
+
fields = {}
|
125
|
+
record_type = ''
|
126
|
+
inside_def = false
|
127
|
+
f.each_line do |line|
|
128
|
+
if line =~ /type (\S+?): record/
|
129
|
+
record_type = $1
|
130
|
+
inside_def = true
|
131
|
+
end
|
132
|
+
|
133
|
+
if inside_def and line =~ /(\S+): (\S+);/
|
134
|
+
fields[$1.to_sym] = $2.to_sym;
|
135
|
+
end
|
136
|
+
|
137
|
+
if line =~ /^\};/
|
138
|
+
if inside_def
|
139
|
+
output[record_type.to_sym] = fields
|
140
|
+
fields = {}
|
141
|
+
inside_def = false
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
yaml = open(output_filename, "w")
|
146
|
+
yaml.puts "# This file was autogenerated on #{Time.now}"
|
147
|
+
yaml.puts "# Please do not edit!"
|
148
|
+
yaml.puts output.to_yaml
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# This is just to get the yaml stuff loaded into the global typemapper hash
|
154
|
+
begin
|
155
|
+
Bro::Typemap.load
|
156
|
+
rescue
|
157
|
+
STDERR.puts "Typemap files not found!"
|
158
|
+
end
|