streams.c revision 119420
155714Skris/*
255714Skris * Copyright (c) 1998 Mark Newton
355714Skris * Copyright (c) 1994 Christos Zoulas
455714Skris * Copyright (c) 1997 Todd Vierling
555714Skris * All rights reserved.
655714Skris *
755714Skris * Redistribution and use in source and binary forms, with or without
855714Skris * modification, are permitted provided that the following conditions
955714Skris * are met:
1055714Skris * 1. Redistributions of source code must retain the above copyright
1155714Skris *    notice, this list of conditions and the following disclaimer.
1255714Skris * 2. Redistributions in binary form must reproduce the above copyright
1355714Skris *    notice, this list of conditions and the following disclaimer in the
1455714Skris *    documentation and/or other materials provided with the distribution.
1555714Skris * 3. The names of the authors may not be used to endorse or promote products
1655714Skris *    derived from this software without specific prior written permission
1755714Skris *
1855714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1955714Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2055714Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2155714Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2255714Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2355714Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2455714Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2555714Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2655714Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2755714Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2855714Skris *
2955714Skris * Stolen from NetBSD /sys/compat/svr4/svr4_net.c.  Pseudo-device driver
3055714Skris * skeleton produced from /usr/share/examples/drivers/make_pseudo_driver.sh
3155714Skris * in 3.0-980524-SNAP then hacked a bit (but probably not enough :-).
3255714Skris *
3355714Skris */
3455714Skris
3555714Skris#include <sys/cdefs.h>
3655714Skris__FBSDID("$FreeBSD: head/sys/dev/streams/streams.c 119420 2003-08-24 18:17:24Z obrien $");
3755714Skris
3855714Skris#include <sys/param.h>
3955714Skris#include <sys/systm.h>
4055714Skris#include <sys/kernel.h>		/* SYSINIT stuff */
4155714Skris#include <sys/conf.h>		/* cdevsw stuff */
4255714Skris#include <sys/malloc.h>		/* malloc region definitions */
4355714Skris#include <sys/file.h>
4455714Skris#include <sys/filedesc.h>
4555714Skris#include <sys/unistd.h>
4655714Skris#include <sys/fcntl.h>
4755714Skris#include <sys/socket.h>
4855714Skris#include <sys/protosw.h>
4955714Skris#include <sys/socketvar.h>
5055714Skris#include <sys/un.h>
5155714Skris#include <sys/domain.h>
5255714Skris#include <net/if.h>
5355714Skris#include <netinet/in.h>
5455714Skris#include <sys/proc.h>
5555714Skris#include <sys/uio.h>
5655714Skris
5755714Skris#include <sys/sysproto.h>
5855714Skris
5955714Skris#include <compat/svr4/svr4_types.h>
6055714Skris#include <compat/svr4/svr4_util.h>
61#include <compat/svr4/svr4_signal.h>
62#include <compat/svr4/svr4_ioctl.h>
63#include <compat/svr4/svr4_stropts.h>
64#include <compat/svr4/svr4_socket.h>
65
66static int svr4_soo_close(struct file *, struct thread *);
67static int svr4_ptm_alloc(struct thread *);
68static  d_open_t	streamsopen;
69
70struct svr4_sockcache_head svr4_head;
71
72/* Initialization flag (set/queried by svr4_mod LKM) */
73int svr4_str_initialized = 0;
74
75/*
76 * Device minor numbers
77 */
78enum {
79	dev_ptm			= 10,
80	dev_arp			= 26,
81	dev_icmp		= 27,
82	dev_ip			= 28,
83	dev_tcp			= 35,
84	dev_udp			= 36,
85	dev_rawip		= 37,
86	dev_unix_dgram		= 38,
87	dev_unix_stream		= 39,
88	dev_unix_ord_stream	= 40
89};
90
91static dev_t dt_ptm, dt_arp, dt_icmp, dt_ip, dt_tcp, dt_udp, dt_rawip,
92	dt_unix_dgram, dt_unix_stream, dt_unix_ord_stream;
93
94static struct fileops svr4_netops = {
95	.fo_read = soo_read,
96	.fo_write = soo_write,
97	.fo_ioctl = soo_ioctl,
98	.fo_poll = soo_poll,
99	.fo_kqfilter = soo_kqfilter,
100	.fo_stat = soo_stat,
101	.fo_close =  svr4_soo_close
102};
103
104#define CDEV_MAJOR 103
105static struct cdevsw streams_cdevsw = {
106	.d_open =	streamsopen,
107	.d_name =	"streams",
108	.d_maj =	CDEV_MAJOR,
109};
110
111struct streams_softc {
112	struct isa_device *dev;
113} ;
114
115#define UNIT(dev) minor(dev)	/* assume one minor number per unit */
116
117typedef	struct streams_softc *sc_p;
118
119static	int
120streams_modevent(module_t mod, int type, void *unused)
121{
122	switch (type) {
123	case MOD_LOAD:
124		/* XXX should make sure it isn't already loaded first */
125		dt_ptm = make_dev(&streams_cdevsw, dev_ptm, 0, 0, 0666,
126			"ptm");
127		dt_arp = make_dev(&streams_cdevsw, dev_arp, 0, 0, 0666,
128			"arp");
129		dt_icmp = make_dev(&streams_cdevsw, dev_icmp, 0, 0, 0666,
130			"icmp");
131		dt_ip = make_dev(&streams_cdevsw, dev_ip, 0, 0, 0666,
132			"ip");
133		dt_tcp = make_dev(&streams_cdevsw, dev_tcp, 0, 0, 0666,
134			"tcp");
135		dt_udp = make_dev(&streams_cdevsw, dev_udp, 0, 0, 0666,
136			"udp");
137		dt_rawip = make_dev(&streams_cdevsw, dev_rawip, 0, 0, 0666,
138			"rawip");
139		dt_unix_dgram = make_dev(&streams_cdevsw, dev_unix_dgram,
140			0, 0, 0666, "ticlts");
141		dt_unix_stream = make_dev(&streams_cdevsw, dev_unix_stream,
142			0, 0, 0666, "ticots");
143		dt_unix_ord_stream = make_dev(&streams_cdevsw,
144			dev_unix_ord_stream, 0, 0, 0666, "ticotsord");
145
146		if (! (dt_ptm && dt_arp && dt_icmp && dt_ip && dt_tcp &&
147				dt_udp && dt_rawip && dt_unix_dgram &&
148				dt_unix_stream && dt_unix_ord_stream)) {
149			printf("WARNING: device config for STREAMS failed\n");
150			printf("Suggest unloading streams KLD\n");
151		}
152		return 0;
153	case MOD_UNLOAD:
154	  	/* XXX should check to see if it's busy first */
155		destroy_dev(dt_ptm);
156		destroy_dev(dt_arp);
157		destroy_dev(dt_icmp);
158		destroy_dev(dt_ip);
159		destroy_dev(dt_tcp);
160		destroy_dev(dt_udp);
161		destroy_dev(dt_rawip);
162		destroy_dev(dt_unix_dgram);
163		destroy_dev(dt_unix_stream);
164		destroy_dev(dt_unix_ord_stream);
165
166		return 0;
167	default:
168		break;
169	}
170	return 0;
171}
172
173static moduledata_t streams_mod = {
174	"streams",
175	streams_modevent,
176	0
177};
178DECLARE_MODULE(streams, streams_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
179MODULE_VERSION(streams, 1);
180
181/*
182 * We only need open() and close() routines.  open() calls socreate()
183 * to allocate a "real" object behind the stream and mallocs some state
184 * info for use by the svr4 emulator;  close() deallocates the state
185 * information and passes the underlying object to the normal socket close
186 * routine.
187 */
188static  int
189streamsopen(dev_t dev, int oflags, int devtype, struct thread *td)
190{
191	int type, protocol;
192	int fd;
193	struct file *fp;
194	struct socket *so;
195	int error;
196	int family;
197	struct proc *p = td->td_proc;
198
199	PROC_LOCK(p);
200	if (td->td_dupfd >= 0) {
201	  PROC_UNLOCK(p);
202	  return ENODEV;
203	}
204	PROC_UNLOCK(p);
205
206	switch (minor(dev)) {
207	case dev_udp:
208	  family = AF_INET;
209	  type = SOCK_DGRAM;
210	  protocol = IPPROTO_UDP;
211	  break;
212
213	case dev_tcp:
214	  family = AF_INET;
215	  type = SOCK_STREAM;
216	  protocol = IPPROTO_TCP;
217	  break;
218
219	case dev_ip:
220	case dev_rawip:
221	  family = AF_INET;
222	  type = SOCK_RAW;
223	  protocol = IPPROTO_IP;
224	  break;
225
226	case dev_icmp:
227	  family = AF_INET;
228	  type = SOCK_RAW;
229	  protocol = IPPROTO_ICMP;
230	  break;
231
232	case dev_unix_dgram:
233	  family = AF_LOCAL;
234	  type = SOCK_DGRAM;
235	  protocol = 0;
236	  break;
237
238	case dev_unix_stream:
239	case dev_unix_ord_stream:
240	  family = AF_LOCAL;
241	  type = SOCK_STREAM;
242	  protocol = 0;
243	  break;
244
245	case dev_ptm:
246	  return svr4_ptm_alloc(td);
247
248	default:
249	  return EOPNOTSUPP;
250	}
251
252	if ((error = falloc(td, &fp, &fd)) != 0)
253	  return error;
254
255	if ((error = socreate(family, &so, type, protocol,
256	    td->td_ucred, td)) != 0) {
257	  FILEDESC_LOCK(p->p_fd);
258	  p->p_fd->fd_ofiles[fd] = 0;
259	  FILEDESC_UNLOCK(p->p_fd);
260	  ffree(fp);
261	  return error;
262	}
263
264	FILEDESC_LOCK(p->p_fd);
265	fp->f_data = so;
266	fp->f_flag = FREAD|FWRITE;
267	fp->f_ops = &svr4_netops;
268	fp->f_type = DTYPE_SOCKET;
269	FILEDESC_UNLOCK(p->p_fd);
270
271	(void)svr4_stream_get(fp);
272	PROC_LOCK(p);
273	td->td_dupfd = fd;
274	PROC_UNLOCK(p);
275	return ENXIO;
276}
277
278static int
279svr4_ptm_alloc(td)
280	struct thread *td;
281{
282	struct proc *p = td->td_proc;
283	/*
284	 * XXX this is very, very ugly.  But I can't find a better
285	 * way that won't duplicate a big amount of code from
286	 * sys_open().  Ho hum...
287	 *
288	 * Fortunately for us, Solaris (at least 2.5.1) makes the
289	 * /dev/ptmx open automatically just open a pty, that (after
290	 * STREAMS I_PUSHes), is just a plain pty.  fstat() is used
291	 * to get the minor device number to map to a tty.
292	 *
293	 * Cycle through the names. If sys_open() returns ENOENT (or
294	 * ENXIO), short circuit the cycle and exit.
295	 */
296	static char ptyname[] = "/dev/ptyXX";
297	static char ttyletters[] = "pqrstuwxyzPQRST";
298	static char ttynumbers[] = "0123456789abcdef";
299	caddr_t sg = stackgap_init();
300	char *path = stackgap_alloc(&sg, sizeof(ptyname));
301	struct open_args oa;
302	int l = 0, n = 0;
303	register_t fd = -1;
304	int error;
305
306	oa.path = path;
307	oa.flags = O_RDWR;
308	oa.mode = 0;
309
310	while (fd == -1) {
311		ptyname[8] = ttyletters[l];
312		ptyname[9] = ttynumbers[n];
313
314		if ((error = copyout(ptyname, path, sizeof(ptyname))) != 0)
315			return error;
316
317		switch (error = open(td, &oa)) {
318		case ENOENT:
319		case ENXIO:
320			return error;
321		case 0:
322			PROC_LOCK(p);
323			td->td_dupfd = td->td_retval[0];
324			PROC_UNLOCK(p);
325			return ENXIO;
326		default:
327			if (ttynumbers[++n] == '\0') {
328				if (ttyletters[++l] == '\0')
329					break;
330				n = 0;
331			}
332		}
333	}
334	return ENOENT;
335}
336
337
338struct svr4_strm *
339svr4_stream_get(fp)
340	struct file *fp;
341{
342	struct socket *so;
343	struct svr4_strm *st;
344
345	if (fp == NULL || fp->f_type != DTYPE_SOCKET)
346		return NULL;
347
348	so = fp->f_data;
349
350	/*
351	 * mpfixme: lock socketbuffer here
352	 */
353	if (so->so_emuldata) {
354		return so->so_emuldata;
355	}
356
357	/* Allocate a new one. */
358	st = malloc(sizeof(struct svr4_strm), M_TEMP, M_WAITOK);
359	st->s_family = so->so_proto->pr_domain->dom_family;
360	st->s_cmd = ~0;
361	st->s_afd = -1;
362	st->s_eventmask = 0;
363	/*
364	 * avoid a race where we loose due to concurrancy issues
365	 * of two threads trying to allocate the so_emuldata.
366	 */
367	if (so->so_emuldata) {
368		/* lost the race, use the existing emuldata */
369		FREE(st, M_TEMP);
370		st = so->so_emuldata;
371	} else {
372		/* we won, or there was no race, use our copy */
373		so->so_emuldata = st;
374		fp->f_ops = &svr4_netops;
375	}
376
377	return st;
378}
379
380void
381svr4_delete_socket(p, fp)
382	struct proc *p;
383	struct file *fp;
384{
385	struct svr4_sockcache_entry *e;
386	void *cookie = ((struct socket *)fp->f_data)->so_emuldata;
387
388	while (svr4_str_initialized != 2) {
389		if (atomic_cmpset_acq_int(&svr4_str_initialized, 0, 1)) {
390			TAILQ_INIT(&svr4_head);
391			atomic_store_rel_int(&svr4_str_initialized, 2);
392		}
393		return;
394	}
395
396	TAILQ_FOREACH(e, &svr4_head, entries)
397		if (e->p == p && e->cookie == cookie) {
398			TAILQ_REMOVE(&svr4_head, e, entries);
399			DPRINTF(("svr4_delete_socket: %s [%p,%d,%d]\n",
400				 e->sock.sun_path, p, (int)e->dev, e->ino));
401			free(e, M_TEMP);
402			return;
403		}
404}
405
406static int
407svr4_soo_close(struct file *fp, struct thread *td)
408{
409        struct socket *so = fp->f_data;
410
411	/*	CHECKUNIT_DIAG(ENXIO);*/
412
413	svr4_delete_socket(td->td_proc, fp);
414	free(so->so_emuldata, M_TEMP);
415	return soo_close(fp, td);
416}
417