/* * 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 #include #include #include #include #include #include #include #include #include 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; }