0

I wrote a basic code which will print process name using seq_file in proc file.

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>   
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/seq_file.h>
#include <linux/sched.h>
#include <linux/sched/task.h>
#include <linux/sched/signal.h>

MODULE_LICENSE("GPL");


static struct proc_dir_entry *dir_entry;

/**
 * This function is called at the beginning of a sequence.
 * ie, when:
 *  - the /proc file is read (first time)
 *  - after the function stop (end of sequence)
 *
 */

static void *my_seq_start(struct seq_file *s, loff_t *pos)
{
    loff_t index = *pos;
    if (index == 0) {
        seq_printf(s, "Current all the processes in system:\n"
                "%-24s%-5s\n", "name", "pid");
        return &init_task;
    }
    else {
        return NULL;
    }

}

/**
 * This function is called after the beginning of a sequence.
 * It's called untill the return is NULL (this ends the sequence).
 *
 */

static void *my_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
    struct task_struct *task = (struct task_struct *)v;
    task = next_task(task);
    if ((*pos != 0) && (task == &init_task)) {
        return NULL;
    }
    ++*pos;
    return task;
}

/**
 * This function is called at the end of a sequence
 *
 */
static void my_seq_stop(struct seq_file *s, void *v)
{
}

/**
 * This function is called for each "step" of a sequence
 *
 */
static int my_seq_show(struct seq_file *s, void *v)
{
    struct task_struct * task = (struct task_struct *)v;
    seq_printf(s, "%-24s%-5d\n", task->comm, task->pid);
    return 0;
}

static struct seq_operations my_seq_ops = {
    .start = my_seq_start,
    .next  = my_seq_next,
    .stop  = my_seq_stop,
    .show  = my_seq_show
};

static int my_open(struct inode *inode, struct file *filp)
{
    return seq_open(filp, &my_seq_ops);
}


static struct file_operations proc_ops = 
{
    .owner = THIS_MODULE,
    .open = my_open, //user supplied
    .read = seq_read, //interface supplied
    .release = seq_release, //interface supplied
    .llseek = seq_lseek //interface supplied
};

static int proc_init(void)
{
    dir_entry = proc_mkdir("embedded", NULL);

    proc_create("pid",0640,dir_entry,&proc_ops);
    pr_info("ldd proc entry created\n");
    return 0;
}

static void proc_cleanup(void)
{
    remove_proc_entry("pid", dir_entry);
    remove_proc_entry("embedded", NULL);
}

module_init(proc_init);
module_exit(proc_cleanup);

Not all the processes are printed, it stops after printing 300 processes. Is there any limit in the number of iterations we can move with seq api of procfs.

Did I make any mistake in the above code? Can we print only page size with seq api.

md.jamal
  • 4,067
  • 8
  • 45
  • 108
  • 30 * 300 = 9000 which is more than a size of one page. Something else prevents you to see all of them. – 0andriy Sep 07 '19 at 10:03
  • So, page size is not the culprit. – md.jamal Sep 07 '19 at 10:28
  • "Not all the processes are printed, it stops after printing 300 processes." - Are you sure that there is more processes? Have you tried to add some `printk` calls into `my_seq_next` function, e.g. into the branch which returns NULL? – Tsyvarev Sep 07 '19 at 13:01
  • I replaced seq_printk with printk and it prints all the processes. It's not exactly 300 for your information – md.jamal Sep 07 '19 at 15:21
  • 1
    What if you do **both** `printk` and `seq_printk`, do they print the same amount of processes? Note, that proper traversal of tasks with `next_task` implies to be wrapped with RCU read lock. Otherwise it could be a chance to get wrong task list (or even cause some error). – Tsyvarev Sep 07 '19 at 16:58
  • As for possible limit for `seq_file` iterations, according to the code there is not such thing. Moreover, even single element of the iteration may have a big size. – Tsyvarev Sep 07 '19 at 17:00
  • I have found a solution. There is some limit with seq_file most probably PAGE_SIZE. You can look into fs/seq_file.c. It called start again and I instead of returning NULL, i returned next_task(task) until i looped back to init_task – md.jamal Sep 09 '19 at 05:07
  • 1
    The code doesn't look very safe. Processes could be created or destroyed while you are iterating through the list. – Ian Abbott Sep 09 '19 at 15:11
  • To make it safer, call `rcu_read_lock();` in `my_seq_start`, and call `rcu_read_unlock();` in `my_seq_stop`. – Ian Abbott Sep 09 '19 at 15:23
  • How large is the userspace buffer you are `read()`ing into? – Ian Abbott Sep 09 '19 at 15:30
  • I am using cat command to read the data – md.jamal Sep 10 '19 at 01:33

1 Answers1

0

start() is invoked after stop() every time. See the first image here: https://linux.die.net/lkmpg/x861.html

You could do something like this:

static void *my_seq_start(struct seq_file *seq, loff_t *pos)
{
    struct task_struct *taskptr; 
    loff_t off = 0;
    rcu_read_lock();
    seq_printf(seq, "Current all the processes in system:\n"
                "%-6s%-24s\n", "pid", "name");
    for_each_process(taskptr) {
        if (*pos == off++) {
            return taskptr;}
    }
    return NULL;    //returning NULL if we reached the end
}