diff --git a/ftpCommandElaborate.c b/ftpCommandElaborate.c index 2b6dee4..53e22c8 100755 --- a/ftpCommandElaborate.c +++ b/ftpCommandElaborate.c @@ -178,6 +178,12 @@ int parseCommandPass(ftpDataType *data, int socketId) // my_printf("\n TOO MANY LOGIN FAILS! \n"); data->clients[socketId].closeTheClient = 1; returnCode = socketPrintf(data, socketId, "s", "430 Too many login failure detected, your ip will be blacklisted for 5 minutes\r\n"); + + char *theLogString[STRING_SZ_LARGE]; + memset(theLogString, 0, STRING_SZ_LARGE); + snprintf(theLogString, STRING_SZ_LARGE, "An ip %s has been blacklisted due too many password errors. ", element.ipAddress, data->clients[socketId].clientIpAddress); + addLog(theLogString, CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC); + if (returnCode <= 0) { addLog("socketPrintfError ", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC); @@ -561,7 +567,8 @@ int parseCommandPasv(ftpDataType *data, int socketId) if (returnCode != 0) { my_printfError("\nError in pthread_create %d", returnCode); - addLog("Pthead Create error ", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC); + addLog("Pthead create error restarting the server", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC); + exit(0); return FTP_COMMAND_PROCESSED_WRITE_ERROR; } @@ -596,7 +603,8 @@ int parseCommandEpsv(ftpDataType *data, int socketId) if (returnCode != 0) { my_printfError("\nError in pthread_create %d", returnCode); - addLog("Pthead Create error ", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC); + addLog("Pthead create error restarting the server", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC); + exit(0); return FTP_COMMAND_PROCESSED_WRITE_ERROR; } @@ -635,7 +643,8 @@ int parseCommandPort(ftpDataType *data, int socketId) if (returnCode != 0) { my_printfError("\nError in pthread_create %d", returnCode); - addLog("Pthead Create error ", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC); + addLog("Pthead create error restarting the server", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC); + exit(0); return FTP_COMMAND_PROCESSED_WRITE_ERROR; } diff --git a/ftpData.c b/ftpData.c index defec42..49a937c 100755 --- a/ftpData.c +++ b/ftpData.c @@ -670,8 +670,14 @@ void deleteListDataInfoVector(DYNV_VectorGenericDataType *theVector) void cancelWorker(ftpDataType *data, int clientId) { void *pReturn; + addLog("Cancelling thread because is busy", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC); int returnCode = pthread_cancel(data->clients[clientId].workerData.workerThread); + if (returnCode != 0) + { + addLog("Cancelling thread ERROR", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC); + } returnCode = pthread_join(data->clients[clientId].workerData.workerThread, &pReturn); + data->clients[clientId].workerData.threadHasBeenCreated = 0; } diff --git a/ftpData.h b/ftpData.h index aeda0d3..18cd5b9 100755 --- a/ftpData.h +++ b/ftpData.h @@ -41,6 +41,7 @@ #define STRING_SZ_SMALL 100 +#define STRING_SZ_LARGE 4096 #define CLIENT_COMMAND_STRING_SIZE 4096 #define CLIENT_BUFFER_STRING_SIZE 4096 diff --git a/ftpServer.c b/ftpServer.c index affe3bb..94762b4 100755 --- a/ftpServer.c +++ b/ftpServer.c @@ -122,6 +122,8 @@ void workerCleanup(void *socketId) void *connectionWorkerHandle(void * socketId) { int theSocketId = *(int *)socketId; + // Enable cancellation for this thread + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_cleanup_push(workerCleanup, (void *) &theSocketId); ftpData.clients[theSocketId].workerData.threadIsAlive = 1; ftpData.clients[theSocketId].workerData.threadHasBeenCreated = 1; @@ -538,7 +540,7 @@ void runFtpServer(void) respawnProcess(); //Init log - logInit(ftpData.ftpParameters.logFolder); + logInit(ftpData.ftpParameters.logFolder, ftpData.ftpParameters.maximumLogFileCount); //Socket main creator ftpData.connectionData.theMainSocket = createSocket(&ftpData); @@ -549,14 +551,14 @@ void runFtpServer(void) /* the maximum socket fd is now the main socket descriptor */ ftpData.connectionData.maxSocketFD = ftpData.connectionData.theMainSocket+1; - returnCode = pthread_create(&watchDogThread, NULL, watchDog, NULL); if(returnCode != 0) - { + { my_printf("pthread_create WatchDog Error %d", returnCode); - exit(0); - } + addLog("Pthead create error restarting the server", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC); + exit(0); + } //Endless loop ftp process while (1) @@ -565,7 +567,6 @@ void runFtpServer(void) //Update watchdog timer updateWatchDogTime((int)time(NULL)); - /* my_printf("\nUsed memory : %lld", DYNMEM_GetTotalMemory()); int memCount = 0; diff --git a/library/log.c b/library/log.c index 85f44ef..3cebf0c 100644 --- a/library/log.c +++ b/library/log.c @@ -4,15 +4,20 @@ #include #include #include +#include +#include +#include +#include +#include + #include "log.h" #include "../debugHelper.h" #include "dynamicVectors.h" #include "fileManagement.h" - - -#define LOG_LINE_SIZE 1024 + PATH_MAX +#define LOG_LINE_SIZE 1024 + PATH_MAX +#define LOG_FILENAME_PREFIX "uftpLog_" static void logThread(void * arg); @@ -23,7 +28,136 @@ static sem_t logsem; // Semaphore for controlling log to write static pthread_t pLogThread; static pthread_mutex_t mutex; -static char logFolder[PATH_MAX]; // Semaphore for controlling log to write +static int logFilesNumber; + +static char logFolder[PATH_MAX]; + +#define MAX_FILENAME_LENGTH 256 + + +static long long is_date_format(const char* str); +static int delete_old_logs(const char* folder_path, int days_to_keep); + +static long long is_date_format(const char* str) +{ + char year[5]; + char month[3]; + char day[3]; + + memset(year, 0, 5); + memset(month, 0, 3); + memset(day, 0, 3); + + if (strlen(str) != 10 || str[4] != '-' || str[7] != '-') + { + return 0; + } + + // Check for valid digits in year, month, and day components + for (int i = 0; i < 4; i++) + { + if (!isdigit(str[i])) { + return 0; + } + year[i] = str[i]; + } + + for (int i = 5; i < 7; i++) { + if (!isdigit(str[i])) { + return 0; + } + } + for (int i = 8; i < 10; i++) { + if (!isdigit(str[i])) { + return 0; + } + } + + month[0] = str[5]; + month[1] = str[6]; + + day[0] = str[8]; + day[1] = str[9]; + + return atoll(year)*365 + atoll(month)*31 + atoll(day); +} + +static int delete_old_logs(const char* folder_path, int days_to_keep) +{ + unsigned long long n_of_day_file; + unsigned long long n_of_day_today; + + struct stat statbuf; + DIR* dir; + struct dirent* entry; + char full_path[PATH_MAX]; + + dir = opendir(folder_path); + if (dir == NULL) + { + perror("opendir"); + return 1; + } + + time_t now = time(NULL); + struct tm *info = localtime(&now); + char timeToday[11]; + if (strftime(timeToday, sizeof(timeToday), "%Y-%m-%d", info) == 0) + { + my_printfError("strftime error"); + return; + } + + n_of_day_today = is_date_format(timeToday); + + while ((entry = readdir(dir)) != NULL) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; // Skip . and .. entries + } + + snprintf(full_path, sizeof(full_path), "%s%s", folder_path, entry->d_name); + + if (stat(full_path, &statbuf) == -1) + { + perror("stat"); + continue; + } + + if (!S_ISREG(statbuf.st_mode)) + { + continue; // Not a regular file + } + + if (strncmp(entry->d_name, LOG_FILENAME_PREFIX, strlen(LOG_FILENAME_PREFIX)) != 0) + { + continue; // Not a log file + } + + n_of_day_file = is_date_format(entry->d_name + strlen(LOG_FILENAME_PREFIX)); + + if (!n_of_day_file) + { + continue; // Invalid date format + } + + if (n_of_day_file+days_to_keep < n_of_day_today) + { + my_printf("\nRemoving old log file: %s", full_path); + + // File is older than specified days + if (remove(full_path) == -1) + { + perror("remove"); + continue; + } + } + } + + closedir(dir); + return 0; +} + // STATIC SECTION static void logThread(void * arg) @@ -41,7 +175,7 @@ static void logThread(void * arg) memset(theLogFilename, 0, PATH_MAX); - if (strftime(logName, sizeof(logName), "uftpLog_%Y-%m-%d", info) == 0) + if (strftime(logName, sizeof(logName), LOG_FILENAME_PREFIX"%Y-%m-%d", info) == 0) { my_printfError("strftime error"); return; @@ -81,11 +215,18 @@ static void logThread(void * arg) } } -int logInit(char * folder) +int logInit(char * folder, int numberOfLogFiles) { int returnCode; my_printf("\n Init logging system.."); + logFilesNumber = numberOfLogFiles; + + delete_old_logs(folder, numberOfLogFiles); + + if (logFilesNumber <= 0) + return; + DYNV_VectorString_Init(&logQueue); DYNV_VectorString_Init(&workerQueue); @@ -97,7 +238,8 @@ int logInit(char * folder) returnCode = pthread_create(&pLogThread, NULL, &logThread, NULL); if (returnCode != 0) { - my_printfError("Error while creating log thread."); + addLog("Pthead create error restarting the server", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC); + exit(0); return 0; } @@ -114,6 +256,9 @@ int logInit(char * folder) void addLog(char* logString, char * currFile, int currLine, char * currFunction) { + if (logFilesNumber <= 0) + return; + char theLogString[LOG_LINE_SIZE]; char debugInfo[LOG_LINE_SIZE]; memset(theLogString, 0, LOG_LINE_SIZE); diff --git a/library/log.h b/library/log.h index 25cf7f5..0324a14 100644 --- a/library/log.h +++ b/library/log.h @@ -25,7 +25,7 @@ #ifndef LOG_H #define LOG_H -int logInit(char * folder); +int logInit(char * folder, int numberOfLogFiles); void addLog(char* logString, char * currFile, int currLine, char * currFunction); #endif \ No newline at end of file diff --git a/uftpd.cfg b/uftpd.cfg index 831015d..12e38df 100755 --- a/uftpd.cfg +++ b/uftpd.cfg @@ -16,6 +16,12 @@ SINGLE_INSTANCE = true #Run in background, daemon mode ok DAEMON_MODE = true +# Folder where to save the logs, use the same format below, the folder must terminate with / +LOG_FOLDER = /var/log/ + +# Maximum number of logs to keep, if 0 log functionality is disabled +MAXIMUM_LOG_FILES = 0 + # Idle timeout in seconds, client are disconnected for inactivity after the # specified amount of time in seconds, set to 0 to disable IDLE_MAX_TIMEOUT = 3600