diff options
Diffstat (limited to 'usr.sbin/tzsetup')
| -rw-r--r-- | usr.sbin/tzsetup/Makefile | 20 | ||||
| -rw-r--r-- | usr.sbin/tzsetup/Makefile.depend | 16 | ||||
| -rw-r--r-- | usr.sbin/tzsetup/baseline | 670 | ||||
| -rw-r--r-- | usr.sbin/tzsetup/tzsetup.8 | 163 | ||||
| -rw-r--r-- | usr.sbin/tzsetup/tzsetup.c | 996 | 
5 files changed, 1865 insertions, 0 deletions
| diff --git a/usr.sbin/tzsetup/Makefile b/usr.sbin/tzsetup/Makefile new file mode 100644 index 000000000000..f20834baf25d --- /dev/null +++ b/usr.sbin/tzsetup/Makefile @@ -0,0 +1,20 @@ +.include <src.opts.mk> + +PROG=	tzsetup +MAN=	tzsetup.8 + +CFLAGS+= -I. + +.if !defined(BOOTSTRAPPING) +WARNS?=	3 +CFLAGS+=	-I${SRCTOP}/contrib/bsddialog/lib -DHAVE_BSDDIALOG +LIBADD=		bsddialog +.endif + +ZONETAB=	${SRCTOP}/contrib/tzdata/zone1970.tab +baseline: ${PROG} ${ZONETAB} +	${.OBJDIR}/${PROG} -d ${ZONETAB} > ${.CURDIR}/baseline + +.PHONY:	baseline + +.include <bsd.prog.mk> diff --git a/usr.sbin/tzsetup/Makefile.depend b/usr.sbin/tzsetup/Makefile.depend new file mode 100644 index 000000000000..63fe0b5e1069 --- /dev/null +++ b/usr.sbin/tzsetup/Makefile.depend @@ -0,0 +1,16 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ +	include \ +	include/xlocale \ +	lib/${CSU_DIR} \ +	lib/libbsddialog \ +	lib/libc \ +	lib/libcompiler_rt \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/tzsetup/baseline b/usr.sbin/tzsetup/baseline new file mode 100644 index 000000000000..de9b5f638d09 --- /dev/null +++ b/usr.sbin/tzsetup/baseline @@ -0,0 +1,670 @@ +AF:Afghanistan +  Asia:Asia/Kabul +AL:Albania +  Europe:Europe/Tirane +DZ:Algeria +  Africa:Africa/Algiers +AS:American Samoa +  Pacific:Pacific/Pago_Pago +AD:Andorra +  Europe:Europe/Andorra +AO:Angola +  Africa:Africa/Lagos +AI:Anguilla +  America:America/Puerto_Rico +AQ:Antarctica +  Antarctica:Antarctica/Casey +  Antarctica:Antarctica/Davis +  Antarctica:Antarctica/Mawson +  Antarctica:Antarctica/Palmer +  Antarctica:Antarctica/Rothera +  Antarctica:Antarctica/Troll +  Antarctica:Asia/Urumqi +  Antarctica:Pacific/Auckland +  Antarctica:Pacific/Port_Moresby +  Antarctica:Asia/Riyadh +AG:Antigua and Barbuda +  America:America/Puerto_Rico +AR:Argentina +  America:America/Argentina/Buenos_Aires +  America:America/Argentina/Cordoba +  America:America/Argentina/Salta +  America:America/Argentina/Jujuy +  America:America/Argentina/Tucuman +  America:America/Argentina/Catamarca +  America:America/Argentina/La_Rioja +  America:America/Argentina/San_Juan +  America:America/Argentina/Mendoza +  America:America/Argentina/San_Luis +  America:America/Argentina/Rio_Gallegos +  America:America/Argentina/Ushuaia +AM:Armenia +  Asia:Asia/Yerevan +AW:Aruba +  America:America/Puerto_Rico +AU:Australia +  Australia:Australia/Lord_Howe +  Antarctica:Antarctica/Macquarie +  Australia:Australia/Hobart +  Australia:Australia/Melbourne +  Australia:Australia/Sydney +  Australia:Australia/Broken_Hill +  Australia:Australia/Brisbane +  Australia:Australia/Lindeman +  Australia:Australia/Adelaide +  Australia:Australia/Darwin +  Australia:Australia/Perth +  Australia:Australia/Eucla +AT:Austria +  Europe:Europe/Vienna +AZ:Azerbaijan +  Asia:Asia/Baku +BS:Bahamas +  America:America/Toronto +BH:Bahrain +  Asia:Asia/Qatar +BD:Bangladesh +  Asia:Asia/Dhaka +BB:Barbados +  America:America/Barbados +BY:Belarus +  Europe:Europe/Minsk +BE:Belgium +  Europe:Europe/Brussels +BZ:Belize +  America:America/Belize +BJ:Benin +  Africa:Africa/Lagos +BM:Bermuda +  Atlantic:Atlantic/Bermuda +BT:Bhutan +  Asia:Asia/Thimphu +BO:Bolivia, Plurinational State of +  America:America/La_Paz +BQ:Bonaire, Sint Eustatius and Saba +  America:America/Puerto_Rico +BA:Bosnia and Herzegovina +  Europe:Europe/Belgrade +BW:Botswana +  Africa:Africa/Maputo +BV:Bouvet Island +BR:Brazil +  America:America/Noronha +  America:America/Belem +  America:America/Fortaleza +  America:America/Recife +  America:America/Araguaina +  America:America/Maceio +  America:America/Bahia +  America:America/Sao_Paulo +  America:America/Campo_Grande +  America:America/Cuiaba +  America:America/Santarem +  America:America/Porto_Velho +  America:America/Boa_Vista +  America:America/Manaus +  America:America/Eirunepe +  America:America/Rio_Branco +IO:British Indian Ocean Territory +  Indian:Indian/Chagos +BN:Brunei Darussalam +  Asia:Asia/Kuching +BG:Bulgaria +  Europe:Europe/Sofia +BF:Burkina Faso +  Africa:Africa/Abidjan +BI:Burundi +  Africa:Africa/Maputo +CV:Cabo Verde +  Atlantic:Atlantic/Cape_Verde +KH:Cambodia +  Asia:Asia/Bangkok +CM:Cameroon +  Africa:Africa/Lagos +CA:Canada +  America:America/St_Johns +  America:America/Halifax +  America:America/Glace_Bay +  America:America/Moncton +  America:America/Goose_Bay +  America:America/Toronto +  America:America/Iqaluit +  America:America/Winnipeg +  America:America/Resolute +  America:America/Rankin_Inlet +  America:America/Regina +  America:America/Swift_Current +  America:America/Edmonton +  America:America/Cambridge_Bay +  America:America/Inuvik +  America:America/Dawson_Creek +  America:America/Fort_Nelson +  America:America/Whitehorse +  America:America/Dawson +  America:America/Vancouver +  America:America/Panama +  America:America/Puerto_Rico +  America:America/Phoenix +KY:Cayman Islands +  America:America/Panama +CF:Central African Republic +  Africa:Africa/Lagos +TD:Chad +  Africa:Africa/Ndjamena +CL:Chile +  America:America/Santiago +  America:America/Punta_Arenas +  Pacific:Pacific/Easter +CN:China +  Asia:Asia/Shanghai +  Asia:Asia/Urumqi +CX:Christmas Island +  Indian:Asia/Bangkok +CC:Cocos (Keeling) Islands +  Indian:Asia/Yangon +CO:Colombia +  America:America/Bogota +KM:Comoros +  Indian:Africa/Nairobi +CG:Congo +  Africa:Africa/Lagos +CD:Congo, Democratic Republic of the +  Africa:Africa/Maputo +  Africa:Africa/Lagos +CK:Cook Islands +  Pacific:Pacific/Rarotonga +CR:Costa Rica +  America:America/Costa_Rica +HR:Croatia +  Europe:Europe/Belgrade +CU:Cuba +  America:America/Havana +CW:Curaçao +  America:America/Puerto_Rico +CY:Cyprus +  Asia:Asia/Nicosia +  Asia:Asia/Famagusta +CZ:Czech Republic +  Europe:Europe/Prague +CI:Côte d'Ivoire +  Africa:Africa/Abidjan +DK:Denmark +  Europe:Europe/Berlin +DJ:Djibouti +  Africa:Africa/Nairobi +DM:Dominica +  America:America/Puerto_Rico +DO:Dominican Republic +  America:America/Santo_Domingo +EC:Ecuador +  America:America/Guayaquil +  Pacific:Pacific/Galapagos +EG:Egypt +  Africa:Africa/Cairo +SV:El Salvador +  America:America/El_Salvador +GQ:Equatorial Guinea +  Africa:Africa/Lagos +ER:Eritrea +  Africa:Africa/Nairobi +EE:Estonia +  Europe:Europe/Tallinn +SZ:Eswatini +  Africa:Africa/Johannesburg +ET:Ethiopia +  Africa:Africa/Nairobi +FK:Falkland Islands (Malvinas) +  Atlantic:Atlantic/Stanley +FO:Faroe Islands +  Atlantic:Atlantic/Faroe +FJ:Fiji +  Pacific:Pacific/Fiji +FI:Finland +  Europe:Europe/Helsinki +FR:France +  Europe:Europe/Paris +GF:French Guiana +  America:America/Cayenne +PF:French Polynesia +  Pacific:Pacific/Tahiti +  Pacific:Pacific/Marquesas +  Pacific:Pacific/Gambier +TF:French Southern Territories +  Asia:Asia/Dubai +  Indian:Indian/Maldives +GA:Gabon +  Africa:Africa/Lagos +GM:Gambia +  Africa:Africa/Abidjan +GE:Georgia +  Asia:Asia/Tbilisi +DE:Germany +  Europe:Europe/Zurich +  Europe:Europe/Berlin +GH:Ghana +  Africa:Africa/Abidjan +GI:Gibraltar +  Europe:Europe/Gibraltar +GR:Greece +  Europe:Europe/Athens +GL:Greenland +  America:America/Nuuk +  America:America/Danmarkshavn +  America:America/Scoresbysund +  America:America/Thule +GD:Grenada +  America:America/Puerto_Rico +GP:Guadeloupe +  America:America/Puerto_Rico +GU:Guam +  Pacific:Pacific/Guam +GT:Guatemala +  America:America/Guatemala +GG:Guernsey +  Europe:Europe/London +GN:Guinea +  Africa:Africa/Abidjan +GW:Guinea-Bissau +  Africa:Africa/Bissau +GY:Guyana +  America:America/Guyana +HT:Haiti +  America:America/Port-au-Prince +HM:Heard Island and McDonald Islands +VA:Holy See +  Europe:Europe/Rome +HN:Honduras +  America:America/Tegucigalpa +HK:Hong Kong +  Asia:Asia/Hong_Kong +HU:Hungary +  Europe:Europe/Budapest +IS:Iceland +  Atlantic:Africa/Abidjan +IN:India +  Asia:Asia/Kolkata +ID:Indonesia +  Asia:Asia/Jakarta +  Asia:Asia/Pontianak +  Asia:Asia/Makassar +  Asia:Asia/Jayapura +IR:Iran (Islamic Republic of) +  Asia:Asia/Tehran +IQ:Iraq +  Asia:Asia/Baghdad +IE:Ireland +  Europe:Europe/Dublin +IM:Isle of Man +  Europe:Europe/London +IL:Israel +  Asia:Asia/Jerusalem +IT:Italy +  Europe:Europe/Rome +JM:Jamaica +  America:America/Jamaica +JP:Japan +  Asia:Asia/Tokyo +JE:Jersey +  Europe:Europe/London +JO:Jordan +  Asia:Asia/Amman +KZ:Kazakhstan +  Asia:Asia/Almaty +  Asia:Asia/Qyzylorda +  Asia:Asia/Qostanay +  Asia:Asia/Aqtobe +  Asia:Asia/Aqtau +  Asia:Asia/Atyrau +  Asia:Asia/Oral +KE:Kenya +  Africa:Africa/Nairobi +KI:Kiribati +  Pacific:Pacific/Tarawa +  Pacific:Pacific/Kanton +  Pacific:Pacific/Kiritimati +KP:Korea (Democratic People's Republic of) +  Asia:Asia/Pyongyang +KR:Korea (Republic of) +  Asia:Asia/Seoul +KW:Kuwait +  Asia:Asia/Riyadh +KG:Kyrgyzstan +  Asia:Asia/Bishkek +LA:Lao People's Democratic Republic +  Asia:Asia/Bangkok +LV:Latvia +  Europe:Europe/Riga +LB:Lebanon +  Asia:Asia/Beirut +LS:Lesotho +  Africa:Africa/Johannesburg +LR:Liberia +  Africa:Africa/Monrovia +LY:Libya +  Africa:Africa/Tripoli +LI:Liechtenstein +  Europe:Europe/Zurich +LT:Lithuania +  Europe:Europe/Vilnius +LU:Luxembourg +  Europe:Europe/Brussels +MO:Macao +  Asia:Asia/Macau +MK:Macedonia (the former Yugoslav Republic of) +  Europe:Europe/Belgrade +MG:Madagascar +  Indian:Africa/Nairobi +MW:Malawi +  Africa:Africa/Maputo +MY:Malaysia +  Asia:Asia/Kuching +  Asia:Asia/Singapore +MV:Maldives +  Indian:Indian/Maldives +ML:Mali +  Africa:Africa/Abidjan +MT:Malta +  Europe:Europe/Malta +MH:Marshall Islands +  Pacific:Pacific/Tarawa +  Pacific:Pacific/Kwajalein +MQ:Martinique +  America:America/Martinique +MR:Mauritania +  Africa:Africa/Abidjan +MU:Mauritius +  Indian:Indian/Mauritius +YT:Mayotte +  Indian:Africa/Nairobi +MX:Mexico +  America:America/Mexico_City +  America:America/Cancun +  America:America/Merida +  America:America/Monterrey +  America:America/Matamoros +  America:America/Chihuahua +  America:America/Ciudad_Juarez +  America:America/Ojinaga +  America:America/Mazatlan +  America:America/Bahia_Banderas +  America:America/Hermosillo +  America:America/Tijuana +FM:Micronesia (Federated States of) +  Pacific:Pacific/Kosrae +  Pacific:Pacific/Port_Moresby +  Pacific:Pacific/Guadalcanal +MD:Moldova (Republic of) +  Europe:Europe/Chisinau +MC:Monaco +  Europe:Europe/Paris +MN:Mongolia +  Asia:Asia/Ulaanbaatar +  Asia:Asia/Hovd +  Asia:Asia/Choibalsan +ME:Montenegro +  Europe:Europe/Belgrade +MS:Montserrat +  America:America/Puerto_Rico +MA:Morocco +  Africa:Africa/Casablanca +MZ:Mozambique +  Africa:Africa/Maputo +MM:Myanmar +  Asia:Asia/Yangon +NA:Namibia +  Africa:Africa/Windhoek +NR:Nauru +  Pacific:Pacific/Nauru +NP:Nepal +  Asia:Asia/Kathmandu +NL:Netherlands +  Europe:Europe/Brussels +NC:New Caledonia +  Pacific:Pacific/Noumea +NZ:New Zealand +  Pacific:Pacific/Auckland +  Pacific:Pacific/Chatham +NI:Nicaragua +  America:America/Managua +NE:Niger +  Africa:Africa/Lagos +NG:Nigeria +  Africa:Africa/Lagos +NU:Niue +  Pacific:Pacific/Niue +NF:Norfolk Island +  Pacific:Pacific/Norfolk +MP:Northern Mariana Islands +  Pacific:Pacific/Guam +NO:Norway +  Europe:Europe/Berlin +OM:Oman +  Asia:Asia/Dubai +PK:Pakistan +  Asia:Asia/Karachi +PW:Palau +  Pacific:Pacific/Palau +PS:Palestine, State of +  Asia:Asia/Gaza +  Asia:Asia/Hebron +PA:Panama +  America:America/Panama +PG:Papua New Guinea +  Pacific:Pacific/Port_Moresby +  Pacific:Pacific/Bougainville +PY:Paraguay +  America:America/Asuncion +PE:Peru +  America:America/Lima +PH:Philippines +  Asia:Asia/Manila +PN:Pitcairn +  Pacific:Pacific/Pitcairn +PL:Poland +  Europe:Europe/Warsaw +PT:Portugal +  Europe:Europe/Lisbon +  Atlantic:Atlantic/Madeira +  Atlantic:Atlantic/Azores +PR:Puerto Rico +  America:America/Puerto_Rico +QA:Qatar +  Asia:Asia/Qatar +RO:Romania +  Europe:Europe/Bucharest +RU:Russian Federation +  Europe:Europe/Kaliningrad +  Europe:Europe/Moscow +  Europe:Europe/Simferopol +  Europe:Europe/Kirov +  Europe:Europe/Volgograd +  Europe:Europe/Astrakhan +  Europe:Europe/Saratov +  Europe:Europe/Ulyanovsk +  Europe:Europe/Samara +  Asia:Asia/Yekaterinburg +  Asia:Asia/Omsk +  Asia:Asia/Novosibirsk +  Asia:Asia/Barnaul +  Asia:Asia/Tomsk +  Asia:Asia/Novokuznetsk +  Asia:Asia/Krasnoyarsk +  Asia:Asia/Irkutsk +  Asia:Asia/Chita +  Asia:Asia/Yakutsk +  Asia:Asia/Khandyga +  Asia:Asia/Vladivostok +  Asia:Asia/Ust-Nera +  Asia:Asia/Magadan +  Asia:Asia/Sakhalin +  Asia:Asia/Srednekolymsk +  Asia:Asia/Kamchatka +  Asia:Asia/Anadyr +RW:Rwanda +  Africa:Africa/Maputo +RE:Réunion +  Asia:Asia/Dubai +BL:Saint Barthélemy +  America:America/Puerto_Rico +SH:Saint Helena Ascension and Tristan da Cunha +  Atlantic:Africa/Abidjan +KN:Saint Kitts and Nevis +  America:America/Puerto_Rico +LC:Saint Lucia +  America:America/Puerto_Rico +MF:Saint Martin (French part) +  America:America/Puerto_Rico +PM:Saint Pierre and Miquelon +  America:America/Miquelon +VC:Saint Vincent and the Grenadines +  America:America/Puerto_Rico +WS:Samoa +  Pacific:Pacific/Apia +SM:San Marino +  Europe:Europe/Rome +ST:Sao Tome and Principe +  Africa:Africa/Sao_Tome +SA:Saudi Arabia +  Asia:Asia/Riyadh +SN:Senegal +  Africa:Africa/Abidjan +RS:Serbia +  Europe:Europe/Belgrade +SC:Seychelles +  Asia:Asia/Dubai +SL:Sierra Leone +  Africa:Africa/Abidjan +SG:Singapore +  Asia:Asia/Singapore +SX:Sint Maarten (Dutch part) +  America:America/Puerto_Rico +SK:Slovakia +  Europe:Europe/Prague +SI:Slovenia +  Europe:Europe/Belgrade +SB:Solomon Islands +  Pacific:Pacific/Guadalcanal +SO:Somalia +  Africa:Africa/Nairobi +ZA:South Africa +  Africa:Africa/Johannesburg +GS:South Georgia and the South Sandwich Islands +  Atlantic:Atlantic/South_Georgia +SS:South Sudan +  Africa:Africa/Juba +ES:Spain +  Europe:Europe/Madrid +  Africa:Africa/Ceuta +  Atlantic:Atlantic/Canary +LK:Sri Lanka +  Asia:Asia/Colombo +SD:Sudan +  Africa:Africa/Khartoum +SR:Suriname +  America:America/Paramaribo +SJ:Svalbard and Jan Mayen +  Arctic:Europe/Berlin +SE:Sweden +  Europe:Europe/Berlin +CH:Switzerland +  Europe:Europe/Zurich +SY:Syrian Arab Republic +  Asia:Asia/Damascus +TW:Taiwan +  Asia:Asia/Taipei +TJ:Tajikistan +  Asia:Asia/Dushanbe +TZ:Tanzania United Republic of +  Africa:Africa/Nairobi +TH:Thailand +  Asia:Asia/Bangkok +TL:Timor-Leste +  Asia:Asia/Dili +TG:Togo +  Africa:Africa/Abidjan +TK:Tokelau +  Pacific:Pacific/Fakaofo +TO:Tonga +  Pacific:Pacific/Tongatapu +TT:Trinidad and Tobago +  America:America/Puerto_Rico +TN:Tunisia +  Africa:Africa/Tunis +TR:Turkey +  Europe:Europe/Istanbul +TM:Turkmenistan +  Asia:Asia/Ashgabat +TC:Turks and Caicos Islands +  America:America/Grand_Turk +TV:Tuvalu +  Pacific:Pacific/Tarawa +UG:Uganda +  Africa:Africa/Nairobi +UA:Ukraine +  Europe:Europe/Simferopol +  Europe:Europe/Kyiv +AE:United Arab Emirates +  Asia:Asia/Dubai +GB:United Kingdom of Great Britain and Northern Ireland +  Europe:Europe/London +UM:United States Minor Outlying Islands +  Pacific:Pacific/Pago_Pago +  Pacific:Pacific/Tarawa +US:United States of America +  America:America/New_York +  America:America/Detroit +  America:America/Kentucky/Louisville +  America:America/Kentucky/Monticello +  America:America/Indiana/Indianapolis +  America:America/Indiana/Vincennes +  America:America/Indiana/Winamac +  America:America/Indiana/Marengo +  America:America/Indiana/Petersburg +  America:America/Indiana/Vevay +  America:America/Chicago +  America:America/Indiana/Tell_City +  America:America/Indiana/Knox +  America:America/Menominee +  America:America/North_Dakota/Center +  America:America/North_Dakota/New_Salem +  America:America/North_Dakota/Beulah +  America:America/Denver +  America:America/Boise +  America:America/Phoenix +  America:America/Los_Angeles +  America:America/Anchorage +  America:America/Juneau +  America:America/Sitka +  America:America/Metlakatla +  America:America/Yakutat +  America:America/Nome +  America:America/Adak +  Pacific:Pacific/Honolulu +UY:Uruguay +  America:America/Montevideo +UZ:Uzbekistan +  Asia:Asia/Samarkand +  Asia:Asia/Tashkent +VU:Vanuatu +  Pacific:Pacific/Efate +VE:Venezuela, Bolivarian Republic of +  America:America/Caracas +VN:Viet Nam +  Asia:Asia/Bangkok +  Asia:Asia/Ho_Chi_Minh +VG:Virgin Islands (British) +  America:America/Puerto_Rico +VI:Virgin Islands (U.S.) +  America:America/Puerto_Rico +WF:Wallis and Futuna +  Pacific:Pacific/Tarawa +EH:Western Sahara +  Africa:Africa/El_Aaiun +YE:Yemen +  Asia:Asia/Riyadh +ZM:Zambia +  Africa:Africa/Maputo +ZW:Zimbabwe +  Africa:Africa/Maputo +AX:Åland Islands +  Europe:Europe/Helsinki diff --git a/usr.sbin/tzsetup/tzsetup.8 b/usr.sbin/tzsetup/tzsetup.8 new file mode 100644 index 000000000000..3fd463c31ee5 --- /dev/null +++ b/usr.sbin/tzsetup/tzsetup.8 @@ -0,0 +1,163 @@ +.\" Copyright (c) 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\"    notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\"    notice, this list of conditions and the following disclaimer in the +.\"    documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd June 14, 2024 +.Dt TZSETUP 8 +.Os +.Sh NAME +.Nm tzsetup +.Nd set local timezone +.Sh SYNOPSIS +.Nm +.Op Fl nrs +.Op Fl C Ar chroot_directory +.Op Ar zoneinfo_file | zoneinfo_name +.Sh DESCRIPTION +The +.Nm +utility reads a database of timezone information and presents a menu +allowing the user to select a specific zone without knowing the details +of the database layout. +The selected zone is installed as the system +default zone. +The +.Nm +utility also determines whether any adjustment is necessary for systems where +the hardware clock does not keep UTC. +.Pp +The following options are available: +.Bl -tag -offset indent -width Fl +.It Fl C Ar chroot_directory +Open all files and directories relative to +.Ar chroot_directory . +.It Fl n +Do not create or symlink files. +.It Fl r +Reinstall the zoneinfo file installed last time. +The name is obtained from +.Pa /var/db/zoneinfo . +.It Fl s +Skip the initial question about adjusting the clock if not set to UTC. +.Nm +will neither create nor delete +.Pa /etc/wall_cmos_clock . +On a newly installed system, the hardware clock will keep UTC. +.El +.Pp +It is possible to short-circuit the menu system by specifying the +location of a +.Ar zoneinfo_file +or the name of the +.Ar zoneinfo_name +on the command line; this is intended mainly for pre-configured installation +scripts or people who know which zoneinfo they want to install. +.Sh TIMEZONE DATABASE +The contents of the timezone database are indexed by +.Pa /usr/share/zoneinfo/zone1970.tab . +This file lists, for each timezone data file, the ISO 3166 territory code, +approximate geographical coordinates (in ISO 6709 format), +and location within the territory. +.Pp +The maintainers of the database maintain the following policies: +.Bl -enum -offset indent +.It +At least one zone for every country or inhabited geographical territory. +.It +One zone for every distinct, documented timezone history since the +beginning of the +.Ux +epoch (January 1, 1970, GMT). +.It +Each zone is named for the most populous city therein. +(Where possible, +the database includes pre-1970 history for its city.) +.El +.Pp +The source code to the database +.Pq Pa /usr/src/contrib/tzdata/[a-z]* +contains many additional comments and documentation references for the +historically minded. +.Sh FILES +.Bl -tag -width ".Pa /usr/share/zoneinfo/zone1970.tab" -compact +.It Pa /etc/localtime +current time zone file +.It Pa /etc/wall_cmos_clock +see +.Xr adjkerntz 8 +.It Pa /usr/share/misc/iso3166 +mapping of ISO 3166 territory codes to names +.It Pa /usr/share/zoneinfo +directory for zoneinfo files +.It Pa /usr/share/zoneinfo/zone1970.tab +mapping of timezone file to country and location +.It Pa /var/db/zoneinfo +saved name of the timezone file installed last +.El +.Sh EXAMPLES +Normal usage, to select the right zoneinfo file via the dialog-based +user interface: +.Pp +.Dl # tzsetup +.Pp +Install the file +.Pa /usr/share/zoneinfo/Australia/Sydney : +.Pp +.Dl "# tzsetup /usr/share/zoneinfo/Australia/Sydney" +.Pp +Install the zoneinfo file for Australia/Sydney, assumed to be located in +.Pa /usr/share/zoneinfo : +.Pp +.Dl "# tzsetup Australia/Sydney" +.Pp +After a reinstall of the zoneinfo files, you can reinstall the +latest installed zoneinfo file (as specified in +.Pa /var/db/zoneinfo ) : +.Pp +.Dl "# tzsetup -r" +.Sh SEE ALSO +.Xr date 1 , +.Xr adjtime 2 , +.Xr ctime 3 , +.Xr timezone 3 , +.Xr tzfile 5 , +.Xr adjkerntz 8 , +.Xr zdump 8 , +.Xr zic 8 +.Sh DISCLAIMER +The representation of certain localities as being associated with certain +countries and/or territories is for the purposes of identification only, +and does not imply any endorsement or rejection on the part of the +.Fx +Project of the territorial claims of any entity. +.Sh BUGS +Programs which are already running when +.Nm +creates or updates +.Pa /etc/localtime +will not reflect the updated timezone. +When the system is first configured for a non-UTC +hardware clock, it is necessary to run +.Xr adjkerntz 8 +(which normally happens as a part of system startup) in order to update +the kernel's idea of the correct timezone offset. diff --git a/usr.sbin/tzsetup/tzsetup.c b/usr.sbin/tzsetup/tzsetup.c new file mode 100644 index 000000000000..914eeb145a09 --- /dev/null +++ b/usr.sbin/tzsetup/tzsetup.c @@ -0,0 +1,996 @@ +/* + * Copyright 1996 Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission.  M.I.T. makes + * no representations about the suitability of this software for any + * purpose.  It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Second attempt at a `tzmenu' program, using the separate description + * files provided in newer tzdata releases. + */ + +/* + * When making changes to parser code, run baseline target, check that there are + * no unintended changes and commit updated file. + */ + +#include <sys/cdefs.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <sys/fcntl.h> +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/stat.h> +#include <sys/sysctl.h> + +#ifdef HAVE_BSDDIALOG +#include <bsddialog.h> +#include <locale.h> +#endif + +#define	_PATH_ZONETAB		"/usr/share/zoneinfo/zone1970.tab" +#define	_PATH_ISO3166		"/usr/share/misc/iso3166" +#define	_PATH_ZONEINFO		"/usr/share/zoneinfo" +#define	_PATH_LOCALTIME		"/etc/localtime" +#define	_PATH_DB		"/var/db/zoneinfo" +#define	_PATH_WALL_CMOS_CLOCK	"/etc/wall_cmos_clock" + +#ifdef PATH_MAX +#define	SILLY_BUFFER_SIZE	(2 * PATH_MAX) +#else +#warning "Somebody needs to fix this to dynamically size this buffer." +#define	SILLY_BUFFER_SIZE	2048 +#endif + +/* special return codes for `fire' actions */ +#define DITEM_FAILURE           1 + +/* flags - returned in upper 16 bits of return status */ +#define DITEM_LEAVE_MENU        (1 << 16) +#define DITEM_RECREATE          (1 << 18) + +static char	path_zonetab[MAXPATHLEN], path_iso3166[MAXPATHLEN], +		path_zoneinfo[MAXPATHLEN], path_localtime[MAXPATHLEN], +		path_db[MAXPATHLEN], path_wall_cmos_clock[MAXPATHLEN]; + +static int reallydoit = 1; +static int reinstall = 0; +static char *chrootenv = NULL; + +static void	usage(void); +static int	install_zoneinfo(const char *zoneinfo); +static void	message_zoneinfo_file(const char *title, char *prompt); +static int	install_zoneinfo_file(const char *zoneinfo_file); + +#ifdef HAVE_BSDDIALOG +static struct bsddialog_conf conf; + +/* for use in describing more exotic behaviors */ +typedef struct dialogMenuItem { +	char *prompt; +	char *title; +	int (*fire)(struct dialogMenuItem *self); +	void *data; +} dialogMenuItem; + +static int +xdialog_menu(char *title, char *cprompt, int item_no, dialogMenuItem *ditems) +{ +	int i, result, menurows, choice = 0; +	struct bsddialog_menuitem *listitems; + +	/* initialize list items */ +	listitems = calloc(item_no + 1, sizeof(struct bsddialog_menuitem)); +	if (listitems == NULL) +		errx(1, "Failed to allocate memory in xdialog_menu"); +	for (i = 0; i < item_no; i++) { +		listitems[i].prefix = ""; +		listitems[i].depth = 0; +		listitems[i].bottomdesc = ""; +		listitems[i].on = false; +		listitems[i].name = ditems[i].prompt; +		listitems[i].desc = ditems[i].title; +	} + +again: +	conf.title = title; +	menurows = item_no < 16 ? item_no : 16; +	result = bsddialog_menu(&conf, cprompt, BSDDIALOG_AUTOSIZE, +	    BSDDIALOG_AUTOSIZE, menurows, item_no, listitems, &choice); +	switch (result) { +	case BSDDIALOG_ESC: +		result = -1; +		break; +	case BSDDIALOG_OK: +		if (ditems[choice].fire != NULL) { +			int status; + +			status = ditems[choice].fire(ditems + choice); +			if (status & DITEM_RECREATE) { +				goto again; +			} +		} +		result = 0; +		break; +	case BSDDIALOG_CANCEL: +	default: +		result = 1; +		break; +	} + +	free(listitems); +	return (result); +} + +static int usedialog = 1; + +static int	confirm_zone(const char *filename); +static int	continent_country_menu(dialogMenuItem *); +static int	set_zone(dialogMenuItem *); +static int	set_zone_menu(dialogMenuItem *); +static int	set_zone_utc(void); + +struct continent { +	dialogMenuItem *menu; +	int		nitems; +}; + +static struct continent	africa, america, antarctica, arctic, asia, atlantic; +static struct continent	australia, europe, indian, pacific, utc; + +static struct continent_names { +	const char	*name; +	struct continent *continent; +} continent_names[] = { +	{ "UTC",	&utc }, +	{ "Africa",	&africa }, +	{ "America",	&america }, +	{ "Antarctica",	&antarctica }, +	{ "Arctic",	&arctic }, +	{ "Asia",	&asia }, +	{ "Atlantic",	&atlantic }, +	{ "Australia",	&australia }, +	{ "Europe",	&europe }, +	{ "Indian",	&indian }, +	{ "Pacific",	&pacific }, +}; + +static struct continent_items { +	char		prompt[3]; +	char		title[30]; +} continent_items[] = { +	{ "0",	"UTC" }, +	{ "1",	"Africa" }, +	{ "2",	"America -- North and South" }, +	{ "3",	"Antarctica" }, +	{ "4",	"Arctic Ocean" }, +	{ "5",	"Asia" }, +	{ "6",	"Atlantic Ocean" }, +	{ "7",	"Australia" }, +	{ "8",	"Europe" }, +	{ "9",	"Indian Ocean" }, +	{ "10",	"Pacific Ocean" }, +}; + +#define	NCONTINENTS	\ +    (int)((sizeof(continent_items)) / (sizeof(continent_items[0]))) +static dialogMenuItem continents[NCONTINENTS]; + +#define	OCEANP(x)	((x) == 4 || (x) == 6 || (x) == 9 || (x) == 10) + +static int +continent_country_menu(dialogMenuItem *continent) +{ +	char		title[64], prompt[64]; +	struct continent *contp = continent->data; +	int		isocean = OCEANP(continent - continents); +	int		rv; + +	if (strcmp(continent->title, "UTC") == 0) +		return (set_zone_utc()); + +	/* It's amazing how much good grammar really matters... */ +	if (!isocean) { +		snprintf(title, sizeof(title), "Countries in %s", +		    continent->title); +		snprintf(prompt, sizeof(prompt), "Select a country or region"); +	} else { +		snprintf(title, sizeof(title), "Islands and groups in the %s", +		    continent->title); +		snprintf(prompt, sizeof(prompt), "Select an island or group"); +	} + +	rv = xdialog_menu(title, prompt, contp->nitems, contp->menu); +	return (rv == 0 ? DITEM_LEAVE_MENU : DITEM_RECREATE); +} + +static struct continent * +find_continent(int lineno, const char *name) +{ +	char		*cname, *cp; +	int		i; + +	/* +	 * Both normal (the ones in zone filename, e.g. Europe/Andorra) and +	 * override (e.g. Atlantic/) entries should contain '/'. +	 */ +	cp = strdup(name); +	if (cp == NULL) +		err(1, "strdup"); +	cname = strsep(&cp, "/"); +	if (cp == NULL) +		errx(1, "%s:%d: invalid entry `%s'", path_zonetab, lineno, +		    cname); + +	for (i = 0; i < NCONTINENTS; i++) +		if (strcmp(cname, continent_names[i].name) == 0) { +			free(cname); +			return (continent_names[i].continent); +		} + +	errx(1, "%s:%d: continent `%s' unknown", path_zonetab, lineno, cname); +} + +static const char * +find_continent_name(struct continent *cont) +{ +	int		i; + +	for (i = 0; i < NCONTINENTS; i++) +		if (cont == continent_names[i].continent) +			return (continent_names[i].name); +	return ("Unknown"); +} + +struct country { +	char		*name; +	char		*tlc; +	int		nzones; +	struct continent *override;	/* continent override */ +	struct continent *alternate;	/* extra continent */ +	TAILQ_HEAD(, zone) zones; +	dialogMenuItem	*submenu; +}; + +struct zone { +	TAILQ_ENTRY(zone) link; +	char		*descr; +	char		*filename; +	struct continent *continent; +}; + +/* + * This is the easiest organization... we use ISO 3166 country codes, + * of the two-letter variety, so we just size this array to suit. + * Beats worrying about dynamic allocation. + */ +#define	NCOUNTRIES	(26 * 26) +static struct country countries[NCOUNTRIES]; + +#define	CODE2INT(s)	((s[0] - 'A') * 26 + (s[1] - 'A')) + +/* + * Read the ISO 3166 country code database in _PATH_ISO3166 + * (/usr/share/misc/iso3166).  On error, exit via err(3). + */ +static void +read_iso3166_table(void) +{ +	FILE		*fp; +	struct country	*cp; +	size_t		len; +	char		*s, *t, *name; +	int		lineno; + +	fp = fopen(path_iso3166, "r"); +	if (!fp) +		err(1, "%s", path_iso3166); +	lineno = 0; + +	while ((s = fgetln(fp, &len)) != NULL) { +		lineno++; +		if (s[len - 1] != '\n') +			errx(1, "%s:%d: invalid format", path_iso3166, lineno); +		s[len - 1] = '\0'; +		if (s[0] == '#' || strspn(s, " \t") == len - 1) +			continue; + +		/* Isolate the two-letter code. */ +		t = strsep(&s, "\t"); +		if (t == NULL || strlen(t) != 2) +			errx(1, "%s:%d: invalid format", path_iso3166, lineno); +		if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z') +			errx(1, "%s:%d: invalid code `%s'", path_iso3166, +			    lineno, t); + +		/* Now skip past the three-letter and numeric codes. */ +		name = strsep(&s, "\t");	/* 3-let */ +		if (name == NULL || strlen(name) != 3) +			errx(1, "%s:%d: invalid format", path_iso3166, lineno); +		name = strsep(&s, "\t");	/* numeric */ +		if (name == NULL || strlen(name) != 3) +			errx(1, "%s:%d: invalid format", path_iso3166, lineno); + +		name = s; + +		cp = &countries[CODE2INT(t)]; +		if (cp->name) +			errx(1, "%s:%d: country code `%s' multiply defined: %s", +			    path_iso3166, lineno, t, cp->name); +		cp->name = strdup(name); +		if (cp->name == NULL) +			errx(1, "malloc failed"); +		cp->tlc = strdup(t); +		if (cp->tlc == NULL) +			errx(1, "malloc failed"); +	} + +	fclose(fp); +} + +static struct country * +find_country(int lineno, const char *tlc) +{ +	struct country	*cp; + +	if (strlen(tlc) != 2 || +	    tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z') +		errx(1, "%s:%d: country code `%s' invalid", path_zonetab, +		    lineno, tlc); + +	cp = &countries[CODE2INT(tlc)]; +	if (cp->name == NULL) +		errx(1, "%s:%d: country code `%s' unknown", path_zonetab, +		    lineno, tlc); + +	return (cp); +} + +static void +add_cont_to_country(struct country *cp, struct continent *cont) +{ +	struct zone	*zp; + +	TAILQ_FOREACH(zp, &cp->zones, link) { +		if (zp->continent == cont) +			return; +	} +	cp->alternate = cont; +} + +static void +add_zone_to_country(int lineno, struct country *cp, const char *descr, +    const char *file, struct continent *cont) +{ +	struct zone	*zp; + +	zp = malloc(sizeof(*zp)); +	if (zp == NULL) +		errx(1, "malloc(%zu)", sizeof(*zp)); + +	if (cp->nzones == 0) +		TAILQ_INIT(&cp->zones); + +	if (descr != NULL) { +		zp->descr = strdup(descr); +		if (zp->descr == NULL) +			errx(1, "malloc failed"); +	} else { +		zp->descr = NULL; +	} +	zp->filename = strdup(file); +	if (zp->filename == NULL) +		errx(1, "malloc failed"); +	zp->continent = cp->override != NULL ? cp->override : cont; +	TAILQ_INSERT_TAIL(&cp->zones, zp, link); +	cp->nzones++; +} + +/* + * This comparison function intentionally sorts all of the null-named + * ``countries''---i.e., the codes that don't correspond to a real + * country---to the end.  Everything else is lexical by country name. + */ +static int +compare_countries(const void *xa, const void *xb) +{ +	const struct country *a = xa, *b = xb; + +	if (a->name == 0 && b->name == 0) +		return (0); +	if (a->name == 0 && b->name != 0) +		return (1); +	if (b->name == 0) +		return (-1); + +	return (strcmp(a->name, b->name)); +} + +/* + * This must be done AFTER all zone descriptions are read, since it breaks + * CODE2INT(). + */ +static void +sort_countries(void) +{ + +	qsort(countries, NCOUNTRIES, sizeof(countries[0]), compare_countries); +} + +static void +read_zones(void) +{ +	FILE		*fp; +	struct continent *cont; +	struct country	*cp; +	size_t		len; +	char		*line, *country_list, *tlc, *file, *descr; +	char		*p, *q; +	int		lineno; +	int		pass = 1; + +	fp = fopen(path_zonetab, "r"); +	if (!fp) +		err(1, "%s", path_zonetab); + +again: +	lineno = 0; +	while ((line = fgetln(fp, &len)) != NULL) { +		lineno++; +		if (line[len - 1] != '\n') +			errx(1, "%s:%d: invalid format", path_zonetab, lineno); +		line[len - 1] = '\0'; + +		switch (pass) +		{ +		case 1: +			/* +			 * First pass: collect overrides, only looking for +			 * single continent ones for the moment. +			 * +			 * zone1970.tab introduced continent overrides in the +			 * following format: +			 * +			 *   #@TLC[,TLC...]<tab>CONTINENT/[,CONTINENT/...] +			 */ +			if (strncmp(line, "#@", strlen("#@")) != 0) +				continue; +			line += 2; +			country_list = strsep(&line, "\t"); +			/* Skip multi-continent overrides */ +			if (strchr(line, ',') != NULL) +				continue; +			cont = find_continent(lineno, line); +			/* Parse and store overrides */ +			while (country_list != NULL) { +				tlc = strsep(&country_list, ","); +				cp = find_country(lineno, tlc); +				cp->override = cont; +			} +			break; +		case 2: +			/* Second pass: parse actual data */ +			if (line[0] == '#') +				continue; + +			country_list = strsep(&line, "\t"); +			/* coord = */ strsep(&line, "\t");	 /* Unused */ +			file = strsep(&line, "\t"); +			cont = find_continent(lineno, file); +			descr = (line != NULL && *line != '\0') ? line : NULL; + +			while (country_list != NULL) { +				tlc = strsep(&country_list, ","); +				cp = find_country(lineno, tlc); +				add_zone_to_country(lineno, cp, descr, file, +				    cont); +			} +			break; +		case 3: +			/* Third pass: collect multi-continent overrides */ +			if (strncmp(line, "#@", strlen("#@")) != 0) +				continue; +			line += 2; +			country_list = strsep(&line, "\t"); +			/* Skip single-continent overrides */ +			if (strchr(line, ',') == NULL) +				continue; +			while (line != NULL) { +				cont = find_continent(lineno, line); +				p = q = strdup(country_list); +				if (p == NULL) +					errx(1, "malloc failed"); +				while (q != NULL) { +					tlc = strsep(&q, ","); +					cp = find_country(lineno, tlc); +					add_cont_to_country(cp, cont); +				} +				free(p); +				strsep(&line, ","); +			} +			break; +		} +	} + +	if (pass++ < 3) { +		errno = 0; +		rewind(fp); +		if (errno != 0) +			err(1, "failed to rewind %s", path_zonetab); +		goto again; +	} +	fclose(fp); +} + +static void +dump_zonetab(void) +{ +	struct country	*cp; +	struct zone	*zp; +	const char *cont; + +	for (cp = countries; cp->name != NULL; cp++) { +		printf("%s:%s\n", cp->tlc, cp->name); +		TAILQ_FOREACH(zp, &cp->zones, link) { +			cont = find_continent_name(zp->continent); +			printf("  %s:%s\n", cont, zp->filename); +		} +	} +} + +static void +make_menus(void) +{ +	struct country	*cp; +	struct zone	*zp, *zp2; +	struct continent *cont; +	dialogMenuItem	*dmi; +	int		i; + +	/* +	 * First, count up all the countries in each continent/ocean. +	 * Be careful to count those countries which have multiple zones +	 * only once for each.  NB: some countries are in multiple +	 * continents/oceans. +	 */ +	for (cp = countries; cp->name; cp++) { +		if (cp->nzones == 0) +			continue; +		TAILQ_FOREACH(zp, &cp->zones, link) { +			cont = zp->continent; +			for (zp2 = TAILQ_FIRST(&cp->zones); +			    zp2->continent != cont; +			    zp2 = TAILQ_NEXT(zp2, link)) +				; +			if (zp2 == zp) +				zp->continent->nitems++; +		} + +		for (i = 0; i < NCONTINENTS; i++) { +			if (cp->alternate == continent_names[i].continent) { +				continent_names[i].continent->nitems++; +			} +		} +	} + +	/* +	 * Now allocate memory for the country menus and initialize +	 * continent menus.  We set nitems back to zero so that we can +	 * use it for counting again when we actually build the menus. +	 */ +	memset(continents, 0, sizeof(continents)); +	for (i = 0; i < NCONTINENTS; i++) { +		continent_names[i].continent->menu = +		    malloc(sizeof(dialogMenuItem) * +		    continent_names[i].continent->nitems); +		if (continent_names[i].continent->menu == NULL) +			errx(1, "malloc for continent menu"); +		continent_names[i].continent->nitems = 0; +		continents[i].prompt = continent_items[i].prompt; +		continents[i].title = continent_items[i].title; +		continents[i].fire = continent_country_menu; +		continents[i].data = continent_names[i].continent; +	} + +	/* +	 * Now that memory is allocated, create the menu items for +	 * each continent.  For multiple-zone countries, also create +	 * the country's zone submenu. +	 */ +	for (cp = countries; cp->name; cp++) { +		if (cp->nzones == 0) +			continue; +		cp->submenu = malloc(cp->nzones * sizeof(*dmi)); +		if (cp->submenu == 0) +			errx(1, "malloc for submenu"); +		cp->nzones = 0; +		TAILQ_FOREACH(zp, &cp->zones, link) { +			cont = zp->continent; +			dmi = &cp->submenu[cp->nzones]; +			memset(dmi, 0, sizeof(*dmi)); +			asprintf(&dmi->prompt, "%d", ++cp->nzones); +			dmi->title = zp->descr; +			dmi->fire = set_zone; +			dmi->data = zp; + +			for (zp2 = TAILQ_FIRST(&cp->zones); +			    zp2->continent != cont; +			    zp2 = TAILQ_NEXT(zp2, link)) +				; +			if (zp2 != zp) +				continue; + +			dmi = &cont->menu[cont->nitems]; +			memset(dmi, 0, sizeof(*dmi)); +			asprintf(&dmi->prompt, "%d", ++cont->nitems); +			dmi->title = cp->name; +			dmi->fire = set_zone_menu; +			dmi->data = cp; +		} + +		if (cp->alternate != NULL) { +			cont = cp->alternate; +			dmi = &cont->menu[cont->nitems]; +			memset(dmi, 0, sizeof(*dmi)); +			asprintf(&dmi->prompt, "%d", ++cont->nitems); +			dmi->title = cp->name; +			dmi->fire = set_zone_menu; +			dmi->data = cp; +		} +	} +} + +static int +set_zone_menu(dialogMenuItem *dmi) +{ +	char		title[64], prompt[64]; +	struct country	*cp = dmi->data; +	int		rv; + +	/* Short cut -- if there's only one zone, don't post a menu. */ +	if (cp->nzones == 1) +		return (cp->submenu[0].fire(&cp->submenu[0])); + +	snprintf(title, sizeof(title), "%s Time Zones", cp->name); +	snprintf(prompt, sizeof(prompt), +	    "Select a zone which observes the same time as your locality."); +	rv = xdialog_menu(title, prompt, cp->nzones, cp->submenu); +	return (rv != 0 ? DITEM_RECREATE : DITEM_LEAVE_MENU); +} + +static int +set_zone_utc(void) +{ +	if (!confirm_zone("UTC")) +		return (DITEM_FAILURE | DITEM_RECREATE); + +	return (install_zoneinfo("UTC")); +} + +static int +confirm_zone(const char *filename) +{ +	char		prompt[64]; +	time_t		t = time(0); +	struct tm	*tm; +	int		rv; + +	setenv("TZ", filename, 1); +	tzset(); +	tm = localtime(&t); + +	snprintf(prompt, sizeof(prompt), +	    "Does the timezone abbreviation `%s' look reasonable?", tm->tm_zone); +	conf.title = "Confirmation"; +	rv = (bsddialog_yesno(&conf, prompt, 5, 72) == BSDDIALOG_YES); +	return (rv); +} + +static int +set_zone(dialogMenuItem *dmi) +{ +	struct zone	*zp = dmi->data; +	int		rv; + +	if (!confirm_zone(zp->filename)) +		return (DITEM_FAILURE | DITEM_RECREATE); + +	rv = install_zoneinfo(zp->filename); +	return (rv); +} + +#endif + +static void message_zoneinfo_file(const char *title, char *prompt) +{ +#ifdef HAVE_BSDDIALOG +	if (usedialog) { +		conf.title = title; +		bsddialog_msgbox(&conf, prompt, 8, 72); +	} else +#endif +		fprintf(stderr, "%s: %s\n", title, prompt); +} + +static int +install_zoneinfo_file(const char *zoneinfo_file) +{ +	char		prompt[SILLY_BUFFER_SIZE]; + +#ifdef VERBOSE +	snprintf(prompt, sizeof(prompt), "Creating symbolic link %s to %s", +	    path_localtime, zoneinfo_file); +	message_zoneinfo_file("Info", prompt); +#endif + +	if (reallydoit) { +		if (access(zoneinfo_file, R_OK) != 0) { +			snprintf(prompt, sizeof(prompt), +			    "Cannot access %s: %s", zoneinfo_file, +			    strerror(errno)); +			message_zoneinfo_file("Error", prompt); +			return (DITEM_FAILURE | DITEM_RECREATE); +		} +		if (unlink(path_localtime) < 0 && errno != ENOENT) { +			snprintf(prompt, sizeof(prompt), +			    "Could not delete %s: %s", +			    path_localtime, strerror(errno)); +			message_zoneinfo_file("Error", prompt); +			return (DITEM_FAILURE | DITEM_RECREATE); +		} +		if (symlink(zoneinfo_file, path_localtime) < 0) { +			snprintf(prompt, sizeof(prompt), +			    "Cannot create symbolic link %s to %s: %s", +			    path_localtime, zoneinfo_file, +			    strerror(errno)); +			message_zoneinfo_file("Error", prompt); +			return (DITEM_FAILURE | DITEM_RECREATE); +		} + +#ifdef VERBOSE +		snprintf(prompt, sizeof(prompt), +		    "Created symbolic link from %s to %s", zoneinfo_file, +		    path_localtime); +		message_zoneinfo_file("Done", prompt); +#endif +	} /* reallydoit */ + +	return (DITEM_LEAVE_MENU); +} + +static int +install_zoneinfo(const char *zoneinfo) +{ +	int		rv; +	FILE		*f; +	char		path_zoneinfo_file[MAXPATHLEN]; + +	if ((size_t)snprintf(path_zoneinfo_file, sizeof(path_zoneinfo_file), +	    "%s/%s", path_zoneinfo, zoneinfo) >= sizeof(path_zoneinfo_file)) +		errx(1, "%s/%s name too long", path_zoneinfo, zoneinfo); +	rv = install_zoneinfo_file(path_zoneinfo_file); + +	/* Save knowledge for later */ +	if (reallydoit && (rv & DITEM_FAILURE) == 0) { +		if ((f = fopen(path_db, "w")) != NULL) { +			fprintf(f, "%s\n", zoneinfo); +			fclose(f); +		} +	} + +	return (rv); +} + +static void +usage(void) +{ + +	fprintf(stderr, "usage: tzsetup [-nrs] [-C chroot_directory]" +	    " [zoneinfo_file | zoneinfo_name]\n"); +	exit(1); +} + +int +main(int argc, char **argv) +{ +#ifdef HAVE_BSDDIALOG +	char		prompt[128]; +	int		fd; +#endif +	int		c, rv, skiputc; +	char		vm_guest[16] = ""; +	size_t		len = sizeof(vm_guest); +	char		*dztpath; + +	dztpath = NULL; +	skiputc = 0; + +#ifdef HAVE_BSDDIALOG +	setlocale(LC_ALL, ""); +#endif + +	/* Default skiputc to 1 for VM guests */ +	if (sysctlbyname("kern.vm_guest", vm_guest, &len, NULL, 0) == 0 && +	    strcmp(vm_guest, "none") != 0) +		skiputc = 1; + +	while ((c = getopt(argc, argv, "C:d:nrs")) != -1) { +		switch (c) { +		case 'C': +			chrootenv = optarg; +			break; +		case 'd': +			dztpath = optarg; +			break; +		case 'n': +			reallydoit = 0; +			break; +		case 'r': +			reinstall = 1; +#ifdef HAVE_BSDDIALOG +			usedialog = 0; +#endif +			break; +		case 's': +			skiputc = 1; +			break; +		default: +			usage(); +		} +	} + +	if (argc - optind > 1) +		usage(); + +	if (chrootenv == NULL) { +		if (dztpath == NULL) +			strcpy(path_zonetab, _PATH_ZONETAB); +		else +			strlcpy(path_zonetab, dztpath, sizeof(path_zonetab)); +		strcpy(path_iso3166, _PATH_ISO3166); +		strcpy(path_localtime, _PATH_LOCALTIME); +		strcpy(path_db, _PATH_DB); +		strcpy(path_wall_cmos_clock, _PATH_WALL_CMOS_CLOCK); +	} else { +		sprintf(path_zonetab, "%s/%s", chrootenv, _PATH_ZONETAB); +		sprintf(path_iso3166, "%s/%s", chrootenv, _PATH_ISO3166); +		sprintf(path_localtime, "%s/%s", chrootenv, _PATH_LOCALTIME); +		sprintf(path_db, "%s/%s", chrootenv, _PATH_DB); +		sprintf(path_wall_cmos_clock, "%s/%s", chrootenv, +		    _PATH_WALL_CMOS_CLOCK); +	} +	/* Symlink target is the same regardless of chroot */ +	strcpy(path_zoneinfo, _PATH_ZONEINFO); + +	/* Override the user-supplied umask. */ +	(void)umask(S_IWGRP | S_IWOTH); + +	if (reinstall == 1) { +		FILE *f; +		char zoneinfo[MAXPATHLEN]; + +		if ((f = fopen(path_db, "r")) != NULL) { +			if (fgets(zoneinfo, sizeof(zoneinfo), f) != NULL) { +				zoneinfo[sizeof(zoneinfo) - 1] = 0; +				if (strlen(zoneinfo) > 0) { +					zoneinfo[strlen(zoneinfo) - 1] = 0; +					rv = install_zoneinfo(zoneinfo); +					exit(rv & ~DITEM_LEAVE_MENU); +				} +				errx(1, "Error reading %s.\n", path_db); +			} +			fclose(f); +			errx(1, +			    "Unable to determine earlier installed zoneinfo " +			    "name. Check %s", path_db); +		} +		errx(1, "Cannot open %s for reading. Does it exist?", path_db); +	} + +	/* +	 * If the arguments on the command-line do not specify a file, +	 * then interpret it as a zoneinfo name +	 */ +	if (optind == argc - 1) { +		struct stat sb; + +		if (stat(argv[optind], &sb) != 0) { +#ifdef HAVE_BSDDIALOG +			usedialog = 0; +#endif +			rv = install_zoneinfo(argv[optind]); +			exit(rv & ~DITEM_LEAVE_MENU); +		} +		/* FALLTHROUGH */ +	} +#ifdef HAVE_BSDDIALOG + +	read_iso3166_table(); +	read_zones(); +	sort_countries(); +	if (dztpath != NULL) { +		dump_zonetab(); +		return (0); +	} +	make_menus(); + +	bsddialog_initconf(&conf); +	conf.clear = true; +	conf.auto_minwidth = 24; +	conf.key.enable_esc = true; + +	if (bsddialog_init() == BSDDIALOG_ERROR) +		errx(1, "Error bsddialog: %s\n", bsddialog_geterror()); + +	if (skiputc == 0) { +		snprintf(prompt, sizeof(prompt), +		    "Is this machine's CMOS clock set to UTC?  " +		    "If it is set to local time,\n" +		    "or you don't know, please choose NO here!"); + +		conf.title = "Select local or UTC (Coordinated Universal Time) clock"; +		if (bsddialog_yesno(&conf, prompt, 7, 73) == BSDDIALOG_YES) { +			if (reallydoit) +				unlink(path_wall_cmos_clock); +		} else { +			if (reallydoit) { +				fd = open(path_wall_cmos_clock, +				    O_WRONLY | O_CREAT | O_TRUNC, +				    S_IRUSR | S_IRGRP | S_IROTH); +				if (fd < 0) { +					bsddialog_end(); +					err(1, "create %s", +					    path_wall_cmos_clock); +				} +				close(fd); +			} +		} +	} +	if (optind == argc - 1) { +		snprintf(prompt, sizeof(prompt), +		    "\nUse the default `%s' zone?", argv[optind]); +		conf.title = "Default timezone provided"; +		if (bsddialog_yesno(&conf, prompt, 7, 72) == BSDDIALOG_YES) { +			rv = install_zoneinfo_file(argv[optind]); +			bsddialog_end(); +			exit(rv & ~DITEM_LEAVE_MENU); +		} +	} +	xdialog_menu("Time Zone Selector", "Select a region", NCONTINENTS, +	    continents); + +	bsddialog_end(); +#else +	usage(); +#endif +	return (0); +} | 
