/* $Id: init.c 24680 2006-10-30 18:45:22Z ion $
 *
 * reactos/subsys/csrss/init.c
 *
 * Initialize the CSRSS subsystem server process.
 *
 * ReactOS Operating System
 *
 */

/* INCLUDES ******************************************************************/

#include <csrss.h>

#define NDEBUG
#include <debug.h>

/* GLOBALS ******************************************************************/

HANDLE CsrHeap = (HANDLE) 0;

HANDLE CsrObjectDirectory = (HANDLE) 0;

UNICODE_STRING CsrDirectoryName;

extern HANDLE CsrssApiHeap;

static unsigned InitCompleteProcCount;
static CSRPLUGIN_INIT_COMPLETE_PROC *InitCompleteProcs = NULL;

static unsigned HardErrorProcCount;
static CSRPLUGIN_HARDERROR_PROC *HardErrorProcs = NULL;

HANDLE hSbApiPort = (HANDLE) 0;

HANDLE hBootstrapOk = (HANDLE) 0;

HANDLE hSmApiPort = (HANDLE) 0;

HANDLE hApiPort = (HANDLE) 0;

/**********************************************************************
 * CsrpAddInitCompleteProc/1
 */
static NTSTATUS FASTCALL
CsrpAddInitCompleteProc(CSRPLUGIN_INIT_COMPLETE_PROC Proc)
{
  CSRPLUGIN_INIT_COMPLETE_PROC *NewProcs;

  DPRINT("CSR: %s called\n", __FUNCTION__);

  NewProcs = RtlAllocateHeap(CsrssApiHeap, 0,
                             (InitCompleteProcCount + 1)
                             * sizeof(CSRPLUGIN_INIT_COMPLETE_PROC));
  if (NULL == NewProcs)
    {
      return STATUS_NO_MEMORY;
    }
  if (0 != InitCompleteProcCount)
    {
      RtlCopyMemory(NewProcs, InitCompleteProcs,
                    InitCompleteProcCount * sizeof(CSRPLUGIN_INIT_COMPLETE_PROC));
      RtlFreeHeap(CsrssApiHeap, 0, InitCompleteProcs);
    }
  NewProcs[InitCompleteProcCount] = Proc;
  InitCompleteProcs = NewProcs;
  InitCompleteProcCount++;

  return STATUS_SUCCESS;
}

static NTSTATUS FASTCALL
CsrpAddHardErrorProc(CSRPLUGIN_HARDERROR_PROC Proc)
{
    CSRPLUGIN_HARDERROR_PROC *NewProcs;

    DPRINT("CSR: %s called\n", __FUNCTION__);

    NewProcs = RtlAllocateHeap(CsrssApiHeap, 0,
                               (HardErrorProcCount + 1)
                               * sizeof(CSRPLUGIN_HARDERROR_PROC));
    if (NULL == NewProcs)
    {
        return STATUS_NO_MEMORY;
    }
    if (0 != HardErrorProcCount)
    {
        RtlCopyMemory(NewProcs, HardErrorProcs,
            HardErrorProcCount * sizeof(CSRPLUGIN_HARDERROR_PROC));
        RtlFreeHeap(CsrssApiHeap, 0, HardErrorProcs);
    }

    NewProcs[HardErrorProcCount] = Proc;
    HardErrorProcs = NewProcs;
    HardErrorProcCount++;

    return STATUS_SUCCESS;
}

/**********************************************************************
 * CallInitComplete/0
 */
static BOOL FASTCALL
CallInitComplete(void)
{
  BOOL Ok;
  unsigned i;

  DPRINT("CSR: %s called\n", __FUNCTION__);

  Ok = TRUE;
  if (0 != InitCompleteProcCount)
    {
      for (i = 0; i < InitCompleteProcCount && Ok; i++)
        {
          Ok = (*(InitCompleteProcs[i]))();
        }
      RtlFreeHeap(CsrssApiHeap, 0, InitCompleteProcs);
    }

  return Ok;
}

