//____________________________________________________________________________
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1995 - 1996.
//
//  File:       SchState.cxx
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    3/27/1996   RaviR   Created
//
//____________________________________________________________________________


#include "..\pch\headers.hxx"
#pragma hdrstop

#include "dbg.h"
#include "macros.h"

#include "..\inc\common.hxx"
#include "..\inc\resource.h"
#include "..\inc\misc.hxx"
#include "resource.h"
#include "..\schedui\schedui.hxx"


#define MAX_MSGLEN 300
#define MAX_SCHED_START_WAIT    60  // seconds


#ifdef _CHICAGO_

#define TIMEOUT_INTERVAL        10000
#define TIMEOUT_INCREMENT       10000
#define TIMEOUT_INTERVAL_MAX    (5 * 60 * 1000) // 5 minutes

//____________________________________________________________________________
//
//  Function:   I_SendMsgTimeout
//
//  Synopsis:   Uses SendMessageTimeout to send the message with SMTO_BLOCK
//              option. If SendMessageTimeout fails due to timeout error,
//              we prompt the user for retry.
//
//  Arguments:  [hwnd] -- IN
//              [uMsg] -- IN
//              [pdwRes] -- IN
//
//  Returns:    BOOL
//
//  History:    3/29/1996   RaviR   Created
//
//____________________________________________________________________________

BOOL
I_SendMsgTimeout(
    HWND    hwnd,
    UINT    uMsg,
    DWORD * pdwRes)
{
    UINT    uTimeOut = TIMEOUT_INTERVAL;
    int     idsErr = 0;
    TCHAR   szErrMsg[MAX_MSGLEN] = TEXT("");
    TCHAR   szErrCaption[MAX_MSGLEN] = TEXT("");

    switch (uMsg)
    {
    case SCHED_WIN9X_GETSVCSTATE:   idsErr = IERR_GETSVCSTATE;     break;
    case SCHED_WIN9X_STOPSVC:       idsErr = IERR_STOPSVC;         break;
    case SCHED_WIN9X_PAUSESVC:      idsErr = IERR_PAUSESVC;        break;
    case SCHED_WIN9X_CONTINUESVC:   idsErr = IERR_CONTINUESVC;     break;
    }

    while (1)
    {
        if (SendMessageTimeout(hwnd, uMsg, 0L, 0L, SMTO_BLOCK,
                                             uTimeOut, pdwRes)
            == TRUE)
        {
            return TRUE;
        }

        if (szErrMsg[0] == TEXT('\0'))
        {
            UINT iCur = 0;

            if (idsErr != 0)
            {
                LoadString(g_hInstance, idsErr, szErrMsg, MAX_MSGLEN);

                iCur = lstrlen(szErrMsg);
                szErrMsg[iCur++] = TEXT(' ');
            }

            LoadString(g_hInstance, IERR_SCHEDSVC, &szErrMsg[iCur],
                                                MAX_MSGLEN - iCur);

            LoadString(g_hInstance, IDS_SCHEDULER_NAME, szErrCaption,
                                                MAX_MSGLEN);
        }

        int iReply = MessageBox(hwnd, szErrMsg, szErrCaption, MB_YESNO);

        if (iReply == IDNO)
        {
            return FALSE;
        }

        if (uTimeOut < TIMEOUT_INTERVAL_MAX)
        {
            uTimeOut += TIMEOUT_INCREMENT;
        }
    }

    return FALSE;
}
#endif // _CHICAGO_



//____________________________________________________________________________
//
//  Function:   GetSchSvcState
//
//  Synopsis:   returns the schedul service status
//
//  Arguments:  [dwState] -- IN
//
//  Returns:    HRESULT
//
//  History:    3/29/1996   RaviR   Created
//
//____________________________________________________________________________

