|
|
//+----------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: setup.cxx
//
// Contents: Task Scheduler setup program
//
// Classes: None.
//
// Functions:
//
// History: 04-Apr-96 MarkBl Created
// 23-Sep-96 AnirudhS Added SetTaskFolderSecurity, etc.
// 30-Sep-96 AnirudhS Added /firstlogon and /logon options
// 15-Nov-96 AnirudhS Conditionally enable the service on NT too
// 01-09-97 DavidMun Add sysagent.exe path value under
// app paths, backup sysagent.exe
// 04-14-97 DavidMun Add DoMemphisSetup
// 03-03-01 JBenton Prefix BUG 333200 use of uninit memory
// 03-10-01 JBenton BUG 142333 tighten Tasks folder security
//
//-----------------------------------------------------------------------------
#include <windows.h>
#include <regstr.h>
#include <tchar.h>
#include <common.hxx>
#include <security.hxx>
#include "setupids.h"
#if defined(_CHICAGO_)
#include <shlobj.h>
#include <shellapi.h>
#include <shlobjp.h>
#include <dbcs.hxx>
#else // NT
#include <userenv.h>
#include <userenvp.h>
#endif // defined(_CHICAGO_)
#define ARRAY_LEN(a) (sizeof(a)/sizeof((a)[0]))
#define ARG_DELIMITERS TEXT(" \t")
#define MINUTES_BEFORE_IDLE_DEFAULT 15
//
// Note that the svchost registry keys to run schedule service as a part
// of netsvcs is set in hivesft.inx file.
//
#define SCHED_SERVICE_EXE_PATH TEXT("%SystemRoot%\\System32\\svchost.exe -k netsvcs")
#define SCHED_SERVICE_EXE TEXT("MSTask.exe")
#define SCHED_SERVICE_DLL TEXT("MSTask.dll")
#define SCHED_SERVICE_PRE_DLL TEXT("mstnt.dll")
#define SCHED_SERVICE_NAME TEXT("Schedule")
#define SCHED_SERVICE_GROUP TEXT("SchedulerGroup")
#define MINUTESBEFOREIDLE TEXT("MinutesBeforeIdle")
#define MAXLOGSIZEKB TEXT("MaxLogSizeKB")
#define TASKSFOLDER TEXT("TasksFolder")
#define FIRSTBOOT TEXT("FirstBoot")
#define SM_SA_KEY TEXT("Software\\Microsoft\\SchedulingAgent")
#define SAGE_EXE TEXT("SAGE.EXE")
#define SAGE_DLL TEXT("SAGE.DLL")
#define SYSAGENT_BAK TEXT("SYSAGENT.BAK")
#define SYSAGENT_EXE TEXT("SYSAGENT.EXE")
#define SAVED_SAGE_EXE TEXT("SAGEEXE.BAK")
#define SAVED_SAGE_DLL TEXT("SAGEDLL.BAK")
#define SAVED_SAGE_LINK TEXT("SAGELNK.BAK")
//
// Entry points from mstask.dll loaded by chicago or daytona versions of this
// program. Note they are used with GetProcAddress, which always wants an
// ANSI string.
//
#define CONVERT_SAGE_TASKS_API "ConvertSageTasksToJobs"
#define CONDITIONALLY_ENABLE_API "ConditionallyEnableService"
#define CONVERT_AT_TASKS_API "ConvertAtJobsToTasks"
//
// Function pointer types used when loading above functions from mstask.dll
//
typedef HRESULT (__stdcall *PSTDAPI)(void); typedef BOOL (__stdcall *PBOOLAPI)(void); typedef VOID (__stdcall *PVOIDAPI)(void);
// NOTE - Debug output is turned off. To turn it on, link in smdebug.lib.
#define schDebugOut(x)
VOID DoPreUnsetup(void);
#if defined(_CHICAGO_)
VOID DoPreSetup(void); VOID DoMemphisSetup(); BOOL GetOriginalShortcutLocation(LPTSTR tszPath);
#else // !_CHICAGO_
typedef struct _MYSIDINFO { PSID_IDENTIFIER_AUTHORITY pIdentifierAuthority; DWORD dwSubAuthority; PSID pSid; } MYSIDINFO;
DWORD SetTaskFolderSecurity(LPCWSTR pwszFolderPath); DWORD AllocateAndInitializeDomainSid( PSID pDomainSid, MYSIDINFO * pDomainSidInfo);
#endif // !_CHICAGO_
void DoSetup(void); void DoLogon(void); void DoFirstLogon(void); BOOL IsLogonMessageSupported(void); void ErrorDialog(UINT ErrorFmtStringID, TCHAR * szRoutine, DWORD ErrorCode);
HINSTANCE ghInstance = NULL;
extern "C" int __cdecl _purecall(void) { return 0; }
//+----------------------------------------------------------------------------
//
// Function: WinMainCRTStartup
//
// Synopsis: entry point
//
//-----------------------------------------------------------------------------
#ifdef _CHICAGO_
void WinMainCRTStartup(void) #else
void _cdecl main(int argc, char ** argv) #endif // _CHICAGO_
{ //
// Skip EXE name and find first parameter, if any
//
LPTSTR ptszStart; LPTSTR szArg1 = _tcstok(ptszStart = GetCommandLine(), ARG_DELIMITERS); szArg1 = _tcstok(NULL, ARG_DELIMITERS); //
// Switch based on the first parameter
//
if (szArg1 == NULL) { ; // Do nothing
#if DBG == 1
MessageBox(NULL, TEXT("Missing command line arguments"), TEXT("Task Scheduler setup/init"), MB_ICONERROR | MB_OK); #endif // DBG
} else if (lstrcmpi(szArg1, SCHED_LOGON_SWITCH) == 0) { DoLogon(); } else if (lstrcmpi(szArg1, SCHED_FIRSTLOGON_SWITCH) == 0) { DoFirstLogon(); } #if defined(_CHICAGO_)
else if (lstrcmpi(szArg1, SCHED_PRESETUP_SWITCH) == 0) { DoPreSetup(); } else if (lstrcmpi(szArg1, SCHED_MEMPHIS_SWITCH) == 0) { DoMemphisSetup(); } #endif // defined(_CHICAGO_)
else if (lstrcmpi(szArg1, SCHED_PREUNSETUP_SWITCH) == 0) { DoPreUnsetup(); } else if (lstrcmpi(szArg1, SCHED_SETUP_SWITCH) == 0) { DoSetup(); } #if DBG == 1
else { MessageBox(NULL, TEXT("Invalid command line"), TEXT("Task Scheduler setup/init"), MB_ICONERROR | MB_OK); } #endif // DBG
}
#if defined(_CHICAGO_)
#define MAX_KEY_LEN (ARRAY_LEN(REGSTR_PATH_APPPATHS) + MAX_PATH)
//+--------------------------------------------------------------------------
//
// Function: GetAppPathInfo
//
// Synopsis: Fill [ptszAppPathDefault] with the default value and
// [ptszAppPathVar] with the Path value in the
// [ptszFilename] application's key under the APPPATHS regkey.
//
// Arguments: [ptszFilename] - application name
// [ptszAppPathDefault] - if not NULL, filled with default value
// [cchDefaultBuf] - size of [ptszAppPathDefault] buffer
// [ptszAppPathVar] - if not NULL, filled with Path value
// [cchPathVarBuf] - size of [cchPathVarBuf] buffer
//
// Modifies: *[ptszAppPathDefault], *[ptszAppPathVar]
//
// History: 11-22-1996 DavidMun Created
//
// Notes: Both values are optional on the registry key, so if a
// requested value isn't found, it is set to "".
//
//---------------------------------------------------------------------------
VOID GetAppPathInfo( LPCTSTR ptszFilename, LPTSTR ptszAppPathDefault, ULONG cchDefaultBuf, LPTSTR ptszAppPathVar, ULONG cchPathVarBuf) { HKEY hkey = NULL; TCHAR tszAppPathKey[MAX_KEY_LEN];
//
// Initialize out vars
//
if (ptszAppPathDefault) { ptszAppPathDefault[0] = TEXT('\0'); }
if (ptszAppPathVar) { ptszAppPathVar[0] = TEXT('\0'); }
//
// Build registry key name for this app
//
lstrcpy(tszAppPathKey, REGSTR_PATH_APPPATHS); lstrcat(tszAppPathKey, TEXT("\\")); lstrcat(tszAppPathKey, ptszFilename);
do { LRESULT lr; lr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, tszAppPathKey, 0, KEY_QUERY_VALUE, &hkey);
if (lr != ERROR_SUCCESS) { schDebugOut((DEB_ERROR, "GetAppPathInfo: RegOpenKeyEx lr=%u\n", GetLastError())); break; }
//
// If the key could be opened, attempt to read requested values.
// Both are optional, so ignore errors.
//
DWORD cb; DWORD dwType;
if (ptszAppPathDefault) { cb = cchDefaultBuf * sizeof(TCHAR); lr = RegQueryValueEx(hkey, NULL, // value name
NULL, // reserved
&dwType, (LPBYTE) ptszAppPathDefault, &cb); }
if (ptszAppPathVar) { cb = cchPathVarBuf * sizeof(TCHAR);
lr = RegQueryValueEx(hkey, TEXT("Path"), // value name
NULL, // reserved
&dwType, (LPBYTE) ptszAppPathVar, &cb); } } while (0);
if (hkey) { RegCloseKey(hkey); } }
//+--------------------------------------------------------------------------
//
// Function: TerminateLast
//
// Synopsis: Find the last occurence of [tch] in [tsz], and overwrite it
// with a null.
//
// Arguments: [tsz] - string to search
// [tch] - char to search for
//
// Modifies: *[tsz]
//
// History: 1-09-1997 DavidMun Created
//
// Notes: If [tch] is not found, [tsz] is not modified.
//
//---------------------------------------------------------------------------
VOID TerminateLast(LPTSTR tsz, TCHAR tch) { LPTSTR ptszLast = NULL; LPTSTR ptsz;
for (ptsz = tsz; *ptsz; ptsz = NextChar(ptsz)) { if (*ptsz == tch) { ptszLast = ptsz; } }
if (ptszLast) { *ptszLast = TEXT('\0'); } }
//+--------------------------------------------------------------------------
//
// Function: DoMemphisSetup
//
// Synopsis: If the app path for SAGE's sysagent.exe exists, replace the
// sysagent.exe at that location with the one in
// %windir%\system.
//
// History: 4-14-1997 DavidMun Created
//
// Notes: This is necessary for Memphis installs because their initial
// setup runs in 16 bit mode. Therefore they can't invoke
// this exe before the ini file runs. Therefore the inf file
// for memphis can't use the CustomLDID section.
//
//---------------------------------------------------------------------------
VOID DoMemphisSetup() { do { //
// Initialize source path
//
UINT cchDirSize; TCHAR tszNewSysAgent[MAX_PATH + 1];
cchDirSize = GetSystemDirectory(tszNewSysAgent, MAX_PATH);
if (!cchDirSize || cchDirSize > MAX_PATH) { schDebugOut((DEB_ERROR, "DoMemphisSetup: GetSystemDirectory %uL\n", GetLastError())); break; } lstrcat(tszNewSysAgent, TEXT("\\") SYSAGENT_EXE);
//
// Initialize destination path to the location of SAGE's sysagent.exe.
//
// If SAGE isn't installed on this system, put our sysagent.exe
// in program files\plus!, since that is created by memphis and is
// the default location if a user install Plus! over Memphis.
//
TCHAR tszDestPath[MAX_PATH+1];
GetAppPathInfo(SYSAGENT_EXE, tszDestPath, MAX_PATH + 1, NULL, 0);
if (!*tszDestPath) { BOOL fOk;
cchDirSize = GetWindowsDirectory(tszDestPath, MAX_PATH);
if (!cchDirSize || cchDirSize > MAX_PATH) { schDebugOut((DEB_ERROR, "DoMemphisSetup: GetWindowsDirectory %uL\n", GetLastError())); break; }
fOk = LoadString(ghInstance, IDS_DEFAULT_SYSAGENT_PATH, tszDestPath + 2, // preserve drive letter and colon
ARRAY_LEN(tszDestPath) - 2); lstrcat(tszDestPath, TEXT("\\") SYSAGENT_EXE); }
//
// Copy the task scheduler version of sysagent.exe over the sage version
//
BOOL fOk = CopyFile(tszNewSysAgent, // pointer to name of an existing file
tszDestPath, // pointer to filename to copy to
FALSE); // don't fail if destination file exists
//
// If the old sysagent.exe was successfully copied over with the new
// sysagent.exe, delete the extra new sysagent.exe in %windir%\system.
//
if (fOk) { DeleteFile(tszNewSysAgent); } } while (0);
//
// Do the rest of the setup, which is common to win95/memphis
//
DoSetup(); }
//+----------------------------------------------------------------------------
//
// Function: DoPreSetup
//
// Synopsis: Makes backups of existing sage binaries, so that they can
// be restored on uninstall.
//
//-----------------------------------------------------------------------------
VOID DoPreSetup(void) { TCHAR tszSourceFile[MAX_PATH + 1]; TCHAR tszDestFile[MAX_PATH + 1]; UINT ccSystemDirSize;
//
// See if there's an app path entry for sysagent.exe.
//
// If not, continue, since the plus pack may never have been installed.
//
// If it is found, get the application full path and truncate at the
// last backslash to make a string with the full path to the
// application's directory. Then add a new value with that string.
//
// This is necessary so the IExpress inf can create a custom LDID
// (logical directory ID) pointing to the directory in which sysagent.exe
// resides.
//
TCHAR tszSysagentInstallDir[MAX_PATH+1]; GetAppPathInfo(SYSAGENT_EXE, tszSysagentInstallDir, MAX_PATH + 1, NULL, 0);
if (*tszSysagentInstallDir) { GetShortPathName(tszSysagentInstallDir, tszSysagentInstallDir, MAX_PATH + 1);
TerminateLast(tszSysagentInstallDir, TEXT('\\'));
HKEY hkSysAgent;
LONG lr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_APPPATHS TEXT("\\") SYSAGENT_EXE, 0, KEY_SET_VALUE, &hkSysAgent);
if (lr == ERROR_SUCCESS) { lr = RegSetValueEx(hkSysAgent, TEXT("InstallDir"), 0, REG_SZ, (LPBYTE) tszSysagentInstallDir, (lstrlen(tszSysagentInstallDir) + 1) * sizeof(TCHAR));
RegCloseKey(hkSysAgent);
if (lr != ERROR_SUCCESS) { schDebugOut((DEB_ERROR, "DoPreSetup: RegSetValueEx error %uL\n", lr)); } } else { schDebugOut((DEB_ERROR, "DoPreSetup: RegOpenKeyEx error %uL\n", lr)); } }
//
// Initialize source and destination names
//
if (!(ccSystemDirSize = GetSystemDirectory(tszSourceFile, MAX_PATH)) || ccSystemDirSize > MAX_PATH) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("GetSystemDirectory"), GetLastError()); return; }
lstrcat(tszSourceFile, TEXT("\\")); lstrcpy(tszDestFile, tszSourceFile);
//
// Backup the existing sage.exe and sage.dll by copying & renaming them
// from the system to the windows dir. Don't fail if the binaries don't
// exist, since sage may never have been installed.
//
// Also fail quietly if the backup copies already exist. This keeps us
// from overwriting saved SAGE binaries if user reinstalls us.
//
lstrcpy(&tszSourceFile[ccSystemDirSize + 1], SAGE_EXE); lstrcpy(&tszDestFile[ccSystemDirSize + 1], SAVED_SAGE_EXE); CopyFile(tszSourceFile, tszDestFile, TRUE);
lstrcpy(&tszSourceFile[ccSystemDirSize + 1], SAGE_DLL); lstrcpy(&tszDestFile[ccSystemDirSize + 1], SAVED_SAGE_DLL); CopyFile(tszSourceFile, tszDestFile, TRUE);
//
// Back up system agent link
//
if (GetOriginalShortcutLocation(tszSourceFile)) { lstrcpy(&tszDestFile[ccSystemDirSize + 1], SAVED_SAGE_LINK); CopyFile(tszSourceFile, tszDestFile, TRUE); }
//
// Backup the sysagent.exe file
//
if (*tszSysagentInstallDir) { lstrcat(tszSysagentInstallDir, TEXT("\\")); lstrcpy(tszSourceFile, tszSysagentInstallDir); lstrcpy(tszDestFile, tszSysagentInstallDir);
lstrcat(tszSourceFile, SYSAGENT_EXE); lstrcat(tszDestFile, SYSAGENT_BAK); CopyFile(tszSourceFile, tszDestFile, TRUE); } }
//+----------------------------------------------------------------------------
//
// Function: DoPreUnsetup
//
// Synopsis: Restore the System Agent start menu shortcut.
//
//-----------------------------------------------------------------------------
VOID DoPreUnsetup(void) { UINT ccSystemDirSize; TCHAR tszSourceFile[MAX_PATH]; TCHAR tszDestFile[MAX_PATH];
//
// Get the full path to the source file (the backup of the link)
//
if (!(ccSystemDirSize = GetSystemDirectory(tszSourceFile, MAX_PATH)) || ccSystemDirSize > MAX_PATH) { schDebugOut((DEB_ERROR, "DoPreUnsetup: GetSystemDirectory error = %u\n", GetLastError())); return; }
tszSourceFile[ccSystemDirSize] = TEXT('\\'); lstrcpy(&tszSourceFile[ccSystemDirSize + 1], SAVED_SAGE_LINK);
//
// Get the full path to the destination file (the original location of the
// link).
//
if (!GetOriginalShortcutLocation(tszDestFile)) { return; }
//
// Now move the source file to the destination file
//
BOOL fOk = MoveFile(tszSourceFile, tszDestFile);
if (!fOk) { schDebugOut((DEB_ERROR, "DoPreUnsetup: MoveFile(%s,%s) error = %u\n", tszSourceFile, tszDestFile, GetLastError())); } }
//+---------------------------------------------------------------------------
//
// Function: GetOriginalShortcutLocation
//
// Synopsis: Fill [tszPath] with the full path to the SAGE shortcut.
//
// Returns: TRUE on success, FALSE on failure
//
// History: 11-06-96 DavidMun Created
//
//----------------------------------------------------------------------------
BOOL GetOriginalShortcutLocation(LPTSTR tszPath) { #define LINK_EXT TEXT(".lnk")
HRESULT hr; LPITEMIDLIST pidl;
hr = SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAMS, &pidl);
if (FAILED(hr)) { schDebugOut((DEB_ERROR, "GetOriginalShortcutLocation: SHGetSpecialFolderLocation hr=0x%x\n", hr)); return FALSE; }
BOOL fOk;
fOk = SHGetPathFromIDList(pidl, tszPath);
ILFree(pidl);
if (!fOk) { schDebugOut((DEB_ERROR, "GetOriginalShortcutLocation: SHGetPathFromIDList failed\n")); return FALSE; }
lstrcat(tszPath, TEXT("\\"));
// In English IDS_SAGE_SHORTCUT_GROUP is "Accessories\\System Tools"
TCHAR tszGroupName[MAX_PATH];
fOk = LoadString(ghInstance, IDS_SAGE_SHORTCUT_GROUP, tszGroupName, ARRAY_LEN(tszGroupName));
if (!fOk) { schDebugOut((DEB_ERROR, "GetOriginalShortcutLocation: LoadString(IDS_SAGE_SHORTCUT_GROUP) error = %u\n", GetLastError())); return FALSE; }
lstrcat(tszPath, tszGroupName);
lstrcat(tszPath, TEXT("\\"));
TCHAR tszLinkName[MAX_PATH];
fOk = LoadString(ghInstance, IDS_SAGE_SHORTCUT, tszLinkName, ARRAY_LEN(tszLinkName));
if (!fOk) { schDebugOut((DEB_ERROR, "GetOriginalShortcutLocation: LoadString(IDS_SAGE_SHORTCUT) error = %u\n", GetLastError())); return FALSE; }
lstrcat(tszPath, tszLinkName); lstrcat(tszPath, LINK_EXT);
return TRUE; }
#else // NT
//+---------------------------------------------------------------------------
//
// Function: DoPreUnsetup
//
// Synopsis: Delete the admin tools (common) scheduled tasks link
//
// History: 11-11-96 DavidMun Created
// 06-17-98 AnirudhS Link no longer created, nothing to do.
//
//----------------------------------------------------------------------------
VOID DoPreUnsetup(void) { ; }
#endif // defined(_CHICAGO_)
//+----------------------------------------------------------------------------
//
// Function: DoSetup
//
// Synopsis: Performs the normal setup procedure
//
//-----------------------------------------------------------------------------
void DoSetup(void) { #if !defined(_CHICAGO_)
#define SCHED_SERVICE_DEPENDENCY L"RpcSs\0"
#define SCC_AT_SVC_KEY L"System\\CurrentControlSet\\Services\\Schedule"
#define TASKS_FOLDER_DEFAULT L"%SystemRoot%\\Tasks"
#endif // _CHICAGO_
#if defined(_CHICAGO_)
TCHAR szServiceExePath[MAX_PATH + 1]; #else
TCHAR szTasksFolder[MAX_PATH + 1] = TEXT(""); #endif // ! _CHICAGO_
TCHAR tszDisplayName[50]; // "Task Scheduler"
DWORD dwTmp; HKEY hKey;
//
// Disable hard-error popups.
//
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
ghInstance = GetModuleHandle(NULL);
//
// Load the service display name.
//
int cch = LoadString(ghInstance, IDS_SERVICE_DISPLAY_NAME, tszDisplayName, ARRAY_LEN(tszDisplayName)); if (!(0 < cch && cch < ARRAY_LEN(tszDisplayName) - 1)) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("LoadString"), GetLastError()); return; }
#if defined(_CHICAGO_)
//
// Compute the path to the service EXE.
//
UINT ccSystemDirSize;
if (!(ccSystemDirSize = GetSystemDirectory(szServiceExePath, MAX_PATH))) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("GetSystemDirectory"), GetLastError()); return; }
lstrcpy(&szServiceExePath[ccSystemDirSize], TEXT("\\")); lstrcpy(&szServiceExePath[ccSystemDirSize + 1], SCHED_SERVICE_EXE); #endif
//
// Create/open the Scheduling Agent key in Software\Microsoft.
//
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, SM_SA_KEY, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwTmp) == ERROR_SUCCESS) { // Set MinutesBeforeIdle to a default value of 15 mins.
// Ignore return code.
//
dwTmp = MINUTES_BEFORE_IDLE_DEFAULT;
RegSetValueEx(hKey, MINUTESBEFOREIDLE, 0, REG_DWORD, (CONST BYTE *)&dwTmp, sizeof(dwTmp));
// Set MaxLogSizeKB to 32K or 0x7FFF.
// Ignore return code.
//
dwTmp = MAX_LOG_SIZE_DEFAULT;
RegSetValueEx(hKey, MAXLOGSIZEKB, 0, REG_DWORD, (CONST BYTE *)&dwTmp, sizeof(dwTmp));
#if !defined(_CHICAGO_)
// Read the tasks folder location. The .INF should've set this.
// If not, default.
//
dwTmp = MAX_PATH * sizeof(TCHAR);
if (RegQueryValueEx(hKey, TASKSFOLDER, NULL, NULL, (LPBYTE)szTasksFolder, &dwTmp) != ERROR_SUCCESS || szTasksFolder[0] == TEXT('\0')) { lstrcpy(szTasksFolder, TASKS_FOLDER_DEFAULT); }
// Set FirstBoot to non-zero.
// Ignore return code.
//
dwTmp = 1;
RegSetValueEx(hKey, FIRSTBOOT, 0, REG_DWORD, (CONST BYTE *)&dwTmp, sizeof(dwTmp));
#endif // ! _CHICAGO_
RegCloseKey(hKey); }
#if !defined(_CHICAGO_)
//
// Set the right permissions on the job folder.
// The default permissions allow anyone to delete any job, which we
// don't want.
//
{ TCHAR szTaskFolderPath[MAX_PATH + 1]; DWORD cch = ExpandEnvironmentStrings(szTasksFolder, szTaskFolderPath, ARRAY_LEN(szTaskFolderPath)); if (cch == 0 || cch > ARRAY_LEN(szTaskFolderPath)) { //
// The job folder path is too long.
//
ErrorDialog(IDS_INSTALL_FAILURE, TEXT("ExpandEnvironmentStrings"), cch ? ERROR_BUFFER_OVERFLOW : GetLastError()); return; }
DWORD dwError = SetTaskFolderSecurity(szTaskFolderPath); if (dwError != ERROR_SUCCESS) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("SetTaskFolderSecurity"), dwError); return; } } #endif // ! _CHICAGO_
HINSTANCE hinstMSTask; #if defined(_CHICAGO_)
hinstMSTask = LoadLibrary(SCHED_SERVICE_DLL); #else
hinstMSTask = LoadLibrary(SCHED_SERVICE_PRE_DLL);
//
// If we're being installed as part of DS setup then we're not using
// iexpress, so dll name is SCHED_SERVICE_DLL.
//
if (!hinstMSTask) { hinstMSTask = LoadLibrary(SCHED_SERVICE_DLL); } #endif // defined(_CHICAGO_)
if (!hinstMSTask) { ErrorDialog(IDS_INSTALL_FAILURE, SCHED_SERVICE_DLL, GetLastError()); return; }
#if defined(_CHICAGO_)
PSTDAPI pfnConvertLegacyJobsToTasks = (PSTDAPI) GetProcAddress(hinstMSTask, CONVERT_SAGE_TASKS_API); #else
PVOIDAPI pfnConvertLegacyJobsToTasks = (PVOIDAPI) GetProcAddress(hinstMSTask, CONVERT_AT_TASKS_API); #endif
PBOOLAPI pfnConditionallyEnableService = (PBOOLAPI) GetProcAddress(hinstMSTask, CONDITIONALLY_ENABLE_API);
if (!pfnConvertLegacyJobsToTasks || !pfnConditionallyEnableService) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("GetProcAddress"), GetLastError()); return; }
pfnConvertLegacyJobsToTasks();
//
// ConditionallyEnableService *MUST* be after ConvertSageTasksToJobs
// or ConvertAtJobsToTasks!
//
#if defined(_CHICAGO_)
//
// If and only if there are jobs to run, enable the service and create
// the "Run = mstinit.exe /firstlogon" registry entry.
//
BOOL fServiceEnabled = pfnConditionallyEnableService();
if (fServiceEnabled) { //
// Start the service, if not already running.
//
HWND hwnd = FindWindow(SCHED_SERVICE_NAME, tszDisplayName);
if (hwnd == NULL) { //
// Create a process to open the log.
//
STARTUPINFO si; PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si)); si.cb = sizeof (STARTUPINFO);
BOOL fRet = CreateProcess(szServiceExePath, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi);
if (fRet == 0) { ErrorDialog(IDS_START_FAILURE, TEXT("CreateProcess"), GetLastError()); return; } } } #else // NT
//
// Install the Win32 service.
//
SC_HANDLE hSCMgr = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
if (hSCMgr == NULL) { //
// Yow, we're hosed.
//
ErrorDialog(IDS_INSTALL_FAILURE, TEXT("OpenSCManager"), GetLastError()); return; }
//
// Is the service already installed? If so, change its parameters;
// otherwise, create it.
//
SC_HANDLE hSvc = OpenService(hSCMgr, SCHED_SERVICE_NAME, SERVICE_CHANGE_CONFIG);
if (hSvc == NULL) { hSvc = CreateService(hSCMgr, SCHED_SERVICE_NAME, tszDisplayName, SERVICE_CHANGE_CONFIG, SERVICE_WIN32_SHARE_PROCESS | SERVICE_INTERACTIVE_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, SCHED_SERVICE_EXE_PATH, SCHED_SERVICE_GROUP, NULL, SCHED_SERVICE_DEPENDENCY, NULL, NULL);
if (hSvc == NULL) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("CreateService"), GetLastError()); CloseServiceHandle(hSCMgr); return; } } else { //
// This path will be followed when we upgrade the At service
// to the Scheduling Agent. The service name will remain the
// same, but the display name will be set to the new display
// name (the At service had no display name) and the image path
// will be changed to point to the new exe.
// (The old binary will be left on disk in order to make it easy
// to revert to it, in case of compatibility problems.)
//
if (!ChangeServiceConfig( hSvc, // hService
SERVICE_WIN32_SHARE_PROCESS | SERVICE_INTERACTIVE_PROCESS, // dwServiceType
SERVICE_AUTO_START, // dwStartType
SERVICE_ERROR_NORMAL, // dwErrorControl
SCHED_SERVICE_EXE_PATH, // lpBinaryPathName
SCHED_SERVICE_GROUP, // lpLoadOrderGroup
NULL, // lpdwTagId
SCHED_SERVICE_DEPENDENCY, // lpDependencies
L".\\LocalSystem", // lpServiceStartName
L"", // lpPassword
tszDisplayName // lpDisplayName
)) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("ChangeServiceConfig"), GetLastError()); CloseServiceHandle(hSvc); CloseServiceHandle(hSCMgr); return; } }
CloseServiceHandle(hSvc); CloseServiceHandle(hSCMgr);
//
// If and only if there are jobs to run, enable the service and create
// the "Run = mstinit.exe /firstlogon" registry entry.
//
pfnConditionallyEnableService();
#endif // _CHICAGO_
}
//+----------------------------------------------------------------------------
//
// Function: DoLogon
//
// Synopsis: Sends a message to the service indicating that a user has
// logged on.
//
//-----------------------------------------------------------------------------
void DoLogon(void) { //
// This instance has been invoked by the Run key of the registry to
// signal the running service that a user has logged on.
//
#ifdef _CHICAGO_
schDebugOut((DEB_ITRACE, "Sending user log on message.\n"));
HWND hwndSvc = FindWindow(SCHED_CLASS, SCHED_TITLE);
if (hwndSvc == NULL) { schDebugOut((DEB_ITRACE, "FindWindow: service window not found (%d)\n", GetLastError())); } else { PostMessage(hwndSvc, WM_SCHED_WIN9X_USER_LOGON, 0, 0); }
#else // NT
schDebugOut((DEB_ITRACE, "Sending user log on notification...........\n"));
SC_HANDLE hSC = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (hSC == NULL) { schDebugOut((DEB_ERROR, "DoLogon: OpenSCManager error = %uL\n", GetLastError())); return; }
SC_HANDLE hSvc = OpenService(hSC, SCHED_SERVICE_NAME, SERVICE_USER_DEFINED_CONTROL); if (hSvc == NULL) { schDebugOut((DEB_ERROR, "DoLogon: OpenService(%s) error = %uL\n", SCHED_SERVICE_NAME, GetLastError())); CloseServiceHandle(hSC); return; }
BOOL fSucceeded; const int NOTIFY_RETRIES = 20; const DWORD NOTIFY_SLEEP = 4000;
//
// Use a retry loop to notify the service. This is done
// because, if the user logs in quickly, the service may not
// be started when the shell runs this instance.
//
for (int i = 1; ; i++) { SERVICE_STATUS Status; fSucceeded = ControlService(hSvc, SERVICE_CONTROL_USER_LOGON, &Status); if (fSucceeded) { break; }
if (i >= NOTIFY_RETRIES) { SetLastError(0); // There's no good error code
break; }
schDebugOut((DEB_ITRACE, "Service notification failed, waiting to " "send it again...\n"));
Sleep(NOTIFY_SLEEP);
}
CloseServiceHandle(hSvc); CloseServiceHandle(hSC);
#endif // _CHICAGO_
}
//+----------------------------------------------------------------------------
//
// Function: DoFirstLogon
//
// Synopsis: Checks whether the shell supports the tray startup notify
// message. If it does, simply removes the "Run = " value from
// the registry, so that this will not run at future logons.
// If it doesn't, changes the "Run = " value's command line
// parameter from "/FirstLogon" to "/Logon", and then calls
// DoLogon.
//
//-----------------------------------------------------------------------------
void DoFirstLogon(void) { // CODEWORK: Use winlogon for logon notifies on NT?
BOOL bLogonMessageSupported = IsLogonMessageSupported();
HKEY hRunKey; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUN, 0, KEY_SET_VALUE, &hRunKey) == ERROR_SUCCESS) { if (bLogonMessageSupported) { RegDeleteValue(hRunKey, SCH_RUN_VALUE); } else { #define NewValue SCHED_SETUP_APP_NAME TEXT(" ") SCHED_LOGON_SWITCH
RegSetValueEx(hRunKey, SCH_RUN_VALUE, 0, REG_SZ, (CONST BYTE *) (NewValue), sizeof(NewValue)); }
RegCloseKey(hRunKey); } // If RegOpenKeyEx fails, we just have to retry at the next logon.
if (! bLogonMessageSupported) { DoLogon(); } }
//+---------------------------------------------------------------------------
//
// Function: IsLogonMessageSupported
//
// Synopsis: Determines whether the currently installed shell version
// broadcasts the "TaskbarCreated" message.
//
// Arguments: None.
//
// Returns: TRUE - the logon message is supported.
// FALSE - it is not supported; or, this cannot be determined.
//
// Notes: The "TaskbarCreated" message tells the service that (1) a
// user has logged on, and (2) it's time to create our tray icon.
//
//----------------------------------------------------------------------------
BOOL IsLogonMessageSupported() { // CODEWORK Use GetFileVersionInfo instead, once the version numbers are fixed.
HINSTANCE hLib = LoadLibrary(TEXT("SHELL32.DLL")); if (hLib == NULL) { return FALSE; }
FARPROC VersionProc = GetProcAddress(hLib, "DllGetVersion");
FreeLibrary(hLib);
//
// Versions of shell32.dll that export DllGetVersion support the logon
// message.
//
return (VersionProc != NULL); }
//+----------------------------------------------------------------------------
//
// Function: ErrorDialog
//
// Synopsis: Displays an error message.
//
//-----------------------------------------------------------------------------
void ErrorDialog(UINT ErrorFmtStringID, TCHAR * szRoutine, DWORD ErrorCode) { #define ERROR_BUFFER_SIZE (MAX_PATH * 2)
TCHAR szErrorFmt[MAX_PATH + 1] = TEXT(""); TCHAR szError[ERROR_BUFFER_SIZE + 1]; TCHAR * pszError = szError;
LoadString(ghInstance, ErrorFmtStringID, szErrorFmt, MAX_PATH);
if (*szErrorFmt) { wsprintf(szError, szErrorFmt, szRoutine, ErrorCode); } else { //
// Not a localizable string, but done just in case LoadString
// should fail for some reason.
//
lstrcpy(szErrorFmt, TEXT("Error installing Task Scheduler; error = 0x%x")); wsprintf(szError, szErrorFmt, ErrorCode); }
MessageBox(NULL, szError, NULL, MB_ICONSTOP | MB_OK); }
#if !defined(_CHICAGO_)
//+---------------------------------------------------------------------------
//
// Function: SetTaskFolderSecurity
//
// Synopsis: Grant the following permissions to the task folder:
//
// LocalSystem All Access.
// Domain Administrators All Access.
// World RWX Access (no permission to delete
// child files).
//
// Arguments: [pwszFolderPath] -- Task folder path.
//
// Notes: None.
//
//----------------------------------------------------------------------------
DWORD SetTaskFolderSecurity(LPCWSTR pwszFolderPath) { #define BASE_SID_COUNT 4
#define DOMAIN_SID_COUNT 1
#define TASK_ACE_COUNT 4
PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL; DWORD Status = ERROR_SUCCESS; DWORD i;
//
// Build the SIDs that will go in the security descriptor.
//
SID_IDENTIFIER_AUTHORITY NtAuth = SECURITY_NT_AUTHORITY; SID_IDENTIFIER_AUTHORITY CreatorAuth = SECURITY_CREATOR_SID_AUTHORITY;
MYSIDINFO rgBaseSidInfo[BASE_SID_COUNT] = { { &NtAuth, // Local System.
SECURITY_LOCAL_SYSTEM_RID, NULL }, { &NtAuth, // Built in domain. (Used for
SECURITY_BUILTIN_DOMAIN_RID, // domain admins SID.)
NULL }, { &NtAuth, // Authenticated user.
SECURITY_AUTHENTICATED_USER_RID, NULL }, { &CreatorAuth, // Creator.
SECURITY_CREATOR_OWNER_RID, NULL }, };
MYSIDINFO rgDomainSidInfo[DOMAIN_SID_COUNT] = { { NULL, // Domain administrators.
DOMAIN_ALIAS_RID_ADMINS, NULL } };
//
// Create the base SIDs.
//
for (i = 0; i < BASE_SID_COUNT; i++) { if (!AllocateAndInitializeSid(rgBaseSidInfo[i].pIdentifierAuthority, 1, rgBaseSidInfo[i].dwSubAuthority, 0, 0, 0, 0, 0, 0, 0, &rgBaseSidInfo[i].pSid)) { Status = GetLastError(); break; } }
if (Status == ERROR_SUCCESS) { //
// Create the domain SIDs.
//
for (i = 0; i < DOMAIN_SID_COUNT; i++) { Status = AllocateAndInitializeDomainSid(rgBaseSidInfo[1].pSid, &rgDomainSidInfo[i]);
if (Status != ERROR_SUCCESS) { break; } } }
//
// Create the security descriptor.
//
PACCESS_ALLOWED_ACE rgAce[TASK_ACE_COUNT] = { NULL, NULL, NULL // Supply this to CreateSD so we
}; // don't have to allocate memory.
MYACE rgMyAce[TASK_ACE_COUNT] = { { FILE_ALL_ACCESS, OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE, rgBaseSidInfo[0].pSid }, // Local System
{ FILE_ALL_ACCESS, OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE, rgDomainSidInfo[0].pSid }, // Domain admins
{ FILE_GENERIC_READ | FILE_GENERIC_EXECUTE | FILE_WRITE_DATA, 0, rgBaseSidInfo[2].pSid }, // Authenticated user
{ FILE_ALL_ACCESS, OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE, rgBaseSidInfo[3].pSid } // Creator
};
if (Status != ERROR_SUCCESS) { goto CleanExit; }
if ((pSecurityDescriptor = CreateSecurityDescriptor(TASK_ACE_COUNT, rgMyAce, rgAce, &Status)) == NULL) { goto CleanExit; }
//
// Finally, set permissions.
//
if (!SetFileSecurity(pwszFolderPath, DACL_SECURITY_INFORMATION, pSecurityDescriptor)) { Status = GetLastError(); goto CleanExit; }
CleanExit: for (i = 0; i < BASE_SID_COUNT; i++) { if (rgBaseSidInfo[i].pSid != NULL) { FreeSid(rgBaseSidInfo[i].pSid); } } for (i = 0; i < DOMAIN_SID_COUNT; i++) { LocalFree(rgDomainSidInfo[i].pSid); } if (pSecurityDescriptor != NULL) { DeleteSecurityDescriptor(pSecurityDescriptor); }
return(Status); }
//+---------------------------------------------------------------------------
//
// Function: AllocateAndInitializeDomainSid
//
// Synopsis:
//
// Arguments: [pDomainSid] --
// [pDomainSidInfo] --
//
// Notes: None.
//
//----------------------------------------------------------------------------
DWORD AllocateAndInitializeDomainSid( PSID pDomainSid, MYSIDINFO * pDomainSidInfo) { UCHAR DomainIdSubAuthorityCount; DWORD SidLength;
//
// Allocate a Sid which has one more sub-authority than the domain ID.
//
DomainIdSubAuthorityCount = *(GetSidSubAuthorityCount(pDomainSid)); SidLength = GetSidLengthRequired(DomainIdSubAuthorityCount + 1);
pDomainSidInfo->pSid = (PSID) LocalAlloc(0, SidLength);
if (pDomainSidInfo->pSid == NULL) { return(ERROR_NOT_ENOUGH_MEMORY); }
//
// Initialize the new SID to have the same initial value as the
// domain ID.
//
if (!CopySid(SidLength, pDomainSidInfo->pSid, pDomainSid)) { LocalFree(pDomainSidInfo->pSid); pDomainSidInfo->pSid = NULL; return(GetLastError()); }
//
// Adjust the sub-authority count and add the relative Id unique
// to the newly allocated SID
//
(*(GetSidSubAuthorityCount(pDomainSidInfo->pSid)))++; *(GetSidSubAuthority(pDomainSidInfo->pSid, DomainIdSubAuthorityCount)) = pDomainSidInfo->dwSubAuthority;
return(ERROR_SUCCESS); }
#endif // !_CHICAGO_
|