1*24a03f35SRobert Mustacchi /*
2*24a03f35SRobert Mustacchi * This file and its contents are supplied under the terms of the
3*24a03f35SRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4*24a03f35SRobert Mustacchi * You may only use this file in accordance with the terms of version
5*24a03f35SRobert Mustacchi * 1.0 of the CDDL.
6*24a03f35SRobert Mustacchi *
7*24a03f35SRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8*24a03f35SRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9*24a03f35SRobert Mustacchi * http://www.illumos.org/license/CDDL.
10*24a03f35SRobert Mustacchi */
11*24a03f35SRobert Mustacchi
12*24a03f35SRobert Mustacchi /*
13*24a03f35SRobert Mustacchi * Copyright 2025 Oxide Computer Company
14*24a03f35SRobert Mustacchi */
15*24a03f35SRobert Mustacchi
16*24a03f35SRobert Mustacchi /*
17*24a03f35SRobert Mustacchi * Dump all of the information discovered by libzoneinfo in a way that's usable
18*24a03f35SRobert Mustacchi * for diffing. We use the following directory layout from the root:
19*24a03f35SRobert Mustacchi *
20*24a03f35SRobert Mustacchi * dir: <continent-name>
21*24a03f35SRobert Mustacchi * file: info
22*24a03f35SRobert Mustacchi * dir: <country-name>
23*24a03f35SRobert Mustacchi * file: info
24*24a03f35SRobert Mustacchi * file: <tz-name>
25*24a03f35SRobert Mustacchi */
26*24a03f35SRobert Mustacchi
27*24a03f35SRobert Mustacchi #include <err.h>
28*24a03f35SRobert Mustacchi #include <stdlib.h>
29*24a03f35SRobert Mustacchi #include <unistd.h>
30*24a03f35SRobert Mustacchi #include <sys/types.h>
31*24a03f35SRobert Mustacchi #include <sys/stat.h>
32*24a03f35SRobert Mustacchi #include <fcntl.h>
33*24a03f35SRobert Mustacchi #include <libzoneinfo.h>
34*24a03f35SRobert Mustacchi #include <sys/debug.h>
35*24a03f35SRobert Mustacchi #include <errno.h>
36*24a03f35SRobert Mustacchi #include <string.h>
37*24a03f35SRobert Mustacchi
38*24a03f35SRobert Mustacchi static void
usage(const char * fmt,...)39*24a03f35SRobert Mustacchi usage(const char *fmt, ...)
40*24a03f35SRobert Mustacchi {
41*24a03f35SRobert Mustacchi if (fmt != NULL) {
42*24a03f35SRobert Mustacchi va_list ap;
43*24a03f35SRobert Mustacchi
44*24a03f35SRobert Mustacchi va_start(ap, fmt);
45*24a03f35SRobert Mustacchi vwarnx(fmt, ap);
46*24a03f35SRobert Mustacchi va_end(ap);
47*24a03f35SRobert Mustacchi }
48*24a03f35SRobert Mustacchi
49*24a03f35SRobert Mustacchi (void) fprintf(stderr, "Usage: zoneinfo_dump -d dir\n");
50*24a03f35SRobert Mustacchi }
51*24a03f35SRobert Mustacchi
52*24a03f35SRobert Mustacchi static void
dump_timezone(int dirfd,const struct tz_continent * cont,const struct tz_country * country,const struct tz_timezone * tz)53*24a03f35SRobert Mustacchi dump_timezone(int dirfd, const struct tz_continent *cont,
54*24a03f35SRobert Mustacchi const struct tz_country *country, const struct tz_timezone *tz)
55*24a03f35SRobert Mustacchi {
56*24a03f35SRobert Mustacchi int fd;
57*24a03f35SRobert Mustacchi char *name;
58*24a03f35SRobert Mustacchi FILE *f;
59*24a03f35SRobert Mustacchi
60*24a03f35SRobert Mustacchi name = strdup(tz->tz_name);
61*24a03f35SRobert Mustacchi if (name == NULL) {
62*24a03f35SRobert Mustacchi err(EXIT_FAILURE, "failed to duplicate tz name %s",
63*24a03f35SRobert Mustacchi tz->tz_name);
64*24a03f35SRobert Mustacchi }
65*24a03f35SRobert Mustacchi
66*24a03f35SRobert Mustacchi for (size_t i = 0; i < strlen(name); i++) {
67*24a03f35SRobert Mustacchi if (name[i] == '/')
68*24a03f35SRobert Mustacchi name[i] = '-';
69*24a03f35SRobert Mustacchi }
70*24a03f35SRobert Mustacchi
71*24a03f35SRobert Mustacchi if ((fd = openat(dirfd, name, O_RDWR| O_CREAT | O_TRUNC, 0644)) < 0) {
72*24a03f35SRobert Mustacchi err(EXIT_FAILURE, "failed to create tz file for %s/%s/%s",
73*24a03f35SRobert Mustacchi cont->ctnt_name, country->ctry_code, name);
74*24a03f35SRobert Mustacchi }
75*24a03f35SRobert Mustacchi
76*24a03f35SRobert Mustacchi if ((f = fdopen(fd, "w")) == NULL) {
77*24a03f35SRobert Mustacchi err(EXIT_FAILURE, "failed to create stdio stream for "
78*24a03f35SRobert Mustacchi "tz %s/%s/%s file", cont->ctnt_name, country->ctry_code,
79*24a03f35SRobert Mustacchi name);
80*24a03f35SRobert Mustacchi }
81*24a03f35SRobert Mustacchi
82*24a03f35SRobert Mustacchi (void) fprintf(f, "name: %s\n", tz->tz_name);
83*24a03f35SRobert Mustacchi (void) fprintf(f, "oname: %s\n", tz->tz_oname);
84*24a03f35SRobert Mustacchi (void) fprintf(f, "id: %s\n", tz->tz_id_desc);
85*24a03f35SRobert Mustacchi (void) fprintf(f, "desc: %s\n", tz->tz_display_desc);
86*24a03f35SRobert Mustacchi (void) fprintf(f, "lat: %d.%u.%u.%u\n", tz->tz_coord.lat_sign,
87*24a03f35SRobert Mustacchi tz->tz_coord.lat_degree, tz->tz_coord.lat_minute,
88*24a03f35SRobert Mustacchi tz->tz_coord.lat_second);
89*24a03f35SRobert Mustacchi (void) fprintf(f, "long: %d.%u.%u.%u\n", tz->tz_coord.long_sign,
90*24a03f35SRobert Mustacchi tz->tz_coord.long_degree, tz->tz_coord.long_minute,
91*24a03f35SRobert Mustacchi tz->tz_coord.long_second);
92*24a03f35SRobert Mustacchi
93*24a03f35SRobert Mustacchi VERIFY0(fflush(f));
94*24a03f35SRobert Mustacchi VERIFY0(fclose(f));
95*24a03f35SRobert Mustacchi
96*24a03f35SRobert Mustacchi free(name);
97*24a03f35SRobert Mustacchi }
98*24a03f35SRobert Mustacchi
99*24a03f35SRobert Mustacchi static void
dump_country(int cfd,const struct tz_continent * cont,struct tz_country * country)100*24a03f35SRobert Mustacchi dump_country(int cfd, const struct tz_continent *cont,
101*24a03f35SRobert Mustacchi struct tz_country *country)
102*24a03f35SRobert Mustacchi {
103*24a03f35SRobert Mustacchi int dirfd, infofd, ret, found = 0;
104*24a03f35SRobert Mustacchi struct tz_timezone *zones;
105*24a03f35SRobert Mustacchi FILE *f;
106*24a03f35SRobert Mustacchi
107*24a03f35SRobert Mustacchi if (mkdirat(cfd, country->ctry_code, 0755) != 0 && errno != EEXIST) {
108*24a03f35SRobert Mustacchi err(EXIT_FAILURE, "failed to make country directory %s/%s",
109*24a03f35SRobert Mustacchi cont->ctnt_name, country->ctry_code);
110*24a03f35SRobert Mustacchi }
111*24a03f35SRobert Mustacchi
112*24a03f35SRobert Mustacchi if ((dirfd = openat(cfd, country->ctry_code, O_DIRECTORY)) < 0) {
113*24a03f35SRobert Mustacchi err(EXIT_FAILURE, "failed to open country %s/%s",
114*24a03f35SRobert Mustacchi cont->ctnt_name, country->ctry_code);
115*24a03f35SRobert Mustacchi }
116*24a03f35SRobert Mustacchi
117*24a03f35SRobert Mustacchi if ((infofd = openat(dirfd, "info", O_RDWR| O_CREAT | O_TRUNC, 0644)) <
118*24a03f35SRobert Mustacchi 0) {
119*24a03f35SRobert Mustacchi err(EXIT_FAILURE, "failed to create info file for country "
120*24a03f35SRobert Mustacchi "%s/%s", cont->ctnt_name, country->ctry_code);
121*24a03f35SRobert Mustacchi }
122*24a03f35SRobert Mustacchi
123*24a03f35SRobert Mustacchi if ((f = fdopen(infofd, "w")) == NULL) {
124*24a03f35SRobert Mustacchi err(EXIT_FAILURE, "failed to create stdio stream for "
125*24a03f35SRobert Mustacchi "country %s/%s info file", cont->ctnt_name,
126*24a03f35SRobert Mustacchi country->ctry_code);
127*24a03f35SRobert Mustacchi }
128*24a03f35SRobert Mustacchi
129*24a03f35SRobert Mustacchi (void) fprintf(f, "name: %s\n", country->ctry_code);
130*24a03f35SRobert Mustacchi (void) fprintf(f, "id: %s\n", country->ctry_id_desc);
131*24a03f35SRobert Mustacchi (void) fprintf(f, "desc: %s\n", country->ctry_display_desc);
132*24a03f35SRobert Mustacchi VERIFY0(country->ctry_status);
133*24a03f35SRobert Mustacchi VERIFY0(fflush(f));
134*24a03f35SRobert Mustacchi VERIFY0(fclose(f));
135*24a03f35SRobert Mustacchi
136*24a03f35SRobert Mustacchi ret = get_timezones_by_country(&zones, country);
137*24a03f35SRobert Mustacchi if (ret < 0) {
138*24a03f35SRobert Mustacchi err(EXIT_FAILURE, "failed to get timezones for country %s/%s",
139*24a03f35SRobert Mustacchi cont->ctnt_name, country->ctry_code);
140*24a03f35SRobert Mustacchi }
141*24a03f35SRobert Mustacchi
142*24a03f35SRobert Mustacchi for (struct tz_timezone *t = zones; t != NULL; t = t->tz_next,
143*24a03f35SRobert Mustacchi found++) {
144*24a03f35SRobert Mustacchi dump_timezone(dirfd, cont, country, t);
145*24a03f35SRobert Mustacchi }
146*24a03f35SRobert Mustacchi
147*24a03f35SRobert Mustacchi if (ret != found) {
148*24a03f35SRobert Mustacchi errx(EXIT_FAILURE, "zoneinfo said %u timezones should exist "
149*24a03f35SRobert Mustacchi "for country %s/%s, but found %u\n", ret, cont->ctnt_name,
150*24a03f35SRobert Mustacchi country->ctry_code, found);
151*24a03f35SRobert Mustacchi }
152*24a03f35SRobert Mustacchi
153*24a03f35SRobert Mustacchi VERIFY0(free_timezones(zones));
154*24a03f35SRobert Mustacchi VERIFY0(close(dirfd));
155*24a03f35SRobert Mustacchi }
156*24a03f35SRobert Mustacchi
157*24a03f35SRobert Mustacchi static void
dump_continent(int root,struct tz_continent * cont)158*24a03f35SRobert Mustacchi dump_continent(int root, struct tz_continent *cont)
159*24a03f35SRobert Mustacchi {
160*24a03f35SRobert Mustacchi int dirfd, infofd, ret, found = 0;
161*24a03f35SRobert Mustacchi struct tz_country *country;
162*24a03f35SRobert Mustacchi FILE *f;
163*24a03f35SRobert Mustacchi
164*24a03f35SRobert Mustacchi if (mkdirat(root, cont->ctnt_name, 0755) != 0 &&
165*24a03f35SRobert Mustacchi errno != EEXIST) {
166*24a03f35SRobert Mustacchi err(EXIT_FAILURE, "failed to make continent %s",
167*24a03f35SRobert Mustacchi cont->ctnt_name);
168*24a03f35SRobert Mustacchi }
169*24a03f35SRobert Mustacchi
170*24a03f35SRobert Mustacchi if ((dirfd = openat(root, cont->ctnt_name, O_DIRECTORY)) < 0) {
171*24a03f35SRobert Mustacchi err(EXIT_FAILURE, "failed to open continent %s",
172*24a03f35SRobert Mustacchi cont->ctnt_name);
173*24a03f35SRobert Mustacchi }
174*24a03f35SRobert Mustacchi
175*24a03f35SRobert Mustacchi if ((infofd = openat(dirfd, "info", O_RDWR| O_CREAT | O_TRUNC, 0644)) <
176*24a03f35SRobert Mustacchi 0) {
177*24a03f35SRobert Mustacchi err(EXIT_FAILURE, "failed to create info file for continent "
178*24a03f35SRobert Mustacchi "%s", cont->ctnt_name);
179*24a03f35SRobert Mustacchi }
180*24a03f35SRobert Mustacchi
181*24a03f35SRobert Mustacchi if ((f = fdopen(infofd, "w")) == NULL) {
182*24a03f35SRobert Mustacchi err(EXIT_FAILURE, "failed to create stdio stream for "
183*24a03f35SRobert Mustacchi "continent %s info file", cont->ctnt_name);
184*24a03f35SRobert Mustacchi }
185*24a03f35SRobert Mustacchi
186*24a03f35SRobert Mustacchi (void) fprintf(f, "name: %s\n", cont->ctnt_name);
187*24a03f35SRobert Mustacchi (void) fprintf(f, "id: %s\n", cont->ctnt_id_desc);
188*24a03f35SRobert Mustacchi (void) fprintf(f, "desc: %s\n", cont->ctnt_display_desc);
189*24a03f35SRobert Mustacchi VERIFY0(fflush(f));
190*24a03f35SRobert Mustacchi VERIFY0(fclose(f));
191*24a03f35SRobert Mustacchi
192*24a03f35SRobert Mustacchi ret = get_tz_countries(&country, cont);
193*24a03f35SRobert Mustacchi if (ret < 0) {
194*24a03f35SRobert Mustacchi err(EXIT_FAILURE, "failed to get countries for continent %s",
195*24a03f35SRobert Mustacchi cont->ctnt_name);
196*24a03f35SRobert Mustacchi }
197*24a03f35SRobert Mustacchi
198*24a03f35SRobert Mustacchi for (struct tz_country *c = country; c != NULL; c = c->ctry_next,
199*24a03f35SRobert Mustacchi found++) {
200*24a03f35SRobert Mustacchi dump_country(dirfd, cont, c);
201*24a03f35SRobert Mustacchi }
202*24a03f35SRobert Mustacchi
203*24a03f35SRobert Mustacchi if (ret != found) {
204*24a03f35SRobert Mustacchi errx(EXIT_FAILURE, "zoneinfo said %u countries should exist "
205*24a03f35SRobert Mustacchi "for continent %s, but found %u\n", ret, cont->ctnt_name,
206*24a03f35SRobert Mustacchi found);
207*24a03f35SRobert Mustacchi }
208*24a03f35SRobert Mustacchi
209*24a03f35SRobert Mustacchi /* For each Country */
210*24a03f35SRobert Mustacchi VERIFY0(free_tz_countries(country));
211*24a03f35SRobert Mustacchi VERIFY0(close(dirfd));
212*24a03f35SRobert Mustacchi }
213*24a03f35SRobert Mustacchi
214*24a03f35SRobert Mustacchi int
main(int argc,char * argv[])215*24a03f35SRobert Mustacchi main(int argc, char *argv[])
216*24a03f35SRobert Mustacchi {
217*24a03f35SRobert Mustacchi int c, dirfd, ret, found = 0;
218*24a03f35SRobert Mustacchi const char *base = NULL;
219*24a03f35SRobert Mustacchi struct tz_continent *conts;
220*24a03f35SRobert Mustacchi
221*24a03f35SRobert Mustacchi while ((c = getopt(argc, argv, ":d:")) != -1) {
222*24a03f35SRobert Mustacchi switch (c) {
223*24a03f35SRobert Mustacchi case 'd':
224*24a03f35SRobert Mustacchi base = optarg;
225*24a03f35SRobert Mustacchi break;
226*24a03f35SRobert Mustacchi case '?':
227*24a03f35SRobert Mustacchi usage("option -%c requires an argument", optopt);
228*24a03f35SRobert Mustacchi exit(EXIT_FAILURE);
229*24a03f35SRobert Mustacchi case ':':
230*24a03f35SRobert Mustacchi usage("unknown option: -%c", optopt);
231*24a03f35SRobert Mustacchi exit(EXIT_FAILURE);
232*24a03f35SRobert Mustacchi }
233*24a03f35SRobert Mustacchi }
234*24a03f35SRobert Mustacchi
235*24a03f35SRobert Mustacchi if (base == NULL) {
236*24a03f35SRobert Mustacchi errx(EXIT_FAILURE, "missing required directory, please use "
237*24a03f35SRobert Mustacchi "the -d flag");
238*24a03f35SRobert Mustacchi }
239*24a03f35SRobert Mustacchi
240*24a03f35SRobert Mustacchi if ((dirfd = open(base, O_RDONLY | O_DIRECTORY)) < 0) {
241*24a03f35SRobert Mustacchi err(EXIT_FAILURE, "failed to open directory %s", base);
242*24a03f35SRobert Mustacchi }
243*24a03f35SRobert Mustacchi
244*24a03f35SRobert Mustacchi ret = get_tz_continents(&conts);
245*24a03f35SRobert Mustacchi if (ret < 0) {
246*24a03f35SRobert Mustacchi err(EXIT_FAILURE, "failed to get continents");
247*24a03f35SRobert Mustacchi }
248*24a03f35SRobert Mustacchi
249*24a03f35SRobert Mustacchi for (struct tz_continent *c = conts; c != NULL; c = c->ctnt_next,
250*24a03f35SRobert Mustacchi found++) {
251*24a03f35SRobert Mustacchi dump_continent(dirfd, c);
252*24a03f35SRobert Mustacchi }
253*24a03f35SRobert Mustacchi
254*24a03f35SRobert Mustacchi if (found != ret) {
255*24a03f35SRobert Mustacchi errx(EXIT_FAILURE, "zoneinfo said %u continents should exist, "
256*24a03f35SRobert Mustacchi "but found %u\n", ret, found);
257*24a03f35SRobert Mustacchi }
258*24a03f35SRobert Mustacchi
259*24a03f35SRobert Mustacchi VERIFY0(free_tz_continents(conts));
260*24a03f35SRobert Mustacchi
261*24a03f35SRobert Mustacchi return (0);
262*24a03f35SRobert Mustacchi }
263