summaryrefslogtreecommitdiffstats
path: root/private/os2/os2ss
diff options
context:
space:
mode:
Diffstat (limited to 'private/os2/os2ss')
-rw-r--r--private/os2/os2ss/makefile6
-rw-r--r--private/os2/os2ss/os2ss.c165
-rw-r--r--private/os2/os2ss/os2ss.rc52
-rw-r--r--private/os2/os2ss/sbcnfg.c1674
-rw-r--r--private/os2/os2ss/sbinit.c96
-rw-r--r--private/os2/os2ss/sbreqst.c181
-rw-r--r--private/os2/os2ss/sources68
7 files changed, 2242 insertions, 0 deletions
diff --git a/private/os2/os2ss/makefile b/private/os2/os2ss/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/os2/os2ss/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/os2/os2ss/os2ss.c b/private/os2/os2ss/os2ss.c
new file mode 100644
index 000000000..c61b8bb62
--- /dev/null
+++ b/private/os2/os2ss/os2ss.c
@@ -0,0 +1,165 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ os2srv.c
+
+Abstract:
+
+ This is the main startup module for the OS/2 Emulation Subsystem Server
+
+Author:
+
+ Steve Wood (stevewo) 22-Aug-1989
+
+Environment:
+
+ User Mode Only
+
+Revision History:
+
+--*/
+
+#include "os2srv.h"
+
+static WCHAR Os2InitName[] = L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\OS/2 Subsystem for NT\\1.0\\os2.ini";
+
+void __cdecl
+main(
+ IN ULONG argc,
+ IN PCH argv[],
+ IN PCH envp[],
+ IN ULONG DebugFlag OPTIONAL
+ )
+{
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING DirectoryName_U;
+ UNICODE_STRING Os2Init_U;
+ HANDLE Os2IniKeyHandle;
+ CHAR localSecurityDescriptor[SECURITY_DESCRIPTOR_MIN_LENGTH];
+ PSECURITY_DESCRIPTOR securityDescriptor;
+ NTSTATUS Status;
+#if PMNT
+ extern VOID Os2SbProbeForInitialSetup(VOID);
+#endif
+
+ UNREFERENCED_PARAMETER(argc);
+ UNREFERENCED_PARAMETER(argv);
+ UNREFERENCED_PARAMETER(envp);
+ UNREFERENCED_PARAMETER(DebugFlag);
+
+#ifdef PMNT
+ //
+ // Check out if this is the first time after system setup that the OS/2 SS
+ // has been run. If so, do the necessary privileged initialization of the
+ // registry for the OS/2 SS
+ //
+ // Note -- This code has been moved to WINLOGON. It's only duplicated here
+ // (in an older and outdated version) so PMNT can be based on NT build 438.
+ //
+ //
+
+ Os2SbProbeForInitialSetup();
+#endif
+
+ //
+ // Create a root directory in the object name space that will be used
+ // to contain all of the named objects created by the OS/2 Emulation
+ // subsystem.
+ //
+
+ RtlInitUnicodeString( &DirectoryName_U, OS2_SS_ROOT_OBJECT_DIRECTORY );
+
+ Status = RtlCreateSecurityDescriptor( (PSECURITY_DESCRIPTOR)
+ &localSecurityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION );
+ ASSERT( NT_SUCCESS( Status ) );
+
+ Status = RtlSetDaclSecurityDescriptor( (PSECURITY_DESCRIPTOR)
+ &localSecurityDescriptor,
+ TRUE,
+ (PACL) NULL,
+ FALSE );
+
+ securityDescriptor = (PSECURITY_DESCRIPTOR) &localSecurityDescriptor;
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &DirectoryName_U,
+ OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
+ NULL,
+ securityDescriptor
+ );
+
+ Status = NtCreateDirectoryObject( &Os2RootDirectory,
+ DIRECTORY_ALL_ACCESS,
+ &ObjectAttributes
+ );
+
+ ASSERT( NT_SUCCESS( Status ) );
+
+ if (!NT_SUCCESS( Status )) {
+#if DBG
+ KdPrint(( "OS2SRV: Unable to initialize server. Status == %X\n",
+ Status
+ ));
+#endif
+ }
+
+ //
+ // Reset OS2.INI key security descriptor (winlogon sets it to admin only)
+ //
+ RtlInitUnicodeString( &Os2Init_U, Os2InitName );
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &Os2Init_U,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ Status = NtOpenKey(&Os2IniKeyHandle,
+ KEY_ALL_ACCESS,
+ &ObjectAttributes
+ );
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ KdPrint(("Os2ss: Can't open os2.ini key, rc = %lx\n", Status));
+#endif
+ }
+
+ Status = NtSetSecurityObject(Os2IniKeyHandle,
+ DACL_SECURITY_INFORMATION,
+ securityDescriptor
+ );
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ KdPrint(("Os2ss: Can't set security on os2.ini key, rc = %lx\n", Status));
+#endif
+ }
+
+ //
+ // Initialize the OS2 Server Session Manager API Port, the listen thread
+ // one request thread.
+ //
+
+ Status = Os2SbApiPortInitialize();
+ ASSERT( NT_SUCCESS( Status ) );
+
+
+
+ //
+ // Connect to the session manager so we can start foreign sessions
+ //
+
+ Status = SmConnectToSm( &Os2SbApiPortName_U,
+ Os2SbApiPort,
+ IMAGE_SUBSYSTEM_OS2_CUI,
+ &Os2SmApiPort
+ );
+ ASSERT( NT_SUCCESS( Status ) );
+
+ NtTerminateThread( NtCurrentThread(), STATUS_SUCCESS);
+}
diff --git a/private/os2/os2ss/os2ss.rc b/private/os2/os2ss/os2ss.rc
new file mode 100644
index 000000000..e5c5e7d5b
--- /dev/null
+++ b/private/os2/os2ss/os2ss.rc
@@ -0,0 +1,52 @@
+/*
+** Template for version resources. Place this in your .rc file,
+** editing the values for VER_FILETYPE, VER_FILESUBTYPE,
+** VER_FILEDESCRIPTION_STR and VER_INTERNALNAME_STR as needed.
+** See winver.h for possible values.
+**
+** Ntverp.h defines several global values that don't need to be
+** changed except for official releases such as betas, sdk updates, etc.
+**
+** Common.ver has the actual version resource structure that all these
+** #defines eventually initialize.
+*/
+
+#include <windows.h>
+
+#include <ntverp.h>
+
+/*-----------------------------------------------*/
+/* the following lines are specific to this file */
+/*-----------------------------------------------*/
+
+/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR
+ * and VER_INTERNALNAME_STR must be defined before including COMMON.VER
+ * The strings don't need a '\0', since common.ver has them.
+ */
+#define VER_FILETYPE VFT_DLL
+/* possible values: VFT_UNKNOWN
+ VFT_APP
+ VFT_DLL
+ VFT_DRV
+ VFT_FONT
+ VFT_VXD
+ VFT_STATIC_LIB
+*/
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+/* possible values VFT2_UNKNOWN
+ VFT2_DRV_PRINTER
+ VFT2_DRV_KEYBOARD
+ VFT2_DRV_LANGUAGE
+ VFT2_DRV_DISPLAY
+ VFT2_DRV_MOUSE
+ VFT2_DRV_NETWORK
+ VFT2_DRV_SYSTEM
+ VFT2_DRV_INSTALLABLE
+ VFT2_DRV_SOUND
+ VFT2_DRV_COMM
+*/
+#define VER_FILEDESCRIPTION_STR "OS/2 System Server"
+#define VER_INTERNALNAME_STR "OS2SS.EXE"
+#define VER_ORIGINALFILENAME_STR "OS2SS.EXE"
+
+#include "common.ver"
diff --git a/private/os2/os2ss/sbcnfg.c b/private/os2/os2ss/sbcnfg.c
new file mode 100644
index 000000000..f93a567d9
--- /dev/null
+++ b/private/os2/os2ss/sbcnfg.c
@@ -0,0 +1,1674 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sbcnfg.c
+
+Abstract:
+
+ This module contains the code necessary to initialize the
+ OS/2 SS entries in the registry. Since the SS modifies the
+ system environment, we need a privileged process to do this,
+ and so we run it in os2ss.exe. This initialization takes
+ place the 1st time that os2ss.exe is run after system setup,
+ or whenever the CONFIG.SYS entry in the registry disappears.
+
+Author:
+
+ Ofer Porat (oferp) 16-Mar-1993
+
+Environment:
+
+ User Mode only
+
+Revision History:
+
+ Code was originally in server\srvcnfg.c.
+
+--*/
+
+//
+// Only compiled for PMNT
+//
+
+#ifdef PMNT
+
+#include <wchar.h>
+#include "os2srv.h"
+
+#define PATHLIST_MAX 1024 // max length of pathlists such as Os2LibPath (in characters)
+#define MAX_CONSYS_SIZE 16384 // max size of config.sys buffers (in bytes)
+#define DOS_DEV_LEN 12 // length of "\\DosDevices\\"
+
+static WCHAR Os2SoftwareDirectory[] = L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft";
+static WCHAR Os2ProductDirectory[] = L"OS/2 Subsystem for NT";
+static WCHAR Os2VersionDirectory[] = L"1.0";
+static WCHAR Os2IniName[] = L"os2.ini";
+static WCHAR Os2ConfigSysName[] = L"config.sys";
+static WCHAR Os2Class[] = L"OS2SS";
+static CHAR Os2ConfigSysDefaultValue[] =
+//
+// The '\a' in the following strings will be replaced by the SystemDirectory Value
+//
+"NTREM Here is a summary of what is allowed to appear in this registry entry:\0"
+"NTREM Comments starting with REM will be visible to the user when s/he opens\0"
+"NTREM c:\\config.sys.\0"
+"NTREM Comments starting with NTREM are only visible by direct access to the\0"
+"NTREM registry.\0"
+"NTREM The following OS/2 configuration commands are significant:\0"
+"NTREM COUNTRY=\0"
+"NTREM CODEPAGE=\0"
+"NTREM DEVINFO=KBD,\0"
+"NTREM Any other commands apart from the exceptions listed below will be\0"
+"NTREM visible to an OS/2 program that opens c:\\config.sys, however they are\0"
+"NTREM not used internally by the NT OS/2 SubSystem.\0"
+"NTREM Exceptions:\0"
+"NTREM The following commands are completely ignored. Their true values\0"
+"NTREM appear in the system environment and should be modified using the\0"
+"NTREM Control Panel System applet. Note that LIBPATH is called Os2LibPath\0"
+"NTREM in the NT system environment.\0"
+"SET PATH=<ignored>\0"
+"LIBPATH=<ignored>\0"
+"NTREM In addition, any \"SET=\" commands (except COMSPEC) will be\0"
+"NTREM completely ignored. You should set OS/2 environment variables just\0"
+"NTREM like any other Windows NT environment variables by using the Control\0"
+"NTREM Panel System applet.\0"
+"NTREM If you have an OS/2 editor available, it is highly recommended that you\0"
+"NTREM modify NT OS/2 config.sys coniguration by editing c:\\config.sys with\0"
+"NTREM this editor. This is the documented way to make such modification, and\0"
+"NTREM is therefore less error-prone.\0"
+"NTREM Now comes the actual text.\0"
+"REM\0"
+"REM This is a fake OS/2 config.sys file used by the NT OS/2 SubSystem.\0"
+"REM The following information resides in the Registry and NOT in a disk file.\0"
+"REM OS/2 Apps that access c:\\config.sys actually manipulate this information.\0"
+"REM\0"
+"PROTSHELL=c:\\os2\\pmshell.exe c:\\os2\\os2.ini c:\\os2\\os2sys.ini \a\\cmd.exe\0"
+"SET COMSPEC=\a\\cmd.exe\0"
+;
+
+static WCHAR Os2ConfigSysKeyName[] =
+L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\OS/2 Subsystem for NT\\1.0\\config.sys";
+static WCHAR Os2EnvironmentDirectory[] =
+L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment";
+static HANDLE Os2EnvironmentKeyHandle = NULL;
+
+static WCHAR Os2OriginalCanonicalConfigSys[] = L"\\DosDevices\\C:\\CONFIG.SYS";
+
+static BOOLEAN Os2LibPathFound = FALSE;
+static WCHAR Os2LibPathValueName[] = L"Os2LibPath";
+static UNICODE_STRING Os2LibPathValueData_U;
+
+static PWSTR pOs2ConfigSys = NULL;
+static ULONG Os2SizeOfConfigSys = 0;
+static PWSTR pOs2UpperCaseConfigSys = NULL;
+
+static WCHAR Os2SystemDirectory[DOS_MAX_PATH_LENGTH];
+
+
+VOID
+Os2InitMBString(
+ PANSI_STRING DestinationString,
+ PCSZ SourceString
+ )
+/*++
+
+Routine Description:
+
+ This routine init an ASCII character string (buffer and length)
+
+Arguments:
+
+ DestinationString - pointer to ansi string to put the result in
+
+ SourceString - pointer to ansi null terminated string to read from
+
+Return Value:
+
+
+Note:
+
+ Od2ProcessCodePage is used as the code page for the mapping.
+
+ Od2CurrentCodePageIsOem is check to see if RtlOem NLS routines
+ can be used.
+--*/
+
+{
+
+ // BUGBUG: add support if Code Page is diff from OEMCP
+
+ RtlInitAnsiString(
+ DestinationString,
+ SourceString);
+
+ return ;
+}
+
+
+NTSTATUS
+Os2MBStringToUnicodeString(
+ PUNICODE_STRING DestinationString,
+ PANSI_STRING SourceString,
+ BOOLEAN AllocateDestinationString
+ )
+/*++
+
+Routine Description:
+
+ This routine map a multibyte character string to its unicode character
+ counterpart.
+
+Arguments:
+
+ DestinationString - pointer to unicode string to get the mapping result
+
+ SourceString - pointer to ansi string to read string to map from
+
+ AllocateDestinationString - flag indicating if need to allocate space
+ for destination string
+
+Return Value:
+
+
+Note:
+
+ Od2ProcessCodePage is used as the code page for the mapping.
+
+ Od2CurrentCodePageIsOem is check to see if RtlOem NLS routines
+ can be used.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ // BUGBUG: add support if Code Page is diff from OEMCP (use Od2CurrentCodePageIsOem)
+
+ Status = RtlOemStringToUnicodeString(
+ DestinationString,
+ (POEM_STRING)SourceString,
+ AllocateDestinationString
+ );
+
+ return(Status);
+}
+
+
+BOOLEAN
+Os2InitOriginalConfigSysProcessing(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function checks if there is an OS/2 config.sys configuration file on the
+ disk. If so, it opens it and reads it in. The file is converted to UNICODE,
+ and an upper case copy of it is made.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE if there's an OS/2 config.sys, and all the operations needed to prepare
+ it have succeeded. FALSE otherwise.
+
+Notes:
+ On success Sets global variables as follows:
+ pOs2ConfigSys - a null-terminated UNICODE copy of the OS/2 config.sys.
+ pOs2UpperCaseConfigSys - an upper case copy of pOs2ConfigSys.
+ Os2SizeOfConfigSys - the number of characters in the above strings.
+
+--*/
+
+{
+ UNICODE_STRING CanonicalConfigDotSys_U;
+ UNICODE_STRING Tmp_U;
+ ANSI_STRING Tmp_MB;
+ OBJECT_ATTRIBUTES Obja;
+ FILE_STANDARD_INFORMATION FileStandardInfo;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatus;
+ HANDLE ConfigSysFileHandle;
+ PSZ pTempOs2ConfigSys;
+
+ // Try opening OS/2's config.sys file
+
+ RtlInitUnicodeString(&CanonicalConfigDotSys_U, Os2OriginalCanonicalConfigSys);
+
+ InitializeObjectAttributes(&Obja,
+ &CanonicalConfigDotSys_U,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+
+ Status = NtOpenFile(&ConfigSysFileHandle,
+ FILE_GENERIC_READ,
+ &Obja,
+ &IoStatus,
+ FILE_SHARE_READ,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT )
+ {
+ KdPrint(("Os2InitOriginalConfigSysProcessing): FAILED - NtOpenFile-1 %lx\n", Status));
+ }
+#endif
+ return (FALSE);
+ }
+
+ // Get the file length
+
+ Status = NtQueryInformationFile(ConfigSysFileHandle,
+ &IoStatus,
+ &FileStandardInfo,
+ sizeof(FileStandardInfo),
+ FileStandardInformation
+ );
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT )
+ {
+ KdPrint(("Os2InitOriginalConfigSysProcessing): FAILED - NtQueryInformationFile %lx\n", Status));
+ }
+#endif
+ NtClose(ConfigSysFileHandle);
+ return (FALSE);
+ }
+
+ Os2SizeOfConfigSys = FileStandardInfo.EndOfFile.LowPart;
+
+ // Allocate space for reading it in
+
+ // the + 1 in following parameter is for inserting the NUL character
+
+ pTempOs2ConfigSys = (PSZ) RtlAllocateHeap(Os2Heap, 0, Os2SizeOfConfigSys + 1);
+
+ if (pTempOs2ConfigSys == NULL) {
+#if DBG
+ IF_OS2_DEBUG( INIT )
+ {
+ KdPrint(("Os2InitOriginalConfigSysProcessing): FAILED - RtlAllocateHeap pTempOs2ConfigSys\n"));
+ }
+#endif
+ Os2SizeOfConfigSys = 0;
+ NtClose(ConfigSysFileHandle);
+ return (FALSE);
+ }
+
+ pOs2ConfigSys = (PWSTR) RtlAllocateHeap(Os2Heap, 0, (Os2SizeOfConfigSys + 1) * sizeof(WCHAR));
+
+ if (pOs2ConfigSys == NULL) {
+#if DBG
+ IF_OS2_DEBUG( INIT )
+ {
+ KdPrint(("Os2InitOriginalConfigSysProcessing): FAILED - RtlAllocateHeap pOs2ConfigSys\n"));
+ }
+#endif
+ Os2SizeOfConfigSys = 0;
+ RtlFreeHeap(Os2Heap, 0, pTempOs2ConfigSys);
+ NtClose(ConfigSysFileHandle);
+ return (FALSE);
+ }
+
+ // Read it in
+
+ Status = NtReadFile(ConfigSysFileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatus,
+ (PVOID)pTempOs2ConfigSys,
+ Os2SizeOfConfigSys,
+ NULL,
+ NULL
+ );
+ NtClose(ConfigSysFileHandle);
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT )
+ {
+ KdPrint(("Os2InitOriginalConfigSysProcessing): FAILED - NtReadFile %lx\n", Status));
+ }
+#endif
+ Os2SizeOfConfigSys = 0;
+ RtlFreeHeap(Os2Heap, 0, pTempOs2ConfigSys);
+ RtlFreeHeap(Os2Heap, 0, pOs2ConfigSys);
+ pOs2ConfigSys = NULL;
+ return (FALSE);
+ }
+
+ pTempOs2ConfigSys[Os2SizeOfConfigSys] = '\0';
+
+ // Convert to UNICODE
+
+ Os2InitMBString(&Tmp_MB, pTempOs2ConfigSys);
+
+ Tmp_U.Buffer = pOs2ConfigSys;
+ Tmp_U.MaximumLength = (USHORT) ((Os2SizeOfConfigSys + 1) * sizeof(WCHAR));
+
+ Os2MBStringToUnicodeString(&Tmp_U, &Tmp_MB ,FALSE);
+
+ Os2SizeOfConfigSys = Tmp_U.Length / sizeof(WCHAR);
+
+ pOs2ConfigSys[Os2SizeOfConfigSys] = UNICODE_NULL;
+
+ RtlFreeHeap(Os2Heap, 0, pTempOs2ConfigSys);
+
+ // Prepare the upper case copy
+
+ pOs2UpperCaseConfigSys = (PWSTR) RtlAllocateHeap(Os2Heap, 0, (Os2SizeOfConfigSys + 1) * sizeof(WCHAR));
+ if (pOs2UpperCaseConfigSys == NULL) {
+#if DBG
+ IF_OS2_DEBUG( INIT )
+ {
+ KdPrint(("Os2InitOriginalConfigSysProcessing): FAILED - RtlAllocateHeap pOs2UpperConfigSys\n"));
+ }
+#endif
+ Os2SizeOfConfigSys = 0;
+ RtlFreeHeap(Os2Heap, 0, pOs2ConfigSys);
+ pOs2ConfigSys = NULL;
+ return (FALSE);
+ }
+
+ wcscpy(pOs2UpperCaseConfigSys, pOs2ConfigSys);
+ Or2UnicodeStrupr(pOs2UpperCaseConfigSys);
+
+ //
+ // Verify that the CONFIG.SYS file is really an OS/2 file and not a
+ // DOS file.
+ // Look for certain strings that MUST appear in an OS/2 CONFIG.SYS
+ // file and don't appear in DOS CONFIG.SYS files
+ //
+
+ if ((wcsstr(pOs2UpperCaseConfigSys, L"LIBPATH") == NULL) ||
+ (wcsstr(pOs2UpperCaseConfigSys, L"PROTSHELL") == NULL) ||
+ (wcsstr(pOs2UpperCaseConfigSys, L"PROTECTONLY") == NULL)
+ ) {
+ Os2SizeOfConfigSys = 0;
+ RtlFreeHeap(Os2Heap, 0, pOs2ConfigSys);
+ RtlFreeHeap(Os2Heap, 0, pOs2UpperCaseConfigSys);
+ pOs2UpperCaseConfigSys = pOs2ConfigSys = NULL;
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+VOID
+Os2SetDirectiveProcessingDispatchFunction(
+ IN ULONG DispatchTableIndex,
+ IN PVOID UserParameter,
+ IN PWSTR Name,
+ IN ULONG NameLen,
+ IN PWSTR Value,
+ IN ULONG ValueLen
+ )
+
+/*++
+
+Routine Description:
+
+ This is a Dispatch Routine that is used to process SET directives in OS/2's config.sys
+ file. The directives are entered into the system environment.
+
+Arguments:
+
+ Standard arguments passed to a Dispatch Function, see the description of
+ Or2IterateEnvironment in ssrtl\consys.c
+
+ UserParameter - points to a pointer which indicates the current position in the
+ config.sys registry entry we're building.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ UNICODE_STRING VarName_U; // for setting up variable name
+ UNICODE_STRING VarValue_U; // for setting up variable value
+ PWSTR Dest = *(PWSTR *) UserParameter;
+ NTSTATUS Status;
+ ULONG ResultLength;
+ WCHAR wch;
+ KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
+
+ // Set up variable name
+
+ VarName_U.Buffer = Value;
+ VarName_U.Length = 0;
+
+ while ((ValueLen > 0) && (*Value != L'=')) {
+ Value++;
+ VarName_U.Length += sizeof(WCHAR);
+ ValueLen--;
+ }
+
+ if (ValueLen == 0 || // End of line reached without finding '='
+ VarName_U.Length == 0) { // Empty name
+ return;
+ }
+
+ VarName_U.MaximumLength = VarName_U.Length;
+
+ // Following SET directives are ignored
+
+ if (Or2UnicodeEqualCI(VarName_U.Buffer, L"COMSPEC=", 8) ||
+ Or2UnicodeEqualCI(VarName_U.Buffer, L"PATH=", 5) ||
+ Or2UnicodeEqualCI(VarName_U.Buffer, L"VIDEO_DEVICES=", 14) ||
+ Or2UnicodeEqualCI(VarName_U.Buffer, L"VIO_IBMVGA=", 11) ||
+ Or2UnicodeEqualCI(VarName_U.Buffer, L"VIO_VGA=", 8) ||
+ Or2UnicodeEqualCI(VarName_U.Buffer, L"PROMPT=", 7)
+ ) {
+ return;
+ }
+
+ // Here, we have a valid name, followed by '='
+ Value++; // Skip the '='
+ ValueLen--;
+
+ // Set up variable value
+
+ VarValue_U.Buffer = Value;
+ VarValue_U.Length = (USHORT) (ValueLen * sizeof(WCHAR)); // what's left of the line
+ VarValue_U.MaximumLength = VarValue_U.Length;
+
+ //
+ // Update the information in the registry with the info
+ // in the file
+ //
+
+#if 0
+ // not in effect anymore
+ //
+ // The KEYS variable is handled in a special way.
+ // It's put in the registry config.sys in order to prevent possible
+ // conflict.
+ //
+
+ if (Or2UnicodeEqualCI(VarName_U.Buffer, L"KEYS=", 5)) {
+ RtlMoveMemory(Dest, L"SET ", 8);
+ Dest += 4;
+ RtlMoveMemory(Dest, VarName_U.Buffer, VarName_U.Length);
+ Dest += VarName_U.Length / sizeof(WCHAR);
+ *Dest++ = L'=';
+ RtlMoveMemory(Dest, VarValue_U.Buffer, VarValue_U.Length);
+ Dest += VarValue_U.Length / sizeof(WCHAR);
+ *Dest++ = UNICODE_NULL;
+ *(PWSTR *) UserParameter = Dest;
+ return;
+ }
+#endif
+
+ //
+ // Otherwise, it's stored in the system environment
+ //
+
+ //
+ // If Os2EnvironmentKeyHandle is NULL, we don't have
+ // write access to the system environment, and we skip
+ // setting the variable.
+ //
+
+ if (Os2EnvironmentKeyHandle == NULL) {
+ return;
+ }
+
+ //
+ // If a value key of the same name already exists,
+ // don't replace it. This is done in order to prevent
+ // the OS/2 SS from overriding possible NT definitions
+ //
+
+ Status = NtQueryValueKey(Os2EnvironmentKeyHandle,
+ &VarName_U,
+ KeyValuePartialInformation,
+ &KeyValueInfo,
+ sizeof(KeyValueInfo),
+ &ResultLength
+ );
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ //
+ // Set the system wide variable to the value specified
+ // in the original OS/2 config.sys
+ //
+
+ wch = Value[ValueLen];
+ Value[ValueLen] = UNICODE_NULL;
+
+ Status = NtSetValueKey(Os2EnvironmentKeyHandle,
+ &VarName_U,
+ (ULONG)0,
+ REG_EXPAND_SZ,
+ VarValue_U.Buffer,
+ VarValue_U.Length + sizeof(WCHAR)
+ );
+
+ Value[ValueLen] = wch;
+
+ if (!NT_SUCCESS(Status))
+ {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SetDirectiveProcessingDispatchFunction: Unable to NtSetValueKey() system env, rc = %X\n",
+ Status));
+ }
+#endif
+ return;
+ }
+
+ } else {
+#if DBG
+ if (Status != STATUS_BUFFER_OVERFLOW) {
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SetDirectiveProcessingDispatchFunction: Unable to NtQueryValueKey() system env, rc = %X\n",
+ Status));
+ }
+ }
+#endif
+ return;
+ }
+}
+
+
+VOID
+Os2CommaProcessingDispatchFunction(
+ IN ULONG DispatchTableIndex,
+ IN PVOID UserParameter,
+ IN PWSTR Name,
+ IN ULONG NameLen,
+ IN PWSTR Value,
+ IN ULONG ValueLen
+ )
+
+/*++
+
+Routine Description:
+
+ This is a Dispatch Routine that is used to process certain directives in OS/2's config.sys
+ file. The directives are copied into the config.sys registry entry we're building.
+ Some directives are truncated after a certain number of commas.
+
+Arguments:
+
+ Standard arguments passed to a Dispatch Function, see the description of
+ Or2IterateEnvironment in ssrtl\consys.c
+
+ UserParameter - points to a pointer which indicates the current position in the
+ config.sys registry entry we're building.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+//
+// Lines passed to this dispatch function need to be copied to the output as are
+// except they should be truncated after a certain number of commas. The
+// following table lists the number of commas. 0 means no truncation.
+//
+ ULONG ItemTable[] = { 1, // COUNTRY
+ 0, // CODEPAGE
+ 2 // DEVINFO (only with KBD)
+ };
+ PWSTR Dest = *(PWSTR *) UserParameter;
+ ULONG CommaCtr;
+
+ if (DispatchTableIndex== 2 && !Or2UnicodeEqualCI(Value, L"KBD,", 4)) {
+ return;
+ }
+
+ // First, copy the Name
+
+ RtlMoveMemory(Dest, Name, NameLen * sizeof(WCHAR));
+ Dest += NameLen;
+ *Dest++ = L'=';
+
+ // Now, copy the value for the right number of commas
+
+ CommaCtr = 0;
+
+ while (ValueLen > 0) {
+ if (ItemTable[DispatchTableIndex] != 0 && *Value == L',') {
+ CommaCtr++;
+ if (CommaCtr == ItemTable[DispatchTableIndex]) {
+ break;
+ }
+ }
+
+ *Dest++ = *Value++;
+ ValueLen--;
+ }
+ *Dest++ = UNICODE_NULL;
+ *(PWSTR *) UserParameter = Dest;
+}
+
+
+VOID
+Os2ProcessOriginalConfigSys(
+ IN OUT PWSTR *DestPtr
+ )
+
+/*++
+
+Routine Description:
+
+ This function processes OS/2's config.sys file after it has been properly initialized
+ by Os2InitOriginalConfigSysProcessing.
+
+Arguments:
+
+ DestPtr - points to a pointer which indicates the current position in the
+ config.sys registry entry we're building.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ static ENVIRONMENT_DISPATCH_TABLE_ENTRY DispatchTable[] =
+ {
+ { L"COUNTRY", L"=", Os2CommaProcessingDispatchFunction, NULL },
+ { L"CODEPAGE", L"=", Os2CommaProcessingDispatchFunction, NULL },
+ { L"DEVINFO", L"=", Os2CommaProcessingDispatchFunction, NULL },
+ { L"LIBPATH", L"=", Or2FillInSearchRecordDispatchFunction, NULL },
+ { L"SET", L" \t", Os2SetDirectiveProcessingDispatchFunction, NULL }
+ };
+ ENVIRONMENT_SEARCH_RECORD LibPathRecord;
+ ULONG i;
+ PWSTR p;
+ WCHAR ch;
+
+ //
+ // Most of the job is done by Or2IterateEnvironment which processes the file
+ // according to a dispatch table.
+ //
+ // LibPath needs to be handled a little differently. We need to process only
+ // the *last* occurence of LIBPATH= in the file (this is the same as OS/2).
+ // Therefore we only record the position of the LIBPATH= statments as we run
+ // into them. After we're finished we'll have the position of the last one,
+ // and we can process that line.
+ //
+
+ for (i = 0; i < 5; i++) {
+ DispatchTable[i].UserParameter = (PVOID) DestPtr;
+ }
+
+ DispatchTable[3].UserParameter = (PVOID)&LibPathRecord;
+ LibPathRecord.DispatchTableIndex = (ULONG)-1;
+
+ Or2IterateEnvironment(pOs2ConfigSys,
+ DispatchTable,
+ 5,
+ CRLF_DELIM);
+
+
+ if (Os2LibPathFound && LibPathRecord.DispatchTableIndex != (ULONG)-1) {
+
+ // handle LIBPATH if there was one
+
+ // get a pointer to the upper case version, so we can append it
+
+ p = pOs2UpperCaseConfigSys + (LibPathRecord.Value - pOs2ConfigSys);
+
+ ch = p[LibPathRecord.ValueLen];
+ p[LibPathRecord.ValueLen] = UNICODE_NULL;
+
+ Or2AppendPathToPath(Os2Heap,
+ p,
+ &Os2LibPathValueData_U,
+ TRUE);
+ p[LibPathRecord.ValueLen] = ch;
+ }
+}
+
+
+VOID
+Os2TerminateOriginalConfigSysProcessing(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Cleans up processing of OS/2's config.sys. Releases the storage used to store the
+ file.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RtlFreeHeap(Os2Heap, 0, pOs2ConfigSys);
+ RtlFreeHeap(Os2Heap, 0, pOs2UpperCaseConfigSys);
+ pOs2UpperCaseConfigSys = pOs2ConfigSys = NULL;
+}
+
+
+NTSTATUS
+Os2SbBuildSD(
+ PSECURITY_DESCRIPTOR SecurityDescriptor,
+ PACL *pDacl
+ )
+
+/*++
+
+Routine Description:
+
+ Builds a security descriptor for creating our registry keys.
+
+ Administrators -- all access
+ everyone -- read access
+
+Arguments:
+
+ SecurityDescriptor -- supplies a pointer to a preallocated SD that will be built
+ pDacl -- returns a pointer to a dacl that should be released from Os2Heap after
+ we're finished using the security descriptor.
+
+Return Value:
+
+ NT error code.
+
+--*/
+
+{
+ PACL Dacl;
+ PACE_HEADER Pace;
+ PSID AdminAliasSid;
+ ULONG DaclSize;
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
+ PSID WorldSid;
+ NTSTATUS Status;
+
+ //
+ // Create the SIDs for local admin and World.
+ //
+
+ Status = RtlAllocateAndInitializeSid(
+ &NtAuthority,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS,
+ 0, 0, 0, 0, 0, 0,
+ &AdminAliasSid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbBuildSD: RtlAllocateAndInitializeSid(Admin), Status = %lx\n", Status));
+ }
+#endif
+ return (Status);
+ }
+
+ Status = RtlAllocateAndInitializeSid(
+ &WorldSidAuthority,
+ 1,
+ SECURITY_WORLD_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &WorldSid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbBuildSD: RtlAllocateAndInitializeSid(World), Status = %lx\n", Status));
+ }
+#endif
+ return (Status);
+ }
+
+ Status = RtlCreateSecurityDescriptor( SecurityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION );
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbBuildSD: RtlCreateSecurityDescriptor, Status = %lx\n", Status));
+ }
+#endif
+ return (Status);
+ }
+
+
+ //
+ // Compute the size of the buffer needed for the
+ // DACL.
+ //
+
+ DaclSize = sizeof( ACL ) +
+ 2 * (sizeof( ACCESS_ALLOWED_ACE ) - sizeof( ULONG ))
+ +
+ RtlLengthSid( AdminAliasSid ) +
+ RtlLengthSid( WorldSid );
+
+ Dacl = (PACL) RtlAllocateHeap(Os2Heap, 0, DaclSize);
+
+ if (Dacl == NULL) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbBuildSD: RtlAllocateHeap failed\n"));
+ }
+#endif
+ return(STATUS_NO_MEMORY);
+ }
+
+ //
+ // Build the ACL
+ //
+
+ Status = RtlCreateAcl ( Dacl, DaclSize, ACL_REVISION2 );
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbBuildSD: RtlCreateAcl, Status = %lx\n", Status));
+ }
+#endif
+ RtlFreeHeap(Os2Heap, 0, Dacl);
+ return (Status);
+ }
+
+ Status = RtlAddAccessAllowedAce (
+ Dacl,
+ ACL_REVISION2,
+ GENERIC_ALL,
+ AdminAliasSid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbBuildSD: RtlAddAccessAllowedAce(Admin), Status = %lx\n", Status));
+ }
+#endif
+ RtlFreeHeap(Os2Heap, 0, Dacl);
+ return (Status);
+ }
+
+ Status = RtlAddAccessAllowedAce (
+ Dacl,
+ ACL_REVISION2,
+ GENERIC_READ,
+ WorldSid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbBuildSD: RtlAddAccessAllowedAce(World), Status = %lx\n", Status));
+ }
+#endif
+ RtlFreeHeap(Os2Heap, 0, Dacl);
+ return (Status);
+ }
+
+ Status = RtlGetAce(
+ Dacl,
+ 0L,
+ &Pace
+ );
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbBuildSD: RtlGetAce(Admin), Status = %lx\n", Status));
+ }
+#endif
+ RtlFreeHeap(Os2Heap, 0, Dacl);
+ return (Status);
+ }
+
+ Pace->AceFlags |= CONTAINER_INHERIT_ACE;
+
+ Status = RtlGetAce(
+ Dacl,
+ 1L,
+ &Pace
+ );
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbBuildSD: RtlGetAce(World), Status = %lx\n", Status));
+ }
+#endif
+ RtlFreeHeap(Os2Heap, 0, Dacl);
+ return (Status);
+ }
+
+ Pace->AceFlags |= CONTAINER_INHERIT_ACE;
+
+ //
+ // Add the ACL to the security descriptor
+ //
+
+ Status = RtlSetDaclSecurityDescriptor(
+ SecurityDescriptor,
+ TRUE,
+ Dacl, // put (PACL) NULL to allow everyone all access
+ FALSE );
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbBuildSD: RtlSetDaclSecurityDescriptor, Status = %lx\n", Status));
+ }
+#endif
+ RtlFreeHeap(Os2Heap, 0, Dacl);
+ return (Status);
+ }
+
+ //
+ // These have been copied into the security descriptor, so
+ // we can free them.
+ //
+
+ RtlFreeSid( AdminAliasSid );
+
+ RtlFreeSid( WorldSid );
+
+ *pDacl = Dacl;
+
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+Os2SbInitializeRegistryKeys(
+ OUT PHANDLE phConfigSysKeyHandle,
+ OUT PHANDLE phEnvironmentKeyHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine creates the OS/2 subsystem key hierarchy in the registry. It also opens
+ the config.sys key, and opens the system environment key.
+
+Arguments:
+
+ phConfigSysKeyHandle - Returns a READ/WRITE handle to the config.sys key in the registry.
+
+ phEnvironmentKeyHandle - Returns a READ/WRITE handle to the system environment key.
+ If this key can't be opened due to access denied, NULL is returned and the
+ return value will be STATUS_SUCCESS.
+
+ Note --- If the return value is not a success return value, none of the above return
+ variables can be considered to be valid.
+
+Return Value:
+
+ The value is an NTSTATUS type that is returned when some failure occurs. It may
+ indicate any of several errors that occur during the APIs called in this function.
+ The return value should be tested with NT_SUCCESS().
+
+--*/
+
+{
+ OBJECT_ATTRIBUTES Obja;
+ UNICODE_STRING Class_U;
+ UNICODE_STRING SoftwareDirectory_U;
+ UNICODE_STRING ProductDirectory_U;
+ UNICODE_STRING VersionDirectory_U;
+ UNICODE_STRING Os2IniName_U;
+ UNICODE_STRING ConfigSysName_U;
+ UNICODE_STRING EnvRegDir_U;
+ HANDLE SoftwareKeyHandle;
+ HANDLE ProductKeyHandle;
+ HANDLE VersionKeyHandle;
+ HANDLE Os2IniKeyHandle;
+ HANDLE ConfigSysKeyHandle;
+ HANDLE EnvironmentKeyHandle;
+ ULONG Disposition;
+ NTSTATUS Status;
+ SECURITY_DESCRIPTOR localSecurityDescriptor;
+ PSECURITY_DESCRIPTOR securityDescriptor;
+ PACL Dacl = NULL;
+
+
+ *phConfigSysKeyHandle = NULL;
+ *phEnvironmentKeyHandle = NULL;
+
+ // We start off by creating/opening the registry key hierarchy for our subsystem
+
+ securityDescriptor = &localSecurityDescriptor;
+
+ Status = Os2SbBuildSD(securityDescriptor,
+ &Dacl);
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbInitializeRegistryKeys: Os2SbBuildSD failed, Status = %lx\n", Status));
+ }
+#endif
+ return (Status);
+ }
+
+ RtlInitUnicodeString(&Class_U, Os2Class);
+
+ RtlInitUnicodeString(&SoftwareDirectory_U, Os2SoftwareDirectory);
+ InitializeObjectAttributes(&Obja,
+ &SoftwareDirectory_U,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+
+ Status = NtOpenKey(&SoftwareKeyHandle,
+ KEY_CREATE_SUB_KEY,
+ &Obja
+ );
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbInitializeRegistryKeys: Can't open software key, rc = %lx\n", Status));
+ }
+#endif
+ if (Dacl != NULL) {
+ RtlFreeHeap(Os2Heap, 0, Dacl);
+ }
+ return (Status);
+ }
+
+ RtlInitUnicodeString(&ProductDirectory_U, Os2ProductDirectory);
+ InitializeObjectAttributes(&Obja,
+ &ProductDirectory_U,
+ OBJ_CASE_INSENSITIVE,
+ SoftwareKeyHandle,
+ securityDescriptor);
+
+ Status = NtCreateKey(&ProductKeyHandle,
+ KEY_CREATE_SUB_KEY,
+ &Obja,
+ 0,
+ &Class_U,
+ REG_OPTION_NON_VOLATILE,
+ &Disposition
+ );
+ NtClose(SoftwareKeyHandle);
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbInitializeRegistryKeys: Can't create product key, rc = %lx\n", Status));
+ }
+#endif
+ if (Dacl != NULL) {
+ RtlFreeHeap(Os2Heap, 0, Dacl);
+ }
+ return (Status);
+ }
+
+ RtlInitUnicodeString(&VersionDirectory_U, Os2VersionDirectory);
+ InitializeObjectAttributes(&Obja,
+ &VersionDirectory_U,
+ OBJ_CASE_INSENSITIVE,
+ ProductKeyHandle,
+ securityDescriptor);
+
+ Status = NtCreateKey(&VersionKeyHandle,
+ KEY_CREATE_SUB_KEY,
+ &Obja,
+ 0,
+ &Class_U,
+ REG_OPTION_NON_VOLATILE,
+ &Disposition
+ );
+ NtClose(ProductKeyHandle);
+ if (!NT_SUCCESS(Status))
+ {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbInitializeRegistryKeys: Can't create version key, rc = %lx\n", Status));
+ }
+#endif
+ if (Dacl != NULL) {
+ RtlFreeHeap(Os2Heap, 0, Dacl);
+ }
+ return (Status);
+ }
+
+ RtlInitUnicodeString(&Os2IniName_U, Os2IniName);
+ InitializeObjectAttributes(&Obja,
+ &Os2IniName_U,
+ OBJ_CASE_INSENSITIVE,
+ VersionKeyHandle,
+ securityDescriptor);
+
+ Status = NtCreateKey(&Os2IniKeyHandle,
+ KEY_CREATE_SUB_KEY,
+ &Obja,
+ 0,
+ &Class_U,
+ REG_OPTION_NON_VOLATILE,
+ &Disposition
+ );
+ if (!NT_SUCCESS(Status))
+ {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbInitializeRegistryKeys: Can't create os2ini key, rc = %lx\n", Status));
+ }
+#endif
+ NtClose(VersionKeyHandle);
+ if (Dacl != NULL) {
+ RtlFreeHeap(Os2Heap, 0, Dacl);
+ }
+ return (Status);
+ }
+ NtClose(Os2IniKeyHandle);
+
+ RtlInitUnicodeString(&ConfigSysName_U, Os2ConfigSysName);
+ InitializeObjectAttributes(&Obja,
+ &ConfigSysName_U,
+ OBJ_CASE_INSENSITIVE,
+ VersionKeyHandle,
+ securityDescriptor);
+
+ Status = NtCreateKey(&ConfigSysKeyHandle,
+ KEY_READ | KEY_WRITE,
+ &Obja,
+ 0,
+ &Class_U,
+ REG_OPTION_NON_VOLATILE,
+ &Disposition
+ );
+
+ if (Dacl != NULL) {
+ RtlFreeHeap(Os2Heap, 0, Dacl);
+ }
+ NtClose(VersionKeyHandle);
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbInitializeRegistryKeys: Can't create/open config.sys key, rc = %lx\n",
+ Status));
+ }
+#endif
+ return(Status);
+ }
+
+ // Open the environment.
+
+ RtlInitUnicodeString(&EnvRegDir_U, Os2EnvironmentDirectory);
+ InitializeObjectAttributes(&Obja,
+ &EnvRegDir_U,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+
+ Status = NtOpenKey(&EnvironmentKeyHandle,
+ KEY_READ | KEY_WRITE,
+ &Obja
+ );
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbInitializeRegistryKeys: Unable to NtOpenKey() the system environment, rc = %lx\n",
+ Status));
+ }
+#endif
+ if (Status != STATUS_ACCESS_DENIED) {
+ NtClose(ConfigSysKeyHandle);
+ return(Status);
+ }
+
+ //
+ // on denied access, return with no error so we can at least create the
+ // config.sys value entry
+ // The caller will know this occured because the environment handle is null.
+ //
+
+ } else {
+ *phEnvironmentKeyHandle = EnvironmentKeyHandle;
+ }
+
+ *phConfigSysKeyHandle = ConfigSysKeyHandle;
+ return (STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+Os2SbInitializeRegistry(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function is responsible for initializing the entire Registry component of the
+ Subsystem. It generates the key hierarchy in the registry.
+
+ In the generation of the key hierarchy, it also generates a config.sys entry.
+ The information in this entry is taken from the following sources:
+
+ -- some default strings we put in (see Os2ConfigSysDefaultValue).
+ -- Information from OS/2's config.sys file is added as follows:
+
+ > SET commands are put in the system environment.
+ Some SET commands are ignored (see Os2SetDirectiveProcessingDispatchFunction)
+ > LIBPATH commands are merged to the Os2LibPath variable in the
+ system environment. A terminating semicolon is added if there
+ is only one path in the path list.
+ > SET PATH= is ignored, and the system path remains the same.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ The value is an NTSTATUS type that is returned when some failure occurs. It may
+ indicate any of several errors that occur during the APIs called in this function.
+ The return value should be tested with NT_SUCCESS(). If an unsuccessful value
+ is returned, it means the registry component was not properly initialized.
+
+--*/
+
+{
+ UNICODE_STRING Os2LibPathValueName_U;
+ HANDLE ConfigSysKeyHandle;
+ NTSTATUS Status;
+ UNICODE_STRING ConfigSysName_U;
+ PWCHAR pInfo;
+ PUCHAR Src;
+ PWCHAR Src1;
+ PWCHAR Dest;
+ WCHAR ch, ch1;
+
+ // Create the key hierarchy
+
+ Status = Os2SbInitializeRegistryKeys(&ConfigSysKeyHandle,
+ &Os2EnvironmentKeyHandle);
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("OS2SS - Os2SbInitializeRegistry: failed Os2SbInitializeRegistryKeys, rc = %lx\n", Status));
+ }
+#endif
+ return(Status);
+ }
+
+ //
+ // This is the 1st time the subsystem is running, create the config.sys entry
+ // and migrate information from os/2's config.sys file.
+ //
+
+ Os2LibPathFound = FALSE;
+ Os2LibPathValueData_U.Buffer = NULL;
+
+ if (Os2EnvironmentKeyHandle == NULL) { // we have no write access to the environment
+ goto Os2NoEnvAccess; // skip over the Os2LibPath stuff
+ }
+
+ // Get Os2LibPath from sys env so we can update it.
+
+ if (!Or2GetEnvPath(&Os2LibPathValueData_U,
+ Os2Heap,
+ PATHLIST_MAX,
+ Os2EnvironmentKeyHandle,
+ Os2LibPathValueName,
+ FALSE)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbInitializeRegistry: Unable to fetch Os2LibPath from sys env\n"));
+ }
+#endif
+ } else {
+
+ // make sure there's at lease one semicolon
+
+ Or2CheckSemicolon(&Os2LibPathValueData_U);
+
+ Os2LibPathFound = TRUE;
+ }
+
+ if (Os2LibPathFound && Os2LibPathValueData_U.Length == 0) { // it's empty (or nonexistent)
+ // set up a default
+ UNICODE_STRING tmp;
+
+ RtlInitUnicodeString(&tmp, Os2SystemDirectory);
+
+ RtlCopyUnicodeString(&Os2LibPathValueData_U, &tmp);
+
+ RtlAppendUnicodeToString(&Os2LibPathValueData_U, L"\\os2\\dll;");
+
+ Os2LibPathValueData_U.Buffer[Os2LibPathValueData_U.Length/sizeof(WCHAR)] = UNICODE_NULL;
+ }
+
+Os2NoEnvAccess:
+
+ // Now set up the initial regisry config.sys entry.
+
+ // Allocate a buffer to build the config.sys entry in. (it's a multi-string)
+
+ pInfo = (PWCHAR) RtlAllocateHeap(Os2Heap, 0, MAX_CONSYS_SIZE);
+ if (pInfo == NULL)
+ {
+ if (Os2LibPathValueData_U.Buffer != NULL) {
+ RtlFreeHeap(Os2Heap, 0, Os2LibPathValueData_U.Buffer);
+ Os2LibPathValueData_U.Buffer = NULL;
+ Os2LibPathFound = FALSE;
+ }
+ if (Os2EnvironmentKeyHandle != NULL) {
+ NtClose(Os2EnvironmentKeyHandle);
+ Os2EnvironmentKeyHandle = NULL;
+ }
+ NtClose(ConfigSysKeyHandle);
+
+ return (STATUS_BUFFER_TOO_SMALL);
+ }
+
+ // Initially, copy our default value into the entry.
+
+ Src = (PUCHAR) Os2ConfigSysDefaultValue;
+ Dest = pInfo;
+ do
+ {
+ ch = (WCHAR) *Src++;
+ if (ch == L'\a')
+ {
+ Src1 = Os2SystemDirectory;
+ ch1 = *Src1++;
+ while (ch1 != UNICODE_NULL)
+ {
+ *Dest++ = ch1;
+ ch1 = *Src1++;
+ }
+ }
+ else
+ {
+ *Dest++ = ch;
+ }
+ } while (!((ch == UNICODE_NULL) && (*Src == '\0')));
+
+
+ // check if the an OS/2 CONFIG.SYS file already exists on
+ // the drive. Its name will be C:\CONFIG.SYS
+ // If it exists, process it for additional information
+ // The system env will also be updated
+
+ if (Os2InitOriginalConfigSysProcessing()) {
+
+ if (Os2EnvironmentKeyHandle == NULL) { // we have no write access to the environment
+
+ //
+ // We couldn't open a write key to the sys env for some reason, so we
+ // won't be able to migrate SET variables to NT from config.sys.
+ // Perhaps a popup should be generated to notify the user of this at
+ // this point.
+ // Anyway, we go on and only write the config.sys entry, skipping
+ // variable migration.
+ //
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbInitializeRegistry: Initializing registry without writing system env\n"));
+ }
+#endif
+ }
+
+ Os2ProcessOriginalConfigSys(&Dest);
+ Os2TerminateOriginalConfigSysProcessing();
+ } else {
+
+ //
+ // add a default "COUNTRY=" line
+ //
+
+ RtlMoveMemory(Dest, L"COUNTRY=", 16);
+ Dest += 8;
+#if 0
+ Dest += swprintf(Dest, L"%.3hu", LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale));
+#else
+ RtlMoveMemory(Dest, L"001", 6);
+ Dest += 3;
+#endif
+ *Dest++ = UNICODE_NULL;
+
+ }
+
+ // Write the new Os2LibPath
+
+ if (Os2LibPathFound) {
+
+ RtlInitUnicodeString(&Os2LibPathValueName_U, Os2LibPathValueName);
+ Status = NtSetValueKey(Os2EnvironmentKeyHandle,
+ &Os2LibPathValueName_U,
+ (ULONG)0,
+ REG_EXPAND_SZ,
+ Os2LibPathValueData_U.Buffer,
+ Os2LibPathValueData_U.Length + sizeof(WCHAR)
+ );
+
+#if DBG
+ if (!NT_SUCCESS(Status))
+ {
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbInitializeRegistry: Unable to NtSetValueKey() system environment, rc = %X\n",
+ Status));
+ }
+ }
+#endif
+
+ Os2LibPathFound = FALSE;
+ RtlFreeHeap(Os2Heap, 0, Os2LibPathValueData_U.Buffer);
+ Os2LibPathValueData_U.Buffer = NULL;
+ }
+
+ if (Os2EnvironmentKeyHandle != NULL) {
+ NtClose(Os2EnvironmentKeyHandle);
+ Os2EnvironmentKeyHandle = NULL;
+ }
+
+ // set the REG_MULTI_SZ terminating NUL
+
+ *Dest++ = UNICODE_NULL;
+
+ // Finally, write the config.sys multi-string entry into the registry.
+
+ RtlInitUnicodeString(&ConfigSysName_U, Os2ConfigSysName);
+ Status = NtSetValueKey(ConfigSysKeyHandle,
+ &ConfigSysName_U,
+ (ULONG)0,
+ REG_MULTI_SZ,
+ (PVOID)pInfo,
+ (PBYTE)Dest - (PBYTE)pInfo
+ );
+ RtlFreeHeap(Os2Heap, 0, pInfo);
+ if (!NT_SUCCESS(Status))
+ {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("Os2SbInitializeRegistry: Unable to NtSetValueKey() registry config.sys, rc = %X\n",
+ Status));
+ }
+#endif
+ NtClose(ConfigSysKeyHandle);
+ return (Status);
+ }
+
+ NtClose(ConfigSysKeyHandle);
+ return (STATUS_SUCCESS);
+}
+
+
+VOID
+Os2SbProbeForInitialSetup(
+ VOID
+ )
+{
+ OBJECT_ATTRIBUTES Obja;
+ UNICODE_STRING ConfigSysKeyName_U;
+ UNICODE_STRING ConfigSysName_U;
+ PUNICODE_STRING SysDir_U;
+ HANDLE ConfigSysKeyHandle;
+ ULONG ResultLength;
+ USHORT Counter;
+ KEY_VALUE_PARTIAL_INFORMATION ValuePartialInformation;
+ NTSTATUS Status;
+
+#if DBG
+ Os2Debug |= OS2_DEBUG_INIT;
+#endif
+
+ RtlInitUnicodeString(&ConfigSysKeyName_U, Os2ConfigSysKeyName);
+ InitializeObjectAttributes(&Obja,
+ &ConfigSysKeyName_U,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+
+ Status = NtOpenKey(&ConfigSysKeyHandle,
+ KEY_READ,
+ &Obja
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ RtlInitUnicodeString(&ConfigSysName_U, Os2ConfigSysName);
+ Status = NtQueryValueKey(ConfigSysKeyHandle,
+ &ConfigSysName_U,
+ KeyValuePartialInformation,
+ &ValuePartialInformation,
+ 0,
+ &ResultLength
+ );
+
+ NtClose(ConfigSysKeyHandle);
+
+ //
+ // The 2 expected status results are:
+ //
+ // STATUS_OBJECT_NAME_NOT_FOUND - config.sys not yet defined
+ // STATUS_BUFFER_TOO_SMALL - config.sys already defined
+ //
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ return;
+ }
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("OS2SS: Can't Read CONFIG.SYS registry value, Status = %lx\n", Status));
+ }
+#endif
+ return;
+ }
+
+ } else {
+ if (Status != STATUS_OBJECT_PATH_NOT_FOUND &&
+ Status != STATUS_OBJECT_NAME_NOT_FOUND &&
+ Status != STATUS_OBJECT_PATH_INVALID) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("OS2SS: Can't Read CONFIG.SYS registry key, Status = %lx\n", Status));
+ }
+#endif
+ return;
+ }
+ }
+
+
+ //
+ // We need to install the registry stuff
+ //
+
+ //
+ // Create a heap to use for dynamic memory allocation.
+ //
+
+ Os2Heap = RtlCreateHeap( HEAP_GROWABLE,
+ NULL,
+ 0x10000L, // Initial size of heap is 64K
+ 0x1000L, // Commit an initial page
+ NULL,
+ NULL // Reserved
+ );
+ if (Os2Heap == NULL) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("OS2SS: Error at RtlCreateHeap of Os2Heap\n"));
+ }
+#endif
+ return;
+ }
+
+ //
+ // Figure out the system directory by cutting it out of our ImagePathName
+ // (should be something like \DosDevices\%SystemRoot%\system32\os2ss.exe)
+ //
+
+ SysDir_U = (PUNICODE_STRING) &NtCurrentPeb()->ProcessParameters->ImagePathName;
+ for (Counter = (SysDir_U->Length / sizeof(WCHAR)) - 1; SysDir_U->Buffer[Counter] != L'\\'; Counter--) {
+ }
+
+ Counter -= DOS_DEV_LEN;
+
+ RtlMoveMemory(Os2SystemDirectory,
+ SysDir_U->Buffer + DOS_DEV_LEN,
+ Counter * sizeof(WCHAR));
+
+ Os2SystemDirectory[Counter] = UNICODE_NULL;
+
+#if 0
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("\nOS2SS: System Directory is "));
+
+ for (Counter = 0; Os2SystemDirectory[Counter] != UNICODE_NULL; Counter++) {
+ KdPrint(("%c", (CHAR) Os2SystemDirectory[Counter]));
+ }
+
+ KdPrint(("|\n"));
+ }
+#endif
+#endif
+
+ Status = Os2SbInitializeRegistry();
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ IF_OS2_DEBUG( INIT ) {
+ KdPrint(("OS2SS: Os2SbInitializeRegistry() failed, Status = %lx\n", Status));
+ }
+#endif
+ }
+
+ RtlDestroyHeap(Os2Heap);
+}
+
+#endif // PMNT
diff --git a/private/os2/os2ss/sbinit.c b/private/os2/os2ss/sbinit.c
new file mode 100644
index 000000000..393888dc1
--- /dev/null
+++ b/private/os2/os2ss/sbinit.c
@@ -0,0 +1,96 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sbinit.c
+
+Abstract:
+
+ This module contains the code to initialize the SbApiPort of the OS/2
+ Emulation Subsystem.
+
+Author:
+
+ Steve Wood (stevewo) 22-Aug-1989
+
+Environment:
+
+ User Mode Only
+
+Revision History:
+
+--*/
+
+#include "os2srv.h"
+
+NTSTATUS
+Os2SbApiPortInitialize( VOID )
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ RtlInitUnicodeString( &Os2SbApiPortName_U, OS2_SS_SBAPI_PORT_NAME );
+
+#if DBG
+ IF_OS2_DEBUG( LPC ) {
+ KdPrint(( "OS2SRV: Creating %wZ port and associated thread\n",
+ &Os2SbApiPortName_U ));
+ }
+#endif
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &Os2SbApiPortName_U,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+ Status = NtCreatePort( &Os2SbApiPort,
+ &ObjectAttributes,
+ sizeof( SBCONNECTINFO ),
+ sizeof( SBAPIMSG ),
+ sizeof( SBAPIMSG ) * 16
+ );
+
+ ASSERT( NT_SUCCESS( Status ) );
+
+ Status = RtlCreateUserThread( NtCurrentProcess(),
+ NULL,
+ TRUE,
+ 0,
+ 0,
+ 0,
+ Os2SbApiRequestThread,
+ NULL,
+ &Os2ServerThreadHandles[ OS2_SS_SBAPI_REQUEST_THREAD ],
+ &Os2ServerThreadClientIds[ OS2_SS_SBAPI_REQUEST_THREAD ]
+ );
+ ASSERT( NT_SUCCESS( Status ) );
+
+ Status = NtResumeThread( Os2ServerThreadHandles[ OS2_SS_SBAPI_REQUEST_THREAD ], NULL );
+ ASSERT( NT_SUCCESS( Status ) );
+
+ return( Status );
+}
+
+
+VOID
+Os2SbApiPortTerminate(
+ NTSTATUS Status
+ )
+{
+#if DBG
+ IF_OS2_DEBUG( LPC ) {
+ KdPrint(( "OS2SRV: Closing %wZ port and associated thread\n",
+ &Os2SbApiPortName_U
+ ));
+ }
+#endif
+ NtTerminateThread( Os2ServerThreadHandles[ OS2_SS_SBAPI_REQUEST_THREAD ],
+ Status
+ );
+
+ NtClose( Os2SbApiPort );
+ NtClose( Os2SmApiPort );
+}
diff --git a/private/os2/os2ss/sbreqst.c b/private/os2/os2ss/sbreqst.c
new file mode 100644
index 000000000..5b16d5cd7
--- /dev/null
+++ b/private/os2/os2ss/sbreqst.c
@@ -0,0 +1,181 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sbreqst.c
+
+Abstract:
+
+ This module contains the Server Request thread procedure for the Sb
+ API calls exported by the OS/2 Emulation SubSystem to the Session
+ Manager SubSystem.
+
+Author:
+
+ Steve Wood (stevewo) 20-Sep-1989
+
+Revision History:
+
+--*/
+
+#include "os2srv.h"
+
+//PSB_API_ROUTINE Os2ServerSbApiDispatch[ SbMaxApiNumber+1 ] = {
+// Os2SbCreateSession,
+// Os2SbTerminateSession,
+// Os2SbForeignSessionComplete,
+// NULL
+//};
+
+#if DBG
+PSZ Os2ServerSbApiName[ SbMaxApiNumber+1 ] = {
+ "SbCreateSession",
+ "SbTerminateSession",
+ "SbForeignSessionComplete",
+ "Unknown Os2 Sb Api Number"
+};
+#endif // DBG
+
+
+NTSTATUS
+Os2SbApiHandleConnectionRequest(
+ IN PSBAPIMSG Message
+ );
+
+NTSTATUS
+Os2SbApiRequestThread(
+ IN PVOID Parameter
+ )
+{
+ NTSTATUS Status;
+ SBAPIMSG ReceiveMsg;
+ PSBAPIMSG ReplyMsg;
+
+ UNREFERENCED_PARAMETER(Parameter);
+
+ ReplyMsg = NULL;
+ while (TRUE) {
+#if DBG
+ IF_OS2_DEBUG( LPC ) {
+ KdPrint(( "OS2SRV: Sb Api Request Thread waiting...\n" ));
+ }
+#endif
+ Status = NtReplyWaitReceivePort( Os2SbApiPort,
+ NULL,
+ (PPORT_MESSAGE)ReplyMsg,
+ (PPORT_MESSAGE)&ReceiveMsg
+ );
+
+ if (Status != 0) {
+ if (NT_SUCCESS( Status )) {
+ continue; // Try again if alerted or a failure
+ }
+ else {
+#if DBG
+ KdPrint(( "OS2SRV: ReceivePort failed - Status == %X\n", Status ));
+#endif
+ break;
+ }
+ }
+
+ //
+ // Check to see if this is a connection request and handle
+ //
+
+ if (ReceiveMsg.h.u2.s2.Type == LPC_CONNECTION_REQUEST) {
+ Os2SbApiHandleConnectionRequest( &ReceiveMsg );
+ ReplyMsg = NULL;
+ continue;
+ }
+
+ if (ReceiveMsg.ApiNumber >= SbMaxApiNumber) {
+#if DBG
+ KdPrint(( "OS2SRV: %lx is invalid Sb ApiNumber\n",
+ ReceiveMsg.ApiNumber
+ ));
+#endif
+
+ ReceiveMsg.ApiNumber = SbMaxApiNumber;
+ }
+
+#if DBG
+ IF_OS2_DEBUG( LPC ) {
+ KdPrint(( "OS2SRV: %s Sb Api Request received from %lx.%lx\n",
+ Os2ServerSbApiName[ ReceiveMsg.ApiNumber ],
+ ReceiveMsg.h.ClientId.UniqueProcess,
+ ReceiveMsg.h.ClientId.UniqueThread
+ ));
+ }
+#endif // DBG
+
+ ReplyMsg = &ReceiveMsg;
+ if (ReceiveMsg.ApiNumber < SbMaxApiNumber) {
+// if (!(*Os2ServerSbApiDispatch[ ReceiveMsg.ApiNumber ])( &ReceiveMsg )) {
+ ReplyMsg = NULL;
+ // }
+// }
+// else {
+// ReplyMsg->ReturnedStatus = STATUS_NOT_IMPLEMENTED;
+ }
+
+#if DBG
+ IF_OS2_DEBUG( LPC ) {
+ if (ReplyMsg != NULL) {
+ KdPrint(( "OS2SRV: %s Sb Api sending %lx status reply to %lx.%lx\n",
+ Os2ServerSbApiName[ ReceiveMsg.ApiNumber ],
+ ReplyMsg->ReturnedStatus,
+ ReplyMsg->h.ClientId.UniqueProcess,
+ ReplyMsg->h.ClientId.UniqueThread
+ ));
+ }
+ }
+#endif // DBG
+ }
+
+ NtTerminateThread( NtCurrentThread(), Status );
+ //
+ // This line should never be executed
+ //
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+Os2SbApiHandleConnectionRequest(
+ IN PSBAPIMSG Message
+ )
+{
+ NTSTATUS st;
+ REMOTE_PORT_VIEW ClientView;
+ HANDLE CommunicationPort;
+
+ //
+ // The protocol for a subsystem is to connect to the session manager,
+ // then to listen and accept a connection from the session manager
+ //
+
+ ClientView.Length = sizeof(ClientView);
+ st = NtAcceptConnectPort(
+ &CommunicationPort,
+ NULL,
+ (PPORT_MESSAGE)Message,
+ TRUE,
+ NULL,
+ &ClientView
+ );
+
+ if ( !NT_SUCCESS(st) ) {
+ KdPrint(("OS2SS: Sb Accept Connection failed %lx\n",st));
+ return st;
+ }
+
+ st = NtCompleteConnectPort(CommunicationPort);
+
+ if ( !NT_SUCCESS(st) ) {
+ KdPrint(("OS2SS: Sb Complete Connection failed %lx\n",st));
+ }
+
+ return st;
+}
diff --git a/private/os2/os2ss/sources b/private/os2/os2ss/sources
new file mode 100644
index 000000000..fd4ef88eb
--- /dev/null
+++ b/private/os2/os2ss/sources
@@ -0,0 +1,68 @@
+!IF 0
+*****************************************************************************
+Copyright (c) 1989, 1990 Microsoft Corporation
+
+Module Name: SOURCES for OS/2 'mini subsystem'. Real functionality
+ is in ..\server
+
+Author: yarons
+
+Revision History:
+ 04-19-91 larrys original version
+*****************************************************************************
+!ENDIF
+
+#------------------------------------------------
+# INFO FOR CREATING LIBRARY
+#------------------------------------------------
+MAJORCOMP=os2
+MINORCOMP=os2ss
+
+TARGETNAME=os2ss
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+NO_READONLY_STRINGS=1
+
+#------------------------------------------------
+# INCLUDE PATH
+#------------------------------------------------
+
+INCLUDES=..\inc
+
+#------------------------------------------------
+# SOURCE FILES (used to make library)
+#------------------------------------------------
+
+SOURCES= sbinit.c \
+ sbreqst.c \
+ sbcnfg.c \
+ os2ss.rc
+
+#------------------------------------------------
+# FLAGS
+#------------------------------------------------
+
+!IFDEF PMNT
+C_DEFINES=-DPMNT
+!ENDIF
+
+#------------------------------------------------
+# EXECUTABLES
+#------------------------------------------------
+
+UMTYPE=ntss
+UMTEST=os2ss
+
+#------------------------------------------------
+# LIBRARIES created by the SOURCES= line (above)
+# (currently commented out - not used)
+#
+# $(BASEDIR)\public\sdk\lib\*\smdll.lib
+#------------------------------------------------
+
+UMAPPL=os2ss
+UMLIBS= obj\*\os2ss.lib \
+ ..\obj\*\os2ssrtl.lib
+UMRES= obj\*\os2ss.res
+COFFBASE=os2ss