Files
CS-LABS/kirill/rgz/daemon.c
2025-12-17 18:30:01 +07:00

246 lines
8.5 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// fifo_server_daemon.c
// Демон: читает из FIFO_REQUEST, обрабатывает текст,
// В КАЖДОЙ СТРОКЕ заменяет пробелом каждый 3-й байт, который НЕ '\n'.
// '\n' сохраняется и сбрасывает счётчик позиции внутри строки.
// Пишет результат в FIFO_RESPONSE и дописывает "\nREPLACEMENTS:<n>\n".
// Реально использует argv[1] как max_replacements (лимит замен): после достижения лимита
// перестаёт делать замены, но продолжает копировать вход как есть.
// Если лимит превышен (т.е. были потенциальные замены дальше), логирует это в syslog.
// Логирует ошибки в syslog, игнорирует SIGPIPE (write() вернёт EPIPE вместо завершения).
#include <stdio.h> // fprintf, snprintf
#include <stdlib.h> // malloc, free, strtoll
#include <string.h> // strlen, strerror, memset
#include <unistd.h> // read, write, close, unlink
#include <fcntl.h> // open, O_RDONLY, O_WRONLY
#include <sys/types.h> // типы
#include <sys/stat.h> // mkfifo
#include <errno.h> // errno
#include <signal.h> // sigaction, SIGINT, SIGTERM, SIGPIPE
#include <syslog.h> // syslog, openlog, closelog
#define FIFO_REQUEST "/tmp/fifo_request"
#define FIFO_RESPONSE "/tmp/fifo_response"
#define BUFFER_SIZE 4096
static volatile sig_atomic_t running = 1;
static void signal_handler(int sig) {
(void) sig;
running = 0;
}
// разбор неотрицательного long long
static long long parse_ll_nonneg(const char *s) {
char *end = NULL;
errno = 0;
long long v = strtoll(s, &end, 10);
if (errno != 0 || end == s || *end != '\0' || v < 0) return -1;
return v;
}
// Надёжная запись "всё или ошибка"
static int write_all(int fd, const void *buf, size_t len) {
const char *p = (const char *) buf;
size_t left = len;
while (left > 0) {
ssize_t n = write(fd, p, left);
if (n < 0) {
if (errno == EINTR) continue;
return -1;
}
if (n == 0) return -1;
p += (size_t) n;
left -= (size_t) n;
}
return 0;
}
// В каждой строке: заменить каждый 3-й НЕ '\n' байт на пробел до max_replacements.
// Возвращает фактическое количество замен; устанавливает *would_exceed=1 если лимит был бы превышен.
static long long process_data_limited(const char *input, size_t input_len,
char *output, size_t output_size,
long long max_replacements,
int *would_exceed) {
if (would_exceed) *would_exceed = 0;
if (!input || !output || input_len == 0 || output_size == 0) return 0;
long long replacements = 0;
size_t out_pos = 0;
size_t pos_in_line = 0; // считает только не-'\n' байты в текущей строке
for (size_t i = 0; i < input_len && out_pos < output_size - 1; i++) {
unsigned char c = (unsigned char) input[i];
if (c == '\n') {
output[out_pos++] = '\n';
pos_in_line = 0;
continue;
}
pos_in_line++;
if (pos_in_line % 3 == 0) {
if (replacements < max_replacements) {
output[out_pos++] = ' ';
replacements++;
} else {
if (would_exceed) *would_exceed = 1;
output[out_pos++] = (char) c; // лимит достигнут: больше не заменяем
}
} else {
output[out_pos++] = (char) c;
}
}
output[out_pos] = '\0';
return replacements;
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <max_replacements>\n", argv[0]);
return 1;
}
long long max_replacements = parse_ll_nonneg(argv[1]);
if (max_replacements < 0) {
fprintf(stderr, "ERROR: Invalid argument (must be non-negative integer)\n");
return 1;
}
openlog("fifo_server_daemon", LOG_PID | LOG_NDELAY, LOG_DAEMON); // openlog/syslog/closelog
syslog(LOG_INFO, "Server started (systemd), max_replacements=%lld", max_replacements);
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
(void) sigaction(SIGINT, &sa, NULL);
(void) sigaction(SIGTERM, &sa, NULL);
signal(SIGPIPE, SIG_IGN); // ignore SIGPIPE => write() fails with EPIPE
unlink(FIFO_REQUEST);
unlink(FIFO_RESPONSE);
if (mkfifo(FIFO_REQUEST, 0666) == -1) {
syslog(LOG_ERR, "mkfifo request failed: %s", strerror(errno));
closelog();
return 1;
}
if (mkfifo(FIFO_RESPONSE, 0666) == -1) {
syslog(LOG_ERR, "mkfifo response failed: %s", strerror(errno));
unlink(FIFO_REQUEST);
closelog();
return 1;
}
syslog(LOG_INFO, "FIFO created: request=%s, response=%s", FIFO_REQUEST, FIFO_RESPONSE);
while (running) {
int fd_req = open(FIFO_REQUEST, O_RDONLY);
if (!running) {
if (fd_req != -1) close(fd_req);
break;
}
if (fd_req == -1) {
if (errno == EINTR) continue;
syslog(LOG_ERR, "open request FIFO failed: %s", strerror(errno));
break;
}
char *input_buffer = (char *) malloc(BUFFER_SIZE);
char *output_buffer = (char *) malloc(BUFFER_SIZE);
if (!input_buffer || !output_buffer) {
syslog(LOG_ERR, "Memory allocation failed");
close(fd_req);
free(input_buffer);
free(output_buffer);
break;
}
ssize_t bytes_read = read(fd_req, input_buffer, BUFFER_SIZE - 1);
close(fd_req);
if (!running) {
free(input_buffer);
free(output_buffer);
break;
}
if (bytes_read < 0) {
if (errno != EINTR) syslog(LOG_ERR, "read request FIFO failed: %s", strerror(errno));
free(input_buffer);
free(output_buffer);
continue;
}
if (bytes_read == 0) {
// EOF (writer closed)
free(input_buffer);
free(output_buffer);
continue;
}
input_buffer[bytes_read] = '\0';
syslog(LOG_INFO, "Request received: %zd bytes", bytes_read);
int would_exceed = 0;
long long replacements = process_data_limited(
input_buffer, (size_t) bytes_read, output_buffer, BUFFER_SIZE, max_replacements, &would_exceed
);
if (would_exceed) {
syslog(LOG_WARNING,
"Replacement limit reached: max=%lld, actual=%lld (further replacements suppressed)",
max_replacements, replacements);
}
int fd_resp = open(FIFO_RESPONSE, O_WRONLY);
if (fd_resp == -1) {
syslog(LOG_ERR, "open response FIFO failed: %s", strerror(errno));
free(input_buffer);
free(output_buffer);
if (!running) break;
continue;
}
size_t output_len = strlen(output_buffer);
if (write_all(fd_resp, output_buffer, output_len) < 0) {
syslog(LOG_ERR, "write response body failed: %s", strerror(errno)); // EPIPE possible
close(fd_resp);
free(input_buffer);
free(output_buffer);
continue;
}
char trailer[128];
if (would_exceed) {
snprintf(trailer, sizeof(trailer), "\nREPLACEMENTS:%lld\nLIMIT_EXCEEDED:%lld\n",
replacements, max_replacements);
} else {
snprintf(trailer, sizeof(trailer), "\nREPLACEMENTS:%lld\n", replacements);
}
if (write_all(fd_resp, trailer, strlen(trailer)) < 0) {
syslog(LOG_WARNING, "write trailer failed: %s", strerror(errno)); // EPIPE possible
}
close(fd_resp);
syslog(LOG_INFO, "Response sent: body=%zu bytes; replacements=%lld; limit=%lld",
output_len, replacements, max_replacements);
free(input_buffer);
free(output_buffer);
}
syslog(LOG_INFO, "Server stopping, cleaning up");
unlink(FIFO_REQUEST);
unlink(FIFO_RESPONSE);
closelog();
return 0;
}