├── .gitignore ├── .travis.yml ├── LICENSE ├── LICENSE-MPL-RabbitMQ ├── Makefile ├── README.md ├── rebar.config ├── rebar.lock └── src ├── supervisor3.app.src └── supervisor3.erl /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .sw? 3 | .*.sw? 4 | *.beam 5 | *.d 6 | /.erlang.mk/ 7 | /doc/ 8 | /ebin/ 9 | .rebar/ 10 | _build 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: erlang 2 | 3 | sudo: required 4 | 5 | notifications: 6 | email: false 7 | 8 | otp_release: 9 | - 22.0 10 | - 21.3 11 | 12 | script: 13 | - make 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This package, the RabbitMQ Management Plugin is licensed under the MPL. For the 2 | MPL, please see LICENSE-MPL-RabbitMQ. 3 | 4 | This package makes use of the following third party libraries: 5 | jQuery - http://jquery.com/ - MIT license, see LICENSE-MIT-jQuery164 6 | EJS - http://embeddedjs.com/ - MIT license, see LICENSE-MIT-EJS10 7 | Sammy - http://code.quirkey.com/sammy/ - MIT license, see LICENSE-MIT-Sammy060 8 | Cowboy - http://ninenines.eu/ - ISC license 9 | base64.js - http://code.google.com/p/stringencoders/ - BSD license, see LICENSE-BSD-base64js 10 | If you have any questions regarding licensing, please contact us at 11 | info@rabbitmq.com. 12 | -------------------------------------------------------------------------------- /LICENSE-MPL-RabbitMQ: -------------------------------------------------------------------------------- 1 | MOZILLA PUBLIC LICENSE 2 | Version 1.1 3 | 4 | --------------- 5 | 6 | 1. Definitions. 7 | 8 | 1.0.1. "Commercial Use" means distribution or otherwise making the 9 | Covered Code available to a third party. 10 | 11 | 1.1. "Contributor" means each entity that creates or contributes to 12 | the creation of Modifications. 13 | 14 | 1.2. "Contributor Version" means the combination of the Original 15 | Code, prior Modifications used by a Contributor, and the Modifications 16 | made by that particular Contributor. 17 | 18 | 1.3. "Covered Code" means the Original Code or Modifications or the 19 | combination of the Original Code and Modifications, in each case 20 | including portions thereof. 21 | 22 | 1.4. "Electronic Distribution Mechanism" means a mechanism generally 23 | accepted in the software development community for the electronic 24 | transfer of data. 25 | 26 | 1.5. "Executable" means Covered Code in any form other than Source 27 | Code. 28 | 29 | 1.6. "Initial Developer" means the individual or entity identified 30 | as the Initial Developer in the Source Code notice required by Exhibit 31 | A. 32 | 33 | 1.7. "Larger Work" means a work which combines Covered Code or 34 | portions thereof with code not governed by the terms of this License. 35 | 36 | 1.8. "License" means this document. 37 | 38 | 1.8.1. "Licensable" means having the right to grant, to the maximum 39 | extent possible, whether at the time of the initial grant or 40 | subsequently acquired, any and all of the rights conveyed herein. 41 | 42 | 1.9. "Modifications" means any addition to or deletion from the 43 | substance or structure of either the Original Code or any previous 44 | Modifications. When Covered Code is released as a series of files, a 45 | Modification is: 46 | A. Any addition to or deletion from the contents of a file 47 | containing Original Code or previous Modifications. 48 | 49 | B. Any new file that contains any part of the Original Code or 50 | previous Modifications. 51 | 52 | 1.10. "Original Code" means Source Code of computer software code 53 | which is described in the Source Code notice required by Exhibit A as 54 | Original Code, and which, at the time of its release under this 55 | License is not already Covered Code governed by this License. 56 | 57 | 1.10.1. "Patent Claims" means any patent claim(s), now owned or 58 | hereafter acquired, including without limitation, method, process, 59 | and apparatus claims, in any patent Licensable by grantor. 60 | 61 | 1.11. "Source Code" means the preferred form of the Covered Code for 62 | making modifications to it, including all modules it contains, plus 63 | any associated interface definition files, scripts used to control 64 | compilation and installation of an Executable, or source code 65 | differential comparisons against either the Original Code or another 66 | well known, available Covered Code of the Contributor's choice. The 67 | Source Code can be in a compressed or archival form, provided the 68 | appropriate decompression or de-archiving software is widely available 69 | for no charge. 70 | 71 | 1.12. "You" (or "Your") means an individual or a legal entity 72 | exercising rights under, and complying with all of the terms of, this 73 | License or a future version of this License issued under Section 6.1. 74 | For legal entities, "You" includes any entity which controls, is 75 | controlled by, or is under common control with You. For purposes of 76 | this definition, "control" means (a) the power, direct or indirect, 77 | to cause the direction or management of such entity, whether by 78 | contract or otherwise, or (b) ownership of more than fifty percent 79 | (50%) of the outstanding shares or beneficial ownership of such 80 | entity. 81 | 82 | 2. Source Code License. 83 | 84 | 2.1. The Initial Developer Grant. 85 | The Initial Developer hereby grants You a world-wide, royalty-free, 86 | non-exclusive license, subject to third party intellectual property 87 | claims: 88 | (a) under intellectual property rights (other than patent or 89 | trademark) Licensable by Initial Developer to use, reproduce, 90 | modify, display, perform, sublicense and distribute the Original 91 | Code (or portions thereof) with or without Modifications, and/or 92 | as part of a Larger Work; and 93 | 94 | (b) under Patents Claims infringed by the making, using or 95 | selling of Original Code, to make, have made, use, practice, 96 | sell, and offer for sale, and/or otherwise dispose of the 97 | Original Code (or portions thereof). 98 | 99 | (c) the licenses granted in this Section 2.1(a) and (b) are 100 | effective on the date Initial Developer first distributes 101 | Original Code under the terms of this License. 102 | 103 | (d) Notwithstanding Section 2.1(b) above, no patent license is 104 | granted: 1) for code that You delete from the Original Code; 2) 105 | separate from the Original Code; or 3) for infringements caused 106 | by: i) the modification of the Original Code or ii) the 107 | combination of the Original Code with other software or devices. 108 | 109 | 2.2. Contributor Grant. 110 | Subject to third party intellectual property claims, each Contributor 111 | hereby grants You a world-wide, royalty-free, non-exclusive license 112 | 113 | (a) under intellectual property rights (other than patent or 114 | trademark) Licensable by Contributor, to use, reproduce, modify, 115 | display, perform, sublicense and distribute the Modifications 116 | created by such Contributor (or portions thereof) either on an 117 | unmodified basis, with other Modifications, as Covered Code 118 | and/or as part of a Larger Work; and 119 | 120 | (b) under Patent Claims infringed by the making, using, or 121 | selling of Modifications made by that Contributor either alone 122 | and/or in combination with its Contributor Version (or portions 123 | of such combination), to make, use, sell, offer for sale, have 124 | made, and/or otherwise dispose of: 1) Modifications made by that 125 | Contributor (or portions thereof); and 2) the combination of 126 | Modifications made by that Contributor with its Contributor 127 | Version (or portions of such combination). 128 | 129 | (c) the licenses granted in Sections 2.2(a) and 2.2(b) are 130 | effective on the date Contributor first makes Commercial Use of 131 | the Covered Code. 132 | 133 | (d) Notwithstanding Section 2.2(b) above, no patent license is 134 | granted: 1) for any code that Contributor has deleted from the 135 | Contributor Version; 2) separate from the Contributor Version; 136 | 3) for infringements caused by: i) third party modifications of 137 | Contributor Version or ii) the combination of Modifications made 138 | by that Contributor with other software (except as part of the 139 | Contributor Version) or other devices; or 4) under Patent Claims 140 | infringed by Covered Code in the absence of Modifications made by 141 | that Contributor. 142 | 143 | 3. Distribution Obligations. 144 | 145 | 3.1. Application of License. 146 | The Modifications which You create or to which You contribute are 147 | governed by the terms of this License, including without limitation 148 | Section 2.2. The Source Code version of Covered Code may be 149 | distributed only under the terms of this License or a future version 150 | of this License released under Section 6.1, and You must include a 151 | copy of this License with every copy of the Source Code You 152 | distribute. You may not offer or impose any terms on any Source Code 153 | version that alters or restricts the applicable version of this 154 | License or the recipients' rights hereunder. However, You may include 155 | an additional document offering the additional rights described in 156 | Section 3.5. 157 | 158 | 3.2. Availability of Source Code. 159 | Any Modification which You create or to which You contribute must be 160 | made available in Source Code form under the terms of this License 161 | either on the same media as an Executable version or via an accepted 162 | Electronic Distribution Mechanism to anyone to whom you made an 163 | Executable version available; and if made available via Electronic 164 | Distribution Mechanism, must remain available for at least twelve (12) 165 | months after the date it initially became available, or at least six 166 | (6) months after a subsequent version of that particular Modification 167 | has been made available to such recipients. You are responsible for 168 | ensuring that the Source Code version remains available even if the 169 | Electronic Distribution Mechanism is maintained by a third party. 170 | 171 | 3.3. Description of Modifications. 172 | You must cause all Covered Code to which You contribute to contain a 173 | file documenting the changes You made to create that Covered Code and 174 | the date of any change. You must include a prominent statement that 175 | the Modification is derived, directly or indirectly, from Original 176 | Code provided by the Initial Developer and including the name of the 177 | Initial Developer in (a) the Source Code, and (b) in any notice in an 178 | Executable version or related documentation in which You describe the 179 | origin or ownership of the Covered Code. 180 | 181 | 3.4. Intellectual Property Matters 182 | (a) Third Party Claims. 183 | If Contributor has knowledge that a license under a third party's 184 | intellectual property rights is required to exercise the rights 185 | granted by such Contributor under Sections 2.1 or 2.2, 186 | Contributor must include a text file with the Source Code 187 | distribution titled "LEGAL" which describes the claim and the 188 | party making the claim in sufficient detail that a recipient will 189 | know whom to contact. If Contributor obtains such knowledge after 190 | the Modification is made available as described in Section 3.2, 191 | Contributor shall promptly modify the LEGAL file in all copies 192 | Contributor makes available thereafter and shall take other steps 193 | (such as notifying appropriate mailing lists or newsgroups) 194 | reasonably calculated to inform those who received the Covered 195 | Code that new knowledge has been obtained. 196 | 197 | (b) Contributor APIs. 198 | If Contributor's Modifications include an application programming 199 | interface and Contributor has knowledge of patent licenses which 200 | are reasonably necessary to implement that API, Contributor must 201 | also include this information in the LEGAL file. 202 | 203 | (c) Representations. 204 | Contributor represents that, except as disclosed pursuant to 205 | Section 3.4(a) above, Contributor believes that Contributor's 206 | Modifications are Contributor's original creation(s) and/or 207 | Contributor has sufficient rights to grant the rights conveyed by 208 | this License. 209 | 210 | 3.5. Required Notices. 211 | You must duplicate the notice in Exhibit A in each file of the Source 212 | Code. If it is not possible to put such notice in a particular Source 213 | Code file due to its structure, then You must include such notice in a 214 | location (such as a relevant directory) where a user would be likely 215 | to look for such a notice. If You created one or more Modification(s) 216 | You may add your name as a Contributor to the notice described in 217 | Exhibit A. You must also duplicate this License in any documentation 218 | for the Source Code where You describe recipients' rights or ownership 219 | rights relating to Covered Code. You may choose to offer, and to 220 | charge a fee for, warranty, support, indemnity or liability 221 | obligations to one or more recipients of Covered Code. However, You 222 | may do so only on Your own behalf, and not on behalf of the Initial 223 | Developer or any Contributor. You must make it absolutely clear than 224 | any such warranty, support, indemnity or liability obligation is 225 | offered by You alone, and You hereby agree to indemnify the Initial 226 | Developer and every Contributor for any liability incurred by the 227 | Initial Developer or such Contributor as a result of warranty, 228 | support, indemnity or liability terms You offer. 229 | 230 | 3.6. Distribution of Executable Versions. 231 | You may distribute Covered Code in Executable form only if the 232 | requirements of Section 3.1-3.5 have been met for that Covered Code, 233 | and if You include a notice stating that the Source Code version of 234 | the Covered Code is available under the terms of this License, 235 | including a description of how and where You have fulfilled the 236 | obligations of Section 3.2. The notice must be conspicuously included 237 | in any notice in an Executable version, related documentation or 238 | collateral in which You describe recipients' rights relating to the 239 | Covered Code. You may distribute the Executable version of Covered 240 | Code or ownership rights under a license of Your choice, which may 241 | contain terms different from this License, provided that You are in 242 | compliance with the terms of this License and that the license for the 243 | Executable version does not attempt to limit or alter the recipient's 244 | rights in the Source Code version from the rights set forth in this 245 | License. If You distribute the Executable version under a different 246 | license You must make it absolutely clear that any terms which differ 247 | from this License are offered by You alone, not by the Initial 248 | Developer or any Contributor. You hereby agree to indemnify the 249 | Initial Developer and every Contributor for any liability incurred by 250 | the Initial Developer or such Contributor as a result of any such 251 | terms You offer. 252 | 253 | 3.7. Larger Works. 254 | You may create a Larger Work by combining Covered Code with other code 255 | not governed by the terms of this License and distribute the Larger 256 | Work as a single product. In such a case, You must make sure the 257 | requirements of this License are fulfilled for the Covered Code. 258 | 259 | 4. Inability to Comply Due to Statute or Regulation. 260 | 261 | If it is impossible for You to comply with any of the terms of this 262 | License with respect to some or all of the Covered Code due to 263 | statute, judicial order, or regulation then You must: (a) comply with 264 | the terms of this License to the maximum extent possible; and (b) 265 | describe the limitations and the code they affect. Such description 266 | must be included in the LEGAL file described in Section 3.4 and must 267 | be included with all distributions of the Source Code. Except to the 268 | extent prohibited by statute or regulation, such description must be 269 | sufficiently detailed for a recipient of ordinary skill to be able to 270 | understand it. 271 | 272 | 5. Application of this License. 273 | 274 | This License applies to code to which the Initial Developer has 275 | attached the notice in Exhibit A and to related Covered Code. 276 | 277 | 6. Versions of the License. 278 | 279 | 6.1. New Versions. 280 | Netscape Communications Corporation ("Netscape") may publish revised 281 | and/or new versions of the License from time to time. Each version 282 | will be given a distinguishing version number. 283 | 284 | 6.2. Effect of New Versions. 285 | Once Covered Code has been published under a particular version of the 286 | License, You may always continue to use it under the terms of that 287 | version. You may also choose to use such Covered Code under the terms 288 | of any subsequent version of the License published by Netscape. No one 289 | other than Netscape has the right to modify the terms applicable to 290 | Covered Code created under this License. 291 | 292 | 6.3. Derivative Works. 293 | If You create or use a modified version of this License (which you may 294 | only do in order to apply it to code which is not already Covered Code 295 | governed by this License), You must (a) rename Your license so that 296 | the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", 297 | "MPL", "NPL" or any confusingly similar phrase do not appear in your 298 | license (except to note that your license differs from this License) 299 | and (b) otherwise make it clear that Your version of the license 300 | contains terms which differ from the Mozilla Public License and 301 | Netscape Public License. (Filling in the name of the Initial 302 | Developer, Original Code or Contributor in the notice described in 303 | Exhibit A shall not of themselves be deemed to be modifications of 304 | this License.) 305 | 306 | 7. DISCLAIMER OF WARRANTY. 307 | 308 | COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, 309 | WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 310 | WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF 311 | DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. 312 | THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE 313 | IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, 314 | YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE 315 | COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER 316 | OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF 317 | ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. 318 | 319 | 8. TERMINATION. 320 | 321 | 8.1. This License and the rights granted hereunder will terminate 322 | automatically if You fail to comply with terms herein and fail to cure 323 | such breach within 30 days of becoming aware of the breach. All 324 | sublicenses to the Covered Code which are properly granted shall 325 | survive any termination of this License. Provisions which, by their 326 | nature, must remain in effect beyond the termination of this License 327 | shall survive. 328 | 329 | 8.2. If You initiate litigation by asserting a patent infringement 330 | claim (excluding declatory judgment actions) against Initial Developer 331 | or a Contributor (the Initial Developer or Contributor against whom 332 | You file such action is referred to as "Participant") alleging that: 333 | 334 | (a) such Participant's Contributor Version directly or indirectly 335 | infringes any patent, then any and all rights granted by such 336 | Participant to You under Sections 2.1 and/or 2.2 of this License 337 | shall, upon 60 days notice from Participant terminate prospectively, 338 | unless if within 60 days after receipt of notice You either: (i) 339 | agree in writing to pay Participant a mutually agreeable reasonable 340 | royalty for Your past and future use of Modifications made by such 341 | Participant, or (ii) withdraw Your litigation claim with respect to 342 | the Contributor Version against such Participant. If within 60 days 343 | of notice, a reasonable royalty and payment arrangement are not 344 | mutually agreed upon in writing by the parties or the litigation claim 345 | is not withdrawn, the rights granted by Participant to You under 346 | Sections 2.1 and/or 2.2 automatically terminate at the expiration of 347 | the 60 day notice period specified above. 348 | 349 | (b) any software, hardware, or device, other than such Participant's 350 | Contributor Version, directly or indirectly infringes any patent, then 351 | any rights granted to You by such Participant under Sections 2.1(b) 352 | and 2.2(b) are revoked effective as of the date You first made, used, 353 | sold, distributed, or had made, Modifications made by that 354 | Participant. 355 | 356 | 8.3. If You assert a patent infringement claim against Participant 357 | alleging that such Participant's Contributor Version directly or 358 | indirectly infringes any patent where such claim is resolved (such as 359 | by license or settlement) prior to the initiation of patent 360 | infringement litigation, then the reasonable value of the licenses 361 | granted by such Participant under Sections 2.1 or 2.2 shall be taken 362 | into account in determining the amount or value of any payment or 363 | license. 364 | 365 | 8.4. In the event of termination under Sections 8.1 or 8.2 above, 366 | all end user license agreements (excluding distributors and resellers) 367 | which have been validly granted by You or any distributor hereunder 368 | prior to termination shall survive termination. 369 | 370 | 9. LIMITATION OF LIABILITY. 371 | 372 | UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT 373 | (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL 374 | DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, 375 | OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR 376 | ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY 377 | CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, 378 | WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER 379 | COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN 380 | INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF 381 | LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY 382 | RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW 383 | PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE 384 | EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO 385 | THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. 386 | 387 | 10. U.S. GOVERNMENT END USERS. 388 | 389 | The Covered Code is a "commercial item," as that term is defined in 390 | 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer 391 | software" and "commercial computer software documentation," as such 392 | terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 393 | C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), 394 | all U.S. Government End Users acquire Covered Code with only those 395 | rights set forth herein. 396 | 397 | 11. MISCELLANEOUS. 398 | 399 | This License represents the complete agreement concerning subject 400 | matter hereof. If any provision of this License is held to be 401 | unenforceable, such provision shall be reformed only to the extent 402 | necessary to make it enforceable. This License shall be governed by 403 | California law provisions (except to the extent applicable law, if 404 | any, provides otherwise), excluding its conflict-of-law provisions. 405 | With respect to disputes in which at least one party is a citizen of, 406 | or an entity chartered or registered to do business in the United 407 | States of America, any litigation relating to this License shall be 408 | subject to the jurisdiction of the Federal Courts of the Northern 409 | District of California, with venue lying in Santa Clara County, 410 | California, with the losing party responsible for costs, including 411 | without limitation, court costs and reasonable attorneys' fees and 412 | expenses. The application of the United Nations Convention on 413 | Contracts for the International Sale of Goods is expressly excluded. 414 | Any law or regulation which provides that the language of a contract 415 | shall be construed against the drafter shall not apply to this 416 | License. 417 | 418 | 12. RESPONSIBILITY FOR CLAIMS. 419 | 420 | As between Initial Developer and the Contributors, each party is 421 | responsible for claims and damages arising, directly or indirectly, 422 | out of its utilization of rights under this License and You agree to 423 | work with Initial Developer and Contributors to distribute such 424 | responsibility on an equitable basis. Nothing herein is intended or 425 | shall be deemed to constitute any admission of liability. 426 | 427 | 13. MULTIPLE-LICENSED CODE. 428 | 429 | Initial Developer may designate portions of the Covered Code as 430 | "Multiple-Licensed". "Multiple-Licensed" means that the Initial 431 | Developer permits you to utilize portions of the Covered Code under 432 | Your choice of the NPL or the alternative licenses, if any, specified 433 | by the Initial Developer in the file described in Exhibit A. 434 | 435 | EXHIBIT A -Mozilla Public License. 436 | 437 | ``The contents of this file are subject to the Mozilla Public License 438 | Version 1.1 (the "License"); you may not use this file except in 439 | compliance with the License. You may obtain a copy of the License at 440 | http://www.mozilla.org/MPL/ 441 | 442 | Software distributed under the License is distributed on an "AS IS" 443 | basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the 444 | License for the specific language governing rights and limitations 445 | under the License. 446 | 447 | The Original Code is RabbitMQ Management Plugin. 448 | 449 | The Initial Developer of the Original Code is GoPivotal, Inc. 450 | Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.'' 451 | 452 | [NOTE: The text of this Exhibit A may differ slightly from the text of 453 | the notices in the Source Code files of the Original Code. You should 454 | use the text of this Exhibit A rather than the text found in the 455 | Original Code Source Code for Your Modifications.] 456 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: 3 | rebar3 do compile,dialyzer,eunit 4 | 5 | .PHONY: hex-publish 6 | hex-publish: distclean 7 | $(verbose) rebar3 hex publish 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Extended Erlang/OTP supervisor 2 | This file is a copy of supervisor.erl from the R16B Erlang/OTP 3 | distribution, with the following modifications: 4 | 5 | 1. The module name is supervisor2. 6 | 7 | 2. A find_child/2 utility function has been added. 8 | 9 | 3. Added an 'intrinsic' restart type. Like the transient type, this 10 | type means the child should only be restarted if the child exits 11 | abnormally. Unlike the transient type, if the child exits 12 | normally, the supervisor itself also exits normally. If the 13 | child is a supervisor and it exits normally (i.e. with reason of 14 | 'shutdown') then the child's parent also exits normally. 15 | 16 | 4. Child specifications can contain, as the restart type, a tuple 17 | {permanent, Delay} | {transient, Delay} | {intrinsic, Delay} 18 | where Delay >= 0 (see point (4) below for intrinsic). The delay, 19 | in seconds, indicates what should happen if a child, upon being 20 | restarted, exceeds the MaxT and MaxR parameters. Thus, if a 21 | child exits, it is restarted as normal. If it exits sufficiently 22 | quickly and often to exceed the boundaries set by the MaxT and 23 | MaxR parameters, and a Delay is specified, then rather than 24 | stopping the supervisor, the supervisor instead continues and 25 | tries to start up the child again, Delay seconds later. 26 | Note that if a child is delay-restarted this will reset the 27 | count of restarts towrds MaxR and MaxT. This matters if MaxT > 28 | Delay, since otherwise we would fail to restart after the delay. 29 | Sometimes, you may wish for a transient or intrinsic child to 30 | exit abnormally so that it gets restarted, but still log 31 | nothing. gen_server will log any exit reason other than 32 | 'normal', 'shutdown' or {'shutdown', _}. Thus the exit reason of 33 | {'shutdown', 'restart'} is interpreted to mean you wish the 34 | child to be restarted according to the delay parameters, but 35 | gen_server will not log the error. Thus from gen_server's 36 | perspective it's a normal exit, whilst from supervisor's 37 | perspective, it's an abnormal exit. 38 | 39 | 5. Normal, and {shutdown, _} exit reasons are all treated the same (i.e. are regarded as normal exits). 40 | 41 | 6. Rename the module to supervisor3. 42 | 43 | 7. Introduce post_init callback. 44 | If Callback:init/1 returns 'post_init', Callback:post_init/1 is called to perform the genuine initialization work. 45 | This is to avoid crashes in Callback:init/1 which may prevent the parent supervisor from restarting it. 46 | 47 | 8. Call os:timestamp/0 and timer:now_diff/2 for timestamps. 48 | 49 | 9. Ignore delayed retry in MaxR accumulation. 50 | 51 | 10. Use specs by default 52 | 53 | Modifications 1-5 are (C) 2010-2013 GoPivotal, Inc. 54 | 55 | Modifications 6-10 are (C) 2015-2016 Klarna AB 56 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [ error 2 | , warn_unused_vars 3 | , warn_shadow_vars 4 | , warn_unused_import 5 | , warn_obsolete_guard 6 | , debug_info 7 | ]}. 8 | 9 | -------------------------------------------------------------------------------- /rebar.lock: -------------------------------------------------------------------------------- 1 | []. 2 | -------------------------------------------------------------------------------- /src/supervisor3.app.src: -------------------------------------------------------------------------------- 1 | %% -*- mode:erlang -*- 2 | {application,supervisor3, 3 | [{description,"A copy of supervisor.erl from the R16B Erlang/OTP with modifications"}, 4 | {vsn,"git"}, 5 | {registered,[]}, 6 | {applications,[kernel,stdlib]}, 7 | {env,[]}, 8 | {modules,[]}, 9 | {licenses, ["MPL"]}, 10 | {links, [{"Github", "https://github.com/klarna/supervisor3"}]}, 11 | {build_tools, ["rebar3"]}, 12 | {files, ["src", "README.md", "Makefile", "erlang.mk", "LICENSE", "LICENSE-MPL-RabbitMQ", 13 | "rebar.config"]}]}. 14 | -------------------------------------------------------------------------------- /src/supervisor3.erl: -------------------------------------------------------------------------------- 1 | %% This file is a copy of supervisor.erl from the R16B Erlang/OTP 2 | %% distribution, with the following modifications: 3 | %% 4 | %% 1) the module name is supervisor2 5 | %% 6 | %% 2) a find_child/2 utility function has been added 7 | %% 8 | %% 3) Added an 'intrinsic' restart type. Like the transient type, this 9 | %% type means the child should only be restarted if the child exits 10 | %% abnormally. Unlike the transient type, if the child exits 11 | %% normally, the supervisor itself also exits normally. If the 12 | %% child is a supervisor and it exits normally (i.e. with reason of 13 | %% 'shutdown') then the child's parent also exits normally. 14 | %% 15 | %% 4) child specifications can contain, as the restart type, a tuple 16 | %% {permanent, Delay} | {transient, Delay} | {intrinsic, Delay} 17 | %% where Delay >= 0 (see point (4) below for intrinsic). The delay, 18 | %% in seconds, indicates what should happen if a child, upon being 19 | %% restarted, exceeds the MaxT and MaxR parameters. Thus, if a 20 | %% child exits, it is restarted as normal. If it exits sufficiently 21 | %% quickly and often to exceed the boundaries set by the MaxT and 22 | %% MaxR parameters, and a Delay is specified, then rather than 23 | %% stopping the supervisor, the supervisor instead continues and 24 | %% tries to start up the child again, Delay seconds later. 25 | %% 26 | %% Note that if a child is delay-restarted this will reset the 27 | %% count of restarts towrds MaxR and MaxT. This matters if MaxT > 28 | %% Delay, since otherwise we would fail to restart after the delay. 29 | %% 30 | %% Sometimes, you may wish for a transient or intrinsic child to 31 | %% exit abnormally so that it gets restarted, but still log 32 | %% nothing. gen_server will log any exit reason other than 33 | %% 'normal', 'shutdown' or {'shutdown', _}. Thus the exit reason of 34 | %% {'shutdown', 'restart'} is interpreted to mean you wish the 35 | %% child to be restarted according to the delay parameters, but 36 | %% gen_server will not log the error. Thus from gen_server's 37 | %% perspective it's a normal exit, whilst from supervisor's 38 | %% perspective, it's an abnormal exit. 39 | %% 40 | %% 5) normal, and {shutdown, _} exit reasons are all treated the same 41 | %% (i.e. are regarded as normal exits) 42 | %% 43 | %% 6) rename the module to supervisor3 44 | %% 45 | %% 7) introduce post_init callback 46 | %% 47 | %% 8) call os:timestamp/0 and timer:now_diff/2 for timestamps 48 | %% 49 | %% 9) ignore delayed retry in MaxR accumulation 50 | %% 51 | %% Modifications 1-5 are (C) 2010-2013 GoPivotal, Inc. 52 | %% Modifications 6-9 are (C) 2015 Klarna AB 53 | %% 54 | %% %CopyrightBegin% 55 | %% 56 | %% Copyright Ericsson AB 1996-2012. All Rights Reserved. 57 | %% 58 | %% The contents of this file are subject to the Erlang Public License, 59 | %% Version 1.1, (the "License"); you may not use this file except in 60 | %% compliance with the License. You should have received a copy of the 61 | %% Erlang Public License along with this software. If not, it can be 62 | %% retrieved online at http://www.erlang.org/. 63 | %% 64 | %% Software distributed under the License is distributed on an "AS IS" 65 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 66 | %% the License for the specific language governing rights and limitations 67 | %% under the License. 68 | %% 69 | %% %CopyrightEnd% 70 | %% 71 | -module(supervisor3). 72 | 73 | -behaviour(gen_server). 74 | 75 | %% External exports 76 | -export([start_link/2, start_link/3, 77 | start_child/2, restart_child/2, 78 | delete_child/2, terminate_child/2, 79 | which_children/1, count_children/1, 80 | find_child/2, check_childspecs/1]). 81 | 82 | %% Internal exports 83 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 84 | terminate/2, code_change/3]). 85 | -export([try_again_restart/3]). 86 | 87 | %%-------------------------------------------------------------------------- 88 | -export_type([child_spec/0, startchild_ret/0, strategy/0, sup_name/0]). 89 | %%-------------------------------------------------------------------------- 90 | 91 | -type child() :: 'undefined' | pid(). 92 | -type child_id() :: term(). 93 | -type mfargs() :: {M :: module(), F :: atom(), A :: [term()] | undefined}. 94 | -type modules() :: [module()] | 'dynamic'. 95 | -type delay() :: non_neg_integer(). 96 | -type restart() :: 'permanent' 97 | | 'transient' 98 | | 'temporary' 99 | | 'intrinsic' 100 | | {'permanent', delay()} 101 | | {'transient', delay()} 102 | | {'intrinsic', delay()}. 103 | -type shutdown() :: 'brutal_kill' | timeout(). 104 | -type worker() :: 'worker' | 'supervisor'. 105 | -type sup_name() :: {'local', Name :: atom()} | {'global', Name :: atom()}. 106 | -type sup_ref() :: (Name :: atom()) 107 | | {Name :: atom(), Node :: node()} 108 | | {'global', Name :: atom()} 109 | | pid(). 110 | -type child_spec() :: {Id :: child_id(), 111 | StartFunc :: mfargs(), 112 | Restart :: restart(), 113 | Shutdown :: shutdown(), 114 | Type :: worker(), 115 | Modules :: modules()}. 116 | 117 | -type strategy() :: 'one_for_all' | 'one_for_one' 118 | | 'rest_for_one' | 'simple_one_for_one'. 119 | 120 | %%-------------------------------------------------------------------------- 121 | 122 | -type tref() :: reference(). 123 | 124 | -record(child, {% pid is undefined when child is not running 125 | pid = undefined :: child() 126 | | {restarting,pid()} 127 | | {delayed_restart,tref()} 128 | | [pid()], 129 | name :: child_id(), 130 | mfargs :: mfargs(), 131 | restart_type :: restart(), 132 | shutdown :: shutdown(), 133 | child_type :: worker(), 134 | modules = [] :: modules()}). 135 | 136 | -type child_rec() :: #child{}. 137 | 138 | -define(DICT, dict). 139 | -define(SETS, sets). 140 | -define(SET, set). 141 | 142 | -type dynamics() :: ?DICT:?DICT() | ?SETS:?SET(). 143 | 144 | -record(state, {name, 145 | strategy, 146 | children = [], 147 | dynamics, 148 | intensity, 149 | period, 150 | restarts = [], 151 | module, 152 | args}). 153 | 154 | -type state() :: #state{name :: atom(), 155 | strategy :: strategy(), 156 | children :: [child_rec()], 157 | dynamics :: dynamics(), 158 | intensity :: non_neg_integer(), 159 | period :: pos_integer(), 160 | restarts :: list(), 161 | module :: module(), 162 | args :: list() 163 | }. 164 | 165 | -define(is_simple(State), State#state.strategy =:= simple_one_for_one). 166 | -define(is_permanent(R), ((R =:= permanent) orelse 167 | (is_tuple(R) andalso 168 | tuple_size(R) == 2 andalso 169 | element(1, R) =:= permanent))). 170 | -define(is_explicit_restart(R), 171 | R == {shutdown, restart}). 172 | 173 | -callback init(Args :: term()) -> 174 | {ok, {{RestartStrategy :: strategy(), 175 | MaxR :: non_neg_integer(), 176 | MaxT :: non_neg_integer()}, 177 | [ChildSpec :: child_spec()]}} 178 | | ignore 179 | | post_init. 180 | 181 | -callback post_init(Args :: term()) -> 182 | {ok, {{RestartStrategy :: strategy(), 183 | MaxR :: non_neg_integer(), 184 | MaxT :: non_neg_integer()}, 185 | [ChildSpec :: child_spec()]}} 186 | | ignore. 187 | 188 | -optional_callbacks([post_init/1]). 189 | -define(restarting(_Pid_), {restarting,_Pid_}). 190 | -define(delayed_restart(_TRef_), {delayed_restart,_TRef_}). 191 | 192 | %%% --------------------------------------------------- 193 | %%% This is a general process supervisor built upon gen_server.erl. 194 | %%% Servers/processes should/could also be built using gen_server.erl. 195 | %%% SupName = {local, atom()} | {global, atom()}. 196 | %%% --------------------------------------------------- 197 | -type startlink_err() :: {'already_started', pid()} 198 | | {'shutdown', term()} 199 | | term(). 200 | -type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}. 201 | 202 | -spec start_link(Module, Args) -> startlink_ret() when 203 | Module :: module(), 204 | Args :: term(). 205 | 206 | start_link(Mod, Args) -> 207 | gen_server:start_link(?MODULE, {self, Mod, Args}, []). 208 | 209 | -spec start_link(SupName, Module, Args) -> startlink_ret() when 210 | SupName :: sup_name(), 211 | Module :: module(), 212 | Args :: term(). 213 | start_link(SupName, Mod, Args) -> 214 | gen_server:start_link(SupName, ?MODULE, {SupName, Mod, Args}, []). 215 | 216 | %%% --------------------------------------------------- 217 | %%% Interface functions. 218 | %%% --------------------------------------------------- 219 | -type startchild_err() :: 'already_present' 220 | | {'already_started', Child :: child()} | term(). 221 | -type startchild_ret() :: {'ok', Child :: child()} 222 | | {'ok', Child :: child(), Info :: term()} 223 | | {'error', startchild_err()}. 224 | 225 | -spec start_child(SupRef, ChildSpec) -> startchild_ret() when 226 | SupRef :: sup_ref(), 227 | ChildSpec :: child_spec() | (List :: [term()]). 228 | start_child(Supervisor, ChildSpec) -> 229 | call(Supervisor, {start_child, ChildSpec}). 230 | 231 | -spec restart_child(SupRef, Id) -> Result when 232 | SupRef :: sup_ref(), 233 | Id :: child_id(), 234 | Result :: {'ok', Child :: child()} 235 | | {'ok', Child :: child(), Info :: term()} 236 | | {'error', Error}, 237 | Error :: 'running' | 'restarting' | 'not_found' | 'simple_one_for_one' | 238 | term(). 239 | restart_child(Supervisor, Name) -> 240 | call(Supervisor, {restart_child, Name}). 241 | 242 | -spec delete_child(SupRef, Id) -> Result when 243 | SupRef :: sup_ref(), 244 | Id :: child_id(), 245 | Result :: 'ok' | {'error', Error}, 246 | Error :: 'running' | 'restarting' | 'not_found' | 'simple_one_for_one'. 247 | delete_child(Supervisor, Name) -> 248 | call(Supervisor, {delete_child, Name}). 249 | 250 | %%----------------------------------------------------------------- 251 | %% Func: terminate_child/2 252 | %% Returns: ok | {error, Reason} 253 | %% Note that the child is *always* terminated in some 254 | %% way (maybe killed). 255 | %%----------------------------------------------------------------- 256 | -spec terminate_child(SupRef, Id) -> Result when 257 | SupRef :: sup_ref(), 258 | Id :: pid() | child_id(), 259 | Result :: 'ok' | {'error', Error}, 260 | Error :: 'not_found' | 'simple_one_for_one'. 261 | terminate_child(Supervisor, Name) -> 262 | call(Supervisor, {terminate_child, Name}). 263 | 264 | -spec which_children(SupRef) -> [{Id,Child,Type,Modules}] when 265 | SupRef :: sup_ref(), 266 | Id :: child_id() | undefined, 267 | Child :: child() | 'restarting', 268 | Type :: worker(), 269 | Modules :: modules(). 270 | which_children(Supervisor) -> 271 | call(Supervisor, which_children). 272 | 273 | -spec count_children(SupRef) -> PropListOfCounts when 274 | SupRef :: sup_ref(), 275 | PropListOfCounts :: [Count], 276 | Count :: {specs, ChildSpecCount :: non_neg_integer()} 277 | | {active, ActiveProcessCount :: non_neg_integer()} 278 | | {supervisors, ChildSupervisorCount :: non_neg_integer()} 279 | | {workers, ChildWorkerCount :: non_neg_integer()}. 280 | count_children(Supervisor) -> 281 | call(Supervisor, count_children). 282 | 283 | -spec find_child(Supervisor, Name) -> [pid()] when 284 | Supervisor :: sup_ref(), 285 | Name :: child_id(). 286 | find_child(Supervisor, Name) -> 287 | [Pid || {Name1, Pid, _Type, _Modules} <- which_children(Supervisor), 288 | Name1 =:= Name]. 289 | 290 | call(Supervisor, Req) -> 291 | gen_server:call(Supervisor, Req, infinity). 292 | 293 | -spec check_childspecs(ChildSpecs) -> Result when 294 | ChildSpecs :: [child_spec()], 295 | Result :: 'ok' | {'error', Error :: term()}. 296 | check_childspecs(ChildSpecs) when is_list(ChildSpecs) -> 297 | case check_startspec(ChildSpecs) of 298 | {ok, _} -> ok; 299 | Error -> {error, Error} 300 | end; 301 | check_childspecs(X) -> {error, {badarg, X}}. 302 | 303 | %%%----------------------------------------------------------------- 304 | %%% Called by timer:apply_after from restart/2 305 | -spec try_again_restart(SupRef, Child, Reason) -> ok when 306 | SupRef :: sup_ref(), 307 | Child :: child_id() | pid(), 308 | Reason :: term(). 309 | try_again_restart(Supervisor, Child, Reason) -> 310 | cast(Supervisor, {try_again_restart, Child, Reason}). 311 | 312 | cast(Supervisor, Req) -> 313 | gen_server:cast(Supervisor, Req). 314 | 315 | %%% --------------------------------------------------- 316 | %%% 317 | %%% Initialize the supervisor. 318 | %%% 319 | %%% --------------------------------------------------- 320 | -type init_sup_name() :: sup_name() | 'self'. 321 | 322 | -type stop_rsn() :: {'shutdown', term()} 323 | | {'bad_return', {module(),'init', term()}} 324 | | {'bad_start_spec', term()} 325 | | {'start_spec', term()} 326 | | {'supervisor_data', term()}. 327 | 328 | -spec init({init_sup_name(), module(), [term()]}) -> 329 | {'ok', state()} | 'ignore' | {'stop', stop_rsn()}. 330 | init({SupName, Mod, Args}) -> 331 | process_flag(trap_exit, true), 332 | case Mod:init(Args) of 333 | {ok, {SupFlags, StartSpec}} -> 334 | do_init(SupName, SupFlags, StartSpec, Mod, Args); 335 | post_init -> 336 | self() ! {post_init, SupName, Mod, Args}, 337 | {ok, #state{}}; 338 | ignore -> 339 | ignore; 340 | Error -> 341 | {stop, {bad_return, {Mod, init, Error}}} 342 | end. 343 | 344 | init_children(State, StartSpec) -> 345 | SupName = State#state.name, 346 | case check_startspec(StartSpec) of 347 | {ok, Children} -> 348 | case start_children(Children, SupName) of 349 | {ok, NChildren} -> 350 | {ok, State#state{children = NChildren}}; 351 | {error, NChildren, Reason} -> 352 | terminate_children(NChildren, SupName), 353 | {stop, {shutdown, Reason}} 354 | end; 355 | Error -> 356 | {stop, {start_spec, Error}} 357 | end. 358 | 359 | init_dynamic(State, [StartSpec]) -> 360 | case check_startspec([StartSpec]) of 361 | {ok, Children} -> 362 | {ok, State#state{children = Children}}; 363 | Error -> 364 | {stop, {start_spec, Error}} 365 | end; 366 | init_dynamic(_State, StartSpec) -> 367 | {stop, {bad_start_spec, StartSpec}}. 368 | 369 | %%----------------------------------------------------------------- 370 | %% Func: start_children/2 371 | %% Args: Children = [child_rec()] in start order 372 | %% SupName = {local, atom()} | {global, atom()} | {pid(), Mod} 373 | %% Purpose: Start all children. The new list contains #child's 374 | %% with pids. 375 | %% Returns: {ok, NChildren} | {error, NChildren, Reason} 376 | %% NChildren = [child_rec()] in termination order (reversed 377 | %% start order) 378 | %%----------------------------------------------------------------- 379 | start_children(Children, SupName) -> start_children(Children, [], SupName). 380 | 381 | start_children([Child|Chs], NChildren, SupName) -> 382 | case do_start_child(SupName, Child) of 383 | {ok, undefined} when Child#child.restart_type =:= temporary -> 384 | start_children(Chs, NChildren, SupName); 385 | {ok, Pid} -> 386 | start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName); 387 | {ok, Pid, _Extra} -> 388 | start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName); 389 | {error, Reason} -> 390 | report_error(start_error, Reason, Child, SupName), 391 | {error, lists:reverse(Chs) ++ [Child | NChildren], 392 | {failed_to_start_child,Child#child.name,Reason}} 393 | end; 394 | start_children([], NChildren, _SupName) -> 395 | {ok, NChildren}. 396 | 397 | do_start_child(SupName, Child) -> 398 | #child{mfargs = {M, F, Args}} = Child, 399 | case catch apply(M, F, Args) of 400 | {ok, Pid} when is_pid(Pid) -> 401 | NChild = Child#child{pid = Pid}, 402 | report_progress(NChild, SupName), 403 | {ok, Pid}; 404 | {ok, Pid, Extra} when is_pid(Pid) -> 405 | NChild = Child#child{pid = Pid}, 406 | report_progress(NChild, SupName), 407 | {ok, Pid, Extra}; 408 | ignore -> 409 | {ok, undefined}; 410 | {error, What} -> {error, What}; 411 | What -> {error, What} 412 | end. 413 | 414 | do_start_child_i(M, F, A) -> 415 | case catch apply(M, F, A) of 416 | {ok, Pid} when is_pid(Pid) -> 417 | {ok, Pid}; 418 | {ok, Pid, Extra} when is_pid(Pid) -> 419 | {ok, Pid, Extra}; 420 | ignore -> 421 | {ok, undefined}; 422 | {error, Error} -> 423 | {error, Error}; 424 | What -> 425 | {error, What} 426 | end. 427 | 428 | %%% --------------------------------------------------- 429 | %%% 430 | %%% Callback functions. 431 | %%% 432 | %%% --------------------------------------------------- 433 | -type call() :: 'which_children' | 'count_children' | {_, _}. % XXX: refine 434 | -spec handle_call(call(), term(), state()) -> {'reply', term(), state()}. 435 | handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) -> 436 | Child = hd(State#state.children), 437 | #child{mfargs = {M, F, A}} = Child, 438 | Args = A ++ EArgs, 439 | case do_start_child_i(M, F, Args) of 440 | {ok, undefined} when Child#child.restart_type =:= temporary -> 441 | {reply, {ok, undefined}, State}; 442 | {ok, Pid} -> 443 | NState = save_dynamic_child(Child#child.restart_type, Pid, Args, 444 | State), 445 | {reply, {ok, Pid}, NState}; 446 | {ok, Pid, Extra} -> 447 | NState = save_dynamic_child(Child#child.restart_type, Pid, Args, 448 | State), 449 | {reply, {ok, Pid, Extra}, NState}; 450 | What -> 451 | {reply, What, State} 452 | end; 453 | 454 | %% terminate_child for simple_one_for_one can only be done with pid 455 | handle_call({terminate_child, Name}, _From, State) when not is_pid(Name), 456 | ?is_simple(State) -> 457 | {reply, {error, simple_one_for_one}, State}; 458 | 459 | handle_call({terminate_child, Name}, _From, State) -> 460 | case get_child(Name, State, ?is_simple(State)) of 461 | {value, Child} -> 462 | case do_terminate(Child, State#state.name) of 463 | #child{restart_type=RT} when RT=:=temporary; 464 | ?is_simple(State) -> 465 | {reply, ok, state_del_child(Child, State)}; 466 | NChild -> 467 | {reply, ok, replace_child(NChild, State)} 468 | end; 469 | false -> 470 | {reply, {error, not_found}, State} 471 | end; 472 | 473 | %%% The requests delete_child and restart_child are invalid for 474 | %%% simple_one_for_one supervisors. 475 | handle_call({_Req, _Data}, _From, State) when ?is_simple(State) -> 476 | {reply, {error, simple_one_for_one}, State}; 477 | 478 | handle_call({start_child, ChildSpec}, _From, State) -> 479 | case check_childspec(ChildSpec) of 480 | {ok, Child} -> 481 | {Resp, NState} = handle_start_child(Child, State), 482 | {reply, Resp, NState}; 483 | What -> 484 | {reply, {error, What}, State} 485 | end; 486 | 487 | handle_call({restart_child, Name}, _From, State) -> 488 | case get_child(Name, State) of 489 | {value, Child} when Child#child.pid =:= undefined -> 490 | case do_start_child(State#state.name, Child) of 491 | {ok, Pid} -> 492 | NState = replace_child(Child#child{pid = Pid}, State), 493 | {reply, {ok, Pid}, NState}; 494 | {ok, Pid, Extra} -> 495 | NState = replace_child(Child#child{pid = Pid}, State), 496 | {reply, {ok, Pid, Extra}, NState}; 497 | Error -> 498 | {reply, Error, State} 499 | end; 500 | {value, #child{pid=?restarting(_)}} -> 501 | {reply, {error, restarting}, State}; 502 | {value, #child{pid=?delayed_restart(_)}} -> 503 | {reply, {error, restarting}, State}; 504 | {value, _} -> 505 | {reply, {error, running}, State}; 506 | _ -> 507 | {reply, {error, not_found}, State} 508 | end; 509 | 510 | handle_call({delete_child, Name}, _From, State) -> 511 | case get_child(Name, State) of 512 | {value, Child} when Child#child.pid =:= undefined -> 513 | NState = remove_child(Child, State), 514 | {reply, ok, NState}; 515 | {value, #child{pid=?restarting(_)}} -> 516 | {reply, {error, restarting}, State}; 517 | {value, #child{pid=?delayed_restart(_)}} -> 518 | {reply, {error, restarting}, State}; 519 | {value, _} -> 520 | {reply, {error, running}, State}; 521 | _ -> 522 | {reply, {error, not_found}, State} 523 | end; 524 | 525 | handle_call(which_children, _From, 526 | #state{children = [#child{restart_type = temporary, 527 | child_type = CT, 528 | modules = Mods}]} = State) 529 | when ?is_simple(State) -> 530 | Reply = lists:map(fun(Pid) -> {undefined, Pid, CT, Mods} end, 531 | ?SETS:to_list(dynamics_db(temporary, 532 | State#state.dynamics))), 533 | {reply, Reply, State}; 534 | 535 | handle_call(which_children, _From, 536 | #state{children = [#child{restart_type = RType, 537 | child_type = CT, 538 | modules = Mods}]} = State) 539 | when ?is_simple(State) -> 540 | Reply = lists:map(fun({?restarting(_),_}) -> {undefined,restarting,CT,Mods}; 541 | ({Pid, _}) -> {undefined, Pid, CT, Mods} end, 542 | ?DICT:to_list(dynamics_db(RType, State#state.dynamics))), 543 | {reply, Reply, State}; 544 | 545 | handle_call(which_children, _From, State) -> 546 | Resp = 547 | lists:map(fun(#child{pid = ?restarting(_), name = Name, 548 | child_type = ChildType, modules = Mods}) -> 549 | {Name, restarting, ChildType, Mods}; 550 | (#child{pid = ?delayed_restart(_), name = Name, 551 | child_type = ChildType, modules = Mods}) -> 552 | {Name, restarting, ChildType, Mods}; 553 | (#child{pid = Pid, name = Name, 554 | child_type = ChildType, modules = Mods}) -> 555 | {Name, Pid, ChildType, Mods} 556 | end, 557 | State#state.children), 558 | {reply, Resp, State}; 559 | 560 | 561 | handle_call(count_children, _From, 562 | #state{children = [#child{restart_type = temporary, 563 | child_type = CT}]} = State) 564 | when ?is_simple(State) -> 565 | {Active, Count} = 566 | ?SETS:fold(fun(Pid, {Alive, Tot}) -> 567 | case is_pid(Pid) andalso is_process_alive(Pid) of 568 | true ->{Alive+1, Tot +1}; 569 | false -> 570 | {Alive, Tot + 1} 571 | end 572 | end, {0, 0}, dynamics_db(temporary, State#state.dynamics)), 573 | Reply = case CT of 574 | supervisor -> [{specs, 1}, {active, Active}, 575 | {supervisors, Count}, {workers, 0}]; 576 | worker -> [{specs, 1}, {active, Active}, 577 | {supervisors, 0}, {workers, Count}] 578 | end, 579 | {reply, Reply, State}; 580 | 581 | handle_call(count_children, _From, 582 | #state{children = [#child{restart_type = RType, 583 | child_type = CT}]} = State) 584 | when ?is_simple(State) -> 585 | {Active, Count} = 586 | ?DICT:fold(fun(Pid, _Val, {Alive, Tot}) -> 587 | case is_pid(Pid) andalso is_process_alive(Pid) of 588 | true -> 589 | {Alive+1, Tot +1}; 590 | false -> 591 | {Alive, Tot + 1} 592 | end 593 | end, {0, 0}, dynamics_db(RType, State#state.dynamics)), 594 | Reply = case CT of 595 | supervisor -> [{specs, 1}, {active, Active}, 596 | {supervisors, Count}, {workers, 0}]; 597 | worker -> [{specs, 1}, {active, Active}, 598 | {supervisors, 0}, {workers, Count}] 599 | end, 600 | {reply, Reply, State}; 601 | 602 | handle_call(count_children, _From, State) -> 603 | %% Specs and children are together on the children list... 604 | {Specs, Active, Supers, Workers} = 605 | lists:foldl(fun(Child, Counts) -> 606 | count_child(Child, Counts) 607 | end, {0,0,0,0}, State#state.children), 608 | 609 | %% Reformat counts to a property list. 610 | Reply = [{specs, Specs}, {active, Active}, 611 | {supervisors, Supers}, {workers, Workers}], 612 | {reply, Reply, State}. 613 | 614 | 615 | count_child(#child{pid = Pid, child_type = worker}, 616 | {Specs, Active, Supers, Workers}) -> 617 | case is_pid(Pid) andalso is_process_alive(Pid) of 618 | true -> {Specs+1, Active+1, Supers, Workers+1}; 619 | false -> {Specs+1, Active, Supers, Workers+1} 620 | end; 621 | count_child(#child{pid = Pid, child_type = supervisor}, 622 | {Specs, Active, Supers, Workers}) -> 623 | case is_pid(Pid) andalso is_process_alive(Pid) of 624 | true -> {Specs+1, Active+1, Supers+1, Workers}; 625 | false -> {Specs+1, Active, Supers+1, Workers} 626 | end. 627 | 628 | 629 | %%% If a restart attempt failed, this message is sent via 630 | %%% timer:apply_after(0,...) in order to give gen_server the chance to 631 | %%% check it's inbox before trying again. 632 | -spec handle_cast({try_again_restart, child_id() | pid(), term()}, state()) -> 633 | {'noreply', state()} | {stop, shutdown, state()}. 634 | handle_cast({try_again_restart,Pid,Reason}, #state{children=[Child]}=State) 635 | when ?is_simple(State) -> 636 | RT = Child#child.restart_type, 637 | RPid = restarting(Pid), 638 | case dynamic_child_args(RPid, dynamics_db(RT, State#state.dynamics)) of 639 | {ok, Args} -> 640 | {M, F, _} = Child#child.mfargs, 641 | NChild = Child#child{pid = RPid, mfargs = {M, F, Args}}, 642 | try_restart(Child#child.restart_type, Reason, NChild, State); 643 | error -> 644 | {noreply, State} 645 | end; 646 | 647 | handle_cast({try_again_restart,Name,Reason}, State) -> 648 | %% we still support >= R12-B3 in which lists:keyfind/3 doesn't exist 649 | case lists:keysearch(Name,#child.name,State#state.children) of 650 | {value, Child = #child{pid=?restarting(_), restart_type=RestartType}} -> 651 | try_restart(RestartType, Reason, Child, State); 652 | _ -> 653 | {noreply,State} 654 | end. 655 | 656 | %% 657 | %% Take care of terminated children. 658 | %% 659 | -spec handle_info(term(), state()) -> 660 | {'noreply', state()} | {'stop', term(), state()}. 661 | handle_info({'EXIT', Pid, Reason}, State) -> 662 | case restart_child(Pid, Reason, State) of 663 | {ok, State1} -> 664 | {noreply, State1}; 665 | {shutdown, State1} -> 666 | {stop, shutdown, State1} 667 | end; 668 | 669 | handle_info({delayed_restart, {RestartType, _Reason, Child}}, State) 670 | when ?is_simple(State) -> 671 | Reason = {?MODULE, delayed_restart}, 672 | try_restart(RestartType, Reason, Child, State#state{restarts = []}); %% [1] 673 | handle_info({delayed_restart, {RestartType, _Reason, Child}}, State) -> 674 | Reason = {?MODULE, delayed_restart}, 675 | case get_child(Child#child.name, State) of 676 | {value, Child1} -> 677 | try_restart(RestartType, Reason, Child1, 678 | State#state{restarts = []}); %% [1] 679 | _What -> 680 | {noreply, State} 681 | end; 682 | %% [1] When we receive a delayed_restart message we want to reset the 683 | %% restarts field since otherwise the MaxT might not have elapsed and 684 | %% we would just delay again and again. Since a common use of the 685 | %% delayed restart feature is for MaxR = 1, MaxT = some huge number 686 | %% (so that we don't end up bouncing around in non-delayed restarts) 687 | %% this is important. 688 | 689 | handle_info({post_init, SupName, Mod, Args}, State0) -> 690 | Res = case Mod:post_init(Args) of 691 | {ok, {SupFlags, StartSpec}} -> 692 | do_init(SupName, SupFlags, StartSpec, Mod, Args); 693 | Error -> 694 | {stop, {bad_return, {Mod, post_init, Error}}} 695 | end, 696 | %% map init/1 result type to handle_* result type 697 | case Res of 698 | {ok, NewState} -> {noreply, NewState}; 699 | {stop, Reason} -> {stop, Reason, State0} 700 | end; 701 | 702 | handle_info(Msg, State) -> 703 | error_logger:error_msg("Supervisor received unexpected message: ~p~n", 704 | [Msg]), 705 | {noreply, State}. 706 | 707 | %% 708 | %% Terminate this server. 709 | %% 710 | -spec terminate(term(), state()) -> 'ok'. 711 | terminate(_Reason, #state{children=[Child]} = State) when ?is_simple(State) -> 712 | terminate_dynamic_children(Child, dynamics_db(Child#child.restart_type, 713 | State#state.dynamics), 714 | State#state.name); 715 | terminate(_Reason, State) -> 716 | terminate_children(State#state.children, State#state.name). 717 | 718 | %% 719 | %% Change code for the supervisor. 720 | %% Call the new call-back module and fetch the new start specification. 721 | %% Combine the new spec. with the old. If the new start spec. is 722 | %% not valid the code change will not succeed. 723 | %% Use the old Args as argument to Module:init/1. 724 | %% NOTE: This requires that the init function of the call-back module 725 | %% does not have any side effects. 726 | %% 727 | -spec code_change(term(), state(), term()) -> 728 | {'ok', state()} | {'error', term()}. 729 | code_change(_, State, _) -> 730 | case (State#state.module):init(State#state.args) of 731 | {ok, {SupFlags, StartSpec}} -> 732 | case catch check_flags(SupFlags) of 733 | ok -> 734 | {Strategy, MaxIntensity, Period} = SupFlags, 735 | update_childspec(State#state{strategy = Strategy, 736 | intensity = MaxIntensity, 737 | period = Period}, 738 | StartSpec); 739 | Error -> 740 | {error, Error} 741 | end; 742 | ignore -> 743 | {ok, State}; 744 | Error -> 745 | Error 746 | end. 747 | 748 | check_flags({Strategy, MaxIntensity, Period}) -> 749 | validStrategy(Strategy), 750 | validIntensity(MaxIntensity), 751 | validPeriod(Period), 752 | ok; 753 | check_flags(What) -> 754 | {bad_flags, What}. 755 | 756 | update_childspec(State, StartSpec) when ?is_simple(State) -> 757 | case check_startspec(StartSpec) of 758 | {ok, [Child]} -> 759 | {ok, State#state{children = [Child]}}; 760 | Error -> 761 | {error, Error} 762 | end; 763 | update_childspec(State, StartSpec) -> 764 | case check_startspec(StartSpec) of 765 | {ok, Children} -> 766 | OldC = State#state.children, % In reverse start order ! 767 | NewC = update_childspec1(OldC, Children, []), 768 | {ok, State#state{children = NewC}}; 769 | Error -> 770 | {error, Error} 771 | end. 772 | 773 | update_childspec1([Child|OldC], Children, KeepOld) -> 774 | case update_chsp(Child, Children) of 775 | {ok,NewChildren} -> 776 | update_childspec1(OldC, NewChildren, KeepOld); 777 | false -> 778 | update_childspec1(OldC, Children, [Child|KeepOld]) 779 | end; 780 | update_childspec1([], Children, KeepOld) -> 781 | %% Return them in (kept) reverse start order. 782 | lists:reverse(Children ++ KeepOld). 783 | 784 | update_chsp(OldCh, Children) -> 785 | case lists:map(fun(Ch) when OldCh#child.name =:= Ch#child.name -> 786 | Ch#child{pid = OldCh#child.pid}; 787 | (Ch) -> 788 | Ch 789 | end, 790 | Children) of 791 | Children -> 792 | false; % OldCh not found in new spec. 793 | NewC -> 794 | {ok, NewC} 795 | end. 796 | 797 | %%% --------------------------------------------------- 798 | %%% Start a new child. 799 | %%% --------------------------------------------------- 800 | 801 | handle_start_child(Child, State) -> 802 | case get_child(Child#child.name, State) of 803 | false -> 804 | case do_start_child(State#state.name, Child) of 805 | {ok, undefined} when Child#child.restart_type =:= temporary -> 806 | {{ok, undefined}, State}; 807 | {ok, Pid} -> 808 | {{ok, Pid}, save_child(Child#child{pid = Pid}, State)}; 809 | {ok, Pid, Extra} -> 810 | {{ok, Pid, Extra}, 811 | save_child(Child#child{pid = Pid}, State)}; 812 | {error, What} -> 813 | {{error, {What, Child}}, State} 814 | end; 815 | {value, OldChild} when is_pid(OldChild#child.pid) -> 816 | {{error, {already_started, OldChild#child.pid}}, State}; 817 | {value, _OldChild} -> 818 | {{error, already_present}, State} 819 | end. 820 | 821 | %%% --------------------------------------------------- 822 | %%% Restart. A process has terminated. 823 | %%% Returns: {ok, state()} | {shutdown, state()} 824 | %%% --------------------------------------------------- 825 | 826 | restart_child(Pid, Reason, #state{children = [Child]} = State) 827 | when ?is_simple(State) -> 828 | RestartType = Child#child.restart_type, 829 | case dynamic_child_args(Pid, dynamics_db(RestartType, 830 | State#state.dynamics)) of 831 | {ok, Args} -> 832 | {M, F, _} = Child#child.mfargs, 833 | NChild = Child#child{pid = Pid, mfargs = {M, F, Args}}, 834 | do_restart(RestartType, Reason, NChild, State); 835 | error -> 836 | {ok, State} 837 | end; 838 | 839 | restart_child(Pid, Reason, State) -> 840 | Children = State#state.children, 841 | %% we still support >= R12-B3 in which lists:keyfind/3 doesn't exist 842 | case lists:keysearch(Pid, #child.pid, Children) of 843 | {value, #child{restart_type = RestartType} = Child} -> 844 | do_restart(RestartType, Reason, Child, State); 845 | false -> 846 | {ok, State} 847 | end. 848 | 849 | try_restart(RestartType, Reason, Child, State) -> 850 | case handle_restart(RestartType, Reason, Child, State) of 851 | {ok, NState} -> {noreply, NState}; 852 | {shutdown, State2} -> {stop, shutdown, State2} 853 | end. 854 | 855 | do_restart(RestartType, Reason, Child, State) -> 856 | maybe_report_error(RestartType, Reason, Child, State), 857 | handle_restart(RestartType, Reason, Child, State). 858 | 859 | maybe_report_error(permanent, Reason, Child, State) -> 860 | report_child_termination(Reason, Child, State); 861 | maybe_report_error({permanent, _}, Reason, Child, State) -> 862 | report_child_termination(Reason, Child, State); 863 | maybe_report_error(_Type, Reason, Child, State) -> 864 | case is_abnormal_termination(Reason) of 865 | true -> report_child_termination(Reason, Child, State); 866 | false -> ok 867 | end. 868 | 869 | report_child_termination(Reason, Child, State) -> 870 | report_error(child_terminated, Reason, Child, State#state.name). 871 | 872 | handle_restart(permanent, _Reason, Child, State) -> 873 | restart(Child, State); 874 | handle_restart(transient, Reason, Child, State) -> 875 | restart_if_explicit_or_abnormal(fun restart/2, 876 | fun delete_child_and_continue/2, 877 | Reason, Child, State); 878 | handle_restart(intrinsic, Reason, Child, State) -> 879 | restart_if_explicit_or_abnormal(fun restart/2, 880 | fun delete_child_and_stop/2, 881 | Reason, Child, State); 882 | handle_restart(temporary, _Reason, Child, State) -> 883 | delete_child_and_continue(Child, State); 884 | handle_restart({permanent, _Delay}=Restart, Reason, Child, State) -> 885 | do_restart_delay(Restart, Reason, Child, State); 886 | handle_restart({transient, _Delay}=Restart, Reason, Child, State) -> 887 | restart_if_explicit_or_abnormal(defer_to_restart_delay(Restart, Reason), 888 | fun delete_child_and_continue/2, 889 | Reason, Child, State); 890 | handle_restart({intrinsic, _Delay}=Restart, Reason, Child, State) -> 891 | restart_if_explicit_or_abnormal(defer_to_restart_delay(Restart, Reason), 892 | fun delete_child_and_stop/2, 893 | Reason, Child, State). 894 | 895 | restart_if_explicit_or_abnormal(RestartHow, Otherwise, Reason, Child, State) -> 896 | case ?is_explicit_restart(Reason) orelse is_abnormal_termination(Reason) of 897 | true -> RestartHow(Child, State); 898 | false -> Otherwise(Child, State) 899 | end. 900 | 901 | defer_to_restart_delay(Restart, Reason) -> 902 | fun(Child, State) -> do_restart_delay(Restart, Reason, Child, State) end. 903 | 904 | delete_child_and_continue(Child, State) -> 905 | {ok, state_del_child(Child, State)}. 906 | 907 | delete_child_and_stop(Child, State) -> 908 | {shutdown, state_del_child(Child, State)}. 909 | 910 | is_abnormal_termination(normal) -> false; 911 | is_abnormal_termination(shutdown) -> false; 912 | is_abnormal_termination({shutdown, _}) -> false; 913 | is_abnormal_termination(_Other) -> true. 914 | 915 | do_restart_delay({RestartType, Delay}, Reason, Child, State) -> 916 | %% Reason =:= {?MODULE, delayed_restart} indicates 917 | %% the first restart after delay (a clean retry) 918 | %% do not add it to MaxR accumulation 919 | IsCleanRetry = Reason =:= {?MODULE, delayed_restart}, 920 | case add_restart(State, IsCleanRetry) of 921 | {ok, NState} -> 922 | maybe_restart(NState#state.strategy, Child, NState); 923 | {terminate, _NState} -> 924 | %% we've reached the max restart intensity, but the 925 | %% add_restart will have added to the restarts 926 | %% field. Given we don't want to die here, we need to go 927 | %% back to the old restarts field otherwise we'll never 928 | %% attempt to restart later, which is why we ignore 929 | %% NState for this clause. 930 | TRef = erlang:send_after(trunc(Delay*1000), self(), 931 | {delayed_restart, 932 | {{RestartType, Delay}, Reason, Child}}), 933 | NState = 934 | case ?is_simple(State) of 935 | true -> 936 | %% delete simple child, added back when restart 937 | state_del_child(Child, State); 938 | false -> 939 | %% Do not delete (set pid = undefined) for non-simple ones 940 | %% the ?restarting(_) state of the child should be kept. 941 | %% 942 | %% Otherwise, in case the child failed to start at delayed retry 943 | %% (e.g. the process crashed in Module:init/1 which leads to 944 | %% a 'start_error' error in restart/3), an immediate 945 | %% 'try_again_restart' loop back cast will be sent in 946 | %% maybe_restart/3, then the 'handle_cast' callback 947 | %% will fail to find the child in ?restarting state, 948 | %% hence ignore the restart, which will in turn 949 | %% cause it to enter a permanent zombie state. 950 | replace_child(Child#child{pid = ?delayed_restart(TRef)}, State) 951 | end, 952 | {ok, NState} 953 | end. 954 | 955 | restart(Child, State) -> 956 | case add_restart(State) of 957 | {ok, NState} -> 958 | maybe_restart(NState#state.strategy, Child, NState); 959 | {terminate, NState} -> 960 | report_error(shutdown, reached_max_restart_intensity, 961 | Child, State#state.name), 962 | {shutdown, remove_child(Child, NState)} 963 | end. 964 | 965 | maybe_restart(Strategy, Child, State) -> 966 | case restart(Strategy, Child, State) of 967 | {try_again, Reason, NState2} -> 968 | %% Leaving control back to gen_server before 969 | %% trying again. This way other incoming requsts 970 | %% for the supervisor can be handled - e.g. a 971 | %% shutdown request for the supervisor or the 972 | %% child. 973 | Id = if ?is_simple(State) -> Child#child.pid; 974 | true -> Child#child.name 975 | end, 976 | timer:apply_after(0,?MODULE,try_again_restart,[self(),Id,Reason]), 977 | {ok,NState2}; 978 | Other -> 979 | Other 980 | end. 981 | 982 | restart(simple_one_for_one, Child, State) -> 983 | #child{pid = OldPid, mfargs = {M, F, A}} = Child, 984 | Dynamics = ?DICT:erase(OldPid, dynamics_db(Child#child.restart_type, 985 | State#state.dynamics)), 986 | case do_start_child_i(M, F, A) of 987 | {ok, Pid} -> 988 | NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)}, 989 | {ok, NState}; 990 | {ok, Pid, _Extra} -> 991 | NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)}, 992 | {ok, NState}; 993 | {error, Error} -> 994 | NState = State#state{dynamics = ?DICT:store(restarting(OldPid), A, 995 | Dynamics)}, 996 | report_error(start_error, Error, Child, State#state.name), 997 | {try_again, Error, NState} 998 | end; 999 | restart(one_for_one, Child, State) -> 1000 | OldPid = Child#child.pid, 1001 | case do_start_child(State#state.name, Child) of 1002 | {ok, Pid} -> 1003 | NState = replace_child(Child#child{pid = Pid}, State), 1004 | {ok, NState}; 1005 | {ok, Pid, _Extra} -> 1006 | NState = replace_child(Child#child{pid = Pid}, State), 1007 | {ok, NState}; 1008 | {error, Reason} -> 1009 | NState = replace_child(Child#child{pid = restarting(OldPid)}, 1010 | State), 1011 | report_error(start_error, Reason, Child, State#state.name), 1012 | {try_again, Reason, NState} 1013 | end; 1014 | restart(rest_for_one, Child, State) -> 1015 | {ChAfter, ChBefore} = split_child(Child#child.pid, State#state.children), 1016 | ChAfter2 = terminate_children(ChAfter, State#state.name), 1017 | case start_children(ChAfter2, State#state.name) of 1018 | {ok, ChAfter3} -> 1019 | {ok, State#state{children = ChAfter3 ++ ChBefore}}; 1020 | {error, ChAfter3, Reason} -> 1021 | NChild = Child#child{pid=restarting(Child#child.pid)}, 1022 | NState = State#state{children = ChAfter3 ++ ChBefore}, 1023 | {try_again, Reason, replace_child(NChild,NState)} 1024 | end; 1025 | restart(one_for_all, Child, State) -> 1026 | Children1 = del_child(Child#child.pid, State#state.children), 1027 | Children2 = terminate_children(Children1, State#state.name), 1028 | case start_children(Children2, State#state.name) of 1029 | {ok, NChs} -> 1030 | {ok, State#state{children = NChs}}; 1031 | {error, NChs, Reason} -> 1032 | NChild = Child#child{pid=restarting(Child#child.pid)}, 1033 | NState = State#state{children = NChs}, 1034 | {try_again, Reason, replace_child(NChild,NState)} 1035 | end. 1036 | 1037 | restarting(Pid) when is_pid(Pid) -> ?restarting(Pid); 1038 | restarting(RPid) -> RPid. 1039 | 1040 | %%----------------------------------------------------------------- 1041 | %% Func: terminate_children/2 1042 | %% Args: Children = [child_rec()] in termination order 1043 | %% SupName = {local, atom()} | {global, atom()} | {pid(),Mod} 1044 | %% Returns: NChildren = [child_rec()] in 1045 | %% startup order (reversed termination order) 1046 | %%----------------------------------------------------------------- 1047 | terminate_children(Children, SupName) -> 1048 | terminate_children(Children, SupName, []). 1049 | 1050 | %% Temporary children should not be restarted and thus should 1051 | %% be skipped when building the list of terminated children, although 1052 | %% we do want them to be shut down as many functions from this module 1053 | %% use this function to just clear everything. 1054 | terminate_children([Child = #child{restart_type=temporary} | Children], 1055 | SupName, Res) -> 1056 | do_terminate(Child, SupName), 1057 | terminate_children(Children, SupName, Res); 1058 | terminate_children([Child | Children], SupName, Res) -> 1059 | NChild = do_terminate(Child, SupName), 1060 | terminate_children(Children, SupName, [NChild | Res]); 1061 | terminate_children([], _SupName, Res) -> 1062 | Res. 1063 | 1064 | do_terminate(Child, SupName) when is_pid(Child#child.pid) -> 1065 | case shutdown(Child#child.pid, Child#child.shutdown) of 1066 | ok -> 1067 | ok; 1068 | {error, normal} when not ?is_permanent(Child#child.restart_type) -> 1069 | ok; 1070 | {error, OtherReason} -> 1071 | report_error(shutdown_error, OtherReason, Child, SupName) 1072 | end, 1073 | Child#child{pid = undefined}; 1074 | do_terminate(Child = #child{pid = ?delayed_restart(TRef)}, _SupName) -> 1075 | erlang:cancel_timer(TRef), 1076 | Child#child{pid = undefined}; 1077 | do_terminate(Child, _SupName) -> 1078 | Child#child{pid = undefined}. 1079 | 1080 | %%----------------------------------------------------------------- 1081 | %% Shutdowns a child. We must check the EXIT value 1082 | %% of the child, because it might have died with another reason than 1083 | %% the wanted. In that case we want to report the error. We put a 1084 | %% monitor on the child an check for the 'DOWN' message instead of 1085 | %% checking for the 'EXIT' message, because if we check the 'EXIT' 1086 | %% message a "naughty" child, who does unlink(Sup), could hang the 1087 | %% supervisor. 1088 | %% Returns: ok | {error, OtherReason} (this should be reported) 1089 | %%----------------------------------------------------------------- 1090 | shutdown(Pid, brutal_kill) -> 1091 | case monitor_child(Pid) of 1092 | ok -> 1093 | exit(Pid, kill), 1094 | receive 1095 | {'DOWN', _MRef, process, Pid, killed} -> 1096 | ok; 1097 | {'DOWN', _MRef, process, Pid, OtherReason} -> 1098 | {error, OtherReason} 1099 | end; 1100 | {error, Reason} -> 1101 | {error, Reason} 1102 | end; 1103 | shutdown(Pid, Time) -> 1104 | case monitor_child(Pid) of 1105 | ok -> 1106 | exit(Pid, shutdown), %% Try to shutdown gracefully 1107 | receive 1108 | {'DOWN', _MRef, process, Pid, shutdown} -> 1109 | ok; 1110 | {'DOWN', _MRef, process, Pid, OtherReason} -> 1111 | {error, OtherReason} 1112 | after Time -> 1113 | exit(Pid, kill), %% Force termination. 1114 | receive 1115 | {'DOWN', _MRef, process, Pid, OtherReason} -> 1116 | {error, OtherReason} 1117 | end 1118 | end; 1119 | {error, Reason} -> 1120 | {error, Reason} 1121 | end. 1122 | 1123 | %% Help function to shutdown/2 switches from link to monitor approach 1124 | monitor_child(Pid) -> 1125 | 1126 | %% Do the monitor operation first so that if the child dies 1127 | %% before the monitoring is done causing a 'DOWN'-message with 1128 | %% reason noproc, we will get the real reason in the 'EXIT'-message 1129 | %% unless a naughty child has already done unlink... 1130 | erlang:monitor(process, Pid), 1131 | unlink(Pid), 1132 | 1133 | receive 1134 | %% If the child dies before the unlik we must empty 1135 | %% the mail-box of the 'EXIT'-message and the 'DOWN'-message. 1136 | {'EXIT', Pid, Reason} -> 1137 | receive 1138 | {'DOWN', _, process, Pid, _} -> 1139 | {error, Reason} 1140 | end 1141 | after 0 -> 1142 | %% If a naughty child did unlink and the child dies before 1143 | %% monitor the result will be that shutdown/2 receives a 1144 | %% 'DOWN'-message with reason noproc. 1145 | %% If the child should die after the unlink there 1146 | %% will be a 'DOWN'-message with a correct reason 1147 | %% that will be handled in shutdown/2. 1148 | ok 1149 | end. 1150 | 1151 | 1152 | %%----------------------------------------------------------------- 1153 | %% Func: terminate_dynamic_children/3 1154 | %% Args: Child = child_rec() 1155 | %% Dynamics = ?DICT() | ?SET() 1156 | %% SupName = {local, atom()} | {global, atom()} | {pid(),Mod} 1157 | %% Returns: ok 1158 | %% 1159 | %% 1160 | %% Shutdown all dynamic children. This happens when the supervisor is 1161 | %% stopped. Because the supervisor can have millions of dynamic children, we 1162 | %% can have an significative overhead here. 1163 | %%----------------------------------------------------------------- 1164 | terminate_dynamic_children(Child, Dynamics, SupName) -> 1165 | {Pids, EStack0} = monitor_dynamic_children(Child, Dynamics), 1166 | Sz = ?SETS:size(Pids), 1167 | EStack = case Child#child.shutdown of 1168 | brutal_kill -> 1169 | ?SETS:fold(fun(P, _) -> exit(P, kill) end, ok, Pids), 1170 | wait_dynamic_children(Child, Pids, Sz, undefined, EStack0); 1171 | infinity -> 1172 | ?SETS:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids), 1173 | wait_dynamic_children(Child, Pids, Sz, undefined, EStack0); 1174 | Time -> 1175 | ?SETS:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids), 1176 | TRef = erlang:start_timer(Time, self(), kill), 1177 | wait_dynamic_children(Child, Pids, Sz, TRef, EStack0) 1178 | end, 1179 | %% Unroll stacked errors and report them 1180 | ?DICT:fold(fun(Reason, Ls, _) -> 1181 | report_error(shutdown_error, Reason, 1182 | Child#child{pid=Ls}, SupName) 1183 | end, ok, EStack). 1184 | 1185 | 1186 | monitor_dynamic_children(#child{restart_type=temporary}, Dynamics) -> 1187 | ?SETS:fold(fun(P, {Pids, EStack}) -> 1188 | case monitor_child(P) of 1189 | ok -> 1190 | {?SETS:add_element(P, Pids), EStack}; 1191 | {error, normal} -> 1192 | {Pids, EStack}; 1193 | {error, Reason} -> 1194 | {Pids, ?DICT:append(Reason, P, EStack)} 1195 | end 1196 | end, {?SETS:new(), ?DICT:new()}, Dynamics); 1197 | monitor_dynamic_children(#child{restart_type=RType}, Dynamics) -> 1198 | ?DICT:fold(fun(P, _, {Pids, EStack}) when is_pid(P) -> 1199 | case monitor_child(P) of 1200 | ok -> 1201 | {?SETS:add_element(P, Pids), EStack}; 1202 | {error, normal} when not ?is_permanent(RType) -> 1203 | {Pids, EStack}; 1204 | {error, Reason} -> 1205 | {Pids, ?DICT:append(Reason, P, EStack)} 1206 | end; 1207 | (?restarting(_), _, {Pids, EStack}) -> 1208 | {Pids, EStack} 1209 | end, {?SETS:new(), ?DICT:new()}, Dynamics). 1210 | 1211 | wait_dynamic_children(_Child, _Pids, 0, undefined, EStack) -> 1212 | EStack; 1213 | wait_dynamic_children(_Child, _Pids, 0, TRef, EStack) -> 1214 | %% If the timer has expired before its cancellation, we must empty the 1215 | %% mail-box of the 'timeout'-message. 1216 | erlang:cancel_timer(TRef), 1217 | receive 1218 | {timeout, TRef, kill} -> 1219 | EStack 1220 | after 0 -> 1221 | EStack 1222 | end; 1223 | wait_dynamic_children(#child{shutdown=brutal_kill} = Child, Pids, Sz, 1224 | TRef, EStack) -> 1225 | receive 1226 | {'DOWN', _MRef, process, Pid, killed} -> 1227 | wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, 1228 | TRef, EStack); 1229 | 1230 | {'DOWN', _MRef, process, Pid, Reason} -> 1231 | wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, 1232 | TRef, ?DICT:append(Reason, Pid, EStack)) 1233 | end; 1234 | wait_dynamic_children(#child{restart_type=RType} = Child, Pids, Sz, 1235 | TRef, EStack) -> 1236 | receive 1237 | {'DOWN', _MRef, process, Pid, shutdown} -> 1238 | wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, 1239 | TRef, EStack); 1240 | 1241 | {'DOWN', _MRef, process, Pid, normal} when not ?is_permanent(RType) -> 1242 | wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, 1243 | TRef, EStack); 1244 | 1245 | {'DOWN', _MRef, process, Pid, Reason} -> 1246 | wait_dynamic_children(Child, ?SETS:del_element(Pid, Pids), Sz-1, 1247 | TRef, ?DICT:append(Reason, Pid, EStack)); 1248 | 1249 | {timeout, TRef, kill} -> 1250 | ?SETS:fold(fun(P, _) -> exit(P, kill) end, ok, Pids), 1251 | wait_dynamic_children(Child, Pids, Sz-1, undefined, EStack) 1252 | end. 1253 | 1254 | %%----------------------------------------------------------------- 1255 | %% Child/State manipulating functions. 1256 | %%----------------------------------------------------------------- 1257 | 1258 | %% Note we do not want to save the parameter list for temporary processes as 1259 | %% they will not be restarted, and hence we do not need this information. 1260 | %% Especially for dynamic children to simple_one_for_one supervisors 1261 | %% it could become very costly as it is not uncommon to spawn 1262 | %% very many such processes. 1263 | save_child(#child{restart_type = temporary, 1264 | mfargs = {M, F, _}} = Child, 1265 | #state{children = Children} = State) -> 1266 | State#state{children = [Child#child{mfargs = {M, F, undefined}} |Children]}; 1267 | save_child(Child, #state{children = Children} = State) -> 1268 | State#state{children = [Child |Children]}. 1269 | 1270 | save_dynamic_child(temporary, Pid, _, #state{dynamics = Dynamics} = State) -> 1271 | State#state{dynamics = ?SETS:add_element(Pid, 1272 | dynamics_db(temporary, Dynamics))}; 1273 | save_dynamic_child(RestartType, Pid, Args, 1274 | #state{dynamics = Dynamics} = State) -> 1275 | State#state{dynamics = ?DICT:store(Pid, Args, 1276 | dynamics_db(RestartType, Dynamics))}. 1277 | 1278 | dynamics_db(temporary, undefined) -> 1279 | ?SETS:new(); 1280 | dynamics_db(_, undefined) -> 1281 | ?DICT:new(); 1282 | dynamics_db(_,Dynamics) -> 1283 | Dynamics. 1284 | 1285 | dynamic_child_args(Pid, Dynamics) -> 1286 | case ?SETS:is_set(Dynamics) of 1287 | true -> 1288 | {ok, undefined}; 1289 | false -> 1290 | ?DICT:find(Pid, Dynamics) 1291 | end. 1292 | 1293 | state_del_child(#child{pid = Pid, restart_type = temporary}, State) 1294 | when ?is_simple(State) -> 1295 | NDynamics = ?SETS:del_element(Pid, dynamics_db(temporary, 1296 | State#state.dynamics)), 1297 | State#state{dynamics = NDynamics}; 1298 | state_del_child(#child{pid = Pid, restart_type = RType}, State) 1299 | when ?is_simple(State) -> 1300 | NDynamics = ?DICT:erase(Pid, dynamics_db(RType, State#state.dynamics)), 1301 | State#state{dynamics = NDynamics}; 1302 | state_del_child(Child, State) -> 1303 | NChildren = del_child(Child#child.name, State#state.children), 1304 | State#state{children = NChildren}. 1305 | 1306 | del_child(Name, [Ch=#child{pid = ?restarting(_)}|_]=Chs) 1307 | when Ch#child.name =:= Name -> 1308 | Chs; 1309 | del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name, 1310 | Ch#child.restart_type =:= temporary -> 1311 | Chs; 1312 | del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name -> 1313 | [Ch#child{pid = undefined} | Chs]; 1314 | del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid, 1315 | Ch#child.restart_type =:= temporary -> 1316 | Chs; 1317 | del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid -> 1318 | [Ch#child{pid = undefined} | Chs]; 1319 | del_child(Name, [Ch|Chs]) -> 1320 | [Ch|del_child(Name, Chs)]; 1321 | del_child(_, []) -> 1322 | []. 1323 | 1324 | %% Chs = [S4, S3, Ch, S1, S0] 1325 | %% Ret: {[S4, S3, Ch], [S1, S0]} 1326 | split_child(Name, Chs) -> 1327 | split_child(Name, Chs, []). 1328 | 1329 | split_child(Name, [Ch|Chs], After) when Ch#child.name =:= Name -> 1330 | {lists:reverse([Ch#child{pid = undefined} | After]), Chs}; 1331 | split_child(Pid, [Ch|Chs], After) when Ch#child.pid =:= Pid -> 1332 | {lists:reverse([Ch#child{pid = undefined} | After]), Chs}; 1333 | split_child(Name, [Ch|Chs], After) -> 1334 | split_child(Name, Chs, [Ch | After]); 1335 | split_child(_, [], After) -> 1336 | {lists:reverse(After), []}. 1337 | 1338 | get_child(Name, State) -> 1339 | get_child(Name, State, false). 1340 | get_child(Pid, State, AllowPid) when AllowPid, is_pid(Pid) -> 1341 | get_dynamic_child(Pid, State); 1342 | get_child(Name, State, _) -> 1343 | lists:keysearch(Name, #child.name, State#state.children). 1344 | 1345 | get_dynamic_child(Pid, #state{children=[Child], dynamics=Dynamics}) -> 1346 | DynamicsDb = dynamics_db(Child#child.restart_type, Dynamics), 1347 | case is_dynamic_pid(Pid, DynamicsDb) of 1348 | true -> 1349 | {value, Child#child{pid=Pid}}; 1350 | false -> 1351 | RPid = restarting(Pid), 1352 | case is_dynamic_pid(RPid, DynamicsDb) of 1353 | true -> 1354 | {value, Child#child{pid=RPid}}; 1355 | false -> 1356 | case erlang:is_process_alive(Pid) of 1357 | true -> false; 1358 | false -> {value, Child} 1359 | end 1360 | end 1361 | end. 1362 | 1363 | is_dynamic_pid(Pid, Dynamics) -> 1364 | case ?SETS:is_set(Dynamics) of 1365 | true -> 1366 | ?SETS:is_element(Pid, Dynamics); 1367 | false -> 1368 | ?DICT:is_key(Pid, Dynamics) 1369 | end. 1370 | 1371 | replace_child(Child, State) -> 1372 | Chs = do_replace_child(Child, State#state.children), 1373 | State#state{children = Chs}. 1374 | 1375 | do_replace_child(Child, [Ch|Chs]) when Ch#child.name =:= Child#child.name -> 1376 | [Child | Chs]; 1377 | do_replace_child(Child, [Ch|Chs]) -> 1378 | [Ch|do_replace_child(Child, Chs)]. 1379 | 1380 | remove_child(Child, State) -> 1381 | Chs = lists:keydelete(Child#child.name, #child.name, State#state.children), 1382 | State#state{children = Chs}. 1383 | 1384 | %%----------------------------------------------------------------- 1385 | %% Func: do_init/5 1386 | %% Args: SupName = {local, atom()} | {global, atom()} | self 1387 | %% Type = {Strategy, MaxIntensity, Period} 1388 | %% Strategy = one_for_one | one_for_all | simple_one_for_one | 1389 | %% rest_for_one 1390 | %% MaxIntensity = integer() >= 0 1391 | %% Period = integer() > 0 1392 | %% Mod :== atom() 1393 | %% Args :== term() 1394 | %% Purpose: Check that Type is of correct type (!) 1395 | %% Returns: {ok, state()} | Error 1396 | %%----------------------------------------------------------------- 1397 | do_init(SupName, Type, StartSpec, Mod, Args) -> 1398 | case catch init_state(SupName, Type, Mod, Args) of 1399 | {ok, State} when ?is_simple(State) -> 1400 | init_dynamic(State, StartSpec); 1401 | {ok, State} -> 1402 | init_children(State, StartSpec); 1403 | Error -> 1404 | {stop, {supervisor_data, Error}} 1405 | end. 1406 | 1407 | init_state(SupName, {Strategy, MaxIntensity, Period}, Mod, Args) -> 1408 | validStrategy(Strategy), 1409 | validIntensity(MaxIntensity), 1410 | validPeriod(Period), 1411 | {ok, #state{name = supname(SupName,Mod), 1412 | strategy = Strategy, 1413 | intensity = MaxIntensity, 1414 | period = Period, 1415 | module = Mod, 1416 | args = Args}}; 1417 | init_state(_SupName, Type, _, _) -> 1418 | {invalid_type, Type}. 1419 | 1420 | validStrategy(simple_one_for_one) -> true; 1421 | validStrategy(one_for_one) -> true; 1422 | validStrategy(one_for_all) -> true; 1423 | validStrategy(rest_for_one) -> true; 1424 | validStrategy(What) -> throw({invalid_strategy, What}). 1425 | 1426 | validIntensity(Max) when is_integer(Max), 1427 | Max >= 0 -> true; 1428 | validIntensity(What) -> throw({invalid_intensity, What}). 1429 | 1430 | validPeriod(Period) when is_integer(Period), 1431 | Period > 0 -> true; 1432 | validPeriod(What) -> throw({invalid_period, What}). 1433 | 1434 | supname(self, Mod) -> {self(), Mod}; 1435 | supname(N, _) -> N. 1436 | 1437 | %%% ------------------------------------------------------ 1438 | %%% Check that the children start specification is valid. 1439 | %%% Shall be a six (6) tuple 1440 | %%% {Name, Func, RestartType, Shutdown, ChildType, Modules} 1441 | %%% where Name is an atom 1442 | %%% Func is {Mod, Fun, Args} == {atom(), atom(), list()} 1443 | %%% RestartType is permanent | temporary | transient | 1444 | %%% intrinsic | {permanent, Delay} | 1445 | %%% {transient, Delay} | {intrinsic, Delay} 1446 | %% where Delay >= 0 1447 | %%% Shutdown = integer() > 0 | infinity | brutal_kill 1448 | %%% ChildType = supervisor | worker 1449 | %%% Modules = [atom()] | dynamic 1450 | %%% Returns: {ok, [child_rec()]} | Error 1451 | %%% ------------------------------------------------------ 1452 | 1453 | check_startspec(Children) -> check_startspec(Children, []). 1454 | 1455 | check_startspec([ChildSpec|T], Res) -> 1456 | case check_childspec(ChildSpec) of 1457 | {ok, Child} -> 1458 | case lists:keymember(Child#child.name, #child.name, Res) of 1459 | true -> {duplicate_child_name, Child#child.name}; 1460 | false -> check_startspec(T, [Child | Res]) 1461 | end; 1462 | Error -> Error 1463 | end; 1464 | check_startspec([], Res) -> 1465 | {ok, lists:reverse(Res)}. 1466 | 1467 | check_childspec({Name, Func, RestartType, Shutdown, ChildType, Mods}) -> 1468 | catch check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods); 1469 | check_childspec(X) -> {invalid_child_spec, X}. 1470 | 1471 | check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods) -> 1472 | validName(Name), 1473 | validFunc(Func), 1474 | validRestartType(RestartType), 1475 | validChildType(ChildType), 1476 | validShutdown(Shutdown, ChildType), 1477 | validMods(Mods), 1478 | {ok, #child{name = Name, mfargs = Func, restart_type = RestartType, 1479 | shutdown = Shutdown, child_type = ChildType, modules = Mods}}. 1480 | 1481 | validChildType(supervisor) -> true; 1482 | validChildType(worker) -> true; 1483 | validChildType(What) -> throw({invalid_child_type, What}). 1484 | 1485 | validName(_Name) -> true. 1486 | 1487 | validFunc({M, F, A}) when is_atom(M), 1488 | is_atom(F), 1489 | is_list(A) -> true; 1490 | validFunc(Func) -> throw({invalid_mfa, Func}). 1491 | 1492 | validRestartType(permanent) -> true; 1493 | validRestartType(temporary) -> true; 1494 | validRestartType(transient) -> true; 1495 | validRestartType(intrinsic) -> true; 1496 | validRestartType({permanent, Delay}) -> validDelay(Delay); 1497 | validRestartType({intrinsic, Delay}) -> validDelay(Delay); 1498 | validRestartType({transient, Delay}) -> validDelay(Delay); 1499 | validRestartType(RestartType) -> throw({invalid_restart_type, 1500 | RestartType}). 1501 | 1502 | validDelay(Delay) when is_number(Delay), 1503 | Delay >= 0 -> true; 1504 | validDelay(What) -> throw({invalid_delay, What}). 1505 | 1506 | validShutdown(Shutdown, _) 1507 | when is_integer(Shutdown), Shutdown > 0 -> true; 1508 | validShutdown(infinity, _) -> true; 1509 | validShutdown(brutal_kill, _) -> true; 1510 | validShutdown(Shutdown, _) -> throw({invalid_shutdown, Shutdown}). 1511 | 1512 | validMods(dynamic) -> true; 1513 | validMods(Mods) when is_list(Mods) -> 1514 | lists:foreach(fun(Mod) -> 1515 | if 1516 | is_atom(Mod) -> ok; 1517 | true -> throw({invalid_module, Mod}) 1518 | end 1519 | end, 1520 | Mods); 1521 | validMods(Mods) -> throw({invalid_modules, Mods}). 1522 | 1523 | 1524 | %%% ------------------------------------------------------ 1525 | %%% Add a new restart and calculate if the max restart 1526 | %%% intensity has been reached (in that case the supervisor 1527 | %%% shall terminate). 1528 | %%% All restarts accured inside the period amount of seconds 1529 | %%% are kept in the #state.restarts list. 1530 | %%% Returns: {ok, State'} | {terminate, State'} 1531 | %%% ------------------------------------------------------ 1532 | 1533 | add_restart(State) -> 1534 | add_restart(State, _IsCleanRetry = false). 1535 | 1536 | add_restart(State, IsCleanRetry) -> 1537 | MaxR = State#state.intensity, 1538 | P = State#state.period, 1539 | R = State#state.restarts, 1540 | Now = os:timestamp(), 1541 | %% when it is a clean retry do not add it to maxR accumulation. 1542 | %% 1543 | %% NOTE: it may still return {terminate, ...} when for example 1544 | %% delayed retry is too quick, i.e. DelayedSeconds < MaxT 1545 | R1 = case IsCleanRetry of 1546 | true -> delete_old_restarts(R, Now, P); 1547 | false -> delete_old_restarts([Now|R], Now, P) 1548 | end, 1549 | State1 = State#state{restarts = R1}, 1550 | case length(R1) of 1551 | Count when Count =< MaxR -> 1552 | {ok, State1}; 1553 | _ -> 1554 | {terminate, State1} 1555 | end. 1556 | 1557 | delete_old_restarts([], _, _) -> []; 1558 | delete_old_restarts([R|Restarts], Now, Period) -> 1559 | case isInPeriod(R, Now, Period) of 1560 | true -> [R | delete_old_restarts(Restarts, Now, Period)]; 1561 | _ -> [] 1562 | end. 1563 | 1564 | isInPeriod(Time, Now, Period) -> 1565 | (timer:now_diff(Now, Time) div 1000000) =< Period. 1566 | 1567 | %%% ------------------------------------------------------ 1568 | %%% Error and progress reporting. 1569 | %%% ------------------------------------------------------ 1570 | 1571 | report_error(Error, Reason, Child, SupName) -> 1572 | ErrorMsg = [{supervisor, SupName}, 1573 | {errorContext, Error}, 1574 | {reason, Reason}, 1575 | {offender, extract_child(Child)}], 1576 | error_logger:error_report(supervisor_report, ErrorMsg). 1577 | 1578 | 1579 | extract_child(Child) when is_list(Child#child.pid) -> 1580 | [{nb_children, length(Child#child.pid)}, 1581 | {id, Child#child.name}, 1582 | {mfargs, Child#child.mfargs}, 1583 | {restart_type, Child#child.restart_type}, 1584 | {shutdown, Child#child.shutdown}, 1585 | {child_type, Child#child.child_type}]; 1586 | extract_child(Child) -> 1587 | [{pid, Child#child.pid}, 1588 | {id, Child#child.name}, 1589 | {mfargs, Child#child.mfargs}, 1590 | {restart_type, Child#child.restart_type}, 1591 | {shutdown, Child#child.shutdown}, 1592 | {child_type, Child#child.child_type}]. 1593 | 1594 | report_progress(Child, SupName) -> 1595 | Progress = [{supervisor, SupName}, 1596 | {started, extract_child(Child)}], 1597 | error_logger:info_report(progress, Progress). 1598 | --------------------------------------------------------------------------------