/*****************************************************************/
/**          Microsoft LAN Manager          **/
/**        Copyright(c) Microsoft Corp., 1988-1991      **/
/*****************************************************************/

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <setjmp.h>

#include <time.h>

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>

ULONG Iterations;


struct {
    ULONG   Flags;
    PUCHAR  String;
} ActFlags[] = {
    POWER_ACTION_QUERY_ALLOWED,     "QueryApps",
    POWER_ACTION_UI_ALLOWED,        "UIAllowed",
    POWER_ACTION_OVERRIDE_APPS,     "OverrideApps",
    POWER_ACTION_DISABLE_WAKES,     "DisableWakes",
    POWER_ACTION_CRITICAL,          "Critical",
    0, NULL
    };

PUCHAR
ActionS(
    IN POWER_ACTION Act
    )
{
    static  UCHAR   line[50];
            PUCHAR  p;

    switch (Act) {
        case PowerActionNone:          p = "None";          break;
        case PowerActionSleep:         p = "Sleep";         break;
        case PowerActionShutdown:      p = "Shutdown";      break;
        case PowerActionHibernate:     p = "Hibernate";     break;
        case PowerActionShutdownReset: p = "ShutdownReset"; break;
        case PowerActionShutdownOff:   p = "ShutdownOff";   break;
        default:
            sprintf(line, "Unknown action %x", Act);
            p = line;
            break;
    }

    return p;
}

PUCHAR
SysPower(
    IN SYSTEM_POWER_STATE   State
    )
{
    static  UCHAR   line[50];
            PUCHAR  p;

    switch (State) {
        case PowerSystemUnspecified:    p = "Unspecified";      break;
        case PowerSystemWorking:        p = "Working";          break;
        case PowerSystemSleeping1:      p = "S1";               break;
        case PowerSystemSleeping2:      p = "S2";               break;
        case PowerSystemSleeping3:      p = "S3";               break;
        case PowerSystemHibernate:      p = "S4 - hibernate";   break;
        case PowerSystemShutdown:       p = "Shutdown";         break;
        default:
            sprintf(line, "Unknown power state %x", State);
            p = line;
            break;
    }

    return p;
}


PUCHAR
Action (
    IN PBOOLEAN CapFlag,
    IN PPOWER_ACTION_POLICY Act
    )
{
    static UCHAR text[200];
    PUCHAR  p;
    UCHAR   c;
    ULONG   i;

    p = text;

    if (CapFlag && !*CapFlag) {
        p += sprintf(p, "Disabled ");
    }

    p += sprintf (p, "%s", ActionS(Act->Action));
    if (Act->Action != PowerActionNone  &&  Act->Flags) {
        c = '(';
        for (i=0; ActFlags[i].Flags; i++) {
            if (Act->Flags & ActFlags[i].Flags) {
                p += sprintf (p, "%c%s", c, ActFlags[i].String);
                c  = '|';
            }
        }
        p += sprintf (p, ")");
    }

    if (Act->EventCode) {
        p += sprintf (p, "-Code=%x", Act->EventCode);
    }

    return text;
}

VOID
SetTimerTime (
    IN HANDLE   h,
    IN PUCHAR   Text,
    IN ULONG    DueTimeInMin
    )
{
    LARGE_INTEGER       SystemTime;
    LARGE_INTEGER       DueTime;
    BOOL                Status;
    SYSTEMTIME          TimeFields;
    UCHAR               s[200];

    NtQuerySystemTime (&SystemTime);
    GetSystemTime (&TimeFields);

    sprintf (s, "%d. Current time is:  %d:%d:%d, ",
        Iterations,
        TimeFields.wHour,
        TimeFields.wMinute,
        TimeFields.wSecond
        );
    printf(s);
    DbgPrint("SHD: %s", s);

    TimeFields.wMinute += (USHORT) DueTimeInMin;
    while (TimeFields.wMinute > 59) {

        TimeFields.wMinute -= 60;
        TimeFields.wHour += 1;

    }

    sprintf (s, "timer set for %d:%d:%d (%d min)  %s",
        TimeFields.wHour,
        TimeFields.wMinute,
        TimeFields.wSecond,
        DueTimeInMin,
        Text
        );
    printf(s);
    DbgPrint(s);

    //
    // Set timer as relative
    //

    DueTime.QuadPart = (ULONGLONG) -600000000L * DueTimeInMin;
    Status = SetWaitableTimer (
                h,
                &DueTime,
                0,
                NULL,
                NULL,
                TRUE
                );

    if (!Status) {

        printf ("\nSetWaitableTimer failed with %x\n", GetLastError());
        DbgPrint ("\nSetWaitableTimer failed with %x\n", GetLastError());
        exit (1);

    }
}


