1139 lines
25 KiB
C
1139 lines
25 KiB
C
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ntinitss.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the code to establish the connection between
|
|
the session console process and the PSX Emulation Subsystem.
|
|
|
|
Author:
|
|
|
|
Avi Nathan (avin) 17-Jul-1991
|
|
|
|
Environment:
|
|
|
|
User Mode Only
|
|
|
|
Revision History:
|
|
|
|
Ellen Aycock-Wright (ellena) 15-Sept-1991 Modified for POSIX
|
|
|
|
--*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <io.h>
|
|
#define _POSIX_
|
|
#include <limits.h>
|
|
#define NTPSX_ONLY
|
|
#include "psxses.h"
|
|
#include <ntsm.h>
|
|
#include "sesport.h"
|
|
|
|
#define COPY_TO_SESSION_DATABASE(pchEnvp) { \
|
|
*ppch++ = TmpPtr - (ULONG_PTR)Buf; \
|
|
(void)strcpy(TmpPtr, pchEnvp); \
|
|
TmpPtr += strlen(TmpPtr); \
|
|
*TmpPtr++ = '\0'; } \
|
|
|
|
#define MyFree(x) if(NULL!=x)free(x);
|
|
|
|
const int iDaysInMonths[] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
|
|
|
|
//
|
|
// PSXSES protocol with PSXSS
|
|
//
|
|
// Connect
|
|
// 1. PSXSES ----------> PSXSS
|
|
//
|
|
//
|
|
// Connect
|
|
// 2. PSXSES <---------- PSXSS
|
|
//
|
|
//
|
|
// SesConCreate
|
|
// 3. PSXSES ----------> PSXSS (StartProcess)
|
|
//
|
|
//
|
|
// PSXSES connects to the PSXSS, then PSXSS connects back. Then PSXSES
|
|
// sends the Create
|
|
//
|
|
//
|
|
|
|
/*
|
|
* prototypes for internal functions.
|
|
*/
|
|
PCHAR BuildTZEnvVar();
|
|
PCHAR ConvertTimeFromBias( LONG );
|
|
int MakeJulianDate( PTIME_FIELDS );
|
|
PCHAR MakeTime( PTIME_FIELDS );
|
|
PCHAR MakeTZName( WCHAR * );
|
|
BOOL CreateConsoleDataSection(VOID);
|
|
PCHAR ConvertPathVar(PCHAR);
|
|
|
|
int
|
|
CountOpenFiles()
|
|
/*++
|
|
|
|
CountOpenFiles -- return the number of file descriptors in
|
|
use.
|
|
|
|
--*/
|
|
{
|
|
int i;
|
|
|
|
i = _dup(0);
|
|
if (-1 == i) {
|
|
if (EBADF == errno) {
|
|
return 0;
|
|
}
|
|
if (EMFILE == errno) {
|
|
return _NFILE;
|
|
}
|
|
// what other error?
|
|
return 3;
|
|
}
|
|
_close(i);
|
|
return i;
|
|
}
|
|
|
|
DWORD
|
|
InitPsxSessionPort(
|
|
VOID
|
|
)
|
|
{
|
|
char PortName[PSX_SES_BASE_PORT_NAME_LENGTH];
|
|
STRING PsxSessionPortName;
|
|
UNICODE_STRING PsxSessionPortName_U;
|
|
UNICODE_STRING PsxSSPortName;
|
|
HANDLE RequestThread;
|
|
DWORD dwThreadId;
|
|
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
ULONG ConnectionInfoLen;
|
|
PSXSESCONNECTINFO ConnectionInfo;
|
|
SECURITY_QUALITY_OF_SERVICE DynamicQos;
|
|
BOOLEAN DeferedPosixLoadAttempted = FALSE;
|
|
|
|
if (!CreateConsoleDataSection()) {
|
|
return(0);
|
|
}
|
|
|
|
//
|
|
// Get a private ID for the session port.
|
|
//
|
|
PSX_GET_SESSION_PORT_NAME((char *)&PortName, GetSessionUniqueId());
|
|
RtlInitAnsiString(&PsxSessionPortName, PortName);
|
|
|
|
//
|
|
// Create session port
|
|
//
|
|
|
|
Status = RtlAnsiStringToUnicodeString(&PsxSessionPortName_U,
|
|
&PsxSessionPortName, TRUE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
PsxSessionPort = NULL;
|
|
return 0;
|
|
}
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes, &PsxSessionPortName_U, 0,
|
|
NULL, NULL);
|
|
Status = NtCreatePort(&PsxSessionPort, &ObjectAttributes,
|
|
sizeof(SCCONNECTINFO), sizeof(SCREQUESTMSG),
|
|
4096 * 16);
|
|
|
|
RtlFreeUnicodeString(&PsxSessionPortName_U);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
PsxSessionPort = NULL;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Create a thread to handle requests to the session port including
|
|
// connection requests.
|
|
//
|
|
|
|
RequestThread = CreateThread( NULL,
|
|
0,
|
|
ServeSessionRequests,
|
|
NULL,
|
|
0,
|
|
&dwThreadId
|
|
);
|
|
if (RequestThread == NULL) {
|
|
NtClose( PsxSessionPort );
|
|
return 0;
|
|
}
|
|
|
|
SetThreadPriority(RequestThread, THREAD_PRIORITY_ABOVE_NORMAL);
|
|
|
|
//
|
|
// connect to PSXSS and notify of the new session and the port associated
|
|
// with it. It will connected back to the session port just created.
|
|
//
|
|
|
|
ConnectionInfo.In.SessionUniqueId = GetSessionUniqueId();
|
|
ConnectionInfoLen = sizeof(ConnectionInfo);
|
|
|
|
PSX_GET_SESSION_OBJECT_NAME(&PsxSSPortName, PSX_SS_SESSION_PORT_NAME);
|
|
|
|
//
|
|
// Set up the security quality of service parameters to use over the
|
|
// port. Use the most efficient (least overhead) - which is dynamic
|
|
// rather than static tracking.
|
|
//
|
|
|
|
DynamicQos.ImpersonationLevel = SecurityImpersonation;
|
|
DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
|
DynamicQos.EffectiveOnly = TRUE;
|
|
|
|
retry_connect:
|
|
Status = NtConnectPort(&PsxSSPortHandle, &PsxSSPortName, &DynamicQos,
|
|
NULL, NULL, NULL, (PVOID)&ConnectionInfo,
|
|
&ConnectionInfoLen);
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if ( DeferedPosixLoadAttempted == FALSE ) {
|
|
HANDLE SmPort;
|
|
UNICODE_STRING PosixName;
|
|
|
|
DeferedPosixLoadAttempted = TRUE;
|
|
|
|
Status = SmConnectToSm(NULL,NULL,0,&SmPort);
|
|
if ( NT_SUCCESS(Status) ) {
|
|
RtlInitUnicodeString(&PosixName,L"POSIX");
|
|
SmLoadDeferedSubsystem(SmPort,&PosixName);
|
|
goto retry_connect;
|
|
}
|
|
}
|
|
|
|
KdPrint(("PSXSES: Unable to connect to %ws: %X\n",
|
|
PsxSSPortName.Buffer, Status));
|
|
return 0;
|
|
}
|
|
return HandleToUlong(ConnectionInfo.Out.SessionPortHandle);
|
|
}
|
|
|
|
VOID
|
|
ScHandleConnectionRequest(
|
|
IN PSCREQUESTMSG Message
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PSCCONNECTINFO ConnectionInfo = &Message->ConnectionRequest;
|
|
HANDLE CommPortHandle;
|
|
PORT_VIEW ServerView;
|
|
|
|
// BUGBUG: Add verification test
|
|
if (FALSE) {
|
|
// Reject
|
|
Status = NtAcceptConnectPort(&CommPortHandle, NULL,
|
|
(PPORT_MESSAGE) Message, FALSE, NULL, NULL);
|
|
} else {
|
|
// ??? Any reply
|
|
ConnectionInfo->dummy = 0;
|
|
|
|
// BUGBUG:
|
|
ServerView.Length = sizeof(ServerView);
|
|
ServerView.SectionOffset = 0L;
|
|
ServerView.ViewSize = 0L;
|
|
|
|
Status = NtAcceptConnectPort(&CommPortHandle, NULL,
|
|
(PPORT_MESSAGE) Message, TRUE, NULL, NULL);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("PSXSES: Accept failed: %X\n",
|
|
Status));
|
|
exit(1);
|
|
}
|
|
//
|
|
// Record the view section address in a
|
|
// global variable.
|
|
//
|
|
// BUGBUG: PsxSesConPortBaseAddress =
|
|
// ServerView.ViewBase;
|
|
|
|
Status = NtCompleteConnectPort(CommPortHandle);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
{
|
|
PCLIENT_AND_PORT pc;
|
|
|
|
pc = malloc(sizeof(*pc));
|
|
if (NULL == pc) {
|
|
return;
|
|
}
|
|
|
|
pc->ClientId = Message->h.ClientId;
|
|
pc->CommPort = CommPortHandle;
|
|
InsertTailList(&ClientPortsList, &pc->Links);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// create a section to be shared by all client processes running in this
|
|
// session. returns a pointer to the base.
|
|
//
|
|
BOOL
|
|
CreateConsoleDataSection(
|
|
VOID
|
|
)
|
|
{
|
|
char SessionName[PSX_SES_BASE_PORT_NAME_LENGTH];
|
|
STRING PsxSessionDataName;
|
|
UNICODE_STRING PsxSessionDataName_U;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE SectionHandle;
|
|
LARGE_INTEGER SectionSize;
|
|
SIZE_T ViewSize=0L;
|
|
BOOLEAN DeferedPosixLoadAttempted = FALSE;
|
|
|
|
//
|
|
// Get a private ID for the session data.
|
|
//
|
|
PSX_GET_SESSION_DATA_NAME((char *)&SessionName, GetSessionUniqueId());
|
|
RtlInitAnsiString(&PsxSessionDataName, SessionName);
|
|
Status = RtlAnsiStringToUnicodeString(&PsxSessionDataName_U,
|
|
&PsxSessionDataName, TRUE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("PSXSES: RtlAnsiStringToUnicode (%s) failed: 0x%x\n",
|
|
&SessionName,
|
|
Status));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes, &PsxSessionDataName_U, 0,
|
|
NULL, NULL);
|
|
|
|
//
|
|
// create a 64k section.
|
|
// BUGBUG: cruiser apis allow io of more then 64k
|
|
//
|
|
|
|
retry_create:
|
|
SectionSize.LowPart = PSX_SESSION_PORT_MEMORY_SIZE;
|
|
SectionSize.HighPart = 0L;
|
|
|
|
Status = NtCreateSection(&SectionHandle,
|
|
SECTION_MAP_WRITE,
|
|
&ObjectAttributes, &SectionSize, PAGE_READWRITE,
|
|
SEC_COMMIT, NULL);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if ( DeferedPosixLoadAttempted == FALSE ) {
|
|
HANDLE SmPort;
|
|
UNICODE_STRING PosixName;
|
|
LARGE_INTEGER TimeOut;
|
|
|
|
DeferedPosixLoadAttempted = TRUE;
|
|
|
|
Status = SmConnectToSm(NULL,NULL,0,&SmPort);
|
|
if ( NT_SUCCESS(Status) ) {
|
|
RtlInitUnicodeString(&PosixName,L"POSIX");
|
|
SmLoadDeferedSubsystem(SmPort,&PosixName);
|
|
|
|
//
|
|
// Sleep for 1 seconds
|
|
//
|
|
|
|
TimeOut.QuadPart = (LONGLONG)1000 * (LONGLONG)-10000;
|
|
NtDelayExecution(FALSE,&TimeOut);
|
|
goto retry_create;
|
|
}
|
|
}
|
|
|
|
RtlFreeUnicodeString(&PsxSessionDataName_U);
|
|
KdPrint(("PSXSES: NtCreateSection (%wZ) failed: 0x%x\n",
|
|
&PsxSessionDataName_U, Status));
|
|
return FALSE;
|
|
}
|
|
|
|
RtlFreeUnicodeString(&PsxSessionDataName_U);
|
|
|
|
//
|
|
// Map the the whole section to virtual address.
|
|
// Let MM locate the view.
|
|
//
|
|
|
|
PsxSessionDataBase = 0L;
|
|
Status = NtMapViewOfSection(SectionHandle, NtCurrentProcess(),
|
|
&PsxSessionDataBase, 0L, 0L, NULL,
|
|
&ViewSize, ViewUnmap, 0L, PAGE_READWRITE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ASSERT(NT_SUCCESS(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
PsxSessionDataSectionHandle = SectionHandle;
|
|
return(TRUE);
|
|
}
|
|
|
|
BOOL
|
|
StartProcess(
|
|
DWORD SessionPortHandle,
|
|
char *PgmName,
|
|
char *CurrentDir,
|
|
int argc,
|
|
char **argv,
|
|
char **envp
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Start the POSIX process running by calling PsxSesConCreate. We use
|
|
the port memory to pass the name of the command, its args, and the
|
|
environment strings. The layout of the port memory looks like this:
|
|
|
|
PsxSessionDataBase:
|
|
Command line, nul-terminated.
|
|
Buff:
|
|
argv[0]
|
|
argv[1]
|
|
...
|
|
NULL
|
|
environ[0]
|
|
environ[1]
|
|
...
|
|
NULL
|
|
<argv strings>
|
|
<environ strings>
|
|
|
|
Since we'll be passing this to the server process, we make all the
|
|
pointers relative to Buff.
|
|
|
|
--*/
|
|
{
|
|
PSXSESREQUESTMSG RequestMsg;
|
|
PSXSESREQUESTMSG ReplyMsg;
|
|
PSCREQ_CREATE Create;
|
|
NTSTATUS Status;
|
|
static char path[256];
|
|
char *Buf;
|
|
char *TmpPtr;
|
|
int i;
|
|
char **ppch;
|
|
char *pch;
|
|
char *pchHomeDrive; // pointer to HOMEDRIVE environ var
|
|
char *pchHomePath; // pointer to HOMEPATH environ var
|
|
BOOLEAN fSuccess;
|
|
|
|
UNICODE_STRING DosPath_U, Path_U;
|
|
ANSI_STRING DosPath_A, Path_A;
|
|
|
|
//
|
|
// Set Header info
|
|
//
|
|
|
|
PORT_MSG_DATA_LENGTH(RequestMsg) = sizeof(RequestMsg) -
|
|
sizeof(PORT_MESSAGE);
|
|
// BUGBUG: too much
|
|
PORT_MSG_TOTAL_LENGTH(RequestMsg) = sizeof(RequestMsg);
|
|
PORT_MSG_ZERO_INIT(RequestMsg) = 0L;
|
|
|
|
//
|
|
// Set request info
|
|
//
|
|
|
|
// BUGBUG: use common memory
|
|
RequestMsg.Request = SesConCreate;
|
|
Create = &RequestMsg.d.Create;
|
|
|
|
Create->SessionUniqueId = GetSessionUniqueId();
|
|
Create->SessionPort = (HANDLE)SessionPortHandle;
|
|
Create->Buffer = PsxSessionDataBase;
|
|
Create->OpenFiles = CountOpenFiles();
|
|
|
|
Buf = PsxSessionDataBase;
|
|
Create->PgmNameOffset = 0;
|
|
|
|
RtlInitAnsiString(&DosPath_A, PgmName);
|
|
|
|
Status = RtlAnsiStringToUnicodeString(&DosPath_U, &DosPath_A, TRUE);
|
|
|
|
fSuccess = RtlDosPathNameToNtPathName_U(
|
|
DosPath_U.Buffer,
|
|
&Path_U,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
RtlFreeUnicodeString(&DosPath_U);
|
|
if (!fSuccess) {
|
|
return fSuccess;
|
|
}
|
|
|
|
Status = RtlUnicodeStringToAnsiString(&Path_A, &Path_U, TRUE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)Path_U.Buffer);
|
|
return FALSE;
|
|
}
|
|
|
|
strcpy(Buf, Path_A.Buffer);
|
|
|
|
RtlFreeAnsiString(&Path_A);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)Path_U.Buffer);
|
|
|
|
Buf += strlen(Buf) + 1;
|
|
|
|
if ((ULONG_PTR)Buf & 0x1)
|
|
++Buf;
|
|
if ((ULONG_PTR)Buf & 0x2)
|
|
Buf += 2;
|
|
|
|
Create->CurrentDirOffset = PtrToUlong(Buf - (ULONG_PTR)PsxSessionDataBase);
|
|
|
|
strcpy(Buf, CurrentDir);
|
|
Buf += strlen(Buf) + 1;
|
|
|
|
if ((ULONG_PTR)Buf & 0x1)
|
|
++Buf;
|
|
if ((ULONG_PTR)Buf & 0x2)
|
|
Buf += 2;
|
|
|
|
Create->ArgsOffset = PtrToUlong(Buf - (ULONG_PTR)PsxSessionDataBase);
|
|
|
|
//
|
|
// We need to know how much space to leave for the argv and environ
|
|
// vectors to know where to start the strings. We have argc, which
|
|
// was passed in, but we need to count the environ strings.
|
|
//
|
|
|
|
argc++; // for trailing argv null
|
|
for (ppch = envp; NULL != *ppch; ++ppch)
|
|
++argc;
|
|
|
|
++argc; // for trailing environ null
|
|
++argc; // for LOGNAME
|
|
++argc; // for HOME
|
|
++argc; // for TZ
|
|
++argc; // for _PSXLIBPATH
|
|
|
|
argc += 1;
|
|
|
|
//
|
|
// TmpPtr indicates the place at which the argument and environment
|
|
// strings will be placed.
|
|
//
|
|
|
|
TmpPtr = &Buf[argc * sizeof(char *)];
|
|
|
|
// copy the argv pointers and strings
|
|
|
|
ppch = (char **)&Buf[0];
|
|
while (NULL != *argv) {
|
|
*ppch++ = TmpPtr - (ULONG_PTR)Buf;
|
|
(void)strcpy(TmpPtr, *argv++);
|
|
|
|
TmpPtr += strlen(TmpPtr);
|
|
*TmpPtr++ = '\0';
|
|
}
|
|
*ppch++ = NULL;
|
|
|
|
Create->EnvironmentOffset = PtrToUlong(TmpPtr - (ULONG_PTR)PsxSessionDataBase);
|
|
|
|
pchHomeDrive = NULL;
|
|
pchHomePath = NULL;
|
|
|
|
while (NULL != *envp) {
|
|
char *p;
|
|
int bPathFound = 0;
|
|
|
|
//
|
|
// Upcase the variable part of each environment string.
|
|
//
|
|
|
|
if (NULL == (p = strchr(*envp, '='))) {
|
|
// no equals sign? Skip this one.
|
|
*envp++;
|
|
continue;
|
|
}
|
|
*p = '\0';
|
|
_strupr(*envp);
|
|
if (0 == strcmp(*envp, "PATH") && !bPathFound) {
|
|
bPathFound = 1;
|
|
// Save PATH as LIBPATH.
|
|
|
|
pch = malloc(strlen(p + 1) + sizeof("_PSXLIBPATH") + 2);
|
|
if (NULL != pch) {
|
|
strcpy(pch, "_PSXLIBPATH=");
|
|
strcat(pch, p + 1);
|
|
COPY_TO_SESSION_DATABASE(pch);
|
|
free(pch);
|
|
}
|
|
|
|
// Convert PATH to POSIX-format
|
|
*p = '=';
|
|
*envp = ConvertPathVar(*envp);
|
|
if (NULL == *envp) {
|
|
// no memory; skip the path
|
|
*envp++;
|
|
continue;
|
|
}
|
|
} else if (0 == strcmp(*envp, "USERNAME")) {
|
|
// Copy USERNAME to LOGNAME
|
|
*p = '=';
|
|
pch = malloc(strlen(*envp));
|
|
if (NULL == pch) {
|
|
continue;
|
|
}
|
|
sprintf(pch,"LOGNAME=%s",strchr(*envp,'=')+1);
|
|
COPY_TO_SESSION_DATABASE(pch);
|
|
free(pch);
|
|
} else if (0 == strcmp(*envp, "HOMEPATH")) {
|
|
pchHomePath = p+1;
|
|
*p = '=';
|
|
} else if (0 == strcmp(*envp, "HOMEDRIVE")) {
|
|
pchHomeDrive = p+1;
|
|
*p = '=';
|
|
} else {
|
|
*p = '=';
|
|
}
|
|
COPY_TO_SESSION_DATABASE(*envp);
|
|
*envp++;
|
|
}
|
|
|
|
//
|
|
// setup the TZ env var
|
|
//
|
|
|
|
pch = BuildTZEnvVar();
|
|
if (NULL != pch) {
|
|
COPY_TO_SESSION_DATABASE(pch);
|
|
free(pch);
|
|
}
|
|
|
|
//
|
|
// if the HOMEPATH env var was not set, we'll have to set HOME to //C/
|
|
//
|
|
|
|
if (NULL == pchHomePath || NULL == pchHomeDrive) {
|
|
COPY_TO_SESSION_DATABASE("HOME=//C/");
|
|
} else {
|
|
char *pch2;
|
|
pch = malloc((5 + strlen(pchHomeDrive)+strlen(pchHomePath))*2);
|
|
if (NULL != pch) {
|
|
sprintf(pch,"HOME=%s%s",pchHomeDrive,pchHomePath);
|
|
pch2 = ConvertPathVar(pch);
|
|
free(pch);
|
|
if (NULL != pch2) {
|
|
COPY_TO_SESSION_DATABASE(pch2);
|
|
}
|
|
}
|
|
}
|
|
|
|
*ppch = NULL;
|
|
|
|
//
|
|
// for all request pass the session handle except for the CREATE request
|
|
// where no session have been allocated yet.
|
|
//
|
|
|
|
RequestMsg.Session = NULL;
|
|
|
|
Status = NtRequestWaitReplyPort(PsxSSPortHandle, (PPORT_MESSAGE)&RequestMsg,
|
|
(PPORT_MESSAGE)&ReplyMsg);
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("PSXSES: Unable to exec process: %X\n", Status));
|
|
return FALSE;
|
|
}
|
|
|
|
ASSERT(PORT_MSG_TYPE(ReplyMsg) == LPC_REPLY);
|
|
|
|
//
|
|
// get the session handle for the newly created session.
|
|
//
|
|
SSSessionHandle = ReplyMsg.d.Create.SessionPort;
|
|
|
|
return(NT_SUCCESS(ReplyMsg.Status));
|
|
}
|
|
|
|
VOID
|
|
TerminateSession(
|
|
IN ULONG ExitStatus
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// remove event handler so we don't try to send a message
|
|
// for a dying session.
|
|
//
|
|
SetEventHandlers(FALSE);
|
|
|
|
// Close the named objects: port, section
|
|
|
|
Status = NtClose(PsxSSPortHandle);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
Status = NtClose(PsxSessionDataSectionHandle);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
// notify PSXSS
|
|
|
|
// Cleanup TaskMan stuff
|
|
|
|
_exit(ExitStatus);
|
|
}
|
|
|
|
#define CTRL_CLOSE_EVENT 2
|
|
#define CTRL_LOGOFF_EVENT 5
|
|
#define CTRL_SHUTDOWN_EVENT 6
|
|
|
|
BOOL
|
|
EventHandlerRoutine(
|
|
IN DWORD CtrlType
|
|
)
|
|
{
|
|
int SignalType;
|
|
BOOL r;
|
|
|
|
#if 0
|
|
//
|
|
// These events are now handled -mjb.
|
|
//
|
|
if (CTRL_CLOSE_EVENT == CtrlType) {
|
|
return FALSE; // not handled
|
|
}
|
|
if (CTRL_LOGOFF_EVENT == CtrlType) {
|
|
return FALSE; // not handled
|
|
}
|
|
if (CTRL_SHUTDOWN_EVENT == CtrlType) {
|
|
return FALSE; // not handled
|
|
}
|
|
#endif
|
|
|
|
switch (CtrlType) {
|
|
case CTRL_C_EVENT:
|
|
r = TRUE;
|
|
SignalType = PSX_SIGINT;
|
|
break;
|
|
|
|
case CTRL_BREAK_EVENT:
|
|
r = TRUE;
|
|
SignalType = PSX_SIGQUIT;
|
|
break;
|
|
default:
|
|
r = FALSE;
|
|
SignalType = PSX_SIGKILL;
|
|
break;
|
|
}
|
|
|
|
SignalSession(SignalType);
|
|
|
|
//
|
|
// return TRUE to avoid having the default handler called and
|
|
// the process exited for us (the signal may be ignored or handled,
|
|
// after all).
|
|
//
|
|
// return FALSE to be exited immediately.
|
|
//
|
|
|
|
return r;
|
|
}
|
|
|
|
PCHAR
|
|
ConvertPathVar(PCHAR opath)
|
|
/*++
|
|
pch is malloced with twice the size of opath because the new path is
|
|
going to be longer than opath because of drive letter conversions
|
|
but will ALWAYS be less than strlen(opath)*2 chars.
|
|
--*/
|
|
{
|
|
char *pch, *p, *q;
|
|
|
|
pch = malloc(strlen(opath) * 2);
|
|
if (NULL == pch) {
|
|
return NULL;
|
|
}
|
|
|
|
p = pch;
|
|
while ('\0' != *opath) {
|
|
if (';' == *opath) {
|
|
// semis become colons
|
|
*p++ = ':';
|
|
} else if ('\\' == *opath) {
|
|
// back-slashes become slashes
|
|
*p++ = '/';
|
|
} else if (':' == *(opath + 1)) {
|
|
// "X:" becomes "//X"
|
|
*p++ = '/';
|
|
*p++ = '/';
|
|
|
|
// the drive letters must be uppercase.
|
|
|
|
*p++ = (char)toupper(*opath);
|
|
|
|
++opath; // skip the colon
|
|
} else {
|
|
*p++ = *opath;
|
|
}
|
|
++opath;
|
|
}
|
|
*p = '\0';
|
|
|
|
return pch;
|
|
}
|
|
|
|
PCHAR
|
|
BuildTZEnvVar(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a buffer and formats the TZ environment
|
|
variable for Posix applications. The returned buffer is of the
|
|
form:
|
|
|
|
TZ=stdoffset[dst[offset][,start[/time],end[/time]]]
|
|
|
|
See IEEE Std 1003.1 page 152 for more information.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Pointer to a buffer containing the formatted TZ variable.
|
|
|
|
--*/
|
|
{
|
|
|
|
RTL_TIME_ZONE_INFORMATION TZInfo;
|
|
NTSTATUS Status;
|
|
int iTmp;
|
|
PCHAR pcRet;
|
|
PCHAR pcOffStr1, pcOffStr2;
|
|
PCHAR pcTimeStr1, pcTimeStr2;
|
|
PCHAR pcStandardName, pcDaylightName;
|
|
BOOL fDstSpecified;
|
|
|
|
pcOffStr1 = pcOffStr2 = NULL;
|
|
pcTimeStr1 = pcTimeStr2 = NULL;
|
|
pcStandardName = pcDaylightName = NULL;
|
|
|
|
pcRet = malloc( 60 + 2 * TZNAME_MAX ); // Conservative guess of max
|
|
if( NULL == pcRet ) {
|
|
return( NULL );
|
|
}
|
|
|
|
Status = RtlQueryTimeZoneInformation( &TZInfo );
|
|
if( !( NT_SUCCESS( Status ) ) ) {
|
|
free( pcRet );
|
|
return( NULL );
|
|
}
|
|
|
|
pcStandardName = MakeTZName( TZInfo.StandardName );
|
|
if (NULL == pcStandardName)
|
|
goto out;
|
|
pcOffStr1 = ConvertTimeFromBias( TZInfo.Bias + TZInfo.StandardBias );
|
|
if (NULL == pcOffStr1)
|
|
goto out;
|
|
|
|
//
|
|
// If DaylightStart.Month is 0, the date is not specified.
|
|
//
|
|
|
|
if (TZInfo.DaylightStart.Month != 0) {
|
|
pcDaylightName = MakeTZName( TZInfo.DaylightName );
|
|
pcTimeStr1 = MakeTime( &TZInfo.DaylightStart );
|
|
if (NULL == pcTimeStr1)
|
|
goto out;
|
|
pcTimeStr2 = MakeTime( &TZInfo.StandardStart );
|
|
if (NULL == pcTimeStr2)
|
|
goto out;
|
|
pcOffStr2 = ConvertTimeFromBias( TZInfo.Bias + TZInfo.DaylightBias );
|
|
if (NULL == pcOffStr2)
|
|
goto out;
|
|
fDstSpecified = TRUE;
|
|
} else {
|
|
fDstSpecified = FALSE;
|
|
}
|
|
|
|
if (fDstSpecified) {
|
|
if (TZInfo.DaylightStart.Year == 0) {
|
|
sprintf(pcRet, "TZ=%s%s%s%s,M%d.%d.%d/%s,M%d.%d.%d/%s",
|
|
pcStandardName, pcOffStr1,
|
|
pcDaylightName, pcOffStr2,
|
|
TZInfo.DaylightStart.Month,
|
|
TZInfo.DaylightStart.Day,
|
|
TZInfo.DaylightStart.Weekday,
|
|
pcTimeStr1,
|
|
TZInfo.StandardStart.Month,
|
|
TZInfo.StandardStart.Day,
|
|
TZInfo.StandardStart.Weekday,
|
|
pcTimeStr2);
|
|
} else {
|
|
sprintf(pcRet, "TZ=%s%s%s%s,J%d/%s,J%d/%s",
|
|
pcStandardName, pcOffStr1,
|
|
pcDaylightName, pcOffStr2,
|
|
MakeJulianDate(&TZInfo.DaylightStart),
|
|
pcTimeStr1,
|
|
MakeJulianDate(&TZInfo.StandardStart),
|
|
pcTimeStr2);
|
|
}
|
|
} else {
|
|
sprintf(pcRet, "TZ=%s%s", pcStandardName, pcOffStr1);
|
|
}
|
|
|
|
out:
|
|
MyFree(pcOffStr1);
|
|
MyFree(pcOffStr2);
|
|
MyFree(pcTimeStr1);
|
|
MyFree(pcTimeStr2);
|
|
MyFree(pcStandardName);
|
|
MyFree(pcDaylightName);
|
|
|
|
return pcRet;
|
|
|
|
}
|
|
|
|
PCHAR
|
|
MakeTZName(
|
|
IN WCHAR * FullName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine formats a time zone name string from a full
|
|
name description of a time zone. The return string is the
|
|
first letter of each word in the input string, or TZNAME_MAX
|
|
chars from the full name if there are fewer than three words.
|
|
The returned buffer should be freed by the caller.
|
|
|
|
Arguments:
|
|
|
|
FullName - pointer to the full name description of a time zone
|
|
|
|
Return Value:
|
|
|
|
Pointer to a buffer containing the formatted time zone name.
|
|
|
|
--*/
|
|
{
|
|
|
|
PWCHAR Rover;
|
|
PCHAR cRet;
|
|
PCHAR DestRover;
|
|
BOOL GrabNext;
|
|
int GrabCount;
|
|
|
|
GrabNext = TRUE;
|
|
GrabCount = 0;
|
|
cRet = malloc( TZNAME_MAX+1 );
|
|
if( NULL == cRet ) {
|
|
return( NULL );
|
|
}
|
|
DestRover = cRet;
|
|
|
|
for( Rover = FullName; *Rover != L'\0'; Rover++ ) {
|
|
if( GrabNext ) {
|
|
if( *Rover == L' ' ) {
|
|
continue;
|
|
}
|
|
wctomb( DestRover, *Rover );
|
|
*DestRover++ = (CHAR)toupper( *DestRover );
|
|
if( ( ++GrabCount ) == TZNAME_MAX ) {
|
|
break;
|
|
}
|
|
GrabNext = FALSE;
|
|
} else {
|
|
if( *Rover == L' ' ) {
|
|
GrabNext = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( GrabCount < 3 ) {
|
|
wcstombs( cRet, FullName, TZNAME_MAX );
|
|
cRet[ TZNAME_MAX ] = '\0';
|
|
} else {
|
|
*DestRover = '\0';
|
|
}
|
|
|
|
return( cRet );
|
|
|
|
}
|
|
|
|
int
|
|
MakeJulianDate(
|
|
IN PTIME_FIELDS tm
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calculates Julian day n (1<=n<=365). Leap years are
|
|
not counted so Feb 29 is not representable.
|
|
|
|
Arguments:
|
|
|
|
x - pointer to TIME_FIELDS structure to use as the input date
|
|
|
|
Return Value:
|
|
|
|
Julian day.
|
|
|
|
--*/
|
|
{
|
|
|
|
int Idx;
|
|
int iJDate;
|
|
|
|
for( Idx = 0, iJDate = tm->Day;
|
|
Idx < tm->Month - 1; ++Idx ) {
|
|
iJDate += iDaysInMonths[Idx];
|
|
}
|
|
if( tm->Month == 2 && tm->Day == 29 ) {
|
|
--iJDate;
|
|
}
|
|
return( iJDate );
|
|
|
|
}
|
|
|
|
PCHAR
|
|
ConvertTimeFromBias(
|
|
IN LONG Bias
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a buffer, and formats a time string in the
|
|
buffer as hh:mm:ss or hh:mm or hh:ss where hh may be only 1 digit.
|
|
The time may be negative. The buffer should be freed by the caller.
|
|
|
|
Arguments:
|
|
|
|
x - pointer to a LONG representing the number of minutes of time
|
|
|
|
Return Value:
|
|
|
|
Pointer to a buffer with the time string.
|
|
|
|
--*/
|
|
{
|
|
|
|
PCHAR cRet;
|
|
int iHours, iMins, iSecs;
|
|
|
|
cRet = malloc( 21 );
|
|
if( NULL == cRet ) {
|
|
return( NULL );
|
|
}
|
|
iHours = Bias / 60;
|
|
iMins = iSecs = 0;
|
|
if( Bias % 60 != 0 ) {
|
|
iMins = Bias / ( 60 * 60 );
|
|
if( Bias % ( 60 * 60 ) != 0 ) {
|
|
iSecs = Bias / ( 60 * 60 * 60 );
|
|
}
|
|
}
|
|
|
|
if( iSecs != 0 ) {
|
|
sprintf( cRet, "%d:%02d:%02d", iHours, iMins, iSecs );
|
|
} else if( iMins != 0 ) {
|
|
sprintf( cRet, "%d:%02d", iHours, iMins );
|
|
} else {
|
|
sprintf( cRet, "%d", iHours );
|
|
}
|
|
return( cRet );
|
|
}
|
|
|
|
PCHAR
|
|
MakeTime(
|
|
IN PTIME_FIELDS x
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a buffer, and formats a time string in the
|
|
buffer as hh:mm:ss or hh:mm or hh:ss where hh may be only 1 digit.
|
|
The buffer should be freed by the caller.
|
|
|
|
Arguments:
|
|
|
|
x - pointer to TIME_FIELDS structure to use as the input time
|
|
|
|
Return Value:
|
|
|
|
Pointer to a buffer with the time string.
|
|
|
|
--*/
|
|
{
|
|
|
|
PCHAR cRet;
|
|
|
|
cRet = malloc( 21 );
|
|
if( NULL == cRet ) {
|
|
return( NULL );
|
|
}
|
|
if( x->Second != 0 ) {
|
|
sprintf( cRet, "%d:%02d:%02d", x->Hour, x->Minute, x->Second );
|
|
} else if( x->Minute != 0 ) {
|
|
sprintf( cRet, "%d:%02d", x->Hour, x->Minute );
|
|
} else {
|
|
sprintf( cRet, "%d", x->Hour );
|
|
}
|
|
return( cRet );
|
|
}
|
|
|
|
void
|
|
SignalSession(
|
|
int SignalType
|
|
)
|
|
{
|
|
PSXSESREQUESTMSG RequestMsg;
|
|
PSXSESREQUESTMSG ReplyMsg;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Set Header info
|
|
//
|
|
|
|
PORT_MSG_DATA_LENGTH(RequestMsg) = sizeof(RequestMsg) -
|
|
sizeof(PORT_MESSAGE);
|
|
PORT_MSG_TOTAL_LENGTH(RequestMsg) = sizeof(RequestMsg);
|
|
// BUGBUG: too much
|
|
PORT_MSG_ZERO_INIT(RequestMsg) = 0L;
|
|
|
|
RequestMsg.Request = SesConSignal;
|
|
RequestMsg.Session = SSSessionHandle;
|
|
RequestMsg.UniqueId = GetSessionUniqueId();
|
|
RequestMsg.d.Signal.Type = SignalType;
|
|
|
|
Status = NtRequestWaitReplyPort(PsxSSPortHandle,
|
|
(PPORT_MESSAGE)&RequestMsg, (PPORT_MESSAGE)&ReplyMsg);
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("PSXSES: Unable to send signal: %X\n", Status));
|
|
ExitProcess(1);
|
|
}
|
|
if (LPC_REPLY != PORT_MSG_TYPE(ReplyMsg)) {
|
|
KdPrint(("PSXSES: unexpected MSG_TYPE %d\n", PORT_MSG_TYPE(ReplyMsg)));
|
|
ExitProcess(1);
|
|
}
|
|
}
|