Skip to content

Instantly share code, notes, and snippets.

@oriapp
Last active May 4, 2024 19:41
Show Gist options
  • Save oriapp/07e1c0b7cb0412537146092558a89d5f to your computer and use it in GitHub Desktop.
Save oriapp/07e1c0b7cb0412537146092558a89d5f to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <ctype.h>
#include <sys/sysinfo.h>
#include <sys/types.h>
#include <pwd.h>
#define BUFFER_SIZE 512
struct Process
{
int pid;
char *name;
char state;
unsigned long memory;
char *user;
};
/**
* @brief Converts bytes to a human-readable format.
*
* @param bytes Number of bytes.
* @return Human-readable string representing the bytes.
*/
char *bytesToHumanReadable(unsigned long bytes)
{
const char *suffix[] = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
int i = 0;
double dblBytes = bytes;
while (dblBytes >= 1024 && i < (sizeof(suffix) / sizeof(suffix[0])) - 1)
{
dblBytes /= 1024;
i++;
}
static char output[BUFFER_SIZE];
snprintf(output, sizeof(output), "%.2f %s", dblBytes, suffix[i]);
return output;
}
/**
* @brief Converts seconds to a human-readable format.
*
* @param seconds Number of seconds.
* @return Human-readable string representing the time.
*/
char *secondsToHumanReadable(unsigned long seconds)
{
unsigned long days = seconds / (24 * 3600);
seconds = seconds % (24 * 3600);
unsigned long hours = seconds / 3600;
seconds %= 3600;
unsigned long minutes = seconds / 60;
seconds %= 60;
static char output[BUFFER_SIZE];
snprintf(output, sizeof(output), "%lu days, %lu hours, %lu minutes, %lu seconds", days, hours, minutes, seconds);
return output;
}
/**
* @brief Retrieves the username corresponding to a UID.
*
* @param uid UID of the user.
* @return Username corresponding to the UID.
*/
char *getUsername(uid_t uid)
{
struct passwd *pwd = getpwuid(uid);
if (pwd != NULL)
{
return pwd->pw_name;
}
else
{
return "Unknown";
}
}
/**
* @brief Reads process information from the /proc directory.
*
* @param processes Pointer to store the process information.
* @return Number of processes read, or -1 on failure.
*/
int readProcesses(struct Process **processes)
{
DIR *dir;
struct dirent *ent;
FILE *fp;
char path[BUFFER_SIZE];
char line[BUFFER_SIZE];
int count = 0;
int maxProcesses = 1024;
*processes = malloc(maxProcesses * sizeof(struct Process));
if (*processes == NULL)
{
perror("Failed to allocate memory");
return -1;
}
if ((dir = opendir("/proc")) != NULL)
{
while ((ent = readdir(dir)) != NULL)
{
if (isdigit(ent->d_name[0]))
{
sprintf(path, "/proc/%s/stat", ent->d_name);
if ((fp = fopen(path, "r")) != NULL)
{
if (fgets(line, sizeof(line), fp) != NULL)
{
sscanf(line, "%d", &((*processes)[count].pid));
char name[BUFFER_SIZE];
char state;
if (sscanf(line, "%*d (%[^)]) %c", name, &state) == 2)
{
(*processes)[count].name = strdup(name);
(*processes)[count].state = state;
}
else
{
(*processes)[count].name = strdup("(unknown)");
(*processes)[count].state = ' ';
}
count++;
}
fclose(fp);
}
}
}
closedir(dir);
}
else
{
perror("Failed to open /proc directory");
free(*processes);
return -1;
}
return count;
}
/**
* @brief Frees memory allocated for process information.
*
* @param processes Pointer to the array of processes.
* @param numProcesses Number of processes in the array.
*/
void freeProcesses(struct Process *processes, int numProcesses)
{
for (int i = 0; i < numProcesses; i++)
{
free(processes[i].name);
free(processes[i].user);
}
free(processes);
}
/**
* @brief Prints process information.
*
* @param processes Array of processes.
* @param numProcesses Number of processes in the array.
*/
void printProcesses(struct Process *processes, int numProcesses)
{
printf("%-8s %-20s %-8s %-8s %-8s\n", "PID", "NAME", "STATE", "MEMORY", "USER");
printf("----------------------------------------------------------------------------------\n");
for (int i = 0; i < numProcesses; i++)
{
printf("%-8d %-20s %-8c %-8s %-8s\n", processes[i].pid, processes[i].name, processes[i].state, bytesToHumanReadable(processes[i].memory), processes[i].user);
}
}
/**
* @brief Prints system information.
*/
void printSystemInfo()
{
struct sysinfo info;
if (sysinfo(&info) == 0)
{
printf("\nSystem Information:\n");
printf("Uptime: %s\n", secondsToHumanReadable(info.uptime));
printf("Load Average (1min/5min/15min): %.2f %.2f %.2f\n", info.loads[0] / 65536.0, info.loads[1] / 65536.0, info.loads[2] / 65536.0);
printf("Total RAM: %s\n", bytesToHumanReadable(info.totalram * info.mem_unit));
printf("Free RAM: %s\n", bytesToHumanReadable(info.freeram * info.mem_unit));
printf("Total Swap: %s\n", bytesToHumanReadable(info.totalswap * info.mem_unit));
printf("Free Swap: %s\n", bytesToHumanReadable(info.freeswap * info.mem_unit));
}
else
{
perror("Failed to get system information");
}
}
/**
* @brief Calculates memory usage for each process.
*
* @param processes Array of processes.
* @param numProcesses Number of processes in the array.
*/
void calculateProcessMemory(struct Process *processes, int numProcesses)
{
FILE *fp;
char path[BUFFER_SIZE];
char line[BUFFER_SIZE];
for (int i = 0; i < numProcesses; i++)
{
sprintf(path, "/proc/%d/statm", processes[i].pid);
fp = fopen(path, "r");
if (fp != NULL)
{
if (fgets(line, sizeof(line), fp) != NULL)
{
unsigned long size, resident, shared;
sscanf(line, "%lu %lu %lu", &size, &resident, &shared);
// Memory is represented in pages, use resident set size (RSS) as memory usage
processes[i].memory = resident * sysconf(_SC_PAGESIZE);
}
fclose(fp);
}
}
}
int main()
{
struct Process *processes;
int numProcesses;
numProcesses = readProcesses(&processes);
if (numProcesses == -1)
{
return 1;
}
// Calculate memory usage for each process
calculateProcessMemory(processes, numProcesses);
// Retrieve usernames for processes
for (int i = 0; i < numProcesses; i++)
{
uid_t uid;
char path[BUFFER_SIZE];
FILE *fp;
sprintf(path, "/proc/%d/status", processes[i].pid);
fp = fopen(path, "r");
if (fp != NULL)
{
char line[BUFFER_SIZE];
while (fgets(line, sizeof(line), fp))
{
if (strncmp(line, "Uid:", 4) == 0)
{
sscanf(line, "Uid: %d", &uid);
processes[i].user = strdup(getUsername(uid));
break;
}
}
fclose(fp);
}
}
printProcesses(processes, numProcesses);
printSystemInfo();
freeProcesses(processes, numProcesses);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment