blob: 5c3224a9238152c5c0187fae076c75e3b8a2b511 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
#include <fuchsia/hardware/rtc/c/fidl.h>
#include <getopt.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <zircon/syscalls.h>
int usage(const char *cmd) {
fprintf(stderr,
"Interact with the real-time or monotonic clocks:\n"
" %s Print the time\n"
" %s --help Print this message\n"
" %s --set YYYY-mm-ddThh:mm:ss Set the time\n"
" %s --monotonic Print nanoseconds since boot\n"
" optionally specify an RTC device with --dev PATH_TO_DEVICE_NODE\n",
cmd, cmd, cmd, cmd);
return -1;
}
char *guess_dev(void) {
char path[19]; // strlen("/dev/class/rtc/###") + 1
DIR *d = opendir("/dev/class/rtc");
if (!d) {
return NULL;
}
struct dirent *de;
while ((de = readdir(d)) != NULL) {
if (strlen(de->d_name) != 3) {
continue;
}
if (isdigit(de->d_name[0]) && isdigit(de->d_name[1]) && isdigit(de->d_name[2])) {
sprintf(path, "/dev/class/rtc/%.3s", de->d_name);
closedir(d);
return strdup(path);
}
}
closedir(d);
return NULL;
}
zx_status_t open_rtc(const char *path, zx_handle_t *handle) {
int rtc_fd = open(path, O_RDONLY);
if (rtc_fd < 0) {
printf("Can not open RTC device\n");
}
return fdio_get_service_handle(rtc_fd, handle);
}
int print_rtc(const char *path) {
zx_handle_t handle;
zx_status_t status = open_rtc(path, &handle);
if (status != ZX_OK) {
return -1;
}
fuchsia_hardware_rtc_Time rtc;
status = fuchsia_hardware_rtc_DeviceGet(handle, &rtc);
if (status != ZX_OK) {
return -1;
}
printf("%04d-%02d-%02dT%02d:%02d:%02d\n", rtc.year, rtc.month, rtc.day, rtc.hours, rtc.minutes,
rtc.seconds);
return 0;
}
int set_rtc(const char *path, const char *time) {
fuchsia_hardware_rtc_Time rtc;
int n = sscanf(time, "%04hd-%02hhd-%02hhdT%02hhd:%02hhd:%02hhd", &rtc.year, &rtc.month, &rtc.day,
&rtc.hours, &rtc.minutes, &rtc.seconds);
if (n != 6) {
printf("Bad time format.\n");
return -1;
}
zx_handle_t handle;
zx_status_t status = open_rtc(path, &handle);
if (status != ZX_OK) {
printf("Can not open RTC device\n");
return status;
}
zx_status_t set_status;
status = fuchsia_hardware_rtc_DeviceSet(handle, &rtc, &set_status);
if (status != ZX_OK) {
return status;
}
return set_status;
}
void print_monotonic(void) { printf("%lu\n", zx_clock_get_monotonic()); }
int main(int argc, char **argv) {
int err = 0;
const char *cmd = basename(argv[0]);
char *path = NULL;
char *set = NULL;
static const struct option opts[] = {
{"set", required_argument, NULL, 's'},
{"dev", required_argument, NULL, 'd'},
{"monotonic", no_argument, NULL, 'm'},
{"help", no_argument, NULL, 'h'},
{0},
};
for (int opt; (opt = getopt_long(argc, argv, "", opts, NULL)) != -1;) {
switch (opt) {
case 's':
set = strdup(optarg);
break;
case 'd':
path = strdup(optarg);
break;
case 'm':
print_monotonic();
goto done;
case 'h':
usage(cmd);
err = 0;
goto done;
default:
err = usage(cmd);
goto done;
}
}
argv += optind;
argc -= optind;
if (argc != 0) {
err = usage(cmd);
goto done;
}
if (!path) {
path = guess_dev();
if (!path) {
fprintf(stderr, "No RTC found.\n");
err = usage(cmd);
goto done;
}
}
if (set) {
err = set_rtc(path, set);
if (err) {
printf("Set RTC failed.\n");
usage(cmd);
}
goto done;
}
err = print_rtc(path);
if (err) {
usage(cmd);
}
done:
free(path);
free(set);
return err;
}