feat: First stable release

This commit is contained in:
Tom MTT. 2022-11-21 10:43:22 +01:00
parent 845677abe1
commit 8453d41a00
10 changed files with 1042 additions and 0 deletions

6
.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
.clangd
*.o
feuille
web/cgi/feuille.cgi

80
arg.h Normal file
View file

@ -0,0 +1,80 @@
/*
* arg.h
* Arg parsing.
*
* Copyright (c) 2016
* Christoph Lohmann <20h at r-36 dot net>
*
* This file is licensed under the MIT License.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
/*
* Copy me if you can.
* by 20h
*/
#ifndef _ARG_H_
#define _ARG_H_
#include <stdlib.h>
#include <stddef.h>
extern char *argv0;
/* use main(int argc, char *argv[]) */
#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
argv[0] && argv[0][0] == '-'\
&& argv[0][1];\
argc--, argv++) {\
char argc_;\
char **argv_;\
int brk_;\
if (argv[0][1] == '-' && argv[0][2] == '\0') {\
argv++;\
argc--;\
break;\
}\
int i_;\
for (i_ = 1, brk_ = 0, argv_ = argv;\
argv[0][i_] && !brk_;\
i_++) {\
if (argv_ != argv)\
break;\
argc_ = argv[0][i_];\
switch (argc_)
#define ARGEND }\
}
#define ARGC() argc_
#define EARGF(x) ((argv[0][i_+1] == '\0' && argv[1] == NULL)?\
((x), abort(), (char *)0) :\
(brk_ = 1, (argv[0][i_+1] != '\0')?\
(&argv[0][i_+1]) :\
(argc--, argv++, argv[0])))
#define ARGF() ((argv[0][i_+1] == '\0' && argv[1] == NULL)?\
(char *)0 :\
(brk_ = 1, (argv[0][i_+1] != '\0')?\
(&argv[0][i_+1]) :\
(argc--, argv++, argv[0])))
#endif

121
bin.c Normal file
View file

@ -0,0 +1,121 @@
/*
* bin.c
* Pastes handling.
*
*
* Copyright (c) 2022
* Tom MTT. <tom@heimdall.pm>
*
* This file is licensed under the 3-Clause BSD License.
* You should have received a copy of the 3-Clause BSD License
* along with this program. If not, see
* <https://basedwa.re/tmtt/feuille/src/branch/main/LICENSE>.
*/
#include "bin.h"
#include <stdio.h> /* for NULL, fclose, fopen, fputs, snprintf, FILE */
#include <stdlib.h> /* for calloc, free, malloc, rand, realloc */
#include <string.h> /* for strlen */
#include <unistd.h> /* for access, F_OK */
#include "feuille.h" /* for Settings, settings */
/* symbols used to generate IDs */
static char *id_symbols = "abcdefghijklmnopqrstuvwxyz0123456789";
/**
* Generate a random ID, until one is available on disk.
* min_length: the minimum ID length. Will be increased if a collision occurs.
* -> a pointer to the ID. Needs to be freed.
*/
char *generate_id(int min_length)
{
int length = min_length;
/* allocate a buffer to store the ID */
char *buffer;
if ((buffer = calloc(length + 1, sizeof(char))) == NULL)
return NULL;
/* for each letter, generate a random one */
for (int i = 0; i < length; i++) {
if (i > 8 * min_length)
return NULL;
buffer[i] = id_symbols[rand() % strlen(id_symbols)];
/* collision? */
if (i == length - 1 && paste_exists(buffer)) {
/* add one to the ID length and re-allocate the buffer */
length++;
void *tmp;
if ((tmp = realloc(buffer, (length + 1) * sizeof(char))) == NULL) {
free(buffer);
return NULL;
}
buffer = tmp;
}
}
buffer[length] = 0;
return buffer;
}
/**
* Check if an ID is already used.
* id: the ID in question.
* -> 1 if exists, 0 if not.
*/
int paste_exists(char *id)
{
return access(id, F_OK) == 0;
}
/**
* Write the paste content to disk.
* paste: the string containing the paste.
* id: the ID of the paste.
* -> 0 if done, -1 if not.
*/
int write_paste(char *paste, char *id)
{
/* open the file with write access */
FILE *file;
if ((file = fopen(id, "w")) == NULL)
return -1;
/* write the content to file */
if (fputs(paste, file) == -1) {
fclose(file);
return -1;
}
/* close the file (obviously) */
fclose(file);
return 0;
}
/**
* Make the full URL for the paste.
* id: the ID of the paste.
* -> a pointer to the URL. Needs to be freed.
*/
char *create_url(char *id)
{
/* calculate the length of the URL */
/* the 3 characters added are the trailing slash, newline and null byte */
int length = strlen(id) + strlen(settings.url) + 3;
/* allocate a buffer to store the URL */
char *buffer;
if ((buffer = malloc(length * sizeof(char))) == NULL)
return NULL;
/* set the buffer to the actual URL */
snprintf(buffer, length, "%s/%s\n", settings.url, id);
return buffer;
}

