divert_procfs.c 8.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
/* $Id: divert_procfs.c,v 1.11.6.2 2001/09/23 22:24:36 kai Exp $
 *
 * Filesystem handling for the diversion supplementary services.
 *
 * Copyright 1998       by Werner Cornelius (werner@isdn4linux.de)
 *
 * This software may be used and distributed according to the terms
 * of the GNU General Public License, incorporated herein by reference.
 *
 */

#include <linux/module.h>
#include <linux/poll.h>
14
#include <linux/slab.h>
L
Linus Torvalds 已提交
15 16 17 18 19
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#else
#include <linux/fs.h>
#endif
20
#include <linux/sched.h>
L
Linus Torvalds 已提交
21
#include <linux/isdnif.h>
22
#include <net/net_namespace.h>
23
#include <linux/mutex.h>
L
Linus Torvalds 已提交
24 25 26 27 28 29 30
#include "isdn_divert.h"


/*********************************/
/* Variables for interface queue */
/*********************************/
ulong if_used = 0;		/* number of interface users */
31
static DEFINE_MUTEX(isdn_divert_mutex);
L
Linus Torvalds 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
static struct divert_info *divert_info_head = NULL;	/* head of queue */
static struct divert_info *divert_info_tail = NULL;	/* pointer to last entry */
static DEFINE_SPINLOCK(divert_info_lock);/* lock for queue */
static wait_queue_head_t rd_queue;

/*********************************/
/* put an info buffer into queue */
/*********************************/
void
put_info_buffer(char *cp)
{
	struct divert_info *ib;
	unsigned long flags;

	if (if_used <= 0)
		return;
	if (!cp)
		return;
	if (!*cp)
		return;
52
	if (!(ib = kmalloc(sizeof(struct divert_info) + strlen(cp), GFP_ATOMIC)))
53
		return;	/* no memory */
L
Linus Torvalds 已提交
54 55
	strcpy(ib->info_start, cp);	/* set output string */
	ib->next = NULL;
56
	spin_lock_irqsave(&divert_info_lock, flags);
L
Linus Torvalds 已提交
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
	ib->usage_cnt = if_used;
	if (!divert_info_head)
		divert_info_head = ib;	/* new head */
	else
		divert_info_tail->next = ib;	/* follows existing messages */
	divert_info_tail = ib;	/* new tail */

	/* delete old entrys */
	while (divert_info_head->next) {
		if ((divert_info_head->usage_cnt <= 0) &&
		    (divert_info_head->next->usage_cnt <= 0)) {
			ib = divert_info_head;
			divert_info_head = divert_info_head->next;
			kfree(ib);
		} else
			break;
	}			/* divert_info_head->next */
74
	spin_unlock_irqrestore(&divert_info_lock, flags);
L
Linus Torvalds 已提交
75 76 77
	wake_up_interruptible(&(rd_queue));
}				/* put_info_buffer */

78 79
#ifdef CONFIG_PROC_FS

L
Linus Torvalds 已提交
80 81 82 83
/**********************************/
/* deflection device read routine */
/**********************************/
static ssize_t
84
isdn_divert_read(struct file *file, char __user *buf, size_t count, loff_t *off)
L
Linus Torvalds 已提交
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
{
	struct divert_info *inf;
	int len;

	if (!*((struct divert_info **) file->private_data)) {
		if (file->f_flags & O_NONBLOCK)
			return -EAGAIN;
		interruptible_sleep_on(&(rd_queue));
	}
	if (!(inf = *((struct divert_info **) file->private_data)))
		return (0);

	inf->usage_cnt--;	/* new usage count */
	file->private_data = &inf->next;	/* next structure */
	if ((len = strlen(inf->info_start)) <= count) {
		if (copy_to_user(buf, inf->info_start, len))
			return -EFAULT;
		*off += len;
		return (len);
	}
	return (0);
}				/* isdn_divert_read */

/**********************************/
/* deflection device write routine */
/**********************************/
static ssize_t
112
isdn_divert_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
L
Linus Torvalds 已提交
113 114 115 116 117 118 119 120 121
{
	return (-ENODEV);
}				/* isdn_divert_write */


