dwmblocks

Kris's build of dwmblocks
git clone git clone https://git.krisyotam.com/krisyotam/dwmblocks.git
Log | Files | Refs | README | LICENSE

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 }