/**************************************************************************** * fs/procfs/fs_procfs.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mount/mount.h" #include "sched/sched.h" /**************************************************************************** * External Definitions ****************************************************************************/ extern const struct procfs_operations g_clk_operations; extern const struct procfs_operations g_cpuinfo_operations; extern const struct procfs_operations g_cpuload_operations; extern const struct procfs_operations g_critmon_operations; extern const struct procfs_operations g_fdt_operations; extern const struct procfs_operations g_iobinfo_operations; extern const struct procfs_operations g_irq_operations; extern const struct procfs_operations g_meminfo_operations; extern const struct procfs_operations g_memdump_operations; extern const struct procfs_operations g_mempool_operations; extern const struct procfs_operations g_module_operations; extern const struct procfs_operations g_pm_operations; extern const struct procfs_operations g_proc_operations; extern const struct procfs_operations g_tcbinfo_operations; extern const struct procfs_operations g_thermal_operations; extern const struct procfs_operations g_uptime_operations; extern const struct procfs_operations g_version_operations; extern const struct procfs_operations g_pressure_operations; /* This is not good. These are implemented in other sub-systems. Having to * deal with them here is not a good coupling. What is really needed is a * run-time procfs registration system vs. a build time, fixed procfs * configuration. */ extern const struct procfs_operations g_mount_operations; extern const struct procfs_operations g_net_operations; extern const struct procfs_operations g_netroute_operations; extern const struct procfs_operations g_part_operations; extern const struct procfs_operations g_smartfs_procfs_operations; /**************************************************************************** * Private Types ****************************************************************************/ /* Table of all known / pre-registered procfs handlers / participants. */ #ifdef CONFIG_FS_PROCFS_REGISTER static const struct procfs_entry_s g_base_entries[] = #else static const struct procfs_entry_s g_procfs_entries[] = #endif { #ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS { "[0-9]*/**", &g_proc_operations, PROCFS_UNKOWN_TYPE }, { "[0-9]*", &g_proc_operations, PROCFS_DIR_TYPE }, #endif #if defined(CONFIG_CLK) && !defined(CONFIG_FS_PROCFS_EXCLUDE_CLK) { "clk", &g_clk_operations, PROCFS_FILE_TYPE }, #endif #if defined(CONFIG_ARCH_HAVE_CPUINFO) && !defined(CONFIG_FS_PROCFS_EXCLUDE_CPUINFO) { "cpuinfo", &g_cpuinfo_operations, PROCFS_FILE_TYPE }, #endif #if !defined(CONFIG_SCHED_CPULOAD_NONE) && \ !defined(CONFIG_FS_PROCFS_EXCLUDE_CPULOAD) { "cpuload", &g_cpuload_operations, PROCFS_FILE_TYPE }, #endif #ifdef CONFIG_SCHED_CRITMONITOR { "critmon", &g_critmon_operations, PROCFS_FILE_TYPE }, #endif #if defined(CONFIG_DEVICE_TREE) && !defined(CONFIG_FS_PROCFS_EXCLUDE_FDT) { "fdt", &g_fdt_operations, PROCFS_FILE_TYPE }, #endif #ifndef CONFIG_FS_PROCFS_EXCLUDE_BLOCKS { "fs/blocks", &g_mount_operations, PROCFS_FILE_TYPE }, #endif #ifndef CONFIG_FS_PROCFS_EXCLUDE_MOUNT { "fs/mount", &g_mount_operations, PROCFS_FILE_TYPE }, #endif #if defined(CONFIG_FS_SMARTFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS) { "fs/smartfs**", &g_smartfs_procfs_operations, PROCFS_UNKOWN_TYPE }, #endif #ifndef CONFIG_FS_PROCFS_EXCLUDE_USAGE { "fs/usage", &g_mount_operations, PROCFS_FILE_TYPE }, #endif #if defined(CONFIG_MM_IOB) && !defined(CONFIG_FS_PROCFS_EXCLUDE_IOBINFO) { "iobinfo", &g_iobinfo_operations, PROCFS_FILE_TYPE }, #endif #ifdef CONFIG_SCHED_IRQMONITOR { "irqs", &g_irq_operations, PROCFS_FILE_TYPE }, #endif #ifndef CONFIG_FS_PROCFS_EXCLUDE_MEMINFO # ifndef CONFIG_FS_PROCFS_EXCLUDE_MEMDUMP { "memdump", &g_memdump_operations, PROCFS_FILE_TYPE }, # endif { "meminfo", &g_meminfo_operations, PROCFS_FILE_TYPE }, #endif #if defined(CONFIG_MM_HEAP_MEMPOOL) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MEMPOOL) { "mempool", &g_mempool_operations, PROCFS_FILE_TYPE }, #endif #if defined(CONFIG_MODULE) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MODULE) { "modules", &g_module_operations, PROCFS_FILE_TYPE }, #endif #if defined(CONFIG_NET) && !defined(CONFIG_FS_PROCFS_EXCLUDE_NET) { "net", &g_net_operations, PROCFS_DIR_TYPE }, # if defined(CONFIG_NET_ROUTE) && !defined(CONFIG_FS_PROCFS_EXCLUDE_ROUTE) { "net/route", &g_netroute_operations, PROCFS_DIR_TYPE }, { "net/route/**", &g_netroute_operations, PROCFS_UNKOWN_TYPE }, # endif { "net/**", &g_net_operations, PROCFS_UNKOWN_TYPE }, #endif #if defined(CONFIG_MTD_PARTITION) && !defined(CONFIG_FS_PROCFS_EXCLUDE_PARTITIONS) { "partitions", &g_part_operations, PROCFS_FILE_TYPE }, #endif #if defined(CONFIG_PM) && defined(CONFIG_PM_PROCFS) { "pm", &g_pm_operations, PROCFS_DIR_TYPE }, { "pm/**", &g_pm_operations, PROCFS_UNKOWN_TYPE }, #endif #ifdef CONFIG_FS_PROCFS_INCLUDE_PRESSURE { "pressure", &g_pressure_operations, PROCFS_DIR_TYPE }, { "pressure/**", &g_pressure_operations, PROCFS_FILE_TYPE }, #endif #ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS { "self", &g_proc_operations, PROCFS_DIR_TYPE }, { "self/**", &g_proc_operations, PROCFS_UNKOWN_TYPE }, #endif #if defined(CONFIG_ARCH_HAVE_TCBINFO) && !defined(CONFIG_FS_PROCFS_EXCLUDE_TCBINFO) { "tcbinfo", &g_tcbinfo_operations, PROCFS_FILE_TYPE }, #endif #ifdef CONFIG_THERMAL_PROCFS { "thermal", &g_thermal_operations, PROCFS_DIR_TYPE }, { "thermal/**", &g_thermal_operations, PROCFS_UNKOWN_TYPE }, #endif #ifndef CONFIG_FS_PROCFS_EXCLUDE_UPTIME { "uptime", &g_uptime_operations, PROCFS_FILE_TYPE }, #endif #ifndef CONFIG_FS_PROCFS_EXCLUDE_VERSION { "version", &g_version_operations, PROCFS_FILE_TYPE }, #endif }; #ifdef CONFIG_FS_PROCFS_REGISTER static const uint8_t g_base_entrycount = sizeof(g_base_entries) / sizeof(struct procfs_entry_s); static FAR struct procfs_entry_s *g_procfs_entries; static uint8_t g_procfs_entrycount; #else static const uint8_t g_procfs_entrycount = sizeof(g_procfs_entries) / sizeof(struct procfs_entry_s); #endif /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /* File system methods */ static int procfs_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode); static int procfs_close(FAR struct file *filep); static ssize_t procfs_read(FAR struct file *filep, FAR char *buffer, size_t buflen); static ssize_t procfs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); static int procfs_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup); static int procfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg); static int procfs_dup(FAR const struct file *oldp, FAR struct file *newp); static int procfs_fstat(FAR const struct file *filep, FAR struct stat *buf); static int procfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s **dir); static int procfs_closedir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); static int procfs_readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir, FAR struct dirent *entry); static int procfs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); static int procfs_bind(FAR struct inode *blkdriver, FAR const void *data, FAR void **handle); static int procfs_unbind(FAR void *handle, FAR struct inode **blkdriver, unsigned int flags); static int procfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf); static int procfs_stat(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf); /* Initialization */ #ifdef CONFIG_FS_PROCFS_REGISTER static int procfs_initialize(void); #endif /**************************************************************************** * Public Data ****************************************************************************/ /* See fs_mount.c -- this structure is explicitly externed there. * We use the old-fashioned kind of initializers so that this will compile * with any compiler. */ const struct mountpt_operations g_procfs_operations = { procfs_open, /* open */ procfs_close, /* close */ procfs_read, /* read */ procfs_write, /* write */ NULL, /* seek */ procfs_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ procfs_poll, /* poll */ NULL, /* sync */ procfs_dup, /* dup */ procfs_fstat, /* fstat */ NULL, /* fchstat */ procfs_opendir, /* opendir */ procfs_closedir, /* closedir */ procfs_readdir, /* readdir */ procfs_rewinddir, /* rewinddir */ procfs_bind, /* bind */ procfs_unbind, /* unbind */ procfs_statfs, /* statfs */ NULL, /* unlink */ NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* rename */ procfs_stat, /* stat */ NULL /* chstat */ }; /* Level 0 contains the directory of active tasks in addition to other * statically registered entries with custom handlers. This structure * contains a snapshot of the active tasks when the directory is first * opened. */ struct procfs_level0_s { struct procfs_dir_priv_s base; /* Base struct for ProcFS dir */ /* Our private data */ uint8_t lastlen; /* length of last reported static dir */ FAR const char *lastread; /* Pointer to last static dir read */ pid_t pid[1]; /* Snapshot of all active task IDs */ }; /* Level 1 is an internal virtual directory (such as /proc/fs) which * will contain one or more additional static entries based on the * configuration. */ struct procfs_level1_s { struct procfs_dir_priv_s base; /* Base struct for ProcFS dir */ /* Our private data */ uint8_t lastlen; /* length of last reported static dir */ uint8_t subdirlen; /* Length of the subdir search */ uint16_t firstindex; /* Index of 1st entry matching this subdir */ FAR const char *lastread; /* Pointer to last static dir read */ }; /**************************************************************************** * Private Functions ****************************************************************************/ #ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS /**************************************************************************** * Name: procfs_enum ****************************************************************************/ static void procfs_enum(FAR struct tcb_s *tcb, FAR void *arg) { FAR struct procfs_level0_s *dir = (FAR struct procfs_level0_s *)arg; int index; DEBUGASSERT(dir); /* Add the PID to the list */ if (dir->base.index >= dir->base.nentries) { return; } index = dir->base.index; dir->pid[index] = tcb->pid; dir->base.index = index + 1; } /**************************************************************************** * Name: procfs_sort_pid ****************************************************************************/ static void procfs_sort_pid(FAR struct procfs_level0_s *level0) { pid_t pid; int i; int j; /* Sort the process id by Bubble. * FIXME: improve searching algorithm. */ for (i = 0; i < level0->base.nentries; i++) { for (j = 0; j < level0->base.nentries - 1 - i; j++) { if (level0->pid[j] > level0->pid[j + 1]) { pid = level0->pid[j]; level0->pid[j] = level0->pid[j + 1]; level0->pid[j + 1] = pid; } } } } #endif /**************************************************************************** * Name: procfs_open ****************************************************************************/ static int procfs_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode) { int x; int ret = -ENOENT; finfo("Open '%s'\n", relpath); /* Perform the stat based on the procfs_entry operations */ for (x = 0; x < g_procfs_entrycount; x++) { /* Test if the path matches this entry's specification */ if (fnmatch(g_procfs_entries[x].pathpattern, relpath, 0) == 0) { /* Match found! Stat using this procfs entry */ DEBUGASSERT(g_procfs_entries[x].ops && g_procfs_entries[x].ops->open); ret = g_procfs_entries[x].ops->open(filep, relpath, oflags, mode); if (ret == OK) { DEBUGASSERT(filep->f_priv); ((FAR struct procfs_file_s *)filep->f_priv)->procfsentry = &g_procfs_entries[x]; break; } } } return ret; } /**************************************************************************** * Name: procfs_close ****************************************************************************/ static int procfs_close(FAR struct file *filep) { FAR struct procfs_file_s *attr; int ret = OK; /* Recover our private data from the struct file instance */ attr = (FAR struct procfs_file_s *)filep->f_priv; DEBUGASSERT(attr); /* Release the file attributes structure */ if (attr->procfsentry->ops->close != NULL) { ret = attr->procfsentry->ops->close(filep); } else { kmm_free(attr); } filep->f_priv = NULL; return ret; } /**************************************************************************** * Name: procfs_read ****************************************************************************/ static ssize_t procfs_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { FAR struct procfs_file_s *handler; finfo("buffer=%p buflen=%d\n", buffer, (int)buflen); /* Recover our private data from the struct file instance */ handler = (FAR struct procfs_file_s *)filep->f_priv; DEBUGASSERT(handler); /* Call the handler's read routine */ return handler->procfsentry->ops->read(filep, buffer, buflen); } /**************************************************************************** * Name: procfs_write ****************************************************************************/ static ssize_t procfs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) { FAR struct procfs_file_s *handler; finfo("buffer=%p buflen=%d\n", buffer, (int)buflen); /* Recover our private data from the struct file instance */ handler = (FAR struct procfs_file_s *)filep->f_priv; DEBUGASSERT(handler); /* Call the handler's write routine */ if (handler->procfsentry->ops->write) { return handler->procfsentry->ops->write(filep, buffer, buflen); } return 0; } static int procfs_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) { FAR struct procfs_file_s *handler; finfo("fds=%p setup=%d\n", fds, setup); /* Recover our private data from the struct file instance */ handler = (FAR struct procfs_file_s *)filep->f_priv; DEBUGASSERT(handler); /* Call the handler's poll routine */ if (handler->procfsentry->ops->poll) { return handler->procfsentry->ops->poll(filep, fds, setup); } return -ENOSYS; } /**************************************************************************** * Name: procfs_ioctl ****************************************************************************/ static int procfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { finfo("cmd: %d arg: %08lx\n", cmd, arg); /* No IOCTL commands supported */ return -ENOTTY; } /**************************************************************************** * Name: procfs_dup * * Description: * Duplicate open file data in the new file structure. * ****************************************************************************/ static int procfs_dup(FAR const struct file *oldp, FAR struct file *newp) { FAR struct procfs_file_s *oldattr; finfo("Dup %p->%p\n", oldp, newp); /* Recover our private data from the old struct file instance */ oldattr = (FAR struct procfs_file_s *)oldp->f_priv; DEBUGASSERT(oldattr); /* Allow lower-level handler do the dup to get it's extra data */ return oldattr->procfsentry->ops->dup(oldp, newp); } /**************************************************************************** * Name: procfs_fstat * * Description: * Obtain information about an open file associated with the file * descriptor 'fd', and will write it to the area pointed to by 'buf'. * ****************************************************************************/ static int procfs_fstat(FAR const struct file *filep, FAR struct stat *buf) { FAR struct procfs_file_s *handler; finfo("buf=%p\n", buf); /* Recover our private data from the struct file instance */ handler = (FAR struct procfs_file_s *)filep->f_priv; DEBUGASSERT(handler); /* The procfs file system contains only directory and data file entries. * Since the file has been opened, we know that this is a data file and, * at a minimum, readable. */ memset(buf, 0, sizeof(struct stat)); buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR; /* If the write method is provided, then let's also claim that the file is * writable. */ if (handler->procfsentry->ops->write != NULL) { buf->st_mode |= S_IWOTH | S_IWGRP | S_IWUSR; } return OK; } /**************************************************************************** * Name: procfs_opendir * * Description: * Open a directory for read access * ****************************************************************************/ static int procfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s **dir) { FAR struct procfs_level0_s *level0; finfo("relpath: \"%s\"\n", relpath ? relpath : "NULL"); DEBUGASSERT(mountpt && dir && relpath); /* The relative must be either: * * "" - The top level directory of task/thread IDs * "" - The sub-directory of task/thread attributes */ if (!relpath || relpath[0] == '\0') { size_t num = 0; /* The path refers to the top level directory. Allocate the level0 * dirent structure. */ #ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS num = g_npidhash; #endif level0 = (FAR struct procfs_level0_s *) kmm_zalloc(sizeof(struct procfs_level0_s) + sizeof(pid_t) * num) ; if (!level0) { ferr("ERROR: Failed to allocate the level0 directory structure\n"); return -ENOMEM; } /* Take a snapshot of all currently active tasks. Any new tasks * added between the opendir() and closedir() call will not be * visible. * * NOTE that interrupts must be disabled throughout the traversal. */ #ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS level0->base.index = 0; level0->base.nentries = num; nxsched_foreach(procfs_enum, level0); level0->base.nentries = level0->base.index; level0->base.index = 0; procfs_sort_pid(level0); #else level0->base.index = 0; level0->base.nentries = 0; #endif /* Initialize lastread entries */ level0->lastread = ""; level0->lastlen = 0; level0->base.procfsentry = NULL; *dir = (FAR struct fs_dirent_s *)level0; } else { int x; int ret; int len = strlen(relpath); /* Search the static array of procfs_entries */ for (x = 0; x < g_procfs_entrycount; x++) { /* Test if the path matches this entry's specification */ if (fnmatch(g_procfs_entries[x].pathpattern, relpath, 0) == 0) { /* Match found! Call the handler's opendir routine. If * successful, this opendir routine will create an entry * derived from struct procfs_dir_priv_s as dir. */ DEBUGASSERT(g_procfs_entries[x].ops != NULL && g_procfs_entries[x].ops->opendir != NULL); ret = g_procfs_entries[x].ops->opendir(relpath, dir); if (ret == OK) { FAR struct procfs_dir_priv_s *dirpriv; DEBUGASSERT(*dir); /* Set the procfs_entry handler */ dirpriv = (FAR struct procfs_dir_priv_s *)(*dir); dirpriv->procfsentry = &g_procfs_entries[x]; } return ret; } /* Test for a sub-string match (e.g. "ls /proc/fs") */ else if (strncmp(g_procfs_entries[x].pathpattern, relpath, len) == 0) { FAR struct procfs_level1_s *level1; /* Doing an intermediate directory search */ /* The path refers to the top level directory. Allocate * the level1 dirent structure. */ level1 = (FAR struct procfs_level1_s *) kmm_zalloc(sizeof(struct procfs_level1_s)); if (!level1) { ferr("ERROR: Failed to allocate the level0 directory " "structure\n"); return -ENOMEM; } level1->base.level = 1; level1->base.index = x; level1->firstindex = x; level1->subdirlen = len; level1->lastread = ""; level1->lastlen = 0; level1->base.procfsentry = NULL; *dir = (FAR struct fs_dirent_s *)level1; break; } } if (x == g_procfs_entrycount) { return -ENOENT; } } return OK; } /**************************************************************************** * Name: procfs_closedir * * Description: Close the directory listing * ****************************************************************************/ static int procfs_closedir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir) { DEBUGASSERT(mountpt && dir); kmm_free(dir); return OK; } /**************************************************************************** * Name: procfs_readdir * * Description: Read the next directory entry * ****************************************************************************/ static int procfs_readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir, FAR struct dirent *entry) { FAR const struct procfs_entry_s *pentry = NULL; FAR struct procfs_dir_priv_s *priv; FAR struct procfs_level0_s *level0; FAR const char *name = NULL; unsigned int index; int ret = -ENOENT; DEBUGASSERT(mountpt && dir); priv = (FAR struct procfs_dir_priv_s *)dir; /* Are we reading the 1st directory level with dynamic PID and static * entries? */ if (priv->level == 0) { level0 = (FAR struct procfs_level0_s *)priv; /* Have we reached the end of the PID information */ index = priv->index; if (index >= priv->nentries) { index -= priv->nentries; /* We must report the next static entry ... no more PID entries. * skip any entries with wildcards in the first segment of the * directory name. */ while (index < g_procfs_entrycount) { pentry = &g_procfs_entries[index]; name = pentry->pathpattern; while (*name != '/' && *name != '\0') { if (*name == '*' || *name == '[' || *name == '?') { /* Wildcard found. Skip this entry */ index++; name = NULL; break; } name++; } /* Test if we skipped this entry */ if (name != NULL) { /* This entry is okay to report. Test if it has a * duplicate first level name as the one we just reported. * This could happen in the event of procfs_entry_s such * as: * * fs/smartfs * fs/nfs * fs/nxffs */ name = g_procfs_entries[index].pathpattern; if (!level0->lastlen || strncmp(name, level0->lastread, level0->lastlen) != 0) { /* Not a duplicate, return the first segment of this * entry */ break; } else { /* Skip this entry ... duplicate 1st level name found */ index++; } } } /* Test if we are at the end of the directory */ if (index < g_procfs_entrycount) { /* Report the next static entry */ level0->lastlen = strcspn(name, "/"); level0->lastread = name; strlcpy(entry->d_name, name, level0->lastlen + 1); /* If the entry is a directory type OR if the reported name is * only a sub-string of the entry (meaning that it contains * '/'), then report this entry as a directory. */ if (pentry->type == PROCFS_DIR_TYPE || level0->lastlen != strlen(name)) { entry->d_type = DTYPE_DIRECTORY; } else { entry->d_type = DTYPE_FILE; } /* Advance to next entry for the next read */ priv->index = priv->nentries + index; ret = OK; } } #ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS else { /* Verify that the pid still refers to an active task/thread */ pid_t pid = level0->pid[index]; FAR struct tcb_s *tcb = nxsched_get_tcb(pid); if (!tcb) { ferr("ERROR: PID %d is no longer valid\n", pid); return -ENOENT; } /* Save the filename=pid and file type=directory */ entry->d_type = DTYPE_DIRECTORY; procfs_snprintf(entry->d_name, NAME_MAX + 1, "%d", pid); /* Set up the next directory entry offset. NOTE that we could use * the standard f_pos instead of our own private index. */ level0->base.index = index + 1; ret = OK; } #endif /* CONFIG_FS_PROCFS_EXCLUDE_PROCESS */ } /* Are we reading an intermediate subdirectory? */ else if (priv->level > 0 && priv->procfsentry == NULL) { FAR struct procfs_level1_s *level1; level1 = (FAR struct procfs_level1_s *)priv; /* Test if this entry matches. We assume all entries of the same * subdirectory are listed in order in the procfs_entry array. */ if (level1->base.index < g_procfs_entrycount && level1->firstindex < g_procfs_entrycount && strncmp(g_procfs_entries[level1->base.index].pathpattern, g_procfs_entries[level1->firstindex].pathpattern, level1->subdirlen) == 0) { /* This entry matches. Report the subdir entry */ name = &g_procfs_entries[level1->base.index]. pathpattern[level1->subdirlen + 1]; level1->lastlen = strcspn(name, "/"); level1->lastread = name; strlcpy(entry->d_name, name, level1->lastlen + 1); /* Some of the search entries contain '**' wildcards. When we * report the entry name, we must remove this wildcard search * specifier. */ while (entry->d_name[level1->lastlen - 1] == '*') { level1->lastlen--; } entry->d_name[level1->lastlen] = '\0'; if (name[level1->lastlen] == '/') { entry->d_type = DTYPE_DIRECTORY; } else { entry->d_type = DTYPE_FILE; } level1->base.index++; ret = OK; } } else { /* We are performing a directory search of one of the subdirectories * and we must let the handler perform the read. */ DEBUGASSERT(priv->procfsentry && priv->procfsentry->ops->readdir); ret = priv->procfsentry->ops->readdir(dir, entry); } return ret; } /**************************************************************************** * Name: procfs_rewindir * * Description: Reset directory read to the first entry * ****************************************************************************/ static int procfs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir) { FAR struct procfs_dir_priv_s *priv; DEBUGASSERT(mountpt && dir); priv = (FAR struct procfs_dir_priv_s *)dir; if (priv->level > 0 && priv->procfsentry == NULL) { priv->index = ((FAR struct procfs_level1_s *)priv)->firstindex; } else { priv->index = 0; } return OK; } /**************************************************************************** * Name: procfs_bind * * Description: This implements a portion of the mount operation. This * function allocates and initializes the mountpoint private data and * binds the block driver inode to the filesystem private data. The final * binding of the private data (containing the block driver) to the * mountpoint is performed by mount(). * ****************************************************************************/ static int procfs_bind(FAR struct inode *blkdriver, FAR const void *data, FAR void **handle) { #ifdef CONFIG_FS_PROCFS_REGISTER /* Make sure that we are properly initialized */ procfs_initialize(); #endif return OK; } /**************************************************************************** * Name: procfs_unbind * * Description: This implements the filesystem portion of the umount * operation. * ****************************************************************************/ static int procfs_unbind(FAR void *handle, FAR struct inode **blkdriver, unsigned int flags) { return OK; } /**************************************************************************** * Name: procfs_statfs * * Description: Return filesystem statistics * ****************************************************************************/ static int procfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf) { /* Fill in the statfs info */ buf->f_type = PROCFS_MAGIC; buf->f_bsize = 0; buf->f_blocks = 0; buf->f_bfree = 0; buf->f_bavail = 0; buf->f_namelen = NAME_MAX; return OK; } /**************************************************************************** * Name: procfs_stat * * Description: Return information about a file or directory * ****************************************************************************/ static int procfs_stat(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf) { int ret = -ENOENT; /* Three path forms are accepted: * * "" - The relative path refers to the top level directory * "" - If refers to a currently active task/thread, then it * is a directory * "/" - If is a recognized attribute then, then it * is a file. */ memset(buf, 0, sizeof(struct stat)); if (!relpath || relpath[0] == '\0') { /* The path refers to the top level directory. * It's a read-only directory. */ buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR; ret = OK; } else { int x; int len = strlen(relpath); /* Perform the stat based on the procfs_entry operations */ for (x = 0; x < g_procfs_entrycount; x++) { /* Test if the path matches this entry's specification */ if (fnmatch(g_procfs_entries[x].pathpattern, relpath, 0) == 0) { /* Match found! Stat using this procfs entry */ DEBUGASSERT(g_procfs_entries[x].ops && g_procfs_entries[x].ops->stat); return g_procfs_entries[x].ops->stat(relpath, buf); } /* Test for an internal subdirectory stat */ else if (strncmp(g_procfs_entries[x].pathpattern, relpath, len) == 0) { /* It's an internal subdirectory */ buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR; ret = OK; break; } } } return ret; } /**************************************************************************** * Name: procfs_initialize * * Description: * Configure the initial set of entries in the procfs file system. * * Input Parameters: * None * * Returned Value: * Zero (OK) on success; a negated errno value on failure * ****************************************************************************/ #ifdef CONFIG_FS_PROCFS_REGISTER int procfs_initialize(void) { /* Are we already initialized? */ if (g_procfs_entries == NULL) { /* No.. allocate a modifiable list of entries */ g_procfs_entries = (FAR struct procfs_entry_s *) kmm_malloc(sizeof(g_base_entries)); if (g_procfs_entries == NULL) { return -ENOMEM; } /* And copy the fixed entries into the allocated array */ memcpy(g_procfs_entries, g_base_entries, sizeof(g_base_entries)); g_procfs_entrycount = g_base_entrycount; } return OK; } #endif /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: procfs_register * * Description: * Add a new entry to the procfs file system. * * NOTE: This function should be called *prior* to mounting the procfs * file system to prevent concurrency problems with the modification of * the procfs data set while it is in use. * * Input Parameters: * entry - Describes the entry to be registered. * * Returned Value: * Zero (OK) on success; a negated errno value on failure * ****************************************************************************/ #ifdef CONFIG_FS_PROCFS_REGISTER int procfs_register(FAR const struct procfs_entry_s *entry) { FAR struct procfs_entry_s *newtable; unsigned int newcount; size_t newsize; int ret = -ENOMEM; /* Make sure that we are properly initialized */ procfs_initialize(); /* realloc the table of procfs entries. * * REVISIT: This reallocation may free memory previously used for the * procfs entry table. If that table were actively in use, then that * could cause procfs logic to use a stale memory pointer! We avoid that * problem by requiring that the procfs file be unmounted when the new * entry is added. That requirement, however, is not enforced explicitly. * * Locking the scheduler as done below is insufficient. As would be just * marking the entries as volatile. */ newcount = g_procfs_entrycount + 1; newsize = newcount * sizeof(struct procfs_entry_s); newtable = (FAR struct procfs_entry_s *) kmm_realloc(g_procfs_entries, newsize); if (newtable != NULL) { /* Copy the new entry at the end of the reallocated table */ memcpy(&newtable[g_procfs_entrycount], entry, sizeof(struct procfs_entry_s)); /* Instantiate the reallocated table */ g_procfs_entries = newtable; g_procfs_entrycount = newcount; ret = OK; } return ret; } #endif