summaryrefslogtreecommitdiff
path: root/lib/pipe-filter.h
blob: da122d51bbf5e76de66ff96578fac9ee45685fdc (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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/* Filtering of data through a subprocess.  -*- coding: utf-8 -*-
   Copyright (C) 2009-2022 Free Software Foundation, Inc.
   Written by Bruno Haible <haible@clisp.cons.org>, 2009,
   and Paolo Bonzini <bonzini@gnu.org>, 2009.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#ifndef _PIPE_FILTER_H
#define _PIPE_FILTER_H

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif


/* Piping data through a subprocess in the naïve way - write data to the
   subprocess and read from the subprocess when you expect it to have
   produced results - is subject to two kinds of deadlocks:
   1) If you write more than PIPE_MAX bytes or, more generally, if you write
      more bytes than the subprocess can handle at once, the subprocess
      may write its data and wait on you to read it, but you are currently
      busy writing.
   2) When you don't know ahead of time how many bytes the subprocess
      will produce, the usual technique of calling read (fd, buf, BUFSIZ)
      with a fixed BUFSIZ will, on Linux 2.2.17 and on BSD systems, cause
      the read() call to block until *all* of the buffer has been filled.
      But the subprocess cannot produce more data until you gave it more
      input.  But you are currently busy reading from it.

   This header file declares four set of functions that pipes data through
   the subprocess, without risking these deadlocks.

   The side that writes data to the subprocess can be seen as a "generator",
   that is, as a subroutine that produces and writes a piece of data here and
   there, see <https://en.wikipedia.org/wiki/Generator_(computer_science)>.
   But often, it can be written in the form of an "iterator", that is, as a
   function that, each time it is invoked, produces and writes one more piece
   of data.

   Similarly, the side that reads data from the subprocess can be seen as
   a "generator", that is, as a subroutine that consumes a piece of data here
   and there.  Often, it can be written in the form of an "iterator", that
   is, as a function that, each time it is invoked, consumes one more piece
   of data.

   This header file declares four set of functions:

                       |   writer   |   reader   |
       ----------------+------------+------------+
       pipe_filter_ii  |  iterator  |  iterator  |
       pipe_filter_ig  |  iterator  |  generator |
       pipe_filter_gi  |  generator |  iterator  |
       pipe_filter_gg  |  generator |  generator |
       ----------------+------------+------------+

   The last one uses threads in order to implement two generators running at
   the same time.  (For the relation between generators, coroutines, and
   threads, see <https://en.wikipedia.org/wiki/Generator_(computer_science)>
   and <https://en.wikipedia.org/wiki/Coroutine>.)  It is therefore only
   portable to platforms with kernel-based POSIX threads.  */

/* These two functions together describe the side that writes data to the
   subprocess when it has the form of an iterator.
   - prepare_write (&num_bytes, p) must either return a pointer to data that
     is ready to be written and set num_bytes to the number of bytes ready to
     be written, or return NULL when no more bytes are to be written.
   - done_write (data_written, num_bytes_written) is called after
     num_bytes_written bytes were written.  It is guaranteed that
     num_bytes_written > 0.
   Here p is always the private_data argument passed to the main function.  */
typedef const void * (*prepare_write_fn) (size_t *num_bytes_p,
                                          void *private_data);
typedef void (*done_write_fn) (void *data_written, size_t num_bytes_written,
                               void *private_data);

/* These two functions together describe the side that reads data from the
   subprocess when it has the form of an iterator.
   - prepare_read (&num_bytes, p) must return a pointer to a buffer for data
     that can be read and set num_bytes to the size of that buffer
     (must be > 0).
   - done_read (data_read, num_bytes_read, p) is called after num_bytes_read
     bytes were read into the buffer.
   Here p is always the private_data argument passed to the main function.  */
typedef void * (*prepare_read_fn) (size_t *num_bytes_p,
                                   void *private_data);
typedef void (*done_read_fn) (void *data_read, size_t num_bytes_read,
                              void *private_data);


/* ============================ pipe_filter_ii ============================ */

/* Create a subprocess and pipe some data through it.
   Arguments:
   - progname is the program name used in error messages.
   - prog_path is the file name of the program to invoke.
   - prog_argv is a NULL terminated argument list, starting with prog_path as
     first element.
   - If null_stderr is true, the subprocess' stderr will be redirected to
     /dev/null, and the usual error message to stderr will be omitted.
     This is suitable when the subprocess does not fulfill an important task.
   - If exit_on_error is true, any error will cause the main process to exit
     with an error status.
   If the subprocess does not terminate correctly, exit if exit_on_error is
   true, otherwise return 127.
   Callback arguments are as described above.

   Data is alternately written to the subprocess, through the functions
   prepare_write and done_write, and read from the subprocess, through the
   functions prepare_read and done_read.

   Note that the prepare_write/done_write functions and the
   prepare_read/done_read functions may be called in different threads than
   the current thread (depending on the platform).  But they will not be
   called after the pipe_filter_ii_execute function has returned.

   Return 0 upon success, or (only if exit_on_error is false):
   - -1 with errno set upon failure,
   - the positive exit code of the subprocess if that failed.  */
extern int
       pipe_filter_ii_execute (const char *progname,
                               const char *prog_path,
                               const char * const *prog_argv,
                               bool null_stderr, bool exit_on_error,
                               prepare_write_fn prepare_write,
                               done_write_fn done_write,
                               prepare_read_fn prepare_read,
                               done_read_fn done_read,
                               void *private_data);


/* ============================ pipe_filter_ig ============================ */

struct pipe_filter_ig;


/* ============================ pipe_filter_gi ============================ */

struct pipe_filter_gi;

/* Finish reading the output via the prepare_read/done_read functions
   specified to pipe_filter_gi_create.

   Note that the prepare_read/done_read functions may be called in a
   different thread than the current thread (depending on the platform).
   However, they will always be called before pipe_filter_gi_close has
   returned.

   The write side of the pipe is closed as soon as pipe_filter_gi_close
   starts, while the read side will be closed just before it finishes.

   Return 0 upon success, or (only if exit_on_error is false):
   - -1 with errno set upon failure,
   - the positive exit code of the subprocess if that failed.  */
extern int
       pipe_filter_gi_close (struct pipe_filter_gi *filter);

/* Create a subprocess and pipe some data through it.
   Arguments:
   - progname is the program name used in error messages.
   - prog_path is the file name of the program to invoke.
   - prog_argv is a NULL terminated argument list, starting with
     prog_path as first element.
   - If null_stderr is true, the subprocess' stderr will be redirected
     to /dev/null, and the usual error message to stderr will be
     omitted.  This is suitable when the subprocess does not fulfill an
     important task.
   - If exit_on_error is true, any error will cause the main process to
     exit with an error status.
   If the subprocess does not start correctly, exit if exit_on_error is
   true, otherwise return NULL and set errno.

   The caller will write to the subprocess through pipe_filter_gi_write
   and finally call pipe_filter_gi_close.  During such calls, the
   prepare_read and done_read function may be called to process any data
   that the subprocess has written.

   Note that the prepare_read/done_read functions may be called in a
   different thread than the current thread (depending on the platform).
   But they will not be called after the pipe_filter_gi_close function has
   returned.

   Return the freshly created 'struct pipe_filter_gi'.  */
extern struct pipe_filter_gi *
       pipe_filter_gi_create (const char *progname,
                              const char *prog_path,
                              const char * const *prog_argv,
                              bool null_stderr, bool exit_on_error,
                              prepare_read_fn prepare_read,
                              done_read_fn done_read,
                              void *private_data)
  _GL_ATTRIBUTE_DEALLOC (pipe_filter_gi_close, 1);

/* Write size bytes starting at buf into the pipe and in the meanwhile
   possibly call the prepare_read and done_read functions specified to
   pipe_filter_gi_create.

   Note that the prepare_read/done_read functions may be called in a
   different thread than the current thread (depending on the platform).
   However, they will always be called before pipe_filter_gi_write has
   returned, or otherwise not sooner than the next call to
   pipe_filter_gi_write or pipe_filter_gi_close.

   Return only after all the entire buffer has been written to the pipe or
   the subprocess has exited.

   Return 0 upon success, or (only if exit_on_error is false):
   - -1 with errno set upon failure,
   - the positive exit code of the subprocess if that failed.  */
extern int
       pipe_filter_gi_write (struct pipe_filter_gi *filter,
                             const void *buf, size_t size);


/* ============================ pipe_filter_gg ============================ */


/* ======================================================================== */


#ifdef __cplusplus
}
#endif


#endif /* _PIPE_FILTER_H */