/* 
 * Copyright (C) 1994, 1995 under GPL: Wayne Meissner
 * 
 * $Author: wayne $
 * $Date: 1995/01/17 15:13:58 $
 * $Source: /home/wayne/src/dosemu/daemon/client/RCS/client.cc,v $
 * $Revision: 2.0 $
 * 
 */

#include <iostream.h>
#include <unistd.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <string.h>
#include <sys/un.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#include "client_server.h"
#include "daemon.h"
#include "client.h"

#define cl_debug (cerr << "client: ")
daemon_server * server = NULL;
int server_fd = -1;
LOCAL int server_initialised = 0;

LOCAL void
sig_handler (int sig) {
//  cl_debug << strsignal (sig) << " received" << endl;
  server_exit();
}

int server_init (void) {
  signal (SIGINT , sig_handler);
  signal (SIGTERM, sig_handler);
  signal (SIGHUP, sig_handler);
  
  server_fd = socket (AF_UNIX, SOCK_STREAM, 0);
  if (server_fd < 0) {
    cerr << "socket() failed " << strerror(errno) << endl;
    return -1;
  }

  struct sockaddr_un addr;
  addr.sun_family = AF_UNIX;
  sprintf (addr.sun_path, "%s/%s", SOCKET_DIR, SOCKET_NAME);
  if (connect(server_fd, (struct sockaddr *)&addr,
	      sizeof(addr.sun_family) + strlen(addr.sun_path)) != 0) {
		cerr << "Connect failed: " << strerror(errno) << endl;
		return -1;
	      }
  cl_debug << "Connected to server" << endl;
  
  DataHeader tmphdr;
  read (server_fd, &tmphdr, sizeof (tmphdr.len));
  
  char * buf = (char *)malloc (tmphdr.len);
  read (server_fd, buf, tmphdr.len - sizeof(tmphdr.len));
	
  key_t daemon_key = *(key_t *)buf;
  cl_debug << "daemon_key = " << (int)daemon_key << endl;
  
  int id = shmget (daemon_key, 0, 0);
  if (id == -1) {
    cl_debug << "couldn't get shared memory area: " << strerror (errno) << endl;
    return (-1);
  }
  cl_debug << "id = " << id << endl;
  
  server = (daemon_server *)shmat (id, 0, 0);
  if ((char *)server == (char *)-1) {
    cl_debug << "shmat failed: " << strerror (errno) << endl;
    exit (-1);
  }
  
  cl_debug << "successfully attached to server memory" << endl;
  cl_debug << "server = " << (void *)server << endl;

  // wait_server();
//  cl_debug << "contents of data area = " << server->data.buf << endl;

  return 0;
}

void 
server_close (void) {
  server_exit();
  exit (-1);
}
  
void server_exit (void) {
  DOS_CMD = DAEMON_CLIENTEXIT;
  DOS_ARG = 0;
  signal_server(); // don't want to wait for a response
  close (server_fd);
}

int32 Daemon_int86 (uint16 ivec) {
//  cl_debug << "doing int86 (" << ivec << ")" << endl;
  DOS_CMD = DAEMON_INT86;
  DOS_ARG = ivec;
  call_dosemu();
  return REG(eax);
}

int32 Daemon_int86 (uint16 ivec, vm86_regs * in, vm86_regs * out) {
  memcpy (&REGS, in, sizeof (REGS));
  call_dosemu();
  memcpy (out, &REGS, sizeof (REGS));
  return REG(eax);
}
  
// FIXME: the shm_key needs to be selected at run time
int32 Daemon_getmem (void * dest, uint16 sseg, uint16 soff, uint32 len) {
  DaemonMemInfo src;
  src.s.seg = sseg;
  src.s.off = soff;
  src.len = len;
  src.shm_key = 0;
  return Daemon_getmem (dest, &src);
}

int32 Daemon_getmem (void * dest, DaemonMemInfo * src) {
  
  int flags = IPC_CREAT | 0666;
  if (src->shm_key == 0)
    src->shm_key = 2000001;
  else
    flags = 0;
  
  int id = shmget (src->shm_key, src->len, flags);
  if (id == -1) {
    cerr << "shmget failed: " << strerror (errno) << endl;
    return -1;
  }
  
  char * buf = (char *)shmat (id, 0, 0);
  if ((int)buf == -1) {
    cerr << "shmat failed: " << strerror (errno) << endl;
    return -1;
  }
  // now the real call to dosemu
  DOS_CMD = DAEMON_GETMEM;
  memcpy (server->data.buf, src, sizeof(*src));
  call_dosemu();
  memcpy (dest, buf, src->len); // copy it out
  shmctl (id, IPC_RMID, NULL);
  shmdt (buf);
  return src->len;
}

int32 Daemon_putmem (DaemonMemInfo * dest, const void * src) {
  int flags = IPC_CREAT | 0666;
  if (dest->shm_key == 0)
    dest->shm_key = 2000001;
  else
    flags = 0;
  
  int id = shmget (dest->shm_key, dest->len, flags);
  if (id == -1) {
    cerr << "shmget failed: " << strerror (errno) << endl;
    return -1;
  }
  
  char * buf = (char *)shmat (id, 0, 0);
  if ((int)buf == -1) {
    cerr << "shmat failed: " << strerror (errno) << endl;
    return -1;
  }
  
  // copy the dest address over
  memcpy (server->data.buf, dest, sizeof (*dest));
  memcpy (buf, src, dest->len); // copy it in
  
  // now the real call to dosemu
  DOS_CMD = DAEMON_PUTMEM;
  call_dosemu();
  shmctl (id, IPC_RMID, NULL);
  shmdt (buf);
  return dest->len;
}

int32 Daemon_putmem (uint16 dseg, uint16 doff, const void * src, uint32 len) {
  DaemonMemInfo dest;
  dest.s.seg = dseg;
  dest.s.off = doff;
  dest.len = len;
  dest.shm_key = 0;
  return Daemon_putmem (&dest, src);
}
  
  
  
void Daemon_spawn (char * cmd) {
  strcpy (server->daemon_comm.buf, cmd);
  DOS_CMD = DAEMON_SPAWN;
  call_dosemu();
}


#if 0
void call_irq (int irq, x86regs * in, x86regs * out) {
  memcpy (REGS, in, sizeof (*in));
  DOS_CMD = CALL_IRQ;
  DOS_ARG = irq;
  call_dosemu();
  memcpy (out, REGS, sizeof(*out));
}
#endif	


/* wait_fd() is specialised, for reading xor writing on one fd */
LOCAL int
wait_fd (int fd, int utimeout, int mode) {
  struct timeval timeout;
  fd_set fds;
  int ret = 0;
  timeout.tv_usec = utimeout;
  timeout.tv_sec = 0;

  FD_ZERO (&fds);
  FD_SET (fd, &fds);

  switch (mode) {
   case O_RDONLY:
    ret = select (10, &fds, NULL, NULL, &timeout);
    break;
    
   case O_WRONLY:
    ret = select (10, NULL, &fds, NULL, &timeout);
    break;
    
   default:
    return -1;
  }
  
  if (FD_ISSET(fd, &fds))
    return 0;
  return -1;
}

