mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-11-23 20:36:33 +01:00
474 lines
16 KiB
C++
474 lines
16 KiB
C++
|
/* Copyright (c) Citrix Systems Inc.
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms,
|
||
|
* with or without modification, are permitted provided
|
||
|
* that the following conditions are met:
|
||
|
*
|
||
|
* * Redistributions of source code must retain the above
|
||
|
* copyright notice, this list of conditions and the
|
||
|
* following disclaimer.
|
||
|
* * Redistributions in binary form must reproduce the above
|
||
|
* copyright notice, this list of conditions and the
|
||
|
* following disclaimer in the documentation and/or other
|
||
|
* materials provided with the distribution.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||
|
* SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* main.cpp
|
||
|
*
|
||
|
* Loads a splash screen bitmap from the resources built into the
|
||
|
* executable. Writes the current XenCenter version numbers onto
|
||
|
* the bitmap, and displays it on screen. Waits for a message
|
||
|
* from XenCenter, and then quits. Also monitors the XenCenter
|
||
|
* process, and quits if it dies (in case XenCenter crashes
|
||
|
* before sending the message).
|
||
|
*
|
||
|
* Parts of code taken from msdn.
|
||
|
*/
|
||
|
|
||
|
// Disable deprecation warnings in the CRT
|
||
|
#define _CRT_SECURE_NO_DEPRECATE
|
||
|
#include <windows.h>
|
||
|
#include <iostream>
|
||
|
#include <sstream>
|
||
|
#include <Tchar.h>
|
||
|
#include <strsafe.h>
|
||
|
#include <vector>
|
||
|
#include <Lmcons.h>
|
||
|
#include <fstream>
|
||
|
#include <ctime>
|
||
|
#include "resource.h"
|
||
|
#include "util.h"
|
||
|
|
||
|
using namespace std;
|
||
|
|
||
|
// Our own made-up IDs for timers
|
||
|
const int ShortTimerId = 1;
|
||
|
const int LongTimerId = 2;
|
||
|
|
||
|
const int ShortTimerInterval = 100;
|
||
|
// How long to wait for XenCenterMain to start
|
||
|
const int LongTimerInterval = 30000;
|
||
|
|
||
|
const int PipeTimeout = 60 * 1000;
|
||
|
|
||
|
// How long the splash should try to acquire the splashscreen lock for
|
||
|
const int SplashLockMaxWait = 60000;
|
||
|
// How many ms the splash screen should wait between attempts
|
||
|
const int SplashLockSleepInterval = 250;
|
||
|
|
||
|
// Size of the splash bitmap
|
||
|
const int ImageSizeX = 415;
|
||
|
const int ImageSizeY = 217;
|
||
|
|
||
|
const TCHAR SplashClassName[] = TEXT("XenCenterSplash0001");
|
||
|
const TCHAR PipeStub[] = TEXT("\\\\.\\pipe\\XenCenter-");
|
||
|
const TCHAR SplashPipeStub[] = TEXT("\\\\.\\pipe\\XenCenterSplash-");
|
||
|
// The path to the main C# XenCenter exe, relative to the location of the splash exe.
|
||
|
const TCHAR XenCenterPath[] = TEXT("XenCenterMain.exe");
|
||
|
const size_t PathLen = 17;
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
const TCHAR ProductVersion[] = TEXT("0.0");
|
||
|
const TCHAR ProductBuild[] = TEXT("0000");
|
||
|
#else
|
||
|
const TCHAR ProductVersion[] = TEXT("@PRODUCT_VERSION@");
|
||
|
const TCHAR ProductBuild[] = TEXT("@BUILD_NUMBER@");
|
||
|
#endif
|
||
|
|
||
|
// The in-memory Device Context
|
||
|
HDC memdc;
|
||
|
|
||
|
PROCESS_INFORMATION pi;
|
||
|
|
||
|
static wstring PipeName(const TCHAR * stub, wstring mainExePath)
|
||
|
{
|
||
|
// Replace '\' with '-' in mainExePath
|
||
|
wstring sanitizedMainExePath(mainExePath);
|
||
|
{
|
||
|
size_t index = sanitizedMainExePath.npos;
|
||
|
while ((index = sanitizedMainExePath.find('\\', 0)) != sanitizedMainExePath.npos)
|
||
|
{
|
||
|
sanitizedMainExePath.replace(index, 1, 1, '-');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DWORD tmp = UNLEN + 1;
|
||
|
TCHAR UserName[UNLEN + 1];
|
||
|
GetUserName(UserName, &tmp);
|
||
|
|
||
|
DWORD pid = GetCurrentProcessId();
|
||
|
DWORD sid = 0;
|
||
|
if (0 == ProcessIdToSessionId(pid, &sid))
|
||
|
{
|
||
|
// Ignore error and force sid to 0.
|
||
|
sid = 0;
|
||
|
}
|
||
|
|
||
|
wostringstream SplashPipePath;
|
||
|
SplashPipePath << stub << sid << '-' << UserName << '-' << sanitizedMainExePath;
|
||
|
|
||
|
wstring s = SplashPipePath.str();
|
||
|
// Max length of named pipe name string is 256 chars
|
||
|
if (s.length() > 256)
|
||
|
{
|
||
|
s.resize(256);
|
||
|
}
|
||
|
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
switch(msg)
|
||
|
{
|
||
|
case WM_CLOSE:
|
||
|
DestroyWindow(hwnd);
|
||
|
break;
|
||
|
case WM_DESTROY:
|
||
|
// Hide the window - will happen anyway if we exit, but not until
|
||
|
// after XenCenterMain exits if --wait was specified.
|
||
|
ShowWindow(hwnd, SW_HIDE);
|
||
|
// Line below sends a WM_QUIT message to this thread
|
||
|
PostQuitMessage(0);
|
||
|
break;
|
||
|
case WM_LBUTTONDOWN:
|
||
|
ShowWindow(hwnd, SW_HIDE);
|
||
|
break;
|
||
|
case WM_PAINT:
|
||
|
{
|
||
|
PAINTSTRUCT ps;
|
||
|
HDC screendc = BeginPaint(hwnd, &ps);
|
||
|
BitBlt(screendc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom,
|
||
|
memdc, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
|
||
|
EndPaint(hwnd, &ps);
|
||
|
}
|
||
|
break;
|
||
|
case WM_TIMER:
|
||
|
{
|
||
|
switch (wParam)
|
||
|
{
|
||
|
case ShortTimerId:
|
||
|
if (WaitForSingleObject(pi.hProcess, 0) != WAIT_TIMEOUT)
|
||
|
{
|
||
|
// XenCenter has closed (e.g. crashed) without killing the splash screen: exit.
|
||
|
KillTimer(hwnd, ShortTimerId);
|
||
|
DestroyWindow(hwnd);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Poll again later
|
||
|
SetTimer(hwnd, ShortTimerId, ShortTimerInterval, NULL);
|
||
|
}
|
||
|
break;
|
||
|
case LongTimerId:
|
||
|
// We've been open too long: close even though we haven't heard from XenCenter
|
||
|
DestroyWindow(hwnd);
|
||
|
break;
|
||
|
default:
|
||
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||
|
}
|
||
|
}
|
||
|
default:
|
||
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
|
||
|
{
|
||
|
UNREFERENCED_PARAMETER(hPrevInstance);
|
||
|
|
||
|
// Record if an error that doesn't prevent us launching XenCenterMain has occurred.
|
||
|
// If set to true, we write the splash log to file and launch XenCenterMain, even
|
||
|
// if the splash screen locking or named pipe argument passing have failed.
|
||
|
bool nonCriticalError = false;
|
||
|
|
||
|
// Open logging output stream. The accumulated contents of this stream are written
|
||
|
// to a log file only in the event of an ErrorExit.
|
||
|
wostringstream logStream;
|
||
|
time_t rawtime = time(NULL);
|
||
|
if (rawtime > -1)
|
||
|
{
|
||
|
logStream << TEXT("splash .exe started at ") << ctime(&rawtime) << endl;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
logStream << TEXT("WARNING: time() returned -1") << endl;
|
||
|
}
|
||
|
|
||
|
// Get the full path to the splash exe
|
||
|
const size_t pathBufSize = 128 * 1024;
|
||
|
wchar_t splashExePath[pathBufSize];
|
||
|
DWORD pathLen = GetModuleFileName(NULL, splashExePath, pathBufSize);
|
||
|
if (pathLen == 0 || pathLen == pathBufSize)
|
||
|
{
|
||
|
ErrorExit(logStream, TEXT("GetModuleFileName"), true);
|
||
|
}
|
||
|
logStream << TEXT("splashExePath: ") << splashExePath << endl;
|
||
|
|
||
|
// Now work out the path to the main exe
|
||
|
wstring mainExePath(splashExePath);
|
||
|
{
|
||
|
size_t index = mainExePath.find_last_of('\\', pathLen);
|
||
|
if (index != mainExePath.npos)
|
||
|
{
|
||
|
mainExePath.resize(index);
|
||
|
mainExePath.push_back('\\');
|
||
|
}
|
||
|
mainExePath.append(XenCenterPath);
|
||
|
}
|
||
|
logStream << TEXT("mainExePath: ") << mainExePath << endl;
|
||
|
|
||
|
// Acquire splash screen lock
|
||
|
HANDLE splashPipeHandle = INVALID_HANDLE_VALUE;
|
||
|
{
|
||
|
wstring s = PipeName(SplashPipeStub, mainExePath);
|
||
|
logStream << "Attempting to acquire splash screen lock: " << s.c_str() << endl;
|
||
|
|
||
|
for (int numTries = 0; numTries * SplashLockSleepInterval < SplashLockMaxWait; numTries++)
|
||
|
{
|
||
|
splashPipeHandle = CreateNamedPipe(s.c_str(),
|
||
|
PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
|
||
|
PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 0, 0, 0, NULL);
|
||
|
|
||
|
if (splashPipeHandle == INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
DWORD lastError = GetLastError();
|
||
|
if (lastError == ERROR_ACCESS_DENIED)
|
||
|
{
|
||
|
// Pipe in use. Sleep and retry.
|
||
|
Sleep(SplashLockSleepInterval);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Unexpected error code
|
||
|
logStream << "WARNING: CreateNamedPipe failed with unexpected error. Error code: " << GetLastError() << endl;
|
||
|
nonCriticalError = true;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// We have acquired the lock
|
||
|
logStream << "Acquired splash screen lock" << endl;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (splashPipeHandle == INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
// Maximum attempts reached without success. Exit.
|
||
|
logStream << "WARNING: Couldn't acquire splash screen lock before timeout." << endl;
|
||
|
nonCriticalError = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// First try to pass the cmd line arguments into the named pipe
|
||
|
{
|
||
|
wstring s = PipeName(PipeStub, mainExePath);
|
||
|
logStream << TEXT("Pipe path 's': ") << s << endl;
|
||
|
|
||
|
// Allocate a buffer for data sent to us through the pipe.
|
||
|
// (Should never actually be any, but the command needs a buffer param anyway)
|
||
|
const int dataOutLength = 64 * 1024;
|
||
|
LPVOID dataOut = malloc(dataOutLength);
|
||
|
if (dataOut == NULL)
|
||
|
{
|
||
|
logStream << TEXT("WARNING: malloc dataOut failed. Error code: ") << GetLastError() << endl;
|
||
|
nonCriticalError = true;
|
||
|
}
|
||
|
|
||
|
DWORD bytesRead;
|
||
|
if (!CallNamedPipe(s.c_str(), lpCmdLine, (DWORD)_tcslen(lpCmdLine) * sizeof(TCHAR), dataOut, dataOutLength, &bytesRead, PipeTimeout))
|
||
|
{
|
||
|
DWORD lastError = GetLastError();
|
||
|
if (lastError == ERROR_FILE_NOT_FOUND)
|
||
|
{
|
||
|
logStream << TEXT("CallNamedPipe gave ERROR_FILE_NOT_FOUND: proceeding to launch XenCenter") << endl;
|
||
|
}
|
||
|
else if (lastError == ERROR_BROKEN_PIPE)
|
||
|
{
|
||
|
logStream << TEXT("CallNamedPipe gave ERROR_BROKEN_PIPE: proceeding to launch XenCenter") << endl;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
logStream << "WARNING: CallNamedPipe failed with unexpected error. Error code: " << GetLastError() << endl;
|
||
|
nonCriticalError = true;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Success: we passed the args into the pipe. Exit.
|
||
|
logStream << "Success: command line arguments were passed into pipe. Exiting." << endl;
|
||
|
exit(0);
|
||
|
}
|
||
|
free(dataOut);
|
||
|
}
|
||
|
|
||
|
// If we get here, sending into the pipe failed. Start XenCenter.
|
||
|
STARTUPINFO si;
|
||
|
ZeroMemory(&si, sizeof(si));
|
||
|
si.cb = sizeof(si);
|
||
|
|
||
|
logStream << TEXT("Running CreateProcess with GetCommandLine(): ") << GetCommandLine() << endl;
|
||
|
if (!CreateProcess(mainExePath.c_str(), // module name
|
||
|
GetCommandLine(), // Command line
|
||
|
NULL, // Process handle not inheritable
|
||
|
NULL, // Thread handle not inheritable
|
||
|
FALSE, // Set handle inheritance to FALSE
|
||
|
NORMAL_PRIORITY_CLASS,
|
||
|
NULL, // Use parent's environment block
|
||
|
NULL, // Use parent's starting directory
|
||
|
&si, // Pointer to STARTUPINFO structure
|
||
|
&pi) // Pointer to PROCESS_INFORMATION structure
|
||
|
)
|
||
|
{
|
||
|
ErrorExit(logStream, TEXT("CreateProcess"), true);
|
||
|
}
|
||
|
CloseHandle(pi.hThread);
|
||
|
|
||
|
if (nonCriticalError)
|
||
|
{
|
||
|
// At least we managed to launch XenCenterMain.
|
||
|
// Probably not appropriate to show the splash screen. Exit now.
|
||
|
ErrorExit(logStream, TEXT("nonCriticalError"), false);
|
||
|
}
|
||
|
|
||
|
// Show the splash screen. First register the window class.
|
||
|
WNDCLASSEX wc;
|
||
|
wc.cbSize = sizeof(WNDCLASSEX);
|
||
|
wc.style = 0;
|
||
|
wc.lpfnWndProc = WndProc;
|
||
|
wc.cbClsExtra = 0;
|
||
|
wc.cbWndExtra = 0;
|
||
|
wc.hInstance = hInstance;
|
||
|
wc.hIcon = NULL;
|
||
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||
|
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
||
|
wc.lpszMenuName = NULL;
|
||
|
wc.lpszClassName = SplashClassName;
|
||
|
wc.hIconSm = NULL;
|
||
|
|
||
|
if (RegisterClassEx(&wc) == NULL)
|
||
|
{
|
||
|
ErrorExit(logStream, TEXT("RegisterClassEx"), false);
|
||
|
}
|
||
|
|
||
|
// Get the screen (desktop) DC
|
||
|
HDC screendc = CreateIC(TEXT("DISPLAY"), NULL, NULL, NULL);
|
||
|
if (screendc == NULL)
|
||
|
{
|
||
|
ErrorExit(logStream, TEXT("CreateIC"), false);
|
||
|
}
|
||
|
|
||
|
// Get the primary monitor desktop size
|
||
|
const int screenwidth = GetSystemMetrics(SM_CXSCREEN);
|
||
|
const int screenheight = GetSystemMetrics(SM_CYSCREEN);
|
||
|
|
||
|
logStream << TEXT("Creating splash window") << endl;
|
||
|
// Create the splash window
|
||
|
int x = (screenwidth-ImageSizeX)/2;
|
||
|
int y = (screenheight-ImageSizeY)/2;
|
||
|
HWND hwnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TOOLWINDOW, SplashClassName, NULL, WS_POPUP, x, y, ImageSizeX, ImageSizeY, NULL, NULL, hInstance, NULL);
|
||
|
if (hwnd == NULL)
|
||
|
{
|
||
|
ErrorExit(logStream, TEXT("CreateWindowEx"), false);
|
||
|
}
|
||
|
|
||
|
// Load the splash bitmap from the embedded resource file
|
||
|
HBITMAP image = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(IDB_BITMAP1), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
|
||
|
if (image == NULL)
|
||
|
{
|
||
|
ErrorExit(logStream, TEXT("LoadImage"), false);
|
||
|
}
|
||
|
|
||
|
// Create the in-memory Device Context
|
||
|
memdc = CreateCompatibleDC(screendc);
|
||
|
if (memdc == NULL)
|
||
|
{
|
||
|
ErrorExit(logStream, TEXT("CreateCompatibleDC(screendc)"), false);
|
||
|
}
|
||
|
|
||
|
// Create a DC for the splash image
|
||
|
HDC filedc = CreateCompatibleDC(screendc);
|
||
|
if (filedc == NULL)
|
||
|
{
|
||
|
ErrorExit(logStream, TEXT("CreateCompatibleDC(filedc)"), false);
|
||
|
}
|
||
|
|
||
|
// Blit the splash image into the memory DC
|
||
|
SelectObject(filedc, image);
|
||
|
HBITMAP bmp = CreateCompatibleBitmap(screendc, ImageSizeX, ImageSizeY);
|
||
|
ReleaseDC(hwnd, screendc);
|
||
|
SelectObject(memdc, bmp);
|
||
|
BitBlt(memdc, 0, 0, ImageSizeX, ImageSizeY, filedc, 0, 0, SRCCOPY);
|
||
|
DeleteObject(image);
|
||
|
DeleteDC(filedc);
|
||
|
|
||
|
logStream << TEXT("Showing splash window") << endl;
|
||
|
// Show the splash window
|
||
|
ShowWindow(hwnd, nCmdShow);
|
||
|
UpdateWindow(hwnd);
|
||
|
|
||
|
// Start timer to poll for XenCenter having started
|
||
|
SetTimer(hwnd, ShortTimerId, ShortTimerInterval, NULL);
|
||
|
|
||
|
// Start timeout timer after which splash window closes anyway
|
||
|
SetTimer(hwnd, LongTimerId, LongTimerInterval, NULL);
|
||
|
|
||
|
MSG msg;
|
||
|
// Start message loop
|
||
|
while(GetMessage(&msg, NULL, 0, 0) > 0)
|
||
|
{
|
||
|
TranslateMessage(&msg);
|
||
|
DispatchMessage(&msg);
|
||
|
}
|
||
|
|
||
|
if (!DisconnectNamedPipe(splashPipeHandle))
|
||
|
{
|
||
|
// Irritating but non-fatal
|
||
|
logStream << "DisconnectNamedPipe failed with error " << GetLastError() << endl;
|
||
|
}
|
||
|
if (!CloseHandle(splashPipeHandle))
|
||
|
{
|
||
|
// Likewise
|
||
|
logStream << "CloseHandle failed with error " << GetLastError() << endl;
|
||
|
}
|
||
|
|
||
|
// Check to see if args contain '--wait': if so, wait for XenCenterMain process to exit
|
||
|
{
|
||
|
wstring args(lpCmdLine);
|
||
|
logStream << TEXT("args: ") << args.c_str() << endl;
|
||
|
if (args.find(TEXT("--wait"), 0) != args.npos)
|
||
|
{
|
||
|
logStream << TEXT("--wait detected: waiting for main XenCenter process to exit") << endl;
|
||
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
||
|
logStream << TEXT("XenCenter process exited") << endl;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CloseHandle(pi.hProcess);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
logStream << TEXT("Exiting normally") << endl;
|
||
|
|
||
|
return (int)msg.wParam;
|
||
|
}
|