summaryrefslogtreecommitdiffstats
path: root/src/collectors/plugins.d/ndsudo.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 11:19:16 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 12:07:37 +0000
commitb485aab7e71c1625cfc27e0f92c9509f42378458 (patch)
treeae9abe108601079d1679194de237c9a435ae5b55 /src/collectors/plugins.d/ndsudo.c
parentAdding upstream version 1.44.3. (diff)
downloadnetdata-b485aab7e71c1625cfc27e0f92c9509f42378458.tar.xz
netdata-b485aab7e71c1625cfc27e0f92c9509f42378458.zip
Adding upstream version 1.45.3+dfsg.upstream/1.45.3+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/collectors/plugins.d/ndsudo.c')
-rw-r--r--src/collectors/plugins.d/ndsudo.c308
1 files changed, 308 insertions, 0 deletions
diff --git a/src/collectors/plugins.d/ndsudo.c b/src/collectors/plugins.d/ndsudo.c
new file mode 100644
index 000000000..13e2ccaaa
--- /dev/null
+++ b/src/collectors/plugins.d/ndsudo.c
@@ -0,0 +1,308 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#define MAX_SEARCH 2
+#define MAX_PARAMETERS 128
+#define ERROR_BUFFER_SIZE 1024
+
+struct command {
+ const char *name;
+ const char *params;
+ const char *search[MAX_SEARCH];
+} allowed_commands[] = {
+ {
+ .name = "nvme-list",
+ .params = "list --output-format=json",
+ .search = {
+ [0] = "nvme",
+ [1] = NULL,
+ },
+ },
+ {
+ .name = "nvme-smart-log",
+ .params = "smart-log {{device}} --output-format=json",
+ .search = {
+ [0] = "nvme",
+ [1] = NULL,
+ },
+ },
+ {
+ .name = "megacli-disk-info",
+ .params = "-LDPDInfo -aAll -NoLog",
+ .search = {
+ [0] = "megacli",
+ [1] = "MegaCli",
+ },
+ },
+ {
+ .name = "megacli-battery-info",
+ .params = "-AdpBbuCmd -aAll -NoLog",
+ .search = {
+ [0] = "megacli",
+ [1] = "MegaCli",
+ },
+ },
+ {
+ .name = "arcconf-ld-info",
+ .params = "GETCONFIG 1 LD",
+ .search = {
+ [0] = "arcconf",
+ [1] = NULL,
+ },
+ },
+ {
+ .name = "arcconf-pd-info",
+ .params = "GETCONFIG 1 PD",
+ .search = {
+ [0] = "arcconf",
+ [1] = NULL,
+ },
+ }
+};
+
+bool command_exists_in_dir(const char *dir, const char *cmd, char *dst, size_t dst_size) {
+ snprintf(dst, dst_size, "%s/%s", dir, cmd);
+ return access(dst, X_OK) == 0;
+}
+
+bool command_exists_in_PATH(const char *cmd, char *dst, size_t dst_size) {
+ if(!dst || !dst_size)
+ return false;
+
+ char *path = getenv("PATH");
+ if(!path)
+ return false;
+
+ char *path_copy = strdup(path);
+ if (!path_copy)
+ return false;
+
+ char *dir;
+ bool found = false;
+ dir = strtok(path_copy, ":");
+ while(dir && !found) {
+ found = command_exists_in_dir(dir, cmd, dst, dst_size);
+ dir = strtok(NULL, ":");
+ }
+
+ free(path_copy);
+ return found;
+}
+
+struct command *find_command(const char *cmd) {
+ size_t size = sizeof(allowed_commands) / sizeof(allowed_commands[0]);
+ for(size_t i = 0; i < size ;i++) {
+ if(strcmp(cmd, allowed_commands[i].name) == 0)
+ return &allowed_commands[i];
+ }
+
+ return NULL;
+}
+
+bool check_string(const char *str, size_t index, char *err, size_t err_size) {
+ const char *s = str;
+ while(*s) {
+ char c = *s++;
+ if(!((c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= '0' && c <= '9') ||
+ c == ' ' || c == '_' || c == '-' || c == '/' || c == '.')) {
+ snprintf(err, err_size, "command line argument No %zu includes invalid character '%c'", index, c);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool check_params(int argc, char **argv, char *err, size_t err_size) {
+ for(int i = 0 ; i < argc ;i++)
+ if(!check_string(argv[i], i, err, err_size))
+ return false;
+
+ return true;
+}
+
+char *find_variable_in_argv(const char *variable, int argc, char **argv, char *err, size_t err_size) {
+ for (int i = 1; i < argc - 1; i++) {
+ if (strcmp(argv[i], variable) == 0)
+ return strdup(argv[i + 1]);
+ }
+
+ snprintf(err, err_size, "variable '%s' is required, but was not provided in the command line parameters", variable);
+
+ return NULL;
+}
+
+bool search_and_replace_params(struct command *cmd, char **params, size_t max_params, const char *filename, int argc, char **argv, char *err, size_t err_size) {
+ if (!cmd || !params || !max_params) {
+ snprintf(err, err_size, "search_and_replace_params() internal error");
+ return false;
+ }
+
+ const char *delim = " ";
+ char *token;
+ char *temp_params = strdup(cmd->params);
+ if (!temp_params) {
+ snprintf(err, err_size, "search_and_replace_params() cannot allocate memory");
+ return false;
+ }
+
+ size_t param_count = 0;
+ params[param_count++] = strdup(filename);
+
+ token = strtok(temp_params, delim);
+ while (token && param_count < max_params - 1) {
+ size_t len = strlen(token);
+
+ char *value = NULL;
+
+ if (strncmp(token, "{{", 2) == 0 && strncmp(token + len - 2, "}}", 2) == 0) {
+ token[0] = '-';
+ token[1] = '-';
+ token[len - 2] = '\0';
+
+ value = find_variable_in_argv(token, argc, argv, err, err_size);
+ }
+ else
+ value = strdup(token);
+
+ if(!value)
+ goto cleanup;
+
+ params[param_count++] = value;
+ token = strtok(NULL, delim);
+ }
+
+ params[param_count] = NULL; // Null-terminate the params array
+ free(temp_params);
+ return true;
+
+cleanup:
+ if(!err[0])
+ snprintf(err, err_size, "memory allocation failure");
+
+ free(temp_params);
+ for (size_t i = 0; i < param_count; ++i) {
+ free(params[i]);
+ params[i] = NULL;
+ }
+ return false;
+}
+
+void show_help() {
+ fprintf(stdout, "\n");
+ fprintf(stdout, "ndsudo\n");
+ fprintf(stdout, "\n");
+ fprintf(stdout, "(C) Netdata Inc.\n");
+ fprintf(stdout, "\n");
+ fprintf(stdout, "A helper to allow Netdata run privileged commands.\n");
+ fprintf(stdout, "\n");
+ fprintf(stdout, " --test\n");
+ fprintf(stdout, " print the generated command that will be run, without running it.\n");
+ fprintf(stdout, "\n");
+ fprintf(stdout, " --help\n");
+ fprintf(stdout, " print this message.\n");
+ fprintf(stdout, "\n");
+
+ fprintf(stdout, "The following commands are supported:\n\n");
+
+ size_t size = sizeof(allowed_commands) / sizeof(allowed_commands[0]);
+ for(size_t i = 0; i < size ;i++) {
+ fprintf(stdout, "- Command : %s\n", allowed_commands[i].name);
+ fprintf(stdout, " Executables: ");
+ for(size_t j = 0; j < MAX_SEARCH && allowed_commands[i].search[j] ;j++) {
+ fprintf(stdout, "%s ", allowed_commands[i].search[j]);
+ }
+ fprintf(stdout, "\n");
+ fprintf(stdout, " Parameters : %s\n\n", allowed_commands[i].params);
+ }
+
+ fprintf(stdout, "The program searches for executables in the system path.\n");
+ fprintf(stdout, "\n");
+ fprintf(stdout, "Variables given as {{variable}} are expected on the command line as:\n");
+ fprintf(stdout, " --variable VALUE\n");
+ fprintf(stdout, "\n");
+ fprintf(stdout, "VALUE can include space, A-Z, a-z, 0-9, _, -, /, and .\n");
+ fprintf(stdout, "\n");
+}
+
+int main(int argc, char *argv[]) {
+ char error_buffer[ERROR_BUFFER_SIZE] = "";
+
+ if (argc < 2) {
+ fprintf(stderr, "at least 2 parameters are needed, but %d were given.\n", argc);
+ return 1;
+ }
+
+ if(!check_params(argc, argv, error_buffer, sizeof(error_buffer))) {
+ fprintf(stderr, "invalid characters in parameters: %s\n", error_buffer);
+ return 2;
+ }
+
+ bool test = false;
+ const char *cmd = argv[1];
+ if(strcmp(cmd, "--help") == 0 || strcmp(cmd, "-h") == 0) {
+ show_help();
+ exit(0);
+ }
+ else if(strcmp(cmd, "--test") == 0) {
+ cmd = argv[2];
+ test = true;
+ }
+
+ struct command *command = find_command(cmd);
+ if(!command) {
+ fprintf(stderr, "command not recognized: %s\n", cmd);
+ return 3;
+ }
+
+ char new_path[] = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin";
+ setenv("PATH", new_path, 1);
+
+ bool found = false;
+ char filename[FILENAME_MAX];
+
+ for(size_t i = 0; i < MAX_SEARCH && !found ;i++) {
+ if(command->search[i]) {
+ found = command_exists_in_PATH(command->search[i], filename, sizeof(filename));
+ if(!found) {
+ size_t len = strlen(error_buffer);
+ snprintf(&error_buffer[len], sizeof(error_buffer) - len, "%s ", command->search[i]);
+ }
+ }
+ }
+
+ if(!found) {
+ fprintf(stderr, "%s: not available in PATH.\n", error_buffer);
+ return 4;
+ }
+ else
+ error_buffer[0] = '\0';
+
+ char *params[MAX_PARAMETERS];
+ if(!search_and_replace_params(command, params, MAX_PARAMETERS, filename, argc, argv, error_buffer, sizeof(error_buffer))) {
+ fprintf(stderr, "command line parameters are not satisfied: %s\n", error_buffer);
+ return 5;
+ }
+
+ if(test) {
+ fprintf(stderr, "Command to run: \n");
+
+ for(size_t i = 0; i < MAX_PARAMETERS && params[i] ;i++)
+ fprintf(stderr, "'%s' ", params[i]);
+
+ fprintf(stderr, "\n");
+
+ exit(0);
+ }
+ else {
+ char *clean_env[] = {NULL};
+ execve(filename, params, clean_env);
+ perror("execve"); // execve only returns on error
+ return 6;
+ }
+}