Un serveur de fichiers HTTP en C basé sur libmicrohttpd (GNU)
EN COURS
Libmicrohttpd est une vieille librairie de GNU, sans doute pas la plus évoluée mais après avoir fait le tour des solutions équivalentes, il m'a semblé qu'en termes techniques, de durée et de licence, elle n'avait pas trop à rougir, et méritait qu'on s'y intéresse.
L'implémentation qui suit a été testée sur un serveur Ubuntu 20.04 LTS (Focal Fossa), elle devrait aussi bien fonctionner sur n'importe quelle distribution debian-based équivalente.
Il serait un peu long de tout détailler, voici d'abord le code principal réduit à un maximum raisonnable:
//---------------------------------------------------------------------------------------- // WebServer.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "strptr.h"
#include "log.h"
#define PORT_HTTP 80
#define PORT_HTTPS 443
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
//----------------------------------------------------------------------------------------
// Globals
static struct MHD_Daemon *gMHDDaemonHTTP = NULL;
static struct MHD_Daemon *gMHDDaemonHTTPS = NULL;
static int gMHDRunning = FALSE;
static char *gAppDirPathPtr = NULL;
static char *gHostsDirPathPtr = NULL;
static char *gTLSPublicKeyPtr = NULL;
static char *gTLSPrivateKeyPtr = NULL;
//----------------------------------------------------------------------------------------
// Prototypes
void AppInit (void);
void AppExit (void);
//----------------------------------------------------------------------------------------
// Signals
void handle_sigterm ()
{
log_timedwriteln(LOG_TYPE_SIGNAL, "Signal : TERM");
gMHDRunning = FALSE;
}
void handle_sigint ()
{
log_timedwriteln(LOG_TYPE_SIGNAL, "Signal : INT");
gMHDRunning = FALSE;
}
void handle_sigsegv ()
{
log_timedwriteln(LOG_TYPE_SIGNAL, "Signal : SEGV");
}
//----------------------------------------------------------------------------------------
// MimeType from Extension
char *get_mimetype_from_extension (const char *theExtension)
{
// https://developer.mozilla.org/fr/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
if (strcmp(theExtension, "html") == 0) return strptr_new("text/html");
if (strcmp(theExtension, "pl") == 0) return strptr_new("text/html");
if (strcmp(theExtension, "js") == 0) return strptr_new("application/javascript");
if (strcmp(theExtension, "css") == 0) return strptr_new("text/css");
if (strcmp(theExtension, "ico") == 0) return strptr_new("image/x-icon");
if (strcmp(theExtension, "jpg") == 0) return strptr_new("image/jpeg");
if (strcmp(theExtension, "jpeg") == 0) return strptr_new("image/jpeg");
if (strcmp(theExtension, "gif") == 0) return strptr_new("image/gif");
if (strcmp(theExtension, "png") == 0) return strptr_new("image/png");
if (strcmp(theExtension, "pdf") == 0) return strptr_new("application/pdf");
return strptr_new("application/octet-stream");
};
//----------------------------------------------------------------------------------------
// Response File Callbacks
/*
static ssize_t response_file_reader (void *theFilePtr, uint64_t pos, char *buf, size_t max)
{
fseek((FILE *)theFilePtr, pos, SEEK_SET);
return fread(buf, 1, max, (FILE *)theFilePtr);
}
static void response_file_free (void *theFilePtr)
{
fclose((FILE *)theFilePtr);
}
*/
//----------------------------------------------------------------------------------------
// Request Callback
static int answer_to_connection (void *cls, struct MHD_Connection *theConnection,
const char *theRequestURL, const char *theMethod, const char *version, const char *upload_data,
size_t *upload_data_size, void **con_cls)
{
char *my400PageHTML = "
400";char *my404PageHTML = "
404";struct MHD_Response *myResponse = NULL;
int myResponseQueueResult;
unsigned int myResponseStatusCode = MHD_HTTP_NO_CONTENT;
const char *myHeaderHostPtr;
char *myRequestHostDirPathPtr = NULL;
char *myRequestFilePathPtr = NULL;
char *myRequestFileMimetypePtr = NULL;
struct stat myRequestFileStat;
void *myResponseBufferPtr = NULL;
size_t myResponseBufferSize = 0;
struct sockaddr **myClientSockAddrPtrPtr;
char myClientIPAddress[20];
(void)cls; /* Unused. Silent compiler warning. */
// (void)theRequestURL; /* Unused. Silent compiler warning. */
// (void)theMethod; /* Unused. Silent compiler warning. */
(void)version; /* Unused. Silent compiler warning. */
(void)upload_data; /* Unused. Silent compiler warning. */
(void)upload_data_size; /* Unused. Silent compiler warning. */
// (void)con_cls; /* Unused. Silent compiler warning. */
if (strcmp(theMethod, MHD_HTTP_METHOD_GET) != 0) return MHD_NO; // unexpected method
if (*con_cls == NULL) // first call
{
*con_cls = theConnection;
return MHD_YES;
};
myClientSockAddrPtrPtr = (struct sockaddr **)MHD_get_connection_info(theConnection,
MHD_CONNECTION_INFO_CLIENT_ADDRESS);
log_sockaddr_to_ipstr(*myClientSockAddrPtrPtr, myClientIPAddress, sizeof(myClientIPAddress));
myHeaderHostPtr = MHD_lookup_connection_value(theConnection, MHD_HEADER_KIND, MHD_HTTP_HEADER_HOST);
if (myHeaderHostPtr != NULL)
{
char *myRequestRevDomainPtr = strptr_new(myHeaderHostPtr);
strptr_revert_domain(myRequestRevDomainPtr);
myRequestHostDirPathPtr = strptr_new("/opt/WebServer/hosts");
myRequestHostDirPathPtr = strptr_cat(myRequestHostDirPathPtr, "/");
myRequestHostDirPathPtr = strptr_cat(myRequestHostDirPathPtr, myRequestRevDomainPtr);
myRequestRevDomainPtr = strptr_free(myRequestRevDomainPtr);
};
if (myRequestHostDirPathPtr != NULL)
{
int myRequestHostDirPathOK = FALSE;
if (stat(myRequestHostDirPathPtr, &myRequestFileStat) == 0)
{
if (S_ISDIR(myRequestFileStat.st_mode))
{
myRequestHostDirPathOK = TRUE;
}
else log_timedwriteln(LOG_TYPE_ERROR, "(%s) RequestHostDirPath not a dir : %s",
myClientIPAddress, myRequestHostDirPathPtr);
}
else log_timedwriteln(LOG_TYPE_ERROR, "(%s) RequestHostDirPath not found : %s",
myClientIPAddress, myRequestHostDirPathPtr);
if (!myRequestHostDirPathOK) myRequestHostDirPathPtr = strptr_free(myRequestHostDirPathPtr);
};
if (myRequestHostDirPathPtr != NULL)
{
myRequestFilePathPtr = strptr_new(myRequestHostDirPathPtr);
myRequestFilePathPtr = strptr_cat(myRequestFilePathPtr, theRequestURL);
if (stat(myRequestFilePathPtr, &myRequestFileStat) == 0)
{
if (S_ISDIR(myRequestFileStat.st_mode))
{
char *myRequestFileDirPathPtr = strptr_new(myRequestFilePathPtr);
myRequestFileDirPathPtr = strptr_rtrim(myRequestFileDirPathPtr, '/');
myRequestFilePathPtr = strptr_cpy(myRequestFilePathPtr, myRequestFileDirPathPtr);
myRequestFilePathPtr = strptr_cat(myRequestFilePathPtr, "/index.html");
if (stat(myRequestFilePathPtr, &myRequestFileStat) == 0)
{
int myRequestFileDesc = open(myRequestFilePathPtr, O_RDONLY);
myResponse = MHD_create_response_from_fd((uint64_t)myRequestFileStat.st_size, myRequestFileDesc);
myResponseStatusCode = MHD_HTTP_OK;
};
if (myResponseStatusCode != MHD_HTTP_OK)
{
log_timedwriteln(LOG_TYPE_ERROR, "(%s) Couldn't stat : %s",
myClientIPAddress, myRequestFilePathPtr);
myResponse = MHD_create_response_from_buffer(strlen(my404PageHTML), my404PageHTML, MHD_RESPMEM_MUST_COPY);
myResponseStatusCode = MHD_HTTP_NOT_FOUND;
};
strptr_free(myRequestFileDirPathPtr);
}
else
{
int myRequestFileDesc = open(myRequestFilePathPtr, O_RDONLY);
myResponse = MHD_create_response_from_fd((uint64_t)myRequestFileStat.st_size, myRequestFileDesc);
myResponseStatusCode = MHD_HTTP_OK;
};
}
else
{
log_timedwriteln(LOG_TYPE_ERROR, "(%s) Couldn't stat : %s",
myClientIPAddress, myRequestFilePathPtr);
myResponse = MHD_create_response_from_buffer(strlen(my404PageHTML), my404PageHTML, MHD_RESPMEM_MUST_COPY);
myResponseStatusCode = MHD_HTTP_NOT_FOUND;
};
}
else
{
myResponse = MHD_create_response_from_buffer(strlen(my400PageHTML), my400PageHTML, MHD_RESPMEM_MUST_COPY);
myResponseStatusCode = MHD_HTTP_BAD_REQUEST;
};
if ((myRequestFileMimetypePtr == NULL) && (myResponseStatusCode == MHD_HTTP_OK))
{
char *myFileNamePtr = strrchr(myRequestFilePathPtr, '/');
if (myFileNamePtr != NULL) myFileNamePtr++;
char *myExtensionPtr = strrchr(myFileNamePtr, '.');
if (myExtensionPtr != NULL) myExtensionPtr++;
myRequestFileMimetypePtr = get_mimetype_from_extension(myExtensionPtr);
};
if ((myRequestFilePathPtr != NULL) && (myResponseStatusCode == MHD_HTTP_OK))
{
log_timedwriteln(LOG_TYPE_ACCESS,
"(%s) Serving file : %s (MimeType: %s)",
myClientIPAddress, myRequestFilePathPtr, myRequestFileMimetypePtr);
};
if (myResponse != NULL)
{
if (myRequestFileMimetypePtr != NULL)
{
MHD_add_response_header(myResponse, "Content-Type", myRequestFileMimetypePtr);
};
myResponseQueueResult = MHD_queue_response(theConnection, myResponseStatusCode, myResponse);
MHD_destroy_response(myResponse);
};
strptr_free(myRequestHostDirPathPtr);
strptr_free(myRequestFilePathPtr);
strptr_free(myRequestFileMimetypePtr);
return myResponseQueueResult;
}
//----------------------------------------------------------------------------------------
// AppInit
void AppInit (void)
{
char myAppFilePath[256];
int myAppFilePathLen;
char *myLogsDirPathPtr = NULL;
if ((myAppFilePathLen = readlink("/proc/self/exe", myAppFilePath, 255)) < 0)
{
syslog(LOG_INFO, "WebServer> readlink failed");
exit(1);
};
myAppFilePath[myAppFilePathLen] = '0';
gAppDirPathPtr = strptr_new(dirname(myAppFilePath));
syslog(LOG_INFO, "WebServer> App dir path: %s", gAppDirPathPtr);
myLogsDirPathPtr = strptr_new(gAppDirPathPtr);
myLogsDirPathPtr = strptr_cat(myLogsDirPathPtr, "/logs");
syslog(LOG_INFO, "WebServer> Logs dir path: %s", myLogsDirPathPtr);
log_init(myLogsDirPathPtr);
myLogsDirPathPtr = strptr_free(myLogsDirPathPtr);
gHostsDirPathPtr = strptr_new(gAppDirPathPtr);
gHostsDirPathPtr = strptr_cat(gHostsDirPathPtr, "/hosts");
syslog(LOG_INFO, "WebServer> Hosts dir path: %s", gHostsDirPathPtr);
}
//----------------------------------------------------------------------------------------
// AppExit
void AppExit (void)
{
strptr_free(gAppDirPathPtr);
strptr_free(gHostsDirPathPtr);
strptr_free(gTLSPrivateKeyPtr);
strptr_free(gTLSPublicKeyPtr);
log_exit();
}
//----------------------------------------------------------------------------------------
// Main
int main (void)
{
syslog(LOG_INFO, "WebServer> Starting");
AppInit();
signal(SIGTERM, handle_sigterm);
signal(SIGINT, handle_sigint);
signal(SIGSEGV, handle_sigsegv);
gMHDDaemonHTTP = MHD_start_daemon(MHD_USE_INTERNAL_POLLING_THREAD, PORT_HTTP, NULL, NULL,
&answer_to_connection, NULL, MHD_OPTION_END);
gMHDRunning = (gMHDDaemonHTTP != NULL) && (gMHDDaemonHTTPS != NULL);
syslog(LOG_INFO, gMHDRunning ? "WebServer> Daemons started" : "WebServer> Daemons NOT started");
while (gMHDRunning)
{
sleep(1);
};
if (gMHDDaemonHTTP != NULL)
{
syslog(LOG_INFO, "WebServer> MHD_stop_daemon(gMHDDaemonHTTP)...");
MHD_stop_daemon(gMHDDaemonHTTP);
gMHDDaemonHTTP = NULL;
};
syslog(LOG_INFO, "WebServer> Now Quit");
AppExit();
return 0;
}
//----------------------------------------------------------------------------------------
// EOF
:CODE
Pour les besoins du test, créez un dossier "/root/webserver" dans lequel vous enregistrerez le code précédent sous "webserver.c". On va créer en même temps deux sous-dossiers nommés "logs" et "settings".
Si vous n'avez pas encore installé les outils de compilation, lancez:
apt-get install build-essential
Installons maintenant libmicrohttpd:
apt-get install libmicrohttpd-dev
Cet exemple utilise deux petites librairies "log" et "strptr" dont les sources sont à la fin de cet article, enregistrez-les au même niveau que "webserver.c".
Auquel on va ajouter le Makefile ci-dessous:
all : webserver
strptr.o : strptr.c
gcc -g -c strptr.c -o strptr.o
log.o : log.c
gcc -g -c log.c -o log.o
webserver.o : webserver.c
gcc -g -c webserver.c -o webserver.o
webserver : webserver.o strptr.o log.o
g++ -g -o webserver webserver.o strptr.o log.o -lmicrohttpd -lpthread -ldl
clean :
rm -f webserver
rm -f *.o
install:
systemctl stop webserver
mkdir -p /opt/webserver
mkdir -p /opt/webserver/hosts
mkdir -p /opt/webserver/settings
mkdir -p /opt/webserver/logs
cp -f webserver /opt/webserver/webserver
cp -f webserver.service /etc/systemd/system/webserver.service
systemctl daemon-reload
ls -lah /opt/webserver/
systemctl start webserver
systemctl status webserver
:CONF
La section install mérite quelques explications, elle installe (comme son nom l'indique) le programme dans la section /opt/ du serveur, avec un fichier de configuration systemd "webserver.service" défini comme suit:
[Unit] Description=WebServer Daemon
[Service]
Type=simple
ExecStart=/opt/fappserver/webserver
[Install]
WantedBy=multi-user.target
:CONF
Librairies log.c/h et strptr.c/h
//------------------------------------------------------------------------------ // strptr.h
char *strptr_new (const char *theSrcPtr);
char *strptr_new_sub (const char *theStrPtr, int theOffset, size_t theLen);
char *strptr_new_load (const char *theFilePath);
char *strptr_free (char *theStrPtr);
char *strptr_cpy (char *theDstStrPtr, const char *theSrcPtr);
char *strptr_cat (char *theDstStrPtr, const char *theSrcPtr);
char *strptr_rtrim (char *theStrPtr, char theTrimChar);
int strptr_startswith (char *theStrPtr, char *theStartStrPtr);
char *strptr_indpart (char *theStrPtr, int thePartNum, char theSeparator);
char *strptr_replacesub (char *theStrPtr, int theOffset, size_t theSize, char *theReplaceStrPtr);
void strptr_revert_domain (char *theDomainStrPtr);
//------------------------------------------------------------------------------
// EOF
:CODE
//------------------------------------------------------------------------------ // strptr.c
#include
#include
#include
#include
#include
#include "strptr.h"
//------------------------------------------------------------------------------
// strptr_new*
char *strptr_new (const char *theSrcPtr)
{
size_t myNewStrSize = theSrcPtr != NULL ? strlen(theSrcPtr) + 1 : 1;
char *myNewStrPtr = (char *)malloc(myNewStrSize);
if (myNewStrPtr == NULL) return NULL;
strncpy(myNewStrPtr, theSrcPtr, myNewStrSize);
myNewStrPtr[myNewStrSize - 1] = '0';
return myNewStrPtr;
}
char *strptr_new_sub (const char *theStrPtr, int theOffset, size_t theLen)
{
char *mySubStrPtr = (char *)malloc(theLen + 1);
if (mySubStrPtr == NULL) return NULL;
strncpy(mySubStrPtr, &theStrPtr[theOffset], theLen);
mySubStrPtr[theLen] = '0';
return mySubStrPtr;
}
char *strptr_new_load (const char *theFilePath)
{
struct stat myFileStat;
FILE *myFilePtr;
long myFileSize;
long myFileRead;
if (stat(theFilePath, &myFileStat) < 0) return NULL;
if (!S_ISREG(myFileStat.st_mode)) return NULL;
myFilePtr = fopen(theFilePath, "rb");
if (myFilePtr == NULL) return NULL;
fseek(myFilePtr, 0, SEEK_END);
myFileSize = ftell(myFilePtr);
fseek(myFilePtr, 0, SEEK_SET);
char *myNewStrPtr = (char *)malloc(myFileSize + 1);
if (myNewStrPtr != NULL)
{
myFileRead = fread(myNewStrPtr, 1, myFileSize, myFilePtr);
};
myNewStrPtr[myFileSize] = '0';
fclose(myFilePtr);
if (myFileRead == myFileSize) return myNewStrPtr;
free(myNewStrPtr);
return NULL;
}
//------------------------------------------------------------------------------
// strptr_free
char *strptr_free (char *theStrPtr)
{
if (theStrPtr == NULL) return NULL;
free((void *)theStrPtr);
return NULL;
}
//------------------------------------------------------------------------------
// strptr_cpy strptr_cat
char *strptr_cpy (char *theDstStrPtr, const char *theSrcPtr)
{
size_t myNewStrSize = theSrcPtr != NULL ? strlen(theSrcPtr) + 1 : 1;
char *myNewStrPtr = (char *)realloc(theDstStrPtr, myNewStrSize);
if (myNewStrPtr == NULL) return NULL;
strncpy(myNewStrPtr, theSrcPtr, myNewStrSize);
myNewStrPtr[myNewStrSize - 1] = '0';
return myNewStrPtr;
}
char *strptr_cat (char *theDstStrPtr, const char *theSrcPtr)
{
size_t myOldStrLen = strlen(theDstStrPtr);
size_t mySrcPtrLen = theSrcPtr != NULL ? strlen(theSrcPtr) : 0;
size_t myNewStrSize = myOldStrLen + mySrcPtrLen + 1;
char *myNewStrPtr = (char *)realloc(theDstStrPtr, myNewStrSize);
if (myNewStrPtr == NULL) return NULL;
strncpy(myNewStrPtr + myOldStrLen, theSrcPtr, mySrcPtrLen);
myNewStrPtr[myNewStrSize - 1] = '0';
return myNewStrPtr;
}
//------------------------------------------------------------------------------
// strptr_rtrim strptr_startswith
char *strptr_rtrim (char *theStrPtr, char theTrimChar)
{
char *myNewStrPtr = strptr_new(theStrPtr);
for (int myLastCharPos = strlen(myNewStrPtr) - 1; myLastCharPos >= 0; myLastCharPos--)
{
if (myNewStrPtr[myLastCharPos] != theTrimChar) break;
myNewStrPtr[myLastCharPos] = '0';
};
myNewStrPtr = (char *)realloc(myNewStrPtr, strlen(myNewStrPtr) + 1);
strptr_free(theStrPtr);
return myNewStrPtr;
}
int strptr_startswith (char *theStrPtr, char *theStartStrPtr)
{
for (int myCharNum = 0; theStartStrPtr[myCharNum] != '0'; myCharNum++)
{
if (theStrPtr[myCharNum] == '0') return 0;
if (theStrPtr[myCharNum] != theStartStrPtr[myCharNum]) return 0;
};
return 1;
}
char *strptr_indpart (char *theStrPtr, int thePartNum, char theSeparator)
{
int myPartStartCharNum = -1;
int myPartSize = 0;
for (int mySrcCharNum = 0, myPartNum = 0; theStrPtr[mySrcCharNum] != '0'; mySrcCharNum++)
{
if (theStrPtr[mySrcCharNum] == theSeparator)
{
myPartNum++;
continue;
};
if (myPartNum < thePartNum) continue;
if (myPartNum > thePartNum) break;
if (myPartStartCharNum < 0) myPartStartCharNum = mySrcCharNum;
myPartSize++;
};
char *myPartStrPtr = (myPartStartCharNum >= 0)
? strptr_new_sub(theStrPtr, myPartStartCharNum, myPartSize)
: NULL;
return myPartStrPtr;
}
char *strptr_replacesub (char *theStrPtr, int theOffset, size_t theSize, char *theReplaceStrPtr)
{
int myReplaceStrLen = strlen(theReplaceStrPtr);
size_t myNewStrPtrLen = strlen(theStrPtr) - theSize + myReplaceStrLen;
char *myNewStrPtr = theStrPtr;
int myCharNum;
if (myReplaceStrLen > theSize)
{
myNewStrPtr = (char *)realloc(myNewStrPtr, myNewStrPtrLen + 1);
for (myCharNum = myNewStrPtrLen; myCharNum >= theOffset + myReplaceStrLen; myCharNum--)
{
myNewStrPtr[myCharNum] = myNewStrPtr[myCharNum - myReplaceStrLen + theSize];
};
};
if (myReplaceStrLen < theSize)
{
for (myCharNum = theOffset + myReplaceStrLen; myCharNum <= myNewStrPtrLen; myCharNum++)
{
myNewStrPtr[myCharNum] = myNewStrPtr[myCharNum + theSize - myReplaceStrLen];
};
myNewStrPtr = (char *)realloc(myNewStrPtr, myNewStrPtrLen + 1);
};
for (myCharNum = 0; myCharNum < myReplaceStrLen; myCharNum++)
{
myNewStrPtr[theOffset + myCharNum] = theReplaceStrPtr[myCharNum];
};
return myNewStrPtr;
}
void strptr_revert_domain (char *theDomainStrPtr)
{
// first revert the whole string
int myCharNum;
int myLeftCharNum = 0;
int myRightCharNum = strlen(theDomainStrPtr) - 1;
char myLeftChar, myRightChar;
while (myLeftCharNum < myRightCharNum)
{
myLeftChar = theDomainStrPtr[myLeftCharNum];
myRightChar = theDomainStrPtr[myRightCharNum];
theDomainStrPtr[myRightCharNum] = myLeftChar;
theDomainStrPtr[myLeftCharNum] = myRightChar;
myLeftCharNum++;
myRightCharNum--;
};
// then we revert the substrings
for (myLeftCharNum = 0, myCharNum = 0;
myCharNum <= strlen(theDomainStrPtr);
myCharNum++)
{
char myChar = theDomainStrPtr[myCharNum];
if ((myChar != '.') && (myChar != '0')) continue;
myRightCharNum = myCharNum - 1;
while (myLeftCharNum < myRightCharNum)
{
myLeftChar = theDomainStrPtr[myLeftCharNum];
myRightChar = theDomainStrPtr[myRightCharNum];
theDomainStrPtr[myRightCharNum] = myLeftChar;
theDomainStrPtr[myLeftCharNum] = myRightChar;
myLeftCharNum++;
myRightCharNum--;
};
myLeftCharNum = myCharNum + 1;
};
}
//------------------------------------------------------------------------------
// EOF
:CODE
//---------------------------------------------------------------------------------------- // log.h
//----------------------------------------------------------------------------------------
// Definitions
#define LOG_TYPE_ACCESS "access"
#define LOG_TYPE_ERROR "error"
#define LOG_TYPE_SIGNAL "signal"
//----------------------------------------------------------------------------------------
// Prototypes
void log_init (const char * theLogsDirPath);
void log_exit ();
void log_write (const char *theLogType, const char * theFormatStr, ...);
void log_timedwriteln (const char *theLogType, const char * theFormatStr, ...);
void log_sockaddr_to_ipstr (const struct sockaddr *theSockAddrPtr,
char *theIPStrPtr, size_t theIPStrSize);
//----------------------------------------------------------------------------------------
// EOF
:CODE
//---------------------------------------------------------------------------------------- // log.c
#include
#include
#include
#include
#include
#include
#include "strptr.h"
#include "log.h"
//----------------------------------------------------------------------------------------
// Globals
static char *gLOG_LogsDirPath = NULL;
//----------------------------------------------------------------------------------------
// Prototypes
char *log_build_filepath (const char *theLogType);
//----------------------------------------------------------------------------------------
// log_init
void log_init (const char * theLogsDirPath)
{
gLOG_LogsDirPath = strptr_new(theLogsDirPath);
}
//----------------------------------------------------------------------------------------
// log_exit
void log_exit ()
{
gLOG_LogsDirPath = strptr_free(gLOG_LogsDirPath);
}
//----------------------------------------------------------------------------------------
// log_build_filepath
char *log_build_filepath (const char *theLogType)
{
time_t myTime;
struct tm *myTimeInfo;
char myDateString[20];
char *myLogFilePath = strptr_new(gLOG_LogsDirPath);
myLogFilePath = strptr_cat(myLogFilePath, "/");
time(&myTime);
myTimeInfo = localtime(&myTime);
strftime(myDateString, sizeof(myDateString), "%F", myTimeInfo);
myLogFilePath = strptr_cat(myLogFilePath, myDateString);
myLogFilePath = strptr_cat(myLogFilePath, "-");
myLogFilePath = strptr_cat(myLogFilePath, theLogType);
myLogFilePath = strptr_cat(myLogFilePath, ".log");
return myLogFilePath;
}
//----------------------------------------------------------------------------------------
// log_write
void log_write (const char *theLogType, const char * theFormatStr, ...)
{
char * myLogFilePath;
FILE * myLogFile;
va_list myVAList;
myLogFilePath = log_build_filepath(theLogType);
if (myLogFilePath != NULL)
{
myLogFile = fopen(myLogFilePath, "a");
if (myLogFile != NULL)
{
va_start(myVAList, theFormatStr);
vfprintf(myLogFile, theFormatStr, myVAList);
va_end(myVAList);
};
fclose(myLogFile);
strptr_free(myLogFilePath);
};
}
void log_timedwriteln (const char *theLogType, const char * theFormatStr, ...)
{
char * myLogFilePath;
FILE * myLogFile;
va_list myVAList;
myLogFilePath = log_build_filepath(theLogType);
if (myLogFilePath != NULL)
{
myLogFile = fopen(myLogFilePath, "a");
if (myLogFile != NULL)
{
time_t myTime;
struct tm * myTimeInfo;
char myTempString[40];
time(&myTime);
myTimeInfo = localtime(&myTime);
strftime(myTempString, sizeof(myTempString), "[%F %T] ", myTimeInfo);
fputs(myTempString, myLogFile);
va_start(myVAList, theFormatStr);
vfprintf(myLogFile, theFormatStr, myVAList);
va_end(myVAList);
fputs("n", myLogFile);
};
fclose(myLogFile);
strptr_free(myLogFilePath);
};
}
//----------------------------------------------------------------------------------------
// log_sockaddr_to_ipstr
void log_sockaddr_to_ipstr (const struct sockaddr *theSockAddrPtr,
char *theIPStrPtr, size_t theIPStrSize)
{
memset(theIPStrPtr, '0', theIPStrSize);
switch(theSockAddrPtr->sa_family)
{
case AF_INET:
inet_ntop(AF_INET, &(((struct sockaddr_in *)theSockAddrPtr)->sin_addr),
theIPStrPtr, theIPStrSize - 1);
break;
case AF_INET6:
inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)theSockAddrPtr)->sin6_addr),
theIPStrPtr, theIPStrSize - 1);
break;
};
}
//----------------------------------------------------------------------------------------
// EOF
:CODE