tools

various tools I have been using throughout the years
Log | Files | Refs | README | LICENSE

mpd_update_library.c (5977B)


      1 #define _POSIX_C_SOURCE 200809L
      2 
      3 #include "mpd_common.h"
      4 
      5 #include <errno.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 #include <time.h>
      9 #include <unistd.h>
     10 
     11 #define UPDATE_RETRIES 5U
     12 
     13 static void usage(FILE *file, const char *program) {
     14   fprintf(file, "Usage: %s [--no-wait] [--rescan] [FILE|-]\n", program);
     15 }
     16 
     17 static int compare_strings(const void *left, const void *right) {
     18   const char *const *a = left;
     19   const char *const *b = right;
     20   return strcmp(*a, *b);
     21 }
     22 
     23 static bool top_directories(const struct mt_strvec *paths,
     24                             struct mt_strvec *directories) {
     25   for (size_t i = 0; i < paths->len; i++) {
     26     const char *slash = strchr(paths->items[i], '/');
     27     size_t length = slash == NULL ? strlen(paths->items[i])
     28                                   : (size_t)(slash - paths->items[i]);
     29     if (!mt_strvec_push_n(directories, paths->items[i], length)) {
     30       perror("mpd: store update directory");
     31       return false;
     32     }
     33   }
     34 
     35   qsort(directories->items, directories->len, sizeof(*directories->items),
     36         compare_strings);
     37   size_t output = 0;
     38   for (size_t i = 0; i < directories->len; i++) {
     39     if (output > 0U &&
     40         strcmp(directories->items[output - 1U], directories->items[i]) == 0) {
     41       free(directories->items[i]);
     42     } else {
     43       directories->items[output++] = directories->items[i];
     44     }
     45   }
     46   directories->len = output;
     47   return true;
     48 }
     49 
     50 static void retry_delay(unsigned attempt) {
     51   unsigned milliseconds = 250U << attempt;
     52   if (milliseconds > 4000U)
     53     milliseconds = 4000U;
     54   struct timespec delay = {.tv_sec = (time_t)(milliseconds / 1000U),
     55                            .tv_nsec = (long)(milliseconds % 1000U) * 1000000L};
     56   while (nanosleep(&delay, &delay) < 0 && errno == EINTR) {
     57   }
     58 }
     59 
     60 static struct mpd_connection *reconnect(unsigned attempt) {
     61   if (attempt > 0U)
     62     retry_delay(attempt - 1U);
     63   return mt_connect();
     64 }
     65 
     66 static bool submit_directory(struct mpd_connection **connection,
     67                              const char *directory, bool rescan) {
     68   for (unsigned attempt = 0; attempt < UPDATE_RETRIES; attempt++) {
     69     if (*connection == NULL)
     70       *connection = reconnect(attempt);
     71     if (*connection == NULL)
     72       continue;
     73 
     74     unsigned id = rescan ? mpd_run_rescan(*connection, directory)
     75                          : mpd_run_update(*connection, directory);
     76     if (id != 0U) {
     77       printf("Submitted update for '%s' (job %u).\n", directory, id);
     78       return true;
     79     }
     80 
     81     enum mpd_error error = mpd_connection_get_error(*connection);
     82     mt_report_error(*connection, rescan ? "submit rescan" : "submit update");
     83     if (error == MPD_ERROR_SERVER || error == MPD_ERROR_ARGUMENT)
     84       return false;
     85     mpd_connection_free(*connection);
     86     *connection = NULL;
     87   }
     88   fprintf(stderr, "mpd: exhausted retries for '%s'\n", directory);
     89   return false;
     90 }
     91 
     92 static bool wait_for_updates(struct mpd_connection **connection) {
     93   for (unsigned failures = 0; failures < UPDATE_RETRIES;) {
     94     if (*connection == NULL)
     95       *connection = reconnect(failures);
     96     if (*connection == NULL) {
     97       failures++;
     98       continue;
     99     }
    100 
    101     struct mpd_status *status = mpd_run_status(*connection);
    102     if (status == NULL) {
    103       mt_report_error(*connection, "check update status");
    104       mpd_connection_free(*connection);
    105       *connection = NULL;
    106       failures++;
    107       continue;
    108     }
    109     unsigned update_id = mpd_status_get_update_id(status);
    110     mpd_status_free(status);
    111     if (update_id == 0U)
    112       return true;
    113 
    114     enum mpd_idle event = mpd_run_idle_mask(*connection, MPD_IDLE_UPDATE);
    115     if ((event & MPD_IDLE_UPDATE) != 0) {
    116       failures = 0;
    117       continue;
    118     }
    119     mt_report_error(*connection, "wait for database update");
    120     mpd_connection_free(*connection);
    121     *connection = NULL;
    122     failures++;
    123   }
    124   fprintf(stderr, "mpd: exhausted retries while waiting for updates\n");
    125   return false;
    126 }
    127 
    128 int main(int argc, char **argv) {
    129   bool wait = true;
    130   bool rescan = false;
    131   const char *path = NULL;
    132   for (int i = 1; i < argc; i++) {
    133     if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
    134       usage(stdout, argv[0]);
    135       return 0;
    136     }
    137     if (strcmp(argv[i], "--no-wait") == 0) {
    138       wait = false;
    139     } else if (strcmp(argv[i], "--rescan") == 0) {
    140       rescan = true;
    141     } else if (argv[i][0] == '-' && strcmp(argv[i], "-") != 0) {
    142       usage(stderr, argv[0]);
    143       return 2;
    144     } else if (path == NULL) {
    145       path = argv[i];
    146     } else {
    147       usage(stderr, argv[0]);
    148       return 2;
    149     }
    150   }
    151 
    152   struct mt_strvec paths;
    153   struct mt_strvec directories;
    154   mt_strvec_init(&paths);
    155   mt_strvec_init(&directories);
    156   bool ok = true;
    157 
    158   if (path == NULL && isatty(STDIN_FILENO)) {
    159     ok = mt_strvec_push(&directories, "");
    160   } else {
    161     bool close_input;
    162     FILE *input = mt_open_input(path, &close_input);
    163     if (input == NULL)
    164       ok = false;
    165     else {
    166       ok = mt_read_lines(input, &paths);
    167       if (close_input && fclose(input) != 0) {
    168         perror("mpd: close input");
    169         ok = false;
    170       }
    171       if (ok)
    172         ok = top_directories(&paths, &directories);
    173     }
    174   }
    175   if (!ok) {
    176     mt_strvec_clear(&paths);
    177     mt_strvec_clear(&directories);
    178     return 1;
    179   }
    180   if (directories.len == 0U) {
    181     printf("No directories found in input.\n");
    182     mt_strvec_clear(&paths);
    183     mt_strvec_clear(&directories);
    184     return 0;
    185   }
    186 
    187   struct mpd_connection *connection = NULL;
    188   for (size_t i = 0; i < directories.len; i++) {
    189     if (!submit_directory(&connection, directories.items[i], rescan)) {
    190       ok = false;
    191       break;
    192     }
    193   }
    194   if (ok && wait) {
    195     printf("Waiting for database updates to finish...\n");
    196     ok = wait_for_updates(&connection);
    197   }
    198   if (ok)
    199     printf("Processed %zu director%s successfully.\n", directories.len,
    200            directories.len == 1U ? "y" : "ies");
    201 
    202   if (connection != NULL)
    203     mpd_connection_free(connection);
    204   mt_strvec_clear(&paths);
    205   mt_strvec_clear(&directories);
    206   return ok ? 0 : 1;
    207 }