summaryrefslogtreecommitdiff
path: root/ruby
diff options
context:
space:
mode:
authorRuss Allbery <rra@stanford.edu>2012-02-22 14:55:57 -0800
committerRuss Allbery <rra@stanford.edu>2012-02-22 14:55:57 -0800
commit02b54ff4cfd2299b32cc2dc88d68da2e3de0370d (patch)
tree8f5b520387016378620f5550715f21cde44dec92 /ruby
parentbd8e7a093fe9295e702679d935d014f210ff5f0f (diff)
Add timeout support in the Ruby bindings
Change-Id: I108a825614ab6e6d048fc83251a74a73cd89a4fd
Diffstat (limited to 'ruby')
-rw-r--r--ruby/README28
-rw-r--r--ruby/remctl.c72
-rw-r--r--ruby/test_remctl.rb.in36
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