/* * Copyright (C) 1996-2005 The Free Software Foundation, Inc. * * 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 2, 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. */ /* Code for the buffer data structure. */ #include #include "cvs.h" #include "buffer.h" #if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) #ifdef HAVE_WINSOCK_H # include #else # include #endif /* OS/2 doesn't have EIO. FIXME: this whole notion of turning a different error into EIO strikes me as pretty dubious. */ #if !defined (EIO) #define EIO EBADPOS #endif /* Linked list of available buffer_data structures. */ static struct buffer_data *free_buffer_data; /* Local functions. */ static void buf_default_memory_error PROTO ((struct buffer *)); static void allocate_buffer_datas PROTO((void)); static struct buffer_data *get_buffer_data PROTO((void)); /* Initialize a buffer structure. */ struct buffer * buf_initialize (input, output, flush, block, shutdown, memory, closure) int (*input) PROTO((void *, char *, int, int, int *)); int (*output) PROTO((void *, const char *, int, int *)); int (*flush) PROTO((void *)); int (*block) PROTO((void *, int)); int (*shutdown) PROTO((struct buffer *)); void (*memory) PROTO((struct buffer *)); void *closure; { struct buffer *buf; buf = (struct buffer *) xmalloc (sizeof (struct buffer)); buf->data = NULL; buf->last = NULL; buf->nonblocking = 0; buf->input = input; buf->output = output; buf->flush = flush; buf->block = block; buf->shutdown = shutdown; buf->memory_error = memory ? memory : buf_default_memory_error; buf->closure = closure; return buf; } /* Free a buffer structure. */ void buf_free (buf) struct buffer *buf; { if (buf->closure != NULL) { free (buf->closure); buf->closure = NULL; } if (buf->data != NULL) { buf->last->next = free_buffer_data; free_buffer_data = buf->data; } free (buf); } /* Initialize a buffer structure which is not to be used for I/O. */ struct buffer * buf_nonio_initialize (memory) void (*memory) PROTO((struct buffer *)); { return (buf_initialize ((int (*) PROTO((void *, char *, int, int, int *))) NULL, (int (*) PROTO((void *, const char *, int, int *))) NULL, (int (*) PROTO((void *))) NULL, (int (*) PROTO((void *, int))) NULL, (int (*) PROTO((struct buffer *))) NULL, memory, (void *) NULL)); } /* Default memory error handler. */ static void buf_default_memory_error (buf) struct buffer *buf; { error (1, 0, "out of memory"); } /* Allocate more buffer_data structures. */ static void allocate_buffer_datas () { struct buffer_data *alc; char *space; int i; /* Allocate buffer_data structures in blocks of 16. */ #define ALLOC_COUNT (16) alc = xmalloc (ALLOC_COUNT * sizeof (struct buffer_data)); space = (char *) valloc (ALLOC_COUNT * BUFFER_DATA_SIZE); if (!space) { free (alc); return; } for (i = 0; i < ALLOC_COUNT; i++, alc++, space += BUFFER_DATA_SIZE) { alc->next = free_buffer_data; free_buffer_data = alc; alc->text = space; } } /* Get a new buffer_data structure. */ static struct buffer_data * get_buffer_data () { struct buffer_data *ret; if (free_buffer_data == NULL) { allocate_buffer_datas (); if (free_buffer_data == NULL) return NULL; } ret = free_buffer_data; free_buffer_data = ret->next; return ret; } /* See whether a buffer and its file descriptor is empty. */ int buf_empty (buf) struct buffer *buf; { /* Try and read any data on the file descriptor first. * We already know the descriptor is non-blocking. */ buf_input_data (buf, NULL); return buf_empty_p (buf); } /* See whether a buffer is empty. */ int buf_empty_p (buf) struct buffer *buf; { struct buffer_data *data; for (data = buf->data; data != NULL; data = data->next) if (data->size > 0) return 0; return 1; } #ifdef SERVER_FLOWCONTROL /* * Count how much data is stored in the buffer.. * Note that each buffer is a xmalloc'ed chunk BUFFER_DATA_SIZE. */ int buf_count_mem (buf) struct buffer *buf; { struct buffer_data *data; int mem = 0; for (data = buf->data; data != NULL; data = data->next) mem += BUFFER_DATA_SIZE; return mem; } #endif /* SERVER_FLOWCONTROL */ /* Add data DATA of length LEN to BUF. */ void buf_output (buf, data, len) struct buffer *buf; const char *data; int len; { if (buf->data != NULL && (((buf->last->text + BUFFER_DATA_SIZE) - (buf->last->bufp + buf->last->size)) >= len)) { memcpy (buf->last->bufp + buf->last->size, data, len); buf->last->size += len; return; } while (1) { struct buffer_data *newdata; newdata = get_buffer_data (); if (newdata == NULL) { (*buf->memory_error) (buf); return; } if (buf->data == NULL) buf->data = newdata; else buf->last->next = newdata; newdata->next = NULL; buf->last = newdata; newdata->bufp = newdata->text; if (len <= BUFFER_DATA_SIZE) { newdata->size = len; memcpy (newdata->text, data, len); return; } newdata->size = BUFFER_DATA_SIZE; memcpy (newdata->text, data, BUFFER_DATA_SIZE); data += BUFFER_DATA_SIZE; len -= BUFFER_DATA_SIZE; } /*NOTREACHED*/ } /* Add a '\0' terminated string to BUF. */ void buf_output0 (buf, string) struct buffer *buf; const char *string; { buf_output (buf, string, strlen (string)); } /* Add a single character to BUF. */ void buf_append_char (buf, ch) struct buffer *buf; int ch; { if (buf->data != NULL && (buf->last->text + BUFFER_DATA_SIZE != buf->last->bufp + buf->last->size)) { *(buf->last->bufp + buf->last->size) = ch; ++buf->last->size; } else { char b; b = ch; buf_output (buf, &b, 1); } } /* * Send all the output we've been saving up. Returns 0 for success or * errno code. If the buffer has been set to be nonblocking, this * will just write until the write would block. */ int buf_send_output (buf) struct buffer *buf; { if (buf->output == NULL) abort (); while (buf->data != NULL) { struct buffer_data *data; data = buf->data; if (data->size > 0) { int status, nbytes; status = (*buf->output) (buf->closure, data->bufp, data->size, &nbytes); if (status != 0) { /* Some sort of error. Discard the data, and return. */ buf->last->next = free_buffer_data; free_buffer_data = buf->data; buf->data = NULL; buf->last = NULL; return status; } if (nbytes != data->size) { /* Not all the data was written out. This is only permitted in nonblocking mode. Adjust the buffer, and return. */ assert (buf->nonblocking); data->size -= nbytes; data->bufp += nbytes; return 0; } } buf->data = data->next; data->next = free_buffer_data; free_buffer_data = data; } buf->last = NULL; return 0; } /* * Flush any data queued up in the buffer. If BLOCK is nonzero, then * if the buffer is in nonblocking mode, put it into blocking mode for * the duration of the flush. This returns 0 on success, or an error * code. */ int buf_flush (buf, block) struct buffer *buf; int block; { int nonblocking; int status; if (buf->flush == NULL) abort (); nonblocking = buf->nonblocking; if (nonblocking && block) { status = set_block (buf); if (status != 0) return status; } status = buf_send_output (buf); if (status == 0) status = (*buf->flush) (buf->closure); if (nonblocking && block) { int blockstat; blockstat = set_nonblock (buf); if (status == 0) status = blockstat; } return status; } /* * Set buffer BUF to nonblocking I/O. Returns 0 for success or errno * code. */ int set_nonblock (buf) struct buffer *buf; { int status; if (buf->nonblocking) return 0; if (buf->block == NULL) abort (); status = (*buf->block) (buf->closure, 0); if (status != 0) return status; buf->nonblocking = 1; return 0; } /* * Set buffer BUF to blocking I/O. Returns 0 for success or errno * code. */ int set_block (buf) struct buffer *buf; { int status; if (! buf->nonblocking) return 0; if (buf->block == NULL) abort (); status = (*buf->block) (buf->closure, 1); if (status != 0) return status; buf->nonblocking = 0; return 0; } /* * Send a character count and some output. Returns errno code or 0 for * success. * * Sending the count in binary is OK since this is only used on a pipe * within the same system. */ int buf_send_counted (buf) struct buffer *buf; { int size; struct buffer_data *data; size = 0; for (data = buf->data; data != NULL; data = data->next) size += data->size; data = get_buffer_data (); if (data == NULL) { (*buf->memory_error) (buf); return ENOMEM; } data->next = buf->data; buf->data = data; if (buf->last == NULL) buf->last = data; data->bufp = data->text; data->size = sizeof (int); *((int *) data->text) = size; return buf_send_output (buf); } /* * Send a special count. COUNT should be negative. It will be * handled speciallyi by buf_copy_counted. This function returns 0 or * an errno code. * * Sending the count in binary is OK since this is only used on a pipe * within the same system. */ int buf_send_special_count (buf, count) struct buffer *buf; int count; { struct buffer_data *data; data = get_buffer_data (); if (data == NULL) { (*buf->memory_error) (buf); return ENOMEM; } data->next = buf->data; buf->data = data; if (buf->last == NULL) buf->last = data; data->bufp = data->text; data->size = sizeof (int); *((int *) data->text) = count; return buf_send_output (buf); } /* Append a list of buffer_data structures to an buffer. */ void buf_append_data (buf, data, last) struct buffer *buf; struct buffer_data *data; struct buffer_data *last; { if (data != NULL) { if (buf->data == NULL) buf->data = data; else buf->last->next = data; buf->last = last; } } /* Append the data on one buffer to another. This removes the data from the source buffer. */ void buf_append_buffer (to, from) struct buffer *to; struct buffer *from; { buf_append_data (to, from->data, from->last); from->data = NULL; from->last = NULL; } /* * Copy the contents of file F into buffer_data structures. We can't * copy directly into an buffer, because we want to handle failure and * succeess differently. Returns 0 on success, or -2 if out of * memory, or a status code on error. Since the caller happens to * know the size of the file, it is passed in as SIZE. On success, * this function sets *RETP and *LASTP, which may be passed to * buf_append_data. */ int buf_read_file (f, size, retp, lastp) FILE *f; long size; struct buffer_data **retp; struct buffer_data **lastp; { int status; *retp = NULL; *lastp = NULL; while (size > 0) { struct buffer_data *data; int get; data = get_buffer_data (); if (data == NULL) { status = -2; goto error_return; } if (*retp == NULL) *retp = data; else (*lastp)->next = data; data->next = NULL; *lastp = data; data->bufp = data->text; data->size = 0; if (size > BUFFER_DATA_SIZE) get = BUFFER_DATA_SIZE; else get = size; errno = EIO; if (fread (data->text, get, 1, f) != 1) { status = errno; goto error_return; } data->size += get; size -= get; } return 0; error_return: if (*retp != NULL) { (*lastp)->next = free_buffer_data; free_buffer_data = *retp; } return status; } /* * Copy the contents of file F into buffer_data structures. We can't * copy directly into an buffer, because we want to handle failure and * succeess differently. Returns 0 on success, or -2 if out of * memory, or a status code on error. On success, this function sets * *RETP and *LASTP, which may be passed to buf_append_data. */ int buf_read_file_to_eof (f, retp, lastp) FILE *f; struct buffer_data **retp; struct buffer_data **lastp; { int status; *retp = NULL; *lastp = NULL; while (!feof (f)) { struct buffer_data *data; int get, nread; data = get_buffer_data (); if (data == NULL) { status = -2; goto error_return; } if (*retp == NULL) *retp = data; else (*lastp)->next = data; data->next = NULL; *lastp = data; data->bufp = data->text; data->size = 0; get = BUFFER_DATA_SIZE; errno = EIO; nread = fread (data->text, 1, get, f); if (nread == 0 && !feof (f)) { status = errno; goto error_return; } data->size = nread; } return 0; error_return: if (*retp != NULL) { (*lastp)->next = free_buffer_data; free_buffer_data = *retp; } return status; } /* Return the number of bytes in a chain of buffer_data structures. */ int buf_chain_length (buf) struct buffer_data *buf; { int size = 0; while (buf) { size += buf->size; buf = buf->next; } return size; } /* Return the number of bytes in a buffer. */ int buf_length (buf) struct buffer *buf; { return buf_chain_length (buf->data); } /* * Read an arbitrary amount of data into an input buffer. The buffer * will be in nonblocking mode, and we just grab what we can. Return * 0 on success, or -1 on end of file, or -2 if out of memory, or an * error code. If COUNTP is not NULL, *COUNTP is set to the number of * bytes read. */ int buf_input_data (buf, countp) struct buffer *buf; int *countp; { if (buf->input == NULL) abort (); if (countp != NULL) *countp = 0; while (1) { int get; int status, nbytes; if (buf->data == NULL || (buf->last->bufp + buf->last->size == buf->last->text + BUFFER_DATA_SIZE)) { struct buffer_data *data; data = get_buffer_data (); if (data == NULL) { (*buf->memory_error) (buf); return -2; } if (buf->data == NULL) buf->data = data; else buf->last->next = data; data->next = NULL; buf->last = data; data->bufp = data->text; data->size = 0; } get = ((buf->last->text + BUFFER_DATA_SIZE) - (buf->last->bufp + buf->last->size)); status = (*buf->input) (buf->closure, buf->last->bufp + buf->last->size, 0, get, &nbytes); if (status != 0) return status; buf->last->size += nbytes; if (countp != NULL) *countp += nbytes; if (nbytes < get) { /* If we did not fill the buffer, then presumably we read all the available data. */ return 0; } } /*NOTREACHED*/ } /* * Read a line (characters up to a \012) from an input buffer. (We * use \012 rather than \n for the benefit of non Unix clients for * which \n means something else). This returns 0 on success, or -1 * on end of file, or -2 if out of memory, or an error code. If it * succeeds, it sets *LINE to an allocated buffer holding the contents * of the line. The trailing \012 is not included in the buffer. If * LENP is not NULL, then *LENP is set to the number of bytes read; * strlen may not work, because there may be embedded null bytes. */ int buf_read_line (buf, line, lenp) struct buffer *buf; char **line; int *lenp; { if (buf->input == NULL) abort (); *line = NULL; while (1) { int len, finallen = 0; struct buffer_data *data; char *nl; /* See if there is a newline in BUF. */ len = 0; for (data = buf->data; data != NULL; data = data->next) { nl = memchr (data->bufp, '\012', data->size); if (nl != NULL) { finallen = nl - data->bufp; len += finallen; break; } len += data->size; } /* If we found a newline, copy the line into a memory buffer, and remove it from BUF. */ if (data != NULL) { char *p; struct buffer_data *nldata; p = xmalloc (len + 1); if (p == NULL) return -2; *line = p; nldata = data; data = buf->data; while (data != nldata) { struct buffer_data *next; memcpy (p, data->bufp, data->size); p += data->size; next = data->next; data->next = free_buffer_data; free_buffer_data = data; data = next; } memcpy (p, data->bufp, finallen); p[finallen] = '\0'; data->size -= finallen + 1; data->bufp = nl + 1; buf->data = data; if (lenp != NULL) *lenp = len; return 0; } /* Read more data until we get a newline. */ while (1) { int size, status, nbytes; char *mem; if (buf->data == NULL || (buf->last->bufp + buf->last->size == buf->last->text + BUFFER_DATA_SIZE)) { data = get_buffer_data (); if (data == NULL) { (*buf->memory_error) (buf); return -2; } if (buf->data == NULL) buf->data = data; else buf->last->next = data; data->next = NULL; buf->last = data; data->bufp = data->text; data->size = 0; } mem = buf->last->bufp + buf->last->size; size = (buf->last->text + BUFFER_DATA_SIZE) - mem; /* We need to read at least 1 byte. We can handle up to SIZE bytes. This will only be efficient if the underlying communication stream does its own buffering, or is clever about getting more than 1 byte at a time. */ status = (*buf->input) (buf->closure, mem, 1, size, &nbytes); if (status != 0) return status; buf->last->size += nbytes; /* Optimize slightly to avoid an unnecessary call to memchr. */ if (nbytes == 1) { if (*mem == '\012') break; } else { if (memchr (mem, '\012', nbytes) != NULL) break; } } } } /* * Extract data from the input buffer BUF. This will read up to WANT * bytes from the buffer. It will set *RETDATA to point at the bytes, * and set *GOT to the number of bytes to be found there. Any buffer * call which uses BUF may change the contents of the buffer at *DATA, * so the data should be fully processed before any further calls are * made. This returns 0 on success, or -1 on end of file, or -2 if * out of memory, or an error code. */ int buf_read_data (buf, want, retdata, got) struct buffer *buf; int want; char **retdata; int *got; { if (buf->input == NULL) abort (); while (buf->data != NULL && buf->data->size == 0) { struct buffer_data *next; next = buf->data->next; buf->data->next = free_buffer_data; free_buffer_data = buf->data; buf->data = next; if (next == NULL) buf->last = NULL; } if (buf->data == NULL) { struct buffer_data *data; int get, status, nbytes; data = get_buffer_data (); if (data == NULL) { (*buf->memory_error) (buf); return -2; } buf->data = data; buf->last = data; data->next = NULL; data->bufp = data->text; data->size = 0; if (want < BUFFER_DATA_SIZE) get = want; else get = BUFFER_DATA_SIZE; status = (*buf->input) (buf->closure, data->bufp, get, BUFFER_DATA_SIZE, &nbytes); if (status != 0) return status; data->size = nbytes; } *retdata = buf->data->bufp; if (want < buf->data->size) { *got = want; buf->data->size -= want; buf->data->bufp += want; } else { *got = buf->data->size; buf->data->size = 0; } return 0; } /* * Copy lines from an input buffer to an output buffer. This copies * all complete lines (characters up to a newline) from INBUF to * OUTBUF. Each line in OUTBUF is preceded by the character COMMAND * and a space. */ void buf_copy_lines (outbuf, inbuf, command) struct buffer *outbuf; struct buffer *inbuf; int command; { while (1) { struct buffer_data *data; struct buffer_data *nldata; char *nl; int len; /* See if there is a newline in INBUF. */ nldata = NULL; nl = NULL; for (data = inbuf->data; data != NULL; data = data->next) { nl = memchr (data->bufp, '\n', data->size); if (nl != NULL) { nldata = data; break; } } if (nldata == NULL) { /* There are no more lines in INBUF. */ return; } /* Put in the command. */ buf_append_char (outbuf, command); buf_append_char (outbuf, ' '); if (inbuf->data != nldata) { /* * Simply move over all the buffers up to the one containing * the newline. */ for (data = inbuf->data; data->next != nldata; data = data->next) ; data->next = NULL; buf_append_data (outbuf, inbuf->data, data); inbuf->data = nldata; } /* * If the newline is at the very end of the buffer, just move * the buffer onto OUTBUF. Otherwise we must copy the data. */ len = nl + 1 - nldata->bufp; if (len == nldata->size) { inbuf->data = nldata->next; if (inbuf->data == NULL) inbuf->last = NULL; nldata->next = NULL; buf_append_data (outbuf, nldata, nldata); } else { buf_output (outbuf, nldata->bufp, len); nldata->bufp += len; nldata->size -= len; } } } /* * Copy counted data from one buffer to another. The count is an * integer, host size, host byte order (it is only used across a * pipe). If there is enough data, it should be moved over. If there * is not enough data, it should remain on the original buffer. A * negative count is a special case. if one is seen, *SPECIAL is set * to the (negative) count value and no additional data is gathered * from the buffer; normally *SPECIAL is set to 0. This function * returns the number of bytes it needs to see in order to actually * copy something over. */ int buf_copy_counted (outbuf, inbuf, special) struct buffer *outbuf; struct buffer *inbuf; int *special; { *special = 0; while (1) { struct buffer_data *data; int need; union { char intbuf[sizeof (int)]; int i; } u; char *intp; int count; struct buffer_data *start; int startoff; struct buffer_data *stop; int stopwant; /* See if we have enough bytes to figure out the count. */ need = sizeof (int); intp = u.intbuf; for (data = inbuf->data; data != NULL; data = data->next) { if (data->size >= need) { memcpy (intp, data->bufp, need); break; } memcpy (intp, data->bufp, data->size); intp += data->size; need -= data->size; } if (data == NULL) { /* We don't have enough bytes to form an integer. */ return need; } count = u.i; start = data; startoff = need; if (count < 0) { /* A negative COUNT is a special case meaning that we don't need any further information. */ stop = start; stopwant = 0; } else { /* * We have an integer in COUNT. We have gotten all the * data from INBUF in all buffers before START, and we * have gotten STARTOFF bytes from START. See if we have * enough bytes remaining in INBUF. */ need = count - (start->size - startoff); if (need <= 0) { stop = start; stopwant = count; } else { for (data = start->next; data != NULL; data = data->next) { if (need <= data->size) break; need -= data->size; } if (data == NULL) { /* We don't have enough bytes. */ return need; } stop = data; stopwant = need; } } /* * We have enough bytes. Free any buffers in INBUF before * START, and remove STARTOFF bytes from START, so that we can * forget about STARTOFF. */ start->bufp += startoff; start->size -= startoff; if (start->size == 0) start = start->next; if (stop->size == stopwant) { stop = stop->next; stopwant = 0; } while (inbuf->data != start) { data = inbuf->data; inbuf->data = data->next; data->next = free_buffer_data; free_buffer_data = data; } /* If COUNT is negative, set *SPECIAL and get out now. */ if (count < 0) { *special = count; return 0; } /* * We want to copy over the bytes from START through STOP. We * only want STOPWANT bytes from STOP. */ if (start != stop) { /* Attach the buffers from START through STOP to OUTBUF. */ for (data = start; data->next != stop; data = data->next) ; inbuf->data = stop; data->next = NULL; buf_append_data (outbuf, start, data); } if (stopwant > 0) { buf_output (outbuf, stop->bufp, stopwant); stop->bufp += stopwant; stop->size -= stopwant; } } /*NOTREACHED*/ } /* Shut down a buffer. This returns 0 on success, or an errno code. */ int buf_shutdown (buf) struct buffer *buf; { if (buf->shutdown) return (*buf->shutdown) (buf); return 0; } /* The simplest type of buffer is one built on top of a stdio FILE. For simplicity, and because it is all that is required, we do not implement setting this type of buffer into nonblocking mode. The closure field is just a FILE *. */ static int stdio_buffer_input PROTO((void *, char *, int, int, int *)); static int stdio_buffer_output PROTO((void *, const char *, int, int *)); static int stdio_buffer_flush PROTO((void *)); static int stdio_buffer_shutdown PROTO((struct buffer *buf)); /* Initialize a buffer built on a stdio FILE. */ struct stdio_buffer_closure { FILE *fp; int child_pid; }; struct buffer * stdio_buffer_initialize (fp, child_pid, input, memory) FILE *fp; int child_pid; int input; void (*memory) PROTO((struct buffer *)); { struct stdio_buffer_closure *bc = xmalloc (sizeof (*bc)); bc->fp = fp; bc->child_pid = child_pid; return buf_initialize (input ? stdio_buffer_input : NULL, input ? NULL : stdio_buffer_output, input ? NULL : stdio_buffer_flush, (int (*) PROTO((void *, int))) NULL, stdio_buffer_shutdown, memory, (void *) bc); } /* Return the file associated with a stdio buffer. */ FILE * stdio_buffer_get_file (buf) struct buffer *buf; { struct stdio_buffer_closure *bc; assert(buf->shutdown == stdio_buffer_shutdown); bc = (struct stdio_buffer_closure *) buf->closure; return(bc->fp); } /* The buffer input function for a buffer built on a stdio FILE. */ static int stdio_buffer_input (closure, data, need, size, got) void *closure; char *data; int need; int size; int *got; { struct stdio_buffer_closure *bc = (struct stdio_buffer_closure *) closure; int nbytes; /* Since stdio does its own buffering, we don't worry about getting more bytes than we need. */ if (need == 0 || need == 1) { int ch; ch = getc (bc->fp); if (ch == EOF) { if (feof (bc->fp)) return -1; else if (errno == 0) return EIO; else return errno; } *data = ch; *got = 1; return 0; } nbytes = fread (data, 1, need, bc->fp); if (nbytes == 0) { *got = 0; if (feof (bc->fp)) return -1; else if (errno == 0) return EIO; else return errno; } *got = nbytes; return 0; } /* The buffer output function for a buffer built on a stdio FILE. */ static int stdio_buffer_output (closure, data, have, wrote) void *closure; const char *data; int have; int *wrote; { struct stdio_buffer_closure *bc = (struct stdio_buffer_closure *) closure; *wrote = 0; while (have > 0) { int nbytes; nbytes = fwrite (data, 1, have, bc->fp); if (nbytes != have) { if (errno == 0) return EIO; else return errno; } *wrote += nbytes; have -= nbytes; data += nbytes; } return 0; } /* The buffer flush function for a buffer built on a stdio FILE. */ static int stdio_buffer_flush (closure) void *closure; { struct stdio_buffer_closure *bc = (struct stdio_buffer_closure *) closure; if (fflush (bc->fp) != 0) { if (errno == 0) return EIO; else return errno; } return 0; } static int stdio_buffer_shutdown (buf) struct buffer *buf; { struct stdio_buffer_closure *bc = buf->closure; struct stat s; int closefp, statted; /* Must be a pipe or a socket. What could go wrong? * Well, apparently for disconnected clients under AIX, the * fstat() will return -1 on the server if the client has gone * away. */ if (CVS_FSTAT(fileno(bc->fp), &s) == -1) statted = 0; else statted = 1; closefp = statted; /* Flush the buffer if we can */ if (buf->flush) { buf_flush (buf, 1); buf->flush = NULL; } if (buf->input) { /* There used to be a check here for unread data in the buffer of on * the pipe, but it was deemed unnecessary and possibly dangerous. In * some sense it could be second-guessing the caller who requested it * closed, as well. */ # ifdef SHUTDOWN_SERVER if (current_parsed_root->method != server_method) # endif # ifndef NO_SOCKET_TO_FD { /* shutdown() sockets */ if (statted && S_ISSOCK (s.st_mode)) shutdown (fileno (bc->fp), 0); } # endif /* NO_SOCKET_TO_FD */ # ifdef START_RSH_WITH_POPEN_RW /* Can't be set with SHUTDOWN_SERVER defined */ else if (pclose (bc->fp) == EOF) { error (0, errno, "closing connection to %s", current_parsed_root->hostname); closefp = 0; } # endif /* START_RSH_WITH_POPEN_RW */ buf->input = NULL; } else if (buf->output) { # ifdef SHUTDOWN_SERVER /* FIXME: Should have a SHUTDOWN_SERVER_INPUT & * SHUTDOWN_SERVER_OUTPUT */ if (current_parsed_root->method == server_method) SHUTDOWN_SERVER (fileno (bc->fp)); else # endif # ifndef NO_SOCKET_TO_FD /* shutdown() sockets */ if (statted && S_ISSOCK (s.st_mode)) shutdown (fileno (bc->fp), 1); # else { /* I'm not sure I like this empty block, but the alternative * is a another nested NO_SOCKET_TO_FD switch above. */ } # endif /* NO_SOCKET_TO_FD */ buf->output = NULL; } if (statted && closefp && fclose (bc->fp) == EOF) { if (server_active) { /* Syslog this? */ } # ifdef CLIENT_SUPPORT /* We are already closing the connection. * On error, print a warning and try to * continue to avoid infinte loops. */ else error (0, errno, "closing down connection to %s", current_parsed_root->hostname); # endif /* CLIENT_SUPPORT */ } /* If we were talking to a process, make sure it exited */ if (bc->child_pid) { int w; do w = waitpid (bc->child_pid, (int *) 0, 0); while (w == -1 && errno == EINTR); /* We are already closing the connection. * On error, print a warning and try to * continue to avoid infinte loops. */ if (w == -1) error (0, errno, "waiting for process %d", bc->child_pid); } return 0; } /* Certain types of communication input and output data in packets, where each packet is translated in some fashion. The packetizing buffer type supports that, given a buffer which handles lower level I/O and a routine to translate the data in a packet. This code uses two bytes for the size of a packet, so packets are restricted to 65536 bytes in total. The translation functions should just translate; they may not significantly increase or decrease the amount of data. The actual size of the initial data is part of the translated data. The output translation routine may add up to PACKET_SLOP additional bytes, and the input translation routine should shrink the data correspondingly. */ #define PACKET_SLOP (100) /* This structure is the closure field of a packetizing buffer. */ struct packetizing_buffer { /* The underlying buffer. */ struct buffer *buf; /* The input translation function. Exactly one of inpfn and outfn will be NULL. The input translation function should untranslate the data in INPUT, storing the result in OUTPUT. SIZE is the amount of data in INPUT, and is also the size of OUTPUT. This should return 0 on success, or an errno code. */ int (*inpfn) PROTO((void *fnclosure, const char *input, char *output, int size)); /* The output translation function. This should translate the data in INPUT, storing the result in OUTPUT. The first two bytes in INPUT will be the size of the data, and so will SIZE. This should set *TRANSLATED to the amount of translated data in OUTPUT. OUTPUT is large enough to hold SIZE + PACKET_SLOP bytes. This should return 0 on success, or an errno code. */ int (*outfn) PROTO((void *fnclosure, const char *input, char *output, int size, int *translated)); /* A closure for the translation function. */ void *fnclosure; /* For an input buffer, we may have to buffer up data here. */ /* This is non-zero if the buffered data has been translated. Otherwise, the buffered data has not been translated, and starts with the two byte packet size. */ int translated; /* The amount of buffered data. */ int holdsize; /* The buffer allocated to hold the data. */ char *holdbuf; /* The size of holdbuf. */ int holdbufsize; /* If translated is set, we need another data pointer to track where we are in holdbuf. If translated is clear, then this pointer is not used. */ char *holddata; }; static int packetizing_buffer_input PROTO((void *, char *, int, int, int *)); static int packetizing_buffer_output PROTO((void *, const char *, int, int *)); static int packetizing_buffer_flush PROTO((void *)); static int packetizing_buffer_block PROTO((void *, int)); static int packetizing_buffer_shutdown PROTO((struct buffer *)); /* Create a packetizing buffer. */ struct buffer * packetizing_buffer_initialize (buf, inpfn, outfn, fnclosure, memory) struct buffer *buf; int (*inpfn) PROTO ((void *, const char *, char *, int)); int (*outfn) PROTO ((void *, const char *, char *, int, int *)); void *fnclosure; void (*memory) PROTO((struct buffer *)); { struct packetizing_buffer *pb; pb = (struct packetizing_buffer *) xmalloc (sizeof *pb); memset (pb, 0, sizeof *pb); pb->buf = buf; pb->inpfn = inpfn; pb->outfn = outfn; pb->fnclosure = fnclosure; if (inpfn != NULL) { /* Add PACKET_SLOP to handle larger translated packets, and add 2 for the count. This buffer is increased if necessary. */ pb->holdbufsize = BUFFER_DATA_SIZE + PACKET_SLOP + 2; pb->holdbuf = xmalloc (pb->holdbufsize); } return buf_initialize (inpfn != NULL ? packetizing_buffer_input : NULL, inpfn != NULL ? NULL : packetizing_buffer_output, inpfn != NULL ? NULL : packetizing_buffer_flush, packetizing_buffer_block, packetizing_buffer_shutdown, memory, pb); } /* Input data from a packetizing buffer. */ static int packetizing_buffer_input (closure, data, need, size, got) void *closure; char *data; int need; int size; int *got; { struct packetizing_buffer *pb = (struct packetizing_buffer *) closure; *got = 0; if (pb->holdsize > 0 && pb->translated) { int copy; copy = pb->holdsize; if (copy > size) { memcpy (data, pb->holddata, size); pb->holdsize -= size; pb->holddata += size; *got = size; return 0; } memcpy (data, pb->holddata, copy); pb->holdsize = 0; pb->translated = 0; data += copy; need -= copy; size -= copy; *got = copy; } while (need > 0 || *got == 0) { int get, status, nread, count, tcount; char *bytes; char stackoutbuf[BUFFER_DATA_SIZE + PACKET_SLOP]; char *inbuf, *outbuf; /* If we don't already have the two byte count, get it. */ if (pb->holdsize < 2) { get = 2 - pb->holdsize; status = buf_read_data (pb->buf, get, &bytes, &nread); if (status != 0) { /* buf_read_data can return -2, but a buffer input function is only supposed to return -1, 0, or an error code. */ if (status == -2) status = ENOMEM; return status; } if (nread == 0) { /* The buffer is in nonblocking mode, and we didn't manage to read anything. */ return 0; } if (get == 1) pb->holdbuf[1] = bytes[0]; else { pb->holdbuf[0] = bytes[0]; if (nread < 2) { /* We only got one byte, but we needed two. Stash the byte we got, and try again. */ pb->holdsize = 1; continue; } pb->holdbuf[1] = bytes[1]; } pb->holdsize = 2; } /* Read the packet. */ count = (((pb->holdbuf[0] & 0xff) << 8) + (pb->holdbuf[1] & 0xff)); if (count + 2 > pb->holdbufsize) { char *n; /* We didn't allocate enough space in the initialize function. */ n = xrealloc (pb->holdbuf, count + 2); if (n == NULL) { (*pb->buf->memory_error) (pb->buf); return ENOMEM; } pb->holdbuf = n; pb->holdbufsize = count + 2; } get = count - (pb->holdsize - 2); status = buf_read_data (pb->buf, get, &bytes, &nread); if (status != 0) { /* buf_read_data can return -2, but a buffer input function is only supposed to return -1, 0, or an error code. */ if (status == -2) status = ENOMEM; return status; } if (nread == 0) { /* We did not get any data. Presumably the buffer is in nonblocking mode. */ return 0; } if (nread < get) { /* We did not get all the data we need to fill the packet. buf_read_data does not promise to return all the bytes requested, so we must try again. */ memcpy (pb->holdbuf + pb->holdsize, bytes, nread); pb->holdsize += nread; continue; } /* We have a complete untranslated packet of COUNT bytes. */ if (pb->holdsize == 2) { /* We just read the entire packet (the 2 bytes in PB->HOLDBUF are the size). Save a memcpy by translating directly from BYTES. */ inbuf = bytes; } else { /* We already had a partial packet in PB->HOLDBUF. We need to copy the new data over to make the input contiguous. */ memcpy (pb->holdbuf + pb->holdsize, bytes, nread); inbuf = pb->holdbuf + 2; } if (count <= sizeof stackoutbuf) outbuf = stackoutbuf; else { outbuf = xmalloc (count); if (outbuf == NULL) { (*pb->buf->memory_error) (pb->buf); return ENOMEM; } } status = (*pb->inpfn) (pb->fnclosure, inbuf, outbuf, count); if (status != 0) return status; /* The first two bytes in the translated buffer are the real length of the translated data. */ tcount = ((outbuf[0] & 0xff) << 8) + (outbuf[1] & 0xff); if (tcount > count) error (1, 0, "Input translation failure"); if (tcount > size) { /* We have more data than the caller has provided space for. We need to save some of it for the next call. */ memcpy (data, outbuf + 2, size); *got += size; pb->holdsize = tcount - size; memcpy (pb->holdbuf, outbuf + 2 + size, tcount - size); pb->holddata = pb->holdbuf; pb->translated = 1; if (outbuf != stackoutbuf) free (outbuf); return 0; } memcpy (data, outbuf + 2, tcount); if (outbuf != stackoutbuf) free (outbuf); pb->holdsize = 0; data += tcount; need -= tcount; size -= tcount; *got += tcount; } return 0; } /* Output data to a packetizing buffer. */ static int packetizing_buffer_output (closure, data, have, wrote) void *closure; const char *data; int have; int *wrote; { struct packetizing_buffer *pb = (struct packetizing_buffer *) closure; char inbuf[BUFFER_DATA_SIZE + 2]; char stack_outbuf[BUFFER_DATA_SIZE + PACKET_SLOP + 4]; struct buffer_data *outdata = NULL; char *outbuf; int size, status, translated; if (have > BUFFER_DATA_SIZE) { /* It would be easy to xmalloc a buffer, but I don't think this case can ever arise. */ abort (); } inbuf[0] = (have >> 8) & 0xff; inbuf[1] = have & 0xff; memcpy (inbuf + 2, data, have); size = have + 2; /* The output function is permitted to add up to PACKET_SLOP bytes, and we need 2 bytes for the size of the translated data. If we can guarantee that the result will fit in a buffer_data, we translate directly into one to avoid a memcpy in buf_output. */ if (size + PACKET_SLOP + 2 > BUFFER_DATA_SIZE) outbuf = stack_outbuf; else { outdata = get_buffer_data (); if (outdata == NULL) { (*pb->buf->memory_error) (pb->buf); return ENOMEM; } outdata->next = NULL; outdata->bufp = outdata->text; outbuf = outdata->text; } status = (*pb->outfn) (pb->fnclosure, inbuf, outbuf + 2, size, &translated); if (status != 0) return status; /* The output function is permitted to add up to PACKET_SLOP bytes. */ if (translated > size + PACKET_SLOP) abort (); outbuf[0] = (translated >> 8) & 0xff; outbuf[1] = translated & 0xff; if (outbuf == stack_outbuf) buf_output (pb->buf, outbuf, translated + 2); else { /* if ((have + PACKET_SLOP + 4) > BUFFER_DATA_SIZE), then outdata may be NULL. */ if (outdata == NULL) abort (); outdata->size = translated + 2; buf_append_data (pb->buf, outdata, outdata); } *wrote = have; /* We will only be here because buf_send_output was called on the packetizing buffer. That means that we should now call buf_send_output on the underlying buffer. */ return buf_send_output (pb->buf); } /* Flush data to a packetizing buffer. */ static int packetizing_buffer_flush (closure) void *closure; { struct packetizing_buffer *pb = (struct packetizing_buffer *) closure; /* Flush the underlying buffer. Note that if the original call to buf_flush passed 1 for the BLOCK argument, then the buffer will already have been set into blocking mode, so we should always pass 0 here. */ return buf_flush (pb->buf, 0); } /* The block routine for a packetizing buffer. */ static int packetizing_buffer_block (closure, block) void *closure; int block; { struct packetizing_buffer *pb = (struct packetizing_buffer *) closure; if (block) return set_block (pb->buf); else return set_nonblock (pb->buf); } /* Shut down a packetizing buffer. */ static int packetizing_buffer_shutdown (buf) struct buffer *buf; { struct packetizing_buffer *pb = (struct packetizing_buffer *) buf->closure; return buf_shutdown (pb->buf); } #endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */