mirror of
https://github.com/pissnet/pissircd.git
synced 2025-08-13 21:51:36 +01:00
JSON-RPC: add log.subscribe and log.unsubscribe
https://www.unrealircd.org/docs/JSON-RPC:Log
This commit is contained in:
parent
4945ac9f7e
commit
7c22f37a9f
10 changed files with 175 additions and 18 deletions
doc
include
src
|
@ -58,6 +58,11 @@ You can help us by testing this release and reporting any issues at https://bugs
|
|||
and [User](https://www.unrealircd.org/docs/JSON-RPC:User#Structure_of_a_client_object)
|
||||
response object will be. Especially useful if you don't need all the
|
||||
details in the list calls.
|
||||
* New JSON-RPC methods
|
||||
[`log.subscribe`](https://www.unrealircd.org/docs/JSON-RPC:Log#log.subscribe) and
|
||||
[`log.unsubscribe`](https://www.unrealircd.org/docs/JSON-RPC:Log#log.unsubscribe)
|
||||
to allow real-time streaming of
|
||||
[JSON log events](https://www.unrealircd.org/docs/JSON_logging).
|
||||
* New JSON-RPC method
|
||||
[`rpc.set_issuer`](https://www.unrealircd.org/docs/JSON-RPC:Rpc#rpc.set_issuer)
|
||||
to indiciate who is actually issuing the requests. The admin panel uses this
|
||||
|
|
|
@ -249,6 +249,7 @@ loadmodule "rpc/server_ban";
|
|||
loadmodule "rpc/server_ban_exception";
|
||||
loadmodule "rpc/name_ban";
|
||||
loadmodule "rpc/spamfilter";
|
||||
loadmodule "rpc/log";
|
||||
|
||||
/*** Other ***/
|
||||
// These are modules that don't fit in any of the previous sections
|
||||
|
|
|
@ -1268,7 +1268,7 @@ extern const char *log_type_valtostring(LogType v);
|
|||
#endif
|
||||
extern void do_unreal_log(LogLevel loglevel, const char *subsystem, const char *event_id, Client *client, const char *msg, ...) __attribute__((format(printf,5,0)));
|
||||
extern void do_unreal_log_raw(LogLevel loglevel, const char *subsystem, const char *event_id, Client *client, const char *msg, ...);
|
||||
extern void do_unreal_log_internal_from_remote(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized, Client *from_server);
|
||||
extern void do_unreal_log_internal_from_remote(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, json_t *json, const char *json_serialized, Client *from_server);
|
||||
extern LogData *log_data_string(const char *key, const char *str);
|
||||
extern LogData *log_data_char(const char *key, const char c);
|
||||
extern LogData *log_data_integer(const char *key, int64_t integer);
|
||||
|
@ -1291,6 +1291,9 @@ extern const char *log_level_valtostring(LogLevel loglevel);
|
|||
extern LogLevel log_level_stringtoval(const char *str);
|
||||
extern int valid_event_id(const char *s);
|
||||
extern int valid_subsystem(const char *s);
|
||||
extern LogSource *add_log_source(const char *str);
|
||||
extern void free_log_sources(LogSource *l);
|
||||
extern int log_sources_match(LogSource *logsource, LogLevel loglevel, const char *subsystem, const char *event_id, int matched_already);
|
||||
extern const char *timestamp_iso8601_now(void);
|
||||
extern const char *timestamp_iso8601(time_t v);
|
||||
extern int is_valid_snomask(char c);
|
||||
|
|
|
@ -1839,11 +1839,12 @@ int hooktype_tkl_del(Client *client, TKL *tkl);
|
|||
* @param subsystem Subsystem (eg "operoverride")
|
||||
* @param event_id Event ID (eg "SAJOIN_COMMAND")
|
||||
* @param msg Message(s) in text form
|
||||
* @param json_serialized The associated JSON text
|
||||
* @param json The JSON log entry
|
||||
* @param json_serialized The serialized JSON log entry (as a string)
|
||||
* @param timebuf The [xxxx] time buffer, for convenience
|
||||
* @return The return value is ignored (use return 0)
|
||||
*/
|
||||
int hooktype_log(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized, const char *timebuf);
|
||||
int hooktype_log(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, json_t *json, const char *json_serialized, const char *timebuf);
|
||||
|
||||
/** Called when a local user matches a spamfilter (function prototype for HOOKTYPE_LOCAL_SPAMFILTER).
|
||||
* @param client The client
|
||||
|
|
|
@ -1443,6 +1443,7 @@ struct RPCClient {
|
|||
char *rpc_user; /**< Name of the rpc-user block after authentication, NULL during pre-auth */
|
||||
char *issuer; /**< Optional name of the issuer, set by rpc.set_issuer(), eg logged in user on admin panel, can be NULL */
|
||||
json_t *rehash_request; /**< If a REHASH (request) is currently running, otherwise NULL */
|
||||
LogSource *log_sources; /**< Subscribed to which log sources */
|
||||
};
|
||||
|
||||
struct MessageTag {
|
||||
|
|
24
src/list.c
24
src/list.c
|
@ -147,6 +147,19 @@ Client *make_client(Client *from, Client *servr)
|
|||
return client;
|
||||
}
|
||||
|
||||
/** Free the client->rpc struct.
|
||||
* NOTE: if you want to fully free the entire client, call free_client()
|
||||
*/
|
||||
void free_client_rpc(Client *client)
|
||||
{
|
||||
safe_free(client->rpc->rpc_user);
|
||||
safe_free(client->rpc->issuer);
|
||||
if (client->rpc->rehash_request)
|
||||
json_decref(client->rpc->rehash_request);
|
||||
free_log_sources(client->rpc->log_sources);
|
||||
safe_free(client->rpc);
|
||||
}
|
||||
|
||||
void free_client(Client *client)
|
||||
{
|
||||
if (!list_empty(&client->client_node))
|
||||
|
@ -191,15 +204,10 @@ void free_client(Client *client)
|
|||
del_from_id_hash_table(client->id, client);
|
||||
}
|
||||
}
|
||||
|
||||
if (client->rpc)
|
||||
{
|
||||
safe_free(client->rpc->rpc_user);
|
||||
safe_free(client->rpc->issuer);
|
||||
if (client->rpc->rehash_request)
|
||||
json_decref(client->rpc->rehash_request);
|
||||
safe_free(client->rpc);
|
||||
}
|
||||
|
||||
free_client_rpc(client);
|
||||
|
||||
safe_free(client->ip);
|
||||
|
||||
mp_pool_release(client);
|
||||
|
|
10
src/log.c
10
src/log.c
|
@ -880,7 +880,7 @@ literal:
|
|||
}
|
||||
|
||||
/** Do the actual writing to log files */
|
||||
void do_unreal_log_disk(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized, Client *from_server)
|
||||
void do_unreal_log_disk(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, json_t *json, const char *json_serialized, Client *from_server)
|
||||
{
|
||||
static time_t last_log_file_warning = 0;
|
||||
Log *l;
|
||||
|
@ -893,7 +893,7 @@ void do_unreal_log_disk(LogLevel loglevel, const char *subsystem, const char *ev
|
|||
|
||||
snprintf(timebuf, sizeof(timebuf), "[%s] ", myctime(TStime()));
|
||||
|
||||
RunHook(HOOKTYPE_LOG, loglevel, subsystem, event_id, msg, json_serialized, timebuf);
|
||||
RunHook(HOOKTYPE_LOG, loglevel, subsystem, event_id, msg, json, json_serialized, timebuf);
|
||||
|
||||
if (!loop.forked && (loglevel > ULOG_DEBUG))
|
||||
{
|
||||
|
@ -1535,7 +1535,7 @@ void do_unreal_log_internal(LogLevel loglevel, const char *subsystem, const char
|
|||
|
||||
/* Now call all the loggers: */
|
||||
|
||||
do_unreal_log_disk(loglevel, subsystem, event_id, mmsg, json_serialized, from_server);
|
||||
do_unreal_log_disk(loglevel, subsystem, event_id, mmsg, j, json_serialized, from_server);
|
||||
|
||||
if ((loop.rehashing == 2) || !strcmp(subsystem, "config"))
|
||||
do_unreal_log_control(loglevel, subsystem, event_id, mmsg, j, json_serialized, from_server);
|
||||
|
@ -1572,14 +1572,14 @@ void do_unreal_log_internal(LogLevel loglevel, const char *subsystem, const char
|
|||
}
|
||||
|
||||
void do_unreal_log_internal_from_remote(LogLevel loglevel, const char *subsystem, const char *event_id,
|
||||
MultiLine *msg, const char *json_serialized, Client *from_server)
|
||||
MultiLine *msg, json_t *json, const char *json_serialized, Client *from_server)
|
||||
{
|
||||
if (unreal_log_recursion_trap)
|
||||
return;
|
||||
unreal_log_recursion_trap = 1;
|
||||
|
||||
/* Call the disk loggers */
|
||||
do_unreal_log_disk(loglevel, subsystem, event_id, msg, json_serialized, from_server);
|
||||
do_unreal_log_disk(loglevel, subsystem, event_id, msg, json, json_serialized, from_server);
|
||||
|
||||
/* And to IRC */
|
||||
do_unreal_log_opers(loglevel, subsystem, event_id, msg, json_serialized, from_server);
|
||||
|
|
|
@ -32,7 +32,9 @@ INCLUDES = ../../include/channel.h \
|
|||
../../include/version.h ../../include/whowas.h
|
||||
|
||||
R_MODULES= \
|
||||
rpc.so stats.so user.so server.so channel.so server_ban.so server_ban_exception.so name_ban.so spamfilter.so
|
||||
rpc.so stats.so user.so server.so channel.so server_ban.so \
|
||||
server_ban_exception.so name_ban.so spamfilter.so \
|
||||
log.so
|
||||
|
||||
MODULES=$(R_MODULES)
|
||||
MODULEFLAGS=@MODULEFLAGS@
|
||||
|
|
136
src/modules/rpc/log.c
Normal file
136
src/modules/rpc/log.c
Normal file
|
@ -0,0 +1,136 @@
|
|||
/* log.* RPC calls
|
||||
* (C) Copyright 2023-.. Bram Matthys (Syzop) and the UnrealIRCd team
|
||||
* License: GPLv2 or later
|
||||
*/
|
||||
|
||||
#include "unrealircd.h"
|
||||
|
||||
ModuleHeader MOD_HEADER
|
||||
= {
|
||||
"rpc/log",
|
||||
"1.0.0",
|
||||
"log.* RPC calls",
|
||||
"UnrealIRCd Team",
|
||||
"unrealircd-6",
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
void rpc_log_hook_subscribe(Client *client, json_t *request, json_t *params);
|
||||
void rpc_log_hook_unsubscribe(Client *client, json_t *request, json_t *params);
|
||||
int rpc_log_hook(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, json_t *json, const char *json_serialized, const char *timebuf);
|
||||
|
||||
MOD_INIT()
|
||||
{
|
||||
RPCHandlerInfo r;
|
||||
|
||||
MARK_AS_OFFICIAL_MODULE(modinfo);
|
||||
|
||||
memset(&r, 0, sizeof(r));
|
||||
r.method = "log.subscribe";
|
||||
r.loglevel = ULOG_DEBUG;
|
||||
r.call = rpc_log_hook_subscribe;
|
||||
if (!RPCHandlerAdd(modinfo->handle, &r))
|
||||
{
|
||||
config_error("[rpc/log] Could not register RPC handler");
|
||||
return MOD_FAILED;
|
||||
}
|
||||
|
||||
memset(&r, 0, sizeof(r));
|
||||
r.method = "log.unsubscribe";
|
||||
r.loglevel = ULOG_DEBUG;
|
||||
r.call = rpc_log_hook_unsubscribe;
|
||||
if (!RPCHandlerAdd(modinfo->handle, &r))
|
||||
{
|
||||
config_error("[rpc/log] Could not register RPC handler");
|
||||
return MOD_FAILED;
|
||||
}
|
||||
|
||||
HookAdd(modinfo->handle, HOOKTYPE_LOG, 0, rpc_log_hook);
|
||||
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_LOAD()
|
||||
{
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_UNLOAD()
|
||||
{
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
void rpc_log_hook_subscribe(Client *client, json_t *request, json_t *params)
|
||||
{
|
||||
json_t *result;
|
||||
json_t *sources;
|
||||
size_t index;
|
||||
json_t *value;
|
||||
const char *str;
|
||||
LogSource *s;
|
||||
|
||||
sources = json_object_get(params, "sources");
|
||||
if (!sources || !json_is_array(sources))
|
||||
{
|
||||
rpc_error_fmt(client, request, JSON_RPC_ERROR_INVALID_PARAMS, "Missing parameter: '%s'", "sources");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Erase the old subscriptions first */
|
||||
free_log_sources(client->rpc->log_sources);
|
||||
client->rpc->log_sources = NULL;
|
||||
|
||||
/* Add subscriptions... */
|
||||
json_array_foreach(sources, index, value)
|
||||
{
|
||||
str = json_get_value(value);
|
||||
if (!str)
|
||||
continue;
|
||||
|
||||
s = add_log_source(str);
|
||||
AddListItem(s, client->rpc->log_sources);
|
||||
}
|
||||
|
||||
result = json_boolean(1);
|
||||
|
||||
rpc_response(client, request, result);
|
||||
json_decref(result);
|
||||
}
|
||||
|
||||
/** log.unsubscribe: unsubscribe from all log messages */
|
||||
void rpc_log_hook_unsubscribe(Client *client, json_t *request, json_t *params)
|
||||
{
|
||||
json_t *result;
|
||||
|
||||
free_log_sources(client->rpc->log_sources);
|
||||
client->rpc->log_sources = NULL;
|
||||
result = json_boolean(1);
|
||||
rpc_response(client, request, result);
|
||||
json_decref(result);
|
||||
}
|
||||
|
||||
int rpc_log_hook(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, json_t *json, const char *json_serialized, const char *timebuf)
|
||||
{
|
||||
Client *client;
|
||||
json_t *request = NULL;
|
||||
|
||||
list_for_each_entry(client, &unknown_list, lclient_node)
|
||||
{
|
||||
if (IsRPC(client) && client->rpc->log_sources &&
|
||||
log_sources_match(client->rpc->log_sources, loglevel, subsystem, event_id, 0))
|
||||
{
|
||||
if (request == NULL)
|
||||
{
|
||||
/* Lazy initalization */
|
||||
request = json_object();
|
||||
json_object_set_new(request, "method", json_string_unreal("log.event"));
|
||||
}
|
||||
rpc_response(client, request, json);
|
||||
}
|
||||
}
|
||||
|
||||
if (request)
|
||||
json_decref(request);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -157,7 +157,7 @@ CMD_FUNC(cmd_slog)
|
|||
json_serialized = json_dumps(j, JSON_COMPACT);
|
||||
|
||||
if (json_serialized)
|
||||
do_unreal_log_internal_from_remote(loglevel, subsystem, event_id, mmsg, json_serialized, client);
|
||||
do_unreal_log_internal_from_remote(loglevel, subsystem, event_id, mmsg, j, json_serialized, client);
|
||||
|
||||
/* Broadcast to the other servers */
|
||||
sendto_server(client, 0, 0, recv_mtags, ":%s SLOG %s %s %s :%s",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue