diff --git a/lewdfingerd.c b/lewdfingerd.c index 77a89df..fd60f5d 100644 --- a/lewdfingerd.c +++ b/lewdfingerd.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,8 @@ #include #include #include +#include +#include #include struct pollfd *listens = NULL; @@ -144,9 +147,134 @@ ssize_t writeall_str(int fd, const char *s) { } bool execute_finger_response(int sock, const char *finger_response_file) { - // TODO: Execute the file - (void)sock; - (void)finger_response_file; + int pipes[2]; + if(pipe(pipes) != 0) { + log_perror("pipe"); + return false; + } + + pid_t child = fork(); + if(child < 0) { + log_perror("fork"); + return false; + } + + if(child == 0) { + // Child + + // Close unneeded fds + for(size_t i = 0; i < num_listens; i++) { + close(listens[num_listens].fd); + } + close(sock); + close(pipes[0]); + + // Set up stdout + if(dup2(pipes[1], STDOUT_FILENO) < 0) { + log_perror("dup2"); + _exit(1); + } + + // execv's types require this cast + char *argv[] = {(char*) finger_response_file, NULL}; + execv(finger_response_file, argv); + + log_perror("execv"); + _exit(1); + } else { + // Parent + + // Close unneeded fds + close(pipes[1]); + + struct timespec start_time; + // Doesn't seem this could fail in our configuration + clock_gettime(CLOCK_MONOTONIC, &start_time); + + char iobuf[1024]; + + struct pollfd pipe_poll = {.fd = pipes[0], .events = POLLIN}; + for(;;) { + // Check the child is still running + int status; + waitpid(child, &status, WNOHANG); + if(WIFEXITED(status) || WIFSIGNALED(status)) { + // Child quit + if(WIFSIGNALED(status)) { + log_error("Child died of signal %i\n", WTERMSIG(status)); + } else if(WIFEXITED(status) && WEXITSTATUS(status) != 0) { + log_error("Child exited with nonzero return code %i\n", WEXITSTATUS(status)); + } + break; + } + + struct timespec current_time; + // See previous usage of this + clock_gettime(CLOCK_MONOTONIC, ¤t_time); + + time_t secs_passed = current_time.tv_sec - start_time.tv_sec; + long ns_passed = current_time.tv_nsec - start_time.tv_nsec; + int ms_passed = secs_passed * 1000 + ns_passed / 1000; + // Timeout of 5s + int ms_remaining = 5000 - ms_passed; + + if(ms_remaining <= 0) { + log_error("Child ran too long\n"); + if(kill(child, SIGKILL)) { + log_perror("kill"); + } + + // Wait to get rid of the zombie + waitpid(child, NULL, 0); + + if(writeall_str(sock, "Timeout\r\n") < 0) { + log_perror("writeall_str"); + close(pipes[0]); + return false; + } + + break; + } + + int amount_ready = poll(&pipe_poll, 1, ms_remaining); + + for(int i = 0; i < amount_ready; i++) { + if(pipe_poll.revents & POLLIN) { + ssize_t amount_read = read(pipes[0], iobuf, sizeof(iobuf)); + if(amount_read < 0) { + log_perror("read"); + break; + } + + char *newline_location = memchr(iobuf, '\n', amount_read); + if(newline_location == NULL) { + // No newline, write entire buffer + if(writeall(sock, iobuf, amount_read) < 0) { + log_perror("writeall"); + break; + } + } else { + // Newline found. Write stuff before it and \r\n + if(writeall(sock, iobuf, newline_location - iobuf) < 0 || writeall_str(sock, "\r\n") < 0) { + log_perror("writeall / writeall_str"); + break; + } + + // Write the part after it + char *part_after_nl = newline_location + 1; + size_t part_after_nl_length = amount_read - (part_after_nl - iobuf); + if(writeall(sock, part_after_nl, part_after_nl_length) < 0) { + log_perror("writeall"); + break; + } + } + } + } + } + + close(pipes[0]); + } + return true; }