www.RiffZone.net
code.RiffZone.net
login.RiffZone.net

Retour au Sommaire

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