#include #include #include #include #include #include #include #include #include #include "nfcommander.h" #include "command.h" #include "config.h" #define COMMAND_ALIVE 1 #define COMMAND_STOPPED 2 #define INPUT_BUFFER_MAX 4096 typedef struct { uint8_t flags; pid_t child; int iopipe[2]; nfc_tag_info_t* tag; } command_t; static size_t ncommands = 0; static command_t* commands = NULL; //TODO timeouting / SIGKILL int command_handle(int fd){ size_t n = 0; ssize_t bytes; uint8_t input_buffer[INPUT_BUFFER_MAX]; //find matching command for(n = 0; n < ncommands; n++){ if(commands[n].flags & COMMAND_ALIVE && fd == commands[n].iopipe[0]){ bytes = read(fd, input_buffer, sizeof(input_buffer) - 1); if(bytes < 0){ perror("cmd/recv"); } else if(bytes == 0){ printf("Command %lu pipe closed\n", n); core_manage_fd(commands[n].iopipe[0], 0, system_command); close(commands[n].iopipe[0]); commands[n].iopipe[0] = -1; } else{ //TODO handle input_buffer[bytes] = 0; printf("%d bytes from command %lu: %s\n", bytes, n, input_buffer); } return 0; } } printf("Unhandled command fd\n"); return 1; } void command_reap(){ int wait_status; pid_t status; size_t n; do { status = waitpid(-1, &wait_status, WNOHANG); if(status < 0 && errno != ECHILD){ perror("cmd/reap"); } if(status <= 0){ return; } //find matching command if(status){ for(n = 0; n < ncommands; n++){ if(commands[n].child == status){ commands[n].flags = 0; commands[n].child = -1; if(commands[n].iopipe[0] >= 0){ core_manage_fd(commands[n].iopipe[0], 0, system_command); close(commands[n].iopipe[0]); commands[n].iopipe[0] = -1; } printf("Command %lu terminated\n", n); } } } } while(status); } static int command_spawn(command_t* cmd, char* command_name, char* command_static, size_t arg_length){ char* workdir = config_get("command", "chdir"); char* handler = config_get("command", "handler"); printf("Starting command %s, static %s\n", command_name, command_static); if(pipe(cmd->iopipe)){ perror("cmd/pipe"); return 1; } if(fcntl(cmd->iopipe[0], F_SETFD, FD_CLOEXEC)){ perror("cmd/fcntl"); } cmd->child = fork(); switch(cmd->child){ case -1: perror("cmd/fork"); return 1; case 0: //child if(workdir && chdir(workdir)){ perror("child/chdir"); _exit(1); } //make child a session leader setpgrp(); //connect stdout while((dup2(cmd->iopipe[1], STDOUT_FILENO) == -1) && (errno == EINTR)) { } close(cmd->iopipe[1]); //TODO set environment //run actual command if(handler){ execl(handler, handler, command_name, command_static, NULL); } else{ execl(command_name, command_name, command_static, NULL); } perror("child/exec"); _exit(1); default: //parent cmd->flags |= COMMAND_ALIVE; close(cmd->iopipe[1]); core_manage_fd(cmd->iopipe[0], 1, system_command); } return 0; } int command_start(nfc_tag_info_t* info){ size_t n = 0, p = ncommands, arg_length = 0; uint8_t* command_name = NULL, *command_static = NULL; for(n = 0; n < ncommands; n++){ //if command already in list, do nothing if(commands[n].tag == info){ //this should not normally happen return 0; } //mark available slot if(!commands[n].tag && !commands[n].flags){ p = n; } } //cant run without an actual command if(!info->static_data || !info->static_length){ printf("Tag contains no command data\n"); return 0; } //parse out command data command_name = calloc(info->static_length + 1, sizeof(uint8_t)); if(!command_name){ printf("Failed to allocate memory\n"); return 1; } memcpy(command_name, info->static_data, info->static_length); for(n = 0; n < info->static_length; n++){ if(!isalnum(command_name[n])){ command_name[n] = 0; if(info->static_length - n > 1){ command_static = command_name + n + 1; arg_length = info->static_length - n - 1; } break; } } if(!strlen((char*) command_name)){ printf("Tag contains invalid command data\n"); free(command_name); return 1; } //FIXME might want to parse out any non-graphs if(p == ncommands){ commands = realloc(commands, (p + 1) * sizeof(command_t)); if(!commands){ ncommands = 0; printf("Failed to allocate memory\n"); free(command_name); return 1; } memset(commands + p, 0, sizeof(command_t)); ncommands++; } commands[p].tag = info; if(command_spawn(commands + p, (char*) command_name, (char*) command_static, arg_length)){ //TODO clean up and free command instance } free(command_name); return 0; } static void command_stop_internal(size_t n){ //send SIGTERM to process group if(kill(-commands[n].child, (commands[n].flags & COMMAND_STOPPED) ? SIGKILL : SIGTERM)){ perror("cmd/stop"); } commands[n].flags |= COMMAND_STOPPED; } int command_stop(nfc_tag_info_t* info){ size_t n = 0; for(n = 0; n < ncommands; n++){ if(commands[n].tag == info){ commands[n].tag = NULL; if(commands[n].flags & COMMAND_ALIVE){ command_stop_internal(n); } } } return 0; } void command_free(){ size_t n = 0; printf("Shutting down any remaining commands\n"); for(n = 0; n < ncommands; n++){ while(commands[n].flags & COMMAND_ALIVE){ command_stop_internal(n); command_reap(); } } ncommands = 0; free(commands); commands = NULL; }