1// Copyright 2016 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <zircon/device/rtc.h>
6
7#include <ctype.h>
8#include <dirent.h>
9#include <fcntl.h>
10#include <getopt.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <sys/types.h>
15
16int usage(const char* cmd) {
17    fprintf(
18        stderr,
19        "Interact with the real-time clock:\n"
20        "   %s                              Print the time\n"
21        "   %s --help                       Print this message\n"
22        "   %s --set YYYY-mm-ddThh:mm:ss    Set the time\n"
23        "   optionally specify an RTC device with --dev PATH_TO_DEVICE_NODE\n",
24        cmd,
25        cmd,
26        cmd);
27    return -1;
28}
29
30char *guess_dev(void) {
31    char path[19]; // strlen("/dev/class/rtc/###") + 1
32    DIR *d = opendir("/dev/class/rtc");
33    if (!d) {
34        return NULL;
35    }
36
37    struct dirent *de;
38    while ((de = readdir(d)) != NULL) {
39        if (strlen(de->d_name) != 3) {
40            continue;
41        }
42
43        if (isdigit(de->d_name[0]) &&
44            isdigit(de->d_name[1]) &&
45            isdigit(de->d_name[2])) {
46            sprintf(path, "/dev/class/rtc/%.3s", de->d_name);
47            closedir(d);
48            return strdup(path);
49        }
50    }
51
52    closedir(d);
53    return NULL;
54}
55
56int open_rtc(const char *path, int mode) {
57    int rtc_fd = open(path, mode);
58    if (rtc_fd < 0) {
59        printf("Can not open RTC device\n");
60    }
61    return rtc_fd;
62}
63
64int print_rtc(const char *path) {
65    int rtc_fd = open_rtc(path, O_RDONLY);
66    if (rtc_fd < 0) {
67        return -1;
68    }
69    rtc_t rtc;
70    ssize_t n = ioctl_rtc_get(rtc_fd, &rtc);
71    if (n < (ssize_t)sizeof(rtc_t)) {
72        return -1;
73    }
74    printf(
75        "%04d-%02d-%02dT%02d:%02d:%02d\n",
76        rtc.year,
77        rtc.month,
78        rtc.day,
79        rtc.hours,
80        rtc.minutes,
81        rtc.seconds);
82    return 0;
83}
84
85int set_rtc(const char *path, const char* time) {
86    rtc_t rtc;
87    int n = sscanf(
88        time,
89        "%04hd-%02hhd-%02hhdT%02hhd:%02hhd:%02hhd",
90        &rtc.year,
91        &rtc.month,
92        &rtc.day,
93        &rtc.hours,
94        &rtc.minutes,
95        &rtc.seconds);
96    if (n != 6) {
97        printf("Bad time format.\n");
98        return -1;
99    }
100    int rtc_fd = open_rtc(path, O_WRONLY);
101    if (rtc_fd < 0) {
102        printf("Can not open RTC device\n");
103        return -1;
104    }
105    ssize_t written = ioctl_rtc_set(rtc_fd, &rtc);
106    return (written == sizeof(rtc)) ? 0 : written;
107}
108
109int main(int argc, char** argv) {
110    int err;
111    const char* cmd = basename(argv[0]);
112    char *path = NULL;
113    char *set = NULL;
114    static const struct option opts[] = {
115        {"set",  required_argument, NULL, 's'},
116        {"dev",  required_argument, NULL, 'd'},
117        {"help", no_argument,       NULL, 'h'},
118        {0},
119    };
120    for (int opt; (opt = getopt_long(argc, argv, "", opts, NULL)) != -1;) {
121        switch (opt) {
122        case 's':
123            set = strdup(optarg);
124            break;
125        case 'd':
126            path = strdup(optarg);
127            break;
128        case 'h':
129            usage(cmd);
130            err = 0;
131            goto done;
132        default:
133            err = usage(cmd);
134            goto done;
135        }
136    }
137
138    argv += optind;
139    argc -= optind;
140
141    if (argc != 0) {
142        err = usage(cmd);
143        goto done;
144    }
145
146    if (!path) {
147        path = guess_dev();
148        if (!path) {
149            fprintf(stderr, "No RTC found.\n");
150            err = usage(cmd);
151            goto done;
152        }
153    }
154
155    if (set) {
156        err = set_rtc(path, set);
157        if (err) {
158            printf("Set RTC failed.\n");
159            usage(cmd);
160        }
161        goto done;
162    }
163
164    err = print_rtc(path);
165    if (err) {
166        usage(cmd);
167    }
168
169done:
170    free(path);
171    free(set);
172    return err;
173}
174