]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge topic branch t/for-maint into pu
authorAndre Noll <maan@tuebingen.mpg.de>
Fri, 19 Apr 2024 18:43:25 +0000 (20:43 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Fri, 19 Apr 2024 18:43:25 +0000 (20:43 +0200)
This branch accumulates small but significant hotfixes for known bugs
in the most stable integration branch, "maint". Only serious issues
are fixed here, and no development takes place on this branch.

The purpose of the branch is to give such urgent fixes at least
a small amount of testing in pu before they are incorporated into
maint. During this time the patch can be tweaked, and the documentation
can be improved. Usually the commits of this branch are merged quickly,
however, and it it perfectly normal if this branch is empty.

* refs/heads/t/for-maint:
  play: Fix integer overflow in com_ff().

1  2 
play.c

diff --combined play.c
index bd183b6b8dd48906e08cf142d7ff8c1e05aad48a,e5e053b95e49fb4fad9d412c39b6e30f20924a1b..b28cce63140b4e082249f574e244e4662dc86d3c
--- 1/play.c
--- 2/play.c
+++ b/play.c
@@@ -7,7 -7,6 +7,7 @@@
  #include <lopsub.h>
  
  #include "recv_cmd.lsg.h"
 +#include "filter_cmd.lsg.h"
  #include "play_cmd.lsg.h"
  #include "write_cmd.lsg.h"
  #include "play.lsg.h"
  #include "write.h"
  #include "fd.h"
  
 -/**
 - * Besides playback tasks which correspond to the receiver/filter/writer nodes,
 - * para_play creates two further tasks: The play task and the i9e task. It is
 - * important whether a function can be called in the context of para_play or
 - * i9e or both. As a rule, all command handlers are called only in i9e context via
 - * the line handler (input mode) or the key handler (command mode) below.
 - *
 - * Playlist handling is done exclusively in play context.
 - */
 -
  /** Array of error strings. */
  DEFINE_PARA_ERRLIST;
  
@@@ -41,7 -50,7 +41,7 @@@ static struct lls_parse_result *play_lp
   * Describes a request to change the state of para_play.
   *
   * There is only one variable of this type: \a rq of the global play task
 - * structure. Command handlers only set this variable and the post_select()
 + * structure. Command handlers only set this variable and the post_monitor()
   * function of the play task investigates its value during each iteration of
   * the scheduler run and performs the actual work.
   */
@@@ -108,7 -117,7 +108,7 @@@ INIT_STDERR_LOGGING(loglevel)
  
  char *stat_item_values[NUM_STAT_ITEMS] = {NULL};
  
 -static struct sched sched = {.max_fileno = 0};
 +static struct sched sched;
  static struct play_task play_task, *pt = &play_task;
  
  #define AFH_RECV_CMD (lls_cmd(LSG_RECV_CMD_CMD_AFH, recv_cmd_suite))
@@@ -183,7 -192,6 +183,7 @@@ static char get_playback_state(void
        assert(false);
  };
  
 +/* returns number of milliseconds */
  static long unsigned get_play_time(void)
  {
        char state = get_playback_state();
                return 0;
        if (pt->num_chunks == 0 || pt->seconds == 0)
                return 0;
 -      /* where the stream started (in seconds) */
 -      result = pt->start_chunk * pt->seconds / pt->num_chunks;
 +      /* where the stream started (in milliseconds) */
 +      result = 1000ULL * pt->start_chunk * pt->seconds / pt->num_chunks;
        if (pt->wn.btrn) { /* Add the uptime of the writer node */
                struct timeval diff = {.tv_sec = 0}, wstime;
                btr_get_node_start(pt->wn.btrn, &wstime);
                if (wstime.tv_sec > 0)
                        tv_diff(now, &wstime, &diff);
 -              result += diff.tv_sec;
 +              result += tv2ms(&diff);
        }
 -      result = PARA_MIN(result, pt->seconds);
 +      result = PARA_MIN(result, pt->seconds * 1000);
        result = PARA_MAX(result, 0UL);
        return result;
  }
@@@ -230,7 -238,8 +230,7 @@@ static int get_playback_error(void
                return 0;
        if (task_status(pt->rn.task) >= 0)
                return 0;
 -      if (err == -E_BTR_EOF || err == -E_RECV_EOF || err == -E_EOF
 -                      || err == -E_WRITE_COMMON_EOF)
 +      if (err == -E_EOF)
                return 1;
        return err;
  }
@@@ -256,7 -265,6 +256,7 @@@ static int eof_cleanup(void
        if (decoder->close)
                decoder->close(&pt->fn);
        btr_remove_node(&pt->fn.btrn);
 +      lls_free_parse_result(pt->fn.lpr, FILTER_CMD(pt->fn.filter_num));
        free(pt->fn.conf);
        memset(&pt->fn, 0, sizeof(struct filter_node));
  
@@@ -280,7 -288,7 +280,7 @@@ static int shuffle_compare(__a_unused c
  static void init_shuffle_map(void)
  {
        unsigned n, num_inputs = lls_num_inputs(play_lpr);
 -      shuffle_map = para_malloc(num_inputs * sizeof(unsigned));
 +      shuffle_map = arr_alloc(num_inputs, sizeof(unsigned));
        for (n = 0; n < num_inputs; n++)
                shuffle_map[n] = n;
        if (!OPT_GIVEN(RANDOMIZE))
@@@ -397,16 -405,16 +397,16 @@@ static int load_file(void
        pt->rn.task = task_register(
                &(struct task_info) {
                        .name = lls_command_name(AFH_RECV_CMD),
 -                      .pre_select = AFH_RECV->pre_select,
 -                      .post_select = AFH_RECV->post_select,
 +                      .pre_monitor = AFH_RECV->pre_monitor,
 +                      .post_monitor = AFH_RECV->post_monitor,
                        .context = &pt->rn
                }, &sched);
        sprintf(buf, "%s decoder", af);
        pt->fn.task = task_register(
                &(struct task_info) {
                        .name = buf,
 -                      .pre_select = decoder->pre_select,
 -                      .post_select = decoder->post_select,
 +                      .pre_monitor = decoder->pre_monitor,
 +                      .post_monitor = decoder->post_monitor,
                        .context = &pt->fn
                }, &sched);
        register_writer_node(&pt->wn, pt->fn.btrn, &sched);
@@@ -583,7 -591,7 +583,7 @@@ static char *get_user_key_map_seq(int k
        if (!p)
                return NULL;
        len = p - kma;
 -      result = para_malloc(len + 1);
 +      result = alloc(len + 1);
        memcpy(result, kma, len);
        result[len] = '\0';
        return result;
@@@ -603,7 -611,7 +603,7 @@@ static char *get_key_map_seq_safe(int k
  
        if (len == 1 && isprint(*seq))
                return seq;
 -      sseq = para_malloc(2 + 2 * len + 1);
 +      sseq = alloc(2 + 2 * len + 1);
        sseq[0] = '0';
        sseq[1] = 'x';
        for (n = 0; n < len; n++) {
@@@ -643,7 -651,7 +643,7 @@@ static char **get_mapped_keyseqs(void
        char **result;
        int i;
  
 -      result = para_malloc((NUM_MAPPED_KEYS + 1) * sizeof(char *));
 +      result = arr_alloc(NUM_MAPPED_KEYS + 1, sizeof(char *));
        FOR_EACH_MAPPED_KEY(i) {
                char *seq = get_key_map_seq(i);
                result[i] = seq;
@@@ -832,21 -840,20 +832,21 @@@ EXPORT_PLAY_CMD_HANDLER(play)
  static int com_pause(__a_unused struct lls_parse_result *lpr)
  {
        char state;
 -      long unsigned seconds, ss;
 +      uint64_t ms;
 +      unsigned long cn; /* chunk num */
  
        state = get_playback_state();
        pt->playing = false;
        if (state != 'P')
                return 0;
 -      seconds = get_play_time();
 +      ms = get_play_time();
        pt->playing = false;
 -      ss = 0;
 +      cn = 0;
        if (pt->seconds > 0)
 -              ss = seconds * pt->num_chunks / pt->seconds + 1;
 -      ss = PARA_MAX(ss, 0UL);
 -      ss = PARA_MIN(ss, pt->num_chunks);
 -      pt->start_chunk = ss;
 +              cn = ms * pt->num_chunks / pt->seconds / 1000 + 1;
 +      cn = PARA_MIN(cn, pt->num_chunks);
 +      pt->start_chunk = cn;
 +      pt->rq = CRT_REPOS;
        kill_stream();
        return 0;
  }
@@@ -945,10 -952,10 +945,10 @@@ static int com_ff(struct lls_parse_resu
                return ret;
        if (pt->playing && !pt->fn.btrn)
                return 0;
 -      seconds += get_play_time();
 +      seconds += (get_play_time() + 500) / 1000;
        seconds = PARA_MIN(seconds, (typeof(seconds))pt->seconds - 4);
        seconds = PARA_MAX(seconds, 0);
-       pt->start_chunk = pt->num_chunks * seconds / pt->seconds;
+       pt->start_chunk = (uint64_t)pt->num_chunks * seconds / pt->seconds;
        pt->start_chunk = PARA_MIN(pt->start_chunk, pt->num_chunks - 1);
        pt->start_chunk = PARA_MAX(pt->start_chunk, 0UL);
        if (!pt->playing)
@@@ -1048,9 -1055,9 +1048,9 @@@ static void session_open(void
                char *dot_para = make_message("%s/.paraslash", home);
  
                free(home);
 -              ret = para_mkdir(dot_para, 0777);
 +              ret = para_mkdir(dot_para);
                /* warn, but otherwise ignore mkdir error */
 -              if (ret < 0 && ret != -ERRNO_TO_PARA_ERROR(EEXIST))
 +              if (ret < 0)
                        PARA_WARNING_LOG("Can not create %s: %s\n", dot_para,
                                para_strerror(-ret));
                history_file = make_message("%s/play.history", dot_para);
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGWINCH, &act, NULL);
 -      sched.select_function = i9e_select;
 +      sched.poll_function = i9e_poll;
  
        ici.bound_keyseqs = get_mapped_keyseqs();
        pt->btrn = ici.producer = btr_new_node(&(struct btr_node_description)
@@@ -1095,22 -1102,22 +1095,22 @@@ static void session_update_time_string(
                if (btr_get_input_queue_size(pt->btrn) > 0)
                        return;
        }
 -      ie9_print_status_bar(str, len);
 +      i9e_print_status_bar(str, len);
  }
  
  /*
   * If we are about to die we must call i9e_close() to reset the terminal.
   * However, i9e_close() must be called in *this* context, i.e. from
 - * play_task.post_select() rather than from i9e_post_select(), because
 + * play_task.post_monitor() rather than from i9e_post_monitor(), because
   * otherwise i9e would access freed memory upon return. So the play task must
   * stay alive until the i9e task terminates.
   *
   * We achieve this by sending a fake SIGTERM signal via i9e_signal_dispatch()
 - * and reschedule. In the next iteration, i9e->post_select returns an error and
 + * and reschedule. In the next iteration, i9e->post_monitor returns an error and
   * terminates. Subsequent calls to i9e_get_error() then return negative and we
   * are allowed to call i9e_close() and terminate as well.
   */
 -static int session_post_select(__a_unused struct sched *s)
 +static int session_post_monitor(__a_unused struct sched *s)
  {
        int ret;
  
  
  #else /* HAVE_READLINE */
  
 -static int session_post_select(struct sched *s)
 +static int session_post_monitor(struct sched *s)
  {
        char c;
  
 -      if (!FD_ISSET(STDIN_FILENO, &s->rfds))
 +      if (!sched_read_ok(STDIN_FILENO, s))
                return 0;
        if (read(STDIN_FILENO, &c, 1))
                do_nothing;
@@@ -1156,11 -1163,11 +1156,11 @@@ static void session_update_time_string(
  }
  #endif /* HAVE_READLINE */
  
 -static void play_pre_select(struct sched *s, __a_unused void *context)
 +static void play_pre_monitor(struct sched *s, __a_unused void *context)
  {
        char state;
  
 -      para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno);
 +      sched_monitor_readfd(STDIN_FILENO, s);
        state = get_playback_state();
        if (state == 'R' || state == 'F' || state == 'X')
                return sched_min_delay(s);
@@@ -1180,7 -1187,7 +1180,7 @@@ static unsigned get_time_string(char **
        length = pt->seconds;
        if (length == 0)
                return xasprintf(result, "0:00 [0:00] (0%%/0:00)");
 -      seconds = get_play_time();
 +      seconds = (get_play_time() + 500) / 1000;
        return xasprintf(result, "#%u: %d:%02d [%d:%02d] (%d%%/%d:%02d) %s",
                pt->current_file,
                seconds / 60,
        );
  }
  
 -static int play_post_select(struct sched *s, __a_unused void *context)
 +static int play_post_monitor(struct sched *s, __a_unused void *context)
  {
        int ret;
  
                pt->rq = CRT_TERM_RQ;
                return 0;
        }
 -      ret = session_post_select(s);
 +      ret = session_post_monitor(s);
        if (ret < 0)
                goto out;
        if (!pt->wn.btrn && !pt->fn.btrn) {
  /**
   * The main function of para_play.
   *
 - * \param argc Standard.
 - * \param argv Standard.
 + * \param argc See man page.
 + * \param argv See man page.
 + *
 + * para_play distributes its work by submitting various tasks to the paraslash
 + * scheduler. The receiver, filter and writer tasks, which are used to play an
 + * audio file, require one task each to maintain their underlying buffer tree
 + * node. These tasks only exist when an audio file is playing.
 + *
 + * The i9 task, which is submitted and maintained by the i9e subsystem, reads
 + * an input line and calls the corresponding command handler such as com_stop()
 + * which is implemented in this file. The command handlers typically write a
 + * request to the global play_task structure, whose contents are read and acted
 + * upon by another task, the play task.
 + *
 + * As a rule, playlist handling is performed exclusively in play context, i.e.
 + * in the post-monitor step of the play task, while command handlers are only
 + * called in i9e context.
   *
 - * \return \p EXIT_FAILURE or \p EXIT_SUCCESS.
 + * \return EXIT_FAILURE or EXIT_SUCCESS.
   */
  int main(int argc, char *argv[])
  {
        int ret;
        unsigned num_inputs;
  
 -      sched.default_timeout.tv_sec = 5;
 +      sched.default_timeout = 5000;
        parse_config_or_die(argc, argv);
        session_open();
        num_inputs = lls_num_inputs(play_lpr);
        init_shuffle_map();
 -      pt->invalid = para_calloc(sizeof(*pt->invalid) * num_inputs);
 +      pt->invalid = arr_zalloc(num_inputs, sizeof(*pt->invalid));
        pt->rq = CRT_FILE_CHANGE;
        pt->playing = true;
        pt->task = task_register(&(struct task_info){
                .name = "play",
 -              .pre_select = play_pre_select,
 -              .post_select = play_post_select,
 +              .pre_monitor = play_pre_monitor,
 +              .post_monitor = play_post_monitor,
                .context = pt,
        }, &sched);
        ret = schedule(&sched);