SM: simplify and fix client (subsystem servers) management.
[reactos.git] / reactos / subsys / smss / smapiexec.c
index 7efc800..ebb825c 100644 (file)
-/* $Id$\r
- *\r
- * smapiexec.c - SM_API_EXECUTE_PROGRAM\r
- *\r
- * Reactos Session Manager\r
- *\r
- * --------------------------------------------------------------------\r
- *\r
- * This software is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU General Public License as\r
- * published by the Free Software Foundation; either version 2 of the\r
- * License, or (at your option) any later version.\r
- *\r
- * This software is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
- * General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this software; see the file COPYING.LIB. If not, write\r
- * to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,\r
- * MA 02139, USA.  \r
- *\r
- * --------------------------------------------------------------------\r
- */\r
-#include "smss.h"\r
-\r
-#define NDEBUG\r
-#include <debug.h>\r
-\r
-/**********************************************************************\r
- * SmCreateUserProcess/5\r
- *\r
- */\r
-NTSTATUS STDCALL\r
-SmCreateUserProcess (LPWSTR ImagePath,\r
-                    LPWSTR CommandLine,\r
-                    BOOLEAN WaitForIt,\r
-                    PLARGE_INTEGER Timeout OPTIONAL,\r
-                    BOOLEAN TerminateIt,\r
-                    PRTL_PROCESS_INFO UserProcessInfo OPTIONAL\r
-                    )\r
-{\r
-       UNICODE_STRING                  ImagePathString = {0};\r
-       UNICODE_STRING                  CommandLineString = {0};\r
-       PRTL_USER_PROCESS_PARAMETERS    ProcessParameters = NULL;\r
-       RTL_PROCESS_INFO                ProcessInfo = {0};\r
-       PRTL_PROCESS_INFO               pProcessInfo = & ProcessInfo;\r
-       NTSTATUS                        Status = STATUS_SUCCESS;\r
-\r
-\r
-       DPRINT("SM: %s called\n",__FUNCTION__);\r
-\r
-       RtlInitUnicodeString (& ImagePathString, ImagePath);\r
-       RtlInitUnicodeString (& CommandLineString, CommandLine);\r
-\r
-       RtlCreateProcessParameters(& ProcessParameters,\r
-                                  & ImagePathString,\r
-                                  NULL,\r
-                                  NULL,\r
-                                  & CommandLineString,\r
-                                  SmSystemEnvironment,\r
-                                  NULL,\r
-                                  NULL,\r
-                                  NULL,\r
-                                  NULL);\r
-\r
-       if(NULL != UserProcessInfo)\r
-       {\r
-               /* Use caller provided storage */\r
-               pProcessInfo = UserProcessInfo;\r
-       }\r
-\r
-       Status = RtlCreateUserProcess (& ImagePathString,\r
-                                      OBJ_CASE_INSENSITIVE,\r
-                                      ProcessParameters,\r
-                                      NULL,\r
-                                      NULL,\r
-                                      NULL,\r
-                                      FALSE,\r
-                                      NULL,\r
-                                      NULL,\r
-                                      pProcessInfo);\r
-                   \r
-   RtlDestroyProcessParameters (ProcessParameters);\r
-   \r
-       if (!NT_SUCCESS(Status))\r
-       {\r
-               DPRINT1("SM: %s: Running \"%S\" failed (Status=0x%08lx)\n",\r
-                       __FUNCTION__, ImagePathString.Buffer, Status);\r
-               return Status;\r
-       }\r
-\r
-   ZwResumeThread(pProcessInfo->ThreadHandle, NULL);\r
-\r
-\r
-       /* Wait for process termination */\r
-       if(WaitForIt)\r
-       {\r
-               NtWaitForSingleObject (pProcessInfo->ProcessHandle,\r
-                                      FALSE,\r
-                                      Timeout);\r
-       }\r
-\r
-       /* Terminate process */\r
-       if(TerminateIt)\r
-       {\r
-               NtClose(pProcessInfo->ThreadHandle);\r
-               NtClose(pProcessInfo->ProcessHandle);\r
-       }\r
-       return STATUS_SUCCESS;\r
-}\r
-\r
-/**********************************************************************\r
- * NAME\r
- *     SmLookupSubsystem/5\r
- *\r
- * DESCRIPTION\r
- *     Read from the registry key\r
- *     \Registry\SYSTEM\CurrentControlSet\Control\Session Manager\Subsystems\r
- *     the value which name is Name.\r
- *\r
- * ARGUMENTS\r
- *     Name: name of the program to run, that is a value's name in\r
- *           the SM registry key Subsystems;\r
- *     Data: what the registry gave back for Name;\r
- *     DataLength: how much Data the registry returns;\r
- *     DataType: what is Data?\r
- *     Expand: set it TRUE if you want this function to use the env\r
- *           to possibly expand Data before giving it back.\r
- */\r
-NTSTATUS STDCALL\r
-SmLookupSubsystem (IN     PWSTR   Name,\r
-                  IN OUT PWSTR   Data,\r
-                  IN OUT PULONG  DataLength,\r
-                  IN OUT PULONG  DataType,\r
-                  IN     BOOLEAN Expand)\r
-{\r
-       NTSTATUS           Status = STATUS_SUCCESS;\r
-       UNICODE_STRING     usKeyName = {0};\r
-       OBJECT_ATTRIBUTES  Oa = {0};\r
-       HANDLE             hKey = (HANDLE) 0;\r
-\r
-       DPRINT("SM: %s(Name='%S') called\n", __FUNCTION__, Name);\r
-       /*\r
-        * Prepare the key name to scan and\r
-        * related object attributes.\r
-        */\r
-       RtlInitUnicodeString (& usKeyName,\r
-               L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\SubSystems");\r
-\r
-       InitializeObjectAttributes (& Oa,\r
-                                   & usKeyName,\r
-                                   OBJ_CASE_INSENSITIVE,\r
-                                   NULL,\r
-                                   NULL);\r
-       /*\r
-        * Open the key. This MUST NOT fail, if the\r
-        * request is for a legitimate subsystem.\r
-        */\r
-       Status = NtOpenKey (& hKey,\r
-                             MAXIMUM_ALLOWED,\r
-                             & Oa);\r
-       if(NT_SUCCESS(Status))\r
-       {\r
-               UNICODE_STRING usValueName = {0};\r
-               WCHAR          KeyValueInformation [1024] = {L'\0'};\r
-               ULONG          ResultLength = 0L;\r
-               PKEY_VALUE_PARTIAL_INFORMATION\r
-                              kvpi = (PKEY_VALUE_PARTIAL_INFORMATION) KeyValueInformation;\r
-\r
-               \r
-               RtlInitUnicodeString (& usValueName, Name);\r
-               Status = NtQueryValueKey (hKey,\r
-                                         & usValueName,\r
-                                         KeyValuePartialInformation,\r
-                                         KeyValueInformation,\r
-                                         sizeof KeyValueInformation,\r
-                                         & ResultLength);\r
-               if(NT_SUCCESS(Status))\r
-               {\r
-                       DPRINT("nkvpi.TitleIndex = %ld\n", kvpi->TitleIndex);\r
-                       DPRINT("kvpi.Type        = %ld\n", kvpi->Type);\r
-                       DPRINT("kvpi.DataLength  = %ld\n", kvpi->DataLength);\r
-\r
-                       if((NULL != Data) && (NULL != DataLength) && (NULL != DataType))\r
-                       {\r
-                               *DataType = kvpi->Type;\r
-                               if((Expand) && (REG_EXPAND_SZ == *DataType))\r
-                               {\r
-                                       UNICODE_STRING Source;\r
-                                       WCHAR          DestinationBuffer [2048] = {0};\r
-                                       UNICODE_STRING Destination;\r
-                                       ULONG          Length = 0;\r
-\r
-                                       DPRINT("SM: %s: value will be expanded\n", __FUNCTION__);\r
-\r
-                                       Source.Length        = kvpi->DataLength;\r
-                                       Source.MaximumLength = kvpi->DataLength;\r
-                                       Source.Buffer        = (PWCHAR) & kvpi->Data;\r
-\r
-                                       Destination.Length        = 0;\r
-                                       Destination.MaximumLength = sizeof DestinationBuffer;\r
-                                       Destination.Buffer        = DestinationBuffer;\r
-\r
-                                       Status = RtlExpandEnvironmentStrings_U (SmSystemEnvironment,\r
-                                                                               & Source,\r
-                                                                               & Destination,\r
-                                                                               & Length);\r
-                                       if(NT_SUCCESS(Status))\r
-                                       {\r
-                                               *DataLength = min(*DataLength, Destination.Length);\r
-                                               RtlCopyMemory (Data, Destination.Buffer, *DataLength);                          \r
-                                       }\r
-                                                       \r
-                               }else{\r
-                                       DPRINT("SM: %s: value won't be expanded\n", __FUNCTION__);\r
-                                       *DataLength = min(*DataLength, kvpi->DataLength);\r
-                                       RtlCopyMemory (Data, & kvpi->Data, *DataLength);\r
-                               }\r
-                               *DataType = kvpi->Type;\r
-                       }else{\r
-                               DPRINT1("SM: %s: Data or DataLength or DataType is NULL!\n", __FUNCTION__);\r
-                               Status = STATUS_INVALID_PARAMETER;\r
-                       }\r
-               }else{\r
-                       DPRINT1("%s: NtQueryValueKey failed (Status=0x%08lx)\n", __FUNCTION__, Status);\r
-               }\r
-               NtClose (hKey);\r
-       }else{\r
-               DPRINT1("%s: NtOpenKey failed (Status=0x%08lx)\n", __FUNCTION__, Status);\r
-       }\r
-       return Status;\r
-}\r
-\r
-\r
-/**********************************************************************\r
- * SmExecPgm/1                                                 API\r
- */\r
-SMAPI(SmExecPgm)\r
-{\r
-       PSM_PORT_MESSAGE_EXECPGM ExecPgm = NULL;\r
-       WCHAR                    Name [SM_EXEXPGM_MAX_LENGTH + 1];\r
-       NTSTATUS                 Status = STATUS_SUCCESS;\r
-\r
-       DPRINT("SM: %s called\n",__FUNCTION__);\r
-\r
-       if(NULL == Request)\r
-       {\r
-               DPRINT1("SM: %s: Request == NULL!\n", __FUNCTION__);\r
-               return STATUS_INVALID_PARAMETER;\r
-       }\r
-       DPRINT("SM: %s called from CID(%lx|%lx)\n",\r
-               __FUNCTION__, Request->Header.ClientId.UniqueProcess,\r
-               Request->Header.ClientId.UniqueThread);\r
-       ExecPgm = & Request->ExecPgm;\r
-       /* Check if the name lenght is valid */\r
-       if((ExecPgm->NameLength > 0) &&\r
-          (ExecPgm->NameLength <= SM_EXEXPGM_MAX_LENGTH) &&\r
-          TRUE /* TODO: check LPC payload size */)\r
-       {\r
-               \r
-               RtlZeroMemory (Name, sizeof Name);\r
-               RtlCopyMemory (Name,\r
-                              ExecPgm->Name,\r
-                              (sizeof ExecPgm->Name[0] * ExecPgm->NameLength));\r
-               DPRINT("SM: %s: Name='%S'\n", __FUNCTION__, Name);\r
-               /*\r
-                * Check if program name is internal\r
-                * (Is this correct? Debug is in the registry too)\r
-                */\r
-               if(0 == _wcsicmp(L"DEBUG", Name))\r
-               {\r
-                       /*\r
-                        * Initialize DBGSS.\r
-                        * NOTE: probably in early prototypes it was an\r
-                        * independent process; now it is embedded in the\r
-                        * SM for performance or security.\r
-                        */\r
-                       Request->Status = SmInitializeDbgSs();\r
-               }\r
-               else\r
-               {\r
-                       WCHAR Data [MAX_PATH + 1] = {0};\r
-                       ULONG DataLength = sizeof Data;\r
-                       ULONG DataType = REG_EXPAND_SZ;\r
-\r
-                       /* Lookup Name in the registry */\r
-                       Status = SmLookupSubsystem (Name,\r
-                                                   Data,\r
-                                                   & DataLength,\r
-                                                   & DataType,\r
-                                                   TRUE); /* expand */\r
-                       if(NT_SUCCESS(Status))\r
-                       {\r
-                               WCHAR ImagePath [MAX_PATH + 1] = {0};\r
-\r
-                               wcscpy (ImagePath, L"\\??\\");\r
-                               wcscat (ImagePath, Data);\r
-                       \r
-                               /* Create native process */\r
-                               Request->Status = SmCreateUserProcess(ImagePath,\r
-                                                                     L"", /* FIXME */\r
-                                                                     FALSE, /* wait */\r
-                                                                     NULL,\r
-                                                                     FALSE, /* terminate */\r
-                                                                     NULL);\r
-                       }else{\r
-                               Request->Status = Status;\r
-                       }\r
-               }\r
-       }\r
-       else\r
-       {\r
-               Request->Status = Status = STATUS_INVALID_PARAMETER;\r
-       }\r
-       return Status;\r
-}\r
-\r
-/* EOF */\r
+/* $Id$
+ *
+ * smapiexec.c - SM_API_EXECUTE_PROGRAM
+ *
+ * Reactos Session Manager
+ *
+ * --------------------------------------------------------------------
+ *
+ * This software is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.LIB. If not, write
+ * to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ * MA 02139, USA.  
+ *
+ * --------------------------------------------------------------------
+ */
+#include "smss.h"
+
+#define NDEBUG
+#include <debug.h>
+
+/**********************************************************************
+ * SmCreateUserProcess/5
+ *
+ * DESCRIPTION
+ *
+ * ARGUMENTS
+ *     ImagePath: absolute path of the image to run;
+ *     CommandLine: arguments and options for ImagePath;
+ *     WaitForIt: TRUE for boot time processes and FALSE for
+ *             subsystems bootstrapping;
+ *     Timeout: optional: used if WaitForIt==TRUE;
+ *     ProcessHandle: optional: a duplicated handle for
+               the child process (storage provided by the caller).
+ *
+ * RETURN VALUE
+ *     NTSTATUS:
+ *
+ */
+NTSTATUS STDCALL
+SmCreateUserProcess (LPWSTR ImagePath,
+                    LPWSTR CommandLine,
+                    BOOLEAN WaitForIt,
+                    PLARGE_INTEGER Timeout OPTIONAL,
+                    PRTL_USER_PROCESS_INFORMATION UserProcessInfo OPTIONAL)
+{
+       UNICODE_STRING                  ImagePathString = {0};
+       UNICODE_STRING                  CommandLineString = {0};
+       PRTL_USER_PROCESS_PARAMETERS    ProcessParameters = NULL;
+       RTL_USER_PROCESS_INFORMATION            ProcessInfo = {0};
+       PRTL_USER_PROCESS_INFORMATION           pProcessInfo = & ProcessInfo;
+       NTSTATUS                        Status = STATUS_SUCCESS;
+
+       DPRINT("SM: %s called\n", __FUNCTION__);
+       
+       if (NULL != UserProcessInfo)
+       {
+               pProcessInfo = UserProcessInfo;
+       }
+
+       RtlInitUnicodeString (& ImagePathString, ImagePath);
+       RtlInitUnicodeString (& CommandLineString, CommandLine);
+
+       RtlCreateProcessParameters(& ProcessParameters,
+                                  & ImagePathString,
+                                  NULL,
+                                  NULL,
+                                  & CommandLineString,
+                                  SmSystemEnvironment,
+                                  NULL,
+                                  NULL,
+                                  NULL,
+                                  NULL);
+
+       Status = RtlCreateUserProcess (& ImagePathString,
+                                      OBJ_CASE_INSENSITIVE,
+                                      ProcessParameters,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      FALSE,
+                                      NULL,
+                                      NULL,
+                                      pProcessInfo);
+                   
+       RtlDestroyProcessParameters (ProcessParameters);
+   
+       if (!NT_SUCCESS(Status))
+       {
+               DPRINT1("SM: %s: Running \"%S\" failed (Status=0x%08lx)\n",
+                       __FUNCTION__, ImagePathString.Buffer, Status);
+               return Status;
+       }
+       /*
+        * It the caller is *not* interested in the child info,
+        * resume it immediately.
+        */
+       if (NULL == UserProcessInfo)
+       {
+               Status = NtResumeThread (ProcessInfo.ThreadHandle, NULL);
+               if(!NT_SUCCESS(Status))
+               {
+                       DPRINT1("SM: %s: NtResumeThread failed (Status=0x%08lx)\n",
+                               __FUNCTION__, Status);
+               }
+       }
+
+       /* Wait for process termination */
+       if (WaitForIt)
+       {
+               Status = NtWaitForSingleObject (pProcessInfo->ProcessHandle,
+                                               FALSE,
+                                               Timeout);
+               if (!NT_SUCCESS(Status))
+               {
+                       DPRINT1("SM: %s: NtWaitForSingleObject failed with Status=0x%08lx\n",
+                               __FUNCTION__, Status);
+               }
+               
+       }
+        if (NULL == UserProcessInfo)
+        {
+           NtClose(pProcessInfo->ProcessHandle);
+           NtClose(pProcessInfo->ThreadHandle);
+        }
+       return Status;
+}
+
+
+/**********************************************************************
+ * SmExecPgm/1                                                 API
+ */
+SMAPI(SmExecPgm)
+{
+       PSM_PORT_MESSAGE_EXECPGM ExecPgm = NULL;
+       WCHAR                    Name [SM_EXEXPGM_MAX_LENGTH + 1];
+       NTSTATUS                 Status = STATUS_SUCCESS;
+
+       DPRINT("SM: %s called\n",__FUNCTION__);
+
+       if(NULL == Request)
+       {
+               DPRINT1("SM: %s: Request == NULL!\n", __FUNCTION__);
+               return STATUS_INVALID_PARAMETER;
+       }
+       DPRINT("SM: %s called from CID(%lx|%lx)\n",
+               __FUNCTION__, Request->Header.ClientId.UniqueProcess,
+               Request->Header.ClientId.UniqueThread);
+       ExecPgm = & Request->Request.ExecPgm;
+       /* Check if the name lenght is valid */
+       if((ExecPgm->NameLength > 0) &&
+          (ExecPgm->NameLength <= SM_EXEXPGM_MAX_LENGTH) &&
+          TRUE /* TODO: check LPC payload size */)
+       {
+               WCHAR Data [MAX_PATH + 1] = {0};
+               ULONG DataLength = sizeof Data;
+               ULONG DataType = REG_EXPAND_SZ;
+
+               
+               RtlZeroMemory (Name, sizeof Name);
+               RtlCopyMemory (Name,
+                              ExecPgm->Name,
+                              (sizeof ExecPgm->Name[0] * ExecPgm->NameLength));
+               DPRINT("SM: %s: Name='%S'\n", __FUNCTION__, Name);
+               /* Lookup Name in the registry */
+               Status = SmLookupSubsystem (Name,
+                                           Data,
+                                           & DataLength,
+                                           & DataType,
+                                           SmSystemEnvironment /* expand */);
+               if(NT_SUCCESS(Status))
+               {
+                       /* Is the subsystem definition non-empty? */
+                       if (DataLength > sizeof Data[0])
+                       {
+                               WCHAR ImagePath [MAX_PATH + 1] = {0};
+                               PWCHAR CommandLine = ImagePath;
+                               RTL_USER_PROCESS_INFORMATION ProcessInfo = {0};
+
+                               wcscpy (ImagePath, L"\\??\\");
+                               wcscat (ImagePath, Data);
+                               /*
+                                * Look for the beginning of the command line.
+                                */
+                               for (;  (*CommandLine != L'\0') && (*CommandLine != L' ');
+                                       CommandLine ++);
+                               for (; *CommandLine == L' '; CommandLine ++)
+                               {
+                                       *CommandLine = L'\0';
+                               }
+                               /*
+                                * Create a native process (suspended).
+                                */
+                               ProcessInfo.Size = sizeof ProcessInfo;
+                               Request->SmHeader.Status =
+                                       SmCreateUserProcess(ImagePath,
+                                                             CommandLine,
+                                                             FALSE, /* wait */
+                                                             NULL, /* timeout */
+                                                             & ProcessInfo);
+                               if (NT_SUCCESS(Request->SmHeader.Status))
+                               {
+                                       Status = SmCreateClient (& ProcessInfo, Name);
+                                       if (NT_SUCCESS(Status))
+                                       {
+                                               Status = NtResumeThread (ProcessInfo.ThreadHandle, NULL);
+                                               if (!NT_SUCCESS(Status))
+                                               {
+                                                       DPRINT1("SM: %s: NtResumeThread failed (Status=0x%08lx)\n",
+                                                               __FUNCTION__, Status);
+                                                       //Status = SmDestroyClient TODO
+                                               }
+                                       } else {
+                                               DPRINT1("SM: %s: SmCreateClient failed (Status=0x%08lx)\n",
+                                                       __FUNCTION__, Status);
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               /*
+                                * OK, the definition is empty, but check
+                                * if it is the name of an embedded subsystem.
+                                */
+                               if(0 == _wcsicmp(L"DEBUG", Name))
+                               {
+                                       /*
+                                        * Initialize the embedded DBGSS.
+                                        */
+                                       Request->SmHeader.Status = SmInitializeDbgSs();
+                               }
+                               else
+                               {
+                                       /*
+                                        * Badly defined subsystem. Check the registry!
+                                        */
+                                       Request->SmHeader.Status = STATUS_NOT_FOUND;
+                               }
+                       }
+               } else {
+                       /* It couldn't lookup the Name! */
+                       Request->SmHeader.Status = Status;
+               }
+       }
+       return Status;
+}
+
+/* EOF */