/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */

#include "config.h"
#include <sys/types.h>
#include <sys/time.h>
#ifndef NO_UNIXSOCKS
#include <sys/socket.h>
#include <sys/un.h>
#ifndef NO_FDPASSING
#include <sys/uio.h>
#endif
#include <stdio.h>
#include <strings.h>
#endif
#include "sock.h"
#include "tty.h"
#include "err.h"

static int bufwrite(fd,buf,num)
int fd;
char *buf;
int num;
{
 int r;

 do
  {
   r = write(fd,buf,num);
   if (r > 0)
    {
     buf += r;
     num -= r;
    }
  }
 while ((num > 0) && ((r != -1) || (errno == EINTR)));
 return (r >= 0) ? 0 : -1;
}

static int bufread(fd,buf,num)
int fd;
char *buf;
int num;
{
 int r;

 do
  {
   r = read(fd,buf,num);
   if (r > 0)
    {
     buf += r;
     num -= r;
    }
  }
 while ((num > 0) && ((r != -1) || (errno == EINTR)));
 /* Note that we ignore EOF. */
 return (r >= 0) ? 0 : -1;
}

int pty_readsock(line,path)
char *line;
char *path;
{
#ifdef NO_UNIXSOCKS
 return -1;
#else
 int s;
 struct sockaddr_un sa;

 if ((s = socket(AF_UNIX,SOCK_STREAM,0)) == -1)
   return -1;
 sa.sun_family = AF_UNIX;
 (void) sprintf(sa.sun_path,"re.%s",line + sizeof(DEVSTY) - 3);
 (void) strcpy(path,sa.sun_path);
 (void) unlink(sa.sun_path);
 if (bind(s,(struct sockaddr *) &sa,strlen(sa.sun_path) + 2) == -1)
   return -1;
 if (listen(s,5) == -1)
   return -1;
 return s;
#endif
}

int pty_writesock(line)
char *line;
{
#ifdef NO_UNIXSOCKS
 return -1;
#else
 int s;
 struct sockaddr_un sa;

 if ((s = socket(AF_UNIX,SOCK_STREAM,0)) == -1)
   return -1;
 sa.sun_family = AF_UNIX;
 (void) sprintf(sa.sun_path,"re.%s",line + sizeof(DEVSTY) - 3);
 if (connect(s,(struct sockaddr *) &sa,strlen(sa.sun_path) + 2) == -1)
   return -1;
 (void) unlink(sa.sun_path);
 return s;
#endif
}

int pty_acceptsock(fd)
int fd;
{
#ifdef NO_UNIXSOCKS
 return -1;
#else
 struct sockaddr_un sa;
 int salen = sizeof(sa);

 return accept(fd,(struct sockaddr *) &sa,&salen);
#endif
}

int pty_putgetonefd(fd,fp)
int fd;
int *fp;
{
#ifdef NO_FDPASSING
 return -1;
#else
 struct msghdr msg[2];
 int acc[5];
 struct iovec i[2];

 msg[0].msg_name = 0;
 msg[0].msg_namelen = 0;
 msg[0].msg_iov = &i[0]; /* grrrr */
 msg[0].msg_iovlen = 0;
 msg[0].msg_accrights = (caddr_t) acc;
 msg[0].msg_accrightslen = 5 * sizeof(int);
#ifdef USLEEP
 (void) usleep((unsigned) 100000);
#else
 (void) sleep(1); /* XXX: work around fd passing bug */
#endif
 if (recvmsg(fd,msg,0) == -1)
   return -1;
 if (msg[0].msg_accrightslen != sizeof(int))
   return -1;
 if (*fp != -1)
   (void) close(*fp);
 *fp = acc[0]; /* yay! we've passed a file descriptor! */
 return 0;
#endif
}

int pty_putgetfd(fd,ch,fp)
int fd;
char ch;
int *fp;
{
#ifdef NO_FDPASSING
 return -1;
#else
 if (bufwrite(fd,&ch,1) < 0)
   return -1;
 if (pty_putgetonefd(fd,fp) < 0)
   return -1;
 if (bufwrite(fd,".",1) < 0)
   return -1;
 return 0;
#endif
}

