rb-wartslib 0.9.11 → 0.9.12

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/CHANGES CHANGED
@@ -1,3 +1,9 @@
1
+ version 0.9.12, 2008-01-16
2
+ * renamed Warts::Trace#each_attempt to each_response
3
+ * renamed Warts::Trace#each_hop_and_attempt to each_hop_and_response
4
+ * the previous each_attempt and each_hop_and_attempt methods are still
5
+ available as aliases, though deprecated
6
+
1
7
  version 0.9.11, 2007-11-29:
2
8
  * added more sample code to the 'bin' subdirectory
3
9
  * added GPLv2+ license header to sample code
@@ -24,7 +24,7 @@
24
24
  ## along with this program; if not, write to the Free Software
25
25
  ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26
26
  ##
27
- ## $Id: extract-trace-addrs,v 1.1 2007/11/30 01:14:48 youngh Exp $
27
+ ## $Id: extract-trace-addrs,v 1.2 2007/12/08 00:56:04 youngh Exp $
28
28
  #############################################################################
29
29
 
30
30
  require 'rubygems'
@@ -63,10 +63,10 @@ end
63
63
  def extract_addresses(trace, addresses)
64
64
  dest_response = trace.find_dest_response
65
65
 
66
- trace.each_hop_and_attempt do |hop, attempt, exists|
66
+ trace.each_hop_and_response do |hop, response, exists|
67
67
  next unless exists
68
68
  break if dest_response && hop == dest_response[0]
69
- addresses[trace.hop_addr(hop, attempt)] += 1
69
+ addresses[trace.hop_addr(hop, response)] += 1
70
70
  end
71
71
 
72
72
  addresses[trace.dst] += 1 if dest_response
data/bin/scdump CHANGED
@@ -20,7 +20,7 @@
20
20
  ## along with this program; if not, write to the Free Software
21
21
  ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
22
  ##
23
- ## $Id: scdump,v 1.12 2007/11/29 23:57:41 youngh Exp $
23
+ ## $Id: scdump,v 1.13 2007/12/08 00:43:19 youngh Exp $
24
24
  #############################################################################
25
25
 
26
26
  require 'rubygems'
@@ -64,7 +64,7 @@ def show_trace(trace)
64
64
  path_complete = "I"
65
65
  if dest_response
66
66
  hop = 0
67
- trace.each do |hop, attempt, exists|
67
+ trace.each do |hop, response, exists|
68
68
  break if !exists || hop >= dest_response[0]
69
69
  end
70
70
  path_complete = "C" if hop == dest_response[0]
@@ -79,15 +79,15 @@ def show_trace(trace)
79
79
  trace.each_hop do |hop, exists|
80
80
  if exists
81
81
  hop_str = ""
82
- trace.each_attempt(hop) do |attempt|
82
+ trace.each_response(hop) do |response|
83
83
  # sc_analysis_dump compatibility
84
84
  next if dest_response &&
85
- hop == dest_response[0] && attempt == dest_response[1]
85
+ hop == dest_response[0] && response == dest_response[1]
86
86
 
87
87
  hop_str << ";" unless hop_str.empty?
88
- hop_str << trace.hop_addr(hop, attempt)
89
- hop_str << "," << trace.hop_rtt_str(hop, attempt)
90
- hop_str << "," << trace.hop_probe_id(hop, attempt).to_s
88
+ hop_str << trace.hop_addr(hop, response)
89
+ hop_str << "," << trace.hop_rtt_str(hop, response)
90
+ hop_str << "," << trace.hop_probe_id(hop, response).to_s
91
91
  end
92
92
 
93
93
  unless hop_str.empty?
@@ -20,7 +20,7 @@
20
20
  ## along with this program; if not, write to the Free Software
21
21
  ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
22
  ##
23
- ## $Id: stat-traces,v 1.1 2007/11/30 01:14:48 youngh Exp $
23
+ ## $Id: stat-traces,v 1.3 2007/12/08 00:56:04 youngh Exp $
24
24
  #############################################################################
25
25
 
26
26
  require 'rubygems'
@@ -28,14 +28,62 @@ require 'ostruct'
28
28
  require 'wartslib'
29
29
 
30
30
  class Stats
