123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- #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;
- }
- 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;
- }
- }
|