Function for scanning /proc
I wonder that I have to add or remove some corner-case handlings for read(), malloc(), or realloc().


int scan_ps(struct my_proc ***ptr, int (*filter)(const struct my_proc *));


Make review easier

Allocating to the size of the type rather than the size of an object obliges a reviewer to double check - did code use the right type?  Instead, make code easier to review, code right and maintain.

// ps_list = malloc(sizeof(struct my_proc *)*size);
ps_list = malloc(sizeof *ps_list * size);

// current_ps = malloc(sizeof(struct my_proc));
current_ps = malloc(sizeof *current_ps);


Why use unsigned?

With array sizing and indexing, size_t is the Goldilocks type, neither too narrow nor too wide.

// unsigned int size = 16;
size_t size = 16;


Questionable type

Why use int when read() returns ssize_t?

// int n;
ssize_t n;
n = read(fd, buf, MAX_LEN_STR);


Questionable code

Why potentially shorted n and not report this as an error?  Just read less.

// n = read(fd, buf, MAX_LEN_STR);
// n = (MAX_LEN_STR - 1) &gt; n ? n : (MAX_LEN_STR - 1);

n = read(fd, buf, MAX_LEN_STR - 1);
// or better
n = read(fd, buf, sizeof buf - 1);




Proper use of goto

goto is often mis-used.  This is an acceptable usage.

Why MAX_LEN_STR?

For a max path length, I'd expect PATH_MAX

// char path[MAX_LEN_STR];
char path[PATH_MAX /* maybe + 1 */];


Why 256?

Avoid naked magic numbers as in char cmdline[256];.  Perhaps char cmdline[MAX_LEN_STR];?

Why MAX_LEN_STR?

In limiting string copy, best to use the size of the destination and not some other constant.

// strncpy_v2(current_ps-&gt;cmdline, buf, MAX_LEN_STR);
strncpy_v2(current_ps-&gt;cmdline, buf, sizeof current_ps-&gt;cmdline);


Missing error report

strncpy_v2() does not indicate it failed to copy a complete string. With all the other good error checking, it is curious code does not report this.  If failure cannot occur because of the construction of code, why then have strncpy_v2() versus simple strcpy()?  Being "safe" by not over-writing memory is good, yet not reporting insufficiencies is not.

Sign and potential print specifier mis-match

A pid_t is some signed type.  Ref.  Best to use signed math and cast to a wide signed type to print.

// pid = atoi(dp-&gt;d_name);
pid = atoll(dp-&gt;d_name);

// snprintf(path, sizeof(path), "/proc/%u/cmdline", pid);
snprintf(path, sizeof(path), "/proc/%jd/cmdline", (intmax_t) pid);
// or
snprintf(path, sizeof(path), "/proc/%lld/cmdline", (long long) pid);




Other stuff

What is *ptr in failure?

When scan_ps() fails, returns -1, consider setting *ptr = NULL;

Lose a *

Further, consider using re-architect and using a return value of NULL as an error indication.  Otherwise the return value is a pointer to a NULL terminated array.  You do lose one *, but I'd say that is good.

struct my_proc **scan_ps(int (*filter)(const struct my_proc *));


Why define FALSE, TRUE?

Instead, use stdbool.h.

// #define FALSE               (0)
// #define TRUE                (!FALSE)
//   return TRUE;

#include &lt;stdbool.h&gt;
...
   return true;