tzsetup.c revision 208830
119872Swollman/* 219872Swollman * Copyright 1996 Massachusetts Institute of Technology 319872Swollman * 419872Swollman * Permission to use, copy, modify, and distribute this software and 519872Swollman * its documentation for any purpose and without fee is hereby 619872Swollman * granted, provided that both the above copyright notice and this 719872Swollman * permission notice appear in all copies, that both the above 819872Swollman * copyright notice and this permission notice appear in all 919872Swollman * supporting documentation, and that the name of M.I.T. not be used 1019872Swollman * in advertising or publicity pertaining to distribution of the 1119872Swollman * software without specific, written prior permission. M.I.T. makes 1219872Swollman * no representations about the suitability of this software for any 1319872Swollman * purpose. It is provided "as is" without express or implied 1419872Swollman * warranty. 15179530Sjkim * 1619872Swollman * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 1719872Swollman * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 1819872Swollman * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1919872Swollman * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 2019872Swollman * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2119872Swollman * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2219872Swollman * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 2319872Swollman * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 2419872Swollman * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2519872Swollman * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 2619872Swollman * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2719872Swollman * SUCH DAMAGE. 2819872Swollman */ 2919872Swollman 3019872Swollman/* 3119872Swollman * Second attempt at a `tzmenu' program, using the separate description 3219872Swollman * files provided in newer tzdata releases. 3319872Swollman */ 3419872Swollman 35179530Sjkim#include <sys/cdefs.h> 36179530Sjkim__FBSDID("$FreeBSD: head/usr.sbin/tzsetup/tzsetup.c 208830 2010-06-05 12:49:39Z edwin $"); 3730763Scharnier 3819872Swollman#include <dialog.h> 3919872Swollman#include <err.h> 4019872Swollman#include <errno.h> 4119872Swollman#include <stdio.h> 4219872Swollman#include <stdlib.h> 4319872Swollman#include <string.h> 4466907Swollman#include <time.h> 4519872Swollman#include <unistd.h> 4619872Swollman 4719872Swollman#include <sys/fcntl.h> 48198267Sedwin#include <sys/param.h> 4919872Swollman#include <sys/queue.h> 5019872Swollman#include <sys/stat.h> 5119872Swollman 52179530Sjkim#define _PATH_ZONETAB "/usr/share/zoneinfo/zone.tab" 53179530Sjkim#define _PATH_ISO3166 "/usr/share/misc/iso3166" 54179530Sjkim#define _PATH_ZONEINFO "/usr/share/zoneinfo" 55179530Sjkim#define _PATH_LOCALTIME "/etc/localtime" 56198267Sedwin#define _PATH_DB "/var/db/zoneinfo" 57179530Sjkim#define _PATH_WALL_CMOS_CLOCK "/etc/wall_cmos_clock" 5819872Swollman 59198350Sedwinstatic char path_zonetab[MAXPATHLEN], path_iso3166[MAXPATHLEN], 60198350Sedwin path_zoneinfo[MAXPATHLEN], path_localtime[MAXPATHLEN], 61198350Sedwin path_db[MAXPATHLEN], path_wall_cmos_clock[MAXPATHLEN]; 62198350Sedwin 6319872Swollmanstatic int reallydoit = 1; 64198267Sedwinstatic int reinstall = 0; 65198350Sedwinstatic int usedialog = 1; 66198350Sedwinstatic char *chrootenv = NULL; 6719872Swollman 68179530Sjkimstatic void usage(void); 69179530Sjkimstatic int continent_country_menu(dialogMenuItem *); 70179530Sjkimstatic int set_zone_multi(dialogMenuItem *); 71179530Sjkimstatic int set_zone_whole_country(dialogMenuItem *); 72179530Sjkimstatic int set_zone_menu(dialogMenuItem *); 7319872Swollman 7419872Swollmanstruct continent { 7519872Swollman dialogMenuItem *menu; 76179530Sjkim int nitems; 77179530Sjkim int ch; 78179530Sjkim int sc; 7919872Swollman}; 8019872Swollman 81179530Sjkimstatic struct continent africa, america, antarctica, arctic, asia, atlantic; 82179530Sjkimstatic struct continent australia, europe, indian, pacific; 8319872Swollman 8419872Swollmanstatic struct continent_names { 85179530Sjkim const char *name; 8619872Swollman struct continent *continent; 8719872Swollman} continent_names[] = { 88179530Sjkim { "Africa", &africa }, 89179530Sjkim { "America", &america }, 90179530Sjkim { "Antarctica", &antarctica }, 91179530Sjkim { "Arctic", &arctic }, 92179530Sjkim { "Asia", &asia }, 93179530Sjkim { "Atlantic", &atlantic }, 94179530Sjkim { "Australia", &australia }, 95179530Sjkim { "Europe", &europe }, 96179530Sjkim { "Indian", &indian }, 97179530Sjkim { "Pacific", &pacific } 9819872Swollman}; 9919872Swollman 100179530Sjkimstatic struct continent_items { 101179530Sjkim char prompt[2]; 102179530Sjkim char title[30]; 103179530Sjkim} continent_items[] = { 104179530Sjkim { "1", "Africa" }, 105179530Sjkim { "2", "America -- North and South" }, 106179530Sjkim { "3", "Antarctica" }, 107179530Sjkim { "4", "Arctic Ocean" }, 108179530Sjkim { "5", "Asia" }, 109179530Sjkim { "6", "Atlantic Ocean" }, 110179530Sjkim { "7", "Australia" }, 111179530Sjkim { "8", "Europe" }, 112179530Sjkim { "9", "Indian Ocean" }, 113179530Sjkim { "0", "Pacific Ocean" } 11419872Swollman}; 11519872Swollman 116179530Sjkim#define NCONTINENTS \ 117179530Sjkim (int)((sizeof(continent_items)) / (sizeof(continent_items[0]))) 118179530Sjkimstatic dialogMenuItem continents[NCONTINENTS]; 119179530Sjkim 120179530Sjkim#define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9) 121179530Sjkim 12219872Swollmanstatic int 12319872Swollmancontinent_country_menu(dialogMenuItem *continent) 12419872Swollman{ 125179530Sjkim char title[64], prompt[64]; 12619872Swollman struct continent *contp = continent->data; 127179530Sjkim int isocean = OCEANP(continent - continents); 128179530Sjkim int menulen; 129179530Sjkim int rv; 13019872Swollman 13119872Swollman /* Short cut -- if there's only one country, don't post a menu. */ 132179497Sjkim if (contp->nitems == 1) 133179497Sjkim return (contp->menu[0].fire(&contp->menu[0])); 13419872Swollman 13519872Swollman /* It's amazing how much good grammar really matters... */ 136179530Sjkim if (!isocean) { 137179530Sjkim snprintf(title, sizeof(title), "Countries in %s", 138179530Sjkim continent->title); 139179530Sjkim snprintf(prompt, sizeof(prompt), "Select a country or region"); 140179530Sjkim } else { 141179530Sjkim snprintf(title, sizeof(title), "Islands and groups in the %s", 142179530Sjkim continent->title); 143179530Sjkim snprintf(prompt, sizeof(prompt), "Select an island or group"); 144179530Sjkim } 14519872Swollman 14619872Swollman menulen = contp->nitems < 16 ? contp->nitems : 16; 147179530Sjkim rv = dialog_menu(title, prompt, -1, -1, menulen, -contp->nitems, 148179530Sjkim contp->menu, 0, &contp->ch, &contp->sc); 14919872Swollman if (rv == 0) 150179530Sjkim return (DITEM_LEAVE_MENU); 151179530Sjkim return (DITEM_RECREATE); 15219872Swollman} 15319872Swollman 15419872Swollmanstatic struct continent * 15519872Swollmanfind_continent(const char *name) 15619872Swollman{ 157179530Sjkim int i; 15819872Swollman 159179530Sjkim for (i = 0; i < NCONTINENTS; i++) 16019872Swollman if (strcmp(name, continent_names[i].name) == 0) 161179530Sjkim return (continent_names[i].continent); 162179530Sjkim return (0); 16319872Swollman} 16419872Swollman 16519872Swollmanstruct country { 166179530Sjkim char *name; 167179530Sjkim char *tlc; 168179530Sjkim int nzones; 169179530Sjkim char *filename; /* use iff nzones < 0 */ 170179530Sjkim struct continent *continent; /* use iff nzones < 0 */ 171179530Sjkim TAILQ_HEAD(, zone) zones; /* use iff nzones > 0 */ 172179530Sjkim dialogMenuItem *submenu; /* use iff nzones > 0 */ 17319872Swollman}; 17419872Swollman 17519872Swollmanstruct zone { 17660938Sjake TAILQ_ENTRY(zone) link; 177179530Sjkim char *descr; 178179530Sjkim char *filename; 17919872Swollman struct continent *continent; 18019872Swollman}; 18119872Swollman 18219872Swollman/* 18319872Swollman * This is the easiest organization... we use ISO 3166 country codes, 18419872Swollman * of the two-letter variety, so we just size this array to suit. 18519872Swollman * Beats worrying about dynamic allocation. 18619872Swollman */ 187179530Sjkim#define NCOUNTRIES (26 * 26) 18822181Sjhaystatic struct country countries[NCOUNTRIES]; 18919872Swollman 190179530Sjkim#define CODE2INT(s) ((s[0] - 'A') * 26 + (s[1] - 'A')) 191179530Sjkim 19219872Swollman/* 19319872Swollman * Read the ISO 3166 country code database in _PATH_ISO3166 19419872Swollman * (/usr/share/misc/iso3166). On error, exit via err(3). 19519872Swollman */ 19619872Swollmanstatic void 19719872Swollmanread_iso3166_table(void) 19819872Swollman{ 199179530Sjkim FILE *fp; 200179530Sjkim struct country *cp; 201179530Sjkim size_t len; 202179530Sjkim char *s, *t, *name; 203179530Sjkim int lineno; 20419872Swollman 205198350Sedwin fp = fopen(path_iso3166, "r"); 20619872Swollman if (!fp) 207198350Sedwin err(1, path_iso3166); 20819872Swollman lineno = 0; 20919872Swollman 21019872Swollman while ((s = fgetln(fp, &len)) != 0) { 21119872Swollman lineno++; 21219872Swollman if (s[len - 1] != '\n') 213198350Sedwin errx(1, "%s:%d: invalid format", path_iso3166, lineno); 21419872Swollman s[len - 1] = '\0'; 21530999Sjoerg if (s[0] == '#' || strspn(s, " \t") == len - 1) 21619872Swollman continue; 21719872Swollman 21819872Swollman /* Isolate the two-letter code. */ 21919872Swollman t = strsep(&s, "\t"); 22019872Swollman if (t == 0 || strlen(t) != 2) 221198350Sedwin errx(1, "%s:%d: invalid format", path_iso3166, lineno); 22219872Swollman if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z') 223198350Sedwin errx(1, "%s:%d: invalid code `%s'", path_iso3166, 224179530Sjkim lineno, t); 22519872Swollman 22619872Swollman /* Now skip past the three-letter and numeric codes. */ 227179530Sjkim name = strsep(&s, "\t"); /* 3-let */ 22819872Swollman if (name == 0 || strlen(name) != 3) 229198350Sedwin errx(1, "%s:%d: invalid format", path_iso3166, lineno); 230179530Sjkim name = strsep(&s, "\t"); /* numeric */ 23119872Swollman if (name == 0 || strlen(name) != 3) 232198350Sedwin errx(1, "%s:%d: invalid format", path_iso3166, lineno); 23319872Swollman 23419872Swollman name = s; 23519872Swollman 23619872Swollman cp = &countries[CODE2INT(t)]; 23719872Swollman if (cp->name) 238198350Sedwin errx(1, "%s:%d: country code `%s' multiply defined: %s", 239198350Sedwin path_iso3166, lineno, t, cp->name); 24019872Swollman cp->name = strdup(name); 24156487Scharnier if (cp->name == NULL) 24256487Scharnier errx(1, "malloc failed"); 24319872Swollman cp->tlc = strdup(t); 24456487Scharnier if (cp->tlc == NULL) 24556487Scharnier errx(1, "malloc failed"); 24619872Swollman } 24719872Swollman 24819872Swollman fclose(fp); 24919872Swollman} 25019872Swollman 25119872Swollmanstatic void 25219872Swollmanadd_zone_to_country(int lineno, const char *tlc, const char *descr, 253179530Sjkim const char *file, struct continent *cont) 25419872Swollman{ 255179530Sjkim struct zone *zp; 256179530Sjkim struct country *cp; 25719872Swollman 25819872Swollman if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z') 259198350Sedwin errx(1, "%s:%d: country code `%s' invalid", path_zonetab, 260179530Sjkim lineno, tlc); 26119872Swollman 26219872Swollman cp = &countries[CODE2INT(tlc)]; 26319872Swollman if (cp->name == 0) 264198350Sedwin errx(1, "%s:%d: country code `%s' unknown", path_zonetab, 265179530Sjkim lineno, tlc); 26619872Swollman 26719872Swollman if (descr) { 26819872Swollman if (cp->nzones < 0) 269198350Sedwin errx(1, "%s:%d: conflicting zone definition", 270198350Sedwin path_zonetab, lineno); 27119872Swollman 272179530Sjkim zp = malloc(sizeof(*zp)); 27319872Swollman if (zp == 0) 274179530Sjkim errx(1, "malloc(%zu)", sizeof(*zp)); 27519872Swollman 27619872Swollman if (cp->nzones == 0) 27719872Swollman TAILQ_INIT(&cp->zones); 27819872Swollman 27919872Swollman zp->descr = strdup(descr); 28056487Scharnier if (zp->descr == NULL) 28156487Scharnier errx(1, "malloc failed"); 28219872Swollman zp->filename = strdup(file); 28356487Scharnier if (zp->filename == NULL) 28456487Scharnier errx(1, "malloc failed"); 28519872Swollman zp->continent = cont; 28619872Swollman TAILQ_INSERT_TAIL(&cp->zones, zp, link); 28719872Swollman cp->nzones++; 28819872Swollman } else { 28919872Swollman if (cp->nzones > 0) 290198350Sedwin errx(1, "%s:%d: zone must have description", 291198350Sedwin path_zonetab, lineno); 29219872Swollman if (cp->nzones < 0) 293198350Sedwin errx(1, "%s:%d: zone multiply defined", 294198350Sedwin path_zonetab, lineno); 29519872Swollman cp->nzones = -1; 29619872Swollman cp->filename = strdup(file); 29756487Scharnier if (cp->filename == NULL) 29856487Scharnier errx(1, "malloc failed"); 29919872Swollman cp->continent = cont; 30019872Swollman } 30119872Swollman} 30219872Swollman 30319872Swollman/* 30419872Swollman * This comparison function intentionally sorts all of the null-named 30519872Swollman * ``countries''---i.e., the codes that don't correspond to a real 30619872Swollman * country---to the end. Everything else is lexical by country name. 30719872Swollman */ 30819872Swollmanstatic int 30919872Swollmancompare_countries(const void *xa, const void *xb) 31019872Swollman{ 31119872Swollman const struct country *a = xa, *b = xb; 31219872Swollman 31319872Swollman if (a->name == 0 && b->name == 0) 314179530Sjkim return (0); 31519872Swollman if (a->name == 0 && b->name != 0) 316179530Sjkim return (1); 31719872Swollman if (b->name == 0) 318179530Sjkim return (-1); 31919872Swollman 320179530Sjkim return (strcmp(a->name, b->name)); 32119872Swollman} 32219872Swollman 32319872Swollman/* 32419872Swollman * This must be done AFTER all zone descriptions are read, since it breaks 32519872Swollman * CODE2INT(). 32619872Swollman */ 32719872Swollmanstatic void 32819872Swollmansort_countries(void) 32919872Swollman{ 330179530Sjkim 331179530Sjkim qsort(countries, NCOUNTRIES, sizeof(countries[0]), compare_countries); 33219872Swollman} 33319872Swollman 33419872Swollmanstatic void 33519872Swollmanread_zones(void) 33619872Swollman{ 337179530Sjkim char contbuf[16]; 338179530Sjkim FILE *fp; 33919872Swollman struct continent *cont; 340179530Sjkim size_t len; 341179530Sjkim char *line, *tlc, *coord, *file, *descr, *p; 342179530Sjkim int lineno; 34319872Swollman 344198350Sedwin fp = fopen(path_zonetab, "r"); 34519872Swollman if (!fp) 346198350Sedwin err(1, path_zonetab); 34719872Swollman lineno = 0; 34819872Swollman 34919872Swollman while ((line = fgetln(fp, &len)) != 0) { 35019872Swollman lineno++; 35119872Swollman if (line[len - 1] != '\n') 352198350Sedwin errx(1, "%s:%d: invalid format", path_zonetab, lineno); 35319872Swollman line[len - 1] = '\0'; 35419872Swollman if (line[0] == '#') 35519872Swollman continue; 35619872Swollman 35719872Swollman tlc = strsep(&line, "\t"); 35819872Swollman if (strlen(tlc) != 2) 359198350Sedwin errx(1, "%s:%d: invalid country code `%s'", 360198350Sedwin path_zonetab, lineno, tlc); 36119872Swollman coord = strsep(&line, "\t"); 36219872Swollman file = strsep(&line, "\t"); 36319872Swollman p = strchr(file, '/'); 36419872Swollman if (p == 0) 365198350Sedwin errx(1, "%s:%d: invalid zone name `%s'", path_zonetab, 366179530Sjkim lineno, file); 36719872Swollman contbuf[0] = '\0'; 36819872Swollman strncat(contbuf, file, p - file); 36919872Swollman cont = find_continent(contbuf); 37019872Swollman if (!cont) 371198350Sedwin errx(1, "%s:%d: invalid region `%s'", path_zonetab, 372179530Sjkim lineno, contbuf); 37319872Swollman 374179530Sjkim descr = (line != NULL && *line != '\0') ? line : NULL; 37519872Swollman 37619872Swollman add_zone_to_country(lineno, tlc, descr, file, cont); 37719872Swollman } 37819872Swollman fclose(fp); 37919872Swollman} 38019872Swollman 38119872Swollmanstatic void 38219872Swollmanmake_menus(void) 38319872Swollman{ 384179530Sjkim struct country *cp; 385179530Sjkim struct zone *zp, *zp2; 38619872Swollman struct continent *cont; 387179530Sjkim dialogMenuItem *dmi; 388179530Sjkim int i; 38919872Swollman 39019872Swollman /* 39119872Swollman * First, count up all the countries in each continent/ocean. 39219872Swollman * Be careful to count those countries which have multiple zones 39319872Swollman * only once for each. NB: some countries are in multiple 39419872Swollman * continents/oceans. 39519872Swollman */ 39619872Swollman for (cp = countries; cp->name; cp++) { 39719872Swollman if (cp->nzones == 0) 39819872Swollman continue; 39919872Swollman if (cp->nzones < 0) { 40019872Swollman cp->continent->nitems++; 40119872Swollman } else { 40270486Sben TAILQ_FOREACH(zp, &cp->zones, link) { 40319872Swollman cont = zp->continent; 40470486Sben for (zp2 = TAILQ_FIRST(&cp->zones); 405179530Sjkim zp2->continent != cont; 406179530Sjkim zp2 = TAILQ_NEXT(zp2, link)) 40719872Swollman ; 40819872Swollman if (zp2 == zp) 40919872Swollman zp->continent->nitems++; 41019872Swollman } 41119872Swollman } 41219872Swollman } 41319872Swollman 41419872Swollman /* 415179530Sjkim * Now allocate memory for the country menus and initialize 416179530Sjkim * continent menus. We set nitems back to zero so that we can 417179530Sjkim * use it for counting again when we actually build the menus. 41819872Swollman */ 419179530Sjkim memset(continents, 0, sizeof(continents)); 42019872Swollman for (i = 0; i < NCONTINENTS; i++) { 42119872Swollman continent_names[i].continent->menu = 422179530Sjkim malloc(sizeof(dialogMenuItem) * 423179530Sjkim continent_names[i].continent->nitems); 42419872Swollman if (continent_names[i].continent->menu == 0) 42556487Scharnier errx(1, "malloc for continent menu"); 42619872Swollman continent_names[i].continent->nitems = 0; 427179530Sjkim continents[i].prompt = continent_items[i].prompt; 428179530Sjkim continents[i].title = continent_items[i].title; 429179530Sjkim continents[i].fire = continent_country_menu; 430179530Sjkim continents[i].data = continent_names[i].continent; 43119872Swollman } 43219872Swollman 43319872Swollman /* 43419872Swollman * Now that memory is allocated, create the menu items for 43519872Swollman * each continent. For multiple-zone countries, also create 43619872Swollman * the country's zone submenu. 43719872Swollman */ 43819872Swollman for (cp = countries; cp->name; cp++) { 43919872Swollman if (cp->nzones == 0) 44019872Swollman continue; 44119872Swollman if (cp->nzones < 0) { 44219872Swollman dmi = &cp->continent->menu[cp->continent->nitems]; 443179530Sjkim memset(dmi, 0, sizeof(*dmi)); 444179530Sjkim asprintf(&dmi->prompt, "%d", ++cp->continent->nitems); 44519872Swollman dmi->title = cp->name; 44619872Swollman dmi->checked = 0; 44719872Swollman dmi->fire = set_zone_whole_country; 44819872Swollman dmi->selected = 0; 44919872Swollman dmi->data = cp; 45019872Swollman } else { 451179530Sjkim cp->submenu = malloc(cp->nzones * sizeof(*dmi)); 45219872Swollman if (cp->submenu == 0) 45356487Scharnier errx(1, "malloc for submenu"); 45419872Swollman cp->nzones = 0; 45570486Sben TAILQ_FOREACH(zp, &cp->zones, link) { 45619872Swollman cont = zp->continent; 45719872Swollman dmi = &cp->submenu[cp->nzones]; 458179530Sjkim memset(dmi, 0, sizeof(*dmi)); 459179530Sjkim asprintf(&dmi->prompt, "%d", ++cp->nzones); 46019872Swollman dmi->title = zp->descr; 46119872Swollman dmi->checked = 0; 46219872Swollman dmi->fire = set_zone_multi; 46319872Swollman dmi->selected = 0; 46419872Swollman dmi->data = zp; 46519872Swollman 46670486Sben for (zp2 = TAILQ_FIRST(&cp->zones); 467179530Sjkim zp2->continent != cont; 468179530Sjkim zp2 = TAILQ_NEXT(zp2, link)) 46919872Swollman ; 47019872Swollman if (zp2 != zp) 47119872Swollman continue; 47219872Swollman 47319872Swollman dmi = &cont->menu[cont->nitems]; 474179530Sjkim memset(dmi, 0, sizeof(*dmi)); 47519872Swollman asprintf(&dmi->prompt, "%d", ++cont->nitems); 47619872Swollman dmi->title = cp->name; 47719872Swollman dmi->checked = 0; 47819872Swollman dmi->fire = set_zone_menu; 47919872Swollman dmi->selected = 0; 48019872Swollman dmi->data = cp; 48119872Swollman } 48219872Swollman } 48319872Swollman } 48419872Swollman} 48519872Swollman 48619872Swollmanstatic int 48719872Swollmanset_zone_menu(dialogMenuItem *dmi) 48819872Swollman{ 489179530Sjkim char title[64], prompt[64]; 490179530Sjkim struct country *cp = dmi->data; 491179530Sjkim int menulen; 492179530Sjkim int rv; 49319872Swollman 494179530Sjkim snprintf(title, sizeof(title), "%s Time Zones", cp->name); 495179530Sjkim snprintf(prompt, sizeof(prompt), 496179530Sjkim "Select a zone which observes the same time as your locality."); 49719872Swollman menulen = cp->nzones < 16 ? cp->nzones : 16; 498179530Sjkim rv = dialog_menu(title, prompt, -1, -1, menulen, -cp->nzones, 499179530Sjkim cp->submenu, 0, 0, 0); 50019872Swollman if (rv != 0) 501179530Sjkim return (DITEM_RECREATE); 502179530Sjkim return (DITEM_LEAVE_MENU); 50319872Swollman} 50419872Swollman 50519872Swollmanstatic int 506198350Sedwininstall_zoneinfo_file(const char *zoneinfo_file) 50719872Swollman{ 508179530Sjkim char buf[1024]; 509179530Sjkim char title[64], prompt[64]; 510179530Sjkim struct stat sb; 511179530Sjkim ssize_t len; 512179530Sjkim int fd1, fd2, copymode; 51319872Swollman 514198350Sedwin if (lstat(path_localtime, &sb) < 0) { 51519872Swollman /* Nothing there yet... */ 51619872Swollman copymode = 1; 517179530Sjkim } else if (S_ISLNK(sb.st_mode)) 51819872Swollman copymode = 0; 51919872Swollman else 52019872Swollman copymode = 1; 52119872Swollman 52221915Sjkh#ifdef VERBOSE 52319872Swollman if (copymode) 524179530Sjkim snprintf(prompt, sizeof(prompt), 525198350Sedwin "Copying %s to %s", zoneinfo_file, path_localtime); 52619872Swollman else 527179530Sjkim snprintf(prompt, sizeof(prompt), 528198350Sedwin "Creating symbolic link %s to %s", 529198350Sedwin path_localtime, zoneinfo_file); 530198267Sedwin if (usedialog) 531198267Sedwin dialog_notify(prompt); 532198267Sedwin else 533198267Sedwin fprintf(stderr, "%s\n", prompt); 53421915Sjkh#endif 53519872Swollman 53619872Swollman if (reallydoit) { 53719872Swollman if (copymode) { 538198350Sedwin fd1 = open(zoneinfo_file, O_RDONLY, 0); 53919872Swollman if (fd1 < 0) { 540179530Sjkim snprintf(title, sizeof(title), "Error"); 541179530Sjkim snprintf(prompt, sizeof(prompt), 542198350Sedwin "Could not open %s: %s", zoneinfo_file, 543179530Sjkim strerror(errno)); 544198267Sedwin if (usedialog) 545198267Sedwin dialog_mesgbox(title, prompt, 8, 72); 546198267Sedwin else 547198267Sedwin fprintf(stderr, "%s\n", prompt); 548179530Sjkim return (DITEM_FAILURE | DITEM_RECREATE); 54919872Swollman } 55019872Swollman 551198350Sedwin unlink(path_localtime); 552198350Sedwin fd2 = open(path_localtime, O_CREAT | O_EXCL | O_WRONLY, 553179530Sjkim S_IRUSR | S_IRGRP | S_IROTH); 55419872Swollman if (fd2 < 0) { 555179530Sjkim snprintf(title, sizeof(title), "Error"); 556179530Sjkim snprintf(prompt, sizeof(prompt), 557198350Sedwin "Could not open %s: %s", 558198350Sedwin path_localtime, strerror(errno)); 559198267Sedwin if (usedialog) 560198267Sedwin dialog_mesgbox(title, prompt, 8, 72); 561198267Sedwin else 562198267Sedwin fprintf(stderr, "%s\n", prompt); 563179530Sjkim return (DITEM_FAILURE | DITEM_RECREATE); 56419872Swollman } 56519872Swollman 566179530Sjkim while ((len = read(fd1, buf, sizeof(buf))) > 0) 567208830Sedwin if ((len = write(fd2, buf, len)) < 0) 568208830Sedwin break; 56919872Swollman 57019872Swollman if (len == -1) { 571179530Sjkim snprintf(title, sizeof(title), "Error"); 572179530Sjkim snprintf(prompt, sizeof(prompt), 573198350Sedwin "Error copying %s to %s %s", zoneinfo_file, 574198350Sedwin path_localtime, strerror(errno)); 575198267Sedwin if (usedialog) 576198267Sedwin dialog_mesgbox(title, prompt, 8, 72); 577198267Sedwin else 578198267Sedwin fprintf(stderr, "%s\n", prompt); 57919872Swollman /* Better to leave none than a corrupt one. */ 580198350Sedwin unlink(path_localtime); 581179530Sjkim return (DITEM_FAILURE | DITEM_RECREATE); 58219872Swollman } 58319872Swollman close(fd1); 58419872Swollman close(fd2); 58519872Swollman } else { 586198350Sedwin if (access(zoneinfo_file, R_OK) != 0) { 587179530Sjkim snprintf(title, sizeof(title), "Error"); 588179530Sjkim snprintf(prompt, sizeof(prompt), 589198350Sedwin "Cannot access %s: %s", zoneinfo_file, 590179530Sjkim strerror(errno)); 591198267Sedwin if (usedialog) 592198267Sedwin dialog_mesgbox(title, prompt, 8, 72); 593198267Sedwin else 594198267Sedwin fprintf(stderr, "%s\n", prompt); 595179530Sjkim return (DITEM_FAILURE | DITEM_RECREATE); 59619872Swollman } 597198350Sedwin unlink(path_localtime); 598198350Sedwin if (symlink(zoneinfo_file, path_localtime) < 0) { 599179530Sjkim snprintf(title, sizeof(title), "Error"); 600179530Sjkim snprintf(prompt, sizeof(prompt), 601198350Sedwin "Cannot create symbolic link %s to %s: %s", 602198350Sedwin path_localtime, zoneinfo_file, 603179530Sjkim strerror(errno)); 604198267Sedwin if (usedialog) 605198267Sedwin dialog_mesgbox(title, prompt, 8, 72); 606198267Sedwin else 607198267Sedwin fprintf(stderr, "%s\n", prompt); 608179530Sjkim return (DITEM_FAILURE | DITEM_RECREATE); 60919872Swollman } 61019872Swollman } 61119872Swollman } 61219872Swollman 61321915Sjkh#ifdef VERBOSE 614179530Sjkim snprintf(title, sizeof(title), "Done"); 61519872Swollman if (copymode) 616179530Sjkim snprintf(prompt, sizeof(prompt), 617198350Sedwin "Copied timezone file from %s to %s", zoneinfo_file, 618198350Sedwin path_localtime); 61919872Swollman else 620198350Sedwin snprintf(prompt, sizeof(prompt), 621198350Sedwin "Created symbolic link from %s to %s", zoneinfo_file, 622198350Sedwin path_localtime); 623198267Sedwin if (usedialog) 624198267Sedwin dialog_mesgbox(title, prompt, 8, 72); 625198267Sedwin else 626198267Sedwin fprintf(stderr, "%s\n", prompt); 62721915Sjkh#endif 628198267Sedwin 629198350Sedwin return (DITEM_LEAVE_MENU); 630198350Sedwin} 631198350Sedwin 632198350Sedwinstatic int 633198350Sedwininstall_zoneinfo(const char *zoneinfo) 634198350Sedwin{ 635198350Sedwin int rv; 636198350Sedwin FILE *f; 637198350Sedwin char path_zoneinfo_file[MAXPATHLEN]; 638198350Sedwin 639198350Sedwin sprintf(path_zoneinfo_file, "%s/%s", path_zoneinfo, zoneinfo); 640198350Sedwin rv = install_zoneinfo_file(path_zoneinfo_file); 641198350Sedwin 642198267Sedwin /* Save knowledge for later */ 643198350Sedwin if ((f = fopen(path_db, "w")) != NULL) { 644198350Sedwin fprintf(f, "%s\n", zoneinfo); 645198267Sedwin fclose(f); 646198267Sedwin } 647198267Sedwin 648198350Sedwin return (rv); 64919872Swollman} 65019872Swollman 65119872Swollmanstatic int 65219872Swollmanconfirm_zone(const char *filename) 65319872Swollman{ 654179530Sjkim char title[64], prompt[64]; 655179530Sjkim time_t t = time(0); 656179530Sjkim struct tm *tm; 657179530Sjkim int rv; 65819872Swollman 65919872Swollman setenv("TZ", filename, 1); 66019872Swollman tzset(); 66119872Swollman tm = localtime(&t); 66219872Swollman 663179530Sjkim snprintf(title, sizeof(title), "Confirmation"); 664179530Sjkim snprintf(prompt, sizeof(prompt), 665179530Sjkim "Does the abbreviation `%s' look reasonable?", tm->tm_zone); 666179530Sjkim rv = !dialog_yesno(title, prompt, 5, 72); 667179530Sjkim return (rv); 66819872Swollman} 66919872Swollman 67019872Swollmanstatic int 67119872Swollmanset_zone_multi(dialogMenuItem *dmi) 67219872Swollman{ 673179530Sjkim struct zone *zp = dmi->data; 674179530Sjkim int rv; 67519872Swollman 67619872Swollman if (!confirm_zone(zp->filename)) 677179530Sjkim return (DITEM_FAILURE | DITEM_RECREATE); 67819872Swollman 679198350Sedwin rv = install_zoneinfo(zp->filename); 680179530Sjkim return (rv); 68119872Swollman} 68219872Swollman 68319872Swollmanstatic int 68419872Swollmanset_zone_whole_country(dialogMenuItem *dmi) 68519872Swollman{ 686179530Sjkim struct country *cp = dmi->data; 687179530Sjkim int rv; 68819872Swollman 68919872Swollman if (!confirm_zone(cp->filename)) 690179530Sjkim return (DITEM_FAILURE | DITEM_RECREATE); 69119872Swollman 692198350Sedwin rv = install_zoneinfo(cp->filename); 693179530Sjkim return (rv); 69419872Swollman} 69519872Swollman 69630763Scharnierstatic void 697179530Sjkimusage(void) 69830763Scharnier{ 699179530Sjkim 700198267Sedwin fprintf(stderr, "usage: tzsetup [-nrs] [zoneinfo file]\n"); 70130763Scharnier exit(1); 70230763Scharnier} 70330763Scharnier 704179530Sjkim#if defined(__sparc64__) 705179530Sjkim#define DIALOG_UTC dialog_yesno 706179530Sjkim#else 707179530Sjkim#define DIALOG_UTC dialog_noyes 708179530Sjkim#endif 709179530Sjkim 71019872Swollmanint 71119872Swollmanmain(int argc, char **argv) 71219872Swollman{ 713179530Sjkim char title[64], prompt[128]; 714198267Sedwin int c, fd, rv, skiputc; 71519872Swollman 716195339Sattilio skiputc = 0; 717198350Sedwin while ((c = getopt(argc, argv, "C:nrs")) != -1) { 71819872Swollman switch(c) { 719198350Sedwin case 'C': 720198350Sedwin chrootenv = optarg; 721198350Sedwin break; 72219872Swollman case 'n': 72319872Swollman reallydoit = 0; 72419872Swollman break; 725198267Sedwin case 'r': 726198267Sedwin reinstall = 1; 727198350Sedwin usedialog = 0; 728198267Sedwin break; 729195339Sattilio case 's': 730195339Sattilio skiputc = 1; 731195339Sattilio break; 73219872Swollman default: 73330763Scharnier usage(); 73419872Swollman } 73519872Swollman } 73619872Swollman 73743544Swollman if (argc - optind > 1) 73830763Scharnier usage(); 73919872Swollman 740198350Sedwin if (chrootenv == NULL) { 741198350Sedwin strcpy(path_zonetab, _PATH_ZONETAB); 742198350Sedwin strcpy(path_iso3166, _PATH_ISO3166); 743198350Sedwin strcpy(path_zoneinfo, _PATH_ZONEINFO); 744198350Sedwin strcpy(path_localtime, _PATH_LOCALTIME); 745198350Sedwin strcpy(path_db, _PATH_DB); 746198350Sedwin strcpy(path_wall_cmos_clock, _PATH_WALL_CMOS_CLOCK); 747198350Sedwin } else { 748198350Sedwin sprintf(path_zonetab, "%s/%s", chrootenv, _PATH_ZONETAB); 749198350Sedwin sprintf(path_iso3166, "%s/%s", chrootenv, _PATH_ISO3166); 750198350Sedwin sprintf(path_zoneinfo, "%s/%s", chrootenv, _PATH_ZONEINFO); 751198350Sedwin sprintf(path_localtime, "%s/%s", chrootenv, _PATH_LOCALTIME); 752198350Sedwin sprintf(path_db, "%s/%s", chrootenv, _PATH_DB); 753198350Sedwin sprintf(path_wall_cmos_clock, "%s/%s", chrootenv, 754198350Sedwin _PATH_WALL_CMOS_CLOCK); 755198350Sedwin } 756198350Sedwin 757198350Sedwin 75849435Sru /* Override the user-supplied umask. */ 759179530Sjkim (void)umask(S_IWGRP | S_IWOTH); 76049435Sru 76119872Swollman read_iso3166_table(); 76219872Swollman read_zones(); 76319872Swollman sort_countries(); 76419872Swollman make_menus(); 76519872Swollman 766198267Sedwin if (reinstall == 1) { 767198267Sedwin FILE *f; 768198267Sedwin char zonefile[MAXPATHLEN]; 769198350Sedwin char path_db[MAXPATHLEN]; 770198267Sedwin 771198350Sedwin zonefile[0] = '\0'; 772198350Sedwin path_db[0] = '\0'; 773198350Sedwin if (chrootenv != NULL) { 774198350Sedwin sprintf(zonefile, "%s/", chrootenv); 775198350Sedwin sprintf(path_db, "%s/", chrootenv); 776198350Sedwin } 777198350Sedwin strcat(zonefile, _PATH_ZONEINFO); 778198350Sedwin strcat(zonefile, "/"); 779198350Sedwin strcat(path_db, _PATH_DB); 780198350Sedwin 781198350Sedwin if ((f = fopen(path_db, "r")) != NULL) { 782198350Sedwin if (fgets(zonefile, sizeof(zonefile), f) != NULL) { 783198267Sedwin zonefile[sizeof(zonefile) - 1] = 0; 784198267Sedwin if (strlen(zonefile) > 0) { 785198267Sedwin zonefile[strlen(zonefile) - 1] = 0; 786198350Sedwin rv = install_zoneinfo(zonefile); 787198267Sedwin exit(rv & ~DITEM_LEAVE_MENU); 788198267Sedwin } 789198350Sedwin errx(1, "Error reading %s.\n", path_db); 790198267Sedwin } 791198267Sedwin fclose(f); 792198267Sedwin errx(1, 793198267Sedwin "Unable to determine earlier installed zoneinfo " 794198350Sedwin "file. Check %s", path_db); 795198267Sedwin } 796198350Sedwin errx(1, "Cannot open %s for reading. Does it exist?", path_db); 797198267Sedwin } 798198267Sedwin 799198350Sedwin /* 800198350Sedwin * If the arguments on the command-line do not specify a file, 801198350Sedwin * then interpret it as a zoneinfo name 802198350Sedwin */ 803198350Sedwin if (optind == argc - 1) { 804198350Sedwin struct stat sb; 805198350Sedwin 806198350Sedwin if (stat(argv[optind], &sb) != 0) { 807198350Sedwin usedialog = 0; 808198350Sedwin rv = install_zoneinfo(argv[optind]); 809198350Sedwin exit(rv & ~DITEM_LEAVE_MENU); 810198350Sedwin } 811198350Sedwin /* FALLTHROUGH */ 812198350Sedwin } 813198350Sedwin 81419872Swollman init_dialog(); 815195339Sattilio if (skiputc == 0) { 816195339Sattilio snprintf(title, sizeof(title), 817195339Sattilio "Select local or UTC (Greenwich Mean Time) clock"); 818195339Sattilio snprintf(prompt, sizeof(prompt), 819195339Sattilio "Is this machine's CMOS clock set to UTC? " 820195339Sattilio "If it is set to local time,\n" 821195339Sattilio "or you don't know, please choose NO here!"); 822195339Sattilio if (!DIALOG_UTC(title, prompt, 7, 72)) { 823195339Sattilio if (reallydoit) 824195339Sattilio unlink(_PATH_WALL_CMOS_CLOCK); 825195339Sattilio } else { 826195339Sattilio if (reallydoit) { 827195339Sattilio fd = open(_PATH_WALL_CMOS_CLOCK, 828195339Sattilio O_WRONLY | O_CREAT | O_TRUNC, 829195339Sattilio S_IRUSR | S_IRGRP | S_IROTH); 830198254Sedwin if (fd < 0) { 831198254Sedwin end_dialog(); 832195339Sattilio err(1, "create %s", 833195339Sattilio _PATH_WALL_CMOS_CLOCK); 834198254Sedwin } 835195339Sattilio close(fd); 836195339Sattilio } 83741852Speter } 838195339Sattilio dialog_clear_norefresh(); 83932394Ssteve } 84043544Swollman if (optind == argc - 1) { 841179530Sjkim snprintf(title, sizeof(title), "Default timezone provided"); 842179530Sjkim snprintf(prompt, sizeof(prompt), 843179530Sjkim "\nUse the default `%s' zone?", argv[optind]); 844179530Sjkim if (!dialog_yesno(title, prompt, 7, 72)) { 845198350Sedwin rv = install_zoneinfo_file(argv[optind]); 84643544Swollman dialog_clear(); 84743544Swollman end_dialog(); 848198267Sedwin exit(rv & ~DITEM_LEAVE_MENU); 84943544Swollman } 85043544Swollman dialog_clear_norefresh(); 85143544Swollman } 852179530Sjkim snprintf(title, sizeof(title), "Time Zone Selector"); 853179530Sjkim snprintf(prompt, sizeof(prompt), "Select a region"); 854179530Sjkim dialog_menu(title, prompt, -1, -1, NCONTINENTS, -NCONTINENTS, 855179530Sjkim continents, 0, NULL, NULL); 85643544Swollman 85722815Sjkh dialog_clear(); 85819872Swollman end_dialog(); 859179530Sjkim return (0); 86019872Swollman} 861