procfs: Add support for routing tables.

This commit is contained in:
Gregory Nutt 2017-08-11 14:50:39 -06:00
parent fdca465da6
commit 65698da12d
9 changed files with 300 additions and 122 deletions

5
TODO
View file

@ -2097,6 +2097,11 @@ o Build system
$ make apps_distclean
One solution to this might be to making the special target
.PRECIOUS depend on apps/libapps.a. Then if make receives a
signal, it will not delete apps/libapps.a. This would have to
be done in all Makefiles.
Status Open
Priority: Medium-High. It is a rare event that control-C happens at just the
point in time. However, when it does occur the resulting code may

View file

@ -63,6 +63,11 @@ config FS_PROCFS_EXCLUDE_NET
depends on NET
default n
config FS_PROC_EXCLUDE_ROUTE
bool "Exclude routing table"
depends on !FS_PROCFS_EXCLUDE_NET && NET_ROUTE
default n
config FS_PROCFS_EXCLUDE_MTD
bool "Exclude mtd"
depends on MTD

View file

@ -87,6 +87,7 @@ extern const struct procfs_operations uptime_operations;
*/
extern const struct procfs_operations net_procfsoperations;
extern const struct procfs_operations net_procfs_routeoperations;
extern const struct procfs_operations mtd_procfsoperations;
extern const struct procfs_operations part_procfsoperations;
extern const struct procfs_operations smartfs_procfsoperations;
@ -112,42 +113,46 @@ static const struct procfs_entry_s g_procfs_entries[] =
#endif
{
#ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS
{ "[0-9]*/**", &proc_operations },
{ "[0-9]*", &proc_operations },
{ "[0-9]*/**", &proc_operations, PROCFS_UNKOWN_TYPE },
{ "[0-9]*", &proc_operations, PROCFS_DIR_TYPE },
#endif
#if defined(CONFIG_SCHED_CPULOAD) && !defined(CONFIG_FS_PROCFS_EXCLUDE_CPULOAD)
{ "cpuload", &cpuload_operations },
{ "cpuload", &cpuload_operations, PROCFS_FILE_TYPE },
#endif
#if defined(CONFIG_MM_KERNEL_HEAP) && !defined(CONFIG_FS_PROCFS_EXCLUDE_KMM)
{ "kmm", &kmm_operations },
{ "kmm", &kmm_operations, PROCFS_FILE_TYPE },
#endif
#if defined(CONFIG_MODULE) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MODULE)
{ "modules", &module_operations },
{ "modules", &module_operations, PROCFS_DIR_TYPE },
#endif
#if defined(CONFIG_FS_SMARTFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
//{ "fs/smartfs", &smartfs_procfsoperations },
{ "fs/smartfs**", &smartfs_procfsoperations },
//{ "fs/smartfs", &smartfs_procfsoperations, PROCFS_DIR_TYPE },
{ "fs/smartfs**", &smartfs_procfsoperations, PROCFS_UNKOWN_TYPE },
#endif
#if defined(CONFIG_NET) && !defined(CONFIG_FS_PROCFS_EXCLUDE_NET)
{ "net", &net_procfsoperations },
{ "net/**", &net_procfsoperations },
{ "net", &net_procfsoperations, PROCFS_DIR_TYPE },
#if defined(CONFIG_NET_ROUTE) && !defined(CONFIG_FS_PROCFS_EXCLUDE_ROUTE)
{ "net/route", &net_procfs_routeoperations, PROCFS_DIR_TYPE },
{ "net/route/**", &net_procfs_routeoperations, PROCFS_UNKOWN_TYPE },
#endif
{ "net/**", &net_procfsoperations, PROCFS_UNKOWN_TYPE },
#endif
#if defined(CONFIG_MTD) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MTD)
{ "mtd", &mtd_procfsoperations },
{ "mtd", &mtd_procfsoperations, PROCFS_FILE_TYPE },
#endif
#if defined(CONFIG_MTD_PARTITION) && !defined(CONFIG_FS_PROCFS_EXCLUDE_PARTITIONS)
{ "partitions", &part_procfsoperations },
{ "partitions", &part_procfsoperations, PROCFS_DIR_TYPE },
#endif
#if !defined(CONFIG_FS_PROCFS_EXCLUDE_UPTIME)
{ "uptime", &uptime_operations },
{ "uptime", &uptime_operations, PROCFS_FILE_TYPE },
#endif
};
@ -348,6 +353,7 @@ static int procfs_open(FAR struct file *filep, FAR const char *relpath,
((struct procfs_file_s *) filep->f_priv)->procfsentry =
&g_procfs_entries[x];
break;
}
}
}
@ -673,6 +679,7 @@ static int procfs_closedir(FAR struct inode *mountpt,
static int procfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
{
FAR const struct procfs_entry_s *entry = NULL;
FAR struct procfs_dir_priv_s *priv;
FAR struct procfs_level0_s *level0;
FAR struct tcb_s *tcb;
@ -705,7 +712,9 @@ static int procfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
while (index < priv->nentries + g_procfs_entrycount)
{
name = g_procfs_entries[index - priv->nentries].pathpattern;
entry = &g_procfs_entries[index - priv->nentries];
name = entry->pathpattern;
while (*name != '/' && *name != '\0')
{
if (*name == '*' || *name == '[' || *name == '?')
@ -772,7 +781,7 @@ static int procfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
strncpy(dir->fd_dir.d_name, name, level0->lastlen);
dir->fd_dir.d_name[level0->lastlen] = '\0';
if (name[level0->lastlen] == '/')
if (entry->type == PROCFS_DIR_TYPE)
{
dir->fd_dir.d_type = DTYPE_DIRECTORY;
}

View file

@ -93,6 +93,15 @@ struct procfs_operations
/* Procfs handler prototypes ************************************************/
/* These are the types of entries that may appear in the procfs: */
enum procfs_entry_e
{
PROCFS_UNKOWN_TYPE = 0, /* Unknown type */
PROCFS_FILE_TYPE, /* File type */
PROCFS_DIR_TYPE, /* Directory type */
};
/* This is a procfs entry that each handler should provide to supply
* specific operations for file and directory handling.
*/
@ -101,6 +110,7 @@ struct procfs_entry_s
{
FAR const char *pathpattern;
FAR const struct procfs_operations *ops;
uint8_t type;
};
/* Specifies the common elements for an open file in the procfs

View file

@ -47,6 +47,12 @@ ifeq ($(CONFIG_NET_STATISTICS),y)
NET_CSRCS += net_statistics.c
endif
# Routing table
ifeq ($(CONFIG_NET_ROUTE),y)
NET_CSRCS += net_procfs_route.c
endif
# Include packet socket build support
DEPPATH += --dep-path procfs

View file

@ -56,6 +56,7 @@
#include <nuttx/fs/fs.h>
#include <nuttx/fs/procfs.h>
#include <nuttx/fs/dirent.h>
#include <nuttx/lib/regex.h>
#include <nuttx/net/netdev.h>
#include "netdev/netdev.h"
@ -64,6 +65,26 @@
#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_PROCFS) && \
!defined(CONFIG_FS_PROCFS_EXCLUDE_NET)
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Directory entry indices */
#if defined(CONFIG_NET_STATISTICS) && defined(CONFIG_NET_ROUTE)
# define STAT_INDEX 0
# define ROUTE_INDEX 1
# define DEV_INDEX 2
#elif defined(CONFIG_NET_STATISTICS)
# define STAT_INDEX 0
# define DEV_INDEX 1
#elif defined(CONFIG_NET_ROUTE)
# define ROUTE_INDEX 0
# define DEV_INDEX 1
#else
# define DEV_INDEX 0
#endif
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
@ -112,6 +133,8 @@ const struct procfs_operations net_procfsoperations =
netprocfs_stat /* stat */
};
extern const struct procfs_operations net_procfs_routeoperations;
/****************************************************************************
* Private Functions
****************************************************************************/
@ -125,6 +148,7 @@ static int netprocfs_open(FAR struct file *filep, FAR const char *relpath,
{
FAR struct netprocfs_file_s *priv;
FAR struct net_driver_s *dev;
enum netprocfs_entry_e entry;
finfo("Open '%s'\n", relpath);
@ -141,18 +165,29 @@ static int netprocfs_open(FAR struct file *filep, FAR const char *relpath,
return -EACCES;
}
#ifdef CONFIG_NET_STATISTICS
/* "net/stat" is an acceptable value for the relpath only if network layer
* statistics are enabled.
*/
#ifdef CONFIG_NET_STATISTICS
if (strcmp(relpath, "net/stat") == 0)
{
/* A NULL network device reference is a clue that we are processing
* the network statistics file.
*/
entry = NETPROCFS_SUBDIR_STAT;
dev = NULL;
}
else
#endif
dev = NULL;
#ifdef CONFIG_NET_ROUTE
/* "net/route" is an acceptable value for the relpath only if routing
* table support is initialized.
*/
if (match("net/route/**", relpath))
{
/* Use the /net/route directory */
return net_procfs_routeoperations.open(filep, relpath, oflags, mode);
}
else
#endif
@ -180,11 +215,14 @@ static int netprocfs_open(FAR struct file *filep, FAR const char *relpath,
ferr("ERROR: relpath is '%s'\n", relpath);
return -ENOENT;
}
entry = NETPROCFS_SUBDIR_DEV;
}
/* Allocate the open file structure */
priv = (FAR struct netprocfs_file_s *)kmm_zalloc(sizeof(struct netprocfs_file_s));
priv = (FAR struct netprocfs_file_s *)
kmm_zalloc(sizeof(struct netprocfs_file_s));
if (!priv)
{
ferr("ERROR: Failed to allocate file attributes\n");
@ -193,7 +231,8 @@ static int netprocfs_open(FAR struct file *filep, FAR const char *relpath,
/* Initialize the open-file structure */
priv->dev = dev;
priv->dev = dev;
priv->entry = entry;
/* Save the open file structure as the open-specific state in
* filep->f_priv.
@ -240,24 +279,33 @@ static ssize_t netprocfs_read(FAR struct file *filep, FAR char *buffer,
priv = (FAR struct netprocfs_file_s *)filep->f_priv;
DEBUGASSERT(priv);
#ifdef CONFIG_NET_STATISTICS
/* A NULL device structure reference is the key that we are showing the
* network statistics.
*/
/* Read according to the sub-directory */
if (priv->dev == NULL)
switch (priv->entry)
{
/* Show the network layer statistics */
case NETPROCFS_SUBDIR_DEV:
/* Show device-specific statistics */
nreturned = netprocfs_read_netstats(priv, buffer, buflen);
}
else
nreturned = netprocfs_read_devstats(priv, buffer, buflen);
break;
#ifdef CONFIG_NET_STATISTICS
case NETPROCFS_SUBDIR_STAT:
/* Show the network layer statistics */
nreturned = netprocfs_read_netstats(priv, buffer, buflen);
break;
#endif
{
/* Otherwise, we are showing device-specific statistics */
nreturned = netprocfs_read_devstats(priv, buffer, buflen);
}
#ifdef CONFIG_NET_ROUTE
case NETPROCFS_SUBDIR_ROUTE:
nerr("ERROR: Cannot read from directory net/route\n");
#endif
default:
nerr("ERROR: Invalid entry for reading: %u\n", priv->entry);
nreturned = -EINVAL;
}
/* Update the file offset */
@ -321,13 +369,56 @@ static int netprocfs_opendir(FAR const char *relpath,
{
FAR struct netprocfs_level1_s *level1;
int ndevs;
int ret;
finfo("relpath: \"%s\"\n", relpath ? relpath : "NULL");
DEBUGASSERT(relpath && dir && !dir->u.procfs);
/* "net" is the only value of relpath that is a directory */
/* "net" and "net/route" are the only values of relpath that are
* directories.
*/
if (strcmp(relpath, "net") != 0)
#ifdef CONFIG_NET_ROUTE
if (match("net/route", relpath) || match("net/route/**", relpath))
{
/* Use the /net/route directory */
return net_procfs_routeoperations.opendir(relpath, dir);
}
#endif
/* Assume that path refers to the 1st level subdirectory. Allocate the
* level1 the dirent structure before checking.
*/
level1 = (FAR struct netprocfs_level1_s *)
kmm_zalloc(sizeof(struct netprocfs_level1_s));
if (level1 == NULL)
{
ferr("ERROR: Failed to allocate the level1 directory structure\n");
return -ENOMEM;
}
level1->base.level = 1;
if (strcmp(relpath, "net") == 0)
{
/* Count the number of network devices */
ndevs = netdev_count();
/* Initialze base structure components */
level1->base.nentries = ndevs;
#ifdef CONFIG_NET_STATISTICS
level1->base.nentries++;
#endif
#ifdef CONFIG_NET_ROUTE
level1->base.nentries++;
#endif
}
else
{
/* REVISIT: We really need to check if the relpath refers to a network
* device. In that case, we need to return -ENOTDIR. Otherwise, we
@ -335,38 +426,16 @@ static int netprocfs_opendir(FAR const char *relpath,
*/
ferr("ERROR: Bad relpath: %s\n", relpath);
return -ENOTDIR;
ret = -ENOTDIR;
goto errout_with_alloc;
}
/* The path refers to the 1st level sbdirectory. Allocate the level1
* dirent structure.
*/
level1 = (FAR struct netprocfs_level1_s *)
kmm_zalloc(sizeof(struct netprocfs_level1_s));
if (!level1)
{
ferr("ERROR: Failed to allocate the level1 directory structure\n");
return -ENOMEM;
}
/* Count the number of network devices */
ndevs = netdev_count();
/* Initialze base structure components */
level1->base.level = 1;
#ifdef CONFIG_NET_STATISTICS
level1->base.nentries = ndevs + 1;
#else
level1->base.nentries = ndevs;
#endif
level1->base.index = 0;
dir->u.procfs = (FAR void *) level1;
dir->u.procfs = (FAR void *)level1;
return OK;
errout_with_alloc:
kmm_free(level1);
return ret;
}
/****************************************************************************
@ -404,63 +473,86 @@ static int netprocfs_readdir(FAR struct fs_dirent_s *dir)
FAR struct netprocfs_level1_s *level1;
FAR struct net_driver_s *dev;
int index;
int ret;
DEBUGASSERT(dir && dir->u.procfs);
level1 = dir->u.procfs;
DEBUGASSERT(level1->base.level == 1);
DEBUGASSERT(level1->base.level > 0);
/* Have we reached the end of the directory */
index = level1->base.index;
DEBUGASSERT(index <= level1->base.nentries);
if (index >= level1->base.nentries)
{
/* We signal the end of the directory by returning the special
* error -ENOENT.
*/
finfo("Entry %d: End of directory\n", index);
return -ENOENT;
}
#ifdef CONFIG_NET_STATISTICS
else if (index == 0)
{
/* Copy the network statistics directory entry */
dir->fd_dir.d_type = DTYPE_FILE;
strncpy(dir->fd_dir.d_name, "stat", NAME_MAX + 1);
}
else
#endif
{
int devndx = index;
#ifdef CONFIG_NET_STATISTICS
/* Subtract one to account for index == 0 which is used for network
* status.
*/
devndx--;
#endif
/* Find the device corresponding to this device index */
dev = netdev_findbyindex(devndx);
/* Copy the device statistics file entry */
dir->fd_dir.d_type = DTYPE_FILE;
strncpy(dir->fd_dir.d_name, dev->d_ifname, NAME_MAX + 1);
}
/* Set up the next directory entry offset. NOTE that we could use the
* standard f_pos instead of our own private index.
/* Are we searching this directory? Or is it just an intermediate on the
* way to a sub-directory?
*/
level1->base.index = index + 1;
return OK;
if (level1->base.level == 1)
{
/* This directory.. Have we reached the end of the directory? */
index = level1->base.index;
DEBUGASSERT(index <= level1->base.nentries);
if (index >= level1->base.nentries)
{
/* We signal the end of the directory by returning the special
* error -ENOENT.
*/
finfo("Entry %d: End of directory\n", index);
return -ENOENT;
}
#ifdef CONFIG_NET_STATISTICS
else if (index == STAT_INDEX)
{
/* Copy the network statistics directory entry */
dir->fd_dir.d_type = DTYPE_FILE;
strncpy(dir->fd_dir.d_name, "stat", NAME_MAX + 1);
}
else
#endif
#ifdef CONFIG_NET_ROUTE
if (index == ROUTE_INDEX)
{
/* Copy the network statistics directory entry */
dir->fd_dir.d_type = DTYPE_DIRECTORY;
strncpy(dir->fd_dir.d_name, "route", NAME_MAX + 1);
}
else
#endif
{
int devndx = index - DEV_INDEX;
/* Find the device corresponding to this device index */
dev = netdev_findbyindex(devndx);
/* Copy the device statistics file entry */
dir->fd_dir.d_type = DTYPE_FILE;
strncpy(dir->fd_dir.d_name, dev->d_ifname, NAME_MAX + 1);
}
/* Set up the next directory entry offset. NOTE that we could use the
* standard f_pos instead of our own private index.
*/
level1->base.index = index + 1;
ret = OK;
}
else
{
/* We are performing a directory search of one of the subdirectories
* and we must let the handler perform the read.
*/
DEBUGASSERT(level1->base.procfsentry != NULL &&
level1->base.procfsentry->ops->readdir != NULL);
ret = level1->base.procfsentry->ops->readdir(dir);
}
return ret;
}
/****************************************************************************
@ -505,6 +597,15 @@ static int netprocfs_stat(FAR const char *relpath, FAR struct stat *buf)
buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR;
}
else
#endif
#ifdef CONFIG_NET_ROUTE
/* Check for network statistics "net/stat" */
if (strcmp(relpath, "net/route") == 0)
{
buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR;
}
else
#endif
{
FAR struct net_driver_s *dev;

View file

@ -82,7 +82,7 @@ static int netprocfs_retransmissions(FAR struct netprocfs_file_s *netfile);
/* Line generating functions */
static const linegen_t g_linegen[] =
static const linegen_t g_stat_linegen[] =
{
netprocfs_header,
netprocfs_received,
@ -111,7 +111,7 @@ static const linegen_t g_linegen[] =
#endif /* CONFIG_NET_TCP */
};
#define NSTAT_LINES (sizeof(g_linegen) / sizeof(linegen_t))
#define NSTAT_LINES (sizeof(g_stat_linegen) / sizeof(linegen_t))
/****************************************************************************
* Private Functions
@ -456,7 +456,7 @@ static int netprocfs_retransmissions(FAR struct netprocfs_file_s *netfile)
ssize_t netprocfs_read_netstats(FAR struct netprocfs_file_s *priv,
FAR char *buffer, size_t buflen)
{
return netprocfs_read_linegen(priv, buffer, buflen, g_linegen, NSTAT_LINES);
return netprocfs_read_linegen(priv, buffer, buflen, g_stat_linegen, NSTAT_LINES);
}
#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS &&

View file

@ -84,7 +84,7 @@ static int netprocfs_errors(FAR struct netprocfs_file_s *netfile);
/* Line generating functions */
static const linegen_t g_linegen[] =
static const linegen_t g_netstat_linegen[] =
{
netprocfs_linklayer
#ifdef CONFIG_NET_IPv4
@ -105,7 +105,7 @@ static const linegen_t g_linegen[] =
#endif /* CONFIG_NETDEV_STATISTICS */
};
#define NSTAT_LINES (sizeof(g_linegen) / sizeof(linegen_t))
#define NSTAT_LINES (sizeof(g_netstat_linegen) / sizeof(linegen_t))
/****************************************************************************
* Private Functions
@ -554,7 +554,8 @@ static int netprocfs_errors(FAR struct netprocfs_file_s *netfile)
ssize_t netprocfs_read_devstats(FAR struct netprocfs_file_s *priv,
FAR char *buffer, size_t buflen)
{
return netprocfs_read_linegen(priv, buffer, buflen, g_linegen, NSTAT_LINES);
return netprocfs_read_linegen(priv, buffer, buflen, g_netstat_linegen,
NSTAT_LINES);
}
#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS &&

View file

@ -50,6 +50,10 @@
* Pre-processor Definitions
****************************************************************************/
#ifdef CONFIG_FS_PROCFS_EXCLUDE_ROUTE
# undef CONFIG_NET_ROUTE
#endif
/* Determines the size of an intermediate buffer that must be large enough
* to handle the longest line generated by this logic.
*/
@ -60,6 +64,19 @@
* Public Type Definitions
****************************************************************************/
/* Describes the /net directory entries */
enum netprocfs_entry_e
{
NETPROCFS_SUBDIR_DEV = 0 /* Multiple instances, e.g. /proc/net/eth0 */
#ifdef CONFIG_NET_STATISTICS
, NETPROCFS_SUBDIR_STAT /* /proc/net/stat */
#endif
#ifdef CONFIG_NET_ROUTE
, NETPROCFS_SUBDIR_ROUTE /* /proc/net/route */
#endif
};
/* This structure describes one open "file" */
struct net_driver_s; /* Forward reference */
@ -70,6 +87,7 @@ struct netprocfs_file_s
uint8_t lineno; /* Line number */
uint8_t linesize; /* Number of valid characters in line[] */
uint8_t offset; /* Offset to first valid character in line[] */
uint8_t entry; /* See enum netprocfs_entry_e */
char line[NET_LINELEN]; /* Pre-allocated buffer for formatted lines */
};
@ -148,6 +166,29 @@ ssize_t netprocfs_read_netstats(FAR struct netprocfs_file_s *priv,
FAR char *buffer, size_t buflen);
#endif
/****************************************************************************
* Name: netprocfs_read_routes
*
* Description:
* Read and format routing table entries.
*
* Input Parameters:
* priv - A reference to the network procfs file structure
* buffer - The user-provided buffer into which network status will be
* returned.
* bulen - The size in bytes of the user provided buffer.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned
* on failure.
*
****************************************************************************/
#ifdef CONFIG_NET_ROUTE
ssize_t netprocfs_read_routes(FAR struct netprocfs_file_s *priv,
FAR char *buffer, size_t buflen);
#endif
/****************************************************************************
* Name: netprocfs_read_devstats
*