BOOL
FASTCALL
CallHardError(void)
{
    BOOL Ok;
    unsigned i;

    DPRINT("CSR: %s called\n", __FUNCTION__);

    Ok = TRUE;
    if (0 != HardErrorProcCount)
    {
        for (i = 0; i < HardErrorProcCount && Ok; i++)
        {
            Ok = (*(HardErrorProcs[i]))();
        }
    }

    return Ok;
}

ULONG
InitializeVideoAddressSpace(VOID);

/**********************************************************************
 * CsrpCreateObjectDirectory/3
 */
static NTSTATUS
CsrpCreateObjectDirectory (int argc, char ** argv, char ** envp)
{
   NTSTATUS Status;
   OBJECT_ATTRIBUTES Attributes;

  DPRINT("CSR: %s called\n", __FUNCTION__);


	/* create object directory ('\Windows') */
	RtlCreateUnicodeString (&CsrDirectoryName,
	                        L"\\Windows");

	InitializeObjectAttributes (&Attributes,
	                            &CsrDirectoryName,
	                            OBJ_OPENIF,
	                            NULL,
	                            NULL);

	Status = NtOpenDirectoryObject(&CsrObjectDirectory,
	                               0xF000F, /* ea:??? */
	                               &Attributes);
	return Status;
}

/**********************************************************************
 * CsrpInitVideo/3
 *
 * TODO: we need a virtual device for sessions other than
 * TODO: the console one
 */
static NTSTATUS
CsrpInitVideo (int argc, char ** argv, char ** envp)
{
  OBJECT_ATTRIBUTES ObjectAttributes;
  UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\??\\DISPLAY1");
  IO_STATUS_BLOCK Iosb;
  HANDLE VideoHandle = (HANDLE) 0;
  NTSTATUS Status = STATUS_SUCCESS;

  DPRINT("CSR: %s called\n", __FUNCTION__);

  InitializeVideoAddressSpace();

  InitializeObjectAttributes(&ObjectAttributes,
			     &DeviceName,
			     0,
			     NULL,
			     NULL);
  Status = NtOpenFile(&VideoHandle,
		      FILE_ALL_ACCESS,
		      &ObjectAttributes,
		      &Iosb,
		      0,
		      0);
  if (NT_SUCCESS(Status))
    {
      NtClose(VideoHandle);
    }
  return Status;
}

/**********************************************************************
 * CsrpInitWin32Csr/3
 *
 * TODO: this function should be turned more general to load an
 * TODO: hosted server DLL as received from the command line;
 * TODO: for instance: ServerDll=winsrv:ConServerDllInitialization,2
 * TODO:               ^method   ^dll   ^api                       ^sid
 * TODO:
 * TODO: CsrpHostServerDll (LPWSTR DllName,
 * TODO:                    LPWSTR ApiName,
 * TODO:                    DWORD  ServerId)
 */
static NTSTATUS
CsrpInitWin32Csr (int argc, char ** argv, char ** envp)
{
  NTSTATUS Status;
  UNICODE_STRING DllName;
  HINSTANCE hInst;
  ANSI_STRING ProcName;
  CSRPLUGIN_INITIALIZE_PROC InitProc;
  CSRSS_EXPORTED_FUNCS Exports;
  PCSRSS_API_DEFINITION ApiDefinitions;
  PCSRSS_OBJECT_DEFINITION ObjectDefinitions;
  CSRPLUGIN_INIT_COMPLETE_PROC InitCompleteProc;
  CSRPLUGIN_HARDERROR_PROC HardErrorProc;

  DPRINT("CSR: %s called\n", __FUNCTION__);

  RtlInitUnicodeString(&DllName, L"win32csr.dll");
  Status = LdrLoadDll(NULL, 0, &DllName, (PVOID *) &hInst);
  if (! NT_SUCCESS(Status))
    {
      return Status;
    }
  RtlInitAnsiString(&ProcName, "Win32CsrInitialization");
  Status = LdrGetProcedureAddress(hInst, &ProcName, 0, (PVOID *) &InitProc);
  if (! NT_SUCCESS(Status))
    {
      return Status;
    }
  Exports.CsrInsertObjectProc = CsrInsertObject;
  Exports.CsrGetObjectProc = CsrGetObject;
  Exports.CsrReleaseObjectProc = CsrReleaseObject;
  Exports.CsrEnumProcessesProc = CsrEnumProcesses;
  if (! (*InitProc)(&ApiDefinitions, &ObjectDefinitions, &InitCompleteProc,
                    &HardErrorProc, &Exports, CsrssApiHeap))
    {
      return STATUS_UNSUCCESSFUL;
    }

  Status = CsrApiRegisterDefinitions(ApiDefinitions);
  if (! NT_SUCCESS(Status))
    {
      return Status;
    }
  Status = CsrRegisterObjectDefinitions(ObjectDefinitions);
  if (! NT_SUCCESS(Status))
    {
      return Status;
    }
  if (NULL != InitCompleteProc)
    {
      Status = CsrpAddInitCompleteProc(InitCompleteProc);
    }
  if (HardErrorProc) Status = CsrpAddHardErrorProc(HardErrorProc);

  return Status;
}

