Skip to content

Commit

Permalink
Initial revision
Browse files Browse the repository at this point in the history
  • Loading branch information
walac committed Jan 31, 2021
0 parents commit c42a500
Show file tree
Hide file tree
Showing 9 changed files with 300 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build/
.cache/
11 changes: 11 additions & 0 deletions CMakeLists.txt
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)
27 changes: 27 additions & 0 deletions README.md
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
```
40 changes: 40 additions & 0 deletions src/error.c
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;
}
17 changes: 17 additions & 0 deletions src/error.h
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__)
162 changes: 162 additions & 0 deletions src/main.c
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(&current, &last);
}
} while (errno == EINTR);

return 0;
}
33 changes: 33 additions & 0 deletions src/socket.c
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;
}
3 changes: 3 additions & 0 deletions src/socket.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

int create_socket(const char *path);
5 changes: 5 additions & 0 deletions src/util.h
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

0 comments on commit c42a500

Please sign in to comment.