Added signal handler and self connector to gracefully shutdown

This commit is contained in:
Dan Snyder 2025-01-15 10:27:23 -05:00 committed by dan
parent 82fb0ec4bd
commit 54f22771dc
4 changed files with 449 additions and 376 deletions

View File

@ -1,6 +1,7 @@
{
"files.associations": {
"stdlib.h": "c",
"process.h": "c"
"process.h": "c",
"unistd.h": "c"
}
}

382
src/process.c Normal file
View File

@ -0,0 +1,382 @@
#include <dirent.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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<depth; i++) printf(" ");
if (depth > 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;
}

View File

@ -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

View File

@ -1,7 +1,6 @@
#include <arpa/inet.h>
#include <dirent.h>
#include <stdbool.h>
#include <stdint.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -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<depth; i++) printf(" ");
if (depth > 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<strlen(argv[1]); i++) {
if (argv[1][i] < '0' || argv[1][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;
}