Beispiel #1
0
    WindowsCommandLine::WindowsCommandLine(int argc, wchar_t* argvW[], wchar_t* envpW[]) :
            _argv(NULL), _envp(NULL) {

        // Construct UTF-8 copy of arguments
        vector<string> utf8args;
        vector<size_t> utf8argLength;
        size_t blockSize = argc * sizeof(char*);
        size_t blockPtr = blockSize;
        for (int i = 0; i < argc; ++i) {
            utf8args.push_back( toUtf8String(argvW[i]) );
            size_t argLength = utf8args[i].length() + 1;
            utf8argLength.push_back(argLength);
            blockSize += argLength;
        }
        _argv = static_cast<char**>(malloc(blockSize));
        for (int i = 0; i < argc; ++i) {
            _argv[i] = reinterpret_cast<char*>(_argv) + blockPtr;
            strcpy_s(_argv[i], utf8argLength[i], utf8args[i].c_str());
            blockPtr += utf8argLength[i];
        }

        // Construct UTF-8 copy of environment strings
        size_t envCount = 0;
        wchar_t** envpWptr = &envpW[0];
        while (*envpWptr++) {
            ++envCount;
        }
        vector<string> utf8envs;
        vector<size_t> utf8envLength;
        blockSize = (envCount + 1) * sizeof(char*);
        blockPtr = blockSize;
        for (size_t i = 0; i < envCount; ++i) {
            utf8envs.push_back( toUtf8String(envpW[i]) );
            size_t envLength = utf8envs[i].length() + 1;
            utf8envLength.push_back(envLength);
            blockSize += envLength;
        }
        _envp = static_cast<char**>(malloc(blockSize));
        size_t i;
        for (i = 0; i < envCount; ++i) {
            _envp[i] = reinterpret_cast<char*>(_envp) + blockPtr;
            strcpy_s(_envp[i], utf8envLength[i], utf8envs[i].c_str());
            blockPtr += utf8envLength[i];
        }
        _envp[i] = NULL;
    }
Beispiel #2
0
    bool ServiceController::removeService( const std::wstring& serviceName ) {
        SC_HANDLE schSCManager = ::OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
        if ( schSCManager == NULL ) {
            DWORD err = ::GetLastError();
            cerr << "Error connecting to the Service Control Manager: " << GetWinErrMsg(err) << endl;
            return false;
        }

        SC_HANDLE schService = ::OpenService( schSCManager, serviceName.c_str(), SERVICE_ALL_ACCESS );
        if ( schService == NULL ) {
            cerr << "Could not find a service named " << toUtf8String(serviceName) << " to uninstall." << endl;
            ::CloseServiceHandle( schSCManager );
            return false;
        }

        SERVICE_STATUS serviceStatus;

        // stop service if its running
        if ( ::ControlService( schService, SERVICE_CONTROL_STOP, &serviceStatus ) ) {
            cerr << "Service " << toUtf8String(serviceName) << " is currently running. Stopping service." << endl;
            while ( ::QueryServiceStatus( schService, &serviceStatus ) ) {
                if ( serviceStatus.dwCurrentState == SERVICE_STOP_PENDING ) {
                    Sleep( 1000 );
                }
                else { break; }
            }
            cerr << "Service stopped." << endl;
        }

        cerr << "Deleting service " << toUtf8String(serviceName) << "." << endl;
        bool serviceRemoved = ::DeleteService( schService );

        ::CloseServiceHandle( schService );
        ::CloseServiceHandle( schSCManager );

        if (serviceRemoved) {
            cerr << "Service deleted successfully." << endl;
        }
        else {
            cerr << "Failed to delete service." << endl;
        }

        return serviceRemoved;
    }
