@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge a small amount of remaining "libphutil/" code with Phabricator, break libphutil dependency

Summary: Ref T13395. Moves a small amount of remaining "libphutil/" code into "phabricator/" and stops us from loading "libphutil/".

Test Plan: Browsed around; there are likely remaining issues.

Maniphest Tasks: T13395

Differential Revision: https://secure.phabricator.com/D20981

+2831 -14
+769
externals/cldr/cldr_windows_timezones.xml
··· 1 + <?xml version="1.0" encoding="UTF-8" ?> 2 + <!DOCTYPE supplementalData SYSTEM "../../common/dtd/ldmlSupplemental.dtd"> 3 + <!-- 4 + Copyright © 1991-2013 Unicode, Inc. 5 + CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/) 6 + For terms of use, see http://www.unicode.org/copyright.html 7 + --> 8 + 9 + <supplementalData> 10 + <version number="$Revision$"/> 11 + <windowsZones> 12 + <mapTimezones otherVersion="7e00402" typeVersion="2016i"> 13 + 14 + <!-- (UTC-12:00) International Date Line West --> 15 + <mapZone other="Dateline Standard Time" territory="001" type="Etc/GMT+12"/> 16 + <mapZone other="Dateline Standard Time" territory="ZZ" type="Etc/GMT+12"/> 17 + 18 + <!-- (UTC-11:00) Coordinated Universal Time-11 --> 19 + <mapZone other="UTC-11" territory="001" type="Etc/GMT+11"/> 20 + <mapZone other="UTC-11" territory="AS" type="Pacific/Pago_Pago"/> 21 + <mapZone other="UTC-11" territory="NU" type="Pacific/Niue"/> 22 + <mapZone other="UTC-11" territory="UM" type="Pacific/Midway"/> 23 + <mapZone other="UTC-11" territory="ZZ" type="Etc/GMT+11"/> 24 + 25 + <!-- (UTC-10:00) Aleutian Islands --> 26 + <mapZone other="Aleutian Standard Time" territory="001" type="America/Adak"/> 27 + <mapZone other="Aleutian Standard Time" territory="US" type="America/Adak"/> 28 + 29 + <!-- (UTC-10:00) Hawaii --> 30 + <mapZone other="Hawaiian Standard Time" territory="001" type="Pacific/Honolulu"/> 31 + <mapZone other="Hawaiian Standard Time" territory="CK" type="Pacific/Rarotonga"/> 32 + <mapZone other="Hawaiian Standard Time" territory="PF" type="Pacific/Tahiti"/> 33 + <mapZone other="Hawaiian Standard Time" territory="UM" type="Pacific/Johnston"/> 34 + <mapZone other="Hawaiian Standard Time" territory="US" type="Pacific/Honolulu"/> 35 + <mapZone other="Hawaiian Standard Time" territory="ZZ" type="Etc/GMT+10"/> 36 + 37 + <!-- (UTC-09:30) Marquesas Islands --> 38 + <mapZone other="Marquesas Standard Time" territory="001" type="Pacific/Marquesas"/> 39 + <mapZone other="Marquesas Standard Time" territory="PF" type="Pacific/Marquesas"/> 40 + 41 + <!-- (UTC-09:00) Alaska --> 42 + <mapZone other="Alaskan Standard Time" territory="001" type="America/Anchorage"/> 43 + <mapZone other="Alaskan Standard Time" territory="US" type="America/Anchorage America/Juneau America/Metlakatla America/Nome America/Sitka America/Yakutat"/> 44 + 45 + <!-- (UTC-09:00) Coordinated Universal Time-09 --> 46 + <mapZone other="UTC-09" territory="001" type="Etc/GMT+9"/> 47 + <mapZone other="UTC-09" territory="PF" type="Pacific/Gambier"/> 48 + <mapZone other="UTC-09" territory="ZZ" type="Etc/GMT+9"/> 49 + 50 + <!-- (UTC-08:00) Baja California --> 51 + <mapZone other="Pacific Standard Time (Mexico)" territory="001" type="America/Tijuana"/> 52 + <mapZone other="Pacific Standard Time (Mexico)" territory="MX" type="America/Tijuana America/Santa_Isabel"/> 53 + 54 + <!-- (UTC-08:00) Coordinated Universal Time-08 --> 55 + <mapZone other="UTC-08" territory="001" type="Etc/GMT+8"/> 56 + <mapZone other="UTC-08" territory="PN" type="Pacific/Pitcairn"/> 57 + <mapZone other="UTC-08" territory="ZZ" type="Etc/GMT+8"/> 58 + 59 + <!-- (UTC-08:00) Pacific Time (US & Canada) --> 60 + <mapZone other="Pacific Standard Time" territory="001" type="America/Los_Angeles"/> 61 + <mapZone other="Pacific Standard Time" territory="CA" type="America/Vancouver America/Dawson America/Whitehorse"/> 62 + <mapZone other="Pacific Standard Time" territory="US" type="America/Los_Angeles"/> 63 + <mapZone other="Pacific Standard Time" territory="ZZ" type="PST8PDT"/> 64 + 65 + <!-- (UTC-07:00) Arizona --> 66 + <mapZone other="US Mountain Standard Time" territory="001" type="America/Phoenix"/> 67 + <mapZone other="US Mountain Standard Time" territory="CA" type="America/Dawson_Creek America/Creston America/Fort_Nelson"/> 68 + <mapZone other="US Mountain Standard Time" territory="MX" type="America/Hermosillo"/> 69 + <mapZone other="US Mountain Standard Time" territory="US" type="America/Phoenix"/> 70 + <mapZone other="US Mountain Standard Time" territory="ZZ" type="Etc/GMT+7"/> 71 + 72 + <!-- (UTC-07:00) Chihuahua, La Paz, Mazatlan --> 73 + <mapZone other="Mountain Standard Time (Mexico)" territory="001" type="America/Chihuahua"/> 74 + <mapZone other="Mountain Standard Time (Mexico)" territory="MX" type="America/Chihuahua America/Mazatlan"/> 75 + 76 + <!-- (UTC-07:00) Mountain Time (US & Canada) --> 77 + <mapZone other="Mountain Standard Time" territory="001" type="America/Denver"/> 78 + <mapZone other="Mountain Standard Time" territory="CA" type="America/Edmonton America/Cambridge_Bay America/Inuvik America/Yellowknife"/> 79 + <mapZone other="Mountain Standard Time" territory="MX" type="America/Ojinaga"/> 80 + <mapZone other="Mountain Standard Time" territory="US" type="America/Denver America/Boise"/> 81 + <mapZone other="Mountain Standard Time" territory="ZZ" type="MST7MDT"/> 82 + 83 + <!-- (UTC-06:00) Central America --> 84 + <mapZone other="Central America Standard Time" territory="001" type="America/Guatemala"/> 85 + <mapZone other="Central America Standard Time" territory="BZ" type="America/Belize"/> 86 + <mapZone other="Central America Standard Time" territory="CR" type="America/Costa_Rica"/> 87 + <mapZone other="Central America Standard Time" territory="EC" type="Pacific/Galapagos"/> 88 + <mapZone other="Central America Standard Time" territory="GT" type="America/Guatemala"/> 89 + <mapZone other="Central America Standard Time" territory="HN" type="America/Tegucigalpa"/> 90 + <mapZone other="Central America Standard Time" territory="NI" type="America/Managua"/> 91 + <mapZone other="Central America Standard Time" territory="SV" type="America/El_Salvador"/> 92 + <mapZone other="Central America Standard Time" territory="ZZ" type="Etc/GMT+6"/> 93 + 94 + <!-- (UTC-06:00) Central Time (US & Canada) --> 95 + <mapZone other="Central Standard Time" territory="001" type="America/Chicago"/> 96 + <mapZone other="Central Standard Time" territory="CA" type="America/Winnipeg America/Rainy_River America/Rankin_Inlet America/Resolute"/> 97 + <mapZone other="Central Standard Time" territory="MX" type="America/Matamoros"/> 98 + <mapZone other="Central Standard Time" territory="US" type="America/Chicago America/Indiana/Knox America/Indiana/Tell_City America/Menominee America/North_Dakota/Beulah America/North_Dakota/Center America/North_Dakota/New_Salem"/> 99 + <mapZone other="Central Standard Time" territory="ZZ" type="CST6CDT"/> 100 + 101 + <!-- (UTC-06:00) Easter Island --> 102 + <mapZone other="Easter Island Standard Time" territory="001" type="Pacific/Easter"/> 103 + <mapZone other="Easter Island Standard Time" territory="CL" type="Pacific/Easter"/> 104 + 105 + <!-- (UTC-06:00) Guadalajara, Mexico City, Monterrey --> 106 + <mapZone other="Central Standard Time (Mexico)" territory="001" type="America/Mexico_City"/> 107 + <mapZone other="Central Standard Time (Mexico)" territory="MX" type="America/Mexico_City America/Bahia_Banderas America/Merida America/Monterrey"/> 108 + 109 + <!-- (UTC-06:00) Saskatchewan --> 110 + <mapZone other="Canada Central Standard Time" territory="001" type="America/Regina"/> 111 + <mapZone other="Canada Central Standard Time" territory="CA" type="America/Regina America/Swift_Current"/> 112 + 113 + <!-- (UTC-05:00) Bogota, Lima, Quito, Rio Branco --> 114 + <mapZone other="SA Pacific Standard Time" territory="001" type="America/Bogota"/> 115 + <mapZone other="SA Pacific Standard Time" territory="BR" type="America/Rio_Branco America/Eirunepe"/> 116 + <mapZone other="SA Pacific Standard Time" territory="CA" type="America/Coral_Harbour"/> 117 + <mapZone other="SA Pacific Standard Time" territory="CO" type="America/Bogota"/> 118 + <mapZone other="SA Pacific Standard Time" territory="EC" type="America/Guayaquil"/> 119 + <mapZone other="SA Pacific Standard Time" territory="JM" type="America/Jamaica"/> 120 + <mapZone other="SA Pacific Standard Time" territory="KY" type="America/Cayman"/> 121 + <mapZone other="SA Pacific Standard Time" territory="PA" type="America/Panama"/> 122 + <mapZone other="SA Pacific Standard Time" territory="PE" type="America/Lima"/> 123 + <mapZone other="SA Pacific Standard Time" territory="ZZ" type="Etc/GMT+5"/> 124 + 125 + <!-- (UTC-05:00) Chetumal --> 126 + <mapZone other="Eastern Standard Time (Mexico)" territory="001" type="America/Cancun"/> 127 + <mapZone other="Eastern Standard Time (Mexico)" territory="MX" type="America/Cancun"/> 128 + 129 + <!-- (UTC-05:00) Eastern Time (US & Canada) --> 130 + <mapZone other="Eastern Standard Time" territory="001" type="America/New_York"/> 131 + <mapZone other="Eastern Standard Time" territory="BS" type="America/Nassau"/> 132 + <mapZone other="Eastern Standard Time" territory="CA" type="America/Toronto America/Iqaluit America/Montreal America/Nipigon America/Pangnirtung America/Thunder_Bay"/> 133 + <mapZone other="Eastern Standard Time" territory="US" type="America/New_York America/Detroit America/Indiana/Petersburg America/Indiana/Vincennes America/Indiana/Winamac America/Kentucky/Monticello America/Louisville"/> 134 + <mapZone other="Eastern Standard Time" territory="ZZ" type="EST5EDT"/> 135 + 136 + <!-- (UTC-05:00) Haiti --> 137 + <mapZone other="Haiti Standard Time" territory="001" type="America/Port-au-Prince"/> 138 + <mapZone other="Haiti Standard Time" territory="HT" type="America/Port-au-Prince"/> 139 + 140 + <!-- (UTC-05:00) Havana --> 141 + <mapZone other="Cuba Standard Time" territory="001" type="America/Havana"/> 142 + <mapZone other="Cuba Standard Time" territory="CU" type="America/Havana"/> 143 + 144 + <!-- (UTC-05:00) Indiana (East) --> 145 + <mapZone other="US Eastern Standard Time" territory="001" type="America/Indianapolis"/> 146 + <mapZone other="US Eastern Standard Time" territory="US" type="America/Indianapolis America/Indiana/Marengo America/Indiana/Vevay"/> 147 + 148 + <!-- (UTC-04:00) Asuncion --> 149 + <mapZone other="Paraguay Standard Time" territory="001" type="America/Asuncion"/> 150 + <mapZone other="Paraguay Standard Time" territory="PY" type="America/Asuncion"/> 151 + 152 + <!-- (UTC-04:00) Atlantic Time (Canada) --> 153 + <mapZone other="Atlantic Standard Time" territory="001" type="America/Halifax"/> 154 + <mapZone other="Atlantic Standard Time" territory="BM" type="Atlantic/Bermuda"/> 155 + <mapZone other="Atlantic Standard Time" territory="CA" type="America/Halifax America/Glace_Bay America/Goose_Bay America/Moncton"/> 156 + <mapZone other="Atlantic Standard Time" territory="GL" type="America/Thule"/> 157 + 158 + <!-- (UTC-04:00) Caracas --> 159 + <mapZone other="Venezuela Standard Time" territory="001" type="America/Caracas"/> 160 + <mapZone other="Venezuela Standard Time" territory="VE" type="America/Caracas"/> 161 + 162 + <!-- (UTC-04:00) Cuiaba --> 163 + <mapZone other="Central Brazilian Standard Time" territory="001" type="America/Cuiaba"/> 164 + <mapZone other="Central Brazilian Standard Time" territory="BR" type="America/Cuiaba America/Campo_Grande"/> 165 + 166 + <!-- (UTC-04:00) Georgetown, La Paz, Manaus, San Juan --> 167 + <mapZone other="SA Western Standard Time" territory="001" type="America/La_Paz"/> 168 + <mapZone other="SA Western Standard Time" territory="AG" type="America/Antigua"/> 169 + <mapZone other="SA Western Standard Time" territory="AI" type="America/Anguilla"/> 170 + <mapZone other="SA Western Standard Time" territory="AW" type="America/Aruba"/> 171 + <mapZone other="SA Western Standard Time" territory="BB" type="America/Barbados"/> 172 + <mapZone other="SA Western Standard Time" territory="BL" type="America/St_Barthelemy"/> 173 + <mapZone other="SA Western Standard Time" territory="BO" type="America/La_Paz"/> 174 + <mapZone other="SA Western Standard Time" territory="BQ" type="America/Kralendijk"/> 175 + <mapZone other="SA Western Standard Time" territory="BR" type="America/Manaus America/Boa_Vista America/Porto_Velho"/> 176 + <mapZone other="SA Western Standard Time" territory="CA" type="America/Blanc-Sablon"/> 177 + <mapZone other="SA Western Standard Time" territory="CW" type="America/Curacao"/> 178 + <mapZone other="SA Western Standard Time" territory="DM" type="America/Dominica"/> 179 + <mapZone other="SA Western Standard Time" territory="DO" type="America/Santo_Domingo"/> 180 + <mapZone other="SA Western Standard Time" territory="GD" type="America/Grenada"/> 181 + <mapZone other="SA Western Standard Time" territory="GP" type="America/Guadeloupe"/> 182 + <mapZone other="SA Western Standard Time" territory="GY" type="America/Guyana"/> 183 + <mapZone other="SA Western Standard Time" territory="KN" type="America/St_Kitts"/> 184 + <mapZone other="SA Western Standard Time" territory="LC" type="America/St_Lucia"/> 185 + <mapZone other="SA Western Standard Time" territory="MF" type="America/Marigot"/> 186 + <mapZone other="SA Western Standard Time" territory="MQ" type="America/Martinique"/> 187 + <mapZone other="SA Western Standard Time" territory="MS" type="America/Montserrat"/> 188 + <mapZone other="SA Western Standard Time" territory="PR" type="America/Puerto_Rico"/> 189 + <mapZone other="SA Western Standard Time" territory="SX" type="America/Lower_Princes"/> 190 + <mapZone other="SA Western Standard Time" territory="TT" type="America/Port_of_Spain"/> 191 + <mapZone other="SA Western Standard Time" territory="VC" type="America/St_Vincent"/> 192 + <mapZone other="SA Western Standard Time" territory="VG" type="America/Tortola"/> 193 + <mapZone other="SA Western Standard Time" territory="VI" type="America/St_Thomas"/> 194 + <mapZone other="SA Western Standard Time" territory="ZZ" type="Etc/GMT+4"/> 195 + 196 + <!-- (UTC-04:00) Santiago --> 197 + <mapZone other="Pacific SA Standard Time" territory="001" type="America/Santiago"/> 198 + <mapZone other="Pacific SA Standard Time" territory="AQ" type="Antarctica/Palmer"/> 199 + <mapZone other="Pacific SA Standard Time" territory="CL" type="America/Santiago"/> 200 + 201 + <!-- (UTC-04:00) Turks and Caicos --> 202 + <mapZone other="Turks And Caicos Standard Time" territory="001" type="America/Grand_Turk"/> 203 + <mapZone other="Turks And Caicos Standard Time" territory="TC" type="America/Grand_Turk"/> 204 + 205 + <!-- (UTC-03:30) Newfoundland --> 206 + <mapZone other="Newfoundland Standard Time" territory="001" type="America/St_Johns"/> 207 + <mapZone other="Newfoundland Standard Time" territory="CA" type="America/St_Johns"/> 208 + 209 + <!-- (UTC-03:00) Araguaina --> 210 + <mapZone other="Tocantins Standard Time" territory="001" type="America/Araguaina"/> 211 + <mapZone other="Tocantins Standard Time" territory="BR" type="America/Araguaina"/> 212 + 213 + <!-- (UTC-03:00) Brasilia --> 214 + <mapZone other="E. South America Standard Time" territory="001" type="America/Sao_Paulo"/> 215 + <mapZone other="E. South America Standard Time" territory="BR" type="America/Sao_Paulo"/> 216 + 217 + <!-- (UTC-03:00) Cayenne, Fortaleza --> 218 + <mapZone other="SA Eastern Standard Time" territory="001" type="America/Cayenne"/> 219 + <mapZone other="SA Eastern Standard Time" territory="AQ" type="Antarctica/Rothera"/> 220 + <mapZone other="SA Eastern Standard Time" territory="BR" type="America/Fortaleza America/Belem America/Maceio America/Recife America/Santarem"/> 221 + <mapZone other="SA Eastern Standard Time" territory="FK" type="Atlantic/Stanley"/> 222 + <mapZone other="SA Eastern Standard Time" territory="GF" type="America/Cayenne"/> 223 + <mapZone other="SA Eastern Standard Time" territory="SR" type="America/Paramaribo"/> 224 + <mapZone other="SA Eastern Standard Time" territory="ZZ" type="Etc/GMT+3"/> 225 + 226 + <!-- (UTC-03:00) City of Buenos Aires --> 227 + <mapZone other="Argentina Standard Time" territory="001" type="America/Buenos_Aires"/> 228 + <mapZone other="Argentina Standard Time" territory="AR" type="America/Buenos_Aires America/Argentina/La_Rioja America/Argentina/Rio_Gallegos America/Argentina/Salta America/Argentina/San_Juan America/Argentina/San_Luis America/Argentina/Tucuman America/Argentina/Ushuaia America/Catamarca America/Cordoba America/Jujuy America/Mendoza"/> 229 + 230 + <!-- (UTC-03:00) Greenland --> 231 + <mapZone other="Greenland Standard Time" territory="001" type="America/Godthab"/> 232 + <mapZone other="Greenland Standard Time" territory="GL" type="America/Godthab"/> 233 + 234 + <!-- (UTC-03:00) Montevideo --> 235 + <mapZone other="Montevideo Standard Time" territory="001" type="America/Montevideo"/> 236 + <mapZone other="Montevideo Standard Time" territory="UY" type="America/Montevideo"/> 237 + 238 + <!-- (UTC-03:00) Saint Pierre and Miquelon --> 239 + <mapZone other="Saint Pierre Standard Time" territory="001" type="America/Miquelon"/> 240 + <mapZone other="Saint Pierre Standard Time" territory="PM" type="America/Miquelon"/> 241 + 242 + <!-- (UTC-03:00) Salvador --> 243 + <mapZone other="Bahia Standard Time" territory="001" type="America/Bahia"/> 244 + <mapZone other="Bahia Standard Time" territory="BR" type="America/Bahia"/> 245 + 246 + <!-- (UTC-02:00) Coordinated Universal Time-02 --> 247 + <mapZone other="UTC-02" territory="001" type="Etc/GMT+2"/> 248 + <mapZone other="UTC-02" territory="BR" type="America/Noronha"/> 249 + <mapZone other="UTC-02" territory="GS" type="Atlantic/South_Georgia"/> 250 + <mapZone other="UTC-02" territory="ZZ" type="Etc/GMT+2"/> 251 + 252 + <!-- (UTC-01:00) Azores --> 253 + <mapZone other="Azores Standard Time" territory="001" type="Atlantic/Azores"/> 254 + <mapZone other="Azores Standard Time" territory="GL" type="America/Scoresbysund"/> 255 + <mapZone other="Azores Standard Time" territory="PT" type="Atlantic/Azores"/> 256 + 257 + <!-- (UTC-01:00) Cabo Verde Is. --> 258 + <mapZone other="Cape Verde Standard Time" territory="001" type="Atlantic/Cape_Verde"/> 259 + <mapZone other="Cape Verde Standard Time" territory="CV" type="Atlantic/Cape_Verde"/> 260 + <mapZone other="Cape Verde Standard Time" territory="ZZ" type="Etc/GMT+1"/> 261 + 262 + <!-- (UTC) Coordinated Universal Time --> 263 + <mapZone other="UTC" territory="001" type="Etc/GMT"/> 264 + <mapZone other="UTC" territory="GL" type="America/Danmarkshavn"/> 265 + <mapZone other="UTC" territory="ZZ" type="Etc/GMT"/> 266 + 267 + <!-- (UTC+00:00) Casablanca --> 268 + <mapZone other="Morocco Standard Time" territory="001" type="Africa/Casablanca"/> 269 + <mapZone other="Morocco Standard Time" territory="EH" type="Africa/El_Aaiun"/> 270 + <mapZone other="Morocco Standard Time" territory="MA" type="Africa/Casablanca"/> 271 + 272 + <!-- (UTC+00:00) Dublin, Edinburgh, Lisbon, London --> 273 + <mapZone other="GMT Standard Time" territory="001" type="Europe/London"/> 274 + <mapZone other="GMT Standard Time" territory="ES" type="Atlantic/Canary"/> 275 + <mapZone other="GMT Standard Time" territory="FO" type="Atlantic/Faeroe"/> 276 + <mapZone other="GMT Standard Time" territory="GB" type="Europe/London"/> 277 + <mapZone other="GMT Standard Time" territory="GG" type="Europe/Guernsey"/> 278 + <mapZone other="GMT Standard Time" territory="IE" type="Europe/Dublin"/> 279 + <mapZone other="GMT Standard Time" territory="IM" type="Europe/Isle_of_Man"/> 280 + <mapZone other="GMT Standard Time" territory="JE" type="Europe/Jersey"/> 281 + <mapZone other="GMT Standard Time" territory="PT" type="Europe/Lisbon Atlantic/Madeira"/> 282 + 283 + <!-- (UTC+00:00) Monrovia, Reykjavik --> 284 + <mapZone other="Greenwich Standard Time" territory="001" type="Atlantic/Reykjavik"/> 285 + <mapZone other="Greenwich Standard Time" territory="BF" type="Africa/Ouagadougou"/> 286 + <mapZone other="Greenwich Standard Time" territory="CI" type="Africa/Abidjan"/> 287 + <mapZone other="Greenwich Standard Time" territory="GH" type="Africa/Accra"/> 288 + <mapZone other="Greenwich Standard Time" territory="GM" type="Africa/Banjul"/> 289 + <mapZone other="Greenwich Standard Time" territory="GN" type="Africa/Conakry"/> 290 + <mapZone other="Greenwich Standard Time" territory="GW" type="Africa/Bissau"/> 291 + <mapZone other="Greenwich Standard Time" territory="IS" type="Atlantic/Reykjavik"/> 292 + <mapZone other="Greenwich Standard Time" territory="LR" type="Africa/Monrovia"/> 293 + <mapZone other="Greenwich Standard Time" territory="ML" type="Africa/Bamako"/> 294 + <mapZone other="Greenwich Standard Time" territory="MR" type="Africa/Nouakchott"/> 295 + <mapZone other="Greenwich Standard Time" territory="SH" type="Atlantic/St_Helena"/> 296 + <mapZone other="Greenwich Standard Time" territory="SL" type="Africa/Freetown"/> 297 + <mapZone other="Greenwich Standard Time" territory="SN" type="Africa/Dakar"/> 298 + <mapZone other="Greenwich Standard Time" territory="ST" type="Africa/Sao_Tome"/> 299 + <mapZone other="Greenwich Standard Time" territory="TG" type="Africa/Lome"/> 300 + 301 + <!-- (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna --> 302 + <mapZone other="W. Europe Standard Time" territory="001" type="Europe/Berlin"/> 303 + <mapZone other="W. Europe Standard Time" territory="AD" type="Europe/Andorra"/> 304 + <mapZone other="W. Europe Standard Time" territory="AT" type="Europe/Vienna"/> 305 + <mapZone other="W. Europe Standard Time" territory="CH" type="Europe/Zurich"/> 306 + <mapZone other="W. Europe Standard Time" territory="DE" type="Europe/Berlin Europe/Busingen"/> 307 + <mapZone other="W. Europe Standard Time" territory="GI" type="Europe/Gibraltar"/> 308 + <mapZone other="W. Europe Standard Time" territory="IT" type="Europe/Rome"/> 309 + <mapZone other="W. Europe Standard Time" territory="LI" type="Europe/Vaduz"/> 310 + <mapZone other="W. Europe Standard Time" territory="LU" type="Europe/Luxembourg"/> 311 + <mapZone other="W. Europe Standard Time" territory="MC" type="Europe/Monaco"/> 312 + <mapZone other="W. Europe Standard Time" territory="MT" type="Europe/Malta"/> 313 + <mapZone other="W. Europe Standard Time" territory="NL" type="Europe/Amsterdam"/> 314 + <mapZone other="W. Europe Standard Time" territory="NO" type="Europe/Oslo"/> 315 + <mapZone other="W. Europe Standard Time" territory="SE" type="Europe/Stockholm"/> 316 + <mapZone other="W. Europe Standard Time" territory="SJ" type="Arctic/Longyearbyen"/> 317 + <mapZone other="W. Europe Standard Time" territory="SM" type="Europe/San_Marino"/> 318 + <mapZone other="W. Europe Standard Time" territory="VA" type="Europe/Vatican"/> 319 + 320 + <!-- (UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague --> 321 + <mapZone other="Central Europe Standard Time" territory="001" type="Europe/Budapest"/> 322 + <mapZone other="Central Europe Standard Time" territory="AL" type="Europe/Tirane"/> 323 + <mapZone other="Central Europe Standard Time" territory="CZ" type="Europe/Prague"/> 324 + <mapZone other="Central Europe Standard Time" territory="HU" type="Europe/Budapest"/> 325 + <mapZone other="Central Europe Standard Time" territory="ME" type="Europe/Podgorica"/> 326 + <mapZone other="Central Europe Standard Time" territory="RS" type="Europe/Belgrade"/> 327 + <mapZone other="Central Europe Standard Time" territory="SI" type="Europe/Ljubljana"/> 328 + <mapZone other="Central Europe Standard Time" territory="SK" type="Europe/Bratislava"/> 329 + 330 + <!-- (UTC+01:00) Brussels, Copenhagen, Madrid, Paris --> 331 + <mapZone other="Romance Standard Time" territory="001" type="Europe/Paris"/> 332 + <mapZone other="Romance Standard Time" territory="BE" type="Europe/Brussels"/> 333 + <mapZone other="Romance Standard Time" territory="DK" type="Europe/Copenhagen"/> 334 + <mapZone other="Romance Standard Time" territory="ES" type="Europe/Madrid Africa/Ceuta"/> 335 + <mapZone other="Romance Standard Time" territory="FR" type="Europe/Paris"/> 336 + 337 + <!-- (UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb --> 338 + <mapZone other="Central European Standard Time" territory="001" type="Europe/Warsaw"/> 339 + <mapZone other="Central European Standard Time" territory="BA" type="Europe/Sarajevo"/> 340 + <mapZone other="Central European Standard Time" territory="HR" type="Europe/Zagreb"/> 341 + <mapZone other="Central European Standard Time" territory="MK" type="Europe/Skopje"/> 342 + <mapZone other="Central European Standard Time" territory="PL" type="Europe/Warsaw"/> 343 + 344 + <!-- (UTC+01:00) West Central Africa --> 345 + <mapZone other="W. Central Africa Standard Time" territory="001" type="Africa/Lagos"/> 346 + <mapZone other="W. Central Africa Standard Time" territory="AO" type="Africa/Luanda"/> 347 + <mapZone other="W. Central Africa Standard Time" territory="BJ" type="Africa/Porto-Novo"/> 348 + <mapZone other="W. Central Africa Standard Time" territory="CD" type="Africa/Kinshasa"/> 349 + <mapZone other="W. Central Africa Standard Time" territory="CF" type="Africa/Bangui"/> 350 + <mapZone other="W. Central Africa Standard Time" territory="CG" type="Africa/Brazzaville"/> 351 + <mapZone other="W. Central Africa Standard Time" territory="CM" type="Africa/Douala"/> 352 + <mapZone other="W. Central Africa Standard Time" territory="DZ" type="Africa/Algiers"/> 353 + <mapZone other="W. Central Africa Standard Time" territory="GA" type="Africa/Libreville"/> 354 + <mapZone other="W. Central Africa Standard Time" territory="GQ" type="Africa/Malabo"/> 355 + <mapZone other="W. Central Africa Standard Time" territory="NE" type="Africa/Niamey"/> 356 + <mapZone other="W. Central Africa Standard Time" territory="NG" type="Africa/Lagos"/> 357 + <mapZone other="W. Central Africa Standard Time" territory="TD" type="Africa/Ndjamena"/> 358 + <mapZone other="W. Central Africa Standard Time" territory="TN" type="Africa/Tunis"/> 359 + <mapZone other="W. Central Africa Standard Time" territory="ZZ" type="Etc/GMT-1"/> 360 + 361 + <!-- (UTC+01:00) Windhoek --> 362 + <mapZone other="Namibia Standard Time" territory="001" type="Africa/Windhoek"/> 363 + <mapZone other="Namibia Standard Time" territory="NA" type="Africa/Windhoek"/> 364 + 365 + <!-- (UTC+02:00) Amman --> 366 + <mapZone other="Jordan Standard Time" territory="001" type="Asia/Amman"/> 367 + <mapZone other="Jordan Standard Time" territory="JO" type="Asia/Amman"/> 368 + 369 + <!-- (UTC+02:00) Athens, Bucharest --> 370 + <mapZone other="GTB Standard Time" territory="001" type="Europe/Bucharest"/> 371 + <mapZone other="GTB Standard Time" territory="CY" type="Asia/Nicosia"/> 372 + <mapZone other="GTB Standard Time" territory="GR" type="Europe/Athens"/> 373 + <mapZone other="GTB Standard Time" territory="RO" type="Europe/Bucharest"/> 374 + 375 + <!-- (UTC+02:00) Beirut --> 376 + <mapZone other="Middle East Standard Time" territory="001" type="Asia/Beirut"/> 377 + <mapZone other="Middle East Standard Time" territory="LB" type="Asia/Beirut"/> 378 + 379 + <!-- (UTC+02:00) Cairo --> 380 + <mapZone other="Egypt Standard Time" territory="001" type="Africa/Cairo"/> 381 + <mapZone other="Egypt Standard Time" territory="EG" type="Africa/Cairo"/> 382 + 383 + <!-- (UTC+02:00) Chisinau --> 384 + <mapZone other="E. Europe Standard Time" territory="001" type="Europe/Chisinau"/> 385 + <mapZone other="E. Europe Standard Time" territory="MD" type="Europe/Chisinau"/> 386 + 387 + <!-- (UTC+02:00) Damascus --> 388 + <mapZone other="Syria Standard Time" territory="001" type="Asia/Damascus"/> 389 + <mapZone other="Syria Standard Time" territory="SY" type="Asia/Damascus"/> 390 + 391 + <!-- (UTC+02:00) Gaza, Hebron --> 392 + <mapZone other="West Bank Standard Time" territory="001" type="Asia/Hebron"/> 393 + <mapZone other="West Bank Standard Time" territory="PS" type="Asia/Hebron Asia/Gaza"/> 394 + 395 + <!-- (UTC+02:00) Harare, Pretoria --> 396 + <mapZone other="South Africa Standard Time" territory="001" type="Africa/Johannesburg"/> 397 + <mapZone other="South Africa Standard Time" territory="BI" type="Africa/Bujumbura"/> 398 + <mapZone other="South Africa Standard Time" territory="BW" type="Africa/Gaborone"/> 399 + <mapZone other="South Africa Standard Time" territory="CD" type="Africa/Lubumbashi"/> 400 + <mapZone other="South Africa Standard Time" territory="LS" type="Africa/Maseru"/> 401 + <mapZone other="South Africa Standard Time" territory="MW" type="Africa/Blantyre"/> 402 + <mapZone other="South Africa Standard Time" territory="MZ" type="Africa/Maputo"/> 403 + <mapZone other="South Africa Standard Time" territory="RW" type="Africa/Kigali"/> 404 + <mapZone other="South Africa Standard Time" territory="SZ" type="Africa/Mbabane"/> 405 + <mapZone other="South Africa Standard Time" territory="ZA" type="Africa/Johannesburg"/> 406 + <mapZone other="South Africa Standard Time" territory="ZM" type="Africa/Lusaka"/> 407 + <mapZone other="South Africa Standard Time" territory="ZW" type="Africa/Harare"/> 408 + <mapZone other="South Africa Standard Time" territory="ZZ" type="Etc/GMT-2"/> 409 + 410 + <!-- (UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius --> 411 + <mapZone other="FLE Standard Time" territory="001" type="Europe/Kiev"/> 412 + <mapZone other="FLE Standard Time" territory="AX" type="Europe/Mariehamn"/> 413 + <mapZone other="FLE Standard Time" territory="BG" type="Europe/Sofia"/> 414 + <mapZone other="FLE Standard Time" territory="EE" type="Europe/Tallinn"/> 415 + <mapZone other="FLE Standard Time" territory="FI" type="Europe/Helsinki"/> 416 + <mapZone other="FLE Standard Time" territory="LT" type="Europe/Vilnius"/> 417 + <mapZone other="FLE Standard Time" territory="LV" type="Europe/Riga"/> 418 + <mapZone other="FLE Standard Time" territory="UA" type="Europe/Kiev Europe/Uzhgorod Europe/Zaporozhye"/> 419 + 420 + <!-- (UTC+02:00) Istanbul --> 421 + <mapZone other="Turkey Standard Time" territory="001" type="Europe/Istanbul"/> 422 + <mapZone other="Turkey Standard Time" territory="TR" type="Europe/Istanbul"/> 423 + 424 + <!-- (UTC+02:00) Jerusalem --> 425 + <mapZone other="Israel Standard Time" territory="001" type="Asia/Jerusalem"/> 426 + <mapZone other="Israel Standard Time" territory="IL" type="Asia/Jerusalem"/> 427 + 428 + <!-- (UTC+02:00) Kaliningrad --> 429 + <mapZone other="Kaliningrad Standard Time" territory="001" type="Europe/Kaliningrad"/> 430 + <mapZone other="Kaliningrad Standard Time" territory="RU" type="Europe/Kaliningrad"/> 431 + 432 + <!-- (UTC+02:00) Tripoli --> 433 + <mapZone other="Libya Standard Time" territory="001" type="Africa/Tripoli"/> 434 + <mapZone other="Libya Standard Time" territory="LY" type="Africa/Tripoli"/> 435 + 436 + <!-- (UTC+03:00) Baghdad --> 437 + <mapZone other="Arabic Standard Time" territory="001" type="Asia/Baghdad"/> 438 + <mapZone other="Arabic Standard Time" territory="IQ" type="Asia/Baghdad"/> 439 + 440 + <!-- (UTC+03:00) Kuwait, Riyadh --> 441 + <mapZone other="Arab Standard Time" territory="001" type="Asia/Riyadh"/> 442 + <mapZone other="Arab Standard Time" territory="BH" type="Asia/Bahrain"/> 443 + <mapZone other="Arab Standard Time" territory="KW" type="Asia/Kuwait"/> 444 + <mapZone other="Arab Standard Time" territory="QA" type="Asia/Qatar"/> 445 + <mapZone other="Arab Standard Time" territory="SA" type="Asia/Riyadh"/> 446 + <mapZone other="Arab Standard Time" territory="YE" type="Asia/Aden"/> 447 + 448 + <!-- (UTC+03:00) Minsk --> 449 + <mapZone other="Belarus Standard Time" territory="001" type="Europe/Minsk"/> 450 + <mapZone other="Belarus Standard Time" territory="BY" type="Europe/Minsk"/> 451 + 452 + <!-- (UTC+03:00) Moscow, St. Petersburg, Volgograd --> 453 + <mapZone other="Russian Standard Time" territory="001" type="Europe/Moscow"/> 454 + <mapZone other="Russian Standard Time" territory="RU" type="Europe/Moscow Europe/Kirov Europe/Volgograd"/> 455 + <mapZone other="Russian Standard Time" territory="UA" type="Europe/Simferopol"/> 456 + 457 + <!-- (UTC+03:00) Nairobi --> 458 + <mapZone other="E. Africa Standard Time" territory="001" type="Africa/Nairobi"/> 459 + <mapZone other="E. Africa Standard Time" territory="AQ" type="Antarctica/Syowa"/> 460 + <mapZone other="E. Africa Standard Time" territory="DJ" type="Africa/Djibouti"/> 461 + <mapZone other="E. Africa Standard Time" territory="ER" type="Africa/Asmera"/> 462 + <mapZone other="E. Africa Standard Time" territory="ET" type="Africa/Addis_Ababa"/> 463 + <mapZone other="E. Africa Standard Time" territory="KE" type="Africa/Nairobi"/> 464 + <mapZone other="E. Africa Standard Time" territory="KM" type="Indian/Comoro"/> 465 + <mapZone other="E. Africa Standard Time" territory="MG" type="Indian/Antananarivo"/> 466 + <mapZone other="E. Africa Standard Time" territory="SD" type="Africa/Khartoum"/> 467 + <mapZone other="E. Africa Standard Time" territory="SO" type="Africa/Mogadishu"/> 468 + <mapZone other="E. Africa Standard Time" territory="SS" type="Africa/Juba"/> 469 + <mapZone other="E. Africa Standard Time" territory="TZ" type="Africa/Dar_es_Salaam"/> 470 + <mapZone other="E. Africa Standard Time" territory="UG" type="Africa/Kampala"/> 471 + <mapZone other="E. Africa Standard Time" territory="YT" type="Indian/Mayotte"/> 472 + <mapZone other="E. Africa Standard Time" territory="ZZ" type="Etc/GMT-3"/> 473 + 474 + <!-- (UTC+03:30) Tehran --> 475 + <mapZone other="Iran Standard Time" territory="001" type="Asia/Tehran"/> 476 + <mapZone other="Iran Standard Time" territory="IR" type="Asia/Tehran"/> 477 + 478 + <!-- (UTC+04:00) Abu Dhabi, Muscat --> 479 + <mapZone other="Arabian Standard Time" territory="001" type="Asia/Dubai"/> 480 + <mapZone other="Arabian Standard Time" territory="AE" type="Asia/Dubai"/> 481 + <mapZone other="Arabian Standard Time" territory="OM" type="Asia/Muscat"/> 482 + <mapZone other="Arabian Standard Time" territory="ZZ" type="Etc/GMT-4"/> 483 + 484 + <!-- (UTC+04:00) Astrakhan, Ulyanovsk --> 485 + <mapZone other="Astrakhan Standard Time" territory="001" type="Europe/Astrakhan"/> 486 + <mapZone other="Astrakhan Standard Time" territory="RU" type="Europe/Astrakhan Europe/Ulyanovsk"/> 487 + 488 + <!-- (UTC+04:00) Baku --> 489 + <mapZone other="Azerbaijan Standard Time" territory="001" type="Asia/Baku"/> 490 + <mapZone other="Azerbaijan Standard Time" territory="AZ" type="Asia/Baku"/> 491 + 492 + <!-- (UTC+04:00) Izhevsk, Samara --> 493 + <mapZone other="Russia Time Zone 3" territory="001" type="Europe/Samara"/> 494 + <mapZone other="Russia Time Zone 3" territory="RU" type="Europe/Samara"/> 495 + 496 + <!-- (UTC+04:00) Port Louis --> 497 + <mapZone other="Mauritius Standard Time" territory="001" type="Indian/Mauritius"/> 498 + <mapZone other="Mauritius Standard Time" territory="MU" type="Indian/Mauritius"/> 499 + <mapZone other="Mauritius Standard Time" territory="RE" type="Indian/Reunion"/> 500 + <mapZone other="Mauritius Standard Time" territory="SC" type="Indian/Mahe"/> 501 + 502 + <!-- (UTC+04:00) Tbilisi --> 503 + <mapZone other="Georgian Standard Time" territory="001" type="Asia/Tbilisi"/> 504 + <mapZone other="Georgian Standard Time" territory="GE" type="Asia/Tbilisi"/> 505 + 506 + <!-- (UTC+04:00) Yerevan --> 507 + <mapZone other="Caucasus Standard Time" territory="001" type="Asia/Yerevan"/> 508 + <mapZone other="Caucasus Standard Time" territory="AM" type="Asia/Yerevan"/> 509 + 510 + <!-- (UTC+04:30) Kabul --> 511 + <mapZone other="Afghanistan Standard Time" territory="001" type="Asia/Kabul"/> 512 + <mapZone other="Afghanistan Standard Time" territory="AF" type="Asia/Kabul"/> 513 + 514 + <!-- (UTC+05:00) Ashgabat, Tashkent --> 515 + <mapZone other="West Asia Standard Time" territory="001" type="Asia/Tashkent"/> 516 + <mapZone other="West Asia Standard Time" territory="AQ" type="Antarctica/Mawson"/> 517 + <mapZone other="West Asia Standard Time" territory="KZ" type="Asia/Oral Asia/Aqtau Asia/Aqtobe"/> 518 + <mapZone other="West Asia Standard Time" territory="MV" type="Indian/Maldives"/> 519 + <mapZone other="West Asia Standard Time" territory="TF" type="Indian/Kerguelen"/> 520 + <mapZone other="West Asia Standard Time" territory="TJ" type="Asia/Dushanbe"/> 521 + <mapZone other="West Asia Standard Time" territory="TM" type="Asia/Ashgabat"/> 522 + <mapZone other="West Asia Standard Time" territory="UZ" type="Asia/Tashkent Asia/Samarkand"/> 523 + <mapZone other="West Asia Standard Time" territory="ZZ" type="Etc/GMT-5"/> 524 + 525 + <!-- (UTC+05:00) Ekaterinburg --> 526 + <mapZone other="Ekaterinburg Standard Time" territory="001" type="Asia/Yekaterinburg"/> 527 + <mapZone other="Ekaterinburg Standard Time" territory="RU" type="Asia/Yekaterinburg"/> 528 + 529 + <!-- (UTC+05:00) Islamabad, Karachi --> 530 + <mapZone other="Pakistan Standard Time" territory="001" type="Asia/Karachi"/> 531 + <mapZone other="Pakistan Standard Time" territory="PK" type="Asia/Karachi"/> 532 + 533 + <!-- (UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi --> 534 + <mapZone other="India Standard Time" territory="001" type="Asia/Calcutta"/> 535 + <mapZone other="India Standard Time" territory="IN" type="Asia/Calcutta"/> 536 + 537 + <!-- (UTC+05:30) Sri Jayawardenepura --> 538 + <mapZone other="Sri Lanka Standard Time" territory="001" type="Asia/Colombo"/> 539 + <mapZone other="Sri Lanka Standard Time" territory="LK" type="Asia/Colombo"/> 540 + 541 + <!-- (UTC+05:45) Kathmandu --> 542 + <mapZone other="Nepal Standard Time" territory="001" type="Asia/Katmandu"/> 543 + <mapZone other="Nepal Standard Time" territory="NP" type="Asia/Katmandu"/> 544 + 545 + <!-- (UTC+06:00) Astana --> 546 + <mapZone other="Central Asia Standard Time" territory="001" type="Asia/Almaty"/> 547 + <mapZone other="Central Asia Standard Time" territory="AQ" type="Antarctica/Vostok"/> 548 + <mapZone other="Central Asia Standard Time" territory="CN" type="Asia/Urumqi"/> 549 + <mapZone other="Central Asia Standard Time" territory="IO" type="Indian/Chagos"/> 550 + <mapZone other="Central Asia Standard Time" territory="KG" type="Asia/Bishkek"/> 551 + <mapZone other="Central Asia Standard Time" territory="KZ" type="Asia/Almaty Asia/Qyzylorda"/> 552 + <mapZone other="Central Asia Standard Time" territory="ZZ" type="Etc/GMT-6"/> 553 + 554 + <!-- (UTC+06:00) Dhaka --> 555 + <mapZone other="Bangladesh Standard Time" territory="001" type="Asia/Dhaka"/> 556 + <mapZone other="Bangladesh Standard Time" territory="BD" type="Asia/Dhaka"/> 557 + <mapZone other="Bangladesh Standard Time" territory="BT" type="Asia/Thimphu"/> 558 + 559 + <!-- (UTC+06:00) Omsk --> 560 + <mapZone other="Omsk Standard Time" territory="001" type="Asia/Omsk"/> 561 + <mapZone other="Omsk Standard Time" territory="RU" type="Asia/Omsk"/> 562 + 563 + <!-- (UTC+06:30) Yangon (Rangoon) --> 564 + <mapZone other="Myanmar Standard Time" territory="001" type="Asia/Rangoon"/> 565 + <mapZone other="Myanmar Standard Time" territory="CC" type="Indian/Cocos"/> 566 + <mapZone other="Myanmar Standard Time" territory="MM" type="Asia/Rangoon"/> 567 + 568 + <!-- (UTC+07:00) Bangkok, Hanoi, Jakarta --> 569 + <mapZone other="SE Asia Standard Time" territory="001" type="Asia/Bangkok"/> 570 + <mapZone other="SE Asia Standard Time" territory="AQ" type="Antarctica/Davis"/> 571 + <mapZone other="SE Asia Standard Time" territory="CX" type="Indian/Christmas"/> 572 + <mapZone other="SE Asia Standard Time" territory="ID" type="Asia/Jakarta Asia/Pontianak"/> 573 + <mapZone other="SE Asia Standard Time" territory="KH" type="Asia/Phnom_Penh"/> 574 + <mapZone other="SE Asia Standard Time" territory="LA" type="Asia/Vientiane"/> 575 + <mapZone other="SE Asia Standard Time" territory="TH" type="Asia/Bangkok"/> 576 + <mapZone other="SE Asia Standard Time" territory="VN" type="Asia/Saigon"/> 577 + <mapZone other="SE Asia Standard Time" territory="ZZ" type="Etc/GMT-7"/> 578 + 579 + <!-- (UTC+07:00) Barnaul, Gorno-Altaysk --> 580 + <mapZone other="Altai Standard Time" territory="001" type="Asia/Barnaul"/> 581 + <mapZone other="Altai Standard Time" territory="RU" type="Asia/Barnaul"/> 582 + 583 + <!-- (UTC+07:00) Hovd --> 584 + <mapZone other="W. Mongolia Standard Time" territory="001" type="Asia/Hovd"/> 585 + <mapZone other="W. Mongolia Standard Time" territory="MN" type="Asia/Hovd"/> 586 + 587 + <!-- (UTC+07:00) Krasnoyarsk --> 588 + <mapZone other="North Asia Standard Time" territory="001" type="Asia/Krasnoyarsk"/> 589 + <mapZone other="North Asia Standard Time" territory="RU" type="Asia/Krasnoyarsk Asia/Novokuznetsk"/> 590 + 591 + <!-- (UTC+07:00) Novosibirsk --> 592 + <mapZone other="N. Central Asia Standard Time" territory="001" type="Asia/Novosibirsk"/> 593 + <mapZone other="N. Central Asia Standard Time" territory="RU" type="Asia/Novosibirsk"/> 594 + 595 + <!-- (UTC+07:00) Tomsk --> 596 + <mapZone other="Tomsk Standard Time" territory="001" type="Asia/Tomsk"/> 597 + <mapZone other="Tomsk Standard Time" territory="RU" type="Asia/Tomsk"/> 598 + 599 + <!-- (UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi --> 600 + <mapZone other="China Standard Time" territory="001" type="Asia/Shanghai"/> 601 + <mapZone other="China Standard Time" territory="CN" type="Asia/Shanghai"/> 602 + <mapZone other="China Standard Time" territory="HK" type="Asia/Hong_Kong"/> 603 + <mapZone other="China Standard Time" territory="MO" type="Asia/Macau"/> 604 + 605 + <!-- (UTC+08:00) Irkutsk --> 606 + <mapZone other="North Asia East Standard Time" territory="001" type="Asia/Irkutsk"/> 607 + <mapZone other="North Asia East Standard Time" territory="RU" type="Asia/Irkutsk"/> 608 + 609 + <!-- (UTC+08:00) Kuala Lumpur, Singapore --> 610 + <mapZone other="Singapore Standard Time" territory="001" type="Asia/Singapore"/> 611 + <mapZone other="Singapore Standard Time" territory="BN" type="Asia/Brunei"/> 612 + <mapZone other="Singapore Standard Time" territory="ID" type="Asia/Makassar"/> 613 + <mapZone other="Singapore Standard Time" territory="MY" type="Asia/Kuala_Lumpur Asia/Kuching"/> 614 + <mapZone other="Singapore Standard Time" territory="PH" type="Asia/Manila"/> 615 + <mapZone other="Singapore Standard Time" territory="SG" type="Asia/Singapore"/> 616 + <mapZone other="Singapore Standard Time" territory="ZZ" type="Etc/GMT-8"/> 617 + 618 + <!-- (UTC+08:00) Perth --> 619 + <mapZone other="W. Australia Standard Time" territory="001" type="Australia/Perth"/> 620 + <mapZone other="W. Australia Standard Time" territory="AU" type="Australia/Perth"/> 621 + 622 + <!-- (UTC+08:00) Taipei --> 623 + <mapZone other="Taipei Standard Time" territory="001" type="Asia/Taipei"/> 624 + <mapZone other="Taipei Standard Time" territory="TW" type="Asia/Taipei"/> 625 + 626 + <!-- (UTC+08:00) Ulaanbaatar --> 627 + <mapZone other="Ulaanbaatar Standard Time" territory="001" type="Asia/Ulaanbaatar"/> 628 + <mapZone other="Ulaanbaatar Standard Time" territory="MN" type="Asia/Ulaanbaatar Asia/Choibalsan"/> 629 + 630 + <!-- (UTC+08:30) Pyongyang --> 631 + <mapZone other="North Korea Standard Time" territory="001" type="Asia/Pyongyang"/> 632 + <mapZone other="North Korea Standard Time" territory="KP" type="Asia/Pyongyang"/> 633 + 634 + <!-- (UTC+08:45) Eucla --> 635 + <mapZone other="Aus Central W. Standard Time" territory="001" type="Australia/Eucla"/> 636 + <mapZone other="Aus Central W. Standard Time" territory="AU" type="Australia/Eucla"/> 637 + 638 + <!-- (UTC+09:00) Chita --> 639 + <mapZone other="Transbaikal Standard Time" territory="001" type="Asia/Chita"/> 640 + <mapZone other="Transbaikal Standard Time" territory="RU" type="Asia/Chita"/> 641 + 642 + <!-- (UTC+09:00) Osaka, Sapporo, Tokyo --> 643 + <mapZone other="Tokyo Standard Time" territory="001" type="Asia/Tokyo"/> 644 + <mapZone other="Tokyo Standard Time" territory="ID" type="Asia/Jayapura"/> 645 + <mapZone other="Tokyo Standard Time" territory="JP" type="Asia/Tokyo"/> 646 + <mapZone other="Tokyo Standard Time" territory="PW" type="Pacific/Palau"/> 647 + <mapZone other="Tokyo Standard Time" territory="TL" type="Asia/Dili"/> 648 + <mapZone other="Tokyo Standard Time" territory="ZZ" type="Etc/GMT-9"/> 649 + 650 + <!-- (UTC+09:00) Seoul --> 651 + <mapZone other="Korea Standard Time" territory="001" type="Asia/Seoul"/> 652 + <mapZone other="Korea Standard Time" territory="KR" type="Asia/Seoul"/> 653 + 654 + <!-- (UTC+09:00) Yakutsk --> 655 + <mapZone other="Yakutsk Standard Time" territory="001" type="Asia/Yakutsk"/> 656 + <mapZone other="Yakutsk Standard Time" territory="RU" type="Asia/Yakutsk Asia/Khandyga"/> 657 + 658 + <!-- (UTC+09:30) Adelaide --> 659 + <mapZone other="Cen. Australia Standard Time" territory="001" type="Australia/Adelaide"/> 660 + <mapZone other="Cen. Australia Standard Time" territory="AU" type="Australia/Adelaide Australia/Broken_Hill"/> 661 + 662 + <!-- (UTC+09:30) Darwin --> 663 + <mapZone other="AUS Central Standard Time" territory="001" type="Australia/Darwin"/> 664 + <mapZone other="AUS Central Standard Time" territory="AU" type="Australia/Darwin"/> 665 + 666 + <!-- (UTC+10:00) Brisbane --> 667 + <mapZone other="E. Australia Standard Time" territory="001" type="Australia/Brisbane"/> 668 + <mapZone other="E. Australia Standard Time" territory="AU" type="Australia/Brisbane Australia/Lindeman"/> 669 + 670 + <!-- (UTC+10:00) Canberra, Melbourne, Sydney --> 671 + <mapZone other="AUS Eastern Standard Time" territory="001" type="Australia/Sydney"/> 672 + <mapZone other="AUS Eastern Standard Time" territory="AU" type="Australia/Sydney Australia/Melbourne"/> 673 + 674 + <!-- (UTC+10:00) Guam, Port Moresby --> 675 + <mapZone other="West Pacific Standard Time" territory="001" type="Pacific/Port_Moresby"/> 676 + <mapZone other="West Pacific Standard Time" territory="AQ" type="Antarctica/DumontDUrville"/> 677 + <mapZone other="West Pacific Standard Time" territory="FM" type="Pacific/Truk"/> 678 + <mapZone other="West Pacific Standard Time" territory="GU" type="Pacific/Guam"/> 679 + <mapZone other="West Pacific Standard Time" territory="MP" type="Pacific/Saipan"/> 680 + <mapZone other="West Pacific Standard Time" territory="PG" type="Pacific/Port_Moresby"/> 681 + <mapZone other="West Pacific Standard Time" territory="ZZ" type="Etc/GMT-10"/> 682 + 683 + <!-- (UTC+10:00) Hobart --> 684 + <mapZone other="Tasmania Standard Time" territory="001" type="Australia/Hobart"/> 685 + <mapZone other="Tasmania Standard Time" territory="AU" type="Australia/Hobart Australia/Currie"/> 686 + 687 + <!-- (UTC+10:00) Vladivostok --> 688 + <mapZone other="Vladivostok Standard Time" territory="001" type="Asia/Vladivostok"/> 689 + <mapZone other="Vladivostok Standard Time" territory="RU" type="Asia/Vladivostok Asia/Ust-Nera"/> 690 + 691 + <!-- (UTC+10:30) Lord Howe Island --> 692 + <mapZone other="Lord Howe Standard Time" territory="001" type="Australia/Lord_Howe"/> 693 + <mapZone other="Lord Howe Standard Time" territory="AU" type="Australia/Lord_Howe"/> 694 + 695 + <!-- (UTC+11:00) Bougainville Island --> 696 + <mapZone other="Bougainville Standard Time" territory="001" type="Pacific/Bougainville"/> 697 + <mapZone other="Bougainville Standard Time" territory="PG" type="Pacific/Bougainville"/> 698 + 699 + <!-- (UTC+11:00) Chokurdakh --> 700 + <mapZone other="Russia Time Zone 10" territory="001" type="Asia/Srednekolymsk"/> 701 + <mapZone other="Russia Time Zone 10" territory="RU" type="Asia/Srednekolymsk"/> 702 + 703 + <!-- (UTC+11:00) Magadan --> 704 + <mapZone other="Magadan Standard Time" territory="001" type="Asia/Magadan"/> 705 + <mapZone other="Magadan Standard Time" territory="RU" type="Asia/Magadan"/> 706 + 707 + <!-- (UTC+11:00) Norfolk Island --> 708 + <mapZone other="Norfolk Standard Time" territory="001" type="Pacific/Norfolk"/> 709 + <mapZone other="Norfolk Standard Time" territory="NF" type="Pacific/Norfolk"/> 710 + 711 + <!-- (UTC+11:00) Sakhalin --> 712 + <mapZone other="Sakhalin Standard Time" territory="001" type="Asia/Sakhalin"/> 713 + <mapZone other="Sakhalin Standard Time" territory="RU" type="Asia/Sakhalin"/> 714 + 715 + <!-- (UTC+11:00) Solomon Is., New Caledonia --> 716 + <mapZone other="Central Pacific Standard Time" territory="001" type="Pacific/Guadalcanal"/> 717 + <mapZone other="Central Pacific Standard Time" territory="AQ" type="Antarctica/Casey"/> 718 + <mapZone other="Central Pacific Standard Time" territory="AU" type="Antarctica/Macquarie"/> 719 + <mapZone other="Central Pacific Standard Time" territory="FM" type="Pacific/Ponape Pacific/Kosrae"/> 720 + <mapZone other="Central Pacific Standard Time" territory="NC" type="Pacific/Noumea"/> 721 + <mapZone other="Central Pacific Standard Time" territory="SB" type="Pacific/Guadalcanal"/> 722 + <mapZone other="Central Pacific Standard Time" territory="VU" type="Pacific/Efate"/> 723 + <mapZone other="Central Pacific Standard Time" territory="ZZ" type="Etc/GMT-11"/> 724 + 725 + <!-- (UTC+12:00) Anadyr, Petropavlovsk-Kamchatsky --> 726 + <mapZone other="Russia Time Zone 11" territory="001" type="Asia/Kamchatka"/> 727 + <mapZone other="Russia Time Zone 11" territory="RU" type="Asia/Kamchatka Asia/Anadyr"/> 728 + 729 + <!-- (UTC+12:00) Auckland, Wellington --> 730 + <mapZone other="New Zealand Standard Time" territory="001" type="Pacific/Auckland"/> 731 + <mapZone other="New Zealand Standard Time" territory="AQ" type="Antarctica/McMurdo"/> 732 + <mapZone other="New Zealand Standard Time" territory="NZ" type="Pacific/Auckland"/> 733 + 734 + <!-- (UTC+12:00) Coordinated Universal Time+12 --> 735 + <mapZone other="UTC+12" territory="001" type="Etc/GMT-12"/> 736 + <mapZone other="UTC+12" territory="KI" type="Pacific/Tarawa"/> 737 + <mapZone other="UTC+12" territory="MH" type="Pacific/Majuro Pacific/Kwajalein"/> 738 + <mapZone other="UTC+12" territory="NR" type="Pacific/Nauru"/> 739 + <mapZone other="UTC+12" territory="TV" type="Pacific/Funafuti"/> 740 + <mapZone other="UTC+12" territory="UM" type="Pacific/Wake"/> 741 + <mapZone other="UTC+12" territory="WF" type="Pacific/Wallis"/> 742 + <mapZone other="UTC+12" territory="ZZ" type="Etc/GMT-12"/> 743 + 744 + <!-- (UTC+12:00) Fiji --> 745 + <mapZone other="Fiji Standard Time" territory="001" type="Pacific/Fiji"/> 746 + <mapZone other="Fiji Standard Time" territory="FJ" type="Pacific/Fiji"/> 747 + 748 + <!-- (UTC+12:45) Chatham Islands --> 749 + <mapZone other="Chatham Islands Standard Time" territory="001" type="Pacific/Chatham"/> 750 + <mapZone other="Chatham Islands Standard Time" territory="NZ" type="Pacific/Chatham"/> 751 + 752 + <!-- (UTC+13:00) Nuku'alofa --> 753 + <mapZone other="Tonga Standard Time" territory="001" type="Pacific/Tongatapu"/> 754 + <mapZone other="Tonga Standard Time" territory="KI" type="Pacific/Enderbury"/> 755 + <mapZone other="Tonga Standard Time" territory="TK" type="Pacific/Fakaofo"/> 756 + <mapZone other="Tonga Standard Time" territory="TO" type="Pacific/Tongatapu"/> 757 + <mapZone other="Tonga Standard Time" territory="ZZ" type="Etc/GMT-13"/> 758 + 759 + <!-- (UTC+13:00) Samoa --> 760 + <mapZone other="Samoa Standard Time" territory="001" type="Pacific/Apia"/> 761 + <mapZone other="Samoa Standard Time" territory="WS" type="Pacific/Apia"/> 762 + 763 + <!-- (UTC+14:00) Kiritimati Island --> 764 + <mapZone other="Line Islands Standard Time" territory="001" type="Pacific/Kiritimati"/> 765 + <mapZone other="Line Islands Standard Time" territory="KI" type="Pacific/Kiritimati"/> 766 + <mapZone other="Line Islands Standard Time" territory="ZZ" type="Etc/GMT-14"/> 767 + </mapTimezones> 768 + </windowsZones> 769 + </supplementalData>
+46
resources/timezones/generate-timezone-map.php
··· 1 + #!/usr/bin/env php 2 + <?php 3 + 4 + $root = dirname(dirname(dirname(__FILE__))); 5 + require_once $root.'/scripts/init/init-script.php'; 6 + 7 + $xml = $root.'/externals/cldr/cldr_windows_timezones.xml'; 8 + $xml = Filesystem::readFile($xml); 9 + $xml = new SimpleXMLElement($xml); 10 + 11 + $result_map = array(); 12 + 13 + $ignore = array( 14 + 'UTC', 15 + 'UTC-11', 16 + 'UTC-02', 17 + 'UTC-08', 18 + 'UTC-09', 19 + 'UTC+12', 20 + ); 21 + $ignore = array_fuse($ignore); 22 + 23 + $zones = $xml->windowsZones->mapTimezones->mapZone; 24 + foreach ($zones as $zone) { 25 + $windows_name = (string)$zone['other']; 26 + $target_name = (string)$zone['type']; 27 + 28 + // Ignore the offset-based timezones from the CLDR map, since we handle 29 + // these later. 30 + if (isset($ignore[$windows_name])) { 31 + continue; 32 + } 33 + 34 + // We've already seen this timezone so we don't need to add it to the map 35 + // again. 36 + if (isset($result_map[$windows_name])) { 37 + continue; 38 + } 39 + 40 + $result_map[$windows_name] = $target_name; 41 + } 42 + 43 + asort($result_map); 44 + 45 + echo id(new PhutilJSON()) 46 + ->encodeFormatted($result_map);
+126
resources/timezones/windows-timezones.json
··· 1 + { 2 + "Egypt Standard Time": "Africa/Cairo", 3 + "Morocco Standard Time": "Africa/Casablanca", 4 + "South Africa Standard Time": "Africa/Johannesburg", 5 + "W. Central Africa Standard Time": "Africa/Lagos", 6 + "E. Africa Standard Time": "Africa/Nairobi", 7 + "Libya Standard Time": "Africa/Tripoli", 8 + "Namibia Standard Time": "Africa/Windhoek", 9 + "Aleutian Standard Time": "America/Adak", 10 + "Alaskan Standard Time": "America/Anchorage", 11 + "Tocantins Standard Time": "America/Araguaina", 12 + "Paraguay Standard Time": "America/Asuncion", 13 + "Bahia Standard Time": "America/Bahia", 14 + "SA Pacific Standard Time": "America/Bogota", 15 + "Argentina Standard Time": "America/Buenos_Aires", 16 + "Eastern Standard Time (Mexico)": "America/Cancun", 17 + "Venezuela Standard Time": "America/Caracas", 18 + "SA Eastern Standard Time": "America/Cayenne", 19 + "Central Standard Time": "America/Chicago", 20 + "Mountain Standard Time (Mexico)": "America/Chihuahua", 21 + "Central Brazilian Standard Time": "America/Cuiaba", 22 + "Mountain Standard Time": "America/Denver", 23 + "Greenland Standard Time": "America/Godthab", 24 + "Turks And Caicos Standard Time": "America/Grand_Turk", 25 + "Central America Standard Time": "America/Guatemala", 26 + "Atlantic Standard Time": "America/Halifax", 27 + "Cuba Standard Time": "America/Havana", 28 + "US Eastern Standard Time": "America/Indianapolis", 29 + "SA Western Standard Time": "America/La_Paz", 30 + "Pacific Standard Time": "America/Los_Angeles", 31 + "Central Standard Time (Mexico)": "America/Mexico_City", 32 + "Saint Pierre Standard Time": "America/Miquelon", 33 + "Montevideo Standard Time": "America/Montevideo", 34 + "Eastern Standard Time": "America/New_York", 35 + "US Mountain Standard Time": "America/Phoenix", 36 + "Haiti Standard Time": "America/Port-au-Prince", 37 + "Canada Central Standard Time": "America/Regina", 38 + "Pacific SA Standard Time": "America/Santiago", 39 + "E. South America Standard Time": "America/Sao_Paulo", 40 + "Newfoundland Standard Time": "America/St_Johns", 41 + "Pacific Standard Time (Mexico)": "America/Tijuana", 42 + "Central Asia Standard Time": "Asia/Almaty", 43 + "Jordan Standard Time": "Asia/Amman", 44 + "Arabic Standard Time": "Asia/Baghdad", 45 + "Azerbaijan Standard Time": "Asia/Baku", 46 + "SE Asia Standard Time": "Asia/Bangkok", 47 + "Altai Standard Time": "Asia/Barnaul", 48 + "Middle East Standard Time": "Asia/Beirut", 49 + "India Standard Time": "Asia/Calcutta", 50 + "Transbaikal Standard Time": "Asia/Chita", 51 + "Sri Lanka Standard Time": "Asia/Colombo", 52 + "Syria Standard Time": "Asia/Damascus", 53 + "Bangladesh Standard Time": "Asia/Dhaka", 54 + "Arabian Standard Time": "Asia/Dubai", 55 + "West Bank Standard Time": "Asia/Hebron", 56 + "W. Mongolia Standard Time": "Asia/Hovd", 57 + "North Asia East Standard Time": "Asia/Irkutsk", 58 + "Israel Standard Time": "Asia/Jerusalem", 59 + "Afghanistan Standard Time": "Asia/Kabul", 60 + "Russia Time Zone 11": "Asia/Kamchatka", 61 + "Pakistan Standard Time": "Asia/Karachi", 62 + "Nepal Standard Time": "Asia/Katmandu", 63 + "North Asia Standard Time": "Asia/Krasnoyarsk", 64 + "Magadan Standard Time": "Asia/Magadan", 65 + "N. Central Asia Standard Time": "Asia/Novosibirsk", 66 + "Omsk Standard Time": "Asia/Omsk", 67 + "North Korea Standard Time": "Asia/Pyongyang", 68 + "Myanmar Standard Time": "Asia/Rangoon", 69 + "Arab Standard Time": "Asia/Riyadh", 70 + "Sakhalin Standard Time": "Asia/Sakhalin", 71 + "Korea Standard Time": "Asia/Seoul", 72 + "China Standard Time": "Asia/Shanghai", 73 + "Singapore Standard Time": "Asia/Singapore", 74 + "Russia Time Zone 10": "Asia/Srednekolymsk", 75 + "Taipei Standard Time": "Asia/Taipei", 76 + "West Asia Standard Time": "Asia/Tashkent", 77 + "Georgian Standard Time": "Asia/Tbilisi", 78 + "Iran Standard Time": "Asia/Tehran", 79 + "Tokyo Standard Time": "Asia/Tokyo", 80 + "Tomsk Standard Time": "Asia/Tomsk", 81 + "Ulaanbaatar Standard Time": "Asia/Ulaanbaatar", 82 + "Vladivostok Standard Time": "Asia/Vladivostok", 83 + "Yakutsk Standard Time": "Asia/Yakutsk", 84 + "Ekaterinburg Standard Time": "Asia/Yekaterinburg", 85 + "Caucasus Standard Time": "Asia/Yerevan", 86 + "Azores Standard Time": "Atlantic/Azores", 87 + "Cape Verde Standard Time": "Atlantic/Cape_Verde", 88 + "Greenwich Standard Time": "Atlantic/Reykjavik", 89 + "Cen. Australia Standard Time": "Australia/Adelaide", 90 + "E. Australia Standard Time": "Australia/Brisbane", 91 + "AUS Central Standard Time": "Australia/Darwin", 92 + "Aus Central W. Standard Time": "Australia/Eucla", 93 + "Tasmania Standard Time": "Australia/Hobart", 94 + "Lord Howe Standard Time": "Australia/Lord_Howe", 95 + "W. Australia Standard Time": "Australia/Perth", 96 + "AUS Eastern Standard Time": "Australia/Sydney", 97 + "Dateline Standard Time": "Etc/GMT+12", 98 + "Astrakhan Standard Time": "Europe/Astrakhan", 99 + "W. Europe Standard Time": "Europe/Berlin", 100 + "GTB Standard Time": "Europe/Bucharest", 101 + "Central Europe Standard Time": "Europe/Budapest", 102 + "E. Europe Standard Time": "Europe/Chisinau", 103 + "Turkey Standard Time": "Europe/Istanbul", 104 + "Kaliningrad Standard Time": "Europe/Kaliningrad", 105 + "FLE Standard Time": "Europe/Kiev", 106 + "GMT Standard Time": "Europe/London", 107 + "Belarus Standard Time": "Europe/Minsk", 108 + "Russian Standard Time": "Europe/Moscow", 109 + "Romance Standard Time": "Europe/Paris", 110 + "Russia Time Zone 3": "Europe/Samara", 111 + "Central European Standard Time": "Europe/Warsaw", 112 + "Mauritius Standard Time": "Indian/Mauritius", 113 + "Samoa Standard Time": "Pacific/Apia", 114 + "New Zealand Standard Time": "Pacific/Auckland", 115 + "Bougainville Standard Time": "Pacific/Bougainville", 116 + "Chatham Islands Standard Time": "Pacific/Chatham", 117 + "Easter Island Standard Time": "Pacific/Easter", 118 + "Fiji Standard Time": "Pacific/Fiji", 119 + "Central Pacific Standard Time": "Pacific/Guadalcanal", 120 + "Hawaiian Standard Time": "Pacific/Honolulu", 121 + "Line Islands Standard Time": "Pacific/Kiritimati", 122 + "Marquesas Standard Time": "Pacific/Marquesas", 123 + "Norfolk Standard Time": "Pacific/Norfolk", 124 + "West Pacific Standard Time": "Pacific/Port_Moresby", 125 + "Tonga Standard Time": "Pacific/Tongatapu" 126 + }
+131
scripts/daemon/exec/exec_daemon.php
··· 1 + #!/usr/bin/env php 2 + <?php 3 + 4 + if (function_exists('pcntl_async_signals')) { 5 + pcntl_async_signals(true); 6 + } else { 7 + declare(ticks = 1); 8 + } 9 + 10 + require_once dirname(__FILE__).'/../../__init_script__.php'; 11 + 12 + if (!posix_isatty(STDOUT)) { 13 + $sid = posix_setsid(); 14 + if ($sid <= 0) { 15 + throw new Exception(pht('Failed to create new process session!')); 16 + } 17 + } 18 + 19 + $args = new PhutilArgumentParser($argv); 20 + $args->setTagline(pht('daemon executor')); 21 + $args->setSynopsis(<<<EOHELP 22 + **exec_daemon.php** [__options__] __daemon__ ... 23 + Run an instance of __daemon__. 24 + EOHELP 25 + ); 26 + $args->parse( 27 + array( 28 + array( 29 + 'name' => 'trace', 30 + 'help' => pht('Enable debug tracing.'), 31 + ), 32 + array( 33 + 'name' => 'trace-memory', 34 + 'help' => pht('Enable debug memory tracing.'), 35 + ), 36 + array( 37 + 'name' => 'verbose', 38 + 'help' => pht('Enable verbose activity logging.'), 39 + ), 40 + array( 41 + 'name' => 'label', 42 + 'short' => 'l', 43 + 'param' => 'label', 44 + 'help' => pht( 45 + 'Optional process label. Makes "%s" nicer, no behavioral effects.', 46 + 'ps'), 47 + ), 48 + array( 49 + 'name' => 'daemon', 50 + 'wildcard' => true, 51 + ), 52 + )); 53 + 54 + $trace_memory = $args->getArg('trace-memory'); 55 + $trace_mode = $args->getArg('trace') || $trace_memory; 56 + $verbose = $args->getArg('verbose'); 57 + 58 + if (function_exists('posix_isatty') && posix_isatty(STDIN)) { 59 + fprintf(STDERR, pht('Reading daemon configuration from stdin...')."\n"); 60 + } 61 + $config = @file_get_contents('php://stdin'); 62 + $config = id(new PhutilJSONParser())->parse($config); 63 + 64 + PhutilTypeSpec::checkMap( 65 + $config, 66 + array( 67 + 'log' => 'optional string|null', 68 + 'argv' => 'optional list<wild>', 69 + 'load' => 'optional list<string>', 70 + 'down' => 'optional int', 71 + )); 72 + 73 + $log = idx($config, 'log'); 74 + 75 + if ($log) { 76 + ini_set('error_log', $log); 77 + PhutilErrorHandler::setErrorListener(array('PhutilDaemon', 'errorListener')); 78 + } 79 + 80 + $load = idx($config, 'load', array()); 81 + foreach ($load as $library) { 82 + $library = Filesystem::resolvePath($library); 83 + phutil_load_library($library); 84 + } 85 + 86 + PhutilErrorHandler::initialize(); 87 + 88 + $daemon = $args->getArg('daemon'); 89 + if (!$daemon) { 90 + throw new PhutilArgumentUsageException( 91 + pht('Specify which class of daemon to start.')); 92 + } else if (count($daemon) > 1) { 93 + throw new PhutilArgumentUsageException( 94 + pht('Specify exactly one daemon to start.')); 95 + } else { 96 + $daemon = head($daemon); 97 + if (!class_exists($daemon)) { 98 + throw new PhutilArgumentUsageException( 99 + pht( 100 + 'No class "%s" exists in any known library.', 101 + $daemon)); 102 + } else if (!is_subclass_of($daemon, 'PhutilDaemon')) { 103 + throw new PhutilArgumentUsageException( 104 + pht( 105 + 'Class "%s" is not a subclass of "%s".', 106 + $daemon, 107 + 'PhutilDaemon')); 108 + } 109 + } 110 + 111 + $argv = idx($config, 'argv', array()); 112 + $daemon = newv($daemon, array($argv)); 113 + 114 + if ($trace_mode) { 115 + $daemon->setTraceMode(); 116 + } 117 + 118 + if ($trace_memory) { 119 + $daemon->setTraceMemory(); 120 + } 121 + 122 + if ($verbose) { 123 + $daemon->setVerbose(true); 124 + } 125 + 126 + $down_duration = idx($config, 'down'); 127 + if ($down_duration) { 128 + $daemon->setScaledownDuration($down_duration); 129 + } 130 + 131 + $daemon->execute();
+8 -4
scripts/init/lib.php
··· 8 8 ini_set( 9 9 'include_path', 10 10 $include_path.PATH_SEPARATOR.dirname(__FILE__).'/../../../'); 11 - @include_once 'libphutil/scripts/__init_script__.php'; 12 - if (!@constant('__LIBPHUTIL__')) { 13 - echo "ERROR: Unable to load libphutil. Update your PHP 'include_path' to ". 14 - "include the parent directory of libphutil/.\n"; 11 + 12 + $ok = @include_once 'arcanist/scripts/init/init-script.php'; 13 + if (!$ok) { 14 + echo 15 + 'FATAL ERROR: Unable to load the "Arcanist" library. '. 16 + 'Put "arcanist/" next to "phabricator/" on disk.'; 17 + echo "\n"; 18 + 15 19 exit(1); 16 20 } 17 21
+10
src/__phutil_library_map__.php
··· 5623 5623 'PhutilCodeSnippetContextFreeGrammar' => 'infrastructure/lipsum/code/PhutilCodeSnippetContextFreeGrammar.php', 5624 5624 'PhutilConsoleSyntaxHighlighter' => 'infrastructure/markup/syntax/highlighter/PhutilConsoleSyntaxHighlighter.php', 5625 5625 'PhutilContextFreeGrammar' => 'infrastructure/lipsum/PhutilContextFreeGrammar.php', 5626 + 'PhutilDaemon' => 'infrastructure/daemon/PhutilDaemon.php', 5627 + 'PhutilDaemonHandle' => 'infrastructure/daemon/PhutilDaemonHandle.php', 5628 + 'PhutilDaemonOverseer' => 'infrastructure/daemon/PhutilDaemonOverseer.php', 5629 + 'PhutilDaemonOverseerModule' => 'infrastructure/daemon/PhutilDaemonOverseerModule.php', 5630 + 'PhutilDaemonPool' => 'infrastructure/daemon/PhutilDaemonPool.php', 5626 5631 'PhutilDefaultSyntaxHighlighter' => 'infrastructure/markup/syntax/highlighter/PhutilDefaultSyntaxHighlighter.php', 5627 5632 'PhutilDefaultSyntaxHighlighterEngine' => 'infrastructure/markup/syntax/engine/PhutilDefaultSyntaxHighlighterEngine.php', 5628 5633 'PhutilDefaultSyntaxHighlighterEnginePygmentsFuture' => 'infrastructure/markup/syntax/highlighter/pygments/PhutilDefaultSyntaxHighlighterEnginePygmentsFuture.php', ··· 12522 12527 'PhutilCodeSnippetContextFreeGrammar' => 'PhutilContextFreeGrammar', 12523 12528 'PhutilConsoleSyntaxHighlighter' => 'Phobject', 12524 12529 'PhutilContextFreeGrammar' => 'Phobject', 12530 + 'PhutilDaemon' => 'Phobject', 12531 + 'PhutilDaemonHandle' => 'Phobject', 12532 + 'PhutilDaemonOverseer' => 'Phobject', 12533 + 'PhutilDaemonOverseerModule' => 'Phobject', 12534 + 'PhutilDaemonPool' => 'Phobject', 12525 12535 'PhutilDefaultSyntaxHighlighter' => 'Phobject', 12526 12536 'PhutilDefaultSyntaxHighlighterEngine' => 'PhutilSyntaxHighlighterEngine', 12527 12537 'PhutilDefaultSyntaxHighlighterEnginePygmentsFuture' => 'FutureProxy',
+2 -2
src/applications/calendar/parser/ics/PhutilICSParser.php
··· 849 849 ); 850 850 851 851 // Load the map of Windows timezones. 852 - $root_path = dirname(phutil_get_library_root('phutil')); 853 - $windows_path = $root_path.'/resources/timezones/windows_timezones.json'; 852 + $root_path = dirname(phutil_get_library_root('phabricator')); 853 + $windows_path = $root_path.'/resources/timezones/windows-timezones.json'; 854 854 $windows_data = Filesystem::readFile($windows_path); 855 855 $windows_zones = phutil_json_decode($windows_data); 856 856
+393
src/infrastructure/daemon/PhutilDaemon.php
··· 1 + <?php 2 + 3 + /** 4 + * Scaffolding for implementing robust background processing scripts. 5 + * 6 + * 7 + * Autoscaling 8 + * =========== 9 + * 10 + * Autoscaling automatically launches copies of a daemon when it is busy 11 + * (scaling the pool up) and stops them when they're idle (scaling the pool 12 + * down). This is appropriate for daemons which perform highly parallelizable 13 + * work. 14 + * 15 + * To make a daemon support autoscaling, the implementation should look 16 + * something like this: 17 + * 18 + * while (!$this->shouldExit()) { 19 + * if (work_available()) { 20 + * $this->willBeginWork(); 21 + * do_work(); 22 + * $this->sleep(0); 23 + * } else { 24 + * $this->willBeginIdle(); 25 + * $this->sleep(1); 26 + * } 27 + * } 28 + * 29 + * In particular, call @{method:willBeginWork} before becoming busy, and 30 + * @{method:willBeginIdle} when no work is available. If the daemon is launched 31 + * into an autoscale pool, this will cause the pool to automatically scale up 32 + * when busy and down when idle. 33 + * 34 + * See @{class:PhutilHighIntensityIntervalDaemon} for an example of a simple 35 + * autoscaling daemon. 36 + * 37 + * Launching a daemon which does not make these callbacks into an autoscale 38 + * pool will have no effect. 39 + * 40 + * @task overseer Communicating With the Overseer 41 + * @task autoscale Autoscaling Daemon Pools 42 + */ 43 + abstract class PhutilDaemon extends Phobject { 44 + 45 + const MESSAGETYPE_STDOUT = 'stdout'; 46 + const MESSAGETYPE_HEARTBEAT = 'heartbeat'; 47 + const MESSAGETYPE_BUSY = 'busy'; 48 + const MESSAGETYPE_IDLE = 'idle'; 49 + const MESSAGETYPE_DOWN = 'down'; 50 + const MESSAGETYPE_HIBERNATE = 'hibernate'; 51 + 52 + const WORKSTATE_BUSY = 'busy'; 53 + const WORKSTATE_IDLE = 'idle'; 54 + 55 + private $argv; 56 + private $traceMode; 57 + private $traceMemory; 58 + private $verbose; 59 + private $notifyReceived; 60 + private $inGracefulShutdown; 61 + private $workState = null; 62 + private $idleSince = null; 63 + private $scaledownDuration; 64 + 65 + final public function setVerbose($verbose) { 66 + $this->verbose = $verbose; 67 + return $this; 68 + } 69 + 70 + final public function getVerbose() { 71 + return $this->verbose; 72 + } 73 + 74 + final public function setScaledownDuration($scaledown_duration) { 75 + $this->scaledownDuration = $scaledown_duration; 76 + return $this; 77 + } 78 + 79 + final public function getScaledownDuration() { 80 + return $this->scaledownDuration; 81 + } 82 + 83 + final public function __construct(array $argv) { 84 + $this->argv = $argv; 85 + 86 + $router = PhutilSignalRouter::getRouter(); 87 + $handler_key = 'daemon.term'; 88 + if (!$router->getHandler($handler_key)) { 89 + $handler = new PhutilCallbackSignalHandler( 90 + SIGTERM, 91 + __CLASS__.'::onTermSignal'); 92 + $router->installHandler($handler_key, $handler); 93 + } 94 + 95 + pcntl_signal(SIGINT, array($this, 'onGracefulSignal')); 96 + pcntl_signal(SIGUSR2, array($this, 'onNotifySignal')); 97 + 98 + // Without discard mode, this consumes unbounded amounts of memory. Keep 99 + // memory bounded. 100 + PhutilServiceProfiler::getInstance()->enableDiscardMode(); 101 + 102 + $this->beginStdoutCapture(); 103 + } 104 + 105 + final public function __destruct() { 106 + $this->endStdoutCapture(); 107 + } 108 + 109 + final public function stillWorking() { 110 + $this->emitOverseerMessage(self::MESSAGETYPE_HEARTBEAT, null); 111 + 112 + if ($this->traceMemory) { 113 + $daemon = get_class($this); 114 + fprintf( 115 + STDERR, 116 + "%s %s %s\n", 117 + '<RAMS>', 118 + $daemon, 119 + pht( 120 + 'Memory Usage: %s KB', 121 + new PhutilNumber(memory_get_usage() / 1024, 1))); 122 + } 123 + } 124 + 125 + final public function shouldExit() { 126 + return $this->inGracefulShutdown; 127 + } 128 + 129 + final protected function shouldHibernate($duration) { 130 + // Don't hibernate if we don't have very long to sleep. 131 + if ($duration < 30) { 132 + return false; 133 + } 134 + 135 + // Never hibernate if we're part of a pool and could scale down instead. 136 + // We only hibernate the last process to drop the pool size to zero. 137 + if ($this->getScaledownDuration()) { 138 + return false; 139 + } 140 + 141 + // Don't hibernate for too long. 142 + $duration = min($duration, phutil_units('3 minutes in seconds')); 143 + 144 + $this->emitOverseerMessage( 145 + self::MESSAGETYPE_HIBERNATE, 146 + array( 147 + 'duration' => $duration, 148 + )); 149 + 150 + $this->log( 151 + pht( 152 + 'Preparing to hibernate for %s second(s).', 153 + new PhutilNumber($duration))); 154 + 155 + return true; 156 + } 157 + 158 + final protected function sleep($duration) { 159 + $this->notifyReceived = false; 160 + $this->willSleep($duration); 161 + $this->stillWorking(); 162 + 163 + $scale_down = $this->getScaledownDuration(); 164 + 165 + $max_sleep = 60; 166 + if ($scale_down) { 167 + $max_sleep = min($max_sleep, $scale_down); 168 + } 169 + 170 + if ($scale_down) { 171 + if ($this->workState == self::WORKSTATE_IDLE) { 172 + $dur = $this->getIdleDuration(); 173 + $this->log(pht('Idle for %s seconds.', $dur)); 174 + } 175 + } 176 + 177 + while ($duration > 0 && 178 + !$this->notifyReceived && 179 + !$this->shouldExit()) { 180 + 181 + // If this is an autoscaling clone and we've been idle for too long, 182 + // we're going to scale the pool down by exiting and not restarting. The 183 + // DOWN message tells the overseer that we don't want to be restarted. 184 + if ($scale_down) { 185 + if ($this->workState == self::WORKSTATE_IDLE) { 186 + if ($this->idleSince && ($this->idleSince + $scale_down < time())) { 187 + $this->inGracefulShutdown = true; 188 + $this->emitOverseerMessage(self::MESSAGETYPE_DOWN, null); 189 + $this->log( 190 + pht( 191 + 'Daemon was idle for more than %s second(s), '. 192 + 'scaling pool down.', 193 + new PhutilNumber($scale_down))); 194 + break; 195 + } 196 + } 197 + } 198 + 199 + sleep(min($duration, $max_sleep)); 200 + $duration -= $max_sleep; 201 + $this->stillWorking(); 202 + } 203 + } 204 + 205 + protected function willSleep($duration) { 206 + return; 207 + } 208 + 209 + public static function onTermSignal($signo) { 210 + self::didCatchSignal($signo); 211 + } 212 + 213 + final protected function getArgv() { 214 + return $this->argv; 215 + } 216 + 217 + final public function execute() { 218 + $this->willRun(); 219 + $this->run(); 220 + } 221 + 222 + abstract protected function run(); 223 + 224 + final public function setTraceMemory() { 225 + $this->traceMemory = true; 226 + return $this; 227 + } 228 + 229 + final public function getTraceMemory() { 230 + return $this->traceMemory; 231 + } 232 + 233 + final public function setTraceMode() { 234 + $this->traceMode = true; 235 + PhutilServiceProfiler::installEchoListener(); 236 + PhutilConsole::getConsole()->getServer()->setEnableLog(true); 237 + $this->didSetTraceMode(); 238 + return $this; 239 + } 240 + 241 + final public function getTraceMode() { 242 + return $this->traceMode; 243 + } 244 + 245 + final public function onGracefulSignal($signo) { 246 + self::didCatchSignal($signo); 247 + $this->inGracefulShutdown = true; 248 + } 249 + 250 + final public function onNotifySignal($signo) { 251 + self::didCatchSignal($signo); 252 + $this->notifyReceived = true; 253 + $this->onNotify($signo); 254 + } 255 + 256 + protected function onNotify($signo) { 257 + // This is a hook for subclasses. 258 + } 259 + 260 + protected function willRun() { 261 + // This is a hook for subclasses. 262 + } 263 + 264 + protected function didSetTraceMode() { 265 + // This is a hook for subclasses. 266 + } 267 + 268 + final protected function log($message) { 269 + if ($this->verbose) { 270 + $daemon = get_class($this); 271 + fprintf(STDERR, "%s %s %s\n", '<VERB>', $daemon, $message); 272 + } 273 + } 274 + 275 + private static function didCatchSignal($signo) { 276 + $signame = phutil_get_signal_name($signo); 277 + fprintf( 278 + STDERR, 279 + "%s Caught signal %s (%s).\n", 280 + '<SGNL>', 281 + $signo, 282 + $signame); 283 + } 284 + 285 + 286 + /* -( Communicating With the Overseer )------------------------------------ */ 287 + 288 + 289 + private function beginStdoutCapture() { 290 + ob_start(array($this, 'didReceiveStdout'), 2); 291 + } 292 + 293 + private function endStdoutCapture() { 294 + ob_end_flush(); 295 + } 296 + 297 + public function didReceiveStdout($data) { 298 + if (!strlen($data)) { 299 + return ''; 300 + } 301 + 302 + return $this->encodeOverseerMessage(self::MESSAGETYPE_STDOUT, $data); 303 + } 304 + 305 + private function encodeOverseerMessage($type, $data) { 306 + $structure = array($type); 307 + 308 + if ($data !== null) { 309 + $structure[] = $data; 310 + } 311 + 312 + return json_encode($structure)."\n"; 313 + } 314 + 315 + private function emitOverseerMessage($type, $data) { 316 + $this->endStdoutCapture(); 317 + echo $this->encodeOverseerMessage($type, $data); 318 + $this->beginStdoutCapture(); 319 + } 320 + 321 + public static function errorListener($event, $value, array $metadata) { 322 + // If the caller has redirected the error log to a file, PHP won't output 323 + // messages to stderr, so the overseer can't capture them. Install a 324 + // listener which just echoes errors to stderr, so the overseer is always 325 + // aware of errors. 326 + 327 + $console = PhutilConsole::getConsole(); 328 + $message = idx($metadata, 'default_message'); 329 + 330 + if ($message) { 331 + $console->writeErr("%s\n", $message); 332 + } 333 + if (idx($metadata, 'trace')) { 334 + $trace = PhutilErrorHandler::formatStacktrace($metadata['trace']); 335 + $console->writeErr("%s\n", $trace); 336 + } 337 + } 338 + 339 + 340 + /* -( Autoscaling )-------------------------------------------------------- */ 341 + 342 + 343 + /** 344 + * Prepare to become busy. This may autoscale the pool up. 345 + * 346 + * This notifies the overseer that the daemon has become busy. If daemons 347 + * that are part of an autoscale pool are continuously busy for a prolonged 348 + * period of time, the overseer may scale up the pool. 349 + * 350 + * @return this 351 + * @task autoscale 352 + */ 353 + protected function willBeginWork() { 354 + if ($this->workState != self::WORKSTATE_BUSY) { 355 + $this->workState = self::WORKSTATE_BUSY; 356 + $this->idleSince = null; 357 + $this->emitOverseerMessage(self::MESSAGETYPE_BUSY, null); 358 + } 359 + 360 + return $this; 361 + } 362 + 363 + 364 + /** 365 + * Prepare to idle. This may autoscale the pool down. 366 + * 367 + * This notifies the overseer that the daemon is no longer busy. If daemons 368 + * that are part of an autoscale pool are idle for a prolonged period of 369 + * time, they may exit to scale the pool down. 370 + * 371 + * @return this 372 + * @task autoscale 373 + */ 374 + protected function willBeginIdle() { 375 + if ($this->workState != self::WORKSTATE_IDLE) { 376 + $this->workState = self::WORKSTATE_IDLE; 377 + $this->idleSince = time(); 378 + $this->emitOverseerMessage(self::MESSAGETYPE_IDLE, null); 379 + } 380 + 381 + return $this; 382 + } 383 + 384 + protected function getIdleDuration() { 385 + if (!$this->idleSince) { 386 + return null; 387 + } 388 + 389 + $now = time(); 390 + return ($now - $this->idleSince); 391 + } 392 + 393 + }
+506
src/infrastructure/daemon/PhutilDaemonHandle.php
··· 1 + <?php 2 + 3 + final class PhutilDaemonHandle extends Phobject { 4 + 5 + const EVENT_DID_LAUNCH = 'daemon.didLaunch'; 6 + const EVENT_DID_LOG = 'daemon.didLogMessage'; 7 + const EVENT_DID_HEARTBEAT = 'daemon.didHeartbeat'; 8 + const EVENT_WILL_GRACEFUL = 'daemon.willGraceful'; 9 + const EVENT_WILL_EXIT = 'daemon.willExit'; 10 + 11 + private $pool; 12 + private $properties; 13 + private $future; 14 + private $argv; 15 + 16 + private $restartAt; 17 + private $busyEpoch; 18 + 19 + private $pid; 20 + private $daemonID; 21 + private $deadline; 22 + private $heartbeat; 23 + private $stdoutBuffer; 24 + private $shouldRestart = true; 25 + private $shouldShutdown; 26 + private $hibernating = false; 27 + private $shouldSendExitEvent = false; 28 + 29 + private function __construct() { 30 + // <empty> 31 + } 32 + 33 + public static function newFromConfig(array $config) { 34 + PhutilTypeSpec::checkMap( 35 + $config, 36 + array( 37 + 'class' => 'string', 38 + 'argv' => 'optional list<string>', 39 + 'load' => 'optional list<string>', 40 + 'log' => 'optional string|null', 41 + 'down' => 'optional int', 42 + )); 43 + 44 + $config = $config + array( 45 + 'argv' => array(), 46 + 'load' => array(), 47 + 'log' => null, 48 + 'down' => 15, 49 + ); 50 + 51 + $daemon = new self(); 52 + $daemon->properties = $config; 53 + $daemon->daemonID = $daemon->generateDaemonID(); 54 + 55 + return $daemon; 56 + } 57 + 58 + public function setDaemonPool(PhutilDaemonPool $daemon_pool) { 59 + $this->pool = $daemon_pool; 60 + return $this; 61 + } 62 + 63 + public function getDaemonPool() { 64 + return $this->pool; 65 + } 66 + 67 + public function getBusyEpoch() { 68 + return $this->busyEpoch; 69 + } 70 + 71 + public function getDaemonClass() { 72 + return $this->getProperty('class'); 73 + } 74 + 75 + private function getProperty($key) { 76 + return idx($this->properties, $key); 77 + } 78 + 79 + public function setCommandLineArguments(array $arguments) { 80 + $this->argv = $arguments; 81 + return $this; 82 + } 83 + 84 + public function getCommandLineArguments() { 85 + return $this->argv; 86 + } 87 + 88 + public function getDaemonArguments() { 89 + return $this->getProperty('argv'); 90 + } 91 + 92 + public function didLaunch() { 93 + $this->restartAt = time(); 94 + $this->shouldSendExitEvent = true; 95 + 96 + $this->dispatchEvent( 97 + self::EVENT_DID_LAUNCH, 98 + array( 99 + 'argv' => $this->getCommandLineArguments(), 100 + 'explicitArgv' => $this->getDaemonArguments(), 101 + )); 102 + 103 + return $this; 104 + } 105 + 106 + public function isRunning() { 107 + return (bool)$this->future; 108 + } 109 + 110 + public function isHibernating() { 111 + return 112 + !$this->isRunning() && 113 + !$this->isDone() && 114 + $this->hibernating; 115 + } 116 + 117 + public function wakeFromHibernation() { 118 + if (!$this->isHibernating()) { 119 + return $this; 120 + } 121 + 122 + $this->logMessage( 123 + 'WAKE', 124 + pht( 125 + 'Process is being awakened from hibernation.')); 126 + 127 + $this->restartAt = time(); 128 + $this->update(); 129 + 130 + return $this; 131 + } 132 + 133 + public function isDone() { 134 + return (!$this->shouldRestart && !$this->isRunning()); 135 + } 136 + 137 + public function getFuture() { 138 + return $this->future; 139 + } 140 + 141 + public function update() { 142 + if (!$this->isRunning()) { 143 + if (!$this->shouldRestart) { 144 + return; 145 + } 146 + if (!$this->restartAt || (time() < $this->restartAt)) { 147 + return; 148 + } 149 + if ($this->shouldShutdown) { 150 + return; 151 + } 152 + $this->startDaemonProcess(); 153 + } 154 + 155 + $future = $this->future; 156 + 157 + $result = null; 158 + if ($future->isReady()) { 159 + $result = $future->resolve(); 160 + } 161 + 162 + list($stdout, $stderr) = $future->read(); 163 + $future->discardBuffers(); 164 + 165 + if (strlen($stdout)) { 166 + $this->didReadStdout($stdout); 167 + } 168 + 169 + $stderr = trim($stderr); 170 + if (strlen($stderr)) { 171 + foreach (phutil_split_lines($stderr, false) as $line) { 172 + $this->logMessage('STDE', $line); 173 + } 174 + } 175 + 176 + if ($result !== null) { 177 + list($err) = $result; 178 + 179 + if ($err) { 180 + $this->logMessage('FAIL', pht('Process exited with error %s.', $err)); 181 + } else { 182 + $this->logMessage('DONE', pht('Process exited normally.')); 183 + } 184 + 185 + $this->future = null; 186 + 187 + if ($this->shouldShutdown) { 188 + $this->restartAt = null; 189 + } else { 190 + $this->scheduleRestart(); 191 + } 192 + } 193 + 194 + $this->updateHeartbeatEvent(); 195 + $this->updateHangDetection(); 196 + } 197 + 198 + private function updateHeartbeatEvent() { 199 + if ($this->heartbeat > time()) { 200 + return; 201 + } 202 + 203 + $this->heartbeat = time() + $this->getHeartbeatEventFrequency(); 204 + $this->dispatchEvent(self::EVENT_DID_HEARTBEAT); 205 + } 206 + 207 + private function updateHangDetection() { 208 + if (!$this->isRunning()) { 209 + return; 210 + } 211 + 212 + if (time() > $this->deadline) { 213 + $this->logMessage('HANG', pht('Hang detected. Restarting process.')); 214 + $this->annihilateProcessGroup(); 215 + $this->scheduleRestart(); 216 + } 217 + } 218 + 219 + private function scheduleRestart() { 220 + // Wait a minimum of a few sceconds before restarting, but we may wait 221 + // longer if the daemon has initiated hibernation. 222 + $default_restart = time() + self::getWaitBeforeRestart(); 223 + if ($default_restart >= $this->restartAt) { 224 + $this->restartAt = $default_restart; 225 + } 226 + 227 + $this->logMessage( 228 + 'WAIT', 229 + pht( 230 + 'Waiting %s second(s) to restart process.', 231 + new PhutilNumber($this->restartAt - time()))); 232 + } 233 + 234 + /** 235 + * Generate a unique ID for this daemon. 236 + * 237 + * @return string A unique daemon ID. 238 + */ 239 + private function generateDaemonID() { 240 + return substr(getmypid().':'.Filesystem::readRandomCharacters(12), 0, 12); 241 + } 242 + 243 + public function getDaemonID() { 244 + return $this->daemonID; 245 + } 246 + 247 + public function getPID() { 248 + return $this->pid; 249 + } 250 + 251 + private function getCaptureBufferSize() { 252 + return 65535; 253 + } 254 + 255 + private function getRequiredHeartbeatFrequency() { 256 + return 86400; 257 + } 258 + 259 + public static function getWaitBeforeRestart() { 260 + return 5; 261 + } 262 + 263 + public static function getHeartbeatEventFrequency() { 264 + return 120; 265 + } 266 + 267 + private function getKillDelay() { 268 + return 3; 269 + } 270 + 271 + private function getDaemonCWD() { 272 + $root = dirname(phutil_get_library_root('phabricator')); 273 + return $root.'/scripts/daemon/exec/'; 274 + } 275 + 276 + private function newExecFuture() { 277 + $class = $this->getDaemonClass(); 278 + $argv = $this->getCommandLineArguments(); 279 + $buffer_size = $this->getCaptureBufferSize(); 280 + 281 + // NOTE: PHP implements proc_open() by running 'sh -c'. On most systems this 282 + // is bash, but on Ubuntu it's dash. When you proc_open() using bash, you 283 + // get one new process (the command you ran). When you proc_open() using 284 + // dash, you get two new processes: the command you ran and a parent 285 + // "dash -c" (or "sh -c") process. This means that the child process's PID 286 + // is actually the 'dash' PID, not the command's PID. To avoid this, use 287 + // 'exec' to replace the shell process with the real process; without this, 288 + // the child will call posix_getppid(), be given the pid of the 'sh -c' 289 + // process, and send it SIGUSR1 to keepalive which will terminate it 290 + // immediately. We also won't be able to do process group management because 291 + // the shell process won't properly posix_setsid() so the pgid of the child 292 + // won't be meaningful. 293 + 294 + $config = $this->properties; 295 + unset($config['class']); 296 + $config = phutil_json_encode($config); 297 + 298 + return id(new ExecFuture('exec ./exec_daemon.php %s %Ls', $class, $argv)) 299 + ->setCWD($this->getDaemonCWD()) 300 + ->setStdoutSizeLimit($buffer_size) 301 + ->setStderrSizeLimit($buffer_size) 302 + ->write($config); 303 + } 304 + 305 + /** 306 + * Dispatch an event to event listeners. 307 + * 308 + * @param string Event type. 309 + * @param dict Event parameters. 310 + * @return void 311 + */ 312 + private function dispatchEvent($type, array $params = array()) { 313 + $data = array( 314 + 'id' => $this->getDaemonID(), 315 + 'daemonClass' => $this->getDaemonClass(), 316 + 'childPID' => $this->getPID(), 317 + ) + $params; 318 + 319 + $event = new PhutilEvent($type, $data); 320 + 321 + try { 322 + PhutilEventEngine::dispatchEvent($event); 323 + } catch (Exception $ex) { 324 + phlog($ex); 325 + } 326 + } 327 + 328 + private function annihilateProcessGroup() { 329 + $pid = $this->getPID(); 330 + 331 + $pgid = posix_getpgid($pid); 332 + if ($pid && $pgid) { 333 + posix_kill(-$pgid, SIGTERM); 334 + sleep($this->getKillDelay()); 335 + posix_kill(-$pgid, SIGKILL); 336 + $this->pid = null; 337 + } 338 + } 339 + 340 + private function startDaemonProcess() { 341 + $this->logMessage('INIT', pht('Starting process.')); 342 + 343 + $this->deadline = time() + $this->getRequiredHeartbeatFrequency(); 344 + $this->heartbeat = time() + self::getHeartbeatEventFrequency(); 345 + $this->stdoutBuffer = ''; 346 + $this->hibernating = false; 347 + 348 + $this->future = $this->newExecFuture(); 349 + $this->future->start(); 350 + 351 + $this->pid = $this->future->getPID(); 352 + } 353 + 354 + private function didReadStdout($data) { 355 + $this->stdoutBuffer .= $data; 356 + while (true) { 357 + $pos = strpos($this->stdoutBuffer, "\n"); 358 + if ($pos === false) { 359 + break; 360 + } 361 + $message = substr($this->stdoutBuffer, 0, $pos); 362 + $this->stdoutBuffer = substr($this->stdoutBuffer, $pos + 1); 363 + 364 + try { 365 + $structure = phutil_json_decode($message); 366 + } catch (PhutilJSONParserException $ex) { 367 + $structure = array(); 368 + } 369 + 370 + switch (idx($structure, 0)) { 371 + case PhutilDaemon::MESSAGETYPE_STDOUT: 372 + $this->logMessage('STDO', idx($structure, 1)); 373 + break; 374 + case PhutilDaemon::MESSAGETYPE_HEARTBEAT: 375 + $this->deadline = time() + $this->getRequiredHeartbeatFrequency(); 376 + break; 377 + case PhutilDaemon::MESSAGETYPE_BUSY: 378 + if (!$this->busyEpoch) { 379 + $this->busyEpoch = time(); 380 + } 381 + break; 382 + case PhutilDaemon::MESSAGETYPE_IDLE: 383 + $this->busyEpoch = null; 384 + break; 385 + case PhutilDaemon::MESSAGETYPE_DOWN: 386 + // The daemon is exiting because it doesn't have enough work and it 387 + // is trying to scale the pool down. We should not restart it. 388 + $this->shouldRestart = false; 389 + $this->shouldShutdown = true; 390 + break; 391 + case PhutilDaemon::MESSAGETYPE_HIBERNATE: 392 + $config = idx($structure, 1); 393 + $duration = (int)idx($config, 'duration', 0); 394 + $this->restartAt = time() + $duration; 395 + $this->hibernating = true; 396 + $this->busyEpoch = null; 397 + $this->logMessage( 398 + 'ZZZZ', 399 + pht( 400 + 'Process is preparing to hibernate for %s second(s).', 401 + new PhutilNumber($duration))); 402 + break; 403 + default: 404 + // If we can't parse this or it isn't a message we understand, just 405 + // emit the raw message. 406 + $this->logMessage('STDO', pht('<Malformed> %s', $message)); 407 + break; 408 + } 409 + } 410 + } 411 + 412 + public function didReceiveNotifySignal($signo) { 413 + $pid = $this->getPID(); 414 + if ($pid) { 415 + posix_kill($pid, $signo); 416 + } 417 + } 418 + 419 + public function didReceiveReloadSignal($signo) { 420 + $signame = phutil_get_signal_name($signo); 421 + if ($signame) { 422 + $sigmsg = pht( 423 + 'Reloading in response to signal %d (%s).', 424 + $signo, 425 + $signame); 426 + } else { 427 + $sigmsg = pht( 428 + 'Reloading in response to signal %d.', 429 + $signo); 430 + } 431 + 432 + $this->logMessage('RELO', $sigmsg, $signo); 433 + 434 + // This signal means "stop the current process gracefully, then launch 435 + // a new identical process once it exits". This can be used to update 436 + // daemons after code changes (the new processes will run the new code) 437 + // without aborting any running tasks. 438 + 439 + // We SIGINT the daemon but don't set the shutdown flag, so it will 440 + // naturally be restarted after it exits, as though it had exited after an 441 + // unhandled exception. 442 + 443 + posix_kill($this->getPID(), SIGINT); 444 + } 445 + 446 + public function didReceiveGracefulSignal($signo) { 447 + $this->shouldShutdown = true; 448 + $this->shouldRestart = false; 449 + 450 + $signame = phutil_get_signal_name($signo); 451 + if ($signame) { 452 + $sigmsg = pht( 453 + 'Graceful shutdown in response to signal %d (%s).', 454 + $signo, 455 + $signame); 456 + } else { 457 + $sigmsg = pht( 458 + 'Graceful shutdown in response to signal %d.', 459 + $signo); 460 + } 461 + 462 + $this->logMessage('DONE', $sigmsg, $signo); 463 + 464 + posix_kill($this->getPID(), SIGINT); 465 + } 466 + 467 + public function didReceiveTerminateSignal($signo) { 468 + $this->shouldShutdown = true; 469 + $this->shouldRestart = false; 470 + 471 + $signame = phutil_get_signal_name($signo); 472 + if ($signame) { 473 + $sigmsg = pht( 474 + 'Shutting down in response to signal %s (%s).', 475 + $signo, 476 + $signame); 477 + } else { 478 + $sigmsg = pht('Shutting down in response to signal %s.', $signo); 479 + } 480 + 481 + $this->logMessage('EXIT', $sigmsg, $signo); 482 + $this->annihilateProcessGroup(); 483 + } 484 + 485 + private function logMessage($type, $message, $context = null) { 486 + $this->getDaemonPool()->logMessage($type, $message, $context); 487 + 488 + $this->dispatchEvent( 489 + self::EVENT_DID_LOG, 490 + array( 491 + 'type' => $type, 492 + 'message' => $message, 493 + 'context' => $context, 494 + )); 495 + } 496 + 497 + public function didExit() { 498 + if ($this->shouldSendExitEvent) { 499 + $this->dispatchEvent(self::EVENT_WILL_EXIT); 500 + $this->shouldSendExitEvent = false; 501 + } 502 + 503 + return $this; 504 + } 505 + 506 + }
+405
src/infrastructure/daemon/PhutilDaemonOverseer.php
··· 1 + <?php 2 + 3 + /** 4 + * Oversees a daemon and restarts it if it fails. 5 + * 6 + * @task signals Signal Handling 7 + */ 8 + final class PhutilDaemonOverseer extends Phobject { 9 + 10 + private $argv; 11 + private static $instance; 12 + 13 + private $config; 14 + private $pools = array(); 15 + private $traceMode; 16 + private $traceMemory; 17 + private $daemonize; 18 + private $log; 19 + private $libraries = array(); 20 + private $modules = array(); 21 + private $verbose; 22 + private $startEpoch; 23 + private $autoscale = array(); 24 + private $autoscaleConfig = array(); 25 + 26 + const SIGNAL_NOTIFY = 'signal/notify'; 27 + const SIGNAL_RELOAD = 'signal/reload'; 28 + const SIGNAL_GRACEFUL = 'signal/graceful'; 29 + const SIGNAL_TERMINATE = 'signal/terminate'; 30 + 31 + private $err = 0; 32 + private $inAbruptShutdown; 33 + private $inGracefulShutdown; 34 + 35 + public function __construct(array $argv) { 36 + PhutilServiceProfiler::getInstance()->enableDiscardMode(); 37 + 38 + $args = new PhutilArgumentParser($argv); 39 + $args->setTagline(pht('daemon overseer')); 40 + $args->setSynopsis(<<<EOHELP 41 + **launch_daemon.php** [__options__] __daemon__ 42 + Launch and oversee an instance of __daemon__. 43 + EOHELP 44 + ); 45 + $args->parseStandardArguments(); 46 + $args->parse( 47 + array( 48 + array( 49 + 'name' => 'trace-memory', 50 + 'help' => pht('Enable debug memory tracing.'), 51 + ), 52 + array( 53 + 'name' => 'verbose', 54 + 'help' => pht('Enable verbose activity logging.'), 55 + ), 56 + array( 57 + 'name' => 'label', 58 + 'short' => 'l', 59 + 'param' => 'label', 60 + 'help' => pht( 61 + 'Optional process label. Makes "%s" nicer, no behavioral effects.', 62 + 'ps'), 63 + ), 64 + )); 65 + $argv = array(); 66 + 67 + if ($args->getArg('trace')) { 68 + $this->traceMode = true; 69 + $argv[] = '--trace'; 70 + } 71 + 72 + if ($args->getArg('trace-memory')) { 73 + $this->traceMode = true; 74 + $this->traceMemory = true; 75 + $argv[] = '--trace-memory'; 76 + } 77 + $verbose = $args->getArg('verbose'); 78 + if ($verbose) { 79 + $this->verbose = true; 80 + $argv[] = '--verbose'; 81 + } 82 + 83 + $label = $args->getArg('label'); 84 + if ($label) { 85 + $argv[] = '-l'; 86 + $argv[] = $label; 87 + } 88 + 89 + $this->argv = $argv; 90 + 91 + if (function_exists('posix_isatty') && posix_isatty(STDIN)) { 92 + fprintf(STDERR, pht('Reading daemon configuration from stdin...')."\n"); 93 + } 94 + $config = @file_get_contents('php://stdin'); 95 + $config = id(new PhutilJSONParser())->parse($config); 96 + 97 + $this->libraries = idx($config, 'load'); 98 + $this->log = idx($config, 'log'); 99 + $this->daemonize = idx($config, 'daemonize'); 100 + 101 + $this->config = $config; 102 + 103 + if (self::$instance) { 104 + throw new Exception( 105 + pht('You may not instantiate more than one Overseer per process.')); 106 + } 107 + 108 + self::$instance = $this; 109 + 110 + $this->startEpoch = time(); 111 + 112 + if (!idx($config, 'daemons')) { 113 + throw new PhutilArgumentUsageException( 114 + pht('You must specify at least one daemon to start!')); 115 + } 116 + 117 + if ($this->log) { 118 + // NOTE: Now that we're committed to daemonizing, redirect the error 119 + // log if we have a `--log` parameter. Do this at the last moment 120 + // so as many setup issues as possible are surfaced. 121 + ini_set('error_log', $this->log); 122 + } 123 + 124 + if ($this->daemonize) { 125 + // We need to get rid of these or the daemon will hang when we TERM it 126 + // waiting for something to read the buffers. TODO: Learn how unix works. 127 + fclose(STDOUT); 128 + fclose(STDERR); 129 + ob_start(); 130 + 131 + $pid = pcntl_fork(); 132 + if ($pid === -1) { 133 + throw new Exception(pht('Unable to fork!')); 134 + } else if ($pid) { 135 + exit(0); 136 + } 137 + 138 + $sid = posix_setsid(); 139 + if ($sid <= 0) { 140 + throw new Exception(pht('Failed to create new process session!')); 141 + } 142 + } 143 + 144 + $this->logMessage( 145 + 'OVER', 146 + pht( 147 + 'Started new daemon overseer (with PID "%s").', 148 + getmypid())); 149 + 150 + $this->modules = PhutilDaemonOverseerModule::getAllModules(); 151 + 152 + $this->installSignalHandlers(); 153 + } 154 + 155 + public function addLibrary($library) { 156 + $this->libraries[] = $library; 157 + return $this; 158 + } 159 + 160 + public function run() { 161 + $this->createDaemonPools(); 162 + 163 + while (true) { 164 + if ($this->shouldReloadDaemons()) { 165 + $this->didReceiveSignal(SIGHUP); 166 + } 167 + 168 + $futures = array(); 169 + 170 + $running_pools = false; 171 + foreach ($this->getDaemonPools() as $pool) { 172 + $pool->updatePool(); 173 + 174 + if (!$this->shouldShutdown()) { 175 + if ($pool->isHibernating()) { 176 + if ($this->shouldWakePool($pool)) { 177 + $pool->wakeFromHibernation(); 178 + } 179 + } 180 + } 181 + 182 + foreach ($pool->getFutures() as $future) { 183 + $futures[] = $future; 184 + } 185 + 186 + if ($pool->getDaemons()) { 187 + $running_pools = true; 188 + } 189 + } 190 + 191 + $this->updateMemory(); 192 + 193 + $this->waitForDaemonFutures($futures); 194 + 195 + if (!$futures && !$running_pools) { 196 + if ($this->shouldShutdown()) { 197 + break; 198 + } 199 + } 200 + } 201 + 202 + exit($this->err); 203 + } 204 + 205 + 206 + private function waitForDaemonFutures(array $futures) { 207 + assert_instances_of($futures, 'ExecFuture'); 208 + 209 + if ($futures) { 210 + // TODO: This only wakes if any daemons actually exit. It would be a bit 211 + // cleaner to wait on any I/O with Channels. 212 + $iter = id(new FutureIterator($futures)) 213 + ->setUpdateInterval(1); 214 + foreach ($iter as $future) { 215 + break; 216 + } 217 + } else { 218 + if (!$this->shouldShutdown()) { 219 + sleep(1); 220 + } 221 + } 222 + } 223 + 224 + private function createDaemonPools() { 225 + $configs = $this->config['daemons']; 226 + 227 + $forced_options = array( 228 + 'load' => $this->libraries, 229 + 'log' => $this->log, 230 + ); 231 + 232 + foreach ($configs as $config) { 233 + $config = $forced_options + $config; 234 + 235 + $pool = PhutilDaemonPool::newFromConfig($config) 236 + ->setOverseer($this) 237 + ->setCommandLineArguments($this->argv); 238 + 239 + $this->pools[] = $pool; 240 + } 241 + } 242 + 243 + private function getDaemonPools() { 244 + return $this->pools; 245 + } 246 + 247 + private function updateMemory() { 248 + if (!$this->traceMemory) { 249 + return; 250 + } 251 + 252 + $this->logMessage( 253 + 'RAMS', 254 + pht( 255 + 'Overseer Memory Usage: %s KB', 256 + new PhutilNumber(memory_get_usage() / 1024, 1))); 257 + } 258 + 259 + public function logMessage($type, $message, $context = null) { 260 + $always_log = false; 261 + switch ($type) { 262 + case 'OVER': 263 + case 'SGNL': 264 + case 'PIDF': 265 + $always_log = true; 266 + break; 267 + } 268 + 269 + if ($always_log || $this->traceMode || $this->verbose) { 270 + error_log(date('Y-m-d g:i:s A').' ['.$type.'] '.$message); 271 + } 272 + } 273 + 274 + 275 + /* -( Signal Handling )---------------------------------------------------- */ 276 + 277 + 278 + /** 279 + * @task signals 280 + */ 281 + private function installSignalHandlers() { 282 + $signals = array( 283 + SIGUSR2, 284 + SIGHUP, 285 + SIGINT, 286 + SIGTERM, 287 + ); 288 + 289 + foreach ($signals as $signal) { 290 + pcntl_signal($signal, array($this, 'didReceiveSignal')); 291 + } 292 + } 293 + 294 + 295 + /** 296 + * @task signals 297 + */ 298 + public function didReceiveSignal($signo) { 299 + $this->logMessage( 300 + 'SGNL', 301 + pht( 302 + 'Overseer ("%d") received signal %d ("%s").', 303 + getmypid(), 304 + $signo, 305 + phutil_get_signal_name($signo))); 306 + 307 + switch ($signo) { 308 + case SIGUSR2: 309 + $signal_type = self::SIGNAL_NOTIFY; 310 + break; 311 + case SIGHUP: 312 + $signal_type = self::SIGNAL_RELOAD; 313 + break; 314 + case SIGINT: 315 + // If we receive SIGINT more than once, interpret it like SIGTERM. 316 + if ($this->inGracefulShutdown) { 317 + return $this->didReceiveSignal(SIGTERM); 318 + } 319 + 320 + $this->inGracefulShutdown = true; 321 + $signal_type = self::SIGNAL_GRACEFUL; 322 + break; 323 + case SIGTERM: 324 + // If we receive SIGTERM more than once, terminate abruptly. 325 + $this->err = 128 + $signo; 326 + if ($this->inAbruptShutdown) { 327 + exit($this->err); 328 + } 329 + 330 + $this->inAbruptShutdown = true; 331 + $signal_type = self::SIGNAL_TERMINATE; 332 + break; 333 + default: 334 + throw new Exception( 335 + pht( 336 + 'Signal handler called with unknown signal type ("%d")!', 337 + $signo)); 338 + } 339 + 340 + foreach ($this->getDaemonPools() as $pool) { 341 + $pool->didReceiveSignal($signal_type, $signo); 342 + } 343 + } 344 + 345 + 346 + /* -( Daemon Modules )----------------------------------------------------- */ 347 + 348 + 349 + private function getModules() { 350 + return $this->modules; 351 + } 352 + 353 + private function shouldReloadDaemons() { 354 + $modules = $this->getModules(); 355 + 356 + $should_reload = false; 357 + foreach ($modules as $module) { 358 + try { 359 + // NOTE: Even if one module tells us to reload, we call the method on 360 + // each module anyway to make calls a little more predictable. 361 + 362 + if ($module->shouldReloadDaemons()) { 363 + $this->logMessage( 364 + 'RELO', 365 + pht( 366 + 'Reloading daemons (triggered by overseer module "%s").', 367 + get_class($module))); 368 + $should_reload = true; 369 + } 370 + } catch (Exception $ex) { 371 + phlog($ex); 372 + } 373 + } 374 + 375 + return $should_reload; 376 + } 377 + 378 + private function shouldWakePool(PhutilDaemonPool $pool) { 379 + $modules = $this->getModules(); 380 + 381 + $should_wake = false; 382 + foreach ($modules as $module) { 383 + try { 384 + if ($module->shouldWakePool($pool)) { 385 + $this->logMessage( 386 + 'WAKE', 387 + pht( 388 + 'Waking pool "%s" (triggered by overseer module "%s").', 389 + $pool->getPoolLabel(), 390 + get_class($module))); 391 + $should_wake = true; 392 + } 393 + } catch (Exception $ex) { 394 + phlog($ex); 395 + } 396 + } 397 + 398 + return $should_wake; 399 + } 400 + 401 + private function shouldShutdown() { 402 + return $this->inGracefulShutdown || $this->inAbruptShutdown; 403 + } 404 + 405 + }
+71
src/infrastructure/daemon/PhutilDaemonOverseerModule.php
··· 1 + <?php 2 + 3 + /** 4 + * Overseer modules allow daemons to be externally influenced. 5 + * 6 + * See @{class:PhabricatorDaemonOverseerModule} for a concrete example. 7 + */ 8 + abstract class PhutilDaemonOverseerModule extends Phobject { 9 + 10 + private $throttles = array(); 11 + 12 + 13 + /** 14 + * This method is used to indicate to the overseer that daemons should reload. 15 + * 16 + * @return bool True if the daemons should reload, otherwise false. 17 + */ 18 + public function shouldReloadDaemons() { 19 + return false; 20 + } 21 + 22 + 23 + /** 24 + * Should a hibernating daemon pool be awoken immediately? 25 + * 26 + * @return bool True to awaken the pool immediately. 27 + */ 28 + public function shouldWakePool(PhutilDaemonPool $pool) { 29 + return false; 30 + } 31 + 32 + 33 + public static function getAllModules() { 34 + return id(new PhutilClassMapQuery()) 35 + ->setAncestorClass(__CLASS__) 36 + ->execute(); 37 + } 38 + 39 + 40 + /** 41 + * Throttle checks from executing too often. 42 + * 43 + * If you throttle a check like this, it will only execute once every 2.5 44 + * seconds: 45 + * 46 + * if ($this->shouldThrottle('some.check', 2.5)) { 47 + * return; 48 + * } 49 + * 50 + * @param string Throttle key. 51 + * @param float Duration in seconds. 52 + * @return bool True to throttle the check. 53 + */ 54 + protected function shouldThrottle($name, $duration) { 55 + $throttle = idx($this->throttles, $name, 0); 56 + $now = microtime(true); 57 + 58 + // If not enough time has elapsed, throttle the check. 59 + $elapsed = ($now - $throttle); 60 + if ($elapsed < $duration) { 61 + return true; 62 + } 63 + 64 + // Otherwise, mark the current time as the last time we ran the check, 65 + // then let it continue. 66 + $this->throttles[$name] = $now; 67 + 68 + return false; 69 + } 70 + 71 + }
+360
src/infrastructure/daemon/PhutilDaemonPool.php
··· 1 + <?php 2 + 3 + final class PhutilDaemonPool extends Phobject { 4 + 5 + private $properties = array(); 6 + private $commandLineArguments; 7 + 8 + private $overseer; 9 + private $daemons = array(); 10 + private $argv; 11 + 12 + private $lastAutoscaleUpdate; 13 + private $inShutdown; 14 + 15 + private function __construct() { 16 + // <empty> 17 + } 18 + 19 + public static function newFromConfig(array $config) { 20 + PhutilTypeSpec::checkMap( 21 + $config, 22 + array( 23 + 'class' => 'string', 24 + 'label' => 'string', 25 + 'argv' => 'optional list<string>', 26 + 'load' => 'optional list<string>', 27 + 'log' => 'optional string|null', 28 + 'pool' => 'optional int', 29 + 'up' => 'optional int', 30 + 'down' => 'optional int', 31 + 'reserve' => 'optional int|float', 32 + )); 33 + 34 + $config = $config + array( 35 + 'argv' => array(), 36 + 'load' => array(), 37 + 'log' => null, 38 + 'pool' => 1, 39 + 'up' => 2, 40 + 'down' => 15, 41 + 'reserve' => 0, 42 + ); 43 + 44 + $pool = new self(); 45 + $pool->properties = $config; 46 + 47 + return $pool; 48 + } 49 + 50 + public function setOverseer(PhutilDaemonOverseer $overseer) { 51 + $this->overseer = $overseer; 52 + return $this; 53 + } 54 + 55 + public function getOverseer() { 56 + return $this->overseer; 57 + } 58 + 59 + public function setCommandLineArguments(array $arguments) { 60 + $this->commandLineArguments = $arguments; 61 + return $this; 62 + } 63 + 64 + public function getCommandLineArguments() { 65 + return $this->commandLineArguments; 66 + } 67 + 68 + private function shouldShutdown() { 69 + return $this->inShutdown; 70 + } 71 + 72 + private function newDaemon() { 73 + $config = $this->properties; 74 + 75 + if (count($this->daemons)) { 76 + $down_duration = $this->getPoolScaledownDuration(); 77 + } else { 78 + // TODO: For now, never scale pools down to 0. 79 + $down_duration = 0; 80 + } 81 + 82 + $forced_config = array( 83 + 'down' => $down_duration, 84 + ); 85 + 86 + $config = $forced_config + $config; 87 + 88 + $config = array_select_keys( 89 + $config, 90 + array( 91 + 'class', 92 + 'log', 93 + 'load', 94 + 'argv', 95 + 'down', 96 + )); 97 + 98 + $daemon = PhutilDaemonHandle::newFromConfig($config) 99 + ->setDaemonPool($this) 100 + ->setCommandLineArguments($this->getCommandLineArguments()); 101 + 102 + $daemon_id = $daemon->getDaemonID(); 103 + $this->daemons[$daemon_id] = $daemon; 104 + 105 + $daemon->didLaunch(); 106 + 107 + return $daemon; 108 + } 109 + 110 + public function getDaemons() { 111 + return $this->daemons; 112 + } 113 + 114 + public function getFutures() { 115 + $futures = array(); 116 + foreach ($this->getDaemons() as $daemon) { 117 + $future = $daemon->getFuture(); 118 + if ($future) { 119 + $futures[] = $future; 120 + } 121 + } 122 + 123 + return $futures; 124 + } 125 + 126 + public function didReceiveSignal($signal, $signo) { 127 + switch ($signal) { 128 + case PhutilDaemonOverseer::SIGNAL_GRACEFUL: 129 + case PhutilDaemonOverseer::SIGNAL_TERMINATE: 130 + $this->inShutdown = true; 131 + break; 132 + } 133 + 134 + foreach ($this->getDaemons() as $daemon) { 135 + switch ($signal) { 136 + case PhutilDaemonOverseer::SIGNAL_NOTIFY: 137 + $daemon->didReceiveNotifySignal($signo); 138 + break; 139 + case PhutilDaemonOverseer::SIGNAL_RELOAD: 140 + $daemon->didReceiveReloadSignal($signo); 141 + break; 142 + case PhutilDaemonOverseer::SIGNAL_GRACEFUL: 143 + $daemon->didReceiveGracefulSignal($signo); 144 + break; 145 + case PhutilDaemonOverseer::SIGNAL_TERMINATE: 146 + $daemon->didReceiveTerminateSignal($signo); 147 + break; 148 + default: 149 + throw new Exception( 150 + pht( 151 + 'Unknown signal "%s" ("%d").', 152 + $signal, 153 + $signo)); 154 + } 155 + } 156 + } 157 + 158 + public function getPoolLabel() { 159 + return $this->getPoolProperty('label'); 160 + } 161 + 162 + public function getPoolMaximumSize() { 163 + return $this->getPoolProperty('pool'); 164 + } 165 + 166 + public function getPoolScaleupDuration() { 167 + return $this->getPoolProperty('up'); 168 + } 169 + 170 + public function getPoolScaledownDuration() { 171 + return $this->getPoolProperty('down'); 172 + } 173 + 174 + public function getPoolMemoryReserve() { 175 + return $this->getPoolProperty('reserve'); 176 + } 177 + 178 + public function getPoolDaemonClass() { 179 + return $this->getPoolProperty('class'); 180 + } 181 + 182 + private function getPoolProperty($key) { 183 + return idx($this->properties, $key); 184 + } 185 + 186 + public function updatePool() { 187 + $daemons = $this->getDaemons(); 188 + 189 + foreach ($daemons as $key => $daemon) { 190 + $daemon->update(); 191 + 192 + if ($daemon->isDone()) { 193 + $daemon->didExit(); 194 + 195 + unset($this->daemons[$key]); 196 + 197 + if ($this->shouldShutdown()) { 198 + $this->logMessage( 199 + 'DOWN', 200 + pht( 201 + 'Pool "%s" is exiting, with %s daemon(s) remaining.', 202 + $this->getPoolLabel(), 203 + new PhutilNumber(count($this->daemons)))); 204 + } else { 205 + $this->logMessage( 206 + 'POOL', 207 + pht( 208 + 'Autoscale pool "%s" scaled down to %s daemon(s).', 209 + $this->getPoolLabel(), 210 + new PhutilNumber(count($this->daemons)))); 211 + } 212 + } 213 + } 214 + 215 + $this->updateAutoscale(); 216 + } 217 + 218 + public function isHibernating() { 219 + foreach ($this->getDaemons() as $daemon) { 220 + if (!$daemon->isHibernating()) { 221 + return false; 222 + } 223 + } 224 + 225 + return true; 226 + } 227 + 228 + public function wakeFromHibernation() { 229 + if (!$this->isHibernating()) { 230 + return $this; 231 + } 232 + 233 + $this->logMessage( 234 + 'WAKE', 235 + pht( 236 + 'Autoscale pool "%s" is being awakened from hibernation.', 237 + $this->getPoolLabel())); 238 + 239 + $did_wake_daemons = false; 240 + foreach ($this->getDaemons() as $daemon) { 241 + if ($daemon->isHibernating()) { 242 + $daemon->wakeFromHibernation(); 243 + $did_wake_daemons = true; 244 + } 245 + } 246 + 247 + if (!$did_wake_daemons) { 248 + // TODO: Pools currently can't scale down to 0 daemons, but we should 249 + // scale up immediately here once they can. 250 + } 251 + 252 + $this->updatePool(); 253 + 254 + return $this; 255 + } 256 + 257 + private function updateAutoscale() { 258 + if ($this->shouldShutdown()) { 259 + return; 260 + } 261 + 262 + // Don't try to autoscale more than once per second. This mostly stops the 263 + // logs from getting flooded in verbose mode. 264 + $now = time(); 265 + if ($this->lastAutoscaleUpdate >= $now) { 266 + return; 267 + } 268 + $this->lastAutoscaleUpdate = $now; 269 + 270 + $daemons = $this->getDaemons(); 271 + 272 + // If this pool is already at the maximum size, we can't launch any new 273 + // daemons. 274 + $max_size = $this->getPoolMaximumSize(); 275 + if (count($daemons) >= $max_size) { 276 + $this->logMessage( 277 + 'POOL', 278 + pht( 279 + 'Autoscale pool "%s" already at maximum size (%s of %s).', 280 + $this->getPoolLabel(), 281 + new PhutilNumber(count($daemons)), 282 + new PhutilNumber($max_size))); 283 + return; 284 + } 285 + 286 + $scaleup_duration = $this->getPoolScaleupDuration(); 287 + 288 + foreach ($daemons as $daemon) { 289 + $busy_epoch = $daemon->getBusyEpoch(); 290 + // If any daemons haven't started work yet, don't scale the pool up. 291 + if (!$busy_epoch) { 292 + $this->logMessage( 293 + 'POOL', 294 + pht( 295 + 'Autoscale pool "%s" has an idle daemon, declining to scale.', 296 + $this->getPoolLabel())); 297 + return; 298 + } 299 + 300 + // If any daemons started work very recently, wait a little while 301 + // to scale the pool up. 302 + $busy_for = ($now - $busy_epoch); 303 + if ($busy_for < $scaleup_duration) { 304 + $this->logMessage( 305 + 'POOL', 306 + pht( 307 + 'Autoscale pool "%s" has not been busy long enough to scale up '. 308 + '(busy for %s of %s seconds).', 309 + $this->getPoolLabel(), 310 + new PhutilNumber($busy_for), 311 + new PhutilNumber($scaleup_duration))); 312 + return; 313 + } 314 + } 315 + 316 + // If we have a configured memory reserve for this pool, it tells us that 317 + // we should not scale up unless there's at least that much memory left 318 + // on the system (for example, a reserve of 0.25 means that 25% of system 319 + // memory must be free to autoscale). 320 + 321 + // Note that the first daemon is exempt: we'll always launch at least one 322 + // daemon, regardless of any memory reservation. 323 + if (count($daemons)) { 324 + $reserve = $this->getPoolMemoryReserve(); 325 + if ($reserve) { 326 + // On some systems this may be slightly more expensive than other 327 + // checks, so we only do it once we're prepared to scale up. 328 + $memory = PhutilSystem::getSystemMemoryInformation(); 329 + $free_ratio = ($memory['free'] / $memory['total']); 330 + 331 + // If we don't have enough free memory, don't scale. 332 + if ($free_ratio <= $reserve) { 333 + $this->logMessage( 334 + 'POOL', 335 + pht( 336 + 'Autoscale pool "%s" does not have enough free memory to '. 337 + 'scale up (%s free of %s reserved).', 338 + $this->getPoolLabel(), 339 + new PhutilNumber($free_ratio, 3), 340 + new PhutilNumber($reserve, 3))); 341 + return; 342 + } 343 + } 344 + } 345 + 346 + $this->logMessage( 347 + 'AUTO', 348 + pht( 349 + 'Scaling pool "%s" up to %s daemon(s).', 350 + $this->getPoolLabel(), 351 + new PhutilNumber(count($daemons) + 1))); 352 + 353 + $this->newDaemon(); 354 + } 355 + 356 + public function logMessage($type, $message, $context = null) { 357 + return $this->getOverseer()->logMessage($type, $message, $context); 358 + } 359 + 360 + }
-1
src/view/widget/AphrontStackTraceView.php
··· 19 19 20 20 $callsigns = array( 21 21 'arcanist' => 'ARC', 22 - 'phutil' => 'PHU', 23 22 'phabricator' => 'P', 24 23 ); 25 24
+4 -7
support/startup/PhabricatorStartup.php
··· 205 205 'include_path', 206 206 $libraries_root.PATH_SEPARATOR.ini_get('include_path')); 207 207 208 - @include_once $root.'libphutil/src/__phutil_library_init__.php'; 209 - if (!@constant('__LIBPHUTIL__')) { 208 + $ok = @include_once $root.'arcanist/src/init/init-library.php'; 209 + if (!$ok) { 210 210 self::didFatal( 211 - "Unable to load libphutil. Put libphutil/ next to phabricator/, or ". 212 - "update your PHP 'include_path' to include the parent directory of ". 213 - "libphutil/."); 211 + 'Unable to load the "Arcanist" library. Put "arcanist/" next to '. 212 + '"phabricator/" on disk.'); 214 213 } 215 - 216 - phutil_load_library('arcanist/src'); 217 214 218 215 // Load Phabricator itself using the absolute path, so we never end up doing 219 216 // anything surprising (loading index.php and libraries from different