Index: src/rrd_lastupdate.c
===================================================================
--- src/rrd_lastupdate.c	(revision 2018)
+++ src/rrd_lastupdate.c	(working copy)
@@ -16,6 +16,38 @@
     char    **ds_names;
     char    **last_ds;
     unsigned long ds_count, i;
+
+    last_update = rrd_lastupdate_s (argc, argv,
+            &ds_count, &ds_names, &last_ds);
+    if (last_update < 0)
+        return (-1);
+
+    for (i = 0; i < ds_count; i++)
+        printf(" %s", ds_names[i]);
+    printf ("\n\n");
+
+    printf ("%10lu:", last_update);
+    for (i = 0; i < ds_count; i++) {
+        printf(" %s", last_ds[i]);
+        free(last_ds[i]);
+        free(ds_names[i]);
+    }
+    printf("\n");
+
+    free(last_ds);
+    free(ds_names);
+
+    return (0);
+} /* int rrd_lastupdate */
+
+time_t rrd_lastupdate_s(
+    int argc,
+    char **argv,
+    unsigned long *ds_cnt,  /* number of data sources in file */
+    char ***ds_namv,    /* names of data sources */
+    char ***last_ds)
+{
+    time_t    last_update = -1;
     int status;
 
     char *opt_daemon = NULL;
@@ -37,58 +69,43 @@
             break;
 
         switch (opt) {
-        case 'd':
-            if (opt_daemon != NULL)
+            case 'd':
+                if (opt_daemon != NULL)
                     free (opt_daemon);
-            opt_daemon = strdup (optarg);
-            if (opt_daemon == NULL)
-            {
-                rrd_set_error ("strdup failed.");
+                opt_daemon = strdup (optarg);
+                if (opt_daemon == NULL)
+                {
+                    rrd_set_error ("strdup failed.");
+                    return (-1);
+                }
+                break;
+
+            default:
+                rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file>",
+                               argv[0]);
                 return (-1);
-            }
-            break;
-
-        default:
-            rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file>",
-                    argv[0]);
-            return (-1);
-            break;
+                break;
         }
     }                   /* while (42) */
-
+    
     if ((argc - optind) != 1) {
         rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file>",
-                argv[0]);
+                       argv[0]);
         return (-1);
     }
-
+    
     status = rrdc_flush_if_daemon(opt_daemon, argv[optind]);
     if (opt_daemon) free (opt_daemon);
     if (status) return (-1);
-
+    
     status = rrd_lastupdate_r (argv[optind],
-            &last_update, &ds_count, &ds_names, &last_ds);
+            &last_update, ds_cnt, ds_namv, last_ds);
     if (status != 0)
-        return (status);
+        return (-1);
+    
+    return (last_update);
+} /* int rrd_lastupdate_s */
 
-    for (i = 0; i < ds_count; i++)
-        printf(" %s", ds_names[i]);
-    printf ("\n\n");
-
-    printf ("%10lu:", last_update);
-    for (i = 0; i < ds_count; i++) {
-        printf(" %s", last_ds[i]);
-        free(last_ds[i]);
-        free(ds_names[i]);
-    }
-    printf("\n");
-
-    free(last_ds);
-    free(ds_names);
-
-    return (0);
-} /* int rrd_lastupdate */
-
 int rrd_lastupdate_r(const char *filename,
         time_t *ret_last_update,
         unsigned long *ret_ds_count,
Index: src/librrd.sym.in.in
===================================================================
--- src/librrd.sym.in.in	(revision 2018)
+++ src/librrd.sym.in.in	(working copy)
@@ -31,6 +31,7 @@
 rrd_last
 rrd_last_r
 rrd_lastupdate
+rrd_lastupdate_s
 rrd_lastupdate_r
 rrd_lock
 rrd_mkdir_p
Index: src/rrd.h
===================================================================
--- src/rrd.h	(revision 2018)
+++ src/rrd.h	(working copy)
@@ -192,6 +192,12 @@
     int,
     char **);
     int rrd_lastupdate(int argc, char **argv);
+    time_t rrd_lastupdate_s(
+    int,
+    char **,
+    unsigned long *,
+    char ***,
+    char ***);
     time_t    rrd_first(
     int,
     char **);
Index: bindings/lua/rrdlua.c
===================================================================
--- bindings/lua/rrdlua.c	(revision 2018)
+++ bindings/lua/rrdlua.c	(working copy)
@@ -268,6 +268,43 @@
 }
 
 static int
