diff --git a/.vscode/settings.json b/.vscode/settings.json index 9deab0a..7d9d24b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "files.associations": { - "stdlib.h": "c" + "stdlib.h": "c", + "process.h": "c" } } \ No newline at end of file diff --git a/src/lxcstat.c b/src/lxcstat.c new file mode 100644 index 0000000..1e54740 --- /dev/null +++ b/src/lxcstat.c @@ -0,0 +1,75 @@ +#include +#include +#include + +#include "lxcstat.h" +#include "parsing.h" + +int parseLxcConfigFile(lxcinfo *lxc, char *filedata) { + lxc->cpucount = 0; + lxc->memlimit = 0; + + char *temp = filedata; + lxc->cpucount = findAndParseField(&temp, "cores"); + temp = filedata; + + lxc->memlimit = findAndParseField(&temp, "memory") * 1024 * 1024; + + char *ptr = strstr(filedata, "hostname:"); + ptr += strlen("hostname:"); + while (*ptr == ' ') ptr++; + strtok(ptr, "\n\t "); + strcpy(lxc->hostname, ptr); +} + +int readLxcConfig(lxcinfo *lxc, int lxcid) { + char *fname = malloc(1024); + char *buffer = malloc(4096); + + sprintf(fname, "/etc/pve/lxc/%d.conf", lxcid); + FILE *file = fopen(fname, "rb"); + if (file == NULL) { + printf("Failed to open <%s>: %d\n", fname, errno); + return -1; + } + fread(buffer, 1, 4096, file); + fclose(file); + + parseLxcConfigFile(lxc, buffer); + free(buffer); + free(fname); + return 0; +} + +lxcinfo *getLXCInfo(lxcinfo **lxcs, int lxcid) { + lxcinfo *lxc = *lxcs; + while (lxc != NULL) { + if (lxc->lxcid == lxcid) return lxc; + lxc = lxc->next; + } + + lxc = malloc(sizeof(lxcinfo)); + lxc->lxcid = lxcid; + + if (readLxcConfig(lxc, lxcid)) { + // failed to read LXC + free(lxc); + return NULL; + } + + if (*lxcs == NULL) { + // head was NULL - first LXC + *lxcs = lxc; + lxc->head = lxc; + lxc->tail = lxc; + lxc->next = NULL; + } else { + // other LXCs exist - chain to tail + (*lxcs)->tail->next = lxc; + (*lxcs)->tail = lxc; + lxc->head = *lxcs; + lxc->next = NULL; + } + + return lxc; +} diff --git a/src/lxcstat.h b/src/lxcstat.h new file mode 100644 index 0000000..f96490b --- /dev/null +++ b/src/lxcstat.h @@ -0,0 +1,16 @@ +#ifndef LXCSTAT_H +#define LXCSTAT_H + +typedef struct _lxc_info { + struct _lxc_info *next; + struct _lxc_info *head; + struct _lxc_info *tail; // only valid for head lxcinfo + int lxcid; + int cpucount; + int memlimit; + char hostname[128]; +} lxcinfo; + +lxcinfo *getLXCInfo(lxcinfo **lxcs, int lxcid); + +#endif \ No newline at end of file diff --git a/src/parsing.h b/src/parsing.h new file mode 100644 index 0000000..baeca34 --- /dev/null +++ b/src/parsing.h @@ -0,0 +1,44 @@ +#ifndef PARSING_H +#define PARSING_H + +#include +#include +#include + +static inline uint64_t fast_str2ull(char **str) { + uint64_t result = 0; + int maxlen = 20; // length of maximum value of 18446744073709551615 + + while (maxlen-- && **str >= '0' && **str <= '9') { + result *= 10; + result += **str - '0'; + (*str)++; + } + + return result; +} + +static inline int64_t fast_str2ll(char **str) { + bool neg = false; + + if (**str == '-') { + neg = true; + (*str)++; + } + + uint64_t res = fast_str2ull(str); + int64_t result = (int64_t)res; + + return neg ? -result : result; +} + +static inline uint64_t findAndParseField(char **filedata, char *fld) { + char *field = strstr(*filedata, fld); + if (!field) return -1; + while (*field < '0' || *field > '9') field++; + *filedata = field; + uint64_t val = fast_str2ull(filedata); + return val; +} + +#endif \ No newline at end of file diff --git a/src/process.h b/src/process.h index d640bc0..2e0058a 100644 --- a/src/process.h +++ b/src/process.h @@ -8,17 +8,16 @@ typedef struct st_process { struct st_process *next; struct st_process *prev; uint32_t flags; + char cpuset[128]; + int tgid; // attributes identifying thread int lxc; int pid; int ppid; - int tgid; int level; char label[128]; - char cpuset[128]; - char lxcname[128]; - int iskernel; // statistics - each outputs 1 line per process + int iskernel; uint64_t cpuTime; uint64_t childCpuTime; uint64_t nThreads; diff --git a/src/proxmon.c b/src/proxmon.c deleted file mode 100644 index 36017ad..0000000 --- a/src/proxmon.c +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include -#include - -int main3(int argc, char *argv[]) { - DIR *procdir = opendir("/proc"); - struct dirent *pDirent; - - while ((pDirent = readdir(procdir)) != NULL) { - printf("[%s]\n", pDirent->d_name); - } -} diff --git a/src/server.c b/src/server.c index e288247..6d0b393 100644 --- a/src/server.c +++ b/src/server.c @@ -1,362 +1,333 @@ +#include +#include +#include +#include #include #include -#include #include -#include -#include #include #include -#include -#include + #include "process.h" +#include "lxcstat.h" +#include "parsing.h" #ifndef PF_KTHREAD -# define PF_KTHREAD 0x00200000 +#define PF_KTHREAD 0x00200000 #endif -static inline uint64_t fast_str2ull(char** str) { - uint64_t result = 0; - int maxlen = 20; // length of maximum value of 18446744073709551615 - - while (maxlen-- && **str >= '0' && **str <= '9') { - result *= 10; - result += **str - '0'; - (*str)++; - } - - return result; -} - -static inline int64_t fast_str2ll(char** str) { - bool neg = false; - - if (**str == '-') { - neg = true; - (*str)++; - } - - uint64_t res = fast_str2ull(str); - int64_t result = (int64_t) res; - - return neg ? -result : result; -} - -#define skipRange(inclusive,exclusive) \ - for (int i##inclusive=0; i##inclusivepid = fast_str2ull(&location); - location += 2; - - // (2) comm - %s - char *end = strrchr(location, ')'); - size_t length = end - location; - memcpy(proc->label, location, length); - proc->label[length] = 0; - location = end + 2; + char *location = filedata; + // (1) pid - %d + proc->pid = fast_str2ull(&location); + location += 2; - // (3) State - %c - location += 2; + // (2) comm - %s + char *end = strrchr(location, ')'); + size_t length = end - location; + memcpy(proc->label, location, length); + proc->label[length] = 0; + location = end + 2; - // (4) ppid - %d - proc->ppid = fast_str2ull(&location); - location += 1; + // (3) State - %c + location += 2; - // skip [5 - 9) - skipRange(5, 9); + // (4) ppid - %d + proc->ppid = fast_str2ull(&location); + location += 1; - // (9) flags - %u - proc->flags = fast_str2ull(&location); - location += 1; + // skip [5 - 9) + skipRange(5, 9); - proc->iskernel = (proc->flags & PF_KTHREAD) ? true : false; + // (9) flags - %u + proc->flags = fast_str2ull(&location); + location += 1; - // skip [10 - 14) - skipRange(10, 14); + proc->iskernel = (proc->flags & PF_KTHREAD) ? true : false; - // (14) utime - %lu - proc->cpuTime = fast_str2ull(&location); - location += 1; + // skip [10 - 14) + skipRange(10, 14); - // (15) stime - %lu - proc->cpuTime += fast_str2ull(&location); - location += 1; + // (14) utime - %lu + proc->cpuTime = fast_str2ull(&location); + location += 1; - // (16) cutime - %lu - proc->childCpuTime = fast_str2ull(&location); - location += 1; + // (15) stime - %lu + proc->cpuTime += fast_str2ull(&location); + location += 1; - // (17) cstime - %lu - proc->childCpuTime += fast_str2ull(&location); - location += 1; + // (16) cutime - %lu + proc->childCpuTime = fast_str2ull(&location); + location += 1; - // skip 18-19 - skipRange(18, 20); + // (17) cstime - %lu + proc->childCpuTime += fast_str2ull(&location); + location += 1; - // (20) num_threads - %ld - proc->nThreads = fast_str2ull(&location); - location += 1; + // skip 18-19 + skipRange(18, 20); - return 0; -} + // (20) num_threads - %ld + proc->nThreads = fast_str2ull(&location); + location += 1; -static inline uint64_t findAndParseField(char **filedata, char *fld) { - char *field = strstr(*filedata, fld); - if (!field) return -1; - while (*field < '0' || *field > '9') field++; - *filedata = field; - uint64_t val = fast_str2ull(filedata); - return val; + return 0; } int parseStatusFile(Process *proc, char *filedata) { - proc->tgid = findAndParseField(&filedata, "Tgid"); - proc->pid = findAndParseField(&filedata, "Pid"); + proc->tgid = findAndParseField(&filedata, "Tgid"); + proc->pid = findAndParseField(&filedata, "Pid"); - if (proc->pid != proc->tgid) return -1; // ignore child threads, as their stats are the same as the parent + if (proc->pid != proc->tgid) + return -1; // ignore child threads, as their stats are the same as the + // parent - proc->resident = 1024 * findAndParseField(&filedata, "VmRSS"); - proc->swap = 1024 * findAndParseField(&filedata, "VmSwap"); - return 0; + proc->resident = 1024 * findAndParseField(&filedata, "VmRSS"); + proc->swap = 1024 * findAndParseField(&filedata, "VmSwap"); + return 0; } int parseIOFile(Process *proc, char *filedata) { - proc->read = findAndParseField(&filedata, "read_bytes"); - proc->written = findAndParseField(&filedata, "write_bytes"); - return 0; + proc->read = findAndParseField(&filedata, "read_bytes"); + proc->written = findAndParseField(&filedata, "write_bytes"); + return 0; } char *readProcesses(char *procdir) { - int len; - struct dirent *pDirent; - DIR *pDir; + int len; + struct dirent *pDirent; + DIR *pDir; - // Ensure we can open directory. + // Ensure we can open directory. - pDir = opendir(procdir); - if (pDir == NULL) { - printf("Cannot open directory '%s'\n", procdir); - return NULL; + pDir = opendir(procdir); + if (pDir == NULL) { + printf("Cannot open directory '%s'\n", procdir); + return NULL; + } + + // Process each entry. + + char *buffer = malloc(4096); + char *fname = malloc(1024); + char first; + FILE *file; + + Process *cur = malloc(sizeof(Process)); + cur->prev = NULL; + cur->next = NULL; + Process *head = cur; + lxcinfo *lxcs = NULL; + + while ((pDirent = readdir(pDir)) != NULL) { + first = pDirent->d_name[0]; + if (first < '0' || first > '9') continue; + sprintf(fname, "/proc/%s/status", pDirent->d_name); + file = fopen(fname, "rb"); + if (file == NULL) continue; + fread(buffer, 1, 4096, file); + fclose(file); + + if (parseStatusFile(cur, buffer)) continue; + + // truncate 'status' to 'stat' + len = strlen(fname); + fname[len - 2] = 0; + file = fopen(fname, "rb"); + if (file == NULL) continue; + fread(buffer, 1, 4096, file); + fclose(file); + + if (parseStatFile(cur, buffer)) continue; + + sprintf(fname, "/proc/%s/io", pDirent->d_name); + file = fopen(fname, "rb"); + if (file == NULL) continue; + fread(buffer, 1, 4096, file); + fclose(file); + + if (parseIOFile(cur, buffer)) continue; + + sprintf(fname, "/proc/%s/cpuset", pDirent->d_name); + file = fopen(fname, "rb"); + if (file == NULL) goto nextProcess; + fread(cur->cpuset, 1, sizeof(cur->cpuset), file); + fclose(file); + + strtok(cur->cpuset, "\n\t "); + char *lxcTag = "/lxc/"; + if (memcmp(cur->cpuset, lxcTag, strlen(lxcTag)) == 0) { + // Resides in LXC -- read file and tag + sscanf(cur->cpuset, "/lxc/%d/ns", &cur->lxc); + if (getLXCInfo(&lxcs, cur->lxc) == NULL) + printf("Failed to read LXC config <%d>\n", cur->lxc); + + } else { + cur->lxc = -1; } - // Process each entry. + nextProcess: + cur->next = malloc(sizeof(Process)); + cur->next->prev = cur; + cur = cur->next; + } - char *buffer = malloc(4096); - char *fname = malloc(1024); - char first; - FILE *file; + // clean up unused last node + cur = cur->prev; + free(cur->next); + cur->next = NULL; - Process *cur = malloc(sizeof(Process)); - cur->prev = NULL; - cur->next = NULL; - Process *head = cur; + int clocks = sysconf(_SC_CLK_TCK); + char *output = malloc(8 * 1024 * 1024); + char *ptr = output; - while ((pDirent = readdir(pDir)) != NULL) { - first = pDirent->d_name[0]; - if (first < '0' || first > '9') continue; - sprintf(fname, "/proc/%s/status", pDirent->d_name); - file = fopen(fname, "rb"); - if (file == NULL) continue; - fread(buffer, 1, 4096, file); - fclose(file); + cur = head; + while (cur != NULL) { + // create process descriptor + sprintf(buffer, + "pid=\"%d\",ppid=\"%d\",label=\"%s\",lxc=\"%d\",level=\"%d\"", + cur->pid, cur->ppid, cur->label, cur->lxc, cur->level); + ptr += sprintf(ptr, "process_cpu_time_seconds{%s} %f\n", buffer, (double) cur->cpuTime / clocks); + ptr += sprintf(ptr, "process_child_cpu_time_seconds{%s} %f\n", buffer,(double) cur->childCpuTime / clocks); + ptr += sprintf(ptr, "process_num_threads{%s} %llu\n", buffer, cur->nThreads); + ptr += sprintf(ptr, "process_resident_bytes{%s} %llu\n", buffer, cur->resident); + ptr += sprintf(ptr, "process_swap_bytes{%s} %llu\n", buffer, cur->swap); + ptr += sprintf(ptr, "process_fileio_bytes_written{%s} %llu\n", buffer, cur->written); + ptr += sprintf(ptr, "process_fileio_bytes_read{%s} %llu\n", buffer, cur->read); + ptr += sprintf(ptr, "process_is_kernel_process{%s} %d\n", buffer, cur->iskernel); + // free and proceed + Process *prev = cur; + cur = cur->next; + free(prev); + } - if (parseStatusFile(cur, buffer)) continue; + lxcinfo *lxc = lxcs; + while (lxc != NULL) { + sprintf(buffer, "lxc=\"%d\",lxcname=\"%s\"", lxc->lxcid, lxc->hostname); + ptr += sprintf(ptr, "lxc_cpu_core_count{%s} %u\n", buffer, lxc->cpucount); + ptr += sprintf(ptr, "lxc_memory_limit_bytes{%s} %llu\n", buffer, lxc->memlimit); + // free and proceed + lxcinfo *prev = lxc; + lxc = lxc->next; + free(prev); + } - // truncate 'status' to 'stat' - len = strlen(fname); - fname[len-2] = 0; - file = fopen(fname, "rb"); - if (file == NULL) continue; - fread(buffer, 1, 4096, file); - fclose(file); - - if (parseStatFile(cur, buffer)) continue; - - sprintf(fname, "/proc/%s/io", pDirent->d_name); - file = fopen(fname, "rb"); - if (file == NULL) continue; - fread(buffer, 1, 4096, file); - fclose(file); - - if (parseIOFile(cur, buffer)) continue; - - sprintf(fname, "/proc/%s/cpuset", pDirent->d_name); - file = fopen(fname, "rb"); - if (file == NULL) goto nextProcess; - fread(cur->cpuset, 1, sizeof(cur->cpuset), file); - fclose(file); - - strtok(cur->cpuset, "\n\t "); - char *lxcTag = "/lxc/"; - if (memcmp(cur->cpuset, lxcTag, strlen(lxcTag)) == 0) { - // Resides in LXC -- read file and tag - sscanf(cur->cpuset, "/lxc/%d/ns", &cur->lxc); - sprintf(fname, "/etc/pve/lxc/%d.conf", cur->lxc); - file = fopen(fname, "rb"); - if (file == NULL) goto nextProcess; - fread(buffer, 1, 4096, file); - fclose(file); - - char *ptr = strstr(buffer, "hostname:"); - ptr += strlen("hostname:"); - while (*ptr == ' ') ptr++; - strtok(ptr, "\n\t "); - strcpy(cur->lxcname, ptr); - } else { - cur->lxcname[0] = 0; - cur->lxc = -1; - } - -nextProcess: - cur->next = malloc(sizeof(Process)); - cur->next->prev = cur; - cur = cur->next; - } - - // clean up unused last node - cur = cur->prev; - free(cur->next); - cur->next = NULL; - - int clocks = sysconf(_SC_CLK_TCK); - char *output = malloc(8*1024*1024); - char *ptr = output; - - cur = head; - while (cur != NULL) { - // create process descriptor - sprintf(buffer, "pid=\"%d\",ppid=\"%d\",label=\"%s\",kernel=\"%d\",ctid=\"%d\",lxc=\"%s\"", cur->pid, cur->ppid, cur->label, cur->iskernel, cur->lxc, cur->lxcname); - ptr += sprintf(ptr, "proc_cpu_time{%s} %f\n", buffer, (double) cur->cpuTime / clocks); - ptr += sprintf(ptr, "proc_child_cpu_time{%s} %f\n", buffer, (double) cur->childCpuTime / clocks); - ptr += sprintf(ptr, "proc_num_threads{%s} %llu\n", buffer, cur->nThreads); - ptr += sprintf(ptr, "proc_resident_bytes{%s} %llu\n", buffer, cur->resident); - ptr += sprintf(ptr, "proc_swap_bytes{%s} %llu\n", buffer, cur->swap); - ptr += sprintf(ptr, "proc_fileio_bytes_written{%s} %llu\n", buffer, cur->written); - ptr += sprintf(ptr, "proc_fileio_bytes_read{%s} %llu\n", buffer, cur->read); - cur = cur->next; - } - - cur = head; - while (cur != NULL) { - Process *prev = cur; - cur = cur->next; - free(prev); - } - - - closedir(pDir); - free(buffer); - free(fname); - return output; + closedir(pDir); + free(buffer); + free(fname); + return output; } #define BUFFER_SIZE 1024 void handle_client(int client_socket) { - char *data = readProcesses("/proc"); - int length = strlen(data); - printf("Got data of length: %d\n", length); + char *data = readProcesses("/proc"); + int length = strlen(data); + printf("Got data of length: %d\n", length); - // HTTP response - const char *http_headers_fmt = - "HTTP/1.1 200 OK\r\n" - "Content-Type: text/plain\r\n" - "Content-Length: %u\r\n" - "Connection: close\r\n" - "\r\n"; + // HTTP response + const char *http_headers_fmt = + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: %u\r\n" + "Connection: close\r\n" + "\r\n"; - char headers[BUFFER_SIZE]; - sprintf(headers, http_headers_fmt, length); - - printf("Sending headers\n"); - if (send(client_socket, headers, strlen(headers), 0) == -1) { - printf("Failed to send headers\n"); - free(data); - return; - } else { - printf("Sent headers\n"); - } - - // Send the response to the client - int total_sent = 0; - - while (total_sent < length) { - int amt = length - total_sent; - int sent = send(client_socket, data+total_sent, amt, 0); - printf("Tried sending %d actually sent %d\n", amt, sent); - total_sent += sent; - } - - printf("Closing connection\n"); - // Close the client socket - shutdown(client_socket, SHUT_WR); - sleep(1); - close(client_socket); + char headers[BUFFER_SIZE]; + sprintf(headers, http_headers_fmt, length); + printf("Sending headers\n"); + if (send(client_socket, headers, strlen(headers), 0) == -1) { + printf("Failed to send headers\n"); free(data); + return; + } else { + printf("Sent headers\n"); + } + + // Send the response to the client + int total_sent = 0; + + while (total_sent < length) { + int amt = length - total_sent; + int sent = send(client_socket, data + total_sent, amt, 0); + printf("Tried sending %d actually sent %d\n", amt, sent); + total_sent += sent; + } + + printf("Closing connection\n"); + // Close the client socket + shutdown(client_socket, SHUT_WR); + sleep(1); + close(client_socket); + + free(data); } int main(int argc, char *argv[]) { + int port = 9101; - int port = 9101; - - if (argc == 1) { - char *buf = readProcesses("/proc"); - if (buf == NULL) return 4; - printf(buf); - return 0; - - } else { - port = atoi(argv[1]); - } - - int server_socket, client_socket; - struct sockaddr_in server_addr, client_addr; - socklen_t addr_len = sizeof(client_addr); - - // Create the server socket - if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - perror("Socket creation failed"); - exit(EXIT_FAILURE); - } - - // Configure the server address - server_addr.sin_family = AF_INET; - server_addr.sin_addr.s_addr = INADDR_ANY; - server_addr.sin_port = htons(port); - - // Bind the socket to the specified port - if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { - perror("Bind failed"); - close(server_socket); - exit(EXIT_FAILURE); - } - - // Listen for incoming connections - if (listen(server_socket, 5) == -1) { - perror("Listen failed"); - close(server_socket); - exit(EXIT_FAILURE); - } - - printf("Server is listening on port %d\n", port); - - // Accept and handle incoming connections - while (1) { - if ((client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &addr_len)) == -1) { - perror("Accept failed"); - continue; - } - - // Handle the client in a separate function - handle_client(client_socket); - } - - // Close the server socket (this won't actually be reached in this example) - close(server_socket); - + if (argc == 1) { + char *buf = readProcesses("/proc"); + if (buf == NULL) return 4; + printf(buf); return 0; + + } else { + port = atoi(argv[1]); + } + + int server_socket, client_socket; + struct sockaddr_in server_addr, client_addr; + socklen_t addr_len = sizeof(client_addr); + + // Create the server socket + if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + perror("Socket creation failed"); + exit(EXIT_FAILURE); + } + + // Configure the server address + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(port); + + // Bind the socket to the specified port + if (bind(server_socket, (struct sockaddr *)&server_addr, + sizeof(server_addr)) == -1) { + perror("Bind failed"); + close(server_socket); + exit(EXIT_FAILURE); + } + + // Listen for incoming connections + if (listen(server_socket, 5) == -1) { + perror("Listen failed"); + close(server_socket); + exit(EXIT_FAILURE); + } + + printf("Server is listening on port %d\n", port); + + // Accept and handle incoming connections + while (1) { + if ((client_socket = accept(server_socket, (struct sockaddr *)&client_addr, + &addr_len)) == -1) { + perror("Accept failed"); + continue; + } + + // Handle the client in a separate function + handle_client(client_socket); + } + + // Close the server socket (this won't actually be reached in this example) + close(server_socket); + + return 0; }