25
bin.h Normal file
View file

@ -0,0 +1,25 @@
/*
* bin.h
* bin.c header declarations.
*
* Copyright (c) 2022
* Tom MTT. <tom@heimdall.pm>
*
* This file is licensed under the 3-Clause BSD License.
* You should have received a copy of the 3-Clause BSD License
* along with this program. If not, see
* <https://basedwa.re/tmtt/feuille/src/branch/main/LICENSE>.
*/
#ifndef _BIN_H_
#define _BIN_H_
#include "feuille.h"
int paste_exists(char *);
int write_paste(char *, char *);
char *generate_id(int);
char *create_url(char *);
#endif

402
feuille.c Normal file
View file

@ -0,0 +1,402 @@
/*
* feuille.c
* Main source file.
*
* Copyright (c) 2022
* Tom MTT. <tom@heimdall.pm>
*
* This file is licensed under the 3-Clause BSD License.
* You should have received a copy of the 3-Clause BSD License
* along with this program. If not, see
* <https://basedwa.re/tmtt/feuille/src/branch/main/LICENSE>.
*/
#define _DEFAULT_SOURCE
#include "feuille.h"
#include <errno.h> /* for errno, ERANGE, EFBIG, ENOENT */
#include <limits.h> /* for USHRT_MAX, ULONG_MAX, CHAR_MAX, PATH_MAX, UCHA... */
#include <locale.h> /* for NULL, setlocale, LC_ALL */
#include <pwd.h> /* for getpwnam, passwd */
#include <stdio.h> /* for freopen, puts, stderr, stdin, stdout */
#include <stdlib.h> /* for strtoll, exit, free, realpath, srand */
#include <string.h> /* for strerror, strlen */
#include <sys/stat.h> /* for mkdir */
#include <sys/wait.h> /* for wait */
#include <syslog.h> /* for syslog, openlog, LOG_WARNING, LOG_NDELAY, LOG_... */
#include <time.h> /* for time */
#include <unistd.h> /* for fork, access, chdir, chown, chroot, close, getpid */
#include "arg.h" /* for EARGF, ARGBEGIN, ARGEND */
#include "bin.h" /* for create_url, generate_id, write_paste */
#include "server.h" /* for send_response, accept_connection, close_connec... */
#include "util.h" /* for verbose, die, error */
char *argv0;
/* default settings */
Settings settings = {
.address = "0.0.0.0",
.url = "http://localhost",
.output = "/var/www/htdocs/feuille",
.user = "www",
.id_length = 4,
.worker_count = 1,
.port = 8888,
.timeout = 4,
.max_size = 2097152, /* = 2MiB = 1024 * 1024 * 2 */
.buffer_size = 131072, /* = 128KiB = 1024 * 128 */
.verbose = 0,
.foreground = 0
};
/* functions declarations */
static void usage(int exit_code);
static void version(void);
/**
* Display feuille's basic usage.
* exit_code: the exit code to be used.
*/
void usage(int exit_code)
{
die(exit_code, "usage: %s [-abfhiopstuUvVw]\n"
" see `man feuille'.\n", argv0);
}
/**
* Display feuille's author(s) and version.
*/
void version(void)
{
die(0, "%s %s by Tom MTT. <tom@heimdall.pm>\n", argv0, VERSION);
}
/**
* Feuille's main function.
* argc: the argument count.
* argv: the argument values.
* -> an exit code.
*/
int main(int argc, char *argv[])
{
/* pledge stage 1 */
#ifdef __OpenBSD__
pledge("stdio rpath wpath cpath inet chown getpw proc id", "stdio wpath inet");
#endif
/* locale */
setlocale(LC_ALL, "");
/* syslog setup */
openlog("feuille", LOG_NDELAY | LOG_PERROR, LOG_USER);
/* settings */
long long tmp;
/* set number of workers */
if ((tmp = sysconf(_SC_NPROCESSORS_ONLN)) > 0 && tmp <= USHRT_MAX)
settings.worker_count = tmp;
ARGBEGIN {
case 'a':
/* set address */
settings.address = EARGF(usage(1));
break;
case 'b':
/* set buffer size */
tmp = strtoll(EARGF(usage(1)), NULL, 10);
if (tmp <= 0 || tmp > ULONG_MAX || errno == ERANGE)
die(ERANGE, "invalid buffer size.\n"
"see `man feuille'.\n");
settings.buffer_size = tmp;
break;
case 'f':
/* enable foreground execution */
settings.foreground = 1;
break;
case 'h':
/* get help */
usage(0);
break;
case 'i':
/* set id length */
tmp = strtoll(EARGF(usage(1)), NULL, 10) + 1;
if (tmp - 1 < 4 || tmp > UCHAR_MAX || errno == ERANGE)
die(ERANGE, "invalid id length.\n"
"see `man feuille'.\n");
settings.id_length = tmp;
break;
case 'o':
/* set output folder */
settings.output = EARGF(usage(1));
break;
case 'p':
/* set port */
tmp = strtoll(EARGF(usage(1)), NULL, 10);
if (tmp <= 0 || tmp > USHRT_MAX || errno == ERANGE)
die(ERANGE, "invalid port.\n"
"see `man feuille'.\n");
settings.port = tmp;
break;
case 's':
/* set max size */
tmp = strtoll(EARGF(usage(1)), NULL, 10);
if (tmp <= 0 || tmp > ULONG_MAX || errno == ERANGE)
die(ERANGE, "invalid maximum size.\n"
"see `man feuille'.\n");
settings.max_size = tmp;
break;
case 't':
/* set timeout */
tmp = strtoll(EARGF(usage(1)), NULL, 10);
if (tmp < 0 || tmp > UINT_MAX || errno == ERANGE)
die(ERANGE, "invalid timeout.\n"
"see `man feuille'.\n");
settings.timeout = tmp;
break;
case 'u':
/* set user */
settings.user = EARGF(usage(1));
break;
case 'U':
/* set url */
settings.url = EARGF(usage(1));
if (settings.url[strlen(settings.url) - 1] == '/')
settings.url[strlen(settings.url) - 1] = 0;
break;
case 'v':
/* enable verbose mode */
if (settings.verbose == CHAR_MAX)
die(ERANGE, "why? just why?\n"
"please see `man feuille' and go touch grass.\n");
settings.verbose++;
break;
case 'V':
/* get version */
version();
break;
case 'w':
/* set worker count */
tmp = strtoll(EARGF(usage(1)), NULL, 10);
if (tmp <= 0 || tmp > USHRT_MAX || errno == ERANGE)
die(ERANGE, "invalid worker count.\n"
"see `man feuille'.\n");
settings.worker_count = tmp;
break;
default:
usage(1);
} ARGEND;
if (argc != 0)
usage(1);
/* output folder checks */
char path[PATH_MAX];
if (mkdir(settings.output, 0755) == 0)
verbose(2, "creating folder `%s'...", settings.output);
if (realpath(settings.output, path) == NULL)
die(errno, "Could not get real path of directory `%s': %s\n", settings.output, strerror(errno));
if (access(path, W_OK) != 0)
die(errno, "Cannot write to directory `%s': %s\n", path, strerror(errno));
chdir(path);
/* server socket creation (before dropping root permissions) */
verbose(1, "initializing server socket...");
int server;
if ((server = initialize_server()) == -1)
die(errno, "Failed to initialize server socket: %s\n", strerror(errno));
/* chroot and drop root permissions */
if (getuid() == 0) {
if (strlen(settings.user) == 0)
settings.user = "nobody";
verbose(2, "getting uid and gid of user `%s'...", settings.user);
struct passwd *user;
if ((user = getpwnam(settings.user)) == NULL)
die(1, "User `%s' doesn't exist\n", settings.user);
int uid = user->pw_uid;
int gid = user->pw_gid;
verbose(2, "setting owner of `%s' to `%s'...", path, settings.user);
chown(path, uid, gid);
/* chroot */
verbose(2, "chroot'ing into `%s'...", path);
chroot(path);
/* privileges drop */
verbose(2, "dropping root privileges...");
setgid(gid);
setuid(uid);
} else {
puts("");
syslog(LOG_WARNING, "running as non-root user.");
syslog(LOG_WARNING, "`chroot' and user switching have been disabled.");
puts("");
}
/* run feuille in the background */
if (!settings.foreground) {
verbose(1, "making feuille run in the background...");
verbose(2, "closing input / output file descriptors...");
int pid;
if ((pid = fork()) < 0)
exit(1);
else if (pid > 0)
exit(0);
if (setsid() < 0)
exit(1);
freopen("/dev/null", "r", stdin);
freopen("/dev/null", "w", stdout);
freopen("/dev/null", "w", stderr);
}
/* pledge stage 2 */
#ifdef __OpenBSD__
pledge("stdio proc inet", NULL);
#endif
/* create a thread pool for incoming connections */
verbose(1, "initializing worker pool...");
int pid;
for (int i = 1; i <= settings.worker_count; i++) {
if ((pid = fork()) == 0) {
verbose(2, " worker n. %d...", i);
pid = getpid();
/* feed the random number god */
srand(time(0) + pid);
/* accept loop */
int connection;
while ((connection = accept_connection(server))) {
verbose(1, "--- new incoming connection. connection ID: %d:%d ---", pid, time(0));
char *paste = NULL;
char *id = NULL;
char *url = NULL;
/* read paste from connection */
verbose(1, "reading paste from incoming connection...");
if ((paste = read_paste(connection)) != NULL) {
/* generate random ID */
verbose(1, "done.");
verbose(2, "generating a random ID...");
if ((id = generate_id(settings.id_length)) != NULL) {
/* write paste to disk */
verbose(2, "done.");
verbose(1, "writing paste `%s' to disk...", id);
if (write_paste(paste, id) == 0) {
/* create URL */
verbose(1, "done.");
verbose(2, "making the right URL...");
if ((url = create_url(id)) != NULL) {
verbose(2, "done.", url);
verbose(1, "sending the link to the client...");
send_response(connection, url);
verbose(1, "All done.");
} else {
error("error while making a valid URL.");
send_response(connection, "Could not create your paste URL.\nPlease try again later.\n");
}
} else {
error("error while writing paste to disk.");
send_response(connection, "Could not write your paste to disk.\nPlease try again later.\n");
}
} else {
error("error while generating a random ID.");
send_response(connection, "Could not generate your paste ID.\nPlease try again later.\n");
}
} else {
if (errno == EFBIG)
send_response(connection, "File too big.\n");
if (errno == ENOENT)
send_response(connection, "Timeout'd.\n");
error("error %d while reading paste from incoming connection.", errno);
}
/* free resources */
free(paste);
free(id);
free(url);
/* close connection */
close_connection(connection);
}
} else if (pid < 0)
die(errno, "Could not initialize worker n. %d: %s\n", i, strerror(errno));
}
/* pledge stage 3 */
#ifdef __OpenBSD__
pledge("stdio", NULL);
#endif
sleep(1);
verbose(1, "all workers have been initialized.");
verbose(1, "beginning to accept incoming connections.");
/* wait for children to finish */
while(wait(0) > 0);
close(server);
return 0;
}

36
feuille.h Normal file
View file

@ -0,0 +1,36 @@
/*
* feuille.h
* feuille.c header declarations.
*
* Copyright (c) 2022
* Tom MTT. <tom@heimdall.pm>
*
* This file is licensed under the 3-Clause BSD License.
* You should have received a copy of the 3-Clause BSD License
* along with this program. If not, see
* <https://basedwa.re/tmtt/feuille/src/branch/main/LICENSE>.
*/
#ifndef _FEUILLE_H_
#define _FEUILLE_H_
typedef struct Settings {
char *address;
char *url;
char *output;
char *user;
unsigned char id_length;
unsigned short worker_count;
unsigned short port;
unsigned int timeout; /* seconds */
unsigned long max_size; /* bytes */
unsigned long buffer_size; /* bytes */
char verbose;
char foreground;
} Settings;
extern Settings settings;
#endif

240
server.c Normal file
View file

@ -0,0 +1,240 @@
/*
* server.c
* Server handling.
*
* Copyright (c) 2022
* Tom MTT. <tom@heimdall.pm>
*
* This file is licensed under the 3-Clause BSD License.
* You should have received a copy of the 3-Clause BSD License
* along with this program. If not, see
* <https://basedwa.re/tmtt/feuille/src/branch/main/LICENSE>.
*/
#include "server.h"
#include <arpa/inet.h> /* for inet_pton */
#include <errno.h> /* for errno, EFBIG, ENOENT */
#include <netinet/in.h> /* for sockaddr_in, sockaddr_in6, htons */
#include <stdio.h> /* for puts */
#include <stdlib.h> /* for free, NULL, malloc, realloc */
#include <string.h> /* for strcmp, strlen */
#include <strings.h> /* for bzero */
#include <syslog.h> /* for syslog, LOG_WARNING */
#include <sys/socket.h> /* for setsockopt, bind, socket, AF_INET */
#include <sys/time.h> /* for timeval */
#include <unistd.h> /* for close */
#include "feuille.h" /* for Settings, settings */
#include "util.h" /* for verbose */
/**
* Initialize the server socket.
* -> the actual socket.
*/
int initialize_server()
{
int server;
/* initialize socket IPv4 and IPv6 addresses */
verbose(3, "initializing address structs...");
struct sockaddr_in server_address_v4;
bzero(&server_address_v4, sizeof(server_address_v4));
int ipv6_only = 1;
struct sockaddr_in6 server_address_v6;
bzero(&server_address_v6, sizeof(server_address_v6));
if (strcmp(settings.address, "*") == 0) {
settings.address = "::";
ipv6_only = 0;
}
/* dirty hack to detect and convert IPv4 / IPv6 addresses */
verbose(3, "detecting address family...");
if (inet_pton(AF_INET, settings.address, &server_address_v4.sin_addr) == 1) {
verbose(3, "IPv4 address detected.");
/* set socket family and port */
server_address_v4.sin_family = AF_INET;
server_address_v4.sin_port = htons(settings.port);
/* create socket */
verbose(3, "creating server socket...");
if ((server = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return -1;
/* set socket options */
verbose(3, "setting socket options...");
/* reuse address when restarting feuille */
verbose(3, " SO_REUSEADDR...");
if (setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0)
return -1;
/* bind address and port */
verbose(3, "binding address on the socket...");
if (bind(server, (struct sockaddr *)&server_address_v4, sizeof(server_address_v4)) < 0)
return -1;
} else if (inet_pton(AF_INET6, settings.address, &server_address_v6.sin6_addr) == 1) {
verbose(3, "IPv6 address detected.");
/* set socket family and port */
server_address_v6.sin6_family = AF_INET6;
server_address_v6.sin6_port = htons(settings.port);
/* create socket */
verbose(3, "creating server socket...");
if ((server = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
return -1;
/* set socket options */
verbose(3, "setting socket options...");
/* reuse address when restarting feuille */
verbose(3, " SO_REUSEADDR...");
if (setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0)
return -1;
/* Enable dual-stack mode on supported platforms */
#ifndef __OpenBSD__
verbose(3, " IPV6_V6ONLY...");
if (setsockopt(server, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only)) < 0)
return -1;
#else
if (ipv6_only == 0) {
puts("");
syslog(LOG_WARNING, "dual-stack mode is disabled on OpenBSD.");
syslog(LOG_WARNING, "feuille will only listen on the `::' IPv6 address.");
puts("");
}
#endif
/* bind address and port */
verbose(3, "binding address on the socket...");
if (bind(server, (struct sockaddr *)&server_address_v6, sizeof(server_address_v6)) < 0)
return -1;
} else
return -1;
/* start listening to incoming connections */
verbose(3, "starting to listen on the socket...");
if (listen(server, 1) < 0)
return -1;
return server;
}
/**
* Accept incoming connections.
* socket: the server socket.
* -> the socket associated with the connection.
*/
int accept_connection(int socket)
{
/* accept the connection */
/* TODO: maybe retrieve IP address for logging? *maybe* */
int connection = accept(socket, (struct sockaddr *)NULL, NULL);
/* set the timeout for the connection */
struct timeval timeout = { settings.timeout, 0 };
if (setsockopt(connection, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0)
return -1;
if (setsockopt(connection, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0)
return -1;
return connection;
}
/**
* Close a connection.
* connection: the socket associated with the connection.
*/
void close_connection(int connection)
{
/* prevent reading / writing to the socket */
shutdown(connection, SHUT_RDWR);
/* close the socket */
close(connection);
}
/**
* Read the incoming data from a connection.
* connection: the socket associated with the connection.
* -> the string containing all data sent, or NULL if an error occured. Needs to be freed.
*/
char *read_paste(int connection)
{
unsigned long buffer_size = settings.buffer_size;
unsigned long total_size = 0;
/* allocate buffer to store the data */
char *buffer;
if ((buffer = malloc((buffer_size + 1) * sizeof(char))) == NULL)
return NULL;
/* read all data until EOF is received, or max file size is reached, or the socket timeouts... */
/* each time, the data is appended to the buffer, once it's been reallocated a larger size */
long size;
while ((size = recv(connection, buffer + total_size, buffer_size - total_size, 0)) > 0) {
total_size += size;
/* have we reached max file size? */
if (total_size >= settings.max_size) {
/* yup, free the buffer and return an error */
free(buffer);
errno = EFBIG;
return NULL;
}
/* have we reached the end of the buffer? */
if (total_size == buffer_size) {
/* yup, increase the buffer size */
buffer_size += settings.buffer_size;
/* reallocate the buffer with a larger size */
void *tmp;
if ((tmp = realloc(buffer, (buffer_size + 1) * sizeof(char))) == NULL) {
free(buffer);
return NULL;
}
buffer = tmp;
}
}
/* is the buffer empty? */
if (total_size == 0) {
/* yup, free the buffer and return an error */
free(buffer);
errno = ENOENT;
return NULL;
}
/* end the buffer with a null byte */
buffer[total_size] = 0;
return buffer;
}
/**
* Send a response to the client.
* connection: the socket associated with the connection.
* data: the string to be sent.
* -> -1 on error, number of bytes sent on success.
*/
int send_response(int connection, char *data) {
return send(connection, data, strlen(data), 0);
}

27
server.h Normal file
View file

@ -0,0 +1,27 @@
/*
* server.h
* server.c header declarations.
*
* Copyright (c) 2022
* Tom MTT. <tom@heimdall.pm>
*
* This file is licensed under the 3-Clause BSD License.
* You should have received a copy of the 3-Clause BSD License
* along with this program. If not, see
* <https://basedwa.re/tmtt/feuille/src/branch/main/LICENSE>.
*/
#ifndef _SERVER_H_
#define _SERVER_H_
#include "feuille.h"
int initialize_server();
int accept_connection(int);
void close_connection(int);
char *read_paste(int);
int send_response(int, char *);
#endif

84
util.c Normal file
View file

@ -0,0 +1,84 @@
/*
* util.c
* Common utils.
*
* Copyright (c) 2022
* Tom MTT. <tom@heimdall.pm>
*
* This file is licensed under the 3-Clause BSD License.
* You should have received a copy of the 3-Clause BSD License
* along with this program. If not, see
* <https://basedwa.re/tmtt/feuille/src/branch/main/LICENSE>.
*/
#define _DEFAULT_SOURCE
#include "util.h"
#include <stdarg.h> /* for va_end, va_list, va_start */
#include <stdio.h> /* for snprintf, vsnprintf, vfprintf, BUFSIZ, stderr */
#include <stdlib.h> /* for exit */
#include <syslog.h> /* for syslog, LOG_DEBUG, LOG_ERR */
#include <unistd.h> /* for getpid */
#include "feuille.h" /* for Settings, settings */
/* reuse that buffer for syslogs */
char syslog_buffer[BUFSIZ];
/**
* Die with an error message.
* exit_code: the exit code to be used.
* string: the string to be displayed. Printf format is supported.
*/
void die(int exit_code, char *string, ...)
{
va_list ap;
va_start(ap, string);
vfprintf(stderr, string, ap);
va_end(ap);
exit(exit_code);
}
/**
* Send an error message to syslog.
* string: the string to be displayed. Printf format is supported.
*/
void error(char *string, ...)
{
va_list ap;
va_start(ap, string);
/* prepend the PID of the current thread */
int length = snprintf(syslog_buffer, sizeof(syslog_buffer), "ERROR[%d]: ", getpid());
/* send the string to syslog */
vsnprintf(syslog_buffer + length, sizeof(syslog_buffer) - length, string, ap);
syslog(LOG_ERR, "%s", syslog_buffer);
va_end(ap);
}
/**
* Send a message to syslog if in verbose mode.
* string: the string to be displayed. Printf format is supported.
*/
void verbose(int level, char *string, ...)
{
if (settings.verbose < level)
return;
va_list ap;
va_start(ap, string);
/* prepend the PID of the current thread */
int length = snprintf(syslog_buffer, sizeof(syslog_buffer), "DEBUG%d[%d]: ", level, getpid());
/* send the string to syslog */
vsnprintf(syslog_buffer + length, sizeof(syslog_buffer) - length, string, ap);
syslog(LOG_DEBUG, "%s", syslog_buffer);
va_end(ap);
}

21
util.h Normal file
View file

@ -0,0 +1,21 @@
/*
* util.h
* util.c header declarations.
*
* Copyright (c) 2022
* Tom MTT. <tom@heimdall.pm>
*
* This file is licensed under the 3-Clause BSD License.
* You should have received a copy of the 3-Clause BSD License
* along with this program. If not, see
* <https://basedwa.re/tmtt/feuille/src/branch/main/LICENSE>.
*/
#ifndef _UTIL_H_
#define _UTIL_H_
void die(int, char *, ...);
void error(char *, ...);
void verbose(int, char *, ...);
#endif