diff options
Diffstat (limited to 'sys/dev/streams/streams.c')
-rw-r--r-- | sys/dev/streams/streams.c | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/sys/dev/streams/streams.c b/sys/dev/streams/streams.c new file mode 100644 index 0000000..41adb1a --- /dev/null +++ b/sys/dev/streams/streams.c @@ -0,0 +1,428 @@ +/* + * Copyright (c) 1998 Mark Newton + * Copyright (c) 1994 Christos Zoulas + * Copyright (c) 1997 Todd Vierling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Stolen from NetBSD /sys/compat/svr4/svr4_net.c. Pseudo-device driver + * skeleton produced from /usr/share/examples/drivers/make_pseudo_driver.sh + * in 3.0-980524-SNAP then hacked a bit (but probably not enough :-). + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> /* SYSINIT stuff */ +#include <sys/conf.h> /* cdevsw stuff */ +#include <sys/malloc.h> /* malloc region definitions */ +#include <sys/file.h> +#include <sys/filedesc.h> +#include <sys/unistd.h> +#include <sys/fcntl.h> +#include <sys/socket.h> +#include <sys/protosw.h> +#include <sys/socketvar.h> +#include <sys/un.h> +#include <sys/domain.h> +#include <net/if.h> +#include <netinet/in.h> +#include <sys/proc.h> +#include <sys/uio.h> + +#include <sys/sysproto.h> + +#include <compat/svr4/svr4_types.h> +#include <compat/svr4/svr4_util.h> +#include <compat/svr4/svr4_signal.h> +#include <compat/svr4/svr4_ioctl.h> +#include <compat/svr4/svr4_stropts.h> +#include <compat/svr4/svr4_socket.h> + +static int svr4_soo_close(struct file *, struct thread *); +static int svr4_ptm_alloc(struct thread *); +static d_open_t streamsopen; + +struct svr4_sockcache_entry { + struct proc *p; /* Process for the socket */ + void *cookie; /* Internal cookie used for matching */ + struct sockaddr_un sock;/* Pathname for the socket */ + dev_t dev; /* Device where the socket lives on */ + ino_t ino; /* Inode where the socket lives on */ + TAILQ_ENTRY(svr4_sockcache_entry) entries; +}; + +TAILQ_HEAD(svr4_sockcache_head, svr4_sockcache_entry) svr4_head; + +/* Initialization flag (set/queried by svr4_mod LKM) */ +int svr4_str_initialized = 0; + +/* + * Device minor numbers + */ +enum { + dev_ptm = 10, + dev_arp = 26, + dev_icmp = 27, + dev_ip = 28, + dev_tcp = 35, + dev_udp = 36, + dev_rawip = 37, + dev_unix_dgram = 38, + dev_unix_stream = 39, + dev_unix_ord_stream = 40 +}; + +static dev_t dt_ptm, dt_arp, dt_icmp, dt_ip, dt_tcp, dt_udp, dt_rawip, + dt_unix_dgram, dt_unix_stream, dt_unix_ord_stream; + +static struct fileops svr4_netops = { + soo_read, soo_write, soo_ioctl, soo_poll, sokqfilter, + soo_stat, svr4_soo_close +}; + +#define CDEV_MAJOR 103 +static struct cdevsw streams_cdevsw = { + /* open */ streamsopen, + /* close */ noclose, + /* read */ noread, + /* write */ nowrite, + /* ioctl */ noioctl, + /* poll */ nopoll, + /* mmap */ nommap, + /* strategy */ nostrategy, + /* name */ "streams", + /* maj */ CDEV_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ 0, +}; + +struct streams_softc { + struct isa_device *dev; +} ; + +#define UNIT(dev) minor(dev) /* assume one minor number per unit */ + +typedef struct streams_softc *sc_p; + +static int +streams_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + /* XXX should make sure it isn't already loaded first */ + dt_ptm = make_dev(&streams_cdevsw, dev_ptm, 0, 0, 0666, + "ptm"); + dt_arp = make_dev(&streams_cdevsw, dev_arp, 0, 0, 0666, + "arp"); + dt_icmp = make_dev(&streams_cdevsw, dev_icmp, 0, 0, 0666, + "icmp"); + dt_ip = make_dev(&streams_cdevsw, dev_ip, 0, 0, 0666, + "ip"); + dt_tcp = make_dev(&streams_cdevsw, dev_tcp, 0, 0, 0666, + "tcp"); + dt_udp = make_dev(&streams_cdevsw, dev_udp, 0, 0, 0666, + "udp"); + dt_rawip = make_dev(&streams_cdevsw, dev_rawip, 0, 0, 0666, + "rawip"); + dt_unix_dgram = make_dev(&streams_cdevsw, dev_unix_dgram, + 0, 0, 0666, "ticlts"); + dt_unix_stream = make_dev(&streams_cdevsw, dev_unix_stream, + 0, 0, 0666, "ticots"); + dt_unix_ord_stream = make_dev(&streams_cdevsw, + dev_unix_ord_stream, 0, 0, 0666, "ticotsord"); + + if (! (dt_ptm && dt_arp && dt_icmp && dt_ip && dt_tcp && + dt_udp && dt_rawip && dt_unix_dgram && + dt_unix_stream && dt_unix_ord_stream)) { + printf("WARNING: device config for STREAMS failed\n"); + printf("Suggest unloading streams KLD\n"); + } + return 0; + case MOD_UNLOAD: + /* XXX should check to see if it's busy first */ + destroy_dev(dt_ptm); + destroy_dev(dt_arp); + destroy_dev(dt_icmp); + destroy_dev(dt_ip); + destroy_dev(dt_tcp); + destroy_dev(dt_udp); + destroy_dev(dt_rawip); + destroy_dev(dt_unix_dgram); + destroy_dev(dt_unix_stream); + destroy_dev(dt_unix_ord_stream); + + return 0; + default: + break; + } + return 0; +} + +static moduledata_t streams_mod = { + "streams", + streams_modevent, + 0 +}; +DECLARE_MODULE(streams, streams_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); +MODULE_VERSION(streams, 1); + +/* + * We only need open() and close() routines. open() calls socreate() + * to allocate a "real" object behind the stream and mallocs some state + * info for use by the svr4 emulator; close() deallocates the state + * information and passes the underlying object to the normal socket close + * routine. + */ +static int +streamsopen(dev_t dev, int oflags, int devtype, struct thread *td) +{ + int type, protocol; + int fd; + struct file *fp; + struct socket *so; + int error; + int family; + struct proc *p = td->td_proc; + + PROC_LOCK(p); + if (td->td_dupfd >= 0) { + PROC_UNLOCK(p); + return ENODEV; + } + PROC_UNLOCK(p); + + switch (minor(dev)) { + case dev_udp: + family = AF_INET; + type = SOCK_DGRAM; + protocol = IPPROTO_UDP; + break; + + case dev_tcp: + family = AF_INET; + type = SOCK_STREAM; + protocol = IPPROTO_TCP; + break; + + case dev_ip: + case dev_rawip: + family = AF_INET; + type = SOCK_RAW; + protocol = IPPROTO_IP; + break; + + case dev_icmp: + family = AF_INET; + type = SOCK_RAW; + protocol = IPPROTO_ICMP; + break; + + case dev_unix_dgram: + family = AF_LOCAL; + type = SOCK_DGRAM; + protocol = 0; + break; + + case dev_unix_stream: + case dev_unix_ord_stream: + family = AF_LOCAL; + type = SOCK_STREAM; + protocol = 0; + break; + + case dev_ptm: + return svr4_ptm_alloc(td); + + default: + return EOPNOTSUPP; + } + + if ((error = falloc(td, &fp, &fd)) != 0) + return error; + + if ((error = socreate(family, &so, type, protocol, + td->td_ucred, td)) != 0) { + FILEDESC_LOCK(p->p_fd); + p->p_fd->fd_ofiles[fd] = 0; + FILEDESC_UNLOCK(p->p_fd); + ffree(fp); + return error; + } + + FILEDESC_LOCK(p->p_fd); + fp->f_data = (caddr_t)so; + fp->f_flag = FREAD|FWRITE; + fp->f_ops = &svr4_netops; + fp->f_type = DTYPE_SOCKET; + FILEDESC_UNLOCK(p->p_fd); + + (void)svr4_stream_get(fp); + PROC_LOCK(p); + td->td_dupfd = fd; + PROC_UNLOCK(p); + return ENXIO; +} + +static int +svr4_ptm_alloc(td) + struct thread *td; +{ + struct proc *p = td->td_proc; + /* + * XXX this is very, very ugly. But I can't find a better + * way that won't duplicate a big amount of code from + * sys_open(). Ho hum... + * + * Fortunately for us, Solaris (at least 2.5.1) makes the + * /dev/ptmx open automatically just open a pty, that (after + * STREAMS I_PUSHes), is just a plain pty. fstat() is used + * to get the minor device number to map to a tty. + * + * Cycle through the names. If sys_open() returns ENOENT (or + * ENXIO), short circuit the cycle and exit. + */ + static char ptyname[] = "/dev/ptyXX"; + static char ttyletters[] = "pqrstuwxyzPQRST"; + static char ttynumbers[] = "0123456789abcdef"; + caddr_t sg = stackgap_init(); + char *path = stackgap_alloc(&sg, sizeof(ptyname)); + struct open_args oa; + int l = 0, n = 0; + register_t fd = -1; + int error; + + SCARG(&oa, path) = path; + SCARG(&oa, flags) = O_RDWR; + SCARG(&oa, mode) = 0; + + while (fd == -1) { + ptyname[8] = ttyletters[l]; + ptyname[9] = ttynumbers[n]; + + if ((error = copyout(ptyname, path, sizeof(ptyname))) != 0) + return error; + + switch (error = open(td, &oa)) { + case ENOENT: + case ENXIO: + return error; + case 0: + PROC_LOCK(p); + td->td_dupfd = td->td_retval[0]; + PROC_UNLOCK(p); + return ENXIO; + default: + if (ttynumbers[++n] == '\0') { + if (ttyletters[++l] == '\0') + break; + n = 0; + } + } + } + return ENOENT; +} + + +struct svr4_strm * +svr4_stream_get(fp) + struct file *fp; +{ + struct socket *so; + struct svr4_strm *st; + + if (fp == NULL || fp->f_type != DTYPE_SOCKET) + return NULL; + + so = (struct socket *) fp->f_data; + + /* + * mpfixme: lock socketbuffer here + */ + if (so->so_emuldata) { + return so->so_emuldata; + } + + /* Allocate a new one. */ + st = malloc(sizeof(struct svr4_strm), M_TEMP, M_WAITOK); + st->s_family = so->so_proto->pr_domain->dom_family; + st->s_cmd = ~0; + st->s_afd = -1; + st->s_eventmask = 0; + /* + * avoid a race where we loose due to concurrancy issues + * of two threads trying to allocate the so_emuldata. + */ + if (so->so_emuldata) { + /* lost the race, use the existing emuldata */ + FREE(st, M_TEMP); + st = so->so_emuldata; + } else { + /* we won, or there was no race, use our copy */ + so->so_emuldata = st; + fp->f_ops = &svr4_netops; + } + + return st; +} + +void +svr4_delete_socket(p, fp) + struct proc *p; + struct file *fp; +{ + struct svr4_sockcache_entry *e; + void *cookie = ((struct socket *) fp->f_data)->so_emuldata; + + while (svr4_str_initialized != 2) { + if (atomic_cmpset_acq_int(&svr4_str_initialized, 0, 1)) { + TAILQ_INIT(&svr4_head); + atomic_store_rel_int(&svr4_str_initialized, 2); + } + return; + } + + TAILQ_FOREACH(e, &svr4_head, entries) + if (e->p == p && e->cookie == cookie) { + TAILQ_REMOVE(&svr4_head, e, entries); + DPRINTF(("svr4_delete_socket: %s [%p,%d,%d]\n", + e->sock.sun_path, p, (int)e->dev, e->ino)); + free(e, M_TEMP); + return; + } +} + +static int +svr4_soo_close(struct file *fp, struct thread *td) +{ + struct socket *so = (struct socket *)fp->f_data; + + /* CHECKUNIT_DIAG(ENXIO);*/ + + svr4_delete_socket(td->td_proc, fp); + free(so->so_emuldata, M_TEMP); + return soo_close(fp, td); +} |