CSRSS_API_DEFINITION NativeDefinitions[] =
  {
    CSRSS_DEFINE_API(CREATE_PROCESS,               CsrCreateProcess),
    CSRSS_DEFINE_API(TERMINATE_PROCESS,            CsrTerminateProcess),
    CSRSS_DEFINE_API(CONNECT_PROCESS,              CsrConnectProcess),
    CSRSS_DEFINE_API(REGISTER_SERVICES_PROCESS,    CsrRegisterServicesProcess),
    CSRSS_DEFINE_API(GET_SHUTDOWN_PARAMETERS,      CsrGetShutdownParameters),
    CSRSS_DEFINE_API(SET_SHUTDOWN_PARAMETERS,      CsrSetShutdownParameters),
    CSRSS_DEFINE_API(GET_INPUT_HANDLE,             CsrGetInputHandle),
    CSRSS_DEFINE_API(GET_OUTPUT_HANDLE,            CsrGetOutputHandle),
    CSRSS_DEFINE_API(CLOSE_HANDLE,                 CsrCloseHandle),
    CSRSS_DEFINE_API(VERIFY_HANDLE,                CsrVerifyHandle),
    CSRSS_DEFINE_API(DUPLICATE_HANDLE,             CsrDuplicateHandle),
    CSRSS_DEFINE_API(GET_INPUT_WAIT_HANDLE,        CsrGetInputWaitHandle),
    { 0, 0, NULL }
  };

static NTSTATUS STDCALL
CsrpCreateListenPort (IN     LPWSTR  Name,
		      IN OUT PHANDLE Port,
		      IN     PTHREAD_START_ROUTINE ListenThread)
{
	NTSTATUS           Status = STATUS_SUCCESS;
	OBJECT_ATTRIBUTES  PortAttributes;
	UNICODE_STRING     PortName;

	DPRINT("CSR: %s called\n", __FUNCTION__);

	RtlInitUnicodeString (& PortName, Name);
	InitializeObjectAttributes (& PortAttributes,
				    & PortName,
				    0,
				    NULL,
				    NULL);
	Status = NtCreatePort ( Port,
				& PortAttributes,
				LPC_MAX_DATA_LENGTH, /* TODO: make caller set it*/
				LPC_MAX_MESSAGE_LENGTH, /* TODO: make caller set it*/
				0); /* TODO: make caller set it*/
	if(!NT_SUCCESS(Status))
	{
		DPRINT1("CSR: %s: NtCreatePort failed (Status=%08lx)\n",
			__FUNCTION__, Status);
		return Status;
	}
	Status = RtlCreateUserThread(NtCurrentProcess(),
                               NULL,
                               FALSE,
                               0,
                               0,
                               0,
                               (PTHREAD_START_ROUTINE) ListenThread,
                               *Port,
                               NULL,
                               NULL);
	return Status;
}

/* === INIT ROUTINES === */

