2
\$\begingroup\$

The following logic provides iteration over /proc/<PID>/maps for a Linux process. What I'm primarily (though not solely) interested in for this review is whether or not I've made any invalid assumptions about the layout of the maps file.

iterate_map.h

#ifndef REAP_ITERATE_MAP_H
#define REAP_ITERATE_MAP_H
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#define REAP_PATH_SIZE 256
enum reapRetValue {
 REAP_RET_OK, /**< The function was successful.*/
 REAP_RET_DONE, /**< The iteration is finished.*/
 REAP_RET_BAD_USAGE, /**< A funciton was called with invalid arguments.*/
 REAP_RET_OUT_OF_MEMORY, /**< A memory allocation failed.*/
 REAP_RET_NO_PERMISSION, /**< No permission to access a requested resource.*/
 REAP_RET_NOT_FOUND, /**< A requested resource was not found.*/
 REAP_RET_TOO_MANY_LINKS, /**< Too many symbolic links encountered.*/
 REAP_RET_FILE_READ, /**< Failed to read from a file.*/
 REAP_RET_OTHER, /**< Catch-all error.*/
};
/**
 * @brief Iterates over all line in a /proc/<PID>/maps file.
 *
 * @note User code should not access the iterator's fields.
 */
typedef struct reapMapIterator {
 FILE *file;
} reapMapIterator;
/**
 * @brief Result generated by a reapMapIterator.
 */
typedef struct reapMapResult {
 unsigned long start; /**< The start of the memory section.*/
 unsigned long end; /**< The end of the memory section.*/
 unsigned int offset; /**< The offset of the memory section in the referent file.*/
 int permissions; /**< The permissions of the memory section.*/
 dev_t device; /**< The device number of the referent file.*/
 ino_t inode; /**< The inode of the referent file.*/
 char name[REAP_PATH_SIZE]; /**< The name of the referent file.*/
} reapMapResult;
/**
 * @brief Initializes an iterator.
 *
 * @param pid The PID.
 * @param iterator A pointer to the iterator.
 *
 * @return REAP_RET_OK if successful and an error code otherwise.
 */
int
reapMapIteratorInit(pid_t pid, reapMapIterator *iterator);
/**
 * @brief Closes an iterator.
 *
 * @param iterator A pointer to the iterator.
 */
void
reapMapIteratorClose(reapMapIterator *iterator);
/**
 * @brief Gets the next result.
 *
 * @param iterator A pointer to the iterator.
 * @param[out] result A pointer to the result to be populated.
 *
 * @return REAP_RET_OK if a result was found, REAP_RET_DONE if the iteration is finished, and an
 * error code otherwise.
 */
int
reapMapIteratorNext(const reapMapIterator *iterator, reapMapResult *result);
#endif // REAP_ITERATE_MAP_H

iterate_map.c

#include <errno.h>
#include <string.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <reap/iterate_map.h>
static int
translateErrno(void)
{
 switch (errno) {
 case EINVAL: return REAP_RET_BAD_USAGE;
 case EACCES: return REAP_RET_NO_PERMISSION;
 case ELOOP: return REAP_RET_TOO_MANY_LINKS;
 case ENOENT:
 case ENOTDIR:
 case EIO: return REAP_RET_FILE_READ;
 case ENOMEM: return REAP_RET_OUT_OF_MEMORY;
 default: return REAP_RET_OTHER;
 }
}
int
reapMapIteratorInit(pid_t pid, reapMapIterator *iterator)
{
 char buffer[100];
 if (pid <= 0 || !iterator) {
 return REAP_RET_BAD_USAGE;
 }
 snprintf(buffer, sizeof(buffer), "/proc/%li/maps", (long)pid);
 iterator->file = fopen(buffer, "r");
 if (!iterator->file) {
 return translateErrno();
 }
 return REAP_RET_OK;
}
void
reapMapIteratorClose(reapMapIterator *iterator)
{
 if (iterator && iterator->file) {
 fclose(iterator->file);
 iterator->file = NULL;
 }
}
int
reapMapIteratorNext(const reapMapIterator *iterator, reapMapResult *result)
{
 int num_matches;
 unsigned int major, minor;
 unsigned long inode;
 char r, w, x;
 char line[256];
 if (!iterator || !iterator->file || !result) {
 return REAP_RET_BAD_USAGE;
 }
 if (!fgets(line, sizeof(line), iterator->file)) {
 if (ferror(iterator->file)) {
 return REAP_RET_FILE_READ;
 }
 else {
 return REAP_RET_DONE;
 }
 }
 num_matches = sscanf(line, "%lx-%lx %c%c%c%*c %x %x:%x %lu %s", &result->start, &result->end, &r, &w, &x,
 &result->offset, &major, &minor, &inode, result->name);
 if (num_matches < 9) {
 return REAP_RET_OTHER;
 }
 if (num_matches == 9) {
 result->name[0] = '0円';
 }
 result->permissions = 0;
 if (r == 'r') {
 result->permissions |= PROT_READ;
 }
 if (w == 'w') {
 result->permissions |= PROT_WRITE;
 }
 if (x == 'x') {
 result->permissions |= PROT_EXEC;
 }
 result->device = makedev(major, minor);
 result->inode = inode;
 return REAP_RET_OK;
}
200_success
145k22 gold badges190 silver badges478 bronze badges
asked Aug 22, 2022 at 2:03
\$\endgroup\$
4
  • \$\begingroup\$ Why 256 in #define REAP_PATH_SIZE 256? 256 deserves some explanation. \$\endgroup\$ Commented Aug 25, 2022 at 7:45
  • \$\begingroup\$ Are you asking why it's a power of 2 or why I'm not using the already-defined PATH_SIZE? \$\endgroup\$ Commented Aug 25, 2022 at 13:20
  • \$\begingroup\$ Daniel Walker, OK, why power of 2, why not PATH_SIZE, why undocumented? \$\endgroup\$ Commented Aug 25, 2022 at 14:45
  • \$\begingroup\$ I didn't want to make the structure take up too much space. Consider if I wanted to form an array or linked list of all of a process' map entries. That's a lot of memory to allocate when, in all likelihood, none of the paths will be anywhere near PATH_SIZE in length. \$\endgroup\$ Commented Aug 27, 2022 at 2:19

1 Answer 1

2
\$\begingroup\$

If user code is not supposed to access the innards of a reapMapIterator, it's best to make it an incomplete type in the header, and define it only in the implementation. A small change is needed to the interface, so that reapMapIteratorInit() returns an allocated reapMapIterator and reapMapIteratorClose() also frees the memory.

I don't like the translation of errno values to reapRetValue - the former are much more usable, given that strerror() already exists to provide translated user messages.

answered Aug 22, 2022 at 11:45
\$\endgroup\$
1
  • \$\begingroup\$ Perhaps I could return -1*errno instead so that the caller knows when an errno value is being returned. \$\endgroup\$ Commented Aug 22, 2022 at 13:56

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.