936 lines · 21.7 KB · C
RawDownload
/* Helpers for initial module or kernel cmdline parsing   Copyright (C) 2001 Rusty Russell.     This program is free software; you can redistribute it and/or modify    it under the terms of the GNU General Public License as published by    the Free Software Foundation; either version 2 of the License, or    (at your option) any later version.     This program 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 General Public License for more details.     You should have received a copy of the GNU General Public License    along with this program; if not, write to the Free Software    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA*/#include <linux/kernel.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/module.h>#include <linux/device.h>#include <linux/err.h>#include <linux/slab.h>#include <linux/ctype.h> /* Protects all parameters, and incidentally kmalloced_param list. */static DEFINE_MUTEX(param_lock); /* This just allows us to keep track of which parameters are kmalloced. */struct kmalloced_param {	struct list_head list;	char val[];};static LIST_HEAD(kmalloced_params); static void *kmalloc_parameter(unsigned int size){	struct kmalloced_param *p; 	p = kmalloc(sizeof(*p) + size, GFP_KERNEL);	if (!p)		return NULL; 	list_add(&p->list, &kmalloced_params);	return p->val;} /* Does nothing if parameter wasn't kmalloced above. */static void maybe_kfree_parameter(void *param){	struct kmalloced_param *p; 	list_for_each_entry(p, &kmalloced_params, list) {		if (p->val == param) {			list_del(&p->list);			kfree(p);			break;		}	}} static char dash2underscore(char c){	if (c == '-')		return '_';	return c;} bool parameqn(const char *a, const char *b, size_t n){	size_t i; 	for (i = 0; i < n; i++) {		if (dash2underscore(a[i]) != dash2underscore(b[i]))			return false;	}	return true;} bool parameq(const char *a, const char *b){	return parameqn(a, b, strlen(a)+1);} static int parse_one(char *param,		     char *val,		     const char *doing,		     const struct kernel_param *params,		     unsigned num_params,		     s16 min_level,		     s16 max_level,		     int (*handle_unknown)(char *param, char *val,				     const char *doing)){	unsigned int i;	int err; 	/* Find parameter */	for (i = 0; i < num_params; i++) {		if (parameq(param, params[i].name)) {			if (params[i].level < min_level			    || params[i].level > max_level)				return 0;			/* No one handled NULL, so do it here. */			if (!val && params[i].ops->set != param_set_bool			    && params[i].ops->set != param_set_bint)				return -EINVAL;			pr_debug("handling %s with %p\n", param,				params[i].ops->set);			mutex_lock(&param_lock);			err = params[i].ops->set(val, &params[i]);			mutex_unlock(&param_lock);			return err;		}	} 	if (handle_unknown) {		pr_debug("doing %s: %s='%s'\n", doing, param, val);		return handle_unknown(param, val, doing);	} 	pr_debug("Unknown argument '%s'\n", param);	return -ENOENT;} /* You can use " around spaces, but can't escape ". *//* Hyphens and underscores equivalent in parameter names. */static char *next_arg(char *args, char **param, char **val){	unsigned int i, equals = 0;	int in_quote = 0, quoted = 0;	char *next; 	if (*args == '"') {		args++;		in_quote = 1;		quoted = 1;	} 	for (i = 0; args[i]; i++) {		if (isspace(args[i]) && !in_quote)			break;		if (equals == 0) {			if (args[i] == '=')				equals = i;		}		if (args[i] == '"')			in_quote = !in_quote;	} 	*param = args;	if (!equals)		*val = NULL;	else {		args[equals] = '\0';		*val = args + equals + 1; 		/* Don't include quotes in value. */		if (**val == '"') {			(*val)++;			if (args[i-1] == '"')				args[i-1] = '\0';		}		if (quoted && args[i-1] == '"')			args[i-1] = '\0';	} 	if (args[i]) {		args[i] = '\0';		next = args + i + 1;	} else		next = args + i; 	/* Chew up trailing spaces. */	return skip_spaces(next);} /* Args looks like "foo=bar,bar2 baz=fuz wiz". */int parse_args(const char *doing,	       char *args,	       const struct kernel_param *params,	       unsigned num,	       s16 min_level,	       s16 max_level,	       int (*unknown)(char *param, char *val, const char *doing)){	char *param, *val; 	/* Chew leading spaces */	args = skip_spaces(args); 	if (*args)		pr_debug("doing %s, parsing ARGS: '%s'\n", doing, args); 	while (*args) {		int ret;		int irq_was_disabled; 		args = next_arg(args, &param, &val);		irq_was_disabled = irqs_disabled();		ret = parse_one(param, val, doing, params, num,				min_level, max_level, unknown);		if (irq_was_disabled && !irqs_disabled())			pr_warn("%s: option '%s' enabled irq's!\n",				doing, param); 		switch (ret) {		case -ENOENT:			pr_err("%s: Unknown parameter `%s'\n", doing, param);			return ret;		case -ENOSPC:			pr_err("%s: `%s' too large for parameter `%s'\n",			       doing, val ?: "", param);			return ret;		case 0:			break;		default:			pr_err("%s: `%s' invalid for parameter `%s'\n",			       doing, val ?: "", param);			return ret;		}	} 	/* All parsed OK. */	return 0;} /* Lazy bastard, eh? */#define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn)      	\	int param_set_##name(const char *val, const struct kernel_param *kp) \	{								\		tmptype l;						\		int ret;						\									\		ret = strtolfn(val, 0, &l);				\		if (ret < 0 || ((type)l != l))				\			return ret < 0 ? ret : -EINVAL;			\		*((type *)kp->arg) = l;					\		return 0;						\	}								\	int param_get_##name(char *buffer, const struct kernel_param *kp) \	{								\		return sprintf(buffer, format, *((type *)kp->arg));	\	}								\	struct kernel_param_ops param_ops_##name = {			\		.set = param_set_##name,				\		.get = param_get_##name,				\	};								\	EXPORT_SYMBOL(param_set_##name);				\	EXPORT_SYMBOL(param_get_##name);				\	EXPORT_SYMBOL(param_ops_##name)  STANDARD_PARAM_DEF(byte, unsigned char, "%c", unsigned long, strict_strtoul);STANDARD_PARAM_DEF(short, short, "%hi", long, strict_strtol);STANDARD_PARAM_DEF(ushort, unsigned short, "%hu", unsigned long, strict_strtoul);STANDARD_PARAM_DEF(int, int, "%i", long, strict_strtol);STANDARD_PARAM_DEF(uint, unsigned int, "%u", unsigned long, strict_strtoul);STANDARD_PARAM_DEF(long, long, "%li", long, strict_strtol);STANDARD_PARAM_DEF(ulong, unsigned long, "%lu", unsigned long, strict_strtoul); int param_set_charp(const char *val, const struct kernel_param *kp){	if (strlen(val) > 1024) {		pr_err("%s: string parameter too long\n", kp->name);		return -ENOSPC;	} 	maybe_kfree_parameter(*(char **)kp->arg); 	/* This is a hack.  We can't kmalloc in early boot, and we	 * don't need to; this mangled commandline is preserved. */	if (slab_is_available()) {		*(char **)kp->arg = kmalloc_parameter(strlen(val)+1);		if (!*(char **)kp->arg)			return -ENOMEM;		strcpy(*(char **)kp->arg, val);	} else		*(const char **)kp->arg = val; 	return 0;}EXPORT_SYMBOL(param_set_charp); int param_get_charp(char *buffer, const struct kernel_param *kp){	return sprintf(buffer, "%s", *((char **)kp->arg));}EXPORT_SYMBOL(param_get_charp); static void param_free_charp(void *arg){	maybe_kfree_parameter(*((char **)arg));} struct kernel_param_ops param_ops_charp = {	.set = param_set_charp,	.get = param_get_charp,	.free = param_free_charp,};EXPORT_SYMBOL(param_ops_charp); /* Actually could be a bool or an int, for historical reasons. */int param_set_bool(const char *val, const struct kernel_param *kp){	/* No equals means "set"... */	if (!val) val = "1"; 	/* One of =[yYnN01] */	return strtobool(val, kp->arg);}EXPORT_SYMBOL(param_set_bool); int param_get_bool(char *buffer, const struct kernel_param *kp){	/* Y and N chosen as being relatively non-coder friendly */	return sprintf(buffer, "%c", *(bool *)kp->arg ? 'Y' : 'N');}EXPORT_SYMBOL(param_get_bool); struct kernel_param_ops param_ops_bool = {	.set = param_set_bool,	.get = param_get_bool,};EXPORT_SYMBOL(param_ops_bool); /* This one must be bool. */int param_set_invbool(const char *val, const struct kernel_param *kp){	int ret;	bool boolval;	struct kernel_param dummy; 	dummy.arg = &boolval;	ret = param_set_bool(val, &dummy);	if (ret == 0)		*(bool *)kp->arg = !boolval;	return ret;}EXPORT_SYMBOL(param_set_invbool); int param_get_invbool(char *buffer, const struct kernel_param *kp){	return sprintf(buffer, "%c", (*(bool *)kp->arg) ? 'N' : 'Y');}EXPORT_SYMBOL(param_get_invbool); struct kernel_param_ops param_ops_invbool = {	.set = param_set_invbool,	.get = param_get_invbool,};EXPORT_SYMBOL(param_ops_invbool); int param_set_bint(const char *val, const struct kernel_param *kp){	struct kernel_param boolkp;	bool v;	int ret; 	/* Match bool exactly, by re-using it. */	boolkp = *kp;	boolkp.arg = &v; 	ret = param_set_bool(val, &boolkp);	if (ret == 0)		*(int *)kp->arg = v;	return ret;}EXPORT_SYMBOL(param_set_bint); struct kernel_param_ops param_ops_bint = {	.set = param_set_bint,	.get = param_get_int,};EXPORT_SYMBOL(param_ops_bint); /* We break the rule and mangle the string. */static int param_array(const char *name,		       const char *val,		       unsigned int min, unsigned int max,		       void *elem, int elemsize,		       int (*set)(const char *, const struct kernel_param *kp),		       s16 level,		       unsigned int *num){	int ret;	struct kernel_param kp;	char save; 	/* Get the name right for errors. */	kp.name = name;	kp.arg = elem;	kp.level = level; 	*num = 0;	/* We expect a comma-separated list of values. */	do {		int len; 		if (*num == max) {			pr_err("%s: can only take %i arguments\n", name, max);			return -EINVAL;		}		len = strcspn(val, ","); 		/* nul-terminate and parse */		save = val[len];		((char *)val)[len] = '\0';		BUG_ON(!mutex_is_locked(&param_lock));		ret = set(val, &kp); 		if (ret != 0)			return ret;		kp.arg += elemsize;		val += len+1;		(*num)++;	} while (save == ','); 	if (*num < min) {		pr_err("%s: needs at least %i arguments\n", name, min);		return -EINVAL;	}	return 0;} static int param_array_set(const char *val, const struct kernel_param *kp){	const struct kparam_array *arr = kp->arr;	unsigned int temp_num; 	return param_array(kp->name, val, 1, arr->max, arr->elem,			   arr->elemsize, arr->ops->set, kp->level,			   arr->num ?: &temp_num);} static int param_array_get(char *buffer, const struct kernel_param *kp){	int i, off, ret;	const struct kparam_array *arr = kp->arr;	struct kernel_param p; 	p = *kp;	for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) {		if (i)			buffer[off++] = ',';		p.arg = arr->elem + arr->elemsize * i;		BUG_ON(!mutex_is_locked(&param_lock));		ret = arr->ops->get(buffer + off, &p);		if (ret < 0)			return ret;		off += ret;	}	buffer[off] = '\0';	return off;} static void param_array_free(void *arg){	unsigned int i;	const struct kparam_array *arr = arg; 	if (arr->ops->free)		for (i = 0; i < (arr->num ? *arr->num : arr->max); i++)			arr->ops->free(arr->elem + arr->elemsize * i);} struct kernel_param_ops param_array_ops = {	.set = param_array_set,	.get = param_array_get,	.free = param_array_free,};EXPORT_SYMBOL(param_array_ops); int param_set_copystring(const char *val, const struct kernel_param *kp){	const struct kparam_string *kps = kp->str; 	if (strlen(val)+1 > kps->maxlen) {		pr_err("%s: string doesn't fit in %u chars.\n",		       kp->name, kps->maxlen-1);		return -ENOSPC;	}	strcpy(kps->string, val);	return 0;}EXPORT_SYMBOL(param_set_copystring); int param_get_string(char *buffer, const struct kernel_param *kp){	const struct kparam_string *kps = kp->str;	return strlcpy(buffer, kps->string, kps->maxlen);}EXPORT_SYMBOL(param_get_string); struct kernel_param_ops param_ops_string = {	.set = param_set_copystring,	.get = param_get_string,};EXPORT_SYMBOL(param_ops_string);

Lines 1–500 of 936.