Beispiel #3
0
std::string errnoWithDescription(int errNumber) {
#if defined(_WIN32)
    if (errNumber == -1)
        errNumber = GetLastError();
#else
    if (errNumber < 0)
        errNumber = errno;
#endif

    char buf[kBuflen];
    char* msg{nullptr};

#if defined(__GNUC__) && defined(_GNU_SOURCE) && !(__ANDROID_API__ <= 22) && !defined(EMSCRIPTEN)
    msg = strerror_r(errNumber, buf, kBuflen);
#elif defined(_WIN32)

    LPWSTR errorText = nullptr;
    FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
                       FORMAT_MESSAGE_IGNORE_INSERTS,
                   nullptr,
                   errNumber,
                   0,
                   reinterpret_cast<LPWSTR>(&errorText),  // output
                   0,                                     // minimum size for output buffer
                   nullptr);

    if (errorText) {
        ON_BLOCK_EXIT([&errorText] { LocalFree(errorText); });
        std::string utf8ErrorText = toUtf8String(errorText);
        auto size = utf8ErrorText.find_first_of("\r\n");
        if (size == std::string::npos) {  // not found
            size = utf8ErrorText.length();
        }

        if (size >= kBuflen) {
            size = kBuflen - 1;
        }

        memcpy(buf, utf8ErrorText.c_str(), size);
        buf[size] = '\0';
        msg = buf;
    } else if (strerror_s(buf, kBuflen, errNumber) != 0) {
        msg = buf;
    }
#else /* XSI strerror_r */
    if (strerror_r(errNumber, buf, kBuflen) == 0) {
        msg = buf;
    }
#endif

    if (!msg) {
        return str::stream() << kUnknownMsg << errNumber;
    }

    return {msg};
}
Beispiel #4
0
StatusWith<std::vector<PerfCounterCollector::CounterInfo>> PerfCounterCollector::addCounters(
    StringData path) {
    std::wstring pathWide = toNativeString(path.toString().c_str());
    DWORD pathListLength = 0;
    PDH_STATUS status = PdhExpandCounterPathW(pathWide.c_str(), nullptr, &pathListLength);

    if (status != PDH_MORE_DATA) {
        return {ErrorCodes::WindowsPdhError,
                str::stream() << formatFunctionCallError("PdhExpandCounterPathW", status)
                              << " for counter '"
                              << path
                              << "'"};
    }

    auto buf = stdx::make_unique<wchar_t[]>(pathListLength);

    status = PdhExpandCounterPathW(pathWide.c_str(), buf.get(), &pathListLength);

    if (status != ERROR_SUCCESS) {
        return {ErrorCodes::WindowsPdhError,
                formatFunctionCallError("PdhExpandCounterPathW", status)};
    }

    std::vector<CounterInfo> counters;

    // Windows' PdhExpandWildCardPathW returns a nullptr terminated array of nullptr separated
    // strings.
    std::vector<std::string> counterNames;

    const wchar_t* ptr = buf.get();
    while (ptr && *ptr) {
        counterNames.emplace_back(toUtf8String(ptr));
        ptr += wcslen(ptr) + 1;
    }

    // Sort to ensure we have a predictable ordering in the final BSON
    std::sort(counterNames.begin(), counterNames.end());

    for (const auto& name : counterNames) {

        auto swCounterInfo = addCounter(name);
        if (!swCounterInfo.isOK()) {
            return swCounterInfo.getStatus();
        }

        counters.emplace_back(std::move(swCounterInfo.getValue()));
    }

    return {std::move(counters)};
}
StatusWith<std::unique_ptr<SharedLibrary>> SharedLibrary::create(
    const boost::filesystem::path& full_path) {
    LOG(1) << "Loading library: " << toUtf8String(full_path.c_str());

    HMODULE handle = LoadLibraryW(full_path.c_str());
    if (handle == nullptr) {
        return StatusWith<std::unique_ptr<SharedLibrary>>(
            ErrorCodes::InternalError,
            str::stream() << "Load library failed: " << errnoWithDescription());
    }

    return StatusWith<std::unique_ptr<SharedLibrary>>(
        std::unique_ptr<SharedLibrary>(new SharedLibrary(handle)));
}
    /* create a process dump.
        To use, load up windbg.  Set your symbol and source path.
        Open the crash dump file.  To see the crashing context, use .ecxr
        */
    void doMinidump(struct _EXCEPTION_POINTERS* exceptionInfo) {
        LPCWSTR dumpFilename = L"mongo.dmp";
        HANDLE hFile = CreateFileW(dumpFilename,
            GENERIC_WRITE,
            0,
            NULL,
            CREATE_ALWAYS,
            FILE_ATTRIBUTE_NORMAL,
            NULL);
        if ( INVALID_HANDLE_VALUE == hFile ) {
            DWORD lasterr = GetLastError();
            log() << "failed to open minidump file " << toUtf8String(dumpFilename) << " : "
                  << errnoWithDescription( lasterr ) << std::endl;
            return;
        }

        MINIDUMP_EXCEPTION_INFORMATION aMiniDumpInfo;
        aMiniDumpInfo.ThreadId = GetCurrentThreadId();
        aMiniDumpInfo.ExceptionPointers = exceptionInfo;
        aMiniDumpInfo.ClientPointers = TRUE;

        log() << "writing minidump diagnostic file " << toUtf8String(dumpFilename) << std::endl;
        BOOL bstatus = MiniDumpWriteDump(GetCurrentProcess(),
            GetCurrentProcessId(),
            hFile,
            MiniDumpNormal,
            &aMiniDumpInfo,
            NULL,
            NULL);
        if ( FALSE == bstatus ) {
            DWORD lasterr = GetLastError();
            log() << "failed to create minidump : "
                  << errnoWithDescription( lasterr ) << std::endl;
        }

        CloseHandle(hFile);
    }