HRESULT
GetSchSvcState(
    DWORD &dwState)
{
#ifdef _CHICAGO_

    HWND hwnd = FindWindow(SCHED_CLASS, SCHED_TITLE);

    if (hwnd == NULL)
    {
        dwState = SERVICE_STOPPED;
    }
    else
    {
        if (I_SendMsgTimeout(hwnd, SCHED_WIN9X_GETSVCSTATE, &dwState) == FALSE)
        {
            return E_FAIL;
        }
    }

    return S_OK;

#else // _NT1X_

    SC_HANDLE   hSchSvc = NULL;
    HRESULT     hr = S_OK;

    do
    {
        hSchSvc = OpenScheduleService(SERVICE_QUERY_STATUS);
        if (hSchSvc == NULL)
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            DEBUG_OUT_LASTERROR;
            break;
        }

        SERVICE_STATUS SvcStatus;

        if (QueryServiceStatus(hSchSvc, &SvcStatus) == FALSE)
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            DEBUG_OUT_LASTERROR;
            break;
        }

        dwState = SvcStatus.dwCurrentState;

    } while (0);

    if (hSchSvc != NULL)
    {
        CloseServiceHandle(hSchSvc);
    }

    return hr;

#endif // _NT1X_
}

//____________________________________________________________________________
//
//  Function:   StartScheduler
//
//  Synopsis:   Start the schedule service
//
//  Returns:    HRESULT
//
//  History:    3/29/1996   RaviR   Created
//
//____________________________________________________________________________

HRESULT
StartScheduler(void)
{
#ifdef _CHICAGO_

    //
    // Persist the change.
    //
    AutoStart(TRUE);

    //
    // See if it is running.
    //
    HWND hwnd = FindWindow(SCHED_CLASS, SCHED_TITLE);

    if (hwnd != NULL)
    {
        // It is already running.
        return S_OK;
    }

    //
    //  Create a process to open the log.
    //

    STARTUPINFO         sui;
    PROCESS_INFORMATION pi;

    ZeroMemory(&sui, sizeof(sui));
    sui.cb = sizeof (STARTUPINFO);

    TCHAR szApp[MAX_PATH];
    LPTSTR pszFilePart;

    DWORD dwRet = SearchPath(NULL, SCHED_SERVICE_APP_NAME, NULL, MAX_PATH,
                                                        szApp, &pszFilePart);

    if (dwRet == 0)
    {
        DEBUG_OUT_LASTERROR;
        return HRESULT_FROM_WIN32(GetLastError());
    }

    BOOL fRet = CreateProcess(
                    szApp,              // lpszImageName
                    NULL,               // lpszCommandLine
                    NULL,               // lpsaProcess - security attributes
                    NULL,               // lpsaThread  - security attributes
                    FALSE,              // fInheritHandles
                    CREATE_NEW_CONSOLE  // fdwCreate - creation flags
                        | CREATE_NEW_PROCESS_GROUP,
                    NULL,               // lpvEnvironment
                    NULL,               // lpszCurDir
                    &sui,               // lpsiStartInfo
                    &pi );              // lppiProcInfo

    if (fRet == 0)
    {
        DEBUG_OUT_LASTERROR;
        return HRESULT_FROM_WIN32(GetLastError());
    }

    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    return S_OK;

#else // _NT1X_

    SC_HANDLE   hSchSvc = NULL;
    HRESULT     hr = S_OK;

    do
    {
        hSchSvc = OpenScheduleService(
                              SERVICE_CHANGE_CONFIG | SERVICE_START |
                              SERVICE_QUERY_STATUS);
        if (hSchSvc == NULL)
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            DEBUG_OUT_LASTERROR;
            break;
        }

        SERVICE_STATUS SvcStatus;

        if (QueryServiceStatus(hSchSvc, &SvcStatus) == FALSE)
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            DEBUG_OUT_LASTERROR;
            break;
        }

        if (SvcStatus.dwCurrentState == SERVICE_RUNNING)
        {
            // The service is already running.
            break;
        }

        if (StartService(hSchSvc, 0, NULL) == FALSE)
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            DEBUG_OUT_LASTERROR;
            break;
        }

        //
        // Persist the change.  Since the service started successfully,
        // don't complain if this fails.
        //
        HRESULT hrAuto = AutoStart(TRUE);
        CHECK_HRESULT(hrAuto);

    } while (0);

    if (hSchSvc != NULL)
    {
        CloseServiceHandle(hSchSvc);
    }

    return hr;

#endif // _NT1X_
}