/**********************************************************************
 * CsrpCreateHeap/3
 */
static NTSTATUS
CsrpCreateHeap (int argc, char ** argv, char ** envp)
{
	DPRINT("CSR: %s called\n", __FUNCTION__);

	CsrssApiHeap = RtlCreateHeap(HEAP_GROWABLE,
        	                       NULL,
                	               65536,
                        	       65536,
	                               NULL,
        	                       NULL);
	if (CsrssApiHeap == NULL)
	{
		return STATUS_UNSUCCESSFUL;
	}
	return STATUS_SUCCESS;
}

/**********************************************************************
 * CsrpCreateCallbackPort/3
 */
static NTSTATUS
CsrpCreateCallbackPort (int argc, char ** argv, char ** envp)
{
	DPRINT("CSR: %s called\n", __FUNCTION__);

	return CsrpCreateListenPort (L"\\Windows\\SbApiPort",
				     & hSbApiPort,
				     ServerSbApiPortThread);
}

/**********************************************************************
 * CsrpRegisterSubsystem/3
 */
static NTSTATUS
CsrpRegisterSubsystem (int argc, char ** argv, char ** envp)
{
	NTSTATUS           Status = STATUS_SUCCESS;
	OBJECT_ATTRIBUTES  BootstrapOkAttributes;
	UNICODE_STRING     Name;

	DPRINT("CSR: %s called\n", __FUNCTION__);

	/*
	 * Create the event object the callback port
	 * thread will signal *if* the SM will
	 * authorize us to bootstrap.
	 */
	RtlInitUnicodeString (& Name, L"\\CsrssBooting");
	InitializeObjectAttributes(& BootstrapOkAttributes,
				   & Name,
				   0, NULL, NULL);
	Status = NtCreateEvent (& hBootstrapOk,
				EVENT_ALL_ACCESS,
				& BootstrapOkAttributes,
				SynchronizationEvent,
				FALSE);
	if(!NT_SUCCESS(Status))
	{
		DPRINT("CSR: %s: NtCreateEvent failed (Status=0x%08lx)\n",
			__FUNCTION__, Status);
		return Status;
	}
	/*
	 * Let's tell the SM a new environment
	 * subsystem server is in the system.
	 */
	RtlInitUnicodeString (& Name, L"\\Windows\\SbApiPort");
	DPRINT("CSR: %s: registering with SM for\n  IMAGE_SUBSYSTEM_WINDOWS_CUI == 3\n", __FUNCTION__);
	Status = SmConnectApiPort (& Name,
				   hSbApiPort,
				   IMAGE_SUBSYSTEM_WINDOWS_CUI,
				   & hSmApiPort);
	if(!NT_SUCCESS(Status))
	{
		DPRINT("CSR: %s unable to connect to the SM (Status=0x%08lx)\n",
			__FUNCTION__, Status);
		NtClose (hBootstrapOk);
		return Status;
	}
	/*
	 *  Wait for SM to reply OK... If the SM
	 *  won't answer, we hang here forever!
	 */
	DPRINT("CSR: %s: waiting for SM to OK boot...\n", __FUNCTION__);
	Status = NtWaitForSingleObject (hBootstrapOk,
					FALSE,
					NULL);
	NtClose (hBootstrapOk);
	return Status;
}

/**********************************************************************
 * 	EnvpToUnicodeString/2
 */
