source: roaraudio/libroar/vio_cmd.c @ 1264:dbc74da142f9

Last change on this file since 1264:dbc74da142f9 was 1264:dbc74da142f9, checked in by phi, 15 years ago

added interface to interact with gzip

File size: 8.7 KB
Line 
1//vio_cmd.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2008
5 *
6 *  This file is part of libroar a part of RoarAudio,
7 *  a cross-platform sound system for both, home and professional use.
8 *  See README for details.
9 *
10 *  This file is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 3
12 *  as published by the Free Software Foundation.
13 *
14 *  libroar is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this software; see the file COPYING.  If not, write to
21 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22 *
23 *  NOTE for everyone want's to change something and send patches:
24 *  read README and HACKING! There a addition information on
25 *  the license of this document you need to read before you send
26 *  any patches.
27 *
28 *  NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc
29 *  or libpulse*:
30 *  The libs libroaresd, libroararts and libroarpulse link this lib
31 *  and are therefore GPL. Because of this it may be illigal to use
32 *  them with any software that uses libesd, libartsc or libpulse*.
33 */
34
35#include "libroar.h"
36
37int roar_vio_open_cmd(struct roar_vio_calls * calls, struct roar_vio_calls * dst,
38                      char * reader, char * writer, int options) {
39 struct roar_vio_cmd_state * state;
40
41 if ( calls == NULL || dst == NULL )
42  return -1;
43
44 if ( reader == NULL && writer == NULL )
45  return -1;
46
47 if ( (state = malloc(sizeof(struct roar_vio_cmd_state))) == NULL )
48  return -1;
49
50 ROAR_DBG("roar_vio_open_cmd(*): pre reqs are OK");
51
52 // clear all
53 memset(calls, 0, sizeof(struct roar_vio_calls));
54 memset(state, 0, sizeof(struct roar_vio_cmd_state));
55
56 // init reader and writer:
57 state->reader.pid = -1;
58 state->reader.in  = -1;
59 state->reader.out = -1;
60
61 if ( reader != NULL )
62  state->reader.cmd = strdup(reader);
63
64 state->writer.pid = -1;
65 state->writer.in  = -1;
66 state->writer.out = -1;
67
68 if ( writer != NULL )
69  state->writer.cmd = strdup(writer);
70
71 // init state
72 state->next    = dst;
73 state->options = options;
74
75 // init calls
76 calls->close    = roar_vio_cmd_close;
77 calls->read     = roar_vio_cmd_read;
78 calls->write    = roar_vio_cmd_write;
79 calls->nonblock = roar_vio_cmd_nonblock;
80 calls->sync     = roar_vio_cmd_sync;
81 calls->inst     = (void*) state;
82
83 ROAR_DBG("roar_vio_open_cmd(*): var setup OK");
84
85 if ( !(options & ROAR_VIO_CMD_OPTS_ON_DEMAND) ) {
86  if ( reader != NULL )
87   if ( roar_vio_cmd_fork(&(state->reader)) == -1 )
88    return roar_vio_cmd_close(calls);
89
90  if ( writer != NULL )
91   if ( roar_vio_cmd_fork(&(state->writer)) == -1 )
92    return roar_vio_cmd_close(calls);
93 }
94
95 return 0;
96}
97
98int roar_vio_cmd_close(struct roar_vio_calls * vio) {
99 struct roar_vio_cmd_state * state = (struct roar_vio_cmd_state *)vio->inst;
100
101 if ( state->reader.opened )
102  roar_vio_cmd_wait(&(state->reader));
103
104 if ( state->writer.opened )
105  roar_vio_cmd_wait(&(state->writer));
106
107 if ( state->reader.cmd != NULL )
108  free(state->reader.cmd);
109
110 if ( state->writer.cmd != NULL )
111  free(state->writer.cmd);
112
113 roar_vio_close(state->next);
114
115 free(state);
116
117 return 0;
118}
119
120int roar_vio_cmd_fork(struct roar_vio_cmd_child * child) {
121 int in[2], out[2];
122
123 if ( child == NULL )
124  return -1;
125
126 if ( child->opened )
127  return 0;
128
129 if ( child->cmd == NULL )
130  return -1;
131
132 // open some pipes...
133 if ( pipe(in) != 0 )
134  return -1;
135
136 if ( pipe(out) != 0 ) {
137  close(in[0]);
138  close(in[1]);
139  return -1;
140 }
141
142 child->pid = fork();
143
144 switch (child->pid) {
145  case -1:
146    close(in[0]);
147    close(in[1]);
148    close(out[0]);
149    close(out[1]);
150    return -1;
151   break;
152  case 0:
153    close(in[0]);
154    close(out[1]);
155    close(ROAR_STDIN);
156    close(ROAR_STDOUT);
157
158    if ( dup2(out[0], ROAR_STDIN) == -1 )
159     _exit(1);
160
161    if ( dup2(in[1], ROAR_STDOUT) == -1 )
162     _exit(1);
163
164    execlp("/bin/sh", "/bin/sh", "-c", child->cmd, NULL);
165
166    _exit(1);
167   break;
168 }
169
170 close(in[1]);
171 close(out[0]);
172
173 child->opened = 1;
174 child->in     = in[0];
175 child->out    = out[1];
176
177 return 0;
178}
179
180int roar_vio_cmd_wait(struct roar_vio_cmd_child * child) {
181 int status;
182
183 if ( child == NULL )
184  return -1;
185
186 if ( !child->opened )
187  return 0;
188
189 if ( child->out != -1 )
190  close(child->out);
191
192 if ( child->in  != -1 )
193  close(child->in);
194
195 waitpid(child->pid, &status, 0);
196
197 return 0;
198}
199
200// VIOs:
201
202ssize_t roar_vio_cmd_read    (struct roar_vio_calls * vio, void *buf, size_t count) {
203 struct roar_vio_cmd_state * state = (struct roar_vio_cmd_state *)vio->inst;
204 fd_set rfhs[1], wfhs[1];
205 struct timeval tv;
206 size_t done = 0;
207 int max_fh;
208 int ret;
209 char   tbuf[ROAR_VIO_CMD_BUFSIZE];
210 char * tp   = NULL;
211 size_t tlen = 0;
212
213 ROAR_DBG("roar_vio_cmd_read(*) = ?");
214
215 if ( !state->reader.opened ) {
216  if ( buf == NULL && count == 0 ) /* sync: no need to do anything if no reader is forked :) */
217   return 0;
218
219  if ( !(state->options & ROAR_VIO_CMD_OPTS_ON_DEMAND) ) /* we are not on demand and no reader exists? -> err */
220   return -1;
221
222  if ( roar_vio_cmd_fork(&(state->reader)) == -1 )
223   return -1;
224 }
225
226 while (done < count) {
227  if ( state->options & ROAR_VIO_CMD_OPTS_NONBLOCK ) {
228   tv.tv_sec  =    0;
229   tv.tv_usec =    1;
230  } else {
231   tv.tv_sec  = 3600;
232   tv.tv_usec =    0;
233  }
234
235  FD_ZERO(rfhs);
236  FD_ZERO(wfhs);
237
238  FD_SET(state->reader.in,  rfhs);
239
240  if ( state->reader.out != -1 ) {
241   FD_SET(state->reader.out, wfhs);
242  }
243
244#ifdef DEBUG
245  if ( FD_ISSET(state->reader.in, rfhs) ) {
246   ROAR_DBG("roar_vio_cmd_read(*): reader set in fh group");
247  }
248#endif
249
250  max_fh = state->reader.in > state->reader.out ? state->reader.in : state->reader.out;
251  ROAR_DBG("roar_vio_cmd_read(*): max_fh=%i", max_fh);
252
253  if ( (ret = select(max_fh + 1, rfhs, wfhs, NULL, &tv)) == -1 )
254   return -1;
255
256  ROAR_DBG("roar_vio_cmd_read(*): select(*) = %i", ret);
257  ROAR_DBG("roar_vio_cmd_read(*): reader=%i, writer=%i", state->reader.in, state->reader.out);
258
259  if ( ret > 0 ) {
260   if ( FD_ISSET(state->reader.in, rfhs) ) {
261    ROAR_DBG("roar_vio_cmd_read(*): event on reader");
262
263    if ( (ret = read(state->reader.in, buf+done, count-done)) == -1 )
264     break;
265
266    if ( ret == 0 )
267     break;
268
269    done += ret;
270   }
271
272   if ( state->reader.out != -1 && FD_ISSET(state->reader.out, wfhs) ) {
273    ROAR_DBG("roar_vio_cmd_read(*): event on writer");
274
275    if ( !tlen ) {
276     tp   = tbuf;
277     tlen = 0;
278     if ( (tlen = roar_vio_read(state->next, tp, ROAR_VIO_CMD_BUFSIZE)) == -1 )
279      continue;
280    }
281
282    if ( tlen ) {
283     if ( (ret = write(state->reader.out, tp, tlen)) > 0 ) {
284      tlen -= ret;
285      tp   += ret;
286     }
287    } else {
288     close(state->reader.out);
289     state->reader.out = -1;
290    }
291   }
292  }
293
294  if ( state->options & ROAR_VIO_CMD_OPTS_NONBLOCK )
295   break;
296 }
297
298 if ( tlen ) { /* we have some data to write to the child... */
299  // TODO: try to write it out...
300  return -1;
301 }
302
303 return done;
304}
305
306ssize_t roar_vio_cmd_write   (struct roar_vio_calls * vio, void *buf, size_t count) {
307 struct roar_vio_cmd_state * state = (struct roar_vio_cmd_state *)vio->inst;
308
309 if ( !state->writer.opened ) {
310  if ( buf == NULL && count == 0 ) /* sync: no need to do anything if no writer is forked :) */
311   return 0;
312
313  if ( !(state->options & ROAR_VIO_CMD_OPTS_ON_DEMAND) ) /* we are not on demand and no writer exists? -> err */
314   return -1;
315
316  if ( roar_vio_cmd_fork(&(state->writer)) == -1 )
317   return -1;
318 }
319
320
321 return -1;
322}
323
324int     roar_vio_cmd_nonblock(struct roar_vio_calls * vio, int state) {
325 struct roar_vio_cmd_state * self = (struct roar_vio_cmd_state *)vio->inst;
326
327 self->options |= ROAR_VIO_CMD_OPTS_NONBLOCK;
328
329 if ( state == ROAR_SOCKET_BLOCK )
330  self->options -= ROAR_VIO_CMD_OPTS_NONBLOCK;
331
332 roar_vio_nonblock(self->next, state); // this should help, but may not nessessery.
333
334 return 0;
335}
336
337int     roar_vio_cmd_sync    (struct roar_vio_calls * vio) {
338 struct roar_vio_cmd_state * state = (struct roar_vio_cmd_state *)vio->inst;
339 int oldblock;
340 int ret = 0;
341
342 oldblock = state->options & ROAR_VIO_CMD_OPTS_NONBLOCK ? ROAR_SOCKET_NONBLOCK : ROAR_SOCKET_BLOCK;
343
344 if ( roar_vio_cmd_nonblock(vio, ROAR_SOCKET_BLOCK) == -1 )
345  return -1;
346
347 if ( roar_vio_cmd_write(vio, NULL, 0) == -1 )
348  ret = -1;
349
350 if ( roar_vio_cmd_read(vio, NULL, 0) == -1 )
351  ret = -1;
352
353 if ( roar_vio_cmd_nonblock(vio, oldblock) == -1 )
354  return -1;
355
356 return ret;
357}
358
359// MISC:
360int roar_vio_open_gzip(struct roar_vio_calls * calls, struct roar_vio_calls * dst, int level) {
361#ifdef ROAR_HAVE_BIN_GZIP
362 char   wbuf[80];
363 char * writer = ROAR_HAVE_BIN_GZIP " -c";
364
365 if ( level != -1 ) {
366  snprintf(wbuf, 80, "%s -c%i", ROAR_HAVE_BIN_GZIP, level);
367  writer = wbuf;
368 }
369
370 return roar_vio_open_cmd(calls, dst, ROAR_HAVE_BIN_GZIP " -dc", writer, ROAR_VIO_CMD_OPTS_ON_DEMAND);
371#else
372 return -1;
373#endif
374}
375
376//ll
Note: See TracBrowser for help on using the repository browser.