6

Hello I require following information about process with some PID:

name, ppid, state, #ofOpenFiles, #ofThreads

I know the example of /proc/pid/stat file is like :

15 (watchdog/1) S 2 0 0 0 -1 69239104 0 0 0 0 0 69 0 0 -100 0 1 0 6 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744073709551615 0 0 17 1 99 1 0 0 0 0 0 0 0 0 0 0 0

My current attempt of parsing such file:

FILE *fp;
           char buff[255];
           fp= fopen("/proc/123/stat", "r");
           if(fp == NULL){

           }else{

             fscanf(fp, "%d %s %c %d %d %d %d %d %u %lu ....", &pid, &name, &ppid......)
             fclose(fp);
           }

I don't find this very good method. How to do this?

Jonzi
  • 141
  • 1
  • 2
  • 13

1 Answers1

2

The solution you describe looks good (especially using @kaylum's idea of * format specifier). Note that you can use the same variable several times to ignore parameters:

fscanf(fp, "%d %s %c %d %d %d %d %d %u %lu ...", &pid, &name, &ppid, &dummy, &dummy, &dummy, ...);

You can also look into strtok to read each line "token" by token. You can use it to create a function returning an array of char* like in this other question and get the i-th element (with all proper NULL and size checks).

Edit: If the file name contains spaces then you'll have to either use a regex or parse the string manually with e.g. strtok() to detect the proper format.

Matthieu
  • 2,736
  • 4
  • 57
  • 87
  • 3
    What happens if the stat file looks like this: 49102 (Name with space) S 3249 3506 3506 0 -1 1077936192 573 0 0 0 671 575 0 0 20 0 11 0 10835724 808615936 13699 18446744073709551615 4194304 8101684 140732335739408 140538183277872 140538250385161 0 4 4096 1260 1 0 0 -1 7 0 0 0 0 0 10201672 10213228 40337408 140732335748225 140732335748579 140732335748579 140732335751059 0 Here the name would be "(Name" and the following reading of fscanf would be incorrect. – txs Sep 18 '18 at 23:34
  • @txs then it won't work. You'll have to resort to regex or manual parsing. I edited to make it clearer. Will that be enough to remove the downvote? – Matthieu Sep 19 '18 at 06:04
  • 1
    @txs, in that case you have to do something like this: `"%*s (%*16[^)]) %s"`. I don't need the first value and the 2nd value in this case, I only need the third value as string. If you need those, just remove the `*` and assign proper variables... – 15 Volts Jan 31 '21 at 19:33
  • 1
    Also note, the string itself can be more that 16 characters if that's a kernel process. The process can also be wrapped inside `()` without any `\`, like `(sd-pam)` for example. So you need to match all the recursive `)`. This works: `fscanf(f, "%*llu (%*[^)]%*[)] %c", _s) ;` Again, this ignores the first 2 values for example. You can't reliably say the length of the 2nd field, because it's not limited to `TASK_COMM_LEN` for kernel space programs. – 15 Volts Jan 31 '21 at 21:47