From 42c46f2d684f3a8ff9ef40f0bb6c21b899daf6d7 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 17 Jan 2025 10:39:15 -0400 Subject: [PATCH] MT#55283 use string hashing for CLI parsing Change-Id: I270deca0e5fc9f07be501f9a4c336244fc50be9b --- daemon/.ycm_extra_conf.py | 3 ++ daemon/Makefile | 2 +- daemon/cli.c | 62 ++++++++++++++++------------------ t/Makefile | 8 ++--- utils/const_str_hash | 71 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 38 deletions(-) diff --git a/daemon/.ycm_extra_conf.py b/daemon/.ycm_extra_conf.py index 138ff7540..723ec7c5a 100644 --- a/daemon/.ycm_extra_conf.py +++ b/daemon/.ycm_extra_conf.py @@ -50,8 +50,11 @@ flags = [ '-DHAVE_LIBURING', '-D__csh_lookup(x)=str_hash(x)', '-D__csh_lookup_n(n,x)=__csh_lookup(x)', + '-D__csh_lookup_section(n,x)=__csh_lookup(x)', '-DCSH_LOOKUP(x)=' + csh_lookup_str, '-DCSH_LOOKUP_N(n,x)=CSH_LOOKUP(x)', + '-DCSH_SECTION=0', + '-DCSH_NUM_LOOKUPS=0', '-O2', '-fstack-protector', '--param=ssp-buffer-size=4', diff --git a/daemon/Makefile b/daemon/Makefile index 731775897..8599cdab5 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -89,7 +89,7 @@ include ../lib/mqtt.Makefile SRCS= main.c kernel.c helpers.c control_tcp.c call.c control_udp.c redis.c \ cookie_cache.c udp_listener.c control_ng_flags_parser.c control_ng.strhash.c sdp.strhash.c stun.c rtcp.c \ - crypto.c rtp.c call_interfaces.strhash.c dtls.c log.c cli.c graphite.c ice.c \ + crypto.c rtp.c call_interfaces.strhash.c dtls.c log.c cli.strhash.c graphite.c ice.c \ media_socket.c homer.c recording.c statistics.c cdr.c ssrc.c iptables.c tcp_listener.c \ codec.c load.c dtmf.c timerthread.c media_player.c jitter_buffer.c t38.c websocket.c \ mqtt.c janus.strhash.c audio_player.c arena.c diff --git a/daemon/cli.c b/daemon/cli.c index 87f27fb1a..958149128 100644 --- a/daemon/cli.c +++ b/daemon/cli.c @@ -34,12 +34,19 @@ #include "media_player.h" typedef struct cli_handler_t cli_handler_t; +typedef struct cli_command_t cli_command_t; typedef void (*cli_handler_func)(str *, struct cli_writer *, const cli_handler_t *); -struct cli_handler_t { + +struct cli_command_t { const char *cmd; cli_handler_func handler; const cli_handler_t *next; }; +struct cli_handler_t { + unsigned int section; + const cli_command_t *commands; + unsigned int num_commands; +}; static void cli_generic_handler(str *instr, struct cli_writer *cw, const cli_handler_t *); static void cli_incoming_terminate(str *instr, struct cli_writer *cw, const cli_handler_t *); @@ -135,10 +142,10 @@ static void cli_incoming_media_evict_players(str *instr, struct cli_writer *cw, #endif -#define HANDLER_START(n) static const cli_handler_t n[] = { -#define HANDLER_CMD(c, f) { .cmd = c, .handler = f, }, -#define HANDLER_GENERIC(c, h) { .cmd = c, .handler = cli_generic_handler, .next = h, }, -#define HANDLER_END { NULL, } }; +#define HANDLER_START(n) static const cli_handler_t n = { .section = CSH_SECTION, .commands = (const struct cli_command_t []) { +#define HANDLER_CMD(c, f) [CSH_LOOKUP(c)] = { .cmd = c, .handler = f }, +#define HANDLER_GENERIC(c, h) [CSH_LOOKUP(c)] = { .cmd = c, .handler = cli_generic_handler, .next = &h }, +#define HANDLER_END }, .num_commands = CSH_NUM_LOOKUPS }; HANDLER_START(cli_set_handlers) HANDLER_CMD("maxopenfiles", cli_incoming_set_maxopenfiles) @@ -274,40 +281,29 @@ static void cli_list_tag_info(struct cli_writer *cw, struct call_monologue *ml); -static void cli_handler_do(const cli_handler_t *handlers, str *instr, +static void cli_handler_do(const cli_handler_t *handler, str *instr, struct cli_writer *cw) { - const cli_handler_t *h; - if (!str_cmp(instr, "help")) { cw->cw_printf(cw, "Available sub-commands at this level:\n"); - for (h = handlers; h->cmd; h++) - cw->cw_printf(cw, "\t%s\n", h->cmd); + for (unsigned int i = 0; i < handler->num_commands; i++) + cw->cw_printf(cw, "\t%s\n", handler->commands[i].cmd); return; } - for (h = handlers; h->cmd; h++) { - if (str_shift_cmp(instr, h->cmd)) - continue; - // check if followed by space or newline or end of line - if (instr->len) { - if (instr->s[0] == ' ') { - while (instr->len && instr->s[0] == ' ') - str_shift(instr, 1); - } - else if (instr->s[0] == '\n' || instr->s[0] == '\r') - instr->len = 0; - else { - // not a match. rewind and continue - str_unshift(instr, strlen(h->cmd)); - continue; - } - } - h->handler(instr, cw, h); + str cmd; + if (!str_token_sep(&cmd, instr, ' ')) { + cw->cw_printf(cw, "Incomplete command: " STR_FORMAT "\n", STR_FMT(instr)); + return; + } + + int val = __csh_lookup_section(handler->section, &cmd); + if (val < 0 || val >= handler->num_commands) { + cw->cw_printf(cw, "Unknown command: " STR_FORMAT "\n", STR_FMT(&cmd)); return; } - cw->cw_printf(cw, "Unknown or incomplete command: " STR_FORMAT "\n", STR_FMT(instr)); + handler->commands[val].handler(instr, cw, handler->commands[val].next); } static void destroy_own_foreign_calls(bool foreign_call, unsigned int uint_keyspace_db) { @@ -1045,7 +1041,7 @@ static void cli_generic_handler(str *instr, struct cli_writer *cw, const cli_han return; } - cli_handler_do(handler->next, instr, cw); + cli_handler_do(handler, instr, cw); } static void cli_incoming_terminate(str *instr, struct cli_writer *cw, const cli_handler_t *handler) { @@ -1305,7 +1301,7 @@ static void cli_stream_readable(struct streambuf_stream *s) { void cli_handle(str *instr, struct cli_writer *cw) { ilogs(control, LOG_INFO, "Got CLI command: " STR_FORMAT_M, STR_FMT_M(instr)); - cli_handler_do(cli_top_handlers, instr, cw); + cli_handler_do(&cli_top_handlers, instr, cw); release_closed_sockets(); } @@ -1530,7 +1526,7 @@ static void cli_incoming_call(str *instr, struct cli_writer *cw, const cli_handl return; } - cli_handler_do(cli_call_handlers, instr, cw); + cli_handler_do(&cli_call_handlers, instr, cw); if (cw->call) { rwlock_unlock_w(&cw->call->master_lock); @@ -1590,7 +1586,7 @@ static void cli_incoming_call_tag(str *instr, struct cli_writer *cw, const cli_h return; } - cli_handler_do(cli_tag_handlers, instr, cw); + cli_handler_do(&cli_tag_handlers, instr, cw); cw->ml = NULL; } diff --git a/t/Makefile b/t/Makefile index f5377db35..214fda5e4 100644 --- a/t/Makefile +++ b/t/Makefile @@ -69,7 +69,7 @@ include ../lib/codec-chain.Makefile SRCS= test-bitstr.c aes-crypt.c aead-aes-crypt.c test-const_str_hash.strhash.c aead-decrypt.c LIBSRCS= loglib.c auxlib.c str.c rtplib.c ssllib.c mix_buffer.c bufferpool.c bencode.c DAEMONSRCS= crypto.c ssrc.c helpers.c rtp.c -HASHSRCS= +HASHSRCS= cli.c ifeq ($(with_transcoding),yes) SRCS+= test-transcode.c test-dtmf-detect.c test-payload-tracker.c test-resample.c test-stats.c @@ -82,7 +82,7 @@ LIBSRCS+= codeclib.strhash.c resample.c socket.c streambuf.c dtmflib.c poller.c DAEMONSRCS+= control_ng_flags_parser.c codec.c call.c ice.c kernel.c media_socket.c stun.c bencode.c \ dtls.c recording.c statistics.c rtcp.c redis.c iptables.c graphite.c \ cookie_cache.c udp_listener.c homer.c load.c cdr.c dtmf.c timerthread.c \ - media_player.c jitter_buffer.c t38.c tcp_listener.c mqtt.c websocket.c cli.c \ + media_player.c jitter_buffer.c t38.c tcp_listener.c mqtt.c websocket.c \ audio_player.c arena.c HASHSRCS+= call_interfaces.c control_ng.c sdp.c janus.c LIBASM= mvr2s_x64_avx2.S mvr2s_x64_avx512.S mix_in_x64_avx2.S mix_in_x64_avx512bw.S mix_in_x64_sse2.S @@ -255,7 +255,7 @@ test-stats: test-stats.o $(COMMONOBJS) codeclib.strhash.o resample.o codec.o ssr control_ng_flags_parser.o control_ng.strhash.o graphite.o \ streambuf.o cookie_cache.o udp_listener.o homer.o load.o cdr.o dtmf.o timerthread.o \ media_player.o jitter_buffer.o dtmflib.o t38.o tcp_listener.o mqtt.o janus.strhash.o \ - websocket.o cli.o mvr2s_x64_avx2.o mvr2s_x64_avx512.o audio_player.o mix_buffer.o \ + websocket.o cli.strhash.o mvr2s_x64_avx2.o mvr2s_x64_avx512.o audio_player.o mix_buffer.o \ mix_in_x64_avx2.o mix_in_x64_sse2.o mix_in_x64_avx512bw.o bufferpool.o uring.o arena.o test-transcode: test-transcode.o $(COMMONOBJS) codeclib.strhash.o resample.o codec.o ssrc.o call.o ice.o helpers.o \ @@ -264,7 +264,7 @@ test-transcode: test-transcode.o $(COMMONOBJS) codeclib.strhash.o resample.o cod control_ng_flags_parser.o control_ng.strhash.o \ streambuf.o cookie_cache.o udp_listener.o homer.o load.o cdr.o dtmf.o timerthread.o \ media_player.o jitter_buffer.o dtmflib.o t38.o tcp_listener.o mqtt.o janus.strhash.o websocket.o \ - cli.o mvr2s_x64_avx2.o mvr2s_x64_avx512.o audio_player.o mix_buffer.o \ + cli.strhash.o mvr2s_x64_avx2.o mvr2s_x64_avx512.o audio_player.o mix_buffer.o \ mix_in_x64_avx2.o mix_in_x64_sse2.o mix_in_x64_avx512bw.o bufferpool.o uring.o arena.o test-resample: test-resample.o $(COMMONOBJS) codeclib.strhash.o resample.o dtmflib.o mvr2s_x64_avx2.o \ diff --git a/utils/const_str_hash b/utils/const_str_hash index deb292654..44298afd3 100755 --- a/utils/const_str_hash +++ b/utils/const_str_hash @@ -12,18 +12,59 @@ print("/******** GENERATED FILE ********/\n"); my $rewritten_input = ''; my @sections; my @slots; +my (%defines_0, %defines_1, %defines_2); # collect keywords and rewrite input file with in lookup keys +sub def_sub { + my ($subs, @vals) = @_; + my $i = 0; + my $repl = $subs->[$i++]; + for my $r (@vals) { + my $key = $subs->[$i++]; + $repl =~ s/(\W|^)\Q$key\E(\W|$)/$1$r$2/g; + } + return $repl; +} + while (my $line = ) { my $num = scalar(@sections); my $new_section; + if ($line =~ /CSH_SECTION|CSH_LOOKUP|CSH_NUM_LOOKUPS/ + && $line =~ /^#define\s+(\w+)(?:\s+(.*?)|\(\s*(\w+)\s*\)\s+(.*?)|\(\s*(\w+)\s*,\s*(\w+)\s*\)\s+(.*?))$/) { + if ($2) { + $defines_0{$1} = $2; + } + elsif ($3) { + $defines_1{$1} = [$4, $3]; + } + elsif ($5) { + $defines_2{$1} = [$7, $5, $6]; + } + $rewritten_input .= "\n"; + next; + } + for my $def (keys(%defines_0)) { + my $sub = $defines_0{$def}; + $line =~ s/(\W|^)\Q$def\E(\W|$)/$1$sub$2/g; + } + for my $def (keys(%defines_1)) { + my $subs = $defines_1{$def}; + $line =~ s/(\W|^)\Q$def\E\(\s*(\S+|"[^"]*")\s*\)(\W|$)/$1 . def_sub($subs, $2) . $3/eg; + } + for my $def (keys(%defines_2)) { + my $subs = $defines_2{$def}; + $line =~ s/(\W|^)\Q$def\E\(\s*(\S+|"[^"]*")\s*,\s*(\S+|"[^"]*")\s*\)(\W|$)/$1 . def_sub($subs, $2, $3) . $4/eg; + } if (($line =~ s/(__csh_lookup)(\s*\()/$1_$num$2/)) { $new_section = 0; } elsif (($line =~ s/(__csh_lookup)_n(\s*\()\s*(\d+)\s*,\s*/$1_$num$2/)) { $new_section = $3; } + elsif (($line =~ s/CSH_SECTION/$num/)) { + $new_section = 0; + } if (defined($new_section)) { $rewritten_input .= $line; my $section = { keys => '', vals => {}, num => $num }; @@ -31,6 +72,14 @@ while (my $line = ) { $slots[$new_section] = $num; next; } + if ($line =~ s/CSH_NUM_LOOKUPS/{}/) { + my $section = $sections[$slots[0]]; + die unless $section; + my $n = values(%{$section->{vals}}); + $line =~ s/{}/$n/; + $rewritten_input .= $line; + next; + } my ($rewrite, $key); if ($line =~ s/CSH_LOOKUP\(\s*"(.*?)"\s*\)/{}/) { $rewrite = 0; @@ -99,6 +148,7 @@ for my $section (@sections) { # add convenience function print <s) return -1; @@ -107,9 +157,30 @@ static int __csh_lookup_$num(const str *s) { return -1; return h->num; } +#pragma GCC diagnostic pop END } +print("static const struct __csh_hash_lookup *(*__csh_lookup_funcs[])(const char *, long unsigned int) = {\n"); +for my $section (@sections) { + print("\t__csh_lookup_raw_$section->{num},\n"); +} +print <= G_N_ELEMENTS(__csh_lookup_funcs)) + return -1; + if (!s->s) + return -1; + const struct __csh_hash_lookup *h = __csh_lookup_funcs[n](s->s, s->len); + if (!h) + return -1; + return h->num; +} +#pragma GCC diagnostic pop +END + # adjust diagnostic line numbers and originating file print $ARGV[0] ? "#line 1 \"$ARGV[0]\"\n" : "#line 1\n"; print $rewritten_input;