/*
* pcfdate - get the time from a pcfclock device and set the system time
*
* Copyright (C) 1999-2001 Andreas Voegele
*
* 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 in the file COPYING; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA.
*/
#if HAVE_CONFIG_H
# include "config.h"
#endif
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <locale.h>
char *program_name;
int quiet;
int set_time;
int utc;
int verbose;
/* The following offset is added to the system time. The offset is
(69*2500) since the radio clock transmits 69 bits with a period of
2500 microseconds per bit. */
#define OFFSET (69*2500)
#define DATA_ERROR 1
#define READ_ERROR 2
#define OPEN_ERROR 3
#define OUT_OF_MEMORY_ERROR 4
void
print_error(const char *format, ...)
{
va_list ap;
va_start(ap, format);
fprintf(stderr, "%s: ", program_name);
vfprintf(stderr, format, ap);
va_end(ap);
}
int
read_and_set_time(const char *device)
{
int fd;
char timecode[18], *tz;
ssize_t n;
struct tm tm;
time_t newtime;
fd = open(device, O_RDONLY | O_NONBLOCK);
if (fd == -1) {
return OPEN_ERROR;
}
if ((n = read(fd, timecode, 18)) == -1) {
print_error("%s: %s\n", device, strerror(errno));
close(fd);
return READ_ERROR;
}
close(fd);
if (n != 18 || timecode[0] != 9) {
print_error("%s: %s\n", device, strerror(EIO));
return DATA_ERROR;
}
tm.tm_sec = timecode[3] * 10 + timecode[2];
tm.tm_min = timecode[5] * 10 + timecode[4];
tm.tm_hour = timecode[7] * 10 + timecode[6];
tm.tm_mday = timecode[11] * 10 + timecode[10];
tm.tm_mon = timecode[13] * 10 + timecode[12] - 1;
tm.tm_year = timecode[15] * 10 + timecode[14];
if (tm.tm_year < 99)
tm.tm_year += 100;
tm.tm_isdst = (timecode[8] & 1) ? 1 : (timecode[8] & 2) ? 0 : -1;
/* Set the time zone temporarily to CET. */
tz = getenv("TZ");
if (tz) {
if (!(tz = strdup(tz))) {
print_error("%s\n", strerror(ENOMEM));
return OUT_OF_MEMORY_ERROR;
}
}
if (putenv("TZ=CET") == -1) {
print_error("%s\n", strerror(errno));
free(tz);
return OUT_OF_MEMORY_ERROR;
}
/* Convert the time structure to calendar time representation. */
newtime = mktime(&tm);
/* Restore the old timezone. */
if (tz) {
static char Buf[256];
#if HAVE_SNPRINTF
snprintf(Buf, sizeof(Buf), "TZ=%s", tz);
#else
sprintf(Buf, "TZ=%s", tz);
#endif
putenv(Buf);
free(tz);
} else {
putenv("TZ");
}
if (newtime == (time_t) -1) {
print_error("%s: %s\n", device, strerror(EIO));
return DATA_ERROR;
}
/* Set the system time. */
if (set_time) {
struct timeval tv;
tv.tv_sec = newtime;
tv.tv_usec = timecode[16] * 31250;
if (timecode[17] & 1)
tv.tv_usec += 500000;
if (tv.tv_usec + OFFSET >= 1000000) {
tv.tv_usec += OFFSET - 1000000;
++tv.tv_sec;
} else {
tv.tv_usec += OFFSET;
}
if (settimeofday(&tv, NULL) == -1) {
print_error("%s\n", strerror(errno));
}
}
/* Output the time. */
if (!quiet) {
struct tm *tp;
char buf[256];
tp = localtime(&newtime);
strftime(buf, sizeof(buf), "%+", tp);
fprintf(stdout, "%s", buf);
if (verbose) {
/*
fprintf(stdout, " - DST: \t%s\n", (
(timecode[8]&3)==1 ? "Yes" :
((timecode[8]&3)==2 ? "No" : "unknown" ))
);
fprintf(stdout, " - Sync:\t%s\n", (timecode[1] & 1 ? "Error" : "Ok"));
fprintf(stdout, " - Battery:\t%s\n", (timecode[8] & 4 ? "Replace" : "Ok"));
*/
/* Not too verbose: */
fprintf(stdout," [Sync: %s, Battery: %s]",
(timecode[1] & 1 ? "Error" : "Ok"),
(timecode[8] & 4 ? "Replace" : "Ok"));
}
fprintf(stdout, "\n");
}
return 0;
}
int
main(int argc, char *argv[])
{
int rc = 1, error, c, i;
char *p;
program_name = argv[0];
p = strrchr(program_name, '/');
if (p)
program_name = p + 1;
setlocale(LC_ALL, "");
while ((c = getopt(argc, argv, "qsuv")) != -1) {
switch (c) {
case 'q':
quiet = 1;
break;
case 's':
set_time = 1;
break;
case 'u':
utc = 1;
break;
case 'v':
verbose = 1;
quiet = 0;
break;
}
}
if (utc) {
if (putenv("TZ=UTC0") == -1) {
print_error("%s\n", strerror(errno));
return 1;
}
}
if (optind < argc) {
for (i = optind; i < argc; ++i) {
error = read_and_set_time(argv[i]);
if (error == OPEN_ERROR) {
print_error("%s: %s\n", argv[i],
strerror(errno));
} else if (error == 0) {
rc = 0;
break;
}
}
} else {
for (i = 0; i < 3; ++i) {
char device[128];
sprintf(device, "/dev/pcfclocks/%d", i);
error = read_and_set_time(device);
if (error == OPEN_ERROR) {
sprintf(device, "/dev/pcfclock%d", i);
error = read_and_set_time(device);
if (error == OPEN_ERROR) {
print_error("%s: %s\n", device,
strerror(errno));
}
}
if (error == 0) {
rc = 0;
break;
}
}
}
return rc;
}
syntax highlighted by Code2HTML, v. 0.9.1