31
- attr_accessor :traces, :responding, :complete, :stop_reason
31
+ attr_accessor :traces, :responding, :complete, :stop_reason, :icmp_reason
32
32
  attr_accessor :hop_histogram, :zero_dead
33
33
 
34
+ # Based on http://www.iana.org/assignments/icmp-parameters
35
+ ICMP_TYPE_NAMES = {
36
+ 0 => "Echo Reply",
37
+ 3 => "Dest Unreachable",
38
+ 4 => "Source Quench",
39
+ 5 => "Redirect",
40
+ 11 => "Time Exceeded",
41
+ 12 => "Parameter Problem"
42
+ }
43
+
44
+ ICMP_CODE_NAMES = Hash.new { |h,k| h[k] = {} }
45
+ ICMP_CODE_NAMES.merge!({
46
+ 0 => {
47
+ 0 => "" # No Code
48
+ },
49
+ 3 => {
50
+ 0 => "Net Unreachable",
51
+ 1 => "Host Unreachable",
52
+ 2 => "Protocol Unreachable",
53
+ 3 => "Port Unreachable",
54
+ 4 => "Fragmentation Needed and DF Set",
55
+ 5 => "Source Route Failed",
56
+ 6 => "Destination Network Unknown",
57
+ 7 => "Destination Host Unknown",
58
+ 8 => "Source Host Isolated",
59
+ 9 => "Admin Prohibited: Dest Network",
60
+ 10 => "Admin Prohibited: Dest Host",
61
+ 11 => "Dest Network Unreachable for TOS",
62
+ 12 => "Dest Host Unreachable for TOS",
63
+ 13 => "Administratively Prohibited",
64
+ 14 => "Host Precedence Violation",
65
+ 15 => "Precedence Cutoff in Effect"
66
+ },
67
+ 4 => {
68
+ 0 => "" # No Code
69
+ },
70
+ 11 => {
71
+ 0 => "TTL Exceeded in Transit",
72
+ 1 => "Fragment Reassembly Time Exceeded"
73
+ },
74
+ 12 => {
75
+ 0 => "Pointer Indicates the Error",
76
+ 1 => "Missing a Required Option",
77
+ 2 => "Bad Length"
78
+ }
79
+ })
80
+
34
81
  def initialize
35
82
  @traces = 0
36
83
  @responding = 0
37
84
  @complete = 0
38
85
  @stop_reason = Hash.new 0
86
+ @icmp_reason = Hash.new 0 # "#{icmp_type} #{icmp_code}"
39
87
 
40
88
  # from scamper_trace.h:SCAMPER_TRACE_STOP_*
