Add logging levels, fix a few bugs, add connection log path, use flush_buffer everywhere and handle partial flushes correctly, fix a memory leak, ping the server (to test if the connection is alive)

This commit is contained in:
Alex 2020-07-29 16:49:59 +02:00
parent 6e3db7c7f3
commit 04c876e339
Signed by: caskd
GPG Key ID: F92BA85F61F4C173
14 changed files with 254 additions and 107 deletions

View File

@ -11,7 +11,7 @@ elseif (CMAKE_BUILD_TYPE EQUAL "Release")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -Werror")
endif()
set(USE_HELPERS TRUE)
set(BUILD_HELPERS TRUE)
add_subdirectory(lib/uIRC)
add_executable(uircd
src/main.c
@ -19,6 +19,7 @@ add_executable(uircd
src/memory.c
src/misc.c
src/net.c
src/log.c
)
target_link_libraries(uircd uirc)
set_property(TARGET uircd PROPERTY C_STANDARD 99)

@ -1 +1 @@
Subproject commit e71d730947729854731689ef924effea267c90f1
Subproject commit 0f73ea68d8defe4af5fc861e69d9c314c56c80ee

View File

@ -17,85 +17,89 @@
*/
#include "filesystem.h"
int write_log(char* line, IRC_Message* mesg, Connection* conn)
int write_log(char* line, char* root, IRC_Message* mesg, Connection* conn)
{
char path[MAXPATH];
char buf[MAXLINE];
int len = 0;
if ((len = assemble_log_path(mesg, conn, path)) >= 0) {
if (mkdir_bottomup(path) == 1) {
if (snprintf(path + len, MAXPATH - len, "/out.log") == 8) {
FILE* logfile;
if ((logfile = fopen(path, "a")) == NULL) {
fprintf(stderr, "Error on opening file \"%s\" for appending. %s (%i)\n", path, strerror(errno), errno);
return 0;
} else {
if (line == NULL && mesg != NULL)
Assm_mesg(buf, mesg, MAXLINE);
if (fprintf(logfile, "%s\n", (line != NULL) ? line : buf) <= 0)
fprintf(stderr, "Error on writing to file \"%s\". %s (%i)\n", path, strerror(errno), errno);
}
fclose(logfile);
} else {
fprintf(stderr, "Couldn't append filename to path.\n");
return 0;
}
} else {
fprintf(stderr, "Failed to create directories.\n");
char path[MAXPATH], bufs[2][MAXPATH], *arrpath[6] = {0}, **ppos = arrpath;
if (root != NULL)
*(ppos++) = root;
if (snprintf(bufs[0], MAXPATH, "%s.%s", conn->data.addr, conn->data.port) <= 0)
return 0;
cleanup_path_names(bufs[0]);
*(ppos++) = bufs[0];
if ((mesg->cmd == NOTICE || mesg->cmd == PRIVMSG) && mesg->name.nick != NULL) {
if (strchr("#!&+", *mesg->args[0]) != NULL)
*(ppos++) = "channel";
else
*(ppos++) = "user";
if (snprintf(bufs[1], MAXPATH, "%s", strcmp(conn->names.nick, mesg->args[0]) ? mesg->args[0] : mesg->name.nick) <= 0)
return 0;
}
cleanup_path_names(bufs[1]);
*(ppos++) = bufs[1];
} else
*(ppos++) = "global";
if (!mkdir_bottomup(arrpath))
return 0;
*(ppos++) = "out.log";
if (!assemble_path(arrpath, path, MAXPATH))
return 0;
FILE* logfile;
if ((logfile = fopen(path, "a")) == NULL) {
LOG_ERRNO(LOG_ERROR, "Error on opening log file.", path, conn);
return 0;
} else {
fprintf(stderr, "Couldn't assemble path.\n");
if (line == NULL && mesg != NULL)
Assm_mesg(bufs[0], mesg, MAXPATH);
if (fprintf(logfile, "%s\n", (line != NULL) ? line : bufs[0]) <= 0)
LOG_ERRNO(LOG_ERROR, "Error on writing to the log file", path, conn);
}
fclose(logfile);
return 1;
}
int assemble_path(char** path, char* out, int len)
{
if (path == NULL || *path == NULL || len <= 0)
return 0;
char* orig = out;
int cprint;
for (char** i = path; i != NULL && *i != NULL && out - orig < len; i++) {
if (i != path && len - (out - orig) > 0)
*(out++) = '/';
if ((cprint = snprintf(out, len - (out - orig), "%s", *i)) > 0)
out += cprint;
else
return 0;
}
return 1;
}
int assemble_log_path(IRC_Message* mesg, Connection* conn, char* path)
int mkdir_bottomup(char** dir)
{
char* pos = path;
if ((pos += sprintf(pos, "%s.%s/%s", conn->data.addr, conn->data.port, (mesg->cmd == NOTICE || mesg->cmd == PRIVMSG) ? (strchr("#!&+", *mesg->args[0])) ? "channel" : "user" : "global")) <= path)
return 0;
if (mesg->cmd == PRIVMSG || mesg->cmd == NOTICE) {
char* orig = pos;
if ((pos += sprintf(pos, "/%s", (strcmp(conn->names.nick, mesg->args[0])) ? mesg->args[0] : mesg->name.nick)) <= orig)
return 0;
cleanup_path_names(orig + 1);
}
return pos - path; /* Returns a pointer to / before the filename for use with mkdir_bottomup */
}
int mkdir_bottomup(char* dir)
{
if (dir == NULL)
if (dir == NULL || *dir == NULL)
return -1;
char* cdelim = dir;
struct stat inf;
if ((stat(dir, &inf)) != 0) {
if (errno != ENOENT) {
fprintf(stderr, "Could not check if directory exists. %s (%i)\n", strerror(errno), errno);
return 0;
}
do {
cdelim = strchr(cdelim, '/');
if (cdelim != NULL)
*cdelim = '\0';
if (mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0 && errno != EEXIST) {
if (cdelim != NULL)
*(cdelim++) = '/';
int cprint, len = MAXPATH;
char buf[len], *bufp = buf;
for (char** i = dir; i != NULL && *i != NULL && bufp - buf < len; i++) {
if (i != dir && len - (bufp - buf) > 0)
*(bufp++) = '/';
if ((cprint = snprintf(bufp, len - (bufp - buf), "%s", *i)) > 0) {
if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0 && errno != EEXIST)
return 0;
}
if (cdelim != NULL)
*(cdelim++) = '/';
} while (cdelim != NULL);
bufp += cprint;
} else
return 0;
}
return 1;
}
void cleanup_path_names(char* str)
int cleanup_path_names(char* name)
{
for (; *str; str++) {
if (isalpha(*str))
*str = tolower(*str);
else if (strchr(".+-_#&", *str) == NULL && !isdigit(*str))
*str = '_';
if (name == NULL)
return 0;
if (!strcmp("..", name) || !strcmp(".", name))
*name = '_';
for (; *name; name++) {
if (isalpha(*name))
*name = tolower(*name);
else if (strchr(".+-_#&", *name) == NULL && !isdigit(*name))
*name = '_';
}
return 1;
}

View File

@ -20,6 +20,7 @@
#include "../lib/uIRC/include/mappings.h"
#include "global.h"
#include "structs.h"
#include "log.h"
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
@ -29,8 +30,8 @@
#ifndef _UIRCD_INCLUDED_FS
#define _UIRCD_INCLUDED_FS
void cleanup_path_names(char* str);
int assemble_log_path(IRC_Message* mesg, Connection* conn, char* path);
int write_log(char* line, IRC_Message* mesg, Connection* conn);
int mkdir_bottomup(char* dir);
int cleanup_path_names(char* name);
int assemble_path(char** path, char* out, int len);
int write_log(char* line, char* root, IRC_Message* mesg, Connection* conn);
int mkdir_bottomup(char** dir);
#endif

View File

@ -28,6 +28,7 @@
extern sig_atomic_t volatile actcon;
extern int totcon;
extern int loglevel;
extern Connection cons[MAXCONN];
extern char sendbuf[MAXLINE + 1];
extern int sendbuflen;

49
src/log.c Normal file
View File

@ -0,0 +1,49 @@
/*
* This file is part of uIRCd. (https://git.redxen.eu/caskd/uIRCd)
* Copyright (c) 2019, 2020 Alex-David Denes
*
* uIRCd 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
* any later version.
*
* uIRCd 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 uIRCd. If not, see <https://www.gnu.org/licenses/>.
*/
#include "log.h"
int plog(int level, char* mesg, char* subject, Connection* conn, int line, char* file, bool use_errno)
{
if (level > loglevel)
return 0;
char* prf = NULL;
switch (level) {
case (LOG_DEBUG): prf = "[D]"; break;
case (LOG_INFO): prf = "[I]"; break;
case (LOG_WARN): prf = "[W]"; break;
case (LOG_ERROR): prf = "[E]"; break;
case (LOG_FATAL): prf = "[F]"; break;
default: prf = "[?]"; break;
}
fprintf(stderr, "%s ", prf);
if (mesg != NULL)
fprintf(stderr, "%s ", mesg);
else
fprintf(stderr, "An error occured! ");
if (use_errno && errno)
fprintf(stderr, "%s. ", strerror(errno));
if (subject != NULL)
fprintf(stderr, "S: \"%s\" ", subject);
if (conn != NULL)
fprintf(stderr, "(Connection: %s:%s on socket %i) ", conn->data.addr, conn->data.port, conn->fds[0]);
if (level == LOG_DEBUG)
fprintf(stderr, "LINE: %i, FILE: %s", line, file);
putc('\n', stderr);
return 1;
}

38
src/log.h Normal file
View File

@ -0,0 +1,38 @@
/*
* This file is part of uIRCd. (https://git.redxen.eu/caskd/uIRCd)
* Copyright (c) 2019, 2020 Alex-David Denes
*
* uIRCd 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
* any later version.
*
* uIRCd 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 uIRCd. If not, see <https://www.gnu.org/licenses/>.
*/
#include "global.h"
#include "structs.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#ifndef _UIRCD_INCLUDED_LOG
#define _UIRCD_INCLUDED_LOG
#define LOG_FATAL 0
#define LOG_ERROR 1
#define LOG_WARN 2
#define LOG_INFO 3
#define LOG_DEBUG 4
#define LOG(level, mesg, subj, conn) plog(level, mesg, subj, conn, __LINE__, __FILE__, false)
#define LOG_ERRNO(level, mesg, subj, conn) plog(level, mesg, subj, conn, __LINE__, __FILE__, true)
int plog(int level, char* mesg, char* subject, Connection* conn, int line, char* file, bool use_errno);
#endif

View File

@ -23,11 +23,12 @@ int totcon = 0;
Connection cons[MAXCONN] = {0};
char sendbuf[MAXLINE + 1];
int sendbuflen;
int loglevel = LOG_FATAL;
int main(int argc, char* argv[])
{
char c, *pos;
while ((c = getopt(argc, argv, "c:l:j:Vvh")) != -1) {
char c, *pos, *root = NULL;
while ((c = getopt(argc, argv, "C:m:c:l:j:V:vh")) != -1) {
switch (c) {
case 'c': {
cons[totcon].data.chans = point_after(optarg, ',');
@ -45,10 +46,12 @@ int main(int argc, char* argv[])
cons[totcon++].connstate = CONN_PENDING;
break;
}
case 'l': break; // TODO: Reserved for log path
case 'l': root = optarg; break;
case 'm': break; // TODO: Reserved for quit message
case 'C': break; // TODO: Reserved for config file path
case 'V': break; // TODO: Reserved for verbose output
case 'V':
loglevel = atoi(optarg);
break;
case 'v':
printf("uIRCd version %s\n", VERSION);
return EXIT_SUCCESS;
@ -56,54 +59,78 @@ int main(int argc, char* argv[])
}
}
if (totcon < 1) {
fprintf(stderr, "No connection provided.\n");
LOG(LOG_FATAL, "No connection was provided.", NULL, NULL);
return EXIT_FAILURE;
}
signal(SIGINT, stop_loop);
signal(SIGTERM, stop_loop);
IRC_Message buffer = {0};
int res;
time_t tmp;
do {
for (int t = 0; t < totcon; t++) {
if (cons[t].connstate == CONN_ACTIVE && (tmp = time(NULL)) - cons[t].lastping >= 10) {
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_PING("UIRC PONG", NULL), sizeof(sendbuf))) > 0) {
LOG(LOG_DEBUG, "Sending ping...", NULL, &cons[t]);
flush_buffer(&cons[t]);
cons[t].lastping = tmp;
}
}
if (!connection_manager(&cons[t]))
continue;
switch ((res = readline_mem(&cons[t].buf))) {
case READLINE_LINE_READY: {
LOG(LOG_DEBUG, "Line ready.", cons[t].buf.cur_line, &cons[t]);
char origmsg[513];
strncpy(origmsg, cons[t].buf.cur_line, 512);
if (Tok_mesg(cons[t].buf.cur_line, &buffer) == 1) {
handle_irc_message(&buffer, cons[t].fds[0], &cons[t]);
write_log(origmsg, &buffer, cons);
LOG(LOG_DEBUG, "Tokenized message successfully.", NULL, &cons[t]);
handle_irc_message(&buffer, &cons[t]);
write_log(origmsg, root, &buffer, cons);
} else
fprintf(stderr, "Received invalid IRC message (see RFC2812)\n");
LOG(LOG_WARN, "Received invalid IRC message (see RFC2812)", origmsg, &cons[t]);
break;
}
case READLINE_TRYAGAIN: break;
case READLINE_INTERRUPTED: {
LOG(LOG_DEBUG, "Caught interrupt. Shutting down.", NULL, &cons[t]);
actcon = 0;
break;
}
default: {
if (close(cons[t].fds[0]))
fprintf(stderr, "Error on closing socket %i. %s (%i)\n", cons[t].fds[0], strerror(errno), errno);
cons[t].fds[0] = -1;
cons[t].connstate = CONN_CLOSED;
char* resp = NULL;
switch (res) {
case READLINE_EOF: resp = "The connection was closed."; break;
case READLINE_BUFINIT_FAIL: resp = "The buffer could not be initialised."; break;
case READLINE_REALLOC_FAIL: resp = "The buffer could not be extended or reallocated."; break;
case READLINE_SOCKET_FAIL: resp = "The socket was invalid or closed."; break;
case READLINE_POLL_FAIL: resp = "The socket could not be polled."; break;
default: resp = "An unknown error happened."; break;
case READLINE_EOF:
cons[t].connstate = CONN_PENDING;
LOG(LOG_INFO, "Encountered EOF. Reconnecting...", NULL, &cons[t]);
break;
case READLINE_BUFINIT_FAIL:
cons[t].connstate = CONN_CLOSING;
LOG_ERRNO(LOG_ERROR, "Buffer initialisation failed! Disabling connection.", NULL, &cons[t]);
break;
case READLINE_REALLOC_FAIL:
cons[t].connstate = CONN_CLOSING;
LOG_ERRNO(LOG_ERROR, "The buffer couldn't be reallocated. Disabling connection.", NULL, &cons[t]);
break;
case READLINE_SOCKET_FAIL:
cons[t].connstate = CONN_PENDING;
LOG_ERRNO(LOG_WARN, "Socket was invalid or closed. Reconnecting...", NULL, &cons[t]);
break;
case READLINE_POLL_FAIL:
cons[t].connstate = CONN_PENDING;
LOG_ERRNO(LOG_WARN, "Failed to poll the socket. Reconnecting...", NULL, &cons[t]);
break;
default:
cons[t].connstate = CONN_CLOSING;
LOG_ERRNO(LOG_ERROR, "An unknown error happened. Disabling connection.", NULL, &cons[t]);
break;
}
fprintf(stderr, "%s Closing socket %i.\n", resp, cons[t].fds[0]);
actcon--;
break;
}
}
if (actcon <= 0)
if (actcon <= 0) {
LOG(LOG_INFO, "No more active connections. Closing.", NULL, &cons[t]);
break;
}
}
} while (actcon > 0);
close_gracefully();

View File

@ -20,6 +20,7 @@
#include "../lib/uIRC/include/uirc.h"
#include "filesystem.h"
#include "global.h"
#include "log.h"
#include "memory.h"
#include "misc.h"
#include "net.h"
@ -28,6 +29,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define VERSION "alpha"

View File

@ -33,18 +33,20 @@ char* point_after(char* string, char point)
*(ret++) = '\0';
return ret;
}
int handle_irc_message(IRC_Message* mesg, int socket, Connection* conn)
int handle_irc_message(IRC_Message* mesg, Connection* conn)
{
switch (mesg->cmd) {
case (PING): {
LOG(LOG_DEBUG, "Responding to ping.", mesg->trailing, conn);
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_PONG(mesg->trailing, NULL), sizeof(sendbuf))) > 0)
write(socket, sendbuf, sendbuflen);
flush_buffer(conn);
break;
}
/* Autojoin channels from current connection on first response from the server */
case (RPL_WELCOME): {
LOG(LOG_DEBUG, "Auto-joining channels.", conn->data.chans, conn);
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_JOIN(conn->data.chans, NULL), sizeof(sendbuf))) > 0)
write(socket, sendbuf, sendbuflen);
flush_buffer(conn);
break;
}
case (RPL_BOUNCE): {

View File

@ -19,8 +19,8 @@
#include "../lib/uIRC/include/functions.h"
#include "../lib/uIRC/include/mappings.h"
#include "global.h"
#include "structs.h"
#include "net.h"
#include "structs.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
@ -33,5 +33,5 @@
char* point_after(char* string, char point);
void stop_loop();
int handle_irc_message(IRC_Message* mesg, int socket, Connection* conn);
int handle_irc_message(IRC_Message* mesg, Connection* conn);
#endif

View File

@ -27,7 +27,7 @@ signed int init_conn(Connection* info)
if ((getaddrres = getaddrinfo(info->data.addr, info->data.port, NULL, &conn)) == 0) {
break;
} else {
fprintf(stderr, "%s %s:%s. %s (%i)\n", "Failed to get the address info for", info->data.addr, info->data.port, gai_strerror(getaddrres), getaddrres);
LOG_ERRNO(LOG_ERROR, "Failed to get address info.", NULL, info);
if (i == 2 || (getaddrres != EAI_AGAIN && getaddrres != EAI_NONAME)) {
freeaddrinfo(conn);
return -1;
@ -41,7 +41,7 @@ signed int init_conn(Connection* info)
return -1;
}
if ((sockfd = socket(conn->ai_family, conn->ai_socktype, conn->ai_protocol)) < 0) {
fprintf(stderr, "%s. %s (%i)\n", "Failed to open the socket", strerror(errno), errno);
LOG_ERRNO(LOG_ERROR, "Failed to open a socket.", NULL, info);
return -1;
}
for (unsigned int i = 0; i < 3; i++) {
@ -49,7 +49,7 @@ signed int init_conn(Connection* info)
if (connectres == 0 || connectres == EINTR) { /* TODO: Lookup if EINTR expects async conn OR connects async, assuming connects */
break;
} else {
fprintf(stderr, "%s %s:%s. %s (%i)\n", "Failed to connect to host", info->data.addr, info->data.port, strerror(errno), errno);
LOG_ERRNO(LOG_ERROR, "Failed to connect to host.", NULL, info);
if (i == 2 || (connectres != EADDRNOTAVAIL && connectres != ETIMEDOUT && connectres != ECONNRESET && connectres != ECONNREFUSED)) {
close(sockfd);
return -1;
@ -69,7 +69,12 @@ int connection_manager(Connection* conn)
switch (conn->connstate) {
case CONN_PENDING: {
int tmp;
conn->fds[0] = CONN_CLOSED;
conn->connstate = CONN_CLOSED;
conn->fds[0] = -1;
if (conn->buf.buffer != NULL) {
free(conn->buf.buffer);
conn->buf.buffer = NULL;
}
if ((tmp = init_conn(conn)) > 0) {
conn->fds[0] = tmp;
conn->buf.fd = &conn->fds[0];
@ -79,17 +84,19 @@ int connection_manager(Connection* conn)
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_USER(conn->names.user, conn->names.real, 0), sizeof(sendbuf))) > 0)
flush_buffer(conn);
actcon++;
LOG(LOG_INFO, "Connection established!", NULL, conn);
return 1;
}
return 0;
}
case CONN_CLOSING: {
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_QUIT("UIRCD DEVELOPMENT VERSION BETA"), sizeof(sendbuf))) > 0) {
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_QUIT(">> uIRC beta"), sizeof(sendbuf))) > 0)
flush_buffer(conn);
}
LOG(LOG_INFO, "Closing connection.", NULL, conn);
conn->connstate = CONN_CLOSED;
close(conn->fds[0]);
conn->fds[0] = -1;
if (conn->buf.buffer != NULL) free(conn->buf.buffer);
return 0;
}
case CONN_CLOSED: return 0;
@ -106,6 +113,17 @@ void close_gracefully()
}
void flush_buffer(Connection* conn)
{
if (write(conn->fds[0], sendbuf, sendbuflen) == -1)
fprintf(stderr, "Error encountered on sending message. %s (%i)\n", strerror(errno), errno);
int res;
char* pos = sendbuf;
for (;;) {
if ((res = write(conn->fds[0], pos, sendbuflen - (pos - sendbuf))) != sendbuflen - (pos - sendbuf)) {
if (res == -1) {
conn->connstate = CONN_PENDING;
LOG(LOG_ERROR, "Couldn't flush send buffer over network. Will attempt to reconnect.", NULL, conn);
break;
} else
pos += res;
} else
break;
}
}

View File

@ -18,10 +18,12 @@
#include "../lib/uIRC/include/functions.h"
#include "global.h"
#include "log.h"
#include "structs.h"
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>

View File

@ -17,6 +17,7 @@
*/
#include "../lib/uIRC/include/types.h"
#include "time.h"
#ifndef _UIRCD_INCLUDED_STRUCTS
#define _UIRCD_INCLUDED_STRUCTS
@ -40,5 +41,6 @@ typedef struct {
Buffer_Info buf;
signed int fds[2];
signed int connstate;
time_t lastping;
} Connection;
#endif