summaryrefslogtreecommitdiffstats
path: root/input/pipe.c
blob: 12922f590cf5c63272dc606631024167c935556f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#ifndef __MINGW32__
#include <poll.h>
#endif

#include "common/msg.h"
#include "misc/bstr.h"
#include "osdep/io.h"
#include "input.h"
#include "cmd_parse.h"

static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

struct priv {
    struct mp_log *log;
    char *filename;
    struct mp_input_src *src;
    int wakeup_pipe[2];
};

static void *reader_thread(void *ctx)
{
    struct priv *p = ctx;
    pthread_detach(pthread_self());

    int mode = O_RDONLY;
#ifndef __MINGW32__
    // Use RDWR for FIFOs to ensure they stay open over multiple accesses.
    // Note that on Windows due to how the API works, using RDONLY should
    // be ok.
    struct stat st;
    if (stat(p->filename, &st) == 0 && S_ISFIFO(st.st_mode))
        mode = O_RDWR;
#endif
    int fd = -1;
    bool close_fd = true;
    if (strcmp(p->filename, "/dev/stdin") == 0) { // mainly for win32
        fd = STDIN_FILENO;
        close_fd = false;
    }
    if (fd < 0)
        fd = open(p->filename, mode);
    if (fd < 0) {
        MP_ERR(p, "Can't open %s.\n", p->filename);
        goto done;
    }

    while (1) {
#ifndef __MINGW32__
        struct pollfd fds[2] = {
            { .fd = fd, .events = POLLIN },
            { .fd = p->wakeup_pipe[0], .events = POLLIN },
        };
        poll(fds, 2, -1);
        if (!(fds[0].revents & POLLIN))
            break;
#endif
        char buffer[128];
        int r = read(fd, buffer, sizeof(buffer));
        if (r <= 0)
            break;

        pthread_mutex_lock(&lock);
        if (!p->src) {
            pthread_mutex_unlock(&lock);
            break;
        }
        mp_input_src_feed_cmd_text(p->src, buffer, r);
        pthread_mutex_unlock(&lock);
    }

    if (close_fd)
        close(fd);

done:
    pthread_mutex_lock(&lock);
    if (p->src)
        p->src->priv = NULL;
    pthread_mutex_unlock(&lock);
    close(p->wakeup_pipe[0]);
    close(p->wakeup_pipe[1]);
    talloc_free(p);
    return NULL;
}

static void close_pipe(struct mp_input_src *src)
{
    pthread_mutex_lock(&lock);
    struct priv *p = src->priv;
    // Windows pipe have a severe problem: they can't be made non-blocking (not
    // after creation), and you can't wait on them. The only things that work
    // are cancellation (Vista+, broken in wine) or forceful thread termination.
    // So don't bother with "correct" termination, and just abandon the reader
    // thread.
    // On Unix, we interrupt it using the wakeup pipe.
    if (p) {
#ifndef __MINGW32__
        write(p->wakeup_pipe[1], &(char){0}, 1);
#endif
        p->src = NULL;
    }
    pthread_mutex_unlock(&lock);
}

void mp_input_add_pipe(struct input_ctx *ictx, const char *filename)
{
    struct mp_input_src *src = mp_input_add_src(ictx);
    if (!src)
        return;

    struct priv *p = talloc_zero(NULL, struct priv);
    src->priv = p;
    p->filename = talloc_strdup(p, filename);
    p->src = src;
    p->log = mp_log_new(p, src->log, NULL);
    mp_make_wakeup_pipe(p->wakeup_pipe);

    pthread_t thread;
    if (pthread_create(&thread, NULL, reader_thread, p)) {
        close(p->wakeup_pipe[0]);
        close(p->wakeup_pipe[1]);
        talloc_free(p);
        mp_input_src_kill(src);
    } else {
        src->close = close_pipe;
    }
}