static ULONG FASTCALL
EnvpToUnicodeString (char ** envp, PUNICODE_STRING UnicodeEnv)
{
	ULONG        CharCount = 0;
	ULONG        Index = 0;
	ANSI_STRING  AnsiEnv;

	UnicodeEnv->Buffer = NULL;
	
	for (Index=0; NULL != envp[Index]; Index++)
	{
		CharCount += strlen (envp[Index]);
		++ CharCount;
	}
	++ CharCount;
	
	AnsiEnv.Buffer = RtlAllocateHeap (RtlGetProcessHeap(), 0, CharCount);
	if (NULL != AnsiEnv.Buffer)
	{

		PCHAR WritePos = AnsiEnv.Buffer;
		
		for (Index=0; NULL != envp[Index]; Index++)
		{
			strcpy (WritePos, envp[Index]);
			WritePos += strlen (envp[Index]) + 1;
		}

      /* FIXME: the last (double) nullterm should perhaps not be included in Length
       * but only in MaximumLength. -Gunnar */
		AnsiEnv.Buffer [CharCount-1] = '\0';
		AnsiEnv.Length             = CharCount;
		AnsiEnv.MaximumLength      = CharCount;
      
		RtlAnsiStringToUnicodeString (UnicodeEnv, & AnsiEnv, TRUE);
		RtlFreeHeap (RtlGetProcessHeap(), 0, AnsiEnv.Buffer);
	}
	return CharCount;
}
/**********************************************************************
 * 	CsrpLoadKernelModeDriver/3
 */
static NTSTATUS
CsrpLoadKernelModeDriver (int argc, char ** argv, char ** envp)
{
	NTSTATUS        Status = STATUS_SUCCESS;
	WCHAR           Data [MAX_PATH + 1];
	ULONG           DataLength = sizeof Data;
	ULONG           DataType = 0;
	UNICODE_STRING  Environment;


	DPRINT("SM: %s called\n", __FUNCTION__);


	EnvpToUnicodeString (envp, & Environment);	
	Status = SmLookupSubsystem (L"Kmode",
				    Data,
				    & DataLength,
				    & DataType,
				    Environment.Buffer);
	RtlFreeUnicodeString (& Environment);
	if((STATUS_SUCCESS == Status) && (DataLength > sizeof Data[0]))
	{
		WCHAR                      ImagePath [MAX_PATH + 1] = {0};
		UNICODE_STRING             ModuleName;

		wcscpy (ImagePath, L"\\??\\");
		wcscat (ImagePath, Data);
		RtlInitUnicodeString (& ModuleName, ImagePath);
		Status = NtSetSystemInformation(/* FIXME: SystemLoadAndCallImage */
		                                SystemExtendServiceTableInformation,
						& ModuleName,
						sizeof ModuleName);
		if(!NT_SUCCESS(Status))
		{
			DPRINT("WIN: %s: loading Kmode failed (Status=0x%08lx)\n",
				__FUNCTION__, Status);
		}
	}
	return Status;
}

/**********************************************************************
 * CsrpCreateApiPort/2
 */
static NTSTATUS
CsrpCreateApiPort (int argc, char ** argv, char ** envp)
{
	DPRINT("CSR: %s called\n", __FUNCTION__);

    CsrInitProcessData();
	return CsrpCreateListenPort (L"\\Windows\\ApiPort",
				     & hApiPort,
#ifdef NTLPC
                     (PTHREAD_START_ROUTINE)ClientConnectionThread);
#else
				     ServerApiPortThread);
#endif
}

/**********************************************************************
 * CsrpApiRegisterDef/0
 */
static NTSTATUS
CsrpApiRegisterDef (int argc, char ** argv, char ** envp)
{
	return CsrApiRegisterDefinitions(NativeDefinitions);
}

/**********************************************************************
 * CsrpCCTS/2
 */
static NTSTATUS
CsrpCCTS (int argc, char ** argv, char ** envp)
{
    ULONG Dummy;
    ULONG DummyLength = sizeof(Dummy);
	return CsrClientConnectToServer(L"\\Windows",
			0, &Dummy, &DummyLength, NULL);
}

/**********************************************************************
 * CsrpRunWinlogon/0
 *
 * Start the logon process (winlogon.exe).
 *
 * TODO: this should be moved in CsrpCreateSession/x (one per session)
 * TODO: in its own desktop (one logon desktop per winstation).
 */
