/*
 * nasd_common.c
 *
 * Shared "general" functionality for NASD entities
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1996,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_types.h>
#include <nasd/nasd_general.h>
#include <nasd/nasd_sys.h>
#include <nasd/nasd_common.h>
#include <nasd/nasd_mem.h>

#if NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE
#include <dce/dce_error.h>
#include <dce/rpc.h>
#endif /* NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE */

#ifdef NETBSD
#include <errno.h>
#endif

/* external utility routines */
extern char* nasd_error_string(nasd_status_t error);

#if defined(DEC_OSF) && defined(KERNEL)
char nasd_panicbuf[NASD_PANICBUF_LEN];
#endif /* DEC_OSF && KERNEL */


#if NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE
nasd_status_t
nasd_dce_direct_bind_to_server(
  unsigned_char_t       *protseq,        /* "ncadg_ip_udp" */
  unsigned_char_t       *host,           /* server hostname */
  unsigned_char_t       *endpoint,       /* port number as a string */ 
  rpc_binding_handle_t  *binding_handle) /* OUT: binding handle */
{
    unsigned_char_t             *string_binding;
    unsigned_char_t             *netoptions = 0;
    unsigned32                  status;
    int i, retries = 5;

    /*
     * compose a string binding from the arguments
     */
    rpc_string_binding_compose (
				NULL,                               /* no object uuid */
				(unsigned_char_t *) protseq,        /* protocol sequence */
				(unsigned_char_t *) host,        /* host address */
				(unsigned_char_t *) endpoint,        /* transport endpoint */
				(unsigned_char_t *) netoptions,        /* network options */
				&string_binding,
				&status);

  if (status != rpc_s_ok)  {
    return(NASD_FAIL);
  }

  /*
   * convert the string binding to a binding handle
   */
  for(i=0;i<retries;i++) {
    rpc_binding_from_string_binding (string_binding, binding_handle, &status);
    if (status == rpc_s_ok) {
      break;
    }
  }

  rpc_string_free(&string_binding, &status); /* XXX - no cleanup on failure above */

  if (status != rpc_s_ok) {
    return(NASD_FAIL);
  }

  return(NASD_SUCCESS);
}

nasd_status_t
nasd_dce_direct_unbind_server(rpc_binding_handle_t *binding_handle)
{
  unsigned32 status;

  rpc_binding_free(binding_handle, &status);

  if (status != rpc_s_ok)
    return(NASD_FAIL);

  return(NASD_SUCCESS);
}
#endif /* NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE */



#define DOTIME(_tv_) _nasd_timestr((time_t)((_tv_).ts_sec)), (_tv_).ts_sec, (_tv_).ts_nsec

char *
_nasd_timestr(
  time_t  t)
{
  static char str[128];
#ifdef KERNEL
  sprintf(str, "%lu", (unsigned long)t);
#else /* KERNEL */
  strcpy(str, ctime(&t));
  if (str[strlen(str)-1] == '\n') {
    str[strlen(str)-1] = '\0';
  }
#endif /* KERNEL */
  return(str);
}

char *
_nasd_timestr_r(
  time_t   t,
  char    *str)
{
#ifdef KERNEL
  sprintf(str, "%lu", (unsigned long)t);
#else /* KERNEL */
  strcpy(str, ctime(&t));
  if (str[strlen(str)-1] == '\n') {
    str[strlen(str)-1] = '\0';
  }
#endif /* KERNEL */
  return(str);
}

char *
nasd_timestr_r(
  nasd_timespec_t   ts,
  char             *str)
{
#ifdef KERNEL
  sprintf(str, "%d:%09d", ts.ts_sec, ts.ts_nsec);
#else /* KERNEL */
  time_t t;

  t = ts.ts_sec;
  strcpy(str, ctime(&t));
  if (str[strlen(str)-1] == '\n') {
    str[strlen(str)-1] = '\0';
  }
#endif /* KERNEL */
  return(str);
}

void 
nasd_debug_breakpoint()
{
  /* To cause a programmatic breakpoint, set a breakpoint here and
     then call this fn when you want to break. */
#if NASD_BREAKPOINT_IS_PANIC > 0
  nasd_panic("nasd_debug_breakpoint called");
#endif /* NASD_BREAKPOINT_IS_PANIC > 0 */
}

/*
 * Print the contents of a nasd attribute
 */
