Re: seq_file and exporting dynamically allocated data

From: Tigran Aivazian
Date: Mon Nov 17 2003 - 04:43:02 EST


Here are two files: simple.c kernel module and user.c user test program.
If you (or anyone) believe it is possible to return more than a single
page on a read(2) please change them accordingly and let me know.

simple.c
=======================

/*
* simple.c --- Provide access to process data via /proc/scan
*/

#include <linux/init.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/file.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>

#include <asm/uaccess.h>

/* Red Hat Linux 2.4.20-8 has its own next_task() macro */
#ifndef next_task
#define next_task(p) (p)->next_task
#endif

struct proc {
int pid; /* process id */
int uid; /* real user id */
char comm[17]; /* process name */
char wasted[200]; /* wasted space, filled with 'A' */
};

#define PROCLEN (sizeof(struct proc))

MODULE_DESCRIPTION("Simple module for testing seq_file");
MODULE_AUTHOR("Tigran Aivazian <tigran@xxxxxxxxxxx>");
MODULE_LICENSE("GPL");
EXPORT_NO_SYMBOLS;

static int simple_show(struct seq_file *m, void *v)
{
struct task_struct *p = (struct task_struct *)v;
struct proc proc;

proc.pid = p->pid;
proc.uid = p->uid;
strncpy(proc.comm, p->comm, 16);
memset(proc.wasted, 'A', 200);

if (m->count + PROCLEN < m->size) {
memcpy(m->buf + m->count, &proc, PROCLEN);
m->count += PROCLEN;
return 0;
} else {
m->count = m->size;
return -1;
}
}

static void *c_start(struct seq_file *m, loff_t *ppos)
{
struct task_struct *p = &init_task;
int n = *ppos;

read_lock(&tasklist_lock);

if (n == 0)
return (void *)p;

while (n--)
p = next_task(p);

return (void *)p;
}

static void *c_next(struct seq_file *m, void *v, loff_t *ppos)
{
struct task_struct *p= (struct task_struct *)v;

p = next_task(p);
if (p == &init_task)
p = NULL;
else
++*ppos;

return (void *)p;
}

static void c_stop(struct seq_file *m, void *v)
{
read_unlock(&tasklist_lock);
}

struct seq_operations seqops = {
.start = c_start,
.next = c_next,
.stop = c_stop,
.show = simple_show,
};

static int simple_open(struct inode *inode, struct file *file)
{
return seq_open(file, &seqops);
}


static struct file_operations fileops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};


static int __init simple_init(void)
{
struct proc_dir_entry *entry = create_proc_entry("scan", 0, NULL);

if (entry) {
entry->proc_fops = &fileops;
printk(KERN_INFO "simple: /proc/scan created\n");
return 0;
} else {
printk(KERN_ERR "simple: failed to create /proc/scan\n");
return -1;
}
}

static void __exit simple_exit(void)
{
remove_proc_entry("scan", 0);
}

module_init(simple_init)
module_exit(simple_exit)

user.c
=============================

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>

struct proc {
int pid; /* process id */
int uid; /* real user id */
char comm[17]; /* process name */
char wasted[200]; /* wasted space, filled with 'A' */
};


#define PROCLEN (sizeof(struct proc))
#define MAXPROCS 100
struct proc procs[MAXPROCS];

int main(int argc, char *argv[])
{
int fd, len, nproc, i, j, count = 0;
static int first_scan = 1;

fd = open("/proc/scan", O_RDONLY);
if (fd == -1) {
fprintf(stderr, "%s: open(\"/proc/scan\"), errno=%d (%s)\n",
argv[0], errno, strerror(errno));
exit(1);
}

while (1) {
len = read(fd, procs, MAXPROCS*PROCLEN);
nproc = len/PROCLEN;

if ((procs[0].pid == 0) && !first_scan) {
printf("Total: count=%d procs\n\n", count);
count = 0;
break;
}
printf("\tnproc=%d\n", nproc);

if (first_scan)
first_scan = 0;

count += nproc;

printf("%d bytes (%d procs)\n", len, nproc);
for (i=0; i<nproc; i++) {
printf("proc[%d]: pid=%d, uid=%d, comm=<%s>\n",
i, procs[i].pid, procs[i].uid, procs[i].comm);
for (j=0; j<200; j++)
if (procs[i].wasted[j] != 'A') {
fprintf(stderr, "Corruption i=%d, j=%d\n", i, j);
exit(1);
}
}
}
return 0;
}

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