//____________________________________________________________________________
//
//  Function:   StopScheduler
//
//  Synopsis:   Stops the schedule service
//
//  Returns:    HRESULT
//
//  History:    3/29/1996   RaviR   Created
//
//____________________________________________________________________________

HRESULT
StopScheduler(void)
{
#ifdef _CHICAGO_

    //
    // Persist the change.
    //
    AutoStart(FALSE);

    //
    // See if it is running or not.
    //
    HWND hwnd = FindWindow(SCHED_CLASS, SCHED_TITLE);

    if (hwnd == NULL)
    {
        // already stopped
        return S_OK;
    }
    else if (I_SendMsgTimeout(hwnd, SCHED_WIN9X_STOPSVC, NULL) == TRUE)
    {
        return S_OK;
    }

    return E_FAIL;

#else // _NT1X_

    SC_HANDLE   hSchSvc = NULL;
    HRESULT     hr = S_OK;

    do
    {
        hSchSvc = OpenScheduleService(
                              SERVICE_CHANGE_CONFIG | SERVICE_STOP |
                              SERVICE_QUERY_STATUS);
        if (hSchSvc == NULL)
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            DEBUG_OUT_LASTERROR;
            break;
        }

        SERVICE_STATUS SvcStatus;

        if (QueryServiceStatus(hSchSvc, &SvcStatus) == FALSE)
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            DEBUG_OUT_LASTERROR;
            break;
        }

        if (SvcStatus.dwCurrentState == SERVICE_STOPPED)
        {
            // The service is already stopped.
            break;
        }

        if (ControlService(hSchSvc, SERVICE_CONTROL_STOP, &SvcStatus) == FALSE)
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            DEBUG_OUT_LASTERROR;
            break;
        }

        //
        // Persist the change.  Since the service was stopped successfully,
        // don't complain if this fails.
        //
        HRESULT hrAuto = AutoStart(FALSE);
        CHECK_HRESULT(hrAuto);

    } while (0);

    if (hSchSvc != NULL)
    {
        CloseServiceHandle(hSchSvc);
    }

    return hr;

#endif // _NT1X_
}

//____________________________________________________________________________
//
//  Function:   PauseScheduler
//
//  Synopsis:   If fPause==TRUE requests the schedule service to pauses,
//              else to continue.
//
//  Arguments:  [fPause] -- IN
//
//  Returns:    HRESULT
//
//  History:    3/29/1996   RaviR   Created
//
//____________________________________________________________________________

HRESULT
PauseScheduler(
    BOOL fPause)
{
#ifdef _CHICAGO_

    HWND hwnd = FindWindow(SCHED_CLASS, SCHED_TITLE);
    UINT uMsg = fPause ? SCHED_WIN9X_PAUSESVC : SCHED_WIN9X_CONTINUESVC;

    if ((hwnd != NULL) && (I_SendMsgTimeout(hwnd, uMsg, NULL) == TRUE))
    {
        return S_OK;
    }

    return E_FAIL;

#else // _NT1X_

    SC_HANDLE   hSchSvc = NULL;
    HRESULT     hr = S_OK;

    do
    {
        hSchSvc = OpenScheduleService(
                            SERVICE_PAUSE_CONTINUE | SERVICE_QUERY_STATUS);

        if (hSchSvc == NULL)
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            DEBUG_OUT_LASTERROR;
            break;
        }

        SERVICE_STATUS SvcStatus;

        if (QueryServiceStatus(hSchSvc, &SvcStatus) == FALSE)
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            DEBUG_OUT_LASTERROR;
            break;
        }

        if (fPause == TRUE)
        {
            if ((SvcStatus.dwCurrentState == SERVICE_PAUSED) ||
                (SvcStatus.dwCurrentState == SERVICE_PAUSE_PENDING))
            {
                // Nothing to do here.
                break;
            }
            else if ((SvcStatus.dwCurrentState == SERVICE_STOPPED) ||
                     (SvcStatus.dwCurrentState == SERVICE_STOP_PENDING))
            {
                Win4Assert(0 && "Unexpected");
                hr = E_UNEXPECTED;
                CHECK_HRESULT(hr);
                break;
            }
            else
            {
                if (ControlService(hSchSvc, SERVICE_CONTROL_PAUSE, &SvcStatus)
                    == FALSE)
                {
                    hr = HRESULT_FROM_WIN32(GetLastError());
                    DEBUG_OUT_LASTERROR;
                    break;
                }
            }
        }
        else // continue
        {
            if ((SvcStatus.dwCurrentState == SERVICE_RUNNING) ||
                (SvcStatus.dwCurrentState == SERVICE_CONTINUE_PENDING))
            {
                // Nothing to do here.
                break;
            }
            else if ((SvcStatus.dwCurrentState == SERVICE_STOPPED) ||
                     (SvcStatus.dwCurrentState == SERVICE_STOP_PENDING))
            {
                Win4Assert(0 && "Unexpected");
                hr = E_UNEXPECTED;
                CHECK_HRESULT(hr);
                break;
            }
            else
            {
                if (ControlService(hSchSvc, SERVICE_CONTROL_CONTINUE,
                                                            &SvcStatus)
                    == FALSE)
                {
                    hr = HRESULT_FROM_WIN32(GetLastError());
                    DEBUG_OUT_LASTERROR;
                    break;
                }
            }
        }

    } while (0);

    if (hSchSvc != NULL)
    {
        CloseServiceHandle(hSchSvc);
    }

    return hr;