Beispiel #7
0
    string errnoWithDescription(int x) {
#if defined(_WIN32)
        if( x < 0 ) 
            x = GetLastError();
#else
        if( x < 0 ) 
            x = errno;
#endif
        stringstream s;
        s << "errno:" << x << ' ';

#if defined(_WIN32)
        LPWSTR errorText = NULL;
        FormatMessageW(
            FORMAT_MESSAGE_FROM_SYSTEM
            |FORMAT_MESSAGE_ALLOCATE_BUFFER
            |FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,
            x, 0,
            reinterpret_cast<LPWSTR>( &errorText ),  // output
            0, // minimum size for output buffer
            NULL);
        if( errorText ) {
            string x = toUtf8String(errorText);
            for( string::iterator i = x.begin(); i != x.end(); i++ ) {
                if( *i == '\n' || *i == '\r' )
                    break;
                s << *i;
            }
            LocalFree(errorText);
        }
        else
            s << strerror(x);
        /*
        DWORD n = FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL, x,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPTSTR) &lpMsgBuf, 0, NULL);
        */
#else
        s << strerror(x);
#endif
        return s.str();
    }
Beispiel #8
0
 WindowsCommandLine::WindowsCommandLine( int argc, wchar_t* argvW[] ) {
     vector < string >   utf8args;
     vector < size_t >   utf8argLength;
     size_t blockSize = argc * sizeof( char * );
     size_t blockPtr = blockSize;
     for ( int i = 0; i < argc; ++i ) {
         utf8args.push_back( toUtf8String( argvW[ i ] ) );
         size_t argLength = utf8args[ i ].length() + 1;
         utf8argLength.push_back( argLength );
         blockSize += argLength;
     }
     _argv = reinterpret_cast< char** >( malloc( blockSize ) );
     for ( int i = 0; i < argc; ++i ) {
         _argv[ i ] = reinterpret_cast< char * >( _argv ) + blockPtr;
         strcpy_s( _argv[ i ], utf8argLength[ i ], utf8args[ i ].c_str() );
         blockPtr += utf8argLength[ i ];
     }
 }