void
nasd_printstat(
  nasd_attribute_t  *attr)
{
  nasd_printf("  block_preallocation     %6" NASD_64u_FMT "\n", attr->block_preallocation);
  nasd_printf("  blocks_used             %6" NASD_64u_FMT "\n", attr->blocks_used);
  nasd_printf("  block_size              %6u\n", attr->block_size);
  nasd_printf("  object_len              %6" NASD_64u_FMT "\n", attr->object_len);
  nasd_printf("  attr_modify_time        %s (%d:%09d)\n", DOTIME(attr->attr_modify_time));
  nasd_printf("  object_modify_time      %s (%d:%09d)\n", DOTIME(attr->object_modify_time));
  nasd_printf("  object_create_time      %s (%d:%09d)\n", DOTIME(attr->object_create_time));
  nasd_printf("  fs_attr_modify_time     %s (%d:%09d)\n", DOTIME(attr->fs_attr_modify_time));
  nasd_printf("  fs_object_modify_time   %s (%d:%09d)\n", DOTIME(attr->fs_object_modify_time));
  nasd_printf("  av                      %6u\n", (unsigned)attr->av);
}


nasd_status_t
nasd_str_to_nasd_id(
  char               *str,
  nasd_identifier_t  *nidp)
{
  nasd_identifier_t n;
  nasd_uint64 v, j;
  int base, i, f;
  char c;

  *nidp = n = 0;

  if ((str[0] == '0') && (str[1] == 'x')) {
    base = 16;
    f = 2;
  }
  else {
    base = 10;
    f = 0;
  }

  for(j=1,i=strlen(&str[f])-1;i>=0;i--,j*=base) {
    c = str[f+i];
    v = (-1);
    if ((c >= '0') && (c <= '9')) {
      v = c - '0';
    }
    else if (base == 16) {
      if ((c >= 'a') && (c <= 'f'))
        v = (c - 'a') + 10;
      else if ((c >= 'A') && (c <= 'F'))
        v = (c - 'A') + 10;
    }
    else {
      return(NASD_BAD_IDENTIFIER);
    }
    n += v * j;
  }

  *nidp = n;

  return(NASD_SUCCESS);
}

#ifndef KERNEL
nasd_status_t
nasd_raw_disk_len(
  int           fd,
  nasd_uint64  *lenp)
{
  nasd_uint64 max, min, pos, val;
  int rc;
  char *i;

  /* this needs to be page-aligned, or IO to a raw-device disk will
     fail with EINVAL */
  NASD_Valloc(i, 512, (char *));
  NASD_ASSERT(i != NULL);

  max = (nasd_uint64) (~((nasd_uint64)0x80<<((sizeof(max)-1)*CHAR_BIT))>>9);
  min = 0;
  val = (min + ((max - min) >> 1)) << 9;

  do {
    pos = nasd_lseek(fd, val, SEEK_SET, &rc);

    if (rc != 0) {
      max = min + ((max - min) >> 1);
    }
    else {
      rc = read(fd, i, 512);
      if (rc == 0 ||
          ((rc < 0) && (errno == ENOSPC)))
        max = val >> 9;
      else
        min = val >> 9;
    }
    val = (min + ((max - min) >> 1)) << 9;
  } while (max > min + 1);
  *lenp = (nasd_uint64)(val+512);
  NASD_Free(i, 512);
  return(NASD_SUCCESS);
}


nasd_status_t
nasd_kernel_atomic_test()
{
  int ret;

  ret = nasd_srv(NASD_SC_ATOMIC_MATH_TEST, NULL);
  if (ret)
    return(NASD_FAIL);

  return(NASD_SUCCESS);
}


nasd_status_t
nasd_kernel_timeout_test(
  nasd_timespec_t  delta)
{
  nasd_timeout_test_t tt;
  int ret;

  tt.nasd_status = NASD_SUCCESS;
  tt.delta = delta;
  ret = nasd_srv(NASD_SC_TIMEOUT_TEST, &tt);
  if (ret)
    return(NASD_FAIL);
  return(tt.nasd_status);
}

#endif /* !KERNEL */


nasd_status_t
nasd_check_mem_list(
  nasd_mem_list_t  *memlist)
{
  if (memlist == NULL)
    goto bad;
  if (memlist->len <= 0)
    goto bad;
  if (memlist->nelem <= 0)
    goto bad;
  if (memlist->addr == NULL)
    goto bad;

  return(NASD_SUCCESS);
bad:
  return(NASD_BAD_MEM_LIST);
}


