diff --git a/src/launcher/launchctl.c b/src/launcher/launchctl.c index 1c906749..bfe92e12 100644 --- a/src/launcher/launchctl.c +++ b/src/launcher/launchctl.c @@ -19,12 +19,268 @@ #define PROGNAME "launchctl" +#define fatal(ExitCode, format, ...) (warn(format, __VA_ARGS__), ExitProcess(ExitCode)) + +static void vwarn(const char *format, va_list ap) +{ + char buf[1024]; + /* wvsprintf is only safe with a 1024 byte buffer */ + size_t len; + DWORD BytesTransferred; + + wvsprintfA(buf, format, ap); + buf[sizeof buf - 1] = '\0'; + + len = lstrlenA(buf); + buf[len++] = '\n'; + + WriteFile(GetStdHandle(STD_ERROR_HANDLE), + buf, (DWORD)len, &BytesTransferred, 0); +} + +static void warn(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vwarn(format, ap); + va_end(ap); +} + +static void usage(void) +{ + fatal(ERROR_INVALID_PARAMETER, + "usage: %s COMMAND ARGS\n" + "\n" + "commands:\n" + " start ClassName InstanceName Args...\n" + " stop InstanceName\n" + " list\n" + " info\n", + PROGNAME); +} + +static void report(PWSTR PipeBuf, ULONG PipeBufSize) +{ + if (0 == PipeBufSize) + warn("KO received empty buffer from launcher"); + else if (L'$' == PipeBuf[0]) + { + if (1 == PipeBufSize) + warn("OK"); + else + { + for (PWSTR P = PipeBuf, PipeBufEnd = P + PipeBufSize / sizeof(WCHAR); PipeBufEnd > P; P++) + if (L'\0' == *P) + *P = L'\n'; + + if (PipeBufSize < PIPE_BUFFER_SIZE) + PipeBuf[PipeBufSize / sizeof(WCHAR)] = L'\0'; + else + PipeBuf[PIPE_BUFFER_SIZE / sizeof(WCHAR) - 1] = L'\0'; + + warn("OK\n%S", PipeBuf + 1); + } + } + else if (L'!' == PipeBuf[0]) + { + if (PipeBufSize < PIPE_BUFFER_SIZE) + PipeBuf[PipeBufSize / sizeof(WCHAR)] = L'\0'; + else + PipeBuf[PIPE_BUFFER_SIZE / sizeof(WCHAR) - 1] = L'\0'; + + warn("KO %S", PipeBuf + 1); + } + else + warn("KO received corrupted buffer from launcher", 0); +} + +int start(PWSTR PipeBuf, ULONG PipeBufSize, + PWSTR ClassName, PWSTR InstanceName, DWORD Argc, PWSTR *Argv) +{ + PWSTR P; + DWORD ClassNameSize, InstanceNameSize, ArgvSize; + DWORD LastError, BytesTransferred; + + ClassNameSize = lstrlenW(ClassName) + 1; + InstanceNameSize = lstrlenW(InstanceName) + 1; + ArgvSize = 0; + for (DWORD Argi = Argc; Argc > Argi; Argi++) + ArgvSize += lstrlenW(Argv[Argi]); + + if (PipeBufSize < (1 + ClassNameSize + InstanceNameSize + ArgvSize) * sizeof(WCHAR)) + return ERROR_INVALID_PARAMETER; + + P = PipeBuf; + *P++ = LauncherSvcInstanceStart; + memcpy(P, ClassName, ClassNameSize * sizeof(WCHAR)); P += ClassNameSize; + memcpy(P, InstanceName, InstanceNameSize * sizeof(WCHAR)); P += InstanceNameSize; + for (DWORD Argi = Argc; Argc > Argi; Argi++) + { + ArgvSize = lstrlenW(Argv[Argi]); + memcpy(P, Argv[Argi], ArgvSize * sizeof(WCHAR)); P += ArgvSize; + } + + if (CallNamedPipeW(L"" PIPE_NAME, + PipeBuf, (DWORD)((P - PipeBuf) * sizeof(WCHAR)), PipeBuf, PipeBufSize, + &BytesTransferred, NMPWAIT_USE_DEFAULT_WAIT)) + { + LastError = 0; + report(PipeBuf, BytesTransferred); + } + else + LastError = GetLastError(); + + return LastError; +} + +int stop(PWSTR PipeBuf, ULONG PipeBufSize, + PWSTR InstanceName) +{ + PWSTR P; + DWORD InstanceNameSize; + DWORD LastError, BytesTransferred; + + InstanceNameSize = lstrlenW(InstanceName) + 1; + + if (PipeBufSize < (1 + InstanceNameSize) * sizeof(WCHAR)) + return ERROR_INVALID_PARAMETER; + + P = PipeBuf; + *P++ = LauncherSvcInstanceStop; + memcpy(P, InstanceName, InstanceNameSize * sizeof(WCHAR)); + + if (CallNamedPipeW(L"" PIPE_NAME, + PipeBuf, (DWORD)((P - PipeBuf) * sizeof(WCHAR)), PipeBuf, PipeBufSize, + &BytesTransferred, NMPWAIT_USE_DEFAULT_WAIT)) + { + LastError = 0; + report(PipeBuf, BytesTransferred); + } + else + LastError = GetLastError(); + + return LastError; +} + +int list(PWSTR PipeBuf, ULONG PipeBufSize) +{ + PWSTR P; + DWORD LastError, BytesTransferred; + + if (PipeBufSize < 1 * sizeof(WCHAR)) + return ERROR_INVALID_PARAMETER; + + P = PipeBuf; + *P++ = LauncherSvcInstanceList; + + if (CallNamedPipeW(L"" PIPE_NAME, + PipeBuf, (DWORD)((P - PipeBuf) * sizeof(WCHAR)), PipeBuf, PipeBufSize, + &BytesTransferred, NMPWAIT_USE_DEFAULT_WAIT)) + { + LastError = 0; + report(PipeBuf, BytesTransferred); + } + else + LastError = GetLastError(); + + return LastError; +} + +int info(PWSTR PipeBuf, ULONG PipeBufSize, + PWSTR InstanceName) +{ + PWSTR P; + DWORD InstanceNameSize; + DWORD LastError, BytesTransferred; + + InstanceNameSize = lstrlenW(InstanceName) + 1; + + if (PipeBufSize < (1 + InstanceNameSize) * sizeof(WCHAR)) + return ERROR_INVALID_PARAMETER; + + P = PipeBuf; + *P++ = LauncherSvcInstanceInfo; + memcpy(P, InstanceName, InstanceNameSize * sizeof(WCHAR)); + + if (CallNamedPipeW(L"" PIPE_NAME, + PipeBuf, (DWORD)((P - PipeBuf) * sizeof(WCHAR)), PipeBuf, PipeBufSize, + &BytesTransferred, NMPWAIT_USE_DEFAULT_WAIT)) + { + LastError = 0; + report(PipeBuf, BytesTransferred); + } + else + LastError = GetLastError(); + + return LastError; +} + int wmain(int argc, wchar_t **argv) { + PWSTR PipeBuf = 0; + + /* allocate our PipeBuf early on; freed on process exit by the system */ + PipeBuf = MemAlloc(PIPE_BUFFER_SIZE); + if (0 == PipeBuf) + return ERROR_NO_SYSTEM_RESOURCES; + + argc--; + argv++; + + if (0 == argc) + usage(); + + if (0 == lstrcmp(L"start", argv[0])) + { + if (3 > argc || argc > 12) + usage(); + + return start(PipeBuf, PIPE_BUFFER_SIZE, argv[1], argv[2], argc - 3, argv + 3); + } + else + if (0 == lstrcmp(L"stop", argv[0])) + { + if (2 != argc) + usage(); + + return stop(PipeBuf, PIPE_BUFFER_SIZE, argv[1]); + } + if (0 == lstrcmp(L"list", argv[0])) + { + if (1 != argc) + usage(); + + return list(PipeBuf, PIPE_BUFFER_SIZE); + } + if (0 == lstrcmp(L"info", argv[0])) + { + if (2 != argc) + usage(); + + return info(PipeBuf, PIPE_BUFFER_SIZE, argv[1]); + } + else + usage(); + return 0; } -int wmainCRTStartup(void) +void wmainCRTStartup(void) { - return wmain(0, 0); + DWORD Argc; + PWSTR *Argv; + + extern HANDLE ProcessHeap; + ProcessHeap = GetProcessHeap(); + if (0 == ProcessHeap) + ExitProcess(GetLastError()); + + Argv = CommandLineToArgvW(GetCommandLineW(), &Argc); + if (0 == Argv) + ExitProcess(GetLastError()); + + ExitProcess(wmain(Argc, Argv)); } + +HANDLE ProcessHeap; diff --git a/src/launcher/launcher.c b/src/launcher/launcher.c index ee53d342..06146f30 100644 --- a/src/launcher/launcher.c +++ b/src/launcher/launcher.c @@ -20,8 +20,6 @@ #define PROGNAME "WinFsp-Launcher" #define REGKEY "SYSTEM\\CurrentControlSet\\Services\\" PROGNAME "\\Services" -HANDLE ProcessHeap; - typedef struct { PWSTR ClassName; @@ -367,7 +365,7 @@ static NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, - 1, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, 0, 0); + 1, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, PIPE_DEFAULT_TIMEOUT, 0); if (INVALID_HANDLE_VALUE == SvcPipe) goto fail; @@ -630,13 +628,17 @@ static VOID SvcPipeTransact(PWSTR PipeBuf, PULONG PSize) int wmain(int argc, wchar_t **argv) { - ProcessHeap = GetProcessHeap(); - if (0 == ProcessHeap) - return GetLastError(); return FspServiceRun(L"" PROGNAME, SvcStart, SvcStop, 0); } -int wmainCRTStartup(void) +void wmainCRTStartup(void) { - return wmain(0, 0); + extern HANDLE ProcessHeap; + ProcessHeap = GetProcessHeap(); + if (0 == ProcessHeap) + ExitProcess(GetLastError()); + + ExitProcess(wmain(0, 0)); } + +HANDLE ProcessHeap; diff --git a/src/launcher/launcher.h b/src/launcher/launcher.h index 551b33e9..d967a275 100644 --- a/src/launcher/launcher.h +++ b/src/launcher/launcher.h @@ -23,6 +23,7 @@ #define PIPE_NAME "\\\\.\\pipe\\WinFsp.{14E7137D-22B4-437A-B0C1-D21D1BDF3767}" #define PIPE_BUFFER_SIZE 2048 +#define PIPE_DEFAULT_TIMEOUT 3000 enum {