#endif // _NT1X_
}



//+--------------------------------------------------------------------------
//
//  Function:   UserCanChangeService
//
//  Synopsis:   Returns TRUE if the UI should allow the user to invoke
//              service start, stop, pause/continue, or at account options.
//
//  Returns:    TRUE if focus is local machine and OS is win95 or it is NT
//              and the user is an admin.
//
//  History:    4-16-1997   DavidMun   Created
//
//---------------------------------------------------------------------------

BOOL
UserCanChangeService(
    LPCTSTR pszServerFocus)
{
    if (pszServerFocus)
    {
        return FALSE;
    }

#if defined(_CHICAGO_)
    return TRUE;
#else
    //
    // Determine if user is an admin.  If not, some items under the
    // advanced menu will be disabled.
    // BUGBUG  A more accurate way to do this would be to attempt to open
    // the relevant registry keys and service handle.
    //
    return IsThreadCallerAnAdmin(NULL);
#endif // !defined(_CHICAGO_)
}


//+--------------------------------------------------------------------------
//
//  Function:   PromptForServiceStart
//
//  Synopsis:   If the service is not started or is paused, prompt the user
//              to allow us to start/continue it.
//
//  Returns:    S_OK    - service was already running, or was successfully
//                          started or continued.
//              S_FALSE - user elected not to start/continue service
//              E_*     - error starting/continuing service
//
//  History:    4-16-1997   DavidMun   Created
//
//---------------------------------------------------------------------------

HRESULT
PromptForServiceStart(
    HWND hwnd)
{
    HRESULT hr = S_OK;

    do
    {
        //
        // Get the current service state
        //

        DWORD dwState = SERVICE_STOPPED;

        hr = GetSchSvcState(dwState);

        if (FAILED(hr))
        {
            dwState = SERVICE_STOPPED;
            hr = S_OK; // reset
        }

        //
        // Determine the required action
        //

        UINT uMsg = 0;

        if (dwState == SERVICE_STOPPED ||
            dwState == SERVICE_STOP_PENDING)
        {
            uMsg = IDS_START_SERVICE;
        }
        else if (dwState == SERVICE_PAUSED ||
                 dwState == SERVICE_PAUSE_PENDING)
        {
            uMsg = IDS_CONTINUE_SERVICE;
        }
        else if (dwState == SERVICE_START_PENDING)
        {
            uMsg = IDS_START_PENDING;
        }

        //
        // If the service is running, there's nothing to do.
        //

        if (!uMsg)
        {
            hr = S_OK;
            break;
        }

        if (uMsg == IDS_START_PENDING)
        {
            SchedUIMessageDialog(hwnd,
                                 uMsg,
                                 MB_SETFOREGROUND      |
                                    MB_TASKMODAL       |
                                    MB_ICONINFORMATION |
                                    MB_OK,
                                 NULL);
        }
        else
        {
            if (SchedUIMessageDialog(hwnd, uMsg,
                        MB_SETFOREGROUND |
                        MB_TASKMODAL |
                        MB_ICONQUESTION |
                        MB_YESNO,
                        NULL)
                == IDNO)
            {
                hr = S_FALSE;
                break;
            }
        }

        CWaitCursor waitCursor;

        if (uMsg == IDS_START_SERVICE)
        {
            hr = StartScheduler();

            if (FAILED(hr))
            {
                SchedUIErrorDialog(hwnd, IERR_STARTSVC, (LPTSTR)NULL);
                break;
            }

            // Give the schedule service time to start up.
            Sleep(2000);
        }
        else if (uMsg == IDS_CONTINUE_SERVICE)
        {
            PauseScheduler(FALSE);
        }

        for (int count=0; count < 60; count++)
        {
            GetSchSvcState(dwState);

            if (dwState == SERVICE_RUNNING)
            {
                break;
            }

            Sleep(1000); // Sleep for 1 seconds.
        }

        if (dwState != SERVICE_RUNNING)
        {
            //
            // unable to start/continue the service.
            //

            SchedUIErrorDialog(hwnd,
                (uMsg == IDS_START_SERVICE) ? IERR_STARTSVC
                                                : IERR_CONTINUESVC,
                (LPTSTR)NULL);
            hr = E_FAIL;
            break;
        }
    } while (0);

    return hr;
}




