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