0335d82008-04-18Stephen R. van den Berg /* || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information. || This module has been deprecated. */ #include "global.h" #include "config.h" #include "module.h" #include "threads.h" #include "stralloc.h" #include "pike_macros.h" #include "object.h" #include "constants.h" #include "interpret.h" #include "svalue.h" #include "pike_error.h" #include "builtin_functions.h" #include "fdlib.h" #include <sys/types.h> #include <sys/stat.h> #ifdef HAVE_SYS_FILE_H #include <sys/file.h> #endif /* HAVE_SYS_FILE_H */ #include <errno.h> /* Some <sys/mman.h>'s (eg AIX 5L/ia64) contain a #define of MAP_VARIABLE * for use as the opposite of MAP_FIXED. * * "program.h" above has a conflicting definition of MAP_VARIABLE. */ #undef MAP_VARIABLE #ifdef HAVE_SYS_MMAN_H #include <sys/mman.h> #else #ifdef HAVE_LINUX_MMAN_H #include <linux/mman.h> #else #ifdef HAVE_MMAP /* sys/mman.h is _probably_ there anyway. */ #include <sys/mman.h> #endif #endif #endif #ifdef HAVE_SYS_ID_H #include <sys/id.h> #endif /* HAVE_SYS_ID_H */ #include <fcntl.h> #ifndef S_ISREG #ifdef S_IFREG #define S_ISREG(mode) (((mode) & (S_IFMT)) == (S_IFREG)) #else #define S_ISREG(mode) (((mode) & (_S_IFMT)) == (_S_IFREG)) #endif #endif #define sp Pike_sp /* #define PIPE_STRING_DEBUG #define PIPE_MMAP_DEBUG #define PIPE_FILE_DEBUG #define BLOCKING_CLOSE */ #ifndef SEEK_SET #ifdef L_SET #define SEEK_SET L_SET #else /* !L_SET */ #define SEEK_SET 0 #endif /* L_SET */ #endif /* SEEK_SET */ #ifndef SEEK_CUR #ifdef L_INCR #define SEEK_CUR L_INCR #else /* !L_INCR */ #define SEEK_CUR 1 #endif /* L_INCR */ #endif /* SEEK_CUR */ #ifndef SEEK_END #ifdef L_XTND #define SEEK_END L_XTND #else /* !L_XTND */ #define SEEK_END 2 #endif /* L_XTND */ #endif /* SEEK_END */ #if 0 #define INSISTANT_WRITE #endif #ifndef MAP_FILE # define MAP_FILE 0 #endif #define READ_BUFFER_SIZE 65536 #define MAX_BYTES_IN_BUFFER 65536
105cbd2008-04-19Henrik Grubbström (Grubba) /*! @module Pipe *! *! Single socket output. *! *! Regular file output and (multiple, adding) socket output *! with no mmap input. *! *! Multiple socket output without regular file output illegal.
0335d82008-04-18Stephen R. van den Berg  */ static struct program *pipe_program, *output_program; #ifdef THIS #undef THIS #endif #define THIS ((struct pipe *)(Pike_fp->current_storage)) #define THISOBJ (Pike_fp->current_object) struct input { enum { I_NONE,I_OBJ,I_BLOCKING_OBJ,I_STRING,I_MMAP } type; union { struct object *obj; struct pike_string *str; char *mmap; } u; size_t len; /* current input: string or mmap len */ ptrdiff_t set_blocking_offset, set_nonblocking_offset; struct input *next; }; struct output { struct object *obj; ptrdiff_t write_offset, set_blocking_offset, set_nonblocking_offset; int fd; enum { O_RUN, /* waiting for callback */ O_SLEEP /* sleeping; waiting for more data */ } mode; size_t pos; /* position in buffer */ struct object *next; struct pipe *the_pipe; }; struct buffer { struct pike_string *s; struct buffer *next; };
105cbd2008-04-19Henrik Grubbström (Grubba) /*! @class pipe *! *! Concatenation pipe. */
0335d82008-04-18Stephen R. van den Berg struct pipe { int living_outputs; /* number of output objects */ struct svalue done_callback; struct svalue output_closed_callback; struct svalue id; /* * if fd is -1: use fd * else firstinput's type is I_MMAP: use firstinput's mmap * else use buffer */ int fd; /* buffer fd or -1 */ unsigned long bytes_in_buffer; size_t pos; /* fd: size of buffer file */ /* current position of first element (buffer or mmap) */ struct buffer *firstbuffer,*lastbuffer; short sleeping; /* sleeping; buffer is full */ short done; struct input *firstinput,*lastinput; struct object *firstoutput; unsigned long sent; }; static ptrdiff_t offset_input_read_callback; static ptrdiff_t offset_input_close_callback; static ptrdiff_t offset_output_write_callback; static ptrdiff_t offset_output_close_callback; static ptrdiff_t mmapped, nobjects, nstrings, noutputs; static ptrdiff_t ninputs, nbuffers, sbuffers; void close_and_free_everything(struct object *o,struct pipe *); static INLINE void output_finish(struct object *obj); static INLINE void output_try_write_some(struct object *obj); /********** internal ********************************************************/ /* Push a callback to this object given the internal function number. */ static void push_callback(ptrdiff_t no) {
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL(*Pike_sp, T_FUNCTION, DO_NOT_WARN(no + Pike_fp->context->identifier_level), object, THISOBJ); add_ref(THISOBJ);
0335d82008-04-18Stephen R. van den Berg  Pike_sp++; } /* Allocate a new struct input, link it last in the linked list */ static INLINE struct input *new_input(void) { struct input *i; ninputs++; i=ALLOC_STRUCT(input); i->type=I_NONE; i->next=NULL; if (THIS->lastinput) THIS->lastinput->next=i; else THIS->firstinput=i; THIS->lastinput=i; return i; } /* Free an input struct and all that it stands for */ static INLINE void free_input(struct input *i) { debug_malloc_touch(i); ninputs--; switch (i->type) { case I_OBJ: case I_BLOCKING_OBJ: if (!i->u.obj) break; if (i->u.obj->prog) { #ifdef BLOCKING_CLOSE apply_low(i->u.obj,i->set_blocking_offset,0); pop_stack(); #endif apply(i->u.obj,"close",0); pop_stack(); destruct(i->u.obj); } free_object(i->u.obj); nobjects--; i->u.obj=0; break; case I_STRING: free_string(i->u.str); nstrings--; break; case I_MMAP: #if defined(HAVE_MMAP) && defined(HAVE_MUNMAP) munmap(i->u.mmap,i->len); mmapped -= i->len; #else Pike_error("I_MMAP input when MMAP is diabled!"); #endif break; case I_NONE: break; }
0ec7522014-04-27Martin Nilsson  free(i);
0335d82008-04-18Stephen R. van den Berg } /* do the done_callback, then close and free everything */ static INLINE void pipe_done(void) {
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(THIS->done_callback) != T_INT)
0335d82008-04-18Stephen R. van den Berg  { assign_svalue_no_free(sp++,&THIS->id); apply_svalue(&(THIS->done_callback),1); pop_stack(); if(!THISOBJ->prog) /* We will not free anything in this case. */ return; /* Pike_error("Pipe done callback destructed pipe.\n"); */ } close_and_free_everything(THISOBJ,THIS); } static void finished_p(void) { if(THIS->done) return; if(THIS->fd != -1) { if(THIS->living_outputs > 1) return; if(THIS->firstinput) return; }else{ if(THIS->living_outputs) return; } pipe_done(); } /* Allocate a new buffer and put it at the end of the chain of buffers * scheduled for output. Return 1 if we have more bytes in buffers * than allowed afterwards. */ static INLINE int append_buffer(struct pike_string *s) /* 1=buffer full */ { struct buffer *b; debug_malloc_touch(s); if(THIS->fd!= -1) {
6659eb2009-08-07Henrik Grubbström (Grubba)  ptrdiff_t len = s->len; char *data = s->str; ptrdiff_t bytes;
3171202013-02-08Henrik Grubbström (Grubba)  while ((fd_lseek(THIS->fd, THIS->pos, SEEK_SET) < 0) && (errno == EINTR)) ;
6659eb2009-08-07Henrik Grubbström (Grubba)  while (len > 0) { do { bytes = fd_write(THIS->fd, data, len); } while ((bytes < 0) && (errno == EINTR)); if (bytes < 0) break; data += bytes; len -= bytes; }
0335d82008-04-18Stephen R. van den Berg  THIS->pos+=s->len; return 0; } else { nbuffers++; b=ALLOC_STRUCT(buffer); b->next=NULL; b->s=s; sbuffers += s->len; add_ref(s); if (THIS->lastbuffer) THIS->lastbuffer->next=b; else THIS->firstbuffer=b; THIS->lastbuffer=b; THIS->bytes_in_buffer+=s->len; } return THIS->bytes_in_buffer > MAX_BYTES_IN_BUFFER; } /* Wake up the sleepers */ static void low_start(void) { struct object *obj, *next; struct output *o; add_ref(THISOBJ); /* dont kill yourself now */ for(obj=THIS->firstoutput;obj;obj=next) { /* add_ref(obj); */ /* Hang on PLEASE!! /hubbe */ o=(struct output *)(obj->storage); next=o->next; if (o->obj && o->mode==O_SLEEP) { if (!o->obj->prog) { output_finish(obj); } else { push_int(0); push_callback(offset_output_write_callback); push_callback(offset_output_close_callback); apply_low(o->obj,o->set_nonblocking_offset,3); /* output_try_write_some(obj); */ /* o->mode=O_RUN; */ /* Hubbe */ } } /* next=o->next; */ /* free_object(obj); */ } free_object(THISOBJ); } /* Read some data from the blocking object. * */ static int read_some_data(void) { struct pipe *this = THIS; struct input * i = this->firstinput; if (!i || i->type != I_BLOCKING_OBJ) { Pike_fatal("PIPE: read_some_data(): Bad input type!\n"); return -1; } push_int(8192); push_int(1); /* We don't care if we don't get all 8192 bytes. */ apply(i->u.obj, "read", 2);
017b572011-10-28Henrik Grubbström (Grubba)  if ((TYPEOF(sp[-1]) == T_STRING) && (sp[-1].u.string->len > 0)) {
0335d82008-04-18Stephen R. van den Berg  append_buffer(sp[-1].u.string); pop_stack(); THIS->sleeping = 1; return(1); /* Success */ } /* FIXME: Should we check the return value here? */ pop_stack(); /* EOF */ return(0); /* EOF */ } /* Let's guess what this function does.... * */ static INLINE void input_finish(void) { struct input *i; while(1) { /* Get the next input from the queue */ i=THIS->firstinput->next; free_input(THIS->firstinput); THIS->firstinput=i; if(!i) break; switch(i->type) { case I_OBJ: THIS->sleeping=0; push_callback(offset_input_read_callback); push_int(0); push_callback(offset_input_close_callback); apply_low(i->u.obj,i->set_nonblocking_offset,3); pop_stack(); return; case I_BLOCKING_OBJ: if (read_some_data()) return; continue; case I_MMAP: if (THIS->fd==-1) return; continue; case I_STRING: append_buffer(i->u.str); case I_NONE: break; } } THIS->sleeping=0; low_start(); finished_p(); } /* This function reads some data from the file cache.. * Called when we want some data to send. */ static INLINE struct pike_string* gimme_some_data(size_t pos) { struct buffer *b; ptrdiff_t len; struct pipe *this = THIS; /* We have a file cache, read from it */ if (this->fd!=-1) {
4a09bd2009-08-07Henrik Grubbström (Grubba)  char buffer[READ_BUFFER_SIZE]; ptrdiff_t sz = this->pos-pos;
0335d82008-04-18Stephen R. van den Berg 
4a09bd2009-08-07Henrik Grubbström (Grubba)  if (sz <= 0) return NULL; /* no data */ if (sz > READ_BUFFER_SIZE) sz = READ_BUFFER_SIZE;
0335d82008-04-18Stephen R. van den Berg  THREADS_ALLOW();
3171202013-02-08Henrik Grubbström (Grubba)  while ((fd_lseek(this->fd, pos, SEEK_SET) < 0) && (errno == EINTR)) ;
0335d82008-04-18Stephen R. van den Berg  THREADS_DISALLOW(); do { THREADS_ALLOW();
4a09bd2009-08-07Henrik Grubbström (Grubba)  len = fd_read(this->fd, buffer, sz);
0335d82008-04-18Stephen R. van den Berg  THREADS_DISALLOW(); if (len < 0) { if (errno != EINTR) { return(NULL); } check_threads_etc(); } } while(len < 0); /* * FIXME: What if len is 0? */ return make_shared_binary_string(buffer,len); } if (pos<this->pos) return make_shared_string("buffer underflow"); /* shit */ /* We want something in the next buffer */ while (this->firstbuffer && pos>=this->pos+this->firstbuffer->s->len) { /* Free the first buffer, and update THIS->pos */ b=this->firstbuffer; this->pos+=b->s->len; this->bytes_in_buffer-=b->s->len; this->firstbuffer=b->next; if (!b->next) this->lastbuffer=NULL; sbuffers-=b->s->len; nbuffers--; free_string(b->s);
0ec7522014-04-27Martin Nilsson  free(b);
0335d82008-04-18Stephen R. van den Berg  /* Wake up first input if it was sleeping and we * have room for more in the buffer. */ if (this->sleeping && this->firstinput && this->bytes_in_buffer<MAX_BYTES_IN_BUFFER) { if (this->firstinput->type == I_BLOCKING_OBJ) { if (!read_some_data()) { this->sleeping = 0; input_finish(); } } else { this->sleeping=0; push_callback(offset_input_read_callback); push_int(0); push_callback(offset_input_close_callback); apply(this->firstinput->u.obj, "set_nonblocking", 3); pop_stack(); } } } while (!this->firstbuffer) { if (this->firstinput) { #if defined(HAVE_MMAP) && defined(HAVE_MUNMAP) if (this->firstinput->type==I_MMAP) { char *src; struct pike_string *tmp; if (pos >= this->firstinput->len + this->pos) /* end of mmap */ { this->pos += this->firstinput->len; input_finish(); continue; } len = this->firstinput->len + this->pos - pos; if (len > READ_BUFFER_SIZE) len=READ_BUFFER_SIZE; tmp = begin_shared_string( len ); src = this->firstinput->u.mmap + pos - this->pos; /* This thread_allow/deny is at the cost of one extra memory copy */ THREADS_ALLOW(); MEMCPY(tmp->str, src, len); THREADS_DISALLOW(); return end_shared_string(tmp); } else #endif if (this->firstinput->type!=I_OBJ) { /* FIXME: What about I_BLOCKING_OBJ? */ input_finish(); /* shouldn't be anything else ... maybe a finished object */ } } return NULL; /* no data */ } if (pos==this->pos) { add_ref(this->firstbuffer->s); return this->firstbuffer->s; } return make_shared_binary_string(this->firstbuffer->s->str+ pos-this->pos, this->firstbuffer->s->len- pos+this->pos); } /* * close and free the contents of a struct output * Note that the output struct is not freed or unlinked here, * that is taken care of later. */ static INLINE void output_finish(struct object *obj) { struct output *o, *oi; struct object *obji; debug_malloc_touch(obj); o=(struct output *)(obj->storage); if (o->obj) { if(obj==THIS->firstoutput){ THIS->firstoutput=o->next; } else for(obji=THIS->firstoutput;obji;obji=oi->next) { oi=(struct output *)(obji->storage); if(oi->next==obj) { oi->next=o->next; } } if(o->obj->prog) { #ifdef BLOCKING_CLOSE apply_low(o->obj,o->set_blocking_offset,0); pop_stack(); #endif push_int(0); apply(o->obj,"set_id",1); pop_stack(); apply(o->obj,"close",0); pop_stack(); if(!THISOBJ->prog) Pike_error("Pipe done callback destructed pipe.\n"); destruct(o->obj); } free_object(o->obj); noutputs--; o->obj=NULL; THIS->living_outputs--; finished_p(); /* Moved by per, one line down.. :) */ /* free_object(THISOBJ); */ /* What? /Hubbe */ } } /* * Try to write some data to our precious output */ static INLINE void output_try_write_some(struct object *obj) { struct output *out; struct pike_string *s; size_t len; INT_TYPE ret; debug_malloc_touch(obj); out=(struct output*)(obj->storage); #ifdef INSISTANT_WRITE do { #endif /* Get some data to write */ s=gimme_some_data(out->pos); if (!s) /* out of data */ { /* out of data, goto sleep */ if (!THIS->firstinput || !out->obj->prog) /* end of life */ { output_finish(obj); } else { apply_low(out->obj, out->set_blocking_offset, 0); pop_stack(); /* from apply */ out->mode=O_SLEEP; } return; } len=s->len; push_string(s); apply_low(out->obj,out->write_offset,1); out->mode=O_RUN; ret=-1;
017b572011-10-28Henrik Grubbström (Grubba)  if(TYPEOF(sp[-1]) == T_INT) ret=sp[-1].u.integer;
0335d82008-04-18Stephen R. van den Berg  pop_stack(); if (ret==-1) /* error, byebye */ { output_finish(obj); return; } out->pos+=ret; THIS->sent+=ret; #ifdef INSISTANT_WRITE } while(ret == len); #endif } /********** methods *********************************************************/
105cbd2008-04-19Henrik Grubbström (Grubba) /*! @decl void input(object obj) *! *! Add an input file to this pipe. */
0335d82008-04-18Stephen R. van den Berg static void pipe_input(INT32 args) { struct input *i; int fd=-1; /* Per, one less warning to worry about... */ struct object *obj;
017b572011-10-28Henrik Grubbström (Grubba)  if (args<1 || TYPEOF(sp[-args]) != T_OBJECT)
0335d82008-04-18Stephen R. van den Berg  Pike_error("Bad/missing argument 1 to pipe->input().\n"); obj=sp[-args].u.object; if(!obj || !obj->prog) Pike_error("pipe->input() on destructed object.\n"); push_int(0); apply(sp[-args-1].u.object,"set_id", 1); pop_stack(); i=new_input(); #if defined(HAVE_MMAP) && defined(HAVE_MUNMAP) /* We do not handle mmaps if we have a buffer */ if(THIS->fd == -1) { char *m; struct stat s; apply(obj, "query_fd", 0);
017b572011-10-28Henrik Grubbström (Grubba)  if(TYPEOF(sp[-1]) == T_INT) fd=sp[-1].u.integer;
0335d82008-04-18Stephen R. van den Berg  pop_stack(); if (fd != -1 && fstat(fd,&s)==0) { off_t filep=fd_lseek(fd, 0L, SEEK_CUR); /* keep the file pointer */ size_t len = s.st_size - filep; if(S_ISREG(s.st_mode) /* regular file */ && ((m=(char *)mmap(0, len, PROT_READ, MAP_FILE|MAP_SHARED,fd,filep))+1)) { mmapped += len; i->type=I_MMAP; i->len = len; i->u.mmap=m; #if defined(HAVE_MADVISE) && defined(MADV_SEQUENTIAL) /* Mark the pages as sequential read only access... */ madvise(m, len, MADV_SEQUENTIAL); #endif pop_n_elems(args); push_int(0); return; } } } #endif i->u.obj=obj; nobjects++; i->type=I_OBJ; add_ref(i->u.obj); i->set_nonblocking_offset=find_identifier("set_nonblocking",i->u.obj->prog); i->set_blocking_offset=find_identifier("set_blocking",i->u.obj->prog); if (i->set_nonblocking_offset<0 || i->set_blocking_offset<0) { if (find_identifier("read", i->u.obj->prog) < 0) { /* Not even a read function */ free_object(i->u.obj); i->u.obj=NULL; i->type=I_NONE; nobjects--; Pike_error("illegal file object%s%s\n", ((i->set_nonblocking_offset<0)?"; no set_nonblocking":""), ((i->set_blocking_offset<0)?"; no set_blocking":"")); } else { /* Try blocking mode */ i->type = I_BLOCKING_OBJ; if (i==THIS->firstinput) { /* * FIXME: What if read_som_data() returns 0? */ read_some_data(); } return; } } if (i==THIS->firstinput) { push_callback(offset_input_read_callback); push_int(0); push_callback(offset_input_close_callback); apply_low(i->u.obj,i->set_nonblocking_offset,3); pop_stack(); } else { /* DOESN'T WORK!!! */ push_int(0); push_int(0); push_callback(offset_input_close_callback); apply_low(i->u.obj,i->set_nonblocking_offset,3); pop_stack(); } pop_n_elems(args); push_int(0); }
105cbd2008-04-19Henrik Grubbström (Grubba) /*! @decl void write(string bytes) *! *! Add an input string to this pipe. */
0335d82008-04-18Stephen R. van den Berg static void pipe_write(INT32 args) { struct input *i;
017b572011-10-28Henrik Grubbström (Grubba)  if (args<1 || TYPEOF(sp[-args]) != T_STRING)
0335d82008-04-18Stephen R. van den Berg  Pike_error("illegal argument to pipe->write()\n"); if (!THIS->firstinput) { append_buffer(sp[-args].u.string); pop_n_elems(args); push_int(0); return; } i=new_input(); i->type=I_STRING; nstrings++; add_ref(i->u.str=sp[-args].u.string); pop_n_elems(args-1); } void f_mark_fd(INT32 args);
105cbd2008-04-19Henrik Grubbström (Grubba) /*! @decl void output(object obj, int|void start_pos) *! *! Add an output file object. */
0335d82008-04-18Stephen R. van den Berg static void pipe_output(INT32 args) { struct object *obj; struct output *o; int fd; struct stat s; struct buffer *b; if (args<1 ||
017b572011-10-28Henrik Grubbström (Grubba)  TYPEOF(sp[-args]) != T_OBJECT ||
0335d82008-04-18Stephen R. van den Berg  !sp[-args].u.object || !sp[-args].u.object->prog) Pike_error("Bad/missing argument 1 to pipe->output().\n"); if (args==2 &&
017b572011-10-28Henrik Grubbström (Grubba)  TYPEOF(sp[1-args]) != T_INT)
0335d82008-04-18Stephen R. van den Berg  Pike_error("Bad argument 2 to pipe->output().\n"); if (THIS->fd==-1) /* no buffer */ { /* test if usable as buffer */ apply(sp[-args].u.object,"query_fd",0);
017b572011-10-28Henrik Grubbström (Grubba)  if ((TYPEOF(sp[-1]) == T_INT)
0335d82008-04-18Stephen R. van den Berg  && (fd=sp[-1].u.integer)>=0 && (fstat(fd,&s)==0) && S_ISREG(s.st_mode) && (THIS->fd=fd_dup(fd))!=-1 ) { /* keep the file pointer of the duped fd */ THIS->pos=fd_lseek(fd, 0L, SEEK_CUR); THIS->living_outputs++; while (THIS->firstbuffer) {
b71a5b2009-08-07Henrik Grubbström (Grubba)  ptrdiff_t len; char *data;
0335d82008-04-18Stephen R. van den Berg  b=THIS->firstbuffer; THIS->firstbuffer=b->next;
3171202013-02-08Henrik Grubbström (Grubba)  while ((fd_lseek(THIS->fd, THIS->pos, SEEK_SET) < 0) && (errno == EINTR)) ;
b71a5b2009-08-07Henrik Grubbström (Grubba)  len = b->s->len; data = b->s->str; while (len > 0) { ptrdiff_t bytes; do { bytes = fd_write(THIS->fd, data, len); } while((bytes < 0) && (errno == EINTR)); if (bytes < 0) break; len -= bytes; data += bytes; }
0335d82008-04-18Stephen R. van den Berg  sbuffers-=b->s->len; nbuffers--; free_string(b->s);
0ec7522014-04-27Martin Nilsson  free(b);
0335d82008-04-18Stephen R. van den Berg  } THIS->lastbuffer=NULL; /* keep the file pointer of the duped fd THIS->pos=0; */ push_int(0); apply(sp[-args-2].u.object,"set_id", 1); pop_n_elems(args+2); /* ... and from apply x 2 */ return; } pop_stack(); /* from apply */ } THIS->living_outputs++; /* add_ref(THISOBJ); */ /* Weird */ /* Allocate a new struct output */ obj=clone_object(output_program,0); o=(struct output *)(obj->storage); o->next=THIS->firstoutput; THIS->firstoutput=obj; noutputs++; o->obj=NULL; add_ref(o->obj=sp[-args].u.object); o->write_offset=find_identifier("write",o->obj->prog); o->set_nonblocking_offset=find_identifier("set_nonblocking",o->obj->prog); o->set_blocking_offset=find_identifier("set_blocking",o->obj->prog); if (o->write_offset<0 || o->set_nonblocking_offset<0 || o->set_blocking_offset<0) { free_object(o->obj); Pike_error("illegal file object%s%s%s\n", ((o->write_offset<0)?"; no write":""), ((o->set_nonblocking_offset<0)?"; no set_nonblocking":""), ((o->set_blocking_offset<0)?"; no set_blocking":"")); } o->mode=O_RUN; /* keep the file pointer of the duped fd o->pos=0; */ /* allow start position as 2nd argument for additional outputs o->pos=THIS->pos; */ if(args>=2) o->pos=sp[1-args].u.integer; else o->pos=THIS->pos; push_object(obj); /* Ok, David, this is probably correct, but I dare you to explain why :) */ apply(o->obj,"set_id",1); pop_stack(); push_int(0); push_callback(offset_output_write_callback); push_callback(offset_output_close_callback); apply_low(o->obj,o->set_nonblocking_offset,3); pop_stack(); pop_n_elems(args-1); }
105cbd2008-04-19Henrik Grubbström (Grubba) /*! @decl void set_done_callback(void|function(mixed:mixed) done_cb, @ *! void|mixed id) *! *! Set the callback function to be called when all the outputs *! have been sent. */
0335d82008-04-18Stephen R. van den Berg static void pipe_set_done_callback(INT32 args) { if (args==0) { free_svalue(&THIS->done_callback);
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(THIS->done_callback, T_INT);
0335d82008-04-18Stephen R. van den Berg  return; }
017b572011-10-28Henrik Grubbström (Grubba)  if (args<1 || (TYPEOF(sp[-args]) != T_FUNCTION && TYPEOF(sp[-args]) != T_ARRAY))
0335d82008-04-18Stephen R. van den Berg  Pike_error("Illegal argument to set_done_callback()\n"); if (args>1) { free_svalue(&THIS->id); assign_svalue_no_free(&(THIS->id),sp-args+1); } free_svalue(&THIS->done_callback); assign_svalue_no_free(&(THIS->done_callback),sp-args); pop_n_elems(args-1); }
105cbd2008-04-19Henrik Grubbström (Grubba) /*! @decl void set_output_closed_callback(void|function(mixed, object:mixed) close_cb, @ *! void|mixed id) *! *! Set the callback function to be called when one of the outputs has *! been closed from the other side. */
0335d82008-04-18Stephen R. van den Berg static void pipe_set_output_closed_callback(INT32 args) { if (args==0) { free_svalue(&THIS->output_closed_callback);
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(THIS->output_closed_callback, T_INT);
0335d82008-04-18Stephen R. van den Berg  return; }
017b572011-10-28Henrik Grubbström (Grubba)  if (args<1 || (TYPEOF(sp[-args]) != T_FUNCTION && TYPEOF(sp[-args]) != T_ARRAY))
0335d82008-04-18Stephen R. van den Berg  Pike_error("Illegal argument to set_output_closed_callback()\n"); if (args>1) { free_svalue(&THIS->id); assign_svalue_no_free(&(THIS->id),sp-args+1); } free_svalue(&THIS->output_closed_callback); assign_svalue_no_free(&(THIS->output_closed_callback),sp-args); pop_n_elems(args-1); }
105cbd2008-04-19Henrik Grubbström (Grubba) /*! @decl void finish() *! *! Terminate and reinitialize the pipe. */
0335d82008-04-18Stephen R. van den Berg static void pipe_finish(INT32 args) { pop_n_elems(args); push_int(0); pipe_done(); }
105cbd2008-04-19Henrik Grubbström (Grubba) /*! @decl void start() *! *! Start sending the input(s) to the output(s). */
0335d82008-04-18Stephen R. van den Berg static void pipe_start(INT32 args) /* force start */ { low_start(); if(args) pop_n_elems(args-1); }
105cbd2008-04-19Henrik Grubbström (Grubba) /*! @decl int bytes_sent() *! *! Return the number of bytes sent. */
0335d82008-04-18Stephen R. van den Berg static void f_bytes_sent(INT32 args) { pop_n_elems(args); push_int(THIS->sent); } /********** callbacks *******************************************************/ static void pipe_write_output_callback(INT32 args) {
017b572011-10-28Henrik Grubbström (Grubba)  if (args<1 || TYPEOF(sp[-args]) != T_OBJECT)
0335d82008-04-18Stephen R. van den Berg  Pike_error("Illegal argument to pipe->write_output_callback\n"); if(!sp[-args].u.object->prog) return; if(sp[-args].u.object->prog != output_program) Pike_error("Illegal argument to pipe->write_output_callback\n"); debug_malloc_touch(sp[-args].u.object); output_try_write_some(sp[-args].u.object); pop_n_elems(args-1); } static void pipe_close_output_callback(INT32 args) { struct output *o;
017b572011-10-28Henrik Grubbström (Grubba)  if (args<1 || TYPEOF(sp[-args]) != T_OBJECT)
0335d82008-04-18Stephen R. van den Berg  if(!sp[-args].u.object->prog) return; if(sp[-args].u.object->prog != output_program) Pike_error("Illegal argument to pipe->close_output_callback\n"); o=(struct output *)(sp[-args].u.object->storage);
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(THIS->output_closed_callback) != T_INT)
0335d82008-04-18Stephen R. van den Berg  { assign_svalue_no_free(sp++,&THIS->id); push_object(o->obj); apply_svalue(&(THIS->output_closed_callback),2); pop_stack(); } output_finish(sp[-args].u.object); pop_n_elems(args-1); } static void pipe_read_input_callback(INT32 args) { struct input *i; struct pike_string *s;
017b572011-10-28Henrik Grubbström (Grubba)  if (args<2 || TYPEOF(sp[1-args]) != T_STRING)
0335d82008-04-18Stephen R. van den Berg  Pike_error("Illegal argument to pipe->read_input_callback\n"); i=THIS->firstinput; if (!i) Pike_error("Pipe read callback without any inputs left.\n"); s=sp[1-args].u.string; if(append_buffer(s)) { /* THIS DOES NOT WORK */ push_int(0); push_int(0); push_callback(offset_input_close_callback); apply_low(i->u.obj,i->set_nonblocking_offset,3); pop_stack(); THIS->sleeping=1; } low_start(); pop_n_elems(args-1); } static void pipe_close_input_callback(INT32 args) { struct input *i; i=THIS->firstinput; if(!i) Pike_error("Input close callback without inputs!\n"); if(i->type != I_OBJ) Pike_error("Premature close callback on pipe!.\n"); if (i->u.obj->prog) { #ifdef BLOCKING_CLOSE apply_low(i->u.obj,i->set_blocking_offset,0); pop_stack(); #endif apply(i->u.obj,"close",0); pop_stack(); } nobjects--; free_object(i->u.obj); i->type=I_NONE; input_finish(); if(args) pop_n_elems(args-1); }
105cbd2008-04-19Henrik Grubbström (Grubba) /*! @decl string version() *! *! Return the version of the module. */
0335d82008-04-18Stephen R. van den Berg static void pipe_version(INT32 args) { pop_n_elems(args); push_text("PIPE ver 2.0"); } /********** init/exit *******************************************************/ void close_and_free_everything(struct object *thisobj,struct pipe *p) { struct buffer *b; struct input *i; struct output *o; struct object *obj; debug_malloc_touch(thisobj); debug_malloc_touch(p); if(p->done){ return; } p->done=1; if (thisobj) add_ref(thisobj); /* don't kill object during this */ while (p->firstbuffer) { b=p->firstbuffer; p->firstbuffer=b->next; sbuffers-=b->s->len; nbuffers--; free_string(b->s); b->next=NULL;
0ec7522014-04-27Martin Nilsson  free(b); /* Hubbe */
0335d82008-04-18Stephen R. van den Berg  } p->lastbuffer=NULL; while (p->firstinput) { i=p->firstinput; p->firstinput=i->next; free_input(i); } p->lastinput=NULL; while (p->firstoutput) { obj=p->firstoutput; o=(struct output *)(obj->storage); p->firstoutput=o->next; output_finish(obj); free_object(obj); } if (p->fd!=-1) { fd_close(p->fd); p->fd=-1; } p->living_outputs=0; if (thisobj) free_object(thisobj); free_svalue(& p->done_callback); free_svalue(& p->output_closed_callback); free_svalue(& p->id);
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(p->done_callback, T_INT); SET_SVAL_TYPE(p->output_closed_callback, T_INT); SET_SVAL_TYPE(p->id, T_INT);
0335d82008-04-18Stephen R. van den Berg  /* p->done=0; */ }
926b192013-05-27Martin Nilsson static void init_pipe_struct(struct object *DEBUGUSED(o))
0335d82008-04-18Stephen R. van den Berg { debug_malloc_touch(o); THIS->firstbuffer=THIS->lastbuffer=NULL; THIS->firstinput=THIS->lastinput=NULL; THIS->firstoutput=NULL; THIS->bytes_in_buffer=0; THIS->pos=0; THIS->sleeping=0; THIS->done=0; THIS->fd=-1;
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(THIS->done_callback, T_INT); SET_SVAL_TYPE(THIS->output_closed_callback, T_INT); SET_SVAL_TYPE(THIS->id, T_INT);
0335d82008-04-18Stephen R. van den Berg  THIS->id.u.integer=0; THIS->living_outputs=0; THIS->sent=0; }
74dfe82012-12-30Jonas Walldén static void exit_pipe_struct(struct object *UNUSED(o))
0335d82008-04-18Stephen R. van den Berg { close_and_free_everything(NULL,THIS); }
926b192013-05-27Martin Nilsson static void exit_output_struct(struct object *DEBUGUSED(obj))
0335d82008-04-18Stephen R. van den Berg { struct output *o; debug_malloc_touch(obj); o=(struct output *)(Pike_fp->current_storage); if (o->obj) { if(o->obj->prog) { #ifdef BLOCKING_CLOSE apply_low(o->obj,o->set_blocking_offset,0); pop_stack(); #endif push_int(0); apply(o->obj,"set_id",1); pop_stack(); apply(o->obj,"close",0); pop_stack(); if(!THISOBJ->prog) Pike_error("Pipe done callback destructed pipe.\n"); } free_object(o->obj); noutputs--; o->obj=0; o->fd=-1; } }
926b192013-05-27Martin Nilsson static void init_output_struct(struct object *DEBUGUSED(ob))
0335d82008-04-18Stephen R. van den Berg { struct output *o; debug_malloc_touch(ob); o=(struct output *)(Pike_fp->current_storage); o->obj=0; } /********** Pike init *******************************************************/ void port_setup_program(void); void f__pipe_debug(INT32 args) { pop_n_elems(args); push_int(DO_NOT_WARN(noutputs)); push_int(DO_NOT_WARN(ninputs)); push_int(DO_NOT_WARN(nstrings)); push_int(DO_NOT_WARN(nobjects)); push_int(DO_NOT_WARN(mmapped)); push_int(DO_NOT_WARN(nbuffers)); push_int(DO_NOT_WARN(sbuffers)); f_aggregate(7); }
105cbd2008-04-19Henrik Grubbström (Grubba) /*! @endclass */ /*! @endmodule */
0335d82008-04-18Stephen R. van den Berg PIKE_MODULE_INIT { start_new_program(); ADD_STORAGE(struct pipe); /* function(object:void) */ ADD_FUNCTION("input",pipe_input,tFunc(tObj,tVoid),0); /* function(object,void|int:void) */ ADD_FUNCTION("output",pipe_output,tFunc(tObj tOr(tVoid,tInt),tVoid),0); /* function(string:void) */ ADD_FUNCTION("write",pipe_write,tFunc(tStr,tVoid),0); /* function(:void) */ ADD_FUNCTION("start",pipe_start,tFunc(tNone,tVoid),0); /* function(:void) */ ADD_FUNCTION("finish",pipe_finish,tFunc(tNone,tVoid),0); /* function(void|function(mixed,object:mixed),void|mixed:void) */ ADD_FUNCTION("set_output_closed_callback",pipe_set_output_closed_callback,tFunc(tOr(tVoid,tFunc(tMix tObj,tMix)) tOr(tVoid,tMix),tVoid),0); /* function(void|function(mixed:mixed),void|mixed:void) */ ADD_FUNCTION("set_done_callback",pipe_set_done_callback,tFunc(tOr(tVoid,tFunc(tMix,tMix)) tOr(tVoid,tMix),tVoid),0); /* function(int:void) */ ADD_FUNCTION("_output_close_callback",pipe_close_output_callback,tFunc(tInt,tVoid),0); /* function(int:void) */ ADD_FUNCTION("_input_close_callback",pipe_close_input_callback,tFunc(tInt,tVoid),0); /* function(int:void) */ ADD_FUNCTION("_output_write_callback",pipe_write_output_callback,tFunc(tInt,tVoid),0); /* function(int,string:void) */ ADD_FUNCTION("_input_read_callback",pipe_read_input_callback,tFunc(tInt tStr,tVoid),0); /* function(:string) */ ADD_FUNCTION("version",pipe_version,tFunc(tNone,tStr),0); /* function(:int) */ ADD_FUNCTION("bytes_sent",f_bytes_sent,tFunc(tNone,tInt),0); set_init_callback(init_pipe_struct); set_exit_callback(exit_pipe_struct); pipe_program=end_program(); add_program_constant("pipe",pipe_program, 0); offset_output_close_callback=find_identifier("_output_close_callback", pipe_program); offset_input_close_callback=find_identifier("_input_close_callback", pipe_program); offset_output_write_callback=find_identifier("_output_write_callback", pipe_program); offset_input_read_callback=find_identifier("_input_read_callback", pipe_program); start_new_program(); ADD_STORAGE(struct output); set_init_callback(init_output_struct); set_exit_callback(exit_output_struct); output_program=end_program(); add_program_constant("__output",output_program, 0); /* function(:array) */ ADD_FUNCTION("_pipe_debug", f__pipe_debug,tFunc(tNone,tArray), 0); } PIKE_MODULE_EXIT { if(pipe_program) free_program(pipe_program); pipe_program=0; if(output_program) free_program(output_program); output_program=0; }