//+--------------------------------------------------------------------------
//
//  Function:   QuietStartContinueService
//
//  Synopsis:   Start or continue the service without requiring user
//              interaction.
//
//  Returns:    S_OK   - service running
//              E_FAIL - timeout or failure
//
//  History:    5-19-1997   DavidMun   Created
//
//---------------------------------------------------------------------------

HRESULT
QuietStartContinueService()
{
    HRESULT hr = S_OK;
    DWORD dwState = SERVICE_STOPPED;

    do
    {
        hr = GetSchSvcState(dwState);

        if (FAILED(hr))
        {
            dwState = SERVICE_STOPPED;
            hr = S_OK; // reset
        }

        //
        // If the service is running, there's nothing to do.
        //

        if (dwState == SERVICE_RUNNING)
        {
            break;
        }

        //
        // If it's stopped, request a start.  If it's paused, request
        // continue.
        //

        CWaitCursor waitCursor;

        switch (dwState)
        {
        case SERVICE_STOPPED:
        case SERVICE_STOP_PENDING:
            hr = StartScheduler();
            break;

        case SERVICE_PAUSED:
        case SERVICE_PAUSE_PENDING:
            hr = PauseScheduler(FALSE);
            break;
        }

        if (FAILED(hr))
        {
            CHECK_HRESULT(hr);
            break;
        }

        //
        // Wait for its state to change to running
        //

        for (int count=0; count < MAX_SCHED_START_WAIT; count++)
        {
            GetSchSvcState(dwState);

            if (dwState == SERVICE_RUNNING)
            {
                break;
            }

            Sleep(1000); // Sleep for 1 seconds.
        }

        if (dwState != SERVICE_RUNNING)
        {
            //
            // unable to start/continue the service.
            //

            hr = E_FAIL;
            CHECK_HRESULT(hr);
        }
    } while (0);

    return hr;
}




//____________________________________________________________________________
//
//  Function:   OpenScheduleService
//
//  Synopsis:   Opens a handle to the "Schedule" service
//
//  Arguments:  [dwDesiredAccess] -- desired access
//
//  Returns:    Handle; if NULL, use GetLastError()
//
//  History:    15-Nov-1996 AnirudhS  Created
//
//____________________________________________________________________________

#ifndef _CHICAGO_
SC_HANDLE
OpenScheduleService(DWORD dwDesiredAccess)
{
    SC_HANDLE hSC = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);

    if (hSC == NULL)
    {
        DEBUG_OUT_LASTERROR;
        return NULL;
    }

    SC_HANDLE hSchSvc = OpenService(hSC, SCHED_SERVICE_NAME, dwDesiredAccess);

    CloseServiceHandle(hSC);

    if (hSchSvc == NULL)
    {
        DEBUG_OUT_LASTERROR;
    }

    return hSchSvc;
}
#endif // !_CHICAGO_