/* $Id: uptime.c,v 1.10 2002/08/22 19:06:58 nlaredo Exp $
 * ----------------------------------------------------------------------- *
 * This program 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 program 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 program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * ----------------------------------------------------------------------- *
 * uptime.c -- Nathan Laredo
 */

#include <windows.h>
#include <stdio.h>

/* define some C99 types for windows */
typedef unsigned __int32 uint32_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int64 uint64_t;
typedef unsigned __int8 uint8_t;
typedef __int32 int32_t;
typedef __int16 int16_t;
typedef __int64 int64_t;
typedef __int8 int8_t;

static char msgstr[4096];
static int users = 0;

#define KEY_SYSTEM	L"2"
#define IDX_UPTIME	674
#define IDX_PROCQUEUE	44

static void get_boottime(ULARGE_INTEGER *boottime,
			 ULARGE_INTEGER *nowtime,
			 uint32_t *procqueue)
{
	BYTE pbuf[4096];
	PPERF_DATA_BLOCK ppdb = (PPERF_DATA_BLOCK) pbuf;
	DWORD psz = sizeof(pbuf);
	PPERF_OBJECT_TYPE ppot;
	PPERF_COUNTER_DEFINITION ppcd;
	PPERF_COUNTER_BLOCK ppcb;
	DWORD i;

	memset(pbuf, 0, psz);
	nowtime->QuadPart = boottime->QuadPart = 0;	/* 0 = error */

	/* get current 64-bit boottime in 100ns units */
	RegQueryValueExW(HKEY_PERFORMANCE_DATA, KEY_SYSTEM,
		NULL, NULL, pbuf, &psz);
	RegCloseKey(HKEY_PERFORMANCE_DATA);

	if(memcmp(pbuf, L"PERF", 8)) {
		/* PERF_DATA_BLOCK signature not present */
		return;
	}

	/* parse returned performance data */
	ppot = (PPERF_OBJECT_TYPE) &pbuf[ppdb->HeaderLength];

	ppcd = (PPERF_COUNTER_DEFINITION)
		&pbuf[ppdb->HeaderLength + ppot->HeaderLength];

	ppcb = (PPERF_COUNTER_BLOCK)
		&(((uint8_t *)ppot)[ppot->DefinitionLength]);

	nowtime->QuadPart = ppdb->PerfTime100nSec.QuadPart;

	/* get uptime and processor queue length */
	for (i = 0; i < ppot->NumCounters; i++) {
		if (ppcd->CounterNameTitleIndex == IDX_UPTIME) {
			boottime->QuadPart = (int64_t) *(uint64_t *)
				&(((uint8_t *)ppcb)[ppcd->CounterOffset]);
		}
		if (ppcd->CounterNameTitleIndex == IDX_PROCQUEUE) {
			*procqueue = (int32_t) *(uint32_t *)
				&(((uint8_t *)ppcb)[ppcd->CounterOffset]);
		}
		ppcd++;
	}
}

static BOOL CALLBACK ewsproc(LPTSTR lpszWindowStation, LPARAM lParam)
{
	users++;
	return TRUE;
}

static void uptime(void)
{
	ULARGE_INTEGER bt, nt, ut;
	uint32_t procqueue, days;
	SYSTEMTIME bst, nst, ust;
	TCHAR btstr[128], ntstr[128], bdstr[128];

	get_boottime(&bt, &nt, &procqueue);
	ut.QuadPart = nt.QuadPart - bt.QuadPart;
	FileTimeToSystemTime((FILETIME *) &bt, &bst);
	FileTimeToSystemTime((FILETIME *) &nt, &nst);
	FileTimeToSystemTime((FILETIME *) &ut, &ust);
	SystemTimeToTzSpecificLocalTime(NULL, &bst, &bst);
	SystemTimeToTzSpecificLocalTime(NULL, &nst, &nst);
	GetTimeFormat(LOCALE_USER_DEFAULT, LOCALE_USE_CP_ACP, &bst, NULL,
		      btstr, sizeof(btstr));
	GetTimeFormat(LOCALE_USER_DEFAULT, LOCALE_USE_CP_ACP, &nst, NULL,
		      ntstr, sizeof(ntstr));
	GetDateFormat(LOCALE_USER_DEFAULT, LOCALE_USE_CP_ACP,
		      &bst, NULL, bdstr, sizeof(bdstr));
	sprintf(msgstr, " %s  up ", ntstr);
	ut.QuadPart /= 864000000000;

	days = (uint32_t) ut.QuadPart;
	if (days > 1) {
		sprintf(&msgstr[strlen(msgstr)], "%d days, ", days);
	} else if (days == 1) {
		sprintf(&msgstr[strlen(msgstr)], "1 day, ");
	}
	sprintf(&msgstr[strlen(msgstr)], "%02d:%02d:%02d, ",
		ust.wHour, ust.wMinute, ust.wSecond);

	sprintf(&msgstr[strlen(msgstr)], "%d user%s, ", users,
		users == 1 ? "" : "s");
	
	sprintf(&msgstr[strlen(msgstr)], "boot %s %s, "
		"load: %d", bdstr, btstr, procqueue);
	printf(msgstr);
}

int main(int argc, char **argv)
{
	int count = atoi(argv[argc - 1]);

	EnumWindowStations(ewsproc, 0);

	uptime();
	printf("\r\n");
	
	for (; count > 0; count--) {
		Sleep(1000);
		printf("\r");
		uptime();
		if (count == 1) {
			printf("\r\n");
		}
	}
	return 0;
}
/* EOF */