+lua_rrd_lastupdate (lua_State * L)
+{
+    int argc = lua_gettop(L) + 1;
+    char **argv = make_argv("lastupdate", L);
+    unsigned long i, ds_cnt;
+    char    **names, **data;
+    time_t  result;
+    
+    reset_rrd_state();
+    result = rrd_lastupdate_s(argc, argv, &ds_cnt, &names, &data);
+    free(argv);
+    if (rrd_test_error()) luaL_error(L, rrd_get_error());
+    
+    lua_pushnumber(L, (lua_Number) result);
+    
+    /* create the ds names array */
+    lua_newtable(L);
+    for (i=0; i<ds_cnt; i++) {
+        lua_pushstring(L, names[i]);
+        lua_rawseti(L, -2, i+1);
+        rrd_freemem(names[i]);
+    }
+    rrd_freemem(names);
+    
+    /* create the data array */
+    lua_newtable(L);
+    for (i=0; i<ds_cnt; i++) {
+        lua_pushstring(L, data[i]);
+        lua_rawseti(L, -2, i+1);
+        rrd_freemem(data[i]);
+    }
+    rrd_freemem(data);
+    
+    return 3;
+}
+
+static int
 lua_rrd_graph (lua_State * L)
 {
   int argc = lua_gettop(L) + 1;
@@ -349,6 +386,7 @@
   {"first", lua_rrd_first},
   {"graph", lua_rrd_graph},
   {"last", lua_rrd_last},
+  {"lastupdate", lua_rrd_lastupdate},
   {"resize", lua_rrd_resize},
   {"restore", lua_rrd_restore},
   {"tune", lua_rrd_tune},
Index: bindings/python/rrdtoolmodule.c
===================================================================
--- bindings/python/rrdtoolmodule.c	(revision 2018)
+++ bindings/python/rrdtoolmodule.c	(working copy)
@@ -408,6 +408,54 @@
     return r;
 }
 
+static char PyRRD_lastupdate__doc__[] =
+"lastupdate(filename): Returns the timestamp and the value stored for each datum in the most recent update of an RRD";
+
+static PyObject *PyRRD_lastupdate(
+                            PyObject UNUSED(*self),
+                            PyObject * args)
+{
+    PyObject *r;
+    int       argc;
+    char    **argv;
+    char    **ds_names;
+    char    **data;
+    unsigned long ds_cnt;
+    time_t    retval;
+    
+    if (create_args("lastupdate", args, &argc, &argv) < 0)
+        return NULL;
+    
+    if ((retval = rrd_lastupdate_s (argc, argv, &ds_cnt, &ds_names, &data)) == -1 ) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else {
+        /* Return :
+         (last, (name1, name2, ...), (data1, data2, ...)) */
+        PyObject *ds_names_tup, *data_tup;
+        unsigned long i;
+
+        r = PyTuple_New(3);
+        ds_names_tup = PyTuple_New(ds_cnt);
+        data_tup = PyTuple_New(ds_cnt);
+        PyTuple_SET_ITEM(r, 0, PyInt_FromLong((long) retval));
+        PyTuple_SET_ITEM(r, 1, ds_names_tup);
+        PyTuple_SET_ITEM(r, 2, data_tup);
+
+        for (i = 0; i < ds_cnt; i++) {
+            PyTuple_SET_ITEM(ds_names_tup, i, PyString_FromString(ds_names[i]));
+            PyTuple_SET_ITEM(data_tup, i, PyString_FromString(data[i]));
+            rrd_freemem(ds_names[i]);
+            rrd_freemem(data[i]);
+        }        
+        rrd_freemem(ds_names);   /* rrdtool don't use PyMem_Malloc :) */
+        rrd_freemem(data);
+    }        
+    destroy_args(&argv);
+    return r;
+}
+
 static char PyRRD_resize__doc__[] =
     "resize(args...): alters the size of an RRA.\n"
     "    resize filename rra-num GROW|SHRINK rows";
@@ -598,6 +646,7 @@
     meth("tune", PyRRD_tune, PyRRD_tune__doc__),
     meth("first", PyRRD_first, PyRRD_first__doc__),
     meth("last", PyRRD_last, PyRRD_last__doc__),
+    meth("lastupdate", PyRRD_lastupdate, PyRRD_lastupdate__doc__),
     meth("resize", PyRRD_resize, PyRRD_resize__doc__),
     meth("info", PyRRD_info, PyRRD_info__doc__),
     meth("graphv", PyRRD_graphv, PyRRD_graphv__doc__),
