688 lines
16 KiB
C
688 lines
16 KiB
C
/*
|
|
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
|
|
* All rights reserved
|
|
*
|
|
* "THE BEER-WARE LICENSE" (Revision 42):
|
|
* Sergey Lyubka wrote this file. As long as you retain this notice you
|
|
* can do whatever you want with this stuff. If we meet some day, and you think
|
|
* this stuff is worth it, you can buy me a beer in return.
|
|
*/
|
|
|
|
#include "defs.h"
|
|
|
|
static SERVICE_STATUS ss;
|
|
static SERVICE_STATUS_HANDLE hStatus;
|
|
static SERVICE_DESCRIPTION service_descr = {"Web server"};
|
|
|
|
static void
|
|
fix_directory_separators(char *path)
|
|
{
|
|
for (; *path != '\0'; path++) {
|
|
if (*path == '/')
|
|
*path = '\\';
|
|
if (*path == '\\')
|
|
while (path[1] == '\\' || path[1] == '/')
|
|
(void) memmove(path + 1,
|
|
path + 2, strlen(path + 2) + 1);
|
|
}
|
|
}
|
|
|
|
static int
|
|
protect_against_code_disclosure(const wchar_t *path)
|
|
{
|
|
WIN32_FIND_DATAW data;
|
|
HANDLE handle;
|
|
const wchar_t *p;
|
|
|
|
/*
|
|
* Protect against CGI code disclosure under Windows.
|
|
* This is very nasty hole. Windows happily opens files with
|
|
* some garbage in the end of file name. So fopen("a.cgi ", "r")
|
|
* actually opens "a.cgi", and does not return an error! And since
|
|
* "a.cgi " does not have valid CGI extension, this leads to
|
|
* the CGI code disclosure.
|
|
* To protect, here we delete all fishy characters from the
|
|
* end of file name.
|
|
*/
|
|
|
|
if ((handle = FindFirstFileW(path, &data)) == INVALID_HANDLE_VALUE)
|
|
return (FALSE);
|
|
|
|
FindClose(handle);
|
|
|
|
for (p = path + wcslen(path); p > path && p[-1] != L'\\';)
|
|
p--;
|
|
|
|
if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
|
wcscmp(data.cFileName, p) != 0)
|
|
return (FALSE);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
int
|
|
_shttpd_open(const char *path, int flags, int mode)
|
|
{
|
|
char buf[FILENAME_MAX];
|
|
wchar_t wbuf[FILENAME_MAX];
|
|
|
|
_shttpd_strlcpy(buf, path, sizeof(buf));
|
|
fix_directory_separators(buf);
|
|
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
|
|
|
|
if (protect_against_code_disclosure(wbuf) == FALSE)
|
|
return (-1);
|
|
|
|
return (_wopen(wbuf, flags));
|
|
}
|
|
|
|
int
|
|
_shttpd_stat(const char *path, struct stat *stp)
|
|
{
|
|
char buf[FILENAME_MAX], *p;
|
|
wchar_t wbuf[FILENAME_MAX];
|
|
|
|
_shttpd_strlcpy(buf, path, sizeof(buf));
|
|
fix_directory_separators(buf);
|
|
|
|
p = buf + strlen(buf) - 1;
|
|
while (p > buf && *p == '\\' && p[-1] != ':')
|
|
*p-- = '\0';
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
|
|
|
|
return (_wstat(wbuf, (struct _stat *) stp));
|
|
}
|
|
|
|
int
|
|
_shttpd_remove(const char *path)
|
|
{
|
|
char buf[FILENAME_MAX];
|
|
wchar_t wbuf[FILENAME_MAX];
|
|
|
|
_shttpd_strlcpy(buf, path, sizeof(buf));
|
|
fix_directory_separators(buf);
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
|
|
|
|
return (_wremove(wbuf));
|
|
}
|
|
|
|
int
|
|
_shttpd_rename(const char *path1, const char *path2)
|
|
{
|
|
char buf1[FILENAME_MAX];
|
|
char buf2[FILENAME_MAX];
|
|
wchar_t wbuf1[FILENAME_MAX];
|
|
wchar_t wbuf2[FILENAME_MAX];
|
|
|
|
_shttpd_strlcpy(buf1, path1, sizeof(buf1));
|
|
_shttpd_strlcpy(buf2, path2, sizeof(buf2));
|
|
fix_directory_separators(buf1);
|
|
fix_directory_separators(buf2);
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, buf1, -1, wbuf1, sizeof(wbuf1));
|
|
MultiByteToWideChar(CP_UTF8, 0, buf2, -1, wbuf2, sizeof(wbuf2));
|
|
|
|
return (_wrename(wbuf1, wbuf2));
|
|
}
|
|
|
|
int
|
|
_shttpd_mkdir(const char *path, int mode)
|
|
{
|
|
char buf[FILENAME_MAX];
|
|
wchar_t wbuf[FILENAME_MAX];
|
|
|
|
_shttpd_strlcpy(buf, path, sizeof(buf));
|
|
fix_directory_separators(buf);
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
|
|
|
|
return (_wmkdir(wbuf));
|
|
}
|
|
|
|
static char *
|
|
wide_to_utf8(const wchar_t *str)
|
|
{
|
|
char *buf = NULL;
|
|
if (str) {
|
|
int nchar = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
|
|
if (nchar > 0) {
|
|
buf = malloc(nchar);
|
|
if (!buf)
|
|
errno = ENOMEM;
|
|
else if (!WideCharToMultiByte(CP_UTF8, 0, str, -1, buf, nchar, NULL, NULL)) {
|
|
free(buf);
|
|
buf = NULL;
|
|
errno = EINVAL;
|
|
}
|
|
} else
|
|
errno = EINVAL;
|
|
} else
|
|
errno = EINVAL;
|
|
return buf;
|
|
}
|
|
|
|
char *
|
|
_shttpd_getcwd(char *buffer, int maxlen)
|
|
{
|
|
char *result = NULL;
|
|
wchar_t *wbuffer, *wresult;
|
|
|
|
if (buffer) {
|
|
/* User-supplied buffer */
|
|
wbuffer = malloc(maxlen * sizeof(wchar_t));
|
|
if (wbuffer == NULL)
|
|
return NULL;
|
|
} else
|
|
/* Dynamically allocated buffer */
|
|
wbuffer = NULL;
|
|
wresult = _wgetcwd(wbuffer, maxlen);
|
|
if (wresult) {
|
|
int err = errno;
|
|
if (buffer) {
|
|
/* User-supplied buffer */
|
|
int n = WideCharToMultiByte(CP_UTF8, 0, wresult, -1, buffer, maxlen, NULL, NULL);
|
|
if (n == 0)
|
|
err = ERANGE;
|
|
free(wbuffer);
|
|
result = buffer;
|
|
} else {
|
|
/* Buffer allocated by _wgetcwd() */
|
|
result = wide_to_utf8(wresult);
|
|
err = errno;
|
|
free(wresult);
|
|
}
|
|
errno = err;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DIR *
|
|
opendir(const char *name)
|
|
{
|
|
DIR *dir = NULL;
|
|
char path[FILENAME_MAX];
|
|
wchar_t wpath[FILENAME_MAX];
|
|
|
|
if (name == NULL || name[0] == '\0') {
|
|
errno = EINVAL;
|
|
} else if ((dir = malloc(sizeof(*dir))) == NULL) {
|
|
errno = ENOMEM;
|
|
} else {
|
|
_shttpd_snprintf(path, sizeof(path), "%s/*", name);
|
|
fix_directory_separators(path);
|
|
MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, sizeof(wpath));
|
|
dir->handle = FindFirstFileW(wpath, &dir->info);
|
|
|
|
if (dir->handle != INVALID_HANDLE_VALUE) {
|
|
dir->result.d_name[0] = '\0';
|
|
} else {
|
|
free(dir);
|
|
dir = NULL;
|
|
}
|
|
}
|
|
|
|
return (dir);
|
|
}
|
|
|
|
int
|
|
closedir(DIR *dir)
|
|
{
|
|
int result = -1;
|
|
|
|
if (dir != NULL) {
|
|
if (dir->handle != INVALID_HANDLE_VALUE)
|
|
result = FindClose(dir->handle) ? 0 : -1;
|
|
|
|
free(dir);
|
|
}
|
|
|
|
if (result == -1)
|
|
errno = EBADF;
|
|
|
|
return (result);
|
|
}
|
|
|
|
struct dirent *
|
|
readdir(DIR *dir)
|
|
{
|
|
struct dirent *result = 0;
|
|
|
|
if (dir && dir->handle != INVALID_HANDLE_VALUE) {
|
|
if(!dir->result.d_name ||
|
|
FindNextFileW(dir->handle, &dir->info)) {
|
|
result = &dir->result;
|
|
|
|
WideCharToMultiByte(CP_UTF8, 0, dir->info.cFileName,
|
|
-1, result->d_name,
|
|
sizeof(result->d_name), NULL, NULL);
|
|
}
|
|
} else {
|
|
errno = EBADF;
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
int
|
|
_shttpd_set_non_blocking_mode(int fd)
|
|
{
|
|
unsigned long on = 1;
|
|
|
|
return (ioctlsocket(fd, FIONBIO, &on));
|
|
}
|
|
|
|
void
|
|
_shttpd_set_close_on_exec(int fd)
|
|
{
|
|
fd = 0; /* Do nothing. There is no FD_CLOEXEC on Windows */
|
|
}
|
|
|
|
#if !defined(NO_CGI)
|
|
|
|
struct threadparam {
|
|
SOCKET s;
|
|
HANDLE hPipe;
|
|
big_int_t content_len;
|
|
};
|
|
|
|
|
|
enum ready_mode_t {IS_READY_FOR_READ, IS_READY_FOR_WRITE};
|
|
|
|
/*
|
|
* Wait until given socket is in ready state. Always return TRUE.
|
|
*/
|
|
static int
|
|
is_socket_ready(int sock, enum ready_mode_t mode)
|
|
{
|
|
fd_set read_set, write_set;
|
|
|
|
FD_ZERO(&read_set);
|
|
FD_ZERO(&write_set);
|
|
|
|
if (mode == IS_READY_FOR_READ)
|
|
FD_SET(sock, &read_set);
|
|
else
|
|
FD_SET(sock, &write_set);
|
|
|
|
select(sock + 1, &read_set, &write_set, NULL, NULL);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
/*
|
|
* Thread function that reads POST data from the socket pair
|
|
* and writes it to the CGI process.
|
|
*/
|
|
static void//DWORD WINAPI
|
|
stdoutput(void *arg)
|
|
{
|
|
struct threadparam *tp = arg;
|
|
int n, sent, stop = 0;
|
|
big_int_t total = 0;
|
|
DWORD k;
|
|
char buf[BUFSIZ];
|
|
size_t max_recv;
|
|
|
|
max_recv = min(sizeof(buf), tp->content_len - total);
|
|
while (!stop &&
|
|
max_recv > 0 &&
|
|
is_socket_ready(tp->s, IS_READY_FOR_READ) &&
|
|
(n = recv(tp->s, buf, max_recv, 0)) > 0) {
|
|
if (n == -1 && ERRNO == EWOULDBLOCK)
|
|
continue;
|
|
for (sent = 0; !stop && sent < n; sent += k)
|
|
if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0))
|
|
stop++;
|
|
total += n;
|
|
max_recv = min(sizeof(buf), tp->content_len - total);
|
|
}
|
|
|
|
CloseHandle(tp->hPipe); /* Suppose we have POSTed everything */
|
|
free(tp);
|
|
}
|
|
|
|
/*
|
|
* Thread function that reads CGI output and pushes it to the socket pair.
|
|
*/
|
|
static void
|
|
stdinput(void *arg)
|
|
{
|
|
struct threadparam *tp = arg;
|
|
static int ntotal;
|
|
int k, stop = 0;
|
|
DWORD n, sent;
|
|
char buf[BUFSIZ];
|
|
|
|
while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) {
|
|
ntotal += n;
|
|
for (sent = 0; !stop && sent < n; sent += k) {
|
|
if (is_socket_ready(tp->s, IS_READY_FOR_WRITE) &&
|
|
(k = send(tp->s, buf + sent, n - sent, 0)) <= 0) {
|
|
if (k == -1 && ERRNO == EWOULDBLOCK) {
|
|
k = 0;
|
|
continue;
|
|
}
|
|
stop++;
|
|
}
|
|
}
|
|
}
|
|
CloseHandle(tp->hPipe);
|
|
|
|
/*
|
|
* Windows is a piece of crap. When this thread closes its end
|
|
* of the socket pair, the other end (get_cgi() function) may loose
|
|
* some data. I presume, this happens if get_cgi() is not fast enough,
|
|
* and the data written by this end does not "push-ed" to the other
|
|
* end socket buffer. So after closesocket() the remaining data is
|
|
* gone. If I put shutdown() before closesocket(), that seems to
|
|
* fix the problem, but I am not sure this is the right fix.
|
|
* XXX (submitted by James Marshall) we do not do shutdown() on UNIX.
|
|
* If fork() is called from user callback, shutdown() messes up things.
|
|
*/
|
|
shutdown(tp->s, 2);
|
|
|
|
closesocket(tp->s);
|
|
free(tp);
|
|
|
|
_endthread();
|
|
}
|
|
|
|
static void
|
|
spawn_stdio_thread(int sock, HANDLE hPipe, void (*func)(void *),
|
|
big_int_t content_len)
|
|
{
|
|
struct threadparam *tp;
|
|
DWORD tid;
|
|
|
|
tp = malloc(sizeof(*tp));
|
|
assert(tp != NULL);
|
|
|
|
tp->s = sock;
|
|
tp->hPipe = hPipe;
|
|
tp->content_len = content_len;
|
|
_beginthread(func, 0, tp);
|
|
}
|
|
|
|
int
|
|
_shttpd_spawn_process(struct conn *c, const char *prog, char *envblk,
|
|
char *envp[], int sock, const char *dir)
|
|
{
|
|
HANDLE a[2], b[2], h[2], me;
|
|
DWORD flags;
|
|
char *p, *interp, cmdline[FILENAME_MAX], line[FILENAME_MAX];
|
|
FILE *fp;
|
|
STARTUPINFOA si;
|
|
PROCESS_INFORMATION pi;
|
|
|
|
me = GetCurrentProcess();
|
|
flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS;
|
|
|
|
/* FIXME add error checking code here */
|
|
CreatePipe(&a[0], &a[1], NULL, 0);
|
|
CreatePipe(&b[0], &b[1], NULL, 0);
|
|
DuplicateHandle(me, a[0], me, &h[0], 0, TRUE, flags);
|
|
DuplicateHandle(me, b[1], me, &h[1], 0, TRUE, flags);
|
|
|
|
(void) memset(&si, 0, sizeof(si));
|
|
(void) memset(&pi, 0, sizeof(pi));
|
|
|
|
/* XXX redirect CGI errors to the error log file */
|
|
si.cb = sizeof(si);
|
|
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
|
si.wShowWindow = SW_HIDE;
|
|
si.hStdOutput = h[1];
|
|
si.hStdInput = h[0];
|
|
|
|
/* If CGI file is a script, try to read the interpreter line */
|
|
interp = c->ctx->options[OPT_CGI_INTERPRETER];
|
|
if (interp == NULL) {
|
|
if ((fp = fopen(prog, "r")) != NULL) {
|
|
(void) fgets(line, sizeof(line), fp);
|
|
if (memcmp(line, "#!", 2) != 0)
|
|
line[2] = '\0';
|
|
/* Trim whitespaces from interpreter name */
|
|
for (p = &line[strlen(line) - 1]; p > line &&
|
|
isspace(*p); p--)
|
|
*p = '\0';
|
|
(void) fclose(fp);
|
|
}
|
|
interp = line + 2;
|
|
(void) _shttpd_snprintf(cmdline, sizeof(cmdline), "%s%s%s",
|
|
line + 2, line[2] == '\0' ? "" : " ", prog);
|
|
}
|
|
|
|
if ((p = strrchr(prog, '/')) != NULL)
|
|
prog = p + 1;
|
|
|
|
(void) _shttpd_snprintf(cmdline, sizeof(cmdline), "%s %s", interp, prog);
|
|
|
|
(void) _shttpd_snprintf(line, sizeof(line), "%s", dir);
|
|
fix_directory_separators(line);
|
|
fix_directory_separators(cmdline);
|
|
|
|
/*
|
|
* Spawn reader & writer threads before we create CGI process.
|
|
* Otherwise CGI process may die too quickly, loosing the data
|
|
*/
|
|
spawn_stdio_thread(sock, b[0], stdinput, 0);
|
|
spawn_stdio_thread(sock, a[1], stdoutput, c->rem.content_len);
|
|
|
|
if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
|
|
CREATE_NEW_PROCESS_GROUP, envblk, line, &si, &pi) == 0) {
|
|
_shttpd_elog(E_LOG, c,
|
|
"redirect: CreateProcess(%s): %d", cmdline, ERRNO);
|
|
return (-1);
|
|
} else {
|
|
CloseHandle(h[0]);
|
|
CloseHandle(h[1]);
|
|
CloseHandle(pi.hThread);
|
|
CloseHandle(pi.hProcess);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
#endif /* !NO_CGI */
|
|
|
|
#define ID_TRAYICON 100
|
|
#define ID_QUIT 101
|
|
static NOTIFYICONDATA ni;
|
|
|
|
static LRESULT CALLBACK
|
|
WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
POINT pt;
|
|
HMENU hMenu;
|
|
|
|
switch (msg) {
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case ID_QUIT:
|
|
exit(EXIT_SUCCESS);
|
|
break;
|
|
}
|
|
break;
|
|
case WM_USER:
|
|
switch (lParam) {
|
|
case WM_RBUTTONUP:
|
|
case WM_LBUTTONUP:
|
|
case WM_LBUTTONDBLCLK:
|
|
hMenu = CreatePopupMenu();
|
|
AppendMenu(hMenu, 0, ID_QUIT, "Exit SHTTPD");
|
|
GetCursorPos(&pt);
|
|
TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, hWnd, NULL);
|
|
DestroyMenu(hMenu);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return (DefWindowProc(hWnd, msg, wParam, lParam));
|
|
}
|
|
|
|
static void
|
|
systray(void *arg)
|
|
{
|
|
WNDCLASS cls;
|
|
HWND hWnd;
|
|
MSG msg;
|
|
|
|
(void) memset(&cls, 0, sizeof(cls));
|
|
|
|
cls.lpfnWndProc = (WNDPROC) WindowProc;
|
|
cls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
|
|
cls.lpszClassName = "shttpd v." SHTTPD_VERSION;
|
|
|
|
if (!RegisterClass(&cls))
|
|
_shttpd_elog(E_FATAL, NULL, "RegisterClass: %d", ERRNO);
|
|
else if ((hWnd = CreateWindow(cls.lpszClassName, "",
|
|
WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, NULL, arg)) == NULL)
|
|
_shttpd_elog(E_FATAL, NULL, "CreateWindow: %d", ERRNO);
|
|
ShowWindow(hWnd, SW_HIDE);
|
|
|
|
ni.cbSize = sizeof(ni);
|
|
ni.uID = ID_TRAYICON;
|
|
ni.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
|
ni.hIcon = LoadIcon(NULL, IDI_APPLICATION);
|
|
ni.hWnd = hWnd;
|
|
_shttpd_snprintf(ni.szTip, sizeof(ni.szTip), "SHTTPD web server");
|
|
ni.uCallbackMessage = WM_USER;
|
|
Shell_NotifyIcon(NIM_ADD, &ni);
|
|
|
|
while (GetMessage(&msg, hWnd, 0, 0)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
int
|
|
_shttpd_set_systray(struct shttpd_ctx *ctx, const char *opt)
|
|
{
|
|
HWND hWnd;
|
|
char title[512];
|
|
static WNDPROC oldproc;
|
|
|
|
if (!_shttpd_is_true(opt))
|
|
return (TRUE);
|
|
|
|
FreeConsole();
|
|
GetConsoleTitle(title, sizeof(title));
|
|
hWnd = FindWindow(NULL, title);
|
|
ShowWindow(hWnd, SW_HIDE);
|
|
_beginthread(systray, 0, hWnd);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
int
|
|
_shttpd_set_nt_service(struct shttpd_ctx *ctx, const char *action)
|
|
{
|
|
SC_HANDLE hSCM, hService;
|
|
char path[FILENAME_MAX], key[128];
|
|
HKEY hKey;
|
|
DWORD dwData;
|
|
|
|
|
|
if (!strcmp(action, "install")) {
|
|
if ((hSCM = OpenSCManager(NULL, NULL,
|
|
SC_MANAGER_ALL_ACCESS)) == NULL)
|
|
_shttpd_elog(E_FATAL, NULL, "Error opening SCM (%d)", ERRNO);
|
|
|
|
GetModuleFileName(NULL, path, sizeof(path));
|
|
|
|
hService = CreateService(hSCM, SERVICE_NAME, SERVICE_NAME,
|
|
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
|
|
SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path,
|
|
NULL, NULL, NULL, NULL, NULL);
|
|
|
|
if (!hService)
|
|
_shttpd_elog(E_FATAL, NULL,
|
|
"Error installing service (%d)", ERRNO);
|
|
|
|
ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION,
|
|
&service_descr);
|
|
_shttpd_elog(E_FATAL, NULL, "Service successfully installed");
|
|
|
|
|
|
} else if (!strcmp(action, "uninstall")) {
|
|
|
|
if ((hSCM = OpenSCManager(NULL, NULL,
|
|
SC_MANAGER_ALL_ACCESS)) == NULL) {
|
|
_shttpd_elog(E_FATAL, NULL, "Error opening SCM (%d)", ERRNO);
|
|
} else if ((hService = OpenService(hSCM,
|
|
SERVICE_NAME, DELETE)) == NULL) {
|
|
_shttpd_elog(E_FATAL, NULL,
|
|
"Error opening service (%d)", ERRNO);
|
|
} else if (!DeleteService(hService)) {
|
|
_shttpd_elog(E_FATAL, NULL,
|
|
"Error deleting service (%d)", ERRNO);
|
|
} else {
|
|
_shttpd_elog(E_FATAL, NULL, "Service deleted");
|
|
}
|
|
|
|
} else {
|
|
_shttpd_elog(E_FATAL, NULL, "Use -service <install|uninstall>");
|
|
}
|
|
|
|
/* NOTREACHED */
|
|
return (TRUE);
|
|
}
|
|
|
|
static void WINAPI
|
|
ControlHandler(DWORD code)
|
|
{
|
|
if (code == SERVICE_CONTROL_STOP || code == SERVICE_CONTROL_SHUTDOWN) {
|
|
ss.dwWin32ExitCode = 0;
|
|
ss.dwCurrentState = SERVICE_STOPPED;
|
|
}
|
|
|
|
SetServiceStatus(hStatus, &ss);
|
|
}
|
|
|
|
static void WINAPI
|
|
ServiceMain(int argc, char *argv[])
|
|
{
|
|
char path[MAX_PATH], *p, *av[] = {"shttpd_service", path, NULL};
|
|
struct shttpd_ctx *ctx;
|
|
|
|
ss.dwServiceType = SERVICE_WIN32;
|
|
ss.dwCurrentState = SERVICE_RUNNING;
|
|
ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
|
|
|
hStatus = RegisterServiceCtrlHandler(SERVICE_NAME, ControlHandler);
|
|
SetServiceStatus(hStatus, &ss);
|
|
|
|
GetModuleFileName(NULL, path, sizeof(path));
|
|
|
|
if ((p = strrchr(path, DIRSEP)) != NULL)
|
|
*++p = '\0';
|
|
|
|
strcat(path, CONFIG_FILE); /* woo ! */
|
|
|
|
ctx = shttpd_init(NELEMS(av) - 1, av);
|
|
if ((ctx = shttpd_init(NELEMS(av) - 1, av)) == NULL)
|
|
_shttpd_elog(E_FATAL, NULL, "Cannot initialize SHTTP context");
|
|
|
|
while (ss.dwCurrentState == SERVICE_RUNNING)
|
|
shttpd_poll(ctx, INT_MAX);
|
|
shttpd_fini(ctx);
|
|
|
|
ss.dwCurrentState = SERVICE_STOPPED;
|
|
ss.dwWin32ExitCode = -1;
|
|
SetServiceStatus(hStatus, &ss);
|
|
}
|
|
|
|
void
|
|
try_to_run_as_nt_service(void)
|
|
{
|
|
static SERVICE_TABLE_ENTRY service_table[] = {
|
|
{SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
if (StartServiceCtrlDispatcher(service_table))
|
|
exit(EXIT_SUCCESS);
|
|
}
|