Beispiel #9
0
int StringConverter::toUtf8(const UnicodeString& str, char* buffer, int bufferSize, bool nullTerminated) {
    std::string utf8Str = toUtf8String(str);

    // we want byte count, not real character count here, so length() is okay
    int ret = utf8Str.length();

    if (ret < bufferSize) {
        memcpy(buffer, utf8Str.c_str(), ret);
    } else {
        return -1;
    }

    if (!nullTerminated) {
        return ret;
    } else {
        if (ret+1 < bufferSize) {
            buffer[ret++] = 0;

            return ret;
        } else {
            return -1;
        }
    }
}
Beispiel #10
0
    bool ServiceController::installService( const std::wstring& serviceName, const std::wstring& displayName, const std::wstring& serviceDesc, const std::wstring& serviceUser, const std::wstring& servicePassword, const std::string dbpath, int argc, char* argv[] ) {
        assert(argc >= 1);

        stringstream commandLine;

        if ( strchr(argv[0], ':') ) { // a crude test for fully qualified path
            commandLine << '"' << argv[0] << "\" ";
        }
        else {
            char buffer[256];
            assert( _getcwd(buffer, 256) );
            commandLine << '"' << buffer << '\\' << argv[0] << "\" ";
        }

        for ( int i = 1; i < argc; i++ ) {
            std::string arg( argv[ i ] );
            // replace install command to indicate process is being started as a service
            if ( arg == "--install" || arg == "--reinstall" ) {
                arg = "--service";
            }
            else if ( arg == "--dbpath" && i + 1 < argc ) {
                commandLine << arg << "  \"" << dbpath << "\"  ";
                i++;
                continue;
            }
            else if ( arg == "--logpath" && i + 1 < argc ) {
                commandLine << arg << "  \"" << argv[i+1] << "\"  ";
                i++;
                continue;
            }
            else if ( arg.length() > 9 && arg.substr(0, 9) == "--service" ) {
                // Strip off --service(Name|User|Password) arguments
                i++;
                continue;
            }
            commandLine << arg << "  ";
        }

        SC_HANDLE schSCManager = ::OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
        if ( schSCManager == NULL ) {
            DWORD err = ::GetLastError();
            cerr << "Error connecting to the Service Control Manager: " << GetWinErrMsg(err) << endl;
            return false;
        }

        // Make sure servise doesn't already exist.
        // TODO: Check to see if service is in "Deleting" status, suggest the user close down Services MMC snap-ins.
        SC_HANDLE schService = ::OpenService( schSCManager, serviceName.c_str(), SERVICE_ALL_ACCESS );
        if ( schService != NULL ) {
            cerr << "There is already a service named " << toUtf8String(serviceName) << ". Aborting" << endl;
            ::CloseServiceHandle( schService );
            ::CloseServiceHandle( schSCManager );
            return false;
        }
        std::basic_ostringstream< TCHAR > commandLineWide;
        commandLineWide << commandLine.str().c_str();

        cerr << "Creating service " << toUtf8String(serviceName) << "." << endl;

        // create new service
        schService = ::CreateService( schSCManager, serviceName.c_str(), displayName.c_str(),
                                      SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
                                      SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
                                      commandLineWide.str().c_str(), NULL, NULL, L"\0\0", NULL, NULL );
        if ( schService == NULL ) {
            DWORD err = ::GetLastError();
            cerr << "Error creating service: " << GetWinErrMsg(err) << endl;
            ::CloseServiceHandle( schSCManager );
            return false;
        }

        cerr << "Service creation successful." << endl;
        cerr << "Service can be started from the command line via 'net start \"" << toUtf8String(serviceName) << "\"'." << endl;

        bool serviceInstalled;

        // TODO: If neccessary grant user "Login as a Service" permission.
        if ( !serviceUser.empty() ) {
            std::wstring actualServiceUser;
            if ( serviceUser.find(L"\\") == string::npos ) {
                actualServiceUser = L".\\" + serviceUser;
            }
            else {
                actualServiceUser = serviceUser;
            }

            cerr << "Setting service login credentials. User: "******"Setting service login failed. Service has 'LocalService' permissions." << endl;
            }
        }

        // set the service description
        SERVICE_DESCRIPTION serviceDescription;
        serviceDescription.lpDescription = (LPTSTR)serviceDesc.c_str();
        serviceInstalled = ::ChangeServiceConfig2( schService, SERVICE_CONFIG_DESCRIPTION, &serviceDescription );


        if ( serviceInstalled ) {
            SC_ACTION aActions[ 3 ] = { { SC_ACTION_RESTART, 0 }, { SC_ACTION_RESTART, 0 }, { SC_ACTION_RESTART, 0 } };

            SERVICE_FAILURE_ACTIONS serviceFailure;
            ZeroMemory( &serviceFailure, sizeof( SERVICE_FAILURE_ACTIONS ) );
            serviceFailure.cActions = 3;
            serviceFailure.lpsaActions = aActions;

            // set service recovery options
            serviceInstalled = ::ChangeServiceConfig2( schService, SERVICE_CONFIG_FAILURE_ACTIONS, &serviceFailure );

        }
        else {
            cerr << "Could not set service description. Check the event log for more details." << endl;
        }

        ::CloseServiceHandle( schService );
        ::CloseServiceHandle( schSCManager );

        return serviceInstalled;
    }