/***************************************/
/* select routines for various kernels */
/***************************************/
static unsigned int
122
isdn_divert_poll(struct file *file, poll_table *wait)
L
Linus Torvalds 已提交
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
{
	unsigned int mask = 0;

	poll_wait(file, &(rd_queue), wait);
	/* mask = POLLOUT | POLLWRNORM; */
	if (*((struct divert_info **) file->private_data)) {
		mask |= POLLIN | POLLRDNORM;
	}
	return mask;
}				/* isdn_divert_poll */

/****************/
/* Open routine */
/****************/
static int
isdn_divert_open(struct inode *ino, struct file *filep)
{
	unsigned long flags;

142 143
	spin_lock_irqsave(&divert_info_lock, flags);
	if_used++;
L
Linus Torvalds 已提交
144 145 146 147
	if (divert_info_head)
		filep->private_data = &(divert_info_tail->next);
	else
		filep->private_data = &divert_info_head;
148
	spin_unlock_irqrestore(&divert_info_lock, flags);
L
Linus Torvalds 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161
	/*  start_divert(); */
	return nonseekable_open(ino, filep);
}				/* isdn_divert_open */

/*******************/
/* close routine   */
/*******************/
static int
isdn_divert_close(struct inode *ino, struct file *filep)
{
	struct divert_info *inf;
	unsigned long flags;

162
	spin_lock_irqsave(&divert_info_lock, flags);
L
Linus Torvalds 已提交
163 164 165 166 167 168 169 170 171 172 173 174
	if_used--;
	inf = *((struct divert_info **) filep->private_data);
	while (inf) {
		inf->usage_cnt--;
		inf = inf->next;
	}
	if (if_used <= 0)
		while (divert_info_head) {
			inf = divert_info_head;
			divert_info_head = divert_info_head->next;
			kfree(inf);
		}
175
	spin_unlock_irqrestore(&divert_info_lock, flags);
L
Linus Torvalds 已提交
176 177 178 179 180 181
	return (0);
}				/* isdn_divert_close */

/*********/
/* IOCTL */
/*********/
182
static int isdn_divert_ioctl_unlocked(struct file *file, uint cmd, ulong arg)
L
Linus Torvalds 已提交
183 184 185 186 187 188 189 190 191 192 193
{
	divert_ioctl dioctl;
	int i;
	unsigned long flags;
	divert_rule *rulep;
	char *cp;

	if (copy_from_user(&dioctl, (void __user *) arg, sizeof(dioctl)))
		return -EFAULT;

	switch (cmd) {
194 195 196
	case IIOCGETVER:
		dioctl.drv_version = DIVERT_IIOC_VERSION;	/* set version */
		break;
L
Linus Torvalds 已提交
197

198 199 200 201
	case IIOCGETDRV:
		if ((dioctl.getid.drvid = divert_if.name_to_drv(dioctl.getid.drvnam)) < 0)
			return (-EINVAL);
		break;
L
Linus Torvalds 已提交
202

203 204 205 206 207 208 209 210
	case IIOCGETNAM:
		cp = divert_if.drv_to_name(dioctl.getid.drvid);
		if (!cp)
			return (-EINVAL);
		if (!*cp)
			return (-EINVAL);
		strcpy(dioctl.getid.drvnam, cp);
		break;
L
Linus Torvalds 已提交
211

212 213 214 215 216
	case IIOCGETRULE:
		if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx)))
			return (-EINVAL);
		dioctl.getsetrule.rule = *rulep;	/* copy data */
		break;
L
Linus Torvalds 已提交
217

218 219
	case IIOCMODRULE:
		if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx)))
