add procfs
This commit is contained in:
parent
1d78d11567
commit
3e91a6836a
|
@ -176,6 +176,9 @@ if RT_USING_SMART
|
|||
bool "Using Pseudo-Teletype Filesystem (UNIX98 PTY)"
|
||||
depends on RT_USING_DFS_DEVFS
|
||||
default y
|
||||
config RT_USING_DFS_PROCFS
|
||||
bool "Enable proc file system"
|
||||
default n
|
||||
endif
|
||||
|
||||
config RT_USING_DFS_CROMFS
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
# 进程文件系统 (procfs)
|
||||
|
||||
## 数据结构
|
||||
|
||||
```c
|
||||
struct proc_dentry
|
||||
{
|
||||
rt_uint32_t mode;
|
||||
rt_atomic_t ref_count;
|
||||
|
||||
struct proc_dentry *parent;
|
||||
struct dfs_vfs_node node;
|
||||
|
||||
const struct dfs_file_ops *fops;
|
||||
const struct proc_ops *ops;
|
||||
|
||||
char *name;
|
||||
void *data;
|
||||
};
|
||||
```
|
||||
|
||||
```log
|
||||
root { mode: S_IFDIR, ref_count: 1, parent: root, name: /, child->next: file1->node }
|
||||
|
|
||||
|—— file1 { mode: S_IFREG, ref_count: 1, parent: root, name: file1, node->next: link1->node }
|
||||
|—— link1 { mode: S_IFLNK, ref_count: 1, parent: root, name: link1, data: fullpath, node->next: dir1->node }
|
||||
|—— dir1 { mode: S_IFDIR, ref_count: 1, parent: root, name: dir1, node->next: file3->node, child->next: file2->node }
|
||||
| |
|
||||
| |—— dir2 { mode: S_IFDIR, ref_count: 1, parent: dir1, name: dir2, node->next: link2->node }
|
||||
| |—— link2 { mode: S_IFLNK, ref_count: 1, parent: dir1, name: link2, data: fullpath, node->next: file2->node }
|
||||
| |—— file2 { mode: S_IFREG, ref_count: 1, parent: dir1, name: file2 }
|
||||
|
|
||||
|—— file3 { mode: S_IFREG, ref_count: 1, parent: root, name: file3 }
|
||||
```
|
||||
|
||||
## API 介绍
|
||||
|
||||
```c
|
||||
struct proc_dentry *dfs_proc_find(const char *name);
|
||||
|
||||
struct proc_dentry *proc_mkdir_data(const char *name, mode_t mode, struct proc_dentry *parent,
|
||||
const struct dfs_file_ops *fops, void *data);
|
||||
struct proc_dentry *proc_mkdir_mode(const char *name, mode_t mode, struct proc_dentry *parent);
|
||||
struct proc_dentry *proc_mkdir(const char *name, struct proc_dentry *parent);
|
||||
|
||||
struct proc_dentry *proc_create_data(const char *name, mode_t mode, struct proc_dentry *parent,
|
||||
const struct dfs_file_ops *fops, void *data);
|
||||
|
||||
struct proc_dentry *proc_symlink(const char *name, struct proc_dentry *parent, const char *dest);
|
||||
|
||||
struct proc_dentry *proc_acquire(struct proc_dentry *dentry);
|
||||
void proc_release(struct proc_dentry *dentry);
|
||||
|
||||
void proc_remove(struct proc_dentry *dentry);
|
||||
```
|
||||
|
||||
- dfs_proc_find
|
||||
|
||||
查找指定节点,并返回节点数据指针
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ---- | ---------------------------------------------------- |
|
||||
| name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2” |
|
||||
|
||||
- proc_mkdir_data
|
||||
|
||||
创建一个目录,并返回节点数据指针
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ------ | ------------------------------------------------------------ |
|
||||
| name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2”<br />从 parent 起始的完整路径 |
|
||||
| mode | 权限配置 |
|
||||
| parent | 指定创建目录的起始节点 |
|
||||
| fops | 文件操作接口配置 |
|
||||
| data | 私有数据 |
|
||||
|
||||
- proc_mkdir_mode
|
||||
|
||||
创建一个目录,并返回节点数据指针
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ------ | ------------------------------------------------------------ |
|
||||
| name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2”<br />从 parent 起始的完整路径 |
|
||||
| mode | 权限配置 |
|
||||
| parent | 指定创建目录的起始节点 |
|
||||
|
||||
- proc_mkdir
|
||||
|
||||
创建一个目录,并返回节点数据指针
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ---- | ------------------------------------------------------------ |
|
||||
| name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2”<br />从 parent 起始的完整路径 |
|
||||
| mode | 权限配置 |
|
||||
|
||||
- proc_create_data
|
||||
|
||||
创建一个文件,并返回节点数据指针
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ------ | ------------------------------------------------------------ |
|
||||
| name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2”<br />从 parent 起始的完整路径 |
|
||||
| mode | 权限配置 |
|
||||
| parent | 指定创建文件的起始节点 |
|
||||
| fops | 文件操作接口配置 |
|
||||
| data | 私有数据 |
|
||||
|
||||
- proc_symlink
|
||||
|
||||
创建一个符号链接,并返回节点数据指针
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ------ | ------------------------------------------------------------ |
|
||||
| name | 从 procfs 的 root 起始的完整路径,比如 “/dir1/file2”<br />从 parent 起始的完整路径 |
|
||||
| parent | 指定创建文件的起始节点 |
|
||||
| dest | 链接的目标文件完整路径 |
|
||||
|
||||
- proc_acquire
|
||||
|
||||
引用一个节点,并返回节点数据指针
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ------ | -------------- |
|
||||
| dentry | 需要引用的节点 |
|
||||
|
||||
- proc_release
|
||||
|
||||
释放一个节点
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ------ | -------------- |
|
||||
| dentry | 需要释放的节点 |
|
||||
|
||||
- proc_remove
|
||||
|
||||
删除一个节点包含子节点
|
||||
|
||||
| 入参 | 说明 |
|
||||
| ------ | -------------- |
|
||||
| dentry | 需要删除的节点 |
|
||||
|
||||
## msh 调试命令
|
||||
|
||||
- proc_dump
|
||||
|
||||
遍历打印指定节点含子节点的信息(名称、引用计数),比如 `proc_dump /dir1` 或者 `proc_dump`
|
||||
|
||||
- proc_remove
|
||||
|
||||
删除指定节点含子节点,比如 `proc_remove /dir1` 或者 `proc_remove /file3`
|
||||
|
||||
- proc_symlink
|
||||
|
||||
创建一个符号链接,`proc_symlink /link3 /mnt`
|
||||
|
||||
- proc_echo
|
||||
|
||||
创建一个空文件,`proc_echo /file4`
|
||||
|
||||
- proc_mkdir
|
||||
|
||||
创建一个空目录,`proc_mkdir /dir3`
|
||||
|
||||
- proc_pid
|
||||
|
||||
创建一个 pid 目录,`proc_pid /101`
|
|
@ -0,0 +1,11 @@
|
|||
# RT-Thread building script for component
|
||||
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
CPPPATH = [cwd]
|
||||
|
||||
group = DefineGroup('Filesystem', src, depend = ['RT_USING_DFS', 'RT_USING_DFS_PROCFS'], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
|
@ -0,0 +1,733 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
/*
|
||||
* This is the root in the proc tree..
|
||||
*/
|
||||
static struct proc_dentry _proc_root = {
|
||||
.mode = S_IFDIR | (S_IRUSR | S_IRGRP | S_IROTH) | (S_IXUSR | S_IXGRP | S_IXOTH),
|
||||
.ref_count = 1,
|
||||
|
||||
.parent = &_proc_root,
|
||||
.node.sibling = RT_LIST_OBJECT_INIT(_proc_root.node.sibling),
|
||||
.node.subnode = RT_LIST_OBJECT_INIT(_proc_root.node.subnode),
|
||||
|
||||
.fops = RT_NULL,
|
||||
|
||||
.name = "/",
|
||||
.data = RT_NULL,
|
||||
};
|
||||
|
||||
static int _proc_find(struct proc_dentry **parent, const char *name)
|
||||
{
|
||||
struct proc_dentry *dentry = RT_NULL, *tmp;
|
||||
|
||||
dfs_vfs_for_each_subnode(dentry, tmp, (*parent), node)
|
||||
{
|
||||
if (dentry == RT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (rt_strcmp(dentry->name, name) == 0)
|
||||
{
|
||||
*parent = dentry;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int proc_find(struct proc_dentry **parent, const char **name, rt_bool_t force_lookup)
|
||||
{
|
||||
int ret = 0;
|
||||
char *tmp = RT_NULL;
|
||||
|
||||
if (!(*parent))
|
||||
{
|
||||
*parent = &_proc_root;
|
||||
}
|
||||
|
||||
tmp = rt_strdup(*name);
|
||||
if (tmp)
|
||||
{
|
||||
char *begin = tmp, *end = RT_NULL;
|
||||
if (*begin == '/')
|
||||
{
|
||||
begin++;
|
||||
if (*begin == '\0')
|
||||
{
|
||||
rt_free(tmp);
|
||||
*parent = proc_acquire(*parent);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
end = rt_strstr(begin, "/");
|
||||
if (end)
|
||||
{
|
||||
*end = '\0';
|
||||
ret = _proc_find(parent, begin);
|
||||
if (ret < 0 || !S_ISDIR((*parent)->mode))
|
||||
{
|
||||
*parent = RT_NULL;
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
begin = end + 1;
|
||||
}
|
||||
else if (force_lookup)
|
||||
{
|
||||
ret = _proc_find(parent, begin);
|
||||
if (ret < 0)
|
||||
{
|
||||
if ((*parent)->ops && (*parent)->ops->lookup)
|
||||
{
|
||||
*parent = (*parent)->ops->lookup(*parent, begin);
|
||||
if (*parent == RT_NULL)
|
||||
{
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*parent = RT_NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*parent = proc_acquire(*parent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
*parent = proc_acquire(*parent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*name = *name + (begin - tmp);
|
||||
|
||||
rt_free(tmp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *single_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
return NULL + (*index == 0);
|
||||
}
|
||||
|
||||
static void *single_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
++*index;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void single_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static int proc_open(struct dfs_file *file)
|
||||
{
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
if (entry->single_show)
|
||||
{
|
||||
struct dfs_seq_ops *seq_ops = (struct dfs_seq_ops *)rt_calloc(1, sizeof(struct dfs_seq_ops));
|
||||
if (seq_ops)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
seq_ops->start = single_start;
|
||||
seq_ops->next = single_next;
|
||||
seq_ops->stop = single_stop;
|
||||
seq_ops->show = entry->single_show;
|
||||
|
||||
ret = dfs_seq_open(file, seq_ops);
|
||||
if (ret != 0)
|
||||
{
|
||||
rt_free(seq_ops);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return dfs_seq_open(file, entry->seq_ops);
|
||||
}
|
||||
|
||||
static int proc_close(struct dfs_file *file)
|
||||
{
|
||||
struct dfs_seq_file *seq = file->data;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
if (seq && entry->single_show && seq->ops)
|
||||
{
|
||||
rt_free((void *)seq->ops);
|
||||
seq->ops = RT_NULL;
|
||||
}
|
||||
|
||||
return dfs_seq_release(file);
|
||||
}
|
||||
|
||||
static const struct dfs_file_ops proc_file_ops = {
|
||||
.open = proc_open,
|
||||
.read = dfs_seq_read,
|
||||
.lseek = dfs_seq_lseek,
|
||||
.close = proc_close,
|
||||
};
|
||||
|
||||
static struct proc_dentry *proc_create(struct proc_dentry **parent, const char *name, mode_t mode)
|
||||
{
|
||||
int ret = 0;
|
||||
struct proc_dentry *dentry = RT_NULL;
|
||||
|
||||
ret = proc_find(parent, &name, 0);
|
||||
if (ret >= 0)
|
||||
{
|
||||
dentry = *parent;
|
||||
ret = proc_find(&dentry, &name, 1);
|
||||
if (ret < 0)
|
||||
{
|
||||
dentry = rt_calloc(1, sizeof(struct proc_dentry));
|
||||
if (dentry)
|
||||
{
|
||||
dentry->mode = mode;
|
||||
dentry->ref_count = 1;
|
||||
dentry->name = rt_strdup(name);
|
||||
dfs_vfs_init_node(&dentry->node);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
proc_release(dentry);
|
||||
dentry = RT_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The dentry reference count is incremented by one
|
||||
*
|
||||
* @param dentry
|
||||
*
|
||||
* @return dentry
|
||||
*/
|
||||
struct proc_dentry *proc_acquire(struct proc_dentry *dentry)
|
||||
{
|
||||
if (dentry)
|
||||
{
|
||||
dentry->ref_count += 1;
|
||||
}
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The dentry reference count is minus one, or release
|
||||
*
|
||||
* @param dentry
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void proc_release(struct proc_dentry *dentry)
|
||||
{
|
||||
if (dentry)
|
||||
{
|
||||
if (dentry->ref_count == 1)
|
||||
{
|
||||
if (dentry->name)
|
||||
{
|
||||
rt_free(dentry->name);
|
||||
}
|
||||
|
||||
if (S_ISLNK(dentry->mode) && dentry->data)
|
||||
{
|
||||
rt_free(dentry->data);
|
||||
}
|
||||
|
||||
rt_free(dentry);
|
||||
}
|
||||
else
|
||||
{
|
||||
dentry->ref_count -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct proc_dentry *proc_register(struct proc_dentry *parent, struct proc_dentry *child)
|
||||
{
|
||||
child->parent = parent;
|
||||
dfs_vfs_append_node(&parent->node, &child->node);
|
||||
child->ref_count += 1;
|
||||
child->pid = parent->pid;
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a dir
|
||||
*
|
||||
* @param name fullpath based on _proc_root or parent
|
||||
* @param mode permission configuration
|
||||
* @param parent can be empty
|
||||
* @param fops
|
||||
* @param data
|
||||
*
|
||||
* @return dentry
|
||||
*/
|
||||
struct proc_dentry *proc_mkdir_data(const char *name, mode_t mode, struct proc_dentry *parent,
|
||||
const struct dfs_file_ops *fops, void *data)
|
||||
{
|
||||
struct proc_dentry *dentry, *_parent = parent;
|
||||
|
||||
if (mode == 0)
|
||||
mode = (S_IRUSR | S_IRGRP | S_IROTH) | (S_IXUSR | S_IXGRP | S_IXOTH);
|
||||
|
||||
dentry = proc_create(&_parent, name, S_IFDIR | mode);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->fops = fops;
|
||||
dentry->data = data;
|
||||
|
||||
dentry = proc_register(_parent, dentry);
|
||||
}
|
||||
proc_release(_parent);
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a dir
|
||||
*
|
||||
* @param name fullpath based on _proc_root or parent
|
||||
* @param mode permission configuration
|
||||
* @param parent can be empty
|
||||
*
|
||||
* @return dentry
|
||||
*/
|
||||
struct proc_dentry *proc_mkdir_mode(const char *name, mode_t mode, struct proc_dentry *parent)
|
||||
{
|
||||
return proc_mkdir_data(name, mode, parent, NULL, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a dir
|
||||
*
|
||||
* @param name fullpath based on _proc_root or parent
|
||||
* @param parent can be empty
|
||||
*
|
||||
* @return dentry
|
||||
*/
|
||||
struct proc_dentry *proc_mkdir(const char *name, struct proc_dentry *parent)
|
||||
{
|
||||
return proc_mkdir_data(name, 0, parent, NULL, NULL);
|
||||
}
|
||||
|
||||
static struct proc_dentry *proc_create_reg(const char *name, mode_t mode, struct proc_dentry **parent)
|
||||
{
|
||||
struct proc_dentry *dentry = RT_NULL;
|
||||
|
||||
if ((mode & S_IFMT) == 0)
|
||||
mode |= S_IFREG;
|
||||
if ((mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
|
||||
mode |= S_IRUSR | S_IRGRP | S_IROTH;
|
||||
|
||||
if (!S_ISREG(mode))
|
||||
{
|
||||
*parent = RT_NULL;
|
||||
return dentry;
|
||||
}
|
||||
|
||||
return proc_create(parent, name, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a file
|
||||
*
|
||||
* @param name fullpath based on _proc_root or parent
|
||||
* @param mode permission configuration
|
||||
* @param parent can be empty
|
||||
* @param fops
|
||||
* @param data
|
||||
*
|
||||
* @return dentry
|
||||
*/
|
||||
struct proc_dentry *proc_create_data(const char *name, mode_t mode, struct proc_dentry *parent,
|
||||
const struct dfs_file_ops *fops, void *data)
|
||||
{
|
||||
struct proc_dentry *dentry, *_parent = parent;
|
||||
|
||||
dentry = proc_create_reg(name, mode, &_parent);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->fops = fops ? fops : &proc_file_ops;
|
||||
dentry->data = data;
|
||||
|
||||
dentry = proc_register(_parent, dentry);
|
||||
}
|
||||
proc_release(_parent);
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a file
|
||||
*
|
||||
* @param name fullpath based on _proc_root or parent
|
||||
* @param mode permission configuration
|
||||
* @param parent can be empty
|
||||
* @param show
|
||||
* @param data
|
||||
*
|
||||
* @return dentry
|
||||
*/
|
||||
struct proc_dentry *proc_create_single_data(const char *name, mode_t mode, struct proc_dentry *parent,
|
||||
int (*show)(struct dfs_seq_file *, void *), void *data)
|
||||
{
|
||||
struct proc_dentry *dentry, *_parent = parent;
|
||||
|
||||
dentry = proc_create_reg(name, mode, &_parent);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->fops = &proc_file_ops;
|
||||
dentry->single_show = show;
|
||||
dentry->data = data;
|
||||
|
||||
dentry = proc_register(_parent, dentry);
|
||||
}
|
||||
proc_release(_parent);
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a symlink
|
||||
*
|
||||
* @param name fullpath based on _proc_root or parent
|
||||
* @param parent can be empty
|
||||
* @param dest link file fullpath
|
||||
*
|
||||
* @return dentry
|
||||
*/
|
||||
struct proc_dentry *proc_symlink(const char *name, struct proc_dentry *parent, const char *dest)
|
||||
{
|
||||
struct proc_dentry *dentry, *_parent = parent;
|
||||
|
||||
dentry = proc_create(&_parent, name, (S_IFLNK | (S_IRUSR | S_IRGRP | S_IROTH)
|
||||
| (S_IWUSR | S_IWGRP | S_IWOTH) | (S_IXUSR | S_IXGRP | S_IXOTH)));
|
||||
if (dentry)
|
||||
{
|
||||
dentry->data = (void *)rt_strdup(dest);
|
||||
if (dentry->data)
|
||||
{
|
||||
dentry = proc_register(_parent, dentry);
|
||||
}
|
||||
else
|
||||
{
|
||||
proc_release(dentry);
|
||||
dentry = NULL;
|
||||
}
|
||||
}
|
||||
proc_release(_parent);
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
static void remove_proc_subtree(struct proc_dentry *dentry)
|
||||
{
|
||||
struct proc_dentry *iter = RT_NULL, *iter_tmp, *tmp = RT_NULL;
|
||||
|
||||
dfs_vfs_for_each_subnode(iter, iter_tmp, dentry, node)
|
||||
{
|
||||
if (iter == RT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (tmp)
|
||||
{
|
||||
proc_release(tmp);
|
||||
tmp = RT_NULL;
|
||||
}
|
||||
|
||||
tmp = iter;
|
||||
|
||||
if (S_ISDIR(dentry->mode))
|
||||
{
|
||||
remove_proc_subtree(iter);
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp)
|
||||
{
|
||||
proc_release(tmp);
|
||||
tmp = RT_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief remove a dentry
|
||||
*
|
||||
* @param dentry
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void proc_remove(struct proc_dentry *dentry)
|
||||
{
|
||||
if (dentry && dentry != &_proc_root)
|
||||
{
|
||||
if (S_ISDIR(dentry->mode))
|
||||
{
|
||||
remove_proc_subtree(dentry);
|
||||
}
|
||||
|
||||
dfs_vfs_remove_node(&dentry->node);
|
||||
proc_release(dentry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief find dentry exist
|
||||
*
|
||||
* @param name fullpath based on _proc_root
|
||||
*
|
||||
* @return dentry
|
||||
*/
|
||||
struct proc_dentry *dfs_proc_find(const char *name)
|
||||
{
|
||||
struct proc_dentry *dentry = RT_NULL;
|
||||
|
||||
proc_find(&dentry, &name, 1);
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief remove a dentry on parent
|
||||
*
|
||||
* @param name fullpath based on parent
|
||||
* @param parent
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void proc_remove_dentry(const char *name, struct proc_dentry *parent)
|
||||
{
|
||||
struct proc_dentry *dentry = parent;
|
||||
|
||||
if (proc_find(&dentry, &name, 1) >= 0)
|
||||
{
|
||||
proc_remove(dentry);
|
||||
proc_release(dentry);
|
||||
}
|
||||
}
|
||||
|
||||
#define _COLOR_RED "\033[31m"
|
||||
#define _COLOR_GREEN "\033[32m"
|
||||
#define _COLOR_BLUE "\033[34m"
|
||||
#define _COLOR_CYAN "\033[36m"
|
||||
#define _COLOR_WHITE "\033[37m"
|
||||
#define _COLOR_NORMAL "\033[0m"
|
||||
|
||||
static void dump_proc_subtree(struct proc_dentry *dentry, int tab)
|
||||
{
|
||||
struct proc_dentry *iter = RT_NULL, *tmp;
|
||||
|
||||
dfs_vfs_for_each_subnode(iter, tmp, dentry, node)
|
||||
{
|
||||
if (iter == RT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for(int i = 0; i < tab; i ++)
|
||||
{
|
||||
rt_kprintf("%-4s", i + 1 >= tab ? "|-" : " ");
|
||||
}
|
||||
|
||||
if (S_ISDIR(iter->mode))
|
||||
{
|
||||
rt_kprintf(_COLOR_BLUE "%-20s" _COLOR_NORMAL " %d\n", iter->name, iter->ref_count);
|
||||
dump_proc_subtree(iter, tab + 1);
|
||||
}
|
||||
else if (S_ISLNK(iter->mode))
|
||||
{
|
||||
rt_kprintf(_COLOR_CYAN "%-20s" _COLOR_NORMAL " %d\n", iter->name, iter->ref_count);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("%-20s %d\n", iter->name, iter->ref_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void proc_dump(struct proc_dentry *dentry)
|
||||
{
|
||||
if (dentry)
|
||||
{
|
||||
if (S_ISDIR(dentry->mode))
|
||||
{
|
||||
rt_kprintf(_COLOR_BLUE "%-20s" _COLOR_NORMAL " %d\n", dentry->name, dentry->ref_count);
|
||||
dump_proc_subtree(dentry, 1);
|
||||
}
|
||||
else if (S_ISLNK(dentry->mode))
|
||||
{
|
||||
rt_kprintf(_COLOR_CYAN "%-20s" _COLOR_NORMAL " %d\n", dentry->name, dentry->ref_count);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("%-20s %d\n", dentry->name, dentry->ref_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int msh_proc_dump(int argc, char** argv)
|
||||
{
|
||||
const char *name = argc > 1 ? argv[1] : "/";
|
||||
struct proc_dentry *dentry = RT_NULL;
|
||||
|
||||
int ret = proc_find(&dentry, &name, 1);
|
||||
if (ret >= 0)
|
||||
{
|
||||
proc_dump(dentry);
|
||||
}
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT_ALIAS(msh_proc_dump, proc_dump, proc dump);
|
||||
|
||||
static int msh_proc_remove(int argc, char** argv)
|
||||
{
|
||||
if (argc > 1)
|
||||
{
|
||||
const char *name = argv[1];
|
||||
struct proc_dentry *dentry = RT_NULL;
|
||||
|
||||
int ret = proc_find(&dentry, &name, 1);
|
||||
if (ret >= 0)
|
||||
{
|
||||
if (dentry != &_proc_root)
|
||||
{
|
||||
proc_remove(dentry);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct proc_dentry *iter = RT_NULL, *iter_tmp, *tmp = RT_NULL;
|
||||
|
||||
dfs_vfs_for_each_subnode(iter, iter_tmp, dentry, node)
|
||||
{
|
||||
if (iter == RT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (tmp)
|
||||
{
|
||||
proc_remove(tmp);
|
||||
}
|
||||
|
||||
tmp = iter;
|
||||
}
|
||||
|
||||
if (tmp)
|
||||
{
|
||||
proc_remove(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
proc_release(dentry);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("proc_remove path\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT_ALIAS(msh_proc_remove, proc_remove, proc remove);
|
||||
|
||||
static int msh_proc_symlink(int argc, char** argv)
|
||||
{
|
||||
if (argc > 2)
|
||||
{
|
||||
struct proc_dentry *entry = proc_symlink(argv[1], 0, argv[2]);
|
||||
if (entry)
|
||||
{
|
||||
proc_release(entry);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("proc_symlink path dest\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT_ALIAS(msh_proc_symlink, proc_symlink, proc symlink);
|
||||
|
||||
static int msh_proc_echo(int argc, char** argv)
|
||||
{
|
||||
if (argc > 1)
|
||||
{
|
||||
for(int i = 1; i <= argc - 1; i ++)
|
||||
{
|
||||
struct proc_dentry *entry = proc_create_data(argv[i], 0, 0, 0, 0);
|
||||
if (entry)
|
||||
{
|
||||
proc_release(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("proc_echo path\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT_ALIAS(msh_proc_echo, proc_echo, proc echo);
|
||||
|
||||
static int msh_proc_mkdir(int argc, char** argv)
|
||||
{
|
||||
if (argc > 1)
|
||||
{
|
||||
for(int i = 1; i <= argc - 1; i ++)
|
||||
{
|
||||
struct proc_dentry *entry = proc_mkdir(argv[i], 0);
|
||||
if (entry)
|
||||
{
|
||||
proc_release(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("proc_mkdir path\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT_ALIAS(msh_proc_mkdir, proc_mkdir, proc mkdir);
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#ifndef __PROC_H__
|
||||
#define __PROC_H__
|
||||
|
||||
#include <dfs_file.h>
|
||||
#include <dfs_seq_file.h>
|
||||
#include <dfs_vfs.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
struct proc_dentry;
|
||||
|
||||
struct proc_ops
|
||||
{
|
||||
struct proc_dentry *(*lookup)(struct proc_dentry *parent, const char *name);
|
||||
int (*readlink)(struct proc_dentry *dentry, char *buf, int len);
|
||||
};
|
||||
|
||||
struct proc_dentry
|
||||
{
|
||||
rt_uint32_t mode;
|
||||
rt_atomic_t ref_count;
|
||||
|
||||
struct proc_dentry *parent;
|
||||
struct dfs_vfs_node node;
|
||||
|
||||
const struct dfs_file_ops *fops;
|
||||
const struct proc_ops *ops;
|
||||
const struct dfs_seq_ops *seq_ops;
|
||||
int (*single_show)(struct dfs_seq_file *seq, void *data);
|
||||
|
||||
int pid;
|
||||
|
||||
char *name;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct proc_dentry *dfs_proc_find(const char *name);
|
||||
|
||||
struct proc_dentry *proc_mkdir_data(const char *name, mode_t mode, struct proc_dentry *parent,
|
||||
const struct dfs_file_ops *fops, void *data);
|
||||
struct proc_dentry *proc_mkdir_mode(const char *name, mode_t mode, struct proc_dentry *parent);
|
||||
struct proc_dentry *proc_mkdir(const char *name, struct proc_dentry *parent);
|
||||
|
||||
struct proc_dentry *proc_create_data(const char *name, mode_t mode, struct proc_dentry *parent,
|
||||
const struct dfs_file_ops *fops, void *data);
|
||||
struct proc_dentry *proc_create_single_data(const char *name, mode_t mode, struct proc_dentry *parent,
|
||||
int (*show)(struct dfs_seq_file *, void *), void *data);
|
||||
|
||||
struct proc_dentry *proc_symlink(const char *name, struct proc_dentry *parent, const char *dest);
|
||||
|
||||
struct proc_dentry *proc_acquire(struct proc_dentry *dentry);
|
||||
void proc_release(struct proc_dentry *dentry);
|
||||
|
||||
void proc_remove(struct proc_dentry *dentry);
|
||||
void proc_remove_dentry(const char *name, struct proc_dentry *parent);
|
||||
|
||||
int proc_pid(int pid);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
static char *__proc_cmdline = NULL;
|
||||
|
||||
int proc_cmdline_save(const char *cmdline)
|
||||
{
|
||||
if (__proc_cmdline)
|
||||
{
|
||||
free(__proc_cmdline);
|
||||
__proc_cmdline = NULL;
|
||||
}
|
||||
|
||||
__proc_cmdline = strdup(cmdline);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int single_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
if (__proc_cmdline)
|
||||
{
|
||||
dfs_seq_puts(seq, __proc_cmdline);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proc_cmdline_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_single_data("cmdline", 0, NULL, single_show, NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_cmdline_init);
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
static void *seq_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
off_t i = *index; // seq->index
|
||||
|
||||
return NULL + (i == 0);
|
||||
}
|
||||
|
||||
static void seq_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
off_t i = *index + 1; // seq->index
|
||||
|
||||
*index = i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int seq_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
dfs_seq_puts(seq, "rt_weak const struct dfs_seq_ops *cpuinfo_get_seq_ops(void)\n--need your own function--\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_seq_ops seq_ops = {
|
||||
.start = seq_start,
|
||||
.stop = seq_stop,
|
||||
.next = seq_next,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
rt_weak const struct dfs_seq_ops *cpuinfo_get_seq_ops(void)
|
||||
{
|
||||
return &seq_ops;
|
||||
}
|
||||
|
||||
static int proc_open(struct dfs_file *file)
|
||||
{
|
||||
return dfs_seq_open(file, cpuinfo_get_seq_ops());
|
||||
}
|
||||
|
||||
static int proc_close(struct dfs_file *file)
|
||||
{
|
||||
return dfs_seq_release(file);
|
||||
}
|
||||
|
||||
static const struct dfs_file_ops file_ops = {
|
||||
.open = proc_open,
|
||||
.read = dfs_seq_read,
|
||||
.lseek = dfs_seq_lseek,
|
||||
.close = proc_close,
|
||||
};
|
||||
|
||||
int proc_cpuinfo_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_data("cpuinfo", 0, NULL, &file_ops, NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_cpuinfo_init);
|
|
@ -0,0 +1,307 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
#include <rthw.h>
|
||||
#include <rtthread.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define LIST_FIND_OBJ_NR 8
|
||||
|
||||
struct device_show
|
||||
{
|
||||
char *buf;
|
||||
int size;
|
||||
int len;
|
||||
int index;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
rt_list_t *list;
|
||||
rt_list_t **array;
|
||||
rt_uint8_t type;
|
||||
int nr; /* input: max nr, can't be 0 */
|
||||
int nr_out; /* out: got nr */
|
||||
} list_get_next_t;
|
||||
|
||||
static void list_find_init(list_get_next_t *p, rt_uint8_t type, rt_list_t **array, int nr)
|
||||
{
|
||||
struct rt_object_information *info;
|
||||
rt_list_t *list;
|
||||
|
||||
info = rt_object_get_information((enum rt_object_class_type)type);
|
||||
list = &info->object_list;
|
||||
|
||||
p->list = list;
|
||||
p->type = type;
|
||||
p->array = array;
|
||||
p->nr = nr;
|
||||
p->nr_out = 0;
|
||||
}
|
||||
|
||||
static rt_list_t *list_get_next(rt_list_t *current, list_get_next_t *arg)
|
||||
{
|
||||
int first_flag = 0;
|
||||
rt_base_t level;
|
||||
rt_list_t *node, *list;
|
||||
rt_list_t **array;
|
||||
struct rt_object_information *info;
|
||||
int nr;
|
||||
|
||||
arg->nr_out = 0;
|
||||
|
||||
if (!arg->nr || !arg->type)
|
||||
{
|
||||
return (rt_list_t *)RT_NULL;
|
||||
}
|
||||
|
||||
list = arg->list;
|
||||
info = rt_list_entry(list, struct rt_object_information, object_list);
|
||||
|
||||
if (!current) /* find first */
|
||||
{
|
||||
node = list;
|
||||
first_flag = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = current;
|
||||
}
|
||||
|
||||
level = rt_spin_lock_irqsave(&info->spinlock);
|
||||
|
||||
if (!first_flag)
|
||||
{
|
||||
struct rt_object *obj;
|
||||
/* The node in the list? */
|
||||
obj = rt_list_entry(node, struct rt_object, list);
|
||||
if ((obj->type & ~RT_Object_Class_Static) != arg->type)
|
||||
{
|
||||
rt_spin_unlock_irqrestore(&info->spinlock, level);
|
||||
return (rt_list_t *)RT_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
nr = 0;
|
||||
array = arg->array;
|
||||
while (1)
|
||||
{
|
||||
node = node->next;
|
||||
|
||||
if (node == list)
|
||||
{
|
||||
node = (rt_list_t *)RT_NULL;
|
||||
break;
|
||||
}
|
||||
nr++;
|
||||
*array++ = node;
|
||||
if (nr == arg->nr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rt_spin_unlock_irqrestore(&info->spinlock, level);
|
||||
arg->nr_out = nr;
|
||||
return node;
|
||||
}
|
||||
|
||||
static char *const device_type_str[RT_Device_Class_Unknown] =
|
||||
{
|
||||
"Character Device",
|
||||
"Block Device",
|
||||
"Network Interface",
|
||||
"MTD Device",
|
||||
"CAN Device",
|
||||
"RTC",
|
||||
"Sound Device",
|
||||
"Graphic Device",
|
||||
"I2C Bus",
|
||||
"USB Slave Device",
|
||||
"USB Host Bus",
|
||||
"USB OTG Bus",
|
||||
"SPI Bus",
|
||||
"SPI Device",
|
||||
"SDIO Bus",
|
||||
"PM Pseudo Device",
|
||||
"Pipe",
|
||||
"Portal Device",
|
||||
"Timer Device",
|
||||
"Miscellaneous Device",
|
||||
"Sensor Device",
|
||||
"Touch Device",
|
||||
"Phy Device",
|
||||
"Security Device",
|
||||
"WLAN Device",
|
||||
"Pin Device",
|
||||
"ADC Device",
|
||||
"DAC Device",
|
||||
"WDT Device",
|
||||
"PWM Device",
|
||||
"Bus Device",
|
||||
};
|
||||
|
||||
static void save_info(struct device_show *dev, char *dev_name)
|
||||
{
|
||||
char tmp[256] = {0};
|
||||
int len;
|
||||
|
||||
dev->index ++;
|
||||
|
||||
rt_snprintf(tmp, 256, "%d %s\n", dev->index, dev_name);
|
||||
tmp[255] = 0;
|
||||
|
||||
len = rt_strlen(tmp);
|
||||
if (dev->size > dev->len + len)
|
||||
{
|
||||
strcat(dev->buf, tmp);
|
||||
dev->len += len;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dev->buf == RT_NULL)
|
||||
{
|
||||
dev->buf = rt_calloc(1, 4096);
|
||||
}
|
||||
else
|
||||
{
|
||||
dev->buf = rt_realloc(dev->buf, dev->size + 4096);
|
||||
}
|
||||
if (dev->buf)
|
||||
{
|
||||
dev->size += 4096;
|
||||
strcat(dev->buf, tmp);
|
||||
dev->len += len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void list_device(struct device_show *dev)
|
||||
{
|
||||
rt_base_t level;
|
||||
list_get_next_t find_arg;
|
||||
struct rt_object_information *info;
|
||||
rt_list_t *obj_list[LIST_FIND_OBJ_NR];
|
||||
rt_list_t *next = (rt_list_t *)RT_NULL;
|
||||
|
||||
list_find_init(&find_arg, RT_Object_Class_Device, obj_list, sizeof(obj_list) / sizeof(obj_list[0]));
|
||||
info = rt_list_entry(find_arg.list, struct rt_object_information, object_list);
|
||||
|
||||
do
|
||||
{
|
||||
next = list_get_next(next, &find_arg);
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < find_arg.nr_out; i++)
|
||||
{
|
||||
struct rt_object *obj;
|
||||
struct rt_device *device;
|
||||
|
||||
obj = rt_list_entry(obj_list[i], struct rt_object, list);
|
||||
level = rt_spin_lock_irqsave(&info->spinlock);
|
||||
if ((obj->type & ~RT_Object_Class_Static) != find_arg.type)
|
||||
{
|
||||
rt_spin_unlock_irqrestore(&info->spinlock, level);
|
||||
continue;
|
||||
}
|
||||
|
||||
rt_spin_unlock_irqrestore(&info->spinlock, level);
|
||||
|
||||
device = (struct rt_device *)obj;
|
||||
|
||||
if (device->type < RT_Device_Class_Unknown)
|
||||
{
|
||||
save_info(dev + device->type, device->parent.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (next != (rt_list_t *)RT_NULL);
|
||||
}
|
||||
|
||||
static int show_info(struct dfs_seq_file *seq)
|
||||
{
|
||||
struct device_show _show[RT_Device_Class_Unknown] = {0};
|
||||
|
||||
list_device(_show);
|
||||
|
||||
for (int i = 0; i < RT_Device_Class_Unknown; i++)
|
||||
{
|
||||
if (_show[i].buf)
|
||||
{
|
||||
dfs_seq_printf(seq, "%s:\n", device_type_str[i]);
|
||||
dfs_seq_write(seq, _show[i].buf, _show[i].len);
|
||||
dfs_seq_putc(seq, '\n');
|
||||
|
||||
rt_free(_show[i].buf);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *seq_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
off_t i = *index; // seq->index
|
||||
|
||||
return NULL + (i == 0);
|
||||
}
|
||||
|
||||
static void seq_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
off_t i = *index + 1; // seq->index
|
||||
|
||||
*index = i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int seq_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
show_info(seq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_seq_ops seq_ops = {
|
||||
.start = seq_start,
|
||||
.stop = seq_stop,
|
||||
.next = seq_next,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
int proc_devices_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_data("devices", 0, NULL, NULL, NULL);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->seq_ops = &seq_ops;
|
||||
}
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_devices_init);
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
#include <dfs_fs.h>
|
||||
|
||||
static void *seq_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
off_t i = *index; // seq->index
|
||||
struct dfs_filesystem_type *fs = dfs_filesystems();
|
||||
|
||||
if (fs)
|
||||
{
|
||||
while (i--)
|
||||
{
|
||||
fs = fs->next;
|
||||
if (!fs)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fs;
|
||||
}
|
||||
|
||||
static void seq_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
off_t i = *index + 1; // seq->index
|
||||
struct dfs_filesystem_type *fs = (struct dfs_filesystem_type *)data;
|
||||
|
||||
*index = i;
|
||||
|
||||
return fs->next;
|
||||
}
|
||||
|
||||
static int seq_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
struct dfs_filesystem_type *fs = (struct dfs_filesystem_type *)data;
|
||||
|
||||
dfs_seq_printf(seq, "%-9s%s\n", (fs->fs_ops->flags == FS_NEED_DEVICE) ? "" : "nodev", fs->fs_ops->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_seq_ops seq_ops = {
|
||||
.start = seq_start,
|
||||
.stop = seq_stop,
|
||||
.next = seq_next,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
int proc_filesystems_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_data("filesystems", 0, NULL, NULL, NULL);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->seq_ops = &seq_ops;
|
||||
}
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_filesystems_init);
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
#include <mm_page.h>
|
||||
|
||||
|
||||
extern void rt_memory_info(rt_size_t *total,
|
||||
rt_size_t *used,
|
||||
rt_size_t *max_used);
|
||||
|
||||
static int single_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
dfs_seq_printf(seq, "0.13 0.16 0.17 1/1035 380436\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proc_loadavg_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_single_data("loadavg", 0, NULL, single_show, NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_loadavg_init);
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
#include <mm_page.h>
|
||||
|
||||
|
||||
extern void rt_memory_info(rt_size_t *total,
|
||||
rt_size_t *used,
|
||||
rt_size_t *max_used);
|
||||
|
||||
static int single_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
rt_size_t total, used, max_used, freed;
|
||||
rt_size_t total_sum = 0;
|
||||
rt_size_t total_freed = 0;
|
||||
|
||||
rt_memory_info(&total, &used, &max_used);
|
||||
total_sum = total_sum + total;
|
||||
total_freed = total_freed + total - used;
|
||||
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "MemMaxUsed:", max_used / 1024);
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "MemAvailable:", (total - used) / 1024);
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "Cached:", 0);
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "SReclaimable:", 0);
|
||||
|
||||
rt_page_get_info(&total, &freed);
|
||||
total_sum = total_sum + total * RT_MM_PAGE_SIZE;
|
||||
total_freed = total_freed + freed * RT_MM_PAGE_SIZE;
|
||||
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "MemTotal:", total_sum / 1024);
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "MemFree:", total_freed / 1024);
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "LowPageTotal:", total * RT_MM_PAGE_SIZE / 1024);
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "lowPageFree:", freed * RT_MM_PAGE_SIZE/ 1024);
|
||||
|
||||
rt_page_high_get_info(&total, &freed);
|
||||
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "HighPageTotal:", total * RT_MM_PAGE_SIZE / 1024);
|
||||
dfs_seq_printf(seq, "%-16s%8d KB\n", "HighPageFree:", freed * RT_MM_PAGE_SIZE / 1024);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proc_meminfo_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_single_data("meminfo", 0, NULL, single_show, NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_meminfo_init);
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
#include <dfs_mnt.h>
|
||||
|
||||
|
||||
const char *mnt_flag(int flag)
|
||||
{
|
||||
/*if (flag & MNT_READONLY)
|
||||
{
|
||||
return "ro";
|
||||
}*/
|
||||
|
||||
return "rw";
|
||||
}
|
||||
|
||||
static struct dfs_mnt* mnt_show(struct dfs_mnt *mnt, void *parameter)
|
||||
{
|
||||
struct dfs_seq_file *seq = (struct dfs_seq_file *)parameter;
|
||||
|
||||
if (mnt)
|
||||
{
|
||||
if (mnt->dev_id)
|
||||
{
|
||||
dfs_seq_printf(seq, "%s %s %s %s 0 0\n", mnt->dev_id->parent.name, mnt->fullpath,
|
||||
mnt->fs_ops->name, mnt_flag(mnt->flags));
|
||||
}
|
||||
else
|
||||
{
|
||||
dfs_seq_printf(seq, "%s %s %s %s 0 0\n", mnt->fs_ops->name, mnt->fullpath,
|
||||
mnt->fs_ops->name, mnt_flag(mnt->flags));
|
||||
}
|
||||
}
|
||||
|
||||
return RT_NULL;
|
||||
}
|
||||
|
||||
static void *seq_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
off_t i = *index; // seq->index
|
||||
|
||||
return NULL + (i == 0);
|
||||
}
|
||||
|
||||
static void seq_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
off_t i = *index + 1; // seq->index
|
||||
|
||||
*index = i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int seq_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
dfs_mnt_foreach(mnt_show, seq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_seq_ops seq_ops = {
|
||||
.start = seq_start,
|
||||
.stop = seq_stop,
|
||||
.next = seq_next,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
int proc_mounts_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_data("mounts", 0, NULL, NULL, NULL);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->seq_ops = &seq_ops;
|
||||
}
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_mounts_init);
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
#ifdef RT_USING_LWIP
|
||||
#include "lwip/opt.h"
|
||||
#endif
|
||||
|
||||
#if LWIP_ROUTE
|
||||
extern int inet_route_foreach(void (*func)(const char *name, uint32_t ip_addr, uint32_t netmask, void *parameter), void *parameter);
|
||||
#endif
|
||||
|
||||
static void *seq_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
off_t i = *index; // seq->index
|
||||
|
||||
return NULL + (i == 0);
|
||||
}
|
||||
|
||||
static void seq_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
off_t i = *index + 1; // seq->index
|
||||
|
||||
*index = i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void route_show(const char *name, uint32_t ip_addr, uint32_t netmask, void *parameter)
|
||||
{
|
||||
struct dfs_seq_file *seq = (struct dfs_seq_file *)parameter;
|
||||
/* "Iface\tDestination\tGateway "
|
||||
"\tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU"
|
||||
"\tWindow\tIRTT"); */
|
||||
/* "%63s%lx%lx%X%d%d%d%lx%d%d%d\n" */
|
||||
dfs_seq_printf(seq, "%s ", name);
|
||||
dfs_seq_printf(seq, "%lx ", ip_addr);
|
||||
dfs_seq_printf(seq, "%lx ", 0);
|
||||
dfs_seq_printf(seq, "%X ", 1);
|
||||
dfs_seq_printf(seq, "%d ", 0);
|
||||
dfs_seq_printf(seq, "%d ", 0);
|
||||
dfs_seq_printf(seq, "%d ", 0);
|
||||
dfs_seq_printf(seq, "%lx ", netmask);
|
||||
dfs_seq_printf(seq, "%d ", 0);
|
||||
dfs_seq_printf(seq, "%d ", 0);
|
||||
dfs_seq_printf(seq, "%d\n", 0);
|
||||
}
|
||||
|
||||
static int seq_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
dfs_seq_printf(seq, "\n");
|
||||
#if LWIP_ROUTE
|
||||
inet_route_foreach(route_show, seq);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_seq_ops seq_ops = {
|
||||
.start = seq_start,
|
||||
.stop = seq_stop,
|
||||
.next = seq_next,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
int proc_net_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry;
|
||||
|
||||
dentry = proc_mkdir("net", NULL);
|
||||
if (!dentry)
|
||||
return -1;
|
||||
|
||||
proc_release(dentry);
|
||||
|
||||
dentry = proc_create_data("net/route", 0, NULL, NULL, NULL);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->seq_ops = &seq_ops;
|
||||
}
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_net_init);
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
#include <rthw.h>
|
||||
#include <rtthread.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define LIST_FIND_OBJ_NR 8
|
||||
|
||||
typedef struct
|
||||
{
|
||||
rt_list_t *list;
|
||||
rt_list_t **array;
|
||||
rt_uint8_t type;
|
||||
int nr; /* input: max nr, can't be 0 */
|
||||
int nr_out; /* out: got nr */
|
||||
} list_get_next_t;
|
||||
|
||||
static void list_find_init(list_get_next_t *p, rt_uint8_t type, rt_list_t **array, int nr)
|
||||
{
|
||||
struct rt_object_information *info;
|
||||
rt_list_t *list;
|
||||
|
||||
info = rt_object_get_information((enum rt_object_class_type)type);
|
||||
list = &info->object_list;
|
||||
|
||||
p->list = list;
|
||||
p->type = type;
|
||||
p->array = array;
|
||||
p->nr = nr;
|
||||
p->nr_out = 0;
|
||||
}
|
||||
|
||||
static rt_list_t *list_get_next(rt_list_t *current, list_get_next_t *arg)
|
||||
{
|
||||
int first_flag = 0;
|
||||
rt_base_t level;
|
||||
rt_list_t *node, *list;
|
||||
rt_list_t **array;
|
||||
struct rt_object_information *info;
|
||||
int nr;
|
||||
|
||||
arg->nr_out = 0;
|
||||
|
||||
if (!arg->nr || !arg->type)
|
||||
{
|
||||
return (rt_list_t *)RT_NULL;
|
||||
}
|
||||
|
||||
list = arg->list;
|
||||
info = rt_list_entry(list, struct rt_object_information, object_list);
|
||||
|
||||
if (!current) /* find first */
|
||||
{
|
||||
node = list;
|
||||
first_flag = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = current;
|
||||
}
|
||||
|
||||
level = rt_spin_lock_irqsave(&info->spinlock);
|
||||
|
||||
if (!first_flag)
|
||||
{
|
||||
struct rt_object *obj;
|
||||
/* The node in the list? */
|
||||
obj = rt_list_entry(node, struct rt_object, list);
|
||||
if ((obj->type & ~RT_Object_Class_Static) != arg->type)
|
||||
{
|
||||
rt_spin_unlock_irqrestore(&info->spinlock, level);
|
||||
return (rt_list_t *)RT_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
nr = 0;
|
||||
array = arg->array;
|
||||
while (1)
|
||||
{
|
||||
node = node->next;
|
||||
|
||||
if (node == list)
|
||||
{
|
||||
node = (rt_list_t *)RT_NULL;
|
||||
break;
|
||||
}
|
||||
nr++;
|
||||
*array++ = node;
|
||||
if (nr == arg->nr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rt_spin_unlock_irqrestore(&info->spinlock, level);
|
||||
arg->nr_out = nr;
|
||||
return node;
|
||||
}
|
||||
|
||||
static int show_info(struct dfs_seq_file *seq)
|
||||
{
|
||||
rt_base_t level;
|
||||
list_get_next_t find_arg;
|
||||
struct rt_object_information *info;
|
||||
rt_list_t *obj_list[LIST_FIND_OBJ_NR];
|
||||
rt_list_t *next = (rt_list_t *)RT_NULL;
|
||||
|
||||
list_find_init(&find_arg, RT_Object_Class_Device, obj_list, sizeof(obj_list) / sizeof(obj_list[0]));
|
||||
info = rt_list_entry(find_arg.list, struct rt_object_information, object_list);
|
||||
|
||||
do
|
||||
{
|
||||
next = list_get_next(next, &find_arg);
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < find_arg.nr_out; i++)
|
||||
{
|
||||
struct rt_object *obj;
|
||||
struct rt_device *device;
|
||||
|
||||
obj = rt_list_entry(obj_list[i], struct rt_object, list);
|
||||
level = rt_spin_lock_irqsave(&info->spinlock);
|
||||
if ((obj->type & ~RT_Object_Class_Static) != find_arg.type)
|
||||
{
|
||||
rt_spin_unlock_irqrestore(&info->spinlock, level);
|
||||
continue;
|
||||
}
|
||||
|
||||
rt_spin_unlock_irqrestore(&info->spinlock, level);
|
||||
|
||||
device = (struct rt_device *)obj;
|
||||
|
||||
if (device->type == RT_Device_Class_Block)
|
||||
{
|
||||
struct rt_device_blk_geometry geometry = { 0 };
|
||||
|
||||
rt_device_control(device, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry);
|
||||
|
||||
dfs_seq_printf(seq, "%4d %7d %14llu %s\n", 0, 0,
|
||||
geometry.sector_count, device->parent.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (next != (rt_list_t *)RT_NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *seq_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
off_t i = *index; // seq->index
|
||||
|
||||
return NULL + (i == 0);
|
||||
}
|
||||
|
||||
static void seq_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
off_t i = *index + 1; // seq->index
|
||||
|
||||
*index = i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int seq_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
dfs_seq_puts(seq, "major minor #blocks name\n\n");
|
||||
/* data: The return value of the start or next*/
|
||||
show_info(seq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_seq_ops seq_ops = {
|
||||
.start = seq_start,
|
||||
.stop = seq_stop,
|
||||
.next = seq_next,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
int proc_partitions_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_data("partitions", 0, NULL, NULL, NULL);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->seq_ops = &seq_ops;
|
||||
}
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_partitions_init);
|
|
@ -0,0 +1,449 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
#define __RT_IPC_SOURCE__
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "lwp_internal.h"
|
||||
#include <dfs_dentry.h>
|
||||
#include "lwp_internal.h"
|
||||
|
||||
#if defined(RT_USING_SMART)
|
||||
|
||||
#include "lwp.h"
|
||||
#include "lwp_pid.h"
|
||||
#include <lwp_user_mm.h>
|
||||
|
||||
struct pid_dentry
|
||||
{
|
||||
const char *name;
|
||||
mode_t mode;
|
||||
const struct dfs_file_ops *fops;
|
||||
const struct proc_ops *ops;
|
||||
const struct dfs_seq_ops *seq_ops;
|
||||
int (*single_show)(struct dfs_seq_file *seq, void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
static char stat_transform(int __stat)
|
||||
{
|
||||
switch (__stat)
|
||||
{
|
||||
case RT_THREAD_RUNNING:
|
||||
return 'R';
|
||||
default:
|
||||
return 'T';
|
||||
}
|
||||
}
|
||||
|
||||
static int stat_single_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
struct proc_dentry *dentry = (struct proc_dentry *)seq->file->vnode->data;
|
||||
rt_list_t *list;
|
||||
int mask = 0;
|
||||
rt_thread_t thread;
|
||||
rt_uint64_t user_time_lwp = 0;
|
||||
rt_uint64_t system_time_lwp = 0;
|
||||
int lwp_oncpu = RT_CPUS_NR;
|
||||
int lwp_oncpu_ok = 0;
|
||||
struct rt_lwp *lwp = RT_NULL;
|
||||
char** argv = RT_NULL;
|
||||
char *filename = RT_NULL;
|
||||
char *dot = RT_NULL;
|
||||
|
||||
lwp_pid_lock_take();
|
||||
|
||||
lwp = lwp_from_pid_locked(dentry->pid);
|
||||
argv = lwp_get_command_line_args(lwp);
|
||||
|
||||
if (lwp)
|
||||
{
|
||||
dfs_seq_printf(seq,"%d ",dentry->pid);
|
||||
if (argv)
|
||||
{
|
||||
filename = strrchr(argv[0], '/');
|
||||
dot = strchr(argv[0], '.');
|
||||
|
||||
if (filename != NULL)
|
||||
{
|
||||
filename++;
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = argv[0];
|
||||
}
|
||||
|
||||
if (dot != NULL)
|
||||
{
|
||||
*dot = '\0';
|
||||
}
|
||||
|
||||
if (filename != NULL)
|
||||
{
|
||||
dfs_seq_printf(seq,"(%s) ", filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
dfs_seq_printf(seq,"(%s) ", argv[0]);
|
||||
}
|
||||
|
||||
lwp_free_command_line_args(argv);
|
||||
}
|
||||
else
|
||||
{
|
||||
dfs_seq_printf(seq,"(%s) ", "");
|
||||
}
|
||||
|
||||
if (lwp->terminated)
|
||||
{
|
||||
dfs_seq_printf(seq,"%c ",'Z');
|
||||
}
|
||||
else
|
||||
{
|
||||
list = lwp->t_grp.next;
|
||||
while (list != &lwp->t_grp)
|
||||
{
|
||||
thread = rt_list_entry(list, struct rt_thread, sibling);
|
||||
user_time_lwp = user_time_lwp + thread->user_time;
|
||||
system_time_lwp = system_time_lwp + thread->system_time;
|
||||
|
||||
#if RT_CPUS_NR > 1
|
||||
#define ONCPU(thread) RT_SCHED_CTX(thread).oncpu
|
||||
#else
|
||||
#define ONCPU(thread) 0
|
||||
#endif
|
||||
if (lwp_oncpu_ok == 0)
|
||||
{
|
||||
lwp_oncpu = ONCPU(thread);
|
||||
lwp_oncpu_ok = 1;
|
||||
}
|
||||
if (stat_transform(RT_SCHED_CTX(thread).stat) == 'R')
|
||||
{
|
||||
lwp_oncpu = ONCPU(thread);
|
||||
mask = 1;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
if (mask == 1)
|
||||
{
|
||||
dfs_seq_printf(seq,"%c ",'R');
|
||||
}
|
||||
else
|
||||
{
|
||||
dfs_seq_printf(seq,"%c ",'S');
|
||||
}
|
||||
}
|
||||
lwp_pid_lock_release();
|
||||
|
||||
if (lwp->parent != NULL)
|
||||
dfs_seq_printf(seq,"%d ",lwp->parent->pid);
|
||||
else
|
||||
dfs_seq_printf(seq,"0 ");
|
||||
|
||||
dfs_seq_printf(seq, "1 1 0 -1 4194560 48245 133976064 732 425574 ");
|
||||
dfs_seq_printf(seq,"%llu ",user_time_lwp);//utime
|
||||
dfs_seq_printf(seq,"%llu ",system_time_lwp);//stime
|
||||
dfs_seq_printf(seq, "1204291 518742 20 0 1 0 50 ");
|
||||
dfs_seq_printf(seq, "%d ",rt_aspace_count_vsz(lwp->aspace));//VSZ
|
||||
dfs_seq_printf(seq, "1422 18446744073709551615 ");
|
||||
dfs_seq_printf(seq, "1 1 0 0 0 0 671173123 4096 1260 0 0 0 17 ");
|
||||
dfs_seq_printf(seq, "%d ", lwp_oncpu);//CPU
|
||||
dfs_seq_printf(seq, "0 0 0 0 0 0 0 0 0 0 0 0 0");
|
||||
dfs_seq_printf(seq,"\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
lwp_pid_lock_release();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmdline_single_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
struct proc_dentry *dentry = (struct proc_dentry *)seq->file->vnode->data;
|
||||
struct rt_lwp *lwp;
|
||||
char** argv;
|
||||
|
||||
lwp_pid_lock_take();
|
||||
lwp = lwp_from_pid_locked(dentry->pid);
|
||||
argv = lwp_get_command_line_args(lwp);
|
||||
lwp_pid_lock_release();
|
||||
|
||||
if (argv)
|
||||
{
|
||||
for (int i = 0; argv[i] != NULL; i++)
|
||||
{
|
||||
dfs_seq_printf(seq, "%s ", argv[i]);
|
||||
}
|
||||
dfs_seq_puts(seq, "\n");
|
||||
|
||||
lwp_free_command_line_args(argv);
|
||||
}
|
||||
else
|
||||
{
|
||||
dfs_seq_puts(seq, "error\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct proc_dentry *proc_pid_fd_lookup(struct proc_dentry *parent, const char *name)
|
||||
{
|
||||
struct proc_dentry *dentry = RT_NULL;
|
||||
char num[DIRENT_NAME_MAX];
|
||||
struct rt_lwp *lwp;
|
||||
struct dfs_fdtable *table;
|
||||
|
||||
lwp_pid_lock_take();
|
||||
lwp = lwp_from_pid_locked(parent->pid);
|
||||
table = lwp ? &lwp->fdt : RT_NULL;
|
||||
lwp_pid_lock_release();
|
||||
|
||||
if (!table)
|
||||
{
|
||||
return RT_NULL;
|
||||
}
|
||||
|
||||
dfs_file_lock();
|
||||
for (int i = 0; i < table->maxfd; i++)
|
||||
{
|
||||
struct dfs_file *file = table->fds[i];
|
||||
if (file)
|
||||
{
|
||||
rt_snprintf(num, DIRENT_NAME_MAX, "%d", i);
|
||||
if (rt_strcmp(num, name) == 0)
|
||||
{
|
||||
dentry = rt_calloc(1, sizeof(struct proc_dentry));
|
||||
if (dentry)
|
||||
{
|
||||
dentry->mode = (S_IFLNK | (S_IRUSR | S_IRGRP | S_IROTH) | (S_IWUSR | S_IWGRP | S_IWOTH) | (S_IXUSR | S_IXGRP | S_IXOTH));
|
||||
dentry->ref_count = 1;
|
||||
dentry->name = rt_strdup(name);
|
||||
dentry->data = (void *)dfs_dentry_full_path(file->dentry);
|
||||
|
||||
if (dentry->data == RT_NULL)
|
||||
{
|
||||
//todo add vnode->data
|
||||
if (file->vnode->type == FT_SOCKET)
|
||||
dentry->data = (void *)rt_strdup("socket");
|
||||
else if (file->vnode->type == FT_USER)
|
||||
dentry->data = (void *)rt_strdup("user");
|
||||
else if (file->vnode->type == FT_DEVICE)
|
||||
dentry->data = (void *)rt_strdup("device");
|
||||
else
|
||||
dentry->data = (void *)rt_strdup("unknown");
|
||||
}
|
||||
|
||||
dentry->pid = parent->pid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dfs_file_unlock();
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
int proc_pid_fd_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count)
|
||||
{
|
||||
int ret = 0, index = 0;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
struct rt_lwp *lwp;
|
||||
struct dfs_fdtable *table;
|
||||
|
||||
lwp_pid_lock_take();
|
||||
lwp = lwp_from_pid_locked(entry->pid);
|
||||
LWP_LOCK(lwp);
|
||||
table = lwp ? &lwp->fdt : RT_NULL;
|
||||
|
||||
if (!table->fds)
|
||||
{
|
||||
LWP_UNLOCK(lwp);
|
||||
lwp_pid_lock_release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
count = (count / sizeof(struct dirent));
|
||||
if (count == 0)
|
||||
{
|
||||
LWP_UNLOCK(lwp);
|
||||
lwp_pid_lock_release();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dfs_file_lock();
|
||||
for (int i = 0; i < table->maxfd; i++)
|
||||
{
|
||||
struct dfs_file *df = table->fds[i];
|
||||
if (df)
|
||||
{
|
||||
if (index >= file->fpos)
|
||||
{
|
||||
struct dirent *d = dirp + index - file->fpos;
|
||||
|
||||
d->d_type = DT_SYMLINK;
|
||||
d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
|
||||
rt_snprintf(d->d_name, DIRENT_NAME_MAX, "%d", i);
|
||||
d->d_namlen = rt_strlen(d->d_name);
|
||||
|
||||
ret++;
|
||||
}
|
||||
|
||||
index++;
|
||||
if (index - file->fpos >= count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
dfs_file_unlock();
|
||||
LWP_UNLOCK(lwp);
|
||||
lwp_pid_lock_release();
|
||||
|
||||
if (ret > 0)
|
||||
{
|
||||
file->fpos = index;
|
||||
ret = ret * sizeof(struct dirent);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct proc_ops proc_pid_fd_ops = {
|
||||
.lookup = proc_pid_fd_lookup,
|
||||
};
|
||||
|
||||
static const struct dfs_file_ops proc_pid_fd_fops = {
|
||||
.getdents = proc_pid_fd_getdents,
|
||||
};
|
||||
|
||||
int proc_pid_exe_readlink(struct proc_dentry *dentry, char *buf, int len)
|
||||
{
|
||||
struct rt_lwp *lwp;
|
||||
|
||||
lwp = lwp_self();
|
||||
len = rt_snprintf(buf, len, "%s", lwp ? lwp->exe_file : "null");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct proc_ops proc_pid_exe_ops = {
|
||||
.readlink = proc_pid_exe_readlink,
|
||||
};
|
||||
|
||||
int proc_pid_cwd_readlink(struct proc_dentry *dentry, char *buf, int len)
|
||||
{
|
||||
struct rt_lwp *lwp;
|
||||
|
||||
lwp = lwp_self();
|
||||
len = rt_snprintf(buf, len, "%s", lwp ? lwp->working_directory : "null");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct proc_ops proc_pid_cwd_ops = {
|
||||
.readlink = proc_pid_cwd_readlink,
|
||||
};
|
||||
|
||||
static struct pid_dentry pid_dentry_base[] = {
|
||||
{"cmdline", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, 0, 0, 0, cmdline_single_show, 0},
|
||||
{"cwd", S_IFLNK | S_IRUSR | S_IXUSR, 0, &proc_pid_cwd_ops, 0, 0},
|
||||
{"exe", S_IFLNK | S_IRUSR | S_IXUSR, 0, &proc_pid_exe_ops, 0, 0},
|
||||
{"fd", S_IFDIR | S_IRUSR | S_IXUSR, &proc_pid_fd_fops, &proc_pid_fd_ops, 0, 0, 0},
|
||||
{"mounts", S_IFLNK | S_IRUSR | S_IXUSR, 0, 0, 0, 0, "/proc/mounts"},
|
||||
{"stat", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, 0, 0, 0, stat_single_show, 0},
|
||||
};
|
||||
|
||||
int proc_pid(int pid)
|
||||
{
|
||||
char pid_str[64] = {0};
|
||||
struct proc_dentry *dentry;
|
||||
|
||||
rt_snprintf(pid_str, 64, "%d", pid);
|
||||
pid_str[63] = 0;
|
||||
|
||||
dentry = proc_mkdir(pid_str, 0);
|
||||
if (dentry)
|
||||
{
|
||||
struct proc_dentry *ent;
|
||||
|
||||
dentry->pid = pid;
|
||||
for (int j = 0; j < sizeof(pid_dentry_base) / sizeof(struct pid_dentry); j++)
|
||||
{
|
||||
if (S_ISDIR(pid_dentry_base[j].mode))
|
||||
{
|
||||
ent = proc_mkdir_data(pid_dentry_base[j].name, pid_dentry_base[j].mode, dentry,
|
||||
pid_dentry_base[j].fops, pid_dentry_base[j].data);
|
||||
}
|
||||
else if (S_ISLNK(pid_dentry_base[j].mode))
|
||||
{
|
||||
if (pid_dentry_base[j].data == RT_NULL)
|
||||
{
|
||||
pid_dentry_base[j].data = "NULL";
|
||||
}
|
||||
|
||||
ent = proc_symlink(pid_dentry_base[j].name, dentry, pid_dentry_base[j].data);
|
||||
}
|
||||
else
|
||||
{
|
||||
ent = proc_create_data(pid_dentry_base[j].name, pid_dentry_base[j].mode, dentry,
|
||||
pid_dentry_base[j].fops, pid_dentry_base[j].data);
|
||||
}
|
||||
|
||||
if (ent)
|
||||
{
|
||||
if (pid_dentry_base[j].ops)
|
||||
{
|
||||
ent->ops = pid_dentry_base[j].ops;
|
||||
}
|
||||
|
||||
if (pid_dentry_base[j].seq_ops)
|
||||
{
|
||||
ent->seq_ops = pid_dentry_base[j].seq_ops;
|
||||
}
|
||||
|
||||
if (pid_dentry_base[j].single_show)
|
||||
{
|
||||
ent->single_show = pid_dentry_base[j].single_show;
|
||||
}
|
||||
|
||||
proc_release(ent);
|
||||
}
|
||||
}
|
||||
proc_release(dentry);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msh_proc_pid(int argc, char **argv)
|
||||
{
|
||||
if (argc > 1)
|
||||
{
|
||||
for (int i = 1; i <= argc - 1; i++)
|
||||
{
|
||||
proc_pid(atoi(argv[i]));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT_ALIAS(msh_proc_pid, proc_pid, proc pid);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
#if defined(RT_USING_SMART)
|
||||
|
||||
#include <lwp.h>
|
||||
|
||||
|
||||
int proc_self_readlink(struct proc_dentry *dentry, char *buf, int len)
|
||||
{
|
||||
struct rt_lwp *lwp = RT_NULL;
|
||||
|
||||
lwp = lwp_self();
|
||||
if (lwp)
|
||||
{
|
||||
rt_snprintf(buf, len, "%d", lwp_to_pid(lwp));
|
||||
buf[len - 1] = 0;
|
||||
return rt_strlen(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_snprintf(buf, len, "null");
|
||||
buf[len - 1] = 0;
|
||||
return rt_strlen(buf);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const struct proc_ops proc_pid_fd_ops = {
|
||||
.readlink = proc_self_readlink,
|
||||
};
|
||||
|
||||
int proc_self_init(void)
|
||||
{
|
||||
struct proc_dentry *ent;
|
||||
|
||||
ent = proc_symlink("self", NULL, "NULL");
|
||||
if (ent)
|
||||
{
|
||||
ent->ops = &proc_pid_fd_ops;
|
||||
}
|
||||
proc_release(ent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_self_init);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
|
||||
static void *seq_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
off_t i = *index; // seq->index
|
||||
|
||||
return NULL + (i == 0);
|
||||
}
|
||||
|
||||
static void seq_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
off_t i = *index + 1; // seq->index
|
||||
|
||||
*index = i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int seq_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
int i;
|
||||
rt_cpu_t pcpu;
|
||||
rt_uint64_t user_total = 0;
|
||||
rt_uint64_t system_total = 0;
|
||||
rt_uint64_t idle_total = 0;
|
||||
|
||||
for (i = 0; i < RT_CPUS_NR; i++)
|
||||
{
|
||||
pcpu = rt_cpu_index(i);
|
||||
user_total = user_total + pcpu->cpu_stat.user;
|
||||
system_total = system_total + pcpu->cpu_stat.system;
|
||||
idle_total = idle_total + pcpu->cpu_stat.idle;
|
||||
}
|
||||
dfs_seq_printf(seq, "cpu %llu 0 %llu %llu 0 0 0 0 0 0\n", user_total, system_total, idle_total);
|
||||
|
||||
for (i = 0; i < RT_CPUS_NR; i++)
|
||||
{
|
||||
pcpu = rt_cpu_index(i);
|
||||
dfs_seq_printf(seq, "cpu%d ",i);
|
||||
dfs_seq_printf(seq, "%llu ",pcpu->cpu_stat.user);//user
|
||||
dfs_seq_printf(seq, "0 ");//nice
|
||||
dfs_seq_printf(seq, "%llu ",pcpu->cpu_stat.system);//system
|
||||
dfs_seq_printf(seq, "%llu ",pcpu->cpu_stat.idle);//idle
|
||||
dfs_seq_printf(seq, "0 ");//iowait
|
||||
dfs_seq_printf(seq, "0 ");//irq
|
||||
dfs_seq_printf(seq, "0 ");//softirq
|
||||
dfs_seq_printf(seq, "0 0 0\n");//steal,guest,guest_nice
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_seq_ops seq_ops = {
|
||||
.start = seq_start,
|
||||
.stop = seq_stop,
|
||||
.next = seq_next,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
rt_weak const struct dfs_seq_ops *stat_get_seq_ops(void)
|
||||
{
|
||||
return &seq_ops;
|
||||
}
|
||||
|
||||
static int proc_open(struct dfs_file *file)
|
||||
{
|
||||
return dfs_seq_open(file, stat_get_seq_ops());
|
||||
}
|
||||
|
||||
static int proc_close(struct dfs_file *file)
|
||||
{
|
||||
return dfs_seq_release(file);
|
||||
}
|
||||
|
||||
static const struct dfs_file_ops file_ops = {
|
||||
.open = proc_open,
|
||||
.read = dfs_seq_read,
|
||||
.lseek = dfs_seq_lseek,
|
||||
.close = proc_close,
|
||||
};
|
||||
|
||||
int proc_stat_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_data("stat", 0, NULL, &file_ops, NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_stat_init);
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
|
||||
static void *seq_start(struct dfs_seq_file *seq, off_t *index)
|
||||
{
|
||||
off_t i = *index; // seq->index
|
||||
|
||||
return NULL + (i == 0);
|
||||
}
|
||||
|
||||
static void seq_stop(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
off_t i = *index + 1; // seq->index
|
||||
|
||||
*index = i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int seq_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
/* data: The return value of the start or next*/
|
||||
dfs_seq_puts(seq, "todo\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_seq_ops seq_ops = {
|
||||
.start = seq_start,
|
||||
.stop = seq_stop,
|
||||
.next = seq_next,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
void proc_tty_register_driver(void *driver)
|
||||
{
|
||||
//todo
|
||||
}
|
||||
|
||||
void proc_tty_unregister_driver(void *driver)
|
||||
{
|
||||
//todo
|
||||
}
|
||||
|
||||
int proc_tty_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry;
|
||||
|
||||
dentry = proc_mkdir("tty", NULL);
|
||||
if (!dentry)
|
||||
return -1;
|
||||
|
||||
proc_release(dentry);
|
||||
|
||||
dentry = proc_mkdir("tty/ldisc", NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
dentry = proc_mkdir_mode("tty/driver", S_IRUSR|S_IXUSR, NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
dentry = proc_create_data("tty/ldiscs", 0, NULL, NULL, NULL);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->seq_ops = &seq_ops;
|
||||
}
|
||||
proc_release(dentry);
|
||||
|
||||
dentry = proc_create_data("tty/drivers", 0, NULL, NULL, NULL);
|
||||
if (dentry)
|
||||
{
|
||||
dentry->seq_ops = &seq_ops;
|
||||
}
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_tty_init);
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
|
||||
static int single_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
dfs_seq_printf(seq, "%lu.%02lu %lu.%02lu\n",
|
||||
(unsigned long)rt_tick_get_millisecond() / 1000, (unsigned long)(rt_tick_get_millisecond() % 1000) / 100,
|
||||
(unsigned long)rt_tick_get_millisecond() / 1000, (unsigned long)(rt_tick_get_millisecond() % 1000) / 100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proc_uptime_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_single_data("uptime", 0, NULL, single_show, NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_uptime_init);
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
|
||||
static int single_show(struct dfs_seq_file *seq, void *data)
|
||||
{
|
||||
dfs_seq_puts(seq, "\n \\ | /\n");
|
||||
#ifdef RT_USING_SMART
|
||||
dfs_seq_puts(seq, "- RT - Thread Smart Operating System\n");
|
||||
#else
|
||||
dfs_seq_puts(seq, "- RT - Thread Operating System\n");
|
||||
#endif
|
||||
dfs_seq_printf(seq, " / | \\ %d.%d.%d build %s %s\n",
|
||||
(rt_int32_t)RT_VERSION_MAJOR, (rt_int32_t)RT_VERSION_MINOR, (rt_int32_t)RT_VERSION_PATCH,
|
||||
__DATE__, __TIME__);
|
||||
dfs_seq_puts(seq, " 2006 - 2022 Copyright by RT-Thread team\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proc_version_init(void)
|
||||
{
|
||||
struct proc_dentry *dentry = proc_create_single_data("version", 0, NULL, single_show, NULL);
|
||||
proc_release(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_ENV_EXPORT(proc_version_init);
|
|
@ -0,0 +1,445 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dfs.h>
|
||||
#include <dfs_fs.h>
|
||||
#include <dfs_file.h>
|
||||
#include <dfs_posix.h>
|
||||
#include <dfs_mnt.h>
|
||||
#include <dfs_dentry.h>
|
||||
|
||||
#include "proc.h"
|
||||
#include "procfs.h"
|
||||
|
||||
#define PROC_DEBUG(...) //rt_kprintf
|
||||
|
||||
static int dfs_procfs_open(struct dfs_file *file)
|
||||
{
|
||||
rt_err_t ret = RT_EOK;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
|
||||
RT_ASSERT(file->vnode->ref_count > 0);
|
||||
if (file->vnode->ref_count > 1)
|
||||
{
|
||||
file->fpos = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (entry->fops && entry->fops->open)
|
||||
{
|
||||
ret = entry->fops->open(file);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dfs_procfs_close(struct dfs_file *file)
|
||||
{
|
||||
rt_err_t ret = RT_EOK;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
RT_ASSERT(file->vnode->ref_count > 0);
|
||||
if (file->vnode->ref_count > 1)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (entry && entry->fops && entry->fops->close)
|
||||
{
|
||||
ret = entry->fops->close(file);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t dfs_procfs_read(struct dfs_file *file, void *buf, size_t count, off_t *pos)
|
||||
{
|
||||
ssize_t ret = -RT_ERROR;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
if (entry && entry->fops && entry->fops->read)
|
||||
{
|
||||
ret = entry->fops->read(file, buf, count, pos);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t dfs_procfs_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos)
|
||||
{
|
||||
ssize_t ret = -RT_ERROR;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
if (entry && entry->fops && entry->fops->write)
|
||||
{
|
||||
ret = entry->fops->write(file, buf, count, pos);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dfs_procfs_ioctl(struct dfs_file *file, int cmd, void *args)
|
||||
{
|
||||
int ret = -RT_ERROR;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
if (entry && entry->fops && entry->fops->ioctl)
|
||||
{
|
||||
ret = entry->fops->ioctl(file, cmd, args);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dfs_procfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count)
|
||||
{
|
||||
int ret = 0;
|
||||
rt_uint32_t index = 0;
|
||||
struct dirent *d;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
if (entry)
|
||||
{
|
||||
struct proc_dentry *iter = RT_NULL, *tmp;
|
||||
|
||||
/* make integer count */
|
||||
count = (count / sizeof(struct dirent));
|
||||
if (count == 0)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dfs_vfs_for_each_subnode(iter, tmp, entry, node)
|
||||
{
|
||||
if (iter == RT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (index >= file->fpos)
|
||||
{
|
||||
d = dirp + index - file->fpos;
|
||||
|
||||
if (S_ISDIR(entry->mode))
|
||||
{
|
||||
d->d_type = DT_DIR;
|
||||
}
|
||||
else if (S_ISLNK(entry->mode))
|
||||
{
|
||||
d->d_type = DT_SYMLINK;
|
||||
}
|
||||
else
|
||||
{
|
||||
d->d_type = DT_REG;
|
||||
}
|
||||
|
||||
d->d_namlen = rt_strlen(iter->name);
|
||||
d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
|
||||
rt_strncpy(d->d_name, iter->name, rt_strlen(iter->name) + 1);
|
||||
|
||||
ret ++;
|
||||
}
|
||||
|
||||
index++;
|
||||
if (index - file->fpos >= count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret > 0)
|
||||
{
|
||||
file->fpos = index;
|
||||
}
|
||||
|
||||
if (entry->fops && entry->fops->getdents && ret < count)
|
||||
{
|
||||
int r;
|
||||
|
||||
file->fpos -= index;
|
||||
|
||||
r = entry->fops->getdents(file, dirp + ret, (count - ret) * sizeof(struct dirent));
|
||||
|
||||
ret = ret * sizeof(struct dirent);
|
||||
|
||||
if (r > 0)
|
||||
{
|
||||
ret += r;
|
||||
}
|
||||
|
||||
file->fpos += index;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = ret * sizeof(struct dirent);
|
||||
}
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dfs_procfs_poll(struct dfs_file *file, struct rt_pollreq *req)
|
||||
{
|
||||
int ret = -RT_ERROR;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
if (entry && entry->fops && entry->fops->poll)
|
||||
{
|
||||
ret = entry->fops->poll(file, req);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dfs_procfs_flush(struct dfs_file *file)
|
||||
{
|
||||
int ret = -RT_ERROR;
|
||||
struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
|
||||
|
||||
if (entry && entry->fops && entry->fops->flush)
|
||||
{
|
||||
ret = entry->fops->flush(file);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, file->dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dfs_procfs_mount(struct dfs_mnt *mnt, unsigned long rwflag, const void *data)
|
||||
{
|
||||
RT_ASSERT(mnt != RT_NULL);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static int dfs_procfs_umount(struct dfs_mnt *mnt)
|
||||
{
|
||||
RT_ASSERT(mnt != RT_NULL);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static int dfs_procfs_readlink(struct dfs_dentry *dentry, char *buf, int len)
|
||||
{
|
||||
int ret = 0;
|
||||
struct proc_dentry *entry = dfs_proc_find(dentry->pathname);
|
||||
|
||||
if (entry)
|
||||
{
|
||||
if (S_ISLNK(entry->mode) && entry->data)
|
||||
{
|
||||
if (entry->ops && entry->ops->readlink)
|
||||
{
|
||||
ret = entry->ops->readlink(entry, buf, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_strncpy(buf, (const char *)entry->data, len);
|
||||
buf[len - 1] = '\0';
|
||||
ret = rt_strlen(buf);
|
||||
}
|
||||
}
|
||||
|
||||
proc_release(entry);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dfs_procfs_unlink(struct dfs_dentry *dentry)
|
||||
{
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, dentry->pathname, -1);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
static int dfs_procfs_stat(struct dfs_dentry *dentry, struct stat *st)
|
||||
{
|
||||
int ret = RT_EOK;
|
||||
struct dfs_vnode *vnode;
|
||||
|
||||
if (dentry && dentry->vnode)
|
||||
{
|
||||
vnode = dentry->vnode;
|
||||
|
||||
st->st_dev = (dev_t)(dentry->mnt->dev_id);
|
||||
st->st_ino = (ino_t)dfs_dentry_full_path_crc32(dentry);
|
||||
|
||||
st->st_gid = vnode->gid;
|
||||
st->st_uid = vnode->uid;
|
||||
st->st_mode = vnode->mode;
|
||||
st->st_nlink = vnode->nlink;
|
||||
st->st_size = vnode->size;
|
||||
st->st_mtim.tv_nsec = vnode->mtime.tv_nsec;
|
||||
st->st_mtim.tv_sec = vnode->mtime.tv_sec;
|
||||
st->st_ctim.tv_nsec = vnode->ctime.tv_nsec;
|
||||
st->st_ctim.tv_sec = vnode->ctime.tv_sec;
|
||||
st->st_atim.tv_nsec = vnode->atime.tv_nsec;
|
||||
st->st_atim.tv_sec = vnode->atime.tv_sec;
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s ret: %d\n", __func__, __LINE__, dentry->pathname, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dfs_procfs_statfs(struct dfs_mnt *mnt, struct statfs *buf)
|
||||
{
|
||||
if (mnt && buf)
|
||||
{
|
||||
buf->f_bsize = 512;
|
||||
buf->f_blocks = 2048 * 64; // 64M
|
||||
buf->f_bfree = buf->f_blocks;
|
||||
buf->f_bavail = buf->f_bfree;
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d\n", __func__, __LINE__);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static struct dfs_vnode *dfs_procfs_lookup(struct dfs_dentry *dentry)
|
||||
{
|
||||
struct dfs_vnode *vnode = RT_NULL;
|
||||
struct proc_dentry *entry = dfs_proc_find(dentry->pathname);
|
||||
|
||||
if (entry)
|
||||
{
|
||||
vnode = dfs_vnode_create();
|
||||
if (vnode)
|
||||
{
|
||||
vnode->nlink = 1;
|
||||
vnode->size = 0;
|
||||
if (S_ISDIR(entry->mode))
|
||||
{
|
||||
vnode->mode = entry->mode;
|
||||
vnode->type = FT_DIRECTORY;
|
||||
}
|
||||
else if (S_ISLNK(entry->mode))
|
||||
{
|
||||
vnode->mode = entry->mode;
|
||||
vnode->type = FT_SYMLINK;
|
||||
}
|
||||
else
|
||||
{
|
||||
vnode->mode = entry->mode;
|
||||
vnode->type = FT_REGULAR;
|
||||
}
|
||||
|
||||
vnode->data = entry;
|
||||
vnode->mnt = dentry->mnt;
|
||||
}
|
||||
|
||||
proc_release(entry);
|
||||
}
|
||||
|
||||
PROC_DEBUG(" %s %d >> %s\n", __func__, __LINE__, dentry->pathname);
|
||||
|
||||
return vnode;
|
||||
}
|
||||
|
||||
static struct dfs_vnode *dfs_procfs_create_vnode(struct dfs_dentry *dentry, int type, mode_t mode)
|
||||
{
|
||||
return RT_NULL;
|
||||
}
|
||||
|
||||
static int dfs_procfs_free_vnode(struct dfs_vnode *vnode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dfs_file_ops _dev_fops =
|
||||
{
|
||||
.open = dfs_procfs_open,
|
||||
.close = dfs_procfs_close,
|
||||
.lseek = generic_dfs_lseek,
|
||||
.read = dfs_procfs_read,
|
||||
.write = dfs_procfs_write,
|
||||
.ioctl = dfs_procfs_ioctl,
|
||||
.getdents = dfs_procfs_getdents,
|
||||
.poll = dfs_procfs_poll,
|
||||
.flush = dfs_procfs_flush,
|
||||
};
|
||||
|
||||
static const struct dfs_filesystem_ops _procfs_ops =
|
||||
{
|
||||
.name = "procfs",
|
||||
|
||||
.default_fops = &_dev_fops,
|
||||
|
||||
.mount = dfs_procfs_mount,
|
||||
.umount = dfs_procfs_umount,
|
||||
.readlink = dfs_procfs_readlink,
|
||||
.unlink = dfs_procfs_unlink,
|
||||
.stat = dfs_procfs_stat,
|
||||
.statfs = dfs_procfs_statfs,
|
||||
.lookup = dfs_procfs_lookup,
|
||||
.create_vnode = dfs_procfs_create_vnode,
|
||||
.free_vnode = dfs_procfs_free_vnode,
|
||||
};
|
||||
|
||||
static struct dfs_filesystem_type _procfs =
|
||||
{
|
||||
.fs_ops = &_procfs_ops,
|
||||
};
|
||||
|
||||
int dfs_procfs_init(void)
|
||||
{
|
||||
/* register procfs file system */
|
||||
dfs_register(&_procfs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_COMPONENT_EXPORT(dfs_procfs_init);
|
||||
|
||||
int proc_read_data(struct dfs_file *file, void *buf, size_t count, off_t *pos)
|
||||
{
|
||||
if (file->fpos >= file->vnode->size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (file->data)
|
||||
{
|
||||
count = file->vnode->size - file->fpos >= count ? count : file->vnode->size - file->fpos;
|
||||
rt_strncpy(buf, file->data + file->fpos, count);
|
||||
|
||||
file->fpos += count;
|
||||
*pos = file->fpos;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#ifndef __PROC_FS_H__
|
||||
#define __PROC_FS_H__
|
||||
|
||||
#include <dfs_file.h>
|
||||
|
||||
int dfs_procfs_init(void);
|
||||
|
||||
int proc_read_data(struct dfs_file *file, void *buf, size_t count, off_t *pos);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue