Added dynamic pages with dynamic linking during runtime

This commit is contained in:
Nate Choe
2022-02-14 12:08:09 -06:00
parent 8df741a078
commit 6825f12163
28 changed files with 435 additions and 84 deletions

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@ work/*.o
build/swebs
example/logs
example/swebs.pid
example/library.so

View File

@@ -1,21 +1,25 @@
SRC = $(wildcard src/*.c)
OBJ = $(subst .c,.o,$(subst src,work,$(SRC)))
LIBS = -pthread -pie -lrt $(shell pkg-config --libs gnutls)
CFLAGS := -O2 -pipe -Wall -Wpedantic -Werror -ansi -D_POSIX_C_SOURCE=200809L
CFLAGS += -Isrc/include -fpie $(shell pkg-config --cflags gnutls)
CFLAGS := -O2 -pipe -Wall -Wpedantic -ansi
CFLAGS += -Isrc/ -fpie -D_POSIX_C_SOURCE=200809L $(shell pkg-config --cflags gnutls)
INSTALLDIR := /usr/sbin
HEADERDIR := /usr/include/
INCLUDE_DIRECTORY := swebs
OUT = swebs
build/$(OUT): $(OBJ)
$(CC) $(OBJ) -o build/$(OUT) $(LIBS)
work/%.o: src/%.c $(wildcard src/include/*.h)
work/%.o: src/%.c $(wildcard src/swebs/*.h)
$(CC) $(CFLAGS) $< -c -o $@
install: build/$(OUT)
cp build/$(OUT) $(INSTALLDIR)/$(OUT)
cp -r src/$(INCLUDE_DIRECTORY) $(HEADERDIR)/$(INCLUDE_DIRECTORY)
if ! id swebs >> /dev/null 2>&1; then useradd -M swebs; fi
uninstall: $(INSTALLDIR)/$(OUT)
rm $(INSTALLDIR)/$(OUT)
rm -r $(HEADERDIR)/$(INCLUDE_DIRECTORY)
if id swebs >> /dev/null 2>&1; then userdel swebs; fi

View File

@@ -0,0 +1,62 @@
# Part 1: The problem
A static webpage is easy. Just tell swebs to read a few files given a few http paths and you're done. Dyanmic pages are harder. There are a few solutions, you could execute a program and return stdout (too slow), encourage each website to create their own forks of swebs and make it really easy to directly modify the source code to add a dynamic page (inelegant), write a php interpreter (bloated), execute a program the user creates and keep it running while the web server communicates with it (what's the point of writing a web server if the user does all the work of setting up a server and responding to requests?)
# Part 2: The solution
The solution I thought of was to dynamically load a C library the user optionally writes that creates pages. The user is responsible for writing C code that generates pages, and swebs is responsible for parsing requests and asking for said pages. The library the user creates must be a shared object file, and define this function:
```int getResponse(Request *request, Response *response)```
```getResponse``` returns the HTTP response code of that request.
```Request``` and ```Response``` are defined in ```<swebs/types.h>```. ```getResponse()```. ```<swebs/types.h>``` is guarunteed to be included by ```<swebs/swebs.h>```, where ```getResponse()``` is defined.
The specific library to use is set with the ```library``` global variable in a sitefile.
The various data types important to you in this scenario are:
```
typedef struct {
char *field;
char *value;
} Field;
/*HTTP field*/
typedef struct {
long fieldCount;
Field *fields;
} Request;
/*HTTP request, pretty self explanatory*/
typedef enum {
FILE_KNOWN_LENGTH,
/* A file where the total length is known (i.e. a file on disk) */
FILE_UNKNOWN_LENGTH,
/* A file where the total length is unknown (i.e. a pipe) */
BUFFER,
/* A buffer stored in memory. free() will be called on said buffer. */
DEFAULT
/* The default response for the response code */
} ResponseType;
typedef struct {
int fd;
size_t len;
/* This field is sometimes optional */
} File;
typedef struct {
void *data;
/* This data will be freed. */
size_t len;
} Buffer;
typedef struct {
ResponseType type;
union {
File file;
Buffer buffer;
} response;
} Response;
```

View File

@@ -28,9 +28,11 @@ sitefiles also allow comments with #
# Part 4: Global variables
* ```port``` - the port to use. Note that this is a global variable, and so one instance of swebs cannot use multiple ports.
* ```transport``` - the type of connection to use. One of:
* TCP (default)
* TLS
* ```key``` - The filepath of the private key to use if transport == TLS
* ```cert``` - The filepath of the certificate to use if transport == TLS
* ```timeout``` - The amount of time to wait for data before closing the connection in ms
* ```library``` - the path of a library that is linked in during runtime if ```DYNAMIC_LINKED_PAGES```is set.

2
example/Makefile Normal file
View File

@@ -0,0 +1,2 @@
library.so: library.c
$(CC) $< -o $@ -shared -I../src/

14
example/library.c Normal file
View File

@@ -0,0 +1,14 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <swebs/swebs.h>
int getResponse(Request *request, Response *response) {
char *str = "<h1>Hello world!</h1>";
response->type = BUFFER;
response->response.buffer.data = malloc(30);
strcpy(response->response.buffer.data, str);
response->response.buffer.len = strlen(str);
return 200;
}

View File

@@ -1,3 +1,3 @@
#!/bin/sh
../build/swebs -P swebs.pid -s sitefile -o logs -p 8000 -b 100 -j2
../build/swebs -p swebs.pid -s sitefile -o logs -b 100 -j2

View File

@@ -1,7 +1,10 @@
define port 8000
#define transport TLS
define key domain.key
define cert domain.crt
define timeout 2000
define library ./library.so
set host localhost:8000
read / site/index.html
@@ -10,7 +13,7 @@ throw /blog/forbidden 403
exec /blog/2022-1-31 site/blog/blog/2022-1-31.sh
read /blog/.* site/blog/
#/blog/2021-1-25.html turns into site/blog//blog/2021-1-25.html
exec /dynamic site/build-page.sh
linked /library
set host 127.0.0.1:8000
read / site/easteregg.html
read /egg.png site/egg.png

View File

@@ -19,11 +19,11 @@
#include <stdlib.h>
#include <string.h>
#include <util.h>
#include <runner.h>
#include <sitefile.h>
#include <responses.h>
#include <connections.h>
#include <swebs/util.h>
#include <swebs/runner.h>
#include <swebs/sitefile.h>
#include <swebs/responses.h>
#include <swebs/connections.h>
int newConnection(Stream *stream, Connection *ret) {
struct timespec currentTime;

42
src/dynamic.c Normal file
View File

@@ -0,0 +1,42 @@
/*
swebs - a simple web server
Copyright (C) 2022 Nate Choe
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <swebs/config.h>
#if DYNAMIC_LINKED_PAGES
#include <dlfcn.h>
#include <swebs/dynamic.h>
int (*loadGetResponse(char *library))(Request *, Response *) {
void *handle = dlopen(library, RTLD_LAZY);
int (*ret)(Request *, Response *);
*(void **) &ret = dlsym(handle, "getResponse");
/* Dirty hack to make this code ANSI C compliant*/
return ret;
}
#else
#include <stdlib.h>
#include <swebs/types.h>
int (*loadGetResponse(char *library))(Request *, Response *) {
/* The code should NEVER reach this state */
exit(EXIT_FAILURE);
}
#endif

View File

@@ -24,10 +24,10 @@
#include <unistd.h>
#include <pthread.h>
#include <util.h>
#include <runner.h>
#include <sockets.h>
#include <sitefile.h>
#include <swebs/util.h>
#include <swebs/runner.h>
#include <swebs/sockets.h>
#include <swebs/sitefile.h>
static void daemonize(char *pidfile) {
pid_t pid;
@@ -66,7 +66,6 @@ int main(int argc, char **argv) {
char *logout = "/var/log/swebs.log";
char *sitefile = NULL;
int processes = sysconf(_SC_NPROCESSORS_ONLN) + 1;
uint16_t port = 443;
int backlog = 100;
char shouldDaemonize = 0;
char *pidfile = "/run/swebs.pid";
@@ -79,7 +78,7 @@ int main(int argc, char **argv) {
int i;
for (;;) {
int c = getopt(argc, argv, "o:j:s:p:b:c:BP:hl");
int c = getopt(argc, argv, "o:j:s:b:c:Bp:hl");
if (c == -1)
break;
switch (c) {
@@ -92,16 +91,13 @@ int main(int argc, char **argv) {
case 's':
sitefile = optarg;
break;
case 'p':
port = atoi(optarg);
break;
case 'b':
backlog = atoi(optarg);
break;
case 'B':
shouldDaemonize = 1;
break;
case 'P':
case 'p':
pidfile = optarg;
break;
case 'l':
@@ -126,10 +122,9 @@ NULL
" -o [out] Set the log file (default: /var/log/swebs.log)",
" -j [cores] Use that many cores (default: $(nproc)+1)",
" -s [site file] Use that site file (required)",
" -p [port] Set the port (default: 443)",
" -b [backlog] Set the socket backlog (default: 100)",
" -B Run swebs in the background and daemonize",
" -P [pidfile] Specify PID file if daemonizing",
" -p [pidfile] Specify PID file if daemonizing",
" (defualt: /run/swebs.pid)",
" -l Show some legal details",
" -h Show this help message",
@@ -155,11 +150,11 @@ NULL
switch (site->type) {
case TCP: default:
listener = createListener(TCP, port, backlog);
listener = createListener(TCP, site->port, backlog);
break;
case TLS:
initTLS();
listener = createListener(TLS, port, backlog,
listener = createListener(TLS, site->port, backlog,
site->key, site->cert);
break;
}

View File

@@ -26,8 +26,8 @@
#include <sys/stat.h>
#include <sys/wait.h>
#include <responses.h>
#include <responseutil.h>
#include <swebs/responses.h>
#include <swebs/responseutil.h>
static int readResponse(Connection *conn, char *path) {
int fd = -1;
@@ -151,6 +151,38 @@ error:
return 1;
}
static int linkedResponse(Connection *conn,
int (*getResponse)(Request *request, Response *response)) {
Request request;
Response response;
int code;
int ret;
request.fieldCount = conn->fieldCount;
request.fields = conn->fields;
code = getResponse(&request, &response);
switch (response.type) {
case FILE_KNOWN_LENGTH:
return sendKnownPipe(conn->stream, getCode(code),
response.response.file.fd,
response.response.file.len);
case FILE_UNKNOWN_LENGTH:
return sendPipe(conn->stream, getCode(code),
response.response.file.fd);
case BUFFER:
ret = sendBinaryResponse(conn->stream, getCode(code),
response.response.buffer.data,
response.response.buffer.len);
free(response.response.buffer.data);
return ret;
case DEFAULT:
return sendErrorResponse(conn->stream, getCode(code));
}
return 1;
}
static int fullmatch(regex_t *regex, char *str) {
regmatch_t match;
if (regexec(regex, str, 1, &match, 0))
@@ -190,6 +222,20 @@ int sendResponse(Connection *conn, Sitefile *site) {
sendErrorResponse(conn->stream,
site->content[i].arg);
break;
case LINKED:
#if DYNAMIC_LINKED_PAGES
if (!site->getResponse)
sendErrorResponse(conn->stream,
ERROR_500);
else
linkedResponse(conn,
site->getResponse);
#else
/* Unreachable state */
sendErrorResponse(conn->stream,
ERROR_500);
#endif
break;
default:
sendErrorResponse(conn->stream,
ERROR_500);

View File

@@ -22,7 +22,7 @@
#include <unistd.h>
#include <responseutil.h>
#include <swebs/responseutil.h>
#define CONST_FIELDS "Server: swebs/0.1\r\n"
@@ -55,7 +55,7 @@ static int sendStreamValist(Stream *stream, char *format, ...) {
va_start(ap, format);
vsprintf(data, format, ap);
if (resilientSend(stream, data, len) < len) {
if (resilientSend(stream, data, len)) {
free(data);
return 1;
}
@@ -124,20 +124,9 @@ int sendHeader(Stream *stream, const char *status, size_t len) {
int sendSeekableFile(Stream *stream, const char *status, int fd) {
off_t len;
size_t totalSent = 0;
len = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
sendHeader(stream, status, len);
for (;;) {
char buffer[1024];
ssize_t inBuffer = read(fd, buffer, sizeof(buffer));
if (inBuffer < 0)
return 1;
if (inBuffer == 0)
return totalSent != len;
if (resilientSend(stream, buffer, inBuffer))
return 1;;
}
return sendKnownPipe(stream, status, fd, len);
}
int sendPipe(Stream *stream, const char *status, int fd) {
@@ -177,3 +166,18 @@ error:
sendErrorResponse(stream, ERROR_500);
return 1;
}
int sendKnownPipe(Stream *stream, const char *status, int fd, size_t len) {
size_t totalSent = 0;
sendHeader(stream, status, len);
for (;;) {
char buffer[1024];
ssize_t inBuffer = read(fd, buffer, sizeof(buffer));
if (inBuffer < 0)
return 1;
if (inBuffer == 0)
return totalSent != len;
if (resilientSend(stream, buffer, inBuffer))
return 1;
}
}

View File

@@ -22,9 +22,9 @@
#include <poll.h>
#include <unistd.h>
#include <runner.h>
#include <sitefile.h>
#include <connections.h>
#include <swebs/runner.h>
#include <swebs/sitefile.h>
#include <swebs/connections.h>
void *runServer(RunnerArgs *args) {
Sitefile *site = args->site;

View File

@@ -20,9 +20,18 @@
#include <string.h>
#include <stdlib.h>
#include <util.h>
#include <sitefile.h>
#include <responseutil.h>
#include <swebs/config.h>
#include <swebs/util.h>
#include <swebs/sitefile.h>
#include <swebs/responseutil.h>
#if DYNAMIC_LINKED_PAGES
#include <swebs/dynamic.h>
#endif
/*
* This if isn't technically necessary, but it generates warnings, which is
* good.
* */
typedef enum {
SUCCESS,
@@ -173,6 +182,7 @@ Sitefile *parseSitefile(char *path) {
int argc;
char **argv;
int allocatedLength = 50;
char gotPort = 0;
Sitefile *ret;
if (file == NULL)
@@ -186,6 +196,9 @@ Sitefile *parseSitefile(char *path) {
ret->timeout = 0;
ret->size = 0;
ret->content = malloc(allocatedLength * sizeof(SiteCommand));
#if DYNAMIC_LINKED_PAGES
ret->getResponse = NULL;
#endif
if (ret->content == NULL) {
free(ret);
return NULL;
@@ -194,6 +207,8 @@ Sitefile *parseSitefile(char *path) {
ReturnCode status = getCommand(file, &argc, &argv);
switch (status) {
case FILE_END:
if (!gotPort)
goto nterror;
fclose(file);
return ret;
case ERROR: case LINE_END:
@@ -226,12 +241,26 @@ Sitefile *parseSitefile(char *path) {
else
goto error;
}
else if (strcmp(argv[1], "port") == 0) {
ret->port = atoi(argv[2]);
gotPort = 1;
}
else if (strcmp(argv[1], "key") == 0)
ret->key = strdup(argv[2]);
else if (strcmp(argv[1], "cert") == 0)
ret->cert = strdup(argv[2]);
else if (strcmp(argv[1], "timeout") == 0)
ret->timeout = atoi(argv[2]);
else if (strcmp(argv[1], "library") == 0) {
#if DYNAMIC_LINKED_PAGES
ret->getResponse = loadGetResponse(argv[2]);
#else
fprintf(stderr,
"This version of swebs has no dynamic page support\n"
);
exit(EXIT_FAILURE);
#endif
}
else
goto error;
continue;
@@ -245,31 +274,37 @@ Sitefile *parseSitefile(char *path) {
goto error;
ret->content = newcontent;
}
if (argc < 3)
goto error;
if (regcomp(&ret->content[ret->size].path, argv[1],
cflags))
goto error;
if (strcmp(argv[0], "read") == 0) {
if (argc < 3)
goto error;
ret->content[ret->size].arg = strdup(argv[2]);
if (ret->content[ret->size].arg == NULL)
goto error;
ret->content[ret->size].command = READ;
}
else if (strcmp(argv[0], "exec") == 0) {
if (argc < 3)
goto error;
ret->content[ret->size].arg = strdup(argv[2]);
if (ret->content[ret->size].arg == NULL)
goto error;
ret->content[ret->size].command = EXEC;
}
else if (strcmp(argv[0], "throw") == 0) {
if (argc < 3)
goto error;
ret->content[ret->size].arg = getCode(atoi(argv[2]));
if (ret->content[ret->size].arg == NULL)
goto error;
ret->content[ret->size].command = THROW;
}
else if (strcmp(argv[0], "linked") == 0)
ret->content[ret->size].command = LINKED;
else
goto error;
freeTokens(argc, argv);

View File

@@ -26,7 +26,7 @@
#include <netinet/in.h>
#include <gnutls/gnutls.h>
#include <sockets.h>
#include <swebs/sockets.h>
int initTLS() {
assert(gnutls_global_init() >= 0);

14
src/swebs/config.h Normal file
View File

@@ -0,0 +1,14 @@
/*
* This file specifies which features to enable/disable during compile time.
* 0 - disable a feature
* non-zero - enable a feature
* */
#ifndef HAVE_CONFIG
#define HAVE_CONFIG
/* HEADER GUARD, DO NOT REMOVE*/
#define DYNAMIC_LINKED_PAGES 1
#endif
/* HEADER GUARD, DO NOT REMOVE*/

View File

@@ -18,9 +18,9 @@
#ifndef HAVE_CONNECTIONS
#define HAVE_CONNECTIONS
#include <runner.h>
#include <sockets.h>
#include <sitefile.h>
#include <swebs/runner.h>
#include <swebs/sockets.h>
#include <swebs/sitefile.h>
typedef enum {
RECEIVE_REQUEST,
@@ -28,11 +28,6 @@ typedef enum {
RECEIVE_BODY
} ConnectionSteps;
typedef struct {
char *field;
char *value;
} Field;
typedef struct Connection {
Stream *stream;
ConnectionSteps progress;

25
src/swebs/dynamic.h Normal file
View File

@@ -0,0 +1,25 @@
/*
swebs - a simple web server
Copyright (C) 2022 Nate Choe
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef HAVE_DYNAMIC
#define HAVE_DYNAMIC
#include <swebs/types.h>
int (*loadGetResponse(char *))(Request *, Response *);
/* It takes the name of the library and returns getResponse() */
#endif

View File

@@ -17,8 +17,8 @@
*/
#ifndef HAVE_RESPONSES
#define HAVE_RESPONSES
#include <sitefile.h>
#include <connections.h>
#include <swebs/sitefile.h>
#include <swebs/connections.h>
int sendResponse(Connection *conn, Sitefile *site);
/* returns 1 on error, sets conn->progress to SEND_RESPONSE */

View File

@@ -17,7 +17,7 @@
*/
#ifndef HAVE_RESPONSE_UTIL
#define HAVE_RESPONSE_UTIL
#include <connections.h>
#include <swebs/sockets.h>
#define CODE_200 "200 OK"
#define ERROR_400 "400 Bad Request"
@@ -34,4 +34,5 @@ int sendErrorResponse(Stream *stream, const char *error);
int sendHeader(Stream *stream, const char *status, size_t len);
int sendSeekableFile(Stream *stream, const char *status, int fd);
int sendPipe(Stream *stream, const char *status, int fd);
int sendKnownPipe(Stream *stream, const char *status, int fd, size_t len);
#endif

View File

@@ -19,8 +19,8 @@
#define HAVE_RUNNER
#include <sys/socket.h>
#include <sitefile.h>
#include <connections.h>
#include <swebs/sitefile.h>
#include <swebs/connections.h>
typedef struct {
Sitefile *site;

View File

@@ -18,13 +18,16 @@
#ifndef HAVE_SITEFILE
#define HAVE_SITEFILE
#include <regex.h>
#include <stdint.h>
#include <util.h>
#include <swebs/types.h>
#include <swebs/config.h>
typedef enum {
READ,
EXEC,
THROW
THROW,
LINKED
} Command;
typedef struct {
@@ -42,6 +45,10 @@ typedef struct {
char *key;
char *cert;
int timeout;
uint16_t port;
#if DYNAMIC_LINKED_PAGES
int (*getResponse)(Request *, Response *);
#endif
} Sitefile;
Sitefile *parseSitefile(char *path);

View File

@@ -22,7 +22,7 @@
#include <netinet/in.h>
#include <gnutls/gnutls.h>
#include <util.h>
#include <swebs/types.h>
typedef struct {
SocketType type;

35
src/swebs/swebs.h Normal file
View File

@@ -0,0 +1,35 @@
/*
swebs - a simple web server
Copyright (C) 2022 Nate Choe
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/*
* This file is called swebs.h because it's the main thing that the end users
* are going to be interfacing with.
* */
#ifndef HAVE_SWEBS
#define HAVE_SWEBS
#include <swebs/config.h>
#if DYNAMIC_LINKED_PAGES
#include <swebs/types.h>
int getResponse(Request *request, Response *response);
/* Returns the HTTP response code, the user is responsible for writing this */
#else
#error "This version of swebs has no dynamic linked page support"
#endif
#endif

80
src/swebs/types.h Normal file
View File

@@ -0,0 +1,80 @@
/*
swebs - a simple web server
Copyright (C) 2022 Nate Choe
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef HAVE_TYPES
#define HAVE_TYPES
#include <stddef.h>
#include <swebs/config.h>
typedef enum {
TCP,
TLS
} SocketType;
typedef enum {
GET,
POST,
PUT,
HEAD,
DELETE,
PATCH,
OPTIONS,
INVALID
/*
* this indicates an invalid type of request, there is no request called
* INVALID in HTTP/1.1.
* */
} RequestType;
typedef struct {
char *field;
char *value;
} Field;
typedef struct {
long fieldCount;
Field *fields;
} Request;
typedef enum {
FILE_KNOWN_LENGTH,
FILE_UNKNOWN_LENGTH,
BUFFER,
DEFAULT
/* Return the default value for this error code */
} ResponseType;
typedef struct {
int fd;
size_t len;
/* Sometimes optional */
} File;
typedef struct {
void *data;
size_t len;
} Buffer;
typedef struct {
ResponseType type;
union {
File file;
Buffer buffer;
} response;
} Response;
#endif

View File

@@ -17,25 +17,8 @@
*/
#ifndef HAVE_UTIL
#define HAVE_UTIL
typedef enum {
TCP,
TLS
} SocketType;
typedef enum {
GET,
POST,
PUT,
HEAD,
DELETE,
PATCH,
OPTIONS,
INVALID
/*
* this indicates an invalid type of request, there is no request called
* INVALID in HTTP/1.1.
* */
} RequestType;
#include <swebs/types.h>
int initLogging(char *path);
int createLog(char *msg);

View File

@@ -21,7 +21,8 @@
#include <string.h>
#include <stdint.h>
#include <util.h>
#include <swebs/util.h>
#include <swebs/types.h>
static FILE *logs;