int pty_putgetint(fd,ch,ip)
int fd;
char ch;
int *ip;
{
 if (bufwrite(fd,&ch,1) < 0)
   return -1;
 if (bufread(fd,(char *) ip,sizeof(int)) < 0)
   return -1;
 if (bufwrite(fd,".",1) < 0)
   return -1;
 return 0;
}

int pty_putgetstr(fd,ch,str)
int fd;
char ch;
char str[TTYNAMELEN];
{
 if (bufwrite(fd,&ch,1) < 0)
   return -1;
 if (bufread(fd,str,TTYNAMELEN) < 0)
   return -1;
 if (bufwrite(fd,".",1) < 0)
   return -1;
 return 0;
}

int pty_putgettty(fd,ch,tmo)
int fd;
char ch;
struct ttymodes *tmo;
{
 if (bufwrite(fd,&ch,1) < 0)
   return -1;
 if (bufread(fd,(char *) tmo,sizeof(struct ttymodes)) < 0)
   return -1;
 if (bufwrite(fd,".",1) < 0)
   return -1;
 return 0;
}

int pty_sendonefd(fd,fp)
int fd;
int *fp;
{
#ifdef NO_FDPASSING
 return -1;
#else
 struct msghdr msg[2];
 int acc[5]; /* or just 5? or just 1? who cares */
 struct iovec i[2];

 msg[0].msg_name = 0;
 msg[0].msg_namelen = 0;
 msg[0].msg_iov = i; /* grrrr */
 msg[0].msg_iovlen = 0;
 msg[0].msg_accrights = (caddr_t) acc;
 msg[0].msg_accrightslen = sizeof(int);
 acc[0] = *fp;
 if (sendmsg(fd,&msg[0],0) == -1)
   return -1;
 /* yay! we've passed a file descriptor! */
 return 0;
#endif
}

int pty_sendfd(fd,ch,fp)
int fd;
char ch;
int *fp;
{
#ifdef NO_FDPASSING
 return -1;
#else
 if (bufwrite(fd,&ch,1) < 0)
   return -1;
 if (bufread(fd,&ch,1) < 0)
   return -1;
 if (ch == ' ')
   return 1;
 if (pty_sendonefd(fd,fp) < 0)
   return -1;
 if (bufread(fd,&ch,1) < 0)
   return -1;
 if (ch != '.')
   return 1;
 return 0;
#endif
}

int pty_sendint(fd,ch,ip)
int fd;
char ch;
int *ip;
{
 if (bufwrite(fd,&ch,1) < 0)
   return -1;
 if (bufread(fd,&ch,1) < 0)
   return -1;
 if (ch == ' ')
   return 1;
 if (bufwrite(fd,(char *) ip,sizeof(int)) < 0)
   return -1;
 if (bufread(fd,&ch,1) < 0)
   return -1;
 if (ch != '.')
   return 1;
 return 0;
}

int pty_sendstr(fd,ch,str)
int fd;
char ch;
char str[TTYNAMELEN];
{
 if (bufwrite(fd,&ch,1) < 0)
   return -1;
 if (bufread(fd,&ch,1) < 0)
   return -1;
 if (ch == ' ')
   return 1;
 if (bufwrite(fd,str,TTYNAMELEN) < 0)
   return -1;
 if (bufread(fd,&ch,1) < 0)
   return -1;
 if (ch != '.')
   return 1;
 return 0;
}

int pty_sendtty(fd,ch,tmo)
int fd;
char ch;
struct ttymodes *tmo;
{
 if (bufwrite(fd,&ch,1) < 0)
   return -1;
 if (bufread(fd,&ch,1) < 0)
   return -1;
 if (ch == ' ')
   return 1;
 if (bufwrite(fd,(char *) tmo,sizeof(struct ttymodes)) < 0)
   return -1;
 if (bufread(fd,&ch,1) < 0)
   return -1;
 if (ch != '.')
   return 1;
 return 0;
}

int pty_putch(fd,ch)
int fd;
char *ch;
{
 return bufwrite(fd,ch,1);
}

int pty_getch(fd,ch)
int fd;
char *ch;
{
 return bufread(fd,ch,1);
}
