/* opserv.c - IRC Operator assistance service
* Copyright 2000-2004 srvx Development Team
*
- * This file is part of srvx.
+ * This file is part of x3.
*
* srvx is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
{ "OSMSG_NO_CHANNEL_MODES", "Channel $b%s$b had no modes to clear." },
{ "OSMSG_DEOP_DONE", "Deopped the requested lusers." },
{ "OSMSG_DEOPALL_DONE", "Deopped everyone on $b%s$b." },
+ { "OSMSG_DEHOP_DONE", "Dehalfopped the requested lusers." },
+ { "OSMSG_DEHOPALL_DONE", "Dehalfopped everyone on $b%s$b." },
{ "OSMSG_NO_DEBUG_CHANNEL", "No debug channel has been configured." },
{ "OSMSG_INVITE_DONE", "Invited $b%s$b to $b%s$b." },
{ "OSMSG_ALREADY_THERE", "You are already in $b%s$b." },
{ "OSMSG_MODE_SET", "I have set the modes for $b%s$b." },
{ "OSMSG_OP_DONE", "Opped the requested lusers." },
{ "OSMSG_OPALL_DONE", "Opped everyone on $b%s$b." },
+ { "OSMSG_HOP_DONE", "Halfopped the requested lusers." },
+ { "OSMSG_HOPALL_DONE", "Halfopped everyone on $b%s$b." },
{ "OSMSG_WHOIS_IDENT", "%s (%s@%s) from %d.%d.%d.%d" },
{ "OSMSG_WHOIS_NICK", "Nick : %s" },
{ "OSMSG_WHOIS_HOST", "Host : %s@%s" },
{ "OSMSG_CLONE_JOINED", "$b%s$b has joined $b%s$b." },
{ "OSMSG_CLONE_PARTED", "$b%s$b has left $b%s$b." },
{ "OSMSG_OPS_GIVEN", "I have given ops in $b%s$b to $b%s$b." },
+ { "OSMSG_HOPS_GIVEN", "I have given halfops in $b%s$b to $b%s$b." },
{ "OSMSG_CLONE_SAID", "$b%s$b has spoken to $b%s$b." },
{ "OSMSG_UNKNOWN_SUBCOMMAND", "$b%s$b is not a valid subcommand of $b%s$b." },
{ "OSMSG_UNKNOWN_OPTION", "$b%s$b has not been set." },
{ "OSMSG_CHANINFO_TOPIC_UNKNOWN", "Topic: (none / not gathered)" },
{ "OSMSG_CHANINFO_BAN_COUNT", "Bans (%d):" },
{ "OSMSG_CHANINFO_BAN", "%%s by %%s (%a %b %d %H:%M:%S %Y)" },
+ { "OSMSG_CHANINFO_EXEMPT_COUNT", "Exempts (%d):" },
+ { "OSMSG_CHANINFO_EXEMPT", "%%s by %%s (%a %b %d %H:%M:%S %Y)" },
{ "OSMSG_CHANINFO_MANY_USERS", "%d users (\"/msg $S %s %s users\" for the list)" },
{ "OSMSG_CHANINFO_USER_COUNT", "Users (%d):" },
{ "OSMSG_CSEARCH_CHANNEL_INFO", "%s [%d users] %s %s" },
change.argc = 1;
change.args[0].mode = MODE_BAN;
if (is_ircmask(argv[1]))
- change.args[0].hostmask = strdup(argv[1]);
+ change.args[0].u.hostmask = strdup(argv[1]);
else if ((victim = GetUserH(argv[1])))
- change.args[0].hostmask = generate_hostmask(victim, 0);
+ change.args[0].u.hostmask = generate_hostmask(victim, 0);
else {
reply("OSMSG_INVALID_IRCMASK", argv[1]);
return 0;
}
modcmd_chanmode_announce(&change);
- reply("OSMSG_ADDED_BAN", change.args[0].hostmask, channel->name);
- free((char*)change.args[0].hostmask);
+ reply("OSMSG_ADDED_BAN", change.args[0].u.hostmask, channel->name);
+ free((char*)change.args[0].u.hostmask);
return 1;
}
char buffer[MAXLEN];
const char *fmt;
struct banNode *ban;
+ struct exemptNode *exempt;
struct modeNode *moden;
unsigned int n;
send_message_type(4, user, cmd->parent->bot, buffer, ban->ban, ban->who);
}
}
+ if (channel->exemptlist.used) {
+ reply("OSMSG_CHANINFO_EXEMPT_COUNT", channel->exemptlist.used);
+ fmt = user_find_message(user, "OSMSG_CHANINFO_EXEMPT");
+ for (n = 0; n < channel->exemptlist.used; n++) {
+ exempt = channel->exemptlist.list[n];
+ strftime(buffer, sizeof(buffer), fmt, localtime(&exempt->set));
+ send_message_type(4, user, cmd->parent->bot, buffer, exempt->exempt, exempt->who);
+ }
+ }
if ((argc < 2) && (channel->members.used >= 50)) {
/* early out unless they ask for users */
reply("OSMSG_CHANINFO_MANY_USERS", channel->members.used, argv[0], channel->name);
if (moden->modes & MODE_CHANOP)
send_message_type(4, user, cmd->parent->bot, " @%s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname);
}
+ for (n=0; n<channel->members.used; n++) {
+ moden = channel->members.list[n];
+ if ((moden->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE)) == MODE_HALFOP)
+ send_message_type(4, user, cmd->parent->bot, " %s%s (%s@%s)", "%", moden->user->nick, moden->user->ident, moden->user->hostname);
+ }
for (n=0; n<channel->members.used; n++) {
moden = channel->members.list[n];
- if ((moden->modes & (MODE_CHANOP|MODE_VOICE)) == MODE_VOICE)
+ if ((moden->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE)) == MODE_VOICE)
send_message_type(4, user, cmd->parent->bot, " +%s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname);
}
for (n=0; n<channel->members.used; n++) {
moden = channel->members.list[n];
- if ((moden->modes & (MODE_CHANOP|MODE_VOICE)) == 0)
+ if ((moden->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE)) == 0)
send_message_type(4, user, cmd->parent->bot, " %s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname);
}
return 1;
change = mod_chanmode_alloc(channel->banlist.used);
for (ii=0; ii<channel->banlist.used; ii++) {
change->args[ii].mode = MODE_REMOVE | MODE_BAN;
- change->args[ii].hostmask = channel->banlist.list[ii]->ban;
+ change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
}
modcmd_chanmode_announce(change);
+ for (ii=0; ii<change->argc; ++ii)
+ free((char*)change->args[ii].u.hostmask);
mod_chanmode_free(change);
reply("OSMSG_CLEARBANS_DONE", channel->name);
return 1;
|| !(mn->modes & MODE_CHANOP))
continue;
change->args[count].mode = MODE_REMOVE | MODE_CHANOP;
- change->args[count++].member = mn;
+ change->args[count++].u.member = mn;
}
if (count) {
change->argc = count;
return 1;
}
+static MODCMD_FUNC(cmd_dehop)
+{
+ struct mod_chanmode *change;
+ unsigned int arg, count;
+
+ change = mod_chanmode_alloc(argc-1);
+ for (arg = 1, count = 0; arg < argc; ++arg) {
+ struct userNode *victim = GetUserH(argv[arg]);
+ struct modeNode *mn;
+ if (!victim || IsService(victim)
+ || !(mn = GetUserMode(channel, victim))
+ || !(mn->modes & MODE_HALFOP))
+ continue;
+ change->args[count].mode = MODE_REMOVE | MODE_HALFOP;
+ change->args[count++].u.member = mn;
+ }
+ if (count) {
+ change->argc = count;
+ modcmd_chanmode_announce(change);
+ }
+ mod_chanmode_free(change);
+ reply("OSMSG_DEHOP_DONE");
+ return 1;
+}
+
static MODCMD_FUNC(cmd_deopall)
{
struct mod_chanmode *change;
if (IsService(mn->user) || !(mn->modes & MODE_CHANOP))
continue;
change->args[count].mode = MODE_REMOVE | MODE_CHANOP;
- change->args[count++].member = mn;
+ change->args[count++].u.member = mn;
}
if (count) {
change->argc = count;
return 1;
}
+static MODCMD_FUNC(cmd_dehopall)
+{
+ struct mod_chanmode *change;
+ unsigned int ii, count;
+
+ change = mod_chanmode_alloc(channel->members.used);
+ for (ii = count = 0; ii < channel->members.used; ++ii) {
+ struct modeNode *mn = channel->members.list[ii];
+ if (IsService(mn->user) || !(mn->modes & MODE_HALFOP))
+ continue;
+ change->args[count].mode = MODE_REMOVE | MODE_HALFOP;
+ change->args[count++].u.member = mn;
+ }
+ if (count) {
+ change->argc = count;
+ modcmd_chanmode_announce(change);
+ }
+ mod_chanmode_free(change);
+ reply("OSMSG_DEHOPALL_DONE", channel->name);
+ return 1;
+}
+
static MODCMD_FUNC(cmd_rehash)
{
extern char *services_config;
}
if (mn->modes & MODE_CHANOP)
buff[count++] = '@';
+ if (mn->modes & MODE_HALFOP)
+ buff[count++] = '%';
if (mn->modes & MODE_VOICE)
buff[count++] = '+';
memcpy(buff+count, mn->channel->name, here_len);
return 0;
}
if (GetUserMode(opserv_conf.debug_channel, user)) {
- reply("OSMSG_ALREADY_THERE", channel->name);
+ reply("OSMSG_ALREADY_THERE", opserv_conf.debug_channel->name);
return 0;
}
irc_invite(cmd->parent->bot, target, opserv_conf.debug_channel);
reply("MSG_NOT_CHANNEL_NAME");
return 0;
} else if (!(channel = GetChannel(argv[1]))) {
- channel = AddChannel(argv[1], now, NULL, NULL);
+ channel = AddChannel(argv[1], now, NULL, NULL, NULL);
AddChannelUser(bot, channel)->modes |= MODE_CHANOP;
} else if (GetUserMode(channel, bot)) {
reply("OSMSG_ALREADY_JOINED", channel->name);
mod_chanmode_init(&change);
change.argc = 1;
change.args[0].mode = MODE_CHANOP;
- change.args[0].member = AddChannelUser(bot, channel);
+ change.args[0].u.member = AddChannelUser(bot, channel);
modcmd_chanmode_announce(&change);
}
irc_fetchtopic(bot, channel->name);
struct mod_chanmode change;
mod_chanmode_init(&change);
change.args[0].mode = MODE_CHANOP;
- change.args[0].member = AddChannelUser(bot, channel);
+ change.args[0].u.member = AddChannelUser(bot, channel);
modcmd_chanmode_announce(&change);
}
if (argc < 2) {
mod_chanmode_init(&change);
change.argc = 1;
change.args[0].mode = MODE_BAN;
- change.args[0].hostmask = mask = generate_hostmask(target, 0);
+ change.args[0].u.hostmask = mask = generate_hostmask(target, 0);
modcmd_chanmode_announce(&change);
KickChannelUser(target, channel, cmd->parent->bot, reason);
free(mask);
if (!(inchan = GetUserMode(channel, bot) ? 1 : 0)) {
change = mod_chanmode_alloc(2);
change->args[0].mode = MODE_CHANOP;
- change->args[0].member = AddChannelUser(bot, channel);
+ change->args[0].u.member = AddChannelUser(bot, channel);
change->args[1].mode = MODE_BAN;
- change->args[1].hostmask = "*!*@*";
+ change->args[1].u.hostmask = "*!*@*";
} else {
change = mod_chanmode_alloc(1);
change->args[0].mode = MODE_BAN;
- change->args[0].hostmask = "*!*@*";
+ change->args[0].u.hostmask = "*!*@*";
}
modcmd_chanmode_announce(change);
+ mod_chanmode_free(change);
if (argc < 2) {
reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1);
sprintf(reason, OSMSG_KICK_REQUESTED, user->nick);
if (mn->modes & MODE_CHANOP)
continue;
change->args[count].mode = MODE_CHANOP;
- change->args[count++].member = mn;
+ change->args[count++].u.member = mn;
}
if (count) {
change->argc = count;
return 1;
}
+static MODCMD_FUNC(cmd_hop)
+{
+ struct mod_chanmode *change;
+ unsigned int arg, count;
+
+ change = mod_chanmode_alloc(argc-1);
+ for (arg = 1, count = 0; arg < argc; ++arg) {
+ struct userNode *victim;
+ struct modeNode *mn;
+ if (!(victim = GetUserH(argv[arg])))
+ continue;
+ if (!(mn = GetUserMode(channel, victim)))
+ continue;
+ if (mn->modes & MODE_HALFOP)
+ continue;
+ change->args[count].mode = MODE_HALFOP;
+ change->args[count++].u.member = mn;
+ }
+ if (count) {
+ change->argc = count;
+ modcmd_chanmode_announce(change);
+ }
+ mod_chanmode_free(change);
+ reply("OSMSG_HOP_DONE");
+ return 1;
+}
+
static MODCMD_FUNC(cmd_opall)
{
struct mod_chanmode *change;
if (mn->modes & MODE_CHANOP)
continue;
change->args[count].mode = MODE_CHANOP;
- change->args[count++].member = mn;
+ change->args[count++].u.member = mn;
}
if (count) {
change->argc = count;
return 1;
}
+static MODCMD_FUNC(cmd_hopall)
+{
+ struct mod_chanmode *change;
+ unsigned int ii, count;
+
+ change = mod_chanmode_alloc(channel->members.used);
+ for (ii = count = 0; ii < channel->members.used; ++ii) {
+ struct modeNode *mn = channel->members.list[ii];
+ if (mn->modes & MODE_HALFOP)
+ continue;
+ change->args[count].mode = MODE_HALFOP;
+ change->args[count++].u.member = mn;
+ }
+ if (count) {
+ change->argc = count;
+ modcmd_chanmode_announce(change);
+ }
+ mod_chanmode_free(change);
+ reply("OSMSG_HOPALL_DONE", channel->name);
+ return 1;
+}
+
static MODCMD_FUNC(cmd_whois)
{
struct userNode *target;
if (argv[1][0] == '*')
target = GetUserN(argv[1]+1);
else
- target = GetUserH(argv[1]);
-#else
- target = GetUserH(argv[1]);
#endif
+ target = GetUserH(argv[1]);
if (!target) {
reply("MSG_NICK_UNKNOWN", argv[1]);
return 0;
if (IsService(target)) buffer[bpos++] = 'k';
if (IsDeaf(target)) buffer[bpos++] = 'd';
+ if (target->handle_info) buffer[bpos++] = 'r';
if (IsHiddenHost(target)) buffer[bpos++] = 'x';
if (IsGagged(target)) buffer_cat(" (gagged)");
if (IsRegistering(target)) buffer_cat(" (registered account)");
mod_chanmode_init(&change);
change.argc = 1;
change.args[0].mode = MODE_REMOVE | MODE_BAN;
- change.args[0].hostmask = argv[1];
+ change.args[0].u.hostmask = argv[1];
modcmd_chanmode_announce(&change);
reply("OSMSG_UNBAN_DONE", channel->name);
return 1;
change = mod_chanmode_alloc(channel->members.used);
for (ii = count = 0; ii < channel->members.used; ++ii) {
struct modeNode *mn = channel->members.list[ii];
- if (mn->modes & (MODE_CHANOP|MODE_VOICE))
+ if (mn->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE))
continue;
change->args[count].mode = MODE_VOICE;
- change->args[count++].member = mn;
+ change->args[count++].u.member = mn;
}
if (count) {
change->argc = count;
if (!(mn->modes & MODE_VOICE))
continue;
change->args[count].mode = MODE_REMOVE | MODE_VOICE;
- change->args[count++].member = mn;
+ change->args[count++].u.member = mn;
}
if (count) {
change->argc = count;
return 1;
}
+#if defined(WITH_MALLOC_X3)
+static MODCMD_FUNC(cmd_stats_memory) {
+ extern unsigned long alloc_count, alloc_size;
+ send_message_type(MSG_TYPE_NOXLATE, user, cmd->parent->bot,
+ "%u allocations totalling %u bytes.",
+ alloc_count, alloc_size);
+ return 1;
+}
+#elif defined(WITH_MALLOC_SLAB)
+static MODCMD_FUNC(cmd_stats_memory) {
+ extern unsigned long slab_alloc_count, slab_count, slab_alloc_size;
+ extern unsigned long big_alloc_count, big_alloc_size;
+ send_message_type(MSG_TYPE_NOXLATE, user, cmd->parent->bot,
+ "%u allocations in %u slabs totalling %u bytes.",
+ slab_alloc_count, slab_count, slab_alloc_size);
+ send_message_type(MSG_TYPE_NOXLATE, user, cmd->parent->bot,
+ "%u big allocations totalling %u bytes.",
+
+ return 1;
+}
+#endif
+
static MODCMD_FUNC(cmd_dump)
{
char linedup[MAXLEN], *original;
change = mod_chanmode_alloc(2);
change->modes_set = MODE_SECRET | MODE_INVITEONLY;
change->args[0].mode = MODE_CHANOP;
- change->args[0].member = AddChannelUser(opserv, channel);
+ change->args[0].u.member = AddChannelUser(opserv, channel);
change->args[1].mode = MODE_BAN;
- change->args[1].hostmask = "*!*@*";
+ change->args[1].u.hostmask = "*!*@*";
mod_chanmode_announce(opserv, channel, change);
mod_chanmode_free(change);
for (nn=channel->members.used; nn>0; ) {
if (!GetUserMode(channel, opserv)) {
/* If we aren't in the channel, join it. */
change.args[0].mode = MODE_CHANOP;
- change.args[0].member = AddChannelUser(opserv, channel);
+ change.args[0].u.member = AddChannelUser(opserv, channel);
change.argc++;
}
if (!(channel->modes & MODE_MODERATED))
channel = GetChannel(argv[3]);
if (!irccasecmp(argv[1], "JOIN")) {
if (!channel
- && !(channel = AddChannel(argv[3], now, NULL, NULL))) {
+ && !(channel = AddChannel(argv[3], now, NULL, NULL, NULL))) {
reply("MSG_CHANNEL_UNKNOWN", argv[3]);
return 0;
}
mod_chanmode_init(&change);
change.argc = 1;
change.args[0].mode = MODE_CHANOP;
- change.args[0].member = GetUserMode(channel, clone);
- if (!change.args[0].member) {
+ change.args[0].u.member = GetUserMode(channel, clone);
+ if (!change.args[0].u.member) {
reply("OSMSG_NOT_ON_CHANNEL", clone->nick, channel->name);
return 0;
}
reply("OSMSG_OPS_GIVEN", channel->name, clone->nick);
return 1;
}
+ if (!irccasecmp(argv[1], "HOP")) {
+ struct mod_chanmode change;
+ if (!channel) {
+ reply("MSG_CHANNEL_UNKNOWN", argv[3]);
+ return 0;
+ }
+ mod_chanmode_init(&change);
+ change.argc = 1;
+ change.args[0].mode = MODE_HALFOP;
+ change.args[0].u.member = GetUserMode(channel, clone);
+ if (!change.args[0].u.member) {
+ reply("OSMSG_NOT_ON_CHANNEL", clone->nick, channel->name);
+ return 0;
+ }
+ modcmd_chanmode_announce(&change);
+ reply("OSMSG_HOPS_GIVEN", channel->name, clone->nick);
+ return 1;
+ }
if (argc < 5) {
reply("MSG_MISSING_PARAMS", argv[1]);
OPSERV_SYNTAX();
case '#':
goto find_channel;
case '-':
- discrim->chan_no_modes |= MODE_CHANOP | MODE_VOICE;
+ discrim->chan_no_modes |= MODE_CHANOP | MODE_HALFOP | MODE_VOICE;
break;
case '+':
discrim->chan_req_modes |= MODE_VOICE;
discrim->chan_no_modes |= MODE_CHANOP;
+ discrim->chan_no_modes |= MODE_HALFOP;
+ break;
+ case '%':
+ discrim->chan_req_modes |= MODE_HALFOP;
+ discrim->chan_no_modes |= MODE_CHANOP;
+ discrim->chan_no_modes |= MODE_VOICE;
break;
case '@':
discrim->chan_req_modes |= MODE_CHANOP;
send_message(user, opserv, "MSG_CHANNEL_UNKNOWN", argv[i]);
goto fail;
} else {
- discrim->channel = AddChannel(argv[i]+j, now, NULL, NULL);
+ discrim->channel = AddChannel(argv[i]+j, now, NULL, NULL, NULL);
}
}
LockChannel(discrim->channel);
str2 = database_get_data(conf_node, KEY_DEBUG_CHANNEL_MODES, RECDB_QSTRING);
if (!str2)
str2 = "+tinms";
- opserv_conf.debug_channel = AddChannel(str, now, str2, NULL);
+ opserv_conf.debug_channel = AddChannel(str, now, str2, NULL, NULL);
AddChannelUser(opserv, opserv_conf.debug_channel)->modes |= MODE_CHANOP;
} else {
opserv_conf.debug_channel = NULL;
str2 = database_get_data(conf_node, KEY_ALERT_CHANNEL_MODES, RECDB_QSTRING);
if (!str2)
str2 = "+tns";
- opserv_conf.alert_channel = AddChannel(str, now, str2, NULL);
+ opserv_conf.alert_channel = AddChannel(str, now, str2, NULL, NULL);
AddChannelUser(opserv, opserv_conf.alert_channel)->modes |= MODE_CHANOP;
} else {
opserv_conf.alert_channel = NULL;
str2 = database_get_data(conf_node, KEY_STAFF_AUTH_CHANNEL_MODES, RECDB_QSTRING);
if (!str2)
str2 = "+timns";
- opserv_conf.staff_auth_channel = AddChannel(str, now, str2, NULL);
+ opserv_conf.staff_auth_channel = AddChannel(str, now, str2, NULL, NULL);
AddChannelUser(opserv, opserv_conf.staff_auth_channel)->modes |= MODE_CHANOP;
} else {
opserv_conf.staff_auth_channel = NULL;
init_opserv(const char *nick)
{
OS_LOG = log_register_type("OpServ", "file:opserv.log");
- if (nick)
- opserv = AddService(nick, "Oper Services", NULL);
+ if (nick) {
+ const char *modes = conf_get_data("services/opserv/modes", RECDB_QSTRING);
+ opserv = AddService(nick, modes ? modes : NULL, "Oper Services", NULL);
+ }
conf_register_reload(opserv_conf_read);
memset(level_strings, 0, sizeof(level_strings));
opserv_define_func("DELTRUST", cmd_deltrust, 800, 0, 2);
opserv_define_func("DEOP", cmd_deop, 100, 2, 2);
opserv_define_func("DEOPALL", cmd_deopall, 400, 2, 0);
+ opserv_define_func("DEHOP", cmd_dehop, 100, 2, 2);
+ opserv_define_func("DEHOPALL", cmd_dehopall, 400, 2, 0);
opserv_define_func("DEVOICEALL", cmd_devoiceall, 300, 2, 0);
opserv_define_func("DIE", cmd_die, 900, 0, 2);
opserv_define_func("DUMP", cmd_dump, 999, 0, 2);
opserv_define_func("MODE", cmd_mode, 100, 2, 2);
opserv_define_func("OP", cmd_op, 100, 2, 2);
opserv_define_func("OPALL", cmd_opall, 400, 2, 0);
+ opserv_define_func("HOP", cmd_hop, 100, 2, 2);
+ opserv_define_func("HOPALL", cmd_hopall, 400, 2, 0);
opserv_define_func("PART", cmd_part, 601, 0, 2);
opserv_define_func("QUERY", cmd_query, 0, 0, 0);
opserv_define_func("RAW", cmd_raw, 999, 0, 2);
opserv_define_func("STATS UPLINK", cmd_stats_uplink, 0, 0, 0);
opserv_define_func("STATS UPTIME", cmd_stats_uptime, 0, 0, 0);
opserv_define_func("STATS WARN", cmd_stats_warn, 0, 0, 0);
+#if defined(WITH_MALLOC_X3) || defined(WITH_MALLOC_SLAB)
+ opserv_define_func("STATS MEMORY", cmd_stats_memory, 0, 0, 0);
+#endif
opserv_define_func("TRACE", cmd_trace, 100, 0, 3);
opserv_define_func("TRACE PRINT", NULL, 0, 0, 0);
opserv_define_func("TRACE COUNT", NULL, 0, 0, 0);