From 54f22771dc0ffd7e7367bec87a04a4e30812b041 Mon Sep 17 00:00:00 2001 From: Dan Snyder Date: Wed, 15 Jan 2025 10:27:23 -0500 Subject: [PATCH] Added signal handler and self connector to gracefully shutdown --- .vscode/settings.json | 3 +- src/process.c | 382 +++++++++++++++++++++++++++++++++++++ src/process.h | 12 ++ src/server.c | 428 ++++++------------------------------------ 4 files changed, 449 insertions(+), 376 deletions(-) create mode 100644 src/process.c diff --git a/.vscode/settings.json b/.vscode/settings.json index 7d9d24b..fcee65f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "files.associations": { "stdlib.h": "c", - "process.h": "c" + "process.h": "c", + "unistd.h": "c" } } \ No newline at end of file diff --git a/src/process.c b/src/process.c new file mode 100644 index 0000000..5f04988 --- /dev/null +++ b/src/process.c @@ -0,0 +1,382 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "process.h" +#include "lxcstat.h" +#include "parsing.h" + +#ifndef PF_KTHREAD +#define PF_KTHREAD 0x00200000 +#endif + +#define skipRange(inclusive, exclusive) \ + for (int i##inclusive = 0; i##inclusive < exclusive - inclusive; \ + i##inclusive++) \ + location = strchr(location, ' ') + 1; + +int parseStatFile(Process *proc, char *filedata) { + char *location = filedata; + // (1) pid - %d + proc->pid = 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; + + // (3) State - %c + location += 2; + + // (4) ppid - %d + proc->ppid = fast_str2ull(&location); + location += 1; + + // skip [5 - 9) + skipRange(5, 9); + + // (9) flags - %u + proc->flags = fast_str2ull(&location); + location += 1; + + proc->iskernel = (proc->flags & PF_KTHREAD) ? true : false; + + // skip [10 - 14) + skipRange(10, 14); + + // (14) utime - %lu + proc->cpuTime = fast_str2ull(&location); + location += 1; + + // (15) stime - %lu + proc->cpuTime += fast_str2ull(&location); + proc->cCpuTime = proc->cpuTime; + location += 1; + + // skip 16-19 + skipRange(16, 20); + + // (20) num_threads - %ld + proc->nThreads = fast_str2ull(&location); + location += 1; + + return 0; +} + +int parseStatusFile(Process *proc, char *filedata) { + 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 + + proc->resident = 1024 * findAndParseField(&filedata, "VmRSS"); + proc->swap = 1024 * findAndParseField(&filedata, "VmSwap"); + proc->cMemory = proc->resident; + proc->cSwap = proc->swap; + return 0; +} + +int parseIOFile(Process *proc, char *filedata) { + proc->read = findAndParseField(&filedata, "read_bytes"); + proc->written = findAndParseField(&filedata, "write_bytes"); + proc->cRead = proc->read; + proc->cWrite = proc->written; + return 0; +} + +int resetVisits(Process *head) { + Process *current = head; + int processCount = 0; + + while (current != NULL) { + current->visited = 0; + current = current->next; + processCount++; + } + + return processCount; +} + + +void linkFamily(Process *head) { + int orphans = 1; + + while (orphans) { + orphans = 0; + Process *current = head; + // while on an actual process node, and it has a parent link or doesn't need a parent link (kernel) + while (current != NULL && (current->parent != NULL || current->ppid == current->pid)) current = current->next; + if (current == NULL) break; + orphans = 1; + + Process *parent = head; + while (parent != NULL && parent->pid != current->ppid) parent = parent->next; + if (parent == NULL) { + printf("Found orphan process: %d\n", current->pid); + current->ppid = -1; + continue; + } + + // We have a parent and a child ready to be united + current->parent = parent; + Process **placement = &(parent->child); + while ((*placement) != NULL) placement = &((*placement)->sibling); + *placement = current; + } +} + +void printFamilyTree(Process *head) { + int processCount = resetVisits(head); + int visited = 0; + Process *current = head; + int depth = 0; + + while (visited < processCount) { + if (current == NULL) { + printf("Bad visit count: %d visited of %d processes\n", visited, processCount); + break; + } + + current->visited = 1; + visited++; + + printf("%10d %10d %1d ", current->pid, current->ppid, current->iskernel); + // `ps f` style tree + for (int i=1; i 0) printf(" \\_ "); + printf("%s\n", current->label); + +nextProcess: + if (current->child != NULL && !current->child->visited) { + // process children first + depth++; + current = current->child; + } else if (current->sibling != NULL) { + // process siblings next + current = current->sibling; + } else if (current->parent != NULL) { + // return to parent when tree is exhausted + depth--; + current = current->parent; + goto nextProcess; // parent was already visited, so find next process from parent + } else { + // no parent - scan for unvisited process + while (current != NULL && current->visited) current = current->next; + } + } +} + +void aggregateStats(Process *head) { + int processCount = resetVisits(head); + Process *current = head; + int depth = 0; + int maxdepth = 0; + int visited = 0; + + // assign depth (aka level) of each process + while (visited < processCount) { + if (current == NULL) { + printf("Bad visit count: %d visited of %d processes\n", visited, processCount); + break; + } + + visited++; + current->visited = visited; // store to tag the order the process is visited + current->level = depth; + + if (depth > maxdepth) maxdepth = depth; + +nextProcess: + if (current->child != NULL && !current->child->visited) { + // process children first + depth++; + current = current->child; + } else if (current->sibling != NULL) { + // process siblings next + current = current->sibling; + } else if (current->parent != NULL) { + // return to parent when tree is exhausted + depth--; + current = current->parent; + goto nextProcess; // parent was already visited, so find next process from parent + } else { + // no parent - scan for unvisited process + while (current != NULL && current->visited) current = current->next; + } + } + + // scan for each depth level, tally stats upwards + for (depth=maxdepth; depth>0; depth--) { + current = head; + while (current != NULL) { + while (current != NULL && current->level != depth) current = current->next; + if (current == NULL) continue; + + Process *parent = current->parent; + parent->cCpuTime += current->cCpuTime; + parent->cMemory += current->cMemory; + parent->cSwap += current->cSwap; + parent->cRead += current->cRead; + parent->cWrite += current->cWrite; + current = current->next; + } + } +} + +char *readProcesses(char *procdir) { + int len; + struct dirent *pDirent; + DIR *pDir; + + // Ensure we can open directory. + + 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 *head = calloc(1, sizeof(Process)); + Process *cur = head; + Process *prev = NULL; + lxcinfo *lxcs = NULL; + + // First process node is the kernel + strcpy(cur->label, "kernel"); + cur->lxc = -1; + cur->visited = 1; + + while ((pDirent = readdir(pDir)) != NULL) { + first = pDirent->d_name[0]; + if (first < '0' || first > '9') continue; + + if (cur->visited) { + // get new process if necessary + prev = cur; + cur->next = calloc(1, sizeof(Process)); + cur = cur->next; + } + + 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; + } + +nextProcess: + cur->visited = 1; + } + + if (!cur->visited) { + // if there's an unused node, remove it + prev->next = NULL; + free(cur); + } + + linkFamily(head); + aggregateStats(head); + // printFamilyTree(head); + + 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\",lxc=\"%d\"", + cur->pid, cur->ppid, cur->label, cur->lxc); + // process specific stats + ptr += sprintf(ptr, "process_cpu_time_seconds{%s} %f\n", buffer, (double) cur->cpuTime / clocks); + ptr += sprintf(ptr, "process_num_threads{%s} %lu\n", buffer, cur->nThreads); + ptr += sprintf(ptr, "process_resident_bytes{%s} %lu\n", buffer, cur->resident); + ptr += sprintf(ptr, "process_swap_bytes{%s} %lu\n", buffer, cur->swap); + ptr += sprintf(ptr, "process_fileio_bytes_written{%s} %lu\n", buffer, cur->written); + ptr += sprintf(ptr, "process_fileio_bytes_read{%s} %lu\n", buffer, cur->read); + ptr += sprintf(ptr, "process_is_kernel_process{%s} %d\n", buffer, cur->iskernel); + // cumulative fields + ptr += sprintf(ptr, "process_cumulative_cpu_time_seconds{%s} %lu\n", buffer, cur->cCpuTime); + ptr += sprintf(ptr, "process_cumulative_resident_bytes{%s} %lu\n", buffer, cur->cMemory); + ptr += sprintf(ptr, "process_cumulative_swap_bytes{%s} %lu\n", buffer, cur->cSwap); + ptr += sprintf(ptr, "process_cumulative_bytes_read{%s} %lu\n", buffer, cur->cRead); + ptr += sprintf(ptr, "process_cumulative_bytes_written{%s} %lu\n", buffer, cur->cWrite); + // stats used to make flame chart + ptr += sprintf(ptr, "process_tree_depth{%s} %d\n", buffer, cur->level); + ptr += sprintf(ptr, "process_visit_index{%s} %d\n", buffer, cur->visited); + // free and proceed + Process *prev = cur; + cur = cur->next; + free(prev); + } + + 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); + ptr += sprintf(ptr, "lxc_swap_limit_bytes{%s} %llu\n", buffer, lxc->swaplimit); + // free and proceed + lxcinfo *prev = lxc; + lxc = lxc->next; + free(prev); + } + + closedir(pDir); + free(buffer); + free(fname); + return output; +} \ No newline at end of file diff --git a/src/process.h b/src/process.h index eb8309e..1cd1207 100644 --- a/src/process.h +++ b/src/process.h @@ -35,4 +35,16 @@ typedef struct st_process { uint64_t cWrite; } Process; +char *readProcesses(char *procdir); +void aggregateStats(Process *head); + +int resetVisits(Process *head); +void linkFamily(Process *head); +void printFamilyTree(Process *head); +int parseStatFile(Process *proc, char *filedata); +int parseStatusFile(Process *proc, char *filedata); +int parseIOFile(Process *proc, char *filedata); + + + #endif diff --git a/src/server.c b/src/server.c index 46fb022..08cdbe2 100644 --- a/src/server.c +++ b/src/server.c @@ -1,7 +1,6 @@ #include -#include -#include -#include +#include +#include #include #include #include @@ -10,377 +9,37 @@ #include "process.h" #include "lxcstat.h" -#include "parsing.h" -#ifndef PF_KTHREAD -#define PF_KTHREAD 0x00200000 -#endif +volatile sig_atomic_t stop_server = 0; -#define skipRange(inclusive, exclusive) \ - for (int i##inclusive = 0; i##inclusive < exclusive - inclusive; \ - i##inclusive++) \ - location = strchr(location, ' ') + 1; - -int parseStatFile(Process *proc, char *filedata) { - char *location = filedata; - // (1) pid - %d - proc->pid = 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; - - // (3) State - %c - location += 2; - - // (4) ppid - %d - proc->ppid = fast_str2ull(&location); - location += 1; - - // skip [5 - 9) - skipRange(5, 9); - - // (9) flags - %u - proc->flags = fast_str2ull(&location); - location += 1; - - proc->iskernel = (proc->flags & PF_KTHREAD) ? true : false; - - // skip [10 - 14) - skipRange(10, 14); - - // (14) utime - %lu - proc->cpuTime = fast_str2ull(&location); - location += 1; - - // (15) stime - %lu - proc->cpuTime += fast_str2ull(&location); - proc->cCpuTime = proc->cpuTime; - location += 1; - - // skip 16-19 - skipRange(16, 20); - - // (20) num_threads - %ld - proc->nThreads = fast_str2ull(&location); - location += 1; - - return 0; +void handle_signal(int signal) { + stop_server = 1; // Set the flag to stop the server } -int parseStatusFile(Process *proc, char *filedata) { - proc->tgid = findAndParseField(&filedata, "Tgid"); - proc->pid = findAndParseField(&filedata, "Pid"); +void *self_connector_thread(void *arg) { + int port = *((int *) arg); - 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"); - proc->cMemory = proc->resident; - proc->cSwap = proc->swap; - return 0; -} - -int parseIOFile(Process *proc, char *filedata) { - proc->read = findAndParseField(&filedata, "read_bytes"); - proc->written = findAndParseField(&filedata, "write_bytes"); - proc->cRead = proc->read; - proc->cWrite = proc->written; - return 0; -} - -int resetVisits(Process *head) { - Process *current = head; - int processCount = 0; - - while (current != NULL) { - current->visited = 0; - current = current->next; - processCount++; + while (!stop_server) { + sleep(3); // Wait for the stop signal } - return processCount; -} - - -void linkFamily(Process *head) { - int orphans = 1; - - while (orphans) { - orphans = 0; - Process *current = head; - // while on an actual process node, and it has a parent link or doesn't need a parent link (kernel) - while (current != NULL && (current->parent != NULL || current->ppid == current->pid)) current = current->next; - if (current == NULL) break; - orphans = 1; - - Process *parent = head; - while (parent != NULL && parent->pid != current->ppid) parent = parent->next; - if (parent == NULL) { - printf("Found orphan process: %d\n", current->pid); - current->ppid = -1; - continue; - } - - // We have a parent and a child ready to be united - current->parent = parent; - Process **placement = &(parent->child); - while ((*placement) != NULL) placement = &((*placement)->sibling); - *placement = current; - } -} - -void printFamilyTree(Process *head) { - int processCount = resetVisits(head); - int visited = 0; - Process *current = head; - int depth = 0; - - while (visited < processCount) { - if (current == NULL) { - printf("Bad visit count: %d visited of %d processes\n", visited, processCount); - break; - } - - current->visited = 1; - visited++; - - printf("%10d %10d %1d ", current->pid, current->ppid, current->iskernel); - // `ps f` style tree - for (int i=1; i 0) printf(" \\_ "); - printf("%s\n", current->label); - -nextProcess: - if (current->child != NULL && !current->child->visited) { - // process children first - depth++; - current = current->child; - } else if (current->sibling != NULL) { - // process siblings next - current = current->sibling; - } else if (current->parent != NULL) { - // return to parent when tree is exhausted - depth--; - current = current->parent; - goto nextProcess; // parent was already visited, so find next process from parent - } else { - // no parent - scan for unvisited process - while (current != NULL && current->visited) current = current->next; - } - } -} - -void aggregateStats(Process *head) { - int processCount = resetVisits(head); - Process *current = head; - int depth = 0; - int maxdepth = 0; - int visited = 0; - - // assign depth (aka level) of each process - while (visited < processCount) { - if (current == NULL) { - printf("Bad visit count: %d visited of %d processes\n", visited, processCount); - break; - } - - visited++; - current->visited = visited; // store to tag the order the process is visited - current->level = depth; - - if (depth > maxdepth) maxdepth = depth; - -nextProcess: - if (current->child != NULL && !current->child->visited) { - // process children first - depth++; - current = current->child; - } else if (current->sibling != NULL) { - // process siblings next - current = current->sibling; - } else if (current->parent != NULL) { - // return to parent when tree is exhausted - depth--; - current = current->parent; - goto nextProcess; // parent was already visited, so find next process from parent - } else { - // no parent - scan for unvisited process - while (current != NULL && current->visited) current = current->next; - } - } - - // scan for each depth level, tally stats upwards - for (depth=maxdepth; depth>0; depth--) { - current = head; - while (current != NULL) { - while (current != NULL && current->level != depth) current = current->next; - if (current == NULL) continue; - - Process *parent = current->parent; - parent->cCpuTime += current->cCpuTime; - parent->cMemory += current->cMemory; - parent->cSwap += current->cSwap; - parent->cRead += current->cRead; - parent->cWrite += current->cWrite; - current = current->next; - } - } -} - -char *readProcesses(char *procdir) { - int len; - struct dirent *pDirent; - DIR *pDir; - - // Ensure we can open directory. - - pDir = opendir(procdir); - if (pDir == NULL) { - printf("Cannot open directory '%s'\n", procdir); + // Connect to the server's listening port to unblock accept() + int sock_fd = socket(AF_INET, SOCK_STREAM, 0); + if (sock_fd < 0) { + perror("self-connector socket"); return NULL; } - // Process each entry. + struct sockaddr_in server_address = { + .sin_family = AF_INET, + .sin_port = htons(port), + .sin_addr.s_addr = htonl(INADDR_LOOPBACK), // Connect to localhost + }; - char *buffer = malloc(4096); - char *fname = malloc(1024); - char first; - FILE *file; - - Process *head = calloc(1, sizeof(Process)); - Process *cur = head; - Process *prev = NULL; - lxcinfo *lxcs = NULL; - - // First process node is the kernel - strcpy(cur->label, "kernel"); - cur->lxc = -1; - cur->visited = 1; - - while ((pDirent = readdir(pDir)) != NULL) { - first = pDirent->d_name[0]; - if (first < '0' || first > '9') continue; - - if (cur->visited) { - // get new process if necessary - prev = cur; - cur->next = calloc(1, sizeof(Process)); - cur = cur->next; - } - - 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; - } - -nextProcess: - cur->visited = 1; - } - - if (!cur->visited) { - // if there's an unused node, remove it - prev->next = NULL; - free(cur); - } - - linkFamily(head); - aggregateStats(head); - // printFamilyTree(head); - - 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\",lxc=\"%d\"", - cur->pid, cur->ppid, cur->label, cur->lxc); - // process specific stats - ptr += sprintf(ptr, "process_cpu_time_seconds{%s} %f\n", buffer, (double) cur->cpuTime / clocks); - ptr += sprintf(ptr, "process_num_threads{%s} %lu\n", buffer, cur->nThreads); - ptr += sprintf(ptr, "process_resident_bytes{%s} %lu\n", buffer, cur->resident); - ptr += sprintf(ptr, "process_swap_bytes{%s} %lu\n", buffer, cur->swap); - ptr += sprintf(ptr, "process_fileio_bytes_written{%s} %lu\n", buffer, cur->written); - ptr += sprintf(ptr, "process_fileio_bytes_read{%s} %lu\n", buffer, cur->read); - ptr += sprintf(ptr, "process_is_kernel_process{%s} %d\n", buffer, cur->iskernel); - // cumulative fields - ptr += sprintf(ptr, "process_cumulative_cpu_time_seconds{%s} %lu\n", buffer, cur->cCpuTime); - ptr += sprintf(ptr, "process_cumulative_resident_bytes{%s} %lu\n", buffer, cur->cMemory); - ptr += sprintf(ptr, "process_cumulative_swap_bytes{%s} %lu\n", buffer, cur->cSwap); - ptr += sprintf(ptr, "process_cumulative_bytes_read{%s} %lu\n", buffer, cur->cRead); - ptr += sprintf(ptr, "process_cumulative_bytes_written{%s} %lu\n", buffer, cur->cWrite); - // stats used to make flame chart - ptr += sprintf(ptr, "process_tree_depth{%s} %d\n", buffer, cur->level); - ptr += sprintf(ptr, "process_visit_index{%s} %d\n", buffer, cur->visited); - // free and proceed - Process *prev = cur; - cur = cur->next; - free(prev); - } - - 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); - ptr += sprintf(ptr, "lxc_swap_limit_bytes{%s} %llu\n", buffer, lxc->swaplimit); - // free and proceed - lxcinfo *prev = lxc; - lxc = lxc->next; - free(prev); - } - - closedir(pDir); - free(buffer); - free(fname); - return output; + connect(sock_fd, (struct sockaddr *)&server_address, sizeof(server_address)); + sleep(1); + close(sock_fd); + return NULL; } #define BUFFER_SIZE 1024 @@ -427,25 +86,35 @@ void handle_client(int client_socket) { closeConnection: 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; - if (argc == 1) { char *buf = readProcesses("/proc"); - if (buf == NULL) return 4; + if (buf == NULL) return 1; printf(buf); free(buf); return 0; + } - } else { - port = atoi(argv[1]); + for (int i=0; i '9') { + fprintf(stderr, "Argument '%s' is not a valid port", argv[1]); + return 1; + } + } + + int port = atoi(argv[1]); + // Set up signal handling + struct sigaction sa; + sa.sa_handler = handle_signal; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction(SIGINT, &sa, NULL) == -1 || sigaction(SIGTERM, &sa, NULL) == -1) { + perror("sigaction"); + exit(EXIT_FAILURE); } int server_socket, client_socket; @@ -478,12 +147,19 @@ int main(int argc, char *argv[]) { exit(EXIT_FAILURE); } - printf("Server is listening on port %d\n", port); + // Start the self-connector thread + pthread_t connector_thread; + pthread_create(&connector_thread, NULL, self_connector_thread, (void *) &port); + + printf("Server is listening on port %d (PID %d)\n", port, getpid()); // Accept and handle incoming connections while (1) { - if ((client_socket = accept(server_socket, (struct sockaddr *)&client_addr, - &addr_len)) == -1) { + client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &addr_len); + printf("Got client connection\n"); + if (stop_server) break; // stop if we received SIGTERM or SIGINT + + if (client_socket == -1) { perror("Accept failed"); continue; } @@ -492,8 +168,10 @@ int main(int argc, char *argv[]) { handle_client(client_socket); } - // Close the server socket (this won't actually be reached in this example) + close(client_socket); close(server_socket); + printf("Shutting down\n"); + pthread_join(connector_thread, NULL); return 0; }