Beispiel #11
0
StatusWith<PerfCounterCollector::CounterInfo> PerfCounterCollector::addCounter(StringData path) {

    PDH_HCOUNTER counter{0};

    PDH_STATUS status =
        PdhAddCounterW(_query, toNativeString(path.toString().c_str()).c_str(), NULL, &counter);

    if (status != ERROR_SUCCESS) {
        return {ErrorCodes::WindowsPdhError, formatFunctionCallError("PdhAddCounterW", status)};
    }

    DWORD bufferSize = 0;

    status = PdhGetCounterInfoW(counter, false, &bufferSize, nullptr);

    if (status != PDH_MORE_DATA) {
        return {ErrorCodes::WindowsPdhError, formatFunctionCallError("PdhGetCounterInfoW", status)};
    }

    auto buf = stdx::make_unique<char[]>(bufferSize);
    auto counterInfo = reinterpret_cast<PPDH_COUNTER_INFO>(buf.get());

    status = PdhGetCounterInfoW(counter, false, &bufferSize, counterInfo);

    if (status != ERROR_SUCCESS) {
        return {ErrorCodes::WindowsPdhError, formatFunctionCallError("PdhGetCounterInfoW", status)};
    }

    // A full qualified path is as such:
    // "\\MYMACHINE\\Processor(0)\\% Idle Time"
    // MachineName \\ Object Name (Instance Name) \\ CounterName
    // Ex:
    //  MachineName: MYMACHINE
    //  Object Name: Processor
    //  InstanceName: 0
    //  CounterName: % Idle Time
    // We do not want to use Machine Name, but sometimes we want InstanceName
    //
    std::string firstName = str::stream() << '\\' << toUtf8String(counterInfo->szObjectName) << '\\'
                                          << toUtf8String(counterInfo->szCounterName);

    // Compute a second name
    std::string secondName(firstName);

    bool hasSecondValue = false;

    // Only include base for counters that need it
    if ((counterInfo->dwType & PERF_COUNTER_PRECISION) == PERF_COUNTER_PRECISION) {
        secondName += " Base";
        hasSecondValue = true;
    }

    // InstanceName is null for counters without instance names
    return {CounterInfo{std::move(firstName),
                        std::move(secondName),
                        hasSecondValue,
                        counterInfo->szInstanceName ? toUtf8String(counterInfo->szInstanceName)
                                                    : std::string(),
                        counterInfo->dwType,
                        counter}};
}
Beispiel #12
0
ProgramRunner::ProgramRunner(const BSONObj& args, const BSONObj& env) {
    uassert(ErrorCodes::FailedToParse,
            "cannot pass an empty argument to ProgramRunner",
            !args.isEmpty());

    string program(args.firstElement().valuestrsafe());
    uassert(ErrorCodes::FailedToParse,
            "invalid program name passed to ProgramRunner",
            !program.empty());
    boost::filesystem::path programPath = findProgram(program);
    boost::filesystem::path programName = programPath.stem();

    string prefix("mongod-");
    bool isMongodProgram = string("mongod") == programName ||
        programName.string().compare(0, prefix.size(), prefix) == 0;
    prefix = "mongos-";
    bool isMongosProgram = string("mongos") == programName ||
        programName.string().compare(0, prefix.size(), prefix) == 0;

    if (isMongodProgram) {
        _name = "d";
    } else if (isMongosProgram) {
        _name = "s";
    } else if (programName == "mongobridge") {
        _name = "b";
    } else {
        _name = "sh";
    }

    _argv.push_back(programPath.string());

    _port = -1;

    // Parse individual arguments into _argv
    BSONObjIterator j(args);
    j.next();  // skip program name (handled above)
    while (j.more()) {
        BSONElement e = j.next();
        string str;
        if (e.isNumber()) {
            stringstream ss;
            ss << e.number();
            str = ss.str();
        } else {
            verify(e.type() == mongo::String);
            str = e.valuestr();
        }
        if (str == "--port") {
            _port = -2;
        } else if (_port == -2) {
            _port = strtol(str.c_str(), 0, 10);
        } else if (isMongodProgram && str == "--configsvr") {
            _name = "c";
        }
        _argv.push_back(str);
    }

    // Load explicitly set environment key value pairs into _envp.
    for (const BSONElement& e : env) {
        // Environment variable values must be strings
        verify(e.type() == mongo::String);

        _envp.emplace(std::string(e.fieldName()), std::string(e.valuestr()));
    }

// Import this process' environment into _envp, for all keys that have not already been set.
// We need to do this so that the child process has all the PATH and locale variables, unless
// we explicitly override them.
#ifdef _WIN32
    wchar_t* processEnv = GetEnvironmentStringsW();
    ON_BLOCK_EXIT(
        [](wchar_t* toFree) {
            if (toFree)
                FreeEnvironmentStringsW(toFree);
        },
        processEnv);

    // Windows' GetEnvironmentStringsW returns a NULL terminated array of NULL separated
    // <key>=<value> pairs.
    while (processEnv && *processEnv) {
        std::wstring envKeyValue(processEnv);
        size_t splitPoint = envKeyValue.find('=');
        invariant(splitPoint != std::wstring::npos);
        std::string envKey = toUtf8String(envKeyValue.substr(0, splitPoint));
        std::string envValue = toUtf8String(envKeyValue.substr(splitPoint + 1));
        _envp.emplace(std::move(envKey), std::move(envValue));
        processEnv += envKeyValue.size() + 1;
    }
#else
    // environ is a POSIX defined array of char*s. Each char* in the array is a <key>=<value>\0
    // pair.
    char** environEntry = environ;
    while (*environEntry) {
        std::string envKeyValue(*environEntry);
        size_t splitPoint = envKeyValue.find('=');
        invariant(splitPoint != std::string::npos);
        std::string envKey = envKeyValue.substr(0, splitPoint);
        std::string envValue = envKeyValue.substr(splitPoint + 1);
        _envp.emplace(std::move(envKey), std::move(envValue));
        ++environEntry;
    }
#endif
    bool needsPort = isMongodProgram || isMongosProgram || (programName == "mongobridge");
    if (!needsPort) {
        _port = -1;
    }
    uassert(ErrorCodes::FailedToParse,
            str::stream() << "a port number is expected when running " << program
                          << " from the shell",
            !needsPort || _port >= 0);

    uassert(ErrorCodes::BadValue,
            str::stream() << "can't start " << program << ", port " << _port << " already in use",
            _port < 0 || !registry.isPortRegistered(_port));
}
Beispiel #13
0
    bool ServiceController::installService( const std::wstring& serviceName, const std::wstring& displayName, const std::wstring& serviceDesc, const std::wstring& serviceUser, const std::wstring& servicePassword, const std::string dbpath, int argc, char* argv[] ) {
        assert(argc >= 1);

        stringstream commandLine;

        char exePath[1024];
        GetModuleFileNameA( NULL, exePath, sizeof exePath );
        commandLine << '"' << exePath << "\" ";

        for ( int i = 1; i < argc; i++ ) {
            std::string arg( argv[ i ] );
            // replace install command to indicate process is being started as a service
            if ( arg == "--install" || arg == "--reinstall" ) {
                arg = "--service";
            }
            else if ( arg == "--dbpath" && i + 1 < argc ) {
                commandLine << arg << "  \"" << dbpath << "\"  ";
                i++;
                continue;
            }
            else if ( arg == "--logpath" && i + 1 < argc ) {
                commandLine << arg << "  \"" << argv[i+1] << "\"  ";
                i++;
                continue;
            }
            else if ( arg == "-f" && i + 1 < argc ) {
                commandLine << arg << "  \"" << argv[i+1] << "\"  ";
                i++;
                continue;
            }
            else if ( arg == "--config" && i + 1 < argc ) {
                commandLine << arg << "  \"" << argv[i+1] << "\"  ";
                i++;
                continue;
            }
            else if ( arg == "--pidfilepath" && i + 1 < argc ) {
                commandLine << arg << "  \"" << argv[i+1] << "\"  ";
                i++;
                continue;
            }
            else if ( arg == "--repairpath" && i + 1 < argc ) {
                commandLine << arg << "  \"" << argv[i+1] << "\"  ";
                i++;
                continue;
            }
            else if ( arg == "--keyfile" && i + 1 < argc ) {
                commandLine << arg << "  \"" << argv[i+1] << "\"  ";
                i++;
                continue;
            }
            else if ( arg.length() > 9 && arg.substr(0, 9) == "--service" ) {
                // Strip off --service(Name|User|Password) arguments
                i++;
                continue;
            }
            commandLine << arg << "  ";
        }

        SC_HANDLE schSCManager = ::OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
        if ( schSCManager == NULL ) {
            DWORD err = ::GetLastError();
            cerr << "Error connecting to the Service Control Manager: " << GetWinErrMsg(err) << endl;
            return false;
        }

        // Make sure servise doesn't already exist.
        // TODO: Check to see if service is in "Deleting" status, suggest the user close down Services MMC snap-ins.
        SC_HANDLE schService = ::OpenService( schSCManager, serviceName.c_str(), SERVICE_ALL_ACCESS );
        if ( schService != NULL ) {
            cerr << "There is already a service named " << toUtf8String(serviceName) << ". Aborting" << endl;
            ::CloseServiceHandle( schService );
            ::CloseServiceHandle( schSCManager );
            return false;
        }
        std::basic_ostringstream< TCHAR > commandLineWide;
        commandLineWide << commandLine.str().c_str();

        cerr << "Creating service " << toUtf8String(serviceName) << "." << endl;

        // create new service
        schService = ::CreateService( schSCManager, serviceName.c_str(), displayName.c_str(),
                                      SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
                                      SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
                                      commandLineWide.str().c_str(), NULL, NULL, L"\0\0", NULL, NULL );
        if ( schService == NULL ) {
            DWORD err = ::GetLastError();
            cerr << "Error creating service: " << GetWinErrMsg(err) << endl;
            ::CloseServiceHandle( schSCManager );
            return false;
        }

        cerr << "Service creation successful." << endl;
        cerr << "Service can be started from the command line via 'net start \"" << toUtf8String(serviceName) << "\"'." << endl;

        bool serviceInstalled;

        // TODO: If neccessary grant user "Login as a Service" permission.
        if ( !serviceUser.empty() ) {
            std::wstring actualServiceUser;
            if ( serviceUser.find(L"\\") == string::npos ) {
                actualServiceUser = L".\\" + serviceUser;
            }
            else {
                actualServiceUser = serviceUser;
            }

            cerr << "Setting service login credentials. User: "******"Setting service login failed. Service has 'LocalService' permissions." << endl;
            }
        }

        // set the service description
        SERVICE_DESCRIPTION serviceDescription;
        serviceDescription.lpDescription = (LPTSTR)serviceDesc.c_str();
        serviceInstalled = ::ChangeServiceConfig2( schService, SERVICE_CONFIG_DESCRIPTION, &serviceDescription );