Index: bindings/ruby/main.c
===================================================================
--- bindings/ruby/main.c	(revision 2018)
+++ bindings/ruby/main.c	(working copy)
@@ -319,6 +319,40 @@
         return rb_funcall(rb_cTime, rb_intern("at"), 1, UINT2NUM(last));
 }
 
+VALUE rb_rrd_lastupdate(
+    VALUE self,
+    VALUE args)
+{
+    string_arr a;
+    time_t    last;
+    unsigned long ds_cnt, i;
+    char    **raw_names, **raw_data;
+    VALUE     data, names, result;    
+
+    a = string_arr_new(args);
+    reset_rrd_state();
+    last = rrd_lastupdate_s(a.len, a.strings, &ds_cnt, &raw_names, &raw_data);
+    string_arr_delete(a);
+
+    RRD_CHECK_ERROR names = rb_ary_new();
+    data = rb_ary_new();
+
+    for (i = 0; i < ds_cnt; i++) {
+        rb_ary_push(names, rb_str_new2(raw_names[i]));
+        rrd_freemem(raw_names[i]);
+        rb_ary_push(data, rb_str_new2(raw_data[i]));
+        rrd_freemem(raw_data[i]);
+    }
+    rrd_freemem(raw_names);
+    rrd_freemem(raw_data);
+
+    result = rb_ary_new2(3);
+    rb_ary_store(result, 0, INT2NUM(last));
+    rb_ary_store(result, 1, names);
+    rb_ary_store(result, 2, data);
+    return result;
+}
+
 VALUE rb_rrd_xport(
     VALUE self,
     VALUE args)
@@ -377,6 +411,7 @@
     rb_define_module_function(mRRD, "fetch", rb_rrd_fetch, -2);
     rb_define_module_function(mRRD, "graph", rb_rrd_graph, -2);
     rb_define_module_function(mRRD, "last", rb_rrd_last, -2);
+    rb_define_module_function(mRRD, "lastupdate", rb_rrd_lastupdate, -2);
     rb_define_module_function(mRRD, "resize", rb_rrd_resize, -2);
     rb_define_module_function(mRRD, "restore", rb_rrd_restore, -2);
     rb_define_module_function(mRRD, "tune", rb_rrd_tune, -2);
Index: bindings/perl-shared/RRDs.xs
===================================================================
--- bindings/perl-shared/RRDs.xs	(revision 2018)
+++ bindings/perl-shared/RRDs.xs	(working copy)
@@ -148,6 +148,53 @@
       OUTPUT:
             RETVAL
 
+SV *
+rrd_lastupdate(...)
+	PROTOTYPE: @	
+	PREINIT:
+		time_t        result;		
+		unsigned long ds_cnt,i;
+		char **argv;
+		char **ds_namv,**data;
+		AV *names, *retar;
+	PPCODE:
+		argv = (char **) malloc((items+1)*sizeof(char *));
+		argv[0] = "dummy";
+		for (i = 0; i < items; i++) { 
+		    STRLEN len;
+		    char *handle= SvPV(ST(i),len);
+		    /* actually copy the data to make sure possible modifications
+		       on the argv data does not backfire into perl */ 
+		    argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char));
+		    strcpy(argv[i+1],handle);
+ 	        }
+		rrd_clear_error();
+		result = rrd_lastupdate_s(items+1,argv,&ds_cnt,&ds_namv,&data); 
+		for (i=0; i < items; i++) {
+		    free(argv[i+1]);
+		}
+		free(argv);
+		if (rrd_test_error()) XSRETURN_UNDEF;
+                /* convert the ds_namv into perl format */
+		names=newAV();
+		for (i = 0; i < ds_cnt; i++){
+		    av_push(names,newSVpv(ds_namv[i],0));
+		    rrd_freemem(ds_namv[i]);
+		}
+		rrd_freemem(ds_namv);			
+
+		retar=newAV();
+		/* convert the data array into perl format */
+		for (i = 0; i < ds_cnt; i++){
+		    av_push(retar,newSVpv(data[i],0));
+		    rrd_freemem(data[i]);
+		}
+		rrd_freemem(data);
+		EXTEND(sp,3);
+		PUSHs(sv_2mortal(newSViv((long) result)));
+		PUSHs(sv_2mortal(newRV_noinc((SV*)names)));
+		PUSHs(sv_2mortal(newRV_noinc((SV*)retar)));
+
 int
 rrd_first(...)
       PROTOTYPE: @

