×
Samples Blogs Make Payment About Us Reviews 4.9/5 Order Now

Developing a Linux File Descriptor Viewer Tool in C: A Sample Assignment Solution

September 05, 2024
Philip Dudley
Philip Dudley
🇺🇸 United States
C
Philip Dudley is a seasoned software engineer with over 10 years of experience specializing in C programming and systems-level development. He excels in file descriptor management, memory allocation, and performance optimization. Philip has a strong track record of mentoring students and professionals, helping them master complex programming concepts and improve their coding practices for robust software solutions.
Tip of the day
Start with basic image processing concepts like color spaces and transformations. Practice simple OpenCV examples to build confidence before tackling complex tasks.
News
In 2024, the release of the 'AsyncIO Toolkit' has made asynchronous programming in Python more efficient. Similarly, 'React Quantum' introduces quantum computing concepts to JavaScript, expanding learning opportunities for students.
Key Topics
  • Question
    • Bonus Marks (+4)
    • Examples
    • Useful Libraries
    • How to test your code
    • General Remarks
  • Solution

Welcome to our detailed sample solution for the Linux process and file handle viewer assignment, designed to provide C assignment help. In this project, we showcase the development of a tool that explores and displays various tables related to open files and file descriptors in the Linux operating system. By utilizing the /proc virtual file system, this tool offers a comprehensive view of file descriptor management, including process-specific, system-wide, and vnode-related tables. This example not only highlights essential C programming techniques but also demonstrates how to effectively interact with system-level data. Through this sample, you'll gain a deeper understanding of how to tackle complex programming challenges in C, while receiving practical guidance for real-world applications. Discover how this example, along with our expert support, can provide you with the help you need to excel in your programming tasks. For more comprehensive assistance, don't hesitate to reach out for help with programming assignments.

Question

For this assignment we will build a tool to display the tables used by the OS to keep track of open files, assignation of File Descriptors (FD) and processes.

For doing so, we will need to explore several interfaces the kernel offers us.

In particular one of the most relevant ones is the so-called "virtual File Subsystem" /proc part of the Linux OS (see https://man7.org/linux/man-pages/man5/proc.5.html

Links to an external site. and https://tldp.org/LDP/Linux-Filesystem-Hierarchy/html/proc.html

Links to an external site.).

