Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'agent/tcf/services/registers.c')
-rw-r--r--agent/tcf/services/registers.c664
1 files changed, 664 insertions, 0 deletions
diff --git a/agent/tcf/services/registers.c b/agent/tcf/services/registers.c
new file mode 100644
index 00000000..1bb049d8
--- /dev/null
+++ b/agent/tcf/services/registers.c
@@ -0,0 +1,664 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+
+/*
+ * TCF Registers - CPU registers access service.
+ */
+
+#include <config.h>
+
+#if SERVICE_Registers
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <framework/myalloc.h>
+#include <framework/protocol.h>
+#include <framework/context.h>
+#include <framework/json.h>
+#include <framework/cache.h>
+#include <framework/exceptions.h>
+#include <services/stacktrace.h>
+#include <services/registers.h>
+
+static const char * REGISTERS = "Registers";
+
+static TCFBroadcastGroup * broadcast_group = NULL;
+
+typedef struct Listener {
+ RegistersEventListener * func;
+ void * args;
+} Listener;
+
+static Listener * listeners = NULL;
+static unsigned listener_cnt = 0;
+static unsigned listener_max = 0;
+
+static uint8_t * bbf = NULL;
+static unsigned bbf_pos = 0;
+static unsigned bbf_len = 0;
+
+static void write_boolean_member(OutputStream * out, const char * name, int val) {
+ /* For this service FALSE is same as absence of the member */
+ if (!val) return;
+ write_stream(out, ',');
+ json_write_string(out, name);
+ write_stream(out, ':');
+ json_write_boolean(out, 1);
+}
+
+static void write_context(OutputStream * out, char * id,
+ Context * ctx, int frame, RegisterDefinition * reg_def) {
+ assert(!ctx->exited);
+
+ write_stream(out, '{');
+
+ json_write_string(out, "ID");
+ write_stream(out, ':');
+ json_write_string(out, id);
+
+ write_stream(out, ',');
+ json_write_string(out, "ParentID");
+ write_stream(out, ':');
+ if (reg_def->parent != NULL) {
+ json_write_string(out, register2id(ctx, frame, reg_def->parent));
+ }
+ else if (frame < 0 || is_top_frame(ctx, frame)) {
+ json_write_string(out, ctx->id);
+ }
+ else {
+ json_write_string(out, frame2id(ctx, frame));
+ }
+
+ write_stream(out, ',');
+ json_write_string(out, "ProcessID");
+ write_stream(out, ':');
+ json_write_string(out, context_get_group(ctx, CONTEXT_GROUP_PROCESS)->id);
+
+ write_stream(out, ',');
+ json_write_string(out, "Name");
+ write_stream(out, ':');
+ json_write_string(out, reg_def->name);
+
+ write_stream(out, ',');
+ json_write_string(out, "Size");
+ write_stream(out, ':');
+ json_write_long(out, reg_def->size);
+
+ if (reg_def->dwarf_id >= 0) {
+ write_stream(out, ',');
+ json_write_string(out, "DwarfID");
+ write_stream(out, ':');
+ json_write_long(out, reg_def->dwarf_id);
+ }
+
+ if (reg_def->eh_frame_id >= 0) {
+ write_stream(out, ',');
+ json_write_string(out, "EhFrameID");
+ write_stream(out, ':');
+ json_write_long(out, reg_def->eh_frame_id);
+ }
+
+ write_boolean_member(out, "BigEndian", reg_def->big_endian);
+ write_boolean_member(out, "Float", reg_def->fp_value);
+ write_boolean_member(out, "Readable", !reg_def->no_read);
+ write_boolean_member(out, "Writeable", !reg_def->no_write);
+ write_boolean_member(out, "ReadOnce", reg_def->read_once);
+ write_boolean_member(out, "WriteOnce", reg_def->write_once);
+ write_boolean_member(out, "Volatile", reg_def->volatile_value);
+ write_boolean_member(out, "SideEffects", reg_def->side_effects);
+ write_boolean_member(out, "LeftToRight", reg_def->left_to_right);
+
+ if (reg_def->first_bit > 0) {
+ write_stream(out, ',');
+ json_write_string(out, "FirstBit");
+ write_stream(out, ':');
+ json_write_long(out, reg_def->first_bit);
+ }
+
+ if (reg_def->bits != NULL) {
+ int i = 0;
+ write_stream(out, ',');
+ json_write_string(out, "Bits");
+ write_stream(out, ':');
+ write_stream(out, '[');
+ while (reg_def->bits[i] >= 0) {
+ if (i > 0) write_stream(out, ',');
+ json_write_long(out, reg_def->bits[i++]);
+ }
+ write_stream(out, ']');
+ }
+
+ if (reg_def->values != NULL) {
+ int i = 0;
+ write_stream(out, ',');
+ json_write_string(out, "Values");
+ write_stream(out, ':');
+ write_stream(out, '[');
+ while (reg_def->values[i] != NULL) {
+ NamedRegisterValue * v = reg_def->values[i++];
+ if (i > 0) write_stream(out, ',');
+ write_stream(out, '(');
+ json_write_string(out, "Value");
+ write_stream(out, ':');
+ json_write_binary(out, v->value, reg_def->size);
+ if (v->name != NULL) {
+ write_stream(out, ',');
+ json_write_string(out, "Name");
+ write_stream(out, ':');
+ json_write_string(out, v->name);
+ }
+ if (v->description != NULL) {
+ write_stream(out, ',');
+ json_write_string(out, "Description");
+ write_stream(out, ':');
+ json_write_string(out, v->description);
+ }
+ write_stream(out, '}');
+ }
+ write_stream(out, ']');
+ }
+
+ if (reg_def->memory_address > 0) {
+ write_stream(out, ',');
+ json_write_string(out, "MemoryAddress");
+ write_stream(out, ':');
+ json_write_uint64(out, reg_def->memory_address);
+ }
+
+ if (reg_def->memory_context != NULL) {
+ write_stream(out, ',');
+ json_write_string(out, "MemoryContext");
+ write_stream(out, ':');
+ json_write_string(out, reg_def->memory_context);
+ }
+
+ if (reg_def->role != NULL) {
+ write_stream(out, ',');
+ json_write_string(out, "Role");
+ write_stream(out, ':');
+ json_write_string(out, reg_def->role);
+ }
+ else if (reg_def == get_PC_definition(ctx)) {
+ write_stream(out, ',');
+ json_write_string(out, "Role");
+ write_stream(out, ':');
+ json_write_string(out, "PC");
+ }
+
+ write_stream(out, '}');
+ write_stream(out, 0);
+}
+
+typedef struct GetContextArgs {
+ char token[256];
+ char id[256];
+} GetContextArgs;
+
+static void command_get_context_cache_client(void * x) {
+ GetContextArgs * args = (GetContextArgs *)x;
+ Channel * c = cache_channel();
+ Context * ctx = NULL;
+ int frame = STACK_NO_FRAME;
+ RegisterDefinition * reg_def = NULL;
+ Trap trap;
+
+ if (set_trap(&trap)) {
+ if (id2register(args->id, &ctx, &frame, &reg_def) < 0) exception(errno);
+ clear_trap(&trap);
+ }
+
+ cache_exit();
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, args->token);
+ write_errno(&c->out, trap.error);
+ if (reg_def != NULL) {
+ write_context(&c->out, args->id, ctx, frame, reg_def);
+ }
+ else {
+ write_stringz(&c->out, "null");
+ }
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_get_context(char * token, Channel * c) {
+ GetContextArgs args;
+
+ json_read_string(&c->inp, args.id, sizeof(args.id));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ strlcpy(args.token, token, sizeof(args.token));
+ cache_enter(command_get_context_cache_client, c, &args, sizeof(args));
+}
+
+typedef struct GetChildrenArgs {
+ char token[256];
+ char id[256];
+} GetChildrenArgs;
+
+static void command_get_children_cache_client(void * x) {
+ GetChildrenArgs * args = (GetChildrenArgs *)x;
+ Channel * c = cache_channel();
+ Context * ctx = NULL;
+ int frame = STACK_NO_FRAME;
+ StackFrame * frame_info = NULL;
+ RegisterDefinition * defs = NULL;
+ RegisterDefinition * parent = NULL;
+ Trap trap;
+
+ if (set_trap(&trap)) {
+ if (id2register(args->id, &ctx, &frame, &parent) == 0) {
+ if (frame != STACK_TOP_FRAME && get_frame_info(ctx, frame, &frame_info) < 0) exception(errno);
+ }
+ else if (id2frame(args->id, &ctx, &frame) == 0) {
+ if (get_frame_info(ctx, frame, &frame_info) < 0) exception(errno);
+ }
+ else {
+ ctx = id2ctx(args->id);
+ frame = STACK_TOP_FRAME;
+ }
+ if (ctx != NULL) defs = get_reg_definitions(ctx);
+ clear_trap(&trap);
+ }
+
+ cache_exit();
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, args->token);
+
+ write_errno(&c->out, trap.error);
+
+ write_stream(&c->out, '[');
+ if (defs != NULL) {
+ int cnt = 0;
+ RegisterDefinition * reg_def;
+ for (reg_def = defs; reg_def->name != NULL; reg_def++) {
+ if (reg_def->parent != parent) continue;
+ if (frame == STACK_TOP_FRAME || frame_info->is_top_frame ||
+ reg_def->size == 0 || read_reg_value(frame_info, reg_def, NULL) == 0) {
+ if (cnt > 0) write_stream(&c->out, ',');
+ json_write_string(&c->out, register2id(ctx, frame, reg_def));
+ cnt++;
+ }
+ }
+ }
+ write_stream(&c->out, ']');
+ write_stream(&c->out, 0);
+
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_get_children(char * token, Channel * c) {
+ GetChildrenArgs args;
+
+ json_read_string(&c->inp, args.id, sizeof(args.id));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ strlcpy(args.token, token, sizeof(args.token));
+ cache_enter(command_get_children_cache_client, c, &args, sizeof(args));
+}
+
+void send_event_register_changed(const char * id) {
+ unsigned i;
+ Context * ctx = NULL;
+ int frame = STACK_NO_FRAME;
+ RegisterDefinition * def = NULL;
+ OutputStream * out = &broadcast_group->out;
+
+ id2register(id, &ctx, &frame, &def);
+
+ for (i = 0; i < listener_cnt; i++) {
+ Listener * l = listeners + i;
+ if (l->func->register_changed == NULL) continue;
+ l->func->register_changed(ctx, frame, def, l->args);
+ }
+
+ write_stringz(out, "E");
+ write_stringz(out, REGISTERS);
+ write_stringz(out, "registerChanged");
+
+ json_write_string(out, id);
+ write_stream(out, 0);
+
+ write_stream(out, MARKER_EOM);
+}
+
+typedef struct GetArgs {
+ char token[256];
+ char id[256];
+} GetArgs;
+
+static void command_get_cache_client(void * x) {
+ GetArgs * args = (GetArgs *)x;
+ Channel * c = cache_channel();
+ Trap trap;
+
+ bbf_pos = 0;
+ if (set_trap(&trap)) {
+ int frame = 0;
+ Context * ctx = NULL;
+ RegisterDefinition * reg_def = NULL;
+
+ if (id2register(args->id, &ctx, &frame, &reg_def) < 0) exception(errno);
+ if (!context_has_state(ctx)) exception(ERR_INV_CONTEXT);
+ if (ctx->exited) exception(ERR_ALREADY_EXITED);
+ if (!ctx->stopped) exception(ERR_IS_RUNNING);
+
+ if (reg_def->size > bbf_len) {
+ bbf_len += 0x100 + reg_def->size;
+ bbf = (uint8_t *)loc_realloc(bbf, bbf_len);
+ }
+
+ bbf_pos = reg_def->size;
+ if (is_top_frame(ctx, frame)) {
+ if (context_read_reg(ctx, reg_def, 0, reg_def->size, bbf) < 0) exception(errno);
+ }
+ else {
+ StackFrame * info = NULL;
+ if (get_frame_info(ctx, frame, &info) < 0) exception(errno);
+ if (read_reg_bytes(info, reg_def, 0, reg_def->size, bbf) < 0) exception(errno);
+ }
+
+ clear_trap(&trap);
+ }
+
+ cache_exit();
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, args->token);
+ write_errno(&c->out, trap.error);
+ json_write_binary(&c->out, bbf, bbf_pos);
+ write_stream(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_get(char * token, Channel * c) {
+ GetArgs args;
+
+ json_read_string(&c->inp, args.id, sizeof(args.id));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ strlcpy(args.token, token, sizeof(args.token));
+ cache_enter(command_get_cache_client, c, &args, sizeof(args));
+}
+
+typedef struct SetArgs {
+ char token[256];
+ char id[256];
+ size_t data_len;
+ uint8_t * data;
+} SetArgs;
+
+static void command_set_cache_client(void * x) {
+ SetArgs * args = (SetArgs *)x;
+ Channel * c = cache_channel();
+ Trap trap;
+
+ if (set_trap(&trap)) {
+ int frame = 0;
+ Context * ctx = NULL;
+ RegisterDefinition * reg_def = NULL;
+
+ if (id2register(args->id, &ctx, &frame, &reg_def) < 0) exception(errno);
+ if (!is_top_frame(ctx, frame)) exception(ERR_INV_CONTEXT);
+ if (ctx->exited) exception(ERR_ALREADY_EXITED);
+ if (!ctx->stopped) exception(ERR_IS_RUNNING);
+ if ((size_t)args->data_len > reg_def->size) exception(ERR_INV_DATA_SIZE);
+ if (args->data_len > 0) {
+ if (context_write_reg(ctx, reg_def, 0, args->data_len, args->data) < 0) exception(errno);
+ send_event_register_changed(args->id);
+ }
+ clear_trap(&trap);
+ }
+
+ cache_exit();
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, args->token);
+ write_errno(&c->out, trap.error);
+ write_stream(&c->out, MARKER_EOM);
+
+ loc_free(args->data);
+}
+
+static void command_set(char * token, Channel * c) {
+ SetArgs args;
+
+ json_read_string(&c->inp, args.id, sizeof(args.id));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ args.data = (uint8_t *)json_read_alloc_binary(&c->inp, &args.data_len);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ strlcpy(args.token, token, sizeof(args.token));
+ cache_enter(command_set_cache_client, c, &args, sizeof(args));
+}
+
+typedef struct Location {
+ char id[256];
+ Context * ctx;
+ int frame;
+ StackFrame * frame_info;
+ RegisterDefinition * reg_def;
+ unsigned offs;
+ unsigned size;
+} Location;
+
+static Location * buf = NULL;
+static unsigned buf_pos = 0;
+static unsigned buf_len = 0;
+
+static void read_location(InputStream * inp, void * args) {
+ int ch = read_stream(inp);
+ Location * loc = NULL;
+ if (ch != '[') exception(ERR_JSON_SYNTAX);
+ if (buf_pos >= buf_len) {
+ buf_len = buf_len == 0 ? 0x10 : buf_len * 2;
+ buf = (Location *)loc_realloc(buf, buf_len * sizeof(Location));
+ }
+ loc = buf + buf_pos++;
+ memset(loc, 0, sizeof(Location));
+ json_read_string(inp, loc->id, sizeof(loc->id));
+ if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX);
+ loc->offs = json_read_ulong(inp);
+ if (read_stream(inp) != ',') exception(ERR_JSON_SYNTAX);
+ loc->size = json_read_ulong(inp);
+ if (read_stream(inp) != ']') exception(ERR_JSON_SYNTAX);
+}
+
+static Location * read_location_list(InputStream * inp, unsigned * cnt) {
+ Location * locs = NULL;
+
+ buf_pos = 0;
+ json_read_array(inp, read_location, NULL);
+ locs = (Location *)loc_alloc(buf_pos * sizeof(Location));
+ memcpy(locs, buf, buf_pos * sizeof(Location));
+ *cnt = buf_pos;
+ return locs;
+}
+
+static void check_location_list(Location * locs, unsigned cnt, int setm) {
+ unsigned pos;
+ for (pos = 0; pos < cnt; pos++) {
+ Location * loc = locs + pos;
+
+ if (id2register(loc->id, &loc->ctx, &loc->frame, &loc->reg_def) < 0) exception(errno);
+ if (!context_has_state(loc->ctx)) exception(ERR_INV_CONTEXT);
+ if (loc->ctx->exited) exception(ERR_ALREADY_EXITED);
+ if (!loc->ctx->stopped) exception(ERR_IS_RUNNING);
+ if (loc->offs + loc->size > loc->reg_def->size) exception(ERR_INV_DATA_SIZE);
+
+ if (is_top_frame(loc->ctx, loc->frame)) continue;
+
+ if (setm) exception(ERR_INV_CONTEXT);
+ if (get_frame_info(loc->ctx, loc->frame, &loc->frame_info) < 0) exception(errno);
+ }
+}
+
+typedef struct GetmArgs {
+ char token[256];
+ unsigned locs_cnt;
+ Location * locs;
+} GetmArgs;
+
+static void command_getm_cache_client(void * x) {
+ GetmArgs * args = (GetmArgs *)x;
+ Channel * c = cache_channel();
+ Trap trap;
+
+ bbf_pos = 0;
+ if (set_trap(&trap)) {
+ unsigned locs_pos = 0;
+ check_location_list(args->locs, args->locs_cnt, 0);
+ while (locs_pos < args->locs_cnt) {
+ Location * l = args->locs + locs_pos++;
+ if (bbf_pos + l->size > bbf_len) {
+ bbf_len += 0x100 + l->size;
+ bbf = (uint8_t *)loc_realloc(bbf, bbf_len);
+ }
+ if (l->frame_info == NULL) {
+ if (context_read_reg(l->ctx, l->reg_def, l->offs, l->size, bbf + bbf_pos) < 0) exception(errno);
+ }
+ else {
+ if (read_reg_bytes(l->frame_info, l->reg_def, l->offs, l->size, bbf + bbf_pos) < 0) exception(errno);
+ }
+ bbf_pos += l->size;
+ }
+ clear_trap(&trap);
+ }
+
+ cache_exit();
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, args->token);
+ write_errno(&c->out, trap.error);
+ json_write_binary(&c->out, bbf, bbf_pos);
+ write_stream(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+
+ loc_free(args->locs);
+}
+
+static void command_getm(char * token, Channel * c) {
+ GetmArgs args;
+
+ args.locs = read_location_list(&c->inp, &args.locs_cnt);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ strlcpy(args.token, token, sizeof(args.token));
+ cache_enter(command_getm_cache_client, c, &args, sizeof(args));
+}
+
+typedef struct SetmArgs {
+ char token[256];
+ unsigned locs_cnt;
+ Location * locs;
+ size_t data_len;
+ uint8_t * data;
+} SetmArgs;
+
+static void command_setm_cache_client(void * x) {
+ SetmArgs * args = (SetmArgs *)x;
+ Channel * c = cache_channel();
+ Trap trap;
+
+ if (set_trap(&trap)) {
+ unsigned locs_pos = 0;
+ unsigned data_pos = 0;
+ check_location_list(args->locs, args->locs_cnt, 1);
+ while (locs_pos < args->locs_cnt) {
+ Location * l = args->locs + locs_pos++;
+ assert(l->frame_info == NULL);
+ if (l->size > 0) {
+ if (context_write_reg(l->ctx, l->reg_def, l->offs, l->size, args->data + data_pos) < 0) exception(errno);
+ data_pos += l->size;
+ send_event_register_changed(l->id);
+ }
+ }
+ clear_trap(&trap);
+ }
+
+ cache_exit();
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, args->token);
+ write_errno(&c->out, trap.error);
+ write_stream(&c->out, MARKER_EOM);
+
+ loc_free(args->locs);
+ loc_free(args->data);
+}
+
+static void command_setm(char * token, Channel * c) {
+ SetmArgs args;
+
+ args.locs = read_location_list(&c->inp, &args.locs_cnt);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ args.data = (uint8_t *)json_read_alloc_binary(&c->inp, &args.data_len);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ strlcpy(args.token, token, sizeof(args.token));
+ cache_enter(command_setm_cache_client, c, &args, sizeof(args));
+}
+
+static void read_filter_attrs(InputStream * inp, const char * nm, void * arg) {
+ json_skip_object(inp);
+}
+
+static void command_search(char * token, Channel * c) {
+ char id[256];
+
+ json_read_string(&c->inp, id, sizeof(id));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ json_read_struct(&c->inp, read_filter_attrs, NULL);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, ERR_UNSUPPORTED);
+ write_stringz(&c->out, "null");
+ write_stream(&c->out, MARKER_EOM);
+}
+
+void add_registers_event_listener(RegistersEventListener * listener, void * args) {
+ if (listener_cnt >= listener_max) {
+ listener_max += 8;
+ listeners = (Listener *)loc_realloc(listeners, listener_max * sizeof(Listener));
+ }
+ listeners[listener_cnt].func = listener;
+ listeners[listener_cnt].args = args;
+ listener_cnt++;
+}
+
+void ini_registers_service(Protocol * proto, TCFBroadcastGroup * bcg) {
+ broadcast_group = bcg;
+ add_command_handler(proto, REGISTERS, "getContext", command_get_context);
+ add_command_handler(proto, REGISTERS, "getChildren", command_get_children);
+ add_command_handler(proto, REGISTERS, "get", command_get);
+ add_command_handler(proto, REGISTERS, "set", command_set);
+ add_command_handler(proto, REGISTERS, "getm", command_getm);
+ add_command_handler(proto, REGISTERS, "setm", command_setm);
+ add_command_handler(proto, REGISTERS, "search", command_search);
+}
+
+#endif /* SERVICE_Registers */

Back to the top