static NTSTATUS
CsrpRunWinlogon (int argc, char ** argv, char ** envp)
{
	NTSTATUS                      Status = STATUS_SUCCESS;
	UNICODE_STRING                ImagePath;
	UNICODE_STRING                CommandLine;
	PRTL_USER_PROCESS_PARAMETERS  ProcessParameters = NULL;
	RTL_USER_PROCESS_INFORMATION  ProcessInfo;


	DPRINT("CSR: %s called\n", __FUNCTION__);

	/* initialize the process parameters */
	RtlInitUnicodeString (& ImagePath, L"\\SystemRoot\\system32\\winlogon.exe");
	RtlInitUnicodeString (& CommandLine, L"");
	RtlCreateProcessParameters(& ProcessParameters,
				   & ImagePath,
				   NULL,
				   NULL,
				   & CommandLine,
				   NULL,
				   NULL,
				   NULL,
				   NULL,
				   NULL);
	/* Create the winlogon process */
	Status = RtlCreateUserProcess (& ImagePath,
				       OBJ_CASE_INSENSITIVE,
				       ProcessParameters,
				       NULL,
				       NULL,
				       NULL,
				       FALSE,
				       NULL,
				       NULL,
				       & ProcessInfo);
	/* Cleanup */
	RtlDestroyProcessParameters (ProcessParameters);
	if (!NT_SUCCESS(Status))
	{
		DPRINT1("SM: %s: loading winlogon.exe failed (Status=%08lx)\n",
				__FUNCTION__, Status);
	}
   ZwResumeThread(ProcessInfo.ThreadHandle, NULL);
	return Status;
}

static NTSTATUS
CsrpCreateHardErrorPort (int argc, char ** argv, char ** envp)
{
    return NtSetDefaultHardErrorPort(hApiPort);
}

typedef NTSTATUS (* CSR_INIT_ROUTINE)(int,char**,char**);

struct {
	BOOL Required;
	CSR_INIT_ROUTINE EntryPoint;
	PCHAR ErrorMessage;
} InitRoutine [] = {
	{TRUE, CsrpCreateCallbackPort,   "create the callback port \\Windows\\SbApiPort"},
	{TRUE, CsrpRegisterSubsystem,    "register with SM"},
	{TRUE, CsrpCreateHeap,           "create the CSR heap"},
	{TRUE, CsrpCreateApiPort,        "create the api port \\Windows\\ApiPort"},
    {TRUE, CsrpCreateHardErrorPort,  "create the hard error port"},
	{TRUE, CsrpCreateObjectDirectory,"create the object directory \\Windows"},
	{TRUE, CsrpLoadKernelModeDriver, "load Kmode driver"},
	{TRUE, CsrpInitVideo,            "initialize video"},
	{TRUE, CsrpApiRegisterDef,       "initialize api definitions"},
	{TRUE, CsrpCCTS,                 "connect client to server"},
	{TRUE, CsrpInitWin32Csr,         "load usermode dll"},
	{TRUE, CsrpRunWinlogon,          "run WinLogon"},
};

/**********************************************************************
 * NAME
 * 	CsrServerInitialization
 *
 * DESCRIPTION
 * 	Initialize the Win32 environment subsystem server.
 *
 * RETURN VALUE
 * 	TRUE: Initialization OK; otherwise FALSE.
 */
BOOL STDCALL
CsrServerInitialization (
	int argc,
	char ** argv,
	char ** envp
	)
{
	UINT       i = 0;
	NTSTATUS  Status = STATUS_SUCCESS;

	DPRINT("CSR: %s called\n", __FUNCTION__);

	for (i=0; i < (sizeof InitRoutine / sizeof InitRoutine[0]); i++)
	{
		Status = InitRoutine[i].EntryPoint(argc,argv,envp);
		if(!NT_SUCCESS(Status))
		{
			DPRINT1("CSR: %s: failed to %s (Status=%08lx)\n",
				__FUNCTION__,
				InitRoutine[i].ErrorMessage,
				Status);
			if (InitRoutine[i].Required)
			{
				return FALSE;
			}
		}
	}
	if (CallInitComplete())
	{
		Status = SmCompleteSession (hSmApiPort,hSbApiPort,hApiPort);
		return TRUE;
	}
	return FALSE;
}

/* EOF */