Under /proc we can find (as we already discovered in Assignment #1) a lot of information and resources description from the actual system.

The program, will generate:

  • process FD table
  • System-wide FD Table
  • Vnodes FD table, and
  • a composed view of the previous table

The program will accept multiple commands line arguments:

flagged arguments

  • --per-process, indicates that only the process FD table will be displayed
  • --systemWide, indicates that only the system-wide FD table will be displayed
  • --Vnodes, indicates that the Vnodes FD table will be displayed
  • --composite, indicates that only the composed table will be displayed
  • --threshold=X, where X denotes an integer, indicating that processes which have a number of FD assigned larger than X should be flagged in the output.

For this it will list the PID and number of assigned FDs, e.g. PID (FD)and combination of these, i.e. the program can receive multiple of these flags combined.

Default behaviour: if no argument is passed to the program, then you must implement one of the following approaches -- please state this design decision clearly in your documentation:

  • the program will display the composite table, i.e. same effect as having used the --composite flag
  • the program will display all the tables, i.e. same effect as having used all the flagged arguments --per-process --systemWide --Vnode --composite

positional argument:

  • only one positional argument indicating a particular process id number (PID), if not specified the program will attempt to process all the currently running processes for the user executing the program

Bonus Marks (+4)

  • Add two additional command line arguments: --output_TXT and --output_binary; when these flags are used the program will save the "composite" table in text (ASCII) or binary format into a file named compositeTable.txt or compositeTable.bin respectively.
  • Run a comparison using these two flags, for a few cases, i.e. one PID and larger number (all PIDs for the user), time the run, you can use the time command from the shell and compare the file sizes.

For your comparison to be relevant should have some statistical support with a number of cases between 5 and 10, i.e. average the values in time and sizes over the number of times you run this and include average and standard deviations.

Include this information in your report and elaborate about the observations you notice and conclusions you can draw from them.

Examples

./showFDtables --composite

PIDFDFilenameInode
01187340/dev/null5
11187341socket:[636647]636647
21187342socket:[636647]636647
31187343socket:[641257]641257
41187344anon_inode:[eventpoll]12533
51187345anon_inode:[signalfd12533
61187346anon_inode:inotify12533
71187347/sys/fs/cgroup/user.slice/user-17524891.slice/user@17524891.service12533
81187348anon_inode:[timerfd12533
91187349anon_inode:[eventpoll]12533
1011873410/proc/118734/mountinfo625599
1111873411anon_inode:inotify12533
1211873412anon_inode:inotify12533
1311873413anon_inode:inotify12533
1411873414/proc/swaps4026532089
2911212106socket:[653518]653518
2921212107socket:[652701]652701
2931212109/dev/ptmx87
2941213020/dev/pts/36
2951213021/dev/pts/36
2961213022/dev/pts/36
297121302255/dev/pts/36
2981233060/dev/pts/36
2991233061/cmsfaculty/marcelo/project/desrip.id4598386659
3001233062/dev/pts/36
3011233063/proc1
3021233064/proc/123306/fd671451

./showFDtables 118743

PIDFD
1187430
1187431
1187432
1187433
1187434
1187435
1187436
1187437
1187438
1187439
11874310
11874311
11874312
11874313
11874314
11874315
11874316
11874317
11874318
11874319
11874320
11874321
11874322
11874323
11874324
11874325
PIDFDFilename
1187430/dev/null
1187431socket:[638144]
1187432socket:[638144]
1187433socket:[625628]
1187434pipe:[639499]
1187435pipe:[639499]
1187436/memfd:pulseaudio(deleted)
1187437pipe:[639500]
1187438pipe:[639500]
1187439socket:[642071]
11874310/Users/marcelo/.config/pulse/27303920a74b-device-volumes.tdb
11874311/Users/marcelo/.config/pulse/27303920a74b-stream-volumes.tdb
11874312/Users/marcelo/.config/pulse/27303920a74b-card-database.tdb
11874313anon_inode:inotify
11874314socket:[642069]
11874315socket:[642070]
11874316anon_inode:[eventfd]
11874317anon_inode:[eventfd
11874318anon_inode:[eventfd]
11874319anon_inode:[eventfd]
11874320anon_inode:inotify
11874321anon_inode:[eventfd]
11874322anon_inode:[eventfd]
11874323anon_inode:[eventfd]
11874324anon_inode:[eventfd]
11874325socket:[642073]
FDInode
05
1638144
2638144
3625628
4639499
5639499
66342
7639500
8639500
9642071
104551371476
114551371477
124551371478
1312533
14642069
15642070
1612533
1712533
1812533
1912533
2012533
2112533
2212533
2312533
2412533
25642073
PIDFDFilenameInode
1187430/dev/null5
1187431socket:[638144]638144
1187432socket:[638144]638144
1187433socket:[625628]625628
1187434pipe:[639499]639499
1187435pipe:[639499]639499
1187436/memfd:pulseaudio(deleted)6342
1187437pipe:[639500]639500
1187438pipe:[639500]639500
1187439socket:[642071]642071
11874310/Users/marcelo/.config/pulse/27303920a74b-device-volumes.tdb4551371476
11874311/Users/marcelo/.config/pulse/27303920a74b-stream-volumes.tdb4551371477
11874312/Users/marcelo/.config/pulse/27303920a74b-card-database.tdb4551371478
11874313anon_inode:inotify12533
11874314socket:[642069]642069
11874315socket:[642070]642070
11874316anon_inode:[eventfd]12533
11874317anon_inode:[eventfd12533
11874318anon_inode:[eventfd]12533
11874319anon_inode:[eventfd]12533
11874320anon_inode:inotify12533
11874321anon_inode:[eventfd]12533
11874322anon_inode:[eventfd]12533
11874323anon_inode:[eventfd]12533
11874324anon_inode:[eventfd]12533
11874325socket:[642073]642073

./showFDtables --threshold=20

. . .

((TABLES))

. . .

## Offending processes:

118743 (25), 234678 (456), ...

Useful Libraries

The following libraries may be useful, depending on the approach that you take:

sys/types.h, sys/stat.h, dirent.h, unistd.h, pwd.h, grp.h, errno.h

You will need to look at their documentation and associated functions to investigate their functionalities and possible utilization.

How to test your code

As it happens with tools that monitor live activity testing can be challenging.

You may use some tools from the shell, such as, lsof, ps, etc. to help you monitor and compare the current running processes and corresponding associated FD.

General Remarks

  • You should target this program to work in a Linux type OS, e.g. the workstations from the BV 473 lab. I.e. if your code does not compile and/or run in the lab machines will receive a zero.
  • Write proper modular code, i.e. with functions that have clearly specified goals and tasks using proper arguments and parameters
  • Do not use global variables
  • Include comments and documentation
  • Avoid using any shell command to be run through your C program.

Instead implement the required functionalities using C coding and the references mentioned below.

  • Consider defining your own CDT, depending on your approach this would simplify the overall complexity of your implementation.

Solution

#include "llist.h" /** * Create a new linked list */ llist_t* llist_new() { llist_t *llist; llist = malloc(sizeof(llist_t)); if (llist == NULL) { printf("Error: llist_new : out of memory\n"); exit(1); } llist->head = NULL; llist->tail = NULL; llist->size = 0; return llist; } /** * Insert a new node in the linked list */ void llist_insert(llist_t *llist, void *data) { node_t *node; node = malloc(sizeof(node_t)); if (node == NULL) { printf("Error: llist_insert : out of memory\n"); exit(1); } node->data = data; node->next = NULL; /* if empty list */ if (llist->head == NULL) llist->head = node; else llist->tail->next = node; llist->tail = node; llist->size++; } /** * Delete linked list and free all memory used for the list. Uses the given function * to free the memory used by the data. */ void llist_delete(llist_t *llist, void (*delete_data_fun)(void *)) { node_t *node, *tmp; node = llist->head; while (node) { tmp = node; node = node->next; (*delete_data_fun)(tmp->data); free(tmp); } free(llist); }

Linux-File

Linux-File-1

Linux-File-2

Linux-File-3

Linux-File-4

#include <stdlib.h> #include <string.h> #include <unistd.h> #include <dirent.h> #include <sys/stat.h> #include <sys/types.h> #include "llist.h" #define PROC_FD_LEN 64 #define PATH_LEN 512 /** * Definition of the file info structure */ typedef struct { int fd; ino_t inode; char filename[PATH_LEN]; }file_info_t; /** * Definition of the data saved in the pid linked list: * a pid and a linked list of fd's */ typedef struct { int pid; llist_t *fds; }pid_fds_t; /** * Determines if a given string corresponds to a PID (number) * Returns 1 if the string is a PID (number), 0 if not */ int is_pid(char *str) { char *p; p = str; while (*p != 0) { if (*p < '0' || *p >'9') return 0; p++; } return 1; } /** * Get the flags given as command line arguments * per_proc corresponds to flag --per-process * syswide corresponds to flag --systemWide * vnodes corresponds to flag --Vnodes * composite corresponds to flag --composite * threshold corresponds to the value X of flag --threshold=X * pid corresponds to the positional argument */ void get_command_line_flags(int argc, char **argv, int *per_proc, int *syswide, int *vnodes, int *composite, int *threshold, int *pid) { int i, value, pos_arg; /* initialize flag values */ *per_proc = 0; *syswide = 0; *vnodes = 0; *composite = 0; *threshold = 0; *pid = 0; pos_arg = -1; for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "--per-process")) *per_proc = 1; else if (!strcmp(argv[i], "--systemWide")) *syswide = 1; else if (!strcmp(argv[i], "--Vnodes")) *vnodes = 1; else if (!strcmp(argv[i], "--composite")) *composite = 1; else if (!strcmp(argv[i], "--threshold") || !strcmp(argv[i], "--threshold=")) { printf("Error: missing threshold value.\n"); exit(1); } else if (!strncmp(argv[i], "--threshold=", 12)) { value = atoi(&argv[i][12]); if (value <= 0) { printf("Error: invalid threshold value.\n"); exit(1); } *threshold = value; } else if (argv[i][0] == '-') { printf("Error: invalid option '%s'.\n", argv[i]); exit(1); } else { if (pos_arg == -1) pos_arg = i; else { printf("Error: too many positional arguments.\n"); exit(1); } } } /* if there is a positional argument, get the pid */ if (pos_arg != -1) { if (is_pid(argv[pos_arg])) { value = atoi(argv[pos_arg]); if (value <= 0) { printf("Error: invalid PID: %s.\n", argv[pos_arg]); exit(1); } *pid = value; } else { printf("Error: invalid PID: %s.\n", argv[pos_arg]); exit(1); } } /* if no flag was given, set all flags */ if (!(*per_proc) && !(*syswide) && !(*vnodes) && !(*composite)) { *per_proc = 1; *syswide = 1; *vnodes = 1; *composite = 1; } } /** * Delete the data for a PID list node */ void delete_pid_data(void *data) { pid_fds_t *pfdata; pfdata = (pid_fds_t *) data; /* free fd list */ llist_delete(pfdata->fds, free); /* free data */ free(pfdata); } /** * Get all the fds that belong to a given PID and returns a linked list. */ llist_t *get_pid_fds(int pid) { DIR *fd_dir; char path[PROC_FD_LEN]; char filename[PATH_LEN]; struct dirent *entry; struct stat entry_stat; int fd; ssize_t link_size; llist_t *llist; file_info_t *data; /* build path to file descriptors */ sprintf(path, "/proc/%d/fd", pid); /* read all entries in /proc/[pid]/fd */ fd_dir = opendir(path); /* if pid doesn't have files, return */ if (fd_dir == NULL) return NULL; /* create linked list */ llist = llist_new(); /* traverse directory entries */ while ((entry = readdir(fd_dir)) != NULL) { /* skip dot filenames */ if (entry->d_name[0] != '.') { /* convert file descriptor to number */ fd = atoi(entry->d_name); /* build path for given entry */ sprintf(filename, "%s/%s", path, entry->d_name); /* get file info */ if (stat(filename, &entry_stat) != -1) { data = malloc(sizeof(file_info_t)); /* get link name */ link_size = readlink(filename, data->filename, PATH_LEN); /* insert ending zero */ if (link_size == PATH_LEN) link_size--; data->filename[link_size] = 0; data->fd = fd; data->inode = entry_stat.st_ino; /* insert fd to list */ llist_insert(llist, data); } } } closedir(fd_dir); return llist; } /** * Get a list with the information of all the fd's for the current user. * If pid != 0, only retrieves the information for the given PID. */ llist_t *get_fd_list(int pid) { DIR *proc_dir; char filename[PATH_LEN]; struct dirent *entry; struct stat entry_stat; uid_t uid; pid_t fpid; llist_t *llist; llist_t *llist_fd; pid_fds_t *data; /* initialize linked list */ llist = llist_new(); /* get user id */ uid = getuid(); /* read all entries in /proc */ proc_dir = opendir("/proc"); if (proc_dir == NULL) { perror("opendir"); exit(1); } /* traverse directory entries */ while ((entry = readdir(proc_dir)) != NULL) { /* skip dot filenames */ if (entry->d_name[0] != '.') { /* build path for given entry */ sprintf(filename, "/proc/%s", entry->d_name); /* get file info */ if (stat(filename, &entry_stat) != -1) { /* if is a pid entry and the file belongs to the user */ if (is_pid(entry->d_name) && entry_stat.st_uid == uid) { fpid = atoi(entry->d_name); /* if pid was given, only add files with given pid */ if (pid == 0 || pid == fpid) { llist_fd = get_pid_fds(fpid); if (llist_fd != NULL) { if (llist_fd->size != 0) { data = malloc(sizeof(pid_fds_t)); data->pid = fpid; data->fds = llist_fd; llist_insert(llist, data); } else llist_delete(llist_fd, free); } } } } } } closedir(proc_dir); return llist; } /** * Print per proc fd information from linked list */ void print_per_proc(llist_t *llist) { node_t *node_pid, *node_fd; pid_fds_t *pfdata; file_info_t *fdata; printf("\n"); printf(" PID FD \n"); printf("=============\n"); node_pid = llist->head; while (node_pid) { pfdata = (pid_fds_t *) node_pid->data; node_fd = pfdata->fds->head; while (node_fd) { fdata = (file_info_t *) node_fd->data; printf(" %-7d %d\n", pfdata->pid, fdata->fd); node_fd = node_fd->next; } node_pid = node_pid->next; } } /** * Print system wide fd information from linked list */ void print_syswide(llist_t *llist) { node_t *node_pid, *node_fd; pid_fds_t *pfdata; file_info_t *fdata; printf("\n"); printf(" PID FD Filename\n"); printf("========================================\n"); node_pid = llist->head; while (node_pid) { pfdata = (pid_fds_t *) node_pid->data; node_fd = pfdata->fds->head; while (node_fd) { fdata = (file_info_t *) node_fd->data; printf(" %-7d %-7d %s\n", pfdata->pid, fdata->fd, fdata->filename); node_fd = node_fd->next; } node_pid = node_pid->next; } } /** * Print vnode fd information from linked list */ void print_vnodes(llist_t *llist) { node_t *node_pid, *node_fd; pid_fds_t *pfdata; file_info_t *fdata; printf("\n"); printf(" FD Inode \n"); printf("================\n"); node_pid = llist->head; while (node_pid) { pfdata = (pid_fds_t *) node_pid->data; node_fd = pfdata->fds->head; while (node_fd) { fdata = (file_info_t *) node_fd->data; printf(" %-7d %lu\n", fdata->fd, fdata->inode); node_fd = node_fd->next; } node_pid = node_pid->next; } } /** * Print composite fd information from linked list */ void print_composite(llist_t *llist) { node_t *node_pid, *node_fd; pid_fds_t *pfdata; file_info_t *fdata; printf("\n"); printf(" PID FD Filename Inode \n"); printf("=========================================\n"); node_pid = llist->head; while (node_pid) { pfdata = (pid_fds_t *) node_pid->data; node_fd = pfdata->fds->head; while (node_fd) { fdata = (file_info_t *) node_fd->data; printf(" %-7d %-7d %-16s %lu\n", pfdata->pid, fdata->fd, fdata->filename, fdata->inode); node_fd = node_fd->next; } node_pid = node_pid->next; } } /** * Print pid threshold information from linked list */ void print_threshold(llist_t *llist, int threshold) { node_t *node_pid; pid_fds_t *pfdata; int first = 1; printf("\n"); printf("## Offending processes:\n"); node_pid = llist->head; while (node_pid) { pfdata = (pid_fds_t *) node_pid->data; if (pfdata->fds->size > threshold) { if (!first) printf(", "); printf("%d (%d)", pfdata->pid, pfdata->fds->size); first = 0; } node_pid = node_pid->next; } if (first) printf("None"); printf("\n"); } int main(int argc, char **argv) { int per_proc, syswide, vnodes, composite, threshold, pid; llist_t *llist; get_command_line_flags(argc, argv, &per_proc, &syswide, &vnodes, &composite, &threshold, &pid); /* get info for all fd's */ llist = get_fd_list(pid); if (pid != 0 && llist->size == 0) { printf("Error: PID %d doesn't exist\n", pid); llist_delete(llist, delete_pid_data); return 1; } /* print info depending on the selected flags */ if (per_proc) print_per_proc(llist); if (syswide) print_syswide(llist); if (vnodes) print_vnodes(llist); if (composite) print_composite(llist); if (threshold) print_threshold(llist, threshold); /* delete memory used by linked list */ llist_delete(llist, delete_pid_data); return 0; }

Similar Samples

We offer expertly crafted programming assignments tailored to student needs. Explore our diverse sample questions, showcasing our quality work. Rate our samples to see how we can assist you with your assignments. Trust our service for accurate, timely solutions that enhance your academic success.