procfs 命令行参数解析

文章展示了Linux内核中用于从用户空间拷贝数据到内核空间的`copy_from_user`函数,以及字符串处理函数如`strsep`用于分割字符串。同时,提到了`proc`接口的写操作`hook_write`,它接收用户输入并进行参数解析,将字符串转换为整型。在模块初始化`proc_interface_init`中创建了`/proc/hook`目录及相关控制接口。
摘要由CSDN通过智能技术生成

返回值是没有拷贝成功的字节数,拷贝成功返回0。

static __always_inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long n)
{
        if (likely(check_copy_size(to, n, false)))
                n = _copy_from_user(to, from, n); 
        return n;
}

通过/proc/hook/hook_control传入的命令行参数,总共count个字节,其中count包含命令行中输入的回车符,在做参数解析时需要将最后一个字节替换成00。

# 0a -> 00

[  109.100142] Module init
[  126.618392] raw data: 0000000037ecc164: 76 66 73 5f 72 65 61 64 20 31 20 32 20 33 0a     vfs_read 1 2 3.

[  772.658773] Module init
[  775.256314] raw data: 000000007f19446e: 76 66 73 5f 72 65 61 64 20 31 20 32 20 33 00     vfs_read 1 2 3.

proc_writ返回值为写成功的字节数,如果返回值为0,会一直重试写?

ssize_t (*proc_write)(struct file *, const char __user *, size_t, loff_t *); 

Linux 内核中的字符串分割函数lib/string.c

#ifndef __HAVE_ARCH_STRSEP
/**
 * strsep - Split a string into tokens
 * @s: The string to be searched
 * @ct: The characters to search for
 *
 * strsep() updates @s to point after the token, ready for the next call.
 *
 * It returns empty tokens, too, behaving exactly like the libc function
 * of that name. In fact, it was stolen from glibc2 and de-fancy-fied.
 * Same semantics, slimmer shape. ;)
 */
char *strsep(char **s, const char *ct) 
{
        char *sbegin = *s;
        char *end;

        if (sbegin == NULL)
                return NULL;

        end = strpbrk(sbegin, ct); 
        if (end)
                *end++ = '\0';
        *s = end; 
        return sbegin;
}
EXPORT_SYMBOL(strsep);
#endif

内存拷贝函数memcpy

#ifndef __HAVE_ARCH_MEMCPY
/**
 * memcpy - Copy one area of memory to another
 * @dest: Where to copy to
 * @src: Where to copy from 
 * @count: The size of the area.
 *
 * You should not use this function to access IO space, use memcpy_toio()
 * or memcpy_fromio() instead.
 */
void *memcpy(void *dest, const void *src, size_t count)
{
        char *tmp = dest;
        const char *s = src; 

        while (count--)
                *tmp++ = *s++;
        return dest;
}
EXPORT_SYMBOL(memcpy);
#endif

Linux 内核将字符类型转换成整型/lib/string.c

/**
 * kstrtoint - convert a string to an int 
 * @s: The start of the string. The string must be null-terminated, and may also
 *  include a single newline before its terminating null. The first character
 *  may also be a plus sign or a minus sign.
 * @base: The number base to use. The maximum supported base is 16. If base is
 *  given as 0, then the base of the string is automatically detected with the 
 *  conventional semantics - If it begins with 0x the number will be parsed as a
 *  hexadecimal (case insensitive), if it otherwise begins with 0, it will be
 *  parsed as an octal number. Otherwise it will be parsed as a decimal.
 * @res: Where to write the result of the conversion on success.
 *
 * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error.
 * Preferred over simple_strtol(). Return code must be checked.
 */
int kstrtoint(const char *s, unsigned int base, int *res)
{
        long long tmp;
        int rv; 

        rv = kstrtoll(s, base, &tmp);
        if (rv < 0)
                return rv; 
        if (tmp != (int)tmp)
                return -ERANGE;
        *res = tmp;
        return 0;
}
EXPORT_SYMBOL(kstrtoint);

proc_interface.c

#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/uaccess.h>

#define MAX_PARAM 5
#define MAX_STRING_LEN 64

static struct proc_dir_entry *parent;
static struct proc_dir_entry *node;

