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