home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Geek Gadgets 1
/
ADE-1.bin
/
ade-dist
/
ixemul-45.0-src.tgz
/
tar.out
/
contrib
/
ixemul
/
library
/
unp.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-10-01
|
19KB
|
833 lines
/*
* This file is part of ixemul.library for the Amiga.
* Copyright (C) 1996 by Hans Verkuil
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
Missing features:
datagram support
setsockopt/getsockopt are dummy functions
no timeout while connecting
*/
#define _KERNEL
#include "ixemul.h"
#include "unp.h"
#include "kprintf.h"
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/unix_socket.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <machine/param.h>
#include <string.h>
#include "select.h"
struct ix_unix_name *
find_unix_name(const char *path)
{
struct ix_unix_name *un;
for (un = ix.ix_unix_names; un; un = un->next)
if (!strcmp(un->path, path))
break;
return un;
}
struct sock_stream *
init_stream(void)
{
struct sock_stream *s = kmalloc(sizeof(struct sock_stream));
if (s)
{
s->reader = s->writer = s->buffer;
s->flags = 0;
s->task = 0;
}
return s;
}
static struct ix_unix_name *
add_unix_name(const char *path, int queue_size)
{
struct ix_unix_name *un = kmalloc(sizeof(struct ix_unix_name));
if (un == NULL)
errno_return(ENOMEM, NULL);
if (queue_size == 0)
queue_size = 1;
un->queue = kmalloc(queue_size * 4);
if (un->queue == NULL)
{
kfree(un);
errno_return(ENOMEM, NULL);
}
strcpy(un->path, path);
un->next = ix.ix_unix_names;
ix.ix_unix_names = un;
un->queue_size = queue_size;
un->queue_index = 0;
un->task = 0;
return un;
}
struct sock_stream *
find_stream(struct file *f, int read_stream)
{
struct unix_socket *us;
if (f->f_type == DTYPE_PIPE)
return f->f_ss;
us = f->f_sock;
if (us->server != f)
read_stream = !read_stream;
return (read_stream ? us->to_server : us->from_server);
}
struct sock_stream *
get_stream(struct file *f, int read_stream)
{
struct sock_stream *ss = find_stream(f, read_stream);
if (ss == NULL)
return ss;
Forbid();
for (;;)
{
if (!(ss->flags & UNF_LOCKED))
{
ss->flags &= ~UNF_WANT_LOCK;
ss->flags |= UNF_LOCKED;
/* got it ! */
break;
}
ss->flags |= UNF_WANT_LOCK;
if (ix_sleep((caddr_t)&ss->flags, "get_sock") < 0)
{
Permit();
setrun(FindTask(0));
Forbid();
}
/* have to always recheck whether we really got the lock */
}
Permit();
return ss;
}
void
release_stream(struct sock_stream *ss)
{
if (ss)
{
Forbid ();
if (ss->flags & UNF_WANT_LOCK)
ix_wakeup ((u_int)&ss->flags);
ss->flags &= ~(UNF_WANT_LOCK | UNF_LOCKED);
Permit ();
}
}
static int stream_is_closed(struct sock_stream *ss)
{
return (ss == NULL || (ss->flags & (UNF_NO_READER | UNF_NO_WRITER)) ==
(UNF_NO_READER | UNF_NO_WRITER));
}
static void close_stream(struct file *f, int read_stream)
{
struct sock_stream **ss;
struct unix_socket *us = f->f_sock;
int to_server;
if (us == NULL)
return;
to_server = (us->server != f) ? !read_stream : read_stream;
ss = (to_server ? &us->to_server : &us->from_server);
if (*ss)
{
(*ss)->flags |= (read_stream ? UNF_NO_READER : UNF_NO_WRITER);
if (stream_is_closed(*ss))
{
kfree (*ss);
*ss = NULL;
}
else
{
Forbid();
if ((*ss)->task)
Signal((*ss)->task, 1UL << u.u_pipe_sig);
Permit();
ix_wakeup ((u_int)*ss);
}
}
if (read_stream && us->unix_name && f->f_count == 0)
{
struct ix_unix_name *un;
if (us->unix_name == ix.ix_unix_names)
{
ix.ix_unix_names = ix.ix_unix_names->next;
}
else
{
for (un = ix.ix_unix_names; un; un = un->next)
if (un->next == us->unix_name)
{
un->next = us->unix_name->next;
break;
}
}
kfree(us->unix_name);
us->unix_name = NULL;
}
if (stream_is_closed(us->to_server) && stream_is_closed(us->from_server) &&
us->unix_name == NULL && f->f_count == 0)
{
kfree(us);
f->f_sock = 0;
}
}
int unp_socket(int domain, int type, int protocol, struct unix_socket *sock)
{
int omask, err, fd;
struct file *fp;
struct unix_socket *us;
if (type != SOCK_STREAM || protocol != 0)
errno_return(EPROTONOSUPPORT, -1);
if (sock)
us = sock;
else if ((us = kmalloc(sizeof(struct unix_socket))) == NULL)
errno_return(ENOMEM, -1);
else
{
us->path[0] = 0;
us->from_server = us->to_server = NULL;
us->unix_name = NULL;
us->server = NULL;
us->state = UNS_WAITING;
}
omask = syscall (SYS_sigsetmask, ~0);
if ((err = falloc (&fp, &fd)))
{
errno = err;
syscall (SYS_sigsetmask, omask);
if (sock == NULL)
kfree(us);
return -1;
}
fp->f_sock = us;
_set_socket_params(fp, domain);
syscall (SYS_sigsetmask, omask);
return fd;
}
int unp_bind(int s, const struct sockaddr *name, int namelen)
{
struct file *fp = u.u_ofile[s];
struct ix_unix_name *un;
int tmp;
char *path = ((struct sockaddr_un *)name)->sun_path;
if (fp->f_sock->path[0])
errno_return(EINVAL, -1);
ix_lock_base();
un = find_unix_name(path);
ix_unlock_base();
if (un)
errno_return(EADDRINUSE, -1);
tmp = syscall(SYS_creat, path, 0777);
if (tmp < 0)
return -1;
syscall(SYS_close, tmp);
strcpy(fp->f_sock->path, path);
return 0;
}
int unp_listen(int s, int backlog)
{
struct file *fp = u.u_ofile[s];
struct unix_socket *us = fp->f_sock;
if (!us->path[0] || us->unix_name || us->to_server || us->from_server)
errno_return(EOPNOTSUPP, -1);
ix_lock_base();
us->unix_name = add_unix_name(us->path, backlog);
ix_unlock_base();
return (us->unix_name ? 0 : -1);
}
int unp_accept(int s, struct sockaddr *name, int *namelen)
{
struct file *f = u.u_ofile[s];
struct unix_socket *client = NULL;
struct ix_unix_name *un = f->f_sock->unix_name;
int omask, err = 0, sleep_rc, fd;
struct sockaddr_un *sa = (struct sockaddr_un *)name;
if (un == NULL)
errno_return(EOPNOTSUPP, -1);
omask = syscall (SYS_sigsetmask, ~0);
__get_file (f);
do {
while (un->queue_index == 0)
{
if (f->f_flags & FNDELAY)
{
err = EWOULDBLOCK;
goto error;
}
Forbid ();
__release_file (f);
syscall (SYS_sigsetmask, omask);
sleep_rc = ix_sleep((caddr_t)un, "accept");
Permit ();
if (sleep_rc < 0)
setrun (FindTask (0));
omask = syscall (SYS_sigsetmask, ~0);
__get_file (f);
}
ix_lock_base();
client = NULL;
if (un->queue_index)
{
client = (struct unix_socket *)un->queue[0];
if (--un->queue_index)
memcpy(un->queue, un->queue + 4, un->queue_index * 4);
if (client)
client->state = UNS_PROCESSING;
}
ix_unlock_base();
} while (client == NULL);
error:
__release_file (f);
if (err)
{
syscall (SYS_sigsetmask, omask);
errno_return(err, -1);
}
fd = unp_socket(PF_UNIX, SOCK_STREAM, 0, client);
if (fd == -1)
client->state = UNS_ERROR;
else
{
f = u.u_ofile[fd];
client->server = f;
client->state = UNS_ACCEPTED;
sa->sun_family = AF_UNIX;
strcpy(sa->sun_path, un->path);
sa->sun_len = *namelen = 3 + strlen(sa->sun_path);
}
ix_wakeup((u_int)client);
syscall (SYS_sigsetmask, omask);
return fd;
}
int unp_connect(int s, const struct sockaddr *name, int namelen)
{
struct file *f = u.u_ofile[s];
struct unix_socket *us = f->f_sock;
struct ix_unix_name *un;
int sleep_rc, state;
char *path = ((struct sockaddr_un *)name)->sun_path;
if (us->unix_name)
errno_return(EOPNOTSUPP, -1);
if (us->to_server || us->from_server)
errno_return(EISCONN, -1);
strcpy(us->path, path);
ix_lock_base();
un = find_unix_name(path);
if (un == NULL)
{
ix_unlock_base();
errno_return(EADDRNOTAVAIL, -1);
}
if (un->queue_size == un->queue_index)
{
ix_unlock_base();
errno_return(ECONNREFUSED, -1);
}
us->to_server = init_stream();
if (us->to_server)
us->from_server = init_stream();
if (!us->from_server)
{
if (us->to_server)
kfree(us->to_server);
us->to_server = NULL;
ix_unlock_base();
errno_return(ENOMEM, -1);
}
un->queue[un->queue_index++] = (int)us;
ix_unlock_base();
Forbid();
if (un->task)
Signal(un->task, 1UL << u.u_pipe_sig);
Permit();
ix_wakeup((u_int)un);
do {
Forbid ();
sleep_rc = ix_sleep((caddr_t)us, "connect");
state = us->state;
Permit ();
if (sleep_rc < 0)
setrun (FindTask (0));
} while (sleep_rc < 0 || (state != UNS_ERROR && state != UNS_ACCEPTED));
if (state == UNS_ERROR)
{
kfree(us->to_server);
kfree(us->from_server);
us->to_server = us->from_server = NULL;
errno_return(ECONNREFUSED, -1);
}
return 0;
}
int unp_send(int s, const void *buf, int len, int flags)
{
if (flags)
errno_return(EOPNOTSUPP, -1);
return unp_write(u.u_ofile[s], buf, len);
}
int unp_recv(int s, void *buf, int len, int flags)
{
if (flags)
errno_return(EOPNOTSUPP, -1);
return unp_read(u.u_ofile[s], buf, len);
}
int unp_shutdown(int s, int how)
{
struct file *f = u.u_ofile[s];
switch (how)
{
case 0:
close_stream(f, TRUE);
break;
case 1:
close_stream(f, FALSE);
break;
case 2:
close_stream(f, TRUE);
close_stream(f, FALSE);
break;
}
return 0;
}
int unp_setsockopt(int s, int level, int name, const void *val, int valsize)
{
return 0;
}
int unp_getsockopt(int s, int level, int name, void *val, int *valsize)
{
*valsize = 0;
return 0;
}
int unp_getsockname(int s, struct sockaddr *asa, int *alen)
{
struct file *f = u.u_ofile[s];
struct sockaddr_un *un = (struct sockaddr_un *)asa;
strcpy(un->sun_path, f->f_sock->path);
un->sun_family = AF_UNIX;
un->sun_len = *alen = 5 + strlen(un->sun_path);
return 0;
}
int unp_getpeername(int s, struct sockaddr *asa, int *alen)
{
return unp_getsockname(s, asa, alen);
}
int
stream_read (struct file *f, char *buf, int len)
{
int omask = syscall (SYS_sigsetmask, ~0);
int err = errno;
int really_read = 0;
struct sock_stream *ss = get_stream(f, TRUE);
while (len)
{
if (ss == NULL || ss->reader == ss->writer)
{
if (ss == NULL || (ss->flags & UNF_NO_WRITER))
{
err = 0;
break;
}
if (f->f_flags & FNDELAY)
{
if (!really_read)
{
really_read = -1;
err = EAGAIN;
}
break;
}
else if (really_read)
{
err = 0;
break;
}
else
{
int sleep_rc;
/* wait for something to be read or all readers to close */
Forbid ();
/* sigh.. Forbid() is necessary, or the other end may change
the pipe, and in the worst case also settle for sleep(), and
there it is.. deadlock.. */
release_stream(ss);
/* make read interruptible */
syscall (SYS_sigsetmask, omask);
sleep_rc = ix_sleep ((caddr_t)ss, "sockread");
Permit ();
if (sleep_rc < 0)
setrun (FindTask (0));
omask = syscall (SYS_sigsetmask, ~0);
ss = get_stream(f, TRUE);
continue; /* retry */
}
}
else
{
/* okay, there's something to read from the pipe */
if (ss->reader > ss->writer)
{
/* read till end of buffer and wrap around */
int avail = UNIX_SOCKET_SIZE - (ss->reader - ss->buffer);
int do_read = len < avail ? len : avail;
really_read += do_read;
bcopy (ss->reader, buf, do_read);
len -= do_read;
buf += do_read;
ss->reader += do_read;
if (ss->reader - ss->buffer == UNIX_SOCKET_SIZE)
/* wrap around */
ss->reader = ss->buffer;
}
if (len && ss->reader < ss->writer)
{
int avail = ss->writer - ss->reader;
int do_read = len < avail ? len : avail;
really_read += do_read;
bcopy (ss->reader, buf, do_read);
ss->reader += do_read;
len -= do_read;
buf += do_read;
}
}
ix_wakeup ((u_int)ss);
}
release_stream(ss);
syscall (SYS_sigsetmask, omask);
errno = err;
return really_read;
}
int unp_read(struct file *f, char *buf, int len)
{
if (f->f_sock->state != UNS_ACCEPTED)
errno_return(ENOTCONN, -1);
return stream_read(f, buf, len);
}
int
stream_write (struct file *f, const char *buf, int len)
{
int omask = syscall (SYS_sigsetmask, ~0);
int err = errno;
int really_written = 0;
struct sock_stream *ss = get_stream(f, FALSE);
while (len)
{
if (ss == NULL || (ss->flags & UNF_NO_READER))
{
really_written = -1;
err = EPIPE;
/* this is something no `real' Amiga pipe handler will do ;-)) */
_psignal (FindTask (0), SIGPIPE);
break;
}
/* buffer full ?? */
if (ss->reader == ss->writer + 1
|| (ss->reader == ss->buffer
&& ss->writer == ss->buffer + UNIX_SOCKET_SIZE - 1))
{
if (f->f_flags & FNDELAY)
{
if (! really_written)
{
really_written = -1;
err = EAGAIN;
}
break;
}
else
{
int sleep_rc;
/* wait for something to be read or all readers to close */
Forbid ();
/* sigh.. Forbid() is necessary, or the other end may change
the pipe, and in the worst case also settle for sleep(), and
there it is.. deadlock.. */
release_stream(ss);
/* make write interruptible */
syscall (SYS_sigsetmask, omask);
sleep_rc = ix_sleep ((caddr_t)ss, "sockwrite");
Permit ();
if (sleep_rc < 0)
setrun (FindTask (0));
omask = syscall (SYS_sigsetmask, ~0);
ss = get_stream(f, FALSE);
continue; /* retry */
}
}
else
{
/* okay, there's some space left to write to the pipe */
if (ss->writer >= ss->reader)
{
/* write till end of buffer */
int avail = UNIX_SOCKET_SIZE - 1 - (ss->writer - ss->buffer);
int do_write;
if (ss->reader > ss->buffer)
avail++;
do_write = len < avail ? len : avail;
really_written += do_write;
bcopy (buf, ss->writer, do_write);
len -= do_write;
buf += do_write;
ss->writer += do_write;
if (ss->writer - ss->buffer == UNIX_SOCKET_SIZE)
ss->writer = ss->buffer;
}
if (ss->writer < ss->reader - 1)
{
int avail = ss->reader - ss->writer - 1;
int do_write = len < avail ? len : avail;
really_written += do_write;
bcopy (buf, ss->writer, do_write);
ss->writer += do_write;
len -= do_write;
buf += do_write;
}
Forbid();
if (ss->task)
Signal(ss->task, 1UL << u.u_pipe_sig);
Permit();
}
ix_wakeup ((u_int)ss);
}
release_stream(ss);
syscall (SYS_sigsetmask, omask);
errno = err;
return really_written;
}
int unp_write(struct file *f, const char *buf, int len)
{
if (f->f_sock->state != UNS_ACCEPTED)
errno_return(ENOTCONN, -1);
return stream_write(f, buf, len);
}
int unp_ioctl(struct file *f, int cmd, int inout, int arglen, caddr_t arg)
{
int omask;
int result = 0;
struct sock_stream *ss;
omask = syscall (SYS_sigsetmask, ~0);
ss = get_stream(f, TRUE);
switch (cmd)
{
case FIONREAD:
{
unsigned int *pt = (unsigned int *)arg;
if (ss == NULL)
*pt = 0;
else if (ss->reader < ss->writer)
*pt = ss->writer - ss->reader;
else if (ss->reader > ss->writer)
*pt = UNIX_SOCKET_SIZE - (ss->reader - ss->writer);
else
*pt = 0;
result = 0;
break;
}
case FIONBIO:
{
result = f->f_flags & FNDELAY ? 1 : 0;
if (*(unsigned int *)arg)
f->f_flags |= FNDELAY;
else
f->f_flags &= ~FNDELAY;
/* I didn't find it documented in a manpage, but I assume, we
* should return the former state, not just zero.. */
break;
}
case FIOASYNC:
{
/* DOESN'T WORK YET */
int flags = *(unsigned long*)arg;
result = f->f_flags & FASYNC ? 1 : 0;
if (flags)
f->f_flags |= FASYNC;
else
f->f_flags &= ~FASYNC;
/* ATTENTION: have to call some function here in the future !!! */
/* I didn't find it documented in a manpage, but I assume, we
* should return the former state, not just zero.. */
break;
}
case FIOCLEX:
case FIONCLEX:
case FIOSETOWN:
case FIOGETOWN:
/* this is no error, but nevertheless we don't take any actions.. */
result = 0;
break;
}
release_stream(ss);
syscall (SYS_sigsetmask, omask);
return result;
}
int unp_select(struct file *f, int select_cmd, int io_mode, fd_set *ignored, u_long *also_ignored)
{
struct sock_stream *ss = find_stream(f, io_mode == SELMODE_IN);
SetSignal(0, 1UL << u.u_pipe_sig); /* reset signal */
if (f->f_type != DTYPE_PIPE && ss == NULL)
{
struct ix_unix_name *un = f->f_sock->unix_name;
int result = 1UL << u.u_pipe_sig;
if (un == NULL)
return 0;
ix_lock_base();
un->task = NULL;
if (select_cmd == SELCMD_CHECK || select_cmd == SELCMD_POLL)
{
if (io_mode == SELMODE_IN)
result = un->queue_index != 0;
if (io_mode == SELMODE_OUT)
result = 0;
}
else
un->task = FindTask(0);
ix_unlock_base();
return result;
}
ss->task = NULL;
if (select_cmd == SELCMD_CHECK || select_cmd == SELCMD_POLL)
{
/* we support both, read and write checks (hey, something new ;-)) */
if (io_mode == SELMODE_IN)
return (ss->reader != ss->writer || (ss->flags & UNF_NO_WRITER));
else if (io_mode == SELMODE_OUT)
return !(ss->reader == ss->writer + 1
|| (ss->reader == ss->buffer
&& ss->writer == ss->buffer + UNIX_SOCKET_SIZE - 1))
|| (ss->flags & UNF_NO_READER);
}
ss->task = FindTask(0);
return 1UL << u.u_pipe_sig;
}
int unp_close(struct file *f)
{
ix_lock_base ();
f->f_count--;
if (f->f_count == 0 && f->f_sock)
{
close_stream(f, TRUE);
close_stream(f, FALSE);
}
ix_unlock_base ();
return 0;
}