// single_open的回调函数,实现了seq_operaions的show实例
static int show_proc_info(struct seq_file *p, void *v) 
{
        seq_printf(p, "%s\n", "Seq_printf test string");
        return 0;
}

static int hook_open(struct inode *inode, struct file *file)
{
        return single_open(file, show_proc_info, NULL);
}

static void get_param(char *buffer, int *argc, char (*argv)[MAX_STRING_LEN])
{
        unsigned int index = 0;
        char *token = NULL;

        while ((token = strsep(&buffer, " ")) != NULL) {
                memcpy(argv[index], token, MAX_STRING_LEN);
                index++;
        }   

        *argc = index;
}

static ssize_t hook_write(struct file *file, const char __user *buffer,
                size_t count, loff_t *pos)
{
        int number;
        char *tmp = NULL;
        int argc, ret = 0;
        char argv[MAX_PARAM][MAX_STRING_LEN];

        tmp = (char *)kmalloc(PAGE_SIZE, GFP_KERNEL);
        if (!tmp) {
                ret = -ENOMEM;
                goto out;
        }   

        memset((char*)tmp, 0, PAGE_SIZE);

        ret = copy_from_user(tmp, buffer, count);
        if (ret) {
                ret = -EFAULT;
                goto out;
        }

        // 用户态输入的buffer还包含回车符,替换成\0
        tmp[count -1] = '\0';

        print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS,
                    16, 1, tmp, count, true);

        get_param(tmp, &argc, argv);
        if (argc >= MAX_PARAM) {
                ret = EINVAL;
                goto out;
        }

        printk("argc -> %d argv[0] -> %s argv[1] -> %s\n",
                argc, argv[0], argv[1]);

        if (kstrtoint(argv[1], 0, &number)) {
                printk("Trans string to int failed\n");
                goto out;
        }

        printk("argc -> %d number -> %d\n", argc, number);

out:
        kfree(tmp);
        return count;
}

static int hook_release(struct inode *inode, struct file *file)
{
        return single_release(inode, file);
}

// .proc_read初始化为seq_read,如果自己实现,将会导致seq_printf失效。
static const struct proc_ops hook_fops = {
        .proc_open = hook_open,
        .proc_read = seq_read,
        .proc_write = hook_write,
        .proc_release = hook_release,
};

int proc_interface_init(void)
{
        parent = proc_mkdir("hook", NULL);
        if (IS_ERR(parent)) {
                printk("Create /proc/hook dir failed\n");
                return -1;
        }

        node = proc_create("hook_control", 0, parent, &hook_fops);
        if (IS_ERR(node)) {
                printk("Create /proc/hook/hook_control failed\n");
                goto out;
        }

        return 0;
out:
        remove_proc_entry("hook", NULL);
        return -1;
}

void proc_interface_exit(void)
{
        proc_remove(parent);
}

module.c

#include <linux/module.h>

extern int proc_interface_init(void);
extern void proc_interface_exit(void);

static int __init proc_fs_init(void)
{
        int ret = 0;

        printk("Module init\n");
        ret = proc_interface_init();    
        if (ret != 0) {
                return -1; 
        }   
        return 0;
}

static void __exit proc_fs_exit(void)
{
        proc_interface_exit();
        printk("Module exit\n");
}

module_init(proc_fs_init);
module_exit(proc_fs_exit);

MODULE_LICENSE("GPL");

Makefile

obj-m := proc_control.o

proc_control-y += module.o
proc_control-y += proc_interface.o

BASEINCLUDE ?= /lib/modules/`uname -r`/build

all:
        $(MAKE) -C $(BASEINCLUDE) M=$(PWD) modules;

clean:
        $(MAKE) -C $(BASEINCLUDE) M=$(PWD) clean;
        rm -f *.ko;

查看内核打印信息

[ 1562.028889] raw data: 00000000c77375f0: 76 66 73 5f 72 65 61 64 20 31 20 31 20 31 00     vfs_read 1 1 1.
[ 1562.028892] argc -> 4 argv[0] -> vfs_read argv[1] -> 1
[ 1562.028893] argc -> 4 number -> 1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值