Exemplo n.º 1
0
// wait for the S3 GET/PUT to complete
int stream_sync(ObjectStream* os) {

   ///   // TBD: this should be a per-repo config-option
   ///   static const time_t timeout_sec = 5;
   void* retval;

   // fuse may call fuse-flush multiple times (one for every open stream).
   // but will not call flush after calling close().
   if (! (os->flags & OSF_OPEN)) {
      LOG(LOG_ERR, "%s isn't open\n", os->url);
      errno = EINVAL;            /* ?? */
      return -1;
   }

   // See NOTE, above, regarding the difference between reads and writes.
   if (! pthread_tryjoin_np(os->op, &retval)) {
      LOG(LOG_INFO, "op-thread joined\n");
      os->flags |= OSF_JOINED;
   }
   else {


      // If a stream_get/put timed-out waiting for their
      // writefunc/readfunc, then the locks are likely in an inconsistent
      // state.  [Either (a) the readfunc never posted iob_empty for
      // stream__put(), or (b) the writefunc never posted iob_full for
      // stream_get().]  In either case, the operation started by
      // stream_open() is declared a failure, and we shouldn't do any of
      // the normal cleanup stuff, like trying to write recovery-info, etc.
      // But we do need to the thread to stop before we return, because if
      // the writefunc/readfunc gets another callback, it will access parts
      // of the ObjectStream that are about to be deallocated.
      if (os->flags & OSF_TIMEOUT) {
         LOG(LOG_INFO, "cancelling timed-out thread\n");
         int rc = pthread_cancel(os->op);
         if (rc) {
            LOG(LOG_ERR, "cancellation failed (%s), killing thread\n",
                strerror(errno));
            pthread_kill(os->op, SIGKILL);
            LOG(LOG_INFO, "killed thread\n");
         }

         LOG(LOG_INFO, "waiting for terminated op-thread\n");
         if (stream_wait(os)) {
            LOG(LOG_ERR, "err joining op-thread ('%s')\n", strerror(errno));
            return -1;
         }
      }

      // In this case, the get/put timed-out, but it did so inside SAFE_WAIT_KILL(),
      // rather than SAFE_WAIT(), so the thread has already been cancelled
      // and joined.
      else if (os->flags & OSF_TIMEOUT_K) {
         LOG(LOG_INFO, "timed-out thread already killed\n");
         LOG(LOG_INFO, "op-thread returned %d\n", os->op_rc);
         errno = (os->op_rc ? EIO : 0);
         return os->op_rc;
      }


#if (LIBCURL_VERSION_MAJOR > 7) || ((LIBCURL_VERSION_MAJOR == 7) && (LIBCURL_VERSION_MINOR >= 45))
      // Our installed version of libcurl is 7.19.7.  We are experimenting
      // with a custom-built libcurl based on 7.45.0.  We notice that the
      // latter does not call streaming_readfunc() again, if stream_open()
      // provided a content-length (i.e. the request had a content-length
      // header, rather than being chunked-transfer-encoded), and the full
      // number of chars matching the content-length header has been sent.
      // In such a case, stream_sync() shouldn't use "stream_put(..0)" to
      // get the readfunc to quit, because the readfunc isn't running.
      //
      // NOTE: This behavior may actually be present in earlier versions of
      //     libcurl, in which case, the VERSION_MINOR here should be
      //     adjusted downwards, toward 19.
      else if ((os->flags & OSF_WRITING) &&
               os->content_len &&
               (os->content_len == os->written)) {
         LOG(LOG_INFO, "(wr) wrote content-len, no action needed (flags=0x%04x)\n",
             os->flags);
      }
#endif

      // signal EOF to readfunc
      else if (os->flags & OSF_WRITING) {
         LOG(LOG_INFO, "(wr) sending empty buffer (flags=0x%04x)\n", os->flags);
         if (stream_put(os, NULL, 0)) {
            LOG(LOG_ERR, "stream_put(0) failed\n");
            pthread_kill(os->op, SIGKILL);
            LOG(LOG_INFO, "killed thread\n");
         }
      }

      // signal QUIT to writefunc
      else {
         LOG(LOG_INFO, "(rd) sending empty buffer (flags=0x%04x)\n", os->flags);
         if (stream_get(os, NULL, 0)) {
            LOG(LOG_ERR, "stream_get(0) failed\n");
            pthread_kill(os->op, SIGKILL);
            LOG(LOG_INFO, "killed thread\n");
         }
      }


      // check whether thread has returned.  Could mean a curl
      // error, an S3 protocol error, or server flaking out.
      LOG(LOG_INFO, "waiting for op-thread\n");
      if (stream_wait(os)) {
         LOG(LOG_ERR, "err joining op-thread ('%s')\n", strerror(errno));
         return -1;
      }
   }


   // thread has completed
   os->flags |= OSF_JOINED;

   if ((   os->flags & OSF_READING)
       && (os->op_rc == CURLE_WRITE_ERROR)) {
      // when we signalled writefunc to quit, it provoked this
      LOG(LOG_INFO, "op-thread returned CURLE_WRITE_ERROR as expected\n");
      return 0;
   }
   else {
      LOG(LOG_INFO, "op-thread returned %d\n", os->op_rc);
      errno = (os->op_rc ? EIO : 0);
      return os->op_rc;
   }
}
Exemplo n.º 2
0
/* Starts the process whose arguments are given in the null-terminated array
 * 'argv' and waits for it to exit.  On success returns 0 and stores the
 * process exit value (suitable for passing to process_status_msg()) in
 * '*status'.  On failure, returns a positive errno value and stores 0 in
 * '*status'.
 *
 * If 'stdout_log' is nonnull, then the subprocess's output to stdout (up to a
 * limit of PROCESS_MAX_CAPTURE bytes) is captured in a memory buffer, which
 * when this function returns 0 is stored as a null-terminated string in
 * '*stdout_log'.  The caller is responsible for freeing '*stdout_log' (by
 * passing it to free()).  When this function returns an error, '*stdout_log'
 * is set to NULL.
 *
 * If 'stderr_log' is nonnull, then it is treated like 'stdout_log' except
 * that it captures the subprocess's output to stderr. */
