windows-nt/Source/XPSP1/NT/admin/services/sched/setup/setup.cxx

1592 lines
44 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//+----------------------------------------------------------------------------
//
// 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_