streams.c revision 111815
1259698Sdim/* 2259698Sdim * Copyright (c) 1998 Mark Newton 3259698Sdim * Copyright (c) 1994 Christos Zoulas 4259698Sdim * Copyright (c) 1997 Todd Vierling 5259698Sdim * All rights reserved. 6259698Sdim * 7259698Sdim * Redistribution and use in source and binary forms, with or without 8259698Sdim * modification, are permitted provided that the following conditions 9259698Sdim * are met: 10259698Sdim * 1. Redistributions of source code must retain the above copyright 11259698Sdim * notice, this list of conditions and the following disclaimer. 12259698Sdim * 2. Redistributions in binary form must reproduce the above copyright 13259698Sdim * notice, this list of conditions and the following disclaimer in the 14259698Sdim * documentation and/or other materials provided with the distribution. 15259698Sdim * 3. The names of the authors may not be used to endorse or promote products 16259698Sdim * derived from this software without specific prior written permission 17259698Sdim * 18259698Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19259698Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20259698Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21259698Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22259698Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23259698Sdim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24259698Sdim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25259698Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26259698Sdim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27259698Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28259698Sdim * 29259698Sdim * Stolen from NetBSD /sys/compat/svr4/svr4_net.c. Pseudo-device driver 30259698Sdim * skeleton produced from /usr/share/examples/drivers/make_pseudo_driver.sh 31259698Sdim * in 3.0-980524-SNAP then hacked a bit (but probably not enough :-). 32259698Sdim * 33259698Sdim * $FreeBSD: head/sys/dev/streams/streams.c 111815 2003-03-03 12:15:54Z phk $ 34259698Sdim */ 35259698Sdim 36259698Sdim#include <sys/param.h> 37259698Sdim#include <sys/systm.h> 38259698Sdim#include <sys/kernel.h> /* SYSINIT stuff */ 39259698Sdim#include <sys/conf.h> /* cdevsw stuff */ 40259698Sdim#include <sys/malloc.h> /* malloc region definitions */ 41259698Sdim#include <sys/file.h> 42259698Sdim#include <sys/filedesc.h> 43259698Sdim#include <sys/unistd.h> 44259698Sdim#include <sys/fcntl.h> 45259698Sdim#include <sys/socket.h> 46259698Sdim#include <sys/protosw.h> 47276479Sdim#include <sys/socketvar.h> 48259698Sdim#include <sys/un.h> 49259698Sdim#include <sys/domain.h> 50259698Sdim#include <net/if.h> 51259698Sdim#include <netinet/in.h> 52259698Sdim#include <sys/proc.h> 53259698Sdim#include <sys/uio.h> 54259698Sdim 55259698Sdim#include <sys/sysproto.h> 56259698Sdim 57259698Sdim#include <compat/svr4/svr4_types.h> 58259698Sdim#include <compat/svr4/svr4_util.h> 59259698Sdim#include <compat/svr4/svr4_signal.h> 60259698Sdim#include <compat/svr4/svr4_ioctl.h> 61259698Sdim#include <compat/svr4/svr4_stropts.h> 62259698Sdim#include <compat/svr4/svr4_socket.h> 63259698Sdim 64259698Sdimstatic int svr4_soo_close(struct file *, struct thread *); 65259698Sdimstatic int svr4_ptm_alloc(struct thread *); 66259698Sdimstatic d_open_t streamsopen; 67259698Sdim 68259698Sdimstruct svr4_sockcache_entry { 69259698Sdim struct proc *p; /* Process for the socket */ 70259698Sdim void *cookie; /* Internal cookie used for matching */ 71259698Sdim struct sockaddr_un sock;/* Pathname for the socket */ 72259698Sdim dev_t dev; /* Device where the socket lives on */ 73259698Sdim ino_t ino; /* Inode where the socket lives on */ 74259698Sdim TAILQ_ENTRY(svr4_sockcache_entry) entries; 75259698Sdim}; 76259698Sdim 77259698SdimTAILQ_HEAD(svr4_sockcache_head, svr4_sockcache_entry) svr4_head; 78259698Sdim 79259698Sdim/* Initialization flag (set/queried by svr4_mod LKM) */ 80259698Sdimint svr4_str_initialized = 0; 81259698Sdim 82259698Sdim/* 83280031Sdim * Device minor numbers 84259698Sdim */ 85259698Sdimenum { 86259698Sdim dev_ptm = 10, 87259698Sdim dev_arp = 26, 88259698Sdim dev_icmp = 27, 89259698Sdim dev_ip = 28, 90259698Sdim dev_tcp = 35, 91259698Sdim dev_udp = 36, 92259698Sdim dev_rawip = 37, 93259698Sdim dev_unix_dgram = 38, 94259698Sdim dev_unix_stream = 39, 95259698Sdim dev_unix_ord_stream = 40 96259698Sdim}; 97259698Sdim 98259698Sdimstatic dev_t dt_ptm, dt_arp, dt_icmp, dt_ip, dt_tcp, dt_udp, dt_rawip, 99259698Sdim dt_unix_dgram, dt_unix_stream, dt_unix_ord_stream; 100259698Sdim 101259698Sdimstatic struct fileops svr4_netops = { 102259698Sdim soo_read, soo_write, soo_ioctl, soo_poll, soo_kqfilter, 103259698Sdim soo_stat, svr4_soo_close 104259698Sdim}; 105259698Sdim 106259698Sdim#define CDEV_MAJOR 103 107259698Sdimstatic struct cdevsw streams_cdevsw = { 108259698Sdim .d_open = streamsopen, 109259698Sdim .d_name = "streams", 110259698Sdim .d_maj = CDEV_MAJOR, 111259698Sdim}; 112259698Sdim 113259698Sdimstruct streams_softc { 114259698Sdim struct isa_device *dev; 115259698Sdim} ; 116259698Sdim 117259698Sdim#define UNIT(dev) minor(dev) /* assume one minor number per unit */ 118259698Sdim 119259698Sdimtypedef struct streams_softc *sc_p; 120259698Sdim 121259698Sdimstatic int 122259698Sdimstreams_modevent(module_t mod, int type, void *unused) 123259698Sdim{ 124259698Sdim switch (type) { 125259698Sdim case MOD_LOAD: 126259698Sdim /* XXX should make sure it isn't already loaded first */ 127259698Sdim dt_ptm = make_dev(&streams_cdevsw, dev_ptm, 0, 0, 0666, 128259698Sdim "ptm"); 129259698Sdim dt_arp = make_dev(&streams_cdevsw, dev_arp, 0, 0, 0666, 130259698Sdim "arp"); 131259698Sdim dt_icmp = make_dev(&streams_cdevsw, dev_icmp, 0, 0, 0666, 132259698Sdim "icmp"); 133259698Sdim dt_ip = make_dev(&streams_cdevsw, dev_ip, 0, 0, 0666, 134259698Sdim "ip"); 135259698Sdim dt_tcp = make_dev(&streams_cdevsw, dev_tcp, 0, 0, 0666, 136259698Sdim "tcp"); 137259698Sdim dt_udp = make_dev(&streams_cdevsw, dev_udp, 0, 0, 0666, 138259698Sdim "udp"); 139259698Sdim dt_rawip = make_dev(&streams_cdevsw, dev_rawip, 0, 0, 0666, 140259698Sdim "rawip"); 141259698Sdim dt_unix_dgram = make_dev(&streams_cdevsw, dev_unix_dgram, 142259698Sdim 0, 0, 0666, "ticlts"); 143259698Sdim dt_unix_stream = make_dev(&streams_cdevsw, dev_unix_stream, 144259698Sdim 0, 0, 0666, "ticots"); 145259698Sdim dt_unix_ord_stream = make_dev(&streams_cdevsw, 146259698Sdim dev_unix_ord_stream, 0, 0, 0666, "ticotsord"); 147259698Sdim 148259698Sdim if (! (dt_ptm && dt_arp && dt_icmp && dt_ip && dt_tcp && 149259698Sdim dt_udp && dt_rawip && dt_unix_dgram && 150259698Sdim dt_unix_stream && dt_unix_ord_stream)) { 151259698Sdim printf("WARNING: device config for STREAMS failed\n"); 152259698Sdim printf("Suggest unloading streams KLD\n"); 153259698Sdim } 154259698Sdim return 0; 155259698Sdim case MOD_UNLOAD: 156259698Sdim /* XXX should check to see if it's busy first */ 157259698Sdim destroy_dev(dt_ptm); 158259698Sdim destroy_dev(dt_arp); 159259698Sdim destroy_dev(dt_icmp); 160259698Sdim destroy_dev(dt_ip); 161259698Sdim destroy_dev(dt_tcp); 162259698Sdim destroy_dev(dt_udp); 163259698Sdim destroy_dev(dt_rawip); 164259698Sdim destroy_dev(dt_unix_dgram); 165259698Sdim destroy_dev(dt_unix_stream); 166259698Sdim destroy_dev(dt_unix_ord_stream); 167259698Sdim 168259698Sdim return 0; 169259698Sdim default: 170259698Sdim break; 171259698Sdim } 172259698Sdim return 0; 173259698Sdim} 174259698Sdim 175259698Sdimstatic moduledata_t streams_mod = { 176259698Sdim "streams", 177259698Sdim streams_modevent, 178259698Sdim 0 179259698Sdim}; 180259698SdimDECLARE_MODULE(streams, streams_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); 181259698SdimMODULE_VERSION(streams, 1); 182259698Sdim 183259698Sdim/* 184259698Sdim * We only need open() and close() routines. open() calls socreate() 185259698Sdim * to allocate a "real" object behind the stream and mallocs some state 186259698Sdim * info for use by the svr4 emulator; close() deallocates the state 187259698Sdim * information and passes the underlying object to the normal socket close 188259698Sdim * routine. 189259698Sdim */ 190259698Sdimstatic int 191259698Sdimstreamsopen(dev_t dev, int oflags, int devtype, struct thread *td) 192259698Sdim{ 193259698Sdim int type, protocol; 194259698Sdim int fd; 195259698Sdim struct file *fp; 196259698Sdim struct socket *so; 197259698Sdim int error; 198259698Sdim int family; 199259698Sdim struct proc *p = td->td_proc; 200259698Sdim 201259698Sdim PROC_LOCK(p); 202259698Sdim if (td->td_dupfd >= 0) { 203259698Sdim PROC_UNLOCK(p); 204259698Sdim return ENODEV; 205259698Sdim } 206259698Sdim PROC_UNLOCK(p); 207259698Sdim 208259698Sdim switch (minor(dev)) { 209276479Sdim case dev_udp: 210259698Sdim family = AF_INET; 211259698Sdim type = SOCK_DGRAM; 212259698Sdim protocol = IPPROTO_UDP; 213259698Sdim break; 214259698Sdim 215259698Sdim case dev_tcp: 216259698Sdim family = AF_INET; 217259698Sdim type = SOCK_STREAM; 218259698Sdim protocol = IPPROTO_TCP; 219259698Sdim break; 220259698Sdim 221259698Sdim case dev_ip: 222276479Sdim case dev_rawip: 223259698Sdim family = AF_INET; 224259698Sdim type = SOCK_RAW; 225259698Sdim protocol = IPPROTO_IP; 226259698Sdim break; 227259698Sdim 228259698Sdim case dev_icmp: 229259698Sdim family = AF_INET; 230259698Sdim type = SOCK_RAW; 231259698Sdim protocol = IPPROTO_ICMP; 232259698Sdim break; 233259698Sdim 234259698Sdim case dev_unix_dgram: 235259698Sdim family = AF_LOCAL; 236259698Sdim type = SOCK_DGRAM; 237259698Sdim protocol = 0; 238259698Sdim break; 239259698Sdim 240259698Sdim case dev_unix_stream: 241259698Sdim case dev_unix_ord_stream: 242259698Sdim family = AF_LOCAL; 243259698Sdim type = SOCK_STREAM; 244259698Sdim protocol = 0; 245259698Sdim break; 246259698Sdim 247259698Sdim case dev_ptm: 248259698Sdim return svr4_ptm_alloc(td); 249259698Sdim 250259698Sdim default: 251259698Sdim return EOPNOTSUPP; 252259698Sdim } 253259698Sdim 254259698Sdim if ((error = falloc(td, &fp, &fd)) != 0) 255259698Sdim return error; 256259698Sdim 257259698Sdim if ((error = socreate(family, &so, type, protocol, 258259698Sdim td->td_ucred, td)) != 0) { 259259698Sdim FILEDESC_LOCK(p->p_fd); 260259698Sdim p->p_fd->fd_ofiles[fd] = 0; 261259698Sdim FILEDESC_UNLOCK(p->p_fd); 262259698Sdim ffree(fp); 263259698Sdim return error; 264259698Sdim } 265259698Sdim 266259698Sdim FILEDESC_LOCK(p->p_fd); 267259698Sdim fp->f_data = so; 268259698Sdim fp->f_flag = FREAD|FWRITE; 269259698Sdim fp->f_ops = &svr4_netops; 270259698Sdim fp->f_type = DTYPE_SOCKET; 271259698Sdim FILEDESC_UNLOCK(p->p_fd); 272259698Sdim 273259698Sdim (void)svr4_stream_get(fp); 274259698Sdim PROC_LOCK(p); 275259698Sdim td->td_dupfd = fd; 276259698Sdim PROC_UNLOCK(p); 277259698Sdim return ENXIO; 278259698Sdim} 279259698Sdim 280259698Sdimstatic int 281259698Sdimsvr4_ptm_alloc(td) 282259698Sdim struct thread *td; 283259698Sdim{ 284259698Sdim struct proc *p = td->td_proc; 285259698Sdim /* 286259698Sdim * XXX this is very, very ugly. But I can't find a better 287259698Sdim * way that won't duplicate a big amount of code from 288259698Sdim * sys_open(). Ho hum... 289259698Sdim * 290259698Sdim * Fortunately for us, Solaris (at least 2.5.1) makes the 291259698Sdim * /dev/ptmx open automatically just open a pty, that (after 292259698Sdim * STREAMS I_PUSHes), is just a plain pty. fstat() is used 293259698Sdim * to get the minor device number to map to a tty. 294259698Sdim * 295259698Sdim * Cycle through the names. If sys_open() returns ENOENT (or 296259698Sdim * ENXIO), short circuit the cycle and exit. 297259698Sdim */ 298259698Sdim static char ptyname[] = "/dev/ptyXX"; 299259698Sdim static char ttyletters[] = "pqrstuwxyzPQRST"; 300259698Sdim static char ttynumbers[] = "0123456789abcdef"; 301259698Sdim caddr_t sg = stackgap_init(); 302259698Sdim char *path = stackgap_alloc(&sg, sizeof(ptyname)); 303259698Sdim struct open_args oa; 304259698Sdim int l = 0, n = 0; 305259698Sdim register_t fd = -1; 306259698Sdim int error; 307259698Sdim 308259698Sdim oa.path = path; 309259698Sdim oa.flags = O_RDWR; 310259698Sdim oa.mode = 0; 311259698Sdim 312259698Sdim while (fd == -1) { 313259698Sdim ptyname[8] = ttyletters[l]; 314259698Sdim ptyname[9] = ttynumbers[n]; 315259698Sdim 316259698Sdim if ((error = copyout(ptyname, path, sizeof(ptyname))) != 0) 317259698Sdim return error; 318259698Sdim 319259698Sdim switch (error = open(td, &oa)) { 320259698Sdim case ENOENT: 321259698Sdim case ENXIO: 322259698Sdim return error; 323259698Sdim case 0: 324259698Sdim PROC_LOCK(p); 325259698Sdim td->td_dupfd = td->td_retval[0]; 326259698Sdim PROC_UNLOCK(p); 327276479Sdim return ENXIO; 328259698Sdim default: 329259698Sdim if (ttynumbers[++n] == '\0') { 330259698Sdim if (ttyletters[++l] == '\0') 331259698Sdim break; 332259698Sdim n = 0; 333259698Sdim } 334259698Sdim } 335259698Sdim } 336259698Sdim return ENOENT; 337259698Sdim} 338259698Sdim 339259698Sdim 340259698Sdimstruct svr4_strm * 341259698Sdimsvr4_stream_get(fp) 342259698Sdim struct file *fp; 343259698Sdim{ 344259698Sdim struct socket *so; 345259698Sdim struct svr4_strm *st; 346259698Sdim 347259698Sdim if (fp == NULL || fp->f_type != DTYPE_SOCKET) 348259698Sdim return NULL; 349259698Sdim 350259698Sdim so = fp->f_data; 351259698Sdim 352259698Sdim /* 353259698Sdim * mpfixme: lock socketbuffer here 354259698Sdim */ 355259698Sdim if (so->so_emuldata) { 356259698Sdim return so->so_emuldata; 357259698Sdim } 358259698Sdim 359259698Sdim /* Allocate a new one. */ 360259698Sdim st = malloc(sizeof(struct svr4_strm), M_TEMP, M_WAITOK); 361259698Sdim st->s_family = so->so_proto->pr_domain->dom_family; 362259698Sdim st->s_cmd = ~0; 363280031Sdim st->s_afd = -1; 364259698Sdim st->s_eventmask = 0; 365259698Sdim /* 366259698Sdim * avoid a race where we loose due to concurrancy issues 367259698Sdim * of two threads trying to allocate the so_emuldata. 368259698Sdim */ 369259698Sdim if (so->so_emuldata) { 370259698Sdim /* lost the race, use the existing emuldata */ 371259698Sdim FREE(st, M_TEMP); 372259698Sdim st = so->so_emuldata; 373259698Sdim } else { 374259698Sdim /* we won, or there was no race, use our copy */ 375259698Sdim so->so_emuldata = st; 376259698Sdim fp->f_ops = &svr4_netops; 377259698Sdim } 378259698Sdim 379259698Sdim return st; 380259698Sdim} 381259698Sdim 382259698Sdimvoid 383259698Sdimsvr4_delete_socket(p, fp) 384259698Sdim struct proc *p; 385259698Sdim struct file *fp; 386259698Sdim{ 387259698Sdim struct svr4_sockcache_entry *e; 388259698Sdim void *cookie = ((struct socket *)fp->f_data)->so_emuldata; 389259698Sdim 390 while (svr4_str_initialized != 2) { 391 if (atomic_cmpset_acq_int(&svr4_str_initialized, 0, 1)) { 392 TAILQ_INIT(&svr4_head); 393 atomic_store_rel_int(&svr4_str_initialized, 2); 394 } 395 return; 396 } 397 398 TAILQ_FOREACH(e, &svr4_head, entries) 399 if (e->p == p && e->cookie == cookie) { 400 TAILQ_REMOVE(&svr4_head, e, entries); 401 DPRINTF(("svr4_delete_socket: %s [%p,%d,%d]\n", 402 e->sock.sun_path, p, (int)e->dev, e->ino)); 403 free(e, M_TEMP); 404 return; 405 } 406} 407 408static int 409svr4_soo_close(struct file *fp, struct thread *td) 410{ 411 struct socket *so = fp->f_data; 412 413 /* CHECKUNIT_DIAG(ENXIO);*/ 414 415 svr4_delete_socket(td->td_proc, fp); 416 free(so->so_emuldata, M_TEMP); 417 return soo_close(fp, td); 418} 419