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 <sys/types.h> #include <sys/select.h> #include <sys/socket.h> #include <sys/stat.h> #include <unistd.h> #include <syslog.h> #include <signal.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <libgen.h> #include <dirent.h> #include <fcntl.h> #include <microhttpd.h> #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 = "<html><body>400</body></html>"; char *my404PageHTML = "<html><body>404</body></html>"; 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

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

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

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

//------------------------------------------------------------------------------ // strptr.c #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #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

//---------------------------------------------------------------------------------------- // 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

//---------------------------------------------------------------------------------------- // log.c #include <time.h> #include <stdio.h> #include <stdarg.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #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