VOID __cdecl
main (argc, argv)
int     argc;
char    *argv[];
{
    POWER_ACTION_POLICY Act;
    SYSTEM_POWER_STATE  MinSystemState;
    NTSTATUS            Status;
    PUCHAR              p;
    BOOLEAN             Asynchronous;
    BOOLEAN             MaxLoop;
    BOOLEAN             SleepLoop;
    HANDLE              hToken, SleepTimer;
    ULONG               DelayTime;
    ULONG               MaxCount;
    ULONG               Temp;
    ULONG               WakeTime;
    TOKEN_PRIVILEGES    tkp;

    OpenProcessToken (
        GetCurrentProcess(),
        TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
        &hToken
        );

    LookupPrivilegeValue (
        NULL,
        SE_SHUTDOWN_NAME,
        &tkp.Privileges[0].Luid
        );

    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    AdjustTokenPrivileges (
        hToken,
        FALSE,
        &tkp,
        0,
        NULL,
        0
    );


    RtlZeroMemory(&Act, sizeof(Act));
    MinSystemState = PowerSystemSleeping1;      
    Asynchronous = TRUE;
    SleepLoop = FALSE;
    DelayTime = 1;
    WakeTime = 2;
    Iterations = 0;

    if (argc == 1) {
        printf ("shd shutdown|off|reset|hiber|sleep|doze [sync qapp ui oapp diswake critical] [loop] [maxloop <Int>] [waitTime <InMinutes>] [delayTime <InMinutes>]\n");
        exit (1);
    }

    while (argc) {
        argc--;
        p = *argv;
        argv += 1;

        if (_stricmp(p, "shutdown") == 0)     Act.Action = PowerActionShutdown;
        if (_stricmp(p, "off") == 0)          Act.Action = PowerActionShutdownOff;
        if (_stricmp(p, "reset") == 0)        Act.Action = PowerActionShutdownReset;
        if (_stricmp(p, "hiber") == 0)        Act.Action = PowerActionHibernate;
        if (_stricmp(p, "sleep") == 0)        Act.Action = PowerActionSleep;


        if (_stricmp(p, "qapp") == 0)         Act.Flags |= POWER_ACTION_QUERY_ALLOWED;
        if (_stricmp(p, "ui"  ) == 0)         Act.Flags |= POWER_ACTION_UI_ALLOWED;
        if (_stricmp(p, "oapp") == 0)         Act.Flags |= POWER_ACTION_OVERRIDE_APPS;
        if (_stricmp(p, "diswake") == 0)      Act.Flags |= POWER_ACTION_DISABLE_WAKES;
        if (_stricmp(p, "critical") == 0)     Act.Flags |= POWER_ACTION_CRITICAL;
        if (_stricmp(p, "sync") == 0)         Asynchronous = FALSE;

        if (_stricmp(p, "loop") == 0)         SleepLoop = TRUE;
        if (_stricmp(p, "maxloop") == 0) {

            if (!argc) {

                printf("Must specify an Maximum number with MAXLOOP\n");
                exit(1);

            }
            argc--;
            p = *argv;
            argv += 1;

            Temp = atol(p);
            if (Temp) {

                MaxCount = Temp;
                SleepLoop = TRUE;
                MaxLoop = TRUE;

            }

        }
        if (_stricmp(p, "waittime") == 0) {

            if (!argc) {

                printf("Must Specify an TimeInMinutes number with WAITTIME\n");
                exit(1);

            }
            argc--;
            p = *argv;
            argv += 1;

            Temp = atol(p);
            if (Temp) {

                WakeTime = Temp;

            }

        }
        if (_stricmp(p, "delaytime") == 0) {

            if (!argc) {

                printf("Must Specify a TimeInMinutes number with DELAYTIME\n");
                exit(1);

            }
            argc--;
            p = *argv;
            argv += 1;

            Temp = atol(p);
            if (Temp) {

                DelayTime = Temp;

            }

        }

    }

    if (!SleepLoop) {
        printf ("Calling NtInitiatePowerAction %s\n",
                Asynchronous ? "asynchronous" : "synchronous"
                );
        printf ("System Action........: %s\n",  Action(NULL, &Act));
        printf ("Min system state.....: %s\n",  SysPower(MinSystemState));

        DbgPrint ("SHD: Calling NtInitiatePowerAction %s\n",
                  Asynchronous ? "asynchronous" : "synchronous"
                  );
        DbgPrint ("SHD: System Action........: %s\n",  Action(NULL, &Act));
        DbgPrint ("SHD: Min system state.....: %s\n",  SysPower(MinSystemState));

        Status = NtInitiatePowerAction (
                    Act.Action,
                    MinSystemState,
                    Act.Flags,
                    Asynchronous
                    );
        goto exit_main;

    }


    SleepTimer = CreateWaitableTimer (
                    NULL,
                    TRUE,
                    "SleepLoopTimer"
                    );

    //
    // Remember that this is iteration #0. Do the boundary condition test
    // here since we don't want to do something that the user didn't want
    // us to do, God, forbid.
    //
    Iterations = 0;
    if (MaxLoop && Iterations >= MaxCount) {

        goto exit_main;

    }

    //
    // Use a while loop here, since we don't actually make use of the
    // check unless we have the MaxLoop set
    //
    while (1) {

        //
        // Set wake timer
        //
        SetTimerTime (SleepTimer, "Wake Time", WakeTime);

        //
        // Hibernate the system
        //
        printf (" %s\n", Action(NULL, &Act));
        DbgPrint (" %s\n", Action(NULL, &Act));
        Status = NtInitiatePowerAction (
                    Act.Action,
                    MinSystemState,
                    Act.Flags,
                    FALSE
                    );

        if (!NT_SUCCESS(Status)) {

            printf ("NtInitiatePowerAction failure: %x\n", Status);
            DbgPrint ("SHD: NtInitiazePowerAction failure: %x\n", Status);
            exit (1);

        }

        //
        // Wait for wake timer
        //
        Status = WaitForSingleObject (SleepTimer, -1);
        if (!NT_SUCCESS(Status)) {

            printf ("Wake time wait failed: %x\n", Status);
            DbgPrint ("SHD: Wake time wait failed: %x\n", Status);
            exit (1);

        }

        //
        // Number of times we've been sucessfull
        //
        Iterations += 1;

        //
        // Have we exceeded the number of iterations?
        //
        if (MaxLoop && Iterations >= MaxCount) {

            break;

        }

        //
        // Delay between each loop
        //
        SetTimerTime (SleepTimer, "Delay\n", DelayTime);
        Status = WaitForSingleObject (SleepTimer, -1);
        if (!NT_SUCCESS(Status)) {

            printf ("Delay wait failed: %x\n", Status);
            DbgPrint ("SHD: Delay wait failed: %x\n", Status);
            exit (1);

        }
    }

exit_main:
    printf ("Done. Status %x\n", Status);
    DbgPrint ("SHD: Done. Status %x\n", Status);
    exit (0);

}