- 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
PID | FD | Filename | Inode | |
---|---|---|---|---|
0 | 118734 | 0 | /dev/null | 5 |
1 | 118734 | 1 | socket:[636647] | 636647 |
2 | 118734 | 2 | socket:[636647] | 636647 |
3 | 118734 | 3 | socket:[641257] | 641257 |
4 | 118734 | 4 | anon_inode:[eventpoll] | 12533 |
5 | 118734 | 5 | anon_inode:[signalfd | 12533 |
6 | 118734 | 6 | anon_inode:inotify | 12533 |
7 | 118734 | 7 | /sys/fs/cgroup/user.slice/user-17524891.slice/user@17524891.service | 12533 |
8 | 118734 | 8 | anon_inode:[timerfd | 12533 |
9 | 118734 | 9 | anon_inode:[eventpoll] | 12533 |
10 | 118734 | 10 | /proc/118734/mountinfo | 625599 |
11 | 118734 | 11 | anon_inode:inotify | 12533 |
12 | 118734 | 12 | anon_inode:inotify | 12533 |
13 | 118734 | 13 | anon_inode:inotify | 12533 |
14 | 118734 | 14 | /proc/swaps | 4026532089 |
291 | 121210 | 6 | socket:[653518] | 653518 |
292 | 121210 | 7 | socket:[652701] | 652701 |
293 | 121210 | 9 | /dev/ptmx | 87 |
294 | 121302 | 0 | /dev/pts/3 | 6 |
295 | 121302 | 1 | /dev/pts/3 | 6 |
296 | 121302 | 2 | /dev/pts/3 | 6 |
297 | 121302 | 255 | /dev/pts/3 | 6 |
298 | 123306 | 0 | /dev/pts/3 | 6 |
299 | 123306 | 1 | /cmsfaculty/marcelo/project/desrip.id | 4598386659 |
300 | 123306 | 2 | /dev/pts/3 | 6 |
301 | 123306 | 3 | /proc | 1 |
302 | 123306 | 4 | /proc/123306/fd | 671451 |
./showFDtables 118743
PID | FD |
---|---|
118743 | 0 |
118743 | 1 |
118743 | 2 |
118743 | 3 |
118743 | 4 |
118743 | 5 |
118743 | 6 |
118743 | 7 |
118743 | 8 |
118743 | 9 |
118743 | 10 |
118743 | 11 |
118743 | 12 |
118743 | 13 |
118743 | 14 |
118743 | 15 |
118743 | 16 |
118743 | 17 |
118743 | 18 |
118743 | 19 |
118743 | 20 |
118743 | 21 |
118743 | 22 |
118743 | 23 |
118743 | 24 |
118743 | 25 |
PID | FD | Filename |
---|---|---|
118743 | 0 | /dev/null |
118743 | 1 | socket:[638144] |
118743 | 2 | socket:[638144] |
118743 | 3 | socket:[625628] |
118743 | 4 | pipe:[639499] |
118743 | 5 | pipe:[639499] |
118743 | 6 | /memfd:pulseaudio(deleted) |
118743 | 7 | pipe:[639500] |
118743 | 8 | pipe:[639500] |
118743 | 9 | socket:[642071] |
118743 | 10 | /Users/marcelo/.config/pulse/27303920a74b-device-volumes.tdb |
118743 | 11 | /Users/marcelo/.config/pulse/27303920a74b-stream-volumes.tdb |
118743 | 12 | /Users/marcelo/.config/pulse/27303920a74b-card-database.tdb |
118743 | 13 | anon_inode:inotify |
118743 | 14 | socket:[642069] |
118743 | 15 | socket:[642070] |
118743 | 16 | anon_inode:[eventfd] |
118743 | 17 | anon_inode:[eventfd |
118743 | 18 | anon_inode:[eventfd] |
118743 | 19 | anon_inode:[eventfd] |
118743 | 20 | anon_inode:inotify |
118743 | 21 | anon_inode:[eventfd] |
118743 | 22 | anon_inode:[eventfd] |
118743 | 23 | anon_inode:[eventfd] |
118743 | 24 | anon_inode:[eventfd] |
118743 | 25 | socket:[642073] |
FD | Inode |
---|---|
0 | 5 |
1 | 638144 |
2 | 638144 |
3 | 625628 |
4 | 639499 |
5 | 639499 |
6 | 6342 |
7 | 639500 |
8 | 639500 |
9 | 642071 |
10 | 4551371476 |
11 | 4551371477 |
12 | 4551371478 |
13 | 12533 |
14 | 642069 |
15 | 642070 |
16 | 12533 |
17 | 12533 |
18 | 12533 |
19 | 12533 |
20 | 12533 |
21 | 12533 |
22 | 12533 |
23 | 12533 |
24 | 12533 |
25 | 642073 |
PID | FD | Filename | Inode |
---|---|---|---|
118743 | 0 | /dev/null | 5 |
118743 | 1 | socket:[638144] | 638144 |
118743 | 2 | socket:[638144] | 638144 |
118743 | 3 | socket:[625628] | 625628 |
118743 | 4 | pipe:[639499] | 639499 |
118743 | 5 | pipe:[639499] | 639499 |
118743 | 6 | /memfd:pulseaudio(deleted) | 6342 |
118743 | 7 | pipe:[639500] | 639500 |
118743 | 8 | pipe:[639500] | 639500 |
118743 | 9 | socket:[642071] | 642071 |
118743 | 10 | /Users/marcelo/.config/pulse/27303920a74b-device-volumes.tdb | 4551371476 |
118743 | 11 | /Users/marcelo/.config/pulse/27303920a74b-stream-volumes.tdb | 4551371477 |
118743 | 12 | /Users/marcelo/.config/pulse/27303920a74b-card-database.tdb | 4551371478 |
118743 | 13 | anon_inode:inotify | 12533 |
118743 | 14 | socket:[642069] | 642069 |
118743 | 15 | socket:[642070] | 642070 |
118743 | 16 | anon_inode:[eventfd] | 12533 |
118743 | 17 | anon_inode:[eventfd | 12533 |
118743 | 18 | anon_inode:[eventfd] | 12533 |
118743 | 19 | anon_inode:[eventfd] | 12533 |
118743 | 20 | anon_inode:inotify | 12533 |
118743 | 21 | anon_inode:[eventfd] | 12533 |
118743 | 22 | anon_inode:[eventfd] | 12533 |
118743 | 23 | anon_inode:[eventfd] | 12533 |
118743 | 24 | anon_inode:[eventfd] | 12533 |
118743 | 25 | socket:[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);
}
#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.
C++
C
Database
Embedded System
Python
C++
Data Structures and Algorithms
Python
C
C
C
C
C
C
C
C
C
C
C
C