aboutsummaryrefslogtreecommitdiffstats
path: root/lib/sasl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/sasl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/sasl')
-rw-r--r--lib/sasl/AUTHORS11
-rw-r--r--lib/sasl/Makefile37
-rw-r--r--lib/sasl/doc/html/.gitignore0
-rw-r--r--lib/sasl/doc/man3/.gitignore0
-rw-r--r--lib/sasl/doc/man4/.gitignore0
-rw-r--r--lib/sasl/doc/man6/.gitignore0
-rw-r--r--lib/sasl/doc/pdf/.gitignore0
-rw-r--r--lib/sasl/doc/src/Makefile133
-rw-r--r--lib/sasl/doc/src/alarm_handler.xml125
-rw-r--r--lib/sasl/doc/src/appup.xml330
-rw-r--r--lib/sasl/doc/src/book.xml49
-rw-r--r--lib/sasl/doc/src/error_logging.xml385
-rw-r--r--lib/sasl/doc/src/fascicules.xml18
-rw-r--r--lib/sasl/doc/src/make.dep22
-rw-r--r--lib/sasl/doc/src/note.gifbin0 -> 1539 bytes
-rw-r--r--lib/sasl/doc/src/notes.xml319
-rw-r--r--lib/sasl/doc/src/notes_history.xml128
-rw-r--r--lib/sasl/doc/src/overload.xml151
-rw-r--r--lib/sasl/doc/src/part.xml38
-rw-r--r--lib/sasl/doc/src/part_notes.xml38
-rw-r--r--lib/sasl/doc/src/part_notes_history.xml38
-rw-r--r--lib/sasl/doc/src/rb.xml206
-rw-r--r--lib/sasl/doc/src/ref_man.xml46
-rw-r--r--lib/sasl/doc/src/rel.xml105
-rw-r--r--lib/sasl/doc/src/rel/bar.1.erl17
-rw-r--r--lib/sasl/doc/src/rel/bar.2.erl13
-rw-r--r--lib/sasl/doc/src/rel/ge_h.1.erl28
-rw-r--r--lib/sasl/doc/src/rel/ge_h.2.erl31
-rw-r--r--lib/sasl/doc/src/rel/gs1.1.erl30
-rw-r--r--lib/sasl/doc/src/rel/gs1.2.erl35
-rw-r--r--lib/sasl/doc/src/rel/gs1.3.erl36
-rw-r--r--lib/sasl/doc/src/rel/gs2.1.erl31
-rw-r--r--lib/sasl/doc/src/rel/gs2.2.erl38
-rw-r--r--lib/sasl/doc/src/rel/lists2.1.erl8
-rw-r--r--lib/sasl/doc/src/rel/lists2.2.erl13
-rw-r--r--lib/sasl/doc/src/rel/portc.1.erl31
-rw-r--r--lib/sasl/doc/src/rel/portc.2.erl34
-rw-r--r--lib/sasl/doc/src/rel/sp.1.erl46
-rw-r--r--lib/sasl/doc/src/rel/sp.2.erl58
-rw-r--r--lib/sasl/doc/src/rel/sup.1.erl11
-rw-r--r--lib/sasl/doc/src/rel/sup.2.erl10
-rw-r--r--lib/sasl/doc/src/release_handler.xml697
-rw-r--r--lib/sasl/doc/src/relup.xml93
-rw-r--r--lib/sasl/doc/src/sasl_app.xml182
-rw-r--r--lib/sasl/doc/src/sasl_intro.xml51
-rw-r--r--lib/sasl/doc/src/script.xml168
-rw-r--r--lib/sasl/doc/src/systools.xml362
-rw-r--r--lib/sasl/doc/src/warning.gifbin0 -> 1498 bytes
-rw-r--r--lib/sasl/ebin/.gitignore0
-rw-r--r--lib/sasl/include/.gitignore0
-rw-r--r--lib/sasl/info3
-rw-r--r--lib/sasl/priv/mibs/.gitignore0
-rw-r--r--lib/sasl/priv/test/.gitignore0
-rw-r--r--lib/sasl/src/Makefile100
-rw-r--r--lib/sasl/src/alarm_handler.erl95
-rw-r--r--lib/sasl/src/erlsrv.erl420
-rw-r--r--lib/sasl/src/format_lib_supp.erl224
-rw-r--r--lib/sasl/src/misc_supp.erl106
-rw-r--r--lib/sasl/src/overload.erl224
-rw-r--r--lib/sasl/src/rb.erl697
-rw-r--r--lib/sasl/src/rb_format_supp.erl155
-rw-r--r--lib/sasl/src/release_handler.erl1906
-rw-r--r--lib/sasl/src/release_handler_1.erl647
-rw-r--r--lib/sasl/src/sasl.app.src46
-rw-r--r--lib/sasl/src/sasl.appup.src25
-rw-r--r--lib/sasl/src/sasl.erl162
-rw-r--r--lib/sasl/src/sasl_report.erl135
-rw-r--r--lib/sasl/src/sasl_report_file_h.erl60
-rw-r--r--lib/sasl/src/sasl_report_tty_h.erl50
-rw-r--r--lib/sasl/src/si.erl168
-rw-r--r--lib/sasl/src/si_sasl_supp.erl373
-rw-r--r--lib/sasl/src/systools.erl109
-rw-r--r--lib/sasl/src/systools.hrl71
-rw-r--r--lib/sasl/src/systools_lib.erl219
-rw-r--r--lib/sasl/src/systools_make.erl2155
-rw-r--r--lib/sasl/src/systools_rc.erl1044
-rw-r--r--lib/sasl/src/systools_relup.erl560
-rw-r--r--lib/sasl/vsn.mk1
78 files changed, 13957 insertions, 0 deletions
diff --git a/lib/sasl/AUTHORS b/lib/sasl/AUTHORS
new file mode 100644
index 0000000000..1f15a83885
--- /dev/null
+++ b/lib/sasl/AUTHORS
@@ -0,0 +1,11 @@
+Original Authors and Contributors:
+
+Martin Bj�rklund
+Magnus Fr�berg
+Joe Armstrong
+Peter Lundell
+Esko Vierum�ki
+Patrik Nyblom
+Peter H�gfeldt
+Hans Bolinder
+Gunilla Arendt
diff --git a/lib/sasl/Makefile b/lib/sasl/Makefile
new file mode 100644
index 0000000000..2affcf1e40
--- /dev/null
+++ b/lib/sasl/Makefile
@@ -0,0 +1,37 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+#
+# Macros
+#
+
+SUB_DIRECTORIES = src doc/src
+
+include vsn.mk
+VSN = $(SASL_VSN)
+
+SPECIAL_TARGETS =
+
+#
+# Default Subdir Targets
+#
+include $(ERL_TOP)/make/otp_subdir.mk
+
diff --git a/lib/sasl/doc/html/.gitignore b/lib/sasl/doc/html/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/sasl/doc/html/.gitignore
diff --git a/lib/sasl/doc/man3/.gitignore b/lib/sasl/doc/man3/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/sasl/doc/man3/.gitignore
diff --git a/lib/sasl/doc/man4/.gitignore b/lib/sasl/doc/man4/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/sasl/doc/man4/.gitignore
diff --git a/lib/sasl/doc/man6/.gitignore b/lib/sasl/doc/man6/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/sasl/doc/man6/.gitignore
diff --git a/lib/sasl/doc/pdf/.gitignore b/lib/sasl/doc/pdf/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/sasl/doc/pdf/.gitignore
diff --git a/lib/sasl/doc/src/Makefile b/lib/sasl/doc/src/Makefile
new file mode 100644
index 0000000000..eb880ccb78
--- /dev/null
+++ b/lib/sasl/doc/src/Makefile
@@ -0,0 +1,133 @@
+# ``The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved via the world wide web at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+# AB. All Rights Reserved.''
+#
+# $Id$
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+VSN=$(SASL_VSN)
+APPLICATION=sasl
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+XML_APPLICATION_FILES = ref_man.xml
+XML_REF3_FILES = alarm_handler.xml \
+ overload.xml \
+ rb.xml \
+ release_handler.xml \
+ systools.xml
+
+XML_REF4_FILES = appup.xml rel.xml relup.xml script.xml
+
+XML_REF6_FILES = sasl_app.xml
+
+XML_PART_FILES = part.xml part_notes.xml part_notes_history.xml
+XML_CHAPTER_FILES = sasl_intro.xml \
+ error_logging.xml \
+ notes.xml \
+ notes_history.xml
+
+BOOK_FILES = book.xml
+
+GIF_FILES = \
+ note.gif \
+ warning.gif
+
+XML_FILES = \
+ $(BOOK_FILES) $(XML_CHAPTER_FILES) \
+ $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_REF4_FILES) \
+ $(XML_REF6_FILES) $(XML_APPLICATION_FILES)
+
+
+# ----------------------------------------------------
+
+HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
+
+INFO_FILE = ../../info
+
+MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
+MAN4_FILES = $(XML_REF4_FILES:%.xml=$(MAN4DIR)/%.4)
+MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
+
+HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
+
+TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+XML_FLAGS +=
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+$(HTMLDIR)/%.gif: %.gif
+ $(INSTALL_DATA) $< $@
+
+docs: pdf html man
+
+$(TOP_PDF_FILE): $(XML_FILES)
+
+pdf: $(TOP_PDF_FILE)
+
+html: gifs $(HTML_REF_MAN_FILE)
+
+man: $(MAN3_FILES) $(MAN4_FILES) $(MAN6_FILES)
+
+gifs: $(GIF_FILES:%=$(HTMLDIR)/%) # We depend just to copy them to ../html
+
+debug opt:
+
+clean clean_docs:
+ rm -rf $(HTMLDIR)/*
+ rm -f $(MAN3DIR)/*
+ rm -f $(MAN4DIR)/*
+ rm -f $(MAN6DIR)/*
+ rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ rm -f errs core *~
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_docs_spec: docs
+ $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
+ $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
+ $(INSTALL_DIR) $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(HTMLDIR)/* \
+ $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
+ $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
+ $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
+ $(INSTALL_DIR) $(RELEASE_PATH)/man/man4
+ $(INSTALL_DATA) $(MAN4_FILES) $(RELEASE_PATH)/man/man4
+ $(INSTALL_DIR) $(RELEASE_PATH)/man/man6
+ $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6
+
+release_spec:
+
diff --git a/lib/sasl/doc/src/alarm_handler.xml b/lib/sasl/doc/src/alarm_handler.xml
new file mode 100644
index 0000000000..e4501ce5f0
--- /dev/null
+++ b/lib/sasl/doc/src/alarm_handler.xml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>1996</year>
+ <year>2007</year>
+ <holder>Ericsson AB, All Rights Reserved</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>alarm_handler</title>
+ <prepared>Martin Bj&ouml;rklund</prepared>
+ <responsible>Bjarne Dacker</responsible>
+ <docno></docno>
+ <approved>Bjarne D&auml;cker</approved>
+ <checked>Martin Bj&ouml;rklund</checked>
+ <date>1996-09-10</date>
+ <rev>A</rev>
+ <file>alarm_handler.sgml.t1</file>
+ </header>
+ <module>alarm_handler</module>
+ <modulesummary>An Alarm Handling Process</modulesummary>
+ <description>
+ <p>The alarm handler process is a <c>gen_event</c> event manager
+ process which receives alarms in the system. This process is not
+ intended to be a complete alarm handler. It defines a
+ place to which alarms can be sent. One simple event handler is
+ installed in the alarm handler at start-up, but users are
+ encouraged to write and install their own handlers.
+ </p>
+ <p>The simple event handler sends all alarms as info reports to
+ the error logger, and saves all of them in a list which can be
+ passed to a user defined event handler, which may be installed at
+ a later stage. The list can grow large if many alarms are
+ generated. So it is a good reason to install a better user defined
+ handler.
+ </p>
+ <p>There are functions to set and clear alarms. The format of
+ alarms are defined by the user. For example, an event handler
+ for SNMP could be defined, together with an alarm MIB.
+ </p>
+ <p>The alarm handler is part of the SASL application.
+ </p>
+ <p>When writing new event handlers for the alarm handler, the
+ following events must be handled:
+ </p>
+ <taglist>
+ <tag><c>{set_alarm, {AlarmId, AlarmDescr}}</c></tag>
+ <item>
+ <p>This event is generated by
+ <c>alarm_handler:set_alarm({AlarmId, AlarmDecsr})</c>.
+ </p>
+ </item>
+ <tag><c>{clear_alarm, AlarmId}</c></tag>
+ <item>
+ <p>This event is
+ generated by <c>alarm_handler:clear_alarm(AlarmId)</c>.
+ </p>
+ </item>
+ </taglist>
+ <p>The default simple handler is called <c>alarm_handler</c> and
+ it may be exchanged by calling <c>gen_event:swap_handler/3</c>
+ as <c>gen_event:swap_handler(alarm_handler, {alarm_handler, swap}, {NewHandler, Args})</c>. <c>NewHandler:init({Args, {alarm_handler, Alarms}})</c> is called. Refer to gen_event(3)
+ for further details.
+ </p>
+ </description>
+ <funcs>
+ <func>
+ <name>clear_alarm(AlarmId) -> void()</name>
+ <fsummary>Clear the specified alarms</fsummary>
+ <type>
+ <v>AlarmId = term()</v>
+ </type>
+ <desc>
+ <p>Clears all alarms with id <c>AlarmId</c>.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>get_alarms() -> [alarm()]</name>
+ <fsummary>Get all active alarms</fsummary>
+ <desc>
+ <p>Returns a list of all active alarms. This function can only
+ be used when the simple handler is installed.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>set_alarm(alarm())</name>
+ <fsummary>Set an alarm with an id</fsummary>
+ <type>
+ <v>alarm() = {AlarmId, AlarmDescription}</v>
+ <v>AlarmId = term()</v>
+ <v>AlarmDescription = term()</v>
+ </type>
+ <desc>
+ <p>Sets an alarm with id <c>AlarmId</c>. This id is used at a
+ later stage when the alarm is cleared.
+ </p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>See Also</title>
+ <p>error_logger(3), gen_event(3)
+ </p>
+ </section>
+</erlref>
+
diff --git a/lib/sasl/doc/src/appup.xml b/lib/sasl/doc/src/appup.xml
new file mode 100644
index 0000000000..5182889710
--- /dev/null
+++ b/lib/sasl/doc/src/appup.xml
@@ -0,0 +1,330 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE fileref SYSTEM "fileref.dtd">
+
+<fileref>
+ <header>
+ <copyright>
+ <year>1997</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>appup</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <file>appup</file>
+ <filesummary>Application upgrade file.</filesummary>
+ <description>
+ <p>The <em>application upgrade file</em> defines how an application
+ is upgraded or downgraded in a running system.</p>
+ <p>This file is used by the functions in <c>systools</c> when
+ generating a release upgrade file <c>relup</c>.</p>
+ </description>
+
+ <section>
+ <title>FILE SYNTAX</title>
+ <p>The application upgrade file should be called
+ <c>Application.appup</c> where <c>Application</c> is the name of
+ the application. The file should be located in the <c>ebin</c>
+ directory for the application.</p>
+ <p>The <c>.appup</c> file contains one single Erlang term, which
+ defines the instructions used to upgrade or downgrade
+ the application. The file has the following syntax:</p>
+ <code type="none">
+{Vsn,
+ [{UpFromVsn, Instructions}, ...],
+ [{DownToVsn, Instructions}, ...]}.
+ </code>
+ <list type="bulleted">
+ <item>
+ <p><c>Vsn = string()</c> is the current version of
+ the application.</p>
+ </item>
+ <item>
+ <p><c>UpFromVsn = string()</c> is an earlier version of
+ the application to upgrade from.</p>
+ </item>
+ <item>
+ <p><c>DownToVsn = string()</c> is an earlier version of
+ the application to downgrade to.</p>
+ </item>
+ <item>
+ <p><c>Instructions</c> is a list of <em>release upgrade instructions</em>, see below. It is recommended to use
+ high-level instructions only. These are automatically
+ translated to low-level instructions by <c>systools</c> when
+ creating the <c>relup</c> file.</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>RELEASE UPGRADE INSTRUCTIONS</title>
+ <p>Release upgrade instructions are interpreted by the release
+ handler when an upgrade or downgrade is made. For more
+ information about release handling, refer to <em>OTP Design Principles</em>.</p>
+ <p>A process is said to <em>use</em> a module <c>Mod</c>, if
+ <c>Mod</c> is listed in the <c>Modules</c> part of the child
+ specification used to start the process, see <c>supervisor(3)</c>.
+ In the case of gen_event, an event manager process is said to use
+ <c>Mod</c> if <c>Mod</c> is an installed event handler.</p>
+ <p><em>High-level instructions</em></p>
+ <pre>
+{update, Mod}
+{update, Mod, supervisor}
+{update, Mod, Change}
+{update, Mod, DepMods}
+{update, Mod, Change, DepMods}
+{update, Mod, Change, PrePurge, PostPurge, DepMods}
+{update, Mod, Timeout, Change, PrePurge, PostPurge, DepMods}
+{update, Mod, ModType, Timeout, Change, PrePurge, PostPurge, DepMods}
+ Mod = atom()
+ ModType = static | dynamic
+ Timeout = int()>0 | default | infinity
+ Change = soft | {advanced,Extra}
+ Extra = term()
+ PrePurge = PostPurge = soft_purge | brutal_purge
+ DepMods = [Mod]
+ </pre>
+ <p>Synchronized code replacement of processes using the module
+ <c>Mod</c>. All those processes are suspended using
+ <c>sys:suspend</c>, the new version of the module is loaded and
+ then the processes are resumed using <c>sys:resume</c>.</p>
+ <p><c>Change</c> defaults to <c>soft</c> and defines the type of
+ code change. If it is set to <c>{advanced,Extra}</c>, processes
+ implemented using gen_server, gen_fsm or gen_event will transform
+ their internal state by calling the callback function
+ <c>code_change</c>. Special processes will call the callback
+ function <c>system_code_change/4</c>. In both cases, the term
+ <c>Extra</c> is passed as an argument to the callback function.</p>
+ <p><c>PrePurge</c> defaults to <c>brutal_purge</c> and controls
+ what action to take with processes that are executing old code
+ before loading the new version of the module. If the value
+ is <c>brutal_purge</c>, the processes are killed. If the value is
+ <c>soft_purge</c>, <c>release_handler:install_release/1</c>
+ returns <c>{error,{old_processes,Mod}}</c>.</p>
+ <p><c>PostPurge</c> defaults to <c>brutal_purge</c> and controls
+ what action to take with processes that are executing old code
+ when the new version of the module has been loaded. If the value
+ is <c>brutal_purge</c>, the code is purged when the release is
+ made permanent and the processes are killed. If the value is
+ <c>soft_purge</c>, the release handler will purge the old code
+ when no remaining processes execute the code.</p>
+ <p><c>DepMods</c> defaults to [] and defines which other modules
+ <c>Mod</c> is dependent on. In <c>relup</c>, instructions for
+ suspending processes using <c>Mod</c> will come before
+ instructions for suspending processes using modules in
+ <c>DepMods</c> when upgrading, and vice versa when downgrading.
+ In case of circular dependencies, the order of the instructions in
+ the <c>appup</c> script is kept.</p>
+ <p><c>Timeout</c> defines the timeout when suspending processes.
+ If no value or <c>default</c> is given, the default value for
+ <c>sys:suspend</c> is used.</p>
+ <p><c>ModType</c> defaults to <c>dynamic</c> and specifies if
+ the code is "dynamic", that is if a process using the module does
+ spontaneously switch to new code, or if it is "static".
+ When doing an advanced update and upgrading, the new version of a
+ dynamic module is loaded before the process is asked to change
+ code. When downgrading, the process is asked to change code before
+ loading the new version. For static modules, the new version is
+ loaded before the process is asked to change code, both in
+ the case of upgrading and downgrading. Callback modules are
+ dynamic.</p>
+ <p><c>update</c> with argument <c>supervisor</c> is used when
+ changing the start specification of a supervisor.</p>
+ <pre>
+{load_module, Mod}
+{load_module, Mod, DepMods}
+{load_module, Mod, PrePurge, PostPurge, DepMods}
+ Mod = atom()
+ PrePurge = PostPurge = soft_purge | brutal_purge
+ DepMods = [Mod]
+ </pre>
+ <p>Simple code replacement of the module <c>Mod</c>.</p>
+ <p>See <c>update</c> above for a description of <c>PrePurge</c> and
+ <c>PostPurge</c>.</p>
+ <p><c>DepMods</c> defaults to [] and defines which other modules
+ <c>Mod</c> is dependent on. In <c>relup</c>, instructions for
+ loading these modules will come before the instruction for loading
+ <c>Mod</c> when upgrading, and vice versa when downgrading.</p>
+ <pre>
+{add_module, Mod}
+ Mod = atom()
+ </pre>
+ <p>Loads a new module <c>Mod</c>.</p>
+ <pre>
+{delete_module, Mod}
+ Mod = atom()
+ </pre>
+ <p>Deletes a module <c>Mod</c> using the low-level instructions
+ <c>remove</c> and <c>purge</c>.</p>
+ <pre>
+{add_application, Application}
+ Application = atom()
+ </pre>
+ <p>Adding an application means that the modules defined by
+ the <c>modules</c> key in the <c>.app</c> file are loaded using
+ <c>add_module</c>, then the application is started.</p>
+ <pre>
+{remove_application, Application}
+ Application = atom()
+ </pre>
+ <p>Removing an application means that the application is stopped,
+ the modules are unloaded using <c>delete_module</c> and then
+ the application specification is unloaded from the application
+ controller.</p>
+ <pre>
+{restart_application, Application}
+ Application = atom()
+ </pre>
+ <p>Restarting an application means that the application is
+ stopped and then started again similar to using the instructions
+ <c>remove_application</c> and <c>add_application</c> in sequence.</p>
+ <p><em>Low-level instructions</em></p>
+ <pre>
+{load_object_code, {App, Vsn, [Mod]}}
+ App = Mod = atom()
+ Vsn = string()
+ </pre>
+ <p>Reads each <c>Mod</c> from the directory <c>App-Vsn/ebin</c> as
+ a binary. It does not load the modules. The instruction should be
+ placed first in the script in order to read all new code from file
+ to make the suspend-load-resume cycle less time consuming. After
+ this instruction has been executed, the code server with the new
+ version of <c>App</c>.</p>
+ <pre>
+point_of_no_return
+ </pre>
+ <p>If a crash occurs after this instruction, the system cannot
+ recover and is restarted from the old version of the release.
+ The instruction must only occur once in a script. It should be
+ placed after all <c>load_object_code</c> instructions.</p>
+ <pre>
+{load, {Mod, PrePurge, PostPurge}}
+ Mod = atom()
+ PrePurge = PostPurge = soft_purge | brutal_purge
+ </pre>
+ <p>Before this instruction occurs, <c>Mod</c> must have been loaded
+ using <c>load_object_code</c>. This instruction loads the module.
+ <c>PrePurge</c> is ignored. See the high-level instruction
+ <c>update</c> for a description of <c>PostPurge</c>.</p>
+ <pre>
+{remove, {Mod, PrePurge, PostPurge}}
+ Mod = atom()
+ PrePurge = PostPurge = soft_purge | brutal_purge
+ </pre>
+ <p>Makes the current version of <c>Mod</c> old.
+ <c>PrePurge</c> is ignored. See the high-level instruction
+ <c>update</c> for a description of <c>PostPurge</c>.</p>
+ <pre>
+{purge, [Mod]}
+ Mod = atom()
+ </pre>
+ <p>Purges each module <c>Mod</c>, that is removes the old code.
+ Note that any process executing purged code is killed.</p>
+ <pre>
+{suspend, [Mod | {Mod, Timeout}]}
+ Mod = atom()
+ Timeout = int()>0 | default | infinity
+ </pre>
+ <p>Tries to suspend all processes using a module <c>Mod</c>. If a
+ process does not respond, it is ignored. This may cause
+ the process to die, either because it crashes when it
+ spontaneously switches to new code, or as a result of a purge
+ operation. If no <c>Timeout</c> is specified or <c>default</c> is
+ given, the default value for <c>sys:suspend</c> is used.</p>
+ <pre>
+{resume, [Mod]}
+ Mod = atom()
+ </pre>
+ <p>Resumes all suspended processes using a module <c>Mod</c>.</p>
+ <pre>
+{code_change, [{Mod, Extra}]}
+{code_change, Mode, [{Mod, Extra}]}
+ Mod = atom()
+ Mode = up | down
+ Extra = term()
+ </pre>
+ <p><c>Mode</c> defaults to <c>up</c> and specifies if it is an
+ upgrade or downgrade.</p>
+ <p>This instruction sends a <c>code_change</c> system message to
+ all processes using a module <c>Mod</c> by calling the function
+ <c>sys:change_code</c>, passing the term <c>Extra</c> as argument.</p>
+ <pre>
+{stop, [Mod]}
+ Mod = atom()
+ </pre>
+ <p>Stops all processes using a module <c>Mod</c> by calling
+ <c>supervisor:terminate_child/2</c>. The instruction is useful
+ when the simplest way to change code is to stop and restart the
+ processes which run the code.</p>
+ <pre>
+{start, [Mod]}
+ Mod = atom()
+ </pre>
+ <p>Starts all stopped processes using a module <c>Mod</c> by calling
+ <c>supervisor:restart_child/2</c>.</p>
+ <pre>
+{sync_nodes, Id, [Node]}
+{sync_nodes, Id, {M, F, A}}
+ Id = term()
+ Node = node()
+ M = F = atom()
+ A = [term()]
+ </pre>
+ <p><c>apply(M, F, A)</c> must return a list of nodes.</p>
+ <p>The instruction synchronizes the release installation with other
+ nodes. Each <c>Node</c> must evaluate this command, with the same
+ <c>Id</c>. The local node waits for all other nodes to evaluate
+ the instruction before execution continues. In case a node goes
+ down, it is considered to be an unrecoverable error, and
+ the local node is restarted from the old release. There is no
+ timeout for this instruction, which means that it may hang
+ forever.</p>
+ <pre>
+{apply, {M, F, A}}
+ M = F = atom()
+ A = [term()]
+ </pre>
+ <p>Evaluates <c>apply(M, F, A)</c>. If the instruction appears
+ before the <c>point_of_no_return</c> instruction, a failure is
+ caught. <c>release_handler:install_release/1</c> then returns
+ <c>{error,{'EXIT',Reason}}</c>, unless <c>{error,Error}</c> is
+ thrown or returned. Then it returns <c>{error,Error}</c>.</p>
+ <p>If the instruction appears after the <c>point_of_no_return</c>
+ instruction, and the function call fails, the system is
+ restarted.</p>
+ <pre>
+restart_new_emulator
+ </pre>
+ <p>Shuts down the current emulator and starts a ne one. All
+ processes are terminated gracefully. The new release must still
+ be made permanent when the new emulator is up and running.
+ Otherwise, the old emulator is started in case of a emulator
+ restart. This instruction should be used when a new emulator is
+ introduced, or if a complete reboot of the system should be done.</p>
+ </section>
+
+ <section>
+ <title>SEE ALSO</title>
+ <p><seealso marker="relup">relup(4)</seealso>,
+ <seealso marker="release_handler">release_handler(3)</seealso>,
+ supervisor(3),
+ <seealso marker="systools">systools(3)</seealso></p>
+ </section>
+</fileref>
+
diff --git a/lib/sasl/doc/src/book.xml b/lib/sasl/doc/src/book.xml
new file mode 100644
index 0000000000..de6a533636
--- /dev/null
+++ b/lib/sasl/doc/src/book.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE book SYSTEM "book.dtd">
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header titlestyle="normal">
+ <copyright>
+ <year>1997</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>System Application Support Libraries (SASL)</title>
+ <prepared>OTP Team</prepared>
+ <docno></docno>
+ <date>1999-04-22</date>
+ <rev>B</rev>
+ <file>book.xml</file>
+ </header>
+ <insidecover>
+ </insidecover>
+ <pagetext>System Application Support Libraries (SASL)</pagetext>
+ <preamble>
+ <contents level="2"></contents>
+ </preamble>
+ <parts lift="yes">
+ <xi:include href="part.xml"/>
+ </parts>
+ <applications>
+ <xi:include href="ref_man.xml"/>
+ </applications>
+ <releasenotes>
+ <xi:include href="notes.xml"/>
+ </releasenotes>
+ <listofterms></listofterms>
+ <index></index>
+</book>
+
diff --git a/lib/sasl/doc/src/error_logging.xml b/lib/sasl/doc/src/error_logging.xml
new file mode 100644
index 0000000000..5707bc4d69
--- /dev/null
+++ b/lib/sasl/doc/src/error_logging.xml
@@ -0,0 +1,385 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>1997</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>SASL Error Logging</title>
+ <prepared>Magnus Fr&ouml;berg</prepared>
+ <responsible>Bjarne D&auml;cker</responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>1999-04-13</date>
+ <rev>B</rev>
+ <file>error_logging.xml</file>
+ </header>
+ <p>The SASL application introduces three types of reports:</p>
+ <list type="bulleted">
+ <item>supervisor report</item>
+ <item>progress report</item>
+ <item>crash report.</item>
+ </list>
+ <p>When the SASL application is started, it adds a handler that
+ formats and writes these reports, as specified in the
+ configuration parameters for sasl, i.e the environment variables
+ in the SASL application specification, which is found in the
+ <c>.app</c> file of SASL. See
+ <seealso marker="sasl_app">sasl(Application)</seealso>, and app(File)
+ in the Kernel Reference Manual
+ for the details.</p>
+
+ <section>
+ <title>Supervisor Report</title>
+ <p>A supervisor report is issued when a supervised child terminates in
+ an unexpected way. A supervisor report contains the following
+ items:</p>
+ <taglist>
+ <tag>Supervisor.</tag>
+ <item>The name of the reporting supervisor.</item>
+ <tag>Context.</tag>
+ <item>Indicates in which phase the child terminated
+ from the supervisor's point of view. This can be
+ <c>start_error</c>, <c>child_terminated</c>, or
+ <c>shutdown_error</c>.</item>
+ <tag>Reason.</tag>
+ <item>The termination reason.</item>
+ <tag>Offender.</tag>
+ <item>The start specification for the child.</item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>Progress Report</title>
+ <p>A progress report is issued whenever a supervisor starts or
+ restarts. A progress report contains the following items:</p>
+ <taglist>
+ <tag>Supervisor.</tag>
+ <item>The name of the reporting supervisor.</item>
+ <tag>Started.</tag>
+ <item>The start specification for the successfully
+ started child.</item>
+ </taglist>
+ <marker id="CRASH"></marker>
+ </section>
+
+ <section>
+ <title>Crash Report</title>
+ <p>Processes started with the <c>proc_lib:spawn</c> or
+ <c>proc_lib:spawn_link</c> functions are wrapped within a
+ <c>catch</c>. A crash report is issued whenever such a process
+ terminates with an unexpected reason, which is any reason other
+ than <c>normal</c> or <c>shutdown</c>. Processes using the
+ <c>gen_server</c> and <c>gen_fsm</c> behaviours are examples of
+ such processes. A crash report contains the following items:</p>
+ <taglist>
+ <tag>Crasher.</tag>
+ <item>Information about the crashing process is reported, such
+ as initial function call, exit reason, and message queue.</item>
+ <tag>Neighbours.</tag>
+ <item>Information about processes which are linked to the crashing
+ process and do not trap exits. These processes are the
+ neighbours which will terminate because of this process
+ crash. The information gathered is the same as the information
+ for Crasher, shown in the previous item.</item>
+ </taglist>
+
+ <section>
+ <title>An Example</title>
+ <p>The following example shows the reports which are generated
+ when a process crashes. The example process is an
+ <c>permanent</c> process supervised by the <c>test_sup</c>
+ supervisor. A division by zero is executed and the error is
+ first reported by the faulty process. A crash report is
+ generated as the process was started using the
+ <c>proc_lib:spawn/3</c> function. The supervisor generates a
+ supervisor report showing the process that has crashed, and then a
+ progress report is generated when the process is finally
+ re-started.</p>
+ <pre>
+ =ERROR REPORT==== 27-May-1996::13:38:56 ===
+ &lt;0.63.0>: Divide by zero !
+
+ =CRASH REPORT==== 27-May-1996::13:38:56 ===
+ crasher:
+ pid: &lt;0.63.0>
+ registered_name: []
+ error_info: {badarith,{test,s,[]}}
+ initial_call: {test,s,[]}
+ ancestors: [test_sup,&lt;0.46.0>]
+ messages: []
+ links: [&lt;0.47.0>]
+ dictionary: []
+ trap_exit: false
+ status: running
+ heap_size: 128
+ stack_size: 128
+ reductions: 348
+ neighbours:
+
+ =SUPERVISOR REPORT==== 27-May-1996::13:38:56 ===
+ Supervisor: {local,test_sup}
+ Context: child_terminated
+ Reason: {badarith,{test,s,[]}}
+ Offender: [{pid,&lt;0.63.0>},
+ {name,test},
+ {mfa,{test,t,[]}},
+ {restart_type,permanent},
+ {shutdown,200},
+ {child_type,worker}]
+
+
+ =PROGRESS REPORT==== 27-May-1996::13:38:56 ===
+ Supervisor: {local,test_sup}
+ Started: [{pid,&lt;0.64.0>},
+ {name,test},
+ {mfa,{test,t,[]}},
+ {restart_type,permanent},
+ {shutdown,200},
+ {child_type,worker}]
+ </pre>
+ </section>
+ </section>
+
+ <section>
+ <title>Multi-File Error Report Logging</title>
+ <p>Multi-file error report logging is used to store error messages,
+ which are received by the <c>error_logger</c>. The error messages
+ are stored in several files and each file is smaller than a
+ specified amount of kilobytes, and no more than a specified number
+ of files exist at the same time. The logging is very fast because
+ each error message is written as a binary term.</p>
+ <p>Refer to
+ <c>sasl</c> application in the Reference Manual for more details.</p>
+ </section>
+
+ <section>
+ <title>Report Browser</title>
+ <p>The report browser is used to browse and format error reports
+ written by the error logger handler <c>error_logger_mf_h</c>.</p>
+ <p>The <c>error_logger_mf_h</c> handler writes all reports to a
+ report logging directory. This directory is specified when
+ configuring the SASL application.</p>
+ <p>If the report browser is
+ used off-line, the reports can be copied to another directory
+ which is specified when starting the browser. If no such directory
+ is specified, the browser reads reports from the SASL
+ <c>error_logger_mf_dir</c>.</p>
+
+ <section>
+ <title>Starting the Report Browser</title>
+ <p>Start the <c>rb_server</c> with the function
+ <c>rb:start([Options])</c> as shown in the following
+ example:</p>
+ <pre>
+
+ 5><input>rb:start([{max, 20}]).</input>
+ rb: reading report...done.
+ rb: reading report...done.
+ rb: reading report...done.
+ rb: reading report...done.
+ </pre>
+ </section>
+
+ <section>
+ <title>On-line Help</title>
+ <p>Enter the command <em>rb:help().</em> to access the report
+ browser on-line help system.</p>
+ </section>
+
+ <section>
+ <title>List Reports in the Server</title>
+ <p>The function <c>rb:list()</c> lists all loaded reports:</p>
+ <pre>
+
+ 4><input>rb:list().</input>
+ No Type Process Date Time
+ == ==== ======= ==== ====
+ 20 progress &lt;0.17.0> 1996-10-16 16:14:54
+ 19 progress &lt;0.14.0> 1996-10-16 16:14:55
+ 18 error &lt;0.15.0> 1996-10-16 16:15:02
+ 17 progress &lt;0.14.0> 1996-10-16 16:15:06
+ 16 progress &lt;0.38.0> 1996-10-16 16:15:12
+ 15 progress &lt;0.17.0> 1996-10-16 16:16:14
+ 14 progress &lt;0.17.0> 1996-10-16 16:16:14
+ 13 progress &lt;0.17.0> 1996-10-16 16:16:14
+ 12 progress &lt;0.14.0> 1996-10-16 16:16:14
+ 11 error &lt;0.17.0> 1996-10-16 16:16:21
+ 10 error &lt;0.17.0> 1996-10-16 16:16:21
+ 9 crash_report release_handler 1996-10-16 16:16:21
+ 8 supervisor_report &lt;0.17.0> 1996-10-16 16:16:21
+ 7 progress &lt;0.17.0> 1996-10-16 16:16:21
+ 6 progress &lt;0.17.0> 1996-10-16 16:16:36
+ 5 progress &lt;0.17.0> 1996-10-16 16:16:36
+ 4 progress &lt;0.17.0> 1996-10-16 16:16:36
+ 3 progress &lt;0.14.0> 1996-10-16 16:16:36
+ 2 error &lt;0.15.0> 1996-10-16 16:17:04
+ 1 progress &lt;0.14.0> 1996-10-16 16:17:09
+ ok
+ </pre>
+ </section>
+
+ <section>
+ <title>Show Reports</title>
+ <p>To show details of a specific report, use the function
+ <c>rb:show(Number)</c>:</p>
+ <pre>
+
+10> <input>rb:show(1).</input>
+7> <input>rb:show(4).</input>
+
+PROGRESS REPORT &lt;0.20.0> 1996-10-16 16:16:36
+===============================================================================
+supervisor {local,sasl_sup}
+started
+[{pid,&lt;0.24.0>},
+{name,release_handler},
+{mfa,{release_handler,start_link,[]}},
+{restart_type,permanent},
+{shutdown,2000},
+{child_type,worker}]
+
+ok
+8> rb:show(9).
+
+CRASH REPORT &lt;0.24.0> 1996-10-16 16:16:21
+===============================================================================
+Crashing process
+pid &lt;0.24.0>
+registered_name release_handler
+error_info {undef,{release_handler,mbj_func,[]}}
+initial_call
+{gen,init_it,
+[gen_server,
+&lt;0.20.0>,
+&lt;0.20.0>,
+{erlang,register},
+release_handler,
+release_handler,
+[],
+[]]}
+ancestors [sasl_sup,&lt;0.18.0>]
+messages []
+links [&lt;0.23.0>,&lt;0.20.0>]
+dictionary []
+trap_exit false
+status running
+heap_size 610
+stack_size 142
+reductions 54
+
+ok
+ </pre>
+ </section>
+
+ <section>
+ <title>Search the Reports</title>
+ <p>It is possible to show all reports which contain a common
+ pattern. Suppose a process crashes because it tries to call a
+ non-existing function <c>release_handler:mbj_func.</c> We could
+ then show reports as follows:</p>
+ <pre>
+
+12><input>rb:grep("mbj_func").</input>
+Found match in report number 11
+
+ERROR REPORT &lt;0.24.0> 1996-10-16 16:16:21
+===============================================================================
+
+** undefined function: release_handler:mbj_func[] **
+Found match in report number 10
+
+ERROR REPORT &lt;0.24.0> 1996-10-16 16:16:21
+===============================================================================
+
+** Generic server release_handler terminating
+** Last message in was {unpack_release,hej}
+** When Server state == {state,[],
+"/home/dup/otp2/otp_beam_sunos5_p1g_7",
+[{release,
+"OTP APN 181 01",
+"P1G",
+undefined,
+[],
+permanent}],
+undefined}
+** Reason for termination ==
+** {undef,{release_handler,mbj_func,[]}}
+Found match in report number 9
+
+CRASH REPORT &lt;0.24.0> 1996-10-16 16:16:21
+===============================================================================
+Crashing process
+pid &lt;0.24.0>
+registered_name release_handler
+error_info {undef,{release_handler,mbj_func,[]}}
+initial_call
+{gen,init_it,
+[gen_server,
+&lt;0.20.0>,
+&lt;0.20.0>,
+{erlang,register},
+release_handler,
+release_handler,
+[],
+[]]}
+ancestors [sasl_sup,&lt;0.18.0>]
+messages []
+links [&lt;0.23.0>,&lt;0.20.0>]
+dictionary []
+trap_exit false
+status running
+heap_size 610
+stack_size 142
+reductions 54
+
+Found match in report number 8
+
+SUPERVISOR REPORT &lt;0.20.0> 1996-10-16 16:16:21
+===============================================================================
+Reporting supervisor {local,sasl_sup}
+
+Child process
+errorContext child_terminated
+reason {undef,{release_handler,mbj_func,[]}}
+pid &lt;0.24.0>
+name release_handler
+start_function {release_handler,start_link,[]}
+restart_type permanent
+shutdown 2000
+child_type worker
+
+ok
+ </pre>
+ </section>
+
+ <section>
+ <title>Stop the Server</title>
+ <p>Stop the <c>rb_server</c> with the function
+ <c>rb:stop()</c>:</p>
+ <pre>
+
+13><input>rb:stop().</input>
+ok
+ </pre>
+ </section>
+ </section>
+</chapter>
+
diff --git a/lib/sasl/doc/src/fascicules.xml b/lib/sasl/doc/src/fascicules.xml
new file mode 100644
index 0000000000..0678195e07
--- /dev/null
+++ b/lib/sasl/doc/src/fascicules.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
+
+<fascicules>
+ <fascicule file="part" href="part_frame.html" entry="no">
+ User's Guide
+ </fascicule>
+ <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
+ Reference Manual
+ </fascicule>
+ <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
+ Release Notes
+ </fascicule>
+ <fascicule file="" href="../../../../doc/print.html" entry="no">
+ Off-Print
+ </fascicule>
+</fascicules>
+
diff --git a/lib/sasl/doc/src/make.dep b/lib/sasl/doc/src/make.dep
new file mode 100644
index 0000000000..4843b7934a
--- /dev/null
+++ b/lib/sasl/doc/src/make.dep
@@ -0,0 +1,22 @@
+# ----------------------------------------------------
+# >>>> Do not edit this file <<<<
+# This file was automaticly generated by
+# /home/otp/bin/docdepend
+# ----------------------------------------------------
+
+
+# ----------------------------------------------------
+# TeX files that the DVI file depend on
+# ----------------------------------------------------
+
+book.dvi: alarm_handler.tex appup.tex book.tex error_logging.tex \
+ overload.tex part.tex rb.tex ref_man.tex rel.tex \
+ release_handler.tex relup.tex sasl_app.tex \
+ sasl_intro.tex script.tex systools.tex
+
+# ----------------------------------------------------
+# Source inlined when transforming from source to LaTeX
+# ----------------------------------------------------
+
+book.tex: ref_man.xml
+
diff --git a/lib/sasl/doc/src/note.gif b/lib/sasl/doc/src/note.gif
new file mode 100644
index 0000000000..6fffe30419
--- /dev/null
+++ b/lib/sasl/doc/src/note.gif
Binary files differ
diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml
new file mode 100644
index 0000000000..15387c1232
--- /dev/null
+++ b/lib/sasl/doc/src/notes.xml
@@ -0,0 +1,319 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2004</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>SASL Release Notes</title>
+ <prepared>otp_appnotes</prepared>
+ <docno>nil</docno>
+ <date>nil</date>
+ <rev>nil</rev>
+ <file>notes.xml</file>
+ </header>
+ <p>This document describes the changes made to the SASL application.</p>
+
+<section><title>SASL 2.1.8</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The documentation is now built with open source tools
+ (xsltproc and fop) that exists on most platforms. One
+ visible change is that the frames are removed.</p>
+ <p>
+ Own Id: OTP-8201</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SASL 2.1.7</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The Windows utility Erlsrv, run in interactive mode now
+ accepts options for registering internal service name and
+ description field of Windows registry database.</p>
+ <p>
+ Own Id: OTP-8132</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SASL 2.1.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>When using the SASL application configuration
+ parameter <c>masters</c> the error tuple
+ <c>{error,{no_such_file,{Master,FileName}}}</c> was
+ sometimes returned even though the file <c>FileName</c>
+ existed.</p>
+ <p>
+ Own Id: OTP-7667</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Missing preloaded modules added</p>
+ <p>
+ Own Id: OTP-7820</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SASL 2.1.5.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ A Dialyzer warning was eliminated</p>
+ <p>
+ Own Id: OTP-7635</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SASL 2.1.5.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Minor changes.</p>
+ <p>
+ Own Id: OTP-7388</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SASL 2.1.5.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Minor updates.</p>
+ <p>
+ Own Id: OTP-6998</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+ <section>
+ <title>SASL 2.1.5.1</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>Minor Makefile changes.</p>
+ <p>Own Id: OTP-6689</p>
+ </item>
+ <item>
+ <p>Obsolete guard tests (such as list()) have been replaced
+ with the modern guard tests (such as is_list()).</p>
+ <p>Own Id: OTP-6725</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SASL 2.1.5</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Removed some dead code from <c>erlsrv:get_service/2</c>,
+ <c>release_handler:do_write_file/2</c>,
+ <c>systools_relup:foreach_baserel_up/7</c> and
+ <c>systools_relup:foreach_baserel_dn/7</c>.</p>
+ <p>Own Id: OTP-6499</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SASL 2.1.4</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>Added an option <c>{outdir,Dir}</c> to the functions in
+ <c>systools</c>, making it possible to specify in which
+ directory a boot script, relup file or release package
+ file should be placed.</p>
+ <p>Also, when using <c>systools:make_tar/2</c> to create a
+ release package file, the boot script, relup file and
+ <c>sys.config</c> are now searched for also in the current
+ working directory and any directory specified by
+ the <c>path</c> option, not only in the directory of
+ the <c>.rel</c> file.</p>
+ <p>As part of the work some minor bugs have been corrected:</p>
+ <list type="bulleted">
+ <item>
+ <p><c>systools:make_script/1,2</c> now returns
+ <c>error</c> if the <c>.script</c> and/or <c>.boot</c>
+ file could not be opened for writing, not <c>ok</c>.</p>
+ </item>
+ <item>
+ <p><c>systools:make_tar/1,2</c> can now handle a
+ <c>RelName</c> argument which includes a path.
+ Previously this would cause the <c>.rel</c> file to end
+ up in the wrong directory in the resulting tar file.</p>
+ </item>
+ <item>
+ <p>A documentation error for <c>systools:make_tar/1,2</c>:
+ The <c>.rel</c> file is placed in the <c>releases</c>
+ directory in the tar file, not <c>releases/RelVsn</c>.</p>
+ </item>
+ </list>
+ <p>Own Id: OTP-6226</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SASL 2.1.3</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p><c>release_handler:upgrade_app/2</c> and
+ <c>release_handler:downgrade_app/2,3</c> -- used for
+ testing application upgrade and downgrade according to
+ the <c>.appup</c> file -- now update application
+ configuration parameters correctly. (Thanks to Serge
+ Aleynikov)</p>
+ <p>Own Id: OTP-6162</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SASL 2.1.2</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Fixed some minor bugs in <c>release_handler</c> found by
+ Dialyzer.</p>
+ <p>Own Id: OTP-6039</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SASL 2.1.1</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>Added a number of functions to <c>release_handler</c> which
+ makes it possible to test upgrade and downgrade of
+ applications according to an <c>.appup</c> file "on the
+ fly": <br></br>
+
+ - <c>upgrade_app/2</c> <br></br>
+
+ - <c>upgrade_script/2</c> <br></br>
+
+ - <c>downgrade_app/2,3</c> <br></br>
+
+ - <c>downgrade_script/3</c> <br></br>
+
+ - <c>eval_appup_script/4</c></p>
+ <p>Own Id: OTP-5858</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SASL 2.1</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>A new option <c>{update_paths,Bool}</c> has been added
+ for <c>release_handler:install_release/2</c>. It
+ indicates if all application code paths should be updated
+ (<c>Bool==true</c>), or if only code paths for modified
+ applications should be updated (<c>Bool==false</c>,
+ default).</p>
+ <p><c>release_handler:set_unpacked/2</c> now returns an
+ error tuple if a specified application directory does not
+ exist.</p>
+ <p>*** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>Own Id: OTP-5761</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SASL 2.0.1</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>A bug that made it impossible to call <c>rb:show(N)</c>
+ (<c>N</c> being an integer) twice without getting an error
+ has been fixed.</p>
+ <p>Own Id: OTP-5287</p>
+ </item>
+ </list>
+ </section>
+ </section>
+</chapter>
+
diff --git a/lib/sasl/doc/src/notes_history.xml b/lib/sasl/doc/src/notes_history.xml
new file mode 100644
index 0000000000..50772ae4e3
--- /dev/null
+++ b/lib/sasl/doc/src/notes_history.xml
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2006</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>SASL Release Notes History</title>
+ <prepared>otp_appnotes</prepared>
+ <docno>nil</docno>
+ <date>nil</date>
+ <rev>nil</rev>
+ </header>
+
+ <section>
+ <title>SASL 2.0</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>When doing a code replacement in run-time, updating the
+ internal state of a gen_server, gen_event or gen_fsm, it
+ was stated in the documentation that the first argument
+ <c>OldVsn</c> to the callback function
+ <c>Module:code_change</c> was defined by the <c>vsn</c>
+ attribute in the old version of <c>Module</c>.</p>
+ <p>However, this was not true. For downgrades, <c>OldVsn</c>
+ was <c>{down,Vsn}</c>, where <c>Vsn</c> was fetched from
+ the <c>.app</c> file instead.</p>
+ <p>The version is now always fetched from the module using
+ <c>beam_lib:version/1</c> and the man pages for gen_*
+ have been updated accordingly.</p>
+ <p>*** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>Own Id: OTP-3699</p>
+ </item>
+ <item>
+ <p>The release handling instruction
+ <c>restart_application</c> was translated to the
+ low-level instruction <c>application_remove</c> and a set
+ of <c>load_module</c> instructions.</p>
+ <p>However, <c>application_remove</c> caused the modules
+ listed for the new, not the old, version of the
+ application to be unloaded. If the set of modules was
+ changed, this meant the release handler would try to
+ purge non-existent modules and/or forget to unload
+ modules no longer used.</p>
+ <p><c>restart_application</c> is now translated to a correct
+ set of <c>delete_module</c> and <c>add_module</c>
+ instructions instead, and the <c>application_remove</c>
+ instruction is deprecated.</p>
+ <p>*** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>Own Id: OTP-4805</p>
+ </item>
+ <item>
+ <p><c>release_handler:check_install_release/1</c> returned
+ <c>{error,Reason}</c> if <c>sys.config</c> or
+ <c>relup</c> was missing. Since both these files are
+ optional, the behaviour has been changed to write
+ warnings to the terminal but return an <c>ok</c> tuple
+ instead.</p>
+ <p>*** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>Own Id: OTP-4824</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>Added a clause to <c>systools:make_script/1</c> which
+ makes it possible to provide (atomic) options when
+ calling the function using <c>erl -s</c>.</p>
+ <p>Example: <c>erl -noinput +B -s systools make_script myrel no_module_tests -s erlang halt</c> is equal to calling
+ <c>systools:make_script("myrel", [no_module_tests])</c>.</p>
+ <p>Own Id: OTP-3384</p>
+ </item>
+ <item>
+ <p>Added simplified versions of the <c>update</c> and
+ <c>load_module</c> release handling instructions.</p>
+ <p>Own Id: OTP-4793</p>
+ </item>
+ <item>
+ <p>Added two new release handling instructions: <c>{update, Module, supervisor}</c> and <c>{delete_module, Module}</c>.</p>
+ <p>Own Id: OTP-4800</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SASL 1.10.1</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>The option <c>{abort_on_error,Bool}</c> has been added to
+ <c>rb:start/1</c> and <c>rb:rescan/1</c>. With it you can
+ choose whether or not rb should stop logging if it
+ encounters an unprintable report. When <c>abort_on_error</c>
+ is set to <c>false</c>, rb will resume logging after a bad
+ report has been handled. The error messages rb prints when
+ logging fails have been enhanced.</p>
+ <p>Own Id: OTP-5096 Aux Id: seq8930</p>
+ </item>
+ </list>
+ </section>
+ </section>
+</chapter>
+
diff --git a/lib/sasl/doc/src/overload.xml b/lib/sasl/doc/src/overload.xml
new file mode 100644
index 0000000000..80457e75fa
--- /dev/null
+++ b/lib/sasl/doc/src/overload.xml
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>1996</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>overload</title>
+ <prepared>Peter H&ouml;gfeldt</prepared>
+ <responsible>Peter H&ouml;gfeldt</responsible>
+ <docno></docno>
+ <approved>(Joe Armstrong)</approved>
+ <checked></checked>
+ <date>1996-10-29</date>
+ <rev>A</rev>
+ <file>overload.sgml</file>
+ </header>
+ <module>overload</module>
+ <modulesummary>An Overload Regulation Process</modulesummary>
+ <description>
+ <p><c>overload</c> is a process which indirectly regulates CPU
+ usage in the system. The idea is that a main application calls
+ the <c>request/0</c> function before starting a major job, and
+ proceeds with the job if the return value is positive; otherwise
+ the job must not be started.
+ </p>
+ <p><c>overload</c> is part of the <c>sasl</c> application, and all
+ configuration parameters are defined there.
+ </p>
+ <p>A set of two intensities are maintained, the <c>total intensity</c> and the <c>accept intensity</c>. For that purpose
+ there are two configuration parameters, the <c>MaxIntensity</c>
+ and the <c>Weight</c> value (both are measured in 1/second).
+ </p>
+ <p>Then total and accept intensities are calculated as
+ follows. Assume that the time of the current call to
+ <c>request/0</c> is <c>T(n)</c>, and that the time of the
+ previous call was <c>T(n-1)</c>.
+ </p>
+ <list type="bulleted">
+ <item>
+ <p>The current <c>total intensity</c>, denoted
+ <c>TI(n)</c>, is calculated according to the formula,
+ </p>
+ <p><c>TI(n) = exp(-Weight*(T(n) - T(n-1)) * TI(n-1) + Weight</c>,
+ </p>
+ <p>where <c>TI(n-1)</c> is the previous total intensity.
+ </p>
+ </item>
+ <item>
+ <p>The current <c>accept intensity</c>, denoted
+ <c>AI(n)</c>, is determined by the formula,
+ </p>
+ <p><c>AI(n) = exp(-Weight*(T(n) - T(n-1)) * AI(n-1) + Weight</c>,
+ </p>
+ <p>where <c>AI(n-1)</c> is the previous accept intensity,
+ provided that the value of <c>exp(-Weight*(T(n) - T(n-1)) * AI(n-1)</c> is less than <c>MaxIntensity</c>; otherwise the
+ value is
+ </p>
+ <p><c>AI(n) = exp(-Weight*(T(n) - T(n-1)) * AI(n-1)</c>.
+ </p>
+ </item>
+ </list>
+ <p>The value of configuration parameter <c>Weight</c> controls the
+ speed with which the calculations of intensities will react to
+ changes in the underlying input intensity. The inverted value of
+ <c>Weight</c>,
+ </p>
+ <p><c>T = 1/Weight</c></p>
+ <p>can be thought of as the "time constant"
+ of the intensity calculation formulas. For example, if <c>Weight = 0.1</c>, then a change in the underlying input intensity will be
+ reflected in the <c>total</c> and <c>accept intensities</c> within
+ approximately 10 seconds.
+ </p>
+ <p>The overload process defines one alarm, which it sets using
+ <c>alarm_handler:set_alarm(Alarm)</c>. <c>Alarm</c> is defined
+ as:
+ </p>
+ <taglist>
+ <tag><c>{overload, []}</c></tag>
+ <item>
+ <p>This alarm is set when the current accept intensity exceeds
+ <c>MaxIntensity</c>.
+ </p>
+ </item>
+ </taglist>
+ <p>A new overload alarm is not set until the current accept
+ intensity has fallen below <c>MaxIntensity</c>. To prevent the
+ overload process from generating a lot of set/reset alarms, the
+ alarm is not reset until the current accept intensity has fallen
+ below 75% of <c>MaxIntensity</c>, and it is not until then that
+ the alarm can be set again.
+ </p>
+ </description>
+ <funcs>
+ <func>
+ <name>request() -> accept | reject</name>
+ <fsummary>Request to proceed with current job</fsummary>
+ <desc>
+ <p>Returns <c>accept</c> or <c>reject</c> depending on the
+ current value of the accept intensity. </p>
+ <p>The application
+ calling this function should be processed with the job in
+ question if the return value is <c>accept</c>; otherwise it
+ should not continue with that job.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>get_overload_info() -> OverloadInfo</name>
+ <fsummary>Return current overload information data</fsummary>
+ <type>
+ <v>OverloadInfo = [{total_intensity, TotalIntensity}, {accept_intensity, AcceptIntensity}, {max_intensity, MaxIntensity}, {weight, Weight}, {total_requests, TotalRequests}, {accepted_requests, AcceptedRequests}].</v>
+ <v>TotalIntensity = float() > 0</v>
+ <v>AcceptIntensity = float() > 0</v>
+ <v>MaxIntensity = float() > 0</v>
+ <v>Weight = float() > 0</v>
+ <v>TotalRequests = integer()</v>
+ <v>AcceptedRequests = integer()</v>
+ </type>
+ <desc>
+ <p>Returns the current total and accept intensities, the
+ configuration parameters, and absolute counts of the total
+ number of requests, and accepted number of requests (since
+ the overload process was started).</p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>See Also</title>
+ <p>alarm_handler(3), sasl(3)
+ </p>
+ </section>
+</erlref>
+
diff --git a/lib/sasl/doc/src/part.xml b/lib/sasl/doc/src/part.xml
new file mode 100644
index 0000000000..647380efbd
--- /dev/null
+++ b/lib/sasl/doc/src/part.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>1996</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>SASL User's Guide</title>
+ <prepared>OTP Team</prepared>
+ <docno></docno>
+ <date>1997-06-04</date>
+ <rev>1.3.1</rev>
+ <file>part.xml</file>
+ </header>
+ <description>
+ <p>The System Architecture Support Libraries, <em>SASL</em>,
+ provides support for alarm and release handling etc.</p>
+ </description>
+ <xi:include href="sasl_intro.xml"/>
+ <xi:include href="error_logging.xml"/>
+</part>
+
diff --git a/lib/sasl/doc/src/part_notes.xml b/lib/sasl/doc/src/part_notes.xml
new file mode 100644
index 0000000000..1f572ae922
--- /dev/null
+++ b/lib/sasl/doc/src/part_notes.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2004</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>SASL Release Notes</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <description>
+ <p>The System Architecture Support Libraries, <em>SASL</em>,
+ provides support for alarm and release handling etc.</p>
+ <p>For information about older versions, see
+ <url href="part_notes_history_frame.html">Release Notes History</url>.</p>
+ </description>
+ <xi:include href="notes.xml"/>
+</part>
+
diff --git a/lib/sasl/doc/src/part_notes_history.xml b/lib/sasl/doc/src/part_notes_history.xml
new file mode 100644
index 0000000000..2726d73684
--- /dev/null
+++ b/lib/sasl/doc/src/part_notes_history.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part>
+ <header>
+ <copyright>
+ <year>2006</year>
+ <year>2007</year>
+ <holder>Ericsson AB, All Rights Reserved</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>SASL Release Notes History</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <description>
+ <p>The System Architecture Support Libraries, <em>SASL</em>,
+ provides support for alarm and release handling etc.</p>
+ </description>
+ <include file="notes_history"></include>
+</part>
+
diff --git a/lib/sasl/doc/src/rb.xml b/lib/sasl/doc/src/rb.xml
new file mode 100644
index 0000000000..5b49a7bced
--- /dev/null
+++ b/lib/sasl/doc/src/rb.xml
@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>1996</year>
+ <year>2007</year>
+ <holder>Ericsson AB, All Rights Reserved</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>rb</title>
+ <prepared>Martin Bj&ouml;rklund</prepared>
+ <responsible>Martin Bj&ouml;rklund</responsible>
+ <docno></docno>
+ <approved>Bjarne D&auml;cker</approved>
+ <checked></checked>
+ <date>1996-10-16</date>
+ <rev>A</rev>
+ <file>rb.sgml</file>
+ </header>
+ <module>rb</module>
+ <modulesummary>The Report Browser Tool</modulesummary>
+ <description>
+ <p>The Report Browser (RB) tool makes it possible to browse and
+ format error reports written by the error logger handler
+ <c>log_mf_h</c>.
+ </p>
+ </description>
+ <funcs>
+ <func>
+ <name>grep(RegExp)</name>
+ <fsummary>Search the reports for a regular expression</fsummary>
+ <type>
+ <v>RegExp = string()</v>
+ </type>
+ <desc>
+ <p>All reports containing the regular expression <c>RegExp</c>
+ are printed.
+ </p>
+ <p><c>RegExp</c> is a string containing the regular
+ expression. Refer to the module <c>regexp</c> in the STDLIB
+ reference manual
+ for a definition of valid regular expressions. They are
+ essentially the same as the UNIX command <c>egrep</c>.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>h()</name>
+ <name>help()</name>
+ <fsummary>Print help information</fsummary>
+ <desc>
+ <p>Prints the on-line help information.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>list()</name>
+ <name>list(Type)</name>
+ <fsummary>List all reports</fsummary>
+ <type>
+ <v>Type = type()</v>
+ <v>type() = crash_report | supervisor_report | error | progress</v>
+ </type>
+ <desc>
+ <p>This function lists all reports loaded in the
+ <c>rb_server</c>. Each report is given a unique number that
+ can be used as a reference to the report in the
+ <c>show/1</c> function.
+ </p>
+ <p>If no <c>Type</c> is given, all reports are listed.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>rescan()</name>
+ <name>rescan(Options)</name>
+ <fsummary>Rescan the report directory</fsummary>
+ <type>
+ <v>Options = [opt()]</v>
+ </type>
+ <desc>
+ <p>Rescans the report directory. <c>Options</c> is the same as
+ for <c>start()</c>.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>show()</name>
+ <name>show(Report)</name>
+ <fsummary>Show reports</fsummary>
+ <type>
+ <v>Report = int() | type()</v>
+ </type>
+ <desc>
+ <p>If a type argument is given, all loaded reports of this
+ type are printed. If an integer argument is given, the
+ report with this reference number is printed. If no argument
+ is given, all reports are shown.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>start()</name>
+ <name>start(Options)</name>
+ <fsummary>Start the RB server</fsummary>
+ <type>
+ <v>Options = [opt()]</v>
+ <v>opt() = {start_log, FileName} | {max, MaxNoOfReports} | {report_dir, DirString} | {type, ReportType} | {abort_on_error, Bool}</v>
+ <v>FileName = string() | standard_io</v>
+ <v>MaxNoOfReports = int() | all</v>
+ <v>DirString = string()</v>
+ <v>ReportType = type() | [type()] | all</v>
+ <v>Bool = true | false</v>
+ </type>
+ <desc>
+ <p>The function <c>start/1</c> starts the <c>rb_server</c>
+ with the specified options, while <c>start/0</c> starts with
+ default options. The <c>rb_server</c> must be started before
+ reports can be browsed. When the <c>rb_server</c> is
+ started, the files in the specified directory are
+ scanned. The other functions assume that the server has
+ started.
+ </p>
+ <p><c>{start_log, FileName}</c> starts logging to file. All
+ reports will be printed to the named file. The default is
+ <c>standard_io</c>.
+ </p>
+ <p><c>{max, MaxNoOfReports}</c>. Controls how many reports the
+ <c>rb_server</c> should read on start-up. This option is
+ useful as the directory may contain 20.000 reports. If this
+ option is given, the <c>MaxNoOfReports</c> latest reports
+ will be read. The default is 'all'.
+ </p>
+ <p><c>{report_dir, DirString}</c>. Defines the directory where
+ the error log files are located. The default is <c>{sasl, error_logger_mf_dir}</c>. </p>
+ <p><c>{type, ReportType}</c>. Controls what kind of reports the
+ <c>rb_server</c> should read on start-up. <c>ReportType</c>
+ is a supported type, 'all', or a list of supported
+ types. The default is 'all'.
+ </p>
+ <p><c>{abort_on_error, Bool}</c>. This option specifies whether
+ or not logging should be aborted if rb encounters an unprintable
+ report. (You may get a report on incorrect form if the
+ <c>error_logger</c> function <c>error_msg</c> or
+ <c>info_msg</c> has been called with an invalid format string).
+ If <c>Bool</c> is <c>true</c>, rb will stop logging (and print an
+ error message to stdout) if it encounters a badly formatted report.
+ If logging to file is enabled, an error message will be appended to
+ the log file as well.
+ If <c>Bool</c> is <c>false</c> (which is the default value), rb will
+ print an error message to stdout for every bad report it
+ encounters, but the logging process is never aborted. All printable
+ reports will be written. If logging to file is enabled, rb prints
+ <c>* UNPRINTABLE REPORT *</c> in the log file at the location of an
+ unprintable report.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>start_log(FileName)</name>
+ <fsummary>Redirect all output to <c>FileName</c></fsummary>
+ <type>
+ <v>FileName = string()</v>
+ </type>
+ <desc>
+ <p>Redirects all report output from the RB tool to the
+ specified file.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>stop()</name>
+ <fsummary>Stop the RB server</fsummary>
+ <desc>
+ <p>Stops the <c>rb_server</c>.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>stop_log()</name>
+ <fsummary>Stop logging to file</fsummary>
+ <desc>
+ <p>Closes the log file. The output from the RB tool will be
+ directed to <c>standard_io</c>.
+ </p>
+ </desc>
+ </func>
+ </funcs>
+</erlref>
+
diff --git a/lib/sasl/doc/src/ref_man.xml b/lib/sasl/doc/src/ref_man.xml
new file mode 100644
index 0000000000..09b745a705
--- /dev/null
+++ b/lib/sasl/doc/src/ref_man.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE application SYSTEM "application.dtd">
+
+<application xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>1996</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>SASL Reference Manual</title>
+ <prepared>OTP Team</prepared>
+ <docno></docno>
+ <date>1997-11-17</date>
+ <rev>1.4</rev>
+ <file>application.xml</file>
+ </header>
+ <description>
+ <p>The System Architecture Support Libraries application, <em>SASL</em>,
+ provides support for alarm and release handling etc.</p>
+ </description>
+ <xi:include href="sasl_app.xml"/>
+ <xi:include href="alarm_handler.xml"/>
+ <xi:include href="overload.xml"/>
+ <xi:include href="rb.xml"/>
+ <xi:include href="release_handler.xml"/>
+ <xi:include href="systools.xml"/>
+ <xi:include href="appup.xml"/>
+ <xi:include href="rel.xml"/>
+ <xi:include href="relup.xml"/>
+ <xi:include href="script.xml"/>
+</application>
+
diff --git a/lib/sasl/doc/src/rel.xml b/lib/sasl/doc/src/rel.xml
new file mode 100644
index 0000000000..108f5e7f3e
--- /dev/null
+++ b/lib/sasl/doc/src/rel.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE fileref SYSTEM "fileref.dtd">
+
+<fileref>
+ <header>
+ <copyright>
+ <year>1997</year>
+ <year>2007</year>
+ <holder>Ericsson AB, All Rights Reserved</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>rel</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <file>rel</file>
+ <filesummary>Release resource file</filesummary>
+ <description>
+ <p>The <em>release resource file</em> specifies which applications are
+ are included in a release (system) based on Erlang/OTP.</p>
+ <p>This file is used by the functions in <c>systools</c> when generating
+ start scripts (<c>.script</c>, <c>.boot</c>) and release upgrade
+ files (<c>relup</c>).</p>
+ </description>
+
+ <section>
+ <title>FILE SYNTAX</title>
+ <p>The release resource file should be called <c>Name.rel</c>.</p>
+ <p>The <c>.rel</c> file contains one single Erlang term, which is
+ called a <em>release specification</em>. The file has the
+ following syntax:</p>
+ <code type="none">
+{release, {RelName,Vsn}, {erts, EVsn},
+ [{Application, AppVsn} |
+ {Application, AppVsn, Type} |
+ {Application, AppVsn, IncApps} |
+ {Application, AppVsn, Type, IncApps}]}.
+ </code>
+ <list type="bulleted">
+ <item>
+ <p><c>RelName = string()</c> is the name of the release.</p>
+ </item>
+ <item>
+ <p><c>Vsn = string()</c> is the version of the release.</p>
+ </item>
+ <item>
+ <p><c>EVsn = string()</c> is the version of ERTS the release is
+ intended for.</p>
+ </item>
+ <item>
+ <p><c>Application = atom()</c> is the name of an application
+ included in the release.</p>
+ </item>
+ <item>
+ <p><c>AppVsn = string()</c> is the version of an application
+ included in the release.</p>
+ </item>
+ <item>
+ <p><c>Type = permanent | transient | temporary | load | none</c>
+ is the start type of an application included in the release.</p>
+ <p>If <c>Type = permanent | transient | temporary</c>,
+ the application will be loaded and started in the corresponding
+ way, see <c>application(3)</c>. If <c>Type = load</c>,
+ the application will only be loaded. If <c>Type = none</c>,
+ the application will be neither loaded nor started, although
+ the code for its modules will be loaded.
+ Defaults to <c>permanent</c></p>
+ </item>
+ <item>
+ <p><c>IncApps = [atom()]</c> is a list of applications that are
+ included by an application included in the release.</p>
+ <p>The list must be a subset of the included applications
+ specified in the application resource file
+ (<c>Application.app</c>) and overrides this value. Defaults
+ to the empty list.</p>
+ </item>
+ </list>
+ <note>
+ <p>The list of applications must contain the <c>kernel</c> and
+ <c>stdlib</c> applications.</p>
+ </note>
+ </section>
+
+ <section>
+ <title>SEE ALSO</title>
+ <p>application(3), relup(4), systools(3)</p>
+ </section>
+</fileref>
+
diff --git a/lib/sasl/doc/src/rel/bar.1.erl b/lib/sasl/doc/src/rel/bar.1.erl
new file mode 100644
index 0000000000..610fc4919e
--- /dev/null
+++ b/lib/sasl/doc/src/rel/bar.1.erl
@@ -0,0 +1,17 @@
+-module(bar).
+-vsn(1).
+
+-export([simple/1, complicated_sum/1]).
+
+simple(X) ->
+ case lists2:assoc(simple, X) of
+ {ok, Val} -> Val;
+ false -> false
+ end.
+
+complicated_sum([X, Y, Z]) -> cs(X, Y, Z).
+
+cs([HX | TX], [HY | TY], [HZ | TZ]) ->
+ NewRes = cs(TX, TY, TZ),
+ [HX + HY + HZ | NewRes];
+cs([], [], []) -> [].
diff --git a/lib/sasl/doc/src/rel/bar.2.erl b/lib/sasl/doc/src/rel/bar.2.erl
new file mode 100644
index 0000000000..a831316874
--- /dev/null
+++ b/lib/sasl/doc/src/rel/bar.2.erl
@@ -0,0 +1,13 @@
+-module(bar).
+-vsn(2).
+
+-export([simple/1, complicated_sum/1]).
+
+simple(X) ->
+ case lists2:assoc(simple, X) of
+ {ok, Val} -> Val;
+ false -> false
+ end.
+
+complicated_sum(X) ->
+ lists2:multi_map(fun(A,B,C) -> A+B+C end, X).
diff --git a/lib/sasl/doc/src/rel/ge_h.1.erl b/lib/sasl/doc/src/rel/ge_h.1.erl
new file mode 100644
index 0000000000..ac2d3b3e5f
--- /dev/null
+++ b/lib/sasl/doc/src/rel/ge_h.1.erl
@@ -0,0 +1,28 @@
+-module(ge_h).
+-vsn(1).
+-behaviour(gen_event).
+
+-export([get_events/1]).
+-export([init/1, handle_event/2, handle_call/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+get_events(Mgr) ->
+ gen_event:call(Mgr, ge_h, get_events).
+
+init(_) -> {ok, undefined}.
+
+handle_event(Event, _LastEvent) ->
+ {ok, Event}.
+
+handle_call(get_events, LastEvent) ->
+ {ok, [LastEvent], LastEvent}.
+
+handle_info(Info, LastEvent) ->
+ {ok, LastEvent}.
+
+terminate(Arg, LastEvent) ->
+ ok.
+
+code_change(_OldVsn, LastEvent, _Extra) ->
+ {ok, LastEvent}.
+
diff --git a/lib/sasl/doc/src/rel/ge_h.2.erl b/lib/sasl/doc/src/rel/ge_h.2.erl
new file mode 100644
index 0000000000..837338e399
--- /dev/null
+++ b/lib/sasl/doc/src/rel/ge_h.2.erl
@@ -0,0 +1,31 @@
+-module(ge_h).
+-vsn(2).
+-behaviour(gen_event).
+
+-export([get_events/1]).
+-export([init/1, handle_event/2, handle_call/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+get_events(Mgr) ->
+ gen_event:call(Mgr, ge_h, get_events).
+
+init(_) -> {ok, []}.
+
+handle_event(Event, []) ->
+ {ok, [Event]};
+handle_event(Event, [Event1 | _]) ->
+ {ok, [Event, Event1]}.
+
+handle_call(get_events, Events) ->
+ Events.
+
+handle_info(Info, Events) ->
+ {ok, Events}.
+
+terminate(Arg, Events) ->
+ ok.
+
+code_change(1, undefined, _Extra) ->
+ {ok, []};
+code_change(1, LastEvent, _Extra) ->
+ {ok, [LastEvent]}.
diff --git a/lib/sasl/doc/src/rel/gs1.1.erl b/lib/sasl/doc/src/rel/gs1.1.erl
new file mode 100644
index 0000000000..b85ea45857
--- /dev/null
+++ b/lib/sasl/doc/src/rel/gs1.1.erl
@@ -0,0 +1,30 @@
+-module(gs1).
+-vsn(1).
+-behaviour(gen_server).
+
+-export([get_data/0]).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-record(state, {data}).
+
+get_data() ->
+ gen_server:call(gs1, get_data).
+
+init([Data]) ->
+ {ok, #state{data = Data}}.
+
+handle_call(get_data, _From, State) ->
+ {reply, {ok, State#state.data}, State}.
+
+handle_cast(_Request, State) ->
+ {noreply, State}.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
diff --git a/lib/sasl/doc/src/rel/gs1.2.erl b/lib/sasl/doc/src/rel/gs1.2.erl
new file mode 100644
index 0000000000..8391713c6d
--- /dev/null
+++ b/lib/sasl/doc/src/rel/gs1.2.erl
@@ -0,0 +1,35 @@
+-module(gs1).
+-vsn(2).
+-behaviour(gen_server).
+
+-export([get_data/0, get_time/0]).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-record(state, {data, time}).
+
+get_data() ->
+ gen_server:call(gs1, get_data).
+
+get_time() ->
+ gen_server:call(gs1, get_time).
+
+init([Data]) ->
+ {ok, #state{data = Data, time = erlang:time()}}.
+
+handle_call(get_data, _From, State) ->
+ {reply, {ok, State#state.data}, State};
+handle_call(get_time, _From, State) ->
+ {reply, {ok, State#state.time}, State}.
+
+handle_cast(_Request, State) ->
+ {noreply, State}.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(1, {state, Data}, _Extra) ->
+ {ok, #state{data = Data, time = erlang:time()}}.
diff --git a/lib/sasl/doc/src/rel/gs1.3.erl b/lib/sasl/doc/src/rel/gs1.3.erl
new file mode 100644
index 0000000000..2d4c0870b6
--- /dev/null
+++ b/lib/sasl/doc/src/rel/gs1.3.erl
@@ -0,0 +1,36 @@
+-module(gs1).
+-vsn(2).
+-behaviour(gen_server).
+
+-export([get_data/0, get_time/0]).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-record(state, {data, time}).
+
+get_data() ->
+ gen_server:call(gs1, get_data).
+get_time() ->
+ gen_server:call(gs1, get_time).
+
+init([Data]) ->
+ {ok, #state{data = Data, time = erlang:time()}}.
+
+handle_call(get_data, _From, State) ->
+ {reply, {ok, State#state.data}, State};
+handle_call(get_time, _From, State) ->
+ {reply, {ok, State#state.time}, State}.
+
+handle_cast(_Request, State) ->
+ {noreply, State}.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(1, {state, Data}, _Extra) ->
+ {ok, #state{data = Data, time = erlang:time()}};
+code_change({down, 1}, #state{data = Data}, _Extra) ->
+ {ok, {state, Data}}.
diff --git a/lib/sasl/doc/src/rel/gs2.1.erl b/lib/sasl/doc/src/rel/gs2.1.erl
new file mode 100644
index 0000000000..1b06a9551f
--- /dev/null
+++ b/lib/sasl/doc/src/rel/gs2.1.erl
@@ -0,0 +1,31 @@
+-module(gs2).
+-vsn(1).
+-behaviour(gen_server).
+
+-export([is_operation_ok/1]).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+is_operation_ok(Op) ->
+ gen_server:call(gs2, {is_operation_ok, Op}).
+
+init([Data]) ->
+ {ok, []}.
+
+handle_call({is_operation_ok, Op}, _From, State) ->
+ Data = gs1:get_data(),
+ Reply = lists2:assoc(Op, Data),
+ {reply, Reply, State}.
+
+handle_cast(_Request, State) ->
+ {noreply, State}.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
diff --git a/lib/sasl/doc/src/rel/gs2.2.erl b/lib/sasl/doc/src/rel/gs2.2.erl
new file mode 100644
index 0000000000..3248ea43bb
--- /dev/null
+++ b/lib/sasl/doc/src/rel/gs2.2.erl
@@ -0,0 +1,38 @@
+-module(gs2).
+-vsn(2).
+-behaviour(gen_server).
+
+-export([is_operation_ok/1]).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+is_operation_ok(Op) ->
+ gen_server:call(gs2, {is_operation_ok, Op}).
+
+init([Data]) ->
+ {ok, []}.
+
+handle_call({is_operation_ok, Op}, _From, State) ->
+ Data = gs1:get_data(),
+ Time = gs1:get_time(),
+ Reply = do_things(lists2:assoc(Op, Data), Time),
+ {reply, Reply, State}.
+
+handle_cast(_Request, State) ->
+ {noreply, State}.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+do_things({ok, Val}, Time) ->
+ Val;
+do_things(false, Time) ->
+ {false, Time}.
+
+
diff --git a/lib/sasl/doc/src/rel/lists2.1.erl b/lib/sasl/doc/src/rel/lists2.1.erl
new file mode 100644
index 0000000000..4afe3b5041
--- /dev/null
+++ b/lib/sasl/doc/src/rel/lists2.1.erl
@@ -0,0 +1,8 @@
+-module(lists2).
+-vsn(1).
+
+-export([assoc/2]).
+
+assoc(Key, [{Key, Val} | _]) -> {ok, Val};
+assoc(Key, [H | T]) -> assoc(Key, T);
+assoc(Key, []) -> false.
diff --git a/lib/sasl/doc/src/rel/lists2.2.erl b/lib/sasl/doc/src/rel/lists2.2.erl
new file mode 100644
index 0000000000..734bc36bbb
--- /dev/null
+++ b/lib/sasl/doc/src/rel/lists2.2.erl
@@ -0,0 +1,13 @@
+-module(lists2).
+-vsn(2).
+
+-export([assoc/2, multi_map/2]).
+
+assoc(Key, [{Key, Val} | _]) -> {ok, Val};
+assoc(Key, [H | T]) -> assoc(Key, T);
+assoc(Key, []) -> false.
+
+multi_map(Func, [[] | ListOfLists]) -> [];
+multi_map(Func, ListOfLists) ->
+ [apply(Func, lists:map({erlang, hd}, ListOfLists)) |
+ multi_map(Func, lists:map({erlang, tl}, ListOfLists))].
diff --git a/lib/sasl/doc/src/rel/portc.1.erl b/lib/sasl/doc/src/rel/portc.1.erl
new file mode 100644
index 0000000000..0d387a4e5e
--- /dev/null
+++ b/lib/sasl/doc/src/rel/portc.1.erl
@@ -0,0 +1,31 @@
+-module(portc).
+-vsn(1).
+-behaviour(gen_server).
+
+-export([get_data/0]).
+-export([init/1, handle_call/3, handle_info/2, code_change/3]).
+
+-record(state, {port, data}).
+
+get_data() -> gen_server:call(portc, get_data).
+
+init([]) ->
+ PortProg = code:priv_dir(foo) ++ "/bin/portc",
+ Port = open_port({spawn, PortProg}, [binary, {packet, 2}]),
+ {ok, #state{port = Port}}.
+
+handle_call(get_data, _From, State) ->
+ {reply, {ok, State#state.data}, State}.
+
+handle_info({Port, Cmd}, State) ->
+ NewState = do_cmd(Cmd, State),
+ {noreply, NewState}.
+
+code_change(_, State, change_port_only) ->
+ State#state.port ! close,
+ receive
+ {Port, closed} -> true
+ end,
+ NPortProg = code:priv_dir(foo) ++ "/bin/portc", % get new version
+ NPort = open_port({spawn, NPortProg}, [binary, {packet, 2}]),
+ {ok, State#state{port = NPort}}.
diff --git a/lib/sasl/doc/src/rel/portc.2.erl b/lib/sasl/doc/src/rel/portc.2.erl
new file mode 100644
index 0000000000..6711177f6a
--- /dev/null
+++ b/lib/sasl/doc/src/rel/portc.2.erl
@@ -0,0 +1,34 @@
+-module(portc).
+-vsn(2).
+-behaviour(gen_server).
+
+-export([get_data/0]).
+-export([init/1, handle_call/3, handle_info/2, code_change/3]).
+
+-record(state, {port, data}).
+
+get_data() -> gen_server:call(portc, get_data).
+
+init([]) ->
+ PortProg = code:priv_dir(foo) ++ "/bin/portc",
+ Port = open_port({spawn, PortProg}, [binary, {packet, 2}]),
+ {ok, #state{port = Port}}.
+
+handle_call(get_data, _From, State) ->
+ {reply, {ok, State#state.data}, State}.
+
+handle_info({Port, Cmd}, State) ->
+ NewState = do_cmd(Cmd, State),
+ {noreply, NewState}.
+
+code_change(_, State, change_port_only) ->
+ State#state.port ! close,
+ receive
+ {Port, closed} -> true
+ end,
+ NPortProg = code:priv_dir(foo) ++ "/bin/portc", % get new version
+ NPort = open_port({spawn, NPortProg}, [binary, {packet, 2}]),
+ {ok, State#state{port = NPort}};
+code_change(1, State, change_erl_only) ->
+ NState = transform_state(State),
+ {ok, NState}.
diff --git a/lib/sasl/doc/src/rel/sp.1.erl b/lib/sasl/doc/src/rel/sp.1.erl
new file mode 100644
index 0000000000..deb11286b1
--- /dev/null
+++ b/lib/sasl/doc/src/rel/sp.1.erl
@@ -0,0 +1,46 @@
+-module(sp).
+-vsn(1).
+
+-export([start/0, get_data/0]).
+-export([init/1, system_continue/3, system_terminate/4]).
+
+-record(state, {data}).
+
+start() ->
+ Pid = proc_lib:spawn_link(?MODULE, init, [self()]),
+ {ok, Pid}.
+
+get_data() ->
+ sp_server ! {self(), get_data},
+ receive
+ {sp_server, Data} -> Data
+ end.
+
+init(Parent) ->
+ register(sp_server, self()),
+ process_flag(trap_exit, true),
+ loop(#state{}, Parent).
+
+loop(State, Parent) ->
+ receive
+ {system, From, Request} ->
+ sys:handle_system_msg(Request, From, Parent, ?MODULE, [], State);
+ {'EXIT', Parent, Reason} ->
+ cleanup(State),
+ exit(Reason);
+ {From, get_data} ->
+ From ! {sp_server, State#state.data},
+ loop(State, Parent);
+ _Any ->
+ loop(State, Parent)
+ end.
+
+cleanup(State) -> ok.
+
+%% Here are the sys call back functions
+system_continue(Parent, _, State) ->
+ loop(State, Parent).
+
+system_terminate(Reason, Parent, _, State) ->
+ cleanup(State),
+ exit(Reason).
diff --git a/lib/sasl/doc/src/rel/sp.2.erl b/lib/sasl/doc/src/rel/sp.2.erl
new file mode 100644
index 0000000000..b2282f0610
--- /dev/null
+++ b/lib/sasl/doc/src/rel/sp.2.erl
@@ -0,0 +1,58 @@
+-module(sp).
+-vsn(2).
+
+-export([start/0, get_data/0, set_data/1]).
+-export([init/1, system_continue/3, system_terminate/4,
+ system_code_change/4]).
+
+-record(state, {data, last_pid}).
+
+start() ->
+ Pid = proc_lib:spawn_link(?MODULE, init, [self()]),
+ {ok, Pid}.
+
+get_data() ->
+ sp_server ! {self(), get_data},
+ receive
+ {sp_server, Data} -> Data
+ end.
+
+set_data(Data) ->
+ sp_server ! {self(), set_data, Data}.
+
+init(Parent) ->
+ register(sp_server, self()),
+ process_flag(trap_exit, true),
+ loop(#state{last_pid = no_one}, Parent).
+
+loop(State, Parent) ->
+ receive
+ {system, From, Request} ->
+ sys:handle_system_msg(Request, From, Parent,
+ ?MODULE, [], State);
+ {'EXIT', Parent, Reason} ->
+ cleanup(State),
+ exit(Reason);
+ {From, get_data} ->
+ From ! {sp_server, State#state.data},
+ loop(State, Parent);
+ {From, set_data, Data} ->
+ loop(State#state{data = Data, last_pid = From}, Parent);
+ _Any ->
+ loop(State, Parent)
+ end.
+
+cleanup(State) -> ok.
+
+%% Here are the sys call back functions
+system_continue(Parent, _, State) ->
+ loop(State, Parent).
+
+system_terminate(Reason, Parent, _, State) ->
+ cleanup(State),
+ exit(Reason).
+
+system_code_change({state, Data}, _Mod, 1, _Extra) ->
+ {ok, #state{data = Data, last_pid = no_one}};
+system_code_change(#state{data = Data}, _Mod, {down, 1}, _Extra) ->
+ {ok, {state, Data}}.
diff --git a/lib/sasl/doc/src/rel/sup.1.erl b/lib/sasl/doc/src/rel/sup.1.erl
new file mode 100644
index 0000000000..c73f1161b3
--- /dev/null
+++ b/lib/sasl/doc/src/rel/sup.1.erl
@@ -0,0 +1,11 @@
+-module(sup).
+-vsn(1).
+-behaviour(supervisor).
+-export([init/1]).
+
+init([]) ->
+ SupFlags = {one_for_one, 4, 3600},
+ Server = {my_server, {my_server, start_link, []},
+ permanent, 2000, worker, [my_server]},
+ GS1 = {gs1, {gs1, start_link, []}, permanent, 2000, worker, [gs1]},
+ {ok, {SupFlags, [Server, GS1]}}.
diff --git a/lib/sasl/doc/src/rel/sup.2.erl b/lib/sasl/doc/src/rel/sup.2.erl
new file mode 100644
index 0000000000..783da18f2f
--- /dev/null
+++ b/lib/sasl/doc/src/rel/sup.2.erl
@@ -0,0 +1,10 @@
+-module(sup).
+-vsn(2).
+-behaviour(supervisor).
+-export([init/1]).
+
+init([]) ->
+ SupFlags = {one_for_one, 4, 3600},
+ GS1 = {gs1, {gs1, start_link, []}, permanent, 2000, worker, [gs1]},
+ GS2 = {gs2, {gs2, start_link, []}, permanent, 2000, worker, [gs2]},
+ {ok, {SupFlags, [GS1, GS2]}}.
diff --git a/lib/sasl/doc/src/release_handler.xml b/lib/sasl/doc/src/release_handler.xml
new file mode 100644
index 0000000000..4a973bc5ed
--- /dev/null
+++ b/lib/sasl/doc/src/release_handler.xml
@@ -0,0 +1,697 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>1996</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>release_handler</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <module>release_handler</module>
+ <modulesummary>Unpacking and Installation of Release Packages</modulesummary>
+ <description>
+ <p>The <em>release handler</em> is a process belonging to the SASL
+ application which is responsible for <em>release handling</em>,
+ that is, unpacking, installation, and removal of release packages.</p>
+ <p>An introduction to release handling and a usage example can be
+ found in
+ <seealso marker="doc/design_principles:release_handling">Design Principles</seealso>.
+ </p>
+ <p>A <em>release package</em> is a compressed tar file containing
+ code for a certain version of a release, created by calling
+ <seealso marker="systools#make_tar/1">systools:make_tar/1,2</seealso>.
+ The release package should be placed in the <c>$ROOT/releases</c>
+ directory of the previous version of the release where
+ <c>$ROOT</c> is the installation root directory,
+ <c>code:root_dir()</c>.
+ Another <c>releases</c> directory can be specified using the SASL
+ configuration parameter <c>releases_dir</c>, or the OS environment
+ variable <c>RELDIR</c>. The release handler must have write access
+ to this directory in order to install the new release.
+ The persistent state of the release handler is stored there in a
+ file called <c>RELEASES</c>.</p>
+ <p>A release package should always contain the release resource file
+ <c>Name.rel</c> and a boot script <c>Name.boot</c>. It may contain
+ a release upgrade file <c>relup</c> and a system configuration
+ file <c>sys.config</c>. The <c>.rel</c> file contains information
+ about the release: its name, version, and which ERTS and
+ application versions it uses. The <c>relup</c> file contains
+ scripts for how to upgrade to, or downgrade from, this version of
+ the release.</p>
+ <p>The release package can be <em>unpacked</em>, which extracts
+ the files. An unpacked release can be <em>installed</em>.
+ The currently used version of the release is then upgraded or
+ downgraded to the specified version by evaluating the instructions
+ in <c>relup</c>. An installed release can be made
+ <em>permanent</em>. There can only be one permanent release in
+ the system, and this is the release that is used if the system is
+ restarted. An installed release, except the permanent one, can be
+ <em>removed</em>. When a release is removed, all files that
+ belong to that release only are deleted.</p>
+ <p>Each version of the release has a status. The status can be
+ <c>unpacked</c>, <c>current</c>, <c>permanent</c>, or <c>old</c>.
+ There is always one latest release which either has status
+ <c>permanent</c> (normal case), or <c>current</c> (installed, but
+ not yet made permanent). The following table illustrates
+ the meaning of the status values:</p>
+ <pre>
+Status Action NextStatus
+-------------------------------------------
+ - unpack unpacked
+unpacked install current
+ remove -
+current make_permanent permanent
+ install other old
+ remove -
+permanent make other permanent old
+ install permanent
+old reboot_old permanent
+ install current
+ remove -
+ </pre>
+ <p>The release handler process is a locally registered process on
+ each node. When a release is installed in a distributed system,
+ the release handler on each node must be called. The release
+ installation may be synchronized between nodes. From an operator
+ view, it may be unsatisfactory to specify each node. The aim is
+ to install one release package in the system, no matter how many
+ nodes there are. If this is the case, it is recommended that
+ software management functions are written which take care of
+ this problem. Such a function may have knowledge of the system
+ architecture, so it can contact each individual release handler
+ to install the package.</p>
+ <p>For release handling to work properly, the runtime system needs
+ to have knowledge about which release it is currently running. It
+ must also be able to change (in run-time) which boot script and
+ system configuration file should be used if the system is
+ restarted. This is taken care of automatically if Erlang is
+ started as an embedded system. Read about this in <em>Embedded System</em>. In this case, the system configuration file
+ <c>sys.config</c> is mandatory.</p>
+ <p>A new release may restart the system. Which program to use is
+ specified by the SASL configuration parameter <c>start_prg</c>
+ which defaults to <c>$ROOT/bin/start</c>.</p>
+ <p>The emulator restart on Windows NT expects that the system is
+ started using the <c>erlsrv</c> program (as a service).
+ Furthermore the release handler expects that the service is named
+ <em>NodeName</em>_<em>Release</em>, where <em>NodeName</em> is
+ the first part of the Erlang nodename (up to, but not including
+ the "@") and <em>Release</em> is the current release of
+ the application. The release handler furthermore expects that a
+ program like <c>start_erl.exe</c> is specified as "machine" to
+ <c>erlsrv</c>. During upgrading with restart, a new service will
+ be registered and started. The new service will be set to
+ automatic and the old service removed as soon as the new release
+ is made permanent.</p>
+ <p>The release handler at a node which runs on a diskless machine,
+ or with a read-only file system, must be configured accordingly
+ using the following <c>sasl</c> configuration parameters (see
+ <seealso marker="sasl_app">sasl(6)</seealso> for details):</p>
+ <taglist>
+ <tag><c>masters</c></tag>
+ <item>
+ <p>This node uses a number of master nodes in order to store
+ and fetch release information. All master nodes must be up
+ and running whenever release information is written by this
+ node.</p>
+ </item>
+ <tag><c>client_directory</c></tag>
+ <item>
+ <p>The <c>client_directory</c> in the directory structure of
+ the master nodes must be specified.</p>
+ </item>
+ <tag><c>static_emulator</c></tag>
+ <item>
+ <p>This parameter specifies if the Erlang emulator is
+ statically installed at the client node. A node with a static
+ emulator cannot dynamically switch to a new emulator because
+ the executable files are statically written into memory.</p>
+ </item>
+ </taglist>
+ <p>It is also possible to use the release handler to unpack and
+ install release packages when not running Erlang as an embedded
+ system, but in this case the user must somehow make sure that
+ correct boot scripts and configuration files are used if
+ the system needs to be restarted.</p>
+ <p>There are additional functions for using another file structure
+ than the structure defined in OTP. These functions can be used
+ to test a release upgrade locally.</p>
+ </description>
+ <funcs>
+ <func>
+ <name>check_install_release(Vsn) -> {ok, OtherVsn, Descr} | {error, Reason}</name>
+ <fsummary>Check installation of a release in the system.</fsummary>
+ <type>
+ <v>Vsn = OtherVsn = string()</v>
+ <v>Descr = term()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Checks if the specified version <c>Vsn</c> of the release
+ can be installed. The release must not have status
+ <c>current</c>. Issues warnings if <c>relup</c> or
+ <c>sys.config</c> are not present. If <c>relup</c> is present,
+ its contents are checked and <c>{error,Reason}</c> is
+ returned if an error is found. Also checks that all required
+ applications are present and that all new code can be loaded,
+ or <c>{error,Reason}</c> is returned.</p>
+ <p>This function evaluates all instructions that occur before
+ the <c>point_of_no_return</c> instruction in the release
+ upgrade script.</p>
+ <p>Returns the same as <c>install_release/1</c>. <c>Descr</c>
+ defaults to "" if no <c>relup</c> file is found.</p>
+ </desc>
+ </func>
+ <func>
+ <name>create_RELEASES(Root, RelDir, RelFile, AppDirs) -> ok | {error, Reason}</name>
+ <fsummary>Create an initial RELEASES file.</fsummary>
+ <type>
+ <v>Root = RelDir = RelFile = string()</v>
+ <v>AppDirs = [{App, Vsn, Dir}]</v>
+ <v>&nbsp;App = atom()</v>
+ <v>&nbsp;Vsn = Dir = string()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Creates an initial RELEASES file to be used by the release
+ handler. This file must exist in order to install new
+ releases.</p>
+ <p><c>Root</c> is the root of the installation (<c>$ROOT</c>) as
+ described above. <c>RelDir</c> is the the directory where
+ the <c>RELEASES</c> file should be created (normally
+ <c>$ROOT/releases</c>). <c>RelFile</c> is the name
+ of the <c>.rel</c> file that describes the initial release,
+ including the extension <c>.rel</c>.</p>
+ <p><c>AppDirs</c> can be used to specify from where the modules
+ for the specified applications should be loaded. <c>App</c> is
+ the name of an application, <c>Vsn</c> is the version, and
+ <c>Dir</c> is the name of the directory where <c>App-Vsn</c>
+ is located. The corresponding modules should be located under
+ <c>Dir/App-Vsn/ebin</c>. The directories for applications not
+ specified in <c>AppDirs</c> are assumed to be located in
+ <c>$ROOT/lib</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>install_file(Vsn, File) -> ok | {error, Reason}</name>
+ <fsummary>Install a release file in the release structure.</fsummary>
+ <type>
+ <v>Vsn = File = string()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Installs a release dependent file in the release structure.
+ A release dependent file is a file that must be in
+ the release structure when a new release is installed:
+ <c>start.boot</c>, <c>relup</c> and <c>sys.config</c>.</p>
+ <p>The function can be called, for example, when these files
+ are generated at the target. It should be called after
+ <c>set_unpacked/2</c> has been called.</p>
+ </desc>
+ </func>
+ <func>
+ <name>install_release(Vsn) -> {ok, OtherVsn, Descr} | {error, Reason}</name>
+ <name>install_release(Vsn, [Opt]) -> {ok, OtherVsn, Descr} | {error, Reason}</name>
+ <fsummary>Install a release in the system.</fsummary>
+ <type>
+ <v>Vsn = OtherVsn = string()</v>
+ <v>Opt = {error_action, Action} | {code_change_timeout, Timeout}</v>
+ <v>&nbsp;&nbsp;&nbsp;| {suspend_timeout, Timeout} | {update_paths, Bool}</v>
+ <v>&nbsp;Action = restart | reboot</v>
+ <v>&nbsp;Timeout = default | infinity | int()>0</v>
+ <v>&nbsp;Bool = boolean()</v>
+ <v>Descr = term()</v>
+ <v>Reason = {illegal_option, Opt} | {already_installed, Vsn} | {change_appl_data, term()} | term()</v>
+ </type>
+ <desc>
+ <p>Installs the specified version <c>Vsn</c> of the release.
+ Looks first for a <c>relup</c> file for <c>Vsn</c> and a
+ script <c>{UpFromVsn,Descr1,Instructions1}</c> in this file
+ for upgrading from the current version. If not found,
+ the function looks for a <c>relup</c> file for the current
+ version and a script <c>{Vsn,Descr2,Instructions2}</c> in this
+ file for downgrading to <c>Vsn</c>.</p>
+ <p>If a script is found, the first thing that happens is that
+ the applications specifications are updated according to
+ the <c>.app</c> files and <c>sys.config</c> belonging to
+ the release version <c>Vsn</c>.</p>
+ <p>After the application specifications have been updated,
+ the instructions in the script are evaluated and the function
+ returns <c>{ok,OtherVsn,Descr}</c> if successful.
+ <c>OtherVsn</c> and <c>Descr</c> are the version
+ (<c>UpFromVsn</c> or <c>Vsn</c>) and description
+ (<c>Descr1</c> or <c>Descr2</c>) as specified in the script.</p>
+ <p>If a recoverable error occurs, the function returns
+ <c>{error,Reason}</c> and the original application
+ specifications are restored. If a non-recoverable error
+ occurs, the system is restarted.</p>
+ <p>The option <c>error_action</c> defines if the node should be
+ restarted (<c>init:restart()</c>) or rebooted
+ (<c>init:reboot()</c>) in case of an error during
+ the installation. Default is <c>restart</c>.</p>
+ <p>The option <c>code_change_timeout</c> defines the timeout
+ for all calls to <c>sys:change_code</c>. If no value is
+ specified or <c>default</c> is given, the default value
+ defined in <c>sys</c> is used.</p>
+ <p>The option <c>suspend_timeout</c> defines the timeout for
+ all calls to <c>sys:suspend</c>. If no value is specified,
+ the values defined by the <c>Timeout</c> parameter of
+ the <c>upgrade</c> or <c>suspend</c> instructions are used.
+ If <c>default</c> is specified, the default value defined in
+ <c>sys</c> is used.</p>
+ <p>The option <c>{update_paths,Bool}</c> indicates if all
+ application code paths should be updated (<c>Bool==true</c>),
+ or if only code paths for modified applications should be
+ updated (<c>Bool==false</c>, default). This option only has
+ effect for other application directories than the default
+ <c>$ROOT/lib/App-Vsn</c>, that is, application directories
+ provided in the <c>AppDirs</c> argument in a call to
+ <c>create_RELEASES/4</c> or <c>set_unpacked/2</c>.</p>
+ <p>Example: In the current version <c>CurVsn</c> of a release,
+ the application directory of <c>myapp</c> is
+ <c>$ROOT/lib/myapp-1.0</c>. A new version <c>NewVsn</c> is
+ unpacked outside the release handler, and the release handler
+ is informed about this with a call to:</p>
+ <code type="none">
+release_handler:set_unpacked(RelFile, [{myapp,"1.0","/home/user"},...]).
+=> {ok,NewVsn}
+ </code>
+ <p>If <c>NewVsn</c> is installed with the option
+ <c>{update_paths,true}</c>, afterwards
+ <c>code:lib_dir(myapp)</c> will return
+ <c>/home/user/myapp-1.0</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>make_permanent(Vsn) -> ok | {error, Reason}</name>
+ <fsummary>Make the specified release version permanent.</fsummary>
+ <type>
+ <v>Vsn = string()</v>
+ <v>Reason = {bad_status, Status} | term()</v>
+ </type>
+ <desc>
+ <p>Makes the specified version <c>Vsn</c> of the release
+ permanent.</p>
+ </desc>
+ </func>
+ <func>
+ <name>remove_release(Vsn) -> ok | {error, Reason}</name>
+ <fsummary>Remove a release from the system.</fsummary>
+ <type>
+ <v>Vsn = string()</v>
+ <v>Reason = {permanent, Vsn} | client_node | term()</v>
+ </type>
+ <desc>
+ <p>Removes a release and its files from the system.
+ The release must not be the permanent release. Removes only
+ the files and directories not in use by another release.</p>
+ </desc>
+ </func>
+ <func>
+ <name>reboot_old_release(Vsn) -> ok | {error, Reason}</name>
+ <fsummary>Reboot the system from an old release.</fsummary>
+ <type>
+ <v>Vsn = string()</v>
+ <v>Reason = {bad_status, Status} | term()</v>
+ </type>
+ <desc>
+ <p>Reboots the system by making the old release permanent, and
+ calls <c>init:reboot()</c> directly. The release must have
+ status <c>old</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>set_removed(Vsn) -> ok | {error, Reason}</name>
+ <fsummary>Mark a release as removed.</fsummary>
+ <type>
+ <v>Vsn = string()</v>
+ <v>Reason = {permanent, Vsn} | term()</v>
+ </type>
+ <desc>
+ <p>Makes it possible to handle removal of releases outside
+ the release handler. Tells the release handler that
+ the release is removed from the system. This function does
+ not delete any files.</p>
+ </desc>
+ </func>
+ <func>
+ <name>set_unpacked(RelFile, AppDirs) -> {ok, Vsn} | {error, Reason}</name>
+ <fsummary>Mark a release as unpacked.</fsummary>
+ <type>
+ <v>RelFile = string()</v>
+ <v>AppDirs = [{App, Vsn, Dir}]</v>
+ <v>&nbsp;App = atom()</v>
+ <v>&nbsp;Vsn = Dir = string()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Makes it possible to handle unpacking of releases outside
+ the release handler. Tells the release handler that
+ the release is unpacked. <c>Vsn</c> is extracted from
+ the release resource file <c>RelFile</c>.</p>
+ <p><c>AppDirs</c> can be used to specify from where the modules
+ for the specified applications should be loaded. <c>App</c> is
+ the name of an application, <c>Vsn</c> is the version, and
+ <c>Dir</c> is the name of the directory where <c>App-Vsn</c>
+ is located. The corresponding modules should be located under
+ <c>Dir/App-Vsn/ebin</c>. The directories for applications not
+ specified in <c>AppDirs</c> are assumed to be located in
+ <c>$ROOT/lib</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>unpack_release(Name) -> {ok, Vsn} | {error, Reason}</name>
+ <fsummary>Unpack a release package.</fsummary>
+ <type>
+ <v>Name = Vsn = string()</v>
+ <v>Reason = client_node | term()</v>
+ </type>
+ <desc>
+ <p>Unpacks a release package <c>Name.tar.gz</c> located in
+ the <c>releases</c> directory.</p>
+ <p>Performs some checks on the package - for example checks
+ that all mandatory files are present - and extracts its
+ contents.</p>
+ </desc>
+ </func>
+ <func>
+ <name>which_releases() -> [{Name, Vsn, Apps, Status}]</name>
+ <fsummary>Return all known releases</fsummary>
+ <type>
+ <v>Name = Vsn = string()</v>
+ <v>Apps = ["App-Vsn"]</v>
+ <v>Status = unpacked | current | permanent | old</v>
+ </type>
+ <desc>
+ <p>Returns all releases known to the release handler.</p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>Application Upgrade/Downgrade</title>
+ <p>The following functions can be used to test upgrade and downgrade
+ of single applications (instead of upgrading/downgrading an entire
+ release). A script corresponding to <c>relup</c> is created
+ on-the-fly, based on the <c>.appup</c> file for the application,
+ and evaluated exactly in the same way as <c>release_handler</c>
+ does.</p>
+ <warning>
+ <p>These function is primarily intended for simplified testing of
+ of <c>.appup</c> files. They are not run within the context of
+ the <c>release_handler</c> process. They must therefore
+ <em>not</em> be used together with calls to
+ <c>install_release/1,2</c>, as this will cause
+ <c>release_handler</c> to end up in an inconsistent state.</p>
+ <p>No persistent information is updated, why these functions can
+ be used on any Erlang node, embedded or not. Also, using these
+ functions does not effect which code will be loaded in case of
+ a reboot.</p>
+ <p>If the upgrade or downgrade fails, the application may end up
+ in an inconsistent state.</p>
+ </warning>
+ </section>
+ <funcs>
+ <func>
+ <name>upgrade_app(App, Dir) -> {ok, Unpurged} | restart_new_emulator | {error, Reason}</name>
+ <fsummary>Upgrade to a new application version</fsummary>
+ <type>
+ <v>App = atom()</v>
+ <v>Dir = string()</v>
+ <v>Unpurged = [Module]</v>
+ <v>&nbsp;Module = atom()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Upgrades an application <c>App</c> from the current
+ version to a new version located in <c>Dir</c> according to
+ the <c>.appup</c> script.</p>
+ <p><c>App</c> is the name of the application, which must be
+ started. <c>Dir</c> is the new library directory of
+ <c>App</c>, the corresponding modules as well as
+ the <c>.app</c> and <c>.appup</c> files should be located
+ under <c>Dir/ebin</c>.</p>
+ <p>The function looks in the <c>.appup</c> file and tries to
+ find an upgrade script from the current version of
+ the application using
+ <seealso marker="#upgrade_script/2">upgrade_script/2</seealso>.
+ This script is evaluated using
+ <seealso marker="#eval_appup_script/4">eval_appup_script/4</seealso>,
+ exactly in the same way as
+ <seealso marker="#install_release/1">install_release/1,2</seealso>
+ does.</p>
+ <p>Returns <c>{ok, Unpurged}</c> if evaluating the script is
+ successful, where <c>Unpurged</c> is a list of unpurged
+ modules, or <c>restart_new_emulator</c> if this instruction is
+ encountered in the script, or <c>{error, Reason}</c> if
+ an error occurred when finding or evaluating the script.</p>
+ </desc>
+ </func>
+ <func>
+ <name>downgrade_app(App, Dir) -></name>
+ <name>downgrade_app(App, OldVsn, Dir) -> {ok, Unpurged} | restart_new_emulator | {error, Reason}</name>
+ <fsummary>Downgrade to a previous application version</fsummary>
+ <type>
+ <v>App = atom()</v>
+ <v>Dir = OldVsn = string()</v>
+ <v>Unpurged = [Module]</v>
+ <v>&nbsp;Module = atom()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Downgrades an application <c>App</c> from the current
+ version to a previous version <c>OldVsn</c> located in
+ <c>Dir</c> according to the <c>.appup</c> script.</p>
+ <p><c>App</c> is the name of the application, which must be
+ started. <c>OldVsn</c> is the previous version of
+ the application and can be omitted if <c>Dir</c> is of
+ the format <c>"App-OldVsn"</c>. <c>Dir</c> is the library
+ directory of this previous version of <c>App</c>,
+ the corresponding modules as well as the old <c>.app</c> file
+ should be located under <c>Dir/ebin</c>. The <c>.appup</c>
+ file should be located in the <c>ebin</c> directory of
+ the <em>current</em> library directory of the application
+ (<c>code:lib_dir(App)</c>).</p>
+ <p>The function looks in the <c>.appup</c> file and tries to
+ find an downgrade script to the previous version of
+ the application using
+ <seealso marker="#downgrade_script/3">downgrade_script/3</seealso>.
+ This script is evaluated using
+ <seealso marker="#eval_appup_script/4">eval_appup_script/4</seealso>,
+ exactly in the same way as
+ <seealso marker="#install_release/1">install_release/1,2</seealso>
+ does.</p>
+ <p>Returns <c>{ok, Unpurged}</c> if evaluating the script is
+ successful, where <c>Unpurged</c> is a list of unpurged
+ modules, or <c>restart_new_emulator</c> if this instruction is
+ encountered in the script, or <c>{error, Reason}</c> if
+ an error occurred when finding or evaluating the script.</p>
+ </desc>
+ </func>
+ <func>
+ <name>upgrade_script(App, Dir) -> {ok, NewVsn, Script}</name>
+ <fsummary>Find an application upgrade script</fsummary>
+ <type>
+ <v>App = atom()</v>
+ <v>Dir = string()</v>
+ <v>NewVsn = string()</v>
+ <v>Script = Instructions -- see appup(4)</v>
+ </type>
+ <desc>
+ <p>Tries to find an application upgrade script for <c>App</c>
+ from the current version to a new version located in
+ <c>Dir</c>.</p>
+ <p>The upgrade script can then be evaluated using
+ <seealso marker="#eval_appup_script/4">eval_appup_script/4</seealso>.
+ It is recommended to use
+ <seealso marker="#upgrade_app/2">upgrade_app/2</seealso>
+ instead, but this function is useful in order to inspect
+ the contents of the script.</p>
+ <p><c>App</c> is the name of the application, which must be
+ started. <c>Dir</c> is the new library directory of
+ <c>App</c>, the corresponding modules as well as
+ the <c>.app</c> and <c>.appup</c> files should be located
+ under <c>Dir/ebin</c>.</p>
+ <p>The function looks in the <c>.appup</c> file and tries to
+ find an upgrade script from the current version of
+ the application. High-level instructions are translated to
+ low-level instructions and the instructions are sorted in
+ the same manner as when generating a <c>relup</c> script.</p>
+ <p>Returns <c>{ok, NewVsn, Script}</c> if successful, where
+ <c>NewVsn</c> is the new application version.</p>
+ <p>Failure: If a script cannot be found, the function fails
+ with an appropriate error reason.</p>
+ </desc>
+ </func>
+ <func>
+ <name>downgrade_script(App, OldVsn, Dir) -> {ok, Script}</name>
+ <fsummary>Find an application downgrade script</fsummary>
+ <type>
+ <v>App = atom()</v>
+ <v>OldVsn = Dir = string()</v>
+ <v>Script = Instructions -- see appup(4)</v>
+ </type>
+ <desc>
+ <p>Tries to find an application downgrade script for <c>App</c>
+ from the current version to a previous version <c>OldVsn</c>
+ located in <c>Dir</c>.</p>
+ <p>The downgrade script can then be evaluated using
+ <seealso marker="#eval_appup_script/4">eval_appup_script/4</seealso>.
+ It is recommended to use
+ <seealso marker="#downgrade_app/2">downgrade_app/2,3</seealso>
+ instead, but this function is useful in order to inspect
+ the contents of the script.</p>
+ <p><c>App</c> is the name of the application, which must be
+ started. <c>Dir</c> is the previous library directory of
+ <c>App</c>, the corresponding modules as well as
+ the old <c>.app</c> file should be located under
+ <c>Dir/ebin</c>. The <c>.appup</c> file should be located in
+ the <c>ebin</c> directory of the <em>current</em> library
+ directory of the application (<c>code:lib_dir(App)</c>).</p>
+ <p>The function looks in the <c>.appup</c> file and tries to
+ find an downgrade script from the current version of
+ the application. High-level instructions are translated to
+ low-level instructions and the instructions are sorted in
+ the same manner as when generating a <c>relup</c> script.</p>
+ <p>Returns <c>{ok, Script}</c> if successful.</p>
+ <p>Failure: If a script cannot be found, the function fails
+ with an appropriate error reason.</p>
+ </desc>
+ </func>
+ <func>
+ <name>eval_appup_script(App, ToVsn, ToDir, Script) -> {ok, Unpurged} | restart_new_emulator | {error, Reason}</name>
+ <fsummary>Evaluate an application upgrade or downgrade script</fsummary>
+ <type>
+ <v>App = atom()</v>
+ <v>ToVsn = ToDir = string()</v>
+ <v>Script -- see upgrade_script/2, downgrade_script/3</v>
+ <v>Unpurged = [Module]</v>
+ <v>&nbsp;Module = atom()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Evaluates an application upgrade or downgrade script
+ <c>Script</c>, the result from calling
+ <seealso marker="#upgrade_app/2">upgrade_app/2</seealso> or
+ <seealso marker="#downgrade_app/3">downgrade_app/2,3</seealso>,
+ exactly in the same way as
+ <seealso marker="#install_release/1">install_release/1,2</seealso>
+ does.</p>
+ <p><c>App</c> is the name of the application, which must be
+ started. <c>ToVsn</c> is the version to be upgraded/downgraded
+ to, and <c>ToDir</c> is the library directory of this version.
+ The corresponding modules as well as the <c>.app</c> and
+ <c>.appup</c> files should be located under <c>Dir/ebin</c>.</p>
+ <p>Returns <c>{ok, Unpurged}</c> if evaluating the script is
+ successful, where <c>Unpurged</c> is a list of unpurged
+ modules, or <c>restart_new_emulator</c> if this instruction is
+ encountered in the script, or <c>{error, Reason}</c> if
+ an error occurred when evaluating the script.</p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>Typical Error Reasons</title>
+ <list type="bulleted">
+ <item>
+ <p><c>{bad_masters, Masters}</c> - The master nodes
+ <c>Masters</c> are not alive.</p>
+ </item>
+ <item>
+ <p><c>{bad_rel_file, File}</c> - Specified <c>.rel</c> file
+ <c>File</c> can not be read, or does not contain a single
+ term.</p>
+ </item>
+ <item>
+ <p><c>{bad_rel_data, Data}</c> - Specified <c>.rel</c> file
+ does not contain a recognized release specification, but
+ another term <c>Data</c>.</p>
+ </item>
+ <item>
+ <p><c>{bad_relup_file, File}</c> - Specified <c>relup</c> file
+ <c>Relup</c> contains bad data.</p>
+ </item>
+ <item>
+ <p><c>{cannot_extract_file, Name, Reason}</c> - Problems when
+ extracting from a tar file, <c>erl_tar:extract/2</c> returned
+ <c>{error, {Name, Reason}}</c>.</p>
+ </item>
+ <item>
+ <p><c>{existing_release, Vsn}</c> - Specified release version
+ <c>Vsn</c> is already in use.</p>
+ </item>
+ <item>
+ <p><c>{Master, Reason, When}</c> - Some operation, indicated by
+ the term <c>When</c>, failed on the master node <c>Master</c>
+ with the specified error reason <c>Reason</c>.</p>
+ </item>
+ <item>
+ <p><c>{no_matching_relup, Vsn, CurrentVsn}</c> - Cannot find a
+ script for up/downgrading between <c>CurrentVsn</c> and
+ <c>Vsn</c>.</p>
+ </item>
+ <item>
+ <p><c>{no_such_directory, Path}</c> - The directory <c>Path</c>
+ does not exist.</p>
+ </item>
+ <item>
+ <p><c>{no_such_file, Path}</c> - The path <c>Path</c> (file or
+ directory) does not exist.</p>
+ </item>
+ <item>
+ <p><c>{no_such_file, {Master, Path}}</c> - The path <c>Path</c>
+ (file or directory) does not exist at the master node
+ <c>Master</c>.</p>
+ </item>
+ <item>
+ <p><c>{no_such_release, Vsn}</c> - The specified version
+ <c>Vsn</c> of the release does not exist.</p>
+ </item>
+ <item>
+ <p><c>{not_a_directory, Path}</c> - <c>Path</c> exists, but is
+ not a directory.</p>
+ </item>
+ <item>
+ <p><c>{Posix, File}</c> - Some file operation failed for
+ <c>File</c>. <c>Posix</c> is an atom named from the Posix
+ error codes, such as <c>enoent</c>, <c>eacces</c> or
+ <c>eisdir</c>. See <c>file(3)</c>.</p>
+ </item>
+ <item>
+ <p><c>Posix</c> - Some file operation failed, as above.</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>SEE ALSO</title>
+ <p><seealso marker="doc/design_principles:release_handling">OTP Design Principles</seealso>,
+ <seealso marker="kernel:config">config(4)</seealso>,
+ <seealso marker="relup">relup(4)</seealso>,
+ <seealso marker="rel">rel(4)</seealso>,
+ <seealso marker="script">script(4)</seealso>,
+ <seealso marker="stdlib:sys">sys(3)</seealso>,
+ <seealso marker="systools">systools(3)</seealso></p>
+ </section>
+</erlref>
+
diff --git a/lib/sasl/doc/src/relup.xml b/lib/sasl/doc/src/relup.xml
new file mode 100644
index 0000000000..f7d9fcdd42
--- /dev/null
+++ b/lib/sasl/doc/src/relup.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE fileref SYSTEM "fileref.dtd">
+
+<fileref>
+ <header>
+ <copyright>
+ <year>1997</year>
+ <year>2007</year>
+ <holder>Ericsson AB, All Rights Reserved</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>relup</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <file>relup</file>
+ <filesummary>Release upgrade file</filesummary>
+ <description>
+ <p>The <em>release upgrade file</em> describes how a release is
+ upgraded in a running system.</p>
+ <p>This file is automatically generated by
+ <c>systools:make_relup/3,4</c>, using a release resource file
+ (<c>.rel</c>), application resource files (<c>.app</c>) and
+ application upgrade files (<c>.appup</c>) as input.</p>
+ </description>
+
+ <section>
+ <title>FILE SYNTAX</title>
+ <p>In a target system, the release upgrade file should be located in
+ the <c>OTP_ROOT/erts-EVsn/Vsn</c> directory.</p>
+ <p>The <c>relup</c> file contains one single Erlang term, which
+ defines the instructions used to upgrade the release. The file has
+ the following syntax:</p>
+ <code type="none">
+{Vsn,
+ [{UpFromVsn, Descr, Instructions}, ...],
+ [{DownToVsn, Descr, Instructions}, ...]}.
+ </code>
+ <list type="bulleted">
+ <item>
+ <p><c>Vsn = string()</c> is the current version of the release.</p>
+ </item>
+ <item>
+ <p><c>UpFromVsn = string()</c> is an earlier version of the release
+ to upgrade from.</p>
+ </item>
+ <item>
+ <p><c>Descr = term()</c> is a user defined parameter passed
+ from the <c>systools:make_relup/3,4</c> function. It will
+ be used in the return value of
+ <c>release_handler:install_release/1,2</c>.</p>
+ </item>
+ <item>
+ <p><c>Instructions</c> is a list of low-level release upgrade
+ instructions, see <c>appup(4)</c>.</p>
+ <p>It consists of the release upgrade instructions from
+ the respective application upgrade files (high-level instructions
+ are translated to low-level instructions), in the same order
+ as in the start script.</p>
+ </item>
+ <item>
+ <p><c>DownToVsn = string()</c> is an earlier version of the release
+ to downgrade to.</p>
+ </item>
+ </list>
+ <p>When upgrading from <c>UpFromVsn</c> with
+ <c>release_handler:install_release/1,2</c>, there does not have to be
+ an exact match of versions, but <c>UpFromVsn</c> can be a sub-string
+ of the current release version.</p>
+ </section>
+
+ <section>
+ <title>SEE ALSO</title>
+ <p>app(4), appup(4), rel(4), release_handler(3), systools(3)</p>
+ </section>
+</fileref>
+
diff --git a/lib/sasl/doc/src/sasl_app.xml b/lib/sasl/doc/src/sasl_app.xml
new file mode 100644
index 0000000000..a7fecfc440
--- /dev/null
+++ b/lib/sasl/doc/src/sasl_app.xml
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE appref SYSTEM "appref.dtd">
+
+<appref>
+ <header>
+ <copyright>
+ <year>1996</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>sasl</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <app>sasl</app>
+ <appsummary>The SASL Application</appsummary>
+ <description>
+ <p>This section describes the SASL (System Architecture Support Libraries)
+ application which provides the following services:</p>
+ <list type="bulleted">
+ <item><c>alarm_handler</c></item>
+ <item><c>overload</c></item>
+ <item><c>rb</c></item>
+ <item><c>release_handler</c></item>
+ <item><c>systools</c></item>
+ </list>
+ <p>The SASL application also includes <c>error_logger</c> event
+ handlers for formatting SASL error and crash reports.</p>
+
+ <note>
+ <p>The SASL application in OTP has nothing to do with
+ "Simple Authentication and Security Layer" (RFC 4422).</p>
+ </note>
+
+ </description>
+
+ <section>
+ <title>Error Logger Event Handlers</title>
+ <p>The following error logger event handlers are defined in
+ the SASL application.</p>
+ <taglist>
+ <tag><c>sasl_report_tty_h</c></tag>
+ <item>
+ <p>Formats and writes <em>supervisor reports</em>, <em>crash reports</em> and <em>progress reports</em> to <c>stdio</c>.</p>
+ </item>
+ <tag><c>sasl_report_file_h</c></tag>
+ <item>
+ <p>Formats and writes <em>supervisor reports</em>, <em>crash report</em> and <em>progress report</em> to a single file.</p>
+ </item>
+ <tag><c>error_logger_mf_h</c></tag>
+ <item>
+ <p>This error logger writes <em>all</em> events sent to
+ the error logger to disk. It installs the <c>log_mf_h</c>
+ event handler in the <c>error_logger</c> process.</p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>Configuration</title>
+ <p>The following configuration parameters are defined for the SASL
+ application. See <c>app(4)</c> for more information about
+ configuration parameters:</p>
+ <taglist>
+ <tag><c><![CDATA[sasl_error_logger = Value <optional>]]></c></tag>
+ <item>
+ <p><c>Value</c> is one of:</p>
+ <taglist>
+ <tag><c>tty</c></tag>
+ <item>Installs <c>sasl_report_tty_h</c> in the error logger.
+ This is the default option.</item>
+ <tag><c>{file,FileName}</c></tag>
+ <item>Installs <c>sasl_report_file_h</c> in the error logger.
+ This makes all reports go to the file <c>FileName</c>.
+ <c>FileName</c> is a string.</item>
+ <tag><c>false</c></tag>
+ <item>
+ <p>No SASL error logger handler is installed.</p>
+ </item>
+ </taglist>
+ </item>
+ <tag><c><![CDATA[errlog_type = error | progress | all <optional>]]></c></tag>
+ <item>
+ <p>Restricts the error logging performed by the specified
+ <c>sasl_error_logger</c> to error reports, progress reports,
+ or both. Default is <c>all</c>.</p>
+ </item>
+ <tag><c><![CDATA[error_logger_mf_dir = string() | false<optional>]]></c></tag>
+ <item>
+ <p>Specifies in which directory the files are stored. If this
+ parameter is undefined or <c>false</c>,
+ the <c>error_logger_mf_h</c> is not installed.</p>
+ </item>
+ <tag><c><![CDATA[error_logger_mf_maxbytes = integer() <optional>]]></c></tag>
+ <item>
+ <p>Specifies how large each individual file can be. If this
+ parameter is undefined, the <c>error_logger_mf_h</c> is not
+ installed.</p>
+ </item>
+ <tag><c><![CDATA[error_logger_mf_maxfiles = 0<integer()<256 <optional>]]></c></tag>
+ <item>
+ <p>Specifies how many files are used. If this parameter is
+ undefined, the <c>error_logger_mf_h</c> is not installed.</p>
+ </item>
+ <tag><c><![CDATA[overload_max_intensity = float() > 0 <optional>]]></c></tag>
+ <item>
+ <p>Specifies the maximum intensity for <c>overload</c>. Default
+ is <c>0.8</c>.</p>
+ </item>
+ <tag><c><![CDATA[overload_weight = float() > 0 <optional>]]></c></tag>
+ <item>
+ <p>Specifies the <c>overload</c> weight. Default is <c>0.1</c>.</p>
+ </item>
+ <tag><c><![CDATA[start_prg = string() <optional>]]></c></tag>
+ <item>
+ <p>Specifies which program should be used when restarting
+ the system. Default is <c>$OTP_ROOT/bin/start</c>.</p>
+ </item>
+ <tag><c><![CDATA[masters = [atom()] <optional>]]></c></tag>
+ <item>
+ <p>Specifies which nodes this node uses to read/write release
+ information. This parameter is ignored if
+ the <c>client_directory</c> parameter is not set.</p>
+ </item>
+ <tag><c><![CDATA[client_directory = string() <optional>]]></c></tag>
+ <item>
+ <p>This parameter specifies the client directory at the master
+ nodes. Refer to Release Handling in <em>OTP Design Principles</em> for more information. This parameter is
+ ignored if the <c>masters</c> parameter is not set.</p>
+ </item>
+ <tag><c><![CDATA[static_emulator = true | false <optional>]]></c></tag>
+ <item>
+ <p>Indicates if the Erlang emulator is statically installed. A
+ node with a static emulator cannot switch dynamically to a
+ new emulator as the executable files are written into memory
+ statically. This parameter is ignored if the <c>masters</c>
+ and <c>client_directory</c> parameters are not set.</p>
+ </item>
+ <tag><c><![CDATA[releases_dir = string() <optional>]]></c></tag>
+ <item>
+ <p>Indicates where the <c>releases</c> directory is located.
+ The release handler writes all its files to this directory.
+ If this parameter is not set, the OS environment parameter
+ <c>RELDIR</c> is used. By default, this is
+ <c>$OTP_ROOT/releases</c>.</p>
+ </item>
+ <tag><c><![CDATA[utc_log = true | false <optional>]]></c></tag>
+ <item>
+ <p>If set to <c>true</c>, all dates in textual log outputs are
+ displayed in Universal Coordinated Time with the string
+ <c>UTC</c> appended.</p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>See Also</title>
+ <p><seealso marker="alarm_handler">alarm_handler(3)</seealso>,
+ error_logger(3),
+ log_mf_h(3),
+ <seealso marker="overload">overload(3)</seealso>,
+ <seealso marker="rb">rb(3)</seealso>,
+ <seealso marker="release_handler">release_handler(3)</seealso>,
+ <seealso marker="systools">systools(3)</seealso></p>
+ </section>
+</appref>
+
diff --git a/lib/sasl/doc/src/sasl_intro.xml b/lib/sasl/doc/src/sasl_intro.xml
new file mode 100644
index 0000000000..535f25e044
--- /dev/null
+++ b/lib/sasl/doc/src/sasl_intro.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>1997</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>Introduction</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ <file>sasl_intro.xml</file>
+ </header>
+
+ <section>
+ <title>About This Document</title>
+ <p>The SASL (System Architecture Support Libraries)
+ application provides support for:</p>
+ <list type="bulleted">
+ <item>error logging</item>
+ <item>alarm handling</item>
+ <item>overload regulation</item>
+ <item>release handling</item>
+ <item>report browsing.</item>
+ </list>
+ <p>In this document, "SASL Error Logging" describes the error
+ handler which produces the supervisor, progress, and crash
+ reports which can be written to screen, or to a specified file.
+ It also describes the report browser <c>rb</c>.</p>
+ <p>The chapters about release structure and release handling have
+ been moved to <em>OTP Design Principles</em>.</p>
+ </section>
+</chapter>
+
diff --git a/lib/sasl/doc/src/script.xml b/lib/sasl/doc/src/script.xml
new file mode 100644
index 0000000000..6bac07d106
--- /dev/null
+++ b/lib/sasl/doc/src/script.xml
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE fileref SYSTEM "fileref.dtd">
+
+<fileref>
+ <header>
+ <copyright>
+ <year>1997</year>
+ <year>2007</year>
+ <holder>Ericsson AB, All Rights Reserved</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>script</title>
+ <prepared>Martin Bj&ouml;rklund</prepared>
+ <responsible>Bjarne D&auml;cker</responsible>
+ <docno></docno>
+ <approved>Bjarne D&auml;cker</approved>
+ <checked></checked>
+ <date>97-06-03</date>
+ <rev>A</rev>
+ <file>script.sgml</file>
+ </header>
+ <file>script</file>
+ <filesummary>Boot script</filesummary>
+ <description>
+ <p>The <em>boot script</em> describes how the Erlang runtime system is
+ started. It contains instructions on which code to load and
+ which processes and applications to start.
+ </p>
+ <p>The command <c>erl -boot Name</c> starts the system with a boot
+ file called <c>Name.boot</c>, which is generated from the
+ <c>Name.script</c> file, using <c>systools:script2boot/1</c>.
+ </p>
+ <p>The <c>.script</c> file is generated by <c>systools</c> from a
+ <c>.rel</c> file and <c>.app</c> files.
+ </p>
+ </description>
+
+ <section>
+ <title>FILE SYNTAX</title>
+ <p>The boot script is stored in a file with the extension
+ <c>.script</c></p>
+ <p>The file has the following syntax:
+ </p>
+ <code type="none">
+{script, {Name, Vsn},
+ [
+ {progress, loading},
+ {preLoaded, [Mod1, Mod2, ...]},
+ {path, [Dir1,"$ROOT/Dir",...]}.
+ {primLoad, [Mod1, Mod2, ...]},
+ ...
+ {kernel_load_completed},
+ {progress, loaded},
+ {kernelProcess, Name, {Mod, Func, Args}},
+ ...
+ {apply, {Mod, Func, Args}},
+ ...
+ {progress, started}]}. </code>
+ <list type="bulleted">
+ <item><c>Name = string()</c> defines the name of the system.
+ </item>
+ <item><c>Vsn = string()</c> defines the version of the system.
+ </item>
+ <item><c>{progress, Term}</c> sets the "progress" of the
+ initialization program. The function <c>init:get_status()</c>
+ returns the current value of the progress, which is
+ <c>{InternalStatus,Term}</c>.
+ </item>
+ <item>
+ <p><c>{path, [Dir]}</c> where <c>Dir</c> is a string. This
+ argument sets the load path of the system to <c>[Dir]</c>. The
+ load path used to load modules is obtained from the initial
+ load path, which is given in the script file, together with
+ any path flags which were supplied in the command line
+ arguments. The command line arguments modify the path as
+ follows:</p>
+ <list type="bulleted">
+ <item><c>-pa Dir1 Dir2 ... DirN</c> adds the directories
+ <c>Dir1, Dir2, ..., DirN</c> to the front of the initial
+ load path.
+ </item>
+ <item><c>-pz Dir1 Dir2 ... DirN</c> adds the directories
+ <c>Dir1, Dir2, ..., DirN</c> to the end of the initial
+ load path.
+ </item>
+ <item>
+ <p><c>-path Dir1 Dir2 ... DirN</c> defines a set of
+ directories <c>Dir1, Dir2, ..., DirN</c> which replaces
+ the search path given in the script file. Directory names
+ in the path are interpreted as follows:</p>
+ <list type="bulleted">
+ <item>Directory names starting with <c>/</c> are assumed
+ to be absolute path names.
+ </item>
+ <item>Directory names not starting with <c>/</c> are
+ assumed to be relative the current working directory.
+ </item>
+ <item>The special <c>$ROOT</c> variable can only be used
+ in the script, not as a command line argument. The
+ given directory is relative the Erlang installation
+ directory.
+ </item>
+ </list>
+ </item>
+ </list>
+ </item>
+ <item><c>{primLoad, [Mod]}</c> loads the modules <c>[Mod]</c>
+ from the directories specified in <c>Path</c>. The script
+ interpreter fetches the appropriate module by calling the
+ function <c>erl_prim_loader:get_file(Mod)</c>. A fatal error
+ which terminates the system will occur if the module cannot be
+ located.
+ </item>
+ <item><c>{kernel_load_completed}</c> indicates that all modules
+ which <em>must</em> be loaded <em>before</em> any processes
+ are started are loaded. In interactive mode, all
+ <c>{primLoad,[Mod]}</c> commands interpreted after this
+ command are ignored, and these modules are loaded on demand.
+ In embedded mode, <c>kernel_load_completed</c> is ignored, and
+ all modules are loaded during system start.
+ </item>
+ <item><c>{kernelProcess, Name, {Mod, Func, Args}}</c> starts a
+ "kernel process". The kernel process <c>Name</c> is started
+ by evaluating <c>apply(Mod, Func, Args)</c> which is expected
+ to return <c>{ok, Pid}</c> or <c>ignore</c>. The <c>init</c>
+ process monitors the behaviour of <c>Pid</c> and terminates
+ the system if <c>Pid</c> dies. Kernel processes are key
+ components of the runtime system. Users do not normally add
+ new kernel processes.
+ </item>
+ <item><c>{apply, {Mod, Func, Args}}</c>. The init process simply
+ evaluates <c>apply(Mod, Func, Args)</c>. The system
+ terminates if this results in an error. The boot procedure
+ hangs if this function never returns.
+ </item>
+ </list>
+ <note>
+ <p>In the <c>interactive</c> system the code loader provides
+ demand driven code loading, but in the <c>embedded</c> system
+ the code loader loads all the code immediately. The same
+ version of <c>code</c> is used in both cases. The code server
+ calls <c>init:get_argument(mode)</c> to find out if it should
+ run in demand mode, or non-demand driven mode.
+ </p>
+ </note>
+ </section>
+
+ <section>
+ <title>SEE ALSO</title>
+ <p>systools(3)
+ </p>
+ </section>
+</fileref>
+
diff --git a/lib/sasl/doc/src/systools.xml b/lib/sasl/doc/src/systools.xml
new file mode 100644
index 0000000000..296553bb12
--- /dev/null
+++ b/lib/sasl/doc/src/systools.xml
@@ -0,0 +1,362 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>1996</year>
+ <year>2007</year>
+ <holder>Ericsson AB, All Rights Reserved</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>systools</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <module>systools</module>
+ <modulesummary>A Set of Release Handling Tools.</modulesummary>
+ <description>
+ <p>This module contains functions to generate boot scripts
+ (<c>.boot</c>, <c>.script</c>), release upgrade scripts
+ (<c>relup</c>), and release packages.</p>
+ </description>
+ <funcs>
+ <func>
+ <name>make_relup(Name, UpFrom, DownTo) -> Result</name>
+ <name>make_relup(Name, UpFrom, DownTo, [Opt]) -> Result</name>
+ <fsummary>Generate a release upgrade file <c>relup</c>.</fsummary>
+ <type>
+ <v>Name = string()</v>
+ <v>UpFrom = DownTo = [Name | {Name,Descr}]</v>
+ <v>&nbsp;Descr = term()</v>
+ <v>Opt = {path,[Dir]} | restart_emulator | silent | noexec | {outdir,Dir}</v>
+ <v>&nbsp;Dir = string()</v>
+ <v>Result = ok | error | {ok,Relup,Module,Warnings} | {error,Module,Error}</v>
+ <v>&nbsp;Relup - see relup(4)</v>
+ <v>&nbsp;Module = atom()</v>
+ <v>&nbsp;Warnings = Error = term()</v>
+ </type>
+ <desc>
+ <p>Generates a release upgrade file <c>relup</c> containing a
+ script which describes how to upgrade the system from a number
+ of previous releases, and how to downgrade to a number of
+ previous releases. The script is used by
+ <c>release_handler</c> when installing a new version of a
+ release in run-time.</p>
+ <p>By default, <c>relup</c> is placed in the current working
+ directory. If the option <c>{outdir,Dir}</c> is provided,
+ <c>relup</c> is placed in <c>Dir</c> instead.</p>
+ <p>The release resource file <c>Name.rel</c> is compared with
+ all release resource files <c>Name2.rel</c> specified in
+ <c>UpFrom</c> and <c>DownTo</c>. For each such pair, it is
+ deducted:</p>
+ <list type="bulleted">
+ <item>
+ <p>Which applications should be deleted, that is
+ applications which are listed in <c>Name.rel</c> but not
+ in <c>Name2.rel</c>.</p>
+ </item>
+ <item>
+ <p>Which applications should be added, that is applications
+ which are listed in <c>Name2.rel</c> but not in
+ <c>Name.rel</c>.</p>
+ </item>
+ <item>
+ <p>Which applications should be upgraded/downgraded, that
+ is applications listed in both <c>Name.rel</c> and
+ <c>Name2.rel</c>, but with different versions.</p>
+ </item>
+ <item>
+ <p>If the emulator needs to be restarted after upgrading or
+ downgrading, that is if the ERTS version differs between
+ <c>Name.rel</c> and <c>Name2.rel</c>.</p>
+ </item>
+ </list>
+ <p>Instructions for this are added to the <c>relup</c> script in
+ the above order. Instructions for upgrading or downgrading
+ between application versions are fetched from the relevant
+ application upgrade files <c>App.appup</c>, sorted in
+ the same order as when generating a boot script, see
+ <c>make_script/1,2</c>. High-level instructions are translated
+ into low-level instructions and the result is printed to
+ <c>relup</c>.</p>
+ <p>The optional <c>Descr</c> parameter is included as-is in
+ the <c>relup</c> script, see <c>relup(4)</c>. Defaults to
+ the empty list.</p>
+ <p>All the files are searched for in the code path. It is
+ assumed that the <c>.app</c> and <c>.appup</c> file for an
+ application is located in the same directory.</p>
+ <p>If the option <c>{path,[Dir]}</c> is provided, this path is
+ appended to the current path. The wildcard <c>*</c> is
+ expanded to all matching directories.
+ Example: <c>lib/*/ebin</c>.</p>
+ <p>If the <c>restart_emulator</c> option is supplied, a
+ low-level instruction to restart the emulator is appended to
+ the relup scripts. This ensures that a complete reboot of
+ the system is done when the system is upgraded or downgraded.</p>
+ <p>By default, errors and warnings are printed to tty and
+ the function returns <c>ok</c> or <c>error</c>. If the option
+ <c>silent</c> is provided, the function instead returns
+ <c>{ok,Relup,Module,Warnings}</c> where <c>Relup</c> is
+ the release upgrade script, or it returns
+ <c>{error,Module,Error}</c>. Warnings and errors can be
+ converted to strings by calling
+ <c>Module:format_warning(Warnings)</c> or
+ <c>Module:format_error(Error)</c>.</p>
+ <p>If the option <c>noexec</c> is provided, the function returns
+ the same values as for <c>silent</c> but no <c>relup</c> file
+ is created.</p>
+ </desc>
+ </func>
+ <func>
+ <name>make_script(Name) -> Result</name>
+ <name>make_script(Name, [Opt]) -> Result</name>
+ <fsummary>Generate a boot script <c>.script/.boot</c>.</fsummary>
+ <type>
+ <v>Name = string()</v>
+ <v>Opt = no_module_tests | {path,[Dir]} | local | {variables,[Var]} | exref | {exref,[App]}] | silent | {outdir,Dir}</v>
+ <v>&nbsp;Dir = string()</v>
+ <v>&nbsp;Var = {VarName,Prefix}</v>
+ <v>&nbsp;&nbsp;VarName = Prefix = string()</v>
+ <v>&nbsp;App = atom()</v>
+ <v>Result = ok | error | {ok,Module,Warnings} | {error,Module,Error}</v>
+ <v>&nbsp;Module = atom()</v>
+ <v>&nbsp;Warnings = Error = term()</v>
+ </type>
+ <desc>
+ <p>Generates a boot script <c>Name.script</c> and its binary
+ version, the boot file <c>Name.boot</c>. The boot file
+ specifies which code should be loaded and which applications
+ should be started when the Erlang runtime system is started.
+ See <c>script(4)</c>.</p>
+ <p>The release resource file <c>Name.rel</c> is read to find
+ out which applications are included in the release. Then
+ the relevant application resource files <c>App.app</c> are
+ read to find out which modules should be loaded and if and
+ how the application should be started. (Keys <c>modules</c>
+ and <c>mod</c>, see <c>app(4)</c>).</p>
+ <p>By default, the boot script and boot file are placed in
+ the same directory as <c>Name.rel</c>. That is, in the current
+ working directory unless <c>Name</c> contains a path. If
+ the option <c>{outdir,Dir}</c> is provided, they are placed
+ in <c>Dir</c> instead.</p>
+ <p>The correctness of each application is checked:</p>
+ <list type="bulleted">
+ <item>
+ <p>The version of an application specified in
+ the <c>.rel</c> file should be the same as the version
+ specified in the <c>.app</c> file.</p>
+ </item>
+ <item>
+ <p>There should be no undefined applications, that is,
+ dependencies to applications which are not included in
+ the release. (Key <c>applications</c> in <c>.app</c>
+ file).</p>
+ </item>
+ <item>
+ <p>There should be no circular dependencies among
+ the applications.</p>
+ </item>
+ <item>
+ <p>There should no duplicated modules, that is, modules with
+ the same name but belonging to different applications.</p>
+ </item>
+ <item>
+ <p>A warning is issued if the source code for a module is
+ missing or newer than the object code. <br></br>
+
+ If the <c>no_module_tests</c> option is specified, this
+ check is omitted.</p>
+ </item>
+ </list>
+ <p>The applications are sorted according to the dependencies
+ between the applications. Where there are no dependencies,
+ the order in the <c>.rel</c> file is kept.</p>
+ <p>All files are searched for in the current path. It is
+ assumed that the <c>.app</c> and <c>.beam</c> files for an
+ application is located in the same directory. The <c>.erl</c>
+ files are also assumed to be located in this directory, unless
+ it is an <c>ebin</c> directory in which case they may be
+ located in the corresponding <c>src</c> directory.</p>
+ <p>If the option <c>{path,[Dir]}</c> is provided, this path is
+ appended to the current path. A directory in the path can be
+ given with a wildcard <c>*</c>, this is expanded to all
+ matching directories. Example: <c>"lib/*/ebin"</c>.</p>
+ <p>In the generated boot script all application directories are
+ structured as <c>App-Vsn/ebin</c> and assumed to be located
+ in <c>$ROOT/lib</c>, where <c>$ROOT</c> is the root directory
+ of the installed release. If the <c>local</c> option is
+ supplied, the actual directories where the applications were
+ found are used instead. This is a useful way to test a
+ generated boot script locally.</p>
+ <p>The <c>variables</c> option can be used to specify an
+ installation directory other than <c>$ROOT/lib</c> for some of
+ the applications. If a variable <c>{VarName,Prefix}</c> is
+ specified and an application is found in a directory
+ <c>Prefix/Rest/App[-Vsn]/ebin</c>, this application will get
+ the path <c>VarName/Rest/App-Vsn/ebin</c> in the boot script.
+ If an application is found in a directory <c>Prefix/Rest</c>,
+ the path will be <c>VarName/Rest/App-Vsn/ebin</c>. When
+ starting Erlang, all variables <c>VarName</c> are given
+ values using the <c>boot_var</c> command line flag.</p>
+ <p>Example: If the option <c>{variables,[{"TEST","lib"}]}</c> is
+ supplied, and <c>myapp.app</c> is found in
+ <c>lib/myapp/ebin</c>, then the path to this application in
+ the boot script will be <c>$TEST/myapp-1/ebin"</c>. If
+ <c>myapp.app</c> is found in <c>lib/test</c>, then the path
+ will be <c>$TEST/test/myapp-1/ebin</c>.</p>
+ <p>The checks performed before the boot script is generated can
+ be extended with some cross reference checks by specifying
+ the <c>exref</c> option. These checks are performed with
+ the Xref tool. All applications, or the applications specified
+ with <c>{exref,[App]}</c>, are checked by Xref and
+ warnings are generated for calls to undefined functions.</p>
+ <p>By default, errors and warnings are printed to tty and
+ the function returns <c>ok</c> or <c>error</c>. If the option
+ <c>silent</c> is provided, the function instead returns
+ <c>{ok,Module,Warnings}</c> or <c>{error,Module,Error}</c>.
+ Warnings and errors can be converted to strings by calling
+ <c>Module:format_warning(Warnings)</c> or
+ <c>Module:format_error(Error)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>make_tar(Name) -> Result</name>
+ <name>make_tar(Name, [Opt]) -> Result</name>
+ <fsummary>Create a release package.</fsummary>
+ <type>
+ <v>Name = string()</v>
+ <v>Opt = {dirs,[IncDir]} | {path,[Dir]} | {variables,[Var]} | {var_tar,VarTar} | {erts,Dir} | no_module_tests | exref | {exref,[App]} | silent | {outdir,Dir}</v>
+ <v>&nbsp;Dir = string()</v>
+ <v>&nbsp;IncDir = src | include | atom()</v>
+ <v>&nbsp;Var = {VarName,PreFix}</v>
+ <v>&nbsp;&nbsp;VarName = Prefix = string()</v>
+ <v>&nbsp;VarTar = include | ownfile | omit</v>
+ <v>&nbsp;Machine = atom()</v>
+ <v>&nbsp;App = atom()</v>
+ <v>Result = ok | error | {ok,Module,Warnings} | {error,Module,Error}</v>
+ <v>&nbsp;Module = atom()</v>
+ <v>&nbsp;Warning = Error = term()</v>
+ </type>
+ <desc>
+ <p>Creates a release package file <c>Name.tar.gz</c>. file.
+ This file must be uncompressed and unpacked on the target
+ system using the <c>release_handler</c>, before the new
+ release can be installed.</p>
+ <p>The release resource file <c>Name.rel</c> is read to find out
+ which applications are included in the release. Then
+ the relevant application resource files <c>App.app</c> are
+ read to find out the version and modules of each application.
+ (Keys <c>vsn</c> and <c>modules</c>, see <c>app(4)</c>).</p>
+ <p>By default, the release package file is placed in the same
+ directory as <c>Name.rel</c>. That is, in the current working
+ directory unless <c>Name</c> contains a path. If the option
+ <c>{outdir,Dir}</c> is provided, it is placed in <c>Dir</c>
+ instead.</p>
+ <p>By default, the release package contains the directories
+ <c>lib/App-Vsn/ebin</c> and <c>lib/App-Vsn/priv</c> for each
+ included application. If more directories, the option
+ <c>dirs</c> is supplied. Example:
+ <c>{dirs,[src,examples]}</c>.</p>
+ <p>All these files are searched for in the current path. If
+ the option <c>{path,[Dir]}</c> is provided, this path is
+ appended to the current path. The wildcard <c>*</c> is
+ expanded to all matching directories.
+ Example: <c>"lib/*/ebin"</c>.</p>
+ <p>The <c>variables</c> option can be used to specify an
+ installation directory other than <c>lib</c> for some of
+ the applications. If a variable <c>{VarName,Prefix}</c> is
+ specified and an application is found in a directory
+ <c>Prefix/Rest/App[-Vsn]/ebin</c>, this application will be
+ packed into a separate <c>VarName.tar.gz</c> file as
+ <c>Rest/App-Vsn/ebin</c>.</p>
+ <p>Example: If the option <c>{variables,[{"TEST","lib"}]}</c> is
+ supplied, and <c>myapp.app</c> is found in
+ <c>lib/myapp-1/ebin</c>, the the application <c>myapp</c> is
+ included in <c>TEST.tar.gz</c>:</p>
+ <pre>
+% <input>tar tf TEST.tar</input>
+myapp-1/ebin/myapp.app
+...
+ </pre>
+ <p>The <c>{var_tar,VarTar}</c> option can be used to specify if
+ and where a separate package should be stored. In this option,
+ <c>VarTar</c> is:</p>
+ <list type="bulleted">
+ <item>
+ <p><c>include</c>. Each separate (variable) package is
+ included in the main <c>ReleaseName.tar.gz</c> file. This
+ is the default.</p>
+ </item>
+ <item>
+ <p><c>ownfile</c>. Each separate (variable) package is
+ generated as separate files in the same directory as
+ the <c>ReleaseName.tar.gz</c> file.</p>
+ </item>
+ <item>
+ <p><c>omit</c>. No separate (variable) packages are
+ generated and applications which are found underneath a
+ variable directory are ignored.</p>
+ </item>
+ </list>
+ <p>A directory called <c>releases</c> is also included in
+ the release package, containing <c>Name.rel</c> and a
+ subdirectory called <c>RelVsn</c>. <c>RelVsn</c> is
+ the release version as specified in <c>Name.rel</c>.</p>
+ <p><c>releases/RelVsn</c> contains the boot script
+ <c>Name.boot</c> renamed to <c>start.boot</c> and, if found,
+ the files <c>relup</c> and <c>sys.config</c>. These files
+ are searched for in the same directory as <c>Name.rel</c>,
+ in the current working directory, and in any directories
+ specified using the <c>path</c> option.</p>
+ <p>If the release package should contain a new Erlang runtime
+ system, the <c>bin</c> directory of the specified runtime
+ system <c>{erts,Dir}</c> is copied to <c>erts-ErtsVsn/bin</c>.</p>
+ <p>All checks performed with the <c>make_script</c> function
+ are performed before the release package is created. The
+ <c>no_module_tests</c> and <c>exref</c> options are also
+ valid here.</p>
+ <p>The return value and the handling of errors and warnings
+ are the same as described for <c>make_script</c> above.</p>
+ </desc>
+ </func>
+ <func>
+ <name>script2boot(File) -> ok | error</name>
+ <fsummary>Generate a binary version of a boot script.</fsummary>
+ <type>
+ <v>File = string()</v>
+ </type>
+ <desc>
+ <p>The Erlang runtime system requires that the contents of
+ the script used to boot the system is a binary Erlang term.
+ This function transforms the <c>File.script</c> boot script
+ to a binary term which is stored in the file <c>File.boot</c>.</p>
+ <p>A boot script generated using the <c>make_script</c>
+ function is already transformed to the binary form.</p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>SEE ALSO</title>
+ <p>app(4), appup(4), erl(1), rel(4), release_handler(3), relup(4),
+ script(4)</p>
+ </section>
+</erlref>
+
diff --git a/lib/sasl/doc/src/warning.gif b/lib/sasl/doc/src/warning.gif
new file mode 100644
index 0000000000..96af52360e
--- /dev/null
+++ b/lib/sasl/doc/src/warning.gif
Binary files differ
diff --git a/lib/sasl/ebin/.gitignore b/lib/sasl/ebin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/sasl/ebin/.gitignore
diff --git a/lib/sasl/include/.gitignore b/lib/sasl/include/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/sasl/include/.gitignore
diff --git a/lib/sasl/info b/lib/sasl/info
new file mode 100644
index 0000000000..479392c2ed
--- /dev/null
+++ b/lib/sasl/info
@@ -0,0 +1,3 @@
+group: basic
+short: The System Architecture Support Libraries is a set of tools for
+short: release upgrades and alarm handling etc.
diff --git a/lib/sasl/priv/mibs/.gitignore b/lib/sasl/priv/mibs/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/sasl/priv/mibs/.gitignore
diff --git a/lib/sasl/priv/test/.gitignore b/lib/sasl/priv/test/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/sasl/priv/test/.gitignore
diff --git a/lib/sasl/src/Makefile b/lib/sasl/src/Makefile
new file mode 100644
index 0000000000..9a5d1e42d9
--- /dev/null
+++ b/lib/sasl/src/Makefile
@@ -0,0 +1,100 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(SASL_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/sasl-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+MODULES= alarm_handler sasl sasl_report \
+ sasl_report_file_h sasl_report_tty_h format_lib_supp \
+ misc_supp overload rb rb_format_supp release_handler \
+ release_handler_1 si si_sasl_supp systools \
+ systools_make systools_rc systools_relup systools_lib \
+ erlsrv
+
+HRL_FILES=
+
+INTERNAL_HRL_FILES= systools.hrl
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+APP_FILE= sasl.app
+APPUP_FILE= sasl.appup
+
+APP_SRC= $(APP_FILE).src
+APPUP_SRC= $(APPUP_FILE).src
+
+APP_TARGET= $(EBIN)/$(APP_FILE)
+APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_COMPILE_FLAGS += -I../../stdlib/include
+
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES)
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+
+release_docs_spec:
diff --git a/lib/sasl/src/alarm_handler.erl b/lib/sasl/src/alarm_handler.erl
new file mode 100644
index 0000000000..b118a8cafd
--- /dev/null
+++ b/lib/sasl/src/alarm_handler.erl
@@ -0,0 +1,95 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(alarm_handler).
+
+%%%-----------------------------------------------------------------
+%%% This is the SASL alarm handler process.
+%%% It is a gen_event process. When it is started, a simple
+%%% event handler which logs all alarms is installed.
+%%%-----------------------------------------------------------------
+-export([start_link/0, set_alarm/1, clear_alarm/1, get_alarms/0,
+ add_alarm_handler/1, add_alarm_handler/2,
+ delete_alarm_handler/1]).
+
+-export([init/1,
+ handle_event/2, handle_call/2, handle_info/2,
+ terminate/2]).
+
+start_link() ->
+ case gen_event:start_link({local, alarm_handler}) of
+ {ok, Pid} ->
+ gen_event:add_handler(alarm_handler, alarm_handler, []),
+ {ok, Pid};
+ Error -> Error
+ end.
+
+%%-----------------------------------------------------------------
+%% Func: set_alarm/1
+%% Args: Alarm ::= {AlarmId, term()}
+%% where AlarmId ::= term()
+%%-----------------------------------------------------------------
+set_alarm(Alarm) ->
+ gen_event:notify(alarm_handler, {set_alarm, Alarm}).
+
+%%-----------------------------------------------------------------
+%% Func: clear_alarm/1
+%% Args: AlarmId ::= term()
+%%-----------------------------------------------------------------
+clear_alarm(AlarmId) ->
+ gen_event:notify(alarm_handler, {clear_alarm, AlarmId}).
+
+%%-----------------------------------------------------------------
+%% Func: get_alarms/0
+%% Returns: [{AlarmId, AlarmDesc}]
+%%-----------------------------------------------------------------
+get_alarms() ->
+ gen_event:call(alarm_handler, alarm_handler, get_alarms).
+
+add_alarm_handler(Module) when is_atom(Module) ->
+ gen_event:add_handler(alarm_handler, Module, []).
+
+add_alarm_handler(Module, Args) when is_atom(Module) ->
+ gen_event:add_handler(alarm_handler, Module, Args).
+
+delete_alarm_handler(Module) when is_atom(Module) ->
+ gen_event:delete_handler(alarm_handler, Module, []).
+
+%%-----------------------------------------------------------------
+%% Default Alarm handler
+%%-----------------------------------------------------------------
+init(_) -> {ok, []}.
+
+handle_event({set_alarm, Alarm}, Alarms)->
+ error_logger:info_report([{alarm_handler, {set, Alarm}}]),
+ {ok, [Alarm | Alarms]};
+handle_event({clear_alarm, AlarmId}, Alarms)->
+ error_logger:info_report([{alarm_handler, {clear, AlarmId}}]),
+ {ok, lists:keydelete(AlarmId, 1, Alarms)};
+handle_event(_, Alarms)->
+ {ok, Alarms}.
+
+handle_info(_, Alarms) -> {ok, Alarms}.
+
+handle_call(get_alarms, Alarms) -> {ok, Alarms, Alarms};
+handle_call(_Query, Alarms) -> {ok, {error, bad_query}, Alarms}.
+
+terminate(swap, Alarms) ->
+ {alarm_handler, Alarms};
+terminate(_, _) ->
+ ok.
diff --git a/lib/sasl/src/erlsrv.erl b/lib/sasl/src/erlsrv.erl
new file mode 100644
index 0000000000..f9804c41dc
--- /dev/null
+++ b/lib/sasl/src/erlsrv.erl
@@ -0,0 +1,420 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(erlsrv).
+
+%% Purpose : Control the external erlsrv program.
+
+%%-compile(export_all).
+-export([get_all_services/0,get_service/1,get_service/2,store_service/1,
+ store_service/2,
+ new_service/3, new_service/4, disable_service/2,
+ enable_service/2, disable_service/1, enable_service/1,
+ remove_service/1, erlsrv/1, rename_service/2,
+ rename_service/3]).
+
+erlsrv(EVer) ->
+ Root = code:root_dir(),
+ filename:join([Root, "erts-" ++ EVer, "bin", "erlsrv.exe"]).
+
+current_version() ->
+ hd(string:tokens(erlang:system_info(version),"_ ")).
+
+%%% Returns {ok, Output} | failed | {error, Reason}
+run_erlsrv(Command) ->
+ run_erlsrv(current_version(),Command).
+run_erlsrv(EVer, Command) ->
+ case catch(open_port({spawn, erlsrv(EVer) ++ " " ++ Command},
+ [{line,1000}, in, eof])) of
+ {'EXIT',{Reason,_}} ->
+ {port_error, Reason};
+ Port ->
+ case read_all_data(Port) of
+ [] ->
+ failed;
+ X ->
+ {ok, X}
+ end
+ end.
+
+run_erlsrv_interactive(EVer, Commands) ->
+ case catch(open_port({spawn, erlsrv(EVer) ++ " readargs"},
+ [{line,1000}, eof])) of
+ {'EXIT',{Reason,_}} ->
+ {port_error, Reason};
+ Port ->
+ write_all_data(Port, Commands),
+ case read_all_data(Port) of
+ [] ->
+ failed;
+ X ->
+ {ok, X}
+ end
+ end.
+
+write_all_data(Port,[]) ->
+ Port ! {self(), {command, io_lib:nl()}},
+ ok;
+write_all_data(Port,[H|T]) ->
+ Port ! {self(), {command, H ++ io_lib:nl()}},
+ write_all_data(Port,T).
+
+read_all_data(Port) ->
+ receive
+ {Port, {data, {eol,Data}}} ->
+ [ Data | read_all_data(Port)];
+ _ ->
+ Port ! {self(), close},
+ receive
+ {Port, closed} ->
+ []
+ end
+ end.
+
+
+%%% Get all registered erlsrv services.
+get_all_services() ->
+ case run_erlsrv("list") of
+ failed ->
+ [];
+ {ok, [_]} ->
+ [];
+ {ok, [_H|T]} ->
+ F = fun(X) ->
+ hd(string:tokens(X,"\t "))
+ end,
+ lists:map(F,T);
+ _ ->
+ {error, external_program_failed}
+ end.
+
+disable_service(ServiceName) ->
+ disable_service(current_version(), ServiceName).
+disable_service(EVer, ServiceName) ->
+ run_erlsrv(EVer, "disable " ++ ServiceName).
+enable_service(ServiceName) ->
+ enable_service(current_version(), ServiceName).
+enable_service(EVer, ServiceName) ->
+ run_erlsrv(EVer, "enable " ++ ServiceName).
+remove_service(ServiceName) ->
+ run_erlsrv("remove " ++ ServiceName).
+rename_service(FromName, ToName) ->
+ rename_service(current_version(), FromName, ToName).
+rename_service(EVer, FromName, ToName) ->
+ run_erlsrv(EVer, "rename " ++ FromName ++ " " ++ ToName).
+
+%%% Get all information about a service
+%%% Returns [{Field,Value | []} ...]
+%%% Field is one of:
+%%% servicename : The service name (equal to parameter...)
+%%% stopaction : The erlang expression that shall stop the node
+%%% onfail : Action to take when erlang fails unexpectedly
+%%% machine : Full pathname of the erlang machine or start_erl program
+%%% workdir : The initial working directory of the erlang machine
+%%% sname | name : The short name of the node
+%%% priority : The OS priority of the erlang process
+%%% args : All arguments correctly parsed into a list of strings
+%%% comment : The service description
+%%% internalservicename : The windows internal service name
+%%% env : A list of environment variables and values [{"VAR", "VALUE"}]
+%%% Example:
+%%% [{servicename,"kalle_R4A"},
+%%% {stopaction,"erlang:halt()."},
+%%% {args,["-boot", "nisse","--","-reldir", "c:\myapproot"]}
+%%% {env,[{"FOO","BAR"},{"VEGETABLE","TOMATO"}]}]
+
+get_service(ServiceName) ->
+ get_service(current_version(), ServiceName).
+get_service(EVer, ServiceName) ->
+ case run_erlsrv(EVer, "list " ++ ServiceName) of
+ failed ->
+ {error, no_such_service};
+ {port_error, Reason} ->
+ {error, {port_error, Reason}};
+ {ok, Data} ->
+ Table = [{"Service name",servicename,[]},
+ {"StopAction",stopaction, []},
+ {"OnFail",onfail, "ignore"},
+ {"Machine",machine, []},
+ {"WorkDir",workdir, []},
+ {"SName",sname, []},
+ {"Name",name, []},
+ {"Priority",priority, "default"},
+ {"DebugType",debugtype, "none"},
+ {"Args",args,[]},
+ {"InternalServiceName",internalservicename,[]},
+ {"Comment",comment,[]}],
+ %% Env has special treatment...
+ F = fun(X) ->
+ {Name,Value} = splitline(X),
+ case lists:keysearch(Name,1,Table) of
+ {value,{Name,_Atom,Value}} ->
+ [];
+ {value,{Name,Atom,_}} ->
+ {Atom,Value};
+ _ ->
+ []
+ end
+ end,
+ %%% First split by Env:
+ {Before, After} = split_by_env(Data),
+ FirstPass = lists:flatten(lists:map(F,Before)),
+ %%% If the arguments are there, split them to
+ SecondPass = split_arglist(FirstPass),
+ %%% And now, if After contains anything, that is vwat to
+ %%% have in the environment list...
+ EnvParts = lists:map(
+ fun(S) ->
+ X = string:strip(S,left,$\t),
+ case hd(string:tokens(X,"=")) of
+ X ->
+ %% Can this happen?
+ {X,""};
+ Y ->
+ {Y,
+ lists:sublist(X,length(Y)+2,
+ length(X))}
+ end
+ end,
+ After),
+ case EnvParts of
+ [] ->
+ SecondPass;
+ _ ->
+ lists:append(SecondPass,[{env,EnvParts}])
+ end
+ end.
+
+
+store_service(Service) ->
+ store_service(current_version(),Service).
+store_service(EmulatorVersion,Service) ->
+ case lists:keysearch(servicename,1,Service) of
+ false ->
+ {error, no_servicename};
+ {value, {_,Name}} ->
+ {Action,Service1} = case get_service(Name) of
+ {error, no_such_service} ->
+ {"add",Service};
+ _ ->
+ {"set",
+ lists:keydelete(internalservicename,1,Service)}
+ end,
+ Commands = [Action | build_commands(Name, Service1)],
+ case run_erlsrv_interactive(EmulatorVersion,Commands) of
+ {ok, _} ->
+ ok;
+ X ->
+ {error, X}
+ end;
+ _ ->
+ {error, malformed_description}
+ end.
+
+build_commands(Action, Service) ->
+ [ Action | lists:reverse(build_commands2(Service,[]))].
+
+build_commands2([],A) ->
+ A;
+build_commands2([{env,[]}|T],A) ->
+ build_commands2(T,A);
+build_commands2([{env,[{Var,Val}|Et]}|T],A) ->
+ build_commands2([{env,Et}|T],[Var ++ "=" ++ Val, "-env" | A]);
+build_commands2([{servicename,_}|T],A) ->
+ build_commands2(T,A);
+build_commands2([{Atom,[]} | T],A) ->
+ build_commands2(T,["-" ++ atom_to_list(Atom) | A]);
+build_commands2([{args,L}|T],A) ->
+ build_commands2(T,[concat_args(L),"-args"| A]);
+build_commands2([{Atom,Value} | T],A) ->
+ build_commands2(T,[Value, "-" ++ atom_to_list(Atom) | A]).
+
+concat_args([H|T]) ->
+ H ++ concat_args2(T).
+concat_args2([]) ->
+ "";
+concat_args2([H|T]) ->
+ " " ++ H ++ concat_args2(T).
+
+
+new_service(NewServiceName, OldService, Data) ->
+ new_service(NewServiceName, OldService, Data, []).
+new_service(NewServiceName, OldService, Data, RestartName) ->
+ Tmp0 = lists:keydelete(internalservicename,1,OldService), %Remove when
+ % creating new service from
+ % old.
+ Tmp1 = lists:keyreplace(servicename, 1, Tmp0,
+ {servicename, NewServiceName}),
+ Tmp = case lists:keysearch(env,1,Tmp1) of
+ {value, {env,Env0}} ->
+ Env1 = lists:keydelete("ERLSRV_SERVICE_NAME",1,Env0),
+ lists:keyreplace(env,1,Tmp1,
+ {env, [{"ERLSRV_SERVICE_NAME",
+ RestartName} |
+ Env1]});
+ _ ->
+ Tmp1
+ end,
+
+ ArgsTmp = case lists:keysearch(args, 1, Tmp) of
+ false ->
+ [];
+ {value, {args, OldArgs}} ->
+ OldArgs
+ end,
+ Args = backstrip(ArgsTmp,"++"), %% Remove trailing ++, has no meaning
+ {Found, Tail} = lists:foldr(fun(A,{Flag,AccIn}) ->
+ case {Flag, A} of
+ {true, _} -> {Flag,AccIn};
+ {false, "++"} -> {true, AccIn};
+ _ -> {false, [A|AccIn]}
+ end
+ end, {false,[]}, Args),
+
+ {OtherFlags, _DataDir} = case Found of
+ true ->
+ check_tail(Tail);
+ false ->
+ {[], false}
+ end,
+ NewArgs1 = case Data of
+ [] ->
+ OtherFlags;
+ _ ->
+ ["-data", Data| OtherFlags]
+ end,
+ case Found of
+ false ->
+ A = case NewArgs1 of
+ [] ->
+ [];
+ _ ->
+ ["++" | NewArgs1]
+ end,
+ case {Args,A} of
+ {[],[]} ->
+ Tmp;
+ {[],_} ->
+ Tmp ++ [{args, A}];
+ {_,_} ->
+ lists:keyreplace(args, 1, Tmp, {args, Args ++ A})
+ end;
+ true ->
+ StripArgs = backstrip(Args,["++"|Tail]),
+ NewArgs2 = case NewArgs1 of
+ [] ->
+ [];
+ _ ->
+ ["++" |NewArgs1]
+ end,
+ NewArgs = StripArgs ++ NewArgs2,
+ lists:keyreplace(args, 1, Tmp, {args, NewArgs})
+ end.
+
+
+backstrip(List,Tail) ->
+ lists:reverse(backstrip2(lists:reverse(List),lists:reverse(Tail))).
+backstrip2([A|T1],[A|T2]) ->
+ backstrip2(T1,T2);
+backstrip2(L,_) ->
+ L.
+
+check_tail(Tail) ->
+ {A,B} = check_tail(Tail, [], false),
+ {lists:reverse(A),B}.
+
+check_tail([], OtherFlags, DataDir) ->
+ {OtherFlags, DataDir};
+check_tail(["-data", TheDataDir|T], OtherFlags, _DataDir) ->
+ check_tail(T, OtherFlags, TheDataDir);
+check_tail([H|T],OtherFlags,DataDir) ->
+ check_tail(T,[H|OtherFlags],DataDir).
+
+
+
+
+%%% Recursive, The list is small
+split_arglist([]) ->
+ [];
+split_arglist([{args,Str}|T]) ->
+ [{args,parse_arglist(Str)}|T];
+split_arglist([H|T]) ->
+ [H|split_arglist(T)].
+
+%% Not recursive, may be long...
+parse_arglist(Str) ->
+ lists:reverse(parse_arglist(Str,[])).
+parse_arglist(Str,Accum) ->
+ Stripped = string:strip(Str,left),
+ case length(Stripped) of
+ 0 ->
+ Accum;
+ _ ->
+ {Next, Rest} = pick_argument(Str),
+ parse_arglist(Rest,[Next | Accum])
+ end.
+
+pick_argument(Str) ->
+ {Rev,Rest} = pick_argument(normal,Str,[]),
+ {lists:reverse(Rev),Rest}.
+
+pick_argument(_,[],Acc) ->
+ {Acc, ""};
+pick_argument(normal,[$ |T],Acc) ->
+ {Acc,T};
+pick_argument(normal,[$"|T],Acc) ->
+ pick_argument(quoted,T,[$"|Acc]);
+pick_argument(quoted_escaped,[H|T],Acc) ->
+ pick_argument(quoted,T,[H|Acc]);
+pick_argument(quoted,[$"|T],Acc) ->
+ pick_argument(normal,T,[$"|Acc]);
+pick_argument(quoted,[$\\|T],Acc) ->
+ pick_argument(quoted_escaped,T,[$\\|Acc]);
+pick_argument(quoted,[H|T],Acc) ->
+ pick_argument(quoted,T,[H|Acc]);
+pick_argument(normal,[H|T],Acc) ->
+ pick_argument(normal,T,[H|Acc]).
+
+split_helper("Env:",{Where,0}) ->
+ {Where + 1, Where};
+split_helper(_, {Where,Pos}) ->
+ {Where + 1, Pos}.
+
+split_by_env(Data) ->
+ %%% Find Env...
+ case lists:foldl(fun split_helper/2,{0,0},Data) of
+ {_,0} ->
+ %% Not found, hmmmm....
+ {Data,[]};
+ {Len,Pos} ->
+ {lists:sublist(Data,Pos),lists:sublist(Data,Pos+2,Len)}
+ end.
+
+
+splitline(Line) ->
+ case string:chr(Line,$:) of
+ 0 ->
+ {Line, ""};
+ N ->
+ case length(string:substr(Line,N)) of
+ 1 ->
+ {string:substr(Line,1,N-1),""};
+ _ ->
+ {string:substr(Line,1,N-1),string:substr(Line,N+2)}
+ end
+ end.
diff --git a/lib/sasl/src/format_lib_supp.erl b/lib/sasl/src/format_lib_supp.erl
new file mode 100644
index 0000000000..af15fd3288
--- /dev/null
+++ b/lib/sasl/src/format_lib_supp.erl
@@ -0,0 +1,224 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(format_lib_supp).
+
+%%%---------------------------------------------------------------------
+%%% Description:
+%%% This module contains generic formatting functions for the SUPPort
+%%% tools.
+%%% The main parts are:
+%%% 1) print_info. Prints information tagged by 'header', 'data',
+%%% 'table', 'items' and 'newline'.
+%%%---------------------------------------------------------------------
+
+%% intermodule exports
+-export([print_info/2, print_info/3]).
+
+%% exports for use within module
+-export([maxcol/2]).
+
+%%---------------------------------------------------------------------
+%% Format is an ordered list of:
+%% {header, HeaderString}
+%% {data, List_Of_KeyValue_tuples}
+%% The KeyValues_tuples will be printed on one line
+%% (if possible); 'Key: Value'.
+%% Elements in the list may also be single terms, which are
+%% printed as they are.
+%% {table, {TableName, ColumnNames, Columns}}
+%% ColumnNames is a tuple of names for the columns, and
+%% Columns is a list, where each element is a tuple of
+%% data for that column.
+%% {items, {Name, Items}}
+%% Items is a list of KeyValue_tuples. Will be printed as:
+%% 'Name:
+%% Key1: Value1
+%% KeyN: ValueN'
+%% {newline, N}
+%% Any other format will be ignored.
+%% This list is printed in order. If the header clause is present,
+%% it must be the first element in the format list.
+%% ------------------------------------------------------------------
+print_info(Device, Format) ->
+ print_info(Device, 79, Format).
+print_info(Device, Line, Format) ->
+ print_header(Device, Line, Format),
+ print_format(Device, Line, Format).
+
+print_header(Device, Line, [{header, Header}|_]) ->
+ print_header2(Device, Line, Header);
+print_header(Device, Line, _) ->
+ print_header2(Device, Line, "").
+print_header2(Device, Line, Header) ->
+ Format1 = lists:concat(["~n~", Line, ".", Line, "s~n"]),
+ Format2 = lists:concat(["~", Line, "c~n"]),
+ io:format(Device, Format1, [Header]),
+ io:format(Device, Format2, [$=]).
+
+print_format(Device, _Line, []) ->
+ io:format(Device, '~n', []);
+print_format(Device, Line, [{data, Data}|T]) ->
+ print_data(Device, Line, Data),
+ print_format(Device, Line, T);
+print_format(Device, Line, [{table, Table}|T]) ->
+ print_table(Device, Line, Table),
+ print_format(Device, Line, T);
+print_format(Device, Line, [{items, Items}|T]) ->
+ print_items(Device, Line, Items),
+ print_format(Device, Line, T);
+print_format(Device, Line, [{newline, N}|T]) ->
+ print_newlines(Device, N),
+ print_format(Device, Line, T);
+print_format(Device, Line, [_|T]) -> % ignore any erroneous format.
+ print_format(Device, Line, T).
+
+print_data(_Device, _Line, []) -> ok;
+print_data(Device, Line, [{Key, Value}|T]) ->
+ print_one_line(Device, Line, Key, Value),
+ print_data(Device, Line, T);
+print_data(Device, Line, [Value|T]) ->
+ io:format(Device, "~p~n", [Value]),
+ print_data(Device, Line, T).
+
+print_items(Device, Line, {Name, Items}) ->
+ print_items(Device, Line, Name, Items).
+
+print_table(Device, Line, {TableName, ColumnNames, Columns}) ->
+ print_table(Device, Line, TableName, ColumnNames, Columns).
+
+print_newlines(_Device, 0) -> ok;
+print_newlines(Device, N) when N > 0 ->
+ io:format(Device, '~n', []),
+ print_newlines(Device, N-1).
+
+print_one_line(Device, Line, Key, Value) ->
+ StrKey = term_to_string(Key),
+ KeyLen = lists:min([length(StrKey), Line]),
+ ValueLen = Line - KeyLen,
+ Format1 = lists:concat(["~-", KeyLen, s]),
+ Format2 = lists:concat(["~", ValueLen, "s~n"]),
+ io:format(Device, Format1, [StrKey]),
+ Try = term_to_string(Value),
+ Length = length(Try),
+ if
+ Length < ValueLen ->
+ io:format(Device, Format2, [Try]);
+ true ->
+ io:format(Device, "~n ", []),
+ Format3 = lists:concat(["~", Line, ".9p~n"]),
+ io:format(Device, Format3, [Value])
+ end.
+
+term_to_string(Value) ->
+ lists:flatten(io_lib:format(get_format(Value), [Value])).
+
+get_format(Value) ->
+ case misc_supp:is_string(Value) of
+ true -> "~s";
+ false -> "~p"
+ end.
+
+make_list(0, _Elem) -> [];
+make_list(N, Elem) -> [Elem|make_list(N-1, Elem)].
+
+
+%%-----------------------------------------------------------------
+%% Items
+%%-----------------------------------------------------------------
+print_items(Device, Line, Name, Items) ->
+ print_one_line(Device, Line, Name, " "),
+ print_item_elements(Device, Line, Items).
+
+print_item_elements(_Device, _Line, []) -> ok;
+print_item_elements(Device, Line, [{Key, Value}|T]) ->
+ print_one_line(Device, Line, lists:concat([" ", Key]), Value),
+ print_item_elements(Device, Line, T).
+
+%%-----------------------------------------------------------------
+%% Table handling
+%%-----------------------------------------------------------------
+extra_space_between_columns() -> 3.
+
+find_max_col([Row | T], ColumnSizes) ->
+ find_max_col(T, misc_supp:multi_map({format_lib_supp, maxcol},
+ [Row, ColumnSizes]));
+
+find_max_col([], ColumnSizes) -> ColumnSizes.
+
+maxcol(Term, OldMax) ->
+ lists:max([length(term_to_string(Term)), OldMax]).
+
+make_column_format(With) ->
+ lists:concat(["~", With + extra_space_between_columns(), s]).
+
+is_correct_column_length(_Length, []) -> true;
+is_correct_column_length(Length, [Tuple|T]) ->
+ case size(Tuple) of
+ Length -> is_correct_column_length(Length, T);
+ _ -> false
+ end;
+is_correct_column_length(_, _) -> false.
+
+print_table(Device, Line, TableName, _TupleOfColumnNames, []) ->
+ print_one_line(Device, Line, TableName, "<empty table>"),
+ io:format(Device, "~n", []);
+
+print_table(Device, Line, TableName, TupleOfColumnNames, ListOfTuples)
+ when is_list(ListOfTuples), is_tuple(TupleOfColumnNames) ->
+ case is_correct_column_length(size(TupleOfColumnNames),
+ ListOfTuples) of
+ true ->
+ print_one_line(Device, Line, TableName, " "),
+ ListOfColumnNames = tuple_to_list(TupleOfColumnNames),
+ ListOfLists = lists:map(fun(Tuple) ->
+ tuple_to_list(Tuple)
+ end,
+ ListOfTuples),
+ ColWidths = find_max_col([ListOfColumnNames |
+ ListOfLists],
+ make_list(length(ListOfColumnNames),0)),
+ Format = lists:flatten([lists:map(fun(CW) ->
+ make_column_format(CW)
+ end,
+ ColWidths), "~n"]),
+ io:format(Device, Format, ListOfColumnNames),
+ io:format(Device,
+ lists:concat(['~', extra_space_between_columns(),
+ 'c', '~', lists:sum(ColWidths)
+ + (length(ColWidths) - 1)
+ * extra_space_between_columns(),
+ 'c~n']), [$ , $-]),
+ lists:foreach(fun(List) ->
+ print_row(List, Device, Format)
+ end,
+ ListOfLists),
+ io:format(Device, '~n', []),
+ true;
+ false ->
+ {error, {'a tuple has wrong size',
+ {TableName, TupleOfColumnNames, ListOfTuples}}}
+ end.
+
+%%--------------------------------------------------
+%% Device MUST be 2nd arg because of extraarg ni foreach...
+%%--------------------------------------------------
+print_row(Row, Device, Format) ->
+ io:format(Device, Format,
+ lists:map(fun(Term) -> term_to_string(Term) end,
+ Row)).
diff --git a/lib/sasl/src/misc_supp.erl b/lib/sasl/src/misc_supp.erl
new file mode 100644
index 0000000000..8948fdb797
--- /dev/null
+++ b/lib/sasl/src/misc_supp.erl
@@ -0,0 +1,106 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(misc_supp).
+
+%%%---------------------------------------------------------------------
+%%% Description:
+%%% This module contains MISCellaneous routines for the SUPPort tools.
+%%% 1) The function format_pdict/3 is called by every process that
+%%% wants to format its process dictionary.
+%%% 2) Very generic functions such as, multi_map, is_string...
+%%%
+%%% This module is a part of the BOS. (format_pdict is called from
+%%% init, memsup, disksup, overload (but not the fileserver since it
+%%% formats its pdict its own way).)
+%%%---------------------------------------------------------------------
+
+-export([format_pdict/3, format_tuples/2, assq/2, passq/2, is_string/1,
+ multi_map/2]).
+
+%%-----------------------------------------------------------------
+%% Uses format_tuples to format the data in process dictionary.
+%% This function is called from format_status_info by several modules
+%% that want to format its process dictionary.
+%% Args: Exclude is: list of atoms to exclude
+%%-----------------------------------------------------------------
+format_pdict(normal, _PDict, _Exclude) ->
+ [];
+format_pdict(all, PDict, Exclude) ->
+ case format_tuples(PDict, ['$sys_dict$' | Exclude]) of
+ [] -> [];
+ Data -> [{newline, 1} | Data]
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Format all Key value tuples except for the Keys in the
+%% Exclude list.
+%%-----------------------------------------------------------------
+format_tuples(KeyValues, Exclude) ->
+ case format_tuples(KeyValues, Exclude, []) of
+ [] -> [];
+ Data -> [{data, Data}]
+ end.
+format_tuples([], _Exclude, Res) -> Res;
+format_tuples([{Key, Value} | T], Exclude, Res) ->
+ case lists:member(Key, Exclude) of
+ true ->
+ format_tuples(T, Exclude, Res);
+ false ->
+ format_tuples(T, Exclude, [{Key, Value} | Res])
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% "Very" generic misc stuff:
+%%--------------------------------------------------
+
+assq(Key, List) ->
+ case lists:keysearch(Key, 1, List) of
+ {value, {Key, Val}} -> {value, Val};
+ _ -> false
+ end.
+
+%% Primitive assq. Use to get items from a process dictionary list.
+passq(Key, List) ->
+ case lists:keysearch(Key, 1, List) of
+ {value, {Key, Val}} -> Val;
+ _ -> undefined
+ end.
+
+%% This one doesn't treat [] as a string (as io_lib:char_list)
+is_string([]) -> false;
+is_string(X) -> is_string_2(X).
+
+is_string_2([]) -> true;
+is_string_2([H|T]) when is_integer(H), H >= $ , H =< 255 ->
+ is_string_2(T);
+is_string_2(_) -> false.
+
+%%-----------------------------------------------------------------
+%% Pre: ListOfLists is a list of N lists, each of length M.
+%% Func is a function of arity N.
+%% Returns: A list of length M where element Y is the result of
+%% applying Func on [Elem(Y, List1), ..., Elem(Y, ListN)].
+%%-----------------------------------------------------------------
+multi_map(_Func, [[] | _ListOfLists]) -> [];
+multi_map(Func, ListOfLists) ->
+ [apply(Func, lists:map(fun(List) -> hd(List) end, ListOfLists)) |
+ multi_map(Func,
+ lists:map(fun(List) -> tl(List) end, ListOfLists))].
diff --git a/lib/sasl/src/overload.erl b/lib/sasl/src/overload.erl
new file mode 100644
index 0000000000..3a9a51e8bf
--- /dev/null
+++ b/lib/sasl/src/overload.erl
@@ -0,0 +1,224 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(overload).
+
+-export([start_link/0, request/0, set_config_data/2,
+ get_overload_info/0]).
+
+-export([init/1, handle_call/3, handle_info/2, terminate/2,
+ format_status/2]).
+
+%%%-----------------------------------------------------------------
+%%% This is a rewrite of overload from BS.3, by Peter H�gfeldt.
+%%%
+%%% DESCRIPTION
+%%%
+%%% This module implements a server process that keeps record of the
+%%% intensity of calls of the request/0 function, and answers accept or
+%%% reject depending on if the current average intensity is not greater
+%%% than a specified maximum intensity.
+%%%
+%%% The intensity i is calculated according to the formula:
+%%% i(n) = exp(-K*(T(n) - T(n-1)))*i(n-1) + Kappa
+%%% where i(n) is the intensity at event n, Kappa is a constant, and
+%%% T(n) is the time at event n.
+%%%
+%%% The constant Kappa can be thought of as 1 / T, where T is the time
+%%% constant. Kappa is externally referred to as Weight.
+%%%
+%%% We keep track of two intensities: the total call intensity and the
+%%% intensity of accepted calls.
+%%%-----------------------------------------------------------------
+%%% TODO
+%%%
+%%% 3. Hysteresis.
+%%%
+%%%-----------------------------------------------------------------
+
+-record(state, {total = 0, accept = 0, max, prev_t = get_now(),
+ kappa, call_counts = {0, 0}, alarm = clear}).
+
+-define(clear_timeout, 30000).
+
+start_link() ->
+ gen_server:start_link({local, overload}, overload, [], []).
+
+init([]) ->
+ process_flag(priority, high),
+ MaxIntensity = fetch_config_data(overload_max_intensity),
+ Kappa = fetch_config_data(overload_weight),
+ {ok, #state{max = MaxIntensity, kappa = Kappa}}.
+
+%%-----------------------------------------------------------------
+%% Func: request/0
+%% Purpose: This is a request to proceed, e.g. a request to
+%% establish a call.
+%% Returns: accept | reject
+%%-----------------------------------------------------------------
+request() -> gen_server:call(overload, request).
+
+%%-----------------------------------------------------------------
+%% Func: set_config_data/2
+%% Purpose: Set configuration data, and reset intensities.
+%% Arguments: MaxIntensity (real > 0), Weight (real > 0).
+%% Returns: ok | {error, What}
+%% This function is for debugging purposes and is therefore not
+%% documented at all.
+%%-----------------------------------------------------------------
+set_config_data(MaxIntensity, Weight) ->
+ gen_server:call(overload, {set_config_data, MaxIntensity, Weight}).
+%%-----------------------------------------------------------------
+%% Func: get_overload_info/0
+%% Returns: A list of tagged items: TotalIntensity, AcceptIntensity,
+%% MaxIntensity, Weight, TotalRequests, AcceptedRequests.
+%%-----------------------------------------------------------------
+get_overload_info() -> gen_server:call(overload, get_overload_info).
+
+%%%-----------------------------------------------------------------
+%%% Callback functions from gen_server
+%%%-----------------------------------------------------------------
+handle_call(request, _From, State) ->
+ #state{total = TI, accept = AI, kappa = Kappa, prev_t = PrevT,
+ alarm = Alarm} = State,
+ {TR, AR} = State#state.call_counts,
+ T = get_now(),
+ CurI = new_intensity(AI, T, PrevT, Kappa),
+ NewTI = new_intensity(TI, T, PrevT, Kappa) + Kappa,
+ if
+ CurI =< State#state.max ->
+ %% Hysteresis: If alarm is set, and current intensity has
+ %% fallen below 75% of max intensity, clear alarm.
+ NewAlarm = if
+ CurI =< 0.75*State#state.max ->
+ clear_alarm(Alarm);
+ true ->
+ Alarm
+ end,
+ {reply, accept, State#state{call_counts = {TR+1, AR+1},
+ prev_t = T, total = NewTI,
+ accept = CurI + Kappa,
+ alarm = NewAlarm},
+ ?clear_timeout};
+ true ->
+ %% Set alarm if not already set.
+ NewAlarm = set_alarm(Alarm),
+ {reply, reject,
+ State#state{call_counts = {TR+1, AR}, prev_t = T,
+ total = NewTI, accept = CurI,
+ alarm = NewAlarm},
+ ?clear_timeout}
+ end;
+handle_call({set_config_data, MaxIntensity, Weight}, _From, _State) ->
+ {reply, ok, #state{max = MaxIntensity, kappa = Weight},
+ ?clear_timeout};
+handle_call(get_overload_info, _From, State) ->
+ #state{max = MI, total = TI, accept = AI, kappa = Kappa,
+ prev_t = PrevT, call_counts = {TR, AR}} = State,
+ T = get_now(),
+ CurI = new_intensity(AI, T, PrevT, Kappa),
+ NewTI = new_intensity(TI, T, PrevT, Kappa),
+ Reply = [{total_intensity, NewTI}, {accept_intensity, CurI},
+ {max_intensity, MI}, {weight, Kappa},
+ {total_requests, TR}, {accepted_requests, AR}],
+ {reply, Reply, State#state{total = NewTI, accept = CurI},
+ ?clear_timeout}.
+
+handle_info(timeout, State) ->
+ #state{total = TI, accept = AI, kappa = Kappa, prev_t = PrevT,
+ alarm = Alarm} = State,
+ T = get_now(),
+ CurI = new_intensity(AI, T, PrevT, Kappa),
+ NewTI = new_intensity(TI, T, PrevT, Kappa),
+ if
+ CurI < 0.75* State#state.max ->
+ NewAlarm = clear_alarm(Alarm),
+ {noreply, State#state{total = NewTI, accept = CurI,
+ alarm = NewAlarm}};
+
+ true ->
+ {noreply, State#state{total = NewTI, accept = CurI},
+ ?clear_timeout}
+ end;
+
+handle_info(_, State) ->
+ {noreply, State, ?clear_timeout}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+%%-----------------------------------------------------------------
+%% Internal functions
+%%-----------------------------------------------------------------
+fetch_config_data(Tag) ->
+ case application:get_env(sasl, Tag) of
+ {ok, Value} -> Value;
+ _ -> fetch_default_data(Tag)
+ end.
+
+fetch_default_data(overload_max_intensity) -> 0.8;
+fetch_default_data(overload_weight) -> 0.1.
+
+set_alarm(clear) ->
+ alarm_handler:set_alarm({overload, []}),
+ set;
+set_alarm(Alarm) ->
+ Alarm.
+
+clear_alarm(set) ->
+ alarm_handler:clear_alarm(overload),
+ clear;
+clear_alarm(Alarm) ->
+ Alarm.
+
+%%-----------------------------------------------------------------
+%% The catch protects against floating-point exception.
+%%
+new_intensity(I, T, PrevT, K) ->
+ Diff = sub(T, PrevT)/1000,
+ case catch (I*math:exp(-K*Diff)) of
+ {'EXIT', _} -> % Assume zero.
+ 0.0;
+ Res ->
+ Res
+ end.
+
+%% Mask equal to 2^27 - 1, used below.
+-define(mask27, 16#7ffffff).
+
+%% Returns number of milliseconds in the range [0, 2^27 - 1]. Must have
+%% this since statistics(wall_clock) wraps. Having 2^27 -1 as the max
+%% assures that we always get non-negative integers. 2^27 milliseconds
+%% are approx. 37.28 hours.
+get_now() ->
+ element(1, statistics(wall_clock)) band ?mask27.
+
+%% Returns (X - Y) mod 2^27 (which is in the range [0, 2^27 - 1]).
+sub(X, Y) ->
+ (X + (bnot Y) + 1) band ?mask27.
+
+format_status(Opt, [PDict, #state{max = MI, total = TI, accept = AI,
+ kappa = K,
+ call_counts = {TR, AR}}]) ->
+ [{data, [{"Total Intensity", TI},
+ {"Accept Intensity", AI},
+ {"Max Intensity", MI},
+ {"Weight", K},
+ {"Total requests", TR},
+ {"Accepted requests", AR}]} |
+ misc_supp:format_pdict(Opt, PDict, [])].
diff --git a/lib/sasl/src/rb.erl b/lib/sasl/src/rb.erl
new file mode 100644
index 0000000000..00d86285e5
--- /dev/null
+++ b/lib/sasl/src/rb.erl
@@ -0,0 +1,697 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(rb).
+
+-behaviour(gen_server).
+
+%% External exports
+-export([start/0, start/1, stop/0, rescan/0, rescan/1]).
+-export([list/0, list/1, show/0, show/1, grep/1, start_log/1, stop_log/0]).
+-export([h/0, help/0]).
+
+%% Internal exports
+-export([start_link/1]).
+
+%% gen_server callbacks
+-export([init/1, terminate/2, handle_call/3,
+ handle_cast/2, handle_info/2, code_change/3]).
+
+%%%-----------------------------------------------------------------
+%%% Report Browser Tool.
+%%% Formats Error reports written by log_mf_h
+%%%-----------------------------------------------------------------
+
+-record(state, {dir, data, device, max, type, abort, log}).
+
+%%-----------------------------------------------------------------
+%% Interface functions.
+%% For available options; see print_options().
+%%-----------------------------------------------------------------
+start() -> start([]).
+start(Options) ->
+ supervisor:start_child(sasl_sup,
+ {rb_server, {rb, start_link, [Options]},
+ temporary, brutal_kill, worker, [rb]}).
+
+start_link(Options) ->
+ gen_server:start_link({local, rb_server}, rb, Options, []).
+
+stop() ->
+ gen_server:call(rb_server, stop),
+ supervisor:delete_child(sasl_sup, rb_server).
+
+rescan() -> rescan([]).
+rescan(Options) ->
+ gen_server:call(rb_server, {rescan, Options}, infinity).
+
+list() -> list(all).
+list(Type) -> gen_server:call(rb_server, {list, Type}, infinity).
+
+show() ->
+ gen_server:call(rb_server, show, infinity).
+
+show(Number) when is_integer(Number) ->
+ gen_server:call(rb_server, {show_number, Number}, infinity);
+show(Type) when is_atom(Type) ->
+ gen_server:call(rb_server, {show_type, Type}, infinity).
+
+grep(RegExp) -> gen_server:call(rb_server, {grep, RegExp}, infinity).
+
+start_log(FileName) -> gen_server:call(rb_server, {start_log, FileName}).
+
+stop_log() -> gen_server:call(rb_server, stop_log).
+
+h() -> help().
+help() ->
+ io:format("~nReport Browser Tool - usage~n"),
+ io:format("===========================~n"),
+ io:format("rb:start() - start the rb_server with default options~n"),
+ io:format("rb:start(Options) - where Options is a list of:~n"),
+ print_options(),
+ io:format("rb:h() - print this help~n"),
+ io:format("rb:help() - print this help~n"),
+ io:format("rb:list() - list all reports~n"),
+ io:format("rb:list(Type) - list all reports of type Type~n"),
+ io:format(" currently supported types are:~n"),
+ print_types(),
+ io:format("rb:grep(RegExp) - print reports containing RegExp~n"),
+ io:format("rb:rescan() - rescans the report directory with same~n"),
+ io:format(" options.~n"),
+ io:format("rb:rescan(Options) - rescans the report directory with new~n"),
+ io:format(" options. Options is same as in start/1.~n"),
+ io:format("rb:show(Number) - print report no Number~n"),
+ io:format("rb:show(Type) - print all reports of type Type~n"),
+ io:format("rb:show() - print all reports~n"),
+ io:format("rb:start_log(File) - redirect all reports to file~n"),
+ io:format("rb:stop_log() - close the log file and redirect to~n"),
+ io:format(" standard_io~n"),
+ io:format("rb:stop - stop the rb_server~n").
+
+%%-----------------------------------------------------------------
+%% Internal functions.
+%%-----------------------------------------------------------------
+%%-----------------------------------------------------------------
+%% MAKE SURE THESE TWO FUNCTIONS ARE UPDATED!
+%%-----------------------------------------------------------------
+print_options() ->
+ io:format(" {start_log, FileName}~n"),
+ io:format(" - default: standard_io~n"),
+ io:format(" {max, MaxNoOfReports}~n"),
+ io:format(" - MaxNoOfReports should be an integer or 'all'~n"),
+ io:format(" - default: all~n"),
+ io:format(" {report_dir, DirString}~n"),
+ io:format(" - DirString should be a string without trailing~n"),
+ io:format(" - directory delimiter.~n"),
+ io:format(" - default: {sasl, error_logger_mf_dir}~n"),
+ io:format(" {type, ReportType}~n"),
+ io:format(" - ReportType should be a supported type, 'all'~n"),
+ io:format(" - or a list of supported types~n"),
+ io:format(" - default: all~n"),
+ io:format(" {abort_on_error, Bool}~n"),
+ io:format(" - Bool: true | false~n"),
+ io:format(" - default: false~n").
+
+print_types() ->
+ io:format(" - crash_report~n"),
+ io:format(" - supervisor_report~n"),
+ io:format(" - progress~n"),
+ io:format(" - error~n").
+
+
+init(Options) ->
+ process_flag(priority, low),
+ process_flag(trap_exit, true),
+ Log = get_option(Options, start_log, standard_io),
+ Device = open_log_file(Log),
+ Dir = get_report_dir(Options),
+ Max = get_option(Options, max, all),
+ Type = get_option(Options, type, all),
+ Abort = get_option(Options, abort_on_error, false),
+ Data = scan_files(Dir ++ "/", Max, Type),
+ {ok, #state{dir = Dir ++ "/", data = Data, device = Device,
+ max = Max, type = Type, abort = Abort, log = Log}}.
+
+handle_call({rescan, Options}, _From, State) ->
+ {Device,Log1} =
+ case get_option(Options, start_log, {undefined}) of
+ {undefined} ->
+ {State#state.device,State#state.log};
+ Log ->
+ close_device(State#state.device),
+ {open_log_file(Log),Log}
+ end,
+ Max = get_option(Options, max, State#state.max),
+ Type = get_option(Options, type, State#state.type),
+ Abort = get_option(Options, abort_on_error, false),
+ Data = scan_files(State#state.dir, Max, Type),
+ NewState = State#state{data = Data, max = Max, type = Type,
+ device = Device, abort = Abort, log = Log1},
+ {reply, ok, NewState};
+handle_call(stop, _From, State) ->
+ {stop, normal, stopped, State};
+handle_call(_, _From, #state{data = undefined}) ->
+ {reply, {error, no_data}, #state{}};
+handle_call({list, Type}, _From, State) ->
+ print_list(State#state.data, Type),
+ {reply, ok, State};
+handle_call({start_log, FileName}, _From, State) ->
+ NewDevice = open_log_file(FileName),
+ {reply, ok, State#state{device = NewDevice}};
+handle_call(stop_log, _From, State) ->
+ close_device(State#state.device),
+ {reply, ok, State#state{device = standard_io}};
+handle_call({show_number, Number}, _From, State) ->
+ #state{dir = Dir, data = Data, device = Device, abort = Abort, log = Log} = State,
+ NewDevice = print_report_by_num(Dir, Data, Number, Device, Abort, Log),
+ {reply, ok, State#state{device = NewDevice}};
+handle_call({show_type, Type}, _From, State) ->
+ #state{dir = Dir, data = Data, device = Device, abort = Abort, log = Log} = State,
+ NewDevice = print_typed_reports(Dir, Data, Type, Device, Abort, Log),
+ {reply, ok, State#state{device = NewDevice}};
+handle_call(show, _From, State) ->
+ #state{dir = Dir, data = Data, device = Device, abort = Abort, log = Log} = State,
+ NewDevice = print_all_reports(Dir, Data, Device, Abort, Log),
+ {reply, ok, State#state{device = NewDevice}};
+handle_call({grep, RegExp}, _From, State) ->
+ #state{dir = Dir, data = Data, device = Device, abort = Abort, log = Log} = State,
+ NewDevice = print_grep_reports(Dir, Data, RegExp, Device, Abort, Log),
+ {reply, ok, State#state{device = NewDevice}}.
+
+terminate(_Reason, #state{device = Device}) ->
+ close_device(Device).
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+handle_info(_Info, State) ->
+ {noreply, State}.
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%-----------------------------------------------------------------
+%% Func: open_log_file/1
+%% Args: FileName | standard_io
+%% Returns: A Device for later use in call to io:format
+%%-----------------------------------------------------------------
+open_log_file(standard_io) -> standard_io;
+open_log_file(FileName) ->
+ case file:open(FileName, [write,append]) of
+ {ok, Fd} -> Fd;
+ Error ->
+ io:format("rb: Cannot open file '~s' (~w).~n",
+ [FileName, Error]),
+ io:format("rb: Using standard_io~n"),
+ standard_io
+ end.
+
+close_device(Fd) when is_pid(Fd) ->
+ catch file:close(Fd);
+close_device(_) -> ok.
+
+get_option(Options, Key, Default) ->
+ case lists:keysearch(Key, 1, Options) of
+ {value, {_Key, Value}} -> Value;
+ _ -> Default
+ end.
+
+get_report_dir(Options) ->
+ case lists:keysearch(report_dir, 1, Options) of
+ {value, {_Key, RptDir}} -> RptDir;
+ _ ->
+ case catch application:get_env(sasl, error_logger_mf_dir) of
+ {ok, Dir} -> Dir;
+ _ ->
+ exit("cannot locate report directory")
+ end
+ end.
+
+%%-----------------------------------------------------------------
+%% Func: scan_files(RptDir, Max, Type)
+%% Args: RptDir ::= string().
+%% Max ::= integer() | all, describing how many reports
+%5 to read.
+%% Type ::= atom(), describing which reports to read.
+%% Purpose: Scan all report files one time, and build a list of
+%% small elements
+%% Returns: Data, where Data is a list of
+%% {Number, Type, ShortDescr, Date, Fname, FilePosition}.
+%%-----------------------------------------------------------------
+scan_files(RptDir, Max, Type) ->
+ case file:open(RptDir ++ "/index", [raw, read]) of
+ {ok, Fd} ->
+ case catch file:read(Fd, 1) of
+ {ok, [LastWritten]} ->
+ Files = make_file_list(RptDir, LastWritten),
+ scan_files(RptDir, Files, Max, Type);
+ _ -> exit("cannot read the index file")
+ end;
+ _ -> exit("cannot read the index file")
+ end.
+
+make_file_list(Dir, FirstFileNo) ->
+ case file:list_dir(Dir) of
+ {ok, FileNames} ->
+ FileNumbers = lists:zf(fun(Name) ->
+ case catch list_to_integer(Name) of
+ Int when is_integer(Int) ->
+ {true, Int};
+ _ ->
+ false
+ end
+ end,
+ FileNames),
+ shift(lists:sort(FileNumbers), FirstFileNo);
+ _ -> exit({bad_directory, Dir})
+ end.
+
+shift(List, First) ->
+ shift(List, First, []).
+
+shift([H | T], H, Res) ->
+ [H | Res] ++ lists:reverse(T);
+shift([H | T], First, Res) ->
+ shift(T, First, [H | Res]);
+shift([], _, Res) ->
+ Res.
+
+%%-----------------------------------------------------------------
+%% Func: scan_files(Dir, Files, Max, Type)
+%% Args: Files is a list of FileName.
+%% Purpose: Scan the report files in the index variable.
+%% Returns: {Number, Type, ShortDescr, Date, FileName, FilePosition}
+%%-----------------------------------------------------------------
+scan_files(Dir, Files, Max, Type) ->
+ scan_files(Dir, 1, Files, [], Max, Type).
+scan_files(_Dir, _, [], Res, _Max, _Type) -> Res;
+scan_files(_Dir, _, _Files, Res, Max, _Type) when Max =< 0 -> Res;
+scan_files(Dir, No, [H|T], Res, Max, Type) ->
+ Data = get_report_data_from_file(Dir, No, H, Max, Type),
+ Len = length(Data),
+ NewMax = dec_max(Max, Len),
+ NewNo = No + Len,
+ NewData = Data ++ Res,
+ scan_files(Dir, NewNo, T, NewData, NewMax, Type).
+
+dec_max(all, _) -> all;
+dec_max(X,Y) -> X-Y.
+
+get_report_data_from_file(Dir, No, FileNr, Max, Type) ->
+ Fname = integer_to_list(FileNr),
+ FileName = lists:concat([Dir, Fname]),
+ case file:open(FileName, [read]) of
+ {ok, Fd} when is_pid(Fd) -> read_reports(No, Fd, Fname, Max, Type);
+ _ -> [{No, unknown, "Can't open file " ++ Fname, "???", Fname, 0}]
+ end.
+
+%%-----------------------------------------------------------------
+%% Func: read_reports(No, Fd, Fname, Max, Type)
+%% Purpose: Read reports from one report file.
+%% Returns: A list of {No, Type, ShortDescr, Date, FileName, FilePosition}
+%% Note: We have to read all reports, and then check the max-
+%% variable, because the reports are reversed on the file, and
+%% we may need the last ones.
+%%-----------------------------------------------------------------
+read_reports(No, Fd, Fname, Max, Type) ->
+ io:format("rb: reading report..."),
+ case catch read_reports(Fd, [], Type) of
+ {ok, Res} ->
+ file:close(Fd),
+ io:format("done.~n"),
+ NewRes =
+ if
+ length(Res) > Max ->
+ lists:sublist(Res, 1, Max);
+ true ->
+ Res
+ end,
+ add_report_data(NewRes, No, Fname);
+ {error, [Problem | Res]} ->
+ file:close(Fd),
+ io:format("Error: ~p~n",[Problem]),
+ io:format("Salvaged ~p entries from corrupt report file ~s...~n",
+ [length(Res),Fname]),
+ NewRes =
+ if
+ length([Problem|Res]) > Max ->
+ lists:sublist([Problem|Res], 1, Max);
+ true ->
+ [Problem|Res]
+ end,
+ add_report_data(NewRes, No, Fname);
+ Else ->
+ io:format("err ~p~n", [Else]),
+ [{No, unknown, "Can't read reports from file " ++ Fname,
+ "???", Fname, 0}]
+ end.
+
+%%-----------------------------------------------------------------
+%% Func: add_report_data(Res, No, FName)
+%% Args: Res is a list of {Type, ShortDescr, Date, FilePos}
+%% Purpose: Convert a list of {Type, ShortDescr, Date, FilePos} to
+%% a list of {No, Type, ShortDescr, Date, FileName, FilePos}
+%% Returns: A list of {No, Type, ShortDescr, Date, FileName, FilePos}
+%%-----------------------------------------------------------------
+add_report_data(Res, No, FName) ->
+ add_report_data(Res, No, FName, []).
+add_report_data([{Type, ShortDescr, Date, FilePos}|T], No, FName, Res) ->
+ add_report_data(T, No+1, FName,
+ [{No, Type, ShortDescr, Date, FName, FilePos}|Res]);
+add_report_data([], _No, _FName, Res) -> Res.
+
+read_reports(Fd, Res, Type) ->
+ {ok, FilePos} = file:position(Fd, cur),
+ case catch read_report(Fd) of
+ {ok, Report} ->
+ RealType = get_type(Report),
+ {ShortDescr, Date} = get_short_descr(Report),
+ Rep = {RealType, ShortDescr, Date, FilePos},
+ if
+ Type == all->
+ read_reports(Fd, [Rep | Res], Type);
+ RealType == Type ->
+ read_reports(Fd, [Rep | Res], Type);
+ is_list(Type) ->
+ case lists:member(RealType, Type) of
+ true ->
+ read_reports(Fd, [Rep | Res], Type);
+ _ ->
+ read_reports(Fd, Res, Type)
+ end;
+ true ->
+ read_reports(Fd, Res, Type)
+ end;
+ {error, Error} ->
+ {error, [{unknown, Error, [], FilePos} | Res]};
+ eof ->
+ {ok, Res};
+ {'EXIT', Reason} ->
+ [{unknown, Reason, [], FilePos} | Res]
+ end.
+
+read_report(Fd) ->
+ case io:get_chars(Fd,'',2) of
+ [Hi,Lo] ->
+ Size = get_int16(Hi,Lo),
+ case io:get_chars(Fd,'',Size) of
+ eof ->
+ {error,"Premature end of file"};
+ List ->
+ Bin = list_to_binary(List),
+ Ref = make_ref(),
+ case (catch {Ref,binary_to_term(Bin)}) of
+ {'EXIT',_} ->
+ {error, "Inclomplete erlang term in log"};
+ {Ref,Term} ->
+ {ok, Term}
+ end
+ end;
+ eof ->
+ eof
+ end.
+
+get_int16(Hi,Lo) ->
+ ((Hi bsl 8) band 16#ff00) bor (Lo band 16#ff).
+
+
+%%-----------------------------------------------------------------
+%% Update these functions with the reports that should be possible
+%% to browse with rb.
+%%-----------------------------------------------------------------
+get_type({_Time, {error_report, _Pid, {_, crash_report, _}}}) ->
+ crash_report;
+get_type({_Time, {error_report, _Pid, {_, supervisor_report, _}}}) ->
+ supervisor_report;
+get_type({_Time, {info_report, _Pid, {_, progress, _}}}) ->
+ progress;
+get_type({_Time, {Type, _, _}}) -> Type;
+get_type(_) -> unknown.
+
+get_short_descr({{Date, Time}, {error_report, Pid, {_, crash_report, Rep}}}) ->
+ [OwnRep | _] = Rep,
+ Name =
+ case lists:keysearch(registered_name, 1, OwnRep) of
+ {value, {_Key, []}} ->
+ case lists:keysearch(initial_call, 1, OwnRep) of
+ {value, {_K, {M,_F,_A}}} -> M;
+ _ -> Pid
+ end;
+ {value, {_Key, N}} -> N;
+ _ -> Pid
+ end,
+ NameStr = lists:flatten(io_lib:format("~w", [Name])),
+ {NameStr, date_str(Date, Time)};
+get_short_descr({{Date, Time}, {error_report, Pid, {_, supervisor_report,Rep}}}) ->
+ Name =
+ case lists:keysearch(supervisor, 1, Rep) of
+ {value, {_Key, N}} when is_atom(N) -> N;
+ _ -> Pid
+ end,
+ NameStr = lists:flatten(io_lib:format("~w", [Name])),
+ {NameStr, date_str(Date,Time)};
+get_short_descr({{Date, Time}, {_Type, Pid, _}}) ->
+ NameStr = lists:flatten(io_lib:format("~w", [Pid])),
+ {NameStr, date_str(Date,Time)};
+get_short_descr(_) ->
+ {"???", "???"}.
+
+date_str({Y,Mo,D}=Date,{H,Mi,S}=Time) ->
+ case application:get_env(sasl,utc_log) of
+ {ok,true} ->
+ {{YY,MoMo,DD},{HH,MiMi,SS}} =
+ local_time_to_universal_time({Date,Time}),
+ lists:flatten(io_lib:format("~w-~2.2.0w-~2.2.0w ~2.2.0w:"
+ "~2.2.0w:~2.2.0w UTC",
+ [YY,MoMo,DD,HH,MiMi,SS]));
+ _ ->
+ lists:flatten(io_lib:format("~w-~2.2.0w-~2.2.0w ~2.2.0w:"
+ "~2.2.0w:~2.2.0w",
+ [Y,Mo,D,H,Mi,S]))
+ end.
+
+local_time_to_universal_time({Date,Time}) ->
+ case calendar:local_time_to_universal_time_dst({Date,Time}) of
+ [UCT] ->
+ UCT;
+ [UCT1,_UCT2] ->
+ UCT1;
+ [] -> % should not happen
+ {Date,Time}
+ end.
+
+
+print_list(Data, Type) ->
+ Header = {"No", "Type", "Process", "Date Time"},
+ Width = find_width([Header | Data], 0)+1,
+ DateWidth = find_date_width([Header | Data], 0) +1,
+ Format = lists:concat(["~4s~20s ~", Width, "s~20s~n"]),
+ io:format(Format, tuple_to_list(Header)),
+ io:format(Format, ["==", "====", "=======", "==== ===="]),
+ print_list(Data, Type, Width, DateWidth).
+print_list([], _, _, _) -> true;
+print_list([H|T], Type, Width, DateWidth) ->
+ print_one_report(H, Type, Width, DateWidth),
+ print_list(T, Type, Width, DateWidth).
+
+find_width([], Width) -> Width;
+find_width([H|T], Width) ->
+ Try = length(element(3, H)),
+ if
+ Try > Width -> find_width(T, Try);
+ true -> find_width(T, Width)
+ end.
+find_date_width([], Width) -> Width;
+find_date_width([H|T], Width) ->
+ Try = length(element(4, H)),
+ if
+ Try > Width -> find_date_width(T, Try);
+ true -> find_date_width(T, Width)
+ end.
+
+print_one_report({No, RealType, ShortDescr, Date, _Fname, _FilePos},
+ WantedType,
+ Width, DateWidth) ->
+ if
+ WantedType == all ->
+ print_short_descr(No, RealType, ShortDescr, Date, Width,
+ DateWidth);
+ WantedType == RealType ->
+ print_short_descr(No, RealType, ShortDescr, Date, Width,
+ DateWidth);
+ true -> ok
+ end.
+
+print_short_descr(No, Type, ShortDescr, Date, Width, DateWidth) ->
+ Format = lists:concat(["~4w~20w ~", Width, "s~", DateWidth,"s~n"]),
+ io:format(Format, [No,
+ Type,
+ io_lib:format("~s", [ShortDescr]),
+ Date]).
+
+print_report_by_num(Dir, Data, Number, Device, Abort, Log) ->
+ {_,Device1} = print_report(Dir, Data, Number, Device, Abort, Log),
+ Device1.
+
+print_typed_reports(_Dir, [], _Type, Device, _Abort, _Log) ->
+ Device;
+print_typed_reports(Dir, Data, Type, Device, Abort, Log) ->
+ {Next,Device1} =
+ case element(2, hd(Data)) of
+ Type ->
+ print_report(Dir, Data, element(1, hd(Data)), Device, Abort, Log);
+ _ ->
+ {proceed,Device}
+ end,
+ if Next == abort ->
+ Device1;
+ true ->
+ print_typed_reports(Dir, tl(Data), Type, Device1, Abort, Log)
+ end.
+
+print_all_reports(_Dir, [], Device, _Abort, _Log) ->
+ Device;
+print_all_reports(Dir, Data, Device, Abort, Log) ->
+ {Next,Device1} = print_report(Dir, Data, element(1, hd(Data)),
+ Device, Abort, Log),
+ if Next == abort ->
+ Device1;
+ true ->
+ print_all_reports(Dir, tl(Data), Device1, Abort, Log)
+ end.
+
+print_report(Dir, Data, Number, Device, Abort, Log) ->
+ case find_report(Data, Number) of
+ {Fname, FilePosition} ->
+ FileName = lists:concat([Dir, Fname]),
+ case file:open(FileName, [read]) of
+ {ok, Fd} ->
+ read_rep(Fd, FilePosition, Device, Abort, Log);
+ _ ->
+ io:format("rb: can't open file ~p~n", [Fname]),
+ {proceed,Device}
+ end;
+ no_report ->
+ {proceed,Device}
+ end.
+
+find_report([{No, _Type, _Descr, _Date, Fname, FilePosition}|_T], No) ->
+ {Fname, FilePosition};
+find_report([_H|T], No) ->
+ find_report(T, No);
+find_report([], No) ->
+ io:format("There is no report with number ~p.~n", [No]),
+ no_report.
+
+print_grep_reports(_Dir, [], _RegExp, Device, _Abort, _Log) ->
+ Device;
+print_grep_reports(Dir, Data, RegExp, Device, Abort, Log) ->
+ {Next,Device1} = print_grep_report(Dir, Data, element(1, hd(Data)),
+ Device, RegExp, Abort, Log),
+ if Next == abort ->
+ Device1;
+ true ->
+ print_grep_reports(Dir, tl(Data), RegExp, Device1, Abort, Log)
+ end.
+
+print_grep_report(Dir, Data, Number, Device, RegExp, Abort, Log) ->
+ {Fname, FilePosition} = find_report(Data, Number),
+ FileName = lists:concat([Dir, Fname]),
+ case file:open(FileName, [read]) of
+ {ok, Fd} when is_pid(Fd) ->
+ check_rep(Fd, FilePosition, Device, RegExp, Number, Abort, Log);
+ _ ->
+ io:format("rb: can't open file ~p~n", [Fname]),
+ {proceed,Device}
+ end.
+
+check_rep(Fd, FilePosition, Device, RegExp, Number, Abort, Log) ->
+ case read_rep_msg(Fd, FilePosition) of
+ {Date, Msg} ->
+ MsgStr = lists:flatten(io_lib:format("~p",[Msg])),
+ case regexp:match(MsgStr, RegExp) of
+ {match, _, _} ->
+ io:format("Found match in report number ~w~n", [Number]),
+ case catch rb_format_supp:print(Date, Msg, Device) of
+ {'EXIT', _} ->
+ handle_bad_form(Date, Msg, Device, Abort, Log);
+ _ ->
+ {proceed,Device}
+ end;
+ _ ->
+ {proceed,Device}
+ end;
+ _ ->
+ io:format("rb: Cannot read from file~n"),
+ {proceed,Device}
+ end.
+
+read_rep(Fd, FilePosition, Device, Abort, Log) ->
+ case read_rep_msg(Fd, FilePosition) of
+ {Date, Msg} ->
+ case catch rb_format_supp:print(Date, Msg, Device) of
+ {'EXIT', _} ->
+ handle_bad_form(Date, Msg, Device, Abort, Log);
+ _ ->
+ {proceed,Device}
+ end;
+ _ ->
+ io:format("rb: Cannot read from file~n"),
+ {proceed,Device}
+ end.
+
+handle_bad_form(Date, Msg, Device, Abort, Log) ->
+ io:format("rb: ERROR! A report on bad form was encountered. " ++
+ "It can not be printed to the log.~n~n"),
+ io:format("Details:~n~p ~p~n~n", [Date,Msg]),
+ case {Abort,Device,open_log_file(Log)} of
+ {true,standard_io,standard_io} ->
+ io:format("rb: Logging aborted.~n"),
+ {abort,Device};
+ {false,standard_io,standard_io} ->
+ io:format("rb: Logging resumed...~n~n"),
+ {proceed,Device};
+ {_,_,standard_io} ->
+ io:format("rb: Can not reopen ~p. Logging aborted.~n", [Log]),
+ {abort,Device};
+ {true,_,NewDevice} ->
+ io:format(NewDevice,
+ "~n~n************************* RB ERROR ************************~n" ++
+ "A report on bad form was encountered here and the logging~n" ++
+ "process was aborted. Note that there may well be remaining~n" ++
+ "reports that haven't yet been logged. Please see the rb~n" ++
+ "manual for more info.~n" ++
+ "***********************************************************~n", []),
+ io:format("rb: Logging aborted.~n"),
+ {abort,NewDevice};
+ {false,_,NewDevice} ->
+ io:format(NewDevice,
+ "~n ********* RB: UNPRINTABLE REPORT ********~n~n", []),
+ io:format("rb: Logging resumed...~n~n"),
+ {proceed,NewDevice}
+ end.
+
+read_rep_msg(Fd, FilePosition) ->
+ file:position(Fd, {bof, FilePosition}),
+ Res =
+ case catch read_report(Fd) of
+ {ok, Report} ->
+ {_ShortDescr, Date} = get_short_descr(Report),
+ {Date, Report};
+ _ -> error
+ end,
+ file:close(Fd),
+ Res.
diff --git a/lib/sasl/src/rb_format_supp.erl b/lib/sasl/src/rb_format_supp.erl
new file mode 100644
index 0000000000..b1d83d14d0
--- /dev/null
+++ b/lib/sasl/src/rb_format_supp.erl
@@ -0,0 +1,155 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(rb_format_supp).
+
+%% user interface
+-export([print/3]).
+
+%%-----------------------------------------------------------------
+%% This module prints error reports. Called from rb.
+%%-----------------------------------------------------------------
+
+print(Date, Report, Device) ->
+ Line = 79,
+%% Remove these comments when we can run rb in erl44!!!
+% case catch sasl_report:write_report(Device, Report) of
+% true -> ok;
+% _ ->
+ {_Time, Rep} = Report,
+ case Rep of
+ {error_report, _GL, {Pid, crash_report, CrashReport}} ->
+ Header = format_h(Line, "CRASH REPORT", Pid, Date),
+ format_lib_supp:print_info(Device,
+ Line,
+ [{header, Header} |
+ format_c(CrashReport)]),
+ true;
+ {error_report, _GL, {Pid, supervisor_report, SupReport}} ->
+ Header = format_h(Line, "SUPERVISOR REPORT", Pid, Date),
+ format_lib_supp:print_info(Device,
+ Line,
+ [{header, Header} |
+ format_s(SupReport)]),
+ true;
+ {error_report, _GL, {Pid, _Type, Report1}} ->
+ Header = format_h(Line, "ERROR REPORT", Pid, Date),
+ format_lib_supp:print_info(Device,
+ Line,
+ [{header, Header},
+ {data, Report1}]),
+ true;
+ {info_report, _GL, {Pid, progress, SupProgress}} ->
+ Header = format_h(Line, "PROGRESS REPORT", Pid, Date),
+ format_lib_supp:print_info(Device,
+ Line,
+ [{header, Header} |
+ format_p(SupProgress)]);
+ {info_report, _GL, {Pid, _Type, Report1}} ->
+ Header = format_h(Line, "INFO REPORT", Pid, Date),
+ format_lib_supp:print_info(Device,
+ Line,
+ [{header, Header},
+ {data, Report1}]),
+ true;
+ {warning_report, _GL, {Pid, _Type, Report1}} ->
+ Header = format_h(Line, "WARNING REPORT", Pid, Date),
+ format_lib_supp:print_info(Device,
+ Line,
+ [{header, Header},
+ {data, Report1}]),
+ true;
+ {error, _GL, {Pid, Format, Args}} ->
+ Header = format_h(Line, "ERROR REPORT", Pid, Date),
+ format_lib_supp:print_info(Device,
+ Line,
+ [{header, Header}]),
+ io:format(Device, Format, Args);
+ {info_msg, _GL, {Pid, Format, Args}} ->
+ Header = format_h(Line, "INFO REPORT", Pid, Date),
+ format_lib_supp:print_info(Device,
+ Line,
+ [{header, Header}]),
+ io:format(Device, Format, Args);
+ {warning_msg, _GL, {Pid, Format, Args}} ->
+ Header = format_h(Line, "WARNING REPORT", Pid, Date),
+ format_lib_supp:print_info(Device,
+ Line,
+ [{header, Header}]),
+ io:format(Device, Format, Args);
+ {Type, _GL, TypeReport} ->
+ io:format(Device, "~nInfo type <~w> ~s~n",
+ [Type, Date]),
+ io:format(Device, "~p", [TypeReport]);
+ _ ->
+ io:format("~nPrinting info of unknown type... ~s~n",
+ [Date]),
+ io:format(Device, "~p", [Report])
+% end
+ end.
+
+format_h(Line, Header, Pid, Date) ->
+ NHeader = lists:flatten(io_lib:format("~s ~w", [Header, Pid])),
+ DateLen = length(Date),
+ HeaderLen = Line - DateLen,
+ Format = lists:concat(["~-", HeaderLen, "s~", DateLen, "s"]),
+ io_lib:format(Format, [NHeader, Date]).
+
+
+%%-----------------------------------------------------------------
+%% Crash report
+%%-----------------------------------------------------------------
+format_c([OwnReport, LinkReport]) ->
+ [{items, {"Crashing process", OwnReport}},
+ format_neighbours(LinkReport)].
+
+format_neighbours([Data| Rest]) ->
+ [{newline, 1},
+ {items, {"Neighbour process", Data}} |
+ format_neighbours(Rest)];
+format_neighbours([]) -> [].
+
+%%-----------------------------------------------------------------
+%% Supervisor report
+%%-----------------------------------------------------------------
+format_s(Data) ->
+ SuperName = get_opt(supervisor, Data),
+ ErrorContext = get_opt(errorContext, Data),
+ Reason = get_opt(reason, Data),
+ ChildInfo = get_opt(offender, Data),
+ [{data, [{"Reporting supervisor", SuperName}]},
+ {newline, 1},
+ {items, {"Child process",
+ [{errorContext, ErrorContext},
+ {reason, Reason} |
+ lists:map(fun(CI) -> transform_mfa(CI) end, ChildInfo)]}}].
+
+transform_mfa({mfa, Value}) -> {start_function, Value};
+transform_mfa(X) -> X.
+
+%%-----------------------------------------------------------------
+%% Progress report
+%%-----------------------------------------------------------------
+format_p(Data) ->
+ [{data, Data}].
+
+get_opt(Key, List) ->
+ case lists:keysearch(Key, 1, List) of
+ {value, {_Key, Val}} -> Val;
+ _ -> undefined
+ end.
diff --git a/lib/sasl/src/release_handler.erl b/lib/sasl/src/release_handler.erl
new file mode 100644
index 0000000000..42c3d9dd4b
--- /dev/null
+++ b/lib/sasl/src/release_handler.erl
@@ -0,0 +1,1906 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(release_handler).
+-behaviour(gen_server).
+
+-include_lib("kernel/include/file.hrl").
+
+%% External exports
+-export([start_link/0,
+ create_RELEASES/1, create_RELEASES/2, create_RELEASES/4,
+ unpack_release/1,
+ check_install_release/1, install_release/1, install_release/2,
+ remove_release/1,
+ which_releases/0, make_permanent/1, reboot_old_release/1,
+ set_unpacked/2, set_removed/1, install_file/2]).
+-export([upgrade_app/2, downgrade_app/2, downgrade_app/3,
+ upgrade_script/2, downgrade_script/3,
+ eval_appup_script/4]).
+
+%% Internal exports
+-export([init/1, handle_call/3, handle_info/2, terminate/2,
+ handle_cast/2, code_change/3]).
+
+%% Internal exports, a client release_handler may call this functions.
+-export([do_write_release/3, do_copy_file/2, do_copy_files/2,
+ do_copy_files/1, do_rename_files/1, do_remove_files/1,
+ do_write_file/2, do_ensure_RELEASES/1]).
+
+-record(state, {unpurged = [],
+ root,
+ rel_dir,
+ releases,
+ timer,
+ start_prg,
+ masters = false,
+ client_dir = false,
+ static_emulator = false,
+ pre_sync_nodes = []}).
+
+%%-----------------------------------------------------------------
+%% status action next_status
+%% =============================================
+%% - unpack unpacked
+%% unpacked install current
+%% remove -
+%% current make_permanent permanent
+%% install other old
+%% remove -
+%% permanent make other permanent old
+%% install permanent
+%% old reboot permanen
+%% install current
+%% remove -
+%%-----------------------------------------------------------------
+%% libs = [{Lib, Vsn, Dir}]
+-record(release, {name, vsn, erts_vsn, libs = [], status}).
+
+-define(timeout, 10000).
+
+%%-----------------------------------------------------------------
+%% Assumes the following file structure:
+%% root --- lib --- Appl-Vsn1 --- <src>
+%% | | |- ebin
+%% | | |_ priv
+%% | |_ Appl-Vsn2
+%% |
+%% |- bin --- start (default; {sasl, start_prg} overrides
+%% | |- run_erl
+%% | |- start_erl (reads start_erl.data)
+%% | |_ <to_erl>
+%% |
+%% |- erts-EVsn1 --- bin --- <jam44>
+%% | |- <epmd>
+%% | |_ erl
+%% |- erts-EVsn2
+%% |
+%% |- clients --- ClientName1 --- bin -- start
+%% <clients use same lib and erts as master>
+%% | | |_ releases --- start_erl.data
+%% | | |_ Vsn1 -- start.boot
+%% | |_ ClientName2
+%% |
+%% |- clients --- Type1 --- lib
+%% <clients use own lib and erts>
+%% | | |- erts-EVsn
+%% | | |- bin -- start
+%% | | |_ ClientName1 -- releases -- start_erl.data
+%% | | |_ start.boot (static)
+%% | | |_ Vsn1
+%% | |_ Type2
+%% |
+%% |- releases --- RELEASES
+%% | |_ <Vsn1.tar.Z>
+%% | |
+%% | |- start_erl.data (generated by rh)
+%% | |
+%% | |_ Vsn1 --- start.boot
+%% | | |- <sys.config>
+%% | | |_ relup
+%% | |_ Vsn2
+%% |
+%% |- log --- erlang.log.N (1 .. 5)
+%%
+%% where <Name> means 'for example Name', and root is
+%% init:get_argument(root)
+%%
+%% It is configurable where the start file is located, and what it
+%% is called.
+%% The paramater is {sasl, start_prg} = File
+%% It is also configurable where the releases directory is located.
+%% Default is $ROOT/releases. $RELDIR overrids, and
+%% {sasl, releases_dir} overrides both.
+%%-----------------------------------------------------------------
+start_link() ->
+ gen_server:start_link({local, release_handler}, ?MODULE, [], []).
+
+%%-----------------------------------------------------------------
+%% Args: ReleaseName is the name of the package file
+%% (without .tar.Z (.tar on non unix systems))
+%% Purpose: Copies all files in the release package to their
+%% directories. Checks that all required libs and erts
+%% files are present.
+%% Returns: {ok, Vsn} | {error, Reason}
+%% Reason = {existing_release, Vsn} |
+%% {no_such_file, File} |
+%% {bad_rel_file, RelFile} |
+%% {file_missing, FileName} | (in the tar package)
+%% exit_reason()
+%%-----------------------------------------------------------------
+unpack_release(ReleaseName) ->
+ gen_server:call(release_handler, {unpack_release, ReleaseName}, infinity).
+
+%%-----------------------------------------------------------------
+%% Purpose: Checks the relup script for the specified version.
+%% The release must be unpacked.
+%% Returns: {ok, FromVsn, Descr} | {error, Reason}
+%% Reason = {already_installed, Vsn} |
+%% {bad_relup_file, RelFile} |
+%% {no_such_release, Vsn} |
+%% {no_such_from_vsn, Vsn} |
+%% exit_reason()
+%%-----------------------------------------------------------------
+check_install_release(Vsn) ->
+ gen_server:call(release_handler, {check_install_release, Vsn}, infinity).
+
+
+%%-----------------------------------------------------------------
+%% Purpose: Executes the relup script for the specified version.
+%% The release must be unpacked.
+%% Returns: {ok, FromVsn, Descr} | {error, Reason}
+%% Reason = {already_installed, Vsn} |
+%% {bad_relup_file, RelFile} |
+%% {no_such_release, Vsn} |
+%% {no_such_from_vsn, Vsn} |
+%% {illegal_option, Opt}} |
+%% exit_reason()
+%%-----------------------------------------------------------------
+install_release(Vsn) ->
+ gen_server:call(release_handler,
+ {install_release, Vsn, restart, []},
+ infinity).
+
+install_release(Vsn, Opt) ->
+ case check_install_options(Opt, restart, []) of
+ {ok, ErrorAction, InstallOpt} ->
+ gen_server:call(release_handler,
+ {install_release, Vsn, ErrorAction, InstallOpt},
+ infinity);
+ Error ->
+ Error
+ end.
+
+check_install_options([Opt | Opts], ErrAct, InstOpts) ->
+ case install_option(Opt) of
+ {error_action, EAct} ->
+ check_install_options(Opts, EAct, InstOpts);
+ true ->
+ check_install_options(Opts, ErrAct, [Opt | InstOpts]);
+ false ->
+ {error, {illegal_option, Opt}}
+ end;
+check_install_options([], ErrAct, InstOpts) ->
+ {ok, ErrAct, InstOpts}.
+
+install_option(Opt = {error_action, reboot}) -> Opt;
+install_option(Opt = {error_action, restart}) -> Opt;
+install_option({code_change_timeout, TimeOut}) ->
+ check_timeout(TimeOut);
+install_option({suspend_timeout, TimeOut}) ->
+ check_timeout(TimeOut);
+install_option({update_paths, Bool}) when Bool==true; Bool==false ->
+ true;
+install_option(_Opt) -> false.
+
+check_timeout(infinity) -> true;
+check_timeout(Int) when is_integer(Int), Int > 0 -> true;
+check_timeout(_Else) -> false.
+
+%%-----------------------------------------------------------------
+%% Purpose: Makes the specified release version be the one that is
+%% used when the system starts (or restarts).
+%% The release must be installed (not unpacked).
+%% Returns: ok | {error, Reason}
+%% Reason = {bad_status, Status} |
+%% {no_such_release, Vsn} |
+%% exit_reason()
+%%-----------------------------------------------------------------
+make_permanent(Vsn) ->
+ gen_server:call(release_handler, {make_permanent, Vsn}, infinity).
+
+%%-----------------------------------------------------------------
+%% Purpose: Reboots the system from an old release.
+%%-----------------------------------------------------------------
+reboot_old_release(Vsn) ->
+ gen_server:call(release_handler, {reboot_old_release, Vsn}, infinity).
+
+%%-----------------------------------------------------------------
+%% Purpose: Deletes all files and directories used by the release
+%% version, that are not used by any other release.
+%% The release must not be permanent.
+%% Returns: ok | {error, Reason}
+%% Reason = {permanent, Vsn} |
+%%-----------------------------------------------------------------
+remove_release(Vsn) ->
+ gen_server:call(release_handler, {remove_release, Vsn}, infinity).
+
+%%-----------------------------------------------------------------
+%% Args: RelFile = string()
+%% Libs = [{Lib, LibVsn, Dir}]
+%% Lib = LibVsn = Dir = string()
+%% Purpose: Tells the release handler that a release has been
+%% unpacked, without using the function unpack_release/1.
+%% RelFile is an absolute file name including the extension
+%% .rel.
+%% The release dir will be created. The necessary files can
+%% be installed by calling install_file/2.
+%% The release_handler remebers where all libs are located.
+%% If remove_release is called later,
+%% those libs are removed as well (if no other releases uses
+%% them).
+%% Returns: ok | {error, Reason}
+%%-----------------------------------------------------------------
+set_unpacked(RelFile, LibDirs) ->
+ gen_server:call(release_handler, {set_unpacked, RelFile, LibDirs}).
+
+%%-----------------------------------------------------------------
+%% Args: Vsn = string()
+%% Purpose: Makes it possible to handle removal of releases
+%% outside the release_handler.
+%% This function won't delete any files at all.
+%% Returns: ok | {error, Reason}
+%%-----------------------------------------------------------------
+set_removed(Vsn) ->
+ gen_server:call(release_handler, {set_removed, Vsn}).
+
+%%-----------------------------------------------------------------
+%% Purpose: Makes it possible to install the start.boot,
+%% sys.config and relup files if they are not part of a
+%% standard release package. May be used to
+%% install files that are generated, before install_release
+%% is called.
+%% Returns: ok | {error, {no_such_release, Vsn}}
+%%-----------------------------------------------------------------
+install_file(Vsn, File) when is_list(File) ->
+ gen_server:call(release_handler, {install_file, File, Vsn}).
+
+%%-----------------------------------------------------------------
+%% Returns: [{Name, Vsn, [LibName], Status}]
+%% Status = unpacked | current | permanent | old
+%%-----------------------------------------------------------------
+which_releases() ->
+ gen_server:call(release_handler, which_releases).
+
+%%-----------------------------------------------------------------
+%% check_script(Script, LibDirs) -> ok | {error, Reason}
+%%-----------------------------------------------------------------
+check_script(Script, LibDirs) ->
+ release_handler_1:check_script(Script, LibDirs).
+
+%%-----------------------------------------------------------------
+%% eval_script(Script, Apps, LibDirs, Opts) -> {ok, UnPurged} |
+%% restart_new_emulator |
+%% {error, Error}
+%% {'EXIT', Reason}
+%% If sync_nodes is present, the calling process must have called
+%% net_kernel:monitor_nodes(true) before calling this function.
+%% No! No other process than the release_handler can ever call this
+%% function, if sync_nodes is used.
+%%-----------------------------------------------------------------
+eval_script(Script, Apps, LibDirs, Opts) ->
+ catch release_handler_1:eval_script(Script, Apps, LibDirs, Opts).
+
+%%-----------------------------------------------------------------
+%% Func: create_RELEASES(Root, RelFile, LibDirs) -> ok | {error, Reason}
+%% Types: Root = RelFile = string()
+%% Purpose: Creates an initial RELEASES file.
+%%-----------------------------------------------------------------
+create_RELEASES([Root, RelFile | LibDirs]) ->
+ create_RELEASES(Root, filename:join(Root, "releases"), RelFile, LibDirs).
+
+create_RELEASES(Root, RelFile) ->
+ create_RELEASES(Root, filename:join(Root, "releases"), RelFile, []).
+
+create_RELEASES(Root, RelDir, RelFile, LibDirs) ->
+ case catch check_rel(Root, RelFile, LibDirs, false) of
+ {error, Reason } ->
+ {error, Reason};
+ Rel ->
+ Rel2 = Rel#release{status = permanent},
+ catch write_releases(RelDir, [Rel2], false)
+ end.
+
+%%-----------------------------------------------------------------
+%% Func: upgrade_app(App, Dir) -> {ok, Unpurged}
+%% | restart_new_emulator
+%% | {error, Error}
+%% Types:
+%% App = atom()
+%% Dir = string() assumed to be application directory, the code
+%% located under Dir/ebin
+%% Purpose: Upgrade to the version in Dir according to an appup file
+%%-----------------------------------------------------------------
+upgrade_app(App, NewDir) ->
+ try upgrade_script(App, NewDir) of
+ {ok, NewVsn, Script} ->
+ eval_appup_script(App, NewVsn, NewDir, Script)
+ catch
+ throw:Reason ->
+ {error, Reason}
+ end.
+
+%%-----------------------------------------------------------------
+%% Func: downgrade_app(App, Dir)
+%% downgrade_app(App, Vsn, Dir) -> {ok, Unpurged}
+%% | restart_new_emulator
+%% | {error, Error}
+%% Types:
+%% App = atom()
+%% Vsn = string(), may be omitted if Dir == App-Vsn
+%% Dir = string() assumed to be application directory, the code
+%% located under Dir/ebin
+%% Purpose: Downgrade from the version in Dir according to an appup file
+%% located in the ebin dir of the _current_ version
+%%-----------------------------------------------------------------
+downgrade_app(App, OldDir) ->
+ case string:tokens(filename:basename(OldDir), "-") of
+ [_AppS, OldVsn] ->
+ downgrade_app(App, OldVsn, OldDir);
+ _ ->
+ {error, {unknown_version, App}}
+ end.
+downgrade_app(App, OldVsn, OldDir) ->
+ try downgrade_script(App, OldVsn, OldDir) of
+ {ok, Script} ->
+ eval_appup_script(App, OldVsn, OldDir, Script)
+ catch
+ throw:Reason ->
+ {error, Reason}
+ end.
+
+upgrade_script(App, NewDir) ->
+ OldVsn = ensure_running(App),
+ OldDir = code:lib_dir(App),
+ {NewVsn, Script} = find_script(App, NewDir, OldVsn, up),
+ OldAppl = read_app(App, OldVsn, OldDir),
+ NewAppl = read_app(App, NewVsn, NewDir),
+ case systools_rc:translate_scripts(up,
+ [Script],[NewAppl],[OldAppl]) of
+ {ok, LowLevelScript} ->
+ {ok, NewVsn, LowLevelScript};
+ {error, _SystoolsRC, Reason} ->
+ throw(Reason)
+ end.
+
+downgrade_script(App, OldVsn, OldDir) ->
+ NewVsn = ensure_running(App),
+ NewDir = code:lib_dir(App),
+ {NewVsn, Script} = find_script(App, NewDir, OldVsn, down),
+ OldAppl = read_app(App, OldVsn, OldDir),
+ NewAppl = read_app(App, NewVsn, NewDir),
+ case systools_rc:translate_scripts(dn,
+ [Script],[OldAppl],[NewAppl]) of
+ {ok, LowLevelScript} ->
+ {ok, LowLevelScript};
+ {error, _SystoolsRC, Reason} ->
+ throw(Reason)
+ end.
+
+eval_appup_script(App, ToVsn, ToDir, Script) ->
+ EnvBefore = application_controller:prep_config_change(),
+ AppSpecL = read_appspec(App, ToDir),
+ Res = release_handler_1:eval_script(Script,
+ [], % [AppSpec]
+ [{App, ToVsn, ToDir}],
+ []), % [Opt]
+ case Res of
+ {ok, _Unpurged} ->
+ application_controller:change_application_data(AppSpecL,[]),
+ application_controller:config_change(EnvBefore);
+ _Res ->
+ ignore
+ end,
+ Res.
+
+ensure_running(App) ->
+ case lists:keysearch(App, 1, application:which_applications()) of
+ {value, {_App, _Descr, Vsn}} ->
+ Vsn;
+ false ->
+ throw({app_not_running, App})
+ end.
+
+find_script(App, Dir, OldVsn, UpOrDown) ->
+ Appup = filename:join([Dir, "ebin", atom_to_list(App)++".appup"]),
+ case file:consult(Appup) of
+ {ok, [{NewVsn, UpFromScripts, DownToScripts}]} ->
+ Scripts = case UpOrDown of
+ up -> UpFromScripts;
+ down -> DownToScripts
+ end,
+ case lists:keysearch(OldVsn, 1, Scripts) of
+ {value, {_OldVsn, Script}} ->
+ {NewVsn, Script};
+ false ->
+ throw({version_not_in_appup, OldVsn})
+ end;
+ {error, enoent} ->
+ throw(no_appup_found);
+ {error, Reason} ->
+ throw(Reason)
+ end.
+
+read_app(App, Vsn, Dir) ->
+ AppS = atom_to_list(App),
+ Path = [filename:join(Dir, "ebin")],
+ case systools_make:read_application(AppS, Vsn, Path, []) of
+ {ok, Appl} ->
+ Appl;
+ {error, {not_found, _AppFile}} ->
+ throw({no_app_found, Vsn, Dir});
+ {error, Reason} ->
+ throw(Reason)
+ end.
+
+read_appspec(App, Dir) ->
+ AppS = atom_to_list(App),
+ Path = [filename:join(Dir, "ebin")],
+ case file:path_consult(Path, AppS++".app") of
+ {ok, AppSpecL, _File} ->
+ AppSpecL;
+ {error, Reason} ->
+ throw(Reason)
+ end.
+
+
+
+
+
+
+
+
+%%-----------------------------------------------------------------
+%% Call-back functions from gen_server
+%%-----------------------------------------------------------------
+init([]) ->
+ {ok, [[Root]]} = init:get_argument(root),
+ {CliDir, Masters} = is_client(),
+ ReleaseDir =
+ case application:get_env(sasl, releases_dir) of
+ undefined ->
+ case os:getenv("RELDIR") of
+ false ->
+ if
+ CliDir == false ->
+ filename:join([Root, "releases"]);
+ true ->
+ filename:join([CliDir, "releases"])
+ end;
+ RELDIR ->
+ RELDIR
+ end;
+ {ok, Dir} ->
+ Dir
+ end,
+ Releases =
+ case consult(filename:join(ReleaseDir, "RELEASES"), Masters) of
+ {ok, [Term]} ->
+ transform_release(ReleaseDir, Term, Masters);
+ _ ->
+ {Name, Vsn} = init:script_id(),
+ [#release{name = Name, vsn = Vsn, status = permanent}]
+ end,
+ StartPrg =
+ case application:get_env(start_prg) of
+ {ok, Found2} when is_list(Found2) ->
+ {do_check, Found2};
+ _ ->
+ {no_check, filename:join([Root, "bin", "start"])}
+ end,
+ Static =
+ case application:get_env(static_emulator) of
+ {ok, SFlag} when is_atom(SFlag) -> SFlag;
+ _ -> false
+ end,
+ {ok, #state{root = Root, rel_dir = ReleaseDir, releases = Releases,
+ start_prg = StartPrg, masters = Masters,
+ client_dir = CliDir, static_emulator = Static}}.
+
+handle_call({unpack_release, ReleaseName}, _From, S)
+ when S#state.masters == false ->
+ RelDir = S#state.rel_dir,
+ case catch do_unpack_release(S#state.root, RelDir,
+ ReleaseName, S#state.releases) of
+ {ok, NewReleases, Vsn} ->
+ clean_release(RelDir, ReleaseName),
+ {reply, {ok, Vsn}, S#state{releases = NewReleases}};
+ {error, Reason} ->
+ {reply, {error, Reason}, S};
+ {'EXIT', Reason} ->
+ {reply, {error, Reason}, S}
+ end;
+handle_call({unpack_release, _ReleaseName}, _From, S) ->
+ {reply, {error, client_node}, S};
+
+handle_call({check_install_release, Vsn}, _From, S) ->
+ case catch do_check_install_release(S#state.rel_dir,
+ Vsn,
+ S#state.releases,
+ S#state.masters) of
+ {ok, CurrentVsn, Descr} ->
+ {reply, {ok, CurrentVsn, Descr}, S};
+ {error, Reason} ->
+ {reply, {error, Reason}, S};
+ {'EXIT', Reason} ->
+ {reply, {error, Reason}, S}
+ end;
+
+handle_call({install_release, Vsn, ErrorAction, Opts}, From, S) ->
+ NS = resend_sync_nodes(S),
+ case catch do_install_release(S, Vsn, Opts) of
+ {ok, NewReleases, CurrentVsn, Descr} ->
+ {reply, {ok, CurrentVsn, Descr}, NS#state{releases=NewReleases}};
+ {ok, NewReleases, Unpurged, CurrentVsn, Descr} ->
+ Timer =
+ case S#state.timer of
+ undefined ->
+ {ok, Ref} = timer:send_interval(?timeout, timeout),
+ Ref;
+ Ref -> Ref
+ end,
+ NewS = NS#state{releases = NewReleases, unpurged = Unpurged,
+ timer = Timer},
+ {reply, {ok, CurrentVsn, Descr}, NewS};
+ {error, Reason} ->
+ {reply, {error, Reason}, NS};
+ {restart_new_emulator, CurrentVsn, Descr} ->
+ gen_server:reply(From, {ok, CurrentVsn, Descr}),
+ init:reboot(),
+ {noreply, NS};
+ {'EXIT', Reason} ->
+ io:format("release_handler:"
+ "install_release(Vsn=~p Opts=~p) failed, "
+ "Reason=~p~n", [Vsn, Opts, Reason]),
+ gen_server:reply(From, {error, Reason}),
+ case ErrorAction of
+ restart ->
+ init:restart();
+ reboot ->
+ init:reboot()
+ end,
+ {noreply, NS}
+ end;
+
+handle_call({make_permanent, Vsn}, _From, S) ->
+ case catch do_make_permanent(S, Vsn) of
+ {ok, Releases, Unpurged} ->
+ {reply, ok, S#state{releases = Releases, unpurged = Unpurged}};
+ {error, Reason} ->
+ {reply, {error, Reason}, S};
+ {'EXIT', Reason} ->
+ {reply, {error, Reason}, S}
+ end;
+
+handle_call({reboot_old_release, Vsn}, From, S) ->
+ case catch do_reboot_old_release(S, Vsn) of
+ ok ->
+ gen_server:reply(From, ok),
+ init:reboot(),
+ {noreply, S};
+ {error, Reason} ->
+ {reply, {error, Reason}, S};
+ {'EXIT', Reason} ->
+ {reply, {error, Reason}, S}
+ end;
+
+handle_call({remove_release, Vsn}, _From, S)
+ when S#state.masters == false ->
+ case catch do_remove_release(S#state.root, S#state.rel_dir,
+ Vsn, S#state.releases) of
+ {ok, NewReleases} ->
+ {reply, ok, S#state{releases = NewReleases}};
+ {error, Reason} ->
+ {reply, {error, Reason}, S};
+ {'EXIT', Reason} ->
+ {reply, {error, Reason}, S}
+ end;
+handle_call({remove_release, _Vsn}, _From, S) ->
+ {reply, {error, client_node}, S};
+
+handle_call({set_unpacked, RelFile, LibDirs}, _From, S) ->
+ Root = S#state.root,
+ case catch do_set_unpacked(Root, S#state.rel_dir, RelFile,
+ LibDirs, S#state.releases,
+ S#state.masters) of
+ {ok, NewReleases, Vsn} ->
+ {reply, {ok, Vsn}, S#state{releases = NewReleases}};
+ {error, Reason} ->
+ {reply, {error, Reason}, S};
+ {'EXIT', Reason} ->
+ {reply, {error, Reason}, S}
+ end;
+
+handle_call({set_removed, Vsn}, _From, S) ->
+ case catch do_set_removed(S#state.rel_dir, Vsn,
+ S#state.releases,
+ S#state.masters) of
+ {ok, NewReleases} ->
+ {reply, ok, S#state{releases = NewReleases}};
+ {error, Reason} ->
+ {reply, {error, Reason}, S};
+ {'EXIT', Reason} ->
+ {reply, {error, Reason}, S}
+ end;
+
+handle_call({install_file, File, Vsn}, _From, S) ->
+ Reply =
+ case lists:keysearch(Vsn, #release.vsn, S#state.releases) of
+ {value, _} ->
+ Dir = filename:join([S#state.rel_dir, Vsn]),
+ catch copy_file(File, Dir, S#state.masters);
+ _ ->
+ {error, {no_such_release, Vsn}}
+ end,
+ {reply, Reply, S};
+
+handle_call(which_releases, _From, S) ->
+ Reply = lists:map(fun(#release{name = Name, vsn = Vsn, libs = Libs,
+ status = Status}) ->
+ {Name, Vsn, mk_lib_name(Libs), Status}
+ end, S#state.releases),
+ {reply, Reply, S}.
+
+mk_lib_name([{LibName, Vsn, _Dir} | T]) ->
+ [lists:concat([LibName, "-", Vsn]) | mk_lib_name(T)];
+mk_lib_name([]) -> [].
+
+handle_info(timeout, S) ->
+ case soft_purge(S#state.unpurged) of
+ [] ->
+ timer:cancel(S#state.timer),
+ {noreply, S#state{unpurged = [], timer = undefined}};
+ Unpurged ->
+ {noreply, S#state{unpurged = Unpurged}}
+ end;
+
+handle_info({sync_nodes, Id, Node}, S) ->
+ PSN = S#state.pre_sync_nodes,
+ {noreply, S#state{pre_sync_nodes = [{sync_nodes, Id, Node} | PSN]}};
+
+handle_info(Msg, State) ->
+ error_logger:info_msg("release_handler: got unknown message: ~p~n", [Msg]),
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%%-----------------------------------------------------------------
+%%% Internal functions
+%%%-----------------------------------------------------------------
+is_client() ->
+ case application:get_env(masters) of
+ {ok, Masters} ->
+ Alive = is_alive(),
+ case atom_list(Masters) of
+ true when Alive == true ->
+ case application:get_env(client_directory) of
+ {ok, ClientDir} ->
+ case int_list(ClientDir) of
+ true ->
+ {ClientDir, Masters};
+ _ ->
+ exit({bad_parameter, client_directory,
+ ClientDir})
+ end;
+ _ ->
+ {false, false}
+ end;
+ _ ->
+ exit({bad_parameter, masters, Masters})
+ end;
+ _ ->
+ {false, false}
+ end.
+
+atom_list([A|T]) when is_atom(A) -> atom_list(T);
+atom_list([]) -> true;
+atom_list(_) -> false.
+
+int_list([I|T]) when is_integer(I) -> int_list(T);
+int_list([]) -> true;
+int_list(_) -> false.
+
+resend_sync_nodes(S) ->
+ lists:foreach(fun(Msg) -> self() ! Msg end, S#state.pre_sync_nodes),
+ S#state{pre_sync_nodes = []}.
+
+soft_purge(Unpurged) ->
+ lists:filter(fun({Mod, _PostPurgeMethod}) ->
+ case code:soft_purge(Mod) of
+ true -> false; % No proc left, don't remember Mod
+ false -> true % Still proc left, remember it
+ end
+ end,
+ Unpurged).
+
+brutal_purge(Unpurged) ->
+ lists:filter(fun({Mod, brutal_purge}) -> code:purge(Mod), false;
+ (_) -> true
+ end,
+ Unpurged).
+
+%%-----------------------------------------------------------------
+%% The release package is a RelName.tar.Z (.tar on non unix) file
+%% with the following contents:
+%% - RelName.rel == {release, {Name, Vsn}, {erts, EVsn}, [lib()]}
+%% - <files> according to [lib()]
+%% - lib() = {LibName, LibVsn}
+%% In the Dir, there exists a file called RELEASES, which contains
+%% a [{Vsn, {erts, EVsn}, {libs, [{LibName, LibVsn, LibDir}]}}].
+%% Note that RelDir is an absolute directory name !
+%% Note that this function is not executed by a client
+%% release_handler.
+%%-----------------------------------------------------------------
+do_unpack_release(Root, RelDir, ReleaseName, Releases) ->
+ Tar = filename:join(RelDir, ReleaseName ++ ".tar.gz"),
+ do_check_file(Tar, regular),
+ Rel = ReleaseName ++ ".rel",
+ extract_rel_file(filename:join("releases", Rel), Tar, Root),
+ RelFile = filename:join(RelDir, Rel),
+ Release = check_rel(Root, RelFile, false),
+ #release{vsn = Vsn} = Release,
+ case lists:keysearch(Vsn, #release.vsn, Releases) of
+ {value, _} -> throw({error, {existing_release, Vsn}});
+ _ -> ok
+ end,
+ extract_tar(Root, Tar),
+ NewReleases = [Release#release{status = unpacked} | Releases],
+ write_releases(RelDir, NewReleases, false),
+ Dir = filename:join([RelDir, Vsn]),
+ copy_file(RelFile, Dir, false),
+ {ok, NewReleases, Vsn}.
+
+%% Note that this function is not executed by a client
+%% release_handler.
+clean_release(RelDir, ReleaseName) ->
+ Tar = filename:join(RelDir, ReleaseName ++ ".tar.gz"),
+ Rel = filename:join(RelDir, ReleaseName ++ ".rel"),
+ file:delete(Tar),
+ file:delete(Rel).
+
+check_rel(Root, RelFile, Masters) ->
+ check_rel(Root, RelFile, [], Masters).
+check_rel(Root, RelFile, LibDirs, Masters) ->
+ case consult(RelFile, Masters) of
+ {ok, [RelData]} ->
+ check_rel_data(RelData, Root, LibDirs);
+ {ok, _} ->
+ throw({error, {bad_rel_file, RelFile}});
+ {error, Reason} when is_tuple(Reason) ->
+ throw({error, {bad_rel_file, RelFile}});
+ {error, FileError} -> % FileError is posix atom | no_master
+ throw({error, {FileError, RelFile}})
+ end.
+
+check_rel_data({release, {Name, Vsn}, {erts, EVsn}, Libs}, Root, LibDirs) ->
+ Libs2 =
+ lists:map(fun(LibSpec) ->
+ Lib = element(1, LibSpec),
+ LibVsn = element(2, LibSpec),
+ LibName = lists:concat([Lib, "-", LibVsn]),
+ LibDir =
+ case lists:keysearch(Lib, 1, LibDirs) of
+ {value, {_Lib, _Vsn, Dir}} ->
+ Path = filename:join(Dir,LibName),
+ check_path(Path),
+ Path;
+ _ ->
+ filename:join([Root, "lib", LibName])
+ end,
+ {Lib, LibVsn, LibDir}
+ end,
+ Libs),
+ #release{name = Name, vsn = Vsn, erts_vsn = EVsn,
+ libs = Libs2, status = unpacking};
+check_rel_data(RelData, _Root, _LibDirs) ->
+ throw({error, {bad_rel_data, RelData}}).
+
+check_path(Path) ->
+ case file:read_file_info(Path) of
+ {ok, Info} when Info#file_info.type==directory ->
+ ok;
+ {ok, _Info} ->
+ throw({error, {not_a_directory, Path}});
+ {error, _Reason} ->
+ throw({error, {no_such_directory, Path}})
+ end.
+
+do_check_install_release(RelDir, Vsn, Releases, Masters) ->
+ case lists:keysearch(Vsn, #release.vsn, Releases) of
+ {value, #release{status = current}} ->
+ {error, {already_installed, Vsn}};
+ {value, Release} ->
+ LatestRelease = get_latest_release(Releases),
+ VsnDir = filename:join([RelDir, Vsn]),
+ check_file(filename:join(VsnDir, "start.boot"), regular, Masters),
+ IsRelup = check_opt_file(filename:join(VsnDir, "relup"), regular, Masters),
+ check_opt_file(filename:join(VsnDir, "sys.config"), regular, Masters),
+
+ %% Check that all required libs are present
+ Libs = Release#release.libs,
+ lists:foreach(fun({_Lib, _LibVsn, LibDir}) ->
+ check_file(LibDir, directory, Masters),
+ Ebin = filename:join(LibDir, "ebin"),
+ check_file(Ebin, directory, Masters)
+ end,
+ Libs),
+
+ if
+ IsRelup ->
+ case get_rh_script(LatestRelease, Release, RelDir, Masters) of
+ {ok, {CurrentVsn, Descr, Script}} ->
+ case catch check_script(Script, Libs) of
+ ok ->
+ {ok, CurrentVsn, Descr};
+ Else ->
+ Else
+ end;
+ Error ->
+ Error
+ end;
+ true ->
+ {ok, Vsn, ""}
+ end;
+ _ ->
+ {error, {no_such_release, Vsn}}
+ end.
+
+do_install_release(#state{start_prg = StartPrg,
+ rel_dir = RelDir, releases = Releases,
+ masters = Masters,
+ static_emulator = Static},
+ Vsn, Opts) ->
+ case lists:keysearch(Vsn, #release.vsn, Releases) of
+ {value, #release{status = current}} ->
+ {error, {already_installed, Vsn}};
+ {value, Release} ->
+ LatestRelease = get_latest_release(Releases),
+ case get_rh_script(LatestRelease, Release, RelDir, Masters) of
+ {ok, {CurrentVsn, Descr, Script}} ->
+ mon_nodes(true),
+ EnvBefore = application_controller:prep_config_change(),
+ Apps = change_appl_data(RelDir, Release, Masters),
+ LibDirs = Release#release.libs,
+ case eval_script(Script, Apps, LibDirs, Opts) of
+ {ok, []} ->
+ application_controller:config_change(EnvBefore),
+ mon_nodes(false),
+ NewReleases = set_status(Vsn, current, Releases),
+ {ok, NewReleases, CurrentVsn, Descr};
+ {ok, Unpurged} ->
+ application_controller:config_change(EnvBefore),
+ mon_nodes(false),
+ NewReleases = set_status(Vsn, current, Releases),
+ {ok, NewReleases, Unpurged, CurrentVsn, Descr};
+ restart_new_emulator when Static == true ->
+ throw(static_emulator);
+ restart_new_emulator ->
+ mon_nodes(false),
+ {value, PermanentRelease} =
+ lists:keysearch(permanent, #release.status,
+ Releases),
+ NReleases = set_status(Vsn, current, Releases),
+ NReleases2 = set_status(Vsn,tmp_current,NReleases),
+ write_releases(RelDir, NReleases2, Masters),
+ prepare_restart_new_emulator(StartPrg, RelDir,
+ Release,
+ PermanentRelease,
+ Masters),
+ {restart_new_emulator, CurrentVsn, Descr};
+ Else ->
+ application_controller:config_change(EnvBefore),
+ mon_nodes(false),
+ Else
+ end;
+ Error ->
+ Error
+ end;
+ _ ->
+ {error, {no_such_release, Vsn}}
+ end.
+
+%%% This code chunk updates the services in one of two ways,
+%%% Either the emulator is restarted, in which case the old service
+%%% is to be removed and the new enabled, or the emulator is NOT restarted
+%%% in which case we try to rename the old service to the new name and try
+%%% to update heart's view of what service we are really running.
+do_make_services_permanent(PermanentVsn,Vsn, PermanentEVsn, EVsn) ->
+ PermName = hd(string:tokens(atom_to_list(node()),"@"))
+ ++ "_" ++ PermanentVsn,
+ Name = hd(string:tokens(atom_to_list(node()),"@"))
+ ++ "_" ++ Vsn,
+ case erlsrv:get_service(EVsn,Name) of
+ {error, _Error} ->
+ %% We probably do not need to replace services, just
+ %% rename.
+ case os:getenv("ERLSRV_SERVICE_NAME") == PermName of
+ true ->
+ case erlsrv:rename_service(EVsn,PermName,Name) of
+ {ok,_} ->
+ case erlsrv:get_service(EVsn,Name) of
+ {error,Error2} ->
+ throw({error,Error2});
+ _Data2 ->
+ %% The interfaces for doing this are
+ %% NOT published and may be subject to
+ %% change. Do NOT do this anywhere else!
+
+ os:putenv("ERLSRV_SERVICE_NAME", Name),
+
+ %% Restart heart port program, this
+ %% function is only to be used here.
+ heart:cycle(),
+ ok
+ end;
+ Error3 ->
+ throw({error,{service_rename_failed, Error3}})
+ end;
+ false ->
+ throw({error,service_name_missmatch})
+ end;
+ Data ->
+ UpdData = erlsrv:new_service(Name, Data, []),
+ case erlsrv:store_service(EVsn,UpdData) of
+ ok ->
+ erlsrv:disable_service(PermanentEVsn, PermName),
+ erlsrv:enable_service(EVsn, Name),
+ erlsrv:remove_service(PermName),
+ %%% Read comments about these above...
+ os:putenv("ERLSRV_SERVICE_NAME", Name),
+ heart:cycle(),
+ ok;
+ Error4 ->
+ throw(Error4)
+ end
+ end.
+
+do_make_permanent(#state{releases = Releases,
+ rel_dir = RelDir, unpurged = Unpurged,
+ masters = Masters,
+ static_emulator = Static},
+ Vsn) ->
+ case lists:keysearch(Vsn, #release.vsn, Releases) of
+ {value, #release{erts_vsn = EVsn, status = Status}}
+ when Status /= unpacked, Status /= old, Status /= permanent ->
+ Dir = filename:join([RelDir, Vsn]),
+ Sys =
+ case catch check_file(filename:join(Dir, "sys.config"),
+ regular, Masters) of
+ ok -> filename:join(Dir, "sys");
+ _ -> false
+ end,
+ Boot = filename:join(Dir, "start.boot"),
+ check_file(Boot, regular, Masters),
+ set_permanent_files(RelDir, EVsn, Vsn, Masters, Static),
+ NewReleases = set_status(Vsn, permanent, Releases),
+ write_releases(RelDir, NewReleases, Masters),
+ case os:type() of
+ {win32, nt} ->
+ {value, PermanentRelease} =
+ lists:keysearch(permanent, #release.status,
+ Releases),
+ PermanentVsn = PermanentRelease#release.vsn,
+ PermanentEVsn = PermanentRelease#release.erts_vsn,
+ case catch do_make_services_permanent(PermanentVsn,
+ Vsn,
+ PermanentEVsn,
+ EVsn) of
+ {error,Reason} ->
+ throw({error,{service_update_failed, Reason}});
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end,
+ init:make_permanent(filename:join(Dir, "start"), Sys),
+ {ok, NewReleases, brutal_purge(Unpurged)};
+ {value, #release{status = permanent}} ->
+ {ok, Releases, Unpurged};
+ {value, #release{status = Status}} ->
+ {error, {bad_status, Status}};
+ false ->
+ {error, {no_such_release, Vsn}}
+ end.
+
+
+do_back_service(OldVersion, CurrentVersion,OldEVsn,CurrentEVsn) ->
+ NN = hd(string:tokens(atom_to_list(node()),"@")),
+ OldName = NN ++ "_" ++ OldVersion,
+ CurrentName = NN ++ "_" ++ CurrentVersion,
+ UpdData = case erlsrv:get_service(CurrentEVsn,CurrentName) of
+ {error, Error} ->
+ throw({error,Error});
+ Data ->
+ erlsrv:new_service(OldName, Data, [])
+ end,
+ case erlsrv:store_service(OldEVsn,UpdData) of
+ ok ->
+ erlsrv:disable_service(CurrentEVsn,CurrentName),
+ erlsrv:enable_service(OldEVsn,OldName);
+ Error2 ->
+ throw(Error2)
+ end,
+ OldErlSrv = filename:nativename(erlsrv:erlsrv(OldEVsn)),
+ CurrentErlSrv = filename:nativename(erlsrv:erlsrv(CurrentEVsn)),
+ case heart:set_cmd(CurrentErlSrv ++ " remove " ++ CurrentName ++
+ " & " ++ OldErlSrv ++ " start " ++ OldName) of
+ ok ->
+ ok;
+ Error3 ->
+ throw({error, {'heart:set_cmd() error', Error3}})
+ end.
+
+do_reboot_old_release(#state{releases = Releases,
+ rel_dir = RelDir, masters = Masters,
+ static_emulator = Static},
+ Vsn) ->
+ case lists:keysearch(Vsn, #release.vsn, Releases) of
+ {value, #release{erts_vsn = EVsn, status = old}} ->
+ CurrentRunning = case os:type() of
+ {win32,nt} ->
+ %% Get the current release on NT
+ case lists:keysearch(permanent,
+ #release.status,
+ Releases) of
+ false ->
+ lists:keysearch(current,
+ #release.status,
+ Releases);
+ {value,CR} ->
+ CR
+ end;
+ _ ->
+ false
+ end,
+ set_permanent_files(RelDir, EVsn, Vsn, Masters, Static),
+ NewReleases = set_status(Vsn, permanent, Releases),
+ write_releases(RelDir, NewReleases, Masters),
+ case os:type() of
+ {win32,nt} ->
+ %% Edit up the services and set a reasonable heart
+ %% command
+ do_back_service(Vsn,CurrentRunning#release.vsn,EVsn,
+ CurrentRunning#release.erts_vsn);
+ _ ->
+ ok
+ end,
+ ok;
+ {value, #release{status = Status}} ->
+ {error, {bad_status, Status}};
+ false ->
+ {error, {no_such_release, Vsn}}
+ end.
+
+%%-----------------------------------------------------------------
+%% Depending of if the release_handler is running in normal, client or
+%% client with static emulator the new system version is made permanent
+%% in different ways.
+%%-----------------------------------------------------------------
+set_permanent_files(RelDir, EVsn, Vsn, false, _) ->
+ write_start(filename:join([RelDir, "start_erl.data"]),
+ EVsn ++ " " ++ Vsn,
+ false);
+set_permanent_files(RelDir, EVsn, Vsn, Masters, false) ->
+ write_start(filename:join([RelDir, "start_erl.data"]),
+ EVsn ++ " " ++ Vsn,
+ Masters);
+set_permanent_files(RelDir, _EVsn, Vsn, Masters, _Static) ->
+ VsnDir = filename:join([RelDir, Vsn]),
+ set_static_files(VsnDir, RelDir, Masters).
+
+
+do_remove_service(Vsn) ->
+ %%% Very unconditionally remove the service.
+ ServiceName = hd(string:tokens(atom_to_list(node()),"@"))
+ ++ "_" ++ Vsn,
+ erlsrv:remove_service(ServiceName).
+
+do_remove_release(Root, RelDir, Vsn, Releases) ->
+ % Decide which libs should be removed
+ case lists:keysearch(Vsn, #release.vsn, Releases) of
+ {value, #release{status = permanent}} ->
+ {error, {permanent, Vsn}};
+ {value, #release{libs = RemoveLibs, vsn = Vsn, erts_vsn = EVsn}} ->
+ case os:type() of
+ {win32, nt} ->
+ do_remove_service(Vsn);
+ _ ->
+ ok
+ end,
+
+ NewReleases = lists:keydelete(Vsn, #release.vsn, Releases),
+ RemoveThese =
+ lists:foldl(fun(#release{libs = Libs}, Remove) ->
+ diff_dir(Remove, Libs)
+ end, RemoveLibs, NewReleases),
+ lists:foreach(fun({_Lib, _LVsn, LDir}) ->
+ remove_file(LDir)
+ end, RemoveThese),
+ remove_file(filename:join([RelDir, Vsn])),
+ case lists:keysearch(EVsn, #release.erts_vsn, NewReleases) of
+ {value, _} -> ok;
+ false -> % Remove erts library, no more references to it
+ remove_file(filename:join(Root, "erts-" ++ EVsn))
+ end,
+ write_releases(RelDir, NewReleases, false),
+ {ok, NewReleases};
+ false ->
+ {error, {no_such_release, Vsn}}
+ end.
+
+do_set_unpacked(Root, RelDir, RelFile, LibDirs, Releases, Masters) ->
+ Release = check_rel(Root, RelFile, LibDirs, Masters),
+ #release{vsn = Vsn} = Release,
+ case lists:keysearch(Vsn, #release.vsn, Releases) of
+ {value, _} -> throw({error, {existing_release, Vsn}});
+ false -> ok
+ end,
+ NewReleases = [Release#release{status = unpacked} | Releases],
+ VsnDir = filename:join([RelDir, Vsn]),
+ make_dir(VsnDir, Masters),
+ write_releases(RelDir, NewReleases, Masters),
+ {ok, NewReleases, Vsn}.
+
+do_set_removed(RelDir, Vsn, Releases, Masters) ->
+ case lists:keysearch(Vsn, #release.vsn, Releases) of
+ {value, #release{status = permanent}} ->
+ {error, {permanent, Vsn}};
+ {value, _} ->
+ NewReleases = lists:keydelete(Vsn, #release.vsn, Releases),
+ write_releases(RelDir, NewReleases, Masters),
+ {ok, NewReleases};
+ false ->
+ {error, {no_such_release, Vsn}}
+ end.
+
+
+%%-----------------------------------------------------------------
+%% A relup file consists of:
+%% {Vsn, [{FromVsn, Descr, RhScript}], [{ToVsn, Descr, RhScript}]}.
+%% It describes how to get to this release from previous releases,
+%% and how to get from this release to previous releases.
+%% We can get from a FromVsn that's a substring of CurrentVsn (e.g.
+%% 1.1 is a substring of 1.1.1, but not 1.2), but when we get to
+%% ToVsn, we must have an exact match.
+%%
+%% We do not put any semantics into the version strings, i.e. we
+%% don't know if going from Vsn1 to Vsn2 represents a upgrade or
+%% a downgrade. For both upgrades and downgrades, the relup file
+%% is located in the directory of the latest version. Since we
+%% do not which version is latest, we first suppose that ToVsn >
+%% CurrentVsn, i.e. we perform an upgrade. If we don't find the
+%% corresponding relup instructions, we check if it's possible to
+%% downgrade from CurrentVsn to ToVsn.
+%%-----------------------------------------------------------------
+get_rh_script(#release{vsn = CurrentVsn},
+ #release{vsn = Vsn},
+ RelDir,
+ Masters) ->
+ Relup = filename:join([RelDir, Vsn, "relup"]),
+ case try_upgrade(Vsn, CurrentVsn, Relup, Masters) of
+ {ok, RhScript} ->
+ {ok, RhScript};
+ _ ->
+ Relup2 = filename:join([RelDir, CurrentVsn,"relup"]),
+ case try_downgrade(Vsn, CurrentVsn, Relup2, Masters) of
+ {ok, RhScript} ->
+ {ok, RhScript};
+ _ ->
+ throw({error, {no_matching_relup, Vsn, CurrentVsn}})
+ end
+ end.
+
+try_upgrade(ToVsn, CurrentVsn, Relup, Masters) ->
+ case consult(Relup, Masters) of
+ {ok, [{ToVsn, ListOfRhScripts, _}]} ->
+ case lists:keysearch(CurrentVsn, 1, ListOfRhScripts) of
+ {value, RhScript} ->
+ {ok, RhScript};
+ _ ->
+ error
+ end;
+ {ok, _} ->
+ throw({error, {bad_relup_file, Relup}});
+ {error, Reason} when is_tuple(Reason) ->
+ throw({error, {bad_relup_file, Relup}});
+ {error, enoent} ->
+ error;
+ {error, FileError} -> % FileError is posix atom | no_master
+ throw({error, {FileError, Relup}})
+ end.
+
+try_downgrade(ToVsn, CurrentVsn, Relup, Masters) ->
+ case consult(Relup, Masters) of
+ {ok, [{CurrentVsn, _, ListOfRhScripts}]} ->
+ case lists:keysearch(ToVsn, 1, ListOfRhScripts) of
+ {value, RhScript} ->
+ {ok, RhScript};
+ _ ->
+ error
+ end;
+ {ok, _} ->
+ throw({error, {bad_relup_file, Relup}});
+ {error, Reason} when is_tuple(Reason) ->
+ throw({error, {bad_relup_file, Relup}});
+ {error, FileError} -> % FileError is posix atom | no_master
+ throw({error, {FileError, Relup}})
+ end.
+
+
+%% Status = current | tmp_current | permanent
+set_status(Vsn, Status, Releases) ->
+ lists:zf(fun(Release) when Release#release.vsn == Vsn,
+ Release#release.status == permanent ->
+ %% If a permanent rel is installed, it keeps its
+ %% permanent status (not changed to current).
+ %% The current becomes old though.
+ true;
+ (Release) when Release#release.vsn == Vsn ->
+ {true, Release#release{status = Status}};
+ (Release) when Release#release.status == Status ->
+ {true, Release#release{status = old}};
+ (_) ->
+ true
+ end, Releases).
+
+get_latest_release(Releases) ->
+ case lists:keysearch(current, #release.status, Releases) of
+ {value, Release} ->
+ Release;
+ false ->
+ {value, Release} =
+ lists:keysearch(permanent, #release.status, Releases),
+ Release
+ end.
+
+%% Returns: [{Lib, Vsn, Dir}] to be removed
+diff_dir([H | T], L) ->
+ case memlib(H, L) of
+ true -> diff_dir(T, L);
+ false -> [H | diff_dir(T, L)]
+ end;
+diff_dir([], _) -> [].
+
+memlib({Lib, Vsn, _Dir}, [{Lib, Vsn, _Dir2} | _T]) -> true;
+memlib(Lib, [_H | T]) -> memlib(Lib, T);
+memlib(_Lib, []) -> false.
+
+%% recursively remove file or directory
+remove_file(File) ->
+ case file:read_file_info(File) of
+ {ok, Info} when Info#file_info.type==directory ->
+ case file:list_dir(File) of
+ {ok, Files} ->
+ lists:foreach(fun(File2) ->
+ remove_file(filename:join(File,File2))
+ end, Files),
+ case file:del_dir(File) of
+ ok -> ok;
+ {error, Reason} -> throw({error, Reason})
+ end;
+ {error, Reason} ->
+ throw({error, Reason})
+ end;
+ {ok, _Info} ->
+ case file:delete(File) of
+ ok -> ok;
+ {error, Reason} -> throw({error, Reason})
+ end;
+ {error, _Reason} ->
+ throw({error, {no_such_file, File}})
+
+ end.
+
+do_write_file(File, Str) ->
+ case file:open(File, [write]) of
+ {ok, Fd} ->
+ io:put_chars(Fd, Str),
+ file:close(Fd),
+ ok;
+ {error, Reason} ->
+ {error, {Reason, File}}
+ end.
+
+%%-----------------------------------------------------------------
+%% Change current applications (specifically, update their version,
+%% description and env.)
+%%-----------------------------------------------------------------
+change_appl_data(RelDir, #release{vsn = Vsn}, Masters) ->
+ Dir = filename:join([RelDir, Vsn]),
+ BootFile = filename:join(Dir, "start.boot"),
+ case read_file(BootFile, Masters) of
+ {ok, Bin} ->
+ Config = case consult(filename:join(Dir, "sys.config"), Masters) of
+ {ok, [Conf]} -> Conf;
+ _ -> []
+ end,
+ Appls = get_appls(binary_to_term(Bin)),
+ case application_controller:change_application_data(Appls,Config) of
+ ok -> Appls;
+ {error, Reason} -> exit({change_appl_data, Reason})
+ end;
+ {error, _Reason} ->
+ throw({error, {no_such_file, BootFile}})
+ end.
+
+%%-----------------------------------------------------------------
+%% This function is dependent on the application functions and
+%% the start script syntax.
+%%-----------------------------------------------------------------
+get_appls({script, _, Script}) -> get_appls(Script, []).
+
+%% kernel is taken care of separately
+get_appls([{kernelProcess, application_controller,
+ {application_controller, start, [App]}} |T], Res) ->
+ get_appls(T, [App | Res]);
+%% other applications but kernel
+get_appls([{apply, {application, load, [App]}} |T], Res) ->
+ get_appls(T, [App | Res]);
+get_appls([_ | T], Res) ->
+ get_appls(T, Res);
+get_appls([], Res) ->
+ Res.
+
+
+mon_nodes(true) ->
+ net_kernel:monitor_nodes(true);
+mon_nodes(false) ->
+ net_kernel:monitor_nodes(false),
+ flush().
+
+flush() ->
+ receive
+ {nodedown, _} -> flush();
+ {nodeup, _} -> flush()
+ after
+ 0 -> ok
+ end.
+
+prepare_restart_nt(#release{erts_vsn = EVsn, vsn = Vsn},
+ #release{erts_vsn = PermEVsn, vsn = PermVsn},
+ DataFileName) ->
+ CurrentServiceName = hd(string:tokens(atom_to_list(node()),"@"))
+ ++ "_" ++ PermVsn,
+ FutureServiceName = hd(string:tokens(atom_to_list(node()),"@"))
+ ++ "_" ++ Vsn,
+ CurrentService = case erlsrv:get_service(PermEVsn,CurrentServiceName) of
+ {error, Reason} ->
+ throw({error, Reason});
+ CS ->
+ CS
+ end,
+ FutureService = erlsrv:new_service(FutureServiceName,
+ CurrentService,
+ filename:nativename(DataFileName),
+ %% This is rather icky... On a
+ %% non permanent service, the
+ %% ERLSRV_SERVICE_NAME is
+ %% actually that of an old service,
+ %% to make heart commands work...
+ CurrentServiceName),
+
+ case erlsrv:store_service(EVsn, FutureService) of
+ {error, Rison} ->
+ throw({error,Rison});
+ _ ->
+ erlsrv:disable_service(EVsn, FutureServiceName),
+ ErlSrv = filename:nativename(erlsrv:erlsrv(EVsn)),
+ case heart:set_cmd(ErlSrv ++ " enable " ++ FutureServiceName ++
+ " & " ++ ErlSrv ++ " start " ++
+ FutureServiceName ++
+ " & " ++ ErlSrv ++ " disable " ++
+ FutureServiceName) of
+ ok ->
+ ok;
+ Error ->
+ throw({error, {'heart:set_cmd() error', Error}})
+ end
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Set things up for restarting the new emulator. The actual
+%% restart is performed by calling init:reboot() higher up.
+%%-----------------------------------------------------------------
+prepare_restart_new_emulator(StartPrg, RelDir,
+ Release, PRelease,
+ Masters) ->
+ #release{erts_vsn = EVsn, vsn = Vsn} = Release,
+ Data = EVsn ++ " " ++ Vsn,
+ DataFile = write_new_start_erl(Data, RelDir, Masters),
+ %% Tell heart to use DataFile instead of start_erl.data
+ case os:type() of
+ {win32,nt} ->
+ prepare_restart_nt(Release,PRelease,DataFile);
+ {unix,_} ->
+ StartP = check_start_prg(StartPrg, Masters),
+ case heart:set_cmd(StartP ++ " " ++ DataFile) of
+ ok ->
+ ok;
+ Error ->
+ throw({error, {'heart:set_cmd() error', Error}})
+ end
+ end.
+
+check_start_prg({do_check, StartPrg}, Masters) ->
+ check_file(StartPrg, regular, Masters),
+ StartPrg;
+check_start_prg({_, StartPrg}, _) ->
+ StartPrg.
+
+write_new_start_erl(Data, RelDir, false) ->
+ DataFile = filename:join([RelDir, "new_start_erl.data"]),
+ case do_write_file(DataFile, Data) of
+ ok -> DataFile;
+ Error -> throw(Error)
+ end;
+write_new_start_erl(Data, RelDir, Masters) ->
+ DataFile = filename:join([RelDir, "new_start_erl.data"]),
+ case at_all_masters(Masters, ?MODULE, do_write_file,
+ [DataFile, Data]) of
+ ok -> DataFile;
+ Error -> throw(Error)
+ end.
+
+%%-----------------------------------------------------------------
+%% When a new emulator shall be restarted, the current release
+%% is written with status tmp_current. When the new emulator
+%% is started, this function is called. The tmp_current release
+%% gets status unpacked on disk, and current in memory. If a reboot
+%% is made (due to a crash), the release is just unpacked. If a crash
+%% occurs before a call to transform_release is made, the old emulator
+%% is started, and transform_release is called for it. The tmp_current
+%% release is changed to unpacked.
+%% If the release is made permanent, this is written to disk.
+%%-----------------------------------------------------------------
+transform_release(ReleaseDir, Releases, Masters) ->
+ F = fun(Release) when Release#release.status == tmp_current ->
+ Release#release{status = unpacked};
+ (Release) -> Release
+ end,
+ case lists:map(F, Releases) of
+ Releases ->
+ Releases;
+ DReleases ->
+ write_releases(ReleaseDir, DReleases, Masters),
+ F1 = fun(Release) when Release#release.status == tmp_current ->
+ case init:script_id() of
+ {_Name, Vsn} when Release#release.vsn == Vsn ->
+ Release#release{status = current};
+ _ ->
+ Release#release{status = unpacked}
+ end;
+ (Release) -> Release
+ end,
+ lists:map(F1, Releases)
+ end.
+
+%%-----------------------------------------------------------------
+%% Functions handling files, RELEASES, start_erl.data etc.
+%% This functions consider if the release_handler is a client and
+%% in that case performs the operations at all master nodes or at
+%% none (in case of failure).
+%%-----------------------------------------------------------------
+
+check_opt_file(FileName, Type, Masters) ->
+ case catch check_file(FileName, Type, Masters) of
+ ok ->
+ true;
+ _Error ->
+ io:format("Warning: ~p missing (optional)~n", [FileName]),
+ false
+ end.
+
+check_file(FileName, Type, false) ->
+ do_check_file(FileName, Type);
+check_file(FileName, Type, Masters) ->
+ check_file_masters(FileName, Type, Masters).
+
+%% Check that file exists at all masters.
+check_file_masters(FileName, Type, [Master|Masters]) ->
+ do_check_file(Master, FileName, Type),
+ check_file_masters(FileName, Type, Masters);
+check_file_masters(_FileName, _Type, []) ->
+ ok.
+
+%% Type == regular | directory
+do_check_file(FileName, Type) ->
+ case file:read_file_info(FileName) of
+ {ok, Info} when Info#file_info.type==Type -> ok;
+ {error, _Reason} -> throw({error, {no_such_file, FileName}})
+ end.
+
+do_check_file(Master, FileName, Type) ->
+ case rpc:call(Master, file, read_file_info, [FileName]) of
+ {ok, Info} when Info#file_info.type==Type -> ok;
+ _ -> throw({error, {no_such_file, {Master, FileName}}})
+ end.
+
+%%-----------------------------------------------------------------
+%% If Rel doesn't exists in tar it could have been created
+%% by the user in another way, i.e. ignore this here.
+%%-----------------------------------------------------------------
+extract_rel_file(Rel, Tar, Root) ->
+ erl_tar:extract(Tar, [{files, [Rel]}, {cwd, Root}, compressed]).
+
+extract_tar(Root, Tar) ->
+ case erl_tar:extract(Tar, [keep_old_files, {cwd, Root}, compressed]) of
+ ok ->
+ ok;
+ {error, Reason, Name} -> % Old erl_tar.
+ throw({error, {cannot_extract_file, Name, Reason}});
+ {error, {Name, Reason}} -> % New erl_tar (R3A).
+ throw({error, {cannot_extract_file, Name, Reason}})
+ end.
+
+write_releases(Dir, NewReleases, false) ->
+ case do_write_release(Dir, "RELEASES", NewReleases) of
+ ok -> ok;
+ Error -> throw(Error)
+ end;
+write_releases(Dir, NewReleases, Masters) ->
+ all_masters(Masters),
+ write_releases_m(Dir, NewReleases, Masters).
+
+do_write_release(Dir, RELEASES, NewReleases) ->
+ case file:open(filename:join(Dir, RELEASES), [write]) of
+ {ok, Fd} ->
+ ok = io:format(Fd, "~p.~n", [NewReleases]),
+ file:close(Fd),
+ ok;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%%-----------------------------------------------------------------
+%% Write the "RELEASES" file at all master nodes.
+%% 1. Save "RELEASES.backup" at all nodes.
+%% 2. Save "RELEASES.change" at all nodes.
+%% 3. Update the "RELEASES.change" file at all nodes.
+%% 4. Move "RELEASES.change" to "RELEASES".
+%% 5. Remove "RELEASES.backup" at all nodes.
+%%
+%% If one of the steps above fails, all steps is recovered from
+%% (as long as possible), except for 5 which is allowed to fail.
+%%-----------------------------------------------------------------
+write_releases_m(Dir, NewReleases, Masters) ->
+ RelFile = filename:join(Dir, "RELEASES"),
+ Backup = filename:join(Dir, "RELEASES.backup"),
+ Change = filename:join(Dir, "RELEASES.change"),
+ ensure_RELEASES_exists(Masters, RelFile),
+ case at_all_masters(Masters, ?MODULE, do_copy_files,
+ [RelFile, [Backup, Change]]) of
+ ok ->
+ case at_all_masters(Masters, ?MODULE, do_write_release,
+ [Dir, "RELEASES.change", NewReleases]) of
+ ok ->
+ case at_all_masters(Masters, file, rename,
+ [Change, RelFile]) of
+ ok ->
+ remove_files(all, [Backup, Change], Masters),
+ ok;
+ {error, {Master, R}} ->
+ takewhile(Master, Masters, file, rename,
+ [Backup, RelFile]),
+ remove_files(all, [Backup, Change], Masters),
+ throw({error, {Master, R, move_releases}})
+ end;
+ {error, {Master, R}} ->
+ remove_files(all, [Backup, Change], Masters),
+ throw({error, {Master, R, update_releases}})
+ end;
+ {error, {Master, R}} ->
+ remove_files(Master, [Backup, Change], Masters),
+ throw({error, {Master, R, backup_releases}})
+ end.
+
+ensure_RELEASES_exists(Masters, RelFile) ->
+ case at_all_masters(Masters, ?MODULE, do_ensure_RELEASES, [RelFile]) of
+ ok ->
+ ok;
+ {error, {Master, R}} ->
+ throw({error, {Master, R, ensure_RELEASES_exists}})
+ end.
+
+copy_file(File, Dir, false) ->
+ case do_copy_file(File, Dir) of
+ ok -> ok;
+ Error -> throw(Error)
+ end;
+copy_file(File, Dir, Masters) ->
+ all_masters(Masters),
+ copy_file_m(File, Dir, Masters).
+
+%%-----------------------------------------------------------------
+%% copy File to Dir at every master node.
+%% If an error occurs at a node, the total copy failed.
+%% We do not have to cleanup in case of failure as this
+%% copy_file is harmless.
+%%-----------------------------------------------------------------
+copy_file_m(File, Dir, [Master|Masters]) ->
+ case rpc:call(Master, ?MODULE, do_copy_file, [File, Dir]) of
+ ok -> copy_file_m(File, Dir, Masters);
+ {error, {Reason, F}} -> throw({error, {Master, Reason, F}});
+ Other -> throw({error, {Master, Other, File}})
+ end;
+copy_file_m(_File, _Dir, []) ->
+ ok.
+
+do_copy_file(File, Dir) ->
+ File2 = filename:join(Dir, filename:basename(File)),
+ do_copy_file1(File, File2).
+
+do_copy_file1(File, File2) ->
+ case file:read_file(File) of
+ {ok, Bin} ->
+ case file:write_file(File2, Bin) of
+ ok -> ok;
+ {error, Reason} ->
+ {error, {Reason, File2}}
+ end;
+ {error, Reason} ->
+ {error, {Reason, File}}
+ end.
+
+%%-----------------------------------------------------------------
+%% Copy File to a list of files.
+%%-----------------------------------------------------------------
+do_copy_files(File, [ToFile|ToFiles]) ->
+ case do_copy_file1(File, ToFile) of
+ ok -> do_copy_files(File, ToFiles);
+ Error -> Error
+ end;
+do_copy_files(_, []) ->
+ ok.
+
+%%-----------------------------------------------------------------
+%% Copy each Src file to Dest file in the list of files.
+%%-----------------------------------------------------------------
+do_copy_files([{Src, Dest}|Files]) ->
+ case do_copy_file1(Src, Dest) of
+ ok -> do_copy_files(Files);
+ Error -> Error
+ end;
+do_copy_files([]) ->
+ ok.
+
+%%-----------------------------------------------------------------
+%% Rename each Src file to Dest file in the list of files.
+%%-----------------------------------------------------------------
+do_rename_files([{Src, Dest}|Files]) ->
+ case file:rename(Src, Dest) of
+ ok -> do_rename_files(Files);
+ Error -> Error
+ end;
+do_rename_files([]) ->
+ ok.
+
+%%-----------------------------------------------------------------
+%% Remove a list of files. Ignore failure.
+%%-----------------------------------------------------------------
+do_remove_files([File|Files]) ->
+ file:delete(File),
+ do_remove_files(Files);
+do_remove_files([]) ->
+ ok.
+
+
+%%-----------------------------------------------------------------
+%% Ensure that the RELEASES file exists.
+%% If not create an empty RELEASES file.
+%%-----------------------------------------------------------------
+do_ensure_RELEASES(RelFile) ->
+ case file:read_file_info(RelFile) of
+ {ok, _} -> ok;
+ _ -> do_write_file(RelFile, "[]. ")
+ end.
+
+%%-----------------------------------------------------------------
+%% Make a directory, ignore failures (captured later).
+%%-----------------------------------------------------------------
+make_dir(Dir, false) ->
+ file:make_dir(Dir);
+make_dir(Dir, Masters) ->
+ lists:foreach(fun(Master) -> rpc:call(Master, file, make_dir, [Dir]) end,
+ Masters).
+
+%%-----------------------------------------------------------------
+%% Check that all masters are alive.
+%%-----------------------------------------------------------------
+all_masters(Masters) ->
+ case rpc:multicall(Masters, erlang, info, [version]) of
+ {_, []} -> ok;
+ {_, BadNodes} -> throw({error, {bad_masters, BadNodes}})
+ end.
+
+%%-----------------------------------------------------------------
+%% Evaluate {M,F,A} at all masters.
+%% {M,F,A} is supposed to return ok. Otherwise at_all_masters
+%% returns {error, {Master, Other}}.
+%%-----------------------------------------------------------------
+at_all_masters([Master|Masters], M, F, A) ->
+ case rpc:call(Master, M, F, A) of
+ ok -> at_all_masters(Masters, M, F, A);
+ Error -> {error, {Master, Error}}
+ end;
+at_all_masters([], _, _, _) ->
+ ok.
+
+%%-----------------------------------------------------------------
+%% Evaluate {M,F,A} at all masters until Master is found.
+%% Ignore {M,F,A} return value.
+%%-----------------------------------------------------------------
+takewhile(Master, Masters, M, F, A) ->
+ lists:takewhile(fun(Ma) when Ma == Master ->
+ false;
+ (Ma) ->
+ rpc:call(Ma, M, F, A),
+ true
+ end, Masters),
+ ok.
+
+consult(File, false) -> file:consult(File);
+consult(File, Masters) -> consult_master(Masters, File).
+
+%%-----------------------------------------------------------------
+%% consult the File at any master node.
+%% If the file does not exist at one node it should
+%% not exist at any other node either.
+%%-----------------------------------------------------------------
+consult_master([Master|Ms], File) ->
+ case rpc:call(Master, file, consult, [File]) of
+ {badrpc, _} -> consult_master(Ms, File);
+ Res -> Res
+ end;
+consult_master([], _File) ->
+ {error, no_master}.
+
+read_file(File, false) ->
+ file:read_file(File);
+read_file(File, Masters) ->
+ read_master(Masters, File).
+
+%% Ignore status of each delete !
+remove_files(Master, Files, Masters) ->
+ takewhile(Master, Masters, ?MODULE, do_remove_files, [Files]).
+
+%%-----------------------------------------------------------------
+%% read the File at any master node.
+%% If the file does not exist at one node it should
+%% not exist at any other node either.
+%%-----------------------------------------------------------------
+read_master([Master|Ms], File) ->
+ case rpc:call(Master, file, read_file, [File]) of
+ {badrpc, _} -> read_master(Ms, File);
+ Res -> Res
+ end;
+read_master([], _File) ->
+ {error, no_master}.
+
+%%-----------------------------------------------------------------
+%% Write start_erl.data.
+%%-----------------------------------------------------------------
+write_start(File, Data, false) ->
+ case do_write_file(File, Data) of
+ ok -> ok;
+ Error -> throw(Error)
+ end;
+write_start(File, Data, Masters) ->
+ all_masters(Masters),
+ write_start_m(File, Data, Masters).
+
+
+%%-----------------------------------------------------------------
+%% Write the "start_erl.data" file at all master nodes.
+%% 1. Save "start_erl.backup" at all nodes.
+%% 2. Write the "start_erl.change" file at all nodes.
+%% 3. Move "start_erl.change" to "start_erl.data".
+%% 4. Remove "start_erl.backup" at all nodes.
+%%
+%% If one of the steps above fails, all steps is recovered from
+%% (as long as possible), except for 4 which is allowed to fail.
+%%-----------------------------------------------------------------
+write_start_m(File, Data, Masters) ->
+ Dir = filename:dirname(File),
+ Backup = filename:join(Dir, "start_erl.backup"),
+ Change = filename:join(Dir, "start_erl.change"),
+ case at_all_masters(Masters, ?MODULE, do_copy_files,
+ [File, [Backup]]) of
+ ok ->
+ case at_all_masters(Masters, ?MODULE, do_write_file,
+ [Change, Data]) of
+ ok ->
+ case at_all_masters(Masters, file, rename,
+ [Change, File]) of
+ ok ->
+ remove_files(all, [Backup, Change], Masters),
+ ok;
+ {error, {Master, R}} ->
+ takewhile(Master, Masters, file, rename,
+ [Backup, File]),
+ remove_files(all, [Backup, Change], Masters),
+ throw({error, {Master, R, move_start_erl}})
+ end;
+ {error, {Master, R}} ->
+ remove_files(all, [Backup, Change], Masters),
+ throw({error, {Master, R, write_start_erl}})
+ end;
+ {error, {Master, R}} ->
+ remove_files(Master, [Backup], Masters),
+ throw({error, {Master, R, backup_start_erl}})
+ end.
+
+%%-----------------------------------------------------------------
+%% Copy the "start.boot" and "sys.config" from SrcDir to DestDir at all
+%% master nodes.
+%% 1. Save DestDir/"start.backup" and DestDir/"sys.backup" at all nodes.
+%% 2. Copy files at all nodes.
+%% 3. Remove backup files at all nodes.
+%%
+%% If one of the steps above fails, all steps is recovered from
+%% (as long as possible), except for 3 which is allowed to fail.
+%%-----------------------------------------------------------------
+set_static_files(SrcDir, DestDir, Masters) ->
+ all_masters(Masters),
+ Boot = "start.boot",
+ Config = "sys.config",
+ SrcBoot = filename:join(SrcDir, Boot),
+ DestBoot = filename:join(DestDir, Boot),
+ BackupBoot = filename:join(DestDir, "start.backup"),
+ SrcConf = filename:join(SrcDir, Config),
+ DestConf = filename:join(DestDir, Config),
+ BackupConf = filename:join(DestDir, "sys.backup"),
+
+ case at_all_masters(Masters, ?MODULE, do_copy_files,
+ [[{DestBoot, BackupBoot},
+ {DestConf, BackupConf}]]) of
+ ok ->
+ case at_all_masters(Masters, ?MODULE, do_copy_files,
+ [[{SrcBoot, DestBoot},
+ {SrcConf, DestConf}]]) of
+ ok ->
+ remove_files(all, [BackupBoot, BackupConf], Masters),
+ ok;
+ {error, {Master, R}} ->
+ takewhile(Master, Masters, ?MODULE, do_rename_files,
+ [{BackupBoot, DestBoot},
+ {BackupConf, DestConf}]),
+ remove_files(all, [BackupBoot, BackupConf], Masters),
+ throw({error, {Master, R, copy_start_config}})
+ end;
+ {error, {Master, R}} ->
+ remove_files(Master, [BackupBoot, BackupConf], Masters),
+ throw({error, {Master, R, backup_start_config}})
+ end.
diff --git a/lib/sasl/src/release_handler_1.erl b/lib/sasl/src/release_handler_1.erl
new file mode 100644
index 0000000000..e3e3caba99
--- /dev/null
+++ b/lib/sasl/src/release_handler_1.erl
@@ -0,0 +1,647 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(release_handler_1).
+
+%% External exports
+-export([eval_script/3, eval_script/4, check_script/2]).
+-export([get_vsn/1]). %% exported because used in a test case
+
+-record(eval_state, {bins = [], stopped = [], suspended = [], apps = [],
+ libdirs, unpurged = [], vsns = [], newlibs = [],
+ opts = []}).
+%%-----------------------------------------------------------------
+%% bins = [{Mod, Binary, FileName}]
+%% stopped = [{Mod, [pid()]}] - list of stopped pids for each module
+%% suspended = [{Mod, [pid()]}] - list of suspended pids for each module
+%% apps = [app_spec()] - list of all apps in the new release
+%% libdirs = [{Lib, LibVsn, LibDir}] - Maps Lib to Vsn and Directory
+%% unpurged = [{Mod, soft_purge | brutal_purge}]
+%% vsns = [{Mod, OldVsn, NewVsn}] - remember the old vsn of a mod
+%% before it is removed/a new vsn is loaded; the new vsn
+%% is kept in case of a downgrade, where the code_change
+%% function receives the vsn of the module to downgrade
+%% *to*.
+%% newlibs = [{Lib, Dir}] - list of all new libs; used to change
+%% the code path
+%% opts = [{Tag, Value}] - list of options
+%%-----------------------------------------------------------------
+
+
+%%%-----------------------------------------------------------------
+%%% This is a low-level release handler.
+%%%-----------------------------------------------------------------
+check_script(Script, LibDirs) ->
+ case catch check_old_processes(Script) of
+ ok ->
+ {Before, _After} = split_instructions(Script),
+ case catch lists:foldl(fun(Instruction, EvalState1) ->
+ eval(Instruction, EvalState1)
+ end,
+ #eval_state{libdirs = LibDirs},
+ Before) of
+ EvalState2 when is_record(EvalState2, eval_state) -> ok;
+ {error, Error} -> {error, Error};
+ Other -> {error, Other}
+ end;
+ {error, Mod} ->
+ {error, {old_processes, Mod}}
+ end.
+
+eval_script(Script, Apps, LibDirs) ->
+ eval_script(Script, Apps, LibDirs, []).
+
+eval_script(Script, Apps, LibDirs, Opts) ->
+ case catch check_old_processes(Script) of
+ ok ->
+ {Before, After} = split_instructions(Script),
+ case catch lists:foldl(fun(Instruction, EvalState1) ->
+ eval(Instruction, EvalState1)
+ end,
+ #eval_state{apps = Apps,
+ libdirs = LibDirs,
+ opts = Opts},
+ Before) of
+ EvalState2 when is_record(EvalState2, eval_state) ->
+ case catch lists:foldl(fun(Instruction, EvalState3) ->
+ eval(Instruction, EvalState3)
+ end,
+ EvalState2,
+ After) of
+ EvalState4 when is_record(EvalState4, eval_state) ->
+ {ok, EvalState4#eval_state.unpurged};
+ restart_new_emulator ->
+ restart_new_emulator;
+ Error ->
+ {'EXIT', Error}
+ end;
+ {error, Error} -> {error, Error};
+ Other -> {error, Other}
+ end;
+ {error, Mod} ->
+ {error, {old_processes, Mod}}
+ end.
+
+%%%-----------------------------------------------------------------
+%%% Internal functions
+%%%-----------------------------------------------------------------
+split_instructions(Script) ->
+ split_instructions(Script, []).
+split_instructions([point_of_no_return | T], Before) ->
+ {lists:reverse(Before), [point_of_no_return | T]};
+split_instructions([H | T], Before) ->
+ split_instructions(T, [H | Before]);
+split_instructions([], Before) ->
+ {[], lists:reverse(Before)}.
+
+%%-----------------------------------------------------------------
+%% Func: check_old_processes/1
+%% Args: Script = [instruction()]
+%% Purpose: Check if there is any process that runs an old version
+%% of a module that should be soft_purged, (i.e. not purged
+%% at all if there is any such process). Returns {error, Mod}
+%% if so, ok otherwise.
+%% Returns: ok | {error, Mod}
+%% Mod = atom()
+%%-----------------------------------------------------------------
+check_old_processes(Script) ->
+ lists:foreach(fun({load, {Mod, soft_purge, _PostPurgeMethod}}) ->
+ check_old_code(Mod);
+ ({remove, {Mod, soft_purge, _PostPurgeMethod}}) ->
+ check_old_code(Mod);
+ (_) -> ok
+ end,
+ Script).
+
+check_old_code(Mod) ->
+ lists:foreach(fun(Pid) ->
+ case erlang:check_process_code(Pid, Mod) of
+ false -> ok;
+ true -> throw({error, Mod})
+ end
+ end,
+ erlang:processes()).
+
+%%-----------------------------------------------------------------
+%% An unpurged module is a module for which there exist an old
+%% version of the code. This should only be the case if there are
+%% processes running the old version of the code.
+%%
+%% This functions evaluates each instruction. Note that the
+%% instructions here are low-level instructions. e.g. lelle's
+%% old synchronized_change would be translated to
+%% {load_object_code, Modules},
+%% {suspend, Modules}, [{load, Module}],
+%% {resume, Modules}, {purge, Modules}
+%% Or, for example, if we want to do advanced external code change
+%% on two modules that depend on each other, by killing them and
+%% then restaring them, we could do:
+%% {load_object_code, [Mod1, Mod2]},
+%% % delete old version
+%% {remove, {Mod1, brutal_purge}}, {remove, {Mod2, brutal_purge}},
+%% % now, some procs migth be running prev current (now old) version
+%% % kill them, and load new version
+%% {load, {Mod1, brutal_purge}}, {load, {Mod2, brutal_purge}}
+%% % now, there is one version of the code (new, current)
+%%
+%% NOTE: All load_object_code must be first in the script,
+%% a point_of_no_return must be present (if load_object_code
+%% is present).
+%%
+%% {load_object_code, {Lib, LibVsn, [Mod]}
+%% read the files as binarys. do not make code out of them
+%% {load, {Module, PrePurgeMethod, PostPurgeMethod}}
+%% Module must have been load_object_code:ed. make code out of it
+%% old procs && soft_purge => no new release
+%% old procs && brutal_purge => old procs killed
+%% The new old code will be gc:ed later on, if PostPurgeMethod =
+%% soft_purge. If it is brutal_purge, the code is purged when
+%% the release is made permanent.
+%% {remove, {Module, PrePurgeMethod, PostPurgeMethod}}
+%% make current version old. no current left.
+%% old procs && soft_purge => no new release
+%% old procs && brutal_purge => old procs killed
+%% The new old code will be gc:ed later on, if PostPurgeMethod =
+%% soft_purge. If it is brutal_purge, the code is purged when
+%% the release is made permanent.
+%% {purge, Modules}
+%% kill all procs running old code, delete old code
+%% {suspend, [Module | {Module, Timeout}]}
+%% If a process doesn't repsond - never mind. It will be killed
+%% later on (if a purge is performed).
+%% Hmm, we must do something smart here... we should probably kill it,
+%% but we cant, because its supervisor will restart it directly! Maybe
+%% we should keep a list of those, call supervisor:terminate_child()
+%% when all others are suspended, and call sup:restart_child() when the
+%% others are resumed.
+%% {code_change, [{Module, Extra}]}
+%% {code_change, Mode, [{Module, Extra}]} Mode = up | down
+%% Send code_change only to suspended procs running this code
+%% {resume, [Module]}
+%% resume all previously suspended processes
+%% {stop, [Module]}
+%% stop all procs running this code
+%% {start, [Module]}
+%% starts the procs that were previously stopped for this code.
+%% Note that this will start processes in exactly the same place
+%% in the suptree where there were procs previously.
+%% {sync_nodes, Id, [Node]}
+%% {sync_nodes, Id, {M, F, A}}
+%% Synchronizes with the Nodes (or apply(M,F,A) == Nodes). All Nodes
+%% must also exectue the same line. Waits for all these nodes to get
+%% to this line.
+%% point_of_no_return
+%% restart_new_emulator
+%% {stop_application, Appl} - Impl with apply
+%% {unload_application, Appl} - Impl with {remove..}
+%% {load_application, Appl} - Impl with {load..}
+%% {start_application, Appl} - Impl with apply
+%%-----------------------------------------------------------------
+eval({load_object_code, {Lib, LibVsn, Modules}}, EvalState) ->
+ case lists:keysearch(Lib, 1, EvalState#eval_state.libdirs) of
+ {value, {Lib, LibVsn, LibDir}} ->
+ Ebin = filename:join(LibDir, "ebin"),
+ Ext = code:objfile_extension(),
+ {NewBins, NewVsns} =
+ lists:foldl(fun(Mod, {Bins, Vsns}) ->
+ File = lists:concat([Mod, Ext]),
+ FName = filename:join(Ebin, File),
+ case erl_prim_loader:get_file(FName) of
+ {ok, Bin, FName2} ->
+ NVsns = add_new_vsn(Mod, FName2, Vsns),
+ {[{Mod, Bin, FName2} | Bins],NVsns};
+ error ->
+ throw({error, {no_such_file,FName}})
+ end
+ end,
+ {EvalState#eval_state.bins,
+ EvalState#eval_state.vsns},
+ Modules),
+ NewLibs = [{Lib, Ebin} | EvalState#eval_state.newlibs],
+ EvalState#eval_state{bins = NewBins,
+ newlibs = NewLibs,
+ vsns = NewVsns};
+ {value, {Lib, LibVsn2, _LibDir}} ->
+ throw({error, {bad_lib_vsn, Lib, LibVsn2}})
+ end;
+eval(point_of_no_return, EvalState) ->
+ Libs = case get_opt(update_paths, EvalState, false) of
+ false ->
+ EvalState#eval_state.newlibs; % [{Lib, Path}]
+ true ->
+ lists:map(fun({Lib, _LibVsn, LibDir}) ->
+ Ebin= filename:join(LibDir,"ebin"),
+ {Lib, Ebin}
+ end,
+ EvalState#eval_state.libdirs)
+ end,
+ lists:foreach(fun({Lib, Path}) -> code:replace_path(Lib, Path) end,
+ Libs),
+ EvalState;
+eval({load, {Mod, _PrePurgeMethod, PostPurgeMethod}}, EvalState) ->
+ Bins = EvalState#eval_state.bins,
+ {value, {_Mod, Bin, File}} = lists:keysearch(Mod, 1, Bins),
+ % load_binary kills all procs running old code
+ % if soft_purge, we know that there are no such procs now
+ Vsns = EvalState#eval_state.vsns,
+ NewVsns = add_old_vsn(Mod, Vsns),
+ code:load_binary(Mod, File, Bin),
+ % Now, the prev current is old. There might be procs
+ % running it. Find them.
+ Unpurged = do_soft_purge(Mod,PostPurgeMethod,EvalState#eval_state.unpurged),
+ EvalState#eval_state{bins = lists:keydelete(Mod, 1, Bins),
+ unpurged = Unpurged,
+ vsns = NewVsns};
+eval({remove, {Mod, _PrePurgeMethod, PostPurgeMethod}}, EvalState) ->
+ % purge kills all procs running old code
+ % if soft_purge, we know that there are no such procs now
+ Vsns = EvalState#eval_state.vsns,
+ NewVsns = add_old_vsn(Mod, Vsns),
+ code:purge(Mod),
+ code:delete(Mod),
+ % Now, the prev current is old. There might be procs
+ % running it. Find them.
+ Unpurged =
+ case code:soft_purge(Mod) of
+ true -> EvalState#eval_state.unpurged;
+ false -> [{Mod, PostPurgeMethod} | EvalState#eval_state.unpurged]
+ end,
+%% Bins = EvalState#eval_state.bins,
+%% EvalState#eval_state{bins = lists:keydelete(Mod, 1, Bins),
+ EvalState#eval_state{unpurged = Unpurged, vsns = NewVsns};
+eval({purge, Modules}, EvalState) ->
+ % Now, if there are any processes still executing old code, OR
+ % if some new processes started after suspend but before load,
+ % these are killed.
+ lists:foreach(fun(Mod) -> code:purge(Mod) end, Modules),
+ EvalState;
+eval({suspend, Modules}, EvalState) ->
+ Procs = get_supervised_procs(),
+ NewSuspended =
+ lists:foldl(fun(ModSpec, Suspended) ->
+ {Module, Def} = case ModSpec of
+ {Mod, ModTimeout} ->
+ {Mod, ModTimeout};
+ Mod ->
+ {Mod, default}
+ end,
+ Timeout = get_opt(suspend_timeout, EvalState, Def),
+ Pids = suspend(Module, Procs, Timeout),
+ [{Module, Pids} | Suspended]
+ end,
+ EvalState#eval_state.suspended,
+ Modules),
+ EvalState#eval_state{suspended = NewSuspended};
+eval({resume, Modules}, EvalState) ->
+ NewSuspended =
+ lists:foldl(fun(Mod, Suspended) ->
+ lists:filter(fun({Mod2, Pids}) when Mod2 == Mod ->
+ resume(Pids),
+ false;
+ (_) ->
+ true
+ end,
+ Suspended)
+ end,
+ EvalState#eval_state.suspended,
+ Modules),
+ EvalState#eval_state{suspended = NewSuspended};
+eval({code_change, Modules}, EvalState) ->
+ eval({code_change, up, Modules}, EvalState);
+eval({code_change, Mode, Modules}, EvalState) ->
+ Suspended = EvalState#eval_state.suspended,
+ Vsns = EvalState#eval_state.vsns,
+ Timeout = get_opt(code_change_timeout, EvalState, default),
+ lists:foreach(fun({Mod, Extra}) ->
+ Vsn =
+ case lists:keysearch(Mod, 1, Vsns) of
+ {value, {Mod, OldVsn, _NewVsn}}
+ when Mode == up -> OldVsn;
+ {value, {Mod, _OldVsn, NewVsn}}
+ when Mode == down -> {down, NewVsn};
+ _ when Mode == up -> undefined;
+ _ -> {down, undefined}
+ end,
+ case lists:keysearch(Mod, 1, Suspended) of
+ {value, {_Mod, Pids}} ->
+ change_code(Pids, Mod, Vsn, Extra, Timeout);
+ _ -> ok
+ end
+ end,
+ Modules),
+ EvalState;
+eval({stop, Modules}, EvalState) ->
+ Procs = get_supervised_procs(),
+ NewStopped =
+ lists:foldl(fun(Mod, Stopped) ->
+ Procs2 = stop(Mod, Procs),
+ [{Mod, Procs2} | Stopped]
+ end,
+ EvalState#eval_state.stopped,
+ Modules),
+ EvalState#eval_state{stopped = NewStopped};
+eval({start, Modules}, EvalState) ->
+ NewStopped =
+ lists:foldl(fun(Mod, Stopped) ->
+ lists:filter(fun({Mod2, Procs}) when Mod2 == Mod ->
+ start(Procs),
+ false;
+ (_) ->
+ true
+ end,
+ Stopped)
+ end,
+ EvalState#eval_state.stopped,
+ Modules),
+ EvalState#eval_state{stopped = NewStopped};
+eval({sync_nodes, Id, {M, F, A}}, EvalState) ->
+ sync_nodes(Id, apply(M, F, A)),
+ EvalState;
+eval({sync_nodes, Id, Nodes}, EvalState) ->
+ sync_nodes(Id, Nodes),
+ EvalState;
+eval({apply, {M, F, A}}, EvalState) ->
+ apply(M, F, A),
+ EvalState;
+eval(restart_new_emulator, _EvalState) ->
+ throw(restart_new_emulator).
+
+get_opt(Tag, EvalState, Default) ->
+ case lists:keysearch(Tag, 1, EvalState#eval_state.opts) of
+ {value, {_Tag, Value}} -> Value;
+ false -> Default
+ end.
+
+%%-----------------------------------------------------------------
+%% This is a first approximation. Unfortunately, we might end up
+%% with the situation that after this suspendation, some new
+%% processes start *before* we have loaded the new code, and these
+%% will execute the old code. These processes could be terminated
+%% later on (if the prev current version is purged). The same
+%% goes for processes that didn't respond to the suspend message.
+%%-----------------------------------------------------------------
+suspend(Mod, Procs, Timeout) ->
+ lists:zf(fun({_Sup, _Name, Pid, Mods}) ->
+ case lists:member(Mod, Mods) of
+ true ->
+ case catch sys_suspend(Pid, Timeout) of
+ ok -> {true, Pid};
+ _ ->
+ % If the proc hangs, make sure to
+ % resume it when it gets suspended!
+ catch sys:resume(Pid),
+ false
+ end;
+ false ->
+ false
+ end
+ end,
+ Procs).
+
+sys_suspend(Pid, default) ->
+ sys:suspend(Pid);
+sys_suspend(Pid, Timeout) ->
+ sys:suspend(Pid, Timeout).
+
+resume(Pids) ->
+ lists:foreach(fun(Pid) -> catch sys:resume(Pid) end, Pids).
+
+change_code(Pids, Mod, Vsn, Extra, Timeout) ->
+ Fun = fun(Pid) ->
+ case Timeout of
+ default ->
+ ok = sys:change_code(Pid, Mod, Vsn, Extra);
+ _Else ->
+ ok = sys:change_code(Pid, Mod, Vsn, Extra, Timeout)
+ end
+ end,
+ lists:foreach(Fun, Pids).
+
+stop(Mod, Procs) ->
+ lists:zf(fun({undefined, _Name, _Pid, _Mods}) ->
+ false;
+ ({Sup, Name, _Pid, Mods}) ->
+ case lists:member(Mod, Mods) of
+ true ->
+ case catch supervisor:terminate_child(
+ Sup, Name) of
+ ok -> {true, {Sup, Name}};
+ _ -> false
+ end;
+ false -> false
+ end
+ end,
+ Procs).
+
+start(Procs) ->
+ lists:foreach(fun({Sup, Name}) ->
+ catch supervisor:restart_child(Sup, Name)
+ end,
+ Procs).
+
+%%-----------------------------------------------------------------
+%% Func: get_supervised_procs/0
+%% Purpose: This is the magic function. It finds all process in
+%% the system and which modules they execute as a call_back or
+%% process module.
+%% This is achieved by asking the main supervisor for the
+%% applications for all children and their modules
+%% (recursively).
+%% NOTE: If a supervisor is suspended, it isn't possible to call
+%% which_children. Code change on a supervisor should be
+%% done in another way; the only code in a supervisor is
+%% code for starting children. Therefore, to change a
+%% supervisor module, we should load the new version, and then
+%% delete the old. Then we should perform the start changes
+%% manually, by adding/deleting children.
+%% Returns: [{SuperPid, ChildName, ChildPid, Mods}]
+%%-----------------------------------------------------------------
+%% OTP-3452. For each application the first item contains the pid
+%% of the top supervisor, and the name of the supervisor call-back module.
+%%-----------------------------------------------------------------
+
+get_supervised_procs() ->
+ lists:foldl(
+ fun(Application, Procs) ->
+ case application_controller:get_master(Application) of
+ Pid when is_pid(Pid) ->
+ {Root, _AppMod} = application_master:get_child(Pid),
+ case get_supervisor_module(Root) of
+ {ok, SupMod} ->
+ get_procs(supervisor:which_children(Root),
+ Root) ++
+ [{undefined, undefined, Root, [SupMod]} |
+ Procs];
+ {error, _} ->
+ error_logger:error_msg("release_handler: "
+ "cannot find top "
+ "supervisor for "
+ "application ~w~n",
+ [Application]),
+ get_procs(supervisor:which_children(Root),
+ Root) ++ Procs
+ end;
+ _ -> Procs
+ end
+ end,
+ [],
+ lists:map(fun({Application, _Name, _Vsn}) ->
+ Application
+ end,
+ application:which_applications())).
+
+get_procs([{Name, Pid, worker, dynamic} | T], Sup) when is_pid(Pid) ->
+ Mods = get_dynamic_mods(Pid),
+ [{Sup, Name, Pid, Mods} | get_procs(T, Sup)];
+get_procs([{Name, Pid, worker, Mods} | T], Sup) when is_pid(Pid), is_list(Mods) ->
+ [{Sup, Name, Pid, Mods} | get_procs(T, Sup)];
+get_procs([{Name, Pid, supervisor, Mods} | T], Sup) when is_pid(Pid) ->
+ [{Sup, Name, Pid, Mods} | get_procs(T, Sup)] ++
+ get_procs(supervisor:which_children(Pid), Pid);
+get_procs([_H | T], Sup) ->
+ get_procs(T, Sup);
+get_procs(_, _Sup) ->
+ [].
+
+get_dynamic_mods(Pid) ->
+ {ok,Res} = gen:call(Pid, self(), get_modules),
+ Res.
+
+%% XXXX
+%% Note: The following is a terrible hack done in order to resolve the
+%% problem stated in ticket OTP-3452.
+
+%% XXXX NOTE WELL: This record is from supervisor.erl. Also the record
+%% name is really `state'.
+-record(supervisor_state, {name,
+ strategy,
+ children = [],
+ dynamics = [],
+ intensity,
+ period,
+ restarts = [],
+ module,
+ args}).
+
+%% Return the name of the call-back module that implements the
+%% (top) supervisor SupPid.
+%% Returns: {ok, Module} | {error,undefined}
+%%
+get_supervisor_module(SupPid) ->
+ case catch get_supervisor_module1(SupPid) of
+ {ok, Module} when is_atom(Module) ->
+ {ok, Module};
+ _Other ->
+ io:format("~w: reason: ~w~n", [SupPid, _Other]),
+ {error, undefined}
+ end.
+
+get_supervisor_module1(SupPid) ->
+ {status, _Pid, {module, _Mod},
+ [_PDict, _SysState, _Parent, _Dbg, Misc]} = sys:get_status(SupPid),
+ [_Name, State, _Type, _Time] = Misc,
+ %% Cannot use #supervisor_state{module = Module} = State.
+ {ok, element(#supervisor_state.module, State)}.
+
+%%-----------------------------------------------------------------
+%% Func: do_soft_purge/3
+%% Args: Mod = atom()
+%% PostPurgeMethod = soft_purge | brutal_purge
+%% Unpurged = [{Mod, PostPurgeMethod}]
+%% Purpose: Check if there are any processes left running this code.
+%% If so, make sure Mod is a member in the returned list.
+%% Otherwise, make sure Mod isn't a member in the returned
+%% list.
+%% Returns: An updated list of unpurged modules.
+%%-----------------------------------------------------------------
+do_soft_purge(Mod, PostPurgeMethod, Unpurged) ->
+ IsNoOldProcsLeft = code:soft_purge(Mod),
+ case lists:keymember(Mod, 1, Unpurged) of
+ true when IsNoOldProcsLeft == true -> lists:keydelete(Mod, 1, Unpurged);
+ true -> Unpurged;
+ false when IsNoOldProcsLeft == true -> Unpurged;
+ false -> [{Mod, PostPurgeMethod} | Unpurged]
+ end.
+
+%%-----------------------------------------------------------------
+%% Func: sync_nodes/2
+%% Args: Id = term()
+%% Nodes = [atom()]
+%% Purpose: Synchronizes with all nodes.
+%%-----------------------------------------------------------------
+sync_nodes(Id, Nodes) ->
+ NNodes = lists:delete(node(), Nodes),
+ lists:foreach(fun(Node) ->
+ {release_handler, Node} ! {sync_nodes, Id, node()}
+ end,
+ NNodes),
+ lists:foreach(fun(Node) ->
+ receive
+ {sync_nodes, Id, Node} ->
+ ok;
+ {nodedown, Node} ->
+ throw({sync_error, {nodedown, Node}})
+ end
+ end,
+ NNodes).
+
+add_old_vsn(Mod, Vsns) ->
+ case lists:keysearch(Mod, 1, Vsns) of
+ {value, {Mod, undefined, NewVsn}} ->
+ OldVsn = get_vsn(code:which(Mod)),
+ lists:keyreplace(Mod, 1, Vsns, {Mod, OldVsn, NewVsn});
+ {value, {Mod, _OldVsn, _NewVsn}} ->
+ Vsns;
+ false ->
+ OldVsn = get_vsn(code:which(Mod)),
+ [{Mod, OldVsn, undefined} | Vsns]
+ end.
+
+add_new_vsn(Mod, File, Vsns) ->
+ NewVsn = get_vsn(File),
+ case lists:keysearch(Mod, 1, Vsns) of
+ {value, {Mod, OldVsn, undefined}} ->
+ lists:keyreplace(Mod, 1, Vsns, {Mod, OldVsn, NewVsn});
+ false ->
+ [{Mod, undefined, NewVsn} | Vsns]
+ end.
+
+
+
+%%-----------------------------------------------------------------
+%% Func: get_vsn/1
+%% Args: File = string()
+%% Purpose: Finds the version attribute of a module.
+%% Returns: Vsn
+%% Vsn = term()
+%%-----------------------------------------------------------------
+get_vsn(File) ->
+ {ok, {_Mod, Vsn}} = beam_lib:version(File),
+ case misc_supp:is_string(Vsn) of
+ true ->
+ Vsn;
+ false ->
+ %% If -vsn(Vsn) defines a term which is not a
+ %% string, the value is returned here as [Vsn].
+ case Vsn of
+ [VsnTerm] ->
+ VsnTerm;
+ _ ->
+ Vsn
+ end
+ end.
diff --git a/lib/sasl/src/sasl.app.src b/lib/sasl/src/sasl.app.src
new file mode 100644
index 0000000000..cfe4b81ab6
--- /dev/null
+++ b/lib/sasl/src/sasl.app.src
@@ -0,0 +1,46 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+{application, sasl,
+ [{description, "SASL CXC 138 11"},
+ {vsn, "%VSN%"},
+ {modules, [sasl,
+ alarm_handler,
+ format_lib_supp,
+ misc_supp,
+ overload,
+ rb,
+ rb_format_supp,
+ release_handler,
+ release_handler_1,
+ erlsrv,
+ sasl_report,
+ sasl_report_tty_h,
+ sasl_report_file_h,
+ systools,
+ systools_make,
+ systools_rc,
+ systools_relup,
+ systools_lib
+ ]},
+ {registered, [sasl_sup, alarm_handler, overload, release_handler]},
+ {applications, [kernel, stdlib]},
+ {env, [{sasl_error_logger, tty},
+ {errlog_type, all}]},
+ {mod, {sasl, []}}]}.
+
diff --git a/lib/sasl/src/sasl.appup.src b/lib/sasl/src/sasl.appup.src
new file mode 100644
index 0000000000..64c653a4e5
--- /dev/null
+++ b/lib/sasl/src/sasl.appup.src
@@ -0,0 +1,25 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+{"%VSN%",
+ [{"2.1.4", [{load_module, release_handler},
+ {load_module, systools_relup}]}],
+ [{"2.1.4", [{load_module, release_handler},
+ {load_module, systools_relup}]}]
+}.
diff --git a/lib/sasl/src/sasl.erl b/lib/sasl/src/sasl.erl
new file mode 100644
index 0000000000..979d80159e
--- /dev/null
+++ b/lib/sasl/src/sasl.erl
@@ -0,0 +1,162 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(sasl).
+
+%% External exports
+-export([start/2, stop/1]).
+
+%% Internal exports
+-export([init/1, pred/1]).
+
+%%%-----------------------------------------------------------------
+%%% This module implements the application SASL,
+%%% and a supervisor for SASL.
+%%%-----------------------------------------------------------------
+-behaviour(application).
+
+-record(state, {sasl_error_logger, error_logger_mf}).
+
+start(_, []) ->
+ Handler = get_sasl_error_logger(),
+ Type = get_sasl_error_logger_type(),
+ Mf = get_error_logger_mf(),
+ add_sasl_error_logger(Handler, Type),
+ add_error_logger_mf(Mf),
+ State = #state{sasl_error_logger = Handler, error_logger_mf = Mf},
+ case supervisor:start_link({local, sasl_sup}, sasl, []) of
+ {ok, Pid} -> {ok, Pid, State};
+ Error -> Error
+ end.
+
+stop(State) ->
+ delete_sasl_error_logger(State#state.sasl_error_logger),
+ delete_error_logger_mf(State#state.error_logger_mf).
+
+%%-----------------------------------------------------------------
+%% Internal functions
+%%-----------------------------------------------------------------
+get_sasl_error_logger() ->
+ case application:get_env(sasl, sasl_error_logger) of
+ {ok, false} -> undefined;
+ {ok, tty} -> tty;
+ {ok, {file, File}} when is_list(File) -> {file, File};
+ {ok, Bad} -> exit({bad_config, {sasl, {sasl_error_logger, Bad}}});
+ _ -> undefined
+ end.
+
+get_sasl_error_logger_type() ->
+ case application:get_env(sasl, errlog_type) of
+ {ok, error} -> error;
+ {ok, progress} -> progress;
+ {ok, all} -> all;
+ {ok, Bad} -> exit({bad_config, {sasl, {errlog_type, Bad}}});
+ _ -> all
+ end.
+
+get_error_logger_mf() ->
+ case catch get_mf() of
+ {'EXIT', Reason} ->
+ exit(Reason);
+ Mf ->
+ Mf
+ end.
+
+get_mf() ->
+ Dir = get_mf_dir(),
+ MaxB = get_mf_maxb(),
+ MaxF = get_mf_maxf(),
+ {Dir, MaxB, MaxF}.
+
+get_mf_dir() ->
+ case application:get_env(sasl, error_logger_mf_dir) of
+ {ok, false} -> throw(undefined);
+ {ok, Dir} when is_list(Dir) -> Dir;
+ undefined -> throw(undefined);
+ {ok, Bad} -> exit({bad_config, {sasl, {error_logger_mf_dir, Bad}}})
+ end.
+
+get_mf_maxb() ->
+ case application:get_env(sasl, error_logger_mf_maxbytes) of
+ {ok, MaxB} when is_integer(MaxB) -> MaxB;
+ undefined -> throw(undefined);
+ {ok, Bad} -> exit({bad_config, {sasl, {error_logger_mf_maxbytes, Bad}}})
+ end.
+
+get_mf_maxf() ->
+ case application:get_env(sasl, error_logger_mf_maxfiles) of
+ {ok, MaxF} when is_integer(MaxF), MaxF > 0, MaxF < 256 -> MaxF;
+ undefined -> throw(undefined);
+ {ok, Bad} -> exit({bad_config, {sasl, {error_logger_mf_maxfiles, Bad}}})
+ end.
+
+add_sasl_error_logger(undefined, _Type) -> ok;
+add_sasl_error_logger(Handler, Type) ->
+ error_logger:add_report_handler(mod(Handler), args(Handler, Type)).
+
+delete_sasl_error_logger(undefined) -> ok;
+delete_sasl_error_logger(Type) ->
+ error_logger:delete_report_handler(mod(Type)).
+
+mod(tty) -> sasl_report_tty_h;
+mod({file, _File}) -> sasl_report_file_h.
+
+args({file, File}, Type) -> {File, type(Type)};
+args(_, Type) -> type(Type).
+
+type(error) -> error;
+type(progress) -> progress;
+type(_) -> all.
+
+add_error_logger_mf(undefined) -> ok;
+add_error_logger_mf({Dir, MaxB, MaxF}) ->
+ error_logger:add_report_handler(
+ log_mf_h, log_mf_h:init(Dir, MaxB, MaxF, {sasl, pred})).
+
+delete_error_logger_mf(undefined) -> ok;
+delete_error_logger_mf(_) ->
+ error_logger:delete_report_handler(log_mf_h).
+
+pred({_Type, GL, _Msg}) when node(GL) /= node() -> false;
+pred(_) -> true.
+
+%%%-----------------------------------------------------------------
+%%% supervisor functionality
+%%%-----------------------------------------------------------------
+init([]) ->
+ SupFlags = {one_for_one, 0, 1},
+ %% Reboot node if release_handler crashes!
+ SafeSupervisor = {sasl_safe_sup,
+ {supervisor, start_link,
+ [{local, sasl_safe_sup}, ?MODULE, safe]},
+ permanent, infinity, supervisor, [?MODULE]},
+ ReleaseH = {release_handler,
+ {release_handler, start_link, []},
+ permanent, 2000, worker, []}, % Note! [] for modules! We
+ % can't change code on r_h
+ % this way!!
+ {ok, {SupFlags, [SafeSupervisor, ReleaseH]}};
+init(safe) ->
+ SupFlags = {one_for_one, 4, 3600},
+ AlarmH = {alarm_handler,
+ {alarm_handler, start_link, []},
+ permanent, 2000, worker, dynamic},
+ Overload = {overload,
+ {overload, start_link, []},
+ permanent, 2000, worker, [overload]},
+ {ok, {SupFlags, [AlarmH, Overload]}}.
diff --git a/lib/sasl/src/sasl_report.erl b/lib/sasl/src/sasl_report.erl
new file mode 100644
index 0000000000..bad3a75151
--- /dev/null
+++ b/lib/sasl/src/sasl_report.erl
@@ -0,0 +1,135 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(sasl_report).
+
+-export([write_report/3]).
+
+write_report(Fd, What, {Time, {error_report, _GL, {Pid, Type, Report}}}) ->
+ case is_my_error_report(What, Type) of
+ true ->
+ Head = write_head(Type, Time, Pid),
+ write_report2(Fd, Head, Type, Report);
+ _ -> true
+ end;
+write_report(Fd, What, {Time, {info_report, _GL, {Pid, Type, Report}}}) ->
+ case is_my_info_report(What, Type) of
+ true ->
+ Head = write_head(Type, Time, Pid),
+ write_report2(Fd, Head, Type, Report);
+ _ -> true
+ end;
+write_report(_Fd, _, _) ->
+ false.
+
+is_my_error_report(all, Type) -> is_my_error_report(Type);
+is_my_error_report(error, Type) -> is_my_error_report(Type);
+is_my_error_report(_, _Type) -> false.
+is_my_error_report(supervisor_report) -> true;
+is_my_error_report(crash_report) -> true;
+is_my_error_report(_) -> false.
+
+is_my_info_report(all, Type) -> is_my_info_report(Type);
+is_my_info_report(progress, Type) -> is_my_info_report(Type);
+is_my_info_report(_, _Type) -> false.
+is_my_info_report(progress) -> true;
+is_my_info_report(_) -> false.
+
+write_report2(Fd, Head, supervisor_report, Report) ->
+ Name = sup_get(supervisor, Report),
+ Context = sup_get(errorContext, Report),
+ Reason = sup_get(reason, Report),
+ Offender = sup_get(offender, Report),
+ io:format(Fd, Head ++ " Supervisor: ~p~n Context: ~p~n Reason: "
+ "~80.18p~n Offender: ~80.18p~n~n",
+ [Name,Context,Reason,Offender]);
+write_report2(Fd, Head, progress, Report) ->
+ Format = format_key_val(Report),
+ io:format(Fd, Head ++ "~s", [Format]);
+write_report2(Fd, Head, crash_report, Report) ->
+ Format = proc_lib:format(Report),
+ io:format(Fd, Head ++ "~s", [Format]).
+
+format_key_val([{Tag,Data}|Rep]) ->
+ io_lib:format(" ~16w: ~p~n",[Tag,Data]) ++ format_key_val(Rep);
+format_key_val(_) ->
+ [].
+
+
+sup_get(Tag, Report) ->
+ case lists:keysearch(Tag, 1, Report) of
+ {value, {_, Value}} ->
+ Value;
+ _ ->
+ ""
+ end.
+
+maybe_utc(Time) ->
+ case application:get_env(sasl,utc_log) of
+ {ok,true} ->
+ case calendar:local_time_to_universal_time_dst(Time) of
+ [UTC] ->
+ {utc,UTC};
+ [UTC1,_UTC2] ->
+ {utc,UTC1};
+ [] -> % should not happen
+ Time
+ end;
+ _ ->
+ Time
+ end.
+
+write_head(supervisor_report, Time, Pid) ->
+ write_head1("SUPERVISOR REPORT", maybe_utc(Time), Pid);
+write_head(crash_report, Time, Pid) ->
+ write_head1("CRASH REPORT", maybe_utc(Time), Pid);
+write_head(progress, Time, Pid) ->
+ write_head1("PROGRESS REPORT", maybe_utc(Time), Pid).
+
+write_head1(Type, {utc,{{Y,Mo,D},{H,Mi,S}}}, Pid) when node(Pid) /= node() ->
+ io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC (~p) ===~n",
+ [Type,D,month(Mo),Y,t(H),t(Mi),t(S),node(Pid)]);
+write_head1(Type, {utc,{{Y,Mo,D},{H,Mi,S}}}, _) ->
+ io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n",
+ [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]);
+write_head1(Type, {{Y,Mo,D},{H,Mi,S}}, Pid) when node(Pid) /= node() ->
+ io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s (~p) ===~n",
+ [Type,D,month(Mo),Y,t(H),t(Mi),t(S),node(Pid)]);
+write_head1(Type, {{Y,Mo,D},{H,Mi,S}}, _) ->
+ io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s ===~n",
+ [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]).
+
+t(X) when is_integer(X) ->
+ t1(integer_to_list(X));
+t(_) ->
+ "".
+t1([X]) -> [$0,X];
+t1(X) -> X.
+
+month(1) -> "Jan";
+month(2) -> "Feb";
+month(3) -> "Mar";
+month(4) -> "Apr";
+month(5) -> "May";
+month(6) -> "Jun";
+month(7) -> "Jul";
+month(8) -> "Aug";
+month(9) -> "Sep";
+month(10) -> "Oct";
+month(11) -> "Nov";
+month(12) -> "Dec".
diff --git a/lib/sasl/src/sasl_report_file_h.erl b/lib/sasl/src/sasl_report_file_h.erl
new file mode 100644
index 0000000000..f4810d31cc
--- /dev/null
+++ b/lib/sasl/src/sasl_report_file_h.erl
@@ -0,0 +1,60 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(sasl_report_file_h).
+
+%%%
+%%% A handler that can be connected to the error_logger
+%%% event handler.
+%%% Writes all sasl_* events formatted to file
+%%%
+
+-export([init/1,
+ handle_event/2, handle_call/2, handle_info/2,
+ terminate/2]).
+
+init({File, Type}) ->
+ process_flag(trap_exit, true),
+ case file:open(File, [write]) of
+ {ok,Fd} ->
+ {ok, {Fd, File, Type}};
+ What ->
+ What
+ end.
+
+handle_event({_Type, GL, _Msg}, State) when node(GL) /= node() ->
+ {ok, State};
+handle_event(Event, {Fd, File, Type}) ->
+ sasl_report:write_report(Fd, Type, tag_event(Event)),
+ {ok, {Fd, File, Type}};
+handle_event(_, State) ->
+ {ok, State}.
+
+handle_info({'EXIT', Fd, _Reason}, {Fd, _File, _Type}) ->
+ remove_handler;
+handle_info(_, State) ->
+ {ok, State}.
+
+handle_call(_Query, _State) -> {error, bad_query}.
+
+terminate(_, {Fd, _File, _Type}) ->
+ file:close(Fd),
+ [].
+
+tag_event(Event) ->
+ {calendar:local_time(), Event}.
diff --git a/lib/sasl/src/sasl_report_tty_h.erl b/lib/sasl/src/sasl_report_tty_h.erl
new file mode 100644
index 0000000000..064f0471f2
--- /dev/null
+++ b/lib/sasl/src/sasl_report_tty_h.erl
@@ -0,0 +1,50 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(sasl_report_tty_h).
+
+%%%
+%%% A handler that can be connected to the error_logger
+%%% event handler.
+%%% Writes all sasl_* events formatted to stdout.
+%%%
+
+-export([init/1,
+ handle_event/2, handle_call/2, handle_info/2,
+ terminate/2]).
+
+init(Type) ->
+% should link to user (or group_leader???)
+ {ok, Type}.
+
+handle_event({Type, GL, _Msg}, Type) when node(GL) /= node() ->
+ {ok, Type};
+handle_event(Event, Type) ->
+ sasl_report:write_report(standard_io, Type, tag_event(Event)),
+ {ok, Type}.
+
+handle_info(_, Type) -> {ok, Type}.
+
+handle_call(_Query, _Type) -> {error, bad_query}.
+
+terminate(_Reason, _Type) ->
+ [].
+
+tag_event(Event) ->
+ {calendar:local_time(), Event}.
+
diff --git a/lib/sasl/src/si.erl b/lib/sasl/src/si.erl
new file mode 100644
index 0000000000..eeed7a9f55
--- /dev/null
+++ b/lib/sasl/src/si.erl
@@ -0,0 +1,168 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%-----------------------------------------------------------------
+%% l(format_lib_supp), l(si_sasl_supp), l(si), l(si_ms_aos_supp), l(misc_supp).
+%% c(format_lib_supp), c(si_sasl_supp), c(si), c(si_ms_aos_supp), c(misc_supp).
+%%-----------------------------------------------------------------
+
+
+%%--------------------------------------------------
+%% Description:
+%% Status Inspection, main module.
+%%--------------------------------------------------
+
+-module(si).
+
+
+%% External exports
+-export([h/0, help/0, start/0, start/1, start_log/1, stop_log/0,
+ abbrevs/0, pi/1, pi/2, pi/3, pi/4, ppi/1, ppi/3, stop/0]).
+
+%% Internal exports
+-export([pi_impl/2, test/0]).
+
+
+%%--------------------------------------------------
+%% Table of contents
+%% 1. Interface
+%% 2. Implementation
+
+
+-import(si_sasl_supp, [status_info/1, make_pid/1, p/1]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 1. Interface
+%%--------------------------------------------------
+
+h() -> print_help().
+help() -> print_help().
+
+start() -> si_sasl_supp:start().
+start(Options) -> si_sasl_supp:start(Options).
+
+stop() -> si_sasl_supp:stop().
+
+start_log(FileName) ->
+ si_sasl_supp:start_log(FileName).
+
+stop_log() ->
+ si_sasl_supp:stop_log().
+
+%%%-----------------------------------------------------------------
+%%% All functions can be called with an option 'normal' or 'all';
+%%% default is 'normal'.
+%%%-----------------------------------------------------------------
+
+abbrevs() ->
+ io:format("~p", [lists:append(si_sasl_supp:process_abbrevs(),
+ process_abbrevs())]).
+
+%%-----------------------------------------------------------------
+%% Process Info that tries to determine processtype (=Module), then
+%% it uses this Module:format_info to format data from status_info/1.
+%%-----------------------------------------------------------------
+pi(XPid) ->
+ si_sasl_supp:si_exec({si, pi_impl}, [normal, XPid]).
+
+pi(Opt, XPid) ->
+ si_sasl_supp:si_exec({si, pi_impl}, [si_sasl_supp:valid_opt(Opt), XPid]).
+
+pi(A, B, C) when is_integer(A), is_integer(B), is_integer(C) ->
+ si_sasl_supp:si_exec({si, pi_impl}, [normal, {A, B, C}]).
+
+pi(Opt, A, B, C) when is_integer(A), is_integer(B), is_integer(C) ->
+ si_sasl_supp:si_exec({si, pi_impl}, [si_sasl_supp:valid_opt(Opt), {A, B, C}]).
+
+%%-----------------------------------------------------------------
+%% Pretty print Process_Info.
+%%-----------------------------------------------------------------
+ppi(XPid) ->
+ si_sasl_supp:ppi(XPid).
+ppi(A, B, C) ->
+ si_sasl_supp:ppi(A, B, C).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 2. Implementation
+%%--------------------------------------------------
+
+print_help() ->
+ p("~nStatus Inspection tool - usage"),
+ p("=============================="),
+ p(" For all these functions, Opt is an optional argument"),
+ p(" which can be 'normal' or 'all'; default is 'normal'."),
+ p(" If 'all', all information will be printed."),
+ p(" A Pid can be: \"<A.B.C>\", {A, B, C}, B, a registered_name or an abbrev."),
+ p("ANY PROCESS"),
+ p("si:pi([Opt,] Pid) - Formatted information about any process that"),
+ p(" SI recognises."),
+ p("si:pi([Opt,] A,B,C) - Same as si:pi({A, B, C})."),
+ p("si:ppi(Pid) - Pretty formating of process_info."),
+ p(" Works for any process."),
+ p("MISC"),
+ p("si:abbrevs() - Lists valid abbreviations."),
+ p("si:start_log(Filename) - Logging to file."),
+ p("si:stop_log()"),
+ p("si:start() - Starts Status Inspection (the si_server)."),
+ p("si:start([{start_log, FileName}])"),
+ p("si:stop() - Shut down SI.").
+
+
+%%--------------------------------------------------
+%% Copied (and modified) code from si_sasl_supp.
+%%--------------------------------------------------
+pi_impl(Opt, XPid) ->
+ case make_pid(try_local_expand_abbrev(XPid)) of
+ Pid when is_pid(Pid) ->
+ case status_info(Pid) of
+ {status_info, Pid, {module, Module}, Data} ->
+ si_sasl_supp:do_best_printout(Opt, Pid, Module, Data);
+ {error, Reason} ->
+ si_sasl_supp:ppi_impl(Pid),
+ {error, {"can not get status info from process:",
+ XPid,
+ Reason}};
+ Else ->
+ {error, {"unknown status info", Else}}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%%--------------------------------------------------
+%% Functions for handling of abbreviations
+%%--------------------------------------------------
+try_local_expand_abbrev(Abbrev) ->
+ case si_sasl_supp:expand_abbrev(Abbrev, process_abbrevs()) of
+ {value, {_, RealName}} -> RealName;
+ _ -> Abbrev
+ end.
+
+process_abbrevs() ->
+ [].
+
+%% Test get_status_info/format_status_info for all implemented servers.
+test() ->
+ lists:foreach(fun test_all_registered/1,
+ lists:append(si_sasl_supp:process_abbrevs(),
+ process_abbrevs())).
+
+test_all_registered({Al, _Ful}) ->
+ si:pi(all, Al).
diff --git a/lib/sasl/src/si_sasl_supp.erl b/lib/sasl/src/si_sasl_supp.erl
new file mode 100644
index 0000000000..52dbed2e00
--- /dev/null
+++ b/lib/sasl/src/si_sasl_supp.erl
@@ -0,0 +1,373 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(si_sasl_supp).
+
+-behaviour(gen_server).
+
+%%%---------------------------------------------------------------------------
+%%% Description:
+%%% This module contains the BOS specific parts of the Status Inspection Tool.
+%%%---------------------------------------------------------------------------
+
+
+%% user interface
+-export([h/0, help/0, start_log/1, stop_log/0, abbrevs/0, pi/1, pi/2, pi/3,
+ pi/4, ppi/1, ppi/3, start/0, start/1, stop/0, start_link/1]).
+
+%% intermodule exports
+-export([make_pid/1, make_pid/3, process_abbrevs/0, expand_abbrev/2,
+ status_info/1, valid_opt/1, p/1, do_best_printout/4,
+ si_exec/2, handle_call/3, terminate/2]).
+
+%% exports for use within module
+-export([init/1, start_log_impl/1, pi_impl/2, ppi_impl/1]).
+
+%% other gen_server callbacks (not used)
+-export([handle_cast/2, handle_info/2, code_change/3]).
+
+%%--------------------------------------------------
+%% Table of contents
+%% 1. Interface
+%% 2. SI - Server
+%% 3. Code
+%% 4. Selectors
+%%--------------------------------------------------
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 1. Interface
+%% -----------------------------------------------------
+
+h() -> print_help().
+help() -> print_help().
+
+si_exec(Fun, Args) -> gen_server:call(si_server, {si_exec, Fun, Args}).
+
+start_log(FileName) ->
+ gen_server:call(si_server, {start_log, FileName}).
+
+stop_log() ->
+ gen_server:call(si_server, stop_log).
+
+abbrevs() ->
+ io:format("~p", [process_abbrevs()]).
+
+%%-----------------------------------------------------------------
+%% All functions can be called with an option 'normal' or 'all';
+%% default is 'normal'.
+%%-----------------------------------------------------------------
+%% Process Info that tries to determine processtype (=Module), then
+%% it uses this Module:format_info to format data from status_info/1.
+%%-----------------------------------------------------------------
+pi(XPid) ->
+ si_exec({si_sasl_supp, pi_impl}, [normal, XPid]).
+
+pi(Opt, XPid) ->
+ si_exec({si_sasl_supp, pi_impl}, [valid_opt(Opt), XPid]).
+
+pi(A, B, C) when is_integer(A), is_integer(B), is_integer(C) ->
+ si_exec({si_sasl_supp, pi_impl}, [normal, {A, B, C}]).
+
+pi(Opt, A, B, C) when is_integer(A), is_integer(B), is_integer(C) ->
+ si_exec({si_sasl_supp, pi_impl}, [valid_opt(Opt), {A, B, C}]).
+
+%%-----------------------------------------------------------------
+%% Pretty print Process_Info.
+%%-----------------------------------------------------------------
+ppi(XPid) ->
+ case whereis(si_server) of
+ undefined -> % You can always run ppi.
+ ppi_impl(XPid); % if si_server is down, use standard_io
+ _ ->
+ si_exec({si_sasl_supp, ppi_impl}, [XPid])
+ end.
+ppi(A, B, C) ->
+ case whereis(si_server) of
+ undefined -> % You can always run ppi.
+ ppi_impl({A, B, C}); % if si_server is down, use standard_io
+ _ ->
+ si_exec({si_sasl_supp, ppi_impl}, [{A, B, C}])
+ end.
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 2. SI - Server
+%%--------------------------------------------------
+-record(state, {}).
+
+start() -> start([]).
+start(Options) ->
+ supervisor:start_child(sasl_sup,
+ {si_server, {si_sasl_supp, start_link, [Options]},
+ temporary, brutal_kill, worker, [si_sasl_supp]}).
+
+start_link(_Options) ->
+ gen_server:start_link({local, si_server}, si_sasl_supp, [], []).
+
+stop() ->
+ gen_server:call(si_server, stop),
+ supervisor:delete_child(sasl_sup, si_server).
+
+
+init(Options) ->
+ process_flag(trap_exit, true),
+ start_log_impl(get_option(Options, start_log, standard_io)),
+ {ok, #state{}}.
+
+%%-----------------------------------------------------------------
+%% If an error occurs and we're logging to file: write the error
+%% to the file.
+%% Always return the error.
+%% The only data held by the si_server is the device in its process dictionary.
+%%-----------------------------------------------------------------
+handle_call({si_exec, Fun, Args}, _From, State) ->
+ case catch apply(Fun, Args) of
+ {'EXIT', Reason} ->
+ print_error(get(device),
+ "SI internal error. Reason: ~w~n",
+ [Reason]),
+ {stop, shutdown, {internal_error, Reason}, State};
+ {error, Reason} ->
+ print_error(get(device), "~nSI error: ~w~n", [Reason]),
+ {reply, {error, Reason}, State};
+ X ->
+ {reply, X, State}
+ end;
+handle_call({start_log, FileName}, _From, State) ->
+ start_log_impl(FileName),
+ {reply, ok, State};
+handle_call(stop_log, _From, State) ->
+ start_log_impl(standard_io),
+ {reply, ok, State};
+handle_call(stop, _From, State) ->
+ start_log_impl(standard_io),
+ {stop, normal, stopped, State}.
+
+terminate(_Reason, _State) ->
+ close_device(get(device)),
+ ok.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+handle_info(_Info, State) ->
+ {noreply, State}.
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+close_device(standard_io) -> ok;
+close_device(Fd) -> file:close(Fd).
+
+print_error(standard_io, _, _) -> ok;
+print_error(Device, Format, Args) ->
+ io:format(Device, Format, Args).
+
+get_option(Options, Key, Default) ->
+ case lists:keysearch(Key, 1, Options) of
+ {value, {_Key, Value}} -> Value;
+ _ -> Default
+ end.
+
+open_log_file(undefined, NewFile) ->
+ open_log_file(NewFile);
+open_log_file(standard_io, NewFile) ->
+ open_log_file(NewFile);
+open_log_file(OldFile, NewFile) ->
+ file:close(OldFile),
+ open_log_file(NewFile).
+
+open_log_file(standard_io) -> standard_io;
+open_log_file(FileName) ->
+ case file:open(FileName, [write]) of
+ {ok, Fd} -> Fd;
+ Error ->
+ io:format("si_sasl_supp: Cannot open file '~s' (~w).~n",
+ [FileName, Error]),
+ io:format("si_sasl_supp: Using standard_io~n"),
+ standard_io
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 3. Code
+%%--------------------------------------------------
+
+%%--------------------------------------------------
+%% Makes a Pid of almost anything.
+%% Returns: Pid|{error, Reason}
+%% Fails: Never.
+%%--------------------------------------------------
+make_pid(A,B,C) when is_integer(A), is_integer(B), is_integer(C) ->
+ list_to_pid(lists:concat(["<",A,".",B,".",C,">"])).
+make_pid(P) when is_pid(P) -> P;
+make_pid(undefined) -> undefined;
+make_pid(P) when is_atom(P) ->
+ case whereis(P) of
+ undefined ->
+ case expand_abbrev(P, process_abbrevs()) of
+ {error, Reason} -> {error, Reason};
+ {value, {_Abbrev, FullName}} ->
+ case whereis(FullName) of
+ undefined ->
+ {error, {'process not registered', P}};
+ Pid -> Pid
+ end
+ end;
+ Pid -> Pid
+ end;
+make_pid(P) when is_list(P) -> list_to_pid(P);
+make_pid({A, B, C}) -> make_pid(A, B, C);
+make_pid(X) -> {error, {'can not make a pid of', X}}.
+
+process_abbrevs() ->
+ [{init, init},
+ {fs, file_server}].
+
+%%--------------------------------------------------
+%% Args: Abbrevs is an assoc-list of {Abbrev, RealName}
+%% Returns: {value, {Abbrev, FullName}}|{error, Reason}
+%%--------------------------------------------------
+expand_abbrev(ProcessName, Abbrevs) ->
+ case lists:keysearch(ProcessName, 1, Abbrevs) of
+ {value, {Abbrev, FullName}} ->
+ {value, {Abbrev, FullName}};
+ _ ->
+ case lists:keysearch(ProcessName, 2, Abbrevs) of
+ {value, {Abbrev, FullName}} ->
+ {value, {Abbrev, FullName}};
+ _ ->
+ {error, {'invalid process name', ProcessName}}
+ end
+ end.
+
+%%-----------------------------------------------------------------
+%% This is the function that actually gets the information out
+%% of the agent/server/...
+%% Returns: {status_info, Pid, Type, Data}
+%% | {error, Reason}
+%%-----------------------------------------------------------------
+status_info(Pid) when is_pid(Pid) ->
+ case catch sys:get_status(Pid, 5000) of
+ {status, Pid, Type, Info} ->
+ {status_info, Pid, Type, Info};
+ _ ->
+ {error, {'process does not respond', Pid}}
+ end;
+
+status_info(X) ->
+ {error, {'not a pid', X}}.
+
+%%--------------------------------------------------
+%% Implementation starts here.
+%%--------------------------------------------------
+start_log_impl(FileName) ->
+ put(device, open_log_file(get(device), FileName)).
+
+valid_opt(all) -> all;
+valid_opt(_Opt) -> normal.
+
+
+print_help() ->
+ p("- - - - - - - - PROCESSES - - - - - - - - - "),
+ p("si_sasl_supp:pi([Opt,] Pid) - Formatted information about any process that"),
+ p(" SI recognises."),
+ p("si_sasl_supp:pi([Opt,] A,B,C) - Same as si_sasl_supp:pi({A, B, C})."),
+ p("si_sasl_supp:ppi(Pid) - Pretty formating of process_info."),
+ p(" Works for any process."),
+ p("- - - - - - - - MISC - - - - - - - - - - - "),
+ p("si_sasl_supp:abbrevs() - Lists valid abbreviations."),
+ p("si_sasl_supp:start_log(FileNname)"),
+ p("si_sasl_supp:stop_log()"),
+ p("si_sasl_supp:start() - Starts Status Inspection (the si_server)."),
+ p("si_sasl_supp:start([{start_log, FileName}])"),
+ p("si_sasl_supp:stop() - Shut down SI.").
+
+
+
+%% Convenient shorthand
+p(X) ->
+ io:format(lists:append(X, "~n")).
+
+pi_impl(Opt, XPid) ->
+ case make_pid(XPid) of
+ Pid when is_pid(Pid) ->
+ case status_info(Pid) of
+ {status_info, Pid, {module, Module}, Data} ->
+ do_best_printout(Opt, Pid, Module, Data);
+ {error, Reason} ->
+ ppi_impl(Pid),
+ {error, {"can not get status info from process:",
+ XPid,
+ Reason}}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%%--------------------------------------------------
+%% Is there a format_info for this process? In that case, run it!
+%% Return ok|{error, Reason}
+%% Fails: Never.
+%%--------------------------------------------------
+do_best_printout(Opt, Pid, Mod, Data) when is_pid(Pid) ->
+ case print_info(get(device), Pid, {Mod, format_status}, Opt, Data) of
+ ok -> ok;
+ {error, Reason} ->
+ ppi_impl(Pid),
+ {error, Reason}
+ end.
+
+ppi_impl(XPid) ->
+ case make_pid(XPid) of
+ P when is_pid(P) ->
+ case process_info(P) of
+ undefined ->
+ {error, {'dead process', P}};
+ PI ->
+ Device = case get(device) of
+ undefined -> standard_io;
+ X -> X
+ end,
+ io:format(Device, "~nPretty Process Info~n", []),
+ io:format(Device, "-------------------~n", []),
+ io:format(Device, "~p~n", [PI])
+ end;
+ _ -> {error, {no_pid, XPid}}
+ end.
+
+print_info(Device, Pid, {Module, Func}, Opt, Data) ->
+ case erlang:function_exported(Module, Func, 2) of
+ true ->
+ case catch apply({Module, Func}, [Opt, Data]) of
+ Format when is_list(Format) ->
+ format_lib_supp:print_info(Device, 79,
+ add_pid_to_format(Pid, Format)),
+ ok;
+ Other -> {error, {'invalid format', Other}}
+ end;
+ _ ->
+ {error, {no_such_function, Module, Func}}
+ end.
+
+add_pid_to_format(Pid, [{header, H} | T]) ->
+ [{header, H}, {data, [{"Pid", Pid}]} | T];
+add_pid_to_format(Pid, List) ->
+ [{data, [{"Pid", Pid}]} | List].
+
+
diff --git a/lib/sasl/src/systools.erl b/lib/sasl/src/systools.erl
new file mode 100644
index 0000000000..51ef687047
--- /dev/null
+++ b/lib/sasl/src/systools.erl
@@ -0,0 +1,109 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(systools).
+
+%% Usage:
+%% systools:make_script("RelName")
+%% Make a boot file from RelName.rel.
+%% Generates RelName.{script,boot}
+%% systools:make_tar("RelName")
+%% Make a release package from RelName.rel.
+%% Generates RelName.tar,Z
+%% systools:script2boot(File)
+%% File.script -> File.boot
+%% systools:mk_relup("Target", ["UpFromRel"...], ["DownToRel"...], Opts)
+%% Gather all relup scripts to the relup file
+%%
+
+-export([script2boot/1, script2boot/3, compile_rel/3,
+ make_script/1, make_script/2,
+ make_tar/1, make_tar/2,
+ make_relup/3, make_relup/4]).
+
+-include("erl_compile.hrl").
+
+%%% The behaviour_info functions have been moved to erl_internal in stdlib.
+
+%%-----------------------------------------------------------------
+%% Options is a list of {path, Path} | silent | local where path sets
+%% the search path, silent supresses error message printing on console,
+%% local generates a script with references to the directories there
+%% the applications are found.
+%%-----------------------------------------------------------------
+make_script([RelName|Opts]) when is_atom(RelName) ->
+ make_script([RelName], Opts);
+make_script(RelName) -> make_script(RelName, []).
+
+make_script(RelName, Opt) ->
+ systools_make:make_script(RelName, Opt).
+
+%%-----------------------------------------------------------------
+%% Options is a list of {path, Path} | silent |
+%% {dirs, [src,include,examples,..]} | {erts, ErtsDir} where path
+%% sets the search path, silent supresses error message printing on console,
+%% dirs includes the specified directories (per application) in the
+%% release package and erts specifies that the erts-Vsn/bin directory
+%% should be included in the release package and there it can be found.
+%%-----------------------------------------------------------------
+make_tar(RelName) -> make_tar(RelName, []).
+
+make_tar(RelName, Opt) ->
+ systools_make:make_tar(RelName, Opt).
+
+%%-----------------------------------------------------------------
+%% Create a binary form of a boot script.
+%%-----------------------------------------------------------------
+script2boot(File) ->
+ case systools_lib:file_term2binary(File ++ ".script", File ++ ".boot") of
+ {error,Error} ->
+ io:format(systools_make:format_error(Error)),
+ error;
+ _ ->
+ ok
+ end.
+
+script2boot(File, Output0, _Opt) ->
+ Input = File++".script",
+ Output = Output0++".boot",
+ case systools_lib:file_term2binary(Input, Output) of
+ {error,Error} ->
+ io:format(systools_make:format_error(Error)),
+ error;
+ _ ->
+ ok
+ end.
+
+%%-----------------------------------------------------------------
+%% Options is a list of {path, Path} | silent | noexec where path sets
+%% search path, silent supresses error message printing on console,
+%% noexec supresses writing the output "relup" file
+%%-----------------------------------------------------------------
+make_relup(ReleaseName, UpNameList, DownNameList) ->
+ systools_relup:mk_relup(ReleaseName, UpNameList, DownNameList, []).
+make_relup(ReleaseName, UpNameList, DownNameList, Opts) ->
+ systools_relup:mk_relup(ReleaseName, UpNameList, DownNameList, Opts).
+
+%%-----------------------------------------------------------------
+%% Interface for erl_compile to compile .rel files.
+%%-----------------------------------------------------------------
+compile_rel(Input, Output, Options) ->
+ systools_make:make_script(Input, Output, translate_options(Options)).
+
+translate_options(Opts) ->
+ [{path, Opts#options.includes}|Opts#options.specific].
diff --git a/lib/sasl/src/systools.hrl b/lib/sasl/src/systools.hrl
new file mode 100644
index 0000000000..9a3e98221c
--- /dev/null
+++ b/lib/sasl/src/systools.hrl
@@ -0,0 +1,71 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% systools.hrl
+%%
+
+%% this is the structure of a release
+
+-record(release,
+ {
+ name, %% Name of the release, string().
+ vsn, %% Version of the release, string().
+ erts_vsn, %% Version of erts, string().
+ applications, %% [{Name,Vsn,Type}] list of applications incorporated
+ %% in the release, Name = Type = atom(),
+ %% Vsn = string().
+ incl_apps %% [{Name,[App]}] list of include specifications,
+ %% (appl Name includes appls [App]),
+ %% Name = App = atom().
+ }).
+
+-record(application,
+ {name, %% Name of the application, atom().
+ type = permanent, %% Application start type, atom().
+ vsn = "", %% Version of the application, string().
+ id = "", %% Id of the application, string().
+ description = "", %% Description of application, string().
+ modules = [], %% [Module | {Module,Vsn}] of modules
+ %% incorporated in the application,
+ %% Module = atom(), Vsn = string().
+ uses = [], %% [Application] list of applications required
+ %% by the application, Application = atom().
+ includes = [], %% [Application] list of applications included
+ %% by the application, Application = atom().
+ regs = [], %% [RegNames] a list of registered process
+ %% names used by the application, RegNames =
+ %% atom().
+ env = [], %% [{Key,Value}] environment variable of
+ %% application, Key = Value = term().
+ maxT = infinity, %% Max time an application may exist,
+ %% integer() | infinity.
+ maxP = infinity, %% Max number of processes in an application,
+ %% integer() | infinity.
+ mod = [], %% [] | {Mod, StartArgs}, Mod= atom(),
+ %% StartArgs = list().
+ start_phases = [], %% [] | {Phase, PhaseArgs}, Phase = atom(),
+ %% PhaseArgs = list().
+ dir = "" %% The directory where the .app file was
+ %% found (internal use).
+ }).
+
+
+
+
diff --git a/lib/sasl/src/systools_lib.erl b/lib/sasl/src/systools_lib.erl
new file mode 100644
index 0000000000..b652c109fe
--- /dev/null
+++ b/lib/sasl/src/systools_lib.erl
@@ -0,0 +1,219 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(systools_lib).
+
+%% Purpose : Internal stuff called by systools.erl
+%% : Some of this stuff is quite useful and should *eventually*
+%% : find its way into the standard libraries
+%%
+
+-export([file_term2binary/2, read_term/1, read_term_from_stream/2,
+ get_dirs/1, get_path/1]).
+
+-include_lib("kernel/include/file.hrl").
+
+%% reads a single term form a file - convert it to binary and
+%% dump it in a file
+
+file_term2binary(FileIn, FileOut) ->
+ case read_term(FileIn) of
+ {ok, Term} ->
+ file:write_file(FileOut, term_to_binary(Term)),
+ ok;
+ Other ->
+ Other
+ end.
+
+%%______________________________________________________________________
+%% read_term(File) -> {ok, Term} | Error
+
+read_term(File) ->
+ case file:open(File, [read]) of
+ {ok, Stream} ->
+ Res = read_term_from_stream(Stream, File),
+ file:close(Stream),
+ Res;
+ {error, Error} ->
+ {error, {open,File,Error}}
+ end.
+
+read_term_from_stream(Stream, File) ->
+ R = io:request(Stream, {get_until,'',erl_scan,tokens,[1]}),
+ case R of
+ {ok,Toks,_EndLine} ->
+ case erl_parse:parse_term(Toks) of
+ {ok, Term} ->
+ {ok, Term};
+ {error, Error} ->
+ {error, {parse, File, Error}}
+ end;
+ {error,_E,_EndLine} ->
+ {error,{read,File}};
+ {eof,_EndLine} ->
+ {error, {read,File}}
+ end.
+
+%%% ----------------------------------------------------
+%%% Expand a directory name given with wildcards (*)
+%%% to a list of matching directory names.
+%%% The only handled wildcard is '*' which is translated
+%%% into the regular expression [^/]* .
+%%% If '*' is given as only character between two '/'
+%%% it is instead translated into the regular expression
+%%% [^/]+ , i.e. where must be at least one character
+%%% between two '/'.
+%%%
+%%% Returns: {ok, Dirs} | {error, What}
+%%% ----------------------------------------------------
+
+get_dirs(RegPath) when is_list(RegPath) ->
+ Names = filename:split(RegPath),
+ ExpNames = expand_names(Names),
+ catch get_dirs(ExpNames, [], true);
+get_dirs(_) ->
+ {error, badarg}.
+
+get_path(RegPath) when is_list(RegPath) ->
+ F = fun(RegP) ->
+ case get_dirs(RegP) of
+ {ok, Dirs} -> {true, Dirs};
+ _ -> false
+ end
+ end,
+ flat(lists:zf(F, RegPath), []);
+get_path(_) ->
+ [].
+
+%%
+%% expand_names([Name]) -> {true, Name'} | {false, Name}
+%%
+%% Expand "*" ==> "[^/]+"
+%% "...*..." ==> "[^/]*"
+%%
+%% A single .../*/... is expanded to one or more whatever
+%% except a '/' because it is a place holder for a directory.
+%%
+expand_names(Names) ->
+ lists:map(fun("*") ->
+ {true, "[^/]+"};
+ (N) ->
+ case lists:member($*, N) of
+ true -> {true, expand(N, [])};
+ _ -> {false, N}
+ end
+ end, Names).
+
+expand([$*|T], Ack) ->
+ expand(T, "*]/^[" ++ Ack); %% "[^/]*"
+expand([H|T], Ack) ->
+ expand(T, [H|Ack]);
+expand([], Ack) ->
+ lists:reverse(Ack).
+
+%%
+%% get_dirs(ExpName, FoundSoFar, Root) ->
+%% {ok, Dirs} | {error, Error}
+%%
+%% Use the regular expression RegName to match all
+%% directories at a certain level.
+%%
+
+get_dirs([{false,Name}|T], F, Root) ->
+ get_dirs(T, add_dir(Name, F, Root), false);
+get_dirs([{true,RegName}|T], F, Root) ->
+ get_dirs(T, add_dirs(RegName, F, Root), false);
+get_dirs([], F, _) ->
+ {ok, F}.
+
+add_dir(Name, [], true) -> %% root
+ case dir_p(Name) of
+ true -> [Name];
+ _ -> []
+ end;
+add_dir(Name, Dirs, _Root) ->
+ lists:zf(fun(D0) ->
+ D = filename:join(D0, Name),
+ case dir_p(D) of
+ true -> {true, D};
+ _ -> false
+ end
+ end, Dirs).
+
+add_dirs(RegName, _Dirs, true) ->
+ case regexp_match(RegName, ".", true) of
+ {true, AddDirs} -> AddDirs;
+ _ -> []
+ end;
+add_dirs(RegName, Dirs, Root) ->
+ Fun = fun(Dir) ->
+ regexp_match(RegName, Dir, Root)
+ end,
+ flat(lists:zf(Fun, Dirs), []).
+
+%%
+%% Keep all directories (names) matching RegName and
+%% create full directory names Dir ++ "/" ++ Name.
+%%
+%% Called from lists:zf.
+%% Returns: {true, [Dir]} | false
+%%
+regexp_match(RegName, D0, Root) ->
+ case file:list_dir(D0) of
+ {ok, Files} when length(Files) > 0 ->
+ FR = fun(F) ->
+ case regexp:match(F, RegName) of
+ {match,1,N} when N == length(F) ->
+ DirF = join(D0, F, Root),
+ case dir_p(DirF) of
+ true ->
+ {true, DirF};
+ _ ->
+ false
+ end;
+ _ ->
+ false
+ end
+ end,
+ {true,lists:zf(FR, Files)};
+ _ ->
+ false
+ end.
+
+%% Only join if not root directory.
+join(_, F, true) -> F;
+join(Dir, F, _) -> filename:join(Dir, F).
+
+dir_p(DirF) ->
+ case file:read_file_info(DirF) of
+ {ok, Info} when Info#file_info.type==directory -> true;
+ _ -> false
+ end.
+
+
+flat([H|T], Ack) when is_list(hd(H)) ->
+ flat(T, lists:reverse(H) ++ Ack);
+flat([[]|T], Ack) ->
+ flat(T, Ack);
+flat([H|T], Ack) ->
+ flat(T, [H|Ack]);
+flat([], Ack) ->
+ lists:reverse(Ack).
+
+
+
diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
new file mode 100644
index 0000000000..20a142c763
--- /dev/null
+++ b/lib/sasl/src/systools_make.erl
@@ -0,0 +1,2155 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(systools_make).
+
+%% Purpose : Create start script. RelName.rel --> RelName.{script,boot}.
+%% and create a tar file of a release (RelName.tar.gz)
+
+-export([make_script/1, make_script/2, make_script/3,
+ make_tar/1, make_tar/2]).
+
+-export([format_error/1, format_warning/1]).
+
+-export([read_release/2, get_release/2, get_release/3,
+ get_release/4, pack_app/1]).
+
+-export([read_application/4]).
+
+-import(lists, [filter/2, keysort/2, keysearch/3, map/2, reverse/1,
+ append/1, foldl/3, member/2, foreach/2]).
+
+-include("systools.hrl").
+
+-include_lib("kernel/include/file.hrl").
+
+-define(XREF_SERVER, systools_make).
+
+-compile({inline,[{badarg,2}]}).
+
+%%-----------------------------------------------------------------
+%% Create a boot script from a release file.
+%% Options is a list of {path, Path} | silent | local where path sets
+%% the search path, silent supresses error message printing on console,
+%% local generates a script with references to the directories there
+%% the applications are found.
+%%
+%% New options: {path,Path} can contain wildcards
+%% no_module_tests
+%% {variables,[{Name,AbsString}]}
+%% {machine, jam | beam | vee}
+%% exref | {exref, [AppName]}
+%%-----------------------------------------------------------------
+
+make_script(RelName) when is_list(RelName) ->
+ make_script(RelName, []);
+make_script(RelName) ->
+ badarg(RelName,[RelName]).
+
+make_script(RelName, Flags) when is_list(RelName), is_list(Flags) ->
+ case get_outdir(Flags) of
+ "" ->
+ make_script(RelName, RelName, Flags);
+ OutDir ->
+ %% To maintain backwards compatibility for make_script/3,
+ %% the boot script file name is constructed here, before
+ %% checking the validity of OutDir
+ %% (is done in check_args_script/1)
+ Output = filename:join(OutDir, filename:basename(RelName)),
+ make_script(RelName, Output, Flags)
+ end.
+
+make_script(RelName, Output, Flags) when is_list(RelName),
+ is_list(Output),
+ is_list(Flags) ->
+ case check_args_script(Flags) of
+ [] ->
+ Path0 = get_path(Flags),
+ Path1 = mk_path(Path0), % expand wildcards etc.
+ Path = make_set(Path1 ++ code:get_path()),
+ ModTestP = {not member(no_module_tests, Flags),
+ xref_p(Flags)},
+ case get_release(RelName, Path, ModTestP, machine(Flags)) of
+ {ok, Release, Appls, Warnings} ->
+ case generate_script(Output,Release,Appls,Flags) of
+ ok ->
+ return(ok,Warnings,Flags);
+ Error ->
+ return(Error,Warnings,Flags)
+ end;
+ Error ->
+ return(Error,[],Flags)
+ end;
+ ErrorVars ->
+ badarg(ErrorVars, [RelName, Flags])
+ end;
+
+make_script(RelName, _Output, Flags) when is_list(Flags) ->
+ badarg(RelName,[RelName, Flags]);
+make_script(RelName, _Output, Flags) ->
+ badarg(Flags,[RelName, Flags]).
+
+%% Inlined.
+badarg(BadArg, Args) ->
+ erlang:error({badarg,BadArg}, Args).
+
+machine(Flags) ->
+ case get_flag(machine,Flags) of
+ {machine, Machine} when is_atom(Machine) -> Machine;
+ _ -> false
+ end.
+
+get_path(Flags) ->
+ case get_flag(path,Flags) of
+ {path,Path} when is_list(Path) -> Path;
+ _ -> []
+ end.
+
+get_outdir(Flags) ->
+ case get_flag(outdir,Flags) of
+ {outdir,OutDir} when is_list(OutDir) ->
+ OutDir;
+ _ -> % false | {outdir, Badarg}
+ ""
+ end.
+
+return(ok,Warnings,Flags) ->
+ case member(silent,Flags) of
+ true ->
+ {ok,?MODULE,Warnings};
+ _ ->
+ io:format("~s",[format_warning(Warnings)]),
+ ok
+ end;
+return({error,Mod,Error},_,Flags) ->
+ case member(silent,Flags) of
+ true ->
+ {error,Mod,Error};
+ _ ->
+ io:format("~s",[Mod:format_error(Error)]),
+ error
+ end.
+
+%%-----------------------------------------------------------------
+%% Create a release package from a release file.
+%% Options is a list of {path, Path} | silent |
+%% {dirs, [src,include,examples,..]} | {erts, ErtsDir} where path
+%% sets the search path, silent supresses error message printing,
+%% dirs includes the specified directories (per application) in the
+%% release package and erts specifies that the erts-Vsn/bin directory
+%% should be included in the release package and there it can be found.
+%%
+%% New options: {path,Path} can contain wildcards
+%% no_module_tests
+%% exref | {exref, [AppName]}
+%% {variables,[{Name,AbsString}]}
+%% {machine, jam | beam | vee}
+%% {var_tar, include | ownfile | omit}
+%%
+%% The tar file contains:
+%% lib/App-Vsn/ebin
+%% /priv
+%% [/src]
+%% [/include]
+%% [/doc]
+%% [/examples]
+%% [/...]
+%% Variable1.tar.gz
+%% ...
+%% VariableN.tar.gz
+%% releases/RelName.rel
+%% RelVsn/start.boot
+%% relup
+%% sys.config
+%% erts-EVsn[/bin]
+%%-----------------------------------------------------------------
+
+make_tar(RelName) when is_list(RelName) ->
+ make_tar(RelName, []);
+make_tar(RelName) ->
+ badarg(RelName,[RelName]).
+
+make_tar(RelName, Flags) when is_list(RelName), is_list(Flags) ->
+ case check_args_tar(Flags) of
+ [] ->
+ Path0 = get_path(Flags),
+ Path1 = mk_path(Path0),
+ Path = make_set(Path1 ++ code:get_path()),
+ ModTestP = {not member(no_module_tests, Flags),
+ xref_p(Flags)},
+ case get_release(RelName, Path, ModTestP, machine(Flags)) of
+ {ok, Release, Appls, Warnings} ->
+ case catch mk_tar(RelName, Release, Appls, Flags, Path1) of
+ ok ->
+ return(ok,Warnings,Flags);
+ Error ->
+ return(Error,Warnings,Flags)
+ end;
+ Error ->
+ return(Error,[],Flags)
+ end;
+ ErrorVars ->
+ badarg(ErrorVars, [RelName, Flags])
+ end;
+make_tar(RelName, Flags) when is_list(Flags) ->
+ badarg(RelName,[RelName, Flags]);
+make_tar(RelName, Flags) ->
+ badarg(Flags,[RelName, Flags]).
+
+%%______________________________________________________________________
+%% get_release(File, Path) ->
+%% get_release(File, Path, ModTestP) ->
+%% get_release(File, Path, ModTestP, Machine) ->
+%% {ok, #release, [{{Name,Vsn},#application}], Warnings} | {error, What}
+
+get_release(File, Path) ->
+ get_release(File, Path, true, false).
+
+get_release(File, Path, ModTestP) ->
+ get_release(File, Path, ModTestP, false).
+
+get_release(File, Path, ModTestP, Machine) ->
+ case catch get_release1(File, Path, ModTestP, Machine) of
+ {error, Error} ->
+ {error, ?MODULE, Error};
+ {'EXIT', Why} ->
+ {error, ?MODULE, {'EXIT',Why}};
+ Answer ->
+ Answer
+ end.
+
+get_release1(File, Path, ModTestP, Machine) ->
+ {ok, Release} = read_release(File, Path),
+ {ok, Appls0} = collect_applications(Release, Path),
+ {ok, Appls1} = check_applications(Appls0),
+ {ok, Appls2} = sort_included_applications(Appls1, Release), % OTP-4121
+ {ok, Warnings} = check_modules(Appls2, Path, ModTestP, Machine),
+ {ok, Appls} = sort_appls(Appls2),
+ {ok, Release, Appls, Warnings}.
+
+%%______________________________________________________________________
+%% read_release(File, Path) -> {ok, #release} | throw({error, What})
+
+read_release(File, Path) ->
+ case read_file(File ++ ".rel", ["."|Path]) of
+ {ok, Release, _FullName} ->
+ check_rel(Release);
+ {error,Error} ->
+ throw({error,?MODULE,Error})
+ end.
+
+check_rel(Release) ->
+ case catch check_rel1(Release) of
+ {ok, {Name,Vsn,Evsn,Appl,Incl}} ->
+ {ok, #release{name=Name, vsn=Vsn,
+ erts_vsn=Evsn,
+ applications=Appl,
+ incl_apps=Incl}};
+ {error, Error} ->
+ throw({error,?MODULE,Error});
+ Error ->
+ throw({error,?MODULE,Error})
+ end.
+
+check_rel1({release,{Name,Vsn},{erts,EVsn},Appl}) when is_list(Appl) ->
+ check_name(Name),
+ check_vsn(Vsn),
+ check_evsn(EVsn),
+ {Appls,Incls} = check_appl(Appl),
+ {ok, {Name,Vsn,EVsn,Appls,Incls}};
+check_rel1(_) ->
+ {error, badly_formatted_release}.
+
+check_name(Name) ->
+ case string_p(Name) of
+ true ->
+ Name;
+ _ ->
+ throw({error,{illegal_name, Name}})
+ end.
+
+check_vsn(Vsn) ->
+ case string_p(Vsn) of
+ true ->
+ Vsn;
+ _ ->
+ throw({error,{illegal_form, Vsn}})
+ end.
+
+check_evsn(Vsn) ->
+ case string_p(Vsn) of
+ true ->
+ Vsn;
+ _ ->
+ throw({error,{illegal_form, {erts,Vsn}}})
+ end.
+
+check_appl(Appl) ->
+ case filter(fun({App,Vsn}) when is_atom(App) ->
+ not string_p(Vsn);
+ ({App,Vsn,Incl}) when is_atom(App), is_list(Incl) ->
+ case {string_p(Vsn), a_list_p(Incl)} of
+ {true, true} -> false;
+ _ -> true
+ end;
+ ({App,Vsn,Type}) when is_atom(App), is_atom(Type) ->
+ case {string_p(Vsn), is_app_type(Type)} of
+ {true, true} -> false;
+ _ -> true
+ end;
+ ({App,Vsn,Type,Incl}) when is_atom(App),
+ is_atom(Type),
+ is_list(Incl) ->
+ case {string_p(Vsn),is_app_type(Type),a_list_p(Incl)} of
+ {true, true, true} -> false;
+ _ -> true
+ end;
+ (_) ->
+ true
+ end,
+ Appl) of
+ [] ->
+ mandatory_applications(Appl),
+ split_app_incl(Appl);
+ Illegal ->
+ throw({error, {illegal_applications,Illegal}})
+ end.
+
+mandatory_applications(Appl) ->
+ AppNames = map(fun(AppT) -> element(1, AppT) end,
+ Appl),
+ Mand = mandatory_applications(),
+ case filter(fun(X) -> member(X, AppNames) end, Mand) of
+ Mand ->
+ ok;
+ _ ->
+ throw({error, {missing_mandatory_app, Mand}})
+ end.
+
+mandatory_applications() ->
+ [kernel, stdlib].
+
+split_app_incl(Appl) -> split_app_incl(Appl, [], []).
+
+split_app_incl([{App,Vsn}|Appls], Apps, Incls) ->
+ split_app_incl(Appls, [{App,Vsn,permanent}|Apps], Incls);
+split_app_incl([{App,Vsn,Incl}|Appls], Apps,Incls) when is_list(Incl) ->
+ split_app_incl(Appls, [{App,Vsn,permanent}|Apps], [{App,Incl}|Incls]);
+split_app_incl([{App,Vsn,Type}|Appls], Apps, Incls) ->
+ split_app_incl(Appls, [{App,Vsn,Type}|Apps], Incls);
+split_app_incl([{App,Vsn,Type,Incl}|Appls], Apps, Incls) when is_list(Incl) ->
+ split_app_incl(Appls, [{App,Vsn,Type}|Apps], [{App,Incl}|Incls]);
+split_app_incl([], Apps, Incls) ->
+ {reverse(Apps),reverse(Incls)}.
+
+%%______________________________________________________________________
+%% collect_applications(#release, Path) ->
+%% {ok,[{{Name,Vsn},#application}]} |
+%% throw({error, What})
+%% Read all the application files specified in the release descriptor
+
+collect_applications(Release, Path) ->
+ Appls = Release#release.applications,
+ Incls = Release#release.incl_apps,
+ X = foldl(fun({Name,Vsn,Type}, {Ok, Errs}) ->
+ case read_application(to_list(Name), Vsn, Path, Incls) of
+ {ok, A} ->
+ case {A#application.name,A#application.vsn} of
+ {Name,Vsn} ->
+ {[{{Name,Vsn}, A#application{type=Type}} | Ok],
+ Errs};
+ E ->
+ {Ok, [{bad_application_name, {Name, E}} | Errs]}
+ end;
+ {error, What} ->
+ {Ok, [{error_reading, {Name, What}} | Errs]}
+ end
+ end, {[],[]}, Appls),
+ case X of
+ {A, []} ->
+ {ok, reverse(A)};
+ {_, Errs} ->
+ throw({error, Errs})
+ end.
+
+
+%%______________________________________________________________________
+%% read_application(Name, Vsn, Path, Incls) -> {ok, #release} | {error, What}
+
+read_application(Name, Vsn, Path, Incls) ->
+ read_application(Name, Vsn, Path, Incls, false, no_fault).
+
+read_application(Name, Vsn, [Dir|Path], Incls, Found, FirstError) ->
+ case read_file(Name ++ ".app", [Dir]) of
+ {ok, Term, FullName} ->
+ case parse_application(Term, FullName, Vsn, Incls) of
+ {error, {no_valid_version, {Vsn, OtherVsn}}} when FirstError == no_fault ->
+ NFE = {no_valid_version, {{"should be", Vsn},
+ {"found file", filename:join(Dir, Name++".app"),
+ OtherVsn}}},
+ read_application(Name, Vsn, Path, Incls, true, NFE);
+ {error, {no_valid_version, {Vsn, _OtherVsn}}} ->
+ read_application(Name, Vsn, Path, Incls, true, FirstError);
+ Res ->
+ Res
+ end;
+ {error, {parse, _File, {Line, _Mod, Err}}} when FirstError == no_fault ->
+ read_application(Name, Vsn, Path, Incls, Found,
+ {parse_error, {filename:join(Dir, Name++".app"), Line, Err}});
+ {error, {parse, _File, _Err}} ->
+ read_application(Name, Vsn, Path, Incls, Found, FirstError);
+ {error, _Err} -> %% Not found
+ read_application(Name, Vsn, Path, Incls, Found, FirstError)
+ end;
+read_application(Name, Vsn, [], _, true, no_fault) ->
+ {error, {application_vsn, {Name,Vsn}}};
+read_application(_Name, _Vsn, [], _, true, FirstError) ->
+ {error, FirstError};
+read_application(Name, _, [], _, _, no_fault) ->
+ {error, {not_found, Name ++ ".app"}};
+read_application(_Name, _, [], _, _, FirstError) ->
+ {error, FirstError}.
+
+parse_application({application, Name, Dict}, File, Vsn, Incls)
+ when is_atom(Name),
+ is_list(Dict) ->
+ Items = [vsn,id,description,modules,registered,
+ applications,included_applications,mod,start_phases,env,maxT,maxP],
+ case catch get_items(Items, Dict) of
+ [Vsn,Id,Desc,Mods,Regs,Apps,Incs0,Mod,Phases,Env,MaxT,MaxP] ->
+ case override_include(Name, Incs0, Incls) of
+ {ok, Incs} ->
+ {ok, #application{name=Name,
+ vsn=Vsn,
+ id=Id,
+ description=Desc,
+ modules=Mods,
+ uses=Apps,
+ includes=Incs,
+ regs=Regs,
+ mod=Mod,
+ start_phases=Phases,
+ env=Env,
+ maxT=MaxT,
+ maxP=MaxP,
+ dir=filename:dirname(File)}};
+ {error, IncApps} ->
+ {error, {override_include, IncApps}}
+ end;
+ [OtherVsn,_,_,_,_,_,_,_,_,_,_,_] ->
+ {error, {no_valid_version, {Vsn, OtherVsn}}};
+ Err ->
+ {error, {Err, {application, Name, Dict}}}
+ end;
+parse_application(Other, _, _, _) ->
+ {error, {badly_formatted_application, Other}}.
+
+%% Test if all included applications specifed in the .rel file
+%% exists in the {included_applications,Incs} specified in the
+%% .app file.
+override_include(Name, Incs, Incls) ->
+ case keysearch(Name, 1, Incls) of
+ {value, {Name, I}} ->
+ case specified(I, Incs) of
+ [] ->
+ {ok, I};
+ NotSpec ->
+ {error, NotSpec}
+ end;
+ _ ->
+ {ok, Incs}
+ end.
+
+specified([App|Incls], Spec) ->
+ case member(App, Spec) of
+ true ->
+ specified(Incls, Spec);
+ _ ->
+ [App|specified(Incls, Spec)]
+ end;
+specified([], _) ->
+ [].
+
+get_items([H|T], Dict) ->
+ Item = check_item(keysearch(H, 1, Dict),H),
+ [Item|get_items(T, Dict)];
+get_items([], _Dict) ->
+ [].
+
+check_item({_,{mod,{M,A}}},_) when is_atom(M) ->
+ {M,A};
+check_item({_,{vsn,Vsn}},I) ->
+ case string_p(Vsn) of
+ true -> Vsn;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{id,Id}},I) ->
+ case string_p(Id) of
+ true -> Id;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{description,Desc}},I) ->
+ case string_p(Desc) of
+ true -> Desc;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{applications,Apps}},I) ->
+ case a_list_p(Apps) of
+ true -> Apps;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{included_applications,Apps}},I) ->
+ case a_list_p(Apps) of
+ true -> Apps;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{registered,Regs}},I) ->
+ case a_list_p(Regs) of
+ true -> Regs;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{modules,Mods}},I) ->
+ case mod_list_p(Mods) of
+ true -> Mods;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{start_phases,Phase}},I) ->
+ case t_list_p(Phase) of
+ true -> Phase;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{env,Env}},I) ->
+ case t_list_p(Env) of
+ true -> Env;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{maxT,MaxT}},I) ->
+ case MaxT of
+ MaxT when is_integer(MaxT), MaxT > 0 -> MaxT;
+ infinity -> infinity;
+ _ -> throw({bad_param, I})
+ end;
+check_item({_,{maxP,MaxP}},I) ->
+ case MaxP of
+ MaxP when is_integer(MaxP), MaxP > 0 -> MaxP;
+ infinity -> infinity;
+ _ -> throw({bad_param, I})
+ end;
+check_item(false, included_applications) -> % optional !
+ [];
+check_item(false, mod) -> % mod is optional !
+ [];
+check_item(false, env) -> % env is optional !
+ [];
+check_item(false, id) -> % id is optional !
+ [];
+check_item(false, start_phases) -> % start_phases is optional !
+ undefined;
+check_item(false, maxT) -> % maxT is optional !
+ infinity;
+check_item(false, maxP) -> % maxP is optional !
+ infinity;
+check_item(_, Item) ->
+ throw({missing_param, Item}).
+
+%%______________________________________________________________________
+%% check_applications([{{Name,Vsn},#application}]) ->
+%% ok | throw({error, Error})
+%% check that all referenced applications exists and that no
+%% application register processes with the same name.
+%% Check that included_applications are not specified as used
+%% in another application.
+
+check_applications(Appls) ->
+ undef_appls(Appls),
+ dupl_regs(Appls),
+ %% Make a list Incs = [{Name,App,AppVsn,Dir}]
+ Incs = [{IncApp,App,Appv,A#application.dir} ||
+ {{App,Appv},A} <- Appls,
+ IncApp <- A#application.includes],
+ dupl_incls(Incs),
+ Res = add_top_apps_to_uses(Incs, Appls, []),
+ {ok, Res}.
+
+
+
+undef_appls(Appls) ->
+ case undefined_applications(Appls) of
+ [] ->
+ ok;
+ L ->
+ throw({error, {undefined_applications, make_set(L)}})
+ end.
+
+dupl_regs(Appls) ->
+ %% Make a list Regs = [{Name,App,AppVsn,Dir}]
+ Regs = [{Name,App,Appv,A#application.dir} ||
+ {{App,Appv},A} <- Appls,
+ Name <- A#application.regs],
+ case duplicates(Regs) of
+ [] ->
+ ok;
+ Dups ->
+ throw({error, {duplicate_register, Dups}})
+ end.
+
+
+dupl_incls(Incs) ->
+ case duplicates(Incs) of
+ [] ->
+ ok;
+ Dups ->
+ throw({error, {duplicate_include, Dups}})
+ end.
+
+
+
+%% If an application uses another application which is included in yet
+%% another application, e.g. X uses A, A is included in T; then the A
+%% application in the X applications uses-variable is changed to the T
+%% application's top application to ensure the start order.
+%% Exception: if both X and A have the same top, then it is not
+%% added to avoid circular dependencies.
+%%
+%% add_top_apps_to_uses( list of all included applications in
+%% the system,
+%% list of all applications in the system,
+%% temporary result)
+%% -> new list of all applications
+add_top_apps_to_uses(_InclApps, [], Res) ->
+ %% InclApps = [{IncApp, App, AppVsn, Dir}]
+ Res;
+add_top_apps_to_uses(InclApps, [{Name,Appl} | Appls], Res) ->
+ MyTop = find_top_app(Appl#application.name, InclApps),
+ F = fun(UsedApp, AccIn) when UsedApp == MyTop ->
+ %% UW980513 This is a special case: The included app
+ %% uses its own top app. We'll allow it, but must
+ %% remove the top app from the uses list.
+ AccIn -- [MyTop];
+ (UsedApp, AccIn) ->
+ case lists:keysearch(UsedApp, 1, InclApps) of
+ false ->
+ AccIn;
+ {value, {_,DependApp,_,_}} ->
+ UsedAppTop = find_top_app(DependApp, InclApps),
+ case {lists:member(UsedAppTop, AccIn), MyTop} of
+ {true, _} ->
+ %% the top app is already in the uses
+ %% list, remove UsedApp
+ AccIn -- [UsedApp];
+ {_, UsedAppTop} ->
+ %% both are included in the same app
+ AccIn;
+ _ ->
+ %% change the used app to the used app's
+ %% top application
+ AccIn1 = AccIn -- [UsedApp],
+ AccIn1 ++ [UsedAppTop]
+ end
+ end
+ end,
+
+ NewUses = foldl(F, Appl#application.uses, Appl#application.uses),
+ add_top_apps_to_uses(InclApps, Appls,
+ Res++[{Name, Appl#application{uses = NewUses}}]).
+
+
+
+find_top_app(App, InclApps) ->
+ case lists:keysearch(App, 1, InclApps) of
+ false ->
+ App;
+ {value, {_,TopApp,_,_}} ->
+ find_top_app(TopApp, InclApps)
+ end.
+
+
+
+%%______________________________________________________________________
+%% undefined_applications([{{Name,Vsn},#application}]) ->
+%% [Name] list of applications that were declared in
+%% use declarations but are not contained in the release descriptor
+
+undefined_applications(Appls) ->
+ Uses = append(map(fun({_,A}) ->
+ A#application.uses ++ A#application.includes
+ end, Appls)),
+ Defined = map(fun({{X,_},_}) -> X end, Appls),
+ filter(fun(X) -> not member(X, Defined) end, Uses).
+
+%%______________________________________________________________________
+%% sort_included_applications(Applications, Release) -> Applications
+%% Applications = [{{Name,Vsn},#application}]
+%% Release = #release{}
+%%
+%% Check that included applications are given in the same order as in
+%% the release resource file (.rel). Otherwise load instructions in
+%% the boot script, and consequently release upgrade instructions in
+%% relup, may end up in the wrong order.
+
+sort_included_applications(Applications, Release) when is_tuple(Release) ->
+ {ok,
+ sort_included_applications(Applications, Release#release.applications)};
+
+sort_included_applications([{Tuple,Appl}|Appls], OrderedAppls) ->
+ case Appl#application.includes of
+ Incls when length(Incls)>1 ->
+ IndexedIncls = find_pos(Incls, OrderedAppls),
+ SortedIndexedIncls = lists:keysort(1, IndexedIncls),
+ Incls2 = lists:map(fun({_Index,Name}) -> Name end,
+ SortedIndexedIncls),
+ Appl2 = Appl#application{includes=Incls2},
+ [{Tuple,Appl2}|sort_included_applications(Appls, OrderedAppls)];
+ _Incls ->
+ [{Tuple,Appl}|sort_included_applications(Appls, OrderedAppls)]
+ end;
+sort_included_applications([], _OrderedAppls) ->
+ [].
+
+find_pos([Name|Incs], OrderedAppls) ->
+ [find_pos(1, Name, OrderedAppls)|find_pos(Incs, OrderedAppls)];
+find_pos([], _OrderedAppls) ->
+ [].
+
+find_pos(N, Name, [{Name,_Vsn,_Type}|_OrderedAppls]) ->
+ {N, Name};
+find_pos(N, Name, [_OtherAppl|OrderedAppls]) ->
+ find_pos(N+1, Name, OrderedAppls).
+
+%%______________________________________________________________________
+%% check_modules(Appls, Path, TestP, Machine) ->
+%% {ok, Warnings} | throw({error, What})
+%% where Appls = [{App,Vsn}, #application}]
+%% performs logical checking that we can find all the modules
+%% etc.
+
+check_modules(Appls, Path, TestP, Machine) ->
+ %% first check that all the module names are unique
+ %% Make a list M1 = [{Mod,Vsn,App,AppVsn,Dir}]
+ %% where Vsn = '$$ignore$$' | Specified
+ M1 = [{Mod,Vsn,App,Appv,A#application.dir} ||
+ {{App,Appv},A} <- Appls,
+ {Mod,Vsn} <- get_mod_vsn(A#application.modules)],
+ case duplicates(M1) of
+ [] ->
+ case check_mods(M1, Appls, Path, TestP, Machine) of
+ {error, Errors} ->
+ throw({error, {modules, Errors}});
+ Return ->
+ Return
+ end;
+ Dups ->
+% io:format("** ERROR Duplicate modules: ~p\n", [Dups]),
+ throw({error, {duplicate_modules, Dups}})
+ end.
+
+get_mod_vsn([{Mod,Vsn}|Mods]) ->
+ [{Mod,Vsn}|get_mod_vsn(Mods)];
+get_mod_vsn([Mod|Mods]) ->
+ [{Mod,'$$ignore$$'}|get_mod_vsn(Mods)];
+get_mod_vsn([]) ->
+ [].
+
+%%______________________________________________________________________
+%% Check that all modules exists and that the specified version
+%% corresponds to the version in the module's source code.
+%% Use the module extension of the running machine as extension for
+%% the checked modules.
+
+check_mods(Modules, Appls, Path, {true, XrefP}, Machine) ->
+ Ext = objfile_extension(Machine),
+ IncPath = create_include_path(Appls, Path),
+ Res = append(map(fun(ModT) ->
+ {Mod,_Vsn,App,_,Dir} = ModT,
+ case check_mod(Mod,App,Dir,Ext,IncPath) of
+ ok ->
+ [];
+ {error, Error} ->
+ [{error,{Error, ModT}}];
+ {warning, Warn} ->
+ [{warning,{Warn,ModT}}]
+ end
+ end,
+ Modules)),
+ Res2 = Res ++ check_xref(Appls, Path, XrefP),
+ case filter(fun({error, _}) -> true;
+ (_) -> false
+ end,
+ Res2) of
+ [] ->
+ {ok, filter(fun({warning, _}) -> true;
+ (_) -> false
+ end,
+ Res2)};
+ Errors ->
+ {error, Errors}
+ end;
+check_mods(_, _, _, _, _) ->
+ {ok, []}.
+
+check_xref(_Appls, _Path, false) ->
+ [];
+check_xref(Appls, Path, XrefP) ->
+ AppDirsL = [{App,A#application.dir} || {{App,_Appv},A} <- Appls],
+ AppDirs0 = sofs:relation(AppDirsL),
+ AppDirs = case XrefP of
+ true ->
+ AppDirs0;
+ {true, Apps} ->
+ sofs:restriction(AppDirs0, sofs:set(Apps))
+ end,
+ XrefArgs = [{xref_mode, modules}],
+ case catch xref:start(?XREF_SERVER, XrefArgs) of
+ {ok, _Pid} ->
+ ok;
+ {error, {already_started, _Pid}} ->
+ xref:stop(?XREF_SERVER), %% Clear out any previous data
+ xref:start(?XREF_SERVER, XrefArgs)
+ end,
+ {ok, _} = xref:set_default(?XREF_SERVER, verbose, false),
+ LibPath = case Path == code:get_path() of
+ true -> code_path; % often faster
+ false -> Path
+ end,
+ ok = xref:set_library_path(?XREF_SERVER, LibPath),
+ check_xref(sofs:to_external(AppDirs)).
+
+check_xref([{App,AppDir} | Appls]) ->
+ case xref:add_application(?XREF_SERVER, AppDir, {name,App}) of
+ {ok, _App} ->
+ check_xref(Appls);
+ Error ->
+ xref:stop(?XREF_SERVER),
+ [{error, Error}]
+ end;
+check_xref([]) ->
+ R = case xref:analyze(?XREF_SERVER, undefined_functions) of
+ {ok, []} ->
+ [];
+ {ok, Undefined} ->
+ %% This clause is a (temporary?) fix for hipe.
+ adjust_for_hipe(Undefined);
+ Error ->
+ [{error, Error}]
+ end,
+ xref:stop(?XREF_SERVER),
+ R.
+
+adjust_for_hipe(Undef) ->
+ case erlang:system_info(hipe_architecture) of
+ undefined ->
+ U = lists:filter(fun ({hipe_bifs,_,_}) -> false;
+ ({hipe,_,_}) -> false;
+ (_) -> true
+ end, Undef),
+ if
+ [] == U ->
+ [];
+ true ->
+ [{warning, {exref_undef, U}}]
+ end;
+ _Arch ->
+ %% Some BIFs are not always available on all versions of HiPE.
+ U = lists:filter(fun ({hipe_bifs,write_u64,2}) -> false;
+ (_) -> true
+ end, Undef),
+ [{warning, {exref_undef, U}}]
+ end.
+
+%% Perform cross reference checks between all modules specified
+%% in .app files.
+%%
+xref_p(Flags) ->
+ case member(exref, Flags) of
+ true ->
+ exists_xref(true);
+ _ ->
+ case get_flag(exref, Flags) of
+ {exref, Appls} when is_list(Appls) ->
+ case a_list_p(Appls) of
+ true -> exists_xref({true, Appls});
+ _ -> false
+ end;
+ _ ->
+ false
+ end
+ end.
+
+exists_xref(Flag) ->
+ case code:ensure_loaded(xref) of
+ {error, _} -> false;
+ _ -> Flag
+ end.
+
+objfile_extension(false) ->
+ code:objfile_extension();
+objfile_extension(Machine) ->
+ "." ++ atom_to_list(Machine).
+
+check_mod(Mod,App,Dir,Ext,IncPath) ->
+ ObjFile = mod_to_filename(Dir, Mod, Ext),
+ case file:read_file_info(ObjFile) of
+ {ok,FileInfo} ->
+ LastModTime = FileInfo#file_info.mtime,
+ check_module(Mod, Dir, LastModTime, IncPath);
+ _ ->
+ {error, {module_not_found, App, Mod}}
+ end.
+
+mod_to_filename(Dir, Mod, Ext) ->
+ Parts = packages:split(Mod),
+ filename:join([Dir | Parts]) ++ Ext.
+
+check_module(Mod, Dir, ObjModTime, IncPath) ->
+ {SrcDirs,_IncDirs}= smart_guess(Mod, Dir,IncPath),
+ case locate_src(Mod,SrcDirs) of
+ {ok,_FDir,_File,LastModTime} ->
+ if
+ LastModTime > ObjModTime ->
+ {warning, obj_out_of_date};
+ true ->
+ ok
+ end;
+ _ ->
+ {warning, source_not_found}
+ end.
+
+locate_src(Mod,[Dir|Dirs]) ->
+ File = filename:join(Dir, mod_to_fname(Mod) ++ ".erl"),
+ case file:read_file_info(File) of
+ {ok,FileInfo} ->
+ LastModTime = FileInfo#file_info.mtime,
+ {ok,Dir,File,LastModTime};
+ _ ->
+ locate_src(Mod,Dirs)
+ end;
+locate_src(_,[]) ->
+ false.
+
+mod_to_fname(Mod) ->
+ hd(lists:reverse(packages:split(Mod))).
+
+
+%%______________________________________________________________________
+%% smart_guess(Mod, Dir,IncludePath) -> {[Dirs],[IncDirs]}
+%% Guess the src code and include directory. If dir contains .../ebin
+%% src-dir should be one of .../src or .../src/e_src
+%% If dir does not contain .../ebin set dir to the same directory.
+
+smart_guess(Mod, Dir,IncPath) ->
+ case reverse(filename:split(Dir)) of
+ ["ebin"|D] ->
+ Subdirs = case packages:split(Mod) of
+ [_] -> [];
+ [_|_] = Parts ->
+ lists:reverse(tl(lists:reverse(Parts)))
+ end,
+ D1 = reverse(D),
+ Dirs = [filename:join(D1 ++ ["src" | Subdirs]),
+ filename:join(D1 ++ ["src", "e_src" | Subdirs])],
+ {Dirs,Dirs ++ IncPath};
+ _ ->
+ {[Dir],[Dir] ++ IncPath}
+ end.
+
+%%______________________________________________________________________
+%% generate_script(#release,
+%% [{{Name,Vsn},#application}], Flags) ->
+%% ok | {error, Error}
+%% Writes a script (a la magnus) to the file File.script
+%% and a bootfile to File.boot.
+
+generate_script(Output, Release, Appls, Flags) ->
+ PathFlag = path_flag(Flags),
+ Variables = get_variables(Flags),
+ Preloaded = preloaded(),
+ Mandatory = mandatory_modules(),
+ Script = {script, {Release#release.name,Release#release.vsn},
+ [{preLoaded, Preloaded},
+ {progress, preloaded},
+ {path, create_mandatory_path(Appls, PathFlag, Variables)},
+ {primLoad, Mandatory},
+ {kernel_load_completed},
+ {progress, kernel_load_completed}] ++
+ load_appl_mods(Appls, Mandatory ++ Preloaded,
+ PathFlag, Variables) ++
+ [{path, create_path(Appls, PathFlag, Variables)}] ++
+ create_kernel_procs(Appls) ++
+ create_load_appls(Appls) ++
+ create_start_appls(Appls) ++
+ script_end()
+ },
+
+ ScriptFile = Output ++ ".script",
+ case file:open(ScriptFile, [write]) of
+ {ok, Fd} ->
+ io:format(Fd, "%% script generated at ~w ~w\n~p.\n",
+ [date(), time(), Script]),
+ file:close(Fd),
+
+ BootFile = Output ++ ".boot",
+ case file:write_file(BootFile, term_to_binary(Script)) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ {error, ?MODULE, {open,BootFile,Reason}}
+ end;
+ {error, Reason} ->
+ {error, ?MODULE, {open,ScriptFile,Reason}}
+ end.
+
+path_flag(Flags) ->
+ case {member(local,Flags), member(otp_build, Flags)} of
+ {true, _} -> local;
+ {_, true} -> otp_build;
+ {_, _} -> true
+ end.
+
+get_variables(Flags) ->
+ case get_flag(variables, Flags) of
+ {variables, Variables} when is_list(Variables) ->
+ valid_variables(Variables);
+ _ ->
+ []
+ end.
+
+valid_variables([{Var,Path}|Variables]) when is_list(Var), is_list(Path) ->
+ [{Var,rm_tlsl(Path)}|valid_variables(Variables)];
+valid_variables([{Var,Path}|Variables]) when is_atom(Var), is_list(Path) ->
+ [{to_list(Var),rm_tlsl(Path)}|valid_variables(Variables)];
+valid_variables([_|Variables]) ->
+ valid_variables(Variables);
+valid_variables(_) ->
+ [].
+
+rm_tlsl(P) -> rm_tlsl1(reverse(P)).
+rm_tlsl1([$/|P]) -> rm_tlsl1(P);
+rm_tlsl1(P) -> reverse(P).
+
+%%______________________________________________________________________
+%% Start all applications.
+%% Do not start applications that are included applications !
+
+create_start_appls(Appls) ->
+ Included = append(map(fun({_,A}) ->
+ A#application.includes
+ end, Appls)),
+ create_start_appls(Appls, Included).
+
+create_start_appls([{_,A}|T], Incl) ->
+ App = A#application.name,
+ case lists:member(App, Incl) of
+ false when A#application.type == none ->
+ create_start_appls(T, Incl);
+ false when A#application.type == load ->
+ create_start_appls(T, Incl);
+ false ->
+ [{apply, {application, start_boot, [App,A#application.type]}} |
+ create_start_appls(T, Incl)];
+ _ ->
+ create_start_appls(T, Incl)
+ end;
+create_start_appls([], _) ->
+ [].
+
+%%______________________________________________________________________
+%% Load all applications.
+
+create_load_appls([{{kernel,_},_}|T]) -> %Already added !!
+ create_load_appls(T);
+create_load_appls([{_,A}|T]) when A#application.type == none ->
+ create_load_appls(T);
+create_load_appls([{_,A}|T]) ->
+ [{apply, {application, load, [pack_app(A)]}} |
+ create_load_appls(T)];
+create_load_appls([]) ->
+ [{progress, applications_loaded}].
+
+%%______________________________________________________________________
+%% The final part of the script.
+
+script_end() ->
+ [{apply, {c, erlangrc, []}},
+ {progress, started}].
+
+%%-----------------------------------------------------------------
+%% Function: sort_appls(Appls) -> {ok, Appls'} | throw({error, Error})
+%% Types: Appls = {{Name, Vsn}, #application}]
+%% Purpose: Sort applications according to dependencies among
+%% applications. If order doesn't matter, use the same
+%% order as in the original list.
+%% Alg. written by Ulf Wiger 970917 ([email protected])
+%% Mod. by mbj
+%%-----------------------------------------------------------------
+sort_appls(Appls) -> {ok, sort_appls(Appls, [], [], [])}.
+
+sort_appls([{N, A}|T], Missing, Circular, Visited) ->
+ {Name,_Vsn} = N,
+ {Uses, T1, NotFnd1} = find_all(Name, A#application.uses, T, Visited, [], []),
+ {Incs, T2, NotFnd2} = find_all(Name, lists:reverse(A#application.includes),
+ T1, Visited, [], []),
+ Missing1 = NotFnd1 ++ NotFnd2 ++ Missing,
+ case Uses ++ Incs of
+ [] ->
+ %% No more app that must be started before this one is
+ %% found; they are all already taken care of (and present
+ %% in Visited list)
+ [{N, A}|sort_appls(T, Missing1, Circular, [N|Visited])];
+ L ->
+ %% The apps in L must be started before the app.
+ %% Check if we have already taken care of some app in L,
+ %% in that case we have a circular dependency.
+ NewCircular = [N1 || {N1, _} <- L, N2 <- Visited, N1 == N2],
+ Circular1 = case NewCircular of
+ [] -> Circular;
+ _ -> [N | NewCircular] ++ Circular
+ end,
+ %% L must be started before N, try again, with all apps
+ %% in L added before N.
+ Apps = del_apps(NewCircular, L ++ [{N, A}|T2]),
+ sort_appls(Apps, Missing1, Circular1, [N|Visited])
+ end;
+sort_appls([], [], [], _) ->
+ [];
+sort_appls([], Missing, [], _) ->
+ %% this has already been checked before, but as we have the info...
+ throw({error, {undefined_applications, make_set(Missing)}});
+sort_appls([], [], Circular, _) ->
+ throw({error, {circular_dependencies, make_set(Circular)}});
+sort_appls([], Missing, Circular, _) ->
+ throw({error, {apps, [{circular_dependencies, make_set(Circular)},
+ {undefined_applications, make_set(Missing)}]}}).
+
+find_all(CheckingApp, [Name|T], L, Visited, Found, NotFound) ->
+ case find_app(Name, L) of
+ {value, App} ->
+ {_A,R} = App,
+ %% It is OK to have a dependecy like
+ %% X includes Y, Y uses X.
+ case lists:member(CheckingApp, R#application.includes) of
+ true ->
+ case lists:keymember(Name, 1, Visited) of
+ true ->
+ find_all(CheckingApp, T, L, Visited, Found, NotFound);
+ false ->
+ find_all(CheckingApp, T, L, Visited, Found, [Name|NotFound])
+ end;
+ false ->
+ find_all(CheckingApp, T, L -- [App], Visited, [App|Found], NotFound)
+ end;
+ false ->
+ case lists:keymember(Name, 1, Visited) of
+ true ->
+ find_all(CheckingApp, T, L, Visited, Found, NotFound);
+ false ->
+ find_all(CheckingApp, T, L, Visited, Found, [Name|NotFound])
+ end
+ end;
+find_all(_CheckingApp, [], L, _Visited, Found, NotFound) ->
+ {Found, L, NotFound}.
+
+find_app(Name, [{{Name,Vsn}, Application}|_]) ->
+ {value, {{Name,Vsn},Application}};
+find_app(Name, [_|T]) ->
+ find_app(Name, T);
+find_app(_Name, []) ->
+ false.
+
+del_apps([Name|T], L) ->
+ del_apps(T, lists:keydelete(Name, 1, L));
+del_apps([], L) ->
+ L.
+
+
+%%______________________________________________________________________
+%% Create the load path used in the generated script.
+%% If PathFlag is true a script intended to be used as a complete
+%% system (e.g. in an embbeded system), i.e. all applications are
+%% located under $ROOT/lib.
+%% Otherwise all paths are set according to dir per application.
+
+%% Create the complete path.
+create_path(Appls, PathFlag, Variables) ->
+ make_set(map(fun({{Name,Vsn},App}) ->
+ cr_path(Name, Vsn, App, PathFlag, Variables)
+ end,
+ Appls)).
+
+%% Create the path to a specific application.
+%% (The otp_build flag is only used for OTP internal system make)
+cr_path(Name, Vsn, _, true, []) ->
+ filename:join(["$ROOT", "lib", to_list(Name) ++ "-" ++ Vsn, "ebin"]);
+cr_path(Name, Vsn, App, true, Variables) ->
+ Dir = App#application.dir,
+ N = to_list(Name),
+ Tail = [N ++ "-" ++ Vsn, "ebin"],
+ case variable_dir(Dir, N, Vsn, Variables) of
+ {ok, VarDir} ->
+ filename:join([VarDir] ++ Tail);
+ _ ->
+ filename:join(["$ROOT", "lib"] ++ Tail)
+ end;
+cr_path(Name, _, _, otp_build, _) ->
+ filename:join(["$ROOT", "lib", to_list(Name), "ebin"]);
+cr_path(_, _, App, _, _) ->
+ filename:absname(App#application.dir).
+
+variable_dir(Dir, Name, Vsn, [{Var,Path}|Variables]) ->
+ case lists:prefix(Path,Dir) of
+ true ->
+ D0 = strip_prefix(Path, Dir),
+ case strip_name_ebin(D0, Name, Vsn) of
+ {ok, D} ->
+ {ok, filename:join(["\$" ++ Var] ++ D)};
+ _ ->
+ %% We know at least that we are located
+ %% under the variable dir.
+ {ok, filename:join(["\$" ++ Var] ++ D0)}
+ end;
+ _ ->
+ variable_dir(Dir, Name, Vsn, Variables)
+ end;
+variable_dir(_Dir, _, _, []) ->
+ false.
+
+strip_prefix(Path, Dir) ->
+ L = length(filename:split(Path)),
+ lists:nthtail(L, filename:split(Dir)).
+
+strip_name_ebin(Dir, Name, Vsn) ->
+ FullName = Name ++ "-" ++ Vsn,
+ case reverse(Dir) of
+ ["ebin",Name|D] -> {ok, reverse(D)};
+ ["ebin",FullName|D] -> {ok, reverse(D)};
+ _ -> false
+ end.
+
+%% Create the path to the kernel and stdlib applications.
+create_mandatory_path(Appls, PathFlag, Variables) ->
+ Dirs = [kernel, stdlib],
+ make_set(map(fun({{Name,Vsn}, A}) ->
+ case lists:member(Name, Dirs) of
+ true ->
+ cr_path(Name, Vsn, A, PathFlag, Variables);
+ _ ->
+ ""
+ end
+ end,
+ Appls)).
+
+%%______________________________________________________________________
+%% Load all modules, except those in Mandatory_modules.
+
+load_appl_mods([{{Name,Vsn},A}|Appls], Mand, PathFlag, Variables) ->
+ Mods = map(fun({Mod,_}) -> Mod;
+ (Mod) -> Mod
+ end,
+ A#application.modules),
+ load_commands(filter(fun(Mod) -> not member(Mod, Mand) end, Mods),
+ cr_path(Name, Vsn, A, PathFlag, Variables)) ++
+ load_appl_mods(Appls, Mand, PathFlag, Variables);
+% [{path, [cr_path(Name, Vsn, A, PathFlag, Variables)]},
+% {primLoad, filter(fun(Mod) -> not member(Mod, Mand) end, Mods)} |
+% load_appl_mods(Appls, Mand, PathFlag, Variables)];
+load_appl_mods([], _, _, _) ->
+ [{progress, modules_loaded}].
+
+load_commands(Mods, Path) ->
+ SplitMods = lists:foldl(
+ fun({Parts,M}, [{Last, Acc}|Rest]) ->
+ [_|Tail] = lists:reverse(Parts),
+ case lists:reverse(Tail) of
+ Subs when Subs == Last ->
+ [{Last,[M|Acc]}|Rest];
+ Subs ->
+ [{Subs, [M]}|[{Last,Acc}|Rest]]
+ end
+ end, [{[],[]}],
+ lists:sort([{packages:split(M),M} || M <- Mods])),
+ lists:foldl(
+ fun({Subs,Ms}, Cmds) ->
+ [{path, [filename:join([Path | Subs])]},
+ {primLoad,lists:sort(Ms)} | Cmds]
+ end, [], SplitMods).
+
+
+%%______________________________________________________________________
+%% Pack an application to an application term.
+
+pack_app(#application{name=Name,vsn=V,id=Id,description=D,modules=M,
+ uses=App,includes=Incs,regs=Regs,mod=Mod,start_phases=SF,
+ env=Env,maxT=MaxT,maxP=MaxP}) ->
+ {application, Name,
+ [{description,D},
+ {vsn,V},
+ {id,Id},
+ {modules, M},
+ {registered, Regs},
+ {applications, App},
+ {included_applications, Incs},
+ {env, Env},
+ {start_phases, SF},
+ {maxT, MaxT},
+ {maxP, MaxP} |
+ behave(Mod)]}.
+
+behave([]) ->
+ [];
+behave(Mod) ->
+ [{mod, Mod}].
+
+%%______________________________________________________________________
+%% mandatory modules; this modules must be loaded before processes
+%% can be started. These are a collection of modules from the kernel
+%% and stdlib applications.
+%% Nowadays, error_handler dynamically loads almost every module.
+%% The error_handler self must still be there though.
+
+mandatory_modules() ->
+ %% Sorted
+ [error_handler].
+
+%%______________________________________________________________________
+%% This is the modules that are preloaded into the Erlang system.
+
+preloaded() ->
+ %% Sorted
+ [erl_prim_loader,erlang,init,otp_ring0,prim_file,prim_inet, prim_zip,zlib].
+
+%%______________________________________________________________________
+%% Kernel processes; processes that are specially treated by the init
+%% process. If a kernel process terminates the whole system terminates.
+%% kernel_processes() -> [{Name, Mod, Func, Args}]
+%% where Args is a term or a fun taking the list of applications as arg.
+
+kernel_processes() ->
+ [{heart, heart, start, []},
+ {error_logger, error_logger, start_link, []},
+ {application_controller, application_controller, start,
+ fun(Appls) ->
+ [{_,App}] = filter(fun({{kernel,_},_App}) -> true;
+ (_) -> false
+ end,
+ Appls),
+ [pack_app(App)]
+ end}
+ ].
+
+%%______________________________________________________________________
+%% Create the kernel processes.
+
+create_kernel_procs(Appls) ->
+ map(fun({Name,Mod,Func,Args}) when is_function(Args) ->
+ {kernelProcess, Name, {Mod, Func, Args(Appls)}};
+ ({Name,Mod,Func,Args}) ->
+ {kernelProcess, Name, {Mod, Func, Args}}
+ end,
+ kernel_processes()) ++
+ [{progress, init_kernel_started}].
+
+%%______________________________________________________________________
+%% Make a tar file of the release.
+%% The tar file contains:
+%% lib/App-Vsn/ebin
+%% /priv
+%% [/src]
+%% [/include]
+%% [/doc]
+%% [/examples]
+%% [/...]
+%% Variable1.tar.gz
+%% ...
+%% VariableN.tar.gz
+%% releases/RelName.rel
+%% RelVsn/start.boot
+%% relup
+%% sys.config
+%% erts-EVsn[/bin]
+%%
+%% The VariableN.tar.gz files can also be stored as own files not
+%% included in the main tar file or they can be omitted using
+%% the var_tar option.
+
+mk_tar(RelName, Release, Appls, Flags, Path1) ->
+ TarName = case get_outdir(Flags) of
+ "" ->
+ RelName ++ ".tar.gz";
+ OutDir ->
+ filename:join(OutDir, filename:basename(RelName))
+ ++ ".tar.gz"
+ end,
+ Tar = open_main_tar(TarName),
+ case catch mk_tar(Tar, RelName, Release, Appls, Flags, Path1) of
+ {error,Error} ->
+ del_tar(Tar, TarName),
+ {error,?MODULE,Error};
+ {'EXIT',Reason} ->
+ del_tar(Tar, TarName),
+ {error,?MODULE,Reason};
+ _ ->
+ close_tar(Tar),
+ ok
+ end.
+
+open_main_tar(TarName) ->
+ case catch open_tar(TarName) of
+ {error, Error} ->
+ throw({error,?MODULE,Error});
+ Tar ->
+ Tar
+ end.
+
+mk_tar(Tar, RelName, Release, Appls, Flags, Path1) ->
+ Variables = get_variables(Flags),
+ add_applications(Appls, Tar, Variables, Flags, false),
+ add_variable_tars(Variables, Appls, Tar, Flags),
+ add_system_files(Tar, RelName, Release, Path1),
+ add_erts_bin(Tar, Release, Flags).
+
+add_applications(Appls, Tar, Variables, Flags, Var) ->
+ Res = foldl(fun({{Name,Vsn},App}, Errs) ->
+ case catch add_appl(to_list(Name), Vsn, App,
+ Tar, Variables, Flags, Var) of
+ ok ->
+ Errs;
+ {error, What} ->
+ [{error_add_appl, {Name,What}}|Errs]
+ end
+ end, [], Appls),
+ case Res of
+ [] ->
+ ok;
+ Errors ->
+ throw({error, Errors})
+ end.
+
+%%______________________________________________________________________
+%% Create a tar file for each Variable directory.
+%% Deletes the temporary tar file.
+
+add_variable_tars([Variable|Variables], Appls, Tar, Flags) ->
+ add_variable_tar(Variable, Appls, Tar, Flags),
+ add_variable_tars(Variables, Appls, Tar, Flags);
+add_variable_tars([], _, _, _) ->
+ ok.
+
+add_variable_tar({Variable,P}, Appls, Tar, Flags) ->
+ case var_tar_flag(Flags) of
+ omit ->
+ ok;
+ Flag ->
+ TarName = Variable ++ ".tar.gz",
+ VarTar = open_tar(TarName),
+ case catch add_applications(Appls, VarTar, [{Variable,P}],
+ Flags, Variable) of
+ ok when Flag == include ->
+ close_tar(VarTar),
+ add_to_tar(Tar, TarName, TarName),
+ del_file(TarName);
+ ok when Flag == ownfile ->
+ close_tar(VarTar),
+ ok;
+ Error ->
+ del_tar(VarTar, TarName),
+ throw(Error)
+ end
+ end.
+
+var_tar_flag(Flags) ->
+ case get_flag(var_tar, Flags) of
+ {var_tar, Flag} ->
+ case member(Flag, [include, ownfile, omit]) of
+ true -> Flag;
+ _ -> include
+ end;
+ _ ->
+ include
+ end.
+
+%%______________________________________________________________________
+%% Add all "other" files to Dir/releases/Svsn
+%% add_system_files(Tar,Name,release#,Flags) ->
+%% ok | throw({error,Error})
+
+add_system_files(Tar, RelName, Release, Path1) ->
+ SVsn = Release#release.vsn,
+ RelName0 = filename:basename(RelName),
+
+ add_to_tar(Tar, RelName ++ ".rel",
+ filename:join("releases", RelName0 ++ ".rel")),
+
+ %% OTP-6226 Look for the system files not only in cwd
+ %% --
+ %% (well, actually the boot file was looked for in the same
+ %% directory as RelName, which is not necessarily the same as cwd)
+ %% --
+ %% but also in the path specfied as an option to systools:make_tar
+ %% (but make sure to search the RelName directory and cwd first)
+ Path = case filename:dirname(RelName) of
+ "." ->
+ ["."|Path1];
+ RelDir ->
+ [RelDir, "."|Path1]
+ end,
+
+ ToDir = filename:join("releases", SVsn),
+ case lookup_file(RelName0 ++ ".boot", Path) of
+ false ->
+ throw({error, {tar_error,{add, RelName0++".boot",enoent}}});
+ Boot ->
+ add_to_tar(Tar, Boot, filename:join(ToDir, "start.boot"))
+ end,
+
+ case lookup_file("relup", Path) of
+ false ->
+ ignore;
+ Relup ->
+ add_to_tar(Tar, Relup, filename:join(ToDir, "relup"))
+ end,
+
+ case lookup_file("sys.config", Path) of
+ false ->
+ ignore;
+ Sys ->
+ add_to_tar(Tar, Sys, filename:join(ToDir, "sys.config"))
+ end,
+
+ ok.
+
+lookup_file(Name, [Dir|Path]) ->
+ File = filename:join(Dir, Name),
+ case filelib:is_file(File) of
+ true ->
+ File;
+ false ->
+ lookup_file(Name, Path)
+ end;
+lookup_file(_Name, []) ->
+ false.
+
+%%______________________________________________________________________
+%% Add either a application located under a variable dir or all other
+%% applications to a tar file.
+%% add_appl(Name,Vsn,application#,Tar,Variables,Flags,Var) ->
+%% ok | {error,Error}
+
+add_appl(Name, Vsn, App, Tar, Variables, Flags, Var) ->
+ AppDir = App#application.dir,
+ case add_to(AppDir,Name,Vsn,Variables,Var) of
+ false ->
+ ok;
+ {ok, ToDir} ->
+ ADir = appDir(AppDir),
+ add_priv(ADir, ToDir, Tar),
+ case get_flag(dirs,Flags) of
+ {dirs,Dirs} ->
+ add_dirs(ADir, Dirs, ToDir, Tar);
+ _ ->
+ ok
+ end,
+ BinDir = filename:join(ToDir, "ebin"),
+ add_to_tar(Tar,
+ filename:join(AppDir, Name ++ ".app"),
+ filename:join(BinDir, Name ++ ".app")),
+ add_modules(map(fun({Mod,_}) -> to_list(Mod);
+ (Mod) -> to_list(Mod)
+ end,
+ App#application.modules),
+ Tar,
+ AppDir,
+ BinDir,
+ objfile_extension(machine(Flags)))
+ end.
+
+%%______________________________________________________________________
+%% If an application directory contains a Variable (in AppDir) the
+%% application will be placed in the tar file (if it is this Variable
+%% we corrently is actually storing).
+
+add_to(AppDir,Name,Vsn,Variables,Variable) ->
+ case var_dir(AppDir,Name,Vsn,Variables) of
+ {ok, Variable, RestPath} ->
+ {ok, filename:join(RestPath ++ [Name ++ "-" ++ Vsn])};
+ {ok, _, _} ->
+ false;
+ _ when Variable == false ->
+ {ok, filename:join("lib", Name ++ "-" ++ Vsn)};
+ _ ->
+ false
+ end.
+
+var_dir(Dir, Name, Vsn, [{Var,Path}|Variables]) ->
+ case lists:prefix(Path,Dir) of
+ true ->
+ D0 = strip_prefix(Path, Dir),
+ case strip_name_ebin(D0, Name, Vsn) of
+ {ok, D} ->
+ {ok, Var, D};
+ _ ->
+ false
+ end;
+ _ ->
+ var_dir(Dir, Name, Vsn, Variables)
+ end;
+var_dir(_Dir, _, _, []) ->
+ false.
+
+appDir(AppDir) ->
+ case reverse(filename:split(AppDir)) of
+ ["ebin"|Dir] -> filename:join(reverse(Dir));
+ _ -> AppDir
+ end.
+
+add_modules(Modules, Tar, AppDir, ToDir, Ext) ->
+ foreach(fun(Mod) ->
+ add_to_tar(Tar,
+ filename:join(AppDir, Mod ++ Ext),
+ filename:join(ToDir, Mod ++ Ext))
+ end, Modules).
+
+%%
+%% Add own specified directories to include in the release.
+%% If not found, skip it.
+%%
+add_dirs(AppDir, Dirs, ToDir, Tar) ->
+ foreach(fun(Dir) -> catch add_dir(AppDir, to_list(Dir), ToDir, Tar) end,
+ Dirs).
+
+add_dir(TopDir, Dir, ToDir, Tar) ->
+ FromD = filename:join(TopDir, Dir),
+ case dirp(FromD) of
+ true ->
+ add_to_tar(Tar, FromD, filename:join(ToDir, Dir));
+ _ ->
+ ok
+ end.
+
+%%
+%% Add the priv dir if it exists.
+
+add_priv(ADir, ToDir, Tar) ->
+ Priv = filename:join(ADir, "priv"),
+ case dirp(Priv) of
+ true ->
+ add_to_tar(Tar, Priv, filename:join(ToDir, "priv"));
+ _ ->
+ ok
+ end.
+
+add_erts_bin(Tar, Release, Flags) ->
+ case get_flag(erts,Flags) of
+ {erts,ErtsDir} ->
+ EVsn = Release#release.erts_vsn,
+ FromDir = filename:join([to_list(ErtsDir),
+ "erts-" ++ EVsn, "bin"]),
+ dirp(FromDir),
+ ToDir = filename:join("erts-" ++ EVsn, "bin"),
+ add_to_tar(Tar, FromDir, ToDir);
+ _ ->
+ ok
+ end.
+
+%%______________________________________________________________________
+%% Tar functions.
+
+open_tar(TarName) ->
+ case erl_tar:open(TarName, [write, compressed]) of
+ {ok, Tar} ->
+ Tar;
+ {error, Error} ->
+ throw({error,{tar_error, {open, TarName, Error}}})
+ end.
+
+close_tar(Tar) ->
+ erl_tar:close(Tar).
+
+del_tar(Tar, TarName) ->
+ close_tar(Tar),
+ del_file(TarName).
+
+add_to_tar(Tar, FromFile, ToFile) ->
+ case erl_tar:add(Tar, FromFile, ToFile, [compressed, dereference]) of
+ ok -> ok;
+ {error, Error} ->
+ throw({error, {tar_error, {add, FromFile, Error}}})
+ end.
+
+%%______________________________________________________________________
+%%______________________________________________________________________
+%% utilities!
+
+make_set([]) -> [];
+make_set([""|T]) -> % Ignore empty items.
+ make_set(T);
+make_set([H|T]) ->
+ [H | [ Y || Y<- make_set(T),
+ Y =/= H]].
+
+to_list(A) when is_atom(A) -> atom_to_list(A);
+to_list(L) -> L.
+
+mk_path(Path0) ->
+ Path1 = map(fun(Dir) when is_atom(Dir) -> atom_to_list(Dir);
+ (Dir) -> Dir
+ end, Path0),
+ systools_lib:get_path(Path1).
+
+%% duplicates([Tuple]) -> List of pairs where
+%% element(1, T1) == element(1, T2) and where T1 and T2 are
+%% taken from [Tuple]
+
+duplicates(X) -> duplicates(keysort(1,X), []).
+
+duplicates([H1,H2|T], L) ->
+ case {element(1,H1),element(1,H2)} of
+ {X,X} -> duplicates([H2|T],[{H1,H2}|L]);
+ _ -> duplicates([H2|T],L)
+ end;
+duplicates(_, L) -> L.
+
+%% read_file(File, Path) -> {ok, Term, FullName} | {error, Error}
+%% read a file and check the syntax, i.e. that it contains a correct
+%% Erlang term.
+
+read_file(File, Path) ->
+ case file:path_open(Path, File, [read]) of
+ {ok, Stream, FullName} ->
+ Return = case systools_lib:read_term_from_stream(Stream, File) of
+ {ok, Term} ->
+ {ok, Term, FullName};
+ Other ->
+ Other
+ end,
+ file:close(Stream),
+ Return;
+ _Other ->
+ {error, {not_found, File}}
+ end.
+
+del_file(File) -> file:delete(File).
+
+dirp(Dir) ->
+ case file:read_file_info(Dir) of
+ {ok, FileInfo} -> FileInfo#file_info.type == directory;
+ _ -> false
+ end.
+
+%% Create the include path. Assumptions about the code path is done
+%% and an include directory is added.
+%% Add the official include dir for each found application first in
+%% path !!
+%% If .../ebin exists in a path an .../include directory is assumed to
+%% exist at the same level. If .../ebin is not existing the .../include
+%% directory is assumed anyhow.
+%% Local includes are added for each application later on.
+
+create_include_path(Appls, Path) ->
+ FoundAppDirs = map(fun({_,A}) -> A#application.dir end, Appls),
+ map(fun(Dir) ->
+ case reverse(filename:split(Dir)) of
+ ["ebin"|D] ->
+ filename:join(reverse(D) ++ ["include"]);
+ _ ->
+ filename:join(Dir, "include")
+ end
+ end,
+ FoundAppDirs ++ no_dupl(Path, FoundAppDirs)).
+
+no_dupl([Dir|Path], FoundAppDirs) ->
+ case member(Dir, FoundAppDirs) of
+ true ->
+ no_dupl(Path, FoundAppDirs);
+ _ ->
+ [Dir|no_dupl(Path, FoundAppDirs)]
+ end;
+no_dupl([], _) ->
+ [].
+
+is_app_type(permanent) -> true;
+is_app_type(transient) -> true;
+is_app_type(temporary) -> true;
+is_app_type(none) -> true;
+is_app_type(load) -> true;
+is_app_type(_) -> false.
+
+% check if a term is a string.
+
+string_p([H|T]) when is_integer(H), H >= $ , H < 255 ->
+ string_p(T);
+string_p([$\n|T]) -> string_p(T);
+string_p([$\r|T]) -> string_p(T);
+string_p([$\t|T]) -> string_p(T);
+string_p([$\v|T]) -> string_p(T);
+string_p([$\b|T]) -> string_p(T);
+string_p([$\f|T]) -> string_p(T);
+string_p([$\e|T]) -> string_p(T);
+string_p([]) -> true;
+string_p(_) -> false.
+
+% check if a term is a list of two tuples with the first
+% element as an atom.
+
+t_list_p([{A,_}|T]) when is_atom(A) -> t_list_p(T);
+t_list_p([]) -> true;
+t_list_p(_) -> false.
+
+% check if a term is a list of atoms or two-tuples with the first
+% element as an atom.
+
+mod_list_p([{A,_}|T]) when is_atom(A) -> mod_list_p(T);
+mod_list_p([A|T]) when is_atom(A) -> mod_list_p(T);
+mod_list_p([]) -> true;
+mod_list_p(_) -> false.
+
+% check if a term is a list of atoms.
+
+a_list_p([A|T]) when is_atom(A) -> a_list_p(T);
+a_list_p([]) -> true;
+a_list_p(_) -> false.
+
+%% Get a key-value tuple flag from a list.
+
+get_flag(F,[{F,D}|_]) -> {F,D};
+get_flag(F,[_|Fs]) -> get_flag(F,Fs);
+get_flag(_,_) -> false.
+
+%% Check Options for make_script
+check_args_script(Args) ->
+ cas(Args,
+ {undef, undef, undef, undef, undef, undef, undef, undef, []}).
+
+cas([], {_Path,_Sil,_Loc,_Test,_Var,_Mach,_Xref,_XrefApps, X}) ->
+ X;
+%%% path ---------------------------------------------------------------
+cas([{path, P} | Args], {Path, Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) when is_list(P) ->
+ case check_path(P) of
+ ok ->
+ cas(Args, {P, Sil, Loc, Test, Var, Mach, Xref, XrefApps,X});
+ error ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps,
+ X++[{path,P}]})
+ end;
+%%% silent -------------------------------------------------------------
+cas([silent | Args], {Path, _Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) ->
+ cas(Args, {Path, silent, Loc, Test, Var, Mach, Xref, XrefApps, X});
+%%% local --------------------------------------------------------------
+cas([local | Args], {Path, Sil, _Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) ->
+ cas(Args, {Path, Sil, local, Test, Var, Mach, Xref, XrefApps, X});
+%%% no_module_tests ----------------------------------------------------
+cas([no_module_tests | Args], {Path, Sil, Loc, _Test, Var, Mach,
+ Xref, XrefApps, X}) ->
+ cas(Args,
+ {Path, Sil, Loc, no_module_tests, Var, Mach, Xref, XrefApps,X});
+%%% variables ----------------------------------------------------------
+cas([{variables, V} | Args], {Path, Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) when is_list(V) ->
+ case check_vars(V) of
+ ok ->
+ cas(Args,
+ {Path, Sil, Loc, Test, V, Mach, Xref, XrefApps, X});
+ error ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps,
+ X++[{variables, V}]})
+ end;
+%%% machine ------------------------------------------------------------
+cas([{machine, M} | Args], {Path, Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) when is_atom(M) ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps, X});
+%%% exref --------------------------------------------------------------
+cas([exref | Args], {Path, Sil, Loc, Test, Var, Mach,
+ _Xref, XrefApps, X}) ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach, exref, XrefApps, X});
+%%% exref Apps ---------------------------------------------------------
+cas([{exref, Apps} | Args], {Path, Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) when is_list(Apps) ->
+ case check_apps(Apps) of
+ ok ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach,
+ Xref, Apps, X});
+ error ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X++[{exref, Apps}]})
+ end;
+%%% outdir Dir ---------------------------------------------------------
+cas([{outdir, Dir} | Args], {Path, Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) when is_list(Dir) ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps, X});
+%%% otp_build (secret, not documented) ---------------------------------
+cas([otp_build | Args], {Path, Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps, X});
+%%% ERROR --------------------------------------------------------------
+cas([Y | Args], {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps, X}) ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps,X++[Y]}).
+
+
+
+%% Check Options for make_tar
+check_args_tar(Args) ->
+ cat(Args, {undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, []}).
+
+cat([], {_Path,_Sil,_Dirs,_Erts,_Test,_Var,_VarTar,_Mach,_Xref,_XrefApps, X}) ->
+ X;
+%%% path ---------------------------------------------------------------
+cat([{path, P} | Args], {Path, Sil, Dirs, Erts, Test,
+ Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(P) ->
+ case check_path(P) of
+ ok ->
+ cat(Args, {P, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+ error ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test,
+ Var, VarTar, Mach, Xref, XrefApps, X++[{path,P}]})
+ end;
+%%% silent -------------------------------------------------------------
+cat([silent | Args], {Path, _Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
+ cat(Args, {Path, silent, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+%%% dirs ---------------------------------------------------------------
+cat([{dirs, D} | Args], {Path, Sil, Dirs, Erts, Test,
+ Var, VarTar, Mach, Xref, XrefApps, X}) ->
+ case check_dirs(D) of
+ ok ->
+ cat(Args, {Path, Sil, D, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+ error ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test,
+ Var, VarTar, Mach, Xref, XrefApps, X++[{dirs, D}]})
+ end;
+%%% erts ---------------------------------------------------------------
+cat([{erts, E} | Args], {Path, Sil, Dirs, _Erts, Test,
+ Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(E)->
+ cat(Args, {Path, Sil, Dirs, E, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+%%% no_module_tests ----------------------------------------------------
+cat([no_module_tests | Args], {Path, Sil, Dirs, Erts, _Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
+ cat(Args, {Path, Sil, Dirs, Erts, no_module_tests, Var, VarTar, Mach,
+ Xref, XrefApps, X});
+%%% variables ----------------------------------------------------------
+cat([{variables, V} | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(V) ->
+ case check_vars(V) of
+ ok ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, V, VarTar, Mach, Xref, XrefApps, X});
+ error ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach,
+ Xref, XrefApps, X++[{variables, V}]})
+ end;
+%%% var_tar ------------------------------------------------------------
+cat([{var_tar, VT} | Args], {Path, Sil, Dirs, Erts, Test,
+ Var, _VarTar, Mach, Xref, XrefApps, X}) when VT == include ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, include, Mach, Xref, XrefApps, X});
+cat([{var_tar, VT} | Args], {Path, Sil, Dirs, Erts, Test,
+ Var, _VarTar, Mach, Xref, XrefApps, X}) when VT == ownfile ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, ownfile, Mach, Xref, XrefApps, X});
+cat([{var_tar, VT} | Args], {Path, Sil, Dirs, Erts, Test,
+ Var, _VarTar, Mach, Xref, XrefApps, X}) when VT == omit ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, omit, Mach, Xref, XrefApps, X});
+%%% machine ------------------------------------------------------------
+cat([{machine, M} | Args], {Path, Sil, Dirs, Erts, Test,
+ Var, VarTar, Mach, Xref, XrefApps, X}) when is_atom(M) ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+%%% exref --------------------------------------------------------------
+cat([exref | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, _Xref, XrefApps, X}) ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, exref, XrefApps, X});
+%%% exref Apps ---------------------------------------------------------
+cat([{exref, Apps} | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(Apps) ->
+ case check_apps(Apps) of
+ ok ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach,
+ Xref, Apps, X});
+ error ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach,
+ Xref, XrefApps, X++[{exref, Apps}]})
+ end;
+%%% outdir Dir ---------------------------------------------------------
+cat([{outdir, Dir} | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(Dir) ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach,
+ Xref, XrefApps, X});
+%%% otp_build (secret, not documented) ---------------------------------
+cat([otp_build | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+%%% ERROR --------------------------------------------------------------
+cat([Y | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X++[Y]}).
+
+check_path([]) ->
+ ok;
+check_path([H|T]) when is_list(H) ->
+ check_path(T);
+check_path([_H|_T]) ->
+ error.
+
+check_dirs([]) ->
+ ok;
+check_dirs([H|T]) when is_atom(H) ->
+ check_dirs(T);
+check_dirs([_H|_T]) ->
+ error.
+
+check_vars([]) ->
+ ok;
+check_vars([{Name, Dir} | T]) ->
+ if
+ is_atom(Name), is_list(Dir) ->
+ check_vars(T);
+ is_list(Name), is_list(Dir) ->
+ check_vars(T);
+ true ->
+ error
+ end;
+check_vars(_) ->
+ error.
+
+check_apps([]) ->
+ ok;
+check_apps([H|T]) when is_atom(H) ->
+ check_apps(T);
+check_apps(_) ->
+ error.
+
+
+%% Format error
+
+format_error(badly_formatted_release) ->
+ io_lib:format("Syntax error in the release file~n",[]);
+format_error({illegal_name, Name}) ->
+ io_lib:format("Illegal name (~p) in the release file~n",[Name]);
+format_error({illegal_form, Form}) ->
+ io_lib:format("Illegal tag in the release file: ~p~n",[Form]);
+format_error({missing_parameter,Par}) ->
+ io_lib:format("Missing parameter (~p) in the release file~n",[Par]);
+format_error({illegal_applications,Names}) ->
+ io_lib:format("Illegal applications in the release file: ~p~n",
+ [Names]);
+format_error({missing_mandatory_app,Names}) ->
+ io_lib:format("Mandatory applications (~p) must be specified in the release file~n",
+ [Names]);
+format_error({duplicate_register,Dups}) ->
+ io_lib:format("Duplicated register names: ~n~s",
+ [map(fun({{Reg,App1,_,_},{Reg,App2,_,_}}) ->
+ io_lib:format("\t~p registered in ~p and ~p~n",
+ [Reg,App1,App2])
+ end, Dups)]);
+format_error({undefined_applications,Apps}) ->
+ io_lib:format("Undefined applications: ~p~n",[Apps]);
+format_error({duplicate_modules,Dups}) ->
+ io_lib:format("Duplicated modules: ~n~s",
+ [map(fun({{Mod,_,App1,_,_},{Mod,_,App2,_,_}}) ->
+ io_lib:format("\t~p specified in ~p and ~p~n",
+ [Mod,App1,App2])
+ end, Dups)]);
+format_error({included_and_used, Dups}) ->
+ io_lib:format("Applications both used and included: ~p~n",[Dups]);
+format_error({duplicate_include, Dups}) ->
+ io_lib:format("Duplicated application included: ~n~s",
+ [map(fun({{Name,App1,_,_},{Name,App2,_,_}}) ->
+ io_lib:format("\t~p included in ~p and ~p~n",
+ [Name,App1,App2])
+ end, Dups)]);
+format_error({modules,ModErrs}) ->
+ format_errors(ModErrs);
+format_error({circular_dependencies,Apps}) ->
+ io_lib:format("Circular dependencies among applications: ~p~n",[Apps]);
+format_error({not_found,File}) ->
+ io_lib:format("File not found: ~p~n",[File]);
+format_error({parse,File,{Line,Mod,What}}) ->
+ Str = Mod:format_error(What),
+ io_lib:format("~s:~p: ~s\n",[File, Line, Str]);
+format_error({read,File}) ->
+ io_lib:format("Cannot read ~p~n",[File]);
+format_error({open,File,Error}) ->
+ io_lib:format("Cannot open ~p - ~s~n",
+ [File,file:format_error(Error)]);
+format_error({tar_error,What}) ->
+ form_tar_err(What);
+format_error(ListOfErrors) when is_list(ListOfErrors) ->
+ format_errors(ListOfErrors);
+format_error(E) -> io_lib:format("~p~n",[E]).
+
+format_errors(ListOfErrors) ->
+ map(fun({error,E}) -> form_err(E);
+ (E) -> form_err(E)
+ end, ListOfErrors).
+
+form_err({bad_application_name,{Name,Found}}) ->
+ io_lib:format("~p: Mismatched application id: ~p~n",[Name,Found]);
+form_err({error_reading, {Name, What}}) ->
+ io_lib:format("~p: ~s~n",[Name,form_reading(What)]);
+form_err({module_not_found,App,Mod}) ->
+ io_lib:format("~p: Module (~p) not found~n",[App,Mod]);
+form_err({{vsn_diff,File},{Mod,Vsn,App,_,_}}) ->
+ io_lib:format("~p: Module (~p) version (~p) differs in file ~p~n",
+ [App,Mod,Vsn,File]);
+form_err({error_add_appl, {Name, {tar_error, What}}}) ->
+ io_lib:format("~p: ~s~n",[Name,form_tar_err(What)]);
+form_err(E) ->
+ io_lib:format("~p~n",[E]).
+
+form_reading({not_found,File}) ->
+ io_lib:format("File not found: ~p~n",[File]);
+form_reading({application_vsn, {Name,Vsn}}) ->
+ io_lib:format("Application ~s with version ~p not found~n",[Name, Vsn]);
+form_reading({parse,File,{Line,Mod,What}}) ->
+ Str = Mod:format_error(What),
+ io_lib:format("~s:~p: ~s\n",[File, Line, Str]);
+form_reading({read,File}) ->
+ io_lib:format("Cannot read ~p~n",[File]);
+form_reading({{bad_param, P},_}) ->
+ io_lib:format("Bad parameter in .app file: ~p~n",[P]);
+form_reading({{missing_param,P},_}) ->
+ io_lib:format("Missing parameter in .app file: ~p~n",[P]);
+form_reading({badly_formatted_application,_}) ->
+ io_lib:format("Syntax error in .app file~n",[]);
+form_reading({override_include,Apps}) ->
+ io_lib:format("Tried to include not (in .app file) specified applications: ~p~n",
+ [Apps]);
+form_reading({no_valid_version, {{_, SVsn}, {_, File, FVsn}}}) ->
+ io_lib:format("No valid version (~p) of .app file found. Found file ~p with version ~p~n",
+ [SVsn, File, FVsn]);
+form_reading({parse_error, {File, Line, Error}}) ->
+ io_lib:format("Parse error in file: ~p. Line: ~p Error: ~p; ~n", [File, Line, Error]);
+form_reading(W) ->
+ io_lib:format("~p~n",[W]).
+
+form_tar_err({open, File, Error}) ->
+ io_lib:format("Cannot open tar file ~s - ~p~n",
+ [File, erl_tar:format_error(Error)]);
+form_tar_err({add, File, Error}) ->
+ io_lib:format("Cannot add file ~s to tar file - ~s~n",
+ [File, erl_tar:format_error(Error)]).
+
+%% Format warning
+
+format_warning(Warnings) ->
+ map(fun({warning,W}) -> form_warn(W) end, Warnings).
+
+form_warn({source_not_found,{Mod,_,App,_,_}}) ->
+ io_lib:format("*WARNING* ~p: Source code not found: ~p.erl~n",
+ [App,Mod]);
+form_warn({{parse_error, File},{_,_,App,_,_}}) ->
+ io_lib:format("*WARNING* ~p: Parse error: ~p~n",
+ [App,File]);
+form_warn({obj_out_of_date,{Mod,_,App,_,_}}) ->
+ io_lib:format("*WARNING* ~p: Object code (~p) out of date~n",[App,Mod]);
+form_warn({exref_undef, Undef}) ->
+ F = fun({M,F,A}) ->
+ io_lib:format("*WARNING* Undefined function ~p:~p/~p~n",
+ [M,F,A])
+ end,
+ map(F, Undef);
+form_warn(What) ->
+ io_lib:format("*WARNING* ~p~n", [What]).
diff --git a/lib/sasl/src/systools_rc.erl b/lib/sasl/src/systools_rc.erl
new file mode 100644
index 0000000000..23d1a52b66
--- /dev/null
+++ b/lib/sasl/src/systools_rc.erl
@@ -0,0 +1,1044 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(systools_rc).
+-export([translate_scripts/3, translate_scripts/4, format_error/1]).
+
+-include("systools.hrl").
+
+%%-----------------------------------------------------------------
+%% High-level
+%% ==========
+%% mnesia_backup (not yet implemented)
+%% {update, Mod, Change, PrePurge, PostPurge, [Mod]}
+%% {update, Mod, Timeout, Change, PrePurge, PostPurge, [Mod]}
+%% {update, Mod, ModType, , Change, PrePurge, PostPurge, [Mod]}
+%% {update, Mod, ModType, Timeout, Change, PrePurge, PostPurge, [Mod]}
+%% {load_module, Mod, PrePurge, PostPurge, [Mod]}
+%% {add_module, Mod}
+%% {add_module, Mod, [Mod]}
+%% {remove_module, Mod, PrePurge, PostPurge, [Mod]}
+%% {restart_application, Appl}
+%% {add_application, Appl}
+%% {remove_application, Appl}
+%%
+%% Low-level
+%% =========
+%% {load_object_code, {Lib, LibVsn, Mods}}
+%% point_of_no_return
+%% {load, {Mod, PrePurge, PostPurge}}
+%% {remove, {Mod, PrePurge, PostPurge}}
+%% {purge, Mods}
+%% {suspend, Mods}
+%% {resume, Mods}
+%% {code_change, [{Mod, Extra}]}
+%% {code_change, Mode, [{Mod, Extra}]}
+%% {stop, Mods}
+%% {start, Mods}
+%% {sync_nodes, Id, {M, F, A}}
+%% {sync_nodes, Id, Nodes}
+%% {apply, {M, F, A}}
+%% restart_new_emulator
+%%-----------------------------------------------------------------
+
+%% High-level instructions that contain dependencies
+%%
+-define(DEP_INSTRS, [update, load_module, add_module, remove_module]).
+
+%%-----------------------------------------------------------------
+%% translate_scripts(Scripts, Appls, PreAppls) -> Res
+%% Mode = up | dn
+%% Scripts = [AppupScript]
+%% Appls = PreAppls = [#application]
+%% Res = {ok, LowLevelScript} | {error, ?MODULE, Reason}
+%%-----------------------------------------------------------------
+translate_scripts(Scripts, Appls, PreAppls) ->
+ translate_scripts(up, Scripts, Appls, PreAppls).
+
+translate_scripts(Mode, Scripts, Appls, PreAppls) ->
+ Scripts2 = expand_scripts(Scripts),
+ case catch do_translate_scripts(Mode, Scripts2, Appls, PreAppls) of
+ {ok, NewScript} -> {ok, NewScript};
+ {error, Reason} -> {error, ?MODULE, Reason};
+ {'EXIT', Reason} -> {error, ?MODULE, Reason}
+ end.
+
+expand_scripts([Script|Scripts]) ->
+ [expand_script(Script)|expand_scripts(Scripts)];
+expand_scripts([]) ->
+ [].
+
+expand_script([I|Script]) ->
+ I2 = case I of
+ {load_module, Mod} ->
+ {load_module, Mod, brutal_purge, brutal_purge, []};
+ {load_module, Mod, Mods} when is_list(Mods) ->
+ {load_module, Mod, brutal_purge, brutal_purge, Mods};
+ {update, Mod} ->
+ {update, Mod, soft, brutal_purge, brutal_purge, []};
+ {update, Mod, supervisor} ->
+ {update, Mod, static, default, {advanced,[]},
+ brutal_purge, brutal_purge, []};
+ {update, Mod, Change} when is_tuple(Change) ->
+ {update, Mod, Change, brutal_purge, brutal_purge, []};
+ {update, Mod, Change} when Change==soft ->
+ {update, Mod, Change, brutal_purge, brutal_purge, []};
+ {update, Mod, Mods} when is_list(Mods) ->
+ {update, Mod, soft, brutal_purge, brutal_purge, Mods};
+ {update, Mod, Change, Mods} when is_tuple(Change),
+ is_list(Mods) ->
+ {update, Mod, Change, brutal_purge,brutal_purge, Mods};
+ {update, Mod, Change, Mods} when Change==soft,
+ is_list(Mods) ->
+ {update, Mod, Change, brutal_purge,brutal_purge, Mods};
+ {delete_module, Mod} ->
+ [{remove, {Mod, brutal_purge, brutal_purge}},
+ {purge, [Mod]}];
+ _ ->
+ I
+ end,
+ if
+ is_list(I2) ->
+ I2 ++ expand_script(Script);
+ true ->
+ [I2|expand_script(Script)]
+ end;
+expand_script([]) ->
+ [].
+
+do_translate_scripts(Mode, Scripts, Appls, PreAppls) ->
+ MergedScript = merge_scripts(Scripts),
+ translate_merged_script(Mode, MergedScript, Appls, PreAppls).
+
+%%-----------------------------------------------------------------
+%% All check_ functions performs checks, and throws {error, Reason}
+%% (or fails) in case of error. Called functions may throw error or
+%% fail. The script is split into instructions before and after
+%% point_of_no_return. In Before, only load_object_code and apply are
+%% allowed.
+%% %%-----------------------------------------------------------------
+translate_merged_script(Mode, Script, Appls, PreAppls) ->
+ check_syntax(Script),
+ Script1 = normalize_instrs(Script),
+ {Before, After} = split_script(Script1),
+ check_script(Before, After),
+
+ {Before1, After1} = translate_independent_instrs(Before, After, Appls, PreAppls),
+ {Before2, After2} = translate_dependent_instrs(Mode, Before1, After1,
+ Appls),
+ Before3 = merge_load_object_code(Before2),
+ NewScript = Before3 ++ [point_of_no_return | After2],
+ check_syntax(NewScript),
+ {ok, NewScript}.
+
+%%-----------------------------------------------------------------
+%% SPLIT AND MERGE
+%%-----------------------------------------------------------------
+
+%%-----------------------------------------------------------------
+%% merge_scripts(Scripts) -> Script
+%%
+%% Splits each script into before and after, and merges the before and
+%% after parts.
+%%-----------------------------------------------------------------
+merge_scripts(Scripts) ->
+ {Before, After} =
+ lists:foldl(
+ fun(Script, {B1, A1}) ->
+ {B2, A2} = split_script(Script),
+ {B1 ++ B2, A1 ++ A2}
+ end, {[], []},Scripts),
+ Before ++ [point_of_no_return | After].
+
+%%-----------------------------------------------------------------
+%% split_script(Script) -> {Before, After}
+%%
+%% Splits the script into instructions before and after
+%% point_of_no_return. Puts all load_object_code instructions in
+%% Before. Checks that there is at most one point_of_no_return.
+%% Makes sure that if there was a point_of_no_return, only apply and
+%% load_object_code are before the point_of_no_return.
+%%-----------------------------------------------------------------
+split_script(Script) ->
+ {Before, After} = split_instrs(Script),
+ lists:foreach(
+ fun({load_object_code, _}) -> ok;
+ ({apply, _}) -> ok;
+ (Instruction) ->
+ throw({error, {bad_op_before_point_of_no_return,
+ Instruction}})
+ end, Before),
+ {Found, Rest} = split(fun({load_object_code, _}) -> true;
+ (_) -> false
+ end, After),
+ {Before ++ Found, Rest}.
+
+%% split_instrs(Script) -> {Before, After} Split the
+%% instructions into the set of those that appear before
+%% point_of_no_return, and the set of those that appear after. If
+%% there is no point_of_no_return instruction {[], Script} is
+%% returned.
+split_instrs(Script) ->
+ split_instrs(Script, []).
+split_instrs([point_of_no_return | T], Before) ->
+ case lists:member(point_of_no_return, T) of
+ true -> throw({error, too_many_point_of_no_return});
+ false -> {lists:reverse(Before), T}
+ end;
+split_instrs([H | T], Before) ->
+ split_instrs(T, [H | Before]);
+split_instrs([], Before) ->
+ {[], lists:reverse(Before)}.
+
+%%-----------------------------------------------------------------
+%% CHECKS
+%%-----------------------------------------------------------------
+
+check_script(Before, After) ->
+ check_load(Before, After),
+ check_suspend_resume(After),
+ check_start_stop(After).
+
+%%-----------------------------------------------------------------
+%% Checks that each load has a corresponding load_object_code.
+%%-----------------------------------------------------------------
+check_load(Before, After) ->
+ lists:foreach(
+ fun({load, {Mod, _, _}}) ->
+ case find_object_code(Mod, Before) of
+ true -> ok;
+ false -> throw({error, {no_object_code, Mod}})
+ end;
+ (_) -> ok
+ end, After).
+
+find_object_code(Mod, [{load_object_code, {_, _, Mods}} | T]) ->
+ case lists:member(Mod, Mods) of
+ true -> true;
+ false -> find_object_code(Mod, T)
+ end;
+find_object_code(Mod, [_|T]) ->
+ find_object_code(Mod, T);
+find_object_code(_Mod, []) ->
+ false.
+
+%%-----------------------------------------------------------------
+%% Checks that all suspended Mods are resumed, and that none are
+%% resumed/code_changed but not suspended.
+%%-----------------------------------------------------------------
+check_suspend_resume(Script) ->
+ Suspended = lists:map(fun({Mod, _Timeout}) -> Mod;
+ (Mod) -> Mod
+ end,
+ lists:flatten([X || {suspend, X} <- Script])),
+ Resumed = lists:flatten([X || {resume, X} <- Script]),
+ CodeChanged = lists:flatten([X || {code_change, _, {X, _}} <- Script]),
+ case difference(Suspended, Resumed) of
+ [] -> ok;
+ S2 -> throw({error, {suspended_not_resumed, S2}})
+ end,
+ case difference(Resumed, Suspended) of
+ [] -> ok;
+ R2 -> throw({error, {resumed_not_suspended, R2}})
+ end,
+ case difference(CodeChanged, Suspended) of
+ [] -> ok;
+ C2 -> throw({error, {code_change_not_suspended, C2}})
+ end.
+
+%%-----------------------------------------------------------------
+%% Checks that all stops are started, and that all starts are
+%% stopped.
+%%-----------------------------------------------------------------
+check_start_stop(Script) ->
+ Start = lists:flatten([X || {start, X} <- Script]),
+ Stop = lists:flatten([X || {stop, X} <- Script]),
+ case difference(Start, Stop) of
+ [] -> ok;
+ S2 -> throw({error, {start_not_stop, S2}})
+ end,
+ case difference(Stop, Start) of
+ [] -> ok;
+ S3 -> throw({error, {stop_not_start, S3}})
+ end.
+
+
+%%-----------------------------------------------------------------
+%% NORMALISATION
+%%-----------------------------------------------------------------
+%%-----------------------------------------------------------------
+%% Normalize those instructions that have variants (update and
+%% add_module).
+%%-----------------------------------------------------------------
+normalize_instrs(Script) ->
+ lists:map(fun({update, Mod, Change, PrePurge, PostPurge, Mods}) ->
+ {update, Mod, dynamic, default, Change, PrePurge,
+ PostPurge, Mods};
+ ({update, Mod, Timeout, Change, PrePurge, PostPurge,
+ Mods}) ->
+ {update, Mod, dynamic, Timeout, Change, PrePurge,
+ PostPurge, Mods};
+ ({add_module, Mod}) ->
+ {add_module, Mod, []};
+ (I) ->
+ I
+ end, Script).
+
+%%-----------------------------------------------------------------
+%% TRANSLATION OF INDEPENDENT INSTRUCTIONS
+%%-----------------------------------------------------------------
+
+%% translate_independent_instrs(Before, After, Appls, PreAppls) ->
+%% {NBefore, NAfter}
+%%
+translate_independent_instrs(Before, After, Appls, PreAppls) ->
+ After1 = translate_application_instrs(After, Appls, PreAppls),
+ translate_add_module_instrs(Before, After1).
+
+%%-----------------------------------------------------------------
+%% Translates add_application, remove_application and restart_application
+%% into add_module, remove, purge and apply.
+%%-----------------------------------------------------------------
+translate_application_instrs(Script, Appls, PreAppls) ->
+ %% io:format("Appls ~n~p~n",[Appls]),
+ L = lists:map(
+ fun({add_application, Appl}) ->
+ case lists:keysearch(Appl, #application.name, Appls) of
+ {value, Application} ->
+ Mods =
+ remove_vsn(Application#application.modules),
+ [{add_module, M, []} || M <- Mods] ++
+ [{apply, {application, start,
+ [Appl, permanent]}}];
+ false ->
+ throw({error, {no_such_application, Appl}})
+ end;
+
+ ({remove_application, Appl}) ->
+ case lists:keysearch(Appl, #application.name, Appls) of
+ {value, _Application} ->
+ throw({error, {removed_application_present,
+ Appl}});
+ false ->
+ ignore
+ end,
+ case lists:keysearch(Appl, #application.name, PreAppls) of
+ {value, RemApplication} ->
+ Mods = remove_vsn(RemApplication#application.modules),
+ [{apply, {application, stop, [Appl]}}] ++
+ [{remove, {M, brutal_purge, brutal_purge}} || M <- Mods] ++
+ [{purge, Mods},
+ {apply, {application, unload, [Appl]}}];
+ false ->
+ throw({error, {no_such_application, Appl}})
+ end;
+ ({restart_application, Appl}) ->
+ case lists:keysearch(Appl, #application.name, PreAppls) of
+ {value, PreApplication} ->
+ PreMods =
+ remove_vsn(PreApplication#application.modules),
+
+ case lists:keysearch(Appl, #application.name, Appls) of
+ {value, PostApplication} ->
+ PostMods =
+ remove_vsn(PostApplication#application.modules),
+
+ [{apply, {application, stop, [Appl]}}] ++
+ [{remove, {M, brutal_purge, brutal_purge}} || M <- PreMods] ++
+ [{purge, PreMods}] ++
+ [{add_module, M, []} || M <- PostMods] ++
+ [{apply, {application, start,
+ [Appl, permanent]}}];
+ false ->
+ throw({error, {no_such_application, Appl}})
+ end;
+
+ false ->
+ throw({error, {no_such_application, Appl}})
+ end;
+ (X) -> X
+ end, Script),
+ lists:flatten(L).
+
+remove_vsn(Mods) ->
+ lists:map(fun({Mod, _Vsn}) -> Mod;
+ (Mod) -> Mod
+ end, Mods).
+
+%%-----------------------------------------------------------------
+%% Translates add_module into load_module (high-level transformation)
+%%-----------------------------------------------------------------
+translate_add_module_instrs(Before, After) ->
+ NAfter = lists:map(
+ fun({add_module, Mod, Mods}) ->
+ %% Purge method really doesn't matter. Module
+ %% is new.
+ {load_module, Mod, brutal_purge, brutal_purge, Mods};
+ (I) ->
+ I
+ end, After),
+ {Before, NAfter}.
+
+
+%%-----------------------------------------------------------------
+%% TRANSLATION OF INSTRUCTIONS WITH DEPENDENCIES
+%%-----------------------------------------------------------------
+
+%%-----------------------------------------------------------------
+%% Translates update, load_module and remove_module, and reorder the
+%% instructions according to dependencies. Leaves other instructions
+%% unchanged.
+%%-----------------------------------------------------------------
+translate_dependent_instrs(Mode, Before, After, Appls) ->
+ %% G is the total dependency graph, WCs is the decomposition of
+ %% the vertices (lists of vertices) of G.
+ G = make_dependency_graph(After),
+ WCs = digraph_utils:components(G),
+ {NBefore, NAfter} = translate_dep_loop(G, WCs, After, Appls,
+ [], [], Mode),
+ digraph:delete(G),
+ {Before ++ NBefore, NAfter}.
+
+translate_dep_loop(G, WCs, [I| Is], Appls, Before, After, Mode)
+ when is_tuple(I), size(I) > 1 ->
+ IName = element(1, I),
+ case lists:member(IName, ?DEP_INSTRS) of
+ true ->
+ Mod = element(2, I),
+ DepIs = get_dependent_instructions(G, WCs, Mod),
+ {B2, A2} = translate_dep_to_low(Mode, DepIs, Appls),
+ RemIs = difference([I| Is], DepIs),
+ translate_dep_loop(G, WCs, RemIs, Appls, Before ++ B2,
+ After ++ A2, Mode);
+ false ->
+ translate_dep_loop(G, WCs, Is, Appls, Before,
+ After ++ [I], Mode) % hmm
+ end;
+translate_dep_loop(G, WCs, [I| Is], Appls, Before, After, Mode) ->
+ translate_dep_loop(G, WCs, Is, Appls, Before, After ++ [I], Mode); % hmm
+translate_dep_loop(_G, _WCs, [], _Appls, Before, After, _Mode) ->
+ {Before, After}.
+
+
+%%-----------------------------------------------------------------
+%% make_dependency_graph(Instructions) -> graph()
+%%
+%% The return value is a digraph graph(). A vertex is a module name
+%% Mod, and the associated data is {N, I} where I is the corresponding
+%% instruction, and N numbers the instruction in the order given at
+%% input. Only instructions that have dependencies are considered.
+%% %%-----------------------------------------------------------------
+make_dependency_graph(Instructions) ->
+ %% Filter out dependent instructions
+ DepIs = lists:filter(fun(I) when is_tuple(I) ->
+ IName = element(1, I),
+ lists:member(IName, ?DEP_INSTRS);
+ (_) ->
+ false
+ end, Instructions),
+ {VDs, _} = lists:mapfoldl(
+ fun(I, N) ->
+ Mod = element(2, I),
+ Mods = element(size(I), I),
+ {{Mod, Mods, {N, I}}, N+1}
+ end, 1, DepIs),
+ G = digraph:new(),
+ %% Add vertices
+ lists:foreach(
+ fun({Mod, _Mods, Data}) ->
+ case digraph:vertex(G, Mod) of
+ false ->
+ digraph:add_vertex(G, Mod, Data);
+ _ ->
+ throw({error, {muldef_module, Mod}})
+ end
+ end, VDs),
+ %% Add edges
+ lists:foreach(
+ fun({Mod, Mods, _Data}) ->
+ lists:foreach(
+ fun(M) ->
+ case digraph:add_edge(G, Mod, M) of
+ {error, _Reason} ->
+ throw({error, {undef_module, M}});
+ _ ->
+ ok
+ end
+ end, Mods)
+ end, VDs),
+ G.
+
+%% get_dependent_instructions(G, WCs, Mod) -> DepIs
+%%
+%% G is the global dependency graph, WCs are the weak components
+%% (lists of vertices) of G, and Mod is the module for which we will
+%% pick up all instructions that Mod depends on, or that depend on
+%% Mod.
+%%
+get_dependent_instructions(G, WCs, Mod) ->
+ case lists:filter(fun(C) -> lists:member(Mod, C) end, WCs) of
+ [WC] ->
+ %% restrict G to WC
+ H = restriction(WC, G),
+ %% vertices of S are strong components of H
+ S = condensation(H),
+ Ts = digraph_utils:topsort(S),
+ DepIss = lists:map(
+ fun(T) ->
+ NIs = lists:map(
+ fun(V) ->
+ {_, Data} =
+ digraph:vertex(H, V),
+ Data
+ end, T),
+ %% NIs = [{N, I}]
+ SortedNIs = lists:keysort(1, NIs),
+ lists:map(fun({_N, I}) -> I end, SortedNIs)
+ end, Ts),
+ DepIs = lists:flatten(DepIss), % XXX One level flat only
+ digraph:delete(H),
+ digraph:delete(S),
+ DepIs;
+ [] ->
+ throw({error, {undef_module, Mod}});
+ _ ->
+ throw({error, {muldef_module, Mod}})
+ end.
+
+%% translate_dep_to_low(Mode, Instructions, Appls) -> {Before, After}
+%%
+%% Mode = up | dn
+%% Instructions are in order of dependency.
+%% Appls = [#application]
+%%
+%% Instructions translated are: update, load_module, and remove_module
+%%
+%% Before = [{load_object_code, ...}]
+%% After = [{suspend, ...}] ++ CodeInstrs ++ [{resume, ...}]
+%% CodeInstrs = [{load, ...}] ++ [{code_change, ...}] (Mode == up)
+%% = [{code_change, ...}] ++ [{load, ...}] ++
+%% [{code_change, ...}] (Mode == dn)
+%%
+translate_dep_to_low(Mode, Instructions, Appls) ->
+ UpdateMods =
+ filtermap(fun({update, Mod, _, default, _, _, _, _}) ->
+ {true, Mod};
+ ({update, Mod, _, T, _, _, _, _}) ->
+ {true, {Mod, T}};
+ (_) ->
+ false
+ end,
+ Instructions),
+ RevUpdateMods = lists:reverse(UpdateMods),
+
+ %% Processes are suspended in the order of dependency.
+ SuspendInstrs =
+ if
+ UpdateMods == [] -> [];
+ true -> [{suspend, UpdateMods}]
+ end,
+
+
+ %% Processes are resumed in the reversed order of dependency.
+ ResumeInstrs =
+ if
+ UpdateMods == [] -> [];
+ true -> [{resume,
+ lists:map(fun({Mod, _T}) -> Mod;
+ (Mod) -> Mod
+ end, RevUpdateMods)}]
+ end,
+
+ LoadRemoveInstrs =
+ filtermap(fun({update, Mod, _, _, _, PreP, PostP, _}) ->
+ {true, {load, {Mod, PreP, PostP}}};
+ ({load_module, Mod, PreP, PostP, _}) ->
+ {true, {load, {Mod, PreP, PostP}}};
+ ({remove_module, Mod, PreP, PostP, _}) ->
+ {true, {remove, {Mod, PreP, PostP}}};
+ (_) -> false
+ end,
+ Instructions),
+ RevLoadRemoveInstrs = lists:reverse(LoadRemoveInstrs),
+
+ %% The order of loading object code is unimportant. The order
+ %% chosen is the order of dependency.
+ LoadObjCodeInstrs =
+ filtermap(fun({load, {Mod, _, _}}) ->
+ {Lib, LibVsn} = get_lib(Mod, Appls),
+ {true, {load_object_code, {Lib, LibVsn, [Mod]}}};
+ (_) -> false
+ end, LoadRemoveInstrs),
+ if
+ Mode == up ->
+ %% The order of changing code is unimportant (processes
+ %% are suspended). The order chosen is the order of
+ %% dependency.
+ CodeChangeMods =
+ filtermap(fun({update, Mod, _, _,
+ {advanced, Extra}, _, _, _}) ->
+ {true, {Mod, Extra}};
+ (_) ->
+ false
+ end, Instructions),
+ CodeChangeInstrs =
+ if
+ CodeChangeMods == [] -> [];
+ true -> [{code_change, up, CodeChangeMods}]
+ end,
+ %% RevLoadRemoveInstrs: When upgrading modules are loaded
+ %% in the reversed order of dependency.
+ {LoadObjCodeInstrs,
+ SuspendInstrs ++ RevLoadRemoveInstrs ++ CodeChangeInstrs ++
+ ResumeInstrs};
+
+ Mode == dn ->
+ %% PreCodeChangeMods is the list of all modules that have
+ %% to change code *before* the code is loaded (when
+ %% downgrading). The order is not important (processes are
+ %% suspended). The order chosen is the order of
+ %% dependency.
+ PreCodeChangeMods =
+ [{Mod, Extra} ||
+ {update, Mod, dynamic, _, {advanced, Extra}, _, _, _}
+ <- Instructions],
+ PreCodeChangeInstrs =
+ if
+ PreCodeChangeMods == [] -> [];
+ true -> [{code_change, down, PreCodeChangeMods}]
+ end,
+ %% PostCodeChangeMods is the list of all modules that have
+ %% to change code *after* the code is loaded (when
+ %% downgrading). The order is not important (processes are
+ %% suspended). The order chosen is the order of
+ %% dependency.
+ PostCodeChangeMods =
+ [{Mod, Extra} ||
+ {update, Mod, static, _, {advanced, Extra}, _, _, _}
+ <- Instructions],
+ PostCodeChangeInstrs =
+ if
+ PostCodeChangeMods == [] -> [];
+ true -> [{code_change, down, PostCodeChangeMods}]
+ end,
+ %% LoadRemoveInstrs: When downgrading modules are loaded
+ %% in the order of dependency.
+ {LoadObjCodeInstrs,
+ SuspendInstrs ++ PreCodeChangeInstrs ++
+ LoadRemoveInstrs ++ PostCodeChangeInstrs ++ ResumeInstrs}
+ end.
+
+get_lib(Mod, [#application{name = Name, vsn = Vsn, modules = Modules} | T]) ->
+ %% Module = {Mod, Vsn} | Mod
+ case lists:keysearch(Mod, 1, Modules) of
+ {value, _} ->
+ {Name, Vsn};
+ false ->
+ case lists:member(Mod, Modules) of
+ true -> {Name, Vsn};
+ false -> get_lib(Mod, T)
+ end
+ end;
+get_lib(Mod, []) ->
+ throw({error, {no_such_module, Mod}}).
+
+%%-----------------------------------------------------------------
+%% MERGE LOAD_OBJECT_CODE
+%%-----------------------------------------------------------------
+%%-----------------------------------------------------------------
+%% Merge load_object_code instructions into one load_object_code
+%% instruction per lib (optimization). Order is preserved.
+%%-----------------------------------------------------------------
+merge_load_object_code(Before) ->
+ {Found, Rest} = split(fun({load_object_code, _}) -> true;
+ (_) -> false
+ end, Before),
+ mlo(Found) ++ Rest.
+
+mlo([{load_object_code, {Lib, LibVsn, Mods}} | T]) ->
+ {Same, Other} = split(fun({load_object_code, {Lib2, LibVsn2, _Mods2}})
+ when Lib == Lib2, LibVsn == LibVsn2 -> true;
+ ({load_object_code, {Lib2, LibVsn2, _Mods2}})
+ when Lib == Lib2 ->
+ throw({error, {conflicting_versions,
+ Lib, LibVsn, LibVsn2}});
+ (_) -> false
+ end, T),
+ %% io:format("Same = ~p, Other = ~p~n", [Same, Other]),
+ %% foldr to preserver order.
+ OCode0 = lists:foldr(fun({load_object_code, {_, _, Ms}}, Res) ->
+ U = union(Ms, Res),
+ %% io:format("Ms = ~p, Res = ~p, U = ~p~n",
+ %% [Ms, Res, U]),
+ U
+ end, [], Same),
+ OCode1 = union(Mods, OCode0), % preserve order
+ %% io:format("OCode0 = ~p, OCode1 = ~p~n", [OCode0, OCode1]),
+ [{load_object_code, {Lib, LibVsn, OCode1}} | mlo(Other)];
+mlo([]) -> [].
+
+%%-----------------------------------------------------------------
+%% SYNTAX CHECK
+%%-----------------------------------------------------------------
+%%-----------------------------------------------------------------
+%% Checks the syntax of all instructions.
+%%-----------------------------------------------------------------
+check_syntax([H|T]) ->
+ check_op(H),
+ check_syntax(T);
+check_syntax([]) -> ok.
+
+check_op(mnesia_backup) ->
+ throw({error, {not_yet_implemented, mnesia_backup}});
+check_op({update, Mod, Change, PrePurge, PostPurge, Mods}) ->
+ check_mod(Mod),
+ check_change(Change),
+ check_purge(PrePurge),
+ check_purge(PostPurge),
+ check_list(Mods),
+ lists:foreach(fun(M) -> check_mod(M) end, Mods);
+check_op({update, Mod, Timeout, Change, PrePurge, PostPurge, Mods}) ->
+ check_mod(Mod),
+ check_timeout(Timeout),
+ check_change(Change),
+ check_purge(PrePurge),
+ check_purge(PostPurge),
+ check_list(Mods),
+ lists:foreach(fun(M) -> check_mod(M) end, Mods);
+check_op({update, Mod, ModType, Timeout, Change, PrePurge, PostPurge,
+ Mods}) ->
+ check_mod(Mod),
+ check_mod_type(ModType),
+ check_timeout(Timeout),
+ check_change(Change),
+ check_purge(PrePurge),
+ check_purge(PostPurge),
+ check_list(Mods),
+ lists:foreach(fun(M) -> check_mod(M) end, Mods);
+check_op({load_module, Mod, PrePurge, PostPurge, Mods}) ->
+ check_mod(Mod),
+ check_purge(PrePurge),
+ check_purge(PostPurge),
+ check_list(Mods),
+ lists:foreach(fun(M) -> check_mod(M) end, Mods);
+check_op({add_module, Mod}) ->
+ check_mod(Mod);
+check_op({add_module, Mod, Mods}) ->
+ check_mod(Mod),
+ check_list(Mods),
+ lists:foreach(fun(M) -> check_mod(M) end, Mods);
+check_op({remove_module, Mod, PrePurge, PostPurge, Mods}) ->
+ check_mod(Mod),
+ check_purge(PrePurge),
+ check_purge(PostPurge),
+ check_list(Mods),
+ lists:foreach(fun(M) -> check_mod(M) end, Mods);
+check_op({remove_application, Appl}) ->
+ check_appl(Appl);
+check_op({add_application, Appl}) ->
+ check_appl(Appl);
+check_op({restart_application, Appl}) ->
+ check_appl(Appl);
+check_op(restart) -> ok;
+check_op(reboot) -> ok;
+check_op({load_object_code, {Lib, LibVsn, Mods}}) ->
+ check_lib(Lib),
+ check_lib_vsn(LibVsn),
+ check_list(Mods),
+ lists:foreach(fun(M) -> check_mod(M) end, Mods);
+check_op(point_of_no_return) -> ok;
+check_op({load, {Mod, PrePurge, PostPurge}}) ->
+ check_mod(Mod),
+ check_purge(PrePurge),
+ check_purge(PostPurge);
+check_op({remove, {Mod, PrePurge, PostPurge}}) ->
+ check_mod(Mod),
+ check_purge(PrePurge),
+ check_purge(PostPurge);
+check_op({purge, Mods}) ->
+ check_list(Mods),
+ lists:foreach(fun(M) -> check_mod(M) end, Mods);
+check_op({suspend, Mods}) ->
+ check_list(Mods),
+ lists:foreach(fun({M,T}) -> check_mod(M), check_timeout(T);
+ (M) -> check_mod(M)
+ end, Mods);
+check_op({resume, Mods}) ->
+ check_list(Mods),
+ lists:foreach(fun(M) -> check_mod(M) end, Mods);
+check_op({code_change, Mods}) ->
+ check_list(Mods),
+ lists:foreach(fun({M, _Extra}) -> check_mod(M);
+ (X) -> throw({error, {bad_code_change, X}})
+ end, Mods);
+check_op({code_change, Mode, Mods}) ->
+ check_list(Mods),
+ check_mode(Mode),
+ lists:foreach(fun({M, _Extra}) -> check_mod(M);
+ (X) -> throw({error, {bad_code_change, X}})
+ end, Mods);
+check_op({stop, Mods}) ->
+ check_list(Mods),
+ lists:foreach(fun(M) -> check_mod(M) end, Mods);
+check_op({start, Mods}) ->
+ check_list(Mods),
+ lists:foreach(fun(M) -> check_mod(M) end, Mods);
+check_op({sync_nodes, _Id, {M, F, A}}) ->
+ check_mod(M),
+ check_func(F),
+ check_args(A);
+check_op({sync_nodes, _Id, Nodes}) ->
+ check_list(Nodes),
+ lists:foreach(fun(Node) -> check_node(Node) end, Nodes);
+check_op({apply, {M, F, A}}) ->
+ check_mod(M),
+ check_func(F),
+ check_args(A);
+check_op(restart_new_emulator) -> ok;
+check_op(X) -> throw({error, {bad_instruction, X}}).
+
+check_mod(Mod) when is_atom(Mod) -> ok;
+check_mod(Mod) -> throw({error, {bad_module, Mod}}).
+
+check_change(soft) -> ok;
+check_change({advanced, _}) -> ok;
+check_change(Change) -> throw({error, {bad_change, Change}}).
+
+check_mod_type(static) -> ok;
+check_mod_type(dynamic) -> ok;
+check_mod_type(ModType) -> throw({error, {bad_mod_type, ModType}}).
+
+check_purge(soft_purge) -> ok;
+check_purge(brutal_purge) -> ok;
+check_purge(Purge) -> throw({error, {bad_purge_method, Purge}}).
+
+check_list(List) when is_list(List) -> ok;
+check_list(List) -> throw({error, {bad_list, List}}).
+
+check_args(Args) when is_list(Args) -> ok;
+check_args(Args) -> throw({error, {bad_args_list, Args}}).
+
+check_node(Node) when is_atom(Node) -> ok;
+check_node(Node) -> throw({error, {bad_node, Node}}).
+
+check_appl(Appl) when is_atom(Appl) -> ok;
+check_appl(Appl) -> throw({error, {bad_application, Appl}}).
+
+check_func(Func) when is_atom(Func) -> ok;
+check_func(Func) -> throw({error, {bad_func, Func}}).
+
+check_lib(Lib) when is_atom(Lib) -> ok;
+check_lib(Lib) -> throw({error, {bad_lib, Lib}}).
+
+check_lib_vsn(LibVsn) when is_list(LibVsn) -> ok;
+check_lib_vsn(LibVsn) -> throw({error, {bad_lib_vsn, LibVsn}}).
+
+check_timeout(default) -> ok;
+check_timeout(infinity) -> ok;
+check_timeout(Int) when is_integer(Int), Int > 0 -> ok;
+check_timeout(T) -> throw({error, {bad_timeout, T}}).
+
+check_mode(up) -> ok;
+check_mode(down) -> ok;
+check_mode(Mode) -> throw({error, {bad_mode, Mode}}).
+
+%%-----------------------------------------------------------------
+%% Format error
+%%-----------------------------------------------------------------
+format_error({bad_op_before_point_of_no_return, Instruction}) ->
+ io_lib:format("Bad instruction ~p~nbefore point_of_no_return~n",
+ [Instruction]);
+format_error({no_object_code, Mod}) ->
+ io_lib:format("No load_object_code found for module: ~p~n", [Mod]);
+format_error({suspended_not_resumed, Mods}) ->
+ io_lib:format("Suspended but not resumed: ~p~n", [Mods]);
+format_error({resumed_not_suspended, Mods}) ->
+ io_lib:format("Resumed but not suspended: ~p~n", [Mods]);
+format_error({code_change_not_suspended, Mods}) ->
+ io_lib:format("Code changed but not suspended: ~p~n", [Mods]);
+format_error({start_not_stop, Mods}) ->
+ io_lib:format("Started but not stopped: ~p~n", [Mods]);
+format_error({stop_not_start, Mods}) ->
+ io_lib:format("Stopped but not started: ~p~n", [Mods]);
+format_error({no_such_application, App}) ->
+ io_lib:format("Started undefined application: ~p~n", [App]);
+format_error({removed_application_present, App}) ->
+ io_lib:format("Removed application present: ~p~n", [App]);
+format_error(dup_mnesia_backup) ->
+ io_lib:format("Duplicate mnesia_backup~n", []);
+format_error(bad_mnesia_backup) ->
+ io_lib:format("mnesia_backup in bad position~n", []);
+format_error({conflicting_versions, Lib, V1, V2}) ->
+ io_lib:format("Conflicting versions for ~p, ~p and ~p~n", [Lib, V1, V2]);
+format_error({no_appl_vsn, Appl}) ->
+ io_lib:format("No version specified for application: ~p~n", [Appl]);
+format_error({no_such_module, Mod}) ->
+ io_lib:format("No such module: ~p~n", [Mod]);
+format_error(too_many_point_of_no_return) ->
+ io_lib:format("Too many point_of_no_return~n", []);
+
+format_error({bad_instruction, X}) ->
+ io_lib:format("Bad instruction: ~p~n", [X]);
+format_error({bad_module, X}) ->
+ io_lib:format("Bad module: ~p(should be atom())~n", [X]);
+format_error({bad_code_change, X}) ->
+ io_lib:format("Bad code_change: ~p(should be {Mod, Extra})~n", [X]);
+format_error({bad_change, X}) ->
+ io_lib:format("Bad change spec: ~p(should be soft | {advanced, E})~n", [X]);
+format_error({bad_mod_type, X}) ->
+ io_lib:format("Bad module type: ~p(should be static | dynamic)~n", [X]);
+format_error({bad_purge_method, X}) ->
+ io_lib:format("Bad purge method: ~p(should be soft_purge | brutal_purge)~n",
+ [X]);
+format_error({bad_list, X}) ->
+ io_lib:format("Bad list: ~p~n", [X]);
+format_error({bad_args_list, X}) ->
+ io_lib:format("Bad argument list: ~p~n", [X]);
+format_error({bad_node, X}) ->
+ io_lib:format("Bad node: ~p(should be atom())~n", [X]);
+format_error({bad_application, X}) ->
+ io_lib:format("Bad application: ~p(should be atom())~n", [X]);
+format_error({bad_func, X}) ->
+ io_lib:format("Bad function: ~p(should be atom())~n", [X]);
+format_error({bad_lib, X}) ->
+ io_lib:format("Bad library: ~p(should be atom())~n", [X]);
+format_error({bad_lib_vsn, X}) ->
+ io_lib:format("Bad library version: ~p(should be string())~n", [X]);
+format_error({bad_timeout, X}) ->
+ io_lib:format("Bad timeout: ~p(should be infinity | int() > 0)~n", [X]);
+
+format_error({undef_module, Mod}) ->
+ io_lib:format("Undefined module: ~p~n", [Mod]);
+format_error({muldef_module, Mod}) ->
+ io_lib:format("Multiply defined module: ~p~n", [Mod]);
+format_error(E) ->
+ io_lib:format("~p~n",[E]).
+
+
+%%-----------------------------------------------------------------
+%% MISC SUPPORT
+%%-----------------------------------------------------------------
+
+%% filtermap(F, List1) -> List2
+%% F(H) -> false | true | {true, Val}
+filtermap(F, List) ->
+ lists:zf(F, List).
+
+%% split(F, List1) -> {List2, List3}
+%% F(H) -> true | false. Preserves order.
+split(Fun, [H | T]) ->
+ {Found, Rest} = split(Fun, T),
+ case Fun(H) of
+ true -> {[H | Found], Rest};
+ false -> {Found, [H | Rest]}
+ end;
+split(_Fun, []) ->
+ {[], []}.
+
+union([H|T], L) ->
+ case lists:member(H, L) of
+ true -> union(T,L);
+ false -> [H | union(T, L)]
+ end;
+union([], L) -> L.
+
+difference([H | T], L) ->
+ case lists:member(H, L) of
+ true -> difference(T, L);
+ false -> [H | difference(T, L)]
+ end;
+difference([], _) -> [].
+
+
+%%-----------------------------------------------------------------
+%% GRAPHS
+%%-----------------------------------------------------------------
+
+%% Additions to digraph and digraph utils.
+%% XXX Should be removed in future versions.
+
+%% This function should be included in digraph_utils.
+
+%% condensation(G) -> graph()
+%%
+%% Given a graph G, returns a new graph H where each vertex V in H is
+%% a strong component of G, and where there is an edge from V1 to V2
+%% in H if there are members of v1 and v2 of V1 and V2, respectively,
+%% such that there is an edge from v1 to v2 in G.
+%%
+condensation(G) ->
+ H = digraph:new(),
+ HVs = digraph_utils:strong_components(G),
+ %% Add all vertices
+ lists:foreach(fun(HV) -> digraph:add_vertex(H, HV) end, HVs),
+ %% Add edges
+ lists:foreach(
+ fun(HV1) ->
+ GRs = digraph_utils:reachable(HV1, G),
+ lists:foreach(
+ fun(HV2) ->
+ if
+ HV1 /= HV2 ->
+ case lists:member(hd(HV2), GRs) of
+ true ->
+ digraph:add_edge(H, HV1, HV2);
+ _ ->
+ ok
+ end;
+ true ->
+ ok
+ end
+ end, HVs)
+ end, HVs),
+ H.
+
+
+%% This function should be included in digraph.
+
+%% restriction(Rs, G) -> graph()
+%%
+%% Given a graph G, returns a new graph H that is the restriction of
+%% G to the vertices Rs.
+%%
+restriction(Rs, G) ->
+ H = digraph:new(),
+ %% Add vertices
+ lists:foreach(
+ fun(R) ->
+ case digraph:vertex(G, R) of
+ {R, Data} ->
+ digraph:add_vertex(H, R, Data);
+ _ ->
+ ok
+ end
+ end, Rs),
+ %% Add edges
+ GEs = digraph:edges(G),
+ lists:foreach(
+ fun(GE) ->
+ {_, GV1, GV2, GData} = digraph:edge(G, GE),
+ case {digraph:vertex(H, GV1), digraph:vertex(H, GV2)} of
+ {{GV1, _}, {GV2, _}} ->
+ digraph:add_edge(H, GE, GV1, GV2, GData);
+ _ ->
+ ok
+ end
+ end, GEs),
+ H.
+
+
diff --git a/lib/sasl/src/systools_relup.erl b/lib/sasl/src/systools_relup.erl
new file mode 100644
index 0000000000..177d50be80
--- /dev/null
+++ b/lib/sasl/src/systools_relup.erl
@@ -0,0 +1,560 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(systools_relup).
+
+%%
+%% GENERATING A RELUP FILE
+%%
+%% The purpose of this module is to produce one relup file, based on
+%% one `top' .rel file, a set of `base' .rel files, and application
+%% .app and .appup files.
+
+%% A .rel file contains a release specification that lists the name
+%% and version of the release, the erts version used, and all
+%% applications that are contained in the release.
+%%
+%% In the sequel the term `top' refers to precisely one release that
+%% we upgrade to, or downgrade from. The term `base' refers to one or
+%% several releases that we upgrade from (`base' -> `top'), or
+%% downgrade to (`base' <-- `top'). We should have the following
+%% diagram in mind:
+%%
+%%
+%% TopRel
+%%
+%% / | \
+%% / | \
+%% / | \
+%% / | \
+%% | | |
+%% Base-1-Rel Base-2-Rel... Base-N-Rel .
+%%
+%% .appup files for upgrade or downgrade reside only with the applications
+%% in the `top' release.
+%%
+%% Consider now one of the Base-k-Rel releases, call it BaseRel,
+%% and let
+%%
+%% TopApps = the applications in TopRel
+%% BaseApps = the applications in BaseRel,
+%%
+%% and define the following sets of names:
+%%
+%% TopAppNames = [App.name || App <- TopApps]
+%% BaseAppNames = [App.name || App <- BaseApps] .
+%%
+%% We have the following disjoint sets:
+%%
+%% (1) TopAppNames \ BaseAppNames
+%%
+%% The elements in this set are the (names of) the applications
+%% which are only in the `top' release TopRel.
+%%
+%% (2) TopAppNames /\ BaseAppNames
+%%
+%% The elements in this set are the (names of) the applications that
+%% exist in both releases.
+%%
+%% (3) BaseAppNames \ TopAppNames
+%%
+%% The elements in this set are the (names of) the applications that
+%% are only in the `base' release BaseRel.
+%%
+%% Upgrade (`base' --> `top')
+%% ==========================
+%%
+%% TopAppNames \ BaseAppNames New applications. There are no
+%% .appup files for these. Generate
+%% `add_application' instructions.
+%%
+%% TopAppNames /\ BaseAppNames Same applications. For those that
+%% have different vsns, upgrade according
+%% to instructions in .appup file.
+%%
+%% BaseAppNames \ TopAppNames Old applications. There are no
+%% .appup files for these. Generate
+%% `remove_application' instructions.
+%%
+%% Downgrade ( `top' --> `base')
+%% =============================
+%%
+%% BaseAppNames \ TopAppNames New applications. There are no
+%% .appup files for these. Generate
+%% `add_application' instructions.
+%%
+%% TopAppNames /\ BaseAppNames Same applications. For those that
+%% have different vsns, downgrade
+%% according to instructions in
+%% .appup file.
+%%
+%% TopAppNames \ BaseAppNames Old applications. There are no
+%% .appup files for these. Generate
+%% `remove_application' instructions.
+%%
+%%
+
+-export([mk_relup/3, mk_relup/4, format_error/1, format_warning/1]).
+-include("systools.hrl").
+
+%%-----------------------------------------------------------------
+%% mk_relup(TopRelFile, BaseUpRelDcs, BaseDnRelDcs)
+%% mk_relup(TopRelFile, BaseUpRelDcs, BaseDnRelDcs, Opts) -> Ret
+%%
+%% TopRelFile = rel_filename()
+%% TopUpRelDcs = BaseDnRelDcs = [reldescr()]
+%% reldescr() = rel_filename() | {_rel_filename(), description()}
+%% rel_filename() = description() = string()
+%% Opts = [opt()]
+%% opt() = {path, [path()]} | silent | noexec | restart_emulator
+%% | {outdir, string()}
+%% path() = [string()]
+%% Ret = ok | error | {ok, Relup, Module, Warnings} | {error, Module, Error}
+%%
+%% Creates a "relup" file based on information in the top
+%% .rel file and the up and down .rel files.
+%%
+%% The rel_filename() is stem of a .rel file, i.e. the extension
+%% ".rel" is added to the stem to form the name of the real file.
+%%
+%% XXX WARNING: The default paths used to search for files are those
+%% that are returned by code:get_path(). The default path cannot be
+%% changed, only prepended through the `path' option. That may have
+%% consequences that are hard to predict.
+%%
+%% The option `path' sets search path, `silent' suppresses printing of
+%% error messages to the console, `noexec' inhibits the creation of
+%% the output "relup" file, and restart_emulator ensures that the new
+%% emulator is restarted (as the final step).
+%% ----------------------------------------------------------------
+mk_relup(TopRelFile, BaseUpRelDcs, BaseDnRelDcs) ->
+ mk_relup(TopRelFile, BaseUpRelDcs, BaseDnRelDcs, []).
+mk_relup(TopRelFile, BaseUpRelDcs, BaseDnRelDcs, Opts) ->
+ case check_opts(Opts) of
+ [] ->
+ R = (catch do_mk_relup(TopRelFile,BaseUpRelDcs,BaseDnRelDcs,
+ add_code_path(Opts), Opts)),
+ case {get_opt(silent, Opts), get_opt(noexec, Opts)} of
+ {false, false} ->
+ case R of
+ {ok, _Res, _Mod, Ws} ->
+ print_warnings(Ws),
+ ok;
+ Other ->
+ print_error(Other),
+ error
+ end;
+ _ ->
+ R
+ end;
+ BadArg ->
+ erlang:error({badarg, BadArg})
+ end.
+
+%% Function for checking validity of options in analogy with
+%% check_args_script/1 and check_args_tar/1 in systools_make.
+%% To maintain backwards compatibility, actually only outdir is checked.
+check_opts([{outdir, Dir}|_Opts]) when is_list(Dir) ->
+ [];
+check_opts([{outdir, BadArg}|_Opts]) ->
+ [{outdir, BadArg}];
+check_opts([_Opt|Opts]) ->
+ check_opts(Opts);
+check_opts([]) ->
+ [].
+
+do_mk_relup(TopRelFile, BaseUpRelDcs, BaseDnRelDcs, Path, Opts) ->
+ ModTest = false,
+ case systools_make:get_release(to_list(TopRelFile), Path, ModTest) of
+ %%
+ %% TopRel = #release
+ %% NameVsnApps = [{{Name, Vsn}, #application}]
+ {ok, TopRel, NameVsnApps, Ws0} ->
+ %%
+ %% TopApps = [#application]
+ TopApps = lists:map(fun({_, App}) -> App end, NameVsnApps),
+
+ %% Up
+ {Up, Ws1} = foreach_baserel_up(TopRel, TopApps, BaseUpRelDcs,
+ Path, Opts, Ws0),
+ %% Down
+ {Dn, Ws2} = foreach_baserel_dn(TopRel, TopApps, BaseDnRelDcs,
+ Path, Opts, Ws1),
+ Relup = {TopRel#release.vsn, Up, Dn},
+ write_relup_file(Relup, Opts),
+ {ok, Relup, ?MODULE, Ws2};
+ Other ->
+ throw(Other)
+ end.
+
+%%-----------------------------------------------------------------
+%% foreach_baserel_up(Rel, TopApps, BaseRelDcs, Path, Opts, Ws) -> Ret
+%% foreach_baserel_dn(Rel, TopApps, BaseRelDcs, Path, Opts, Ws) -> Ret
+%%
+%% TopRel = #release
+%% TopApps = [#application]
+%% BaseRelDcs = [reldescr()]
+%% reldescr() = filename() | {filename(), description()}
+%% filename() = description() = string()
+%% Opts = [opt()], opt() = {path, [path()]} | silent | noexec |
+%% restart_emulator
+%% Ws = [term()]
+%% Ret = {VDRs, Ws}
+%% VDRs = [vdr()], vdr() = {Vsn, Description, RUs}
+%%
+%% Generates scripts for each base release.
+%%
+foreach_baserel_up(TopRel, TopApps, BaseRelDcs, Path, Opts, Ws) ->
+ foreach_baserel_up(TopRel, TopApps, BaseRelDcs, Path, Opts,
+ Ws, []).
+
+foreach_baserel_up(TopRel, TopApps, [BaseRelDc|BaseRelDcs], Path, Opts,
+ Ws, Acc) ->
+ BaseRelFile = extract_filename(BaseRelDc),
+
+ {ok, BaseRel} = systools_make:read_release(BaseRelFile, Path),
+
+ %%
+ %% BaseRel = #release
+ %%
+ %% RUs = (release upgrade scripts). We really get separate
+ %% scripts, one for emulator restart, one for each
+ %% application, one for each added applications, and one for
+ %% each removed applications.
+ %%
+ {RUs1, Ws1} = collect_appup_scripts(up, TopApps, BaseRel, Ws, []),
+
+ {RUs2, Ws2} = create_add_app_scripts(BaseRel, TopRel, RUs1, Ws1),
+
+ {RUs3, Ws3} = create_remove_app_scripts(BaseRel, TopRel, RUs2, Ws2),
+
+ {RUs4, Ws4} =
+ check_for_emulator_restart(TopRel, BaseRel, RUs3, Ws3, Opts),
+
+ ModTest = false,
+ BaseApps =
+ case systools_make:get_release(BaseRelFile, Path, ModTest) of
+ {ok, _, NameVsnApps, _Warns} ->
+ lists:map(fun({_,App}) -> App end, NameVsnApps);
+ Other1 ->
+ throw(Other1)
+ end,
+
+ case systools_rc:translate_scripts(up, RUs4, TopApps, BaseApps) of
+ {ok, RUs} ->
+ VDR = {BaseRel#release.vsn,
+ extract_description(BaseRelDc), RUs},
+ foreach_baserel_up(TopRel, TopApps, BaseRelDcs, Path,
+ Opts, Ws4, [VDR| Acc]);
+ XXX ->
+ throw(XXX)
+ end;
+foreach_baserel_up( _, _, [], _, _, Ws, Acc) ->
+ {Acc, Ws}.
+
+foreach_baserel_dn(TopRel, TopApps, BaseRelDcs, Path, Opts, Ws) ->
+ foreach_baserel_dn(TopRel, TopApps, BaseRelDcs, Path, Opts,
+ Ws, []).
+
+foreach_baserel_dn(TopRel, TopApps, [BaseRelDc|BaseRelDcs], Path, Opts,
+ Ws, Acc) ->
+ BaseRelFile = extract_filename(BaseRelDc),
+
+ {ok, BaseRel} = systools_make:read_release(BaseRelFile, Path),
+
+ %% BaseRel = #release
+
+ %% RUs = (release upgrade scripts)
+ %%
+ {RUs1, Ws1} = collect_appup_scripts(dn, TopApps, BaseRel, Ws, []),
+
+ ModTest = false,
+ {BaseApps, Ws2} =
+ case systools_make:get_release(BaseRelFile, Path, ModTest) of
+ %%
+ %% NameVsnApps = [{{Name, Vsn}, #application}]
+ {ok, _, NameVsnApps, Warns} ->
+ %%
+ %% NApps = [#application]
+ NApps = lists:map(fun({_,App}) -> App end, NameVsnApps),
+ {NApps, Warns ++ Ws1};
+ Other ->
+ throw(Other)
+ end,
+
+ RUs2 = RUs1,
+
+ {RUs3, Ws3} = create_add_app_scripts(TopRel, BaseRel, RUs2, Ws2),
+
+ {RUs4, Ws4} = create_remove_app_scripts(TopRel, BaseRel, RUs3, Ws3),
+
+ {RUs5, Ws5} = check_for_emulator_restart(TopRel, BaseRel,
+ RUs4, Ws4, Opts),
+
+ case systools_rc:translate_scripts(dn, RUs5, BaseApps, TopApps) of
+ {ok, RUs} ->
+ VDR = {BaseRel#release.vsn,
+ extract_description(BaseRelDc), RUs},
+ foreach_baserel_dn(TopRel, TopApps, BaseRelDcs, Path,
+ Opts, Ws5, [VDR| Acc]);
+ XXX ->
+ throw(XXX)
+ end;
+foreach_baserel_dn( _, _, [], _, _, Ws, Acc) ->
+ {Acc, Ws}.
+
+
+%% check_for_emulator_restart(Rel1, Rel2, RUs, Ws, Opts) -> {NRUs, NWs}
+%%
+%% Rel1 = Rel2 = #release
+%%
+check_for_emulator_restart(#release{erts_vsn = Vsn1, name = N1},
+ #release{erts_vsn = Vsn2, name = N2}, RUs, Ws,
+ _Opts) when Vsn1 /= Vsn2 ->
+ {RUs++[[restart_new_emulator]], [{erts_vsn_changed, {N1, N2}} | Ws]};
+check_for_emulator_restart(_, _, RUs, Ws, Opts) ->
+ case get_opt(restart_emulator, Opts) of
+ true -> {RUs++[[restart_new_emulator]], Ws};
+ _ -> {RUs, Ws}
+ end.
+
+%% collect_appup_scripts(Mode, TopApps, BaseRel, Ws, RUs) -> {NRUs, NWs}
+%% Mode = up | dn
+%% TopApps = [#application]
+%% BaseRel = #release
+%%
+%% Gets the script corresponding to Mode and BaseRel in the .appup file
+%% for each application.
+%%
+collect_appup_scripts(Mode, [TopApp|TopApps], BaseRel, Ws, RUs) ->
+
+ case lists:keysearch(TopApp#application.name, 1,
+ BaseRel#release.applications) of
+ {value, {_Name, BaseVsn, _Type}} ->
+ %% io:format("collect appup script: ~p~n",
+ %% [TopApp#application.name]),
+ if
+ TopApp#application.vsn == BaseVsn ->
+ %% Same version: nothing to do.
+ collect_appup_scripts(Mode, TopApps, BaseRel, Ws, RUs);
+ true ->
+ %% We must have an upgrade script for BaseVsn
+ {RU1s, Ws1} = get_script_from_appup(Mode, TopApp, BaseVsn,
+ Ws, RUs),
+ collect_appup_scripts(Mode, TopApps, BaseRel, Ws1, RU1s)
+ end;
+ false ->
+ collect_appup_scripts(Mode, TopApps, BaseRel, Ws, RUs)
+ end;
+collect_appup_scripts(_, [], _, Ws, RUs) -> {RUs, Ws}.
+
+
+%% create_add_app_scripts(FromRel, ToRel, RU0s, W0s) -> {RUs, Ws}
+%%
+%% FromRel = ToRel = #release
+%% ToApps = [#application]
+%%
+create_add_app_scripts(FromRel, ToRel, RU0s, W0s) ->
+ AddedNs = [N || {N, _V, _T} <- ToRel#release.applications,
+ not lists:keymember(N, 1, FromRel#release.applications)],
+ %% io:format("Added apps: ~p~n", [AddedNs]),
+ RUs = [[{add_application, N}] || N <- AddedNs],
+ {RUs ++ RU0s, W0s}.
+
+
+%% create_remove_app_scripts(FromRel, ToRel, RU0s, W0s) -> {RUs, Ws}
+%%
+%% FromRel = ToRel = #release
+%% ToApps = [#application]
+%%
+%% XXX ToApps not used.
+%%
+create_remove_app_scripts(FromRel, ToRel, RU0s, W0s) ->
+ RemovedNs = [N || {N, _V, _T} <- FromRel#release.applications,
+ not lists:keymember(N, 1, ToRel#release.applications)],
+ %% io:format("Removed apps: ~p~n", [RemovedNs]),
+ RUs = [[{remove_application, N}] || N <- RemovedNs],
+ {RUs ++ RU0s, W0s}.
+
+%% get_script_from_appup(Mode, TopApp, BaseVsn, Ws, RUs) -> {NRUs, NWs}
+%% Mode = up | dn
+%% TopApp = #application
+%%
+%% XXX We do not operate on Ws and RUs, we just return (possibly) one
+%% warning, and one script. Remove the Ws And RUs arguments and return
+%% only what is relevant.
+%%
+get_script_from_appup(Mode, TopApp, BaseVsn, Ws, RUs) ->
+ FName = filename:join([TopApp#application.dir,
+ to_list(TopApp#application.name) ++ ".appup"]),
+ {VsnRUs, TopVsn} = case systools_lib:read_term(FName) of
+ {ok, {TopVsn0, UpVsnRUs, DnVsnRUs}} ->
+ VsnRUs0 = case Mode of
+ up ->
+ UpVsnRUs;
+ dn ->
+ DnVsnRUs
+ end,
+ {VsnRUs0, TopVsn0};
+ X ->
+ throw({error, ?MODULE, {file_problem,
+ {FName, X}}})
+ end,
+ Ws1 = if
+ TopApp#application.vsn == TopVsn ->
+ Ws;
+ true ->
+ %% XXX Why is this a warning only?
+ [{bad_vsn, {TopVsn, TopApp#application.vsn}}| Ws]
+ end,
+ case lists:keysearch(BaseVsn, 1, VsnRUs) of
+ {value, {_, RU}} ->
+ {RUs ++ [RU], Ws1};
+ _ ->
+ throw({error, ?MODULE, {no_relup, FName, TopApp, BaseVsn}})
+ end.
+
+
+%% Primitives for the "lists of release names" that we upgrade from
+%% and to.
+extract_filename({N, _D}) -> to_list(N);
+extract_filename(N) -> to_list(N).
+
+extract_description({_N, D}) -> D;
+extract_description(_) -> [].
+
+to_list(X) when is_atom(X) -> atom_to_list(X);
+to_list(X) when is_list(X) -> X.
+
+
+%% write_relup_file(Relup, Opts) -> {ok. Relup}
+%%
+%% Writes a relup file.
+%%
+write_relup_file(Relup, Opts) ->
+ case get_opt(noexec, Opts) of
+ true ->
+ ok;
+ _ ->
+ Filename = case get_opt(outdir, Opts) of
+ OutDir when is_list(OutDir) ->
+ filename:join(filename:absname(OutDir),
+ "relup");
+ false ->
+ "relup";
+ Badarg ->
+ throw({error, ?MODULE, {badarg, {outdir,Badarg}}})
+ end,
+
+ case file:open(Filename, [write]) of
+ {ok, Fd} ->
+ io:format(Fd, "~p.~n", [Relup]),
+ file:close(Fd);
+ {error, Reason} ->
+ throw({error, ?MODULE, {file_problem, {"relup", Reason}}})
+ end
+ end,
+ {ok, Relup}.
+
+add_code_path(Opts) ->
+ case get_opt(path, Opts) of
+ false ->
+ code:get_path();
+ Paths0 ->
+ Paths1 = [to_list(P) || P <- Paths0],
+ %% Allow wild-card expansion.
+ Paths2 = systools_lib:get_path(Paths1),
+ make_set(Paths2 ++ code:get_path())
+ end.
+
+get_opt(Opt, Opts) ->
+ case lists:keysearch(Opt, 1, Opts) of
+ {value, {_, Val}} -> Val;
+ _ ->
+ case lists:member(Opt, Opts) of
+ true -> true;
+ _ -> default(Opt)
+ end
+ end.
+
+%% make elements in list unique without rearranging the
+%% elements.
+%%
+%% XXX Not very efficient.
+%%
+make_set([]) -> [];
+make_set([H|T]) ->
+ [H | [ Y || Y<- make_set(T),
+ Y =/= H]].
+
+default(path) -> false;
+default(noexec) -> false;
+default(silent) -> false;
+default(restart_emulator) -> false;
+default(outdir) -> false.
+
+print_error({'EXIT', Err}) ->
+ print_error(Err);
+print_error({error, Mod, Error}) ->
+ S = apply(Mod, format_error, [Error]),
+ io:format(S, []);
+print_error(Other) ->
+ io:format("Error: ~p~n", [Other]).
+
+format_error({file_problem, {"relup", _Posix}}) ->
+ io_lib:format("Could not open file relup~n", []);
+format_error({file_problem, {File, What}}) ->
+ io_lib:format("Could not ~p file ~p~n", [get_reason(What), File]);
+format_error({no_relup, File, App, Vsn}) ->
+ io_lib:format("No release upgrade script entry for ~p-~s to ~p-~s "
+ "in file ~p~n",
+ [App#application.name, App#application.vsn,
+ App#application.name, Vsn, File]);
+
+format_error(Error) ->
+ io:format("~p~n", [Error]).
+
+
+print_warnings(Ws) when is_list(Ws) ->
+ lists:foreach(fun(W) -> print_warning(W) end, Ws);
+print_warnings(W) ->
+ print_warning(W).
+
+print_warning(W) ->
+ S = format_warning(W),
+ io:format("~s", [S]).
+
+format_warning({erts_vsn_changed, {Rel1, Rel2}}) ->
+ io_lib:format("*WARNING* The ERTS version changed between ~p and ~p~n",
+ [Rel1, Rel2]);
+format_warning(What) ->
+ io_lib:format("*WARNING* ~p~n",[What]).
+
+
+get_reason({error, {open, _, _}}) -> open;
+get_reason({error, {read, _, _}}) -> read;
+get_reason({error, {parse, _, _}}) -> parse;
+get_reason({error, {open, _}}) -> open;
+get_reason({error, {read, _}}) -> read;
+get_reason({error, {parse, _}}) -> parse;
+get_reason({open, _}) -> open;
+get_reason({read, _}) -> read;
+get_reason({parse, _}) -> parse;
+get_reason(open) -> open;
+get_reason(read) -> read;
+get_reason(parse) -> parse.
diff --git a/lib/sasl/vsn.mk b/lib/sasl/vsn.mk
new file mode 100644
index 0000000000..f1f50f7d20
--- /dev/null
+++ b/lib/sasl/vsn.mk
@@ -0,0 +1 @@
+SASL_VSN = 2.1.8