diff options
author | Russ Allbery <rra@stanford.edu> | 2012-02-22 14:55:57 -0800 |
---|---|---|
committer | Russ Allbery <rra@stanford.edu> | 2012-02-22 14:55:57 -0800 |
commit | 02b54ff4cfd2299b32cc2dc88d68da2e3de0370d (patch) | |
tree | 8f5b520387016378620f5550715f21cde44dec92 /ruby | |
parent | bd8e7a093fe9295e702679d935d014f210ff5f0f (diff) |
Add timeout support in the Ruby bindings
Change-Id: I108a825614ab6e6d048fc83251a74a73cd89a4fd
Diffstat (limited to 'ruby')
-rw-r--r-- | ruby/README | 28 | ||||
-rw-r--r-- | ruby/remctl.c | 72 | ||||
-rw-r--r-- | ruby/test_remctl.rb.in | 36 |
3 files changed, 131 insertions, 5 deletions
diff --git a/ruby/README b/ruby/README index d3f2f2f..6fb4e10 100644 --- a/ruby/README +++ b/ruby/README @@ -112,6 +112,11 @@ FULL INTERFACE the kernel. To use a specific source IP, assign that IP address (as a string) to Remctl.source_ip before calling Remctl.new. + To set a default timeout for any newly-created Remctl objects, + assign the timeout (in seconds) to Remctl.timeout before calling + Remctl.new. See below under r.set_timeout for more information + about how the timeout is interpreted. + Exceptions: * ArgumentError, TypeError: an invalid argument was supplied @@ -202,6 +207,29 @@ FULL INTERFACE * Remctl::Error: a network or authentication error occurred * Remctl::NotOpen: no connection currently open + r.set_timeout(timeout) + Sets the timeout for connections and commands. All subsequent + operations on this object will be subject to this timeout, including + reopen(). + + Arguments: + + * timeout (required): long, the timeout in seconds or 0 + + If the timeout is 0, this clears any timeout that was previously + set. + + The timeout is a timeout on network activity from the server, not on + a complete operation. So, for example, a timeout of ten seconds + just requires that the server send some data every ten seconds. If + the server sends only tiny amounts of data at a time, the complete + operation could take much longer than ten seconds without triggering + the timeout. + + Exceptions: + + * RemctlError: the timeout was negative + r.close() Close a connection. After calling this method, #reopen must be called for this object before sending any further commands. The diff --git a/ruby/remctl.c b/ruby/remctl.c index b5a46d6..97ff544 100644 --- a/ruby/remctl.c +++ b/ruby/remctl.c @@ -6,7 +6,7 @@ * * Original implementation by Anthony M. Martinez <twopir@nmt.edu> * Copyright 2010 Anthony M. Martinez <twopir@nmt.edu> - * Copyright 2010, 2011 + * Copyright 2010, 2011, 2012 * The Board of Trustees of the Leland Stanford Junior University * * Permission to use, copy, modify, and distribute this software and its @@ -61,7 +61,8 @@ void Init_remctl(void); static VALUE cRemctl, cRemctlResult, eRemctlError, eRemctlNotOpen; /* Since we can't have @ in our names here... */ -static ID AAdefault_port, AAdefault_principal, AAccache, AAsource_ip; +static ID AAdefault_port, AAdefault_principal; +static ID AAccache, AAsource_ip, AAtimeout; static ID Ahost, Aport, Aprincipal; /* Map the remctl_output type constants to strings. */ @@ -295,6 +296,36 @@ rb_remctl_source_ip_set(VALUE self UNUSED, VALUE new) } +/* call-seq: + * Remctl.timeout -> 0 + * + * Return the default timeout used for a Remctl complex connection. A value + * of 0 says to use the default of no timeout. + */ +static VALUE +rb_remctl_timeout_get(VALUE self UNUSED) +{ + return rb_cvar_get(cRemctl, AAtimeout); +} + + +/* call-seq: + * Remctl.timeout = 10 -> 10 + * + * Change the timeout used for a new instance of a complex connection. A + * value of 0 indicates the default of no timeout. + */ +static VALUE +rb_remctl_timeout_set(VALUE self UNUSED, VALUE new) +{ + if (NIL_P(new)) + rb_cvar_set(cRemctl, AAtimeout, UINT2NUM(0)); + else + rb_cvar_set(cRemctl, AAtimeout, new); + return rb_cvar_get(cRemctl, AAtimeout); +} + + /* * Destructor for a Remctl object. Closes the connection and frees the * underlying memory. @@ -348,7 +379,7 @@ static VALUE rb_remctl_reopen(VALUE self) { struct remctl *r; - VALUE vhost, vport, vprinc, vdefccache, vdefsource; + VALUE vhost, vport, vprinc, vdefccache, vdefsource, vdeftimeout; char *host, *princ; unsigned int port; @@ -371,6 +402,12 @@ rb_remctl_reopen(VALUE self) if (!remctl_set_source_ip(r, StringValuePtr(vdefsource))) rb_raise(eRemctlError, "%s", remctl_error(r)); + /* Set the timeout if needed. */ + vdeftimeout = rb_cvar_get(cRemctl, AAtimeout); + if (!NIL_P(vdeftimeout)) + if (!remctl_set_timeout(r, FIX2UINT(vdeftimeout))) + rb_raise(eRemctlError, "%s", remctl_error(r)); + /* Retrieve the stored host, port, and principal values. */ vhost = rb_ivar_get(self, Ahost); vport = rb_ivar_get(self, Aport); @@ -388,6 +425,28 @@ rb_remctl_reopen(VALUE self) /* call-seq: + * r.set_timeout(10) -> nil + * + * Set the timeout on an existing Remctl connection. This affects any further + * commands on that connection. The timeout may be 0 to disable timeouts. + * Raises Remctl::Error if changing the timeout fails. + */ +static VALUE +rb_remctl_set_timeout(VALUE self, VALUE vtimeout) +{ + struct remctl *r; + long timeout; + + GET_REMCTL_OR_RAISE(self, r); + Check_Type(vtimeout, T_FIXNUM); + timeout = NIL_P(vtimeout) ? 0 : FIX2LONG(vtimeout); + if (!remctl_set_timeout(r, timeout)) + rb_raise(eRemctlError, "%s", remctl_error(r)); + return Qnil; +} + + +/* call-seq: * r.command(*args) -> nil * * Call a remote command. Returns nil. @@ -537,6 +596,7 @@ Init_remctl(void) AAdefault_principal = rb_intern("@@default_principal"); AAccache = rb_intern("@@ccache"); AAsource_ip = rb_intern("@@source_ip"); + AAtimeout = rb_intern("@@timeout"); Ahost = rb_intern("@host"); Aport = rb_intern("@port"); Aprincipal = rb_intern("@principal"); @@ -546,6 +606,7 @@ Init_remctl(void) rb_cvar_set(cRemctl, AAdefault_principal, Qnil); rb_cvar_set(cRemctl, AAccache, Qnil); rb_cvar_set(cRemctl, AAsource_ip, Qnil); + rb_cvar_set(cRemctl, AAtimeout, UINT2NUM(0)); /* Getter and setter methods for class variables. */ rb_define_singleton_method(cRemctl, "default_port", @@ -564,6 +625,10 @@ Init_remctl(void) rb_remctl_source_ip_get, 0); rb_define_singleton_method(cRemctl, "source_ip=", rb_remctl_source_ip_set, 1); + rb_define_singleton_method(cRemctl, "timeout", + rb_remctl_timeout_get, 0); + rb_define_singleton_method(cRemctl, "timeout=", + rb_remctl_timeout_set, 1); /* Create the Remctl class. */ rb_define_alloc_func(cRemctl, rb_remctl_alloc); @@ -573,6 +638,7 @@ Init_remctl(void) rb_define_method(cRemctl, "command", rb_remctl_command, -1); rb_define_method(cRemctl, "output", rb_remctl_output, 0); rb_define_method(cRemctl, "noop", rb_remctl_noop, 0); + rb_define_method(cRemctl, "set_timeout", rb_remctl_set_timeout, 1); /* Document-class: Remctl::Result * diff --git a/ruby/test_remctl.rb.in b/ruby/test_remctl.rb.in index 9af0efa..8471fad 100644 --- a/ruby/test_remctl.rb.in +++ b/ruby/test_remctl.rb.in @@ -1,7 +1,7 @@ -# test_remctl.rb -- Test suite for remctl Ruby bindings +# Test suite for remctl Ruby bindings. # # Written by Russ Allbery <rra@stanford.edu> -# Copyright 2010 +# Copyright 2010, 2012 # The Board of Trustees of the Leland Stanford Junior University # # See LICENSE for licensing terms. @@ -224,6 +224,38 @@ class TC_RemctlFull < Test::Unit::TestCase Remctl.source_ip = nil end + def test_full_timeout + unless configured? then return end + + # Test the set_timeout method on an existing object. + r = Remctl.new('127.0.0.1', 14373, @principal) + r.set_timeout(1) + r.command('test', 'sleep') + assert_raise Remctl::Error do + begin + output = r.output + rescue Remctl::Error + assert_equal('error receiving token: timed out', $!.to_s) + raise + end + end + r.close + + # Test the default timeout support. + Remctl.timeout = 1 + r = Remctl.new('127.0.0.1', 14373, @principal) + r.command('test', 'sleep') + assert_raise Remctl::Error do + begin + output = r.output + rescue Remctl::Error + assert_equal('error receiving token: timed out', $!.to_s) + raise + end + end + r.close + end + def test_full_errors unless configured? then return end assert_raise ArgumentError do Remctl.new end |