diff --git a/Common/cm_log.h b/Common/cm_log.h index b4807e2..80f2fd5 100644 --- a/Common/cm_log.h +++ b/Common/cm_log.h @@ -4,5 +4,6 @@ #define __CM_LOG_H__ void cm_log(const char* name, const char* log, ...); +void cm_force_log(const char* log); #endif diff --git a/Common/log.c b/Common/log.c index 79298c7..d8b5123 100644 --- a/Common/log.c +++ b/Common/log.c @@ -4,16 +4,28 @@ #include "cm_string.h" +#include #include #include #include #include #include +FILE* logfile; + bool cm_do_log = false; #define LOGNAME_LENGTH 12 +void cm_force_log(const char* log) { + time_t t = time(NULL); + struct tm* tm = localtime(&t); + char date[513]; + strftime(date, 512, "%a %b %d %H:%M:%S %Z %Y", tm); + fprintf(logfile, "[%s] %s\n", date, log); + fflush(logfile); +} + void cm_log(const char* name, const char* log, ...) { if(!cm_do_log) return; va_list args; @@ -56,7 +68,7 @@ void cm_log(const char* name, const char* log, ...) { } } - fprintf(stderr, "%s %s\n", namebuf, result); + fprintf(logfile, "%s %s\n", namebuf, result); va_end(args); free(result); diff --git a/Module/Makefile b/Module/Makefile index 14f6922..48c192d 100644 --- a/Module/Makefile +++ b/Module/Makefile @@ -14,4 +14,4 @@ all: mod_cgi$(LIB) $(CC) $(CFLAGS) -c -o $@ $< clean: - rm -f *.o *.so *.a + rm -f *.o *.so *.a *.dll diff --git a/Platform/win32-service.mk b/Platform/win32-service.mk new file mode 100644 index 0000000..a27d077 --- /dev/null +++ b/Platform/win32-service.mk @@ -0,0 +1,13 @@ +# $Id$ + +PREFIX = C:/Tewi + +CC = i686-w64-mingw32-gcc +AR = i686-w64-mingw32-ar +WINDRES = i686-w64-mingw32-windres +CFLAGS = -g -std=c99 -DPREFIX=\"$(PREFIX)\" -I $(PWD)/Common -fPIC -DSERVICE -mwindows +LDFLAGS = -mwindows +LIBS = -lws2_32 +EXEC = .exe +LIB = .dll +PREOBJS = tewi.res diff --git a/Platform/win32.mk b/Platform/win32.mk index e5109e4..d40fb64 100644 --- a/Platform/win32.mk +++ b/Platform/win32.mk @@ -11,4 +11,3 @@ LIBS = -lws2_32 EXEC = .exe LIB = .dll PREOBJS = tewi.res -INSTALLER = install.exe diff --git a/Platform/win64-service.mk b/Platform/win64-service.mk new file mode 100644 index 0000000..dca3a01 --- /dev/null +++ b/Platform/win64-service.mk @@ -0,0 +1,13 @@ +# $Id$ + +PREFIX = C:/Tewi + +CC = x86_64-w64-mingw32-gcc +AR = x86_64-w64-mingw32-ar +WINDRES = x86_64-w64-mingw32-windres +CFLAGS = -g -std=c99 -DPREFIX=\"$(PREFIX)\" -I $(PWD)/Common -fPIC -mwindows +LDFLAGS = -mwindows +LIBS = -lws2_32 +EXEC = .exe +LIB = .dll +PREOBJS = tewi.res diff --git a/Platform/win64.mk b/Platform/win64.mk index a9c7456..187462a 100644 --- a/Platform/win64.mk +++ b/Platform/win64.mk @@ -11,4 +11,3 @@ LIBS = -lws2_32 EXEC = .exe LIB = .dll PREOBJS = tewi.res -INSTALLER = install.exe diff --git a/Server/Makefile b/Server/Makefile index 01bc151..c746f1d 100644 --- a/Server/Makefile +++ b/Server/Makefile @@ -7,10 +7,7 @@ include $(PWD)/Platform/$(PLATFORM).mk OBJS = version.o main.o config.o server.o http.o module.o strptime.o $(EXTOBJS) $(PREOBJS) -all: tewi$(EXEC) $(INSTALLER) - -install.exe: tewi$(EXEC) install.nsi - makensis install.nsi +all: tewi$(EXEC) tewi$(EXEC): $(OBJS) ../Common/common.a $(CC) $(LDFLAGS) $(EXTLDFLAGS) -o $@ $(OBJS) $(EXTLIBS) $(LIBS) ../Common/common.a diff --git a/Server/install.nsi b/Server/install.nsi index ec67f26..b03a5db 100644 --- a/Server/install.nsi +++ b/Server/install.nsi @@ -5,7 +5,12 @@ OutFile "install.exe" InstallDir "C:\Tewi" Icon "tewi.ico" LicenseData ../LICENSE + +!include "LogicLib.nsh" +!include "Sections.nsh" + Page license +Page components Page instfiles UninstPage uninstConfirm UninstPage instfiles @@ -17,8 +22,6 @@ Section CreateDirectory "$INSTDIR\bin" SetOutPath "$INSTDIR" File /oname=LICENSE.txt "../LICENSE" - SetOutPath "$INSTDIR\bin" - File "tewi.exe" SetOutPath "$INSTDIR\modules" File "../Module/*.dll" SetOutPath "$INSTDIR\etc" @@ -42,7 +45,64 @@ Section WriteUninstaller "$INSTDIR\uninstall.exe" SectionEnd +Section "Install the executable only" SEL_EXEC + SetOutPath "$INSTDIR\bin" + File "../tewi.exe" + WriteINIStr $INSTDIR\install.ini uninstall service false +SectionEnd + +Section /o "Install the service too (NT-only)" SEL_SERVICE + WriteINIStr $INSTDIR\install.ini uninstall service true + FileOpen $9 $INSTDIR\install.bat w + FileWrite $9 '"$SYSDIR\sc.exe" stop "TewiHTTPd"$\r$\n' + FileClose $9 + nsExec::Exec '"$INSTDIR\install.bat"' + Pop $0 + DetailPrint "Waiting for 1s so service can stop..." + Sleep 1000 + CreateDirectory "$INSTDIR\logs" + SetOutPath "$INSTDIR\bin" + File "../tewi.exe" + File /oname=tewisrv.exe "../tewi-service.exe" + FileOpen $9 $INSTDIR\install.bat w + FileWrite $9 '"$SYSDIR\sc.exe" delete "TewiHTTPd"$\r$\n' + FileWrite $9 '"$SYSDIR\sc.exe" create "TewiHTTPd" DisplayName= "Tewi HTTPd" binpath= "$INSTDIR\bin\tewisrv.exe" start= "auto"$\r$\n' + FileWrite $9 '"$SYSDIR\sc.exe" start "TewiHTTPd"$\r$\n' + FileClose $9 + nsExec::Exec '"$INSTDIR\install.bat"' + Pop $0 + Delete $INSTDIR\install.bat +SectionEnd + +Function .onInit + StrCpy $1 ${SEL_EXEC} +FunctionEnd + +Function .onSelChange + !insertmacro StartRadioButtons $1 + !insertmacro RadioButton ${SEL_EXEC} + !insertmacro RadioButton ${SEL_SERVICE} + !insertmacro EndRadioButtons +FunctionEnd + Section "Uninstall" + ReadINIStr $8 $INSTDIR\install.ini uninstall service + ${If} $8 == "true" + FileOpen $9 $INSTDIR\uninstall.bat w + FileWrite $9 '"$SYSDIR\sc.exe" stop "TewiHTTPd"$\r$\n' + FileClose $9 + nsExec::Exec '"$INSTDIR\uninstall.bat"' + Pop $0 + FileOpen $9 $INSTDIR\uninstall.bat w + DetailPrint "Waiting for 1s so service can stop..." + Sleep 1000 + FileWrite $9 '"$SYSDIR\sc.exe" delete "TewiHTTPd"$\r$\n' + FileClose $9 + nsExec::Exec '"$INSTDIR\uninstall.bat"' + Pop $0 + Delete $INSTDIR\uninstall.bat + ${EndIf} + RMDir /r "$INSTDIR" RMDir /r "$SMPROGRAMS\Tewi HTTPd" diff --git a/Server/main.c b/Server/main.c index bb7bf7a..6105f9c 100644 --- a/Server/main.c +++ b/Server/main.c @@ -14,6 +14,7 @@ #endif #include +#include #include "tw_config.h" #include "tw_server.h" @@ -25,45 +26,105 @@ extern bool cm_do_log; extern struct tw_config config; +extern FILE* logfile; char tw_server[2048]; +int startup(int argc, char** argv); + +#ifdef SERVICE +SERVICE_STATUS status; +SERVICE_STATUS_HANDLE status_handle; + +void WINAPI servhandler(DWORD control){ + switch(control){ + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + status.dwCurrentState = SERVICE_STOP_PENDING; + break; + } + SetServiceStatus(status_handle, &status); +} + +void WINAPI servmain(DWORD argc, LPSTR* argv){ + logfile = fopen(PREFIX "/logs/tewi.log", "a"); + if(logfile == NULL) logfile = stderr; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + status.dwCurrentState = SERVICE_START_PENDING; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + status.dwWin32ExitCode = NO_ERROR; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = 0; + status.dwWaitHint = 0; + status_handle = RegisterServiceCtrlHandler("Tewi HTTPd", servhandler); + if(status_handle == NULL) return; + if(SetServiceStatus(status_handle, &status) == 0) return; + int st = startup(argc, argv); + if(st != -1){ + status.dwWin32ExitCode = NO_ERROR; + status.dwServiceSpecificExitCode = st; + status.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(status_handle, &status); + return; + } + status.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus(status_handle, &status); + tw_server_loop(); + status.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(status_handle, &status); +} +#endif + int main(int argc, char** argv) { + logfile = stderr; +#ifdef SERVICE + SERVICE_TABLE_ENTRY table[] = {{"Tewi HTTPd", servmain}, {NULL, NULL}}; + StartServiceCtrlDispatcher(table); +#else + int st = startup(argc, argv); + if(st != -1) return st; + tw_server_loop(); +#endif +} + +int startup(int argc, char** argv){ int i; const char* confpath = PREFIX "/etc/tewi.conf"; - for(i = 1; i < argc; i++) { - if(argv[i][0] == '-') { - if(strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0) { - if(!cm_do_log) { - cm_do_log = true; -#ifndef NO_SSL - cm_log("", "This is Tewi HTTPd, version %s, using %s", tw_get_version(), OPENSSL_VERSION_TEXT); -#else - cm_log("", "This is Tewi HTTPd, version %s", tw_get_version()); -#endif + if(argv != NULL){ + for(i = 1; i < argc; i++) { + if(argv[i][0] == '-') { + if(strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0) { + if(!cm_do_log) { + cm_do_log = true; + #ifndef NO_SSL + cm_log("", "This is Tewi HTTPd, version %s, using %s", tw_get_version(), OPENSSL_VERSION_TEXT); + #else + cm_log("", "This is Tewi HTTPd, version %s", tw_get_version()); + #endif + } else { + cm_do_log = true; + } + } else if(strcmp(argv[i], "--config") == 0 || strcmp(argv[i], "-C") == 0) { + i++; + if(argv[i] == NULL) { + fprintf(stderr, "Missing argument\n"); + return 1; + } + confpath = argv[i]; + } else if(strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-V") == 0) { + printf("Tewi HTTPd Tewi/%s\n", tw_get_version()); + printf("Under public domain.\n"); + printf("Original by 2024 Nishi\n"); + printf("\n"); + printf("Usage: %s [--config|-C config] [--verbose|-v] [--version|-V]\n", argv[0]); + printf("--config | -C config : Specify config\n"); + printf("--verbose | -v : Verbose mode\n"); + printf("--version | -V : Version information\n"); + return 0; } else { - cm_do_log = true; - } - } else if(strcmp(argv[i], "--config") == 0 || strcmp(argv[i], "-C") == 0) { - i++; - if(argv[i] == NULL) { - fprintf(stderr, "Missing argument\n"); + fprintf(stderr, "Unknown option: %s\n", argv[i]); return 1; } - confpath = argv[i]; - } else if(strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-V") == 0) { - printf("Tewi HTTPd Tewi/%s\n", tw_get_version()); - printf("Under public domain.\n"); - printf("Original by 2024 Nishi\n"); - printf("\n"); - printf("Usage: %s [--config|-C config] [--verbose|-v] [--version|-V]\n", argv[0]); - printf("--config | -C config : Specify config\n"); - printf("--verbose | -v : Verbose mode\n"); - printf("--version | -V : Version information\n"); - return 0; - } else { - fprintf(stderr, "Unknown option: %s\n", argv[i]); - return 1; } } } @@ -77,11 +138,13 @@ int main(int argc, char** argv) { return 1; } sprintf(tw_server, "Tewi/%s (%s)%s", tw_get_version(), tw_get_platform(), config.extension == NULL ? "" : config.extension); - cm_log("Daemon", "Ready, server: %s", tw_server); + char* r = cm_strcat(tw_server, " running..."); + cm_force_log(r); + free(r); #ifndef __MINGW32__ signal(SIGCHLD, SIG_IGN); #else SetConsoleTitle(tw_server); #endif - tw_server_loop(); + return -1; } diff --git a/Server/server.c b/Server/server.c index 330ad67..1b35566 100644 --- a/Server/server.c +++ b/Server/server.c @@ -31,6 +31,7 @@ #ifdef __MINGW32__ #include #include +#include #include "strptime.h" #else @@ -696,6 +697,11 @@ cleanup: ; } +#ifdef SERVICE +extern SERVICE_STATUS status; +extern SERVICE_STATUS_HANDLE status_handle; +#endif + void tw_server_loop(void) { struct timeval tv; while(1) { @@ -709,6 +715,12 @@ void tw_server_loop(void) { int ret = select(FD_SETSIZE, &fdset, NULL, NULL, &tv); if(ret == -1) { break; + }else if(ret == 0){ +#ifdef SERVICE + if(status.dwCurrentState == SERVICE_STOP_PENDING){ + break; + } +#endif } else if(ret > 0) { /* connection */ int i; diff --git a/installer.sh b/installer.sh new file mode 100755 index 0000000..60bcba7 --- /dev/null +++ b/installer.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# $Id$ + +fail() { + rm -f tewi-service.exe + rm -f tewi.exe + exit 1 +} + +make clean || fail +make PLATFORM=$1 -j4 || fail +cp Server/tewi.exe tewi.exe +make clean || fail +make PLATFORM=$1-service -j4 || fail +cp Server/tewi.exe tewi-service.exe +cd Server +makensis install.nsi +rm -f tewi.exe tewi-service.exe +cd ..