/*
 * See the dyninst/COPYRIGHT file for copyright information.
 * 
 * We provide the Paradyn Tools (below described as "Paradyn")
 * on an AS IS basis, and do not warrant its validity or performance.
 * We reserve the right to update, modify, or discontinue this
 * software at any time.  We shall have no obligation to supply such
 * updates or modifications or any other form of support to you.
 * 
 * By your use of Paradyn, you understand and agree that we (or any
 * other person or entity with proprietary rights in Paradyn) are
 * under no obligation to provide either maintenance services,
 * update services, notices of latent defects, or correction of
 * defects for Paradyn.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/************************************************************************
 * $Id: RTposix.c,v 1.37 2008/04/11 23:30:45 legendre Exp $
 * RTposix.c: runtime instrumentation functions for generic posix.
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/mman.h>

#if !defined(os_vxworks)
// VxWorks is missing these headers.
#include <memory.h>
#include <sys/socket.h>
#include <pwd.h>
#endif

#include "dyninstAPI_RT/h/dyninstAPI_RT.h"
#include "dyninstAPI_RT/src/RTcommon.h"
#include "dyninstAPI_RT/src/RTheap.h"

#if defined (os_osf)
#define SOCKLEN_T size_t
#else
#define SOCKLEN_T socklen_t
#endif

#if !(defined(arch_power) && defined(os_linux))
void RTmutatedBinary_init() 
{
    return;
}
#endif

#if defined(__GNUC) || defined(__GNUC__)
#if defined(DYNINST_RT_STATIC_LIB)
/*
 * In the static version of the library, constructors cannot be
 * used to run code at initialization. See DYNINSTglobal_ctors_handler.
 */
void libdyninstAPI_RT_init(void);
#else
void libdyninstAPI_RT_init(void) __attribute__ ((constructor));
#endif
#endif

#if defined (cap_async_events)
struct passwd *passwd_info = NULL;
#endif

void libdyninstAPI_RT_init() 
{
   static int initCalledOnce = 0;

   rtdebug_printf("%s[%d]:  DYNINSTinit:  welcome to libdyninstAPI_RT_init()\n", __FILE__, __LINE__);

   if (initCalledOnce) return;
   initCalledOnce++;

#if defined(arch_x86) || defined(arch_x86_64)
   /* Modern x86-32/x86-64 cpus have non-executable data */
   mark_heaps_exec();
#endif

   /* RTmutatedBinary_init(); */
   
   if (libdyninstAPI_RT_init_localCause != -1 && 
       libdyninstAPI_RT_init_localPid != -1 &&
       libdyninstAPI_RT_init_maxthreads != -1)
   {
      DYNINSTinit(libdyninstAPI_RT_init_localCause, libdyninstAPI_RT_init_localPid,
                  libdyninstAPI_RT_init_maxthreads, libdyninstAPI_RT_init_debug_flag);
   }

   rtdebug_printf("%s[%d]:  did DYNINSTinit\n", __FILE__, __LINE__);
}


/************************************************************************
 * void DYNINSTasyncConnect(int pid)
 *
 * Connect to mutator's async handler thread. <pid> is pid of mutator
************************************************************************/

static int async_socket = -1;
static int needToDisconnect = 0;
static char socket_path[255];