#if 1
        if ( ! serviceInstalled ) {
#else
        // This code sets the mongod service to auto-restart, forever.
        // This might be a fine thing to do except that when mongod or Windows has a crash, the mongo.lock
        // file is still around, so any attempt at a restart will immediately fail.  With auto-restart, we
        // go into a loop, crashing and restarting, crashing and restarting, until someone comes in and
        // disables the service or deletes the mongod.lock file.
        //
        // I'm leaving the old code here for now in case we solve this and are able to turn SC_ACTION_RESTART
        // back on.
        //
        if ( serviceInstalled ) {
            SC_ACTION aActions[ 3 ] = { { SC_ACTION_RESTART, 0 }, { SC_ACTION_RESTART, 0 }, { SC_ACTION_RESTART, 0 } };

            SERVICE_FAILURE_ACTIONS serviceFailure;
            ZeroMemory( &serviceFailure, sizeof( SERVICE_FAILURE_ACTIONS ) );
            serviceFailure.cActions = 3;
            serviceFailure.lpsaActions = aActions;

            // set service recovery options
            serviceInstalled = ::ChangeServiceConfig2( schService, SERVICE_CONFIG_FAILURE_ACTIONS, &serviceFailure );

        }
        else {
#endif
            cerr << "Could not set service description. Check the event log for more details." << endl;
        }

        ::CloseServiceHandle( schService );
        ::CloseServiceHandle( schSCManager );

        return serviceInstalled;
    }

    bool ServiceController::removeService( const std::wstring& serviceName ) {
        SC_HANDLE schSCManager = ::OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
        if ( schSCManager == NULL ) {
            DWORD err = ::GetLastError();
            cerr << "Error connecting to the Service Control Manager: " << GetWinErrMsg(err) << endl;
            return false;
        }

        SC_HANDLE schService = ::OpenService( schSCManager, serviceName.c_str(), SERVICE_ALL_ACCESS );
        if ( schService == NULL ) {
            cerr << "Could not find a service named " << toUtf8String(serviceName) << " to uninstall." << endl;
            ::CloseServiceHandle( schSCManager );
            return false;
        }

        SERVICE_STATUS serviceStatus;

        // stop service if its running
        if ( ::ControlService( schService, SERVICE_CONTROL_STOP, &serviceStatus ) ) {
            cerr << "Service " << toUtf8String(serviceName) << " is currently running. Stopping service." << endl;
            while ( ::QueryServiceStatus( schService, &serviceStatus ) ) {
                if ( serviceStatus.dwCurrentState == SERVICE_STOP_PENDING ) {
                    Sleep( 1000 );
                }
                else { break; }
            }
            cerr << "Service stopped." << endl;
        }

        cerr << "Deleting service " << toUtf8String(serviceName) << "." << endl;
        bool serviceRemoved = ::DeleteService( schService );

        ::CloseServiceHandle( schService );
        ::CloseServiceHandle( schSCManager );

        if (serviceRemoved) {
            cerr << "Service deleted successfully." << endl;
        }
        else {
            cerr << "Failed to delete service." << endl;
        }

        return serviceRemoved;
    }