void
nasd_free_mem_list(
  nasd_mem_list_t  *src)
{
  int maxoff;

  if (src == NULL) 
    return;

  nasd_free_mem_list(src->next);
  maxoff= (src->nelem * src->stride) +  src->len;
  NASD_Free(src->addr, sizeof(nasd_byte_t) * maxoff);
  NASD_Free(src, sizeof(nasd_mem_list_t));
}


/* copy the elements from a list of mem_list entries into buffers.
   used so we don't munge the original buffer when we encrypt.  if
   we were cool, we'd have a freelist of extra nasd_mem_list_t's
   instead of allocating one here.  we're not that cool. 
   we are cool enough to be recursive, though. */
nasd_status_t
nasd_dup_mem_list(nasd_mem_list_t *src, nasd_mem_list_t **dst)
{
  nasd_mem_list_t *l;
  nasd_byte_t *buf;
  int nbytes;
  int i;

  *dst = NULL;

  if(!src || !src->addr) {
    return NASD_SUCCESS;
  }

  nbytes = src->len * src->nelem;
  if(nbytes == 0) {
    return NASD_SUCCESS;
  }

  NASD_Malloc(l, sizeof(nasd_mem_list_t), (nasd_mem_list_t *));
  if(!l) {
    return NASD_NO_MEM;
  }
  bzero(l, sizeof(nasd_mem_list_t));
  *dst = l;

  NASD_Malloc(buf, nbytes, (nasd_byte_t *));
  if(!buf) {
    NASD_Free(l, sizeof(nasd_mem_list_t));
    *dst = NULL;
    return NASD_NO_MEM;
  }

  l->addr = buf;
  l->len = nbytes;
  l->stride = 0;
  l->nelem = 1;
  l->next = NULL;

  /* walk the memlist and copy elements over */
  for(i = 0; i < src->nelem; i++) {
    NASD_ASSERT((src->len * (i+1)) <= nbytes);
    bcopy((nasd_byte_t *)src->addr + (src->stride * i), buf + (src->len * i),
          src->len);
  }

  /* recurse to do the next mem_list entry */
  return nasd_dup_mem_list(src->next, &l->next);
}


/*
 * Note that this yields *addrp in host order
 */
nasd_status_t
nasd_inet_aton(
  char         *hostname,
  nasd_uint32  *addrp)
{
  nasd_uint32 n[4], addr;
  char *sp;
  int i;

  if (hostname == NULL)
    return(NASD_FAIL);

#ifndef KERNEL
  if ((hostname[0] < '0') || (hostname[0] > '9' )) {
    struct hostent he, *hep;
    nasd_status_t rc;

    rc = nasd_gethostbyname_r(hostname, &he);

    if (rc) {
      return(rc);
    }

    hep = &he;
    bcopy((char *)hep->h_addr_list[0], (char *)&addr, 4);
    *addrp = nasd_ntoh32(addr);

    return(NASD_SUCCESS);
  }
#endif /* !KERNEL */

  addr = 0;
  sp = hostname;
  for(i=0;i<4;i++) {
    if ((*sp) == '\0') {
      return(NASD_FAIL);
    }
    if ((*sp) == '.') {
      return(NASD_FAIL);
    }
    n[i] = 0;
    for(;;) {
      if (((*sp) < '0') || ((*sp) > '9')) {
        return(NASD_FAIL);
      }
      n[i] *= 10;
      n[i] += (*sp)-'0';
      if (n[i] > 255) {
        return(NASD_FAIL);
      }
      sp++;
      if ((*sp) == '.') {
        sp++;
        break;
      }
      if ((*sp) == '\0') {
        break;
      }
    }
  }
  if (*sp) {
    return(NASD_FAIL);
  }
  *addrp = (n[0]<<24) | (n[1]<<16) | (n[2]<<8) | n[3];
  return(NASD_SUCCESS);
}


nasd_status_t
nasd_inet_svcton(
  char         *portname,
  nasd_uint16  *ipport)
{
  nasd_uint32 n;
  char *sp;

  /*
   * XXX eventually, make this do /etc/services names too
   */

  if ((portname == NULL) || ((*portname) == '\0'))
    return(NASD_FAIL);

  n = 0;
  for(sp=portname;*sp;sp++) {
    if (((*sp) < '0') || ((*sp) > '9')) {
      return(NASD_FAIL);
    }
    n *= 10;
    n += (nasd_uint32)((*sp) - '0');
    if (n >= 65536) {
      return(NASD_FAIL);
    }
  }

  *ipport = n;
  return(NASD_SUCCESS);
}


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