/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    conio.c

Abstract:

    This module implements server performed console io

Author:

    Matthew Bradburn (mattbr) 18-Dec-1992

Revision History:

--*/


#include <sys/stat.h>
#include "psxsrv.h"
#define NTPSX_ONLY
#include "sesport.h"

BOOLEAN
ConOpen(
    IN PPSX_PROCESS p,
    IN PFILEDESCRIPTOR Fd,
    IN OUT PPSX_API_MSG m
    );

BOOLEAN
ConRead(
    IN PPSX_PROCESS p,
    IN OUT PPSX_API_MSG m,
    IN PFILEDESCRIPTOR Fd
    );

BOOLEAN
ConWrite (
    IN PPSX_PROCESS p,
    IN OUT PPSX_API_MSG m,
    IN PFILEDESCRIPTOR Fd
    );

BOOLEAN
ConDup (
    IN PPSX_PROCESS p,
    IN OUT PPSX_API_MSG m,
    IN PFILEDESCRIPTOR Fd,
    IN PFILEDESCRIPTOR FdDup
    );

BOOLEAN
ConLseek (
    IN PPSX_PROCESS p,
    IN OUT PPSX_API_MSG m,
    IN PFILEDESCRIPTOR Fd
    );

BOOLEAN
ConStat (
    IN PIONODE IoNode,
    IN HANDLE FileHandle,
    OUT struct stat *StatBuf,
    OUT NTSTATUS *Status
    );

VOID
ConLastClose (
    IN PPSX_PROCESS p,
    IN PSYSTEMOPENFILE SystemOpenFile
    )

{
    NTSTATUS st;
    SCREQUESTMSG Request;

#if 0
//
// We don't do this anymore, because if the session manager has
// died, we'll never get a reply, and we'll hang while holding some
// lock or another.
//

    Request.Request = ConRequest;
    Request.d.Con.Request = ScCloseFile;
    Request.d.Con.d.IoBuf.Handle = SystemOpenFile->NtIoHandle;

    PORT_MSG_TOTAL_LENGTH(Request) = sizeof(SCREQUESTMSG);
    PORT_MSG_DATA_LENGTH(Request) = sizeof(SCREQUESTMSG) -
        sizeof(PORT_MESSAGE);
    PORT_MSG_ZERO_INIT(Request) = 0;

    RtlEnterCriticalSection(&SystemOpenFile->Terminal->Lock);

    st = NtRequestWaitReplyPort(
        SystemOpenFile->Terminal->ConsolePort,
        (PPORT_MESSAGE)&Request, (PPORT_MESSAGE)&Request);
    // ASSERT(NT_SUCCESS(st));

    RtlLeaveCriticalSection(&SystemOpenFile->Terminal->Lock);
#endif

    SystemOpenFile->NtIoHandle = NULL;
}


PSXIO_VECTORS ConVectors = {
    ConOpen,
    NULL,
    NULL,
    ConLastClose,
    NULL,
    ConRead,
    ConWrite,
    ConDup,
    ConLseek,
    ConStat
    };


BOOLEAN
ConOpen(
    IN PPSX_PROCESS p,
    IN PFILEDESCRIPTOR Fd,
    IN OUT PPSX_API_MSG m
    )
/*++

Routine Description:

    This routine is called when the path /dev/tty is opened.  Its
    function is to set up the stuff for stat that doesn't exist because
    there isn't really such a file.

Arguments:

    p - Supplies the address of the process making the call.

    m - Supplies the address of the message associated with the request.

    Fd - supplies the address of the file descriptor being written.

Return Value:

    FALSE			- Failure.
    TRUE			- Success.

--*/
{
    PPSX_OPEN_MSG args;
    LARGE_INTEGER time;
    ULONG posix_time;

    args = &m->u.Open;

    NtQuerySystemTime(&time);
    if (!RtlTimeToSecondsSince1970(&time, &posix_time)) {
        posix_time = 0;
    }

    RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
    Fd->SystemOpenFileDesc->IoNode->ModifyDataTime = posix_time;
    Fd->SystemOpenFileDesc->IoNode->ModifyIoNodeTime = posix_time;
    Fd->SystemOpenFileDesc->IoNode->AccessDataTime = posix_time;
    RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);

    return TRUE;
}