int
process_run_capture(char **argv, char **stdout_log, char **stderr_log,
                    int *status)
{
    struct stream s_stdout, s_stderr;
    sigset_t oldsigs;
    pid_t pid;
    int error;

    COVERAGE_INC(process_run_capture);
    if (stdout_log) {
        *stdout_log = NULL;
    }
    if (stderr_log) {
        *stderr_log = NULL;
    }
    *status = 0;
    error = process_prestart(argv);
    if (error) {
        return error;
    }

    error = stream_open(&s_stdout);
    if (error) {
        return error;
    }

    error = stream_open(&s_stderr);
    if (error) {
        stream_close(&s_stdout);
        return error;
    }

    block_sigchld(&oldsigs);
    pid = fork();
    if (pid < 0) {
        int error = errno;

        unblock_sigchld(&oldsigs);
        VLOG_WARN("fork failed: %s", strerror(error));

        stream_close(&s_stdout);
        stream_close(&s_stderr);
        *status = 0;
        return error;
    } else if (pid) {
        /* Running in parent process. */
        struct process *p;

        p = process_register(argv[0], pid);
        unblock_sigchld(&oldsigs);

        close(s_stdout.fds[1]);
        close(s_stderr.fds[1]);
        while (!process_exited(p)) {
            stream_read(&s_stdout);
            stream_read(&s_stderr);

            stream_wait(&s_stdout);
            stream_wait(&s_stderr);
            process_wait(p);
            poll_block();
        }
        stream_read(&s_stdout);
        stream_read(&s_stderr);

        if (stdout_log) {
            *stdout_log = ds_steal_cstr(&s_stdout.log);
        }
        if (stderr_log) {
            *stderr_log = ds_steal_cstr(&s_stderr.log);
        }

        stream_close(&s_stdout);
        stream_close(&s_stderr);

        *status = process_status(p);
        process_destroy(p);
        return 0;
    } else {
        /* Running in child process. */
        int max_fds;
        int i;

        fatal_signal_fork();
        unblock_sigchld(&oldsigs);

        dup2(get_null_fd(), 0);
        dup2(s_stdout.fds[1], 1);
        dup2(s_stderr.fds[1], 2);

        max_fds = get_max_fds();
        for (i = 3; i < max_fds; i++) {
            close(i);
        }

        execvp(argv[0], argv);
        fprintf(stderr, "execvp(\"%s\") failed: %s\n",
                argv[0], strerror(errno));
        exit(EXIT_FAILURE);
    }
}
Exemplo n.º 3
0
int stream_abort(ObjectStream* os) {

   ///   // TBD: this should be a per-repo config-option
   ///   static const time_t timeout_sec = 5;

   // fuse may call fuse-flush multiple times (one for every open stream).
   // but will not call flush after calling close().
   if (! (os->flags & OSF_OPEN)) {
      LOG(LOG_ERR, "%s isn't open\n", os->url);
      errno = EINVAL;            /* ?? */
      return -1;
   }
   else if (! (os->flags & OSF_WRITING)) {
      LOG(LOG_ERR, "%s aborting a read-stream is not supported\n", os->url);
      errno = ENOSYS;
      return -1;
   }

   // See NOTE, above, regarding the difference between reads and writes.
   void* retval;
   if (! pthread_tryjoin_np(os->op, &retval)) {
      LOG(LOG_INFO, "op-thread joined\n");
      os->flags |= OSF_JOINED;
   }
   else {

      if (os->flags & OSF_WRITING) {
         // signal ABORT to readfunc
         LOG(LOG_INFO, "(wr) sending (char*)1 (flags=0x%04x)\n", os->flags);
         os->flags |= OSF_ABORT;
         if (stream_put(os, (const char*)1, 1) != 1) {
            LOG(LOG_ERR, "stream_put((char*)1) failed\n");
            pthread_kill(os->op, SIGKILL);
            LOG(LOG_INFO, "killed thread\n");
         }
      }

      // check whether thread has returned.  Could mean a curl
      // error, an S3 protocol error, or server flaking out.
      LOG(LOG_INFO, "waiting for op-thread\n");
      if (stream_wait(os)) {
         LOG(LOG_ERR, "err joining op-thread\n");
         return -1;
      }
   }

   // thread has completed
   os->flags |= OSF_JOINED;
   LOG(LOG_INFO, "op-thread returned %d\n", os->op_rc);

   if ((os->op_rc == CURLE_ABORTED_BY_CALLBACK)
       && (os->iob.read_pos == (char*)1)) {

      LOG(LOG_INFO, "op-thread return is as expected for ABORT\n");
      return 0;
   }
   else {
      errno = (os->op_rc ? EINVAL : 0);
      return os->op_rc;
   }
}
Exemplo n.º 4
0
static int Open(vlc_object_t *obj)
{
    demux_t *demux = (demux_t *)obj;

    demux_sys_t *sys = malloc(sizeof (*sys));
    if (unlikely(sys == NULL))
        return VLC_ENOMEM;

    sys->context = vlc_pa_connect(obj, &sys->mainloop);
    if (sys->context == NULL) {
        free(sys);
        return VLC_EGENERIC;
    }

    sys->stream = NULL;
    sys->es = NULL;
    sys->discontinuity = false;
    sys->caching = INT64_C(1000) * var_InheritInteger(obj, "live-caching");
    demux->p_sys = sys;

    /* Stream parameters */
    struct pa_sample_spec ss;
    ss.format = PA_SAMPLE_S16NE;
    ss.rate = 48000;
    ss.channels = 2;
    assert(pa_sample_spec_valid(&ss));

    struct pa_channel_map map;
    map.channels = 2;
    map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
    map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
    assert(pa_channel_map_valid(&map));

    const pa_stream_flags_t flags = PA_STREAM_INTERPOLATE_TIMING
                                  | PA_STREAM_AUTO_TIMING_UPDATE
                                  | PA_STREAM_ADJUST_LATENCY
                                  | PA_STREAM_FIX_FORMAT
                                  | PA_STREAM_FIX_RATE
                                  /*| PA_STREAM_FIX_CHANNELS*/;

    const char *dev = NULL;
    if (demux->psz_location != NULL && demux->psz_location[0] != '\0')
        dev = demux->psz_location;

    struct pa_buffer_attr attr = {
        .maxlength = -1,
        .fragsize = pa_usec_to_bytes(sys->caching, &ss) / 2,
    };

    es_format_t fmt;

    /* Create record stream */
    pa_stream *s;
    pa_operation *op;

    pa_threaded_mainloop_lock(sys->mainloop);
    s = pa_stream_new(sys->context, "audio stream", &ss, &map);
    if (s == NULL)
        goto error;

    sys->stream = s;
    pa_stream_set_state_callback(s, stream_state_cb, sys->mainloop);
    pa_stream_set_read_callback(s, stream_read_cb, demux);
    pa_stream_set_buffer_attr_callback(s, stream_buffer_attr_cb, demux);
    pa_stream_set_moved_callback(s, stream_moved_cb, demux);
    pa_stream_set_overflow_callback(s, stream_overflow_cb, demux);
    pa_stream_set_started_callback(s, stream_started_cb, demux);
    pa_stream_set_suspended_callback(s, stream_suspended_cb, demux);
    pa_stream_set_underflow_callback(s, stream_underflow_cb, demux);

    if (pa_stream_connect_record(s, dev, &attr, flags) < 0
     || stream_wait(s, sys->mainloop)) {
        vlc_pa_error(obj, "cannot connect record stream", sys->context);
        goto error;
    }

    /* The ES should be initialized before stream_read_cb(), but how? */
    const struct pa_sample_spec *pss = pa_stream_get_sample_spec(s);
    if ((unsigned)pss->format >= sizeof (fourccs) / sizeof (fourccs[0])) {
        msg_Err(obj, "unknown PulseAudio sample format %u",
                (unsigned)pss->format);
        goto error;
    }

    vlc_fourcc_t format = fourccs[pss->format];
    if (format == 0) { /* FIXME: should renegotiate something else */
        msg_Err(obj, "unsupported PulseAudio sample format %u",
                (unsigned)pss->format);
        goto error;
    }

    es_format_Init(&fmt, AUDIO_ES, format);
    fmt.audio.i_physical_channels = fmt.audio.i_original_channels =
        AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
    fmt.audio.i_channels = ss.channels;
    fmt.audio.i_rate = pss->rate;
    fmt.audio.i_bitspersample = aout_BitsPerSample(format);
    fmt.audio.i_blockalign = fmt.audio.i_bitspersample * ss.channels / 8;
    fmt.i_bitrate = fmt.audio.i_bitspersample * ss.channels * pss->rate;
    sys->framesize = fmt.audio.i_blockalign;
    sys->es = es_out_Add (demux->out, &fmt);

    /* Update the buffer attributes according to actual format */
    attr.fragsize = pa_usec_to_bytes(sys->caching, pss) / 2;
    op = pa_stream_set_buffer_attr(s, &attr, stream_success_cb, sys->mainloop);
    if (likely(op != NULL)) {
        while (pa_operation_get_state(op) == PA_OPERATION_RUNNING)
            pa_threaded_mainloop_wait(sys->mainloop);
        pa_operation_unref(op);
    }
    stream_buffer_attr_cb(s, demux);
    pa_threaded_mainloop_unlock(sys->mainloop);

    demux->pf_demux = NULL;
    demux->pf_control = Control;
    return VLC_SUCCESS;

error:
    pa_threaded_mainloop_unlock(sys->mainloop);
    Close(obj);
    return VLC_EGENERIC;
}

static void Close (vlc_object_t *obj)
{
    demux_t *demux = (demux_t *)obj;
    demux_sys_t *sys = demux->p_sys;
    pa_stream *s = sys->stream;

    if (likely(s != NULL)) {
        pa_threaded_mainloop_lock(sys->mainloop);
        pa_stream_disconnect(s);
        pa_stream_set_state_callback(s, NULL, NULL);
        pa_stream_set_read_callback(s, NULL, NULL);
        pa_stream_set_buffer_attr_callback(s, NULL, NULL);
        pa_stream_set_moved_callback(s, NULL, NULL);
        pa_stream_set_overflow_callback(s, NULL, NULL);
        pa_stream_set_started_callback(s, NULL, NULL);
        pa_stream_set_suspended_callback(s, NULL, NULL);
        pa_stream_set_underflow_callback(s, NULL, NULL);
        pa_stream_unref(s);
        pa_threaded_mainloop_unlock(sys->mainloop);
    }

    vlc_pa_disconnect(obj, sys->context, sys->mainloop);
    free(sys);
}