L
Linus Torvalds 已提交
220
			return (-EINVAL);
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
		spin_lock_irqsave(&divert_lock, flags);
		*rulep = dioctl.getsetrule.rule;	/* copy data */
		spin_unlock_irqrestore(&divert_lock, flags);
		return (0);	/* no copy required */
		break;

	case IIOCINSRULE:
		return (insertrule(dioctl.getsetrule.ruleidx, &dioctl.getsetrule.rule));
		break;

	case IIOCDELRULE:
		return (deleterule(dioctl.getsetrule.ruleidx));
		break;

	case IIOCDODFACT:
		return (deflect_extern_action(dioctl.fwd_ctrl.subcmd,
					      dioctl.fwd_ctrl.callid,
					      dioctl.fwd_ctrl.to_nr));

	case IIOCDOCFACT:
	case IIOCDOCFDIS:
	case IIOCDOCFINT:
		if (!divert_if.drv_to_name(dioctl.cf_ctrl.drvid))
			return (-EINVAL);	/* invalid driver */
		if (strnlen(dioctl.cf_ctrl.msn, sizeof(dioctl.cf_ctrl.msn)) ==
		    sizeof(dioctl.cf_ctrl.msn))
			return -EINVAL;
		if (strnlen(dioctl.cf_ctrl.fwd_nr, sizeof(dioctl.cf_ctrl.fwd_nr)) ==
		    sizeof(dioctl.cf_ctrl.fwd_nr))
			return -EINVAL;
		if ((i = cf_command(dioctl.cf_ctrl.drvid,
				    (cmd == IIOCDOCFACT) ? 1 : (cmd == IIOCDOCFDIS) ? 0 : 2,
				    dioctl.cf_ctrl.cfproc,
				    dioctl.cf_ctrl.msn,
				    dioctl.cf_ctrl.service,
				    dioctl.cf_ctrl.fwd_nr,
				    &dioctl.cf_ctrl.procid)))
			return (i);
		break;

	default:
		return (-EINVAL);
L
Linus Torvalds 已提交
263 264 265 266
	}			/* switch cmd */
	return copy_to_user((void __user *)arg, &dioctl, sizeof(dioctl)) ? -EFAULT : 0;
}				/* isdn_divert_ioctl */

267 268 269 270
static long isdn_divert_ioctl(struct file *file, uint cmd, ulong arg)
{
	long ret;

271
	mutex_lock(&isdn_divert_mutex);
272
	ret = isdn_divert_ioctl_unlocked(file, cmd, arg);
273
	mutex_unlock(&isdn_divert_mutex);
274 275 276 277

	return ret;
}

278
static const struct file_operations isdn_fops =
L
Linus Torvalds 已提交
279 280 281 282 283 284
{
	.owner          = THIS_MODULE,
	.llseek         = no_llseek,
	.read           = isdn_divert_read,
	.write          = isdn_divert_write,
	.poll           = isdn_divert_poll,
285
	.unlocked_ioctl = isdn_divert_ioctl,
L
Linus Torvalds 已提交
286
	.open           = isdn_divert_open,
287
	.release        = isdn_divert_close,
L
Linus Torvalds 已提交
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
};

/****************************/
/* isdn subdir in /proc/net */
/****************************/
static struct proc_dir_entry *isdn_proc_entry = NULL;
static struct proc_dir_entry *isdn_divert_entry = NULL;
#endif	/* CONFIG_PROC_FS */

/***************************************************************************/
/* divert_dev_init must be called before the proc filesystem may be used   */
/***************************************************************************/
int
divert_dev_init(void)
{

	init_waitqueue_head(&rd_queue);

#ifdef CONFIG_PROC_FS
307
	isdn_proc_entry = proc_mkdir("isdn", init_net.proc_net);
L
Linus Torvalds 已提交
308 309
	if (!isdn_proc_entry)
		return (-1);
310 311
	isdn_divert_entry = proc_create("divert", S_IFREG | S_IRUGO,
					isdn_proc_entry, &isdn_fops);
L
Linus Torvalds 已提交
312
	if (!isdn_divert_entry) {
313
		remove_proc_entry("isdn", init_net.proc_net);
L
Linus Torvalds 已提交
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
		return (-1);
	}
#endif	/* CONFIG_PROC_FS */

	return (0);
}				/* divert_dev_init */

/***************************************************************************/
/* divert_dev_deinit must be called before leaving isdn when included as   */
/* a module.                                                               */
/***************************************************************************/
int
divert_dev_deinit(void)
{

#ifdef CONFIG_PROC_FS
	remove_proc_entry("divert", isdn_proc_entry);
331
	remove_proc_entry("isdn", init_net.proc_net);
L
Linus Torvalds 已提交
332 333 334 335
#endif	/* CONFIG_PROC_FS */

	return (0);
}				/* divert_dev_deinit */