BOOLEAN
ConWrite (
    IN PPSX_PROCESS p,
    IN OUT PPSX_API_MSG m,
    IN PFILEDESCRIPTOR Fd
    )

/*++

Routine Description:

    This procedure implements write when the device being written
    is a file.

Arguments:

    p - Supplies the address of the process making the call.

    m - Supplies the address of the message associated with the request.

    Fd - supplies the address of the file descriptor being written.

Return Value:

--*/

{
    PPSX_WRITE_MSG args;
    NTSTATUS st;
    IO_STATUS_BLOCK Iosb;
    LARGE_INTEGER ByteOffset;
    ULONG IoBufferSize;
    FILE_FS_SIZE_INFORMATION SizeInfo;
    ULONG Avail;
    PVOID IoBuffer = NULL;
    LARGE_INTEGER Time;
    ULONG PosixTime;

    SCREQUESTMSG Request;

    args = &m->u.Write;

    args->Command = IO_COMMAND_DO_CONSIO;

    //
    // We need to tell the dll whether to do non-blocking io
    // or not.
    //

    if (Fd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
        args->Scratch1 = O_NONBLOCK;
    } else {
        args->Scratch1 = 0;
    }

    //
    // Replace the given file descriptor with the one that should
    // really be used to do the io to posix.exe.  They might be
    // different if the one passed in was created by duping 0, 1,
    // or 2.
    //

    args->FileDes = HandleToUlong(Fd->SystemOpenFileDesc->NtIoHandle);

    //
    // Update st_mtime and st_ctime.
    //

    NtQuerySystemTime(&Time);
    if (!RtlTimeToSecondsSince1970(&Time, &PosixTime)) {
        PosixTime = 0;
    }

    RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);

    Fd->SystemOpenFileDesc->IoNode->ModifyDataTime = PosixTime;
    Fd->SystemOpenFileDesc->IoNode->ModifyIoNodeTime = PosixTime;

    RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);

    return TRUE;
}



BOOLEAN
ConRead (
    IN PPSX_PROCESS p,
    IN OUT PPSX_API_MSG m,
    IN PFILEDESCRIPTOR Fd
    )

/*++

Routine Description:

    This procedure implements read when the device being read
    is a file.

Arguments:

    p - Supplies the address of the process making the call.

    m - Supplies the address of the message associated with the request.

    Fd - supplies the address of the file descriptor being read.

Return Value:

--*/

{
    PPSX_READ_MSG args;
    NTSTATUS st;
    IO_STATUS_BLOCK Iosb;
    LARGE_INTEGER ByteOffset;
    ULONG IoBufferSize;
    PVOID IoBuffer = NULL;
    LARGE_INTEGER Time;
    ULONG PosixTime;
    PSIGDB SigDb;

    SCREQUESTMSG Request;

    args = &m->u.Read;

    //
    // 1003.1-90 (6.4.1.4): EIO ... The implementation supports job
    // control, the process is in a background process group and is
    // attempting to read from its controlling terminal, and either
    // the process is ignoring or blocking the SIGTTIN signal or
    // the process group of the process is orphaned.
    //

    SigDb = &p->SignalDataBase;

    if (NULL != p->PsxSession->Terminal &&
        p->PsxSession->Terminal->ForegroundProcessGroup !=
            p->ProcessGroupId &&
        p->PsxSession->Terminal == Fd->SystemOpenFileDesc->Terminal &&
        ((SigDb->SignalDisposition[SIGTTIN-1].sa_handler == SIG_IGN ||
            SIGISMEMBER(&SigDb->BlockedSignalMask, SIGTTIN)) ||
            IsGroupOrphaned(p->ProcessGroupId))) {

        m->Error = EIO;
        return TRUE;
    }
    args->Command = IO_COMMAND_DO_CONSIO;

    //
    // We need to tell the dll whether to do non-blocking io
    // or not.
    //

    if (Fd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
        args->Scratch1 = O_NONBLOCK;
    } else {
        args->Scratch1 = 0;
    }

    //
    // Replace the given file descriptor with the one that should
    // really be used to do the io to posix.exe.  They might be
    // different if the one passed in was created by duping 0, 1,
    // or 2.
    //

    args->FileDes = HandleToUlong(Fd->SystemOpenFileDesc->NtIoHandle);

    NtQuerySystemTime(&Time);
    if (!RtlTimeToSecondsSince1970(&Time, &PosixTime)) {
        PosixTime = 0;
    }

    //
    // Update st_atime.
    //

    RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
    Fd->SystemOpenFileDesc->IoNode->AccessDataTime = PosixTime;
    RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);

    return TRUE;
}


