diff --git a/src/cmd/Makefile.am b/src/cmd/Makefile.am index de1df307bf86..57645ad00ccb 100644 --- a/src/cmd/Makefile.am +++ b/src/cmd/Makefile.am @@ -12,7 +12,6 @@ AM_CPPFLAGS = \ fluxcmd_ldadd = \ $(top_builddir)/src/modules/live/liblive.la \ - $(top_builddir)/src/modules/modctl/libmodctl.la \ $(top_builddir)/src/modules/kvs/libkvs.la \ $(top_builddir)/src/modules/api/libapi.la \ $(top_builddir)/src/common/libflux/libflux.la \ @@ -60,3 +59,8 @@ flux_mping_LDADD = \ $(top_builddir)/src/common/liblsd/liblsd.la \ $(fluxcmd_ldadd) \ $(LIBUTIL) +flux_module_LDADD = \ + $(top_builddir)/src/modules/modctl/libmodctl.la \ + $(top_builddir)/src/common/libmrpc/libmrpc.la \ + $(fluxcmd_ldadd) \ + $(LIBUTIL) diff --git a/src/cmd/flux-module.c b/src/cmd/flux-module.c index 237a8f4c3983..3849cfd4bbbd 100644 --- a/src/cmd/flux-module.c +++ b/src/cmd/flux-module.c @@ -31,27 +31,32 @@ #include #include +#include "src/modules/modctl/modctl.h" #include "src/common/libutil/xzmalloc.h" #include "src/common/libutil/jsonutil.h" #include "src/common/libutil/log.h" #include "src/common/libutil/shortjson.h" #include "src/common/libutil/readall.h" +const int max_idle = 99; -#define OPTIONS "+hr:" + +#define OPTIONS "+hr:an:" static const struct option longopts[] = { {"help", no_argument, 0, 'h'}, {"rank", required_argument, 0, 'r'}, + {"all", no_argument, 0, 'a'}, + {"nodeset", required_argument, 0, 'n'}, { 0, 0, 0, 0 }, }; -void mod_lsmod (flux_t h, uint32_t nodeid, int ac, char **av); -void mod_rmmod (flux_t h, uint32_t nodeid, int ac, char **av); -void mod_insmod (flux_t h, uint32_t nodeid, int ac, char **av); +void mod_lsmod (flux_t h, uint32_t nodeid, const char *ns, int ac, char **av); +void mod_rmmod (flux_t h, uint32_t nodeid, const char *ns, int ac, char **av); +void mod_insmod (flux_t h, uint32_t nodeid, const char *ns, int ac, char **av); typedef struct { const char *name; - void (*fun)(flux_t h, uint32_t nodeid, int ac, char **av); + void (*fun)(flux_t h, uint32_t nodeid, const char *ns, int ac, char **av); } func_t; static func_t funcs[] = { @@ -76,7 +81,9 @@ void usage (void) " flux-module [OPTIONS] remove module\n" " flux-module [OPTIONS] load module [arg ...]\n" "where OPTIONS are:\n" -" -r,--rank N specify nodeid to send request\n" +" -r,--rank N specify nodeid to send request\n" +" -n,--nodeset NS use modctl to distribute request to NS\n" +" -a,--all use modctl to distribute request to entire session\n" ); exit (1); } @@ -88,6 +95,8 @@ int main (int argc, char *argv[]) char *cmd; func_t *f; uint32_t nodeid = FLUX_NODEID_ANY; + char *nodeset = NULL; + bool aopt = false; log_init ("flux-module"); @@ -99,6 +108,12 @@ int main (int argc, char *argv[]) case 'r': /* --rank N */ nodeid = strtoul (optarg, NULL, 10); break; + case 'a': /* --all */ + aopt = true; + break; + case 'n': /* --nodeset NS */ + nodeset = xstrdup (optarg); + break; default: usage (); break; @@ -112,14 +127,20 @@ int main (int argc, char *argv[]) if (!(h = flux_api_open ())) err_exit ("flux_api_open"); - f->fun (h, nodeid, argc - optind, argv + optind); + if (!nodeset && aopt) { + int size = flux_size (h); + nodeset = size > 1 ? xasprintf ("[0-%d]", size - 1) : xstrdup ("0"); + } + f->fun (h, nodeid, nodeset, argc - optind, argv + optind); flux_api_close (h); + if (nodeset) + free (nodeset); log_fini (); return 0; } -void mod_insmod (flux_t h, uint32_t nodeid, int ac, char **av) +void mod_insmod (flux_t h, uint32_t nodeid, const char *ns, int ac, char **av) { char *modpath = NULL; char *modname = NULL; @@ -138,20 +159,33 @@ void mod_insmod (flux_t h, uint32_t nodeid, int ac, char **av) if (!(modpath = flux_modfind (searchpath, modname))) msg_exit ("%s: not found in module search path", modname); } - if (flux_insmod (h, nodeid, modpath, ac - 1, av + 1) < 0) - err_exit ("%s", av[0]); + if (ns) { + if (flux_modctl_load (h, ns, modpath, ac - 1, av + 1) < 0) + err_exit ("%s", av[0]); + } else { + if (flux_insmod (h, nodeid, modpath, ac - 1, av + 1) < 0) + err_exit ("%s", av[0]); + } if (modpath) free (modpath); if (modname) free (modname); } -void mod_rmmod (flux_t h, uint32_t nodeid, int ac, char **av) +void mod_rmmod (flux_t h, uint32_t nodeid, const char *ns, int ac, char **av) { + char *modname = NULL; + if (ac != 1) usage (); - if (flux_rmmod (h, nodeid, av[0]) < 0) - err_exit ("%s", av[0]); + modname = av[0]; + if (ns) { + if (flux_modctl_unload (h, ns, modname) < 0) + err_exit ("modctl_unload %s", modname); + } else { + if (flux_rmmod (h, nodeid, modname) < 0) + err_exit ("%s", av[0]); + } } const char *snip (const char *s, int n) @@ -179,7 +213,7 @@ static int lsmod_cb (const char *name, int size, const char *digest, int idle, return 0; } -void mod_lsmod (flux_t h, uint32_t nodeid, int ac, char **av) +void mod_lsmod (flux_t h, uint32_t nodeid, const char *ns, int ac, char **av) { char *svc = "cmb"; @@ -189,8 +223,13 @@ void mod_lsmod (flux_t h, uint32_t nodeid, int ac, char **av) svc = av[0]; printf ("%-20s %6s %7s %4s %s\n", "Module", "Size", "Digest", "Idle", "Nodeset"); - if (flux_lsmod (h, nodeid, svc, lsmod_cb, NULL) < 0) - err_exit ("%s", svc); + if (ns) { + if (flux_modctl_list (h, svc, ns, lsmod_cb, NULL) < 0) + err_exit ("modctl_list"); + } else { + if (flux_lsmod (h, nodeid, svc, lsmod_cb, NULL) < 0) + err_exit ("%s", svc); + } } diff --git a/src/include/flux/core.h b/src/include/flux/core.h index 43a6a4ca1973..97b18114240e 100644 --- a/src/include/flux/core.h +++ b/src/include/flux/core.h @@ -9,7 +9,6 @@ #include "src/modules/api/api.h" #include "src/modules/kvs/kvs.h" #include "src/modules/live/live.h" -#include "src/modules/modctl/modctl.h" #include "src/modules/barrier/barrier.h" #endif /* FLUX_CORE_H */ diff --git a/src/lib/libcore/Makefile.am b/src/lib/libcore/Makefile.am index e8cce9711998..e91c2a0282dd 100644 --- a/src/lib/libcore/Makefile.am +++ b/src/lib/libcore/Makefile.am @@ -9,7 +9,6 @@ libflux_core_la_LIBADD = \ $(top_builddir)/src/modules/kvs/libkvs.la \ $(top_builddir)/src/modules/api/libapi.la \ $(top_builddir)/src/modules/live/liblive.la \ - $(top_builddir)/src/modules/modctl/libmodctl.la \ $(top_builddir)/src/modules/barrier/libbarrier.la \ $(top_builddir)/src/common/libflux/libflux.la \ $(top_builddir)/src/common/libutil/libutil.la \ diff --git a/src/lib/libcore/core.h b/src/lib/libcore/core.h index 1b992a067b5f..98a1a0f3d373 100644 --- a/src/lib/libcore/core.h +++ b/src/lib/libcore/core.h @@ -34,7 +34,6 @@ #include "core/api.h" #include "core/kvs.h" #include "core/live.h" -#include "core/modctl.h" #include "core/barrier.h" #endif /* !_FLUX_CORE_H */ diff --git a/src/modules/modctl/Makefile.am b/src/modules/modctl/Makefile.am index 7bc5ecca75d6..c7b0e2c68361 100644 --- a/src/modules/modctl/Makefile.am +++ b/src/modules/modctl/Makefile.am @@ -10,6 +10,7 @@ AM_CPPFLAGS = \ fluxmod_LTLIBRARIES = modctl.la fluxmod_libadd = \ + $(top_builddir)/src/common/libmrpc/libmrpc.la \ $(top_builddir)/src/modules/kvs/libkvs.la \ $(top_builddir)/src/common/libflux/libflux.la \ $(top_builddir)/src/common/libutil/libutil.la \ @@ -19,13 +20,43 @@ fluxmod_libadd = \ fluxmod_ldflags = -avoid-version -module -shared -export-dynamic \ -export-symbols-regex '^mod_(main|name)$$' -modctl_la_SOURCES = modctl.c +modctl_la_SOURCES = \ + modctl.c \ + proto.c \ + proto.h modctl_la_LDFLAGS = $(fluxmod_ldflags) modctl_la_LIBADD = $(fluxmod_libadd) # # API for module # -fluxcoreinclude_HEADERS = modctl.h +noinst_HEADERS = modctl.h noinst_LTLIBRARIES = libmodctl.la -libmodctl_la_SOURCES = libmodctl.c +libmodctl_la_SOURCES = libmodctl.c proto.c + +# +# Unit tests +# +TESTS = \ + test_proto.t + +test_ldadd = \ + $(top_builddir)/src/common/libutil/libutil.la \ + $(top_builddir)/src/common/libtap/libtap.la \ + $(top_builddir)/src/common/liblsd/liblsd.la \ + $(JSON_LIBS) $(LIBZMQ) $(LIBCZMQ) $(LIBPTHREAD) + +test_cppflags = \ + -DTEST_MAIN \ + -I$(top_srcdir)/src/common/libtap \ + $(AM_CPPFLAGS) + +check_PROGRAMS = $(TESTS) + +TEST_EXTENSIONS = .t +T_LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \ + $(top_srcdir)/config/tap-driver.sh + +test_proto_t_SOURCES = proto.c +test_proto_t_CPPFLAGS = $(test_cppflags) +test_proto_t_LDADD = $(test_ldadd) diff --git a/src/modules/modctl/libmodctl.c b/src/modules/modctl/libmodctl.c index a09991b4349a..b587f17b59d4 100644 --- a/src/modules/modctl/libmodctl.c +++ b/src/modules/modctl/libmodctl.c @@ -36,65 +36,280 @@ #include #include "src/common/libutil/log.h" +#include "src/common/libutil/xzmalloc.h" #include "src/common/libutil/shortjson.h" +#include "src/common/libutil/nodeset.h" +#include "src/common/libmrpc/mrpc.h" -int flux_modctl_rm (flux_t h, const char *name) +#include "modctl.h" +#include "proto.h" + +typedef struct { + char *name; + int size; + char *digest; + int idle; + nodeset_t nodeset; +} module_t; + +static void module_destroy (module_t *m) { - JSON request = Jnew (); - JSON response = NULL; - int rc = -1; + if (m->name) + free (m->name); + if (m->digest) + free (m->digest); + if (m->nodeset) + nodeset_destroy (m->nodeset); + free (m); +} + +static module_t *module_create (const char *name, int size, const char *digest, + int idle, uint32_t nodeid) +{ + module_t *m = xzmalloc (sizeof (*m)); - Jadd_str (request, "name", name); - if ((response = flux_rpc (h, request, "modctl.rm"))) { + m->name = xstrdup (name); + m->size = size; + m->digest = xstrdup (digest); + m->idle = idle; + if (!(m->nodeset = nodeset_new_rank (nodeid))) { + module_destroy (m); errno = EPROTO; + return NULL; + } + return m; +} + +static int module_update (module_t *m, int idle, uint32_t nodeid) +{ + if (idle < m->idle) + m->idle = idle; + if (!nodeset_add_rank (m->nodeset, nodeid)) { + errno = EPROTO; + return -1; + } + return 0; +} + +static int get_rlist_result (zhash_t *mods, flux_mrpc_t mrpc, + uint32_t nodeid, int *ep) +{ + JSON o = NULL; + int rc = -1; + const char *name, *digest; + int i, len, errnum, size, idle; + module_t *m; + + if (flux_mrpc_get_outarg (mrpc, nodeid, &o) < 0) goto done; + if (modctl_rlist_dec (o, &errnum, &len) < 0) + goto done; + for (i = 0; i < len; i++) { + if (modctl_rlist_dec_nth (o, i, &name, &size, &digest, &idle) < 0) + goto done; + if (!(m = zhash_lookup (mods, digest))) { + if (!(m = module_create (name, size, digest, idle, nodeid))) + goto done; + zhash_update (mods, digest, m); + zhash_freefn (mods, digest, (zhash_free_fn *)module_destroy); + } else if (module_update (m, idle, nodeid) < 0) + goto done; } - if (errno != 0) + *ep = errnum; + rc = 0; +done: + Jput (o); + return rc; +} + +static int cb_rlist_result (zhash_t *mods, flux_lsmod_f cb, void *arg) +{ + zlist_t *keys = zhash_keys (mods); + char *key; + int rc = -1; + + if (!keys) { + errno = ENOMEM; goto done; + } + key = zlist_first (keys); + while (key) { + module_t *m = zhash_lookup (mods, key); + if (m) { + const char *ns = nodeset_str (m->nodeset); + if (cb (m->name, m->size, m->digest, m->idle, ns, arg) < 0) + goto done; + } + key = zlist_next (keys); + } rc = 0; done: - Jput (request); - Jput (response); + zlist_destroy (&keys); return rc; } -int flux_modctl_ins (flux_t h, const char *name) +int flux_modctl_list (flux_t h, const char *svc, const char *nodeset, + flux_lsmod_f cb, void *arg) { - JSON request = Jnew (); - JSON response = NULL; + JSON in = NULL; int rc = -1; + uint32_t nodeid; + flux_mrpc_t mrpc = NULL; + int errnum = 0; + zhash_t *mods = NULL; - Jadd_str (request, "name", name); - if ((response = flux_rpc (h, request, "modctl.ins"))) { - errno = EPROTO; + if (!h || !cb) { + errno = EINVAL; + goto done; + } + if (!(mrpc = flux_mrpc_create (h, nodeset))) + goto done; + if (!(in = modctl_tlist_enc (svc))) + goto done; + flux_mrpc_put_inarg (mrpc, in); + if (flux_mrpc (mrpc, "modctl.list") < 0) goto done; + flux_mrpc_rewind_outarg (mrpc); + if (!(mods = zhash_new ())) + oom (); + while ((nodeid = flux_mrpc_next_outarg (mrpc)) != -1) { + if (get_rlist_result (mods, mrpc, nodeid, &errnum) < 0) + goto done; + if (errnum) + goto done; } - if (errno != 0) + if (cb_rlist_result (mods, cb, arg) < 0) goto done; rc = 0; done: - Jput (request); - Jput (response); + zhash_destroy (&mods); + if (mrpc) + flux_mrpc_destroy (mrpc); + Jput (in); + if (errnum) + errno = errnum; return rc; } -int flux_modctl_update (flux_t h) +static int get_rload_errnum (flux_mrpc_t mrpc, uint32_t nodeid, int *ep) { - JSON response = NULL; + JSON o = NULL; int rc = -1; + int errnum; - if ((response = flux_rpc (h, NULL, "modctl.update"))) { - errno = EPROTO; + if (flux_mrpc_get_outarg (mrpc, nodeid, &o) < 0) + goto done; + if (modctl_rload_dec (o, &errnum) < 0) + goto done; + *ep = errnum; + rc = 0; +done: + Jput (o); + return rc; +} + +int flux_modctl_load (flux_t h, const char *nodeset, const char *path, + int argc, char **argv) +{ + int rc = -1; + JSON in = NULL; + flux_mrpc_t mrpc = NULL; + uint32_t nodeid; + int errnum = 0; + + if (!h) { + errno = EINVAL; goto done; } - if (errno != 0) + if (!(mrpc = flux_mrpc_create (h, nodeset))) + goto done; + if (!(in = modctl_tload_enc (path, argc, argv))) goto done; + flux_mrpc_put_inarg (mrpc, in); + if (flux_mrpc (mrpc, "modctl.load") < 0) + goto done; + flux_mrpc_rewind_outarg (mrpc); + while ((nodeid = flux_mrpc_next_outarg (mrpc)) != -1) { + if (get_rload_errnum (mrpc, nodeid, &errnum) < 0) + goto done; + if (errnum) + goto done; + } rc = 0; done: - Jput (response); + if (mrpc) + flux_mrpc_destroy (mrpc); + Jput (in); + if (errnum) + errno = errnum; return rc; } +static int get_runload_errnum (flux_mrpc_t mrpc, uint32_t nodeid, int *ep) +{ + JSON o = NULL; + int rc = -1; + int errnum; + + if (flux_mrpc_get_outarg (mrpc, nodeid, &o) < 0) + goto done; + if (modctl_runload_dec (o, &errnum) < 0) + goto done; + *ep = errnum; + rc = 0; +done: + Jput (o); + return rc; +} + +int flux_modctl_unload (flux_t h, const char *nodeset, const char *name) +{ + flux_mrpc_t mrpc = NULL; + JSON in = NULL; + int rc = -1; + uint32_t nodeid; + int errnum = 0; + int successes = 0, missing = 0; + + if (!h) { + errno = EINVAL; + goto done; + } + if (!(mrpc = flux_mrpc_create (h, nodeset))) + goto done; + if (!(in = modctl_tunload_enc (name))) + goto done; + flux_mrpc_put_inarg (mrpc, in); + if (flux_mrpc (mrpc, "modctl.unload") < 0) + goto done; + flux_mrpc_rewind_outarg (mrpc); + while ((nodeid = flux_mrpc_next_outarg (mrpc)) != -1) { + if (get_runload_errnum (mrpc, nodeid, &errnum) < 0) + goto done; + if (errnum == 0) + successes++; + else if (errnum == ENOENT) + missing++; + else if (errnum) + goto done; + } + /* ignore ENOENT unless nothing worked + */ + if (successes == 0 && missing > 0) { + errnum = ENOENT; + goto done; + } + rc = 0; +done: + if (mrpc) + flux_mrpc_destroy (mrpc); + Jput (in); + if (rc < 0 && errnum > 0) + errno = errnum; + return rc; +} + + /* * vi:tabstop=4 shiftwidth=4 expandtab */ diff --git a/src/modules/modctl/modctl.c b/src/modules/modctl/modctl.c index 6a5a79f132b1..c27909608628 100644 --- a/src/modules/modctl/modctl.c +++ b/src/modules/modctl/modctl.c @@ -37,418 +37,190 @@ #include #include -#if 0 - #include "src/common/libutil/xzmalloc.h" #include "src/common/libutil/log.h" #include "src/common/libutil/shortjson.h" -#include "src/common/libutil/jsonutil.h" -#include "src/common/libutil/nodeset.h" -#include "src/common/libutil/readall.h" - -typedef struct { - flux_t h; - red_t r; - bool master; -} ctx_t; - -static const int red_timeout_msec_master = 20; -static const int red_timeout_msec_slave = 2; - -static void modctl_reduce (flux_t h, zlist_t *items, int batchnum, void *arg); -static void modctl_sink (flux_t h, void *item, int batchnum, void *arg); +#include "src/common/libmrpc/mrpc.h" -static void freectx (ctx_t *ctx) -{ - flux_red_destroy (ctx->r); - free (ctx); -} +#include "proto.h" -static ctx_t *getctx (flux_t h) +static int unload_mrpc_cb (flux_t h, int typemask, zmsg_t **zmsg, void *arg) { - ctx_t *ctx = (ctx_t *)flux_aux_get (h, "modctlsrv"); - - if (!ctx) { - ctx = xzmalloc (sizeof (*ctx)); - ctx->h = h; - ctx->master = flux_treeroot (h); - ctx->r = flux_red_create (h, modctl_sink, ctx); - if (ctx->master) - flux_red_set_timeout_msec (ctx->r, red_timeout_msec_master); - else - flux_red_set_timeout_msec (ctx->r, red_timeout_msec_slave); - flux_red_set_reduce_fn (ctx->r, modctl_reduce); - flux_red_set_flags (ctx->r, FLUX_RED_TIMEDFLUSH); - flux_aux_set (h, "modctlsrv", ctx, (FluxFreeFn)freectx); - } - return ctx; -} - -char *merge_nodelist (const char *a, const char *b) -{ - char *s = NULL; - nodeset_t ns; + JSON o = NULL; + flux_mrpc_t mrpc = NULL; + JSON in = NULL; + JSON out = NULL; + const char *modname; + int errnum = 0; + int rc = 0; - if (!(ns = nodeset_new_str (a))) - goto done; - if (!nodeset_add_str (ns, b)) + if (flux_json_event_decode (*zmsg, &o) < 0) { + flux_log (h, LOG_ERR, "%s: json_event_decode: %s", __FUNCTION__, + strerror (errno)); goto done; - s = xstrdup (nodeset_str (ns)); -done: - if (ns) - nodeset_destroy (ns); - return s; -} - -int merge_idle (int a, int b) -{ - return (a < b ? a : b); -} - -/* Merge b into a. - */ -static void merge_mods (JSON a, JSON b) -{ - json_object_iter iter; - JSON am; - char *s; - const char *anl, *bnl; - int ai, bi, i; - - json_object_object_foreachC (b, iter) { - - /* module in b is new to a */ - if (!Jget_obj (a, iter.key, &am)) { - Jadd_obj (a, iter.key, iter.val); - continue; - } - - /* merge nodelists */ - if (!Jget_str (iter.val, "nodelist", &bnl) - || !Jget_str (am, "nodelist", &anl)) - continue; - s = merge_nodelist (anl, bnl); - json_object_object_del (am, "nodelist"); - Jadd_str (am, "nodelist", s); - free (s); - - /* merge idle time */ - if (!Jget_int (iter.val, "idle", &bi) || !Jget_int (am, "idle", &ai)) - continue; - i = merge_idle (ai, bi); - json_object_object_del (am, "idle"); - Jadd_int (am, "idle", i); } -} - -static void modctl_reduce (flux_t h, zlist_t *items, int batchnum, void *arg) -{ - JSON a, b, amods, bmods; - - if ((a = zlist_pop (items))) { - while ((b = zlist_pop (items))) { - if (Jget_obj (a, "mods", &amods) && Jget_obj (b, "mods", &bmods)) - merge_mods (amods, bmods); - Jput (b); - } - zlist_append (items, a); - } -} - -static void modctl_sink (flux_t h, void *item, int batchnum, void *arg) -{ - ctx_t *ctx = arg; - JSON a = item; - JSON b = NULL, amods, bmods; - int seq; - - if (ctx->master) { /* sink to KVS */ - if (kvs_get (h, "conf.modctl.lsmod", &b) == 0) { - if (Jget_int (b, "seq", &seq) && seq == batchnum - && Jget_obj (a, "mods", &amods) - && Jget_obj (b, "mods", &bmods)) - merge_mods (amods, bmods); - Jput (b); - } - if (kvs_put (h, "conf.modctl.lsmod", a) < 0 || kvs_commit (h) < 0) { - flux_log (ctx->h, LOG_ERR, "%s: %s", __FUNCTION__,strerror (errno)); - } - } else { /* push pustream */ - flux_request_send (h, a, "modctl.push"); + if (!(mrpc = flux_mrpc_create_fromevent (h, o))) { + if (errno != EINVAL) /* EINVAL == not addressed to me */ + flux_log (h, LOG_ERR, "%s: flux_mrpc_create_fromevent: %s", + __FUNCTION__, strerror (errno)); + goto done; } - Jput (a); -} - -static int push_request_cb (flux_t h, int typemask, zmsg_t **zmsg, void *arg) -{ - ctx_t *ctx = arg; - JSON request = NULL; - int seq; - - if (flux_msg_decode (*zmsg, NULL, &request) < 0 || request == NULL - || !Jget_int (request, "seq", &seq)) { - flux_log (ctx->h, LOG_ERR, "%s: bad message", __FUNCTION__); + if (flux_mrpc_get_inarg (mrpc, &in) < 0) + errnum = errno; + else if (modctl_tunload_dec (in, &modname) < 0) + errnum = EPROTO; + else if (!strcmp (modname, "modctl") || !strcmp (modname, "kvs")) + errnum = EINVAL; /* unloading those two would be bad ! */ + else if (flux_rmmod (h, flux_rank (h), modname) < 0) + errnum = errno; + //flux_log (h, LOG_DEBUG, "%s: result %d", __FUNCTION__, errnum); + if ((out = modctl_runload_enc (errnum))) + flux_mrpc_put_outarg (mrpc, out); + else + flux_log (h, LOG_ERR, "%s: modctl_runload_enc: %s", + __FUNCTION__, strerror (errno)); + if (flux_mrpc_respond (mrpc) < 0) { + flux_log (h, LOG_ERR, "flux_mrpc_respond: %s", strerror (errno)); goto done; } - flux_red_append (ctx->r, Jget (request), seq); done: - Jput (request); + Jput (o); + Jput (in); + Jput (out); + if (mrpc) + flux_mrpc_destroy (mrpc); zmsg_destroy (zmsg); - return 0; -} - -static int lsmod_reduce (ctx_t *ctx, int seq) -{ - JSON o = NULL; - JSON lsmod = NULL; - int rc = -1; - - if (!(lsmod = flux_lsmod (ctx->h, -1, "cmb"))) - goto done; - o = Jnew (); - Jadd_int (o, "seq", seq); - Jadd_obj (o, "mods", lsmod); - flux_red_append (ctx->r, o, seq); /* takes ownership of 'o' */ -done: - Jput (lsmod); return rc; } -/* Install module out of KVS. - * KVS content is copied to a tmp file, dlopened, and immediately unlinked. - */ -static int installmod (ctx_t *ctx, const char *name) +static int load_mrpc_cb (flux_t h, int typemask, zmsg_t **zmsg, void *arg) { - char *key = NULL; - JSON mod = NULL, args; - uint8_t *buf = NULL; - int fd = -1, len; - char tmpfile[] = "/tmp/flux-modctl-XXXXXX"; /* FIXME: consider TMPDIR */ - int n, rc = -1; + JSON o = NULL; + flux_mrpc_t mrpc = NULL; + JSON in = NULL; + JSON out = NULL; + const char *path; + int argc = 0; + const char **argv = NULL; int errnum = 0; + int rc = 0; - if (asprintf (&key, "conf.modctl.modules.%s", name) < 0) - oom (); - if (kvs_get (ctx->h, key, &mod) < 0 || !Jget_obj (mod, "args", &args) - || util_json_object_get_data (mod, "data", &buf, &len) < 0) { - errnum = EPROTO; - goto done; /* kvs/parse error */ - } - if ((fd = mkstemp (tmpfile)) < 0) { - errnum = errno; + if (flux_json_event_decode (*zmsg, &o) < 0) { + flux_log (h, LOG_ERR, "%s: json_event_decode: %s", __FUNCTION__, + strerror (errno)); goto done; } - if (write_all (fd, buf, len) < 0) { - errnum = errno; + if (!(mrpc = flux_mrpc_create_fromevent (h, o))) { + if (errno != EINVAL) /* EINVAL == not addressed to me */ + flux_log (h, LOG_ERR, "%s: flux_mrpc_create_fromevent: %s", + __FUNCTION__, strerror (errno)); goto done; } - n = close (fd); - fd = -1; - if (n < 0) { + if (flux_mrpc_get_inarg (mrpc, &in) < 0) errnum = errno; - goto done; - } - if (flux_insmod (ctx->h, -1, tmpfile, FLUX_MOD_FLAGS_MANAGED, args) < 0) { + else if (modctl_tload_dec (in, &path, &argc, &argv) < 0) + errnum = EPROTO; + else if (flux_insmod (h, flux_rank (h), path, argc, (char **)argv) < 0) errnum = errno; - goto done_unlink; - } - rc = 0; -done_unlink: - (void)unlink (tmpfile); -done: - if (fd != -1) - (void)close (fd); - if (key) - free (key); - if (buf) - free (buf); - Jput (mod); - if (errnum != 0) - errno = errnum; - return rc; -} - -/* This function is called whenver conf.modctl.seq is updated by master. - * It syncs the set of loaded modules with the KVS. - */ -static int conf_cb (const char *path, int seq, void *arg, int errnum) -{ - ctx_t *ctx = arg; - kvsitr_t itr; - JSON mod, lsmod = NULL; - json_object_iter iter; - const char *name; - kvsdir_t dir; - - if (errnum == ENOENT) { - seq = 0; - goto done_lsmod; /* not initialized */ - } - if (errnum != 0) { - flux_log (ctx->h, LOG_ERR, "%s", "conf.modctl.seq"); + //flux_log (h, LOG_DEBUG, "%s: result %d", __FUNCTION__, errnum); + if ((out = modctl_rload_enc (errnum))) + flux_mrpc_put_outarg (mrpc, out); + else + flux_log (h, LOG_ERR, "%s: modctl_rload_enc: %s", + __FUNCTION__, strerror (errno)); + if (flux_mrpc_respond (mrpc) < 0) { + flux_log (h, LOG_ERR, "flux_mrpc_respond: %s", strerror (errno)); goto done; } - if (ctx->master) { /* master already loaded/unloaded module */ - goto done_lsmod; - } - if (!(lsmod = flux_lsmod (ctx->h, -1, "cmb"))) { - flux_log (ctx->h, LOG_ERR, "flux_lsmod: %s", strerror (errno)); - goto done; - } - /* Walk through list of modules that should be installed (from kvs), - * insmod-ing any that are not not. - */ - if (kvs_get_dir (ctx->h, &dir, "conf.modctl.modules") == 0) { - if (!(itr = kvsitr_create (dir))) - oom (); - while ((name = kvsitr_next (itr))) { - if (!Jget_obj (lsmod, name, &mod)) - if (installmod (ctx, name) < 0) - flux_log (ctx->h, LOG_ERR, "installmod %s: %s", name, - strerror (errno)); - } - kvsitr_destroy (itr); - } - /* Walk through the list of modules that are installed (from lsmod), - * rmmod-ing any that should not be. - */ - json_object_object_foreachC (lsmod, iter) { - int fl; - char *key; - if (!Jget_int (iter.val, "flags",&fl) || !(fl & FLUX_MOD_FLAGS_MANAGED)) - continue; - if (asprintf (&key, "conf.modctl.modules.%s", iter.key) < 0) - oom (); - if (kvs_get (ctx->h, key, &mod) < 0) { - if (flux_rmmod (ctx->h, -1, iter.key, FLUX_MOD_FLAGS_MANAGED) < 0) - flux_log (ctx->h, LOG_ERR, "flux_rmmod %s: %s", iter.key, - strerror (errno)); - } else - Jput (mod); - free (key); - } -done_lsmod: - /* Fetch (now modified) list of installed modules. - * Push it through the reduction network (ultimately to the KVS on master). - */ - lsmod_reduce (ctx, seq); done: - Jput (lsmod); - return 0; + Jput (o); + if (mrpc) + flux_mrpc_destroy (mrpc); + Jput (in); + if (argv) + free (argv); + Jput (out); + zmsg_destroy (zmsg); + return rc; } -static int seq_incr (flux_t h) +static int lsmod_cb (const char *name, int size, const char *digest, int idle, + const char *nodeset, void *arg) { - int seq = 0; - const char *key = "conf.modctl.seq"; - - (void)kvs_get_int (h, key, &seq); - if (kvs_put_int (h, key, ++seq) < 0 || kvs_commit (h) < 0) - return -1; - return 0; + JSON o = arg; + return modctl_rlist_enc_add (o, name, size, digest, idle); } -static int ins_request_cb (flux_t h, int typemask, zmsg_t **zmsg, void *arg) +static int list_mrpc_cb (flux_t h, int typemask, zmsg_t **zmsg, void *arg) { - ctx_t *ctx = arg; - JSON request = NULL; - const char *name; + JSON o = NULL; + flux_mrpc_t mrpc = NULL; + JSON in = NULL; + JSON out = NULL; + const char *svc; + int errnum = 0; int rc = 0; - if (flux_msg_decode (*zmsg, NULL, &request) < 0 || request == NULL - || !Jget_str (request, "name", &name)) { - flux_log (ctx->h, LOG_ERR, "%s: bad message", __FUNCTION__); + if (flux_json_event_decode (*zmsg, &o) < 0) { + flux_log (h, LOG_ERR, "%s: json_event_decode: %s", __FUNCTION__, + strerror (errno)); goto done; } - if (ctx->master) { - if (installmod (ctx, name) < 0 || seq_incr (h) < 0) { - flux_respond_errnum (h, zmsg, errno); - goto done; - } - flux_respond_errnum (h, zmsg, 0); - } else { - flux_request_sendmsg (h, zmsg); - } -done: - Jput (request); - return rc; -} - -static int rm_request_cb (flux_t h, int typemask, zmsg_t **zmsg, void *arg) -{ - ctx_t *ctx = arg; - JSON request = NULL; - const char *name; - int fl = FLUX_MOD_FLAGS_MANAGED; - int rc = 0; - - if (flux_msg_decode (*zmsg, NULL, &request) < 0 || request == NULL - || !Jget_str (request, "name", &name)) { - flux_log (ctx->h, LOG_ERR, "%s: bad message", __FUNCTION__); + if (!(mrpc = flux_mrpc_create_fromevent (h, o))) { + if (errno != EINVAL) /* EINVAL == not addressed to me */ + flux_log (h, LOG_ERR, "%s: flux_mrpc_create_fromevent: %s", + __FUNCTION__, strerror (errno)); goto done; } - if (ctx->master) { - if (flux_rmmod (ctx->h, -1, name, fl) < 0 || seq_incr (h) < 0) { - flux_respond_errnum (h, zmsg, errno); - goto done; - } - flux_respond_errnum (h, zmsg, 0); - } else { - flux_request_sendmsg (h, zmsg); - } -done: - Jput (request); - return rc; -} - -static int update_request_cb (flux_t h, int typemask, zmsg_t **zmsg, void *arg) -{ - ctx_t *ctx = arg; - JSON request = NULL; - int rc = 0; - - if (flux_msg_decode (*zmsg, NULL, &request) < 0) { - flux_log (ctx->h, LOG_ERR, "%s: bad message", __FUNCTION__); + if (flux_mrpc_get_inarg (mrpc, &in) < 0) + errnum = errno; + else if (modctl_tlist_dec (in, &svc) < 0) + errnum = EPROTO; + else if (!(out = modctl_rlist_enc ())) + errnum = errno; + else if (flux_lsmod (h, flux_rank (h), svc, lsmod_cb, out) < 0) + errnum = errno; + //flux_log (h, LOG_DEBUG, "%s: result %d", __FUNCTION__, errnum); + if (!out || modctl_rlist_enc_errnum (out, errnum) < 0) { + flux_log (h, LOG_ERR, "%s: modctl_rlist_enc: %s", + __FUNCTION__, strerror (errno)); + } else + flux_mrpc_put_outarg (mrpc, out); + if (flux_mrpc_respond (mrpc) < 0) { + flux_log (h, LOG_ERR, "flux_mrpc_respond: %s", strerror (errno)); goto done; } - if (ctx->master) { - if (seq_incr (h) < 0) { - flux_respond_errnum (h, zmsg, errno); - goto done; - } - flux_respond_errnum (h, zmsg, 0); - } else { - flux_request_sendmsg (h, zmsg); - } done: - Jput (request); + Jput (o); + if (mrpc) + flux_mrpc_destroy (mrpc); + Jput (in); + Jput (out); + zmsg_destroy (zmsg); return rc; } static msghandler_t htab[] = { - { FLUX_MSGTYPE_REQUEST, "modctl.push", push_request_cb }, - { FLUX_MSGTYPE_REQUEST, "modctl.ins", ins_request_cb }, - { FLUX_MSGTYPE_REQUEST, "modctl.rm", rm_request_cb }, - { FLUX_MSGTYPE_REQUEST, "modctl.update", update_request_cb }, + { FLUX_MSGTYPE_EVENT, "mrpc.modctl.unload", unload_mrpc_cb }, + { FLUX_MSGTYPE_EVENT, "mrpc.modctl.load", load_mrpc_cb }, + { FLUX_MSGTYPE_EVENT, "mrpc.modctl.list", list_mrpc_cb }, }; const int htablen = sizeof (htab) / sizeof (htab[0]); -#endif - int mod_main (flux_t h, zhash_t *args) { -#if 0 - ctx_t *ctx = getctx (h); - - if (kvs_watch_int (h, "conf.modctl.seq", conf_cb, ctx) < 0) { - flux_log (ctx->h, LOG_ERR, "kvs_watch_int: %s", strerror (errno)); + if (flux_msghandler_addvec (h, htab, htablen, NULL) < 0) { + flux_log (h, LOG_ERR, "flux_msghandler_add: %s", strerror (errno)); return -1; } - if (flux_msghandler_addvec (h, htab, htablen, ctx) < 0) { - flux_log (h, LOG_ERR, "flux_msghandler_add: %s", strerror (errno)); + if (flux_event_subscribe (h, "modctl.") < 0) { + flux_log (h, LOG_ERR, "flux_event_subscribe: %s", strerror (errno)); + return -1; + } + if (flux_event_subscribe (h, "mrpc.modctl.") < 0) { + flux_log (h, LOG_ERR, "flux_event_subscribe: %s", strerror (errno)); return -1; } -#endif if (flux_reactor_start (h) < 0) { flux_log (h, LOG_ERR, "flux_reactor_start: %s", strerror (errno)); return -1; diff --git a/src/modules/modctl/modctl.h b/src/modules/modctl/modctl.h index 871bc1a4bd70..2139892552cf 100644 --- a/src/modules/modctl/modctl.h +++ b/src/modules/modctl/modctl.h @@ -1,11 +1,14 @@ #ifndef _FLUX_CORE_MODCTL_H #define _FLUX_CORE_MODCTL_H +#include #include -int flux_modctl_rm (flux_t h, const char *name); -int flux_modctl_ins (flux_t h, const char *name); -int flux_modctl_update (flux_t h); +int flux_modctl_list (flux_t h, const char *svc, const char *nodeset, + flux_lsmod_f cb, void *arg); +int flux_modctl_load (flux_t h, const char *nodeset, + const char *path, int argc, char **argv); +int flux_modctl_unload (flux_t h, const char *nodeset, const char *name); #endif /* !_FLUX_CORE_MODCTL_H */ diff --git a/src/modules/modctl/proto.c b/src/modules/modctl/proto.c new file mode 100644 index 000000000000..b25cc7c6315c --- /dev/null +++ b/src/modules/modctl/proto.c @@ -0,0 +1,451 @@ +/*****************************************************************************\ + * Copyright (c) 2014 Lawrence Livermore National Security, LLC. Produced at + * the Lawrence Livermore National Laboratory (cf, AUTHORS, DISCLAIMER.LLNS). + * LLNL-CODE-658032 All rights reserved. + * + * This file is part of the Flux resource manager framework. + * For details, see https://github.com/flux-framework. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the license, or (at your option) + * any later version. + * + * Flux is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * See also: http://www.gnu.org/licenses/ +\*****************************************************************************/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/common/libutil/log.h" +#include "src/common/libutil/shortjson.h" +#include "src/common/libutil/xzmalloc.h" + +#include "proto.h" + +JSON modctl_tunload_enc (const char *name) +{ + JSON o = NULL; + + if (!name) { + errno = EINVAL; + goto done; + } + o = Jnew (); + Jadd_str (o, "name", name); +done: + return o; +} + +int modctl_tunload_dec (JSON o, const char **name) +{ + int rc = -1; + + if (!o || !name) { + errno = EINVAL; + goto done; + } + if (!Jget_str (o, "name", name)) { + errno = EPROTO; + goto done; + } + rc = 0; +done: + return rc; +} + +JSON modctl_runload_enc (int errnum) +{ + JSON o = NULL; + + o = Jnew (); + Jadd_int (o, "errnum", errnum); + return o; +} + +int modctl_runload_dec (JSON o, int *errnum) +{ + int rc = -1; + + if (!o || !errnum) { + errno = EINVAL; + goto done; + } + if (!Jget_int (o, "errnum", errnum)) { + errno = EPROTO; + goto done; + } + rc = 0; +done: + return rc; +} + + +JSON modctl_tload_enc (const char *path, int argc, char **argv) +{ + JSON args, o = NULL; + int i; + + if (!path) { + errno = EINVAL; + goto done; + } + for (i = 0; i < argc; i++) { + if (argv[i] == NULL) { + errno = EINVAL; + goto done; + } + } + o = Jnew (); + Jadd_str (o, "path", path); + args = Jnew_ar (); + for (i = 0; i < argc; i++) + Jadd_ar_str (args, argv[i]); + Jadd_obj (o, "args", args); +done: + return o; +} + +int modctl_tload_dec (JSON o, const char **path, int *argc, const char ***argv) +{ + JSON args = NULL; + int i, ac, rc = -1; + const char **av; + + if (!o || !path || !argc || !argv) { + errno = EINVAL; + goto done; + } + if (!Jget_str (o, "path", path) || !Jget_obj (o, "args", &args) + || !Jget_ar_len (args, &ac)) { + errno = EPROTO; + goto done; + } + av = xzmalloc (sizeof (av[0]) * ac); + for (i = 0; i < ac; i++) { + if (!Jget_ar_str (args, i, &av[i])) { + free (av); + errno = EPROTO; + goto done; + } + } + *argc = ac; + *argv = av; + rc = 0; +done: + return rc; +} + +JSON modctl_rload_enc (int errnum) +{ + JSON o = Jnew (); + Jadd_int (o, "errnum", errnum); + return o; +} + +int modctl_rload_dec (JSON o, int *errnum) +{ + int rc = -1; + + if (!o || !errnum) { + errno = EINVAL; + goto done; + } + if (!Jget_int (o, "errnum", errnum)) { + errno = EPROTO; + goto done; + } + rc = 0; +done: + return rc; +} + +JSON modctl_tlist_enc (const char *svc) +{ + JSON o = NULL; + + if (!svc) { + errno = EINVAL; + goto done; + } + o = Jnew (); + Jadd_str (o, "service", svc); +done: + return o; +} + +int modctl_tlist_dec (JSON o, const char **svc) +{ + int rc = -1; + + if (!o || !svc) { + errno = EINVAL; + goto done; + } + if (!Jget_str (o, "service", svc)) { + errno = EPROTO; + goto done; + } + rc = 0; +done: + return rc; +} + +JSON modctl_rlist_enc (void) +{ + JSON mods, o = Jnew (); + mods = Jnew_ar (); + Jadd_obj (o, "modules", mods); + return o; +} + +int modctl_rlist_enc_add (JSON o, const char *name, int size, + const char *digest, int idle) +{ + JSON mods, mod; + int rc = -1; + + if (!o || !name || !digest || size < 0 || idle < 0) { + errno = EINVAL; + goto done; + } + if (!Jget_obj (o, "modules", &mods)) { /* does not take ref on 'mods' */ + errno = EINVAL; + goto done; + } + mod = Jnew (); + Jadd_str (mod, "name", name); + Jadd_int (mod, "size", size); + Jadd_str (mod, "digest", digest); + Jadd_int (mod, "idle", idle); + Jadd_ar_obj (mods, mod); /* takes ref on mod */ + Jput (mod); + rc = 0; +done: + return rc; +} + +int modctl_rlist_enc_errnum (JSON o, int errnum) +{ + Jadd_int (o, "errnum", errnum); + return 0; +} + +int modctl_rlist_dec (JSON o, int *errnum, int *len) +{ + int rc = -1; + JSON mods; + + if (!o || !errnum || !len) { + errno = EINVAL; + goto done; + } + if (!Jget_int (o, "errnum", errnum) || !Jget_obj (o, "modules", &mods) + || !Jget_ar_len (mods, len)) { + errno = EPROTO; + goto done; + } + rc = 0; +done: + return rc; +} + +int modctl_rlist_dec_nth (JSON o, int n, const char **name, + int *size, const char **digest, int *idle) +{ + JSON el, mods; + int rc = -1; + int len; + + if (!o || !name || !size || !digest || !idle) { + errno = EINVAL; + goto done; + } + if (!Jget_obj (o, "modules", &mods) || !Jget_ar_len (mods, &len)) { + errno = EPROTO; + goto done; + } + if (n < 0 || n > len) { + errno = EINVAL; + goto done; + } + if (!Jget_ar_obj (mods, n, &el)) { /* does not take ref on el */ + errno = EPROTO; + goto done; + } + if (!Jget_str (el, "name", name) || !Jget_int (el, "size", size) + || !Jget_str (el, "digest", digest) || !Jget_int (el, "idle", idle)) { + errno = EPROTO; + goto done; + } + rc = 0; +done: + return rc; +} + +#ifdef TEST_MAIN +#include "src/common/libtap/tap.h" + +void test_tload (void) +{ + JSON o; + char *av[] = { "a", "b", "c" }; + const char *path; + int argc; + const char **argv; + + o = modctl_tload_enc ("/foo/bar.so", 3, av); + ok (o != NULL, + "modctl_tload_enc works"); + ok (modctl_tload_dec (o, &path, &argc, &argv) == 0 && path && argc == 3, + "modctl_tload_dec works"); + like (path, "^/foo/bar.so$", + "modctl_tload_dec returned encoded path"); + like (argv[0], "^a$", + "modctl_tload_dec returned encoded argv[0]"); + like (argv[1], "^b$", + "modctl_tload_dec returned encoded argv[1]"); + like (argv[2], "^c$", + "modctl_tload_dec returned encoded argv[2]"); + free (argv); + Jput (o); +} + +void test_rload (void) +{ + JSON o; + int errnum; + + o = modctl_rload_enc (42); + ok (o != NULL, + "modctl_rload_enc works"); + ok (modctl_rload_dec (o, &errnum) == 0, + "modctl_rload_dec works"); + ok (errnum == 42, + "modctl_rload_dec returns encoded errnum"); + Jput (o); + +} + +void test_tunload (void) +{ + JSON o; + const char *name = NULL; + + o = modctl_tunload_enc ("bar"); + ok (o != NULL, + "modctl_tunload_enc works"); + ok (modctl_tunload_dec (o, &name) == 0 && name, + "modctl_tunload_dec works"); + like (name, "^bar$", + "modctl_tunload_dec returned encoded module name"); + Jput (o); +} + +void test_runload (void) +{ + JSON o; + int errnum; + + o = modctl_runload_enc (42); + ok (o != NULL, + "modctl_runload_enc works"); + ok (modctl_runload_dec (o, &errnum) == 0, + "modctl_runload_dec works"); + ok (errnum == 42, + "modctl_runload_dec returns encoded errnum"); + Jput (o); + +} + +void test_tlist (void) +{ + JSON o; + const char *svc; + + o = modctl_tlist_enc ("foo"); + ok (o != NULL, + "modctl_tlist_enc works"); + ok (modctl_tlist_dec (o, &svc) == 0 && svc, + "modctl_tlist_dec works"); + like (svc, "^foo$", + "modctl_tlist_dec returned encoded service"); + Jput (o); +} + +void test_rlist (void) +{ + JSON o; + const char *name, *digest; + int len, errnum, size, idle; + + o = modctl_rlist_enc (); + ok (o != NULL, + "modctl_rlist_enc works"); + ok (modctl_rlist_enc_add (o, "foo", 42, "abba", 6) == 0, + "modctl_rlist_enc_add works 0th time"); + ok (modctl_rlist_enc_add (o, "bar", 69, "argh", 19) == 0, + "modctl_rlist_enc_add works 1st time"); + ok (modctl_rlist_enc_errnum (o, 0) == 0, + "modctl_rlist_enc_errnum works"); + ok (modctl_rlist_dec (o, &errnum, &len) == 0 && errnum == 0 && len == 2, + "modctl_rlist_dec works"); + ok (modctl_rlist_dec_nth (o, 0, &name, &size, &digest, &idle) == 0 + && name && size == 42 && digest && idle == 6, + "modctl_rlist_dec_nth(0) works and returns correct scalar vals"); + like (name, "^foo$", + "modctl_rlist_dec_nth(0) returns encoded name"); + like (digest, "^abba$", + "modctl_rlist_dec_nth(0) returns encoded digest"); + ok (modctl_rlist_dec_nth (o, 1, &name, &size, &digest, &idle) == 0 + && name && size == 69 && digest && idle == 19, + "modctl_rlist_dec_nth(1) works and returns correct scalar vals"); + like (name, "^bar$", + "modctl_rlist_dec_nth(1) returns encoded name"); + like (digest, "^argh$", + "modctl_rlist_dec_nth(1) returns encoded digest"); + + Jput (o); +} + +int main (int argc, char *argv[]) +{ + + plan (29); + + test_tunload (); // 3 + test_runload (); // 3 + + test_tload (); // 6 + test_rload (); // 3 + + test_tlist (); // 3 + test_rlist (); // 11 + + done_testing (); + return (0); +} + +#endif /* TEST_MAIN */ + +/* + * vi:tabstop=4 shiftwidth=4 expandtab + */ diff --git a/src/modules/modctl/proto.h b/src/modules/modctl/proto.h new file mode 100644 index 000000000000..40369f6bca68 --- /dev/null +++ b/src/modules/modctl/proto.h @@ -0,0 +1,33 @@ +/* N.B. Decode functions return pointers to storage owned by the json object, + * except modctl_tload_dec() which returns argv that must be freed + * (but its elements are owned by the json object and should not be freed). + */ + +json_object *modctl_tunload_enc (const char *name); +int modctl_tunload_dec (json_object *o, const char **name); + +json_object *modctl_runload_enc (int errnum); +int modctl_runload_dec (json_object *o, int *errnum); + +json_object *modctl_tload_enc (const char *path, int argc, char **argv); +int modctl_tload_dec (json_object *o, const char **path, + int *argc, const char ***argv); + +json_object *modctl_rload_enc (int errnum); +int modctl_rload_dec (json_object *o, int *errnum); + +json_object *modctl_tlist_enc (const char *svc); +int modctl_tlist_dec (json_object *o, const char **svc); + +json_object *modctl_rlist_enc (void); +int modctl_rlist_enc_add (json_object *o, const char *name, int size, + const char *digest, int idle); +int modctl_rlist_enc_errnum (json_object *o, int errnum); + +int modctl_rlist_dec (json_object *o, int *errnum, int *len); +int modctl_rlist_dec_nth (json_object *o, int n, const char **name, + int *size, const char **digest, int *idle); + +/* + * vi:tabstop=4 shiftwidth=4 expandtab + */