Re: An alternative way of populating /proc

From: Matt Aubury (Matt.Aubury@comlab.ox.ac.uk)
Date: Sun Apr 16 2000 - 17:26:39 EST


Hello again,

As promised I've created an "proper patch" version of the
create_proc_entries API. Hopefully I've addressed a few of the issues
raised, without making the interface (or code size) too
bloated. According to my numbers (which I'm not sure I believe) it
adds only 502 bytes to the size of vmlinux.

The new API is described in the DocBook'd code comments in the patch;
scroll down for details. (Although I've had trouble actually getting
the DocBook documentation to build properly -- I'd welcome any tips).

Once the API has settled down I'll submit it for inclusion: I think it
would be good if it made it into 2.4.0, but if you don't agree please
voice your objections.

Cheers,
Matt

------------------------------

diff -u --recursive --new-file linux-2.3.99pre5/Documentation/DocBook/Makefile linux/Documentation/DocBook/Makefile
--- linux-2.3.99pre5/Documentation/DocBook/Makefile Mon Apr 3 21:24:05 2000
+++ linux/Documentation/DocBook/Makefile Sun Apr 16 21:06:55 2000
@@ -45,6 +45,7 @@
                 $(TOPDIR)/kernel/pm.c \
                 $(TOPDIR)/kernel/ksyms.c \
                 $(TOPDIR)/net/netsyms.c \
+ $(TOPDIR)/fs/proc/build.c \
                 <kernel-api.tmpl >kernel-api.sgml
 
 parportbook.sgml: parportbook.tmpl
diff -u --recursive --new-file linux-2.3.99pre5/Documentation/DocBook/kernel-api.tmpl linux/Documentation/DocBook/kernel-api.tmpl
--- linux-2.3.99pre5/Documentation/DocBook/kernel-api.tmpl Sun Apr 2 23:38:53 2000
+++ linux/Documentation/DocBook/kernel-api.tmpl Sun Apr 16 21:12:58 2000
@@ -74,6 +74,13 @@
      </sect1>
   </chapter>
 
+ <chapter id="fs">
+ <title>Filesystems</title>
+ <sect1><title>procfs</title>
+!Efs/proc/build.c
+ </sect1>
+ </chapter>
+
   <chapter id="modload">
      <title>Module Loading</title>
 !Ekernel/kmod.c
diff -u --recursive --new-file linux-2.3.99pre5/fs/proc/Makefile linux/fs/proc/Makefile
--- linux-2.3.99pre5/fs/proc/Makefile Mon Feb 7 17:56:47 2000
+++ linux/fs/proc/Makefile Sun Apr 16 20:54:07 2000
@@ -9,7 +9,8 @@
 
 O_TARGET := proc.o
 O_OBJS := inode.o root.o base.o generic.o array.o \
- kmsg.o proc_tty.o proc_misc.o kcore.o
+ kmsg.o proc_tty.o proc_misc.o kcore.o \
+ build.o
 OX_OBJS := procfs_syms.o
 M_OBJS :=
 
diff -u --recursive --new-file linux-2.3.99pre5/fs/proc/build.c linux/fs/proc/build.c
--- linux-2.3.99pre5/fs/proc/build.c Thu Jan 1 01:00:00 1970
+++ linux/fs/proc/build.c Sun Apr 16 22:18:39 2000
@@ -0,0 +1,401 @@
+/*
+ * linux/fs/proc/build.c
+ *
+ * Routines for easily building procfs heirarchies.
+ * See the create_proc_entries function for details.
+ *
+ * Copyright (C) 2000 Matthew Aubury
+ */
+
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+
+/*
+ * Local function prototypes
+ */
+static int create_entry(struct proc_dir_entry *parent,
+ char *format, va_list *args);
+static int create_dir_entry(struct proc_dir_entry *parent, char *name,
+ char *format, va_list *args_ptr);
+static int create_file_entry(struct proc_dir_entry *parent, char *name,
+ char *format, va_list *args_ptr);
+static read_proc_t decimal_read_proc;
+static read_proc_t hex_read_proc;
+static read_proc_t string_read_proc;
+static write_proc_t integer_write_proc;
+
+/*
+ * The possible error codes. These are returned as negative numbers,
+ * in traditional kernel style. They're put into an enum just so I
+ * don't end up giving two errors the same code.
+ */
+enum {
+ ESHORTPARSE = 1, /* -1 */
+ ENOTERMINATOR, /* -2 */
+ EBADNAME, /* -3 */
+ EBADDIROPEN, /* -4 */
+ EBADDIRCLOSE, /* -5 */
+ EBADENTRY, /* -6 */
+ EBADFORMAT, /* -7 */
+ EFAILEDALLOCA, /* -8 */
+ EFAILEDCREATE, /* -9 */
+ ENULLPTR /* -10 */
+};
+
+/**
+ * create_proc_entries - build a procfs file/directory hierarchy
+ * @parent: parent proc_dir_entry structure
+ * @format: format string (ala printf, scanf)
+ * @...: additional arguments, NULL terminated
+ *
+ * This function is used to create a hierarchy of files and
+ * directories in procfs.
+ *
+ * The format for a file is "name:contents", where name is the
+ * filename, and contents is zero or more format codes (described
+ * below). The contents may be prefixed with an asterix ("*")
+ * to specify that the file is to be created root writable.
+ * Otherwise file permissions are readable by all, writable by none.
+ *
+ * The format for a directory is "name/{contents}", where name
+ * is the directory name, and contents are zero or more files
+ * or directories, separated by commas. Directory permissions
+ * are always readable and executable by all.
+ *
+ * The valid format codes are:
+ *
+ * %d - Decimal integer, input and output. Supply an (int *)
+ * as the argument.
+ *
+ * %x - Hexadecimal integer, input and output. Supply an (int *)
+ * as the argument.
+ *
+ * %s - Zero-terminated string, output only. Supply a (char *)
+ * as the argument.
+ *
+ * %v - "Virtual" entry, supply a (struct proc_dir_entry **)
+ * pointer as the argument and this will be set the the entry
+ * that is created. This allows you to either tweek the settings
+ * once the entry is created. For instance, you may use this
+ * to arbitrarily set the read_proc and write_proc functions.
+ *
+ * The function returns zero on success, and a negative error
+ * code on an error. Check the source for the meaning of the
+ * error code.
+ *
+ * Example use is:
+ *
+ * int beta;
+ *
+ * char gamma[] = "Gamma";
+ *
+ * struct proc_dir_entry *entry;
+ *
+ * create_proc_entries(NULL, "alpha/{beta:%d,gamma:%s%v}",
+ * &beta, gamma, &entry, NULL);
+ *
+ * entry->uid = 500;
+ */
+int create_proc_entries(struct proc_dir_entry *parent, char *format, ...)
+{
+ int count;
+ va_list args;
+ char *terminator;
+
+ /*
+ * Do the parsing and tree construction
+ */
+ va_start(args, format);
+ count = create_entry(parent, format, &args);
+
+ /*
+ * Check that no parse errors were encountered,
+ * that the entire string was consumed, and that
+ * the argument list was NULL terminated.
+ */
+ if (count < 0)
+ return count;
+ if (count != strlen(format))
+ return -ESHORTPARSE;
+ terminator = va_arg(args, char *);
+ va_end(args);
+ if (terminator != NULL)
+ return -ENOTERMINATOR;
+
+ return 0;
+}
+
+/*
+ * Recursively create procfs entries from the format string and a va_list
+ */
+static int create_entry(struct proc_dir_entry *parent,
+ char *format, va_list *args_ptr)
+{
+ int n = 0, count;
+ char *name;
+
+ /*
+ * Exit immediately (and successfully) if the string is empty
+ */
+ if (format[0] == '\0')
+ return 0;
+
+ /*
+ * Parse the name of the entry, or just return if the
+ * string is empty.
+ */
+ while (format[n] != ':' && format[n] != '/') {
+ if (format[n] == '\0')
+ return -EBADNAME;
+ n++;
+ }
+
+ /*
+ * Stack allocation of copy of string name. We don't want
+ * to use a static length because the creation functions
+ * are mutally recursive, and the in-kernel stack is
+ * rather limited. A kmalloc() is unnecessary.
+ */
+ name = alloca(n + 1);
+ if (!name)
+ return -EFAILEDALLOCA;
+
+ /*
+ * Copy the name string
+ */
+ memcpy(name, format, n);
+ name[n] = '\0';
+
+ /*
+ * Determine type of node
+ */
+ switch (format[n++])
+ {
+ case '/':
+ count = create_dir_entry(parent, name,
+ format + n, args_ptr);
+ break;
+
+ case ':':
+ count = create_file_entry(parent, name,
+ format + n, args_ptr);
+ break;
+
+ default:
+ return -EBADENTRY;
+ }
+
+ if (count < 0)
+ return count;
+
+ /*
+ * Return number of characters consumed
+ */
+ return n + count;
+}
+
+/*
+ * Recursively create procfs directory entries from the format string
+ */
+static int create_dir_entry(struct proc_dir_entry *parent, char *name,
+ char *format, va_list *args_ptr)
+{
+ int n = 0, count;
+ struct proc_dir_entry *entry;
+
+ if (format[n++] != '{')
+ return -EBADDIROPEN;
+
+ entry = create_proc_entry(name, S_IFDIR | S_IRUGO | S_IXUGO, parent);
+ if (entry == NULL)
+ return -EFAILEDCREATE;
+
+ while (format[n] != '}') {
+ count = create_entry(entry, format + n, args_ptr);
+ if (count < 0) {
+ remove_proc_entry(name, parent);
+ return count;
+ }
+ n += count;
+ if (format[n] != ',')
+ break;
+ n++;
+ }
+
+ if (format[n] != '}') {
+ remove_proc_entry(name, parent);
+ return -EBADDIRCLOSE;
+ }
+
+ return n + 1;
+}
+
+/*
+ * Create a procfs file entry
+ */
+static int create_file_entry(struct proc_dir_entry *parent, char *name,
+ char *format, va_list *args_ptr)
+{
+ int n = 0, writable = 0;
+ struct proc_dir_entry *entry;
+ mode_t mode;
+ void *ptr;
+
+ /*
+ * Check for a leading asterix, this makes the value writable
+ * (at least in terms of file permissions: some of the standard
+ * format data types don't allow writing under any circumstances)
+ */
+ if (format[n] == '*') {
+ writable = 1;
+ n++;
+ }
+
+ /*
+ * Build a file entry
+ */
+ mode = S_IFREG | S_IRUGO | (writable ? S_IWUSR : 0);
+ entry = create_proc_entry(name, mode, parent);
+ if (entry == NULL)
+ return -EFAILEDCREATE;
+
+ /*
+ * Fill in the structure according to the format string
+ */
+ while (format[n] == '%') {
+
+ n++;
+ ptr = va_arg(*args_ptr, void *);
+ if (!ptr) {
+ remove_proc_entry(name, parent);
+ return -ENULLPTR;
+ }
+
+ switch (format[n++]) {
+
+ case 'd': /* decimal I/O */
+ entry->read_proc = decimal_read_proc;
+ entry->write_proc = integer_write_proc;
+ entry->data = ptr;
+ break;
+
+ case 'x': /* hex I/O */
+ entry->read_proc = hex_read_proc;
+ entry->write_proc = integer_write_proc;
+ entry->data = ptr;
+ break;
+
+ case 's': /* string output */
+ entry->read_proc = string_read_proc;
+ entry->data = ptr;
+ break;
+
+ case 'v': /* "virtual": access the proc_dir_entry structure */
+ *((struct proc_dir_entry **)ptr) = entry;
+ break;
+
+ default:
+ remove_proc_entry(name, parent);
+ return -EBADFORMAT;
+ break;
+ }
+ }
+
+ return n;
+}
+
+/*
+ * Generic decimal output
+ */
+static int decimal_read_proc(char *buf, char **start, off_t offset,
+ int length, int *eof, void *data)
+{
+ printk("being asked to read back, its %d\n", *((int *)data));
+ return (sprintf(buf, "%d\n", *((int *)data)));
+}
+
+/*
+ * Generic hex output
+ */
+static int hex_read_proc(char *buf, char **start, off_t offset,
+ int length, int *eof, void *data)
+{
+ return (sprintf(buf, "0x%0x\n", *((int *)data)));
+}
+
+/*
+ * Generic string output
+ */
+static int string_read_proc(char *buf, char **start, off_t offset,
+ int length, int *eof, void *data)
+{
+ return (sprintf(buf, "%s\n", ((char *)data)));
+}
+
+/*
+ * Check if a string has a hex prefix
+ */
+static int hexprefix(const char *buffer, int count)
+{
+ if (count <= 2)
+ return 0;
+ if (buffer[0] != '0')
+ return 0;
+ return (buffer[1] == 'x' || buffer[1] == 'X');
+}
+
+/*
+ * Generic integer input
+ */
+static int integer_write_proc(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ int value = 0, neg = 0;
+ unsigned long i;
+ char c;
+
+ /*
+ * Check for negatives
+ */
+ if (count && buffer[0] == '-') {
+ neg = 1;
+ buffer++;
+ count--;
+ }
+
+ if (hexprefix(buffer, count)) {
+ /*
+ * Hex
+ */
+ for (i = 2; i < count; i++) {
+ c = tolower(buffer[i]);
+ if (isdigit(c))
+ value = (value << 4) + (c - '0');
+ else if (isxdigit(c))
+ value = (value << 4) + (c - 'a') + 10;
+ else
+ break;
+ }
+ } else {
+ /*
+ * Decimal
+ */
+ for (i = 0; i < count; i++) {
+ c = buffer[i];
+ if (isdigit(c))
+ value = (value * 10) + (c - '0');
+ else
+ break;
+ }
+ }
+ if (c != '\n' && c != '\0')
+ return -1;
+
+ /*
+ * Update the value, taking sign into account, and return
+ */
+ *(int *)data = neg ? -value : value;
+ return count + neg;
+}
diff -u --recursive --new-file linux-2.3.99pre5/fs/proc/procfs_syms.c linux/fs/proc/procfs_syms.c
--- linux-2.3.99pre5/fs/proc/procfs_syms.c Wed Mar 29 01:26:27 2000
+++ linux/fs/proc/procfs_syms.c Sun Apr 16 21:49:17 2000
@@ -13,6 +13,7 @@
 EXPORT_SYMBOL(proc_mknod);
 EXPORT_SYMBOL(proc_mkdir);
 EXPORT_SYMBOL(create_proc_entry);
+EXPORT_SYMBOL(create_proc_entries);
 EXPORT_SYMBOL(remove_proc_entry);
 EXPORT_SYMBOL(proc_root);
 EXPORT_SYMBOL(proc_root_fs);
diff -u --recursive --new-file linux-2.3.99pre5/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- linux-2.3.99pre5/include/linux/proc_fs.h Sun Apr 16 12:02:01 2000
+++ linux/include/linux/proc_fs.h Sun Apr 16 21:16:34 2000
@@ -165,6 +165,12 @@
         remove_proc_entry(name,proc_net);
 }
 
+/*
+ * build.c
+ */
+extern int create_proc_entries(struct proc_dir_entry *parent,
+ char *format, ...);
+
 #else
 
 extern inline int proc_register(struct proc_dir_entry *a, struct proc_dir_entry *b) { return 0; }
@@ -193,6 +199,9 @@
 
 extern inline void proc_tty_register_driver(struct tty_driver *driver) {};
 extern inline void proc_tty_unregister_driver(struct tty_driver *driver) {};
+
+extern inline int create_proc_entries(struct proc_dir_entry *parent,
+ char *format, ...) { return -999; };
 
 extern struct proc_dir_entry proc_root;
 

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Sun Apr 23 2000 - 21:00:09 EST