#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct { char *label; char *value; } OutputEntry; OutputEntry *output_entries = NULL; int output_count = 0; int max_label_length = 0; void add_output_entry(char *label, char *value) { OutputEntry *entry = malloc(sizeof(OutputEntry)); if (entry == NULL) { perror("Error allocating memory for output entry"); return; } entry->label = malloc(strlen(label) + 1); if (entry->label == NULL) { perror("Error allocating memory for output entry label"); return; } strcpy(entry->label, label); entry->value = malloc(strlen(value) + 1); if (entry->value == NULL) { perror("Error allocating memory for output entry value"); return; } strcpy(entry->value, value); output_entries = realloc(output_entries, sizeof(OutputEntry) * output_count); if (output_entries == NULL) { perror("Error allocating memory for output entries"); return; } output_entries = realloc(output_entries, sizeof(OutputEntry) * (output_count + 1)); if (output_entries == NULL) { perror("Error allocating memory for output entries"); return; } output_entries[output_count] = *entry; output_count++; if (strlen(label) > max_label_length) { max_label_length = strlen(label); } } typedef struct { long long mem_total; long long mem_available; long long swap_total; long long swap_free; } BasicMemInfo; // returns NULL on error int get_basic_memory_info(BasicMemInfo *meminfo) { FILE *fp; char *line = NULL; size_t len = 0; ssize_t read; char key[64]; uint64_t value; char unit[16]; // get available ram and swap info via /proc/meminfo fp = fopen("/proc/meminfo", "r"); if (fp == NULL) { perror("Error opening /proc/meminfo"); return 1; } uint8_t count = 0; while ((read = getline(&line, &len, fp)) != -1) { if (sscanf(line, "%63[^:]: %ld %15s", key, &value, unit) == 3) { char *p = key + strlen(key) - 1; while (p >= key && *p == ' ') { *p = '\0'; p--; } // convert all kilobytes to bytes if (strcmp(key, "MemAvailable") == 0) { meminfo->mem_available = value * 1024; count++; } else if (strcmp(key, "MemTotal") == 0) { meminfo->mem_total = value * 1024; count++; } else if (strcmp(key, "SwapTotal") == 0) { meminfo->swap_total = value * 1024; count++; } else if (strcmp(key, "SwapFree") == 0) { meminfo->swap_free = value * 1024; count++; } // Check if we found all values from /proc/meminfo if (count == 4) { break; } } } if (line) free(line); fclose(fp); if (count != 4) { printf("Error parsing /proc/meminfo\n"); return 1; } return 0; } int count_processes(void) { int count = 0; DIR *dir = opendir("/proc"); if (dir == NULL) { perror("Error opening /proc"); return -1; } struct dirent *ent; while ((ent = readdir(dir)) != NULL) { if (ent->d_type == DT_DIR) { bool is_pid = true; for (int i = 0; ent->d_name[i] != '\0'; i++) { // check if the name is a number (PID) if (!isdigit(ent->d_name[i])) { is_pid = false; break; } } count++; } } closedir(dir); return count; } int main(void) { // TODO: should we cache this like the shell script does? double loadavg[1] = {0}; getloadavg(loadavg, 1); time_t curtime; time(&curtime); struct statfs sf; if (statfs("/", &sf) != 0) { perror("Error calling statfs"); return 1; } uint64_t total_bytes = (uint64_t)sf.f_blocks * sf.f_frsize; uint64_t free_bytes = (uint64_t)sf.f_bfree * sf.f_frsize; uint64_t available_bytes = (uint64_t)sf.f_bavail * sf.f_frsize; // For non-root users uint64_t used_bytes = total_bytes - free_bytes; float formatted_total_space = total_bytes; char *unit = "B"; if (formatted_total_space > (uint64_t)1 << 40) { formatted_total_space /= (uint64_t)1 << 40; unit = "TiB"; } else if (formatted_total_space > (uint64_t)1 << 30) { formatted_total_space /= (uint64_t)1 << 30; unit = "GiB"; } else if (formatted_total_space > (uint64_t)1 << 20) { formatted_total_space /= (uint64_t)1 << 20; unit = "MiB"; } else if (formatted_total_space > (uint64_t)1 << 10) { formatted_total_space /= (uint64_t)1 << 10; unit = "KiB"; } BasicMemInfo meminfo = {0}; if (get_basic_memory_info(&meminfo) != 0) { return 1; } int processes = count_processes(); // count logged in users int users = 0; struct utmp *ut; setutent(); while ((ut = getutent()) != NULL) { if (ut->ut_type == USER_PROCESS) { users++; } } endutent(); printf("\n System information as of %s\n", ctime(&curtime)); char value_buffer[128]; char label_buffer[128]; snprintf(value_buffer, 128, "%.2f", loadavg[0]); add_output_entry("System load", value_buffer); snprintf(value_buffer, 128, "%.2f%% of %.2f%s", (float)((float)used_bytes * 100.0 / (float)total_bytes), formatted_total_space, unit); add_output_entry("Usage of /", value_buffer); snprintf(value_buffer, 128, "%d%%", (uint8_t)(((float)(meminfo.mem_total - meminfo.mem_available) / (float)meminfo.mem_total) * 100.0)); add_output_entry("Memory usage", value_buffer); snprintf(value_buffer, 128, "%d%%", (uint8_t)(((float)(meminfo.swap_total - meminfo.swap_free) / (float)meminfo.swap_total) * 100.0)); add_output_entry("Swap usage", value_buffer); snprintf(value_buffer, 128, "%d", processes); add_output_entry("Processes", value_buffer); snprintf(value_buffer, 128, "%d", users); add_output_entry("Users", value_buffer); struct ifaddrs *ifaddr, *ifa; int family, s; char host[128]; getifaddrs(&ifaddr); for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL) { continue; } if (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) { family = ifa->ifa_addr->sa_family; if (family == AF_INET || family == AF_INET6) { s = getnameinfo(ifa->ifa_addr, (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6), host, 128, NULL, 0, NI_NUMERICHOST); if (s != 0) { printf("Error calling getnameinfo: %s\n", gai_strerror(s)); continue; } if (strcmp(ifa->ifa_name, "lo") == 0) { continue; } if (strncmp(ifa->ifa_name, "docker", 6) == 0 || strncmp(ifa->ifa_name, "br-", 3) == 0 || // Docker bridge interfaces strncmp(ifa->ifa_name, "veth", 4) == 0 || // Docker/VirtualBox interfaces strncmp(ifa->ifa_name, "virbr", 5) == 0 || strcmp(ifa->ifa_name, "lo") == 0) { // libvirt bridge interfaces continue; // Skip container interfaces } snprintf(label_buffer, 128, "%s address for %s", (family == AF_INET) ? "IPv4" : "IPv6", ifa->ifa_name); snprintf(value_buffer, 128, "%s", host); add_output_entry(label_buffer, value_buffer); } } } for (int i = 0; i < output_count; i++) { printf(" %s:%*s%s\n", output_entries[i].label, (int)((max_label_length + 1) - strlen(output_entries[i].label)), "", output_entries[i].value); } printf("\n"); return 0; }