41
89
  @stop_text = {
@@ -59,21 +107,45 @@ class Stats
59
107
  puts "%8d complete (%.1f%%)" % [@complete, pct(@complete, @traces)]
60
108
  puts "%8d dead traces with zero hop count (%.1f%%)" %
61
109
  [ @zero_dead, pct(@zero_dead, @traces) ]
110
+
62
111
  puts
63
- stop_reason.to_a.sort.each do |k, v|
112
+ @stop_reason.to_a.sort.each do |k, v|
64
113
  puts "%8d stop reason %d, %s" % [v, k, @stop_text[k]]
65
114
  end
115
+
116
+ puts
117
+ @icmp_reason.to_a.map{ |k, v| [k >> 8, k & 0xFF, v] }.sort.each do |t, c, v|
118
+ type_name = ICMP_TYPE_NAMES[t]
119
+ code_name = ICMP_CODE_NAMES[t][c]
120
+ names = ""
121
+ if type_name
122
+ names = " (#{type_name}"
123
+ if code_name
124
+ names += ": #{code_name}" unless code_name == ""
125
+ else
126
+ names += ": ?"
127
+ end
128
+ names += ")"
129
+ end
130
+ puts "%8d icmp type %d, code %d%s" % [v, t, c, names]
131
+ end
132
+
66
133
  puts
67
134
  @hop_histogram.to_a.sort.each do |k, v|
68
135
  puts "%8d %d hops" % [v, k]
69
136
  end
70
137
  end
71
138
 
139
+ def add_icmp_reason(icmp_type, icmp_code)
140
+ @icmp_reason[icmp_type << 8 | icmp_code] += 1
141
+ end
142
+
72
143
  def add(other)
73
144
  @traces += other.traces
74
145
  @responding += other.responding
75
146
  @complete += other.complete
76
147
  other.stop_reason.each { |k,v| @stop_reason[k] += v }
148
+ other.icmp_reason.each { |k,v| @icmp_reason[k] += v }
77
149
  other.hop_histogram.each { |k,v| @hop_histogram[k] += v }
78
150
  @zero_dead += other.zero_dead
79
151
  end
@@ -97,6 +169,17 @@ ARGV.each do |path|
97
169
  stats.responding += 1 if trace.dest_responded?
98
170
  stats.complete += 1 if trace.complete?
99
171
  stats.stop_reason[trace.stop_reason] += 1
172
+
173
+ # XXX This doesn't work properly for TCP traces, because TCP traces with
174
+ # stop reason == 1 (completed) have TCP flags but no ICMP type/code
175
+ # values for the response from the destination.
176
+ trace.each_hop_and_response do |hop, response, exists|
177
+ next unless exists
178
+ icmp_type = trace.hop_icmp_type hop, response
179
+ icmp_code = trace.hop_icmp_code hop, response
180
+ stats.add_icmp_reason icmp_type, icmp_code
181
+ end
182
+
100
183
  stats.hop_histogram[trace.path_length] += 1
101
184
  stats.zero_dead += 1 if trace.stop_reason == 5 && trace.hop_count == 0
102
185
  end
data/bin/wdump CHANGED
@@ -25,7 +25,7 @@
25
25
  ## along with this program; if not, write to the Free Software
26
26
  ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
27
  ##
28
- ## $Id: wdump,v 1.10 2007/11/29 23:57:24 youngh Exp $
28
+ ## $Id: wdump,v 1.11 2007/12/08 00:43:48 youngh Exp $
29
29
  #############################################################################
30
30
 
31
31
  require 'rubygems'
@@ -58,13 +58,13 @@ def show_trace(trace)
58
58
  show_attr trace, name
59
59
  end
60
60
 
61
- trace.each do |hop, attempt, exists|
61
+ trace.each do |hop, response, exists|
62
62
  unless exists
63
- puts "hop #{hop}, attempt 0: *"
63
+ puts "hop #{hop}, response 0: *"
64
64
  next
65
65
  end
66
66
 
67
- puts "hop #{hop}, attempt #{attempt}"
67
+ puts "hop #{hop}, response #{response}"
68
68
  [
69
69
  :hop_addr,
70
70
  :hop_flags,
@@ -80,7 +80,7 @@ def show_trace(trace)
80
80
  :hop_rtt_usec
81
81
  ].each do |name|
82
82
  print " "
83
- show_attr trace, name, hop, attempt
83
+ show_attr trace, name, hop, response
84
84
  end
85
85
  end
86
86
  end
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ##############################################################################
4
+ ## Generate a web page showing Ruby scripts with syntax highlighting.
5
+ ##
6
+ ## $Id: gen-code-html,v 1.2 2007/12/04 22:57:31 youngh Exp $
7
+ ##############################################################################
8
+
9
+ require 'rubygems'
10
+ require 'ostruct'
11
+ require 'optparse'
12
+
13
+ require 'syntax/convertors/html'
14
+
15
+ $options = OpenStruct.new
16
+
17
+ opts = OptionParser.new
18
+ opts.banner = "Usage: gen-code-html [options] <script> ..."
19
+
20
+ opts.on("-s", "--snippet", TrueClass,
21
+ "generate HTML fragment for code snippet") do |v|
22
+ $options.snippet = v
23
+ end
24
+
25
+ begin
26
+ ARGV.replace opts.parse(*ARGV)
27
+ rescue OptionParser::ParseError
28
+ $stderr.puts "ERROR: " + $!
29
+ $stderr.puts opts
30
+ exit 1
31
+ end
32
+
33
+ #===========================================================================
34
+
35
+ ARGV.each do |path|
36
+ convertor = Syntax::Convertors::HTML.for_syntax "ruby"
37
+ html = convertor.convert(File.read(path))
38
+
39
+ if $options.snippet
40
+ html.sub!(/^<pre/, "<pre class=\"ruby snippet\"")
41
+ else
42
+ html.sub!(/^<pre/, "<pre class=\"ruby\"")
43
+ end
44
+
45
+ basename = File.basename path
46
+ out = File.open basename + ".html", "w"
47
+
48
+ unless $options.snippet
49
+ out.puts <<HEADER
50
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
51
+ <html>
52
+ <head>
53
+ <title>rb-wartslib: Sample Script: #{basename}</title>
54
+ <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
55
+ <link rel="stylesheet" type="text/css" href="layout.css">
56
+ <link rel="stylesheet" type="text/css" href="style.css">
57
+ <link rel="stylesheet" type="text/css" href="ruby.css">
58
+ </head>
59
+
60
+ <body class="twikiViewPage">
61
+
62
+ <div class="twikiTopBar">
63
+ <div class="twikiTopBarContents">
64
+ <h1 class="pagetitle">rb-wartslib: Ruby Warts Library</h1>
65
+ </div>
66
+ </div>
67
+
68
+ <a name="PageTop"></a>
69
+ <div class="twikiMiddleContainer">
70
+ <div class="twikiLeftBar">
71
+ <div class="twikiLeftBarContents">
72
+ <ul>
73
+ <li> <a class="twikiLink" href="http://rb-wartslib.rubyforge.org/">Project Home Page</a></li>
74
+ <li> <a class="twikiLink" href="http://rubyforge.org/projects/rb-wartslib/">RubyForge Page</a></li>
75
+ </ul>
76
+ <hr>
77
+ <ul>
78
+ <li> <a class="twikiLink" href="index.html#installation">Installation</a></li>
79
+ <li> <a class="twikiLink" href="tutorial.html">Tutorial</a></li>
80
+ <li> <a class="twikiLink" href="reference.html">Class Reference</a></li>
81
+ <li> <a class="twikiLink" href="samples.html"><b>Sample Scripts</b></a></li>
82
+ </ul>
83
+ <hr>
84
+ <ul>
85
+ <li> <a class="twikiLink" href="index.html#contact">Contact</a></li>
86
+ <li> <a class="twikiLink" href="index.html#license">License Note</a></li>
87
+ </ul>
88
+ </div></div></div>
89
+
90
+ <div class="twikiMain">
91
+ <div class="twikiTopic">
92
+ <h1>Sample Script: #{basename}</h1>
93
+ HEADER
94
+ end
95
+
96
+ out.puts html
97
+
98
+ unless $options.snippet
99
+ out.puts <<FOOTER
100
+ <!---->
101
+ </div></div>
102
+ </body></html>
103
+ FOOTER
104
+ end
105
+
106
+ out.close
107
+ end
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ puts "<pre class=\"snippet\">"
4
+ puts "<span class=\"shell_command\"></span>"
5
+ $stdin.each do |line|
6
+ print "<span class=\"command_output\">", line.chomp!, "</span>", "\n"
7
+ end
8
+ puts "</pre>"
9
+
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+
3
+ rm tut-*.rb.html
4
+ ./gen-code-html -s tut-*.rb
@@ -0,0 +1,221 @@
1
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
2
+ <html>
3
+ <head>
4
+ <title>rb-wartslib: Ruby Warts Library</title>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
6
+ <link rel="stylesheet" type="text/css" href="layout.css">
7
+ <link rel="stylesheet" type="text/css" href="style.css">
8
+ </head>
9
+
10
+ <body>
11
+
12
+ <div class="twikiTopBar">
13
+ <div class="twikiTopBarContents">
14
+ <h1 class="pagetitle">rb-wartslib: Ruby Warts Library</h1>
15
+ </div>
16
+ </div>
17
+
18
+ <a name="PageTop"></a>
19
+ <div class="twikiLeftBar">
20
+ <div class="twikiLeftBarContents">
21
+ <ul>
22
+ <li> <a href="index.html"><b>Project Home Page</b></a></li>
23
+ <li> <a href="http://rubyforge.org/projects/rb-wartslib/">RubyForge Page</a></li>
24
+ </ul>
25
+ <hr>
26
+ <ul>
27
+ <li> <a href="#installation">Installation</a></li>
28
+ <li> <a href="tutorial.html">Tutorial</a></li>
29
+ <li> <a href="reference.html">Class Reference</a></li>
30
+ <li> <a href="samples.html">Sample Scripts</a></li>
31
+ </ul>
32
+ <hr>
33
+ <ul>
34
+ <li> <a href="#contact">Contact</a></li>
35
+ <li> <a href="#license">License</a></li>
36
+ </ul>
37
+ </div></div>
38
+
39
+ <div class="twikiMain">
40
+ <div class="twikiTopic">
41
+ <h2>What is it?</h2>
42
+ <p>
43
+ <b>This is a Ruby extension for reading/writing warts files.</b> Warts
44
+ files are output by <a href="http://www.wand.net.nz/scamper/">scamper</a>,
45
+ a tool for performing large-scale traceroute- and ping-based network
46
+ measurements. You can also use this extension to read (but not write)
47
+ <a href="http://www.caida.org/tools/utilities/arts/">arts++</a> files
48
+ produced by <a href="http://www.caida.org/tools/measurement/skitter/index.xml">skitter</a>.
49
+ </p>
50
+
51
+ <p>
52
+ This extension only provides a thin wrapper around the C-language routines
53
+ in <i>scamper</i> for reading/writing warts files. But this is all you
54
+ need to write pleasant little Ruby scripts that compute various statistics
55
+ about collected traces, convert IP paths to AS paths, and perform myriad
56
+ other processing for which, let's be honest, <b>life is too short to be
57
+ writing in C</b>. Write your analysis scripts with <i>rb-wartslib</i>, and
58
+ with a Zen-like peace, spend your newly discovered free time with your
59
+ friends, family, and your neglected small furry pet animals.
60
+ </p>
61
+
62
+ <h2>But Ruby is slow!</h2>
63
+
64
+ <p>
65
+ Yes, it is. But it is also <b>fast enough.</b> For example, some of the
66
+ sample Ruby analysis scripts only take 1&frac12; to 2 times longer than an
67
+ equivalent written in C/C++ because all the warts I/O is done in C code.
68
+ If you're computing the hyperbolic Lagrangian bleibenvalues, then it might
69
+ be a bit slow with Ruby, but what are computers for after all? More
70
+ importantly, it's much easier to write <b>correct</b> code with Ruby than
71
+ C, and correctness is far more important than speed with data analysis.
72
+ </p>
73
+
74
+ <h2>Limitations</h2>
75
+
76
+ <p>
77
+ Although you can use this extension to write out trace data to warts files,
78
+ you can't use this extension itself to create completely new traces in
79
+ memory--that is, you can only write out traces you've read in from a file.
80
+ Furthermore, any read-in data is immutable, with a few careful exceptions,
81
+ because the principle purpose of this extension is to support the analysis
82
+ of existing data rather than the creation of new data. The ability to
83
+ write out (unmodified) traces is still useful for filtering out traces and
84
+ for re-organizing traces into a different set of files (for example, into
85
+ daily files, or into separate files by the type of probing method used).
86
+ </p>
87
+
88
+ <h2><a name="installation"></a>Installation</h2>
89
+
90
+ <p>
91
+ If <i>rb-wartslib</i> were a typical Ruby Gem, you would install it with
92
+ </p>
93
+
94
+ <pre>
95
+ $ sudo gem install rb-wartslib
96
+ </pre>
97
+
98
+ <p>
99
+ However, <i>rb-wartslib</i> must be compiled against the <i>scamper</i>
100
+ source code so there are a few additional steps.
101
+ </p>
102
+
103
+ <p>
104
+ First, download and build <i>scamper;</i> for example:
105
+ </p>
106
+
107
+ <pre>
108
+ $ wget http://www.wand.net.nz/scamper/scamper-cvs-20070523i.tar.gz
109
+ $ tar xvzf scamper-cvs-20070523i.tar.gz
110
+ $ cd scamper-cvs-20070523i
111
+ $ make -f Makefile.gnu (if you're on Linux or MacOS X)
112
+ $ make (if you're on FreeBSD)
113
+ </pre>
114
+
115
+ <p>
116
+ That will build <code>libscamperfile.a</code>, which is what <i>rb-wartslib</i>
117
+ links against.
118
+ </p>
119
+
120
+ <p>
121
+ Now build and install <i>rb-wartslib:</i>
122
+ </p>
123
+
124
+ <pre>
125
+ $ SCAMPER=/Users/youngh/scamper-cvs-20070523h
126
+ (set the above to the directory where you built scamper)
127
+ $ sudo gem install rb-wartslib -- --with-scamper-include=$SCAMPER
128
+ --with-scamper-lib=$SCAMPER
129
+ </pre>
130
+
131
+ <p>
132
+ That's it.
133
+ </p>
134
+
135
+ <h2>Documentation</h2>
136
+
137
+ <p>
138
+ Start by reading the <a href="tutorial.html">tutorial</a>, which discusses
139
+ fundamental concepts and covers most of the common uses. Then look over
140
+ the <a href="samples.html">sample analysis scripts</a>. For a more
141
+ in-depth documentation of the Ruby classes, see
142
+ the <a href="reference.html">reference documentation</a>.
143
+ </p>
144
+
145
+ <p>
146
+ The sample scripts are also included in the <code>bin</code> subdirectory
147
+ of the <i>rb-wartslib</i> distribution file. To find them on your system,
148
+ execute the following after installing <i>rb-wartslib:</i>
149
+ </p>
150
+
151
+ <pre>
152
+ $ gem contents rb-wartslib
153
+ /opt/local/lib/ruby/gems/1.8/gems/rb-wartslib-0.9.11/bin/count-traces
154
+ /opt/local/lib/ruby/gems/1.8/gems/rb-wartslib-0.9.11/bin/create-aspath
155
+ /opt/local/lib/ruby/gems/1.8/gems/rb-wartslib-0.9.11/bin/extract-trace-addrs
156
+ ...
157
+ </pre>
158
+
159
+
160
+ <h2><a name="contact"></a>Contact</h2>
161
+
162
+ <p>
163
+ <i>rb-wartslib</i> is written and maintained by
164
+ <a href="http://www.caida.org/~youngh/">Young Hyun</a> as a part of
165
+ <a href="http://www.caida.org">CAIDA</a>'s work on
166
+ the <a href="http://www.caida.org/projects/ark/"> Archipelago Measurement
167
+ Infrastructure</a>. You may contact him at <code>youngh AT
168
+ rubyforge.org</code>, or use the tracker/forums at
169
+ the <a href="http://rubyforge.org/projects/rb-wartslib/">RubyForge project
170
+ page</a> for getting help.
171
+ </p>
172
+
173
+ <p>
174
+ Please direct <i>scamper</i> questions, including problems building it,
175
+ to <a href="http://www.wand.net.nz/personDetail.php?id=13">Matthew
176
+ Luckie</a>:
177
+ </p>
178
+
179
+ <pre>
180
+ mjl (squigglyatthingie) wand (period) net (period) nz
181
+ </pre>
182
+
183
+
184
+ <h2><a name="license"></a>License</h2>
185
+
186
+ <p>
187
+ The <i>rb-wartslib</i> binding code is licensed under
188
+ <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GPLv2</a>
189
+ <b>or later</b>, but this binding must link with <i>scamper</i>, which is
190
+ licensed under GPLv2 <b>only</b> (no later). Therefore, when your own Ruby
191
+ program makes calls into <i>rb-wartslib</i> (that is, "links"
192
+ to <i>rb-wartslib</i>), then <i>rb-wartslib</i> is effectively under GPLv2
193
+ <b>only</b>, so your client program must be GPLv2 compatible. However, if
194
+ you merely re-use any of the <i>rb-wartslib</i> source code in your own
195
+ programs (but none of the <i>scamper</i> source code), then the license is
196
+ GPLv2 <b>or later</b>. For example, if you wish to use
197
+ the <i>rb-wartslib</i> source code as a template for writing an extension
198
+ of your own (that provides a binding to something else), then the code is
199
+ licensed under GPLv2 or later.
200
+ </p>
201
+
202
+ <p>
203
+ If you don't distribute your client program, then the requirements of the
204
+ GPL do not apply to your client program. In particular, if you write an
205
+ internal tool used only in your organization, then it does <b>not</b> have
206
+ to be released under the GPL to use <i>rb-wartslib</i>; it can have
207
+ whatever license you choose. This freedom is intentionally allowed by the
208
+ GPL.
209
+ </p>
210
+
211
+ <h2>Acknowledgments</h2>
212
+
213
+ <p>
214
+ The design of these web pages draws on one of the styles used by the
215
+ <a href="http://twiki.org/">TWiki</a> wiki tool.
216
+ </p>
217
+
218
+
219
+ <!---->
220
+ </div></div>
221
+ </body></html>