commandfilter.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. #define REDISMODULE_EXPERIMENTAL_API
  2. #include "redismodule.h"
  3. #include <string.h>
  4. static RedisModuleString *log_key_name;
  5. static const char log_command_name[] = "commandfilter.log";
  6. static const char ping_command_name[] = "commandfilter.ping";
  7. static const char unregister_command_name[] = "commandfilter.unregister";
  8. static int in_log_command = 0;
  9. static RedisModuleCommandFilter *filter = NULL;
  10. int CommandFilter_UnregisterCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
  11. {
  12. (void) argc;
  13. (void) argv;
  14. RedisModule_ReplyWithLongLong(ctx,
  15. RedisModule_UnregisterCommandFilter(ctx, filter));
  16. return REDISMODULE_OK;
  17. }
  18. int CommandFilter_PingCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
  19. {
  20. (void) argc;
  21. (void) argv;
  22. RedisModuleCallReply *reply = RedisModule_Call(ctx, "ping", "c", "@log");
  23. if (reply) {
  24. RedisModule_ReplyWithCallReply(ctx, reply);
  25. RedisModule_FreeCallReply(reply);
  26. } else {
  27. RedisModule_ReplyWithSimpleString(ctx, "Unknown command or invalid arguments");
  28. }
  29. return REDISMODULE_OK;
  30. }
  31. int CommandFilter_LogCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
  32. {
  33. RedisModuleString *s = RedisModule_CreateString(ctx, "", 0);
  34. int i;
  35. for (i = 1; i < argc; i++) {
  36. size_t arglen;
  37. const char *arg = RedisModule_StringPtrLen(argv[i], &arglen);
  38. if (i > 1) RedisModule_StringAppendBuffer(ctx, s, " ", 1);
  39. RedisModule_StringAppendBuffer(ctx, s, arg, arglen);
  40. }
  41. RedisModuleKey *log = RedisModule_OpenKey(ctx, log_key_name, REDISMODULE_WRITE|REDISMODULE_READ);
  42. RedisModule_ListPush(log, REDISMODULE_LIST_HEAD, s);
  43. RedisModule_CloseKey(log);
  44. RedisModule_FreeString(ctx, s);
  45. in_log_command = 1;
  46. size_t cmdlen;
  47. const char *cmdname = RedisModule_StringPtrLen(argv[1], &cmdlen);
  48. RedisModuleCallReply *reply = RedisModule_Call(ctx, cmdname, "v", &argv[2], argc - 2);
  49. if (reply) {
  50. RedisModule_ReplyWithCallReply(ctx, reply);
  51. RedisModule_FreeCallReply(reply);
  52. } else {
  53. RedisModule_ReplyWithSimpleString(ctx, "Unknown command or invalid arguments");
  54. }
  55. in_log_command = 0;
  56. return REDISMODULE_OK;
  57. }
  58. void CommandFilter_CommandFilter(RedisModuleCommandFilterCtx *filter)
  59. {
  60. if (in_log_command) return; /* don't process our own RM_Call() from CommandFilter_LogCommand() */
  61. /* Fun manipulations:
  62. * - Remove @delme
  63. * - Replace @replaceme
  64. * - Append @insertbefore or @insertafter
  65. * - Prefix with Log command if @log encounterd
  66. */
  67. int log = 0;
  68. int pos = 0;
  69. while (pos < RedisModule_CommandFilterArgsCount(filter)) {
  70. const RedisModuleString *arg = RedisModule_CommandFilterArgGet(filter, pos);
  71. size_t arg_len;
  72. const char *arg_str = RedisModule_StringPtrLen(arg, &arg_len);
  73. if (arg_len == 6 && !memcmp(arg_str, "@delme", 6)) {
  74. RedisModule_CommandFilterArgDelete(filter, pos);
  75. continue;
  76. }
  77. if (arg_len == 10 && !memcmp(arg_str, "@replaceme", 10)) {
  78. RedisModule_CommandFilterArgReplace(filter, pos,
  79. RedisModule_CreateString(NULL, "--replaced--", 12));
  80. } else if (arg_len == 13 && !memcmp(arg_str, "@insertbefore", 13)) {
  81. RedisModule_CommandFilterArgInsert(filter, pos,
  82. RedisModule_CreateString(NULL, "--inserted-before--", 19));
  83. pos++;
  84. } else if (arg_len == 12 && !memcmp(arg_str, "@insertafter", 12)) {
  85. RedisModule_CommandFilterArgInsert(filter, pos + 1,
  86. RedisModule_CreateString(NULL, "--inserted-after--", 18));
  87. pos++;
  88. } else if (arg_len == 4 && !memcmp(arg_str, "@log", 4)) {
  89. log = 1;
  90. }
  91. pos++;
  92. }
  93. if (log) RedisModule_CommandFilterArgInsert(filter, 0,
  94. RedisModule_CreateString(NULL, log_command_name, sizeof(log_command_name)-1));
  95. }
  96. int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
  97. if (RedisModule_Init(ctx,"commandfilter",1,REDISMODULE_APIVER_1)
  98. == REDISMODULE_ERR) return REDISMODULE_ERR;
  99. if (argc != 2) {
  100. RedisModule_Log(ctx, "warning", "Log key name not specified");
  101. return REDISMODULE_ERR;
  102. }
  103. long long noself = 0;
  104. log_key_name = RedisModule_CreateStringFromString(ctx, argv[0]);
  105. RedisModule_StringToLongLong(argv[1], &noself);
  106. if (RedisModule_CreateCommand(ctx,log_command_name,
  107. CommandFilter_LogCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
  108. return REDISMODULE_ERR;
  109. if (RedisModule_CreateCommand(ctx,ping_command_name,
  110. CommandFilter_PingCommand,"deny-oom",1,1,1) == REDISMODULE_ERR)
  111. return REDISMODULE_ERR;
  112. if (RedisModule_CreateCommand(ctx,unregister_command_name,
  113. CommandFilter_UnregisterCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
  114. return REDISMODULE_ERR;
  115. if ((filter = RedisModule_RegisterCommandFilter(ctx, CommandFilter_CommandFilter,
  116. noself ? REDISMODULE_CMDFILTER_NOSELF : 0))
  117. == NULL) return REDISMODULE_ERR;
  118. return REDISMODULE_OK;
  119. }
  120. int RedisModule_OnUnload(RedisModuleCtx *ctx) {
  121. RedisModule_FreeString(ctx, log_key_name);
  122. return REDISMODULE_OK;
  123. }