BOOLEAN
ConDup (
    IN PPSX_PROCESS p,
    IN OUT PPSX_API_MSG m,
    IN PFILEDESCRIPTOR Fd,
    IN PFILEDESCRIPTOR FdDup
    )

/*++

Routine Description:

    This procedure implements dup and dup2

Arguments:

    p - Supplies the address of the process making the call.

    m - Supplies the address of the message associated with the request.

    Fd - supplies the address of the file descriptor being duplicated.

    FdDup - supplies the address of the duplicate file descriptor.

Return Value:

    ???
--*/

{
    PPSX_DUP_MSG args;

    args = &m->u.Dup;

    //
    //  Copy contents of source file descriptor slot into new descriptor
    //  Note that FD_CLOEXEC must be CLEAR on FdDup.
    //

    *FdDup = *Fd;
    FdDup->Flags &= ~PSX_FD_CLOSE_ON_EXEC;

    //
    // Increment reference count associated with the SystemOpenFile
    // descriptor for this file.
    //

    RtlEnterCriticalSection(&SystemOpenFileLock);

    Fd->SystemOpenFileDesc->HandleCount++;

    RtlLeaveCriticalSection(&SystemOpenFileLock);

    return TRUE;
}


BOOLEAN
ConLseek (
    IN PPSX_PROCESS p,
    IN OUT PPSX_API_MSG m,
    IN PFILEDESCRIPTOR Fd
    )

/*++

Routine Description:

    This procedure implements lseek when the device being seeked on
    is a file.

Arguments:

    p - Supplies the address of the process making the call.

    m - Supplies the address of the message associated with the request.

    Fd - supplies the address of the file descriptor being seekd

Return Value:

    ???

--*/

{
    //
    // Can't seek on a console.
    //

    m->Error = ESPIPE;
    return TRUE;
}


BOOLEAN
ConStat (
    IN PIONODE IoNode,
    IN HANDLE FileHandle,
    OUT struct stat *StatBuf,
    OUT NTSTATUS *pStatus
    )
/*++

Routine Description:

    This procedure implements stat when the device being read
    is a file.

Arguments:

    IoNode - supplies a pointer to the ionode of the file for which stat is
    requested. NULL if no active Ionode entry.

    FileHandle - supplies the Nt file handle of the file .

    StatBuf - Supplies the address of the statbuf portion of the message
    associated with the request.

Return Value:

    TRUE

--*/
{
    ULONG PosixTime;

    StatBuf->st_mode = IoNode->Mode;
    StatBuf->st_ino = (ino_t)IoNode->FileSerialNumber;
    StatBuf->st_dev = IoNode->DeviceSerialNumber;
    StatBuf->st_uid = IoNode->OwnerId;
    StatBuf->st_gid = IoNode->GroupId;
    StatBuf->st_size = 0;

    StatBuf->st_atime = IoNode->AccessDataTime;
    StatBuf->st_mtime = IoNode->ModifyDataTime;
    StatBuf->st_ctime = IoNode->ModifyIoNodeTime;

    StatBuf->st_nlink = 1;

    return TRUE;
}