minix/commands/ibm/readclock.c
2005-04-21 14:53:53 +00:00

211 lines
5.6 KiB
C
Executable file

/* setime - set the system time from the real time clock
Authors: T. Holm & E. Froese
Adapted by: Jorrit .N. Herder */
/************************************************************************/
/* Readclock was updated for security reasons: openeing /dev/mem no */
/* longer automatically grants I/O privileges to the calling process */
/* so that the CMOS' clock could not be read from this program. The */
/* new approach is to rely on the FS to do the CMOS I/O, via the new */
/* system call CMOSTIME (which only reads the current clock value and */
/* cannot update the CMOS clock). */
/* The original readclock.c is still available under backup.c. */
/************************************************************************/
/* */
/* readclock.c */
/* */
/* Read the clock value from the 64 byte CMOS RAM */
/* area, then set system time. */
/* */
/* If the machine ID byte is 0xFC or 0xF8, the device */
/* /dev/mem exists and can be opened for reading, */
/* and no errors in the CMOS RAM are reported by the */
/* RTC, then the time is read from the clock RAM */
/* area maintained by the RTC. */
/* */
/* The clock RAM values are decoded and fed to mktime */
/* to make a time_t value, then stime(2) is called. */
/* */
/* This fails if: */
/* */
/* If the machine ID does not match 0xFC or 0xF8 (no */
/* error message.) */
/* */
/* If the machine ID is 0xFC or 0xF8 and /dev/mem */
/* is missing, or cannot be accessed. */
/* */
/* If the RTC reports errors in the CMOS RAM. */
/* */
/************************************************************************/
/* origination 1987-Dec-29 efth */
/* robustness 1990-Oct-06 C. Sylvain */
/* incorp. B. Evans ideas 1991-Jul-06 C. Sylvain */
/* set time & calibrate 1992-Dec-17 Kees J. Bot */
/* clock timezone 1993-Oct-10 Kees J. Bot */
/* set CMOS clock 1994-Jun-12 Kees J. Bot */
/* removed set CMOS 2004-Sep-06 Jorrit N. Herder */
/************************************************************************/
#include <minix/callnr.h>
#include <minix/config.h>
#include <minix/type.h>
#include <minix/const.h>
#include <minix/com.h>
#include <minix/syslib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <ibm/portio.h>
#include <ibm/cmos.h>
#include <sys/svrctl.h>
#define MAX_RETRIES 1
int nflag = 0; /* Tell what, but don't do it. */
int y2kflag = 0; /* Interpret 1980 as 2000 for clock with Y2K bug. */
char clocktz[128]; /* Timezone of the clock. */
#define MACH_ID_ADDR 0xFFFFE /* BIOS Machine ID at FFFF:000E */
#define PC_AT 0xFC /* Machine ID byte for PC/AT,
PC/XT286, and PS/2 Models 50, 60 */
#define PS_386 0xF8 /* Machine ID byte for PS/2 Model 80 */
/* Manufacturers usually use the ID value of the IBM model they emulate.
* However some manufacturers, notably HP and COMPAQ, have had different
* ideas in the past.
*
* Machine ID byte information source:
* _The Programmer's PC Sourcebook_ by Thom Hogan,
* published by Microsoft Press
*/
void errmsg(char *s);
int bcd_to_dec(int n);
int dec_to_bcd(int n);
void usage(void);
PUBLIC int main(int argc, char **argv)
{
struct tm time1;
struct tm time2;
struct tm tmnow;
char date[64];
time_t now, rtc;
int i, s, mem;
unsigned char mach_id, cmos_state;
struct sysgetenv sysgetenv;
message m;
/* Process options. */
while (argc > 1) {
char *p = *++argv;
if (*p++ != '-') usage();
while (*p != 0) {
switch (*p++) {
case 'n': nflag = 1; break;
case '2': y2kflag = 1; break;
default: usage();
}
}
argc--;
}
/* The hardware clock may run in a different time zone, likely GMT or
* winter time. Select that time zone.
*/
strcpy(clocktz, "TZ=");
sysgetenv.key = "TZ";
sysgetenv.keylen = 2+1;
sysgetenv.val = clocktz+3;
sysgetenv.vallen = sizeof(clocktz)-3;
if (svrctl(SYSGETENV, &sysgetenv) == 0) {
putenv(clocktz);
tzset();
}
/* Read the CMOS real time clock. */
for (i = 0; i < MAX_RETRIES; i++) {
/* sleep, unless first iteration */
if (i > 0) sleep(5);
/* get_time(&time1); */
m.m_type = CMOSTIME;
m.ADDRESS = (void *) &time1;
m.REQUEST = y2kflag;
if (0 != (s=sendrec(FS_PROC_NR, &m))) {
fprintf(stderr, "Couldn't get CMOS time from FS: %d.\n",s);
exit(1);
}
now = time(NULL);
time1.tm_isdst = -1; /* Do timezone calculations. */
time2 = time1;
rtc= mktime(&time1); /* Transform to a time_t. */
if (rtc != -1) break;
fprintf(stderr,
"readclock: Invalid time read from CMOS RTC: %d-%02d-%02d %02d:%02d:%02d\n",
time2.tm_year+1900, time2.tm_mon+1, time2.tm_mday,
time2.tm_hour, time2.tm_min, time2.tm_sec);
}
if (i >= MAX_RETRIES) exit(1);
/* Set system time. */
if (nflag) {
printf("stime(%lu)\n", (unsigned long) rtc);
} else {
if (stime(&rtc) < 0) {
errmsg( "Not allowed to set time." );
exit(1);
}
}
tmnow = *localtime(&rtc);
if (strftime(date, sizeof(date),
"%a %b %d %H:%M:%S %Z %Y", &tmnow) != 0) {
if (date[8] == '0') date[8]= ' ';
printf("%s [CMOS read via FS, see command/ibm/readclock.c]\n", date);
}
exit(0);
}
void errmsg(char *s)
{
static char *prompt = "settime: ";
fprintf(stderr, "%s%s\n", prompt, s);
prompt = "";
}
int bcd_to_dec(int n)
{
return ((n >> 4) & 0x0F) * 10 + (n & 0x0F);
}
int dec_to_bcd(int n)
{
return ((n / 10) << 4) | (n % 10);
}
void usage(void)
{
fprintf(stderr, "Usage: settime [-n2]\n");
exit(1);
}