199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson * Copyright(c) 2020 Intel Corporation
399a2dd95SBruce Richardson */
472b452c5SDmitry Kozlyuk #include <ctype.h>
572b452c5SDmitry Kozlyuk #include <errno.h>
699a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS
799a2dd95SBruce Richardson #include <unistd.h>
899a2dd95SBruce Richardson #include <sys/socket.h>
999a2dd95SBruce Richardson #include <sys/un.h>
1099a2dd95SBruce Richardson #include <pthread.h>
1199a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */
1299a2dd95SBruce Richardson
1399a2dd95SBruce Richardson /* we won't link against libbsd, so just always use DPDKs-specific strlcpy */
1499a2dd95SBruce Richardson #undef RTE_USE_LIBBSD
1599a2dd95SBruce Richardson #include <rte_string_fns.h>
1699a2dd95SBruce Richardson #include <rte_common.h>
1799a2dd95SBruce Richardson #include <rte_spinlock.h>
1899a2dd95SBruce Richardson
1999a2dd95SBruce Richardson #include "telemetry_internal.h"
2099a2dd95SBruce Richardson
2199a2dd95SBruce Richardson #define MAX_LEN 128
2299a2dd95SBruce Richardson #define BUF_SIZE 1024
2399a2dd95SBruce Richardson #define CLIENTS_UNREG_ACTION "\"action\":2"
2499a2dd95SBruce Richardson #define CLIENTS_CMD "\"command\":\"clients\""
2599a2dd95SBruce Richardson #define CLIENTS_DATA "\"data\":{\"client_path\":\""
2699a2dd95SBruce Richardson #define STATS_ACTION "\"action\":0"
2799a2dd95SBruce Richardson #define DATA_REQ_LABEL "\"data\":"
2899a2dd95SBruce Richardson #define TELEMETRY_LEGACY_MAX_CALLBACKS 4
2999a2dd95SBruce Richardson
3099a2dd95SBruce Richardson
3199a2dd95SBruce Richardson static int
3299a2dd95SBruce Richardson register_client(const char *cmd __rte_unused,
3399a2dd95SBruce Richardson const char *params __rte_unused,
3499a2dd95SBruce Richardson char *buffer, int buf_len);
3599a2dd95SBruce Richardson
3699a2dd95SBruce Richardson struct json_command {
3799a2dd95SBruce Richardson char action[MAX_LEN];
3899a2dd95SBruce Richardson char cmd[MAX_LEN];
3999a2dd95SBruce Richardson char data[MAX_LEN];
4099a2dd95SBruce Richardson telemetry_legacy_cb fn;
4199a2dd95SBruce Richardson
4299a2dd95SBruce Richardson };
4399a2dd95SBruce Richardson
4499a2dd95SBruce Richardson struct json_command callbacks[TELEMETRY_LEGACY_MAX_CALLBACKS] = {
4599a2dd95SBruce Richardson {
4699a2dd95SBruce Richardson .action = "\"action\":1",
4799a2dd95SBruce Richardson .cmd = CLIENTS_CMD,
4899a2dd95SBruce Richardson .data = CLIENTS_DATA,
4999a2dd95SBruce Richardson .fn = register_client
5099a2dd95SBruce Richardson }
5199a2dd95SBruce Richardson };
5299a2dd95SBruce Richardson int num_legacy_callbacks = 1;
5399a2dd95SBruce Richardson static rte_spinlock_t callback_sl = RTE_SPINLOCK_INITIALIZER;
5499a2dd95SBruce Richardson
5599a2dd95SBruce Richardson int
rte_telemetry_legacy_register(const char * cmd,enum rte_telemetry_legacy_data_req data_req,telemetry_legacy_cb fn)5699a2dd95SBruce Richardson rte_telemetry_legacy_register(const char *cmd,
5799a2dd95SBruce Richardson enum rte_telemetry_legacy_data_req data_req,
5899a2dd95SBruce Richardson telemetry_legacy_cb fn)
5999a2dd95SBruce Richardson {
6099a2dd95SBruce Richardson if (fn == NULL)
6199a2dd95SBruce Richardson return -EINVAL;
6299a2dd95SBruce Richardson if (num_legacy_callbacks >= (int) RTE_DIM(callbacks))
6399a2dd95SBruce Richardson return -ENOENT;
6499a2dd95SBruce Richardson
6599a2dd95SBruce Richardson rte_spinlock_lock(&callback_sl);
6699a2dd95SBruce Richardson strlcpy(callbacks[num_legacy_callbacks].action, STATS_ACTION, MAX_LEN);
6799a2dd95SBruce Richardson snprintf(callbacks[num_legacy_callbacks].cmd, MAX_LEN,
6899a2dd95SBruce Richardson "\"command\":\"%s\"", cmd);
6999a2dd95SBruce Richardson snprintf(callbacks[num_legacy_callbacks].data, MAX_LEN,
7099a2dd95SBruce Richardson data_req ? "%s{\"" : "%snull",
7199a2dd95SBruce Richardson DATA_REQ_LABEL);
7299a2dd95SBruce Richardson callbacks[num_legacy_callbacks].fn = fn;
7399a2dd95SBruce Richardson num_legacy_callbacks++;
7499a2dd95SBruce Richardson rte_spinlock_unlock(&callback_sl);
7599a2dd95SBruce Richardson
7699a2dd95SBruce Richardson return 0;
7799a2dd95SBruce Richardson }
7899a2dd95SBruce Richardson
7999a2dd95SBruce Richardson static int
register_client(const char * cmd __rte_unused,const char * params,char * buffer __rte_unused,int buf_len __rte_unused)8099a2dd95SBruce Richardson register_client(const char *cmd __rte_unused, const char *params,
8199a2dd95SBruce Richardson char *buffer __rte_unused, int buf_len __rte_unused)
8299a2dd95SBruce Richardson {
8399a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS
8499a2dd95SBruce Richardson pthread_t th;
8599a2dd95SBruce Richardson char data[BUF_SIZE];
8699a2dd95SBruce Richardson int fd;
87c53a5f3eSChengwen Feng int rc;
8899a2dd95SBruce Richardson struct sockaddr_un addrs;
8999a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */
9099a2dd95SBruce Richardson
9199a2dd95SBruce Richardson if (!strchr(params, ':')) {
9299a2dd95SBruce Richardson fprintf(stderr, "Invalid data\n");
9399a2dd95SBruce Richardson return -1;
9499a2dd95SBruce Richardson }
9599a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS
9699a2dd95SBruce Richardson strlcpy(data, strchr(params, ':'), sizeof(data));
97*efdf8182SZhichao Zeng memmove(data, &data[strlen(":\"")], strlen(data));
9899a2dd95SBruce Richardson if (!strchr(data, '\"')) {
9999a2dd95SBruce Richardson fprintf(stderr, "Invalid client data\n");
10099a2dd95SBruce Richardson return -1;
10199a2dd95SBruce Richardson }
10299a2dd95SBruce Richardson *strchr(data, '\"') = 0;
10399a2dd95SBruce Richardson
10499a2dd95SBruce Richardson fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
10599a2dd95SBruce Richardson if (fd < 0) {
10699a2dd95SBruce Richardson perror("Failed to open socket");
10799a2dd95SBruce Richardson return -1;
10899a2dd95SBruce Richardson }
10999a2dd95SBruce Richardson addrs.sun_family = AF_UNIX;
11099a2dd95SBruce Richardson strlcpy(addrs.sun_path, data, sizeof(addrs.sun_path));
11199a2dd95SBruce Richardson
11299a2dd95SBruce Richardson if (connect(fd, (struct sockaddr *)&addrs, sizeof(addrs)) == -1) {
11399a2dd95SBruce Richardson perror("\nClient connection error\n");
11499a2dd95SBruce Richardson close(fd);
11599a2dd95SBruce Richardson return -1;
11699a2dd95SBruce Richardson }
117c53a5f3eSChengwen Feng rc = pthread_create(&th, NULL, &legacy_client_handler,
11899a2dd95SBruce Richardson (void *)(uintptr_t)fd);
119c53a5f3eSChengwen Feng if (rc != 0) {
120c53a5f3eSChengwen Feng fprintf(stderr, "Failed to create legacy client thread: %s\n",
121c53a5f3eSChengwen Feng strerror(rc));
122c53a5f3eSChengwen Feng close(fd);
123c53a5f3eSChengwen Feng return -1;
124c53a5f3eSChengwen Feng }
12571ecc415SStephen Hemminger pthread_detach(th);
12699a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */
12799a2dd95SBruce Richardson return 0;
12899a2dd95SBruce Richardson }
12999a2dd95SBruce Richardson
13099a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS
13199a2dd95SBruce Richardson
13299a2dd95SBruce Richardson static int
send_error_response(int s,int err)13399a2dd95SBruce Richardson send_error_response(int s, int err)
13499a2dd95SBruce Richardson {
13599a2dd95SBruce Richardson const char *desc;
13699a2dd95SBruce Richardson char out_buf[100000];
13799a2dd95SBruce Richardson
13899a2dd95SBruce Richardson switch (err) {
13999a2dd95SBruce Richardson case -ENOMEM:
14099a2dd95SBruce Richardson desc = "Memory Allocation Error";
14199a2dd95SBruce Richardson break;
14299a2dd95SBruce Richardson case -EINVAL:
14399a2dd95SBruce Richardson desc = "Invalid Argument 404";
14499a2dd95SBruce Richardson break;
14599a2dd95SBruce Richardson case -EPERM:
14699a2dd95SBruce Richardson desc = "Unknown";
14799a2dd95SBruce Richardson break;
14899a2dd95SBruce Richardson default:
14999a2dd95SBruce Richardson /* Default case keeps behaviour of Telemetry library */
15099a2dd95SBruce Richardson printf("\nInvalid error type: %d\n", err);
15199a2dd95SBruce Richardson return -EINVAL;
15299a2dd95SBruce Richardson }
15399a2dd95SBruce Richardson int used = snprintf(out_buf, sizeof(out_buf), "{\"status_code\": "
15499a2dd95SBruce Richardson "\"Status Error: %s\", \"data\": null}", desc);
15599a2dd95SBruce Richardson if (write(s, out_buf, used) < 0) {
15699a2dd95SBruce Richardson perror("Error writing to socket");
15799a2dd95SBruce Richardson return -1;
15899a2dd95SBruce Richardson }
15999a2dd95SBruce Richardson return 0;
16099a2dd95SBruce Richardson }
16199a2dd95SBruce Richardson
16299a2dd95SBruce Richardson static void
perform_command(telemetry_legacy_cb fn,const char * param,int s)16399a2dd95SBruce Richardson perform_command(telemetry_legacy_cb fn, const char *param, int s)
16499a2dd95SBruce Richardson {
16599a2dd95SBruce Richardson char out_buf[100000];
16699a2dd95SBruce Richardson int ret, used = 0;
16799a2dd95SBruce Richardson
16899a2dd95SBruce Richardson ret = fn("", param, out_buf, sizeof(out_buf));
16999a2dd95SBruce Richardson if (ret < 0) {
17099a2dd95SBruce Richardson ret = send_error_response(s, ret);
17199a2dd95SBruce Richardson if (ret < 0)
17299a2dd95SBruce Richardson printf("\nCould not send error response\n");
17399a2dd95SBruce Richardson return;
17499a2dd95SBruce Richardson }
17599a2dd95SBruce Richardson used += ret;
17699a2dd95SBruce Richardson if (write(s, out_buf, used) < 0)
17799a2dd95SBruce Richardson perror("Error writing to socket");
17899a2dd95SBruce Richardson }
17999a2dd95SBruce Richardson
18099a2dd95SBruce Richardson static int
parse_client_request(char * buffer,int buf_len,int s)18199a2dd95SBruce Richardson parse_client_request(char *buffer, int buf_len, int s)
18299a2dd95SBruce Richardson {
18399a2dd95SBruce Richardson int i;
18499a2dd95SBruce Richardson char *data = buffer + buf_len;
18599a2dd95SBruce Richardson telemetry_legacy_cb fn = NULL;
18699a2dd95SBruce Richardson const char *valid_sep = ",}";
18799a2dd95SBruce Richardson if (buffer[0] != '{' || buffer[buf_len - 1] != '}')
18899a2dd95SBruce Richardson return -EPERM;
18999a2dd95SBruce Richardson
19099a2dd95SBruce Richardson if (strstr(buffer, CLIENTS_UNREG_ACTION) && strstr(buffer, CLIENTS_CMD)
19199a2dd95SBruce Richardson && strstr(buffer, CLIENTS_DATA))
19299a2dd95SBruce Richardson return 0;
19399a2dd95SBruce Richardson
19499a2dd95SBruce Richardson for (i = 0; i < num_legacy_callbacks; i++) {
19599a2dd95SBruce Richardson char *action_ptr = strstr(buffer, callbacks[i].action);
19699a2dd95SBruce Richardson char *cmd_ptr = strstr(buffer, callbacks[i].cmd);
19799a2dd95SBruce Richardson char *data_ptr = strstr(buffer, callbacks[i].data);
19899a2dd95SBruce Richardson if (!action_ptr || !cmd_ptr || !data_ptr)
19999a2dd95SBruce Richardson continue;
20099a2dd95SBruce Richardson
20199a2dd95SBruce Richardson char action_sep = action_ptr[strlen(callbacks[i].action)];
20299a2dd95SBruce Richardson char cmd_sep = cmd_ptr[strlen(callbacks[i].cmd)];
20399a2dd95SBruce Richardson if (!(strchr(valid_sep, action_sep) && strchr(valid_sep,
20499a2dd95SBruce Richardson cmd_sep)))
20599a2dd95SBruce Richardson return -EPERM;
20699a2dd95SBruce Richardson char data_sep;
20799a2dd95SBruce Richardson
20899a2dd95SBruce Richardson if (!strchr(data_ptr, '{'))
20999a2dd95SBruce Richardson data_sep = data_ptr[strlen(callbacks[i].data)];
21099a2dd95SBruce Richardson else {
21199a2dd95SBruce Richardson if (!strchr(data_ptr, '}'))
21299a2dd95SBruce Richardson return -EINVAL;
21399a2dd95SBruce Richardson char *data_end = strchr(data_ptr, '}');
21499a2dd95SBruce Richardson data = data_ptr + strlen(DATA_REQ_LABEL);
21599a2dd95SBruce Richardson data_sep = data_end[1];
21699a2dd95SBruce Richardson data_end[1] = 0;
21799a2dd95SBruce Richardson }
21899a2dd95SBruce Richardson if (!strchr(valid_sep, data_sep))
21999a2dd95SBruce Richardson return -EPERM;
22099a2dd95SBruce Richardson fn = callbacks[i].fn;
22199a2dd95SBruce Richardson break;
22299a2dd95SBruce Richardson }
22399a2dd95SBruce Richardson
22499a2dd95SBruce Richardson if (!fn)
22599a2dd95SBruce Richardson return -EINVAL;
22699a2dd95SBruce Richardson perform_command(fn, data, s);
22799a2dd95SBruce Richardson return 0;
22899a2dd95SBruce Richardson }
22999a2dd95SBruce Richardson
23099a2dd95SBruce Richardson void *
legacy_client_handler(void * sock_id)23199a2dd95SBruce Richardson legacy_client_handler(void *sock_id)
23299a2dd95SBruce Richardson {
23399a2dd95SBruce Richardson int s = (int)(uintptr_t)sock_id;
23499a2dd95SBruce Richardson int ret;
23599a2dd95SBruce Richardson char buffer_recv[BUF_SIZE];
23699a2dd95SBruce Richardson /* receive data is not null terminated */
23799a2dd95SBruce Richardson int bytes = read(s, buffer_recv, sizeof(buffer_recv) - 1);
23899a2dd95SBruce Richardson
23999a2dd95SBruce Richardson while (bytes > 0) {
24099a2dd95SBruce Richardson buffer_recv[bytes] = 0;
24199a2dd95SBruce Richardson int i, j;
24299a2dd95SBruce Richardson char buffer[BUF_SIZE];
24399a2dd95SBruce Richardson for (i = 0, j = 0; buffer_recv[i] != '\0'; i++) {
24499a2dd95SBruce Richardson buffer[j] = buffer_recv[i];
24599a2dd95SBruce Richardson j += !isspace(buffer_recv[i]);
24699a2dd95SBruce Richardson }
24799a2dd95SBruce Richardson buffer[j] = 0;
24899a2dd95SBruce Richardson ret = parse_client_request(buffer, j, s);
24999a2dd95SBruce Richardson if (ret < 0) {
25099a2dd95SBruce Richardson ret = send_error_response(s, ret);
25199a2dd95SBruce Richardson if (ret < 0)
25299a2dd95SBruce Richardson printf("\nCould not send error response\n");
25399a2dd95SBruce Richardson }
25499a2dd95SBruce Richardson bytes = read(s, buffer_recv, sizeof(buffer_recv) - 1);
25599a2dd95SBruce Richardson }
25699a2dd95SBruce Richardson close(s);
25799a2dd95SBruce Richardson return NULL;
25899a2dd95SBruce Richardson }
25999a2dd95SBruce Richardson
26099a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */
261