-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit c42a500
Showing
9 changed files
with
300 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
build/ | ||
.cache/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
cmake_minimum_required(VERSION 3.16.0) | ||
|
||
project(log-server) | ||
|
||
set(CMAKE_C_STANDARD 11) | ||
|
||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) | ||
|
||
add_executable(log-server src/main.c src/socket.c src/error.c) | ||
target_compile_options(log-server PRIVATE -Wall -Wextra) | ||
target_link_libraries(log-server) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
Simple log server | ||
================= | ||
|
||
This is a simple log server programa. It creates an unix socket (by default | ||
`/dev/log`) and read log messages from it and writes to the configured log | ||
files. | ||
|
||
Build | ||
===== | ||
|
||
```sh | ||
$ mkdir build | ||
$ cd build | ||
$ cmake .. | ||
$ make | ||
``` | ||
|
||
Run | ||
=== | ||
|
||
To run it you can type './log-server -h' to the command line options. | ||
When running in daemon mode, it writes the messages to `/var/log/messages`, | ||
otherwise it writes to the files passed in the command line. | ||
|
||
```sh | ||
$ ./log-server -s /tmp/log /tmp/messages | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#define _GNU_SOURCE | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <errno.h> | ||
|
||
#include "error.h" | ||
|
||
static void | ||
report_error(const char *call, const char *source, unsigned int line) | ||
{ | ||
int saved_errno = errno; | ||
|
||
char *func = strdup(call); | ||
if (!func) goto quit; | ||
|
||
char *p = strchr(func, '('); | ||
if (p) *p = '\0'; | ||
|
||
fprintf(stderr, "%s(%s:%u) - %s\n", func, basename(source), line, strerror(saved_errno)); | ||
|
||
quit: | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
ssize_t check_retval(ssize_t retval, const char *call, const char *source, unsigned int line) | ||
{ | ||
if (retval < 0) | ||
report_error(call, source, line); | ||
|
||
return retval; | ||
} | ||
|
||
void *check_retval_p(void *retval, const char *call, const char *source, unsigned int line) | ||
{ | ||
if (!retval) | ||
report_error(call, source, line); | ||
|
||
return retval; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
#pragma once | ||
|
||
#include <stdio.h> | ||
|
||
ssize_t check_retval(ssize_t retval, const char *call, const char *source, unsigned int line); | ||
|
||
void *check_retval_p(void *retval, const char *call, const char *source, unsigned int line); | ||
|
||
/* | ||
* Check the return value of a posix API (int version) | ||
*/ | ||
#define CHK(expr) check_retval(expr, #expr, __FILE__, __LINE__) | ||
|
||
/* | ||
* Check the return value of a posix API (pointer version) | ||
*/ | ||
#define CHK_P(expr) check_retval_p(expr, #expr, __FILE__, __LINE__) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
#define _GNU_SOURCE 1 | ||
#define _POSIX_C_SOURCE 1 | ||
#include <getopt.h> | ||
#include <stdlib.h> | ||
#include <memory.h> | ||
#include <stdio.h> | ||
#include <stdint.h> | ||
#include <unistd.h> | ||
#include <limits.h> | ||
#include <errno.h> | ||
#include <sys/types.h> | ||
#include <sys/stat.h> | ||
#include <limits.h> | ||
#include <fcntl.h> | ||
#include <signal.h> | ||
#include "socket.h" | ||
#include "error.h" | ||
|
||
static int pid_file; | ||
static char pid_pathname[PATH_MAX]; | ||
|
||
/* | ||
* According to RFC 5424, the syslog size limit is 1024 | ||
*/ | ||
#define BUFFER_SIZE 1024 | ||
|
||
struct message { | ||
char buffer[BUFFER_SIZE]; | ||
ssize_t size; | ||
}; | ||
|
||
static void swap_messages(struct message **m1, struct message **m2) | ||
{ | ||
struct message *tmp = *m1; | ||
*m1 = *m2; | ||
*m2 = tmp; | ||
} | ||
|
||
static void usage(const char *name) | ||
{ | ||
fprintf(stderr, "%s - run a log server\n", name); | ||
fprintf(stderr, "%s [-f|--daemonize] [-s|--socket-path <path>] [-h|--help] [<log-file1> <log-file2> ...]\n", name); | ||
fprintf(stderr, "\t-f|--daemonize - run the process as a daemon\n"); | ||
fprintf(stderr, "\t-s|--socket-path <path> - path to the unix socket file\n"); | ||
fprintf(stderr, "\t-h|--help - print this message\n"); | ||
} | ||
|
||
static void close_pid_file(void) | ||
{ | ||
close(pid_file); | ||
unlink(pid_pathname); | ||
} | ||
|
||
static void handle_signal(int sig) | ||
{ | ||
(void) sig; | ||
exit(EXIT_SUCCESS); | ||
} | ||
|
||
static void install_signal_handler(int sig) | ||
{ | ||
struct sigaction act, oldact; | ||
memset(&act, 0, sizeof act); | ||
act.sa_handler = handle_signal; | ||
sigemptyset(&act.sa_mask); | ||
CHK(sigaction(sig, &act, &oldact)); | ||
} | ||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
int run_as_daemon = 0; | ||
const char *socket_path = "/dev/log"; | ||
const char *optstr = "fhs:"; | ||
|
||
static const struct option long_opts[] = { | ||
{ "help", 0, NULL, 'h' }, | ||
{ "daemonize", 0, NULL, 'f' }, | ||
{ "socket-path", 1, NULL, 's' }, | ||
{ NULL, 0, NULL, 0 }, | ||
}; | ||
|
||
int optch; | ||
|
||
while ((optch = getopt_long(argc, argv, optstr, long_opts, NULL)) != EOF) { | ||
switch (optch) { | ||
case 'h': | ||
usage(argv[0]); | ||
return 0; | ||
case 'f': | ||
run_as_daemon = 1; | ||
break; | ||
case 's': | ||
socket_path = optarg; | ||
break; | ||
default: | ||
usage(argv[0]); | ||
return EXIT_FAILURE; | ||
} | ||
} | ||
|
||
if (run_as_daemon) { | ||
CHK(daemon(0, 0)); | ||
|
||
const ssize_t written = CHK(snprintf(pid_pathname, PATH_MAX, "/var/run/logging-daemon.%lu", (unsigned long) getpid())); | ||
if (written > PATH_MAX) { | ||
fputs("Invalid pid file\n", stderr); | ||
return EXIT_FAILURE; | ||
} | ||
|
||
pid_file = (int) CHK(open(pid_pathname, O_CREAT | O_EXCL, 0600)); | ||
atexit(close_pid_file); | ||
} | ||
|
||
install_signal_handler(SIGINT); | ||
install_signal_handler(SIGTERM); | ||
|
||
size_t nfiles; | ||
int *fds; | ||
|
||
if (run_as_daemon) { | ||
nfiles = 1; | ||
fds = CHK_P(calloc(1, sizeof(int))); | ||
fds[1] = (int) CHK(open("/var/log/messages", O_WRONLY | O_APPEND | O_CREAT, 0660)); | ||
} else { | ||
nfiles = argc - optind; | ||
fds = CHK_P(calloc(nfiles + 1, sizeof(int))); | ||
|
||
for (size_t i = 0; i < nfiles; ++i) | ||
fds[i+1] = (int) CHK(open(argv[optind + i], O_WRONLY | O_APPEND | O_CREAT, 0666)); | ||
} | ||
|
||
fds[0] = STDOUT_FILENO; | ||
|
||
struct message buffer[2] = { | ||
{ {'\0'}, 0 }, | ||
{ {'\0'}, 0 }, | ||
}; | ||
|
||
struct message *current = buffer; | ||
struct message *last = buffer + 1; | ||
|
||
const int log_socket = create_socket(socket_path); | ||
|
||
do { | ||
while ((current->size = read(log_socket, current->buffer, BUFFER_SIZE)) >= 0) { | ||
if (current->size != 0) { | ||
// detect duplicate messages | ||
if (current->size == last->size && !memcmp(current->buffer, last->buffer, current->size)) | ||
continue; | ||
|
||
for (size_t i = 0; i <= nfiles; ++i) { | ||
CHK(write(fds[i], current->buffer, current->size)); | ||
CHK(write(fds[i], "\n", 1)); | ||
} | ||
} | ||
|
||
swap_messages(¤t, &last); | ||
} | ||
} while (errno == EINTR); | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
#include <string.h> | ||
#include <sys/types.h> | ||
#include <sys/socket.h> | ||
#include <sys/stat.h> | ||
#include <sys/un.h> | ||
#include <unistd.h> | ||
#include <errno.h> | ||
#include "error.h" | ||
#include "util.h" | ||
#include "socket.h" | ||
|
||
int create_socket(const char *path) | ||
{ | ||
struct sockaddr_un addr; | ||
|
||
if (strlen(path) + 1 > ARRAY_SIZE(addr.sun_path)) { | ||
errno = E2BIG; | ||
return -1; | ||
} | ||
|
||
unlink(path); | ||
|
||
const int sockfd = CHK(socket(AF_LOCAL, SOCK_DGRAM, 0)); | ||
|
||
strcpy(addr.sun_path, path); | ||
addr.sun_family = AF_LOCAL; | ||
|
||
CHK(bind(sockfd, (struct sockaddr *) &addr, sizeof(addr))); | ||
|
||
CHK(chmod(path, 0666)); | ||
|
||
return sockfd; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#pragma once | ||
|
||
int create_socket(const char *path); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#pragma once | ||
|
||
#ifndef ARRAY_SIZE | ||
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0])) | ||
#endif |