int DYNINSTasyncConnect(int pid)
{
   if (DYNINSTstaticMode)
      return 0;
#if defined (os_vxworks)
   return 1;

#elif defined (cap_async_events)
  int sock_fd;
  struct sockaddr_un sadr;
   int res;
   int mutatee_pid;
   uid_t euid;

   rtdebug_printf("%s[%d]:  DYNINSTasyncConnnect:  entry\n", __FILE__, __LINE__);
   rtdebug_printf("%s[%d]:  DYNINSTinit:  before geteuid\n", __FILE__, __LINE__);

   euid = geteuid();
   passwd_info = getpwuid(euid);
   assert(passwd_info);

  if (async_socket != -1)
  {
	  fprintf(stderr, "%s[%d]: - DYNINSTasyncConnect already initialized\n",
			  __FILE__, __LINE__);

     rtdebug_printf("%s[%d]:  DYNINSTasyncConnnect:  already connected\n", 
			 __FILE__, __LINE__);
     return 0;
  }

  rtdebug_printf("%s[%d]:  DYNINSTasyncConnnect:  before socket 2\n", __FILE__, __LINE__);
  mutatee_pid = getpid();

  snprintf(socket_path, (size_t) 255, "%s/dyninstAsync.%s.%d.%d", 
		  P_tmpdir, passwd_info->pw_name, pid, mutatee_pid);

  rtdebug_printf("%s[%d]:  DYNINSTasyncConnnect:  before socket: %s\n", __FILE__, __LINE__, socket_path);

  errno = 0;

  sock_fd = socket(PF_UNIX, SOCK_STREAM, 0);

  if (sock_fd < 0) 
  {
    fprintf(stderr, "%s[%d]: DYNINSTasyncConnect() socket(%s): %s\n", 
			__FILE__, __LINE__, socket_path, strerror(errno));
    abort();
  }

  rtdebug_printf("%s[%d]:  DYNINSTasyncConnnect:  after socket\n", __FILE__, __LINE__);

  sadr.sun_family = PF_UNIX;
  strcpy(sadr.sun_path, socket_path);

  rtdebug_printf("%s[%d]:  DYNINSTasyncConnnect:  before connect\n", __FILE__, __LINE__);
  res = 0;
  errno = 0;

  res = connect(sock_fd, (struct sockaddr *) &sadr, sizeof(sadr)); 

  if (res < 0)
  {
    perror("DYNINSTasyncConnect() connect()");
  }

  rtdebug_printf("%s[%d]:  DYNINSTasyncConnnect:  after connect to %s, res = %d, -- %s\n", 
		  __FILE__, __LINE__, socket_path, res, strerror(errno));

  /* maybe need to do fcntl to set nonblocking writes on this fd */

  if (async_socket == -1)
  {
	  rtdebug_printf("%s[%d]:  WARN:  async socket has not been reset!!\n", __FILE__, __LINE__);
  }

  async_socket = sock_fd;

  needToDisconnect = 1;

 /* atexit(exit_func); */
  rtdebug_printf("%s[%d]:  leaving DYNINSTasyncConnect\n", __FILE__, __LINE__);
  return 1; 
#else
  fprintf(stderr, "%s[%d]:  called DYNINSTasyncConect when async_events disabled\n",
		  __FILE__, __LINE__);
  return 0;
#endif
}

int DYNINSTasyncDisconnect()
{
   if (DYNINSTstaticMode)
      return 0;
   rtdebug_printf("%s[%d]:  welcome to DYNINSTasyncDisconnect\n", __FILE__, __LINE__);
   if (needToDisconnect) {
      close (async_socket);
      needToDisconnect = 0;
   }
   async_socket = -1;
   return 0;
}

int DYNINSTwriteEvent(void *ev, size_t sz)
{
  int res;

  if (DYNINSTstaticMode)
     return 0;
  
    rtdebug_printf("%s[%d]:  welcome to DYNINSTwriteEvent: %d bytes\n", __FILE__, __LINE__, sz);
  if (-1 == async_socket)
  {
	  rtdebug_printf("%s[%d]:  failed to DYNINSTwriteEvent, no socket\n", __FILE__, __LINE__);
	  return -1;
  }

try_again:
  res = write(async_socket, ev, sz); 
  if (-1 == res) {
    if (errno == EINTR || errno == EAGAIN) 
       goto try_again;
    else {
       perror("write");
       return -1;
    }
  }
  if (res != sz) {
    /*  maybe we need logic to handle partial writes? */
    fprintf(stderr, "%s[%d]:  partial ? write error, %d bytes, should be %d\n",
            __FILE__, __LINE__, res, (int) sz);
    return -1;
  }
  return 0;
}

void *map_region(void *addr, int len, int fd) {
     void *result;
     result = mmap(addr, len, PROT_READ|PROT_WRITE|PROT_EXEC, 
                   DYNINSTheap_mmapFlags, fd, 0);
     if (result == MAP_FAILED)
         return NULL;
     return result;
}

int unmap_region(void *addr, int len) {
    int result;
    result = munmap(addr, len);
    if (result == -1)
        return 0;
    return 1;
}

#if defined(cap_mutatee_traps)
extern void dyninstTrapHandler(int sig, siginfo_t *info, void *context);

int DYNINSTinitializeTrapHandler()
{
   int result;
   struct sigaction new_handler;

   new_handler.sa_sigaction = dyninstTrapHandler;
   //new_handler.sa_restorer = NULL; obsolete
   sigemptyset(&new_handler.sa_mask);
   new_handler.sa_flags = SA_SIGINFO | SA_NODEFER;
   
   result = sigaction(SIGTRAP, &new_handler, NULL);
   return (result == 0) ? 1 /*Success*/ : 0 /*Fail*/ ;
}
#endif
