[PATCH] procfs guide

Erik Mouw (J.A.K.Mouw@its.tudelft.nl)
Mon, 4 Jun 2001 00:18:04 +0200


Hi,

I got some comments on my procfs guide when I posted it a couple of
days ago, so here is a new version. What's new:

- add revision history (Jonathan Lundel)
- remove_proc_entry() is not recursive (Jonathan Lundel)
- numerous spelling corrections (Marc Joosen)
- /proc/sys files are sysctl, not procfs (John Levon)
- add an implicit .c to .sgml rule to the Makefile (me)
- fixed a too long comment in the example code (me)

The patch is against 2.4.5-ac6, but should apply cleanly to 2.4.5-ac7
as well. Please apply.

Erik

-- 
J.A.K. (Erik) Mouw, Information and Communication Theory Group, Department
of Electrical Engineering, Faculty of Information Technology and Systems,
Delft University of Technology, PO BOX 5031,  2600 GA Delft, The Netherlands
Phone: +31-15-2783635  Fax: +31-15-2781843  Email: J.A.K.Mouw@its.tudelft.nl
WWW: http://www-ict.its.tudelft.nl/~erik/

Index: Documentation/DocBook/Makefile =================================================================== RCS file: /home/erik/cvsroot/elinux/Documentation/DocBook/Makefile,v retrieving revision 1.1.1.30 diff -u -r1.1.1.30 Makefile --- Documentation/DocBook/Makefile 2001/05/15 12:14:07 1.1.1.30 +++ Documentation/DocBook/Makefile 2001/06/03 21:09:27 @@ -1,7 +1,7 @@ BOOKS := wanbook.sgml z8530book.sgml mcabook.sgml videobook.sgml \ kernel-api.sgml parportbook.sgml kernel-hacking.sgml \ kernel-locking.sgml via-audio.sgml mousedrivers.sgml sis900.sgml \ - deviceiobook.sgml + deviceiobook.sgml procfs-guide.sgml PS := $(patsubst %.sgml, %.ps, $(BOOKS)) PDF := $(patsubst %.sgml, %.pdf, $(BOOKS)) @@ -9,6 +9,7 @@ IMG-parportbook := parport-share.fig parport-multi.fig parport-structure.fig EPS-parportbook := $(patsubst %.fig, %.eps, $(IMG-parportbook)) JPG-parportbook := $(patsubst %.fig, %.jpeg, $(IMG-parportbook)) +C-procfs-example = procfs_example.sgml books: $(BOOKS) @@ -28,6 +29,15 @@ %.jpeg: %.fig -fig2dev -Ljpeg $< $@ +%.sgml: %.c + echo "<programlisting>" > $@ + expand --tabs=8 < $< | \ + sed -e "s/&/\\&amp;/g" \ + -e "s/</\\&lt;/g" \ + -e "s/>/\\&gt;/g" >> $@ + echo "</programlisting>" >> $@ + + $(TOPDIR)/scripts/docproc: $(MAKE) -C $(TOPDIR)/scripts docproc @@ -67,6 +77,9 @@ $(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/media/video/videodev.c \ <videobook.tmpl >videobook.sgml +procfs-guide.sgml: procfs-guide.tmpl procfs_example.sgml + $(TOPDIR)/scripts/docgen < procfs-guide.tmpl >$@ + APISOURCES := $(TOPDIR)/drivers/media/video/videodev.c \ $(TOPDIR)/arch/i386/kernel/irq.c \ $(TOPDIR)/arch/i386/kernel/mca.c \ @@ -128,6 +141,7 @@ -$(RM) $(BOOKS) -$(RM) $(DVI) $(AUX) $(TEX) $(LOG) $(OUT) -$(RM) $(JPG-parportbook) $(EPS-parportbook) + -$(RM) $(C-procfs-example) mrproper: clean -$(RM) $(PS) $(PDF) Index: Documentation/DocBook/procfs-guide.tmpl =================================================================== RCS file: procfs-guide.tmpl diff -N procfs-guide.tmpl --- /dev/null Thu Mar 22 14:04:47 2001 +++ Documentation/DocBook/procfs-guide.tmpl Sun Jun 3 23:39:23 2001 @@ -0,0 +1,625 @@ +<!-- -*- sgml -*- --> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[ +<!ENTITY procfsexample SYSTEM "procfs_example.sgml"> +]> + +<book id="LKProcfsGuide"> + <bookinfo> + <title>Linux Kernel Procfs Guide</title> + + <authorgroup> + <author> + <firstname>Erik</firstname> + <othername>(J.A.K.)</othername> + <surname>Mouw</surname> + <affiliation> + <orgname>Delft University of Technology</orgname> + <orgdiv>Faculty of Information Technology and Systems</orgdiv> + <address> + <email>J.A.K.Mouw@its.tudelft.nl</email> + <pob>PO BOX 5031</pob> + <postcode>2600 GA</postcode> + <city>Delft</city> + <country>The Netherlands</country> + </address> + </affiliation> + </author> + </authorgroup> + + <revhistory> + <revision> + <revnumber>1.0&nbsp;</revnumber> + <date>May 30, 2001</date> + <revremark>Initial revision posted to linux-kernel</revremark> + </revision> + <revision> + <revnumber>1.1&nbsp;</revnumber> + <date>June 3, 2001</date> + <revremark>Revised after comments from linux-kernel</revremark> + </revision> + </revhistory> + + <copyright> + <year>2001</year> + <holder>Erik Mouw</holder> + </copyright> + + + <legalnotice> + <para> + This documentation is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later + version. + </para> + + <para> + This documentation is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the GNU General Public License for more details. + </para> + + <para> + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + </para> + + <para> + For more details see the file COPYING in the source + distribution of Linux. + </para> + </legalnotice> + </bookinfo> + + + + + <toc> + </toc> + + + + + <preface> + <title>Preface</title> + + <para> + This guide describes the use of the procfs file system from + within the Linux kernel. The idea to write this guide came up on + the #kernelnewbies IRC channel (see <ulink + url="http://www.kernelnewbies.org/">http://www.kernelnewbies.org/</ulink>), + when Jeff Garzik explained the use of procfs and forwarded me a + message Alexander Viro wrote to the linux-kernel mailing list. I + agreed to write it up nicely, so here it is. + </para> + + <para> + I'd like to thank Jeff Garzik + <email>jgarzik@mandrakesoft.com</email> and Alexander Viro + <email>viro@math.psu.edu</email> for their input, Tim Waugh + <email>twaugh@redhat.com</email> for his <ulink + url="http://people.redhat.com/twaugh/docbook/selfdocbook/">Selfdocbook</ulink>, + and Marc Joosen <email>marcj@historia.et.tudelft.nl</email> for + proofreading. + </para> + + <para> + This documentation was written while working on the LART + computing board (<ulink + url="http://www.lart.tudelft.nl/">http://www.lart.tudelft.nl/</ulink>), + which is sponsored by the Mobile Multi-media Communications + (<ulink + url="http://www.mmc.tudelft.nl/">http://www.mmc.tudelft.nl/</ulink>) + and Ubiquitous Communications (<ulink + url="http://www.ubicom.tudelft.nl/">http://www.ubicom.tudelft.nl/</ulink>) + projects. + </para> + + <para> + Erik + </para> + </preface> + + + + + <chapter id="intro"> + <title>Introduction</title> + + <para> + The <filename class="directory">/proc</filename> file system + (procfs) is a special file system in the linux kernel. It's a + virtual file system: it is not associated with a block device + but exists only in memory. The files in the procfs are there to + allow userland programs access to certain information from the + kernel (like process information in <filename + class="directory">/proc/[0-9]+/</filename>), but also for debug + purposes (like <filename>/proc/ksyms</filename>). + </para> + + <para> + This guide describes the use of the procfs file system from + within the Linux kernel. It starts by introducing all relevant + functions to manage the files within the file system. After that + it shows how to communicate with userland, and some tips and + tricks will be pointed out. Finally a complete example will be + shown. + </para> + + <para> + Note that the files in <filename + class="directory">/proc/sys</filename> are sysctl files: they + don't belong to procfs and are governed by a completely + different API described in the Kernel API book. + </para> + </chapter> + + + + + <chapter id="managing"> + <title>Managing procfs entries</title> + + <para> + This chapter describes the functions that various kernel + components use to populate the procfs with files, symlinks, + device nodes, and directories. + </para> + + <para> + A minor note before we start: if you want to use any of the + procfs functions, be sure to include the correct header file! + This should be one of the first lines in your code: + </para> + + <programlisting> +#include &lt;linux/proc_fs.h&gt; + </programlisting> + + + + + <sect1 id="regularfile"> + <title>Creating a regular file</title> + + <funcsynopsis> + <funcprototype> + <funcdef>struct proc_dir_entry* <function>create_proc_entry</function></funcdef> + <paramdef>const char* <parameter>name</parameter></paramdef> + <paramdef>mode_t <parameter>mode</parameter></paramdef> + <paramdef>struct proc_dir_entry* <parameter>parent</parameter></paramdef> + </funcprototype> + </funcsynopsis> + + <para> + This function creates a regular file with the name + <parameter>name</parameter>, file mode + <parameter>mode</parameter> in the directory + <parameter>parent</parameter>. To create a file in the root of + the procfs, use <constant>NULL</constant> as + <parameter>parent</parameter> parameter. When successful, the + function will return a pointer to the freshly created + <structname>struct proc_dir_entry</structname>; otherwise it + will return <constant>NULL</constant>. <xref + linkend="userland"> describes how to do something useful with + regular files. + <para> + + <para> + Note that it is specifically supported that you can pass a + path that spans multiple directories. For example + <function>create_proc_entry</function>(<parameter>"drivers/via0/info"</parameter>) + will create the <filename class="directory">via0</filename> + directory if necessary, with standard + <constant>0755</constant> permissions. + </para> + + <para> + If you only want to be able to read the file, the function + <function>create_proc_read_entry</function> described in <xref + linkend="convenience"> may be used to create and initialise + the procfs entry in one single call. + </para> + </sect1> + + + + + <sect1> + <title>Creating a symlink</title> + + <funcsynopsis> + <funcprototype> + <funcdef>struct proc_dir_entry* + <function>proc_symlink</function></funcdef> <paramdef>const + char* <parameter>name</parameter></paramdef> + <paramdef>struct proc_dir_entry* + <parameter>parent</parameter></paramdef> <paramdef>const + char* <parameter>dest</parameter></paramdef> + </funcprototype> + </funcsynopsis> + + <para> + This creates a symlink in the procfs directory + <parameter>parent</parameter> that points from + <parameter>name</parameter> to + <parameter>dest</parameter>. This translates in userland to + <literal>ln -s</literal> <parameter>dest</parameter> + <parameter>name</parameter>. + </para> + </sect1> + + + + + <sect1> + <title>Creating a device</title> + + <funcsynopsis> + <funcprototype> + <funcdef>struct proc_dir_entry* <function>proc_mknod</function></funcdef> + <paramdef>const char* <parameter>name</parameter></paramdef> + <paramdef>mode_t <parameter>mode</parameter></paramdef> + <paramdef>struct proc_dir_entry* <parameter>parent</parameter></paramdef> + <paramdef>kdev_t <parameter>rdev</parameter></paramdef> + </funcprototype> + </funcsynopsis> + + <para> + Creates a device file <parameter>name</parameter> with mode + <parameter>mode</parameter> in the procfs directory + <parameter>parent</parameter>. The device file will work on + the device <parameter>rdev</parameter>, which can be generated + by using the <literal>MKDEV</literal> macro from + <literal>linux/kdev_t.h</literal>. The + <parameter>mode</parameter> parameter + <emphasis>must</emphasis> contain <constant>S_IFBLK</constant> + or <constant>S_IFCHR</constant> to create a device + node. Compare with userland <literal>mknod + --mode=</literal><parameter>mode</parameter> + <parameter>name</parameter> <parameter>rdev</parameter>. + </para> + </sect1> + + + + + <sect1> + <title>Creating a directory</title> + + <funcsynopsis> + <funcprototype> + <funcdef>struct proc_dir_entry* <function>proc_mkdir</function></funcdef> + <paramdef>const char* <parameter>name</parameter></paramdef> + <paramdef>struct proc_dir_entry* <parameter>parent</parameter></paramdef> + </funcprototype> + </funcsynopsis> + + <para> + Create a directory <parameter>name</parameter> in the procfs + directory <parameter>parent</parameter>. + </para> + </sect1> + + + + + <sect1> + <title>Removing an entry</title> + + <funcsynopsis> + <funcprototype> + <funcdef>void <function>remove_proc_entry</function></funcdef> + <paramdef>const char* <parameter>name</parameter></paramdef> + <paramdef>struct proc_dir_entry* <parameter>parent</parameter></paramdef> + </funcprototype> + </funcsynopsis> + + <para> + Removes the entry <parameter>name</parameter> in the directory + <parameter>parent</parameter> from the procfs. Entries are + removed by their <emphasis>name</emphasis>, not by the + <structname>struct proc_dir_entry</structname> returned by the + various create functions. Note that this function doesn't + recursively remove entries. + </para> + + <para> + Be sure to free the <structfield>data</structfield> entry from + the <structname>struct proc_dir_entry</structname> before + <function>remove_proc_entry</function> is called (that is: if + there was some <structfield>data</structfield> allocated, of + course). See <xref linkend="usingdata"> for more information + on using the <structfield>data</structfield> entry. + </para> + </sect1> + </chapter> + + + + + <chapter id="userland"> + <title>Communicating with userland</title> + + <para> + Instead of reading (or writing) information directly from + kernel memory, procfs works with <emphasis>call back + functions</emphasis> for files: functions that are called when + a specific file is being read or written. Such functions have + to be initialised after the procfs file is created by setting + the <structfield>read_proc</structfield> and/or + <structfield>write_proc</structfield> fields in the + <structname>struct proc_dir_entry*</structname> that the + function <function>create_proc_entry</function> returned: + </para> + + <programlisting> +struct proc_dir_entry* entry; + +entry->read_proc = read_proc_foo; +entry->write_proc = write_proc_foo; + </programlisting> + + <para> + If you only want to use a the + <structfield>read_proc</structfield>, the function + <function>create_proc_read_entry</function> described in <xref + linkend="convenience"> may be used to create and initialise the + procfs entry in one single call. + </para> + + + + <sect1> + <title>Reading data</title> + + <para> + The read function is a call back function that allows userland + processes to read data from the kernel. The read function + should have the following format: + </para> + + <funcsynopsis> + <funcprototype> + <funcdef>int <function>read_func</function></funcdef> + <paramdef>char* <parameter>page</parameter></paramdef> + <paramdef>char** <parameter>start</parameter></paramdef> + <paramdef>off_t <parameter>off</parameter></paramdef> + <paramdef>int <parameter>count</parameter></paramdef> + <paramdef>int* <parameter>eof</parameter></paramdef> + <paramdef>void* <parameter>data</parameter></paramdef> + </funcprototype> + </funcsynopsis> + + <para> + The read function should write its information into the + <parameter>page</parameter>. For proper use, the function + should start writing at an offset of + <parameter>off</parameter> in <parameter>page</parameter> and + write at most <parameter>count</parameter> bytes, but because + most read functions are quite simple and only return a small + amount of information, these two parameters are usually + ignored (it breaks pagers like <literal>more</literal> and + <literal>less</literal>, but <literal>cat</literal> still + works). + </para> + + <para> + If the <parameter>off</parameter> and + <parameter>count</parameter> parameters are properly used, + <parameter>eof</parameter> should be used to signal that the + end of the file has been reached by writing + <literal>1</literal> to the memory location + <parameter>eof</parameter> points to. + </para> + + <para> + The parameter <parameter>start</parameter> doesn't seem to be + used anywhere in the kernel. The <parameter>data</parameter> + parameter can be used to create a single call back function for + several files, see <xref linkend="usingdata">. + </para> + + <para> + The <function>read_func</function> function must return the + number of bytes written into the <parameter>page</parameter>. + </para> + + <para> + <xref linkend="example"> shows how to use a read call back + function. + </para> + </sect1> + + + + + <sect1> + <title>Writing data</title> + + <para> + The write call back function allows a userland process to write + data to the kernel, so it has some kind of control over the + kernel. The write function should have the following format: + </para> + + <funcsynopsis> + <funcprototype> + <funcdef>int <function>write_func</function></funcdef> + <paramdef>struct file* <parameter>file</parameter></paramdef> + <paramdef>const char* <parameter>buffer</parameter></paramdef> + <paramdef>unsigned long <parameter>count</parameter></paramdef> + <paramdef>void* <parameter>data</parameter></paramdef> + </funcprototype> + </funcsynopsis> + + <para> + The write function should read <parameter>count</parameter> + bytes at maximum from the <parameter>buffer</parameter>. Note + that the <parameter>buffer</parameter> doesn't live in the + kernel's memory space, so it should first be copied to kernel + space with <function>copy_from_user</function>. The + <parameter>file</parameter> parameter is usually + ignored. <xref linkend="usingdata"> shows how to use the + <parameter>data</parameter> parameter. + </para> + + <para> + Again, <xref linkend="example"> shows how to use this call back + function. + </para> + </sect1> + + + + + <sect1 id="usingdata"> + <title>A single call back for many files</title> + + <para> + When a large number of almost identical files is used, it's + quite inconvenient to use a separate call back function for + each file. A better approach is to have a single call back + function that distinguishes between the files by using the + <structfield>data</structfield> field in <structname>struct + proc_dir_entry</structname>. First of all, the + <structfield>data</structfield> field has to be initialised: + </para> + + <programlisting> +struct proc_dir_entry* entry; +struct my_file_data *file_data; + +file_data = kmalloc(sizeof(struct my_file_data), GFP_KERNEL); +entry->data = file_data; + </programlisting> + + <para> + The <structfield>data</structfield> field is a <type>void + *</type>, so it can be initialised with anything. + </para> + + <para> + Now that the <structfield>data</structfield> field is set, the + <function>read_proc</function> and + <function>write_proc</function> can use it to distinguish + between files because they get it passed into their + <parameter>data</parameter> parameter: + </para> + + <programlisting> +int foo_read_func(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + if(data == file_data) { + /* special case for this file */ + } else { + /* normal processing */ + } + + return len; +} + </programlisting> + + <para> + Be sure to free the <structfield>data</structfield> data field + when removing the procfs entry. + </para> + </sect1> + </chapter> + + + + + <chapter id="tips"> + <title>Tips and tricks</title> + + + + + <sect1 id="convenience"> + <title>Convenience functions</title> + + <funcsynopsis> + <funcprototype> + <funcdef>struct proc_dir_entry* <function>create_proc_read_entry</function></funcdef> + <paramdef>const char* <parameter>name</parameter></paramdef> + <paramdef>mode_t <parameter>mode</parameter></paramdef> + <paramdef>struct proc_dir_entry* <parameter>parent</parameter></paramdef> + <paramdef>read_proc_t* <parameter>read_proc</parameter></paramdef> + <paramdef>void* <parameter>data</parameter></paramdef> + </funcprototype> + </funcsynopsis> + + <para> + This function creates a regular file in exactly the same way + as <function>create_proc_entry</function> from <xref + linkend="regularfile"> does, but also allows to set the read + function <parameter>read_proc</parameter> in one call. This + function can set the <parameter>data</parameter> as well, like + explained in <xref linkend="usingdata">. + </para> + </sect1> + + + + <sect1> + <title>Modules</title> + + <para> + If procfs is being used from within a module, be sure to set + the <structfield>owner</structfield> field in the + <structname>struct proc_dir_entry</structname> to + <constant>THIS_MODULE</constant>. + <para> + + <programlisting> +struct proc_dir_entry* entry; + +entry->owner = THIS_MODULE; + </programlisting> + </sect1> + + + + + <sect1> + <title>Mode and ownership</title> + + <para> + Sometimes it is useful to change the mode and/or ownership of + a procfs entry. Here is an example that shows how to achieve + that: + </para> + + <programlisting> +struct proc_dir_entry* entry; + +entry->mode = S_IWUSR |S_IRUSR | S_IRGRP | S_IROTH; +entry->uid = 0; +entry->gid = 100; + </programlisting> + + </sect1> + </chapter> + + + + + <chapter id="example"> + <title>Example</title> + + <!-- be careful with the example code: it shouldn't be wider than + approx. 60 columns, or otherwise it won't fit properly on a page + --> + +&procfsexample; + + </chapter> +</book> Index: Documentation/DocBook/procfs_example.c =================================================================== RCS file: procfs_example.c diff -N procfs_example.c --- /dev/null Thu Mar 22 14:04:47 2001 +++ Documentation/DocBook/procfs_example.c Sun Jun 3 23:09:39 2001 @@ -0,0 +1,249 @@ +/* + * procfs_example.c: an example proc interface + * + * Copyright (C) 2001, Erik Mouw (J.A.K.Mouw@its.tudelft.nl) + * + * This file accompanies the procfs-guide in the Linux kernel + * source. Its main use is to demonstrate the concepts and + * functions described in the guide. + * + * This software has been developed while working on the LART + * computing board (http://www.lart.tudelft.nl/), which is + * sponsored by the Mobile Multi-media Communications + * (http://www.mmc.tudelft.nl/) and Ubiquitous Communications + * (http://www.ubicom.tudelft.nl/) projects. + * + * The author can be reached at: + * + * Erik Mouw + * Information and Communication Theory Group + * Faculty of Information Technology and Systems + * Delft University of Technology + * P.O. Box 5031 + * 2600 GA Delft + * The Netherlands + * + * + * This program is free software; you can redistribute + * it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, + * Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/proc_fs.h> +#include <linux/sched.h> +#include <asm/uaccess.h> + + +#define MODULE_VERSION "1.0" +#define MODULE_NAME "procfs_example" + +#define FOOBAR_LEN 8 + +struct fb_data_t { + char name[FOOBAR_LEN + 1]; + char value[FOOBAR_LEN + 1]; +}; + + +static struct proc_dir_entry *example_dir, *foo_file, + *bar_file, *jiffies_file, *tty_device, *symlink; + + +struct fb_data_t foo_data, bar_data; + + +static int proc_read_jiffies(char *page, char **start, + off_t off, int count, + int *eof, void *data) +{ + int len; + + MOD_INC_USE_COUNT; + + len = sprintf(page, "jiffies = %ld\n", + jiffies); + + MOD_DEC_USE_COUNT; + + return len; +} + + +static int proc_read_foobar(char *page, char **start, + off_t off, int count, + int *eof, void *data) +{ + int len; + struct fb_data_t *fb_data = (struct fb_data_t *)data; + + MOD_INC_USE_COUNT; + + len = sprintf(page, "%s = '%s'\n", + fb_data->name, fb_data->value); + + MOD_DEC_USE_COUNT; + + return len; +} + + +static int proc_write_foobar(struct file *file, + const char *buffer, + unsigned long count, + void *data) +{ + int len; + struct fb_data_t *fb_data = (struct fb_data_t *)data; + + MOD_INC_USE_COUNT; + + if(count > FOOBAR_LEN) + len = FOOBAR_LEN; + else + len = count; + + if(copy_from_user(fb_data->value, buffer, len)) { + MOD_DEC_USE_COUNT; + return -EFAULT; + } + + fb_data->value[len] = '\0'; + + MOD_DEC_USE_COUNT; + + return len; +} + + +static int __init init_procfs_example(void) +{ + int rv = 0; + + /* create directory */ + example_dir = proc_mkdir(MODULE_NAME, NULL); + if(example_dir == NULL) { + rv = -ENOMEM; + goto out; + } + + example_dir->owner = THIS_MODULE; + + /* create jiffies using convenience function */ + jiffies_file = create_proc_read_entry("jiffies", + 0444, example_dir, + proc_read_jiffies, + NULL); + if(jiffies_file == NULL) { + rv = -ENOMEM; + goto no_jiffies; + } + + jiffies_file->owner = THIS_MODULE; + + /* create foo and bar files using same callback + * functions + */ + foo_file = create_proc_entry("foo", 0644, example_dir); + if(foo_file == NULL) { + rv = -ENOMEM; + goto no_foo; + } + + strcpy(foo_data.name, "foo"); + strcpy(foo_data.value, "foo"); + foo_file->data = &foo_data; + foo_file->read_proc = proc_read_foobar; + foo_file->write_proc = proc_write_foobar; + foo_file->owner = THIS_MODULE; + + bar_file = create_proc_entry("bar", 0644, example_dir); + if(bar_file == NULL) { + rv = -ENOMEM; + goto no_bar; + } + + strcpy(bar_data.name, "bar"); + strcpy(bar_data.value, "bar"); + bar_file->data = &bar_data; + bar_file->read_proc = proc_read_foobar; + bar_file->write_proc = proc_write_foobar; + bar_file->owner = THIS_MODULE; + + /* create tty device */ + tty_device = proc_mknod("tty", S_IFCHR | 0666, + example_dir, MKDEV(5, 0)); + if(tty_device == NULL) { + rv = -ENOMEM; + goto no_tty; + } + + tty_device->owner = THIS_MODULE; + + /* create symlink */ + symlink = proc_symlink("jiffies_too", example_dir, + "jiffies"); + if(symlink == NULL) { + rv = -ENOMEM; + goto no_symlink; + } + + symlink->owner = THIS_MODULE; + + /* everything OK */ + printk(KERN_INFO "%s %s initialised\n", + MODULE_NAME, MODULE_VERSION); + return 0; + +no_symlink: + remove_proc_entry("tty", example_dir); +no_tty: + remove_proc_entry("bar", example_dir); +no_bar: + remove_proc_entry("foo", example_dir); +no_foo: + remove_proc_entry("jiffies", example_dir); +no_jiffies: + remove_proc_entry(MODULE_NAME, NULL); +out: + return rv; +} + + +static void __exit cleanup_procfs_example(void) +{ + remove_proc_entry("jiffies_too", example_dir); + remove_proc_entry("tty", example_dir); + remove_proc_entry("bar", example_dir); + remove_proc_entry("foo", example_dir); + remove_proc_entry("jiffies", example_dir); + remove_proc_entry(MODULE_NAME, NULL); + + printk(KERN_INFO "%s %s removed\n", + MODULE_NAME, MODULE_VERSION); +} + + +module_init(init_procfs_example); +module_exit(cleanup_procfs_example); + +MODULE_AUTHOR("Erik Mouw"); +MODULE_DESCRIPTION("procfs examples"); + +EXPORT_NO_SYMBOLS;

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