block.c (3989B)
1 #include "block.h" 2 3 #include <stdbool.h> 4 #include <stddef.h> 5 #include <stdint.h> 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <sys/types.h> 10 #include <sys/wait.h> 11 #include <unistd.h> 12 13 #include "config.h" 14 #include "util.h" 15 16 block block_new(const char *const icon, const char *const command, 17 const unsigned int interval, const int signal) { 18 block block = { 19 .icon = icon, 20 .command = command, 21 .interval = interval, 22 .signal = signal, 23 24 .output = {[0] = '\0'}, 25 .fork_pid = -1, 26 }; 27 28 return block; 29 } 30 31 int block_init(block *const block) { 32 if (pipe(block->pipe) != 0) { 33 (void)fprintf(stderr, 34 "error: could not create a pipe for \"%s\" block\n", 35 block->command); 36 return 1; 37 } 38 39 return 0; 40 } 41 42 int block_deinit(block *const block) { 43 int status = close(block->pipe[READ_END]); 44 status |= close(block->pipe[WRITE_END]); 45 if (status != 0) { 46 (void)fprintf(stderr, "error: could not close \"%s\" block's pipe\n", 47 block->command); 48 return 1; 49 } 50 51 return 0; 52 } 53 54 int block_execute(block *const block, const uint8_t button) { 55 // Ensure only one child process exists per block at an instance. 56 if (block->fork_pid != -1) { 57 return 0; 58 } 59 60 block->fork_pid = fork(); 61 if (block->fork_pid == -1) { 62 (void)fprintf( 63 stderr, "error: could not create a subprocess for \"%s\" block\n", 64 block->command); 65 return 1; 66 } 67 68 if (block->fork_pid == 0) { 69 const int write_fd = block->pipe[WRITE_END]; 70 int status = close(block->pipe[READ_END]); 71 72 if (button != 0) { 73 char button_str[4]; 74 (void)snprintf(button_str, LEN(button_str), "%hhu", button); 75 status |= setenv("BLOCK_BUTTON", button_str, 1); 76 } 77 78 const char null = '\0'; 79 if (status != 0) { 80 (void)write(write_fd, &null, sizeof(null)); 81 exit(EXIT_FAILURE); 82 } 83 84 FILE *const file = popen(block->command, "r"); 85 if (file == NULL) { 86 (void)write(write_fd, &null, sizeof(null)); 87 exit(EXIT_FAILURE); 88 } 89 90 // Ensure null-termination since fgets() will leave buffer untouched on 91 // no output. 92 char buffer[LEN(block->output)] = {[0] = null}; 93 (void)fgets(buffer, LEN(buffer), file); 94 95 // Remove trailing newlines. 96 const size_t length = strcspn(buffer, "\n"); 97 buffer[length] = null; 98 99 // Exit if command execution failed or if file could not be closed. 100 if (pclose(file) != 0) { 101 (void)write(write_fd, &null, sizeof(null)); 102 exit(EXIT_FAILURE); 103 } 104 105 const size_t output_size = 106 truncate_utf8_string(buffer, LEN(buffer), MAX_BLOCK_OUTPUT_LENGTH); 107 (void)write(write_fd, buffer, output_size); 108 109 exit(EXIT_SUCCESS); 110 } 111 112 return 0; 113 } 114 115 int block_update(block *const block) { 116 char buffer[LEN(block->output)]; 117 118 const ssize_t bytes_read = 119 read(block->pipe[READ_END], buffer, LEN(buffer)); 120 if (bytes_read == -1) { 121 (void)fprintf(stderr, 122 "error: could not fetch output of \"%s\" block\n", 123 block->command); 124 return 2; 125 } 126 127 // Collect exit-status of the subprocess to avoid zombification. 128 int fork_status = 0; 129 if (waitpid(block->fork_pid, &fork_status, 0) == -1) { 130 (void)fprintf(stderr, 131 "error: could not obtain exit status for \"%s\" block\n", 132 block->command); 133 return 2; 134 } 135 block->fork_pid = -1; 136 137 if (fork_status != 0) { 138 (void)fprintf(stderr, 139 "error: \"%s\" block exited with non-zero status\n", 140 block->command); 141 return 1; 142 } 143 144 (void)strncpy(block->output, buffer, LEN(buffer)); 145 146 return 0; 147 }