/*
 * nasd_udppipe.c
 *
 * Code to handle UDP socket-based pipes.
 *
 * Author: Marc Unangst
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#include <nasd/nasd_options.h>
#include <nasd/nasd_drive_options.h>
#include <nasd/nasd_udppipe.h>
#include <nasd/nasd_freelist.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_itypes.h>
#include <nasd/nasd_shutdown.h>
#include <nasd/nasd_cache.h>

#ifndef KERNEL
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#endif /* KERNEL */

nasd_freelist_t *nasd_udppipe_buflist;

nasd_status_t
nasd_od_udp_init()
{
  nasd_status_t rc;

  NASD_FREELIST_CREATE(nasd_udppipe_buflist, 20, 2, sizeof(nasd_mem_list_t));
  rc = nasd_shutdown_proc(nasd_odc_shutdown, nasd_od_udp_shutdown, NULL);
  if(rc)
    return rc;

  return NASD_SUCCESS;
}

void
nasd_od_udp_shutdown(void *ignored)
{
  NASD_FREELIST_DESTROY(nasd_udppipe_buflist, next, (nasd_mem_list_t *));

  return;
}

nasd_status_t
nasd_od_udp_push(void *state, void *buf, nasd_len_t in_len,
                 nasd_byte_t *ign1, nasd_byte_t *ign2, int *ign3)
{
#ifdef KERNEL
  return NASD_OP_NOT_SUPPORTED;
#else /* KERNEL */
  nasd_udppipe_pipe_state_t *pstate;
  nasd_mem_list_t *bufent;
  nasd_status_t rc;
  struct msghdr msg;
  struct iovec iov[NASD_UIO_MAXIOV];
  int i;

  pstate = (nasd_udppipe_pipe_state_t *) state;

  if(!pstate->head && pstate->packet_complete) {
    /* just send it */
#if DEBUG_UDPPIPE > 0
    nasd_printf("udp_push: sending %d bytes to %s:%d\n", in_len,
           inet_ntoa(pstate->sockaddr.sin_addr),
           ntohs(pstate->sockaddr.sin_port));
#endif /* DEBUG_UDPPIPE */
    rc = sendto(pstate->sock, buf, in_len, 0,
                (struct sockaddr *) &pstate->sockaddr, pstate->sockaddr_len);
    if(rc > 0)
      return NASD_SUCCESS;
    else
      return errno;
  }

  /* otherwise, need to add it to the list; might send it later */
  NASD_FREELIST_GET(nasd_udppipe_buflist, bufent, next, (nasd_mem_list_t *));
  if (bufent == NULL)
    return(NASD_NO_MEM);
  bufent->addr = buf;
  bufent->len = in_len;
  bufent->stride = 0;
  bufent->nelem = 1;
  bufent->next = NULL;

  if(pstate->tail) {
    pstate->tail->next = bufent;
    pstate->tail = bufent;
  } else {
    pstate->head = pstate->tail = bufent;
  }
  pstate->nelem++;

  if(pstate->packet_complete) {
    unsigned int plen = 0;
    unsigned int seg = 0;
    /* time to send the buf chain */
    bzero(&msg, sizeof(struct msghdr));
    msg.msg_name = (void *) &pstate->sockaddr;
    msg.msg_namelen = pstate->sockaddr_len;
    if(pstate->nelem > NASD_UIO_MAXIOV) {
      nasd_printf("udp pipe discipline error: need to send %d > NASD_UIO_MAXIOV bufs\n",
             pstate->nelem);
      return NASD_FAIL;
    }
    msg.msg_iov = iov;
    msg.msg_iovlen = pstate->nelem;
    for(bufent = pstate->head, i = 0; bufent; bufent = bufent->next, i++) {
      msg.msg_iov[i].iov_base = bufent->addr;
      msg.msg_iov[i].iov_len = bufent->len;
      plen += bufent->len;
      seg++;
    }
#if DEBUG_UDPPIPE > 0
    nasd_printf("udp_push: sending %d bytes in %d segments to %s:%d\n", plen,
           seg, inet_ntoa(pstate->sockaddr.sin_addr),
           ntohs(pstate->sockaddr.sin_port));
#endif /* DEBUG_UDPPIPE */
    rc = sendmsg(pstate->sock, &msg, 0);
    NASD_FREELIST_FREE_N(nasd_udppipe_buflist, pstate->head, next,
                         (nasd_mem_list_t *), pstate->nelem);
    pstate->nelem = 0;
    pstate->packet_complete = 0;
    pstate->head = pstate->tail = NULL;
    if(rc > 0) {
      return NASD_SUCCESS;
    } else {
      return errno;
    }
  }

  return NASD_SUCCESS;
#endif /* KERNEL */
}

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
