diff options
396 files changed, 23087 insertions, 27281 deletions
diff --git a/.travis.yml b/.travis.yml index 51453639b0..5a87bd3fd1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -100,8 +100,8 @@ matrix: api_key: secure: vW5PN6zng5H5+TCvwfwpGZsABrdCWYcFwDm3KXq+plsecBmTayu/0jgNso5Z97FbzDGVTLHWchvywEYQWnmrEByyOrqH73v1LN6JEfN99VpSrdFr15IzhblcyU1R9ugYc3WEoYjX0Q1uGelDSWRuuQOPbzy8mZf3D4rSGonyraP7jPTdHhs5P3ZWk6OMFz+tCdF4XohXqbhXIBOeH/EKg0svX2u5IcV01/YOL8LHWz6G7+gqBryEXx1+ngjQXQmMQwd7Yg3WOKE4XV9gX8ixZsbpUPZXAQKF+VOYdEgeiIr1hI0tBQUYX7FYEzYH5MCxqng5RdaPTOAm1oQroyGkIcWSXzDwN4AhJ7xqa/0NRdEaBPdQzPBCc+pVUDkxBR1ytXjBQqdQMnI6184TDiU5XBnj3kmieLkkKPKQNoPve/Y8Q8zutw4GNc7gixGcQCjtAFUbrT73QVRrezQH0qIdt23rivvf2R7CCOWSmgzowrswmtHdgeEVbodUIBPTNp7qzlUk9gDp6vW0XrOC4qEFI+VaY5PsEOXrrxZmI3gGGJgsbfzRvzvvupQcLNERniJ67r/uumbForpL0x1c65scKuMWwcn1wqt2OLbDoIIuM31Ph2HX/09TTqECU7CTvqLT5MnbZHXGjY9c3ch+sY3tSfaEX6aazl/Dqx28c7boCEw= file: - - ${TRAVIS_TAG}-bundle.txt - - ${TRAVIS_TAG}-bundle.tar.gz + - ${TRAVIS_TAG}.0-bundle.txt + - ${TRAVIS_TAG}.0-bundle.tar.gz on: # We only deploy on pushes to tags that match the regexp tags: true diff --git a/OTP_VERSION b/OTP_VERSION index 0398faf11c..3abee4573a 100644 --- a/OTP_VERSION +++ b/OTP_VERSION @@ -1 +1 @@ -22.2.1 +22.2.8 diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 5fefd72d5d..5d274e69c3 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -2935,6 +2935,12 @@ fi # DED_EMU_THR_DEFS=$EMU_THR_DEFS DED_CFLAGS="$CFLAGS $CPPFLAGS $DED_CFLAGS" if test "x$GCC" = xyes; then + # Use -fno-common for gcc, that is link error if multiple definitions of + # global variables are encountered. This is ISO C compliant. + # Until version 10, gcc has had -fcommon as default, which allows and merges + # such dubious duplicates. + LM_TRY_ENABLE_CFLAG([-fno-common], [DED_CFLAGS]) + DED_STATIC_CFLAGS="$DED_CFLAGS" DED_CFLAGS="$DED_CFLAGS -fPIC" fi diff --git a/erts/configure.in b/erts/configure.in index a887f86621..609c457393 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script. -*-m4-*- dnl %CopyrightBegin% dnl -dnl Copyright Ericsson AB 1997-2019. All Rights Reserved. +dnl Copyright Ericsson AB 1997-2020. All Rights Reserved. dnl dnl Licensed under the Apache License, Version 2.0 (the "License"); dnl you may not use this file except in compliance with the License. @@ -566,6 +566,12 @@ if test "x$GCC" = xyes; then WFLAGS="$WFLAGS -Wdeclaration-after-statement" fi CFLAGS=$saved_CFLAGS + + # Use -fno-common for gcc, that is link error if multiple definitions of + # global variables are encountered. This is ISO C compliant. + # Until version 10, gcc has had -fcommon as default, which allows and merges + # such dubious duplicates. + LM_TRY_ENABLE_CFLAG([-fno-common], [CFLAGS]) else WFLAGS="" WERRORFLAGS="" @@ -1384,6 +1390,40 @@ fi AC_SUBST(USE_ESOCK) +AC_ARG_WITH(esock-counter-size, +AS_HELP_STRING([--with-esock-counter-size=SZ], + [Size of the esock counters, in number of bits: 16 | 24 | 32 | 48 | 64; default is 64]), +[], +[with_esock_counter_size=64]) + +case "$with_esock_counter_size" in + 16) + AC_DEFINE(ESOCK_COUNTER_SIZE, [16], [ESOCK counter size]) + ;; + 24) + AC_DEFINE(ESOCK_COUNTER_SIZE, [24], [ESOCK counter size]) + ;; + 32) + AC_DEFINE(ESOCK_COUNTER_SIZE, [32], [ESOCK counter size]) + ;; + 48) + AC_DEFINE(ESOCK_COUNTER_SIZE, [48], [ESOCK counter size]) + ;; + 64) + AC_DEFINE(ESOCK_COUNTER_SIZE, [64], [ESOCK counter size]) + ;; + *) + AC_MSG_WARN([Invalid esock counter size ($with_esock_counter_size), using default (64)]) + AC_DEFINE(ESOCK_COUNTER_SIZE, [64], [ESOCK counter size]) + dnl with_esock_counter_size=64 + ;; +esac +dnl ESOCK_COUNTER_SIZE=$with_esock_counter_size + +dnl We don't actually (currently) use this in erlang +dnl AC_SUBST(ESOCK_COUNTER_SIZE) + + dnl dnl This test kindly borrowed from Tcl dnl @@ -1778,6 +1818,7 @@ AC_CHECK_SIZEOF(long long) AC_CHECK_SIZEOF(size_t) AC_CHECK_SIZEOF(off_t) AC_CHECK_SIZEOF(time_t) +AC_CHECK_SIZEOF(suseconds_t) BITS64= diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index d77d989057..fd4cdac03f 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2001</year><year>2018</year> + <year>2001</year><year>2020</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -134,7 +134,7 @@ <item> <p>If F is a type declaration <c>-Type Name(V_1, ..., V_k) :: T</c>, where <c>Type</c> is either the atom <c>type</c> or the atom - <c>opaque</c>, each <c>V_i</c> is a variable, and <c>T</c> is a type, + <c>opaque</c>, each <c>V_i</c> is a type variable, and <c>T</c> is a type, then Rep(F) = <c>{attribute,LINE,Type,{Name,Rep(T),[Rep(V_1), ..., Rep(V_k)]}}</c>.</p> diff --git a/erts/doc/src/alt_disco.xml b/erts/doc/src/alt_disco.xml index 067eb4992d..d95e62bc8a 100644 --- a/erts/doc/src/alt_disco.xml +++ b/erts/doc/src/alt_disco.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2018</year><year>2018</year> + <year>2018</year><year>2019</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -22,7 +22,7 @@ </legalnotice> - <title>How to Implement an Alternative Service Discovery for Erlang Distribution + <title>How to Implement an Alternative Node Discovery for Erlang Distribution </title> <prepared>Timmo Verlaan</prepared> <responsible></responsible> @@ -34,20 +34,20 @@ <file>alt_disco.xml</file> </header> <p> - This section describes how to implement an alternative discovery mechanism - for Erlang distribution. Discovery is normally done using DNS and the - Erlang Port Mapper Daemon (EPMD) for port discovery. + This section describes how to implement an alternative node discovery + mechanism for Erlang distribution. Node discovery is normally done using DNS + and the Erlang Port Mapper Daemon (EPMD) for port registration and lookup. </p> <note><p> - Support for alternative service discovery mechanisms was added in Erlang/OTP + Support for alternative node discovery mechanisms was added in Erlang/OTP 21. </p></note> <section> <title>Introduction</title> - <p>To implement your own service discovery module you have to write your own + <p>To implement your own node discovery module you have to write your own EPMD module. The <seealso marker="kernel:erl_epmd">EPMD module</seealso> is responsible for providing the location of another node. The distribution modules (<c>inet_tcp_dist</c>/<c>inet_tls_dist</c>) call the EPMD module to diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd.xml index 311483022d..75353cbc07 100644 --- a/erts/doc/src/epmd.xml +++ b/erts/doc/src/epmd.xml @@ -72,6 +72,12 @@ therefore required for an Erlang network to function correctly.</p> + <note><p>On Windows the maximum number of nodes allowed in one + epmd instance is 60. This is because of limitations in the current + implementation. If you need more nodes, you should look into using + and erlang based epmd implementation such as + <url href="https://github.com/erlang/epmd">Erlang EPMD</url>.</p></note> + <taglist> <tag>Starting the port mapper daemon</tag> <item> diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index ed1b0880b4..a37707f7f9 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -575,7 +575,11 @@ <tag><marker id="async_thread_pool_size"/><c><![CDATA[+A size]]></c></tag> <item> <p>Sets the number of threads in async thread pool. Valid range - is 0-1024. Defaults to 1.</p> + is 0-1024. The async thread pool is used by linked-in drivers to + handle work that may take a very long time. Since OTP-21 there are + very few linked-in drivers in the default Erlang/OTP distribution + that uses the async thread pool. Most of them have been migrated to + dirty IO schedulers. Defaults to 1.</p> </item> <tag><c><![CDATA[+B [c | d | i]]]></c></tag> <item> @@ -1015,6 +1019,12 @@ executing on ordinary schedulers. If the amount of dirty CPU schedulers was allowed to be unlimited, dirty CPU bound jobs would potentially starve normal jobs.</p> + <p>Typical users of the dirty CPU schedulers are large garbage collections, + json protocol encode/decoders written as nifs and matrix manipulation + libraries.</p> + <p>You can use <seealso marker="runtime_tools:msacc">msacc(3)</seealso> + in order to see the current load of the dirty CPU schedulers threads + and adjust the number used accordingly.</p> </item> <tag><marker id="+SDPcpu"/><c><![CDATA[+SDPcpu DirtyCPUSchedulersPercentage:DirtyCPUSchedulersOnlinePercentage]]></c></tag> @@ -1043,16 +1053,18 @@ <tag><marker id="+SDio"/><c><![CDATA[+SDio DirtyIOSchedulers]]></c></tag> <item> <p>Sets the number of dirty I/O scheduler threads to create. - Valid range is 0-1024. By - default, the number of dirty I/O scheduler threads created is 10, - same as the default number of threads in the <seealso - marker="#async_thread_pool_size">async thread pool</seealso>.</p> + Valid range is 1-1024. By + default, the number of dirty I/O scheduler threads created is 10.</p> <p>The amount of dirty IO schedulers is not limited by the amount of normal schedulers <seealso marker="#+SDcpu">like the amount of dirty CPU schedulers</seealso>. This since only I/O bound work is expected to execute on dirty I/O schedulers. If the user should schedule CPU bound jobs on dirty I/O schedulers, these jobs might starve ordinary jobs executing on ordinary schedulers.</p> + <p>Typical users of the dirty IO schedulers are reading and writing to files.</p> + <p>You can use <seealso marker="runtime_tools:msacc">msacc(3)</seealso> + in order to see the current load of the dirty IO schedulers threads + and adjust the number used accordingly.</p> </item> <tag><c><![CDATA[+sFlag Value]]></c></tag> <item> diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 3e2d3bb447..07af7621ff 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -2372,10 +2372,26 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> <marker id="erl_drv_mutex_create"></marker> <p>Creates a mutex and returns a pointer to it.</p> <p><c>name</c> is a string identifying the created mutex. It is used - to identify the mutex in planned future debug functionality.</p> + to identify the mutex in debug functionality (see note).</p> <p>Returns <c>NULL</c> on failure. The driver creating the mutex is responsible for destroying it before the driver is unloaded.</p> <p>This function is thread-safe.</p> + <marker id="lock_checker"></marker> + <note><p> + One such debug functionality is the <em>lock checker</em>, which + can detect locking order violations and thereby potential deadlock + bugs. For the lock checker to work the <c>name</c> should be on the + format <c>"App.Type"</c> or <c>"App.Type[Instance]"</c>, where App is + the name of the application, Type is the name of the lock type and + Instance is optional information about each lock instance. "App.Type" + should be a unique name for the lock checker to detect lock order + violations between locks of different types. The Instance information + is currently ignored.</p><p> + For example, if we have mutexes of types "myapp.xtable" and + "myapp.xitem" then the lock checker will make sure either + "myapp.xtable" locks are never locked after "myapp.xitem" locks or + vice versa. + </p></note> </desc> </func> @@ -2675,8 +2691,8 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] <marker id="erl_drv_rwlock_create"></marker> <p>Creates an rwlock and returns a pointer to it.</p> <p><c>name</c> is a string identifying the created rwlock. - It is used to identify the rwlock in planned future - debug functionality.</p> + It is used to identify the rwlock in debug functionality (see note + about the <seealso marker="#lock_checker">lock checker</seealso>).</p> <p>Returns <c>NULL</c> on failure. The driver creating the rwlock is responsible for destroying it before the driver is unloaded.</p> <p>This function is thread-safe.</p> diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 2183f75487..2f3d2f9624 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -4121,6 +4121,71 @@ RealSystem = system + MissedSystem</code> <seealso marker="erl#+spp"><c>+spp</c></seealso> to <c>erl(1)</c>.</p> </item> + <tag><c>{busy_limits_port, {Low, High} | disabled}</c></tag> + <item> + <p>Sets limits that will be used for controlling the + busy state of the port.</p> + <p>When the ports internal output queue size becomes + larger than or equal to <c>High</c> bytes, it enters + the busy state. When it becomes less than <c>Low</c> + bytes it leaves the busy state. When the port is in + the busy state, processes sending commands to it will + be suspended until the port leaves the busy state. + Commands are in this context either + <c>Port ! {Owner, {command, Data}}</c> or + <c>port_command/[2,3]</c>.</p> + <p> + The <c>Low</c> limit is automatically adjusted to the + same as <c>High</c> if it is set larger then <c>High</c>. + Valid range of values for <c>Low</c> and <c>High</c> is + <c>[1, (1 bsl (8*erlang:system_info(wordsize)))-2]</c>. + If the atom <c>disabled</c> is passed, the port will + never enter the busy state.</p> + <p>The defaults are <c>Low = 4096</c> and + <c>High = 8192</c>.</p> + <p><em>Note</em> that this option is only valid when + spawning an executable (port program) by opening the + spawn driver and when opening the <c>fd</c> driver. + This option will cause a failure with a <c>badarg</c> + exception when opening other drivers.</p> + </item> + <tag><c>{busy_limits_msgq, {Low, High} | disabled}</c></tag> + <item> + <p>Sets limits that will be used for controlling the + busy state of the port message queue.</p> + <p>When the ports message queue size becomes larger + than or equal to <c>High</c> bytes it enters the busy + state. When it becomes less than <c>Low</c> bytes it + leaves the busy state. When the port message queue is + in the busy state, processes sending commands to it + will be suspended until the port message queue leaves + the busy state. Commands are in this context either + <c>Port ! {Owner, {command, Data}}</c> or + <c>port_command/[2,3]</c>.</p> + <p>The <c>Low</c> limit is automatically adjusted to the + same as <c>High</c> if it is set larger then <c>High</c>. + Valid range of values for <c>Low</c> and <c>High</c> is + <c>[1, (1 bsl (8*erlang:system_info(wordsize)))-2]</c>. + If the atom <c>disabled</c> is passed, the port + message queue will never enter the busy state.</p> + <p><em>Note</em> that if the driver statically has + disabled the use of this feature, a failure with a + <c>badarg</c> exception will be raised unless this + option also is set to <c>disable</c> or not passed + at all.</p> + <p>The defaults are <c>Low = 4096</c> and + <c>High = 8192</c> unless the driver itself does + modifications of these values.</p> + <p><em>Note</em> that the driver might fail if + it also adjust these limits by itself and you + have disabled this feature.</p> + <p>The spawn driver (used when spawning an executable) + and the <c>fd</c> driver do not disable this feature + and do not adjust these limits by themselves.</p> + <p>For more information see the documentation + <seealso marker="erl_driver#erl_drv_busy_msgq_limits"><c>erl_drv_busy_msgq_limits()</c></seealso>. + </p> + </item> </taglist> <p>Default is <c>stream</c> for all port types and <c>use_stdio</c> for spawned ports.</p> diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml index c824e37976..d6c73a02ec 100644 --- a/erts/doc/src/init.xml +++ b/erts/doc/src/init.xml @@ -101,7 +101,7 @@ </item> <tag><c>home</c></tag> <item> - <p>The home directory:</p> + <p>The home directory (on Unix, the value of $HOME):</p> <pre> 4> <input>init:get_argument(home).</input> {ok,[["/home/harry"]]}</pre> diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 2776407af9..48208574d1 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2018</year> + <year>2004</year><year>2019</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -31,6 +31,107 @@ </header> <p>This document describes the changes made to the ERTS application.</p> +<section><title>Erts 10.6.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A process could get into an inconsistent state where it + was runnable, but never scheduled for execution. This + could occur when a mix of <c>normal</c> and <c>low</c> + priority processes where scheduled on the same type of + dirty scheduler simultaneously.</p> + <p> + Own Id: OTP-16446 Aux Id: ERL-1157 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 10.6.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A process could end up in a state where it got endlessly + rescheduled without making any progress. This occurred + when a system task, such as check of process code (part + of a code purge), was scheduled on a high priority + process trying to execute on a dirty scheduler.</p> + <p> + Own Id: OTP-16436 Aux Id: ERL-1152 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Improved signal handling for processes executing dirty. + For example, avoid busy wait in dirty signal handler + process when process is doing garbage collection on dirty + scheduler.</p> + <p> + Own Id: OTP-16358</p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 10.6.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Taking a scheduler offline could cause timers set while + executing on that scheduler to be delayed until the + scheduler was put online again. This bug was introduced + in ERTS version 10.0 (OTP 21.0).</p> + <p> + Own Id: OTP-16371</p> + </item> + <item> + <p> + The <c>ets:update_counter/4</c> core dumped when given an + ordered_set with write_concurrency enabled and an invalid + position. This bug has been fixed.</p> + <p> + Own Id: OTP-16378 Aux Id: ERL-1125 </p> + </item> + <item> + <p> + A process calling <seealso + marker="erts:erlang#system_flag_multi_scheduling"><c>erlang:system_flag(multi_scheduling, + block)</c></seealso> could end up blocked waiting for the + operation to complete indefinitely.</p> + <p> + Own Id: OTP-16379</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Duplicate entries for [socket:]getopt and [socket:]setopt + in man page.</p> + <p> + Own Id: OTP-16333 Aux Id: ERL-1104 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 10.6.1</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -1445,6 +1546,101 @@ </section> +<section><title>Erts 10.3.5.10</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed bug in <c>ets:update_counter/4</c>, when called + with an invalid <c>UpdateOp</c> and a <c>Key</c> that + does not exist, causing <c>ets:info(T,size)</c> to return + incorrect values. Bug exists since OTP-19.0.2.</p> + <p> + Own Id: OTP-16404 Aux Id: ERL-1127 </p> + </item> + <item> + <p> + A process could get into an inconsistent state where it + was runnable, but never scheduled for execution. This + could occur when a mix of <c>normal</c> and <c>low</c> + priority processes where scheduled on the same type of + dirty scheduler simultaneously.</p> + <p> + Own Id: OTP-16446 Aux Id: ERL-1157 </p> + </item> + <item> + <p> + Corrected the valid range of the <c>erl</c> command line + argument <seealso marker="erts:erl#+SDio"><c>+SDio + <NumberOfDirtyIoSchedulers></c></seealso> from + <c>0..1024</c> to <c>1..1024</c>. <c>+SDio 0</c> was + erroneously allowed which just caused the VM to crash on + the first dirty I/O job scheduled.</p> + <p> + Own Id: OTP-16481</p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 10.3.5.9</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A process could end up in a state where it got endlessly + rescheduled without making any progress. This occurred + when a system task, such as check of process code (part + of a code purge), was scheduled on a high priority + process trying to execute on a dirty scheduler.</p> + <p> + Own Id: OTP-16436 Aux Id: ERL-1152 </p> + </item> + <item> + <p> + Fixed bug in <c>erlang:list_to_ref/1</c> when called with + a reference created by a remote note. Function + <c>list_to_ref/1</c> is intended for debugging and not to + be used in application programs. Bug exist since OTP + 20.0.</p> + <p> + Own Id: OTP-16438</p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 10.3.5.8</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Taking a scheduler offline could cause timers set while + executing on that scheduler to be delayed until the + scheduler was put online again. This bug was introduced + in ERTS version 10.0 (OTP 21.0).</p> + <p> + Own Id: OTP-16371</p> + </item> + <item> + <p> + A process calling <seealso + marker="erts:erlang#system_flag_multi_scheduling"><c>erlang:system_flag(multi_scheduling, + block)</c></seealso> could end up blocked waiting for the + operation to complete indefinitely.</p> + <p> + Own Id: OTP-16379</p> + </item> + </list> + </section> + +</section> + <section><title>Erts 10.3.5.7</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -3708,7 +3904,7 @@ marker="kernel:erl_epmd"><c>erl_epmd</c></seealso> reference manual and ERTS User's Guide <seealso marker="erts:alt_disco">How to Implement an Alternative - Service Discovery for Erlang Distribution</seealso>.</p> + Node Discovery for Erlang Distribution</seealso>.</p> <p> Own Id: OTP-15086 Aux Id: PR-1694 </p> </item> @@ -3717,6 +3913,87 @@ </section> +<section><title>Erts 9.3.3.15</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A process could end up in a state where it got endlessly + rescheduled without making any progress. This occurred + when a system task, such as check of process code (part + of a code purge), was scheduled on a high priority + process trying to execute on a dirty scheduler.</p> + <p> + Own Id: OTP-16436 Aux Id: ERL-1152 </p> + </item> + <item> + <p> + Fixed bug in <c>erlang:list_to_ref/1</c> when called with + a reference created by a remote note. Function + <c>list_to_ref/1</c> is intended for debugging and not to + be used in application programs. Bug exist since OTP + 20.0.</p> + <p> + Own Id: OTP-16438</p> + </item> + <item> + <p> + A process could get into an inconsistent state where it + was runnable, but never scheduled for execution. This + could occur when a mix of <c>normal</c> and <c>low</c> + priority processes where scheduled on the same type of + dirty scheduler simultaneously.</p> + <p> + Own Id: OTP-16446 Aux Id: ERL-1157 </p> + </item> + <item> + <p> + Fixed erroneous mapping of exit reason from <c>kill</c> + to <c>killed</c> on reception of some exit signals due to + a broken link. This bug has existed since ERTS version + 5.5.2 (OTP R11).</p> + <p> + This bug was also unknowingly fixed in ERTS version 10.0 + (OTP 21.0) due to a new ERTS internal implementation of + signaling between processes.</p> + <p> + Own Id: OTP-16465 Aux Id: ERL-1165, OTP-6160, OTP-14589 </p> + </item> + <item> + <p> + Corrected the valid range of the <c>erl</c> command line + argument <seealso marker="erts:erl#+SDio"><c>+SDio + <NumberOfDirtyIoSchedulers></c></seealso> from + <c>0..1024</c> to <c>1..1024</c>. <c>+SDio 0</c> was + erroneously allowed which just caused the VM to crash on + the first dirty I/O job scheduled.</p> + <p> + Own Id: OTP-16481</p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 9.3.3.14</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A process calling <seealso + marker="erts:erlang#system_flag_multi_scheduling"><c>erlang:system_flag(multi_scheduling, + block)</c></seealso> could end up blocked waiting for the + operation to complete indefinitely.</p> + <p> + Own Id: OTP-16379</p> + </item> + </list> + </section> + +</section> + <section><title>Erts 9.3.3.13</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml index c54fe38bfd..2d16ff2074 100644 --- a/erts/doc/src/socket.xml +++ b/erts/doc/src/socket.xml @@ -505,6 +505,14 @@ </func> <func> + <name name="number_of" arity="0" since="OTP @OTP-16309@"/> + <fsummary>Get the number of active sockets.</fsummary> + <desc> + <p>Returns the number of active sockets.</p> + </desc> + </func> + + <func> <name name="open" arity="2" since="OTP 22.0"/> <name name="open" arity="3" since="OTP 22.0"/> <name name="open" arity="4" since="OTP 22.0"/> @@ -900,9 +908,9 @@ <name name="supports" arity="1" clause_i="1" since="OTP 22.0"/> <name name="supports" arity="1" clause_i="2" since="OTP 22.0"/> <name name="supports" arity="1" clause_i="3" since="OTP 22.0"/> - <name name="supports" arity="1" clause_i="4" since="OTP-22.0"/> - <name name="supports" arity="1" clause_i="5" since="@OTP-16153@"/> - <name name="supports" arity="1" clause_i="6" since="@OTP-16153@"/> + <name name="supports" arity="1" clause_i="4" since="OTP 22.0"/> + <name name="supports" arity="1" clause_i="5" since="OTP @OTP-16153@"/> + <name name="supports" arity="1" clause_i="6" since="OTP @OTP-16153@"/> <name name="supports" arity="1" clause_i="7" since="OTP 22.0"/> <name name="supports" arity="2" clause_i="1" since="OTP 22.0"/> <name name="supports" arity="2" clause_i="2" since="OTP 22.0"/> @@ -910,8 +918,8 @@ <name name="supports" arity="2" clause_i="4" since="OTP 22.0"/> <name name="supports" arity="2" clause_i="5" since="OTP 22.0"/> <name name="supports" arity="2" clause_i="6" since="OTP 22.0"/> - <name name="supports" arity="2" clause_i="7" since="@OTP-16153@"/> - <name name="supports" arity="2" clause_i="8" since="@OTP-16153@"/> + <name name="supports" arity="2" clause_i="7" since="OTP @OTP-16153@"/> + <name name="supports" arity="2" clause_i="8" since="OTP @OTP-16153@"/> <name name="supports" arity="2" clause_i="9" since="OTP 22.0"/> <name name="supports" arity="3" clause_i="1" since="OTP 22.0"/> <name name="supports" arity="3" clause_i="2" since="OTP 22.0"/> @@ -928,6 +936,51 @@ </desc> </func> + <func> + <name name="which_sockets" arity="0" since="OTP @OTP-16309@"/> + <name name="which_sockets" arity="1" since="OTP @OTP-16309@"/> + <fsummary>Get the current active sockets.</fsummary> + <desc> + <p>Returns a list of all sockets, according to the + filter rule.</p> + <p>There are several pre-made filter rule(s) and one general: </p> + <taglist> + <tag><c><![CDATA[inet | inet6]]></c></tag> + <item> + <p>Selection based on the domain of the socket. + <br/>Only a subset is valid. </p> + </item> + + <tag><c><![CDATA[stream | dgram | seqpacket]]></c></tag> + <item> + <p>Selection based on the type of the socket. + <br/>Only a subset is valid. </p> + </item> + + <tag><c><![CDATA[sctp | tcp | udp]]></c></tag> + <item> + <p>Selection based on the protocol of the socket. + <br/>Only a subset is valid. </p> + </item> + + <tag><c><![CDATA[pid()]]></c></tag> + <item> + <p>Selection base on which sockets has this pid as + Controlling Process. </p> + </item> + + <tag><c><![CDATA[fun((socket_info()) -> boolean())]]></c></tag> + <item> + <p>The general filter rule. + <br/>A fun that takes the socket info and returns a + <c><![CDATA[boolean()]]></c> + (<c><![CDATA[true]]></c> if the socket sould be included and + <c><![CDATA[false]]></c> if should not). </p> + </item> + </taglist> + </desc> + </func> + </funcs> <section> <title>Examples</title> diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 7f50372532..fb56eadf39 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -635,6 +635,7 @@ GENERATE += $(TTF_DIR)/driver_tab.c ifeq ($(USE_ESOCK), yes) ESOCK_PRELOAD_BEAM = \ + $(ERL_TOP)/erts/preloaded/ebin/socket_registry.beam \ $(ERL_TOP)/erts/preloaded/ebin/socket.beam \ $(ERL_TOP)/erts/preloaded/ebin/prim_net.beam else @@ -904,6 +905,7 @@ RUN_OBJS += \ $(OBJDIR)/module.o $(OBJDIR)/export.o \ $(OBJDIR)/register.o $(OBJDIR)/break.o \ $(OBJDIR)/erl_async.o $(OBJDIR)/erl_lock_check.o \ + $(OBJDIR)/erl_dyn_lock_check.o \ $(OBJDIR)/erl_gc.o $(OBJDIR)/erl_lock_count.o \ $(OBJDIR)/erl_posix_str.o \ $(OBJDIR)/erl_bits.o $(OBJDIR)/erl_math.o \ diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 57f3a53481..7046eb5e65 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -144,6 +144,8 @@ atom bsr_unicode atom build_type atom busy atom busy_dist_port +atom busy_limits_port +atom busy_limits_msgq atom busy_port atom call atom call_count @@ -291,6 +293,7 @@ atom gc_minor_start atom Ge='>=' atom generational atom get_all_trap +atom get_internal_state_blocked atom get_seq_token atom get_size atom get_tcw diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 857c929880..c530063fa1 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -377,6 +377,7 @@ finish_loading_1(BIF_ALIST_1) /* tracing or hipe need thread blocking */ erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); erts_thr_progress_block(); + erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); is_blocking = 1; break; } @@ -464,7 +465,6 @@ staging_epilogue(Process* c_p, int commit, Eterm res, int is_blocking, } if (is_blocking) { erts_thr_progress_unblock(); - erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); } erts_release_code_write_permission(); return res; @@ -605,8 +605,12 @@ BIF_RETTYPE erts_internal_check_dirty_process_code_2(BIF_ALIST_2) BIF_RET(am_false); state = erts_atomic32_read_nob(&rp->state); - dirty = (state & (ERTS_PSFLG_DIRTY_RUNNING - | ERTS_PSFLG_DIRTY_RUNNING_SYS)); + dirty = (state & ERTS_PSFLG_DIRTY_RUNNING); + /* + * Ignore ERTS_PSFLG_DIRTY_RUNNING_SYS (see + * comment in erts_execute_dirty_system_task() + * in erl_process.c). + */ if (!dirty) BIF_RET(am_normal); @@ -661,6 +665,7 @@ BIF_RETTYPE delete_module_1(BIF_ALIST_1) /* tracing or hipe need to go single threaded */ erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); erts_thr_progress_block(); + erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); is_blocking = 1; if (modp->curr.num_breakpoints) { erts_clear_module_break(modp); @@ -794,6 +799,7 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2) erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); erts_thr_progress_block(); + erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); is_blocking = 1; } @@ -1457,12 +1463,14 @@ rla_resume(void *literal_area) } +#ifdef DEBUG static ERTS_INLINE Sint rla_bc_read(int sched_ix, int block_ix) { return (Sint) erts_atomic_read_nob( &release_literal_areas.bc[sched_ix].u.block.counter[block_ix]); } +#endif static ERTS_INLINE Sint rla_bc_read_acqb(int sched_ix, int block_ix) diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 97eac05260..04e9db1f8e 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -4352,12 +4352,16 @@ BIF_RETTYPE list_to_ref_1(BIF_ALIST_1) make_boxed(&etp->header)); ASSERT(enp != erts_this_node); - etp->header = make_external_ref_header(n/2); +#if defined(ARCH_64) + etp->header = make_external_ref_header(n/2 + 1); +#else + etp->header = make_external_ref_header(n); +#endif etp->next = BIF_P->off_heap.first; etp->node = enp; i = 0; #if defined(ARCH_64) - etp->data.ui32[i] = n; + etp->data.ui32[i++] = n; #endif for (j = 0; j < n; j++) { etp->data.ui32[i] = refn[j]; diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 6379e4e04d..c1af14cc89 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -578,8 +578,8 @@ do_break(void) ASSERT(erts_thr_progress_is_blocking()); erts_printf("\n" - "BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded\n" - " (v)ersion (k)ill (D)b-tables (d)istribution\n"); + "BREAK: (a)bort (A)bort with dump (c)ontinue (p)roc info (i)nfo\n" + " (l)oaded (v)ersion (k)ill (D)b-tables (d)istribution\n"); while (1) { if ((i = sys_get_key(0)) <= 0) diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index ecb5291400..1293ad2d83 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -5164,6 +5164,7 @@ erts_processes_monitoring_nodes(Process *c_p) erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); erts_thr_progress_block(); + erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); erts_mtx_lock(&nodes_monitors_mtx); @@ -5194,7 +5195,6 @@ erts_processes_monitoring_nodes(Process *c_p) erts_mtx_unlock(&nodes_monitors_mtx); erts_thr_progress_unblock(); - erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); return ctxt.res; } diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 01b40abe2d..1064c89d84 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -75,7 +75,7 @@ static Export *gather_gc_info_res_trap; static Export *gather_system_check_res_trap; static Export *is_process_alive_trap; - +static Export *get_internal_state_blocked; #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) @@ -3881,8 +3881,7 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) } else if (ERTS_IS_ATOM_STR("node_and_dist_references", BIF_ARG_1)) { /* Used by node_container_SUITE (emulator) */ - Eterm res = erts_get_node_and_dist_references(BIF_P); - BIF_RET(res); + BIF_TRAP1(get_internal_state_blocked, BIF_P, BIF_ARG_1); } else if (ERTS_IS_ATOM_STR("monitoring_nodes", BIF_ARG_1)) { BIF_RET(erts_processes_monitoring_nodes(BIF_P)); @@ -4050,7 +4049,14 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) Eterm* tp = tuple_val(BIF_ARG_1); switch (arityval(tp[0])) { case 2: { - if (ERTS_IS_ATOM_STR("process_status", tp[1])) { + if (ERTS_IS_ATOM_STR("node_and_dist_references", tp[1])) { + if (tp[2] == am_blocked + && erts_is_multi_scheduling_blocked() > 0) { + Eterm res = erts_get_node_and_dist_references(BIF_P); + BIF_RET(res); + } + } + else if (ERTS_IS_ATOM_STR("process_status", tp[1])) { /* Used by timer process_SUITE, timer_bif_SUITE, and node_container_SUITE (emulator) */ if (is_internal_pid(tp[2])) { @@ -5213,6 +5219,10 @@ erts_bif_info_init(void) = erts_export_put(am_erts_internal, am_gather_system_check_result, 1); is_process_alive_trap = erts_export_put(am_erts_internal, am_is_process_alive, 1); + + get_internal_state_blocked = erts_export_put(am_erts_internal, + am_get_internal_state_blocked, + 1); process_info_init(); diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index ed825d3dda..63bfaf8572 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -691,6 +691,10 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) opts.spawn_type = ERTS_SPAWN_ANY; opts.argv = NULL; opts.parallelism = erts_port_parallelism; + opts.high_watermark = 8192; + opts.low_watermark = opts.high_watermark / 2; + opts.port_watermarks_set = 0; + opts.msgq_watermarks_set = 0; erts_osenv_init(&opts.envir); linebuf = 0; @@ -782,6 +786,62 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) opts.parallelism = 0; else goto badarg; + } else if (option == am_busy_limits_port) { + Uint high, low; + if (*tp == am_disabled) + low = high = ERL_DRV_BUSY_MSGQ_DISABLED; + else if (!is_tuple_arity(*tp, 2)) + goto badarg; + else { + Eterm *wtp = tuple_val(*tp); + if (!term_to_Uint(wtp[1], &low)) + goto badarg; + if (!term_to_Uint(wtp[2], &high)) + goto badarg; + if (high < ERL_DRV_BUSY_MSGQ_LIM_MIN) + goto badarg; + if (high > ERL_DRV_BUSY_MSGQ_LIM_MAX) + goto badarg; + if (low < ERL_DRV_BUSY_MSGQ_LIM_MIN) + goto badarg; + if (low > ERL_DRV_BUSY_MSGQ_LIM_MAX) + goto badarg; + if (high == ~((Uint) 0) || low == ~((Uint) 0)) + goto badarg; + if (low > high) + low = high; + } + opts.low_watermark = low; + opts.high_watermark = high; + opts.port_watermarks_set = !0; + } else if (option == am_busy_limits_msgq) { + Uint high, low; + if (*tp == am_disabled) + low = high = ERL_DRV_BUSY_MSGQ_DISABLED; + else if (!is_tuple_arity(*tp, 2)) + goto badarg; + else { + Eterm *wtp = tuple_val(*tp); + if (!term_to_Uint(wtp[1], &low)) + goto badarg; + if (!term_to_Uint(wtp[2], &high)) + goto badarg; + if (high < ERL_DRV_BUSY_MSGQ_LIM_MIN) + goto badarg; + if (high > ERL_DRV_BUSY_MSGQ_LIM_MAX) + goto badarg; + if (low < ERL_DRV_BUSY_MSGQ_LIM_MIN) + goto badarg; + if (low > ERL_DRV_BUSY_MSGQ_LIM_MAX) + goto badarg; + if (high == ~((Uint) 0) || low == ~((Uint) 0)) + goto badarg; + if (low > high) + low = high; + } + opts.low_msgq_watermark = low; + opts.high_msgq_watermark = high; + opts.msgq_watermarks_set = !0; } else { goto badarg; } @@ -820,6 +880,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) nargs = list_val(*nargs); } } + if (opts.read_write == 0) /* implement default */ opts.read_write = DO_READ|DO_WRITE; @@ -827,7 +888,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) if((linebuf && opts.packet_bytes) || (opts.redir_stderr && !opts.use_stdio)) { goto badarg; -} + } /* If we lacked an env option, fill in the global environment without * changes. */ diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index b31d5b86cb..4162a6c591 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -1125,6 +1125,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) if ( (key == am_call_time) || (key == am_all)) { erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN); erts_thr_progress_block(); + erts_proc_lock(p, ERTS_PROC_LOCK_MAIN); } erts_mtx_lock(&erts_dirty_bp_ix_mtx); @@ -1134,7 +1135,6 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) erts_mtx_unlock(&erts_dirty_bp_ix_mtx); if ( (key == am_call_time) || (key == am_all)) { erts_thr_progress_unblock(); - erts_proc_lock(p, ERTS_PROC_LOCK_MAIN); } switch (r) { @@ -2194,6 +2194,7 @@ system_monitor(Process *p, Eterm monitor_pid, Eterm list) system_blocked = 1; erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN); erts_thr_progress_block(); + erts_proc_lock(p, ERTS_PROC_LOCK_MAIN); if (!erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, monitor_pid, 0)) goto error; @@ -2233,7 +2234,6 @@ system_monitor(Process *p, Eterm monitor_pid, Eterm list) erts_system_monitor_flags.busy_dist_port = !!busy_dist_port; erts_thr_progress_unblock(); - erts_proc_lock(p, ERTS_PROC_LOCK_MAIN); BIF_RET(prev); } @@ -2241,7 +2241,6 @@ system_monitor(Process *p, Eterm monitor_pid, Eterm list) if (system_blocked) { erts_thr_progress_unblock(); - erts_proc_lock(p, ERTS_PROC_LOCK_MAIN); } BIF_ERROR(p, BADARG); diff --git a/erts/emulator/beam/erl_db_catree.c b/erts/emulator/beam/erl_db_catree.c index 176966edb5..4e08f89692 100644 --- a/erts/emulator/beam/erl_db_catree.c +++ b/erts/emulator/beam/erl_db_catree.c @@ -2289,7 +2289,10 @@ static int db_lookup_dbterm_catree(Process *p, DbTable *tbl, Eterm key, Eterm ob static void db_finalize_dbterm_catree(int cret, DbUpdateHandle *handle) { DbTableCATree *tb = &(handle->tb->catree); - db_finalize_dbterm_tree_common(cret, handle, NULL); + db_finalize_dbterm_tree_common(cret, + handle, + &handle->u.catree.base_node->u.base.root, + NULL); wunlock_adapt_base_node(tb, handle->u.catree.base_node, handle->u.catree.parent, handle->u.catree.current_level); diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 17d20b2f77..5508f5c34e 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -3148,16 +3148,19 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle) } WUNLOCK_HASH(lck); - DEC_NITEMS(tb); + if (!(handle->flags & DB_INC_TRY_GROW)) + DEC_NITEMS(tb); try_shrink(tb); } else { if (handle->flags & DB_MUST_RESIZE) { + ASSERT(cret == DB_ERROR_NONE); db_finalize_resize(handle, offsetof(HashDbTerm,dbterm)); free_me = b; } if (handle->flags & DB_INC_TRY_GROW) { int nactive; int nitems = INC_NITEMS(tb); + ASSERT(cret == DB_ERROR_NONE); WUNLOCK_HASH(lck); nactive = NACTIVE(tb); diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index 723b3c5d29..49158108a2 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -3287,7 +3287,9 @@ db_lookup_dbterm_tree(Process *p, DbTable *tbl, Eterm key, Eterm obj, return db_lookup_dbterm_tree_common(p, tbl, &tb->root, key, obj, handle, tb); } -void db_finalize_dbterm_tree_common(int cret, DbUpdateHandle *handle, +void db_finalize_dbterm_tree_common(int cret, + DbUpdateHandle *handle, + TreeDbTerm **root, DbTableTree *stack_container) { DbTable *tbl = handle->tb; @@ -3295,7 +3297,12 @@ void db_finalize_dbterm_tree_common(int cret, DbUpdateHandle *handle, if (handle->flags & DB_NEW_OBJECT && cret != DB_ERROR_NONE) { Eterm ret; - db_erase_tree(tbl, GETKEY(&tbl->common, bp->dbterm.tpl), &ret); + db_erase_tree_common(tbl, + root, + GETKEY(&tbl->common, bp->dbterm.tpl), + &ret, + (stack_container == NULL ? + NULL : &stack_container->static_stack)); } else if (handle->flags & DB_MUST_RESIZE) { db_finalize_resize(handle, offsetof(TreeDbTerm,dbterm)); reset_static_stack(stack_container); @@ -3313,7 +3320,7 @@ db_finalize_dbterm_tree(int cret, DbUpdateHandle *handle) { DbTable *tbl = handle->tb; DbTableTree *tb = &tbl->tree; - db_finalize_dbterm_tree_common(cret, handle, tb); + db_finalize_dbterm_tree_common(cret, handle, &tb->root, tb); } static int db_get_binary_info_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret) diff --git a/erts/emulator/beam/erl_db_tree_util.h b/erts/emulator/beam/erl_db_tree_util.h index 14afbd56f7..90ac8c7ba7 100644 --- a/erts/emulator/beam/erl_db_tree_util.h +++ b/erts/emulator/beam/erl_db_tree_util.h @@ -167,7 +167,9 @@ void db_foreach_offheap_tree_common(TreeDbTerm *root, int db_lookup_dbterm_tree_common(Process *p, DbTable *tbl, TreeDbTerm **root, Eterm key, Eterm obj, DbUpdateHandle* handle, DbTableTree *stack_container); -void db_finalize_dbterm_tree_common(int cret, DbUpdateHandle *handle, +void db_finalize_dbterm_tree_common(int cret, + DbUpdateHandle *handle, + TreeDbTerm **root, DbTableTree *stack_container); Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key); diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index ed09a34ae4..2a0b28f232 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -2865,9 +2865,6 @@ Eterm db_add_counter(Eterm** hpp, Wterm counter, Eterm incr) /* Must be called to read elements after db_lookup_dbterm. ** Will decompress if needed. -** HEALFWORD_HEAP: -** Will convert from relative to Wterm format if needed. -** (but only on top level, tuples and lists will still contain rterms) */ Wterm db_do_read_element(DbUpdateHandle* handle, Sint position) { diff --git a/erts/emulator/beam/erl_drv_thread.c b/erts/emulator/beam/erl_drv_thread.c index c5dbc87dee..d90b8b5e07 100644 --- a/erts/emulator/beam/erl_drv_thread.c +++ b/erts/emulator/beam/erl_drv_thread.c @@ -23,6 +23,7 @@ #endif #include "global.h" +#include "erl_dyn_lock_check.h" #include <string.h> #if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) @@ -57,6 +58,9 @@ struct ErlDrvMutex_ { erts_lcnt_ref_t lcnt; #endif char *name; +#ifdef ERTS_DYN_LOCK_CHECK + erts_dlc_t dlc; +#endif }; struct ErlDrvCond_ { @@ -163,6 +167,9 @@ erl_drv_mutex_create(char *name) } else { dmtx->name = no_name; } +#ifdef ERTS_DYN_LOCK_CHECK + erts_dlc_create_lock(&dmtx->dlc, name); +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_init_ref_x(&dmtx->lcnt, dmtx->name, NIL, ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_IO); @@ -198,6 +205,9 @@ erl_drv_mutex_trylock(ErlDrvMutex *dmtx) if (!dmtx) fatal_error(EINVAL, "erl_drv_mutex_trylock()"); res = ethr_mutex_trylock(&dmtx->mtx); +#ifdef ERTS_DYN_LOCK_CHECK + erts_dlc_trylock(&dmtx->dlc, res != EBUSY); +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_trylock(&dmtx->lcnt, res); #endif @@ -209,6 +219,9 @@ erl_drv_mutex_lock(ErlDrvMutex *dmtx) { if (!dmtx) fatal_error(EINVAL, "erl_drv_mutex_lock()"); +#ifdef ERTS_DYN_LOCK_CHECK + erts_dlc_lock(&dmtx->dlc); +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_lock(&dmtx->lcnt); #endif @@ -223,6 +236,9 @@ erl_drv_mutex_unlock(ErlDrvMutex *dmtx) { if (!dmtx) fatal_error(EINVAL, "erl_drv_mutex_unlock()"); +#ifdef ERTS_DYN_LOCK_CHECK + erts_dlc_unlock(&dmtx->dlc); +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_unlock(&dmtx->lcnt); #endif diff --git a/erts/emulator/beam/erl_dyn_lock_check.c b/erts/emulator/beam/erl_dyn_lock_check.c new file mode 100644 index 0000000000..000d5cf247 --- /dev/null +++ b/erts/emulator/beam/erl_dyn_lock_check.c @@ -0,0 +1,553 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2001-2017. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +/* Description: A dynamic lock order checker. + * A global locking order is recorded during runtime + * and continuously checked against itself. + * + * Author: Sverker Eriksson + */ + +/* + * The primary objective is to check the order in which locks are seized + * to avoid deadlocks. The strategy is to continuously construct a directed + * graph describing the order in which locks have been seized. Each edge A->B in + * the graph describes a locking order; I held lock A while locking B. Trylocks + * do not introduce edges in the graph. For each added edge we check that we + * don't introduce cycles in the graph. A cycle would indicate a potential + * deadlock bug, waiting to happen. + * + * We assume that locks are primarily ordered by their lock _types_ + * and secondarily by instance information of locks of the same type. No lock + * order checking is implemented between lock instances of the same type (yet). + * + * The name given when a lock is created is used as identifying its type. + * The '[' character can be used as a delimiter between lock type and + * instance information. Example: "esock.wrt[17]" is of type "esock.wrt". + * + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "erl_dyn_lock_check.h" +#ifdef ERTS_DYN_LOCK_CHECK + +#include "sys.h" +#include "erl_threads.h" + +#define DLC_ASSERT(X) ERTS_ASSERT(X) + +#ifdef ERTS_DYN_LOCK_CHECK_INTERNAL +# define MAX_LOCK_TYPES (64*2) +#else +# define MAX_LOCK_TYPES (32) +#endif + +#define BITS_PER_WORD (sizeof(UWord)*8) +#define LOCK_TYPE_WORDS ((MAX_LOCK_TYPES-1)/BITS_PER_WORD + 1) +#define MAX_LOCK_NAME_SZ 64 + +static erts_atomic_t n_lock_types; +static erts_mtx_t lock_types_mtx; + +struct lock_type +{ + char name[MAX_LOCK_NAME_SZ]; +}; + +static struct lock_type lock_types[MAX_LOCK_TYPES]; + +static erts_tsd_key_t dlc_thread_key; + +/* Thread specific data */ +typedef struct +{ + UWord locked_now[LOCK_TYPE_WORDS]; + /* Bit vector with all lock types currently held by this thread */ + + UWord locked_before[LOCK_TYPE_WORDS]; + /* Bit vector same as 'locked_now' PLUS all unlocked locks that were locked + * by this thread ~before~ the locks in 'locked_now'. + * A lock in 'locked_before' is only cleared when all locked after it + * have been unlocked. + * + * Example 1: + * 1. Lock A: locked_now = A locked_before = A + * 2. Lock B: locked_now = A|B locked_before = A|B + * 3. Unlock A: locked_now = B locked_before = A|B + * 4. Lock C: locked_now = B|C locked_before = A|B|C + * 5. Unlock B: locked_now = C locked_before = A|B|C + * 6. Unlock C: locked_now = locked_before = + * + * Example 2: + * 1. Lock A: locked_now = A locked_before = A + * 2. Trylock B: locked_now = A|B locked_before = A|B + * 3. Unlock A: locked_now = B locked_before = B + * 4. Lock C: locked_now = B|C locked_before = B|C + * 5. Unlock B: locked_now = C locked_before = B|C + * 6. Unlock C: locked_now = locked_before = + * + * The trylock of B imposes no ordering dependency to A (locked before B), + * but it will have a dependency to C (locked after B). + */ + + struct { + unsigned ix; /* lock type id (bit index) */ + unsigned cnt; /* nr of locked instances of this type (may be 0) */ + unsigned trylock; /* true if only trylocked instances */ + } lock_order[MAX_LOCK_TYPES]; + /* The locks in 'locked_before' ordered the way they were locked by this thread */ + + unsigned n_locked; + /* Number or lock types in 'locked_before' and 'lock_order' */ + +} dlc_thread_t; + +static erts_atomic_t locked_before[MAX_LOCK_TYPES][LOCK_TYPE_WORDS]; +/* The recorded global lock order as a bit matrix. + * + * Bit A is set in locked_before[B] if A has been locked before B. + */ + +static int check_lock_order(dlc_thread_t*, erts_dlc_t*); + +/*#define DLC_UNIT_TEST*/ +#ifdef DLC_UNIT_TEST +static int is_dlc_unit_test = 0; +static void dlc_unit_test(void); +# define DLC_IS_UNIT_TEST() (is_dlc_unit_test) +#else +# define DLC_IS_UNIT_TEST() (0) +#endif + +static int dlc_initialized = 0; + +void erts_dlc_init(void) +{ + int i, j; + erts_atomic_init_nob(&n_lock_types, 0); + erts_tsd_key_create(&dlc_thread_key, "dyn_lock_check"); + + for (i = 0; i < MAX_LOCK_TYPES; i++) { + for (j = 0; j < LOCK_TYPE_WORDS; j++) + erts_atomic_init_nob(&locked_before[i][j], 0); + } + + erts_mtx_init(&lock_types_mtx, "dyn_lock_check", NIL, + (ERTS_LOCK_FLAGS_PROPERTY_STATIC | + ERTS_LOCK_FLAGS_CATEGORY_GENERIC)); + + dlc_initialized = 1; + +#ifdef DLC_UNIT_TEST + dlc_unit_test(); +#endif +} + +void erts_dlc_create_lock(erts_dlc_t* dlc, const char* name) +{ + erts_aint_t i, n = erts_atomic_read_nob(&n_lock_types); + int name_len; + + for (i = 0; name[i]; i++) { + if (name[i] == '[') + break; + } + name_len = i; + + for (i=0; i < n; i++) { + if (sys_strncmp(name, lock_types[i].name, name_len) == 0) { + dlc->ix = i; + return; /* already exists */ + } + } + + if (dlc_initialized) + erts_mtx_lock(&lock_types_mtx); + else + DLC_ASSERT(n == 0); + + n = erts_atomic_read_nob(&n_lock_types); + + for ( ; i < n; i++) { + if (sys_strncmp(name, lock_types[i].name, name_len) == 0) { + dlc->ix = i; + goto done; /* already exists (race) */ + } + } + + ERTS_ASSERT(n < MAX_LOCK_TYPES); + ERTS_ASSERT(name_len < MAX_LOCK_NAME_SZ); + sys_strncpy(lock_types[n].name, name, name_len); + lock_types[n].name[name_len] = 0; + erts_atomic_set_nob(&n_lock_types, n+1); + dlc->ix = n; + +done: + if (dlc_initialized) + erts_mtx_unlock(&lock_types_mtx); +} + +#define IX_TO_BIT(IX) ((UWord)1 << ((IX) % BITS_PER_WORD)) +#define IX_TO_WORD(IX) ((IX) / BITS_PER_WORD) + +static dlc_thread_t *get_thr(void) +{ + dlc_thread_t *thr = (dlc_thread_t*) erts_tsd_get(dlc_thread_key); + int i; + + if (!thr) { + thr = malloc(sizeof(dlc_thread_t)); + for (i = 0; i < LOCK_TYPE_WORDS; i++) { + thr->locked_now[i] = 0; + thr->locked_before[i] = 0; + } + thr->n_locked = 0; + erts_tsd_set(dlc_thread_key, thr); + } + return thr; +} + +static ERTS_INLINE int is_bit_set(unsigned ix, const UWord* words) +{ + DLC_ASSERT(ix < MAX_LOCK_TYPES); + return (words[IX_TO_WORD(ix)] & IX_TO_BIT(ix)) != (UWord)0; +} + +static ERTS_INLINE int is_any_bit_set(const UWord* words) +{ + UWord bor = 0; + int i=0; + for (i = 0; i < LOCK_TYPE_WORDS; i++) + bor |= words[i]; + return bor != (UWord)0; +} + +static ERTS_INLINE void set_bit(unsigned ix, UWord* words) +{ + DLC_ASSERT(ix < MAX_LOCK_TYPES); + words[IX_TO_WORD(ix)] |= IX_TO_BIT(ix); +} + +static ERTS_INLINE void clr_bit(unsigned ix, UWord* words) +{ + DLC_ASSERT(ix < MAX_LOCK_TYPES); + words[IX_TO_WORD(ix)] &= ~IX_TO_BIT(ix); +} + +int erts_dlc_lock(erts_dlc_t* dlc) +{ + dlc_thread_t *thr = get_thr(); + + if (thr->n_locked) { + int i; + + DLC_ASSERT(is_any_bit_set(thr->locked_now)); + + /* + * Check if we introduce new lock dependencies + */ + for (i=0; i < LOCK_TYPE_WORDS; i++) { + UWord before = erts_atomic_read_nob(&locked_before[dlc->ix][i]); + UWord new_before = (thr->locked_before[i] & ~before); + + if (new_before) { + if (!check_lock_order(thr, dlc)) { + DLC_ASSERT(DLC_IS_UNIT_TEST()); + return 0; + } + erts_atomic_read_bor_mb(&locked_before[dlc->ix][i], + new_before); + /* check again to detect racing deadlock */ + if (!check_lock_order(thr, dlc)) { + DLC_ASSERT(DLC_IS_UNIT_TEST()); + /* can't continue test as 'locked_before' is inconsistent */ + abort(); + } + } + } + + if (is_bit_set(dlc->ix, thr->locked_now)) { + /* + * Lock of this type already held. + * Must be other instance of last locked lock + */ + DLC_ASSERT(is_bit_set(dlc->ix, thr->locked_before)); + i = thr->n_locked-1; + while (dlc->ix != thr->lock_order[i].ix) { + DLC_ASSERT(thr->lock_order[i].trylock); + i--; + DLC_ASSERT(i >= 0); + } + thr->lock_order[i].cnt++; + thr->lock_order[i].trylock = 0; + return 1; + } + } + else { + DLC_ASSERT(!is_any_bit_set(thr->locked_now)); + DLC_ASSERT(!is_any_bit_set(thr->locked_before)); + } + set_bit(dlc->ix, thr->locked_now); + set_bit(dlc->ix, thr->locked_before); + thr->lock_order[thr->n_locked].ix = dlc->ix; + thr->lock_order[thr->n_locked].cnt = 1; + thr->lock_order[thr->n_locked].trylock = 0; + thr->n_locked++; + return 1; +} + +static ERTS_INLINE int get_lock_order(dlc_thread_t* thr, + erts_dlc_t* dlc) +{ + int i; + DLC_ASSERT(is_bit_set(dlc->ix, thr->locked_before)); + for (i = 0; ; i++) { + DLC_ASSERT(i < thr->n_locked); + if (dlc->ix == thr->lock_order[i].ix) + return i; + } +} + +void erts_dlc_trylock(erts_dlc_t* dlc, int locked) +{ + dlc_thread_t *thr = get_thr(); + + if (!locked) { + /* We have no way to detect trylock of self-locked instance (yet) + * so nothing to do here. */ + return; + } + + if (is_bit_set(dlc->ix, thr->locked_now)) { + int i = get_lock_order(thr, dlc); + DLC_ASSERT(thr->lock_order[i].cnt > 0); + thr->lock_order[i].cnt++; + /* keep .trylock as is */ + } + else { + set_bit(dlc->ix, thr->locked_now); + + if (!is_bit_set(dlc->ix, thr->locked_before)) { + set_bit(dlc->ix, thr->locked_before); + thr->lock_order[thr->n_locked].ix = dlc->ix; + thr->lock_order[thr->n_locked].cnt = 1; + thr->lock_order[thr->n_locked].trylock = 1; + thr->n_locked++; + } + else { + int i = get_lock_order(thr, dlc); + DLC_ASSERT(thr->lock_order[i].cnt == 0); + thr->lock_order[i].cnt = 1; + thr->lock_order[i].trylock = 1; + } + } +} + +void erts_dlc_unlock(erts_dlc_t* dlc) +{ + dlc_thread_t *thr = (dlc_thread_t*) erts_tsd_get(dlc_thread_key); + int i; + + ERTS_ASSERT(thr); + ERTS_ASSERT(is_bit_set(dlc->ix, thr->locked_now)); + DLC_ASSERT(is_bit_set(dlc->ix, thr->locked_before)); + DLC_ASSERT(thr->n_locked > 0); + + i = get_lock_order(thr, dlc); + + DLC_ASSERT(thr->lock_order[i].cnt > 0); + thr->lock_order[i].cnt--; + if (thr->lock_order[i].cnt > 0) + return; /* still locked by other instance */ + + clr_bit(dlc->ix, thr->locked_now); + + /* + * Now clear and forget all our unlocked locks (including this one) + * THAT was not locked *before* any of our still held locked locks. + */ + for (i = thr->n_locked-1; i >= 0; i--) { + if (thr->lock_order[i].cnt) { + DLC_ASSERT(is_bit_set(thr->lock_order[i].ix, thr->locked_now)); + if (!thr->lock_order[i].trylock) { + /* A locked lock, must remember it and all locked before it. */ + break; + } + } + else { /* forget this unlocked lock */ + int j; + + DLC_ASSERT(!is_bit_set(thr->lock_order[i].ix, thr->locked_now)); + DLC_ASSERT(is_bit_set(thr->lock_order[i].ix, thr->locked_before)); + clr_bit(thr->lock_order[i].ix, thr->locked_before); + thr->n_locked--; + + /* and compact all trylocks that we may have skipped over */ + for (j = i; j < thr->n_locked; j++) { + DLC_ASSERT(thr->lock_order[j+1].trylock); + thr->lock_order[j] = thr->lock_order[j+1]; + } + } + } +} + +static int check_lock_order(dlc_thread_t *thr, erts_dlc_t* dlc) +{ + const UWord lock_bit = IX_TO_BIT(dlc->ix % 64); + const unsigned lock_word = IX_TO_WORD(dlc->ix); + int i, error = 0; + + for (i = 0; i < thr->n_locked; i++) { + const unsigned ix = thr->lock_order[i].ix; + + if (ix != dlc->ix && + lock_bit & erts_atomic_read_nob(&locked_before[ix][lock_word])) { + if (!error) { + error = 1; + erts_fprintf(stderr, "###### DYNAMIC LOCK ORDER VIOLATION ######\n"); + erts_fprintf(stderr, "# Trying to lock '%s'\n", lock_types[dlc->ix].name); + } + erts_fprintf(stderr, "# while '%s' is held\n", + lock_types[thr->lock_order[i].ix].name); + } + } + if (error) { + if (DLC_IS_UNIT_TEST()) + return 0; + abort(); + } + return 1; +} + +#ifdef DLC_UNIT_TEST + +static void dlc_clear_order(void) +{ + int i, j, n = erts_atomic_read_nob(&n_lock_types); + + for (i = 0; i < n; i++) { + for (j = 0; j < LOCK_TYPE_WORDS; j++) + erts_atomic_set_nob(&locked_before[i][j], 0); + } +} + +static void dlc_unit_test(void) +{ + erts_aint_t save_n_lock_types = erts_atomic_read_nob(&n_lock_types); + dlc_thread_t* thr = get_thr(); + dlc_thread_t save_thr = *thr; + erts_dlc_t A,B,C,D,E,F; + + ERTS_ASSERT(save_n_lock_types <= 1); /* no need to save existing order */ + + is_dlc_unit_test = 1; + erts_dlc_create_lock(&A, "A"); + erts_dlc_create_lock(&B, "B"); + erts_dlc_create_lock(&C, "C"); + erts_dlc_create_lock(&D, "D"); + erts_dlc_create_lock(&E, "E"); + erts_dlc_create_lock(&F, "F"); + + ERTS_ASSERT(erts_dlc_lock(&A)); + ERTS_ASSERT(erts_dlc_lock(&C)); + ERTS_ASSERT(!erts_dlc_lock(&A)); + + erts_dlc_unlock(&A); + ERTS_ASSERT(!erts_dlc_lock(&A)); + erts_dlc_unlock(&C); + ERTS_ASSERT(erts_dlc_lock(&A)); + ERTS_ASSERT(erts_dlc_lock(&B)); + ERTS_ASSERT(erts_dlc_lock(&C)); + erts_dlc_unlock(&A); + erts_dlc_unlock(&B); + erts_dlc_unlock(&C); + ERTS_ASSERT(erts_dlc_lock(&A)); + ERTS_ASSERT(erts_dlc_lock(&C)); + ERTS_ASSERT(!erts_dlc_lock(&B)); + erts_dlc_unlock(&A); + erts_dlc_unlock(&C); + + dlc_clear_order(); + + ERTS_ASSERT(erts_dlc_lock(&A)); + ERTS_ASSERT(erts_dlc_lock(&B)); + erts_dlc_unlock(&A); + ERTS_ASSERT(erts_dlc_lock(&C)); + erts_dlc_unlock(&B); + ERTS_ASSERT(erts_dlc_lock(&D)); + erts_dlc_unlock(&C); + ERTS_ASSERT(erts_dlc_lock(&E)); + erts_dlc_unlock(&D); + ERTS_ASSERT(erts_dlc_lock(&F)); + erts_dlc_unlock(&E); + erts_dlc_unlock(&F); + ERTS_ASSERT(erts_dlc_lock(&F)); + ERTS_ASSERT(!erts_dlc_lock(&A)); + erts_dlc_unlock(&F); + + dlc_clear_order(); + ERTS_ASSERT(erts_dlc_lock(&A)); + erts_dlc_trylock(&B, 1); + erts_dlc_unlock(&A); + ERTS_ASSERT(erts_dlc_lock(&A)); + erts_dlc_unlock(&A); + erts_dlc_unlock(&B); + + dlc_clear_order(); + ERTS_ASSERT(erts_dlc_lock(&A)); + ERTS_ASSERT(erts_dlc_lock(&B)); + ERTS_ASSERT(erts_dlc_lock(&C)); + erts_dlc_trylock(&D, 1); + erts_dlc_trylock(&E, 1); + ERTS_ASSERT(erts_dlc_lock(&F)); + erts_dlc_unlock(&C); + erts_dlc_unlock(&F); + ERTS_ASSERT(erts_dlc_lock(&B)); + erts_dlc_unlock(&B); + ERTS_ASSERT(!erts_dlc_lock(&A)); + erts_dlc_unlock(&B); + ERTS_ASSERT(erts_dlc_lock(&A)); + erts_dlc_unlock(&A); + erts_dlc_unlock(&A); + erts_dlc_unlock(&D); + erts_dlc_unlock(&E); + + dlc_clear_order(); + ERTS_ASSERT(erts_dlc_lock(&A)); + erts_dlc_trylock(&B, 1); + erts_dlc_trylock(&C, 1); + ERTS_ASSERT(erts_dlc_lock(&B)); + erts_dlc_unlock(&B); + ERTS_ASSERT(!erts_dlc_lock(&C)); + + /* Restore */ + is_dlc_unit_test = 0; + dlc_clear_order(); + erts_atomic_set_nob(&n_lock_types, save_n_lock_types); + *thr = save_thr; +} +#endif /* DLC_UNIT_TEST */ + +#endif /* ERTS_DYN_LOCK_CHECK */ + + + diff --git a/erts/emulator/beam/erl_dyn_lock_check.h b/erts/emulator/beam/erl_dyn_lock_check.h new file mode 100644 index 0000000000..0ff8e4cafa --- /dev/null +++ b/erts/emulator/beam/erl_dyn_lock_check.h @@ -0,0 +1,61 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2001-2017. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +/* Description: A dynamic lock order checker. + * A global locking order is recorded during runtime + * and continuously checked against itself. + * + * Author: Sverker Eriksson + */ + +#ifndef ERL_DYN_LOCK_CHECK_H__ +#define ERL_DYN_LOCK_CHECK_H__ + +#ifdef ERTS_ENABLE_LOCK_CHECK +/* + * ERTS_DYN_LOCK_CHECK enables dynamic lock checker + * on NIF and driver mutexes and rwlocks. + */ +# define ERTS_DYN_LOCK_CHECK +#endif + +#ifdef ERTS_DYN_LOCK_CHECK + +/* + * ERTS_DYN_LOCK_CHECK_INTERNAL will also enable dynamic lock checker + * on ERTS internal mutexes and rwlocks. + */ +/*#define ERTS_DYN_LOCK_CHECK_INTERNAL*/ + + +/* The struct to put in each lock instances */ +typedef struct { + unsigned ix; +} erts_dlc_t; + +void erts_dlc_create_lock(erts_dlc_t* dlc, const char* name); +int erts_dlc_lock(erts_dlc_t* dlc); +void erts_dlc_trylock(erts_dlc_t* dlc, int locked); +void erts_dlc_unlock(erts_dlc_t* dlc); +void erts_dlc_init(void); + +#endif /* ERTS_DYN_LOCK_CHECK */ + +#endif /* !ERL_DYN_LOCK_CHECK_H__ */ diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 547e4064a2..5f0352f5c0 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -694,7 +694,7 @@ void erts_usage(void) erts_fprintf(stderr, "-SDPcpu p1:p2 specify dirty CPU schedulers (p1) and dirty CPU schedulers\n"); erts_fprintf(stderr, " online (p2) as percentages of logical processors configured\n"); erts_fprintf(stderr, " and logical processors available, respectively\n"); - erts_fprintf(stderr, "-SDio n set number of dirty I/O schedulers, valid range is [0-%d]\n", + erts_fprintf(stderr, "-SDio n set number of dirty I/O schedulers, valid range is [1-%d]\n", ERTS_MAX_NO_OF_DIRTY_IO_SCHEDULERS); erts_fprintf(stderr, "-t size set the maximum number of atoms the emulator can handle\n"); erts_fprintf(stderr, " valid range is [%d-%d]\n", @@ -1054,7 +1054,7 @@ early_init(int *argc, char **argv) /* } else if (sys_strncmp(type, "io", 2) == 0) { arg = get_arg(argv[i]+5, argv[i+1], &i); dirty_io_scheds = atoi(arg); - if (dirty_io_scheds < 0 || + if (dirty_io_scheds < 1 || dirty_io_scheds > ERTS_MAX_NO_OF_DIRTY_IO_SCHEDULERS) { erts_fprintf(stderr, "bad number of dirty I/O schedulers %s\n", diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index ba61f721b4..b391c05643 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -147,6 +147,7 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "mtrace_op", NULL }, { "instr_x", NULL }, { "instr", NULL }, + { "dyn_lock_check", NULL }, { "alcu_allocator", "index" }, { "mseg", NULL }, { "get_time", NULL }, diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 1fbe362330..27959ba2bb 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -2209,6 +2209,10 @@ static ErlNifResourceType* find_resource_type(Eterm module, Eterm name) #define in_area(ptr,start,nbytes) \ ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes)) +static ERTS_INLINE int rt_have_callbacks(ErlNifResourceType* rt) +{ + return rt->dtor != NULL; +} static void close_lib(struct erl_module_nif* lib) { @@ -2231,7 +2235,7 @@ static void steal_resource_type(ErlNifResourceType* type) { struct erl_module_nif* lib = type->owner; - if (type->dtor != NULL + if (rt_have_callbacks(type) && erts_refc_dectest(&lib->rt_dtor_cnt, 0) == 0 && lib->mod == NULL) { /* last type with destructor gone, close orphan lib */ @@ -2244,6 +2248,11 @@ static void steal_resource_type(ErlNifResourceType* type) } } +static void resource_dtor_nop(ErlNifEnv* env, void* obj) +{ + /* do nothing */ +} + /* The opened_rt_list is used by enif_open_resource_type() * in order to rollback "creates" and "take-overs" in case the load fails. */ @@ -2306,6 +2315,12 @@ ErlNifResourceType* open_resource_type(ErlNifEnv* env, sys_memzero(&ort->new_callbacks, sizeof(ErlNifResourceTypeInit)); ASSERT(sizeof_init > 0 && sizeof_init <= sizeof(ErlNifResourceTypeInit)); sys_memcpy(&ort->new_callbacks, init, sizeof_init); + if (!ort->new_callbacks.dtor && (ort->new_callbacks.down || + ort->new_callbacks.stop)) { + /* Set dummy dtor for fast rt_have_callbacks() + * This case should be rare anyway */ + ort->new_callbacks.dtor = resource_dtor_nop; + } ort->next = opened_rt_list; opened_rt_list = ort; } @@ -2362,7 +2377,7 @@ static void commit_opened_resource_types(struct erl_module_nif* lib) type->stop = ort->new_callbacks.stop; type->down = ort->new_callbacks.down; - if (type->dtor != NULL) { + if (rt_have_callbacks(type)) { erts_refc_inc(&lib->rt_dtor_cnt, 1); } erts_refc_inc(&lib->rt_cnt, 1); @@ -4169,6 +4184,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) /* Block system (is this the right place to do it?) */ erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); erts_thr_progress_block(); + erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); /* Find calling module */ ASSERT(BIF_P->current != NULL); @@ -4276,17 +4292,12 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) ret = load_nif_error(BIF_P,bad_lib,"Function not found %T:%s/%u", mod_atom, f->name, f->arity); } - else if (f->flags) { - /* - * If the flags field is non-zero and this emulator was - * built with dirty scheduler support, check that the flags - * value is legal. But if this emulator was built without - * dirty scheduler support, treat a non-zero flags field as - * a load error. - */ - if (f->flags != ERL_NIF_DIRTY_JOB_IO_BOUND && f->flags != ERL_NIF_DIRTY_JOB_CPU_BOUND) - ret = load_nif_error(BIF_P, bad_lib, "Illegal flags field value %d for NIF %T:%s/%u", - f->flags, mod_atom, f->name, f->arity); + else if (f->flags != 0 && + f->flags != ERL_NIF_DIRTY_JOB_IO_BOUND && + f->flags != ERL_NIF_DIRTY_JOB_CPU_BOUND) { + ret = load_nif_error(BIF_P, bad_lib, + "Illegal flags field value %d for NIF %T:%s/%u", + f->flags, mod_atom, f->name, f->arity); } else if (erts_codeinfo_to_code(ci_pp[1]) - erts_codeinfo_to_code(ci_pp[0]) < BEAM_NIF_MIN_FUNC_SZ) @@ -4383,7 +4394,6 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) } erts_thr_progress_unblock(); - erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); erts_release_code_write_permission(); erts_free(ERTS_ALC_T_TMP, lib_name); @@ -4413,7 +4423,7 @@ erts_unload_nif(struct erl_module_nif* lib) rt->next = NULL; rt->prev = NULL; if (erts_refc_dectest(&rt->refc, 0) == 0) { - if (rt->dtor != NULL) { + if (rt_have_callbacks(rt)) { erts_refc_dec(&lib->rt_dtor_cnt, 0); } erts_refc_dec(&lib->rt_cnt, 0); diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 0564aec846..51e6a4dc40 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -1270,7 +1270,9 @@ erts_get_node_and_dist_references(struct process *proc) erts_proc_unlock(proc, ERTS_PROC_LOCK_MAIN); erts_thr_progress_block(); - /* No need to lock any thing since we are alone... */ + erts_proc_lock(proc, ERTS_PROC_LOCK_MAIN); + /* No need to lock any thing else since we are alone... + ... except dirty stuff... (?) */ if (references_atoms_need_init) { INIT_AM(heap); @@ -1325,7 +1327,6 @@ erts_get_node_and_dist_references(struct process *proc) clear_system(); erts_thr_progress_unblock(); - erts_proc_lock(proc, ERTS_PROC_LOCK_MAIN); return res; } diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c index 2347527e28..f8d82a8f98 100644 --- a/erts/emulator/beam/erl_proc_sig_queue.c +++ b/erts/emulator/beam/erl_proc_sig_queue.c @@ -560,8 +560,7 @@ erts_make_dirty_proc_handled(Eterm pid, ErtsMessage *mp; Process *sig_handler; - ASSERT(state & (ERTS_PSFLG_DIRTY_RUNNING | - ERTS_PSFLG_DIRTY_RUNNING_SYS)); + ASSERT(state & ERTS_PSFLG_DIRTY_RUNNING); if (prio < 0) prio = (int) ERTS_PSFLGS_GET_USR_PRIO(state); @@ -765,10 +764,13 @@ maybe_elevate_sig_handling_prio(Process *c_p, Eterm other) if (res) { /* ensure handled if dirty executing... */ state = erts_atomic32_read_nob(&rp->state); - if (state & (ERTS_PSFLG_DIRTY_RUNNING - | ERTS_PSFLG_DIRTY_RUNNING_SYS)) { + /* + * We ignore ERTS_PSFLG_DIRTY_RUNNING_SYS. For + * more info see erts_execute_dirty_system_task() + * in erl_process.c. + */ + if (state & ERTS_PSFLG_DIRTY_RUNNING) erts_make_dirty_proc_handled(other, state, my_prio); - } } } } @@ -4565,8 +4567,12 @@ erts_internal_dirty_process_handle_signals_1(BIF_ALIST_1) BIF_RET(am_noproc); state = erts_atomic32_read_nob(&rp->state); - dirty = (state & (ERTS_PSFLG_DIRTY_RUNNING - | ERTS_PSFLG_DIRTY_RUNNING_SYS)); + dirty = (state & ERTS_PSFLG_DIRTY_RUNNING); + /* + * Ignore ERTS_PSFLG_DIRTY_RUNNING_SYS (see + * comment in erts_execute_dirty_system_task() + * in erl_process.c). + */ if (!dirty) BIF_RET(am_normal); @@ -4574,8 +4580,7 @@ erts_internal_dirty_process_handle_signals_1(BIF_ALIST_1) state = erts_atomic32_read_mb(&rp->state); noproc = (state & ERTS_PSFLG_FREE); - dirty = (state & (ERTS_PSFLG_DIRTY_RUNNING - | ERTS_PSFLG_DIRTY_RUNNING_SYS)); + dirty = (state & ERTS_PSFLG_DIRTY_RUNNING); if (busy) { if (noproc) diff --git a/erts/emulator/beam/erl_proc_sig_queue.h b/erts/emulator/beam/erl_proc_sig_queue.h index 2b055e73bc..2789179b34 100644 --- a/erts/emulator/beam/erl_proc_sig_queue.h +++ b/erts/emulator/beam/erl_proc_sig_queue.h @@ -1115,8 +1115,12 @@ erts_proc_notify_new_sig(Process* rp, erts_aint32_t state, state = erts_proc_sys_schedule(rp, state, enable_flag); } - if (state & (ERTS_PSFLG_DIRTY_RUNNING - | ERTS_PSFLG_DIRTY_RUNNING_SYS)) { + if (state & ERTS_PSFLG_DIRTY_RUNNING) { + /* + * We ignore ERTS_PSFLG_DIRTY_RUNNING_SYS. For + * more info see erts_execute_dirty_system_task() + * in erl_process.c. + */ erts_make_dirty_proc_handled(rp->common.id, state, -1); } } diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 4c91c60220..e9ed4a7407 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -7462,11 +7462,16 @@ msb_scheduler_type_switch(ErtsSchedType sched_type, } static ERTS_INLINE void -suspend_normal_scheduler_sleep(ErtsSchedulerData *esdp) +suspend_scheduler_sleep(ErtsSchedulerData *esdp, + int normal_sched, + ErtsMonotonicTime initial_time, + ErtsMonotonicTime timeout_time) { ErtsSchedulerSleepInfo *ssi = esdp->ssi; erts_aint32_t flgs = sched_spin_suspended(ssi, ERTS_SCHED_SUSPEND_SLEEP_SPINCOUNT); + ASSERT(!normal_sched || esdp->type == ERTS_SCHED_NORMAL); + ASSERT(esdp->type != ERTS_SCHED_NORMAL || normal_sched); if (flgs == (ERTS_SSI_FLG_SLEEPING | ERTS_SSI_FLG_WAITING | ERTS_SSI_FLG_SUSPENDED)) { @@ -7475,21 +7480,34 @@ suspend_normal_scheduler_sleep(ErtsSchedulerData *esdp) | ERTS_SSI_FLG_TSE_SLEEPING | ERTS_SSI_FLG_WAITING | ERTS_SSI_FLG_SUSPENDED)) { - int res; + if (!normal_sched) { + while (1) { + int res = erts_tse_wait(ssi->event); + if (res != EINTR) + break; + } + } + else { + ErtsMonotonicTime current_time = initial_time; + while (1) { + int res; + Sint64 timeout; - do { - res = erts_tse_wait(ssi->event); - } while (res == EINTR); + timeout = ERTS_MONOTONIC_TO_NSEC(timeout_time + - current_time + - 1) + 1; + res = erts_tse_twait(ssi->event, timeout); + if (res != EINTR) + break; + current_time = erts_get_monotonic_time(esdp); + if (current_time >= timeout_time) + break; + } + } } } } -static ERTS_INLINE void -suspend_dirty_scheduler_sleep(ErtsSchedulerData *esdp) -{ - suspend_normal_scheduler_sleep(esdp); -} - static void suspend_scheduler(ErtsSchedulerData *esdp) { @@ -7590,18 +7608,31 @@ suspend_scheduler(ErtsSchedulerData *esdp) for (i = 0; msb[i]; i++) { erts_aint32_t clr_flg = 0; - if (msb[i] == &schdlr_sspnd.nmsb - && schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, - ERTS_SCHED_NORMAL) == 1) { - clr_flg = ERTS_SCHDLR_SSPND_CHNG_NMSB; + if (!msb[i]->ongoing) + continue; + + if (msb[i] == &schdlr_sspnd.nmsb) { + if (schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, + ERTS_SCHED_NORMAL) == 1) { + clr_flg = ERTS_SCHDLR_SSPND_CHNG_NMSB; + } } - else if (schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, - ERTS_SCHED_NORMAL) == 1 - && schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, - ERTS_SCHED_DIRTY_CPU) == 0 - && schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, - ERTS_SCHED_DIRTY_IO) == 0) { - clr_flg = ERTS_SCHDLR_SSPND_CHNG_MSB; + else { + ASSERT(msb[i] == &schdlr_sspnd.msb); + if (schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, + ERTS_SCHED_NORMAL) == 1 + && schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, + ERTS_SCHED_DIRTY_CPU) == 0 + && schdlr_sspnd_get_nscheds(&schdlr_sspnd.active, + ERTS_SCHED_DIRTY_IO) == 0) { + + clr_flg = ERTS_SCHDLR_SSPND_CHNG_MSB; + + /* Begin switching between scheduler types executing... */ + ERTS_RUNQ_FLGS_SET_NOB(ERTS_RUNQ_IX(0), ERTS_RUNQ_FLG_MSB_EXEC); + erts_atomic32_read_bor_nob(&ERTS_RUNQ_IX(0)->scheduler->ssi->flags, + ERTS_SSI_FLG_MSB_EXEC); + } } if (clr_flg) { @@ -7684,7 +7715,7 @@ suspend_scheduler(ErtsSchedulerData *esdp) while (1) { if (sched_type != ERTS_SCHED_NORMAL) - suspend_dirty_scheduler_sleep(esdp); + suspend_scheduler_sleep(esdp, 0, 0, 0); else { ErtsMonotonicTime current_time, timeout_time; @@ -7729,7 +7760,7 @@ suspend_scheduler(ErtsSchedulerData *esdp) sched_wall_time_change(esdp, 0); } erts_thr_progress_prepare_wait(erts_thr_prgr_data(NULL)); - suspend_normal_scheduler_sleep(esdp); + suspend_scheduler_sleep(esdp, !0, current_time, timeout_time); erts_thr_progress_finalize_wait(erts_thr_prgr_data(NULL)); current_time = erts_get_monotonic_time(esdp); } @@ -8235,9 +8266,6 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int normal } if (!normal) { - ERTS_RUNQ_FLGS_SET_NOB(ERTS_RUNQ_IX(0), ERTS_RUNQ_FLG_MSB_EXEC); - erts_atomic32_read_bor_nob(&ERTS_RUNQ_IX(0)->scheduler->ssi->flags, - ERTS_SSI_FLG_MSB_EXEC); for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) dcpu_sched_ix_suspend_wake(ix); for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) @@ -9596,6 +9624,24 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) ASSERT(p); /* Wrong qmask in rq->flags? */ +#ifdef DEBUG + switch (((erts_aint32_t) 1) << ERTS_PSFLGS_GET_PRQ_PRIO(state)) { + case MAX_BIT: + ASSERT(qbit == MAX_BIT); + break; + case HIGH_BIT: + ASSERT(qbit == HIGH_BIT); + break; + case NORMAL_BIT: + case LOW_BIT: + ASSERT(qbit == NORMAL_BIT || qbit == LOW_BIT); + break; + default: + ASSERT(0); + break; + } +#endif + if (is_normal_sched) { psflg_running = ERTS_PSFLG_RUNNING; psflg_running_sys = ERTS_PSFLG_RUNNING_SYS; @@ -9606,6 +9652,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) psflg_running = ERTS_PSFLG_DIRTY_RUNNING; psflg_running_sys = ERTS_PSFLG_DIRTY_RUNNING_SYS; psflg_band_mask = ~((erts_aint32_t) 0); + qbit = ((erts_aint32_t) 1) << ERTS_PSFLGS_GET_PRQ_PRIO(state); } if (!(state & ERTS_PSFLG_PROXY)) @@ -9758,25 +9805,33 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) } } else { - if (!(state & ERTS_PSFLGS_DIRTY_WORK)) { - /* Dirty work completed... */ - goto sunlock_sched_out_proc; + /* On dirty scheduler */ + if (!(state & ERTS_PSFLGS_DIRTY_WORK) + | !!(state & (ERTS_PSFLG_SYS_TASKS + | ERTS_PSFLG_EXITING + | ERTS_PSFLG_DIRTY_ACTIVE_SYS))) { + + if (!(state & ERTS_PSFLGS_DIRTY_WORK)) { + /* Dirty work completed... */ + goto sunlock_sched_out_proc; + } + if (state & (ERTS_PSFLG_SYS_TASKS + | ERTS_PSFLG_EXITING)) { + /* + * IMPORTANT! We need to take care of + * scheduled check-process-code requests + * before continuing with dirty execution! + */ + /* Migrate to normal scheduler... */ + goto sunlock_sched_out_proc; + } + if ((state & ERTS_PSFLG_DIRTY_ACTIVE_SYS) + && rq == ERTS_DIRTY_IO_RUNQ) { + /* Migrate to dirty cpu scheduler... */ + goto sunlock_sched_out_proc; + } + } - if (state & (ERTS_PSFLG_ACTIVE_SYS - | ERTS_PSFLG_EXITING)) { - /* - * IMPORTANT! We need to take care of - * scheduled check-process-code requests - * before continuing with dirty execution! - */ - /* Migrate to normal scheduler... */ - goto sunlock_sched_out_proc; - } - if ((state & ERTS_PSFLG_DIRTY_ACTIVE_SYS) - && rq == ERTS_DIRTY_IO_RUNQ) { - /* Migrate to dirty cpu scheduler... */ - goto sunlock_sched_out_proc; - } ASSERT(rq == ERTS_DIRTY_CPU_RUNQ ? (state & (ERTS_PSFLG_DIRTY_CPU_PROC @@ -9790,7 +9845,17 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) if (IS_TRACED(p)) trace_schedule_in(p, state); - if (is_normal_sched) { + if (!is_normal_sched) { + /* On dirty scheduler */ + if (!!(state & ERTS_PSFLG_DIRTY_RUNNING) + & !!(state & (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q))) { + /* Ensure signals are handled while executing dirty... */ + int prio = ERTS_PSFLGS_GET_ACT_PRIO(state); + erts_make_dirty_proc_handled(p->common.id, state, prio); + } + } + else { + /* On normal scheduler */ if (state & ERTS_PSFLG_RUNNING_SYS) { if (state & (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q)) { int local_only = (!!(p->sig_qs.flags & FS_LOCAL_SIGS_ONLY) @@ -10086,10 +10151,14 @@ fetch_sys_task(Process *c_p, erts_aint32_t state, int *qmaskp, int *priop) qmask = c_p->sys_task_qs->qmask; - if ((state & (ERTS_PSFLG_ACTIVE + if ((state & (ERTS_PSFLGS_DIRTY_WORK + | ERTS_PSFLG_ACTIVE | ERTS_PSFLG_EXITING | ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) { - /* No sys tasks if we got exclusively higher prio user work to do */ + /* + * No sys tasks if we got exclusively higher prio user work + * to do; ignoring dirty work... + */ st = NULL; switch (ERTS_PSFLGS_GET_USR_PRIO(state)) { case PRIORITY_MAX: @@ -10484,6 +10553,34 @@ erts_execute_dirty_system_task(Process *c_p) Eterm cla_res = THE_NON_VALUE; ErtsProcSysTask *stasks; + ASSERT(erts_atomic32_read_nob(&c_p->state) + & ERTS_PSFLG_DIRTY_RUNNING_SYS); + /* + * Currently all dirty system tasks are handled while holding + * the main lock. The process is during this in the state + * ERTS_PSFLG_DIRTY_RUNNING_SYS. The dirty signal handlers + * (erts/preloaded/src/erts_dirty_process_signal_handler.erl) + * cannot execute any signal handling on behalf of a process + * executing dirty unless they will be able to acquire the + * main lock. If they try to, they will just end up in a + * busy wait until the lock has been released. + * + * We now therefore do not schedule any handling on dirty + * signal handlers while a process is in the state + * ERTS_PSFLG_DIRTY_RUNNING_SYS. We instead leave the work + * scheduled on the process an let it detect it itself + * when it leaves the ERTS_PSFLG_DIRTY_RUNNING_SYS state. + * See erts_proc_notify_new_sig() in erl_proc_sig_queue.h, + * request_system_task() (check_process_code) in + * erl_process.c, and maybe_elevate_sig_handling_prio() + * in erl_proc_sig_queue.c for scheduling points. + * + * If there are dirty system tasks introduced that execute + * without the main lock held, we most likely want to trigger + * handling of signals via dirty signal handlers for these + * states. + */ + /* * If multiple operations, perform them in the following * order (in order to avoid unnecessary GC): @@ -10579,8 +10676,7 @@ dispatch_system_task(Process *c_p, erts_aint_t fail_state, switch (st->type) { case ERTS_PSTT_CPC: rp = erts_dirty_process_signal_handler; - ASSERT(fail_state & (ERTS_PSFLG_DIRTY_RUNNING - | ERTS_PSFLG_DIRTY_RUNNING_SYS)); + ASSERT(fail_state & ERTS_PSFLG_DIRTY_RUNNING); if (c_p == rp) { ERTS_BIF_PREP_RET(ret, am_dirty_execution); return ret; @@ -10726,9 +10822,12 @@ request_system_task(Process *c_p, Eterm requester, Eterm target, * If the process should start executing dirty * code it is important that this task is * aborted. Therefore this strict fail state... - */ - fail_state |= (ERTS_PSFLG_DIRTY_RUNNING - | ERTS_PSFLG_DIRTY_RUNNING_SYS); + * + * We ignore ERTS_PSFLG_DIRTY_RUNNING_SYS. For + * more info see erts_execute_dirty_system_task() + * in erl_process.c. + */ + fail_state |= ERTS_PSFLG_DIRTY_RUNNING; break; case am_copy_literals: @@ -10751,8 +10850,7 @@ request_system_task(Process *c_p, Eterm requester, Eterm target, noproc: failure = noproc_res; } - else if (fail_state & (ERTS_PSFLG_DIRTY_RUNNING - | ERTS_PSFLG_DIRTY_RUNNING_SYS)) { + else if (fail_state & ERTS_PSFLG_DIRTY_RUNNING) { ret = dispatch_system_task(c_p, fail_state, st, target, priority, operation); goto cleanup_return; @@ -13510,8 +13608,7 @@ erts_dbg_check_halloc_lock(Process *p) esdp = erts_proc_sched_data(p); if (esdp && p == esdp->match_pseudo_process) return 1; - if (erts_thr_progress_is_blocking()) - return 1; + /* erts_thr_progress_is_blocking() is not enough as dirty NIFs may run */ return 0; } #endif diff --git a/erts/emulator/beam/erl_sys_driver.h b/erts/emulator/beam/erl_sys_driver.h index d46e88cb05..8f1963fbd4 100644 --- a/erts/emulator/beam/erl_sys_driver.h +++ b/erts/emulator/beam/erl_sys_driver.h @@ -33,10 +33,39 @@ typedef long ErlDrvEvent; /* An event to be selected on. */ -/* typedef struct _SysDriverOpts SysDriverOpts; defined in sys.h */ +typedef struct _SysDriverOpts SysDriverOpts; #include "erl_driver.h" +/* + * This structure contains options to all built in drivers. + * None of the drivers use all of the fields. + */ + +struct _SysDriverOpts { + Uint ifd; /* Input file descriptor (fd driver). */ + Uint ofd; /* Outputfile descriptor (fd driver). */ + int packet_bytes; /* Number of bytes in packet header. */ + int read_write; /* Read and write bits. */ + int use_stdio; /* Use standard I/O: TRUE or FALSE. */ + int redir_stderr; /* Redirect stderr to stdout: TRUE/FALSE. */ + int hide_window; /* Hide this windows (Windows). */ + int exit_status; /* Report exit status of subprocess. */ + int overlapped_io; /* Only has effect on windows NT et al */ + erts_osenv_t envir; /* Environment of the port process */ + char **argv; /* Argument vector in Unix'ish format. */ + char *wd; /* Working directory. */ + unsigned spawn_type; /* Bitfield of ERTS_SPAWN_DRIVER | + ERTS_SPAWN_EXTERNAL | both*/ + int parallelism; /* Optimize for parallelism */ + ErlDrvSizeT high_watermark; + ErlDrvSizeT low_watermark; + ErlDrvSizeT high_msgq_watermark; + ErlDrvSizeT low_msgq_watermark; + char port_watermarks_set; + char msgq_watermarks_set; +}; + #endif diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h index e81f76d25b..c5c7b626c0 100644 --- a/erts/emulator/beam/erl_threads.h +++ b/erts/emulator/beam/erl_threads.h @@ -249,6 +249,7 @@ #include "erl_lock_check.h" #include "erl_lock_count.h" +#include "erl_dyn_lock_check.h" #if defined(__GLIBC__) && (__GLIBC__ << 16) + __GLIBC_MINOR__ < (2 << 16) + 5 /* @@ -295,6 +296,9 @@ typedef struct { #ifdef DEBUG erts_lock_flags_t flags; #endif +#ifdef ERTS_DYN_LOCK_CHECK_INTERNAL + erts_dlc_t dlc; +#endif } erts_mtx_t; typedef ethr_cond erts_cnd_t; @@ -310,6 +314,9 @@ typedef struct { #ifdef DEBUG erts_lock_flags_t flags; #endif +#ifdef ERTS_DYN_LOCK_CHECK_INTERNAL + erts_dlc_t dlc; +#endif } erts_rwmtx_t; #define ERTS_MTX_OPT_DEFAULT_INITER ETHR_MUTEX_OPT_DEFAULT_INITER @@ -1619,6 +1626,9 @@ erts_mtx_init(erts_mtx_t *mtx, char *name, Eterm extra, erts_lock_flags_t flags) #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_init_ref_x(&mtx->lcnt, name, extra, flags); #endif +#ifdef ERTS_DYN_LOCK_CHECK_INTERNAL + erts_dlc_create_lock(&mtx->dlc, name); +#endif } ERTS_GLB_INLINE void @@ -1630,6 +1640,9 @@ erts_mtx_init_locked(erts_mtx_t *mtx, char *name, Eterm extra, erts_lock_flags_t #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_trylock(1, &mtx->lc); #endif + #ifdef ERTS_DYN_LOCK_CHECK_INTERNAL + erts_dlc_trylock(&mtx->dlc, 1); + #endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_trylock(&mtx->lcnt, 1); #endif @@ -1686,11 +1699,13 @@ erts_mtx_trylock(erts_mtx_t *mtx) erts_lc_trylock(res == 0, &mtx->lc); #endif #endif +#ifdef ERTS_DYN_LOCK_CHECK_INTERNAL + erts_dlc_trylock(&mtx->dlc, res == 0); +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_trylock(&mtx->lcnt, res); #endif return res; - } ERTS_GLB_INLINE void @@ -1707,6 +1722,9 @@ erts_mtx_lock(erts_mtx_t *mtx) erts_lc_lock(&mtx->lc); #endif #endif +#ifdef ERTS_DYN_LOCK_CHECK_INTERNAL + erts_dlc_lock(&mtx->dlc); +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_lock(&mtx->lcnt); #endif @@ -1722,6 +1740,9 @@ erts_mtx_unlock(erts_mtx_t *mtx) #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_unlock(&mtx->lc); #endif +#ifdef ERTS_DYN_LOCK_CHECK_INTERNAL + erts_dlc_unlock(&mtx->dlc); +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_unlock(&mtx->lcnt); #endif @@ -1847,6 +1868,9 @@ erts_rwmtx_init_opt(erts_rwmtx_t *rwmtx, erts_rwmtx_opt_t *opt, #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_init_lock_x(&rwmtx->lc, name, flags, extra); #endif +#ifdef ERTS_DYN_LOCK_CHECK_INTERNAL + erts_dlc_create_lock(&rwmtx->dlc, name); +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_init_ref_x(&rwmtx->lcnt, name, extra, flags); #endif @@ -1909,6 +1933,9 @@ erts_rwmtx_tryrlock(erts_rwmtx_t *rwmtx) erts_lc_trylock_flg(res == 0, &rwmtx->lc, ERTS_LOCK_OPTIONS_READ); #endif #endif +#ifdef ERTS_DYN_LOCK_CHECK_INTERNAL + erts_dlc_trylock(&rwmtx->dlc, res == 0); +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_trylock_opt(&rwmtx->lcnt, res, ERTS_LOCK_OPTIONS_READ); #endif @@ -1930,6 +1957,9 @@ erts_rwmtx_rlock(erts_rwmtx_t *rwmtx) erts_lc_lock_flg(&rwmtx->lc, ERTS_LOCK_OPTIONS_READ); #endif #endif +#ifdef ERTS_DYN_LOCK_CHECK_INTERNAL + erts_dlc_lock(&rwmtx->dlc); +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_lock_opt(&rwmtx->lcnt, ERTS_LOCK_OPTIONS_READ); #endif @@ -1945,6 +1975,9 @@ erts_rwmtx_runlock(erts_rwmtx_t *rwmtx) #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_unlock_flg(&rwmtx->lc, ERTS_LOCK_OPTIONS_READ); #endif +#ifdef ERTS_DYN_LOCK_CHECK_INTERNAL + erts_dlc_unlock(&rwmtx->dlc); +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_unlock_opt(&rwmtx->lcnt, ERTS_LOCK_OPTIONS_READ); #endif @@ -1976,6 +2009,9 @@ erts_rwmtx_tryrwlock(erts_rwmtx_t *rwmtx) erts_lc_trylock_flg(res == 0, &rwmtx->lc, ERTS_LOCK_OPTIONS_RDWR); #endif #endif +#ifdef ERTS_DYN_LOCK_CHECK_INTERNAL + erts_dlc_trylock(&rwmtx->dlc, res == 0); +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_trylock_opt(&rwmtx->lcnt, res, ERTS_LOCK_OPTIONS_RDWR); #endif @@ -1997,6 +2033,9 @@ erts_rwmtx_rwlock(erts_rwmtx_t *rwmtx) erts_lc_lock_flg(&rwmtx->lc, ERTS_LOCK_OPTIONS_RDWR); #endif #endif +#ifdef ERTS_DYN_LOCK_CHECK_INTERNAL + erts_dlc_lock(&rwmtx->dlc); +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_lock_opt(&rwmtx->lcnt, ERTS_LOCK_OPTIONS_RDWR); #endif @@ -2012,6 +2051,9 @@ erts_rwmtx_rwunlock(erts_rwmtx_t *rwmtx) #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_unlock_flg(&rwmtx->lc, ERTS_LOCK_OPTIONS_RDWR); #endif +#ifdef ERTS_DYN_LOCK_CHECK_INTERNAL + erts_dlc_unlock(&rwmtx->dlc); +#endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_unlock_opt(&rwmtx->lcnt, ERTS_LOCK_OPTIONS_RDWR); #endif diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 20a155e1d8..78f7a146f2 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -605,6 +605,19 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */ ERTS_OPEN_DRIVER_RET(NULL, -3, BADARG); } + if (opts->port_watermarks_set && driver != &spawn_driver + && driver != &fd_driver && driver != &vanilla_driver) { + erts_rwmtx_runlock(&erts_driver_list_lock); + ERTS_OPEN_DRIVER_RET(NULL, -3, BADARG); + } + + if (opts->msgq_watermarks_set + && (driver->flags & ERL_DRV_FLAG_NO_BUSY_MSGQ) + && opts->high_msgq_watermark != ERL_DRV_BUSY_MSGQ_DISABLED) { + erts_rwmtx_runlock(&erts_driver_list_lock); + ERTS_OPEN_DRIVER_RET(NULL, -3, BADARG); + } + driver_lock = driver->lock; if (driver->handle != NULL) { @@ -644,6 +657,11 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */ 1)); } + if (opts->msgq_watermarks_set) + erl_drv_busy_msgq_limits(ERTS_Port2ErlDrvPort(port), + &opts->low_msgq_watermark, + &opts->high_msgq_watermark); + error_number = error_type = 0; if (driver->start) { ERTS_MSACC_PUSH_STATE_M(); diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index c534891f30..0d85211be8 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -668,29 +668,6 @@ typedef Eterm ErtsTracer; #include "erl_osenv.h" -/* - * This structure contains options to all built in drivers. - * None of the drivers use all of the fields. - */ - -typedef struct _SysDriverOpts { - Uint ifd; /* Input file descriptor (fd driver). */ - Uint ofd; /* Outputfile descriptor (fd driver). */ - int packet_bytes; /* Number of bytes in packet header. */ - int read_write; /* Read and write bits. */ - int use_stdio; /* Use standard I/O: TRUE or FALSE. */ - int redir_stderr; /* Redirect stderr to stdout: TRUE/FALSE. */ - int hide_window; /* Hide this windows (Windows). */ - int exit_status; /* Report exit status of subprocess. */ - int overlapped_io; /* Only has effect on windows NT et al */ - erts_osenv_t envir; /* Environment of the port process */ - char **argv; /* Argument vector in Unix'ish format. */ - char *wd; /* Working directory. */ - unsigned spawn_type; /* Bitfield of ERTS_SPAWN_DRIVER | - ERTS_SPAWN_EXTERNAL | both*/ - int parallelism; /* Optimize for parallelism */ -} SysDriverOpts; - extern char *erts_default_arg0; extern char os_type[]; diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 66ff8d8450..2ef452fa01 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2019. All Rights Reserved. + * Copyright Ericsson AB 1997-2020. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12607,12 +12607,8 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return ctl_error(EALREADY, rbuf, rsize); if (packet_inet_input(udesc, desc->event) == 0) { - if (timeout == 0) - async_error_am(desc, am_timeout); - else { - if (timeout != INET_INFINITY) - driver_set_timer(desc->port, timeout); - } + if (timeout != INET_INFINITY) + driver_set_timer(desc->port, timeout); } return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize); } diff --git a/erts/emulator/nifs/common/prim_net_nif.c b/erts/emulator/nifs/common/prim_net_nif.c index c95b0d7f70..1fbd3e2366 100644 --- a/erts/emulator/nifs/common/prim_net_nif.c +++ b/erts/emulator/nifs/common/prim_net_nif.c @@ -1061,7 +1061,7 @@ ERL_NIF_TERM nif_getifaddrs(ErlNifEnv* env, return result; #else // HAVE_GETIFADDRS - return esock_make_error(env, esock_atom_notsup); + return esock_make_error(env, esock_atom_enotsup); #endif } diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index e4a3d6157d..621d52af16 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -166,6 +166,7 @@ typedef unsigned int BOOLEAN_T; GLOBAL_ATOM_DEF(drop_source_membership); \ GLOBAL_ATOM_DEF(dstopts); \ GLOBAL_ATOM_DEF(egp); \ + GLOBAL_ATOM_DEF(enotsup); \ GLOBAL_ATOM_DEF(eor); \ GLOBAL_ATOM_DEF(error); \ GLOBAL_ATOM_DEF(errqueue); \ @@ -207,6 +208,7 @@ typedef unsigned int BOOLEAN_T; GLOBAL_ATOM_DEF(keepcnt); \ GLOBAL_ATOM_DEF(keepidle); \ GLOBAL_ATOM_DEF(keepintvl); \ + GLOBAL_ATOM_DEF(kernel); \ GLOBAL_ATOM_DEF(leave_group); \ GLOBAL_ATOM_DEF(level); \ GLOBAL_ATOM_DEF(linger); \ @@ -328,6 +330,7 @@ typedef unsigned int BOOLEAN_T; GLOBAL_ATOM_DEF(unicast_hops); \ GLOBAL_ATOM_DEF(unknown); \ GLOBAL_ATOM_DEF(usec); \ + GLOBAL_ATOM_DEF(user); \ GLOBAL_ATOM_DEF(user_timeout); \ GLOBAL_ATOM_DEF(use_ext_recvinfo); \ GLOBAL_ATOM_DEF(use_min_mtu); \ @@ -378,6 +381,7 @@ GLOBAL_ERROR_REASON_ATOM_DEFS enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8)) #define MKTA(E, A, AL) enif_make_tuple_from_array((E), (A), (AL)) #define MKUI(E,UI) enif_make_uint((E), (UI)) +#define MKUI64(E,UI) enif_make_uint64((E), (UI)) #define MKUL(E,UL) enif_make_ulong((E), (UL)) #define MCREATE(N) enif_mutex_create((N)) @@ -414,6 +418,7 @@ GLOBAL_ERROR_REASON_ATOM_DEFS #define GET_STR(E, L, B, SZ) \ enif_get_string((E), (L), (B), (SZ), ERL_NIF_LATIN1) #define GET_UINT(E, TE, UIP) enif_get_uint((E), (TE), (UIP)) +#define GET_UINT64(E, TE, UIP) enif_get_uint64((E), (TE), (UIP)) #define GET_ULONG(E, TE, ULP) enif_get_long((E), (TE), (ULP)) #define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) #define GET_MAP_VAL(E, M, K, V) enif_get_map_value((E), (M), (K), (V)) diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 84b07252d8..fbda73d518 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2018-2019. All Rights Reserved. + * Copyright Ericsson AB 2018-2020. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -368,7 +368,7 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #define ESOCK_GLOBAL_DEBUG_DEFAULT FALSE #define ESOCK_DEBUG_DEFAULT FALSE -/* Counters and stuff (Don't know where to sent this stuff anyway) */ +/* Counters and stuff (Don't know where to sen2 this stuff anyway) */ #define ESOCK_NIF_IOW_DEFAULT FALSE @@ -487,6 +487,7 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #define ESOCK_RECV_FLAG_LOW ESOCK_RECV_FLAG_CMSG_CLOEXEC #define ESOCK_RECV_FLAG_HIGH ESOCK_RECV_FLAG_TRUNC +#define ESOCK_RECV_BUFFER_COUNT_DEFAULT 0 #define ESOCK_RECV_BUFFER_SIZE_DEFAULT 8192 #define ESOCK_RECV_CTRL_BUFFER_SIZE_DEFAULT 1024 #define ESOCK_SEND_CTRL_BUFFER_SIZE_DEFAULT 1024 @@ -532,6 +533,7 @@ typedef union { /*---------------------------------------------------------------------------- * Interface constants. * + * This section must be "identical" to the corresponding socket.erl */ /* domain */ @@ -576,9 +578,11 @@ typedef union { #define ESOCK_OPT_OTP_RCVCTRLBUF 6 #define ESOCK_OPT_OTP_SNDCTRLBUF 7 #define ESOCK_OPT_OTP_FD 8 +#define ESOCK_OPT_OTP_META 9 #define ESOCK_OPT_OTP_DOMAIN 0xFF01 // INTERNAL AND ONLY GET #define ESOCK_OPT_OTP_TYPE 0xFF02 // INTERNAL AND ONLY GET #define ESOCK_OPT_OTP_PROTOCOL 0xFF03 // INTERNAL AND ONLY GET +#define ESOCK_OPT_OTP_DTP 0xFF04 // INTERNAL AND ONLY GET #define ESOCK_OPT_SOCK_ACCEPTCONN 1 #define ESOCK_OPT_SOCK_BINDTODEVICE 3 @@ -702,7 +706,7 @@ typedef union { /* Socket specific debug */ #define SSDBG( __D__ , proto ) ESOCK_DBG_PRINTF( (__D__)->dbg , proto ) -#define SOCK_CNT_INC( __E__, __D__, SF, ACNT, CNT, INC) \ +#define ESOCK_CNT_INC( __E__, __D__, SF, ACNT, CNT, INC) \ { \ if (cnt_inc(CNT, INC) && (__D__)->iow) { \ esock_send_wrap_msg(__E__, __D__, SF, ACNT); \ @@ -827,6 +831,12 @@ typedef struct { ERL_NIF_TERM ref; // The (unique) reference (ID) of the request } ESockRequestor; +typedef struct{ + // Holding the socket level 'otp' option 'meta' term + ErlNifEnv* env; + ERL_NIF_TERM ref; +} ESockMeta; + typedef struct esock_request_queue_element { struct esock_request_queue_element* nextP; ESockRequestor data; @@ -837,6 +847,48 @@ typedef struct { ESockRequestQueueElement* last; } ESockRequestQueue; +/*** The point of this is primarily testing ***/ + +// #define ESOCK_COUNTER_SIZE 16 +// #define ESOCK_COUNTER_SIZE 24 +// #define ESOCK_COUNTER_SIZE 32 +// #define ESOCK_COUNTER_SIZE 48 + +#if ESOCK_COUNTER_SIZE == 16 + +typedef Uint16 ESockCounter; +#define ESOCK_COUNTER_MAX 0xFFFF +#define MKCT(ENV, TAG, CNT) MKT2((ENV), (TAG), MKUI((ENV), (CNT))) + +#elif ESOCK_COUNTER_SIZE == 24 + +typedef Uint32 ESockCounter; +#define ESOCK_COUNTER_MAX 0xFFFFFF +#define MKCT(ENV, TAG, CNT) MKT2((ENV), (TAG), MKUI((ENV), (CNT))) + +#elif ESOCK_COUNTER_SIZE == 32 + +typedef Uint32 ESockCounter; +#define ESOCK_COUNTER_MAX 0xFFFFFFFF +#define MKCT(ENV, TAG, CNT) MKT2((ENV), (TAG), MKUI((ENV), (CNT))) + +#elif ESOCK_COUNTER_SIZE == 48 + +typedef Uint64 ESockCounter; +#define ESOCK_COUNTER_MAX 0xFFFFFFFFFFFF +#define MKCT(ENV, TAG, CNT) MKT2((ENV), (TAG), MKUI64((ENV), (CNT))) + +#elif ESOCK_COUNTER_SIZE == 64 + +typedef Uint64 ESockCounter; +#define ESOCK_COUNTER_MAX 0xFFFFFFFFFFFFFFFF +#define MKCT(ENV, TAG, CNT) MKT2((ENV), (TAG), MKUI64((ENV), (CNT))) + +#else + +#error "Invalid counter size" + +#endif typedef struct { /* @@ -874,11 +926,13 @@ typedef struct { ESockRequestor* currentWriterP; // NULL or points to currentWriter ESockRequestQueue writersQ; BOOLEAN_T isWritable; - Uint32 writePkgCnt; - Uint32 writeByteCnt; - Uint32 writeTries; - Uint32 writeWaits; - Uint32 writeFails; + ESockCounter writePkgCnt; + ESockCounter writePkgMax; + ESockCounter writePkgMaxCnt; + ESockCounter writeByteCnt; + ESockCounter writeTries; + ESockCounter writeWaits; + ESockCounter writeFails; /* +++ Read stuff +++ */ ErlNifMutex* readMtx; @@ -888,17 +942,23 @@ typedef struct { BOOLEAN_T isReadable; ErlNifBinary rbuffer; // DO WE NEED THIS Uint32 readCapacity; // DO WE NEED THIS - Uint32 readPkgCnt; - Uint32 readByteCnt; - Uint32 readTries; - Uint32 readWaits; - Uint32 readFails; + ESockCounter readPkgCnt; + ESockCounter readPkgMax; + ESockCounter readPkgMaxCnt; + ESockCounter readByteCnt; + ESockCounter readTries; + ESockCounter readWaits; + ESockCounter readFails; /* +++ Accept stuff +++ */ ErlNifMutex* accMtx; ESockRequestor currentAcceptor; ESockRequestor* currentAcceptorP; // NULL or points to currentAcceptor ESockRequestQueue acceptorsQ; + ESockCounter accSuccess; + ESockCounter accTries; + ESockCounter accWaits; + ESockCounter accFails; /* +++ Config & Misc stuff +++ */ ErlNifMutex* cfgMtx; @@ -914,6 +974,7 @@ typedef struct { unsigned int rNumCnt; // recv: Current number of reads (so far) size_t rCtrlSz; // Read control buffer size size_t wCtrlSz; // Write control buffer size + ESockMeta meta; // Level 'otp' option 'meta' term BOOLEAN_T iow; // Inform On (counter) Wrap BOOLEAN_T dbg; @@ -925,6 +986,9 @@ typedef struct { ERL_NIF_TERM closeRef; BOOLEAN_T closeLocal; + /* Lock order: closeMtx, readMtx, accMtx, writeMtx, cfgMtx + * unordered: cntMtx + */ } ESockDescriptor; @@ -934,21 +998,34 @@ typedef struct { /* These are for debugging, testing and the like */ // ERL_NIF_TERM version; // ERL_NIF_TERM buildDate; + + /* XXX Should be locked but too awkward and small gain */ BOOLEAN_T dbg; + /* Registry stuff */ + ErlNifPid regPid; /* Constant - not locked */ + + /* XXX + * Should be locked but too awkward for no gain since it is not used yet + */ BOOLEAN_T iow; // Where do we send this? Subscription? - ErlNifMutex* cntMtx; - Uint32 numSockets; - Uint32 numTypeStreams; - Uint32 numTypeDGrams; - Uint32 numTypeSeqPkgs; - Uint32 numDomainInet; - Uint32 numDomainInet6; - Uint32 numDomainLocal; - Uint32 numProtoIP; - Uint32 numProtoTCP; - Uint32 numProtoUDP; - Uint32 numProtoSCTP; + + ErlNifMutex* cntMtx; /* Locks the below */ + /* Its extreme overkill to have these counters be 64-bit, + * but since the other counters are, its much simpler to + * let to let these be 64-bit also + */ + ESockCounter numSockets; + ESockCounter numTypeStreams; + ESockCounter numTypeDGrams; + ESockCounter numTypeSeqPkgs; + ESockCounter numDomainInet; + ESockCounter numDomainInet6; + ESockCounter numDomainLocal; + ESockCounter numProtoIP; + ESockCounter numProtoTCP; + ESockCounter numProtoUDP; + ESockCounter numProtoSCTP; } ESockData; @@ -1121,6 +1198,7 @@ static ERL_NIF_TERM esock_accept_listening_error(ErlNifEnv* env, int save_errno); static ERL_NIF_TERM esock_accept_listening_accept(ErlNifEnv* env, ESockDescriptor* descP, + ERL_NIF_TERM sockRef, SOCKET accSock, ErlNifPid caller, ESockAddress* remote); @@ -1154,6 +1232,7 @@ static ERL_NIF_TERM esock_accept_busy_retry(ErlNifEnv* env, unsigned int nextState); static BOOLEAN_T esock_accept_accepted(ErlNifEnv* env, ESockDescriptor* descP, + ERL_NIF_TERM sockRef, SOCKET accSock, ErlNifPid pid, ESockAddress* remote, @@ -1182,7 +1261,7 @@ static ERL_NIF_TERM esock_recv(ErlNifEnv* env, ESockDescriptor* descP, ERL_NIF_TERM sendRef, ERL_NIF_TERM recvRef, - int len, + size_t len, int flags); static ERL_NIF_TERM esock_recvfrom(ErlNifEnv* env, ESockDescriptor* descP, @@ -1226,9 +1305,11 @@ static ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env, * *** esock_setopt_otp_rcvbuf *** * *** esock_setopt_otp_rcvctrlbuf *** * *** esock_setopt_otp_sndctrlbuf *** + * *** esock_setopt_otp_meta *** */ #define ESOCK_SETOPT_OTP_FUNCS \ ESOCK_SETOPT_OTP_FUNC_DEF(debug); \ + ESOCK_SETOPT_OTP_FUNC_DEF(meta); \ ESOCK_SETOPT_OTP_FUNC_DEF(iow); \ ESOCK_SETOPT_OTP_FUNC_DEF(ctrl_proc); \ ESOCK_SETOPT_OTP_FUNC_DEF(rcvbuf); \ @@ -1757,12 +1838,15 @@ static ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env, * *** esock_getopt_otp_rcvctrlbuf *** * *** esock_getopt_otp_sndctrlbuf *** * *** esock_getopt_otp_fd *** + * *** esock_getopt_otp_meta *** * *** esock_getopt_otp_domain *** * *** esock_getopt_otp_type *** * *** esock_getopt_otp_protocol *** + * *** esock_getopt_otp_dtp *** */ #define ESOCK_GETOPT_OTP_FUNCS \ ESOCK_GETOPT_OTP_FUNC_DEF(debug); \ + ESOCK_GETOPT_OTP_FUNC_DEF(meta); \ ESOCK_GETOPT_OTP_FUNC_DEF(iow); \ ESOCK_GETOPT_OTP_FUNC_DEF(ctrl_proc); \ ESOCK_GETOPT_OTP_FUNC_DEF(rcvbuf); \ @@ -1771,7 +1855,8 @@ static ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env, ESOCK_GETOPT_OTP_FUNC_DEF(fd); \ ESOCK_GETOPT_OTP_FUNC_DEF(domain); \ ESOCK_GETOPT_OTP_FUNC_DEF(type); \ - ESOCK_GETOPT_OTP_FUNC_DEF(protocol); + ESOCK_GETOPT_OTP_FUNC_DEF(protocol); \ + ESOCK_GETOPT_OTP_FUNC_DEF(dtp); #define ESOCK_GETOPT_OTP_FUNC_DEF(F) \ static ERL_NIF_TERM esock_getopt_otp_##F(ErlNifEnv* env, \ ESockDescriptor* descP) @@ -2245,6 +2330,10 @@ static ERL_NIF_TERM send_check_fail(ErlNifEnv* env, ESockDescriptor* descP, int saveErrno, ERL_NIF_TERM sockRef); +static void send_error_waiting_writers(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM reason); static ERL_NIF_TERM send_check_retry(ErlNifEnv* env, ESockDescriptor* descP, ssize_t written, @@ -2266,29 +2355,29 @@ static void recv_error_current_reader(ErlNifEnv* env, ERL_NIF_TERM reason); static ERL_NIF_TERM recv_check_result(ErlNifEnv* env, ESockDescriptor* descP, - int read, - int toRead, + ssize_t read, + size_t toRead, int saveErrno, ErlNifBinary* bufP, ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef); static ERL_NIF_TERM recv_check_full(ErlNifEnv* env, ESockDescriptor* descP, - int read, - int toRead, + ssize_t read, + size_t toRead, ErlNifBinary* bufP, ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef); static ERL_NIF_TERM recv_check_full_maybe_done(ErlNifEnv* env, ESockDescriptor* descP, - int read, - int toRead, + ssize_t read, + size_t toRead, ErlNifBinary* bufP, ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef); static ERL_NIF_TERM recv_check_full_done(ErlNifEnv* env, ESockDescriptor* descP, - int read, + ssize_t read, ErlNifBinary* bufP, ERL_NIF_TERM sockRef); static ERL_NIF_TERM recv_check_fail(ErlNifEnv* env, @@ -2298,25 +2387,25 @@ static ERL_NIF_TERM recv_check_fail(ErlNifEnv* env, ErlNifBinary* buf2P, ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef); -static ERL_NIF_TERM recv_check_fail_closed(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recv_check_fail_econnreset(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); static ERL_NIF_TERM recv_check_partial(ErlNifEnv* env, ESockDescriptor* descP, - int read, - int toRead, + ssize_t read, + size_t toRead, ErlNifBinary* bufP, ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef); static ERL_NIF_TERM recv_check_partial_done(ErlNifEnv* env, ESockDescriptor* descP, - int read, + ssize_t read, ErlNifBinary* bufP, ERL_NIF_TERM sockRef); static ERL_NIF_TERM recv_check_partial_part(ErlNifEnv* env, ESockDescriptor* descP, - int read, + ssize_t read, ErlNifBinary* bufP, ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef); @@ -2534,8 +2623,8 @@ static BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err); static BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err); #endif -static BOOLEAN_T cnt_inc(Uint32* cnt, Uint32 inc); -static void cnt_dec(Uint32* cnt, Uint32 dec); +static BOOLEAN_T cnt_inc(ESockCounter* cnt, ESockCounter inc); +static void cnt_dec(ESockCounter* cnt, ESockCounter dec); static void inc_socket(int domain, int type, int protocol); static void dec_socket(int domain, int type, int protocol); @@ -2599,6 +2688,8 @@ ESOCK_OPERATOR_FUNCS_DEFS static BOOLEAN_T requestor_pop(ESockRequestQueue* q, ESockRequestor* reqP); +static void requestor_clear(ESockRequestor* reqP); + static BOOLEAN_T qsearch4pid(ErlNifEnv* env, ESockRequestQueue* q, ErlNifPid* pid); @@ -2623,6 +2714,10 @@ static int esock_demonitor(const char* slogan, static void esock_monitor_init(ESockMonitor* mon); static ERL_NIF_TERM esock_make_monitor_term(ErlNifEnv* env, const ESockMonitor* monP); +static void esock_release_current(const char* slogan, + ErlNifEnv* env, + ESockDescriptor* descP, + ESockRequestor* current); #endif // if defined(__WIN32__) @@ -2658,6 +2753,11 @@ static void esock_down_reader(ErlNifEnv* env, ERL_NIF_TERM sockRef, const ErlNifPid* pid); +static void esock_send_reg_add_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef); +static void esock_send_reg_del_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef); + static char* esock_send_wrap_msg(ErlNifEnv* env, ESockDescriptor* descP, ERL_NIF_TERM sockRef, @@ -2676,6 +2776,13 @@ static char* esock_send_msg(ErlNifEnv* env, ERL_NIF_TERM msg, ErlNifEnv* msgEnv); +static ERL_NIF_TERM mk_reg_add_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM mk_reg_del_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM mk_reg_msg(ErlNifEnv* env, + ERL_NIF_TERM tag, + ERL_NIF_TERM sockRef); static ERL_NIF_TERM mk_abort_msg(ErlNifEnv* env, ERL_NIF_TERM sockRef, ERL_NIF_TERM opRef, @@ -2811,6 +2918,7 @@ static char str_exsend[] = "exsend"; // failed send GLOBAL_ATOM_DECL(drop_source_membership); \ GLOBAL_ATOM_DECL(dstopts); \ GLOBAL_ATOM_DECL(egp); \ + GLOBAL_ATOM_DECL(enotsup); \ GLOBAL_ATOM_DECL(eor); \ GLOBAL_ATOM_DECL(error); \ GLOBAL_ATOM_DECL(errqueue); \ @@ -2852,6 +2960,7 @@ static char str_exsend[] = "exsend"; // failed send GLOBAL_ATOM_DECL(keepcnt); \ GLOBAL_ATOM_DECL(keepidle); \ GLOBAL_ATOM_DECL(keepintvl); \ + GLOBAL_ATOM_DECL(kernel); \ GLOBAL_ATOM_DECL(leave_group); \ GLOBAL_ATOM_DECL(level); \ GLOBAL_ATOM_DECL(linger); \ @@ -2972,6 +3081,7 @@ static char str_exsend[] = "exsend"; // failed send GLOBAL_ATOM_DECL(unicast_hops); \ GLOBAL_ATOM_DECL(unknown); \ GLOBAL_ATOM_DECL(usec); \ + GLOBAL_ATOM_DECL(user); \ GLOBAL_ATOM_DECL(user_timeout); \ GLOBAL_ATOM_DECL(use_ext_recvinfo); \ GLOBAL_ATOM_DECL(use_min_mtu); \ @@ -2993,7 +3103,12 @@ ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket') /* *** Local atoms *** */ #define LOCAL_ATOMS \ + LOCAL_ATOM_DECL(acc_success); \ + LOCAL_ATOM_DECL(acc_fails); \ + LOCAL_ATOM_DECL(acc_tries); \ + LOCAL_ATOM_DECL(acc_waits); \ LOCAL_ATOM_DECL(adaptation_layer); \ + LOCAL_ATOM_DECL(add); \ LOCAL_ATOM_DECL(addr_unreach); \ LOCAL_ATOM_DECL(address); \ LOCAL_ATOM_DECL(adm_prohibited); \ @@ -3009,13 +3124,13 @@ ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket') LOCAL_ATOM_DECL(counter_wrap); \ LOCAL_ATOM_DECL(counters); \ LOCAL_ATOM_DECL(data_in); \ + LOCAL_ATOM_DECL(del); \ LOCAL_ATOM_DECL(dest_unreach); \ LOCAL_ATOM_DECL(do); \ LOCAL_ATOM_DECL(dont); \ LOCAL_ATOM_DECL(exclude); \ LOCAL_ATOM_DECL(false); \ LOCAL_ATOM_DECL(frag_needed); \ - LOCAL_ATOM_DECL(global_counters); \ LOCAL_ATOM_DECL(host_unknown); \ LOCAL_ATOM_DECL(host_unreach); \ LOCAL_ATOM_DECL(in4_sockaddr); \ @@ -3041,6 +3156,7 @@ ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket') LOCAL_ATOM_DECL(not_neighbour); \ LOCAL_ATOM_DECL(null); \ LOCAL_ATOM_DECL(num_acceptors); \ + LOCAL_ATOM_DECL(num_cnt_bits); \ LOCAL_ATOM_DECL(num_dinet); \ LOCAL_ATOM_DECL(num_dinet6); \ LOCAL_ATOM_DECL(num_dlocal); \ @@ -3069,6 +3185,7 @@ ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket') LOCAL_ATOM_DECL(read_byte); \ LOCAL_ATOM_DECL(read_fails); \ LOCAL_ATOM_DECL(read_pkg); \ + LOCAL_ATOM_DECL(read_pkg_max); \ LOCAL_ATOM_DECL(read_tries); \ LOCAL_ATOM_DECL(read_waits); \ LOCAL_ATOM_DECL(reject_route); \ @@ -3089,12 +3206,14 @@ ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket') LOCAL_ATOM_DECL(write_byte); \ LOCAL_ATOM_DECL(write_fails); \ LOCAL_ATOM_DECL(write_pkg); \ + LOCAL_ATOM_DECL(write_pkg_max); \ LOCAL_ATOM_DECL(write_tries); \ LOCAL_ATOM_DECL(write_waits); \ LOCAL_ATOM_DECL(zerocopy); /* Local error reason atoms */ #define LOCAL_ERROR_REASON_ATOMS \ + LOCAL_ATOM_DECL(econnreset); \ LOCAL_ATOM_DECL(eisconn); \ LOCAL_ATOM_DECL(enotclosing); \ LOCAL_ATOM_DECL(enotconn); \ @@ -3194,7 +3313,6 @@ static ESOCK_INLINE ErlNifEnv* esock_alloc_env(const char* slogan) * Description: * This is currently just a placeholder... */ -#define MKCT(E, T, C) MKT2((E), (T), MKUI((E), (C))) static ERL_NIF_TERM nif_info(ErlNifEnv* env, @@ -3237,40 +3355,60 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, /* * This function return a property list containing "global" info. + * + * Note that we also include something in the counter list that is not + * actually a counter, the num_cnt_bits. This is the "size" of each counter, + * in number of bits: 16 | 24 | 32 | 48 | 64. */ #if !defined(__WIN32__) static ERL_NIF_TERM esock_global_info(ErlNifEnv* env) { - ERL_NIF_TERM numSockets = MKCT(env, atom_num_sockets, data.numSockets); - ERL_NIF_TERM numTypeDGrams = MKCT(env, atom_num_tdgrams, data.numTypeDGrams); - ERL_NIF_TERM numTypeStreams = MKCT(env, atom_num_tstreams, data.numTypeStreams); - ERL_NIF_TERM numTypeSeqPkgs = MKCT(env, atom_num_tseqpkgs, data.numTypeSeqPkgs); - ERL_NIF_TERM numDomLocal = MKCT(env, atom_num_dlocal, data.numDomainLocal); - ERL_NIF_TERM numDomInet = MKCT(env, atom_num_dinet, data.numDomainInet); - ERL_NIF_TERM numDomInet6 = MKCT(env, atom_num_dinet6, data.numDomainInet6); - ERL_NIF_TERM numProtoIP = MKCT(env, atom_num_pip, data.numProtoIP); - ERL_NIF_TERM numProtoTCP = MKCT(env, atom_num_ptcp, data.numProtoTCP); - ERL_NIF_TERM numProtoUDP = MKCT(env, atom_num_pudp, data.numProtoUDP); - ERL_NIF_TERM numProtoSCTP = MKCT(env, atom_num_psctp, data.numProtoSCTP); - ERL_NIF_TERM gcnt[] = {numSockets, - numTypeDGrams, numTypeStreams, numTypeSeqPkgs, - numDomLocal, numDomInet, numDomInet6, - numProtoIP, numProtoTCP, numProtoUDP, numProtoSCTP}; - unsigned int lenGCnt = sizeof(gcnt) / sizeof(ERL_NIF_TERM); - ERL_NIF_TERM lgcnt = MKLA(env, gcnt, lenGCnt); - ERL_NIF_TERM keys[] = {esock_atom_debug, atom_iow, atom_global_counters}; - ERL_NIF_TERM vals[] = {BOOL2ATOM(data.dbg), BOOL2ATOM(data.iow), lgcnt}; - ERL_NIF_TERM info; - unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); - unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + ERL_NIF_TERM + numBits, numSockets, numTypeDGrams, numTypeStreams, + numTypeSeqPkgs, numDomLocal, numDomInet, numDomInet6, + numProtoIP, numProtoTCP, numProtoUDP, numProtoSCTP; - ESOCK_ASSERT( (numKeys == numVals) ); + MLOCK(data.cntMtx); + numBits = MKCT(env, atom_num_cnt_bits, ESOCK_COUNTER_SIZE); + numSockets = MKCT(env, atom_num_sockets, data.numSockets); + numTypeDGrams = MKCT(env, atom_num_tdgrams, data.numTypeDGrams); + numTypeStreams = MKCT(env, atom_num_tstreams, data.numTypeStreams); + numTypeSeqPkgs = MKCT(env, atom_num_tseqpkgs, data.numTypeSeqPkgs); + numDomLocal = MKCT(env, atom_num_dlocal, data.numDomainLocal); + numDomInet = MKCT(env, atom_num_dinet, data.numDomainInet); + numDomInet6 = MKCT(env, atom_num_dinet6, data.numDomainInet6); + numProtoIP = MKCT(env, atom_num_pip, data.numProtoIP); + numProtoTCP = MKCT(env, atom_num_ptcp, data.numProtoTCP); + numProtoUDP = MKCT(env, atom_num_pudp, data.numProtoUDP); + numProtoSCTP = MKCT(env, atom_num_psctp, data.numProtoSCTP); + MUNLOCK(data.cntMtx); - if (!MKMA(env, keys, vals, numKeys, &info)) - return enif_make_badarg(env); + { + ERL_NIF_TERM gcnt[] = + {numBits, + numSockets, + numTypeDGrams, numTypeStreams, numTypeSeqPkgs, + numDomLocal, numDomInet, numDomInet6, + numProtoIP, numProtoTCP, numProtoUDP, numProtoSCTP}; + unsigned int lenGCnt = + sizeof(gcnt) / sizeof(ERL_NIF_TERM); + ERL_NIF_TERM + lgcnt = MKLA(env, gcnt, lenGCnt), + keys[] = {esock_atom_debug, atom_iow, atom_counters}, + vals[] = {BOOL2ATOM(data.dbg), BOOL2ATOM(data.iow), lgcnt}, + info; + unsigned int + numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM), + numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); - return info; + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, &info)) + return enif_make_badarg(env); + + return info; + } } @@ -3280,7 +3418,7 @@ ERL_NIF_TERM esock_global_info(ErlNifEnv* env) * domain: The domain of the socket * type: The type of the socket * protocol: The protocol of the socket - * (ctrl: Controlling process of the socket) + * ctrl: Controlling process of the socket) * (readable: Is the socket readable) * (writable: Is the socket writable) * (connected: Is the socket connected) @@ -3297,7 +3435,7 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env, ERL_NIF_TERM domain = esock_socket_info_domain(env, descP); ERL_NIF_TERM type = esock_socket_info_type(env, descP); ERL_NIF_TERM protocol = esock_socket_info_protocol(env, descP); - // ERL_NIF_TERM ctrlPid = MKPID(env, &descP->ctrlPid); + ERL_NIF_TERM ctrlPid = MKPID(env, &descP->ctrlPid); ERL_NIF_TERM readable = BOOL2ATOM(descP->isReadable); ERL_NIF_TERM writable = BOOL2ATOM(descP->isWritable); // ERL_NIF_TERM connected = BOOL2ATOM(descP->isConnected); @@ -3308,6 +3446,7 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env, ERL_NIF_TERM keys[] = {esock_atom_domain, esock_atom_type, esock_atom_protocol, + esock_atom_ctrl, atom_readable, atom_writable, atom_counters, @@ -3317,6 +3456,7 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env, ERL_NIF_TERM vals[] = {domain, type, protocol, + ctrlPid, readable, writable, counters, @@ -3409,24 +3549,32 @@ ERL_NIF_TERM esock_socket_info_counters(ErlNifEnv* env, { ERL_NIF_TERM info; - MLOCK(descP->writeMtx); MLOCK(descP->readMtx); + MLOCK(descP->writeMtx); { ERL_NIF_TERM readByteCnt = MKCT(env, atom_read_byte, descP->readByteCnt); ERL_NIF_TERM readFails = MKCT(env, atom_read_fails, descP->readFails); ERL_NIF_TERM readPkgCnt = MKCT(env, atom_read_pkg, descP->readPkgCnt); + ERL_NIF_TERM readPkgMax = MKCT(env, atom_read_pkg_max, descP->readPkgMax); ERL_NIF_TERM readTries = MKCT(env, atom_read_tries, descP->readTries); ERL_NIF_TERM readWaits = MKCT(env, atom_read_waits, descP->readWaits); ERL_NIF_TERM writeByteCnt = MKCT(env, atom_write_byte, descP->writeByteCnt); ERL_NIF_TERM writeFails = MKCT(env, atom_write_fails, descP->writeFails); ERL_NIF_TERM writePkgCnt = MKCT(env, atom_write_pkg, descP->writePkgCnt); + ERL_NIF_TERM writePkgMax = MKCT(env, atom_write_pkg_max, descP->writePkgMax); ERL_NIF_TERM writeTries = MKCT(env, atom_write_tries, descP->writeTries); ERL_NIF_TERM writeWaits = MKCT(env, atom_write_waits, descP->writeWaits); - ERL_NIF_TERM acnt[] = {readByteCnt, readFails, readPkgCnt, + + ERL_NIF_TERM accSuccess = MKCT(env, atom_acc_success, descP->accSuccess); + ERL_NIF_TERM accFails = MKCT(env, atom_acc_fails, descP->accFails); + ERL_NIF_TERM accTries = MKCT(env, atom_acc_tries, descP->accTries); + ERL_NIF_TERM accWaits = MKCT(env, atom_acc_waits, descP->accWaits); + ERL_NIF_TERM acnt[] = {readByteCnt, readFails, readPkgCnt, readPkgMax, readTries, readWaits, - writeByteCnt, writeFails, writePkgCnt, - writeTries, writeWaits}; + writeByteCnt, writeFails, writePkgCnt, writePkgMax, + writeTries, writeWaits, + accSuccess, accFails, accTries, accWaits}; unsigned int lenACnt = sizeof(acnt) / sizeof(ERL_NIF_TERM); info = MKLA(env, acnt, lenACnt); @@ -3438,8 +3586,8 @@ ERL_NIF_TERM esock_socket_info_counters(ErlNifEnv* env, } - MUNLOCK(descP->readMtx); MUNLOCK(descP->writeMtx); + MUNLOCK(descP->readMtx); SSDBG( descP, ("SOCKET", "esock_socket_info_counters -> done with" "\r\n info: %T" @@ -5334,6 +5482,9 @@ ERL_NIF_TERM esock_open(ErlNifEnv* env, inc_socket(domain, type, protocol); + /* And finally update the registry */ + esock_send_reg_add_msg(env, res); + return esock_make_ok2(env, res); } @@ -6146,7 +6297,8 @@ ERL_NIF_TERM esock_accept_listening(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "esock_accept_listening -> success\r\n") ); - res = esock_accept_listening_accept(env, descP, accSock, caller, &remote); + res = esock_accept_listening_accept(env, descP, + sockRef, accSock, caller, &remote); } @@ -6180,6 +6332,8 @@ ERL_NIF_TERM esock_accept_listening_error(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "esock_accept_listening_error -> would block\r\n") ); + ESOCK_CNT_INC(env, descP, sockRef, atom_acc_tries, &descP->accTries, 1); + descP->currentAcceptor.pid = caller; if (MONP("esock_accept_listening -> current acceptor", env, descP, @@ -6201,6 +6355,9 @@ ERL_NIF_TERM esock_accept_listening_error(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "esock_accept_listening -> errno: %d\r\n", save_errno) ); + + ESOCK_CNT_INC(env, descP, sockRef, atom_acc_fails, &descP->accFails, 1); + res = esock_make_error_errno(env, save_errno); } @@ -6215,13 +6372,14 @@ ERL_NIF_TERM esock_accept_listening_error(ErlNifEnv* env, static ERL_NIF_TERM esock_accept_listening_accept(ErlNifEnv* env, ESockDescriptor* descP, + ERL_NIF_TERM sockRef, SOCKET accSock, ErlNifPid caller, ESockAddress* remote) { ERL_NIF_TERM res; - esock_accept_accepted(env, descP, accSock, caller, remote, &res); + esock_accept_accepted(env, descP, sockRef, accSock, caller, remote, &res); return res; } @@ -6342,16 +6500,9 @@ ERL_NIF_TERM esock_accept_accepting_current_accept(ErlNifEnv* env, { ERL_NIF_TERM res; - if (esock_accept_accepted(env, descP, accSock, + if (esock_accept_accepted(env, descP, sockRef, accSock, descP->currentAcceptor.pid, remote, &res)) { - /* Clean out the old cobweb's before trying to invite a new spider */ - - esock_free_env("esock_accept_accepting_current_accept - " - "current-accept-env", - descP->currentAcceptor.env); - descP->currentAcceptor.env = NULL; - if (!activate_next_acceptor(env, descP, sockRef)) { SSDBG( descP, @@ -6362,13 +6513,6 @@ ERL_NIF_TERM esock_accept_accepting_current_accept(ErlNifEnv* env, descP->state = ESOCK_STATE_LISTENING; descP->currentAcceptorP = NULL; - /* Do we really need this? - * The activate_next_acceptor (actually the requestor_pop) function - * initiates these values if there are no waiting acceptor... - */ - descP->currentAcceptor.ref = esock_atom_undefined; - enif_set_pid_undefined(&descP->currentAcceptor.pid); - MON_INIT(&descP->currentAcceptor.mon); } } @@ -6390,10 +6534,8 @@ ERL_NIF_TERM esock_accept_accepting_current_error(ErlNifEnv* env, ERL_NIF_TERM opRef, int save_errno) { - ESockRequestor req; ERL_NIF_TERM res, reason; - req.env = NULL; if (save_errno == ERRNO_BLOCK) { /* @@ -6405,16 +6547,25 @@ ERL_NIF_TERM esock_accept_accepting_current_error(ErlNifEnv* env, "esock_accept_accepting_current_error -> " "would block: try again\r\n") ); + ESOCK_CNT_INC(env, descP, sockRef, atom_acc_waits, &descP->accWaits, 1); + res = esock_accept_busy_retry(env, descP, sockRef, opRef, &descP->currentAcceptor.pid, /* No state change */ descP->state); } else { + ESockRequestor req; + + ESOCK_CNT_INC(env, descP, sockRef, atom_acc_fails, &descP->accFails, 1); + + esock_release_current("esock_accept_accepting_current_error", + env, descP, descP->currentAcceptorP); reason = MKA(env, erl_errno_id(save_errno)); res = esock_make_error(env, reason); + req.env = NULL; while (acceptor_pop(env, descP, &req)) { SSDBG( descP, ("SOCKET", @@ -6426,7 +6577,7 @@ ERL_NIF_TERM esock_accept_accepting_current_error(ErlNifEnv* env, DEMONP("esock_accept_accepting_current_error -> pop'ed writer", env, descP, &req.mon); } - + descP->currentAcceptorP = NULL; } return res; @@ -6475,6 +6626,21 @@ ERL_NIF_TERM esock_accept_busy_retry(ErlNifEnv* env, if ((sres = esock_select_read(env, descP->sock, descP, pid, sockRef, accRef)) < 0) { + + DEMONP("esock_accept_busy_retry - select failed", + env, descP, &descP->currentAcceptor.mon); + /* It is very unlikely that a next acceptor will be able + * to do anything succesful, but we will clean the queue + */ + if (!activate_next_acceptor(env, descP, sockRef)) { + SSDBG( descP, + ("SOCKET", + "esock_accept_busy_retry -> no more acceptors\r\n") ); + + descP->state = ESOCK_STATE_LISTENING; + descP->currentAcceptorP = NULL; + } + reason = MKT2(env, esock_atom_select_failed, MKI(env, sres)); res = esock_make_error(env, reason); } else { @@ -6494,6 +6660,7 @@ ERL_NIF_TERM esock_accept_busy_retry(ErlNifEnv* env, static BOOLEAN_T esock_accept_accepted(ErlNifEnv* env, ESockDescriptor* descP, + ERL_NIF_TERM sockRef, SOCKET accSock, ErlNifPid pid, ESockAddress* remote, @@ -6508,6 +6675,8 @@ BOOLEAN_T esock_accept_accepted(ErlNifEnv* env, * We got one */ + ESOCK_CNT_INC(env, descP, sockRef, atom_acc_success, &descP->accSuccess, 1); + if ((accEvent = sock_create_event(accSock)) == INVALID_EVENT) { save_errno = sock_errno(); while ((sock_close(accSock) == INVALID_SOCKET) && @@ -6530,6 +6699,8 @@ BOOLEAN_T esock_accept_accepted(ErlNifEnv* env, accDescP->rNumCnt = 0; accDescP->rCtrlSz = descP->rCtrlSz; // Inherit buffer size accDescP->wCtrlSz = descP->wCtrlSz; // Inherit buffer size + accDescP->iow = descP->iow; // Inherit iow + accDescP->dbg = descP->dbg; // Inherit debug flag accRef = enif_make_resource(env, accDescP); enif_release_resource(accDescP); @@ -6552,6 +6723,9 @@ BOOLEAN_T esock_accept_accepted(ErlNifEnv* env, accDescP->isReadable = TRUE; accDescP->isWritable = TRUE; + /* And finally update the registry */ + esock_send_reg_add_msg(env, accRef); + *result = esock_make_ok2(env, accRef); return TRUE; @@ -6596,12 +6770,14 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, if ((argc != 4) || !GET_BIN(env, argv[2], &sndData) || !GET_UINT(env, argv[3], &eflags)) { + SGDBG( ("SOCKET", "nif_send -> argv decode failed\r\n") ); return enif_make_badarg(env); } sockRef = argv[0]; // We need this in case we send in case we send abort sendRef = argv[1]; if (!ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) { + SSDBG( descP, ("SOCKET", "nif_send -> get resource failed\r\n") ); return enif_make_badarg(env); } @@ -6613,8 +6789,10 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, "\r\n eFlags: 0x%lX" "\r\n", descP->sock, sockRef, sendRef, sndData.size, eflags) ); - if (!esendflags2sendflags(eflags, &flags)) + if (!esendflags2sendflags(eflags, &flags)) { + SSDBG( descP, ("SOCKET", "nif_send -> sendflags decode failed\r\n") ); return esock_make_error(env, esock_atom_einval); + } SSDBG( descP, ("SOCKET", "nif_send -> flags: 0x%lX\r\n", flags) ); @@ -6659,18 +6837,22 @@ ERL_NIF_TERM esock_send(ErlNifEnv* env, ssize_t written; ERL_NIF_TERM writerCheck; - if (!descP->isWritable) - return enif_make_badarg(env); + if (!descP->isWritable) { + SSDBG( descP, ("SOCKET", "esock_send -> return not writable\r\n") ); + return esock_make_error(env, atom_closed); + } - /* Check if there is already a current writer and if its us */ - if (!send_check_writer(env, descP, sendRef, &writerCheck)) + /* Ensure that we either have no current writer or that we are it */ + if (!send_check_writer(env, descP, sendRef, &writerCheck)) { + SSDBG( descP, ("SOCKET", "esock_send -> writer check failed: " + "\r\n %T\r\n", writerCheck) ); return writerCheck; + } /* We ignore the wrap for the moment. * Maybe we should issue a wrap-message to controlling process... */ - // cnt_inc(&descP->writeTries, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_write_tries, &descP->writeTries, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_write_tries, &descP->writeTries, 1); written = sock_send(descP->sock, sndDataP->data, sndDataP->size, flags); if (IS_SOCKET_ERROR(written)) @@ -6727,6 +6909,7 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, if ((argc != 5) || !GET_BIN(env, argv[2], &sndData) || !GET_UINT(env, argv[4], &eflags)) { + SGDBG( ("SOCKET", "nif_sendto -> argv decode failed\r\n") ); return enif_make_badarg(env); } sockRef = argv[0]; // We need this in case we send abort (to the caller) @@ -6734,6 +6917,7 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, eSockAddr = argv[3]; if (!ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) { + SSDBG( descP, ("SOCKET", "nif_sendto -> get resource failed\r\n") ); return enif_make_badarg(env); } @@ -6792,18 +6976,22 @@ ERL_NIF_TERM esock_sendto(ErlNifEnv* env, ssize_t written; ERL_NIF_TERM writerCheck; - if (!descP->isWritable) - return enif_make_badarg(env); + if (!descP->isWritable) { + SSDBG( descP, ("SOCKET", "esock_sendto -> return not writable\r\n") ); + return esock_make_error(env, atom_closed); + } - /* Check if there is already a current writer and if its us */ - if (!send_check_writer(env, descP, sendRef, &writerCheck)) + /* Ensure that we either have no current writer or we are it */ + if (!send_check_writer(env, descP, sendRef, &writerCheck)) { + SSDBG( descP, ("SOCKET", "esock_sendto -> writer check failed: " + "\r\n %T\r\n", writerCheck) ); return writerCheck; + } /* We ignore the wrap for the moment. * Maybe we should issue a wrap-message to controlling process... */ - // cnt_inc(&descP->writeTries, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_write_tries, &descP->writeTries, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_write_tries, &descP->writeTries, 1); if (toAddrP != NULL) { written = sock_sendto(descP->sock, @@ -6859,6 +7047,7 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, if ((argc != 4) || !IS_MAP(env, argv[2]) || !GET_UINT(env, argv[3], &eflags)) { + SGDBG( ("SOCKET", "nif_sendmsg -> argv decode failed\r\n") ); return enif_make_badarg(env); } sockRef = argv[0]; // We need this in case we send abort (to the caller) @@ -6866,6 +7055,7 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, eMsgHdr = argv[2]; if (!ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) { + SSDBG( descP, ("SOCKET", "nif_sendmsg -> get resource failed\r\n") ); return enif_make_badarg(env); } @@ -6877,8 +7067,10 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, "\r\n", descP->sock, argv[0], sendRef, eflags) ); - if (!esendflags2sendflags(eflags, &flags)) + if (!esendflags2sendflags(eflags, &flags)) { + SSDBG( descP, ("SOCKET", "nif_sendmsg -> sendflags decode failed\r\n") ); return esock_make_error(env, esock_atom_einval); + } MLOCK(descP->writeMtx); @@ -6920,19 +7112,15 @@ ERL_NIF_TERM esock_sendmsg(ErlNifEnv* env, char* xres; if (!descP->isWritable) { - SSDBG( descP, ("SOCKET", "esock_sendmsg -> not writable\r\n") ); - - return enif_make_badarg(env); + return esock_make_error(env, atom_closed); } - /* Check if there is already a current writer and if its us */ + /* Ensure that we either have no current writer or we are it */ if (!send_check_writer(env, descP, sendRef, &writerCheck)) { - - SSDBG( descP, - ("SOCKET", "esock_sendmsg -> writer check failed: " - "\r\n %T\r\n", writerCheck) ); - + SSDBG( descP, + ("SOCKET", "esock_sendmsg -> writer check failed: " + "\r\n %T\r\n", writerCheck) ); return writerCheck; } @@ -7044,8 +7232,7 @@ ERL_NIF_TERM esock_sendmsg(ErlNifEnv* env, /* We ignore the wrap for the moment. * Maybe we should issue a wrap-message to controlling process... */ - // cnt_inc(&descP->writeTries, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_write_tries, &descP->writeTries, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_write_tries, &descP->writeTries, 1); /* And now, finally, try to send the message */ written = sock_sendmsg(descP->sock, &msgHdr, flags); @@ -7164,16 +7351,20 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, #else ESockDescriptor* descP; ERL_NIF_TERM sockRef, recvRef; - int len; + ErlNifUInt64 elen; unsigned int eflags; + ssize_t len; /* ssize_t due to the return type of recv() */ int flags; ERL_NIF_TERM res; if ((argc != 4) || - !GET_INT(env, argv[2], &len) || + !GET_UINT64(env, argv[2], &elen) || !GET_UINT(env, argv[3], &eflags)) { return enif_make_badarg(env); } + len = (ssize_t) elen; + if (elen != (ErlNifUInt64)len) return enif_make_badarg(env); + sockRef = argv[0]; // We need this in case we send abort (to the caller) recvRef = argv[1]; @@ -7184,7 +7375,8 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, if (!erecvflags2recvflags(eflags, &flags)) return esock_make_error(env, esock_atom_einval); - SSDBG( descP, ("SOCKET", "nif_recv -> flags: 0x%lX\r\n", flags) ); + SSDBG( descP, ("SOCKET", "nif_recv -> flags: 0x%lX\r\n", + (unsigned)flags) ); MLOCK(descP->readMtx); @@ -7196,10 +7388,12 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, * is done! */ - res = esock_recv(env, descP, sockRef, recvRef, len, flags); + res = esock_recv(env, descP, sockRef, recvRef, (size_t)len, flags); MUNLOCK(descP->readMtx); + SSDBG( descP, ("SOCKET", "nif_recv -> done: %T\r\n", res) ); + return res; #endif // if defined(__WIN32__) @@ -7217,24 +7411,26 @@ ERL_NIF_TERM esock_recv(ErlNifEnv* env, ESockDescriptor* descP, ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef, - int len, + size_t len, int flags) { ssize_t read; ErlNifBinary buf; ERL_NIF_TERM readerCheck; int save_errno; - int bufSz = (len ? len : descP->rBufSz); + size_t bufSz = (len ? len : descP->rBufSz); SSDBG( descP, ("SOCKET", "esock_recv -> entry with" - "\r\n len: %d (%d:%d)" + "\r\n len: %lu (%d:lu)" "\r\n flags: %d" - "\r\n", len, descP->rNumCnt, bufSz, flags) ); + "\r\n", + (unsigned long)len, descP->rNumCnt, (unsigned long)bufSz, + flags) ); if (!descP->isReadable) - return enif_make_badarg(env); + return esock_make_error(env, atom_closed); - /* Check if there is already a current reader and if its us */ + /* Ensure that we either have no current reader or that we are it */ if (!recv_check_reader(env, descP, recvRef, &readerCheck)) return readerCheck; @@ -7245,11 +7441,11 @@ ERL_NIF_TERM esock_recv(ErlNifEnv* env, if (!ALLOC_BIN(bufSz, &buf)) return esock_make_error(env, atom_exalloc); - // cnt_inc(&descP->readTries, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_read_tries, &descP->readTries, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_tries, &descP->readTries, 1); // If it fails (read = -1), we need errno... - SSDBG( descP, ("SOCKET", "esock_recv -> try read (%d)\r\n", buf.size) ); + SSDBG( descP, ("SOCKET", "esock_recv -> try read (%lu)\r\n", + (unsigned long)buf.size) ); read = sock_recv(descP->sock, buf.data, buf.size, flags); if (IS_SOCKET_ERROR(read)) { save_errno = sock_errno(); @@ -7258,7 +7454,8 @@ ERL_NIF_TERM esock_recv(ErlNifEnv* env, } SSDBG( descP, ("SOCKET", - "esock_recv -> read: %d (%d)\r\n", read, save_errno) ); + "esock_recv -> read: %ld (%d)\r\n", + (long)read, save_errno) ); return recv_check_result(env, descP, read, len, @@ -7396,9 +7593,9 @@ ERL_NIF_TERM esock_recvfrom(ErlNifEnv* env, "\r\n", len, bufSz, flags) ); if (!descP->isReadable) - return enif_make_badarg(env); + return esock_make_error(env, atom_closed); - /* Check if there is already a current reader and if its us */ + /* Ensure that we either have no current reader or that we are it */ if (!recv_check_reader(env, descP, recvRef, &readerCheck)) return readerCheck; @@ -7409,8 +7606,7 @@ ERL_NIF_TERM esock_recvfrom(ErlNifEnv* env, if (!ALLOC_BIN(bufSz, &buf)) return esock_make_error(env, atom_exalloc); - // cnt_inc(&descP->readTries, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_read_tries, &descP->readTries, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_tries, &descP->readTries, 1); addrLen = sizeof(fromAddr); sys_memzero((char*) &fromAddr, addrLen); @@ -7572,9 +7768,9 @@ ERL_NIF_TERM esock_recvmsg(ErlNifEnv* env, "\r\n", bufSz, bufLen, ctrlSz, ctrlLen, flags) ); if (!descP->isReadable) - return enif_make_badarg(env); + return esock_make_error(env, atom_closed); - /* Check if there is already a current reader and if its us */ + /* Ensure that we either have no current reader or that we are it */ if (!recv_check_reader(env, descP, recvRef, &readerCheck)) return readerCheck; @@ -7597,8 +7793,7 @@ ERL_NIF_TERM esock_recvmsg(ErlNifEnv* env, if (!ALLOC_BIN(ctrlSz, &ctrl)) return esock_make_error(env, atom_exalloc); - // cnt_inc(&descP->readTries, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_read_tries, &descP->readTries, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_tries, &descP->readTries, 1); addrLen = sizeof(addr); sys_memzero((char*) &addr, addrLen); @@ -7652,6 +7847,9 @@ ERL_NIF_TERM nif_close(ErlNifEnv* env, return enif_raise_exception(env, MKA(env, "notsup")); #else ESockDescriptor* descP; + ERL_NIF_TERM res; + + SGDBG( ("SOCKET", "nif_close -> entry with argc: %d\r\n", argc) ); if ((argc != 1) || !ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) { @@ -7661,7 +7859,15 @@ ERL_NIF_TERM nif_close(ErlNifEnv* env, if (IS_CLOSED(descP) || IS_CLOSING(descP)) return esock_make_error(env, atom_closed); - return esock_close(env, descP); + MLOCK(descP->closeMtx); + + res = esock_close(env, descP); + + MUNLOCK(descP->closeMtx); + + SSDBG( descP, ("SOCKET", "nif_close -> res: %T\r\n", res) ); + + return res; #endif // if defined(__WIN32__) } @@ -7682,8 +7888,6 @@ ERL_NIF_TERM esock_close(ErlNifEnv* env, descP->currentReaderP, descP->currentAcceptorP) ); - MLOCK(descP->closeMtx); - doClose = esock_close_check(env, descP, &reason); if (doClose) { @@ -7692,8 +7896,6 @@ ERL_NIF_TERM esock_close(ErlNifEnv* env, reply = esock_make_error(env, reason); } - MUNLOCK(descP->closeMtx); - SSDBG( descP, ("SOCKET", "esock_close -> [%d] done when: " "\r\n state: 0x%lX" @@ -7962,13 +8164,14 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env, return enif_make_badarg(env); } - if (IS_CLOSED(descP) || IS_CLOSING(descP)) + if (IS_CLOSED(descP)) return esock_make_error(env, atom_closed); if (!ehow2how(ehow, &how)) return enif_make_badarg(env); return esock_shutdown(env, descP, how); + #endif // if defined(__WIN32__) } @@ -8167,6 +8370,10 @@ ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env, result = esock_setopt_otp_sndctrlbuf(env, descP, eVal); break; + case ESOCK_OPT_OTP_META: + result = esock_setopt_otp_meta(env, descP, eVal); + break; + default: result = esock_make_error(env, esock_atom_einval); break; @@ -8365,6 +8572,32 @@ ERL_NIF_TERM esock_setopt_otp_sndctrlbuf(ErlNifEnv* env, } +/* esock_setopt_otp_meta - Handle the OTP (level) meta options + */ +static +ERL_NIF_TERM esock_setopt_otp_meta(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) +{ + ErlNifPid caller; + + if (enif_self(env, &caller) == NULL) + return esock_make_error(env, atom_exself); + + if (COMPARE_PIDS(&descP->ctrlPid, &caller) != 0) { + SSDBG( descP, ("SOCKET", + "esock_setopt_otp_meta -> not owner (%T)\r\n", + descP->ctrlPid) ); + return esock_make_error(env, esock_atom_not_owner); + } + + enif_clear_env(descP->meta.env); + descP->meta.ref = CP_TERM(descP->meta.env, eVal); + + return esock_atom_ok; +} + + /* The option has *not* been encoded. Instead it has been provided * in "native mode" (option is provided as is and value as a binary). @@ -11727,7 +11960,7 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, eIsEncoded = argv[1]; eOpt = argv[3]; // Is "normally" an int, but if raw mode: {Int, ValueSz} - if (IS_CLOSED(descP) || IS_CLOSING(descP)) + if (IS_CLOSED(descP)) return esock_make_error(env, atom_closed); SSDBG( descP, @@ -11846,6 +12079,10 @@ ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env, result = esock_getopt_otp_fd(env, descP); break; + case ESOCK_OPT_OTP_META: + result = esock_getopt_otp_meta(env, descP); + break; + /* *** INTERNAL *** */ case ESOCK_OPT_OTP_DOMAIN: result = esock_getopt_otp_domain(env, descP); @@ -11859,6 +12096,10 @@ ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env, result = esock_getopt_otp_protocol(env, descP); break; + case ESOCK_OPT_OTP_DTP: + result = esock_getopt_otp_dtp(env, descP); + break; + default: result = esock_make_error(env, esock_atom_einval); break; @@ -11964,128 +12205,187 @@ ERL_NIF_TERM esock_getopt_otp_fd(ErlNifEnv* env, } -/* esock_getopt_otp_domain - Handle the OTP (level) domain option +/* esock_getopt_otp_meta - Handle the OTP (level) meta option */ static -ERL_NIF_TERM esock_getopt_otp_domain(ErlNifEnv* env, - ESockDescriptor* descP) +ERL_NIF_TERM esock_getopt_otp_meta(ErlNifEnv* env, + ESockDescriptor* descP) { - ERL_NIF_TERM result, reason; - int val = descP->domain; + ERL_NIF_TERM eVal = CP_TERM(env, descP->meta.ref); - switch (val) { + return esock_make_ok2(env, eVal); +} + + +static +ERL_NIF_TERM getopt_otp_domain(ErlNifEnv* env, int domain) +{ + ERL_NIF_TERM result; + + switch (domain) { case AF_INET: - result = esock_make_ok2(env, esock_atom_inet); + result = esock_atom_inet; break; #if defined(HAVE_IN6) && defined(AF_INET6) case AF_INET6: - result = esock_make_ok2(env, esock_atom_inet6); + result = esock_atom_inet6; break; #endif #if defined(HAVE_SYS_UN_H) case AF_UNIX: - result = esock_make_ok2(env, esock_atom_local); + result = esock_atom_local; break; #endif default: - reason = MKT2(env, esock_atom_unknown, MKI(env, val)); - result = esock_make_error(env, reason); + result = MKI(env, domain); break; } return result; } +/* + * esock_getopt_otp_domain - Handle the OTP (level) domain option + */ +static +ERL_NIF_TERM esock_getopt_otp_domain(ErlNifEnv* env, + ESockDescriptor* descP) +{ + ERL_NIF_TERM domain, result; + + domain = getopt_otp_domain(env, descP->domain); + result = esock_make_ok2(env, domain); + + return result; +} -/* esock_getopt_otp_type - Handle the OTP (level) type options. - */ static -ERL_NIF_TERM esock_getopt_otp_type(ErlNifEnv* env, - ESockDescriptor* descP) +ERL_NIF_TERM getopt_otp_type(ErlNifEnv* env, int type) { - ERL_NIF_TERM result, reason; - int val = descP->type; + ERL_NIF_TERM result; - switch (val) { + switch (type) { case SOCK_STREAM: - result = esock_make_ok2(env, esock_atom_stream); + result = esock_atom_stream; break; case SOCK_DGRAM: - result = esock_make_ok2(env, esock_atom_dgram); + result = esock_atom_dgram; break; #ifdef HAVE_SCTP case SOCK_SEQPACKET: - result = esock_make_ok2(env, esock_atom_seqpacket); + result = esock_atom_seqpacket; break; #endif case SOCK_RAW: - result = esock_make_ok2(env, esock_atom_raw); + result = esock_atom_raw; break; case SOCK_RDM: - result = esock_make_ok2(env, esock_atom_rdm); + result = esock_atom_rdm; break; default: - reason = MKT2(env, esock_atom_unknown, MKI(env, val)); - result = esock_make_error(env, reason); + result = MKI(env, type); break; } return result; } +/* + * esock_getopt_otp_type - Handle the OTP (level) type options. + */ +static +ERL_NIF_TERM esock_getopt_otp_type(ErlNifEnv* env, + ESockDescriptor* descP) +{ + ERL_NIF_TERM type, result; + + type = getopt_otp_type(env, descP->type); + result = esock_make_ok2(env, type); + + return result; +} -/* esock_getopt_otp_protocol - Handle the OTP (level) protocol options. - */ static -ERL_NIF_TERM esock_getopt_otp_protocol(ErlNifEnv* env, - ESockDescriptor* descP) +ERL_NIF_TERM getopt_otp_protocol(ErlNifEnv* env, + ESockDescriptor* descP) { - ERL_NIF_TERM result, reason; + ERL_NIF_TERM result; int val = descP->protocol; - switch (val) { - case IPPROTO_IP: + switch (val) { + case IPPROTO_IP: #if defined(AF_LOCAL) - if (descP->domain == AF_LOCAL) { - result = esock_make_ok2(env, esock_atom_default); - } else { - result = esock_make_ok2(env, esock_atom_ip); - } + if (descP->domain == AF_LOCAL) { + result = esock_atom_default; + } else { + result = esock_atom_ip; + } #else - result = esock_make_ok2(env, esock_atom_ip); + result = esock_atom_ip; #endif - break; + break; - case IPPROTO_TCP: - result = esock_make_ok2(env, esock_atom_tcp); - break; + case IPPROTO_TCP: + result = esock_atom_tcp; + break; - case IPPROTO_UDP: - result = esock_make_ok2(env, esock_atom_udp); - break; + case IPPROTO_UDP: + result = esock_atom_udp; + break; #if defined(HAVE_SCTP) - case IPPROTO_SCTP: - result = esock_make_ok2(env, esock_atom_sctp); - break; + case IPPROTO_SCTP: + result = esock_atom_sctp; + break; #endif - default: - reason = MKT2(env, esock_atom_unknown, MKI(env, val)); - result = esock_make_error(env, reason); - break; + default: + result = MKI(env, val); + break; } return result; } +/* + * esock_getopt_otp_protocol - Handle the OTP (level) protocol options. + */ +static +ERL_NIF_TERM esock_getopt_otp_protocol(ErlNifEnv* env, + ESockDescriptor* descP) +{ + ERL_NIF_TERM protocol, result; + protocol = getopt_otp_protocol(env, descP); + result = esock_make_ok2(env, protocol); + + return result; +} + + +/* + * esock_getopt_otp_dtp - Handle the OTP (level) type options. + */ +static +ERL_NIF_TERM esock_getopt_otp_dtp(ErlNifEnv* env, + ESockDescriptor* descP) +{ + ERL_NIF_TERM domain, type, protocol, dtp, result; + + domain = getopt_otp_domain(env, descP->domain); + type = getopt_otp_type(env, descP->type); + protocol = getopt_otp_protocol(env, descP); + dtp = MKT3(env, domain, type, protocol); + result = esock_make_ok2(env, dtp); + + return result; +} /* The option has *not* been encoded. Instead it has been provided @@ -14882,12 +15182,6 @@ ERL_NIF_TERM esock_cancel_accept_current(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "esock_cancel_accept_current -> cancel res: %T\r\n", res) ); - /* Clean out the old cobweb's before trying to invite a new spider */ - - esock_free_env("esock_cancel_accept_current - current-accept-env", - descP->currentAcceptor.env); - descP->currentAcceptor.env = NULL; - if (!activate_next_acceptor(env, descP, sockRef)) { SSDBG( descP, @@ -14897,13 +15191,6 @@ ERL_NIF_TERM esock_cancel_accept_current(ErlNifEnv* env, descP->state = ESOCK_STATE_LISTENING; descP->currentAcceptorP = NULL; - /* Do we really need this? - * The activate_next_acceptor (actually the requestor_pop) function - * initiates these values if there are no waiting acceptor... - */ - descP->currentAcceptor.ref = esock_atom_undefined; - enif_set_pid_undefined(&descP->currentAcceptor.pid); - MON_INIT(&descP->currentAcceptor.mon); } SSDBG( descP, ("SOCKET", "esock_cancel_accept_current -> done with result:" @@ -15007,10 +15294,8 @@ ERL_NIF_TERM esock_cancel_send_current(ErlNifEnv* env, if (!activate_next_writer(env, descP, sockRef)) { SSDBG( descP, ("SOCKET", "esock_cancel_send_current -> no more writers\r\n") ); - descP->currentWriterP = NULL; - descP->currentWriter.ref = esock_atom_undefined; - enif_set_pid_undefined(&descP->currentWriter.pid); - esock_monitor_init(&descP->currentWriter.mon); + + descP->currentWriterP = NULL; } SSDBG( descP, ("SOCKET", "esock_cancel_send_current -> done with result:" @@ -15113,10 +15398,8 @@ ERL_NIF_TERM esock_cancel_recv_current(ErlNifEnv* env, if (!activate_next_reader(env, descP, sockRef)) { SSDBG( descP, ("SOCKET", "esock_cancel_recv_current -> no more readers\r\n") ); - descP->currentReaderP = NULL; - descP->currentReader.ref = esock_atom_undefined; - enif_set_pid_undefined(&descP->currentReader.pid); - esock_monitor_init(&descP->currentReader.mon); + + descP->currentReaderP = NULL; } SSDBG( descP, ("SOCKET", "esock_cancel_recv_current -> done with result:" @@ -15181,14 +15464,19 @@ ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv* env, int smode, int rmode) { + /* Assumes cancelling only one mode */ + int selectRes = esock_select_cancel(env, descP->sock, smode, descP); - if (selectRes & rmode) { - /* Was cancelled */ - return esock_atom_ok; - } else if (selectRes > 0) { - /* Has already sent the message */ - return esock_make_error(env, esock_atom_select_sent); + if (selectRes >= 0) { + /* Success */ + if ((selectRes & rmode) != 0) { + /* Was cancelled */ + return esock_atom_ok; + } else { + /* Has already sent the message */ + return esock_make_error(env, esock_atom_select_sent); + } } else { /* Stopped? */ SSDBG( descP, ("SOCKET", @@ -15226,6 +15514,8 @@ BOOLEAN_T send_check_writer(ErlNifEnv* env, if (enif_self(env, &caller) == NULL) { *checkResult = esock_make_error(env, atom_exself); + SSDBG( descP, ("SOCKET", + "send_check_writer -> exself\r\n") ); return FALSE; } @@ -15239,12 +15529,12 @@ BOOLEAN_T send_check_writer(ErlNifEnv* env, if (!writer_search4pid(env, descP, &caller)) *checkResult = writer_push(env, descP, caller, ref); else - *checkResult = esock_make_error(env, esock_atom_eagain); + *checkResult = esock_make_error(env, atom_exbusy); SSDBG( descP, ("SOCKET", "send_check_writer -> queue (push) result: %T\r\n", - checkResult) ); + *checkResult) ); return FALSE; @@ -15344,35 +15634,32 @@ ERL_NIF_TERM send_check_ok(ErlNifEnv* env, ssize_t dataSize, ERL_NIF_TERM sockRef) { - // cnt_inc(&descP->writePkgCnt, 1); - SOCK_CNT_INC(env, descP, sockRef, - atom_write_pkg, &descP->writePkgCnt, 1); - // cnt_inc(&descP->writeByteCnt, written); - SOCK_CNT_INC(env, descP, sockRef, - atom_write_byte, &descP->writeByteCnt, written); - - if (descP->currentWriterP != NULL) { - DEMONP("send_check_ok -> current writer", - env, descP, &descP->currentWriter.mon); - esock_free_env("send_check_ok", descP->currentWriter.env); - descP->currentWriter.env = NULL; - } + ESOCK_CNT_INC(env, descP, sockRef, + atom_write_pkg, &descP->writePkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, + atom_write_byte, &descP->writeByteCnt, written); + descP->writePkgMaxCnt += written; + if (descP->writePkgMaxCnt > descP->writePkgMax) + descP->writePkgMax = descP->writePkgMaxCnt; + descP->writePkgMaxCnt = 0; SSDBG( descP, ("SOCKET", "send_check_ok -> " "everything written (%d,%d) - done\r\n", dataSize, written) ); + if (descP->currentWriterP != NULL) { + DEMONP("send_check_ok -> current writer", + env, descP, &descP->currentWriter.mon); + } /* * Ok, this write is done maybe activate the next (if any) */ - if (!activate_next_writer(env, descP, sockRef)) { - descP->currentWriterP = NULL; - ESOCK_ASSERT(!descP->currentWriter.env); - descP->currentWriter.env = NULL; - descP->currentWriter.ref = esock_atom_undefined; - enif_set_pid_undefined(&descP->currentWriter.pid); - esock_monitor_init(&descP->currentWriter.mon); + + SSDBG( descP, + ("SOCKET", "send_check_ok -> no more writers\r\n") ); + + descP->currentWriterP = NULL; } return esock_atom_ok; @@ -15391,12 +15678,9 @@ ERL_NIF_TERM send_check_fail(ErlNifEnv* env, int saveErrno, ERL_NIF_TERM sockRef) { - ESockRequestor req; - ERL_NIF_TERM reason; + ERL_NIF_TERM reason; - req.env = NULL; - // cnt_inc(&descP->writeFails, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_write_fails, &descP->writeFails, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_write_fails, &descP->writeFails, 1); SSDBG( descP, ("SOCKET", "send_check_fail -> error: %d\r\n", saveErrno) ); @@ -15411,24 +15695,44 @@ ERL_NIF_TERM send_check_fail(ErlNifEnv* env, if (descP->currentWriterP != NULL) { - DEMONP("send_check_fail -> current writer", - env, descP, &descP->currentWriter.mon); + esock_release_current("send_check_fail", + env, descP, descP->currentWriterP); - while (writer_pop(env, descP, &req)) { - SSDBG( descP, - ("SOCKET", "send_check_fail -> abort %T\r\n", req.pid) ); - esock_send_abort_msg(env, sockRef, req.ref, req.env, - reason, &req.pid); - req.env = NULL; - DEMONP("send_check_fail -> pop'ed writer", - env, descP, &req.mon); - } + send_error_waiting_writers(env, descP, sockRef, reason); + + descP->currentWriterP = NULL; } } - return esock_make_error(env, reason); } +/* *** send_error_current_writer *** + * + * Process all waiting writers when a fatal error has occured. + * All waiting writers will be "aborted", that is a + * nif_abort message will be sent (with ref and reason). + */ +static +void send_error_waiting_writers(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM reason) +{ + ESockRequestor req; + + req.env = NULL; /* read by writer_pop before free */ + while (writer_pop(env, descP, &req)) { + SSDBG( descP, + ("SOCKET", "send_error_current_writer -> abort %T\r\n", + req.pid) ); + esock_send_abort_msg(env, sockRef, req.ref, req.env, + reason, &req.pid); + req.env = NULL; + DEMONP("send_error_waiting_writers -> pop'ed writer", + env, descP, &req.mon); + } +} + /* *** send_check_retry *** @@ -15463,15 +15767,17 @@ ERL_NIF_TERM send_check_retry(ErlNifEnv* env, enif_set_pid_undefined(&descP->currentWriter.pid); return esock_make_error(env, atom_exmon); } else { - ESOCK_ASSERT(!descP->currentWriter.env); + ESOCK_ASSERT(descP->currentWriter.env == NULL); descP->currentWriter.env = esock_alloc_env("current-writer"); - descP->currentWriter.ref = CP_TERM(descP->currentWriter.env, sendRef); + descP->currentWriter.ref = + CP_TERM(descP->currentWriter.env, sendRef); descP->currentWriterP = &descP->currentWriter; } + } else { + descP->currentWriter.ref = CP_TERM(descP->currentWriter.env, sendRef); } - // cnt_inc(&descP->writeWaits, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_write_waits, &descP->writeWaits, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_write_waits, &descP->writeWaits, 1); sres = esock_select_write(env, descP->sock, descP, NULL, sockRef, sendRef); @@ -15479,10 +15785,17 @@ ERL_NIF_TERM send_check_retry(ErlNifEnv* env, /* Partial *write* success */ + descP->writePkgMaxCnt += written; + if (sres < 0) { /* Returned: {error, Reason} * Reason: {select_failed, sres, written} */ + + if (descP->writePkgMaxCnt > descP->writePkgMax) + descP->writePkgMax = descP->writePkgMaxCnt; + descP->writePkgMaxCnt = 0; + res = esock_make_error(env, MKT3(env, esock_atom_select_failed, @@ -15537,8 +15850,6 @@ BOOLEAN_T recv_check_reader(ErlNifEnv* env, } if (COMPARE_PIDS(&descP->currentReader.pid, &caller) != 0) { - ERL_NIF_TERM tmp; - /* Not the "current reader", so (maybe) push onto queue */ SSDBG( descP, @@ -15546,15 +15857,14 @@ BOOLEAN_T recv_check_reader(ErlNifEnv* env, "recv_check_reader -> not (current) reader\r\n") ); if (!reader_search4pid(env, descP, &caller)) - tmp = reader_push(env, descP, caller, ref); + *checkResult = reader_push(env, descP, caller, ref); else - tmp = esock_make_error(env, esock_atom_eagain); + *checkResult = esock_make_error(env, atom_exbusy); SSDBG( descP, ("SOCKET", - "recv_check_reader -> queue (push) result: %T\r\n", tmp) ); - - *checkResult = tmp; + "recv_check_reader -> queue (push) result: %T\r\n", + *checkResult) ); return FALSE; @@ -15637,20 +15947,13 @@ ERL_NIF_TERM recv_update_current_reader(ErlNifEnv* env, DEMONP("recv_update_current_reader", env, descP, &descP->currentReader.mon); - esock_free_env("recv_update_current_reader - current-read-env", - descP->currentReader.env); - descP->currentReader.env = NULL; - if (!activate_next_reader(env, descP, sockRef)) { SSDBG( descP, ("SOCKET", "recv_update_current_reader -> no more readers\r\n") ); - descP->currentReaderP = NULL; - descP->currentReader.ref = esock_atom_undefined; - enif_set_pid_undefined(&descP->currentReader.pid); - esock_monitor_init(&descP->currentReader.mon); + descP->currentReaderP = NULL; } } @@ -15665,7 +15968,7 @@ ERL_NIF_TERM recv_update_current_reader(ErlNifEnv* env, * Process the current reader and any waiting readers * when a read (fatal) error has occured. * All waiting readers will be "aborted", that is a - * nif_abort message will be sent (with reaf and reason). + * nif_abort message will be sent (with ref and reason). */ static void recv_error_current_reader(ErlNifEnv* env, @@ -15673,14 +15976,13 @@ void recv_error_current_reader(ErlNifEnv* env, ERL_NIF_TERM sockRef, ERL_NIF_TERM reason) { - ESockRequestor req; - - req.env = NULL; if (descP->currentReaderP != NULL) { + ESockRequestor req; - DEMONP("recv_error_current_reader -> current reader", - env, descP, &descP->currentReader.mon); + esock_release_current("recv_error_current_reader", + env, descP, descP->currentReaderP); + req.env = NULL; /* read by reader_pop before free */ while (reader_pop(env, descP, &req)) { SSDBG( descP, ("SOCKET", "recv_error_current_reader -> abort %T\r\n", @@ -15691,6 +15993,8 @@ void recv_error_current_reader(ErlNifEnv* env, DEMONP("recv_error_current_reader -> pop'ed reader", env, descP, &req.mon); } + + descP->currentReaderP = NULL; } } @@ -15703,8 +16007,8 @@ void recv_error_current_reader(ErlNifEnv* env, static ERL_NIF_TERM recv_check_result(ErlNifEnv* env, ESockDescriptor* descP, - int read, - int toRead, + ssize_t read, + size_t toRead, int saveErrno, ErlNifBinary* bufP, ERL_NIF_TERM sockRef, @@ -15714,11 +16018,12 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "recv_check_result -> entry with" - "\r\n read: %d" - "\r\n toRead: %d" + "\r\n read: %ld" + "\r\n toRead: %lu" "\r\n saveErrno: %d" "\r\n recvRef: %T" - "\r\n", read, toRead, saveErrno, recvRef) ); + "\r\n", + (long)read, (unsigned long)toRead, saveErrno, recvRef) ); /* <KOLLA> @@ -15730,10 +16035,10 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, */ if ((read == 0) && (descP->type == SOCK_STREAM)) { - - res = esock_make_error(env, atom_closed); + ERL_NIF_TERM reason = atom_closed; + res = esock_make_error(env, reason); - SOCK_CNT_INC(env, descP, sockRef, atom_read_fails, &descP->readFails, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_fails, &descP->readFails, 1); /* * When a stream socket peer has performed an orderly shutdown, @@ -15744,7 +16049,7 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, * We must also notify any waiting readers! */ - recv_error_current_reader(env, descP, sockRef, res); + recv_error_current_reader(env, descP, sockRef, reason); FREE_BIN(bufP); @@ -15752,7 +16057,7 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, /* There is a special case: If the provided 'to read' value is * zero (0) (only for type =/= stream). - * That means that we reads as much as we can, using the default + * That means that we read as much as we can, using the default * read buffer size. */ @@ -15762,8 +16067,8 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", - "recv_check_result -> [%d] filled the buffer\r\n", - toRead) ); + "recv_check_result -> [%lu] filled the buffer\r\n", + (unsigned long)toRead) ); res = recv_check_full(env, descP, read, toRead, bufP, sockRef, recvRef); @@ -15781,9 +16086,10 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", - "recv_check_result -> [%d] " - "did not fill the buffer (%d of %d)\r\n", - toRead, read, bufP->size) ); + "recv_check_result -> [%lu] " + "did not fill the buffer (%ld of %lu)\r\n", + (unsigned long)toRead, + (long)read, (unsigned long)bufP->size) ); res = recv_check_partial(env, descP, read, toRead, bufP, sockRef, recvRef); @@ -15807,8 +16113,8 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, static ERL_NIF_TERM recv_check_full(ErlNifEnv* env, ESockDescriptor* descP, - int read, - int toRead, + ssize_t read, + size_t toRead, ErlNifBinary* bufP, ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef) @@ -15832,10 +16138,10 @@ ERL_NIF_TERM recv_check_full(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "recv_check_full -> shall we continue reading" - "\r\n read: %d" - "\r\n rNum: %d" - "\r\n rNumCnt: %d" - "\r\n", read, descP->rNum, descP->rNumCnt) ); + "\r\n read: %ld" + "\r\n rNum: %u" + "\r\n rNumCnt: %u" + "\r\n", (unsigned long)read, descP->rNum, descP->rNumCnt) ); res = recv_check_full_maybe_done(env, descP, read, toRead, bufP, sockRef, recvRef); @@ -15846,8 +16152,9 @@ ERL_NIF_TERM recv_check_full(ErlNifEnv* env, SSDBG( descP, ("SOCKET", - "recv_check_full -> [%d] " - "we got exactly what we could fit\r\n", toRead) ); + "recv_check_full -> [%lu] " + "we got exactly what we could fit\r\n", + (unsigned long)toRead) ); res = recv_check_full_done(env, descP, read, bufP, sockRef); @@ -15873,42 +16180,47 @@ ERL_NIF_TERM recv_check_full(ErlNifEnv* env, static ERL_NIF_TERM recv_check_full_maybe_done(ErlNifEnv* env, ESockDescriptor* descP, - int read, - int toRead, + ssize_t read, + size_t toRead, ErlNifBinary* bufP, ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef) { char* xres; - // cnt_inc(&descP->readByteCnt, read); - SOCK_CNT_INC(env, descP, sockRef, atom_read_byte, &descP->readByteCnt, read); - - if (descP->rNum > 0) { + ESOCK_CNT_INC(env, descP, sockRef, atom_read_byte, &descP->readByteCnt, read); + descP->readPkgMaxCnt += read; - descP->rNumCnt++; - if (descP->rNumCnt >= descP->rNum) { + descP->rNumCnt++; + if (descP->rNumCnt >= descP->rNum) { - descP->rNumCnt = 0; + descP->rNumCnt = 0; - // cnt_inc(&descP->readPkgCnt, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_read_pkg, &descP->readPkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, + atom_read_pkg, &descP->readPkgCnt, 1); + if (descP->readPkgMaxCnt > descP->readPkgMax) + descP->readPkgMax = descP->readPkgMaxCnt; + descP->readPkgMaxCnt = 0; - recv_update_current_reader(env, descP, sockRef); + recv_update_current_reader(env, descP, sockRef); - /* This transfers "ownership" of the *allocated* binary to an - * erlang term (no need for an explicit free). - */ + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ - return esock_make_ok3(env, atom_true, MKBIN(env, bufP)); + return esock_make_ok3(env, atom_true, MKBIN(env, bufP)); - } } /* Yes, we *do* need to continue reading */ if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) { descP->rNumCnt = 0; + + if (descP->readPkgMaxCnt > descP->readPkgMax) + descP->readPkgMax = descP->readPkgMaxCnt; + descP->readPkgMaxCnt = 0; + FREE_BIN(bufP); return esock_make_error_str(env, xres); } @@ -15919,8 +16231,8 @@ ERL_NIF_TERM recv_check_full_maybe_done(ErlNifEnv* env, SSDBG( descP, ("SOCKET", - "recv_check_full_maybe_done -> [%d] " - "we are done for now - read more\r\n", toRead) ); + "recv_check_full_maybe_done -> [%lu] " + "we are done for now - read more\r\n", (unsigned long)toRead) ); return esock_make_ok3(env, atom_false, MKBIN(env, bufP)); } @@ -15934,16 +16246,20 @@ ERL_NIF_TERM recv_check_full_maybe_done(ErlNifEnv* env, static ERL_NIF_TERM recv_check_full_done(ErlNifEnv* env, ESockDescriptor* descP, - int read, + ssize_t read, ErlNifBinary* bufP, ERL_NIF_TERM sockRef) { ERL_NIF_TERM data; - // cnt_inc(&descP->readPkgCnt, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_read_pkg, &descP->readPkgCnt, 1); - // cnt_inc(&descP->readByteCnt, read); - SOCK_CNT_INC(env, descP, sockRef, atom_read_byte, &descP->readByteCnt, read); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_pkg, &descP->readPkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_byte, + &descP->readByteCnt, read); + + descP->readPkgMaxCnt += read; + if (descP->readPkgMaxCnt > descP->readPkgMax) + descP->readPkgMax = descP->readPkgMaxCnt; + descP->readPkgMaxCnt = 0; recv_update_current_reader(env, descP, sockRef); @@ -15978,13 +16294,13 @@ ERL_NIF_TERM recv_check_fail(ErlNifEnv* env, /* +++ Oups - closed +++ */ - SSDBG( descP, ("SOCKET", "recv_check_fail -> closed\r\n") ); + SSDBG( descP, ("SOCKET", "recv_check_fail econnreset -> closed\r\n") ); // This is a bit overkill (to count here), but just in case... - // cnt_inc(&descP->readFails, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_read_fails, &descP->readFails, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_fails, + &descP->readFails, 1); - res = recv_check_fail_closed(env, descP, sockRef, recvRef); + res = recv_check_fail_econnreset(env, descP, sockRef, recvRef); } else if ((saveErrno == ERRNO_BLOCK) || (saveErrno == EAGAIN)) { @@ -15998,8 +16314,8 @@ ERL_NIF_TERM recv_check_fail(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "recv_check_fail -> errno: %d\r\n", saveErrno) ); - // cnt_inc(&descP->readFails, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_read_fails, &descP->readFails, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_fails, + &descP->readFails, 1); res = recv_check_fail_gen(env, descP, saveErrno, sockRef); } @@ -16009,19 +16325,19 @@ ERL_NIF_TERM recv_check_fail(ErlNifEnv* env, -/* *** recv_check_fail_closed *** +/* *** recv_check_fail_econnreset *** * * We detected that the socket was closed wile reading. * Inform current and waiting readers. */ static -ERL_NIF_TERM recv_check_fail_closed(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM recvRef) +ERL_NIF_TERM recv_check_fail_econnreset(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) { - ERL_NIF_TERM res = esock_make_error(env, atom_closed); - int sres; + ERL_NIF_TERM reason = atom_econnreset; + ERL_NIF_TERM res = esock_make_error(env, atom_econnreset); /* <KOLLA> * @@ -16038,16 +16354,18 @@ ERL_NIF_TERM recv_check_fail_closed(ErlNifEnv* env, * </KOLLA> */ + recv_error_current_reader(env, descP, sockRef, reason); + + MUNLOCK(descP->readMtx); + + MLOCK(descP->closeMtx); + MLOCK(descP->readMtx); + + descP->isReadable = FALSE; descP->closeLocal = FALSE; descP->state = ESOCK_STATE_CLOSING; - recv_error_current_reader(env, descP, sockRef, res); - - if ((sres = esock_select_stop(env, descP->sock, descP)) < 0) { - esock_warning_msg("Failed stop select (closed) " - "for current reader (%T): %d\r\n", - recvRef, sres); - } + MUNLOCK(descP->closeMtx); return res; } @@ -16100,11 +16418,11 @@ ERL_NIF_TERM recv_check_fail_gen(ErlNifEnv* env, int saveErrno, ERL_NIF_TERM sockRef) { - ERL_NIF_TERM res = esock_make_error_errno(env, saveErrno); + ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); - recv_error_current_reader(env, descP, sockRef, res); + recv_error_current_reader(env, descP, sockRef, reason); - return res; + return esock_make_error(env, reason); } @@ -16116,8 +16434,8 @@ ERL_NIF_TERM recv_check_fail_gen(ErlNifEnv* env, static ERL_NIF_TERM recv_check_partial(ErlNifEnv* env, ESockDescriptor* descP, - int read, - int toRead, + ssize_t read, + size_t toRead, ErlNifBinary* bufP, ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef) @@ -16133,14 +16451,16 @@ ERL_NIF_TERM recv_check_partial(ErlNifEnv* env, SSDBG( descP, ("SOCKET", - "recv_check_partial -> [%d] split buffer\r\n", toRead) ); + "recv_check_partial -> [%lu] split buffer\r\n", + (unsigned long)toRead) ); res = recv_check_partial_done(env, descP, read, bufP, sockRef); } else { - SSDBG( descP, ("SOCKET", "recv_check_partial -> [%d] " - "only part of message - expect more\r\n", toRead) ); + SSDBG( descP, ("SOCKET", "recv_check_partial -> [%lu] " + "only part of message - expect more\r\n", + (unsigned long)toRead) ); res = recv_check_partial_part(env, descP, read, bufP, sockRef, recvRef); } @@ -16157,17 +16477,21 @@ ERL_NIF_TERM recv_check_partial(ErlNifEnv* env, static ERL_NIF_TERM recv_check_partial_done(ErlNifEnv* env, ESockDescriptor* descP, - int read, + ssize_t read, ErlNifBinary* bufP, ERL_NIF_TERM sockRef) { ERL_NIF_TERM data; descP->rNumCnt = 0; - // cnt_inc(&descP->readPkgCnt, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_read_pkg, &descP->readPkgCnt, 1); - // cnt_inc(&descP->readByteCnt, read); - SOCK_CNT_INC(env, descP, sockRef, atom_read_byte, &descP->readByteCnt, read); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_pkg, &descP->readPkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_byte, + &descP->readByteCnt, read); + + descP->readPkgMaxCnt += read; + if (descP->readPkgMaxCnt > descP->readPkgMax) + descP->readPkgMax = descP->readPkgMaxCnt; + descP->readPkgMaxCnt = 0; recv_update_current_reader(env, descP, sockRef); @@ -16178,7 +16502,8 @@ ERL_NIF_TERM recv_check_partial_done(ErlNifEnv* env, data = MKSBIN(env, data, 0, read); SSDBG( descP, - ("SOCKET", "recv_check_partial_done -> [%d] done\r\n", read) ); + ("SOCKET", "recv_check_partial_done -> [%ld] done\r\n", + (long)read) ); return esock_make_ok3(env, atom_true, data); } @@ -16193,7 +16518,7 @@ ERL_NIF_TERM recv_check_partial_done(ErlNifEnv* env, static ERL_NIF_TERM recv_check_partial_part(ErlNifEnv* env, ESockDescriptor* descP, - int read, + ssize_t read, ErlNifBinary* bufP, ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef) @@ -16210,8 +16535,8 @@ ERL_NIF_TERM recv_check_partial_part(ErlNifEnv* env, data = MKBIN(env, bufP); data = MKSBIN(env, data, 0, read); - // cnt_inc(&descP->readByteCnt, read); - SOCK_CNT_INC(env, descP, sockRef, atom_read_byte, &descP->readByteCnt, read); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_byte, + &descP->readByteCnt, read); /* SELECT for more data */ @@ -16241,7 +16566,7 @@ ERL_NIF_TERM recv_check_partial_part(ErlNifEnv* env, /* The recvfrom function delivers one (1) message. If our buffer - * is to small, the message will be truncated. So, regardless + * is too small, the message will be truncated. So, regardless * if we filled the buffer or not, we have got what we are going * to get regarding this message. */ @@ -16298,11 +16623,10 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, data = MKSBIN(env, data, 0, read); } - // cnt_inc(&descP->readPkgCnt, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_read_pkg, &descP->readPkgCnt, 1); - // cnt_inc(&descP->readByteCnt, read); - SOCK_CNT_INC(env, descP, sockRef, atom_read_byte, - &descP->readByteCnt, read); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_pkg, + &descP->readPkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_byte, + &descP->readByteCnt, read); recv_update_current_reader(env, descP, sockRef); @@ -16361,7 +16685,7 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, * *We* do never actually try to read 0 bytes from a stream socket! */ - SOCK_CNT_INC(env, descP, sockRef, atom_read_fails, &descP->readFails, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_fails, &descP->readFails, 1); FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); @@ -16442,9 +16766,9 @@ ERL_NIF_TERM recvmsg_check_msg(ErlNifEnv* env, * For now, we increment all three... */ - SOCK_CNT_INC(env, descP, sockRef, atom_read_fails, &descP->readFails, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_read_pkg, &descP->readPkgCnt, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_read_byte, + ESOCK_CNT_INC(env, descP, sockRef, atom_read_fails, &descP->readFails, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_pkg, &descP->readPkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_byte, &descP->readByteCnt, read); recv_update_current_reader(env, descP, sockRef); @@ -16458,8 +16782,8 @@ ERL_NIF_TERM recvmsg_check_msg(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "recvmsg_check_result -> (msghdr) encode ok\r\n") ); - SOCK_CNT_INC(env, descP, sockRef, atom_read_pkg, &descP->readPkgCnt, 1); - SOCK_CNT_INC(env, descP, sockRef, atom_read_byte, + ESOCK_CNT_INC(env, descP, sockRef, atom_read_pkg, &descP->readPkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, atom_read_byte, &descP->readByteCnt, read); recv_update_current_reader(env, descP, sockRef); @@ -18653,64 +18977,66 @@ ESockDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) enif_set_pid_undefined(&descP->connPid); MON_INIT(&descP->connMon); - sprintf(buf, "esock[w,%d]", sock); + sprintf(buf, "esock.w[%d]", sock); descP->writeMtx = MCREATE(buf); - enif_set_pid_undefined(&descP->currentWriter.pid); - MON_INIT(&descP->currentWriter.mon); - descP->currentWriter.env = NULL; - descP->currentWriter.ref = esock_atom_undefined; + requestor_clear(&descP->currentWriter); descP->currentWriterP = NULL; // currentWriter not used descP->writersQ.first = NULL; descP->writersQ.last = NULL; descP->isWritable = FALSE; // TRUE; descP->writePkgCnt = 0; + descP->writePkgMax = 0; + descP->writePkgMaxCnt = 0; descP->writeByteCnt = 0; descP->writeTries = 0; descP->writeWaits = 0; descP->writeFails = 0; - sprintf(buf, "esock[r,%d]", sock); + sprintf(buf, "esock.r[%d]", sock); descP->readMtx = MCREATE(buf); - enif_set_pid_undefined(&descP->currentReader.pid); - MON_INIT(&descP->currentReader.mon); - descP->currentReader.env = NULL; - descP->currentReader.ref = esock_atom_undefined; + requestor_clear(&descP->currentReader); descP->currentReaderP = NULL; // currentReader not used descP->readersQ.first = NULL; descP->readersQ.last = NULL; descP->isReadable = FALSE; // TRUE; descP->readPkgCnt = 0; + descP->readPkgMax = 0; + descP->readPkgMaxCnt = 0; descP->readByteCnt = 0; descP->readTries = 0; descP->readWaits = 0; descP->readFails = 0; - sprintf(buf, "esock[acc,%d]", sock); + sprintf(buf, "esock.acc[%d]", sock); descP->accMtx = MCREATE(buf); - enif_set_pid_undefined(&descP->currentAcceptor.pid); - MON_INIT(&descP->currentAcceptor.mon); - descP->currentAcceptor.env = NULL; - descP->currentAcceptor.ref = esock_atom_undefined; + requestor_clear(&descP->currentAcceptor); descP->currentAcceptorP = NULL; // currentAcceptor not used descP->acceptorsQ.first = NULL; descP->acceptorsQ.last = NULL; + descP->accSuccess = 0; + descP->accFails = 0; + descP->accTries = 0; + descP->accWaits = 0; - sprintf(buf, "esock[close,%d]", sock); + sprintf(buf, "esock.close[%d]", sock); descP->closeMtx = MCREATE(buf); descP->closeEnv = NULL; descP->closeRef = esock_atom_undefined; enif_set_pid_undefined(&descP->closerPid); MON_INIT(&descP->closerMon); - sprintf(buf, "esock[cfg,%d]", sock); + sprintf(buf, "esock.cfg[%d]", sock); descP->cfgMtx = MCREATE(buf); descP->rBufSz = ESOCK_RECV_BUFFER_SIZE_DEFAULT; - descP->rNum = 0; + descP->rNum = ESOCK_RECV_BUFFER_COUNT_DEFAULT; descP->rNumCnt = 0; descP->rCtrlSz = ESOCK_RECV_CTRL_BUFFER_SIZE_DEFAULT; descP->wCtrlSz = ESOCK_SEND_CTRL_BUFFER_SIZE_DEFAULT; descP->iow = FALSE; descP->dbg = ESOCK_DEBUG_DEFAULT; + descP->meta.env = esock_alloc_env("alloc_descriptor - " + "meta-env"); + descP->meta.ref = esock_atom_undefined; descP->sock = sock; descP->event = event; @@ -19319,6 +19645,55 @@ size_t my_strnlen(const char *s, size_t maxlen) #endif + + +/* =========================================================================== + * + * Socket Registry message functions + * + * =========================================================================== + */ + +/* Send a (socket) add message to the socket registry process. + * We know that this process *is* alive since the VM would + * terminate otherwise, so there is no need to test if + * the sending fails. + */ +static +void esock_send_reg_add_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM msg = mk_reg_add_msg(env, sockRef); + + esock_send_msg(env, &data.regPid, msg, NULL); +} + + + +/* Send a (socket) del message to the socket registry process. + * We know that this process *is* alive since the VM would + * terminate otherwise, so there is no need to test if + * the sending fails. + */ +static +void esock_send_reg_del_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM msg = mk_reg_del_msg(env, sockRef); + + esock_send_msg(env, &data.regPid, msg, NULL); +} + + + + +/* =========================================================================== + * + * Socket user message functions + * + * =========================================================================== + */ + /* Send an counter wrap message to the controlling process: * A message in the form: * @@ -19354,6 +19729,7 @@ char* esock_send_close_msg(ErlNifEnv* env, { ERL_NIF_TERM sockRef, msg; ErlNifEnv* menv; + char* result; if (descP->closeEnv != NULL) { sockRef = enif_make_resource(descP->closeEnv, descP); @@ -19365,7 +19741,9 @@ char* esock_send_close_msg(ErlNifEnv* env, menv = NULL; // This has the effect that the message will be copied } - return esock_send_msg(env, pid, msg, menv); + result = esock_send_msg(env, pid, msg, menv); + descP->closeEnv = NULL; + return result; } @@ -19414,6 +19792,55 @@ char* esock_send_msg(ErlNifEnv* env, +/* *** mk_reg_add_msg *** + * + * Construct a socket add message for the socket registry. + * + * {'$socket', add, Socket} + * + */ +static +ERL_NIF_TERM mk_reg_add_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef) +{ + return mk_reg_msg(env, atom_add, sockRef); +} + + +/* *** mk_reg_del_msg *** + * + * Construct a socket del message for the socket registry. + * + * {'$socket', del, Socket} + * + */ +static +ERL_NIF_TERM mk_reg_del_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef) +{ + return mk_reg_msg(env, atom_del, sockRef); +} + + +/* *** mk_reg_msg *** + * + * Construct a general message for the socket registry. + * Tag is (at this time) either the atom 'add' or the atom 'del'. + * + * {'$socket', Tag, Socket} + * + */ +static +ERL_NIF_TERM mk_reg_msg(ErlNifEnv* env, + ERL_NIF_TERM tag, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM socket = mk_socket(env, sockRef); + + return MKT3(env, esock_atom_socket_tag, tag, socket); +} + + /* *** mk_abort_msg *** * * Create the abort message, which has the following form: @@ -19577,6 +20004,14 @@ int esock_select_write(ErlNifEnv* env, } +/* *** esock_select_stop *** + * + * WARNING: enif_select may call esock_stop directly + * in which case deadlock is avoided by esock_stop that checks + * if it got a direct call and then does not lock closeMtx. + * + * So closeMtx is supposed to be locked when this function is called. + */ static int esock_select_stop(ErlNifEnv* env, ErlNifEvent event, @@ -19848,15 +20283,19 @@ BOOLEAN_T requestor_pop(ESockRequestQueue* q, return TRUE; } else { /* Queue was empty */ - enif_set_pid_undefined(&reqP->pid); - MON_INIT(&reqP->mon); - reqP->env = NULL; - reqP->ref = esock_atom_undefined; // Just in case + requestor_clear(reqP); return FALSE; } } +static void requestor_clear(ESockRequestor* reqP) { + enif_set_pid_undefined(&reqP->pid); + MON_INIT(&reqP->mon); + reqP->env = NULL; + reqP->ref = esock_atom_undefined; +} + static BOOLEAN_T qsearch4pid(ErlNifEnv* env, @@ -19975,11 +20414,11 @@ BOOLEAN_T qunqueue(ErlNifEnv* env, #if !defined(__WIN32__) static -BOOLEAN_T cnt_inc(Uint32* cnt, Uint32 inc) +BOOLEAN_T cnt_inc(ESockCounter* cnt, ESockCounter inc) { - BOOLEAN_T wrap; - Uint32 max = 0xFFFFFFFF; - Uint32 current = *cnt; + BOOLEAN_T wrap; + ESockCounter max = ESOCK_COUNTER_MAX; + ESockCounter current = *cnt; if ((max - inc) >= current) { *cnt += inc; @@ -19994,9 +20433,9 @@ BOOLEAN_T cnt_inc(Uint32* cnt, Uint32 inc) static -void cnt_dec(Uint32* cnt, Uint32 dec) +void cnt_dec(ESockCounter* cnt, ESockCounter dec) { - Uint32 current = *cnt; + ESockCounter current = *cnt; if (dec > current) *cnt = 0; // The counter cannot be < 0 so this is the best we can do... @@ -20059,7 +20498,7 @@ int esock_demonitor(const char* slogan, res = enif_demonitor_process(env, descP, &monP->mon); if (res == 0) { - esock_monitor_init(monP); + MON_INIT(monP); } else { SSDBG( descP, ("SOCKET", "[%d][%T] %s: demonitor failed: %d\r\n", @@ -20088,6 +20527,17 @@ ERL_NIF_TERM esock_make_monitor_term(ErlNifEnv* env, const ESockMonitor* monP) +static +void esock_release_current(const char* slogan, + ErlNifEnv* env, + ESockDescriptor* descP, + ESockRequestor* current) +{ + DEMONP(slogan, env, descP, ¤t->mon); + esock_free_env(slogan, current->env); + requestor_clear(current); +} + #endif // if !defined(__WIN32__) @@ -20119,12 +20569,12 @@ void esock_dtor(ErlNifEnv* env, void* obj) #if !defined(__WIN32__) ESockDescriptor* descP = (ESockDescriptor*) obj; - SGDBG( ("SOCKET", "dtor -> try destroy write mutex\r\n") ); - MDESTROY(descP->writeMtx); descP->writeMtx = NULL; - SGDBG( ("SOCKET", "dtor -> try destroy read mutex\r\n") ); MDESTROY(descP->readMtx); descP->readMtx = NULL; + SGDBG( ("SOCKET", "dtor -> try destroy write mutex\r\n") ); + MDESTROY(descP->writeMtx); descP->writeMtx = NULL; + SGDBG( ("SOCKET", "dtor -> try destroy accept mutex\r\n") ); MDESTROY(descP->accMtx); descP->accMtx = NULL; @@ -20194,32 +20644,38 @@ void esock_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) ((is_direct_call) ? "called" : "scheduled"), descP->sock, fd) ); /* +++ Lock it down +++ */ - - MLOCK(descP->writeMtx); + + /* If we are called with a direct call; we already have closeMtx + */ + if (!is_direct_call) MLOCK(descP->closeMtx); MLOCK(descP->readMtx); MLOCK(descP->accMtx); + MLOCK(descP->writeMtx); MLOCK(descP->cfgMtx); - if (!is_direct_call) MLOCK(descP->closeMtx); SSDBG( descP, ("SOCKET", "esock_stop -> " "[%d, %T] all mutex(s) locked when counters:" "\r\n writePkgCnt: %u" + "\r\n writePkgMax: %u" "\r\n writeByteCnt: %u" "\r\n writeTries: %u" "\r\n writeWaits: %u" "\r\n writeFails: %u" "\r\n readPkgCnt: %u" + "\r\n readPkgMax: %u" "\r\n readByteCnt: %u" "\r\n readTries: %u" "\r\n readWaits: %u" "\r\n", descP->sock, descP->ctrlPid, descP->writePkgCnt, + descP->writePkgMax, descP->writeByteCnt, descP->writeTries, descP->writeWaits, descP->writeFails, descP->readPkgCnt, + descP->readPkgMax, descP->readByteCnt, descP->readTries, descP->readWaits) ); @@ -20347,15 +20803,22 @@ void esock_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) } } } - + + if (descP->meta.env != NULL) { + esock_free_env("esock_stop - meta-env", descP->meta.env); + descP->meta.env = NULL; + } SSDBG( descP, ("SOCKET", "esock_stop -> unlock all mutex(s)\r\n") ); - if (!is_direct_call) MUNLOCK(descP->closeMtx); MUNLOCK(descP->cfgMtx); + MUNLOCK(descP->writeMtx); MUNLOCK(descP->accMtx); MUNLOCK(descP->readMtx); - MUNLOCK(descP->writeMtx); + if (!is_direct_call) MUNLOCK(descP->closeMtx); + + /* And finally update the registry */ + esock_send_reg_del_msg(env, sockRef); SSDBG( descP, ("SOCKET", "esock_stop -> done (%d, %d)\r\n", descP->sock, fd) ); @@ -20382,7 +20845,8 @@ void esock_stop_handle_current(ErlNifEnv* env, DEMONP("esock_stop_handle_current", env, descP, &reqP->mon); - if (COMPARE_PIDS(&descP->closerPid, &reqP->pid) != 0) { + if ((! enif_is_pid_undefined(&descP->closerPid)) && + (COMPARE_PIDS(&descP->closerPid, &reqP->pid) != 0)) { SSDBG( descP, ("SOCKET", "esock_stop_handle_current -> " "send abort message to current %s %T\r\n", @@ -20391,9 +20855,10 @@ void esock_stop_handle_current(ErlNifEnv* env, if (esock_send_abort_msg(env, sockRef, reqP->ref, reqP->env, atom_closed, &reqP->pid) != NULL) { - esock_warning_msg("Failed sending abort (%T) message to " + esock_warning_msg("esock_stop_handle_current: " + "Failed sending abort (closed) message to " "current %s %T\r\n", - reqP->ref, role, reqP->pid); + role, reqP->pid); } reqP->env = NULL; } @@ -20434,8 +20899,9 @@ void inform_waiting_procs(ErlNifEnv* env, SSDBG( descP, ("SOCKET", - "inform_waiting_procs -> abort request %T (from %T)\r\n", - currentP->data.ref, currentP->data.pid) ); + "inform_waiting_procs -> " + "send abort message to waiting %s %T\r\n", + role, currentP->data.pid) ); if (esock_send_abort_msg(env, sockRef, @@ -20444,9 +20910,10 @@ void inform_waiting_procs(ErlNifEnv* env, reason, ¤tP->data.pid) != NULL) { - esock_warning_msg("Failed sending abort (%T) message to " + esock_warning_msg("inform_waiting_procs: " + "Failed sending abort (%T) message to " "current %s %T\r\n", - currentP->data.ref, + reason, role, currentP->data.pid); @@ -20500,6 +20967,8 @@ void esock_down(ErlNifEnv* env, * we leave it to the stop callback function. */ + MLOCK(descP->closeMtx); + SSDBG( descP, ("SOCKET", "esock_down -> controlling process exit\r\n") ); @@ -20592,6 +21061,8 @@ void esock_down(ErlNifEnv* env, MON2T(env, mon)); } + MUNLOCK(descP->closeMtx); + } else if (COMPARE_PIDS(&descP->connPid, pid) == 0) { /* The connPid is only set during the connection. @@ -20615,19 +21086,19 @@ void esock_down(ErlNifEnv* env, sockRef = enif_make_resource(env, descP); + MLOCK(descP->readMtx); MLOCK(descP->accMtx); + MLOCK(descP->writeMtx); + + if (descP->currentReaderP != NULL) + esock_down_reader(env, descP, sockRef, pid); if (descP->currentAcceptorP != NULL) esock_down_acceptor(env, descP, sockRef, pid); - MUNLOCK(descP->accMtx); - - MLOCK(descP->writeMtx); if (descP->currentWriterP != NULL) esock_down_writer(env, descP, sockRef, pid); - MUNLOCK(descP->writeMtx); - MLOCK(descP->readMtx); - if (descP->currentReaderP != NULL) - esock_down_reader(env, descP, sockRef, pid); + MUNLOCK(descP->writeMtx); + MUNLOCK(descP->accMtx); MUNLOCK(descP->readMtx); } @@ -20663,12 +21134,8 @@ void esock_down_acceptor(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "esock_down_acceptor -> no more writers\r\n") ); - descP->state = ESOCK_STATE_LISTENING; - - descP->currentAcceptorP = NULL; - descP->currentAcceptor.ref = esock_atom_undefined; - enif_set_pid_undefined(&descP->currentAcceptor.pid); - esock_monitor_init(&descP->currentAcceptor.mon); + descP->state = ESOCK_STATE_LISTENING; + descP->currentAcceptorP = NULL; } } else { @@ -20704,12 +21171,11 @@ void esock_down_writer(ErlNifEnv* env, "current writer - try activate next\r\n") ); if (!activate_next_writer(env, descP, sockRef)) { + SSDBG( descP, ("SOCKET", "esock_down_writer -> no active writer\r\n") ); + descP->currentWriterP = NULL; - descP->currentWriter.ref = esock_atom_undefined; - enif_set_pid_undefined(&descP->currentWriter.pid); - esock_monitor_init(&descP->currentWriter.mon); } } else { @@ -20745,13 +21211,12 @@ void esock_down_reader(ErlNifEnv* env, "current reader - try activate next\r\n") ); if (!activate_next_reader(env, descP, sockRef)) { + SSDBG( descP, ("SOCKET", "esock_down_reader -> no more readers\r\n") ); + descP->currentReaderP = NULL; - descP->currentReader.ref = esock_atom_undefined; - enif_set_pid_undefined(&descP->currentReader.pid); - esock_monitor_init(&descP->currentReader.mon); } } else { @@ -20860,8 +21325,11 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) data.dbg = extract_debug(env, load_info); data.iow = extract_iow(env, load_info); + esock_extract_pid_from_map(env, load_info, + MKA(env, "registry"), &data.regPid); + /* +++ Global Counters +++ */ - data.cntMtx = MCREATE("esock[gcnt]"); + data.cntMtx = MCREATE("esock.gcnt"); data.numSockets = 0; data.numTypeDGrams = 0; data.numTypeStreams = 0; diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index b64e055c3e..a30c2e36ae 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -39,6 +39,13 @@ #include "config.h" #endif +#if defined(HAVE_SCTP_H) +#include <netinet/sctp.h> +#ifndef HAVE_SCTP +# define HAVE_SCTP +#endif +#endif + #ifdef HAVE_SOCKLEN_T # define SOCKLEN_T socklen_t #else @@ -99,7 +106,10 @@ static char* make_sockaddr_ll(ErlNifEnv* env, ERL_NIF_TERM addr, ERL_NIF_TERM* sa); #endif - +static BOOLEAN_T esock_extract_from_map(ErlNifEnv* env, + ERL_NIF_TERM map, + ERL_NIF_TERM key, + ERL_NIF_TERM* val); /* +++ esock_encode_iov +++ * @@ -1082,50 +1092,43 @@ char* esock_decode_timeval(ErlNifEnv* env, if (!GET_MAP_VAL(env, eTime, esock_atom_usec, &eUSec)) return ESOCK_STR_EINVAL; - /* On some platforms (e.g. OpenBSD) this is a 'long long' and on others - * (e.g. Linux) its a long. - * As long as they are both 64 bits, its easy (use our own signed 64-bit int - * and then cast). But if they are either not 64 bit, or they are of different size - * then we make it easy on ourselves and use long and then cast to whatever - * type sec is. + /* Use the appropriate variable type and nif function + * to decode the value from Erlang into the struct timeval fields */ -#if (SIZEOF_LONG_LONG == SIZEOF_LONG) && (SIZEOF_LONG == 8) - { + { /* time_t tv_sec; */ +#if (SIZEOF_TIME_T == 8) ErlNifSInt64 sec; if (!GET_INT64(env, eSec, &sec)) return ESOCK_STR_EINVAL; - timeP->tv_sec = (typeof(timeP->tv_sec)) sec; - } -#else - { +#elif (SIZEOF_TIME_T == SIZEOF_INT) + int sec; + if (!GET_INT(env, eSec, &sec)) + return ESOCK_STR_EINVAL; +#else /* long or other e.g undefined */ long sec; if (!GET_LONG(env, eSec, &sec)) return ESOCK_STR_EINVAL; - timeP->tv_sec = (typeof(timeP->tv_sec)) sec; - } #endif + timeP->tv_sec = sec; + } - #if (SIZEOF_INT == 4) - { + { /* suseconds_t tv_usec; */ +#if (SIZEOF_SUSECONDS_T == 8) + ErlNifSInt64 usec; + if (!GET_INT64(env, eSec, &usec)) + return ESOCK_STR_EINVAL; +#elif (SIZEOF_SUSECONDS_T == SIZEOF_INT) int usec; - if (!GET_INT(env, eUSec, &usec)) + if (!GET_INT(env, eSec, &usec)) return ESOCK_STR_EINVAL; - timeP->tv_usec = (typeof(timeP->tv_usec)) usec; - } -#elif (SIZEOF_LONG == 4) - { +#else /* long or other e.g undefined */ long usec; - if (!GET_LONG(env, eUSec, &usec)) + if (!GET_LONG(env, eSec, &usec)) return ESOCK_STR_EINVAL; - timeP->tv_usec = (typeof(timeP->tv_usec)) usec; +#endif + timeP->tv_usec = usec; } -#else - /* Ok, we give up... */ - if (!GET_LONG(env, eUSec, &timeP->tv_usec)) - return ESOCK_STR_EINVAL; -#endif - return NULL; } @@ -1635,7 +1638,7 @@ BOOLEAN_T esock_decode_string(ErlNifEnv* env, /* *** esock_extract_bool_from_map *** * * Extract an boolean item from a map. - * + * This function returns the retreived or the provided default value. */ extern BOOLEAN_T esock_extract_bool_from_map(ErlNifEnv* env, @@ -1645,7 +1648,7 @@ BOOLEAN_T esock_extract_bool_from_map(ErlNifEnv* env, { ERL_NIF_TERM val; - if (!GET_MAP_VAL(env, map, key, &val)) + if (!esock_extract_from_map(env, map, key, &val)) return def; if (!IS_ATOM(env, val)) @@ -1659,6 +1662,59 @@ BOOLEAN_T esock_extract_bool_from_map(ErlNifEnv* env, +/* *** esock_extract_pid_from_map *** + * + * Extract a pid item from a map. + * Returns TRUE on success and FALSE on failure (and then + * the pid value will be set to 'undefined'). + * + */ +extern +BOOLEAN_T esock_extract_pid_from_map(ErlNifEnv* env, + ERL_NIF_TERM map, + ERL_NIF_TERM key, + ErlNifPid* pid) +{ + BOOLEAN_T res; + ERL_NIF_TERM val; + + if (!esock_extract_from_map(env, map, key, &val)) { + enif_set_pid_undefined(pid); + res = FALSE; + } else { + if (enif_get_local_pid(env, val, pid)) { + res = TRUE; + } else { + enif_set_pid_undefined(pid); + res = FALSE; + } + } + + return res; +} + + + +/* *** esock_extract_from_map *** + * + * Extract a value from a map. + * Returns true on success and false on failure. + * + */ +static +BOOLEAN_T esock_extract_from_map(ErlNifEnv* env, + ERL_NIF_TERM map, + ERL_NIF_TERM key, + ERL_NIF_TERM* val) +{ + if (!GET_MAP_VAL(env, map, key, val)) + return FALSE; + else + return TRUE; +} + + + /* *** esock_decode_bool *** * * Decode a boolean value. diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index 7600b5e521..398fb40a5a 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -200,6 +200,11 @@ BOOLEAN_T esock_extract_bool_from_map(ErlNifEnv* env, ERL_NIF_TERM key, BOOLEAN_T def); extern +BOOLEAN_T esock_extract_pid_from_map(ErlNifEnv* env, + ERL_NIF_TERM map, + ERL_NIF_TERM key, + ErlNifPid* pid); +extern BOOLEAN_T esock_decode_bool(ERL_NIF_TERM val); extern ERL_NIF_TERM esock_encode_bool(BOOLEAN_T val); diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index 1b125056f5..941d64f5cd 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -2346,7 +2346,7 @@ uint32_t epoll_events(int kp_fd, int fd) char s[256]; FILE *f; unsigned int pos, flags, mnt_id; - int line = 0; + int hdr_lines, line = 1; sprintf(fname,"/proc/%d/fdinfo/%d",getpid(), kp_fd); f = fopen(fname,"r"); if (!f) { @@ -2354,13 +2354,14 @@ uint32_t epoll_events(int kp_fd, int fd) ASSERT(0); return 0; } - if (fscanf(f,"pos:\t%x\nflags:\t%x", &pos, &flags) != 2) { + hdr_lines = fscanf(f,"pos:\t%x\nflags:\t%x\nmnt_id:\t%x\n", + &pos, &flags, &mnt_id); + if (hdr_lines < 2) { fprintf(stderr,"failed to parse file %s, errno = %d\n", fname, errno); ASSERT(0); return 0; } - if (fscanf(f,"\nmnt_id:\t%x\n", &mnt_id)); - line += 3; + line += hdr_lines; while (fgets(s, sizeof(s) / sizeof(*s), f)) { /* tfd: 10 events: 40000019 data: 180000000a */ int ev_fd; @@ -2414,7 +2415,7 @@ ERTS_POLL_EXPORT(erts_poll_get_selected_events)(ErtsPollSet *ps, char s[256]; FILE *f; unsigned int pos, flags, mnt_id; - int line = 0; + int hdr_lines, line = 1; sprintf(fname,"/proc/%d/fdinfo/%d",getpid(), ps->kp_fd); for (fd = 0; fd < len; fd++) ev[fd] = ERTS_POLL_EV_NONE; @@ -2423,14 +2424,15 @@ ERTS_POLL_EXPORT(erts_poll_get_selected_events)(ErtsPollSet *ps, fprintf(stderr,"failed to open file %s, errno = %d\n", fname, errno); return; } - if (fscanf(f,"pos:\t%x\nflags:\t%x", &pos, &flags) != 2) { + hdr_lines = fscanf(f,"pos:\t%x\nflags:\t%x\nmnt_id:\t%x\n", + &pos, &flags, &mnt_id); + if (hdr_lines < 2) { fprintf(stderr,"failed to parse file %s, errno = %d\n", fname, errno); ASSERT(0); fclose(f); return; } - if (fscanf(f,"\nmnt_id:\t%x\n", &mnt_id)); - line += 3; + line += hdr_lines; while (fgets(s, sizeof(s) / sizeof(*s), f)) { /* tfd: 10 events: 40000019 data: 180000000a */ int fd; diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 4823e549ea..6475a70fbf 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -75,6 +75,7 @@ #include "erl_check_io.h" #include "erl_cpu_topology.h" #include "erl_osenv.h" +#include "erl_dyn_lock_check.h" extern int driver_interrupt(int, int); extern void do_break(void); @@ -276,7 +277,9 @@ erts_sys_pre_init(void) #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_init(); #endif - +#ifdef ERTS_DYN_LOCK_CHECK + erts_dlc_init(); +#endif erts_init_sys_time_sup(); diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index 92020c6f35..6883519dc3 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -87,7 +87,7 @@ static Eterm forker_port; /* Used by the fd driver iff the fd could not be set to non-blocking */ typedef struct ErtsSysBlocking_ { ErlDrvPDL pdl; - int res; + ErlDrvSSizeT res; int err; unsigned int pkey; } ErtsSysBlocking; @@ -112,6 +112,9 @@ typedef struct driver_data { int status; int terminating; ErtsSysBlocking *blocking; + int busy; + ErlDrvSizeT high_watermark; + ErlDrvSizeT low_watermark; } ErtsSysDriverData; #define DIR_SEPARATOR_CHAR '/' @@ -169,7 +172,7 @@ typedef struct driver_data { void erl_sys_late_init(void) { - SysDriverOpts opts; + SysDriverOpts opts = {0}; Port *port; sys_signal(SIGPIPE, SIG_IGN); /* Ignore - we'll handle the write failure */ @@ -371,7 +374,8 @@ create_driver_data(ErlDrvPort port_num, int read_write, int exit_status, int pid, - int is_blocking) + int is_blocking, + SysDriverOpts* opts) { Port *prt; ErtsSysDriverData *driver_data; @@ -430,6 +434,10 @@ create_driver_data(ErlDrvPort port_num, driver_data->ofd = NULL; } + driver_data->busy = 0; + driver_data->high_watermark = opts->high_watermark; + driver_data->low_watermark = opts->low_watermark; + return driver_data; } @@ -719,7 +727,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, dd = create_driver_data(port_num, ifd[0], ofd[1], opts->packet_bytes, DO_WRITE | DO_READ, opts->exit_status, - 0, 0); + 0, 0, opts); { /* send ofd[0] + ifd[1] + stderrfd to forker port */ @@ -999,7 +1007,7 @@ static ErlDrvData fd_start(ErlDrvPort port_num, char* name, return (ErlDrvData)create_driver_data(port_num, opts->ifd, opts->ofd, opts->packet_bytes, opts->read_write, 0, -1, - !non_blocking); + !non_blocking, opts); } static void clear_fd_data(ErtsSysFdData *fdd) @@ -1075,7 +1083,8 @@ static ErlDrvData vanilla_start(ErlDrvPort port_num, char* name, res = (ErlDrvData)(long)create_driver_data(port_num, fd, fd, opts->packet_bytes, - opts->read_write, 0, -1, 0); + opts->read_write, 0, -1, 0, + opts); return res; } @@ -1108,10 +1117,10 @@ static void outputv(ErlDrvData e, ErlIOVec* ev) int pb = dd->packet_bytes; int ofd = dd->ofd ? dd->ofd->fd : -1; ssize_t n; - ErlDrvSizeT sz; char lb[4]; char* lbp; ErlDrvSizeT len = ev->size; + ErlDrvSizeT qsz; /* (len > ((unsigned long)-1 >> (4-pb)*8)) */ /* if (pb >= 0 && (len & (((ErlDrvSizeT)1 << (pb*8))) - 1) != len) {*/ @@ -1130,14 +1139,20 @@ static void outputv(ErlDrvData e, ErlIOVec* ev) if (dd->blocking) driver_pdl_lock(dd->blocking->pdl); - if ((sz = driver_sizeq(ix)) > 0) { - driver_enqv(ix, ev, 0); - + qsz = driver_sizeq(ix); + if (qsz) { + if (qsz == (ErlDrvSizeT) -1) { + if (dd->blocking) + driver_pdl_unlock(dd->blocking->pdl); + driver_failure_posix(ix, EINVAL); + return; + } + driver_enqv(ix, ev, 0); + qsz += ev->size; + if (!dd->busy && qsz >= dd->high_watermark) + set_busy_port(ix, (dd->busy = !0)); if (dd->blocking) driver_pdl_unlock(dd->blocking->pdl); - - if (sz + ev->size >= (1 << 13)) - set_busy_port(ix, 1); } else if (!dd->blocking) { /* We try to write directly if the fd in non-blocking */ @@ -1154,11 +1169,17 @@ static void outputv(ErlDrvData e, ErlIOVec* ev) n = 0; } driver_enqv(ix, ev, n); /* n is the skip value */ + qsz = ev->size - n; + if (!dd->busy && qsz >= dd->high_watermark) + set_busy_port(ix, (dd->busy = !0)); driver_select(ix, ofd, ERL_DRV_WRITE|ERL_DRV_USE, 1); } else { if (ev->size != 0) { driver_enqv(ix, ev, 0); + qsz = ev->size; + if (!dd->busy && qsz >= dd->high_watermark) + set_busy_port(ix, (dd->busy = !0)); driver_pdl_unlock(dd->blocking->pdl); driver_async(ix, &dd->blocking->pkey, fd_async, dd, NULL); @@ -1166,6 +1187,7 @@ static void outputv(ErlDrvData e, ErlIOVec* ev) driver_pdl_unlock(dd->blocking->pdl); } } + /* return 0;*/ } @@ -1177,7 +1199,7 @@ static void output(ErlDrvData e, char* buf, ErlDrvSizeT len) int pb = dd->packet_bytes; int ofd = dd->ofd ? dd->ofd->fd : -1; ssize_t n; - ErlDrvSizeT sz; + ErlDrvSizeT qsz; char lb[4]; char* lbp; struct iovec iv[2]; @@ -1192,11 +1214,15 @@ static void output(ErlDrvData e, char* buf, ErlDrvSizeT len) put_int32(len, lb); lbp = lb + (4-pb); - if ((sz = driver_sizeq(ix)) > 0) { + qsz = driver_sizeq(ix); + if (qsz) { + if (qsz == (ErlDrvSizeT) -1) { + driver_failure_posix(ix, EINVAL); + return; + } driver_enq(ix, lbp, pb); driver_enq(ix, buf, len); - if (sz + len + pb >= (1 << 13)) - set_busy_port(ix, 1); + qsz += len + pb; } else { iv[0].iov_base = lbp; @@ -1213,6 +1239,7 @@ static void output(ErlDrvData e, char* buf, ErlDrvSizeT len) } n = 0; } + qsz = pb + len - n; if (n < pb) { driver_enq(ix, lbp+n, pb-n); driver_enq(ix, buf, len); @@ -1223,6 +1250,10 @@ static void output(ErlDrvData e, char* buf, ErlDrvSizeT len) } driver_select(ix, ofd, ERL_DRV_WRITE|ERL_DRV_USE, 1); } + + if (!dd->busy && qsz >= dd->high_watermark) + set_busy_port(ix, (dd->busy = !0)); + return; /* 0; */ } @@ -1479,6 +1510,8 @@ static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd) int vsize; if ((iv = (struct iovec*) driver_peekq(ix, &vsize)) == NULL) { + if (dd->busy) + set_busy_port(ix, (dd->busy = 0)); driver_select(ix, ready_fd, ERL_DRV_WRITE, 0); if (dd->pid > 0 && dd->ofd->fd < 0) { /* The port was opened with 'in' option, which means we @@ -1494,8 +1527,13 @@ static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd) } vsize = vsize > MAX_VSIZE ? MAX_VSIZE : vsize; if ((n = writev(ready_fd, iv, vsize)) > 0) { - if (driver_deq(ix, n) == 0) - set_busy_port(ix, 0); + ErlDrvSizeT qsz = driver_deq(ix, n); + if (qsz == (ErlDrvSizeT) -1) { + driver_failure_posix(ix, EINVAL); + return; + } + if (dd->busy && qsz < dd->low_watermark) + set_busy_port(ix, (dd->busy = 0)); } else if (n < 0) { if (errno == ERRNO_BLOCK || errno == EINTR) @@ -1519,7 +1557,7 @@ static void stop_select(ErlDrvEvent fd, void* _) static void fd_async(void *async_data) { - int res; + ErlDrvSSizeT res; ErtsSysDriverData *dd = (ErtsSysDriverData *)async_data; SysIOVec *iov0; SysIOVec *iov; @@ -1544,7 +1582,6 @@ fd_async(void *async_data) } while (res < 0 && errno == EINTR); if (res < 0) err = errno; - err = errno; erts_free(ERTS_ALC_T_SYS_WRITE_BUF, iov); } @@ -1560,10 +1597,18 @@ void fd_ready_async(ErlDrvData drv_data, ASSERT(dd->blocking); if (dd->blocking->res > 0) { + ErlDrvSizeT qsz; driver_pdl_lock(dd->blocking->pdl); - if (driver_deq(port_num, dd->blocking->res) == 0) { + qsz = driver_deq(port_num, dd->blocking->res); + if (qsz == (ErlDrvSizeT) -1) { driver_pdl_unlock(dd->blocking->pdl); - set_busy_port(port_num, 0); + driver_failure_posix(port_num, EINVAL); + return; + } + if (dd->busy && qsz < dd->low_watermark) + set_busy_port(port_num, (dd->busy = 0)); + driver_pdl_unlock(dd->blocking->pdl); + if (qsz == 0) { if (dd->terminating) { /* The port is has been ordered to terminate from either fd_flush or port_inp_failure */ @@ -1576,14 +1621,12 @@ void fd_ready_async(ErlDrvData drv_data, return; /* -1; */ } } else { - driver_pdl_unlock(dd->blocking->pdl); /* still data left to write in queue */ driver_async(port_num, &dd->blocking->pkey, fd_async, dd, NULL); return /* 0; */; } } else if (dd->blocking->res < 0) { if (dd->blocking->err == ERRNO_BLOCK) { - set_busy_port(port_num, 1); /* still data left to write in queue */ driver_async(port_num, &dd->blocking->pkey, fd_async, dd, NULL); } else diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index b95aadc9b2..53411a4cc2 100644 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -507,6 +507,9 @@ struct driver_data { AsyncIo out; /* Control block for overlapped writing. */ int report_exit; /* Do report exit status for the port */ erts_atomic32_t refc; /* References to this struct */ + ErlDrvSizeT high_watermark; /* Q size when to go to busy port state */ + ErlDrvSizeT low_watermark; /* Q size when to leave busy port state */ + int busy; }; /* Driver interfaces */ @@ -740,13 +743,8 @@ release_driver_data(DriverData* dp) } ASSERT(dp->inBufSize == 0); - if (dp->outbuf != NULL) { - ASSERT(erts_atomic_read_nob(&sys_misc_mem_sz) >= dp->outBufSize); - erts_atomic_add_nob(&sys_misc_mem_sz, -1*dp->outBufSize); - DRV_BUF_FREE(dp->outbuf); - dp->outBufSize = 0; - dp->outbuf = NULL; - } + /* outbuf is released when queue is released */ + ASSERT(!dp->outbuf); ASSERT(dp->outBufSize == 0); if (dp->port_pid != INVALID_HANDLE_VALUE) { @@ -866,13 +864,18 @@ threaded_handle_closer(LPVOID param) */ static ErlDrvData -set_driver_data(DriverData* dp, HANDLE ifd, HANDLE ofd, int read_write, int report_exit) +set_driver_data(DriverData* dp, HANDLE ifd, HANDLE ofd, int read_write, int report_exit, + SysDriverOpts* opts) { int result; dp->in.fd = ifd; dp->out.fd = ofd; dp->report_exit = report_exit; + dp->high_watermark = opts->high_watermark; + dp->low_watermark = opts->low_watermark; + + dp->busy = 0; if (read_write & DO_READ) { result = driver_select(dp->port_num, (ErlDrvEvent)dp->in.ov.hEvent, @@ -1322,7 +1325,7 @@ spawn_start(ErlDrvPort port_num, char* utf8_name, SysDriverOpts* opts) } #endif retval = set_driver_data(dp, hFromChild, hToChild, opts->read_write, - opts->exit_status); + opts->exit_status, opts); if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO) { /* We assume that this cannot generate a negative number */ erl_drv_set_os_pid(port_num, pid); @@ -2200,7 +2203,8 @@ fd_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) } else if (in == 2 && out == 2) { save_22_port = dp; } - return set_driver_data(dp, (HANDLE) opts->ifd, (HANDLE) opts->ofd, opts->read_write, 0); + return set_driver_data(dp, (HANDLE) opts->ifd, (HANDLE) opts->ofd, opts->read_write, + 0, opts); } } @@ -2268,7 +2272,8 @@ vanilla_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) } if (ofd == INVALID_HANDLE_VALUE) return ERL_DRV_ERROR_GENERAL; - return set_driver_data(dp, ifd, ofd, opts->read_write,0); + return set_driver_data(dp, ifd, ofd, opts->read_write, + 0, opts); } static void @@ -2433,6 +2438,8 @@ output(ErlDrvData drv_data, char* buf, ErlDrvSizeT len) DriverData* dp = (DriverData *) drv_data; int pb; /* The header size for this port. */ char* current; + ErlDrvSizeT qsz, sz; + ErlDrvBinary *bin; pb = dp->packet_bytes; @@ -2452,24 +2459,19 @@ output(ErlDrvData drv_data, char* buf, ErlDrvSizeT len) * Allocate memory for both the message and the header. */ - ASSERT(dp->outbuf == NULL); - ASSERT(dp->outBufSize == 0); - - ASSERT(!dp->outbuf); - dp->outbuf = DRV_BUF_ALLOC(pb+len); - if (!dp->outbuf) { - driver_failure_posix(dp->port_num, ENOMEM); - return ; /* -1; */ + sz = pb+len; + bin = driver_alloc_binary(sz); + if (!bin) { + driver_failure_posix(dp->port_num, ENOMEM); + return ; /* -1; */ } - dp->outBufSize = pb+len; - erts_atomic_add_nob(&sys_misc_mem_sz, dp->outBufSize); - /* * Store header bytes (if any). */ - current = dp->outbuf; + current = bin->orig_bytes; + switch (pb) { case 4: *current++ = (len >> 24) & 255; @@ -2486,18 +2488,34 @@ output(ErlDrvData drv_data, char* buf, ErlDrvSizeT len) if (len) memcpy(current, buf, len); - - if (!async_write_file(&dp->out, dp->outbuf, pb+len)) { - set_busy_port(dp->port_num, 1); - } else { - dp->out.ov.Offset += pb+len; /* For vanilla driver. */ - /* XXX OffsetHigh should be changed too. */ - ASSERT(erts_atomic_read_nob(&sys_misc_mem_sz) >= dp->outBufSize); - erts_atomic_add_nob(&sys_misc_mem_sz, -1*dp->outBufSize); - DRV_BUF_FREE(dp->outbuf); - dp->outBufSize = 0; - dp->outbuf = NULL; + + qsz = driver_sizeq(dp->port_num); + + if (qsz > 0) { + driver_enq_bin(dp->port_num, bin, 0, sz); + qsz += pb+len; } + else { + ASSERT(!dp->outbuf); + dp->outbuf = bin->orig_bytes; + dp->outBufSize = sz; + if (!async_write_file(&dp->out, dp->outbuf, sz)) { + driver_enq_bin(dp->port_num, bin, 0, sz); + qsz = sz; + } else { + dp->out.ov.Offset += pb+len; /* For vanilla driver. */ + /* XXX OffsetHigh should be changed too. */ + dp->outBufSize = 0; + dp->outbuf = NULL; + } + } + + if (!dp->busy && qsz >= dp->high_watermark) + set_busy_port(dp->port_num, (dp->busy = !0)); + + /* Binary either handled or buffered */ + driver_free_binary(bin); + /*return 0;*/ } @@ -2693,20 +2711,19 @@ ready_output(ErlDrvData drv_data, ErlDrvEvent ready_event) DWORD bytesWritten; DriverData *dp = (DriverData *) drv_data; int error; + ErlDrvSizeT qsz; if(dp->out.thread == (HANDLE) -1) { dp->out.async_io_active = 0; } DEBUGF(("ready_output(%p, 0x%x)\n", drv_data, ready_event)); - set_busy_port(dp->port_num, 0); - if (!(dp->outbuf)) { + if (!dp->outbuf) { /* Happens because event sometimes get signalled during a successful write... */ return; } - ASSERT(erts_atomic_read_nob(&sys_misc_mem_sz) >= dp->outBufSize); - erts_atomic_add_nob(&sys_misc_mem_sz, -1*dp->outBufSize); - DRV_BUF_FREE(dp->outbuf); + + qsz = driver_deq(dp->port_num, dp->outBufSize); dp->outBufSize = 0; dp->outbuf = NULL; #ifdef HARD_POLL_DEBUG @@ -2717,15 +2734,32 @@ ready_output(ErlDrvData drv_data, ErlDrvEvent ready_event) poll_debug_write_done(dp->out.ov.hEvent,bytesWritten); #endif - if (error == NO_ERROR) { - dp->out.ov.Offset += bytesWritten; /* For vanilla driver. */ - return ; /* 0; */ + if (error != NO_ERROR) { + (void) driver_select(dp->port_num, ready_event, ERL_DRV_WRITE, 0); + _dosmaperr(error); + driver_failure_posix(dp->port_num, errno); + return; } - - (void) driver_select(dp->port_num, ready_event, ERL_DRV_WRITE, 0); - _dosmaperr(error); - driver_failure_posix(dp->port_num, errno); - /* return 0; */ + + dp->out.ov.Offset += bytesWritten; /* For vanilla driver. */ + + while (qsz > 0) { + int vsize; + SysIOVec *iov = driver_peekq(dp->port_num, &vsize); + ASSERT(iov->iov_base && iov->iov_len); + dp->outbuf = iov->iov_base; + dp->outBufSize = iov->iov_len; + if (!async_write_file(&dp->out, dp->outbuf, dp->outBufSize)) + break; + dp->out.ov.Offset += dp->outBufSize; /* For vanilla driver. */ + /* XXX OffsetHigh should be changed too. */ + qsz = driver_deq(dp->port_num, dp->outBufSize); + dp->outbuf = NULL; + dp->outBufSize = 0; + } + + if (dp->busy && qsz < dp->low_watermark) + set_busy_port(dp->port_num, (dp->busy = 0)); } static void stop_select(ErlDrvEvent e, void* _) diff --git a/erts/emulator/test/dirty_nif_SUITE.erl b/erts/emulator/test/dirty_nif_SUITE.erl index 6f5ec161a3..8e5e2ded6e 100644 --- a/erts/emulator/test/dirty_nif_SUITE.erl +++ b/erts/emulator/test/dirty_nif_SUITE.erl @@ -226,18 +226,21 @@ dirty_call_while_terminated(Config) when is_list(Config) -> element(2, process_info(self(), binary))), - receive after 2000 -> ok end, receive Msg -> ct:fail({unexpected_message, Msg}) after - 0 -> + 1000 -> ok end, - {value, {BinAddr, 4711, 1}} = lists:keysearch(4711, 2, - element(2, - process_info(self(), - binary))), + ok = wait_until(fun() -> + {value, {BinAddr, 4711, 1}} == + lists:keysearch(4711, 2, + element(2, + process_info(self(), + binary))) + end, + 10000), process_flag(trap_exit, OT), try blipp:blupp(Bin) @@ -714,6 +717,39 @@ nif_whereis_proxy(Ref) -> ok end. +wait_until(Fun, infinity) -> + wait_until_aux(Fun, infinity); +wait_until(Fun, MaxTime) -> + End = erlang:monotonic_time(millisecond) + MaxTime, + wait_until_aux(Fun, End). + +wait_until_aux(Fun, End) -> + case Fun() of + true -> + ok; + _ -> + if End == infinity -> + receive after 100 -> ok end, + wait_until_aux(Fun, infinity); + true -> + Now = erlang:monotonic_time(millisecond), + case End =< Now of + true -> + timeout; + _ -> + Wait = case End - Now of + Short when End - Now < 100 -> + Short; + _ -> + 100 + end, + receive after Wait -> ok end, + wait_until_aux(Fun, End) + end + end + end. + + %% The NIFs: lib_loaded() -> false. call_dirty_nif(_,_,_) -> ?nif_stub. diff --git a/erts/emulator/test/esock_misc/esock_iow_client.erl b/erts/emulator/test/esock_misc/esock_iow_client.erl new file mode 100644 index 0000000000..3e48a8f300 --- /dev/null +++ b/erts/emulator/test/esock_misc/esock_iow_client.erl @@ -0,0 +1,94 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2020-2020. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% --------------------------------------------------------------------- +%% +%% Writes small messages to a socket, until counters wrap! +%% +%% --------------------------------------------------------------------- + +-module(esock_iow_client). + +-export([start/1]). + +-define(LIB, esock_iow_lib). +-define(LIMIT, 100000). + +-define(MSG, <<"abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz">>). + + +%% --------------------------------------------------------------------- + +start(ServerPort) -> + Domain = inet, + ?LIB:iprint("open"), + {ok, S} = socket:open(Domain, stream, tcp), + ?LIB:iprint("iow"), + ok = socket:setopt(S, otp, iow, true), + ?LIB:iprint("bind"), + LocalSA = #{family => Domain, + addr => {147,214,93,147}}, + {ok, _} = socket:bind(S, LocalSA), + ?LIB:iprint("connect (to ~w)", [ServerPort]), + ServerSA = LocalSA#{port => ServerPort}, + ok = socket:connect(S, ServerSA), + ?LIB:iprint("connected - now await begin"), + case socket:recv(S, 0, infinity) of + {ok, <<"begin">>} -> + ?LIB:iprint("begin"), + loop(S, 0); + {error, Reason} -> + ?LIB:eprint("failed receive begin: ~p", [Reason]) + end. + +loop(S, N) -> + ok = socket:send(S, ?MSG), + NextN = receive + {'$socket', S, counter_wrap, Cnt} -> + ?LIB:iprint("Counter ~w wrapped", [Cnt]), + N + 1; + Any -> + ?LIB:iprint("Received: ~n ~p~n", [Any]), + N + 1 + after 1 -> + if (N < ?LIMIT) -> + N+1; + true -> + ?LIB:iprint("Counters:" + "~s", [?LIB:format_counters(cnts(S))]), + 0 + end + end, + loop(S, NextN). + + +cnts(S) -> + #{counters := Cnts} = socket:info(S), + Cnts. + diff --git a/erts/emulator/test/esock_misc/esock_iow_lib.erl b/erts/emulator/test/esock_misc/esock_iow_lib.erl new file mode 100644 index 0000000000..6fc1365dca --- /dev/null +++ b/erts/emulator/test/esock_misc/esock_iow_lib.erl @@ -0,0 +1,123 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2020-2020. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(esock_iow_lib). + +-export([ + format_counters/1, format_counters/2, format_counters/3, + iprint/1, iprint/2, + eprint/1, eprint/2, + f/2, + fts/0, fts/1 + ]). + + +%% --------------------------------------------------------------------- + +format_counters(Counters) -> + format_counters(traffic, Counters). + +format_counters(Type, Counters) when (Type =:= listen) orelse (Type =:= traffic) -> + format_counters(" ", Type, Counters). + +format_counters(Prefix, traffic, Counters) -> + ReadByte = proplists:get_value(read_byte, Counters, -1), + ReadFails = proplists:get_value(read_fails, Counters, -1), + ReadPkg = proplists:get_value(read_pkg, Counters, -1), + ReadPkgMax = proplists:get_value(read_pkg_max, Counters, -1), + ReadTries = proplists:get_value(read_tries, Counters, -1), + ReadWaits = proplists:get_value(read_waits, Counters, -1), + WriteByte = proplists:get_value(write_byte, Counters, -1), + WriteFails = proplists:get_value(write_fails, Counters, -1), + WritePkg = proplists:get_value(write_pkg, Counters, -1), + WritePkgMax = proplists:get_value(write_pkg_max, Counters, -1), + WriteTries = proplists:get_value(write_tries, Counters, -1), + WriteWaits = proplists:get_value(write_waits, Counters, -1), + f("~n~sNumber Of Read Bytes: ~p" + "~n~sNumber Of Read Fails: ~p" + "~n~sNumber Of Read Packages: ~p" + "~n~sNumber Of Read Tries: ~p" + "~n~sNumber Of Read Waits: ~p" + "~n~sMax Read Package Size: ~p" + "~n~sNumber Of Write Bytes: ~p" + "~n~sNumber Of Write Fails: ~p" + "~n~sNumber Of Write Packages: ~p" + "~n~sNumber Of Write Tries: ~p" + "~n~sNumber Of Write Waits: ~p" + "~n~sMax Write Package Size: ~p", + [Prefix, ReadByte, + Prefix, ReadFails, + Prefix, ReadPkg, + Prefix, ReadTries, + Prefix, ReadWaits, + Prefix, ReadPkgMax, + Prefix, WriteByte, + Prefix, WriteFails, + Prefix, WritePkg, + Prefix, WriteTries, + Prefix, WriteWaits, + Prefix, WritePkgMax]); + +format_counters(Prefix, listen, Counters) -> + AccSuccess = proplists:get_value(acc_success, Counters, -1), + AccFails = proplists:get_value(acc_fails, Counters, -1), + AccTries = proplists:get_value(acc_tries, Counters, -1), + AccWaits = proplists:get_value(acc_waits, Counters, -1), + f("~n~sNumber Of Successful Accepts: ~p" + "~n~sNumber Of Failed Accepts: ~p" + "~n~sNumber Of Accept Attempts: ~p" + "~n~sNumber Of Accept Waits: ~p", + [Prefix, AccSuccess, + Prefix, AccFails, + Prefix, AccTries, + Prefix, AccWaits]). + +%% --------------------------------------------------------------------- + +iprint(F) -> + iprint(F, []). + +iprint(F, A) -> + print("INFO", F, A). + +eprint(F) -> + iprint(F, []). + +eprint(F, A) -> + print("ERROR", F, A). + +print(Pre, F, A) -> + io:format("*** ~s *** ~s ~n" ++ F ++ "~n~n", [Pre, fts() | A]). + + +f(F, A) -> + lists:flatten(io_lib:format(F, A)). + + +ts() -> + os:timestamp(). + +fts() -> + fts(ts()). + +fts({_N1, _N2, N3} = TS) -> + {_Date, Time} = calendar:now_to_local_time(TS), + {Hour,Min,Sec} = Time, + f("~.2.0w:~.2.0w:~.2.0w.4~w", [Hour, Min, Sec, round(N3/1000)]). diff --git a/erts/emulator/test/esock_misc/esock_iow_server.erl b/erts/emulator/test/esock_misc/esock_iow_server.erl new file mode 100644 index 0000000000..4b364a6ca6 --- /dev/null +++ b/erts/emulator/test/esock_misc/esock_iow_server.erl @@ -0,0 +1,83 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2020-2020. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% --------------------------------------------------------------------- +%% +%% Reads small messages to a socket, until counters wrap! +%% +%% --------------------------------------------------------------------- + +-module(esock_iow_server). + +-export([start/0]). + +-define(LIB, esock_iow_lib). +-define(LIMIT, 100000). + + +%% --------------------------------------------------------------------- + +start() -> + Domain = inet, + ?LIB:iprint("open"), + {ok, LSock} = socket:open(Domain, stream, tcp), + ?LIB:iprint("iow"), + ok = socket:setopt(LSock, otp, iow, true), + ?LIB:iprint("bind"), + LocalSA = #{family => Domain, + addr => {147,214,93,147}}, + {ok, LPort} = socket:bind(LSock, LocalSA), + ?LIB:iprint("listen port: ~p", [LPort]), + ?LIB:iprint("make listen socket"), + ok = socket:listen(LSock), + ?LIB:iprint("accept"), + {ok, CSock} = socket:accept(LSock), + ok = socket:send(CSock, <<"begin">>), + loop(CSock, 0). + +loop(S, N) -> + case socket:recv(S) of + {ok, _} when (N < ?LIMIT) -> + flush(S), + loop(S, N+1); + {ok, _} -> + ?LIB:iprint("Counters:" + "~s", [?LIB:format_counters(cnts(S))]), + flush(S), + loop(S, 0); + {error, Reason} -> + ?LIB:eprint("failed receive: ~p", [Reason]) + end. + + +cnts(S) -> + #{counters := Cnts} = socket:info(S), + Cnts. + +flush(S) -> + receive + {'$socket', S, counter_wrap, Cnt} -> + ?LIB:iprint("Counter ~w wrapped", [Cnt]); + Any -> + ?LIB:iprint("Received: ~n ~p~n", [Any]) + after 0 -> + ok + end. + diff --git a/erts/emulator/test/list_bif_SUITE.erl b/erts/emulator/test/list_bif_SUITE.erl index f95251943d..4ddcd0f60b 100644 --- a/erts/emulator/test/list_bif_SUITE.erl +++ b/erts/emulator/test/list_bif_SUITE.erl @@ -23,6 +23,7 @@ -export([all/0, suite/0]). -export([hd_test/1,tl_test/1,t_length/1,t_list_to_pid/1, + t_list_to_ref/1, t_list_to_ext_pidportref/1, t_list_to_port/1,t_list_to_float/1,t_list_to_integer/1]). @@ -33,6 +34,7 @@ suite() -> all() -> [hd_test, tl_test, t_length, t_list_to_pid, t_list_to_port, + t_list_to_ref, t_list_to_ext_pidportref, t_list_to_float, t_list_to_integer]. %% Tests list_to_integer and string:to_integer @@ -126,6 +128,61 @@ t_list_to_port(Config) when is_list(Config) -> end, ok. +t_list_to_ref(Config) when is_list(Config) -> + Ref = make_ref(), + RefStr = ref_to_list(Ref), + Ref = list_to_ref(RefStr), + case catch list_to_ref(id("Incorrect list")) of + {'EXIT', {badarg, _}} -> + ok; + Res -> + ct:fail("list_to_ref/1 with incorrect arg succeeded.~n" + "Result: ~p", [Res]) + end, + ok. + +%% Test list_to_pid/port/ref for external pids/ports/refs. +t_list_to_ext_pidportref(Config) when is_list(Config) -> + {ok, Node} = slave:start(net_adm:localhost(), t_list_to_ext_pidportref), + Pid = rpc:call(Node, erlang, self, []), + Port = hd(rpc:call(Node, erlang, ports, [])), + Ref = rpc:call(Node, erlang, make_ref, []), + + PidStr = pid_to_list(Pid), + PortStr = port_to_list(Port), + RefStr = ref_to_list(Ref), + + Pid2 = list_to_pid(PidStr), + Port2 = list_to_port(PortStr), + Ref2 = list_to_ref(RefStr), + + %% The local roundtrips of externals does not work + %% as 'creation' is missing in the string formats and we don't know + %% the 'creation' of the connected node. + false = (Pid =:= Pid2), + false = (Port =:= Port2), + false = (Ref =:= Ref2), + + %% Local roundtrip kind of "works" for '==' since OTP-22.0 (bf7c722bd3b) + %% Operator '==' treats 0-creations as wildcards + %% which breaks term transitivity (A==B and B==C => B==C). + true = (Pid == Pid2), + true = (Port == Port2), + true = (Ref == Ref2), + + %% It works when sent back to node with matching name, as 0-creations + %% will be converted to the local node creation. + true = rpc:call(Node, erlang, '=:=', [Pid, Pid2]), + true = rpc:call(Node, erlang, '==', [Pid, Pid2]), + true = rpc:call(Node, erlang, '=:=', [Port, Port2]), + true = rpc:call(Node, erlang, '==', [Port, Port2]), + true = rpc:call(Node, erlang, '=:=', [Ref, Ref2]), + true = rpc:call(Node, erlang, '==', [Ref, Ref2]), + + slave:stop(Node), + ok. + + %% Test list_to_float/1 with correct and incorrect arguments. t_list_to_float(Config) when is_list(Config) -> diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index b824daea67..6a8f7607cd 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -38,6 +38,7 @@ monitor_process_b/1, monitor_process_c/1, monitor_process_d/1, + monitor_process_purge/1, demonitor_process/1, monitor_frenzy/1, hipe/1, @@ -67,6 +68,7 @@ nif_whereis_threaded/1, nif_whereis_proxy/1, nif_ioq/1, pid/1, + id/1, nif_term_type/1 ]). @@ -76,6 +78,9 @@ -define(is_resource, is_reference). +-define(RT_CREATE,1). +-define(RT_TAKEOVER,2). + suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> @@ -125,6 +130,7 @@ groups() -> monitor_process_b, monitor_process_c, monitor_process_d, + monitor_process_purge, demonitor_process]}]. api_groups() -> [api_latest, api_2_4, api_2_0]. @@ -823,6 +829,57 @@ monitor_process_d(Config) -> ok. +%% OTP-16399: Test fire resource monitor after the NIF module been purged. +monitor_process_purge(Config) -> + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "nif_mod"), + {ok,nif_mod,NifModBin} = compile:file(File, [binary,return_errors]), + + monitor_process_purge_do(Config, NifModBin, resource_dtor_A), + erlang:garbage_collect(), + receive after 10 -> ok end, + [{{resource_dtor_A_v1,_},1,4,104}, + {unload,1,5,105}] = nif_mod_call_history(), + + %% This used to crash VM as only resources with destructor + %% prevented NIF lib from being unloaded. + monitor_process_purge_do(Config, NifModBin, null), + erlang:garbage_collect(), + receive after 10 -> ok end, + [{unload,1,4,104}] = nif_mod_call_history(), + ok. + +monitor_process_purge_do(Config, NifModBin, Dtor) -> + io:format("Test with destructor = ~p\n", [Dtor]), + + {module,nif_mod} = erlang:load_module(nif_mod,NifModBin), + + ok = nif_mod:load_nif_lib(Config, 1, [{resource_type, 0, ?RT_CREATE, + "monitor_process_purge", Dtor, + ?RT_CREATE, resource_down_D} + ]), + hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()), + [{load,1,1,101}, + {get_priv_data_ptr,1,2,102}] = nif_mod_call_history(), + + {Pid,MRef} = spawn_opt(fun() -> + receive + return -> ok + end + end, + [link, monitor]), + RBin = <<"blahblah">>, + R = nif_mod:make_new_resource(0, RBin), + 0 = nif_mod:monitor_process(0, R, Pid), + true = erlang:delete_module(nif_mod), + true = erlang:purge_module(nif_mod), + Pid ! return, + [{'DOWN', MRef, process, Pid, normal}] = flush(), + [{{resource_down_D_v1,RBin},1,3,103}] = nif_mod_call_history(), + keep_alive(R), + ok. + + %% Test basic demonitoring demonitor_process(Config) -> ensure_lib_loaded(Config), @@ -1414,10 +1471,6 @@ resource_binary_do() -> ResInfo = get_resource(binary_resource_type,ResBin2), ResInfo. - --define(RT_CREATE,1). --define(RT_TAKEOVER,2). - %% Test resource takeover by module upgrade resource_takeover(Config) when is_list(Config) -> TmpMem = tmpmem(), @@ -3458,6 +3511,7 @@ last_resource_dtor_call() -> last_resource_dtor_call_nif(). id(I) -> I. +keep_alive(Term) -> ?MODULE:id(Term). %% The NIFs: lib_version() -> undefined. diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.c b/erts/emulator/test/nif_SUITE_data/nif_mod.c index 885b8ebaf8..d8c1ca2a2e 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.c +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.c @@ -23,6 +23,10 @@ #include "nif_mod.h" +#if ERL_NIF_MAJOR_VERSION*100 + ERL_NIF_MINOR_VERSION >= 215 +# define HAVE_ENIF_MONITOR_PROCESS +#endif + #define CHECK(X) ((void)((X) || (check_abort(__LINE__),1))) #ifdef __GNUC__ static void check_abort(unsigned line) __attribute__((noreturn)); @@ -42,6 +46,7 @@ static ERL_NIF_TERM am_null; static ERL_NIF_TERM am_resource_type; static ERL_NIF_TERM am_resource_dtor_A; static ERL_NIF_TERM am_resource_dtor_B; +static ERL_NIF_TERM am_resource_down_D; static ERL_NIF_TERM am_return; static NifModPrivData* priv_data(ErlNifEnv* env) @@ -56,6 +61,7 @@ static void init(ErlNifEnv* env) am_resource_type = enif_make_atom(env, "resource_type"); am_resource_dtor_A = enif_make_atom(env, "resource_dtor_A"); am_resource_dtor_B = enif_make_atom(env, "resource_dtor_B"); + am_resource_down_D = enif_make_atom(env, "resource_down_D"); am_return = enif_make_atom(env, "return"); } @@ -107,8 +113,26 @@ static void resource_dtor_B(ErlNifEnv* env, void* a) enif_sizeof_resource(a)); } -/* {resource_type, Ix|null, ErlNifResourceFlags in, "TypeName", dtor(A|B|null), ErlNifResourceFlags out}*/ -static void open_resource_type(ErlNifEnv* env, const ERL_NIF_TERM* arr) +#ifdef HAVE_ENIF_MONITOR_PROCESS +static void resource_down_D(ErlNifEnv* env, void* a, ErlNifPid* pid, ErlNifMonitor* mon) +{ + const char down_name[] = "resource_down_D_v" STRINGIFY(NIF_LIB_VER); + + add_call_with_arg(env, priv_data(env), down_name, (const char*)a, + enif_sizeof_resource(a)); +} +#endif + + +/* {resource_type, + Ix|null, + ErlNifResourceFlags in, + "TypeName", + dtor(A|B|null), + ErlNifResourceFlags out + [, down(D|null)]} +*/ +static void open_resource_type(ErlNifEnv* env, int arity, const ERL_NIF_TERM* arr) { NifModPrivData* data = priv_data(env); char rt_name[30]; @@ -132,10 +156,27 @@ static void open_resource_type(ErlNifEnv* env, const ERL_NIF_TERM* arr) CHECK(enif_is_identical(arr[4], am_resource_dtor_B)); dtor = resource_dtor_B; } - - got_ptr = enif_open_resource_type(env, NULL, rt_name, dtor, - flags.e, &got_res.e); - +#ifdef HAVE_ENIF_MONITOR_PROCESS + if (arity == 7) { + ErlNifResourceTypeInit init; + init.dtor = dtor; + init.stop = NULL; + if (enif_is_identical(arr[6], am_null)) { + init.down = NULL; + } + else { + CHECK(enif_is_identical(arr[6], am_resource_down_D)); + init.down = resource_down_D; + } + got_ptr = enif_open_resource_type_x(env, rt_name, &init, + flags.e, &got_res.e); + } + else +#endif + { + got_ptr = enif_open_resource_type(env, NULL, rt_name, dtor, + flags.e, &got_res.e); + } if (enif_get_uint(env, arr[1], &ix) && ix < RT_MAX && got_ptr != NULL) { data->rt_arr[ix] = got_ptr; } @@ -163,7 +204,8 @@ static void do_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info, int* retvalp) CHECK(enif_get_tuple(env, head, &arity, &arr)); switch (arity) { case 6: - open_resource_type(env, arr); + case 7: + open_resource_type(env, arity, arr); break; case 2: CHECK(arr[0] == am_return); @@ -290,6 +332,25 @@ static ERL_NIF_TERM get_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar return enif_make_binary(env, &obin); } +#ifdef HAVE_ENIF_MONITOR_PROCESS +static ERL_NIF_TERM monitor_process(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + NifModPrivData* data = priv_data(env); + ErlNifPid pid; + unsigned ix; + void* obj; + int ret; + + if (!enif_get_uint(env, argv[0], &ix) || ix >= RT_MAX + || !enif_get_resource(env, argv[1], data->rt_arr[ix], &obj) + || !enif_get_local_pid(env, argv[2], &pid)) { + return enif_make_badarg(env); + } + ret = enif_monitor_process(env, obj, &pid, NULL); + return enif_make_int(env, ret); +} +#endif + static ErlNifFunc nif_funcs[] = { {"lib_version", 0, lib_version}, @@ -297,6 +358,9 @@ static ErlNifFunc nif_funcs[] = {"get_priv_data_ptr", 0, get_priv_data_ptr}, {"make_new_resource", 2, make_new_resource}, {"get_resource", 2, get_resource} +#ifdef HAVE_ENIF_MONITOR_PROCESS + ,{"monitor_process", 3, monitor_process} +#endif }; #if NIF_LIB_VER != 3 diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.erl b/erts/emulator/test/nif_SUITE_data/nif_mod.erl index 8019cfcf82..f3b9dcfaf3 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.erl +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.erl @@ -23,7 +23,8 @@ -include_lib("common_test/include/ct.hrl"). -export([load_nif_lib/2, load_nif_lib/3, start/0, lib_version/0, - get_priv_data_ptr/0, make_new_resource/2, get_resource/2]). + get_priv_data_ptr/0, make_new_resource/2, get_resource/2, + monitor_process/3]). -export([loop/0, upgrade/1]). @@ -89,6 +90,7 @@ nif_api_version() -> %NIF get_priv_data_ptr() -> ?nif_stub. make_new_resource(_,_) -> ?nif_stub. get_resource(_,_) -> ?nif_stub. +monitor_process(_,_,_) -> ?nif_stub. nif_stub_error(Line) -> exit({nif_not_loaded,module,?MODULE,line,Line}). diff --git a/erts/emulator/test/port_bif_SUITE.erl b/erts/emulator/test/port_bif_SUITE.erl index e1e1ec9fb9..333c5bce04 100644 --- a/erts/emulator/test/port_bif_SUITE.erl +++ b/erts/emulator/test/port_bif_SUITE.erl @@ -26,7 +26,7 @@ command_e_1/1, command_e_2/1, command_e_3/1, command_e_4/1, port_info1/1, port_info2/1, port_info_os_pid/1, port_info_race/1, - connect/1, control/1, echo_to_busy/1]). + connect/1, control/1, echo_to_busy/1, busy_options/1]). -export([do_command_e_1/1, do_command_e_2/1, do_command_e_4/1]). @@ -38,7 +38,7 @@ suite() -> all() -> [command, {group, port_info}, connect, control, - echo_to_busy]. + echo_to_busy, busy_options]. groups() -> [{command_e, [], @@ -475,3 +475,171 @@ sub_bin(Bin) when is_binary(Bin) -> B. id(I) -> I. + +busy_options(Config) when is_list(Config) -> + SleepTime = 2000, + SleepTimeX = SleepTime + 100, + MinVal = 1, + MaxVal = (1 bsl (8*erlang:system_info(wordsize))) - 2, + DataDir = proplists:get_value(data_dir, Config), + Sleep = filename:join(DataDir, "sleeper") ++ " " ++ integer_to_list(SleepTime), + Data = "hej hopp! hej hopp! hej hopp! hej hopp! hej hopp! hej hopp! hej hopp! hej hopp! hej hopp! hej hopp!", + + process_flag(trap_exit, true), + Tester = self(), + HejLoop = fun (Prt, _F, 1000) -> + Prt; + (Prt, F, N) -> + Prt ! {Tester, {command, Data}}, + F(Prt, F, N+1) + end, + + io:format("Test1...~n", []), + Start1 = erlang:monotonic_time(millisecond), + Prt1 = open_port({spawn, Sleep}, + [{busy_limits_port, {MinVal, MinVal}}, + {busy_limits_msgq, {MinVal, MinVal}}]), + T1 = spawn_link(fun () -> + HejLoop(Prt1, HejLoop, 0) + end), + true = wait_until(fun () -> + {status, suspended} == process_info(T1, status) + end, + SleepTimeX), + unlink(T1), + exit(T1, kill), + io:format("Test1 done: ~p ms~n", [erlang:monotonic_time(millisecond)-Start1]), + + io:format("Test2...~n", []), + Start2 = erlang:monotonic_time(millisecond), + Prt2 = open_port({spawn, Sleep}, + [{busy_limits_port, {50, 100}}, + {busy_limits_msgq, {50, 100}}]), + T2 = spawn_link(fun () -> + HejLoop(Prt2, HejLoop, 0) + end), + true = wait_until(fun () -> + {status, suspended} == process_info(T2, status) + end, + SleepTimeX), + unlink(T2), + exit(T2, kill), + io:format("Test2 done: ~p ms~n", [erlang:monotonic_time(millisecond)-Start2]), + + io:format("Test3...~n", []), + Start3 = erlang:monotonic_time(millisecond), + + Prt3 = open_port({spawn, Sleep}, + [{busy_limits_port, {MaxVal,MaxVal}}, + {busy_limits_msgq, {MaxVal,MaxVal}}]), + T3 = spawn_link(fun () -> + HejLoop(Prt3, HejLoop, 0) + end), + false = wait_until(fun () -> + {status, suspended} == process_info(T3, status) + end, + SleepTimeX), + unlink(T3), + exit(T3, kill), + io:format("Test3 done: ~p ms~n", [erlang:monotonic_time(millisecond)-Start3]), + + io:format("Test4...~n", []), + Start4 = erlang:monotonic_time(millisecond), + + Prt4 = open_port({spawn, Sleep}, + [{busy_limits_port, disabled}, + {busy_limits_msgq, disabled}]), + T4 = spawn_link(fun () -> + HejLoop(Prt4, HejLoop, 0) + end), + false = wait_until(fun () -> + {status, suspended} == process_info(T4, status) + end, + SleepTimeX), + unlink(T4), + exit(T4, kill), + io:format("Test4 done: ~p ms~n", [erlang:monotonic_time(millisecond)-Start4]), + + try + open_port({spawn, Sleep}, + [{busy_limits_port, {MinVal-1,MinVal-1}}]) + catch + error:badarg -> ok + end, + + try + open_port({spawn, Sleep}, + [{busy_limits_msgq, {MinVal-1,MinVal-1}}]) + catch + error:badarg -> ok + end, + + try + open_port({spawn, Sleep}, + [{busy_limits_port, {MaxVal+1,MaxVal+1}}]) + catch + error:badarg -> ok + end, + + try + open_port({spawn, Sleep}, + [{busy_limits_msgq, {MaxVal+1,MaxVal+1}}]) + catch + error:badarg -> ok + end, + + load_control_drv(Config), + + CtrlPort = open_port({spawn, "control_drv"}, + [{busy_limits_msgq, {50,100}}]), + unlink(CtrlPort), + exit(CtrlPort, kill), + + try + open_port({spawn, "control_drv"}, + [{busy_limits_port, {50,100}}]) + catch + error:badarg -> ok + end, + + receive {'EXIT', Prt1, _} -> ok end, + receive {'EXIT', Prt2, _} -> ok end, + receive {'EXIT', Prt3, _} -> ok end, + receive {'EXIT', Prt4, _} -> ok end, + + ok. + +wait_until(Fun, infinity) -> + wait_until_aux(Fun, infinity); +wait_until(Fun, MaxTime) -> + End = erlang:monotonic_time(millisecond) + MaxTime, + wait_until_aux(Fun, End). + +wait_until_aux(Fun, End) -> + case catch Fun() of + true -> + true; + _ -> + if End == infinity -> + receive after 100 -> ok end, + wait_until_aux(Fun, infinity); + true -> + Now = erlang:monotonic_time(millisecond), + case End =< Now of + true -> + false; + _ -> + Wait = case End - Now of + Short when End - Now < 100 -> + Short; + _ -> + 100 + end, + receive after Wait -> ok end, + wait_until_aux(Fun, End) + end + end + end. + + + diff --git a/erts/emulator/test/port_bif_SUITE_data/Makefile.src b/erts/emulator/test/port_bif_SUITE_data/Makefile.src index 1a2d348ecb..85ecf14eac 100644 --- a/erts/emulator/test/port_bif_SUITE_data/Makefile.src +++ b/erts/emulator/test/port_bif_SUITE_data/Makefile.src @@ -3,7 +3,7 @@ LD = @LD@ CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@ CROSSLDFLAGS = @CROSSLDFLAGS@ -all: control_drv@dll@ port_test@exe@ +all: control_drv@dll@ port_test@exe@ sleeper@exe@ port_test@exe@: port_test@obj@ $(LD) $(CROSSLDFLAGS) -o port_test port_test@obj@ @LIBS@ @@ -11,4 +11,10 @@ port_test@exe@: port_test@obj@ port_test@obj@: port_test.c $(CC) -c -o port_test@obj@ $(CFLAGS) port_test.c +sleeper@exe@: sleeper@obj@ + $(LD) $(CROSSLDFLAGS) -o sleeper sleeper@obj@ @LIBS@ + +sleeper@obj@: sleeper.c + $(CC) -c -o sleeper@obj@ $(CFLAGS) sleeper.c + @SHLIB_RULES@ diff --git a/erts/emulator/test/port_bif_SUITE_data/sleeper.c b/erts/emulator/test/port_bif_SUITE_data/sleeper.c new file mode 100644 index 0000000000..bd662f51a4 --- /dev/null +++ b/erts/emulator/test/port_bif_SUITE_data/sleeper.c @@ -0,0 +1,66 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2020. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#ifndef __WIN32__ +# include <unistd.h> +# include <sys/time.h> +#else +# include "windows.h" +# include "winbase.h" +#endif +#include <sys/types.h> + +int +main(int argc, char *argv[]) +{ + long int ms; + char *endp; + + if (argc != 2) { + fprintf(stderr, "Invalid argument count: %d\n", argc); + exit(1); + } + + errno = 0; + ms = strtol(argv[1], &endp, 10); + if (errno || argv[1] == endp || *endp != '\0' || ms < 0) { + if (errno == 0) + errno = EINVAL; + perror("Invalid timeout value"); + exit(1); + } + +#ifdef __WIN32__ + Sleep(ms); +#else + { + struct timeval t; + t.tv_sec = ms/1000; + t.tv_usec = (ms % 1000) * 1000; + + select(0, NULL, NULL, NULL, &t); + } +#endif + + return 0; +} diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 13dde12e69..0e1c15e160 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -62,7 +62,8 @@ system_task_on_suspended/1, system_task_failed_enqueue/1, gc_request_when_gc_disabled/1, - gc_request_blast_when_gc_disabled/1]). + gc_request_blast_when_gc_disabled/1, + otp_16436/1]). -export([prio_server/2, prio_client/2, init/1, handle_event/2]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -110,7 +111,8 @@ groups() -> {system_task, [], [no_priority_inversion, no_priority_inversion2, system_task_blast, system_task_on_suspended, system_task_failed_enqueue, - gc_request_when_gc_disabled, gc_request_blast_when_gc_disabled]}]. + gc_request_when_gc_disabled, gc_request_blast_when_gc_disabled, + otp_16436]}]. init_per_suite(Config) -> A0 = case application:start(sasl) of @@ -2868,6 +2870,15 @@ gc_request_blast_when_gc_disabled(Config) when is_list(Config) -> receive {'DOWN', M, process, P, _Reason} -> ok end, ok. +otp_16436(Config) when is_list(Config) -> + P = spawn_opt(fun () -> + erts_debug:dirty_io(wait, 1000) + end, + [{priority,high},link]), + erlang:check_process_code(P, non_existing), + unlink(P), + exit(P, kill), + ok. %% Internal functions diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index f61949c75b..59cf66d277 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -58,7 +58,8 @@ scheduler_suspend/1, dirty_scheduler_threads/1, poll_threads/1, - reader_groups/1]). + reader_groups/1, + otp_16446/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -74,7 +75,8 @@ all() -> scheduler_suspend_basic, scheduler_suspend, dirty_scheduler_threads, poll_threads, - reader_groups]. + reader_groups, + otp_16446]. groups() -> [{scheduler_bind, [], @@ -1794,6 +1796,77 @@ reader_groups_map(CPUT, Groups) -> erlang:system_flag(cpu_topology, Old), lists:sort(Res). +otp_16446(Config) when is_list(Config) -> + ct:timetrap({minutes, 1}), + + process_flag(priority, high), + + DIO = erlang:system_info(dirty_io_schedulers), + NoPrioProcs = 10*DIO, + io:format("DIO = ~p~nNoPrioProcs = ~p~n", [DIO, NoPrioProcs]), + + DirtyLoop = fun Loop(P, N) -> + erts_debug:dirty_io(wait,1), + receive {get, From} -> From ! {P, N} + after 0 -> Loop(P,N+1) + end + end, + + Spawn = fun SpawnLoop(_Prio, 0, Acc) -> + Acc; + SpawnLoop(Prio, N, Acc) -> + Pid = spawn_opt(fun () -> DirtyLoop(Prio, 0) end, + [link, {priority, Prio}]), + SpawnLoop(Prio, N-1, [Pid|Acc]) + end, + + Ns = Spawn(normal, NoPrioProcs, []), + Ls = Spawn(low, NoPrioProcs, []), + + receive after 10000 -> ok end, + + RequestInfo = fun (P) -> P ! {get, self()} end, + lists:foreach(RequestInfo, Ns), + lists:foreach(RequestInfo, Ls), + + Collect = fun CollectFun(0, LLs, NLs) -> + {LLs, NLs}; + CollectFun(N, LLs, NLs) -> + receive + {low, Calls} -> + CollectFun(N-1, LLs+Calls, NLs); + {normal, Calls} -> + CollectFun(N-1, LLs, NLs+Calls) + end + end, + + {LLs, NLs} = Collect(2*NoPrioProcs, 0, 0), + + %% expected ratio 0.125, but this is not especially exact... + Ratio = LLs / NLs, + + io:format("LLs = ~p~nNLs = ~p~nRatio = ~p~n", [LLs, NLs, Ratio]), + + true = Ratio > 0.05, + true = Ratio < 0.5, + + WaitUntilDead = fun (P) -> + case is_process_alive(P) of + false -> + ok; + true -> + unlink(P), + exit(P, kill), + false = is_process_alive(P) + end + end, + + lists:foreach(WaitUntilDead, Ns), + lists:foreach(WaitUntilDead, Ls), + Comment = "low/normal ratio: " ++ erlang:float_to_list(Ratio,[{decimals,4}]), + erlang:display(Comment), + {comment, Comment}. + %% %% Utils %% diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl index 4e6baa9e0e..0bfe7539b4 100644 --- a/erts/emulator/test/signal_SUITE.erl +++ b/erts/emulator/test/signal_SUITE.erl @@ -34,7 +34,8 @@ -export([init_per_testcase/2, end_per_testcase/2]). % Test cases --export([xm_sig_order/1]). +-export([xm_sig_order/1, + kill2killed/1]). init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> [{testcase, Func}|Config]. @@ -53,7 +54,8 @@ suite() -> {timetrap, {minutes, 2}}]. all() -> - [xm_sig_order]. + [xm_sig_order, + kill2killed]. %% Test that exit signals and messages are received in correct order @@ -89,6 +91,68 @@ xm_sig_order_proc() -> end, xm_sig_order_proc(). +kill2killed(Config) when is_list(Config) -> + process_flag(trap_exit, true), + kill2killed_test(node()), + {ok, Node} = start_node(Config), + kill2killed_test(Node), + stop_node(Node), + ok. + +kill2killed_test(Node) -> + if Node == node() -> + io:format("Testing against local node", []); + true -> + io:format("Testing against remote node ~p", [Node]) + end, + check_exit(Node, other_exit2, 1), + check_exit(Node, other_exit2, 2), + check_exit(Node, other_exit2, 9), + check_exit(Node, other_exit2, 10), + check_exit(Node, exit2, 1), + check_exit(Node, exit2, 2), + check_exit(Node, exit2, 9), + check_exit(Node, exit2, 10), + check_exit(Node, exit1, 1), + check_exit(Node, exit1, 2), + check_exit(Node, exit1, 9), + check_exit(Node, exit1, 10), + ok. + +check_exit(Node, Type, N) -> + io:format("Testing ~p length ~p~n", [Type, N]), + P = spawn_link_line(Node, node(), Type, N, self()), + if Type == other_exit2 -> + receive + {end_of_line, EOL} -> + exit(EOL, kill) + end; + true -> ok + end, + receive + {'EXIT', P, Reason} -> + if Type == exit1 -> + kill = Reason; + true -> + killed = Reason + end + end. + +spawn_link_line(_NodeA, _NodeB, other_exit2, 0, Tester) -> + Tester ! {end_of_line, self()}, + receive after infinity -> ok end; +spawn_link_line(_NodeA, _NodeB, exit1, 0, _Tester) -> + exit(kill); +spawn_link_line(_NodeA, _NodeB, exit2, 0, _Tester) -> + exit(self(), kill); +spawn_link_line(NodeA, NodeB, Type, N, Tester) -> + spawn_link(NodeA, + fun () -> + spawn_link_line(NodeB, NodeA, Type, N-1, Tester), + receive after infinity -> ok end + end). + + %% %% -- Internal utils -------------------------------------------------------- %% diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl index 44a338a68f..a263ac87bc 100644 --- a/erts/emulator/test/socket_SUITE.erl +++ b/erts/emulator/test/socket_SUITE.erl @@ -130,6 +130,7 @@ %% *** API Options *** api_opt_simple_otp_options/1, + api_opt_simple_otp_meta_option/1, api_opt_simple_otp_rcvbuf_option/1, api_opt_simple_otp_controlling_process/1, api_opt_sock_acceptconn_udp/1, @@ -203,6 +204,9 @@ api_to_recvmsg_tcp4/1, api_to_recvmsg_tcp6/1, + %% Socket Registry + reg_s_single_open_and_close_and_count/1, + %% *** Socket Closure *** sc_cpe_socket_cleanup_tcp4/1, sc_cpe_socket_cleanup_tcp6/1, @@ -695,6 +699,7 @@ groups() -> {api_options_udp, [], api_options_udp_cases()}, %% {api_options_sctp, [], api_options_sctp_cases()}, {api_op_with_timeout, [], api_op_with_timeout_cases()}, + {reg, [], reg_simple_cases()}, {socket_close, [], socket_close_cases()}, {sc_ctrl_proc_exit, [], sc_cp_exit_cases()}, {sc_local_close, [], sc_lc_cases()}, @@ -856,6 +861,7 @@ api_options_cases() -> api_options_otp_cases() -> [ api_opt_simple_otp_options, + api_opt_simple_otp_meta_option, api_opt_simple_otp_rcvbuf_option, api_opt_simple_otp_controlling_process ]. @@ -999,6 +1005,13 @@ api_op_with_timeout_cases() -> api_to_recvmsg_tcp6 ]. +%% Socket Registry "simple" test cases +reg_simple_cases() -> + [ + reg_s_single_open_and_close_and_count + ]. + + %% These cases tests what happens when the socket is closed/shutdown, %% locally or remotely. socket_close_cases() -> @@ -2221,6 +2234,25 @@ analyze_and_print_freebsd_host_info(Version) -> +init_per_group(GroupName, Config) + when (GroupName =:= sc_remote_close) orelse + (GroupName =:= sc_remote_shutdown) orelse + (GroupName =:= traffic) -> + io:format("init_per_group(~w) -> entry with" + "~n Config: ~p" + "~n", [GroupName, Config]), + %% Maybe we should skip the entire suite for this platform, + %% but for now we just skip these groups, which seem to + %% have problems (slave node start). + %% As stated elsewhere, its not really Fedora 16, but + %% the *really* slow VM that is the issue. + try is_old_fedora16() of + ok -> + Config + catch + throw:{skip, _} = SKIP -> + SKIP + end; init_per_group(ttest = _GroupName, Config) -> io:format("init_per_group(~w) -> entry with" "~n Config: ~p" @@ -8825,6 +8857,181 @@ api_opt_simple_otp_options() -> i("await udp evaluator"), ok = ?SEV_AWAIT_FINISH([Tester2]). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Perform some simple getopt and setopt otp meta option +api_opt_simple_otp_meta_option(suite) -> + []; +api_opt_simple_otp_meta_option(doc) -> + []; +api_opt_simple_otp_meta_option(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_opt_simple_otp_meta_option, + fun() -> api_opt_simple_otp_meta_option() end). + +api_opt_simple_otp_meta_option() -> + Get = fun(S) -> + socket:getopt(S, otp, meta) + end, + Set = fun(S, Val) -> + socket:setopt(S, otp, meta, Val) + end, + + MainSeq = + [ + #{desc => "monitor helper", + cmd => fun(#{helper := Pid}) -> + _ = erlang:monitor(process, Pid), + ok + end}, + + #{desc => "create socket", + cmd => fun(#{domain := Domain, + type := Type, + protocol := Protocol} = State) -> + Sock = sock_open(Domain, Type, Protocol), + {ok, State#{sock => Sock}} + end}, + + #{desc => "get default", + cmd => fun(#{sock := Sock}) -> + case Get(Sock) of + {ok, undefined} -> + ok; + {ok, Invalid} -> + {error, {invalid, Invalid}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "set value", + cmd => fun(#{sock := Sock} = State) -> + Value = make_ref(), + case Set(Sock, Value) of + ok -> + {ok, State#{value => Value}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "get value", + cmd => fun(#{sock := Sock, value := Value}) -> + case Get(Sock) of + {ok, Value} -> + ok; + {ok, Invalid} -> + {error, {invalid, Invalid}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "set complex value", + cmd => fun(#{sock := Sock} = State) -> + Value = + #{a => 1, + b => {2, 3}, + c => make_ref(), + d => self(), + e => State}, + case Set(Sock, Value) of + ok -> + {ok, State#{value := Value}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "get complex value", + cmd => fun(#{sock := Sock, value := Value}) -> + case Get(Sock) of + {ok, Value} -> + ok; + {ok, Invalid} -> + {error, {invalid, Invalid}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "start helper", + cmd => fun(#{helper := Pid, sock := Sock, value := Value}) -> + ?SEV_ANNOUNCE_START(Pid, {Sock, Value}), + ok + end}, + + #{desc => "wait for helper ready", + cmd => fun(#{helper := Pid}) -> + ?SEV_AWAIT_READY(Pid, helper, test) + end}, + + #{desc => "socket close", + cmd => fun(#{sock := Sock}) -> + socket:close(Sock) + end}, + + ?SEV_FINISH_NORMAL], + + HelperSeq = + [#{desc => "await start", + cmd => fun (State) -> + {Main, {Sock, Value}} = ?SEV_AWAIT_START(), + {ok, State#{main => Main, + sock => Sock, + value => Value}} + end}, + #{desc => "monitor main", + cmd => fun(#{main := Main}) -> + _ = erlang:monitor(process, Main), + ok + end} + + #{desc => "get value", + cmd => fun(#{sock := Sock, value := Value}) -> + case Get(Sock) of + {ok, Value} -> + ok; + {ok, Invalid} -> + {error, {invalid, Invalid}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "set and fail", + cmd => fun(#{sock := Sock}) -> + Value = self(), + case Set(Sock, Value) of + ok -> + {error, only_owner_may_set}; + {error, not_owner} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "announce ready (test)", + cmd => fun(#{main := Main}) -> + ?SEV_ANNOUNCE_READY(Main, test), + ok + end}, + + ?SEV_FINISH_NORMAL], + + + i("start tcp helper evaluator"), + Helper = ?SEV_START("tcp-helper", HelperSeq, #{}), + + i("start tcp main evaluator"), + MainState = #{domain => inet, type => stream, protocol => tcp, + helper => Helper#ev.pid}, + Main = ?SEV_START("tcp-main", MainSeq, MainState), + + i("await tcp evaluators"), + ok = ?SEV_AWAIT_FINISH([Helper, Main]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -15638,6 +15845,16 @@ api_opt_sock_timestamp_tcp(InitState) -> ERROR end end}, + %% Linux pecularity observed here... + %% Detected on Kernel 4.15.0-72 x96_64. + %% The option set to enable receiving timestamps just above + %% has failed to be effective down in "await recv reply 2 + %% (from server, w timestamp)" below, unless we put the + %% sleep between setting the option and informing + %% the writer that it shall write to the other socket end. + %% A sleep 1 ms improves a lot but does not remove + %% problem completely. Believe it or not. + ?SEV_SLEEP(100), #{desc => "announce ready (timestamp on)", cmd => fun(#{tester := Tester}) -> ?SEV_ANNOUNCE_READY(Tester, timestamp_on), @@ -15658,7 +15875,7 @@ api_opt_sock_timestamp_tcp(InitState) -> ok end}, #{desc => "await recv reply 2 (from server, w timestamp)", - cmd => fun(#{sock := Sock, recv := Recv}) -> + cmd => fun(#{sock := Sock, recv := Recv, get := Get}) -> case Recv(Sock) of {ok, {[#{level := socket, type := timestamp, @@ -15668,6 +15885,8 @@ api_opt_sock_timestamp_tcp(InitState) -> "~n ~p", [TS]), ok; {ok, {BadCMsgHdrs, ?BASIC_REP}} -> + ?SEV_EPRINT("Current timestamp value:" + " ~p", [Get(Sock)]), {error, {unexpected_reply_cmsghdrs, BadCMsgHdrs}}; {ok, {[#{level := socket, @@ -15824,7 +16043,7 @@ api_opt_sock_timestamp_tcp(InitState) -> ?SEV_ANNOUNCE_CONTINUE(Server, accept), ok end}, - ?SEV_SLEEP(?SECS(1)), +%%% ?SEV_SLEEP(?SECS(1)), #{desc => "order client to continue (with connect)", cmd => fun(#{client := Client} = _State) -> ?SEV_ANNOUNCE_CONTINUE(Client, connect), @@ -15999,8 +16218,6 @@ api_opt_sock_timestamp_tcp(InitState) -> ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]). - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Tests that the add_mambership and drop_membership ip options work. @@ -22902,6 +23119,319 @@ api_to_recvmsg_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% +%% REGISTRY %% +%% %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% We create a bunch of different sockets and ensure that the registry +%% has the correct info. + +reg_s_single_open_and_close_and_count(suite) -> + []; +reg_s_single_open_and_close_and_count(doc) -> + []; +reg_s_single_open_and_close_and_count(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(reg_s_single_open_and_close_and_count, + fun() -> + ok = reg_s_single_open_and_close_and_count() + end). + + +reg_s_single_open_and_close_and_count() -> + SupportsIPV6 = + case (catch has_support_ipv6()) of + ok -> + true; + _ -> + false + end, + SupportsLOCAL = + case (catch has_support_unix_domain_socket()) of + ok -> + true; + _ -> + false + end, + SupportsSCTP = + case (catch has_support_sctp()) of + ok -> + true; + _ -> + false + end, + InitSockInfos = + [ + {inet, stream, tcp}, + {inet, dgram, udp} + ] ++ + case SupportsIPV6 of + true -> + [ + {inet6, stream, tcp}, + {inet6, dgram, udp} + ]; + false -> + [] + end ++ + case SupportsLOCAL of + true -> + [ + {local, stream, default}, + {local, dgram, default} + ]; + false -> + [] + end ++ + [ + {inet, stream, tcp}, + {inet, dgram, udp} + ] ++ + case SupportsSCTP of + true -> + [ + {inet, seqpacket, sctp}, + {inet, seqpacket, sctp} + ]; + false -> + [] + end ++ + [ + {inet, stream, tcp}, + {inet, dgram, udp} + ] ++ + case SupportsSCTP andalso SupportsIPV6 of + true -> + [ + {inet6, seqpacket, sctp}, + {inet6, seqpacket, sctp} + ]; + false -> + [] + end, + + i("open sockets"), + Socks = + [fun({Domain, Type, Proto}) -> + i("open socket: ~w, ~w, ~w", [Domain, Type, Proto]), + {ok, Sock} = socket:open(Domain, Type, Proto), + Sock + end(InitSockInfo) || InitSockInfo <- InitSockInfos], + + ?SLEEP(1000), + + + %% **** Total Number Of Sockets **** + + NumSocks1 = length(Socks), + NumberOf1 = socket:number_of(), + + i("verify (total) number of sockets(1): ~w, ~w", [NumSocks1, NumberOf1]), + case (NumSocks1 =:= NumberOf1) of + true -> + ok; + false -> + exit({wrong_number_of_sockets1, NumSocks1, NumberOf1}) + end, + + + %% **** Number Of IPv4 TCP Sockets **** + + %% inet, stream, tcp + SiNumTCP = reg_si_num(InitSockInfos, inet, stream, tcp), + SrNumTCP = reg_sr_num(inet, stream, tcp), + + i("verify number of IPv4 TCP sockets: ~w, ~w", [SiNumTCP, SrNumTCP]), + case (SiNumTCP =:= SrNumTCP) of + true -> + ok; + false -> + exit({wrong_number_of_ipv4_tcp_sockets, SiNumTCP, SrNumTCP}) + end, + + + %% **** Number Of IPv4 UDP Sockets **** + + %% inet, dgram, udp + SiNumUDP = reg_si_num(InitSockInfos, inet, dgram, udp), + SrNumUDP = reg_sr_num(inet, dgram, udp), + + i("verify number of IPv4 UDP sockets: ~w, ~w", [SiNumUDP, SrNumUDP]), + case (SiNumUDP =:= SrNumUDP) of + true -> + ok; + false -> + exit({wrong_number_of_ipv4_udp_sockets, SiNumUDP, SrNumUDP}) + end, + + + %% **** Number Of IPv4 SCTP Sockets **** + + %% inet, seqpacket, sctp + SiNumSCTP = reg_si_num(InitSockInfos, inet, seqpacket, sctp), + SrNumSCTP = reg_sr_num(inet, seqpacket, sctp), + + i("verify number of IPv4 SCTP sockets: ~w, ~w", [SiNumSCTP, SrNumSCTP]), + case (SiNumSCTP =:= SrNumSCTP) of + true -> + ok; + false -> + exit({wrong_number_of_sctp_sockets, SiNumSCTP, SrNumSCTP}) + end, + + + %% **** Number Of IPv4 Sockets **** + + %% inet + SiNumINET = reg_si_num(InitSockInfos, inet), + SrNumINET = reg_sr_num(inet), + + i("verify number of IPv4 sockets: ~w, ~w", [SiNumINET, SrNumINET]), + case (SiNumINET =:= SrNumINET) of + true -> + ok; + false -> + exit({wrong_number_of_ipv4_sockets, SiNumINET, SrNumINET}) + end, + + + %% **** Number Of IPv6 Sockets **** + + %% inet6 + SiNumINET6 = reg_si_num(InitSockInfos, inet6), + SrNumINET6 = reg_sr_num(inet6), + + i("verify number of IPv6 sockets: ~w, ~w", [SiNumINET6, SrNumINET6]), + case (SiNumINET6 =:= SrNumINET6) of + true -> + ok; + false -> + exit({wrong_number_of_ipv6_sockets, SiNumINET6, SrNumINET6}) + end, + + + %% **** Number Of Unix Domain Sockets Sockets **** + + %% local + SiNumLOCAL = reg_si_num(InitSockInfos, local), + SrNumLOCAL = reg_sr_num(local), + + i("verify number of Unix Domain Sockets sockets: ~w, ~w", + [SiNumLOCAL, SrNumLOCAL]), + case (SiNumLOCAL =:= SrNumLOCAL) of + true -> + ok; + false -> + exit({wrong_number_of_local_sockets, SiNumLOCAL, SrNumLOCAL}) + end, + + + %% **** Close *all* Sockets then verify Number Of Sockets **** + + i("close sockets"), + lists:foreach(fun(S) -> + i("close socket"), + ok = socket:close(S) + end, Socks), + + ?SLEEP(1000), + + NumSocks2 = 0, + NumberOf2 = socket:number_of(), + + i("verify number of sockets(2): ~w, ~w", [NumSocks2, NumberOf2]), + case (NumSocks2 =:= NumberOf2) of + true -> + ok; + false -> + exit({wrong_number_of_sockets2, NumSocks2, NumberOf2}) + end, + + ok. + + +reg_si_num(SocksInfo, Domain) + when ((Domain =:= inet) orelse (Domain =:= inet6) orelse (Domain =:= local)) -> + reg_si_num(SocksInfo, Domain, undefined, undefined); +reg_si_num(SocksInfo, Type) + when ((Type =:= stream) orelse (Type =:= dgram) orelse (Type =:= seqpacket)) -> + reg_si_num(SocksInfo, undefined, Type, undefined); +reg_si_num(SocksInfo, Proto) + when ((Proto =:= sctp) orelse (Proto =:= tcp) orelse (Proto =:= udp)) -> + reg_si_num(SocksInfo, undefined, undefined, Proto). + +reg_si_num(SocksInfo, Domain, undefined, undefined) -> + F = fun({D, _T, _P}) when (D =:= Domain) -> true; + (_) -> false + end, + reg_si_num2(F, SocksInfo); +reg_si_num(SocksInfo, undefined, Type, undefined) -> + F = fun({_D, T, _P}) when (T =:= Type) -> true; + (_) -> false + end, + reg_si_num2(F, SocksInfo); +reg_si_num(SocksInfo, undefined, undefined, Proto) -> + F = fun({_D, _T, P}) when (P =:= Proto) -> true; + (_) -> false + end, + reg_si_num2(F, SocksInfo); +reg_si_num(SocksInfo, Domain, Type, Proto) -> + F = fun({D, T, P}) when (D =:= Domain) andalso + (T =:= Type) andalso + (P =:= Proto) -> + true; + (_) -> + false + end, + reg_si_num2(F, SocksInfo). + +reg_si_num2(F, SocksInfo) -> + length(lists:filter(F, SocksInfo)). + + +reg_sr_num(Domain) + when ((Domain =:= inet) orelse (Domain =:= inet6)) -> + length(socket:which_sockets(Domain)); +reg_sr_num(Domain) + when (Domain =:= local) -> + reg_sr_num(Domain, undefined, undefined); +reg_sr_num(Type) + when ((Type =:= stream) orelse (Type =:= dgram) orelse (Type =:= seqpacket)) -> + length(socket:which_sockets(Type)); +reg_sr_num(Proto) + when ((Proto =:= sctp) orelse (Proto =:= tcp) orelse (Proto =:= udp)) -> + length(socket:which_sockets(Proto)). + +reg_sr_num(Domain, undefined, undefined) -> + F = fun(#{domain := D}) when (D =:= Domain) -> + true; + (_X) -> + false + end, + reg_sr_num2(F); +reg_sr_num(Domain, Type, Proto) -> + F = fun(#{domain := D, + type := T, + protocol := P}) when (D =:= Domain) andalso + (T =:= Type) andalso + (P =:= Proto) -> + true; + (_X) -> + %% i("reg_sr_num -> not counting: " + %% "~n ~p", [_X]), + false + end, + reg_sr_num2(F). + +reg_sr_num2(F) -> + length(socket:which_sockets(F)). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %% %% SOCKET CLOSURE %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -23916,7 +24446,9 @@ sc_lc_recvfrom_response_udpL(_Config) when is_list(_Config) -> tc_try(sc_lc_recvfrom_response_udpL, fun() -> has_support_unix_domain_socket() end, fun() -> - Recv = fun(Sock, To) -> socket:recvfrom(Sock, [], To) end, + Recv = fun(Sock, To) -> + socket:recvfrom(Sock, [], To) + end, InitState = #{domain => local, protocol => default, recv => Recv}, @@ -23950,8 +24482,20 @@ sc_lc_receive_response_udp(InitState) -> #{desc => "open socket", cmd => fun(#{domain := Domain, protocol := Proto} = State) -> Sock = sock_open(Domain, dgram, Proto), - SA = sock_sockname(Sock), - {ok, State#{sock => Sock, sa => SA}} + %% SA = sock_sockname(Sock), + case socket:sockname(Sock) of + {ok, SA} -> + {ok, State#{sock => Sock, sa => SA}}; + {error, eafnosupport = Reason} -> + ?SEV_IPRINT("Failed get socket name: " + "~n ~p", [Reason]), + (catch socket:close(Sock)), + {skip, Reason}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Failed get socket name: " + "~n ~p", [Reason]), + ERROR + end end}, #{desc => "bind socket", cmd => fun(#{sock := Sock, local_sa := LSA}) -> @@ -27177,6 +27721,23 @@ traffic_send_and_recv_tcp(InitState) -> cmd => fun(#{lsock := LSock}) -> socket:listen(LSock) end}, + #{desc => "initial (listen socket) counter validation (=zero)", + cmd => fun(#{lsock := LSock} = _State) -> + try socket:info(LSock) of + #{counters := Counters} -> + ?SEV_IPRINT("Validate initial listen socket counters: " + "~s", [format_counters(listen, + Counters)]), + traffic_sar_counters_validation(Counters) + catch + C:E:S -> + ?SEV_EPRINT("Failed get socket info: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + {error, {socket_info_failed, {C, E, S}}} + end + end}, #{desc => "announce ready (init)", cmd => fun(#{domain := local, tester := Tester, @@ -27197,8 +27758,22 @@ traffic_send_and_recv_tcp(InitState) -> #{desc => "accept", cmd => fun(#{lsock := LSock} = State) -> case socket:accept(LSock) of - {ok, Sock} -> - {ok, State#{csock => Sock}}; + {ok, CSock} -> + #{counters := LCnts} = socket:info(LSock), + ?SEV_IPRINT("Validate listen counters: " + "~s", [format_counters(listen, + LCnts)]), + traffic_sar_counters_validation( + LCnts, + [{acc_success, 1}, + {acc_fails, 0}, + {acc_tries, 1}, + {acc_waits, 0}]), + #{counters := CCnts} = socket:info(CSock), + ?SEV_IPRINT("Validate initial accept counters: " + "~s", [format_counters(CCnts)]), + traffic_sar_counters_validation(CCnts), + {ok, State#{csock => CSock}}; {error, _} = ERROR -> ERROR end @@ -27208,7 +27783,7 @@ traffic_send_and_recv_tcp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("Validate initial counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation(Counters) catch C:E:S -> @@ -27248,12 +27823,13 @@ traffic_send_and_recv_tcp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("validate counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation( Counters, - [{read_pkg, Pkg}, - {read_byte, Byte}, - {read_tries, any}]) + [{read_pkg, Pkg}, + {read_byte, Byte}, + {read_tries, any}, + {read_pkg_max, any}]) catch C:E:S -> ?SEV_EPRINT("Failed get socket info: " @@ -27295,15 +27871,17 @@ traffic_send_and_recv_tcp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("validate counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation( Counters, - [{read_pkg, RPkg}, - {read_byte, RByte}, - {write_pkg, SPkg}, - {write_byte, SByte}, - {read_tries, any}, - {write_tries, any}]) + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}, + {read_pkg_max, any}, + {write_pkg_max, any}]) catch C:E:S -> ?SEV_EPRINT("Failed get socket info: " @@ -27346,15 +27924,17 @@ traffic_send_and_recv_tcp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("validate counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation( Counters, - [{read_pkg, RPkg}, - {read_byte, RByte}, - {write_pkg, SPkg}, - {write_byte, SByte}, - {read_tries, any}, - {write_tries, any}]) + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}, + {read_pkg_max, any}, + {write_pkg_max, any}]) catch C:E:S -> ?SEV_EPRINT("Failed get socket info: " @@ -27398,15 +27978,17 @@ traffic_send_and_recv_tcp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("validate counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation( Counters, - [{read_pkg, RPkg}, - {read_byte, RByte}, - {write_pkg, SPkg}, - {write_byte, SByte}, - {read_tries, any}, - {write_tries, any}]) + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}, + {read_pkg_max, any}, + {write_pkg_max, any}]) catch C:E:S -> ?SEV_EPRINT("Failed get socket info: " @@ -27555,12 +28137,13 @@ traffic_send_and_recv_tcp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("validate counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation( Counters, - [{write_pkg, SPkg}, - {write_byte, SByte}, - {write_tries, any}]) + [{write_pkg, SPkg}, + {write_byte, SByte}, + {write_tries, any}, + {write_pkg_max, any}]) catch C:E:S -> ?SEV_EPRINT("Failed get socket info: " @@ -27593,23 +28176,25 @@ traffic_send_and_recv_tcp(InitState) -> end end}, #{desc => "validate (recv 1)", - cmd => fun(#{sock := Sock, - read_pkg := RPkg, - read_byte := RByte, + cmd => fun(#{sock := Sock, + read_pkg := RPkg, + read_byte := RByte, write_pkg := SPkg, write_byte := SByte} = _State) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("validate counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation( Counters, - [{read_pkg, RPkg}, - {read_byte, RByte}, - {write_pkg, SPkg}, - {write_byte, SByte}, - {read_tries, any}, - {write_tries, any}]) + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}, + {read_pkg_max, any}, + {write_pkg_max, any}]) catch C:E:S -> ?SEV_EPRINT("Failed get socket info: " @@ -27653,15 +28238,17 @@ traffic_send_and_recv_tcp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("validate counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation( Counters, - [{read_pkg, RPkg}, - {read_byte, RByte}, - {write_pkg, SPkg}, - {write_byte, SByte}, - {read_tries, any}, - {write_tries, any}]) + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}, + {read_pkg_max, any}, + {write_pkg_max, any}]) catch C:E:S -> ?SEV_EPRINT("Failed get socket info: " @@ -27704,15 +28291,17 @@ traffic_send_and_recv_tcp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("validate counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation( Counters, - [{read_pkg, RPkg}, - {read_byte, RByte}, - {write_pkg, SPkg}, - {write_byte, SByte}, - {read_tries, any}, - {write_tries, any}]) + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}, + {read_pkg_max, any}, + {write_pkg_max, any}]) catch C:E:S -> ?SEV_EPRINT("Failed get socket info: " @@ -27960,34 +28549,153 @@ traffic_send_and_recv_tcp(InitState) -> ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]). +format_counters(Counters) -> + format_counters(traffic, Counters). + +format_counters(Type, Counters) when (Type =:= listen) orelse (Type =:= traffic) -> + format_counters(" ", Type, Counters). + +format_counters(Prefix, traffic, Counters) -> + ReadByte = proplists:get_value(read_byte, Counters, -1), + ReadFails = proplists:get_value(read_fails, Counters, -1), + ReadPkg = proplists:get_value(read_pkg, Counters, -1), + ReadPkgMax = proplists:get_value(read_pkg_max, Counters, -1), + ReadTries = proplists:get_value(read_tries, Counters, -1), + ReadWaits = proplists:get_value(read_waits, Counters, -1), + WriteByte = proplists:get_value(write_byte, Counters, -1), + WriteFails = proplists:get_value(write_fails, Counters, -1), + WritePkg = proplists:get_value(write_pkg, Counters, -1), + WritePkgMax = proplists:get_value(write_pkg_max, Counters, -1), + WriteTries = proplists:get_value(write_tries, Counters, -1), + WriteWaits = proplists:get_value(write_waits, Counters, -1), + f("~n~sNumber Of Read Bytes: ~p" + "~n~sNumber Of Read Fails: ~p" + "~n~sNumber Of Read Packages: ~p" + "~n~sNumber Of Read Tries: ~p" + "~n~sNumber Of Read Waits: ~p" + "~n~sMax Read Package Size: ~p" + "~n~sNumber Of Write Bytes: ~p" + "~n~sNumber Of Write Fails: ~p" + "~n~sNumber Of Write Packages: ~p" + "~n~sNumber Of Write Tries: ~p" + "~n~sNumber Of Write Waits: ~p" + "~n~sMax Write Package Size: ~p", + [Prefix, ReadByte, + Prefix, ReadFails, + Prefix, ReadPkg, + Prefix, ReadTries, + Prefix, ReadWaits, + Prefix, ReadPkgMax, + Prefix, WriteByte, + Prefix, WriteFails, + Prefix, WritePkg, + Prefix, WriteTries, + Prefix, WriteWaits, + Prefix, WritePkgMax]); + +format_counters(Prefix, listen, Counters) -> + AccSuccess = proplists:get_value(acc_success, Counters, -1), + AccFails = proplists:get_value(acc_fails, Counters, -1), + AccTries = proplists:get_value(acc_tries, Counters, -1), + AccWaits = proplists:get_value(acc_waits, Counters, -1), + f("~n~sNumber Of Successful Accepts: ~p" + "~n~sNumber Of Failed Accepts: ~p" + "~n~sNumber Of Accept Attempts: ~p" + "~n~sNumber Of Accept Waits: ~p", + [Prefix, AccSuccess, + Prefix, AccFails, + Prefix, AccTries, + Prefix, AccWaits]). + +all_counters() -> + [ + read_byte, + read_fails, + read_pkg, + read_pkg_max, + read_tries, + read_waits, + write_byte, + write_fails, + write_pkg, + write_pkg_max, + write_tries, + write_waits, + acc_success, + acc_fails, + acc_tries, + acc_waits + ]. + +zero_counters() -> + [{Cnt, 0} || Cnt <- all_counters()]. + +any_counters() -> + [{Cnt, any} || Cnt <- all_counters()]. + + +%% This function ensures that we have a list of "validate counters" +%% that have an entry for each existing counter. + +ensure_counters(Counters) -> + ensure_counters(any_counters(), Counters, []). + +ensure_counters([], [], Acc) -> + lists:reverse(Acc); +ensure_counters([{Cnt, Val}|DefCounters], Counters, Acc) -> + case lists:keysearch(Cnt, 1, Counters) of + {value, {Cnt, _} = T} -> + Counters2 = lists:keydelete(Cnt, 1, Counters), + ensure_counters(DefCounters, Counters2, [T|Acc]); + false -> + ensure_counters(DefCounters, Counters, [{Cnt, Val}|Acc]) + end. traffic_sar_counters_validation(Counters) -> - traffic_sar_counters_validation(Counters, []). + traffic_sar_counters_validation2(Counters, zero_counters()). -traffic_sar_counters_validation(Counters, []) -> +traffic_sar_counters_validation(Counters, ValidateCounters) -> + traffic_sar_counters_validation2(Counters, ensure_counters(ValidateCounters)). + +traffic_sar_counters_validation2(Counters, []) -> + %% ?SEV_IPRINT("traffic_sar_counters_validation2 -> Remaining Counters: " + %% "~n ~p", [Counters]), (catch lists:foreach( fun({_Cnt, 0}) -> ok; - ({Cnt, Val}) -> throw({error, {invalid_counter, Cnt, Val}}) + ({Cnt, Val}) -> + throw({error, {invalid_counter, Cnt, Val}}) end, Counters)); -traffic_sar_counters_validation(Counters, [{Cnt, Val}|ValidateCounters]) -> +traffic_sar_counters_validation2(Counters, [{Cnt, Val}|ValidateCounters]) -> + %% ?SEV_IPRINT("traffic_sar_counters_validation2 -> try validate ~w when" + %% "~n Counters: ~p" + %% "~n ValidateCounters: ~p", [Cnt, Counters, ValidateCounters]), case lists:keysearch(Cnt, 1, Counters) of {value, {Cnt, Val}} -> + %% ?SEV_IPRINT("traffic_sar_counters_validation2 -> ~w validated", [Cnt]), Counters2 = lists:keydelete(Cnt, 1, Counters), - traffic_sar_counters_validation(Counters2, ValidateCounters); + traffic_sar_counters_validation2(Counters2, ValidateCounters); {value, {Cnt, _Val}} when (Val =:= any) -> + %% ?SEV_IPRINT("traffic_sar_counters_validation2 -> " + %% "~w validated (any) when" + %% "~n Counters: ~p", [Cnt, Counters]), Counters2 = lists:keydelete(Cnt, 1, Counters), - traffic_sar_counters_validation(Counters2, ValidateCounters); + traffic_sar_counters_validation2(Counters2, ValidateCounters); {value, {Cnt, InvVal}} -> + ?SEV_EPRINT("traffic_sar_counters_validation2 -> ~w validation failed: " + "~n Expected Value: ~p" + "~n Actual Value: ~p", [Cnt, Val, InvVal]), {error, {invalid_counter, Cnt, InvVal, Val}}; false -> + ?SEV_EPRINT("traffic_sar_counters_validation2 -> " + "~w validation failed: Unknown", [Cnt]), {error, {unknown_counter, Cnt, Counters}} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% This test case is intended to (simply) test that the counters +%% This test case is intended to (simply) test the counters %% for both read and write. %% So that its easy to extend, we use fun's for read and write. %% We use UDP on IPv4. @@ -28013,7 +28721,7 @@ traffic_sendto_and_recvfrom_counters_udp4(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% This test case is intended to (simply) test that the counters +%% This test case is intended to (simply) test the counters %% for both read and write. %% So that its easy to extend, we use fun's for read and write. %% We use UDP on IPv6. @@ -28040,7 +28748,7 @@ traffic_sendto_and_recvfrom_counters_udp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% This test case is intended to (simply) test that the counters +%% This test case is intended to (simply) test the counters %% for both read and write. %% So that its easy to extend, we use fun's for read and write. %% We use default (UDP) on local. @@ -28067,7 +28775,7 @@ traffic_sendto_and_recvfrom_counters_udpL(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% This test case is intended to (simply) test that the counters +%% This test case is intended to (simply) test the counters %% for both read and write. %% So that its easy to extend, we use fun's for read and write. %% We use UDP on IPv4. @@ -28101,7 +28809,7 @@ traffic_sendmsg_and_recvmsg_counters_udp4(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% This test case is intended to (simply) test that the counters +%% This test case is intended to (simply) test the counters %% for both read and write. %% So that its easy to extend, we use fun's for read and write. %% We use UDP on IPv6. @@ -28136,7 +28844,7 @@ traffic_sendmsg_and_recvmsg_counters_udp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% This test case is intended to (simply) test that the counters +%% This test case is intended to (simply) test the counters %% for both read and write. %% So that its easy to extend, we use fun's for read and write. %% We use default (UDP) on local. @@ -28226,7 +28934,7 @@ traffic_send_and_recv_udp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("Validate initial counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation(Counters) catch C:E:S -> @@ -28274,7 +28982,7 @@ traffic_send_and_recv_udp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("validate counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation( Counters, [{read_pkg, Pkg}, @@ -28322,7 +29030,7 @@ traffic_send_and_recv_udp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("validate counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation( Counters, [{read_pkg, RPkg}, @@ -28374,7 +29082,7 @@ traffic_send_and_recv_udp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("validate counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation( Counters, [{read_pkg, RPkg}, @@ -28427,7 +29135,7 @@ traffic_send_and_recv_udp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("validate counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation( Counters, [{read_pkg, RPkg}, @@ -28536,7 +29244,7 @@ traffic_send_and_recv_udp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("Validate initial counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation(Counters) catch C:E:S -> @@ -28579,12 +29287,13 @@ traffic_send_and_recv_udp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("validate counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation( Counters, - [{write_pkg, SPkg}, - {write_byte, SByte}, - {write_tries, any}]) + [{write_pkg, SPkg}, + {write_byte, SByte}, + {write_tries, any}, + {write_pkg_max, any}]) catch C:E:S -> ?SEV_EPRINT("Failed get socket info: " @@ -28637,15 +29346,17 @@ traffic_send_and_recv_udp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("validate counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation( Counters, - [{read_pkg, RPkg}, - {read_byte, RByte}, - {write_pkg, SPkg}, - {write_byte, SByte}, - {read_tries, any}, - {write_tries, any}]) + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}, + {read_pkg_max, any}, + {write_pkg_max, any}]) catch C:E:S -> ?SEV_EPRINT("Failed get socket info: " @@ -28690,15 +29401,17 @@ traffic_send_and_recv_udp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("validate counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation( Counters, - [{read_pkg, RPkg}, - {read_byte, RByte}, - {write_pkg, SPkg}, - {write_byte, SByte}, - {read_tries, any}, - {write_tries, any}]) + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}, + {read_pkg_max, any}, + {write_pkg_max, any}]) catch C:E:S -> ?SEV_EPRINT("Failed get socket info: " @@ -28755,15 +29468,17 @@ traffic_send_and_recv_udp(InitState) -> try socket:info(Sock) of #{counters := Counters} -> ?SEV_IPRINT("validate counters: " - "~n ~p", [Counters]), + "~s", [format_counters(Counters)]), traffic_sar_counters_validation( Counters, - [{read_pkg, RPkg}, - {read_byte, RByte}, - {write_pkg, SPkg}, - {write_byte, SByte}, - {read_tries, any}, - {write_tries, any}]) + [{read_pkg, RPkg}, + {read_byte, RByte}, + {write_pkg, SPkg}, + {write_byte, SByte}, + {read_tries, any}, + {write_tries, any}, + {read_pkg_max, any}, + {write_pkg_max, any}]) catch C:E:S -> ?SEV_EPRINT("Failed get socket info: " @@ -31478,7 +32193,7 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> client := Client} = State) -> case ?SEV_AWAIT_READY(Client, client, send, [{server, Server}]) of - {ok, {_, _, _, _} = Result} -> + {ok, {_, _, _, _, _} = Result} -> ?SEV_IPRINT("client result: " "~n ~p", [Result]), {ok, State#{client_result => Result}}; @@ -31514,7 +32229,7 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> client_result := CRes, num := Num} = State) -> {SSent, SReceived, SStart, SStop} = SRes, - {CSent, CReceived, CStart, CStop} = CRes, + {CSent, CReceived, _, CStart, CStop} = CRes, STime = tdiff(SStart, SStop), CTime = tdiff(CStart, CStop), %% Note that the sizes we are counting is only @@ -31768,9 +32483,10 @@ tpp_tcp_client_msg_exchange_loop(Sock, _Send, _Recv, _Msg, Num, Num, Sent, Received, Start) -> Stop = ?LIB:timestamp(), + Info = socket:info(Sock), case socket:close(Sock) of ok -> - {Sent, Received, Start, Stop}; + {Sent, Received, Info, Start, Stop}; {error, Reason} -> exit({failed_closing, Reason}) end; diff --git a/erts/emulator/test/socket_test_lib.erl b/erts/emulator/test/socket_test_lib.erl index 39cbf0c79f..f05796cbcf 100644 --- a/erts/emulator/test/socket_test_lib.erl +++ b/erts/emulator/test/socket_test_lib.erl @@ -194,49 +194,34 @@ which_local_host_info(Domain) -> which_local_host_info(_Domain, []) -> {error, no_address}; -which_local_host_info(Domain, [{"lo" ++ _, _}|IFL]) -> - which_local_host_info(Domain, IFL); which_local_host_info(Domain, [{"docker" ++ _, _}|IFL]) -> which_local_host_info(Domain, IFL); which_local_host_info(Domain, [{"br-" ++ _, _}|IFL]) -> which_local_host_info(Domain, IFL); which_local_host_info(Domain, [{Name, IFO}|IFL]) -> - try which_local_host_info2(Domain, IFO) of - Info -> - {ok, Info#{name => Name}} - catch - throw:_:_ -> + case if_is_running_and_not_loopback(IFO) of + true -> + try which_local_host_info2(Domain, IFO) of + Info -> + {ok, Info#{name => Name}} + catch + throw:_:_ -> + which_local_host_info(Domain, IFL) + end; + false -> which_local_host_info(Domain, IFL) end; which_local_host_info(Domain, [_|IFL]) -> which_local_host_info(Domain, IFL). -%% which_local_host_info2(Domain, IFO) -> -%% case lists:keysearch(flags, 1, IFO) of -%% {value, {flags, Flags}} -> -%% which_local_host_info2(Domain, IFO, Flags); -%% false -> -%% {error, no_flags} -%% end. - - -%% which_local_host_info2(_Domain, [], _Flags) -> -%% {error, no_address}; -%% which_local_host_info2(inet = _Domain, [{addr, Addr}|_IFO], Flags) -%% when (size(Addr) =:= 4) andalso (element(1, Addr) =/= 127) -> -%% {ok, {Flags, Addr}}; -%% which_local_host_info2(inet6 = _Domain, [{addr, Addr}|_IFO], Flags) -%% when (size(Addr) =:= 8) andalso -%% (element(1, Addr) =/= 0) andalso -%% (element(1, Addr) =/= 16#fe80) -> -%% {ok, {Flags, Addr}}; -%% which_local_host_info2(Domain, [_|IFO], Flags) -> -%% which_local_host_info2(Domain, IFO, Flags). - -%% foo(Info, inet = Domain, IFO) -> -%% foo(Info, Domain, IFO, [flags, addr, netmask, broadaddr, hwaddr]); -%% foo(Info, inet6 = Domain, IFO) -> -%% foo(Info, Domain, IFO, [flags, addr, netmask, hwaddr]). +if_is_running_and_not_loopback(If) -> + lists:keymember(flags, 1, If) andalso + begin + {value, {flags, Flags}} = lists:keysearch(flags, 1, If), + (not lists:member(loopback, Flags)) andalso + lists:member(running, Flags) + end. + which_local_host_info2(inet = _Domain, IFO) -> Addr = which_local_host_info3(addr, IFO, diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index 15fe13c8c0..cdfc4f2882 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -30,7 +30,8 @@ cleanup/1, evil_timers/1, registered_process/1, same_time_yielding/1, same_time_yielding_with_cancel/1, same_time_yielding_with_cancel_other/1, % same_time_yielding_with_cancel_other_accessor/1, - auto_cancel_yielding/1]). + auto_cancel_yielding/1, + suspended_scheduler_timeout/1]). -include_lib("common_test/include/ct.hrl"). @@ -68,7 +69,8 @@ all() -> same_time_yielding, same_time_yielding_with_cancel, same_time_yielding_with_cancel_other, % same_time_yielding_with_cancel_other_accessor, - auto_cancel_yielding]. + auto_cancel_yielding, + suspended_scheduler_timeout]. %% Basic start_timer/3 functionality @@ -630,6 +632,31 @@ auto_cancel_yielding(Config) when is_list(Config) -> Mem = mem(), ok. +suspended_scheduler_timeout(Config) when is_list(Config) -> + Ref = make_ref(), + SchdlrsOnln = erlang:system_info(schedulers_online), + lists:foreach(fun (Sched) -> + process_flag(scheduler, Sched), + erlang:send_after(1000, self(), {Ref, Sched}) + end, + lists:seq(1, SchdlrsOnln)), + process_flag(scheduler, 0), + erlang:system_flag(schedulers_online, 1), + try + lists:foreach(fun (Sched) -> + receive + {Ref, Sched} -> + ok + after 2000 -> + ct:fail({missing_timeout, Sched}) + end + end, + lists:seq(1, SchdlrsOnln)) + after + erlang:system_flag(schedulers_online, SchdlrsOnln) + end, + ok. + process_is_cleaned_up(P) when is_pid(P) -> undefined == erts_debug:get_internal_state({process_status, P}). diff --git a/erts/preloaded/ebin/atomics.beam b/erts/preloaded/ebin/atomics.beam Binary files differindex 610264de8f..aa246f8808 100644 --- a/erts/preloaded/ebin/atomics.beam +++ b/erts/preloaded/ebin/atomics.beam diff --git a/erts/preloaded/ebin/counters.beam b/erts/preloaded/ebin/counters.beam Binary files differindex df4a3dce2d..1fb85276f9 100644 --- a/erts/preloaded/ebin/counters.beam +++ b/erts/preloaded/ebin/counters.beam diff --git a/erts/preloaded/ebin/erl_init.beam b/erts/preloaded/ebin/erl_init.beam Binary files differindex 511f38624d..04559ff767 100644 --- a/erts/preloaded/ebin/erl_init.beam +++ b/erts/preloaded/ebin/erl_init.beam diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam Binary files differindex 7723e420fe..b8adf71297 100644 --- a/erts/preloaded/ebin/erl_prim_loader.beam +++ b/erts/preloaded/ebin/erl_prim_loader.beam diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam Binary files differindex 18a06a7446..4430489710 100644 --- a/erts/preloaded/ebin/erl_tracer.beam +++ b/erts/preloaded/ebin/erl_tracer.beam diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam Binary files differindex d995c7bf49..b744d97a41 100644 --- a/erts/preloaded/ebin/erlang.beam +++ b/erts/preloaded/ebin/erlang.beam diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam Binary files differindex 16b1930635..a94b37854e 100644 --- a/erts/preloaded/ebin/erts_code_purger.beam +++ b/erts/preloaded/ebin/erts_code_purger.beam diff --git a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam Binary files differindex b67b682a6d..dd92fb9582 100644 --- a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam +++ b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam Binary files differindex b648a1c223..d52ae9a2dc 100644 --- a/erts/preloaded/ebin/erts_internal.beam +++ b/erts/preloaded/ebin/erts_internal.beam diff --git a/erts/preloaded/ebin/erts_literal_area_collector.beam b/erts/preloaded/ebin/erts_literal_area_collector.beam Binary files differindex 72d347f401..44103e5d56 100644 --- a/erts/preloaded/ebin/erts_literal_area_collector.beam +++ b/erts/preloaded/ebin/erts_literal_area_collector.beam diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam Binary files differindex be3036d4f2..06074d1717 100644 --- a/erts/preloaded/ebin/init.beam +++ b/erts/preloaded/ebin/init.beam diff --git a/erts/preloaded/ebin/persistent_term.beam b/erts/preloaded/ebin/persistent_term.beam Binary files differindex bb889f181e..dc4a1aceb5 100644 --- a/erts/preloaded/ebin/persistent_term.beam +++ b/erts/preloaded/ebin/persistent_term.beam diff --git a/erts/preloaded/ebin/prim_buffer.beam b/erts/preloaded/ebin/prim_buffer.beam Binary files differindex e7c8adf589..a54a956fac 100644 --- a/erts/preloaded/ebin/prim_buffer.beam +++ b/erts/preloaded/ebin/prim_buffer.beam diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam Binary files differindex 6b3e0220cc..7b39f3e5ba 100644 --- a/erts/preloaded/ebin/prim_eval.beam +++ b/erts/preloaded/ebin/prim_eval.beam diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam Binary files differindex d4cb52e5a8..d0ef080a30 100644 --- a/erts/preloaded/ebin/prim_file.beam +++ b/erts/preloaded/ebin/prim_file.beam diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam Binary files differindex 2293728375..197ff59503 100644 --- a/erts/preloaded/ebin/prim_inet.beam +++ b/erts/preloaded/ebin/prim_inet.beam diff --git a/erts/preloaded/ebin/prim_net.beam b/erts/preloaded/ebin/prim_net.beam Binary files differindex 02dea609fa..76d4f0fc69 100644 --- a/erts/preloaded/ebin/prim_net.beam +++ b/erts/preloaded/ebin/prim_net.beam diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam Binary files differindex bcdd836d5b..75413bb065 100644 --- a/erts/preloaded/ebin/prim_zip.beam +++ b/erts/preloaded/ebin/prim_zip.beam diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam Binary files differindex 3d01d19a67..1a5f34f3ee 100644 --- a/erts/preloaded/ebin/socket.beam +++ b/erts/preloaded/ebin/socket.beam diff --git a/erts/preloaded/ebin/socket_registry.beam b/erts/preloaded/ebin/socket_registry.beam Binary files differnew file mode 100644 index 0000000000..a5eb2c190f --- /dev/null +++ b/erts/preloaded/ebin/socket_registry.beam diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam Binary files differindex 16bc893d41..14558a7424 100644 --- a/erts/preloaded/ebin/zlib.beam +++ b/erts/preloaded/ebin/zlib.beam diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index 38b85915cc..d2153f23e1 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -35,6 +35,7 @@ include $(ERL_TOP)/lib/kernel/vsn.mk ifeq ($(USE_ESOCK), yes) PRE_LOADED_ERL_ESOCK_MODULES = \ + socket_registry \ socket \ prim_net else @@ -81,7 +82,7 @@ APP_FILE= erts.app APP_SRC= $(APP_FILE).src APP_TARGET= $(STATIC_EBIN)/$(APP_FILE) ifeq ($(USE_ESOCK), yes) -APP_ESOCK_MODS= prim_net, socket +APP_ESOCK_MODS= prim_net, socket, socket_registry else APP_ESOCK_MODS= endif diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index 1605c20f2c..658a138694 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -300,12 +300,18 @@ check_file_result(Func, Target, {error,Reason}) -> %% This is equal to calling logger:error/2 which %% we don't want to do from code_server during system boot. %% We don't want to call logger:timestamp() either. - logger ! {log,error,#{label=>{?MODULE,file_error},report=>Report}, - #{pid=>self(), - gl=>group_leader(), - time=>os:system_time(microsecond), - error_logger=>#{tag=>error_report, - type=>std_error}}}, + _ = try + logger ! {log,error,#{label=>{?MODULE,file_error},report=>Report}, + #{pid=>self(), + gl=>group_leader(), + time=>os:system_time(microsecond), + error_logger=>#{tag=>error_report, + type=>std_error}}} + catch _:_ -> + %% If logger has not been started yet we just display it + erlang:display({?MODULE,file_error}), + erlang:display(Report) + end, error end; check_file_result(_, _, Other) -> diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 5df74b9668..82d0aa91f8 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2205,7 +2205,9 @@ nodes(_Arg) -> | binary | eof | {parallelism, Boolean :: boolean()} - | hide. + | hide + | {busy_limits_port, {non_neg_integer(), non_neg_integer()} | disabled} + | {busy_limits_msgq, {non_neg_integer(), non_neg_integer()} | disabled}. open_port(PortName, PortSettings) -> case case erts_internal:open_port(PortName, PortSettings) of Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end; diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index bbe9a9affa..0dd65d3e27 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -103,6 +103,8 @@ -export([ets_lookup_binary_info/2, ets_super_user/1, ets_info_binary/1, ets_raw_first/1, ets_raw_next/2]). +-export([get_internal_state_blocked/1]). + %% %% Await result of send to port %% @@ -816,3 +818,15 @@ ets_info_binary_iter(Tab, Key, Acc) -> [_|_] = BIL -> BIL ++ Acc end, ets_info_binary_iter(Tab, erts_internal:ets_raw_next(Tab, Key), NewAcc). + +-spec get_internal_state_blocked(Arg :: term()) -> term(). + +get_internal_state_blocked(Arg) -> + erlang:system_flag(multi_scheduling, block), + Result = try + erts_debug:get_internal_state({Arg, + blocked}) + after + erlang:system_flag(multi_scheduling, unblock) + end, + Result. diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 1aa5d85c64..f241be8569 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -575,11 +575,11 @@ list_dir_convert([RawName | Rest], SkipInvalid, Result) -> %% This is equal to calling logger:warning/3 which %% we don't want to do from code_server during system boot. %% We don't want to call logger:timestamp() either. - logger ! {log,warning,"Non-unicode filename ~p ignored\n", [RawName], - #{pid=>self(), - gl=>group_leader(), - time=>os:system_time(microsecond), - error_logger=>#{tag=>warning_msg}}}, + catch logger ! {log,warning,"Non-unicode filename ~p ignored\n", [RawName], + #{pid=>self(), + gl=>group_leader(), + time=>os:system_time(microsecond), + error_logger=>#{tag=>warning_msg}}}, list_dir_convert(Rest, SkipInvalid, Result); {error, _} -> {error, {no_translation, RawName}} diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 5984b5bda9..eb1bcdce66 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2019. All Rights Reserved. +%% Copyright Ericsson AB 2018-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -27,6 +27,9 @@ -export([ on_load/0, on_load/1, + number_of/0, + which_sockets/0, which_sockets/1, + ensure_sockaddr/1, debug/1, @@ -148,6 +151,9 @@ ]). +-define(REGISTRY, socket_registry). + + %% The command type has the general form: %% #{ %% command := atom(), @@ -161,13 +167,21 @@ %% -type command() :: debug_command(). -type socket_counters() :: [{socket_counter(), non_neg_integer()}]. --type socket_counter() :: read_byte | read_fails | read_pkg | read_tries | - read_waits | write_byte | write_fails | write_pkg | - write_tries | write_waits. --type socket_info() :: #{counters := socket_counters(), +-type socket_counter() :: read_byte | read_fails | read_pkg | read_pkg_max | + read_tries | read_waits | + write_byte | write_fails | write_pkg | write_pkg_max | + write_tries | write_waits | + acc_success | acc_fails | acc_tries | acc_waits. +-type socket_info() :: #{domain := domain(), + type := type(), + protocol := protocol(), + ctrl := pid(), + counters := socket_counters(), num_readers := non_neg_integer(), num_writers := non_neg_integer(), - num_acceptors := non_neg_integer()}. + num_acceptors := non_neg_integer(), + writable := boolean(), + readable := boolean()}. -type uint8() :: 0..16#FF. -type uint16() :: 0..16#FFFF. @@ -693,244 +707,246 @@ %% This is used in messages sent from the nif-code to erlang processes: %% -%% {?SOCKET_TAG, Socket :: socket(), Tag :: atom(), Info :: term()} -%% --define(SOCKET_TAG, '$socket'). - --define(SOCKET_DOMAIN_LOCAL, 1). --define(SOCKET_DOMAIN_UNIX, ?SOCKET_DOMAIN_LOCAL). --define(SOCKET_DOMAIN_INET, 2). --define(SOCKET_DOMAIN_INET6, 3). - --define(SOCKET_TYPE_STREAM, 1). --define(SOCKET_TYPE_DGRAM, 2). --define(SOCKET_TYPE_RAW, 3). -%% -define(SOCKET_TYPE_RDM, 4). --define(SOCKET_TYPE_SEQPACKET, 5). - --define(SOCKET_PROTOCOL_DEFAULT, 0). --define(SOCKET_PROTOCOL_IP, 1). --define(SOCKET_PROTOCOL_TCP, 2). --define(SOCKET_PROTOCOL_UDP, 3). --define(SOCKET_PROTOCOL_SCTP, 4). --define(SOCKET_PROTOCOL_ICMP, 5). --define(SOCKET_PROTOCOL_IGMP, 6). - --define(SOCKET_LISTEN_BACKLOG_DEFAULT, 5). - --define(SOCKET_ACCEPT_TIMEOUT_DEFAULT, infinity). - --define(SOCKET_SEND_FLAG_CONFIRM, 0). --define(SOCKET_SEND_FLAG_DONTROUTE, 1). --define(SOCKET_SEND_FLAG_EOR, 2). --define(SOCKET_SEND_FLAG_MORE, 3). --define(SOCKET_SEND_FLAG_NOSIGNAL, 4). --define(SOCKET_SEND_FLAG_OOB, 5). - --define(SOCKET_SEND_FLAGS_DEFAULT, []). --define(SOCKET_SEND_TIMEOUT_DEFAULT, infinity). --define(SOCKET_SENDTO_FLAGS_DEFAULT, []). --define(SOCKET_SENDTO_TIMEOUT_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT). --define(SOCKET_SENDMSG_FLAGS_DEFAULT, []). --define(SOCKET_SENDMSG_TIMEOUT_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT). - --define(SOCKET_RECV_FLAG_CMSG_CLOEXEC, 0). --define(SOCKET_RECV_FLAG_ERRQUEUE, 1). --define(SOCKET_RECV_FLAG_OOB, 2). --define(SOCKET_RECV_FLAG_PEEK, 3). --define(SOCKET_RECV_FLAG_TRUNC, 4). - --define(SOCKET_RECV_FLAGS_DEFAULT, []). --define(SOCKET_RECV_TIMEOUT_DEFAULT, infinity). - --define(SOCKET_OPT_LEVEL_OTP, 0). --define(SOCKET_OPT_LEVEL_SOCKET, 1). --define(SOCKET_OPT_LEVEL_IP, 2). --define(SOCKET_OPT_LEVEL_IPV6, 3). --define(SOCKET_OPT_LEVEL_TCP, 4). --define(SOCKET_OPT_LEVEL_UDP, 5). --define(SOCKET_OPT_LEVEL_SCTP, 6). +%% {?ESOCK_TAG, Socket :: socket(), Tag :: atom(), Info :: term()} +%% +-define(ESOCK_TAG, '$socket'). + +-define(ESOCK_DOMAIN_LOCAL, 1). +-define(ESOCK_DOMAIN_UNIX, ?ESOCK_DOMAIN_LOCAL). +-define(ESOCK_DOMAIN_INET, 2). +-define(ESOCK_DOMAIN_INET6, 3). + +-define(ESOCK_TYPE_STREAM, 1). +-define(ESOCK_TYPE_DGRAM, 2). +-define(ESOCK_TYPE_RAW, 3). +%% -define(ESOCK_TYPE_RDM, 4). +-define(ESOCK_TYPE_SEQPACKET, 5). + +-define(ESOCK_PROTOCOL_DEFAULT, 0). +-define(ESOCK_PROTOCOL_IP, 1). +-define(ESOCK_PROTOCOL_TCP, 2). +-define(ESOCK_PROTOCOL_UDP, 3). +-define(ESOCK_PROTOCOL_SCTP, 4). +-define(ESOCK_PROTOCOL_ICMP, 5). +-define(ESOCK_PROTOCOL_IGMP, 6). + +-define(ESOCK_LISTEN_BACKLOG_DEFAULT, 5). + +-define(ESOCK_ACCEPT_TIMEOUT_DEFAULT, infinity). + +-define(ESOCK_SEND_FLAG_CONFIRM, 0). +-define(ESOCK_SEND_FLAG_DONTROUTE, 1). +-define(ESOCK_SEND_FLAG_EOR, 2). +-define(ESOCK_SEND_FLAG_MORE, 3). +-define(ESOCK_SEND_FLAG_NOSIGNAL, 4). +-define(ESOCK_SEND_FLAG_OOB, 5). + +-define(ESOCK_SEND_FLAGS_DEFAULT, []). +-define(ESOCK_SEND_TIMEOUT_DEFAULT, infinity). +-define(ESOCK_SENDTO_FLAGS_DEFAULT, []). +-define(ESOCK_SENDTO_TIMEOUT_DEFAULT, ?ESOCK_SEND_TIMEOUT_DEFAULT). +-define(ESOCK_SENDMSG_FLAGS_DEFAULT, []). +-define(ESOCK_SENDMSG_TIMEOUT_DEFAULT, ?ESOCK_SEND_TIMEOUT_DEFAULT). + +-define(ESOCK_RECV_FLAG_CMSG_CLOEXEC, 0). +-define(ESOCK_RECV_FLAG_ERRQUEUE, 1). +-define(ESOCK_RECV_FLAG_OOB, 2). +-define(ESOCK_RECV_FLAG_PEEK, 3). +-define(ESOCK_RECV_FLAG_TRUNC, 4). + +-define(ESOCK_RECV_FLAGS_DEFAULT, []). +-define(ESOCK_RECV_TIMEOUT_DEFAULT, infinity). + +-define(ESOCK_OPT_LEVEL_OTP, 0). +-define(ESOCK_OPT_LEVEL_SOCKET, 1). +-define(ESOCK_OPT_LEVEL_IP, 2). +-define(ESOCK_OPT_LEVEL_IPV6, 3). +-define(ESOCK_OPT_LEVEL_TCP, 4). +-define(ESOCK_OPT_LEVEL_UDP, 5). +-define(ESOCK_OPT_LEVEL_SCTP, 6). %% *** OTP (socket) options --define(SOCKET_OPT_OTP_DEBUG, 1). --define(SOCKET_OPT_OTP_IOW, 2). --define(SOCKET_OPT_OTP_CTRL_PROC, 3). --define(SOCKET_OPT_OTP_RCVBUF, 4). -%%-define(SOCKET_OPT_OTP_SNDBUF, 5). --define(SOCKET_OPT_OTP_RCVCTRLBUF, 6). --define(SOCKET_OPT_OTP_SNDCTRLBUF, 7). --define(SOCKET_OPT_OTP_FD, 8). --define(SOCKET_OPT_OTP_DOMAIN, 16#FF01). % INTERNAL --define(SOCKET_OPT_OTP_TYPE, 16#FF02). % INTERNAL --define(SOCKET_OPT_OTP_PROTOCOL, 16#FF03). % INTERNAL +-define(ESOCK_OPT_OTP_DEBUG, 1). +-define(ESOCK_OPT_OTP_IOW, 2). +-define(ESOCK_OPT_OTP_CTRL_PROC, 3). +-define(ESOCK_OPT_OTP_RCVBUF, 4). +%%-define(ESOCK_OPT_OTP_SNDBUF, 5). +-define(ESOCK_OPT_OTP_RCVCTRLBUF, 6). +-define(ESOCK_OPT_OTP_SNDCTRLBUF, 7). +-define(ESOCK_OPT_OTP_FD, 8). +-define(ESOCK_OPT_OTP_META, 9). +-define(ESOCK_OPT_OTP_DOMAIN, 16#FF01). % INTERNAL +-define(ESOCK_OPT_OTP_TYPE, 16#FF02). % INTERNAL +-define(ESOCK_OPT_OTP_PROTOCOL, 16#FF03). % INTERNAL +-define(ESOCK_OPT_OTP_DTP, 16#FF04). % INTERNAL %% *** SOCKET (socket) options --define(SOCKET_OPT_SOCK_ACCEPTCONN, 1). -%% -define(SOCKET_OPT_SOCK_ACCEPTFILTER, 2). % FreeBSD --define(SOCKET_OPT_SOCK_BINDTODEVICE, 3). --define(SOCKET_OPT_SOCK_BROADCAST, 4). -%% -define(SOCKET_OPT_SOCK_BUSY_POLL, 5). --define(SOCKET_OPT_SOCK_DEBUG, 6). --define(SOCKET_OPT_SOCK_DOMAIN, 7). --define(SOCKET_OPT_SOCK_DONTROUTE, 8). -%% -define(SOCKET_OPT_SOCK_ERROR, 9). --define(SOCKET_OPT_SOCK_KEEPALIVE, 10). --define(SOCKET_OPT_SOCK_LINGER, 11). -%% -define(SOCKET_OPT_SOCK_MARK, 12). --define(SOCKET_OPT_SOCK_OOBINLINE, 13). --define(SOCKET_OPT_SOCK_PASSCRED, 14). --define(SOCKET_OPT_SOCK_PEEK_OFF, 15). -%% -define(SOCKET_OPT_SOCK_PEERCRED, 16). --define(SOCKET_OPT_SOCK_PRIORITY, 17). --define(SOCKET_OPT_SOCK_PROTOCOL, 18). --define(SOCKET_OPT_SOCK_RCVBUF, 19). -%% -define(SOCKET_OPT_SOCK_RCVBUFFORCE, 20). --define(SOCKET_OPT_SOCK_RCVLOWAT, 21). --define(SOCKET_OPT_SOCK_RCVTIMEO, 22). --define(SOCKET_OPT_SOCK_REUSEADDR, 23). --define(SOCKET_OPT_SOCK_REUSEPORT, 24). -%% -define(SOCKET_OPT_SOCK_RXQ_OVFL, 25). -%% -define(SOCKET_OPT_SOCK_SETFIB, 26). % FreeBSD --define(SOCKET_OPT_SOCK_SNDBUF, 27). -%% -define(SOCKET_OPT_SOCK_SNDBUFFORCE, 28). --define(SOCKET_OPT_SOCK_SNDLOWAT, 29). --define(SOCKET_OPT_SOCK_SNDTIMEO, 30). --define(SOCKET_OPT_SOCK_TIMESTAMP, 31). --define(SOCKET_OPT_SOCK_TYPE, 32). +-define(ESOCK_OPT_SOCK_ACCEPTCONN, 1). +%% -define(ESOCK_OPT_SOCK_ACCEPTFILTER, 2). % FreeBSD +-define(ESOCK_OPT_SOCK_BINDTODEVICE, 3). +-define(ESOCK_OPT_SOCK_BROADCAST, 4). +%% -define(ESOCK_OPT_SOCK_BUSY_POLL, 5). +-define(ESOCK_OPT_SOCK_DEBUG, 6). +-define(ESOCK_OPT_SOCK_DOMAIN, 7). +-define(ESOCK_OPT_SOCK_DONTROUTE, 8). +%% -define(ESOCK_OPT_SOCK_ERROR, 9). +-define(ESOCK_OPT_SOCK_KEEPALIVE, 10). +-define(ESOCK_OPT_SOCK_LINGER, 11). +%% -define(ESOCK_OPT_SOCK_MARK, 12). +-define(ESOCK_OPT_SOCK_OOBINLINE, 13). +-define(ESOCK_OPT_SOCK_PASSCRED, 14). +-define(ESOCK_OPT_SOCK_PEEK_OFF, 15). +%% -define(ESOCK_OPT_SOCK_PEERCRED, 16). +-define(ESOCK_OPT_SOCK_PRIORITY, 17). +-define(ESOCK_OPT_SOCK_PROTOCOL, 18). +-define(ESOCK_OPT_SOCK_RCVBUF, 19). +%% -define(ESOCK_OPT_SOCK_RCVBUFFORCE, 20). +-define(ESOCK_OPT_SOCK_RCVLOWAT, 21). +-define(ESOCK_OPT_SOCK_RCVTIMEO, 22). +-define(ESOCK_OPT_SOCK_REUSEADDR, 23). +-define(ESOCK_OPT_SOCK_REUSEPORT, 24). +%% -define(ESOCK_OPT_SOCK_RXQ_OVFL, 25). +%% -define(ESOCK_OPT_SOCK_SETFIB, 26). % FreeBSD +-define(ESOCK_OPT_SOCK_SNDBUF, 27). +%% -define(ESOCK_OPT_SOCK_SNDBUFFORCE, 28). +-define(ESOCK_OPT_SOCK_SNDLOWAT, 29). +-define(ESOCK_OPT_SOCK_SNDTIMEO, 30). +-define(ESOCK_OPT_SOCK_TIMESTAMP, 31). +-define(ESOCK_OPT_SOCK_TYPE, 32). %% *** IP (socket) options --define(SOCKET_OPT_IP_ADD_MEMBERSHIP, 1). --define(SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP, 2). --define(SOCKET_OPT_IP_BLOCK_SOURCE, 3). -%% -define(SOCKET_OPT_IP_DONTFRAG, 4). % FreeBSD --define(SOCKET_OPT_IP_DROP_MEMBERSHIP, 5). --define(SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP, 6). --define(SOCKET_OPT_IP_FREEBIND, 7). --define(SOCKET_OPT_IP_HDRINCL, 8). --define(SOCKET_OPT_IP_MINTTL, 9). --define(SOCKET_OPT_IP_MSFILTER, 10). --define(SOCKET_OPT_IP_MTU, 11). --define(SOCKET_OPT_IP_MTU_DISCOVER, 12). --define(SOCKET_OPT_IP_MULTICAST_ALL, 13). --define(SOCKET_OPT_IP_MULTICAST_IF, 14). --define(SOCKET_OPT_IP_MULTICAST_LOOP, 15). --define(SOCKET_OPT_IP_MULTICAST_TTL, 16). --define(SOCKET_OPT_IP_NODEFRAG, 17). -%% -define(SOCKET_OPT_IP_OPTIONS, 18). % FreeBSD --define(SOCKET_OPT_IP_PKTINFO, 19). --define(SOCKET_OPT_IP_RECVDSTADDR, 20). % FreeBSD --define(SOCKET_OPT_IP_RECVERR, 21). --define(SOCKET_OPT_IP_RECVIF, 22). --define(SOCKET_OPT_IP_RECVOPTS, 23). --define(SOCKET_OPT_IP_RECVORIGDSTADDR, 24). --define(SOCKET_OPT_IP_RECVTOS, 25). --define(SOCKET_OPT_IP_RECVTTL, 26). --define(SOCKET_OPT_IP_RETOPTS, 27). --define(SOCKET_OPT_IP_ROUTER_ALERT, 28). --define(SOCKET_OPT_IP_SENDSRCADDR, 29). % FreeBSD --define(SOCKET_OPT_IP_TOS, 30). --define(SOCKET_OPT_IP_TRANSPARENT, 31). --define(SOCKET_OPT_IP_TTL, 32). --define(SOCKET_OPT_IP_UNBLOCK_SOURCE, 33). +-define(ESOCK_OPT_IP_ADD_MEMBERSHIP, 1). +-define(ESOCK_OPT_IP_ADD_SOURCE_MEMBERSHIP, 2). +-define(ESOCK_OPT_IP_BLOCK_SOURCE, 3). +%% -define(ESOCK_OPT_IP_DONTFRAG, 4). % FreeBSD +-define(ESOCK_OPT_IP_DROP_MEMBERSHIP, 5). +-define(ESOCK_OPT_IP_DROP_SOURCE_MEMBERSHIP, 6). +-define(ESOCK_OPT_IP_FREEBIND, 7). +-define(ESOCK_OPT_IP_HDRINCL, 8). +-define(ESOCK_OPT_IP_MINTTL, 9). +-define(ESOCK_OPT_IP_MSFILTER, 10). +-define(ESOCK_OPT_IP_MTU, 11). +-define(ESOCK_OPT_IP_MTU_DISCOVER, 12). +-define(ESOCK_OPT_IP_MULTICAST_ALL, 13). +-define(ESOCK_OPT_IP_MULTICAST_IF, 14). +-define(ESOCK_OPT_IP_MULTICAST_LOOP, 15). +-define(ESOCK_OPT_IP_MULTICAST_TTL, 16). +-define(ESOCK_OPT_IP_NODEFRAG, 17). +%% -define(ESOCK_OPT_IP_OPTIONS, 18). % FreeBSD +-define(ESOCK_OPT_IP_PKTINFO, 19). +-define(ESOCK_OPT_IP_RECVDSTADDR, 20). % FreeBSD +-define(ESOCK_OPT_IP_RECVERR, 21). +-define(ESOCK_OPT_IP_RECVIF, 22). +-define(ESOCK_OPT_IP_RECVOPTS, 23). +-define(ESOCK_OPT_IP_RECVORIGDSTADDR, 24). +-define(ESOCK_OPT_IP_RECVTOS, 25). +-define(ESOCK_OPT_IP_RECVTTL, 26). +-define(ESOCK_OPT_IP_RETOPTS, 27). +-define(ESOCK_OPT_IP_ROUTER_ALERT, 28). +-define(ESOCK_OPT_IP_SENDSRCADDR, 29). % FreeBSD +-define(ESOCK_OPT_IP_TOS, 30). +-define(ESOCK_OPT_IP_TRANSPARENT, 31). +-define(ESOCK_OPT_IP_TTL, 32). +-define(ESOCK_OPT_IP_UNBLOCK_SOURCE, 33). %% *** IPv6 (socket) options --define(SOCKET_OPT_IPV6_ADDRFORM, 1). --define(SOCKET_OPT_IPV6_ADD_MEMBERSHIP, 2). --define(SOCKET_OPT_IPV6_AUTHHDR, 3). % Obsolete? -%% -define(SOCKET_OPT_IPV6_AUTH_LEVEL, 4). % FreeBSD -%% -define(SOCKET_OPT_IPV6_CHECKSUM, 5). % FreeBSD --define(SOCKET_OPT_IPV6_DROP_MEMBERSHIP, 6). --define(SOCKET_OPT_IPV6_DSTOPTS, 7). -%% -define(SOCKET_OPT_IPV6_ESP_NETWORK_LEVEL, 8). % FreeBSD -%% -define(SOCKET_OPT_IPV6_ESP_TRANS_LEVEL, 9). % FreeBSD -%% -define(SOCKET_OPT_IPV6_FAITH, 10). % FreeBSD --define(SOCKET_OPT_IPV6_FLOWINFO, 11). --define(SOCKET_OPT_IPV6_HOPLIMIT, 12). --define(SOCKET_OPT_IPV6_HOPOPTS, 13). -%% -define(SOCKET_OPT_IPV6_IPCOMP_LEVEL, 14). % FreeBSD -%% -define(SOCKET_OPT_IPV6_JOIN_GROUP, 15). % FreeBSD -%% -define(SOCKET_OPT_IPV6_LEAVE_GROUP, 16). % FreeBSD --define(SOCKET_OPT_IPV6_MTU, 17). --define(SOCKET_OPT_IPV6_MTU_DISCOVER, 18). --define(SOCKET_OPT_IPV6_MULTICAST_HOPS, 19). --define(SOCKET_OPT_IPV6_MULTICAST_IF, 20). --define(SOCKET_OPT_IPV6_MULTICAST_LOOP, 21). -%% -define(SOCKET_OPT_IPV6_PORTRANGE, 22). % FreeBSD -%% -define(SOCKET_OPT_IPV6_PKTOPTIONS, 23). % FreeBSD --define(SOCKET_OPT_IPV6_RECVERR, 24). --define(SOCKET_OPT_IPV6_RECVHOPLIMIT, 25). --define(SOCKET_OPT_IPV6_RECVPKTINFO, 26). % On FreeBSD: PKTINFO --define(SOCKET_OPT_IPV6_RECVTCLASS, 27). --define(SOCKET_OPT_IPV6_ROUTER_ALERT, 28). --define(SOCKET_OPT_IPV6_RTHDR, 29). --define(SOCKET_OPT_IPV6_TCLASS, 30). % FreeBSD --define(SOCKET_OPT_IPV6_UNICAST_HOPS, 31). -%% -define(SOCKET_OPT_IPV6_USE_MIN_MTU, 32). % FreeBSD --define(SOCKET_OPT_IPV6_V6ONLY, 33). +-define(ESOCK_OPT_IPV6_ADDRFORM, 1). +-define(ESOCK_OPT_IPV6_ADD_MEMBERSHIP, 2). +-define(ESOCK_OPT_IPV6_AUTHHDR, 3). % Obsolete? +%% -define(ESOCK_OPT_IPV6_AUTH_LEVEL, 4). % FreeBSD +%% -define(ESOCK_OPT_IPV6_CHECKSUM, 5). % FreeBSD +-define(ESOCK_OPT_IPV6_DROP_MEMBERSHIP, 6). +-define(ESOCK_OPT_IPV6_DSTOPTS, 7). +%% -define(ESOCK_OPT_IPV6_ESP_NETWORK_LEVEL, 8). % FreeBSD +%% -define(ESOCK_OPT_IPV6_ESP_TRANS_LEVEL, 9). % FreeBSD +%% -define(ESOCK_OPT_IPV6_FAITH, 10). % FreeBSD +-define(ESOCK_OPT_IPV6_FLOWINFO, 11). +-define(ESOCK_OPT_IPV6_HOPLIMIT, 12). +-define(ESOCK_OPT_IPV6_HOPOPTS, 13). +%% -define(ESOCK_OPT_IPV6_IPCOMP_LEVEL, 14). % FreeBSD +%% -define(ESOCK_OPT_IPV6_JOIN_GROUP, 15). % FreeBSD +%% -define(ESOCK_OPT_IPV6_LEAVE_GROUP, 16). % FreeBSD +-define(ESOCK_OPT_IPV6_MTU, 17). +-define(ESOCK_OPT_IPV6_MTU_DISCOVER, 18). +-define(ESOCK_OPT_IPV6_MULTICAST_HOPS, 19). +-define(ESOCK_OPT_IPV6_MULTICAST_IF, 20). +-define(ESOCK_OPT_IPV6_MULTICAST_LOOP, 21). +%% -define(ESOCK_OPT_IPV6_PORTRANGE, 22). % FreeBSD +%% -define(ESOCK_OPT_IPV6_PKTOPTIONS, 23). % FreeBSD +-define(ESOCK_OPT_IPV6_RECVERR, 24). +-define(ESOCK_OPT_IPV6_RECVHOPLIMIT, 25). +-define(ESOCK_OPT_IPV6_RECVPKTINFO, 26). % On FreeBSD: PKTINFO +-define(ESOCK_OPT_IPV6_RECVTCLASS, 27). +-define(ESOCK_OPT_IPV6_ROUTER_ALERT, 28). +-define(ESOCK_OPT_IPV6_RTHDR, 29). +-define(ESOCK_OPT_IPV6_TCLASS, 30). % FreeBSD +-define(ESOCK_OPT_IPV6_UNICAST_HOPS, 31). +%% -define(ESOCK_OPT_IPV6_USE_MIN_MTU, 32). % FreeBSD +-define(ESOCK_OPT_IPV6_V6ONLY, 33). %% *** TCP (socket) options --define(SOCKET_OPT_TCP_CONGESTION, 1). --define(SOCKET_OPT_TCP_CORK, 2). -%% -define(SOCKET_OPT_TCP_INFO, 3). -%% -define(SOCKET_OPT_TCP_KEEPCNT, 4). -%% -define(SOCKET_OPT_TCP_KEEPIDLE, 5). -%% -define(SOCKET_OPT_TCP_KEEPINTVL, 6). --define(SOCKET_OPT_TCP_MAXSEG, 7). -%% -define(SOCKET_OPT_TCP_MD5SIG, 8). --define(SOCKET_OPT_TCP_NODELAY, 9). -%% -define(SOCKET_OPT_TCP_NOOPT, 10). -%% -define(SOCKET_OPT_TCP_NOPUSH, 11). -%% -define(SOCKET_OPT_TCP_SYNCNT, 12). -%% -define(SOCKET_OPT_TCP_USER_TIMEOUT, 13). +-define(ESOCK_OPT_TCP_CONGESTION, 1). +-define(ESOCK_OPT_TCP_CORK, 2). +%% -define(ESOCK_OPT_TCP_INFO, 3). +%% -define(ESOCK_OPT_TCP_KEEPCNT, 4). +%% -define(ESOCK_OPT_TCP_KEEPIDLE, 5). +%% -define(ESOCK_OPT_TCP_KEEPINTVL, 6). +-define(ESOCK_OPT_TCP_MAXSEG, 7). +%% -define(ESOCK_OPT_TCP_MD5SIG, 8). +-define(ESOCK_OPT_TCP_NODELAY, 9). +%% -define(ESOCK_OPT_TCP_NOOPT, 10). +%% -define(ESOCK_OPT_TCP_NOPUSH, 11). +%% -define(ESOCK_OPT_TCP_SYNCNT, 12). +%% -define(ESOCK_OPT_TCP_USER_TIMEOUT, 13). %% *** UDP (socket) options --define(SOCKET_OPT_UDP_CORK, 1). +-define(ESOCK_OPT_UDP_CORK, 1). %% *** SCTP (socket) options -%% -define(SOCKET_OPT_SCTP_ADAPTION_LAYER, 1). --define(SOCKET_OPT_SCTP_ASSOCINFO, 2). -%% -define(SOCKET_OPT_SCTP_AUTH_ACTIVE_KEY, 3). -%% -define(SOCKET_OPT_SCTP_AUTH_ASCONF, 4). -%% -define(SOCKET_OPT_SCTP_AUTH_CHUNK, 5). -%% -define(SOCKET_OPT_SCTP_AUTH_KEY, 6). -%% -define(SOCKET_OPT_SCTP_AUTH_DELETE_KEY, 7). --define(SOCKET_OPT_SCTP_AUTOCLOSE, 8). -%% -define(SOCKET_OPT_SCTP_CONTEXT, 9). -%% -define(SOCKET_OPT_SCTP_DEFAULT_SEND_PARAMS, 10). -%% -define(SOCKET_OPT_SCTP_DELAYED_ACK_TIME, 11). --define(SOCKET_OPT_SCTP_DISABLE_FRAGMENTS, 12). -%% -define(SOCKET_OPT_SCTP_HMAC_IDENT, 13). --define(SOCKET_OPT_SCTP_EVENTS, 14). -%% -define(SOCKET_OPT_SCTP_EXPLICIT_EOR, 15). -%% -define(SOCKET_OPT_SCTP_FRAGMENT_INTERLEAVE, 16). -%% -define(SOCKET_OPT_SCTP_GET_PEER_ADDR_INFO, 17). --define(SOCKET_OPT_SCTP_INITMSG, 18). -%% -define(SOCKET_OPT_SCTP_I_WANT_MAPPED_V4_ADDR, 19). -%% -define(SOCKET_OPT_SCTP_LOCAL_AUTH_CHUNKS, 20). --define(SOCKET_OPT_SCTP_MAXSEG, 21). -%% -define(SOCKET_OPT_SCTP_MAXBURST, 22). --define(SOCKET_OPT_SCTP_NODELAY, 23). -%% -define(SOCKET_OPT_SCTP_PARTIAL_DELIVERY_POINT, 24). -%% -define(SOCKET_OPT_SCTP_PEER_ADDR_PARAMS, 25). -%% -define(SOCKET_OPT_SCTP_PEER_AUTH_CHUNKS, 26). -%% -define(SOCKET_OPT_SCTP_PRIMARY_ADDR, 27). -%% -define(SOCKET_OPT_SCTP_RESET_STREAMS, 28). --define(SOCKET_OPT_SCTP_RTOINFO, 29). -%% -define(SOCKET_OPT_SCTP_SET_PEER_PRIMARY_ADDR, 30). -%% -define(SOCKET_OPT_SCTP_STATUS, 31). -%% -define(SOCKET_OPT_SCTP_USE_EXT_RECVINFO, 32). - --define(SOCKET_SHUTDOWN_HOW_READ, 0). --define(SOCKET_SHUTDOWN_HOW_WRITE, 1). --define(SOCKET_SHUTDOWN_HOW_READ_WRITE, 2). - - --define(SOCKET_SUPPORTS_OPTIONS, 16#0001). --define(SOCKET_SUPPORTS_SCTP, 16#0002). --define(SOCKET_SUPPORTS_IPV6, 16#0003). --define(SOCKET_SUPPORTS_LOCAL, 16#0004). --define(SOCKET_SUPPORTS_SEND_FLAGS, 16#0005). --define(SOCKET_SUPPORTS_RECV_FLAGS, 16#0006). +%% -define(ESOCK_OPT_SCTP_ADAPTION_LAYER, 1). +-define(ESOCK_OPT_SCTP_ASSOCINFO, 2). +%% -define(ESOCK_OPT_SCTP_AUTH_ACTIVE_KEY, 3). +%% -define(ESOCK_OPT_SCTP_AUTH_ASCONF, 4). +%% -define(ESOCK_OPT_SCTP_AUTH_CHUNK, 5). +%% -define(ESOCK_OPT_SCTP_AUTH_KEY, 6). +%% -define(ESOCK_OPT_SCTP_AUTH_DELETE_KEY, 7). +-define(ESOCK_OPT_SCTP_AUTOCLOSE, 8). +%% -define(ESOCK_OPT_SCTP_CONTEXT, 9). +%% -define(ESOCK_OPT_SCTP_DEFAULT_SEND_PARAMS, 10). +%% -define(ESOCK_OPT_SCTP_DELAYED_ACK_TIME, 11). +-define(ESOCK_OPT_SCTP_DISABLE_FRAGMENTS, 12). +%% -define(ESOCK_OPT_SCTP_HMAC_IDENT, 13). +-define(ESOCK_OPT_SCTP_EVENTS, 14). +%% -define(ESOCK_OPT_SCTP_EXPLICIT_EOR, 15). +%% -define(ESOCK_OPT_SCTP_FRAGMENT_INTERLEAVE, 16). +%% -define(ESOCK_OPT_SCTP_GET_PEER_ADDR_INFO, 17). +-define(ESOCK_OPT_SCTP_INITMSG, 18). +%% -define(ESOCK_OPT_SCTP_I_WANT_MAPPED_V4_ADDR, 19). +%% -define(ESOCK_OPT_SCTP_LOCAL_AUTH_CHUNKS, 20). +-define(ESOCK_OPT_SCTP_MAXSEG, 21). +%% -define(ESOCK_OPT_SCTP_MAXBURST, 22). +-define(ESOCK_OPT_SCTP_NODELAY, 23). +%% -define(ESOCK_OPT_SCTP_PARTIAL_DELIVERY_POINT, 24). +%% -define(ESOCK_OPT_SCTP_PEER_ADDR_PARAMS, 25). +%% -define(ESOCK_OPT_SCTP_PEER_AUTH_CHUNKS, 26). +%% -define(ESOCK_OPT_SCTP_PRIMARY_ADDR, 27). +%% -define(ESOCK_OPT_SCTP_RESET_STREAMS, 28). +-define(ESOCK_OPT_SCTP_RTOINFO, 29). +%% -define(ESOCK_OPT_SCTP_SET_PEER_PRIMARY_ADDR, 30). +%% -define(ESOCK_OPT_SCTP_STATUS, 31). +%% -define(ESOCK_OPT_SCTP_USE_EXT_RECVINFO, 32). + +-define(ESOCK_SHUTDOWN_HOW_READ, 0). +-define(ESOCK_SHUTDOWN_HOW_WRITE, 1). +-define(ESOCK_SHUTDOWN_HOW_READ_WRITE, 2). + + +-define(ESOCK_SUPPORTS_OPTIONS, 16#0001). +-define(ESOCK_SUPPORTS_SCTP, 16#0002). +-define(ESOCK_SUPPORTS_IPV6, 16#0003). +-define(ESOCK_SUPPORTS_LOCAL, 16#0004). +-define(ESOCK_SUPPORTS_SEND_FLAGS, 16#0005). +-define(ESOCK_SUPPORTS_RECV_FLAGS, 16#0006). %% =========================================================================== @@ -949,7 +965,60 @@ on_load() -> Extra :: map(). on_load(Extra) -> - ok = erlang:load_nif(atom_to_list(?MODULE), Extra). + %% This is spawned as a system process to prevent init:restart/0 from + %% killing it. + Pid = erts_internal:spawn_system_process(?REGISTRY, start, []), + ok = erlang:load_nif(atom_to_list(?MODULE), Extra#{registry => Pid}). + + +%% *** number_of *** +%% +%% Interface function to the socket registry +%% returns the number of existing (and "alive") sockets. +%% +-spec number_of() -> non_neg_integer(). + +number_of() -> + ?REGISTRY:number_of(). + + +%% *** which_sockets/0,1 *** +%% +%% Interface function to the socket registry +%% Returns a list of all the sockets, accoring to the filter rule. +%% +-spec which_sockets() -> [socket()]. + +which_sockets() -> + ?REGISTRY:which_sockets(fun(_) -> true end). + +-spec which_sockets(FilterRule) -> [socket()] when + FilterRule :: inet | inet6 | + stream | dgram | seqpacket | + sctp | tcp | udp | + pid() | + fun((socket_info()) -> boolean()). + +which_sockets(Domain) + when ((Domain =:= inet) orelse (Domain =:= inet6)) -> + ?REGISTRY:which_sockets(fun(#{domain := D}) when (D =:= Domain) -> true; + (_) -> false end); +which_sockets(Type) + when ((Type =:= stream) orelse (Type =:= dgram) orelse (Type =:= seqpacket)) -> + ?REGISTRY:which_sockets(fun(#{type := T}) when (T =:= Type) -> true; + (_) -> false end); +which_sockets(Proto) + when ((Proto =:= sctp) orelse (Proto =:= tcp) orelse (Proto =:= udp)) -> + ?REGISTRY:which_sockets(fun(#{protocol := P}) when (P =:= Proto) -> true; + (_) -> false end); +which_sockets(CTRL) + when is_pid(CTRL) -> + ?REGISTRY:which_sockets(fun(#{ctrl := C}) when (C =:= CTRL) -> true; + (_) -> false end); +which_sockets(Filter) when is_function(Filter, 1) -> + ?REGISTRY:which_sockets(Filter). + + @@ -1036,7 +1105,7 @@ supports() -> {recv_flags, supports(recv_flags)}]. --dialyzer({nowarn_function, supports/1}). +-dialyzer({no_contracts, supports/1}). -spec supports(options) -> supports_options(); (sctp) -> boolean(); (ipv6) -> boolean(); @@ -1047,21 +1116,21 @@ supports() -> Key1 :: term(). supports(options) -> - nif_supports(?SOCKET_SUPPORTS_OPTIONS); + nif_supports(?ESOCK_SUPPORTS_OPTIONS); supports(sctp) -> - nif_supports(?SOCKET_SUPPORTS_SCTP); + nif_supports(?ESOCK_SUPPORTS_SCTP); supports(ipv6) -> - nif_supports(?SOCKET_SUPPORTS_IPV6); + nif_supports(?ESOCK_SUPPORTS_IPV6); supports(local) -> - nif_supports(?SOCKET_SUPPORTS_LOCAL); + nif_supports(?ESOCK_SUPPORTS_LOCAL); supports(send_flags) -> - nif_supports(?SOCKET_SUPPORTS_SEND_FLAGS); + nif_supports(?ESOCK_SUPPORTS_SEND_FLAGS); supports(recv_flags) -> - nif_supports(?SOCKET_SUPPORTS_RECV_FLAGS); + nif_supports(?ESOCK_SUPPORTS_RECV_FLAGS); supports(_Key1) -> false. --dialyzer({nowarn_function, supports/2}). +-dialyzer({no_contracts, supports/2}). -spec supports(options, socket) -> supports_options_socket(); (options, ip) -> supports_options_ip(); (options, ipv6) -> supports_options_ipv6(); @@ -1084,7 +1153,6 @@ supports(_Key1, _Level) -> false. --dialyzer({nowarn_function, supports/3}). -spec supports(options, socket, Opt :: socket_option()) -> boolean(); (options, ip, Opt :: ip_socket_option()) -> boolean(); (options, ipv6, Opt :: ipv6_socket_option()) -> boolean(); @@ -1096,6 +1164,7 @@ supports(_Key1, _Level) -> Key2 :: term(), Key3 :: term(). +-dialyzer({no_contracts, supports/3}). supports(options, Level, Opt) -> case supports(options, Level) of S when is_list(S) -> @@ -1192,25 +1261,19 @@ open(Domain, Type, Protocol, Extra) when is_map(Extra) -> try begin EDomain = enc_domain(Domain), - EType = enc_type(Domain, Type), - EProtocol = enc_protocol(Type, Protocol), - case nif_open(EDomain, EType, EProtocol, Extra) of - {ok, SockRef} -> - Socket = #socket{ref = SockRef}, - {ok, Socket}; - {error, _} = ERROR -> - ERROR - end + EType = enc_type(Type), + EProtocol = enc_protocol(Protocol), + nif_open(EDomain, EType, EProtocol, Extra) end + of + {ok, SockRef} -> + Socket = #socket{ref = SockRef}, + {ok, Socket}; + {error, _} = ERROR -> + ERROR catch - throw:T -> - T; - %% <WIN32-TEMPORARY> - error:notsup:S -> - erlang:raise(error, notsup, S); - %% </WIN32-TEMPORARY> - error:Reason -> - {error, Reason} + throw:ERROR -> + ERROR end. @@ -1232,31 +1295,23 @@ bind(#socket{ref = SockRef}, Addr) when ((Addr =:= any) orelse (Addr =:= broadcast) orelse (Addr =:= loopback)) -> - try which_domain(SockRef) of - inet -> - nif_bind(SockRef, ?SOCKADDR_IN4_DEFAULT(Addr)); - inet6 when (Addr =:= any) orelse (Addr =:= loopback) -> - nif_bind(SockRef, ?SOCKADDR_IN6_DEFAULT(Addr)); - _ -> - einval() + try + case which_domain(SockRef) of + inet -> + nif_bind(SockRef, ?SOCKADDR_IN4_DEFAULT(Addr)); + inet6 when (Addr =:= any) orelse (Addr =:= loopback) -> + nif_bind(SockRef, ?SOCKADDR_IN6_DEFAULT(Addr)); + Domain -> + invalid_domain(Domain) + end catch - %% <WIN32-TEMPORARY> - error:notsup:S -> - erlang:raise(error, notsup, S); - %% </WIN32-TEMPORARY> throw:ERROR -> ERROR end; bind(#socket{ref = SockRef} = _Socket, Addr) when is_map(Addr) -> try - begin - nif_bind(SockRef, ensure_sockaddr(Addr)) - end + nif_bind(SockRef, ensure_sockaddr(Addr)) catch - %% <WIN32-TEMPORARY> - error:notsup:S -> - erlang:raise(error, notsup, S); - %% </WIN32-TEMPORARY> throw:ERROR -> ERROR end. @@ -1285,35 +1340,28 @@ bind(#socket{ref = SockRef}, Addrs, Action) when is_list(Addrs) andalso ((Action =:= add) orelse (Action =:= remove)) -> try begin - ensure_type(SockRef, seqpacket), - ensure_proto(SockRef, sctp), - validate_addrs(which_domain(SockRef), Addrs), + {Domain, Type, Proto} = which_dtp(SockRef), + ensure_domain(Domain, [inet, inet6]), + ensure_type(Type, seqpacket), + ensure_protocol(Proto, sctp), + validate_addrs(Domain, Addrs), nif_bind(SockRef, Addrs, Action) end catch - %% <WIN32-TEMPORARY> - error:notsup:S -> - erlang:raise(error, notsup, S); - %% </WIN32-TEMPORARY> throw:ERROR -> ERROR end. -ensure_type(SockRef, Type) -> - case which_type(SockRef) of - Type -> - ok; - _InvalidType -> - einval() - end. +ensure_domain(Domain, [Domain | _]) -> ok; +ensure_domain(Domain, [_ | Domains]) -> ensure_domain(Domain, Domains); +ensure_domain(Domain, []) -> invalid_domain(Domain). + +ensure_type(Type, Type) -> ok; +ensure_type(Type, _) -> invalid_type(Type). + +ensure_protocol(Proto, Proto) -> ok; +ensure_protocol(Proto, _) -> invalid_protocol(Proto). -ensure_proto(SockRef, Proto) -> - case which_protocol(SockRef) of - Proto -> - ok; - _InvalidProto -> - einval() - end. validate_addrs(inet = _Domain, Addrs) -> validate_inet_addrs(Addrs); @@ -1373,38 +1421,45 @@ connect(Socket, SockAddr) -> %% <KOLLA> %% Is it possible to connect with family = local for the (dest) sockaddr? %% </KOLLA> -connect(_Socket, _SockAddr, Timeout) - when (is_integer(Timeout) andalso (Timeout =< 0)) -> - {error, timeout}; -connect(#socket{ref = SockRef}, #{family := Fam} = SockAddr, Timeout) - when ((Fam =:= inet) orelse (Fam =:= inet6) orelse (Fam =:= local)) andalso - ((Timeout =:= nowait) orelse - (Timeout =:= infinity) orelse is_integer(Timeout)) -> - TS = timestamp(Timeout), +connect(#socket{ref = SockRef}, SockAddr, Timeout) -> + try + do_connect(SockRef, ensure_sockaddr(SockAddr), deadline(Timeout)) + catch + throw:ERROR -> + ERROR + end. + + +do_connect(SockRef, SockAddr, Deadline) -> case nif_connect(SockRef, ensure_sockaddr(SockAddr)) of + ok -> %% Connected! ok; - {ok, Ref} when (Timeout =:= nowait) -> + + {ok, Ref} when (Deadline =:= nowait) -> %% Connecting, but the caller does not want to wait... ?SELECT(connect, Ref); {ok, Ref} -> %% Connecting... - NewTimeout = next_timeout(TS, Timeout), - receive - {?SOCKET_TAG, #socket{ref = SockRef}, select, Ref} -> - nif_finalize_connection(SockRef) - after NewTimeout -> + Timeout = timeout(Deadline), + receive + {?ESOCK_TAG, #socket{ref = SockRef}, select, Ref} -> + nif_finalize_connection(SockRef) + after Timeout -> cancel(SockRef, connect, Ref), - {error, timeout} - end; - {error, _} = ERROR -> - ERROR + {error, timeout} + end; + + + {error, _} = ERROR -> + ERROR end. + %% =========================================================================== %% %% listen - listen for connections on a socket @@ -1415,7 +1470,7 @@ connect(#socket{ref = SockRef}, #{family := Fam} = SockAddr, Timeout) Reason :: term(). listen(Socket) -> - listen(Socket, ?SOCKET_LISTEN_BACKLOG_DEFAULT). + listen(Socket, ?ESOCK_LISTEN_BACKLOG_DEFAULT). -spec listen(Socket, Backlog) -> ok | {error, Reason} when Socket :: socket(), @@ -1440,7 +1495,7 @@ listen(#socket{ref = SockRef}, Backlog) Reason :: term(). accept(Socket) -> - accept(Socket, ?SOCKET_ACCEPT_TIMEOUT_DEFAULT). + accept(Socket, ?ESOCK_ACCEPT_TIMEOUT_DEFAULT). -spec accept(LSocket, nowait) -> {ok, Socket} | @@ -1456,45 +1511,44 @@ accept(Socket) -> Socket :: socket(), Reason :: term(). -%% Do we really need this optimization? -accept(_, Timeout) when is_integer(Timeout) andalso (Timeout =< 0) -> - {error, timeout}; -accept(#socket{ref = LSockRef}, Timeout) - when is_integer(Timeout) orelse - (Timeout =:= infinity) orelse - (Timeout =:= nowait) -> - do_accept(LSockRef, Timeout). - -do_accept(LSockRef, Timeout) -> - TS = timestamp(Timeout), +accept(#socket{ref = LSockRef}, Timeout) -> + try + do_accept(LSockRef, deadline(Timeout)) + catch + throw:ERROR -> + ERROR + end. + +do_accept(LSockRef, Deadline) -> AccRef = make_ref(), case nif_accept(LSockRef, AccRef) of + {ok, SockRef} -> Socket = #socket{ref = SockRef}, {ok, Socket}; - {error, eagain} when (Timeout =:= nowait) -> + {error, eagain} when (Deadline =:= nowait) -> ?SELECT(accept, AccRef); - {error, eagain} -> %% Each call is non-blocking, but even then it takes %% *some* time, so just to be sure, recalculate before %% the receive. - NewTimeout = next_timeout(TS, Timeout), + Timeout = timeout(Deadline), receive - {?SOCKET_TAG, #socket{ref = LSockRef}, select, AccRef} -> - do_accept(LSockRef, next_timeout(TS, Timeout)); + {?ESOCK_TAG, #socket{ref = LSockRef}, select, AccRef} -> + do_accept(LSockRef, Deadline); - {?SOCKET_TAG, _Socket, abort, {AccRef, Reason}} -> + {?ESOCK_TAG, _Socket, abort, {AccRef, Reason}} -> {error, Reason} - after NewTimeout -> + after Timeout -> cancel(LSockRef, accept, AccRef), {error, timeout} end; + {error, _} = ERROR -> cancel(LSockRef, accept, AccRef), % Just to be on the safe side... ERROR @@ -1513,32 +1567,34 @@ do_accept(LSockRef, Timeout) -> Reason :: term(). send(Socket, Data) -> - send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT). + send(Socket, Data, ?ESOCK_SEND_FLAGS_DEFAULT, ?ESOCK_SEND_TIMEOUT_DEFAULT). -spec send(Socket, Data, Flags) -> ok | {error, Reason} when Socket :: socket(), Data :: iodata(), Flags :: send_flags(), - Reason :: term() - ; (Socket, Data, Timeout :: nowait) -> ok | - {select, SelectInfo} | - {ok, {RestData, SelectInfo}} | - {error, Reason} when + Reason :: term(); + (Socket, Data, Timeout :: nowait) -> + ok | + {ok, {binary(), SelectInfo}} | + {select, SelectInfo} | + {ok, {RestData, SelectInfo}} | + {error, Reason} when Socket :: socket(), Data :: iodata(), RestData :: binary(), SelectInfo :: select_info(), - Reason :: term() - ; (Socket, Data, Timeout) -> ok | {error, Reason} when + Reason :: term(); + (Socket, Data, Timeout) -> ok | {error, Reason} when Socket :: socket(), Data :: iodata(), Timeout :: timeout(), Reason :: term(). send(Socket, Data, Flags) when is_list(Flags) -> - send(Socket, Data, Flags, ?SOCKET_SEND_TIMEOUT_DEFAULT); + send(Socket, Data, Flags, ?ESOCK_SEND_TIMEOUT_DEFAULT); send(Socket, Data, Timeout) -> - send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, Timeout). + send(Socket, Data, ?ESOCK_SEND_FLAGS_DEFAULT, Timeout). -spec send(Socket, Data, Flags, nowait) -> ok | {select, SelectInfo} | @@ -1561,75 +1617,95 @@ send(Socket, Data, Flags, Timeout) when is_list(Data) -> Bin = erlang:list_to_binary(Data), send(Socket, Bin, Flags, Timeout); send(#socket{ref = SockRef}, Data, Flags, Timeout) - when is_binary(Data) andalso - is_list(Flags) andalso - ((Timeout =:= nowait) orelse - (Timeout =:= infinity) orelse - (is_integer(Timeout) andalso (Timeout > 0))) -> - EFlags = enc_send_flags(Flags), - do_send(SockRef, Data, EFlags, Timeout). - -do_send(SockRef, Data, EFlags, Timeout) -> - TS = timestamp(Timeout), + when is_binary(Data), is_list(Flags) -> + To = undefined, + try + begin + EFlags = enc_send_flags(Flags), + Deadline = deadline(Timeout), + send_common(SockRef, Data, To, EFlags, Deadline, send) + end + catch + throw:ERROR -> + ERROR + end. + +send_common(SockRef, Data, To, EFlags, Deadline, SendName) -> + SendRef = make_ref(), - case nif_send(SockRef, SendRef, Data, EFlags) of - ok -> - ok; + case + case SendName of + send -> + nif_send(SockRef, SendRef, Data, EFlags); + sendto -> + nif_sendto(SockRef, SendRef, Data, To, EFlags) + end + of - {ok, Written} when (Timeout =:= nowait) -> - <<_:Written/binary, Rest/binary>> = Data, + ok -> ok; + + + {ok, Written} when (Deadline =:= nowait) -> %% We are partially done, but the user don't want to wait (here) %% for completion - {ok, {Rest, ?SELECT_INFO(send, SendRef)}}; - + <<_:Written/binary, Rest/binary>> = Data, + {ok, {Rest, ?SELECT_INFO(SendName, SendRef)}}; {ok, Written} -> - NewTimeout = next_timeout(TS, Timeout), %% We are partially done, wait for continuation + Timeout = timeout(Deadline), receive - {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} + {?ESOCK_TAG, #socket{ref = SockRef}, select, SendRef} when (Written > 0) -> <<_:Written/binary, Rest/binary>> = Data, - do_send(SockRef, Rest, EFlags, - next_timeout(TS, Timeout)); + send_common( + SockRef, Rest, To, EFlags, Deadline, SendName); - {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} -> - do_send(SockRef, Data, EFlags, - next_timeout(TS, Timeout)); + {?ESOCK_TAG, #socket{ref = SockRef}, select, SendRef} -> + send_common( + SockRef, Data, To, EFlags, Deadline, SendName); - {?SOCKET_TAG, _Socket, abort, {SendRef, Reason}} -> - {error, Reason} + {?ESOCK_TAG, _Socket, abort, {SendRef, Reason}} -> + {error, {Reason, size(Data)}} - after NewTimeout -> - cancel(SockRef, send, SendRef), + after Timeout -> + _ = cancel(SockRef, SendName, SendRef), {error, {timeout, size(Data)}} end; - {error, eagain} when (Timeout =:= nowait) -> - ?SELECT(send, SendRef); + {error, exbusy} = Error when Deadline =:= nowait -> Error; + + {error, exbusy = Reason} -> + %% Internal error: + %% we called send, got eagain, and called send again + %% - without waiting for select message + erlang:error(Reason); + + {error, eagain} when (Deadline =:= nowait) -> + ?SELECT(SendName, SendRef); {error, eagain} -> + Timeout = timeout(Deadline), receive - {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} -> - do_send(SockRef, Data, EFlags, - next_timeout(TS, Timeout)); + {?ESOCK_TAG, #socket{ref = SockRef}, select, SendRef} -> + send_common( + SockRef, Data, To, EFlags, Deadline, SendName); - {?SOCKET_TAG, _Socket, abort, {SendRef, Reason}} -> - {error, Reason} + {?ESOCK_TAG, _Socket, abort, {SendRef, Reason}} -> + {error, {Reason, size(Data)}} after Timeout -> - cancel(SockRef, send, SendRef), + _ = cancel(SockRef, SendName, SendRef), {error, {timeout, size(Data)}} end; - {error, _} = ERROR -> - ERROR - end. - + {error, Reason} -> + {error, {Reason, size(Data)}} + end. %% --------------------------------------------------------------------------- @@ -1643,7 +1719,7 @@ do_send(SockRef, Data, EFlags, Timeout) -> Reason :: term(). sendto(Socket, Data, Dest) -> - sendto(Socket, Data, Dest, ?SOCKET_SENDTO_FLAGS_DEFAULT). + sendto(Socket, Data, Dest, ?ESOCK_SENDTO_FLAGS_DEFAULT). -spec sendto(Socket, Data, Dest, Flags) -> ok | {error, Reason} when Socket :: socket(), @@ -1667,14 +1743,16 @@ sendto(Socket, Data, Dest) -> Reason :: term(). sendto(Socket, Data, Dest, Flags) when is_list(Flags) -> - sendto(Socket, Data, Dest, Flags, ?SOCKET_SENDTO_TIMEOUT_DEFAULT); + sendto(Socket, Data, Dest, Flags, ?ESOCK_SENDTO_TIMEOUT_DEFAULT); sendto(Socket, Data, Dest, Timeout) -> - sendto(Socket, Data, Dest, ?SOCKET_SENDTO_FLAGS_DEFAULT, Timeout). + sendto(Socket, Data, Dest, ?ESOCK_SENDTO_FLAGS_DEFAULT, Timeout). --spec sendto(Socket, Data, Dest, Flags, nowait) -> ok | - {select, SelectInfo} | - {error, Reason} when +-spec sendto(Socket, Data, Dest, Flags, nowait) -> + ok | + {ok, {binary(), SelectInfo}} | + {select, SelectInfo} | + {error, Reason} when Socket :: socket(), Data :: binary(), Dest :: sockaddr(), @@ -1692,75 +1770,21 @@ sendto(Socket, Data, Dest, Timeout) -> sendto(Socket, Data, Dest, Flags, Timeout) when is_list(Data) -> Bin = erlang:list_to_binary(Data), sendto(Socket, Bin, Dest, Flags, Timeout); -sendto(#socket{ref = SockRef}, Data, #{family := Fam} = Dest, Flags, Timeout) - when is_binary(Data) andalso - ((Fam =:= inet) orelse (Fam =:= inet6) orelse (Fam =:= local)) andalso - is_list(Flags) andalso - ((Timeout =:= nowait) orelse - (Timeout =:= infinity) orelse - (is_integer(Timeout) andalso (Timeout > 0))) -> - EFlags = enc_send_flags(Flags), - do_sendto(SockRef, Data, ensure_sockaddr(Dest), EFlags, Timeout). - -do_sendto(SockRef, Data, Dest, EFlags, Timeout) -> - TS = timestamp(Timeout), - SendRef = make_ref(), - case nif_sendto(SockRef, SendRef, Data, Dest, EFlags) of - ok -> - %% We are done - ok; - - {ok, Written} when (Timeout =:= nowait) -> - <<_:Written/binary, Rest/binary>> = Data, - {ok, {Rest, ?SELECT_INFO(sendto, SendRef)}}; - - - {ok, Written} -> - %% We are partially done, wait for continuation - receive - {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} - when (Written > 0) -> - <<_:Written/binary, Rest/binary>> = Data, - do_sendto(SockRef, Rest, Dest, EFlags, - next_timeout(TS, Timeout)); - - {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} -> - do_sendto(SockRef, Data, Dest, EFlags, - next_timeout(TS, Timeout)); - - {?SOCKET_TAG, _Socket, abort, {SendRef, Reason}} -> - {error, Reason} - - after Timeout -> - cancel(SockRef, sendto, SendRef), - {error, timeout} - end; - - - {error, eagain} when (Timeout =:= nowait) -> - ?SELECT(sendto, SendRef); - - - {error, eagain} -> - receive - {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} -> - do_sendto(SockRef, Data, Dest, EFlags, - next_timeout(TS, Timeout)); - - {?SOCKET_TAG, _Socket, abort, {SendRef, Reason}} -> - {error, Reason} - - after Timeout -> - cancel(SockRef, sendto, SendRef), - {error, timeout} - end; - - {error, _} = ERROR -> +sendto(#socket{ref = SockRef}, Data, Dest, Flags, Timeout) + when is_binary(Data), is_list(Flags) -> + try + begin + To = ensure_sockaddr(Dest), + EFlags = enc_send_flags(Flags), + Deadline = deadline(Timeout), + send_common(SockRef, Data, To, EFlags, Deadline, sendto) + end + catch + throw:ERROR -> ERROR end. - %% --------------------------------------------------------------------------- %% %% The only part of the msghdr() that *must* exist (a connected @@ -1769,51 +1793,53 @@ do_sendto(SockRef, Data, Dest, EFlags, Timeout) -> %% used when sending. %% --spec sendmsg(Socket, MsgHdr) -> ok | {error, Reason} when +-spec sendmsg(Socket, MsgHdr) -> + ok | + {ok, Remaining} | + {error, Reason} when Socket :: socket(), MsgHdr :: msghdr(), + Remaining :: erlang:iovec(), Reason :: term(). sendmsg(Socket, MsgHdr) -> sendmsg(Socket, MsgHdr, - ?SOCKET_SENDMSG_FLAGS_DEFAULT, ?SOCKET_SENDMSG_TIMEOUT_DEFAULT). + ?ESOCK_SENDMSG_FLAGS_DEFAULT, ?ESOCK_SENDMSG_TIMEOUT_DEFAULT). -spec sendmsg(Socket, MsgHdr, Flags) -> ok | {error, Reason} when Socket :: socket(), MsgHdr :: msghdr(), Flags :: send_flags(), - Reason :: term() - ; (Socket, MsgHdr, Timeout :: nowait) -> ok | - {select, SelectInfo} | - {error, Reason} when + Reason :: term(); + (Socket, MsgHdr, Timeout :: nowait) -> + ok | + {ok, Remaining} | + {error, Reason} when Socket :: socket(), MsgHdr :: msghdr(), - SelectInfo :: select_info(), - Reason :: term() - ; (Socket, MsgHdr, Timeout) -> ok | {error, Reason} when + Remaining :: erlang:iovec(), + Reason :: term(); + (Socket, MsgHdr, Timeout) -> ok | {error, Reason} when Socket :: socket(), MsgHdr :: msghdr(), Timeout :: timeout(), Reason :: term(). sendmsg(Socket, MsgHdr, Flags) when is_list(Flags) -> - sendmsg(Socket, MsgHdr, Flags, ?SOCKET_SENDMSG_TIMEOUT_DEFAULT); -sendmsg(Socket, MsgHdr, Timeout) - when is_integer(Timeout) orelse (Timeout =:= infinity) -> - sendmsg(Socket, MsgHdr, ?SOCKET_SENDMSG_FLAGS_DEFAULT, Timeout). + sendmsg(Socket, MsgHdr, Flags, ?ESOCK_SENDMSG_TIMEOUT_DEFAULT); +sendmsg(Socket, MsgHdr, Timeout) -> + sendmsg(Socket, MsgHdr, ?ESOCK_SENDMSG_FLAGS_DEFAULT, Timeout). -spec sendmsg(Socket, MsgHdr, Flags, nowait) -> ok | {ok, Remaining} | - {select, SelectInfo} | {error, Reason} when Socket :: socket(), MsgHdr :: msghdr(), Flags :: send_flags(), Remaining :: erlang:iovec(), - SelectInfo :: select_info(), Reason :: term() ; (Socket, MsgHdr, Flags, Timeout) -> ok | @@ -1827,25 +1853,24 @@ sendmsg(Socket, MsgHdr, Timeout) Reason :: term(). sendmsg(#socket{ref = SockRef}, #{iov := IOV} = MsgHdr, Flags, Timeout) - when is_list(IOV) andalso - is_list(Flags) andalso - ((Timeout =:= nowait) orelse - (Timeout =:= infinity) orelse - (is_integer(Timeout) andalso (Timeout > 0))) -> - try ensure_msghdr(MsgHdr) of - M -> + when is_list(IOV), is_list(Flags) -> + try + begin + M = ensure_msghdr(MsgHdr), EFlags = enc_send_flags(Flags), - do_sendmsg(SockRef, M, EFlags, Timeout) + Deadline = deadline(Timeout), + do_sendmsg(SockRef, M, EFlags, Deadline) + end catch - throw:T -> - T; - error:Reason -> - {error, Reason} + throw:ERROR -> + ERROR end. -do_sendmsg(SockRef, MsgHdr, EFlags, Timeout) -> - TS = timestamp(Timeout), + +do_sendmsg(SockRef, MsgHdr, EFlags, Deadline) -> + SendRef = make_ref(), + case nif_sendmsg(SockRef, SendRef, MsgHdr, EFlags) of ok -> %% We are done @@ -1857,28 +1882,39 @@ do_sendmsg(SockRef, MsgHdr, EFlags, Timeout) -> %% be able to handle a message being split. Leave it to %% the caller to figure out (call again with the rest). %% - %% We should really not need to cancel, since this is - %% accepted for sendmsg! + %% We need to cancel this partial write. %% - cancel(SockRef, sendmsg, SendRef), + _ = cancel(SockRef, sendmsg, SendRef), {ok, do_sendmsg_rest(maps:get(iov, MsgHdr), Written)}; - {error, eagain} when (Timeout =:= nowait) -> + {error, exbusy} = Error when Deadline =:= nowait -> Error; + + {error, exbusy = Reason} -> + %% Internal error: + %% we called send, got eagain, and called send again + %% - without waiting for select message + erlang:error(Reason); + + + {error, eagain} when (Deadline =:= nowait) -> ?SELECT(sendmsg, SendRef); - {error, eagain} -> + Timeout = timeout(Deadline), receive - {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} -> - do_sendmsg(SockRef, MsgHdr, EFlags, - next_timeout(TS, Timeout)) + {?ESOCK_TAG, #socket{ref = SockRef}, select, SendRef} -> + do_sendmsg(SockRef, MsgHdr, EFlags, Deadline); + + {?ESOCK_TAG, _Socket, abort, {SendRef, Reason}} -> + {error, Reason} after Timeout -> - cancel(SockRef, sendmsg, SendRef), + _ = cancel(SockRef, sendmsg, SendRef), {error, timeout} end; + {error, _} = ERROR -> ERROR end. @@ -1902,7 +1938,6 @@ ensure_msghdr(_) -> - %% =========================================================================== %% %% recv, recvfrom, recvmsg - receive a message from a socket @@ -1942,8 +1977,8 @@ recv(Socket) -> recv(Socket, Length) -> recv(Socket, Length, - ?SOCKET_RECV_FLAGS_DEFAULT, - ?SOCKET_RECV_TIMEOUT_DEFAULT). + ?ESOCK_RECV_FLAGS_DEFAULT, + ?ESOCK_RECV_TIMEOUT_DEFAULT). -spec recv(Socket, Length, Flags) -> {ok, Data} | {error, Reason} when @@ -1970,9 +2005,9 @@ recv(Socket, Length) -> Reason :: term(). recv(Socket, Length, Flags) when is_list(Flags) -> - recv(Socket, Length, Flags, ?SOCKET_RECV_TIMEOUT_DEFAULT); + recv(Socket, Length, Flags, ?ESOCK_RECV_TIMEOUT_DEFAULT); recv(Socket, Length, Timeout) -> - recv(Socket, Length, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout). + recv(Socket, Length, ?ESOCK_RECV_FLAGS_DEFAULT, Timeout). -spec recv(Socket, Length, Flags, nowait) -> {ok, Data} | {select, SelectInfo} | @@ -1994,154 +2029,134 @@ recv(Socket, Length, Timeout) -> Reason :: term(). recv(#socket{ref = SockRef}, Length, Flags, Timeout) - when (is_integer(Length) andalso (Length >= 0)) andalso - is_list(Flags) andalso - (is_integer(Timeout) orelse - (Timeout =:= infinity) orelse - (Timeout =:= nowait)) -> - EFlags = enc_recv_flags(Flags), - do_recv(SockRef, undefined, Length, EFlags, <<>>, Timeout). - -%% We need to pass the "old recv ref" around because of the special case -%% with Length = 0. This case makes it neccessary to have a timeout function -%% clause since we may never wait for anything (no receive select), and so the -%% the only timeout check will be the function clause. -%% Note that the Timeout value of 'nowait' has a special meaning. It means + when is_integer(Length), Length >= 0, is_list(Flags) -> + try + EFlags = enc_recv_flags(Flags), + Deadline = deadline(Timeout), + do_recv(SockRef, Length, EFlags, Deadline, <<>>) + catch + throw:ERROR -> + ERROR + end. + +%% We will only recurse with Length == 0 if Length is 0, +%% so Length == 0 means to return all available data also when recursing +%% +%% Note that the Deadline value of 'nowait' has a special meaning. It means %% that we will either return with data or with the with {error, NNNN}. In %% wich case the caller will receive a select message at some later time. -do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) - when (Timeout =:= nowait) orelse - (Timeout =:= infinity) orelse - (is_integer(Timeout) andalso (Timeout > 0)) -> - TS = timestamp(Timeout), +%% +do_recv(SockRef, Length, EFlags, Deadline, Acc) -> + RecvRef = make_ref(), case nif_recv(SockRef, RecvRef, Length, EFlags) of - {ok, true = _Complete, Bin} when (size(Acc) =:= 0) -> - {ok, Bin}; + {ok, true = _Complete, Bin} -> - {ok, <<Acc/binary, Bin/binary>>}; + {ok, bincat(Acc, Bin)}; + %% It depends on the amount of bytes we tried to read: %% 0 - Read everything available %% We got something, but there may be more - keep reading. %% > 0 - We got a part of the message and we will be notified %% when there is more to read (a select message) - {ok, false = _Complete, Bin} when (Length =:= 0) -> - do_recv(SockRef, RecvRef, - Length, EFlags, - <<Acc/binary, Bin/binary>>, - next_timeout(TS, Timeout)); - + {ok, false = _Complete, Bin} when Length =:= 0 -> + Timeout = timeout(Deadline), + if + 0 < Timeout -> + do_recv( + SockRef, Length, EFlags, Deadline, bincat(Acc, Bin)); + true -> + {ok, bincat(Acc, Bin)} + end; %% Did not get all the user asked for, but the user also %% specified 'nowait', so deliver what we got and the %% select info. - {ok, false = _Completed, Bin} when (Timeout =:= nowait) andalso - (size(Acc) =:= 0) -> - {ok, {Bin, ?SELECT_INFO(recv, RecvRef)}}; + {ok, false = _Completed, Bin} when Deadline =:= nowait -> + {ok, {bincat(Acc, Bin), ?SELECT_INFO(recv, RecvRef)}}; - - {ok, false = _Completed, Bin} when (size(Acc) =:= 0) -> - %% We got the first chunk of it. - %% We will be notified (select message) when there - %% is more to read. - NewTimeout = next_timeout(TS, Timeout), + {ok, false = _Completed, Bin} -> + %% We got a chunk of it! + Timeout = timeout(Deadline), receive - {?SOCKET_TAG, #socket{ref = SockRef}, select, RecvRef} -> - do_recv(SockRef, RecvRef, - Length-size(Bin), EFlags, - Bin, - next_timeout(TS, Timeout)); - - {?SOCKET_TAG, _Socket, abort, {RecvRef, Reason}} -> + {?ESOCK_TAG, #socket{ref = SockRef}, select, RecvRef} -> + if + 0 < Timeout -> + do_recv( + SockRef, Length - byte_size(Bin), EFlags, + Deadline, bincat(Acc, Bin)); + true -> + {error, {timeout, bincat(Acc, Bin)}} + end; + + {?ESOCK_TAG, _Socket, abort, {RecvRef, Reason}} -> {error, Reason} - after NewTimeout -> + after Timeout -> cancel(SockRef, recv, RecvRef), - {error, {timeout, Acc}} + {error, {timeout, bincat(Acc, Bin)}} end; - {ok, false = _Completed, Bin} -> - %% We got a chunk of it! - NewTimeout = next_timeout(TS, Timeout), - receive - {?SOCKET_TAG, #socket{ref = SockRef}, select, RecvRef} -> - do_recv(SockRef, RecvRef, - Length-size(Bin), EFlags, - <<Acc/binary, Bin/binary>>, - next_timeout(TS, Timeout)); - {?SOCKET_TAG, _Socket, abort, {RecvRef, Reason}} -> - {error, Reason} + {error, exbusy} = Error when Deadline =:= nowait -> Error; - after NewTimeout -> - cancel(SockRef, recv, RecvRef), - {error, {timeout, Acc}} - end; + {error, exbusy = Reason} -> + %% Internal error: + %% we called recv, got eagain, and called recv again + %% - without waiting for select message + erlang:error(Reason); %% The user does not want to wait! %% The user will be informed that there is something to read %% via the select socket message (see below). - - {error, eagain} when (Timeout =:= nowait) andalso (size(Acc) =:= 0) -> - ?SELECT(recv, RecvRef); - {error, eagain} when (Timeout =:= nowait) -> - {ok, {Acc, ?SELECT_INFO(recv, RecvRef)}}; + {error, eagain} when Deadline =:= nowait -> + if + byte_size(Acc) =:= 0 -> + ?SELECT(recv, RecvRef); + true -> + {ok, {Acc, ?SELECT_INFO(recv, RecvRef)}} + end; %% We return with the accumulated binary (if its non-empty) - {error, eagain} when (Length =:= 0) andalso (size(Acc) > 0) -> - %% CAN WE REALLY DO THIS? THE NIF HAS SELECTED!! OR? + {error, eagain} when Length =:= 0, 0 < byte_size(Acc) -> + cancel(SockRef, recv, RecvRef), {ok, Acc}; {error, eagain} -> %% There is nothing just now, but we will be notified when there %% is something to read (a select message). - NewTimeout = next_timeout(TS, Timeout), + Timeout = timeout(Deadline), receive - {?SOCKET_TAG, #socket{ref = SockRef}, select, RecvRef} -> - do_recv(SockRef, RecvRef, - Length, EFlags, - Acc, - next_timeout(TS, Timeout)); - - {?SOCKET_TAG, _Socket, abort, {RecvRef, Reason}} -> + {?ESOCK_TAG, #socket{ref = SockRef}, select, RecvRef} -> + if + 0 < Timeout -> + do_recv( + SockRef, Length, EFlags, Deadline, Acc); + 0 < byte_size(Acc) -> + {error, {timeout, Acc}}; + true -> + {error, timeout} + end; + + {?ESOCK_TAG, _Socket, abort, {RecvRef, Reason}} -> {error, Reason} - after NewTimeout -> + after Timeout -> cancel(SockRef, recv, RecvRef), {error, timeout} end; - {error, closed = Reason} -> - do_close(SockRef), - if - (size(Acc) =:= 0) -> - {error, Reason}; - true -> - {error, {Reason, Acc}} - end; - {error, _} = ERROR when (size(Acc) =:= 0) -> + {error, _} = ERROR when byte_size(Acc) =:= 0 -> ERROR; {error, Reason} -> {error, {Reason, Acc}} - end; - -do_recv(SockRef, RecvRef, 0 = _Length, _Eflags, Acc, _Timeout) -> - %% The current recv operation is to be cancelled, so no need for a ref... - %% The cancel will end our 'read everything you have' and "activate" - %% any waiting reader. - cancel(SockRef, recv, RecvRef), - {ok, Acc}; -do_recv(_SockRef, _RecvRef, _Length, _EFlags, Acc, _Timeout) - when (size(Acc) > 0) -> - {error, {timeout, Acc}}; -do_recv(_SockRef, _RecvRef, _Length, _EFlags, _Acc, _Timeout) -> - {error, timeout}. + end. @@ -2177,8 +2192,8 @@ recvfrom(Socket) -> recvfrom(Socket, BufSz) -> recvfrom(Socket, BufSz, - ?SOCKET_RECV_FLAGS_DEFAULT, - ?SOCKET_RECV_TIMEOUT_DEFAULT). + ?ESOCK_RECV_FLAGS_DEFAULT, + ?ESOCK_RECV_TIMEOUT_DEFAULT). -spec recvfrom(Socket, Flags, nowait) -> {ok, {Source, Data}} | @@ -2229,9 +2244,9 @@ recvfrom(Socket, BufSz) -> recvfrom(Socket, Flags, Timeout) when is_list(Flags) -> recvfrom(Socket, 0, Flags, Timeout); recvfrom(Socket, BufSz, Flags) when is_list(Flags) -> - recvfrom(Socket, BufSz, Flags, ?SOCKET_RECV_TIMEOUT_DEFAULT); + recvfrom(Socket, BufSz, Flags, ?ESOCK_RECV_TIMEOUT_DEFAULT); recvfrom(Socket, BufSz, Timeout) -> - recvfrom(Socket, BufSz, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout). + recvfrom(Socket, BufSz, ?ESOCK_RECV_FLAGS_DEFAULT, Timeout). -spec recvfrom(Socket, BufSz, Flags, nowait) -> {ok, {Source, Data}} | @@ -2256,43 +2271,54 @@ recvfrom(Socket, BufSz, Timeout) -> Reason :: term(). recvfrom(#socket{ref = SockRef}, BufSz, Flags, Timeout) - when (is_integer(BufSz) andalso (BufSz >= 0)) andalso - is_list(Flags) andalso - (is_integer(Timeout) orelse - (Timeout =:= infinity) orelse - (Timeout =:= nowait)) -> - EFlags = enc_recv_flags(Flags), - do_recvfrom(SockRef, BufSz, EFlags, Timeout). - -do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> - TS = timestamp(Timeout), + when is_integer(BufSz), 0 =< BufSz, is_list(Flags) -> + try + EFlags = enc_recv_flags(Flags), + Deadline = deadline(Timeout), + do_recvfrom(SockRef, BufSz, EFlags, Deadline) + catch + throw:ERROR -> + ERROR + end. + +do_recvfrom(SockRef, BufSz, EFlags, Deadline) -> + RecvRef = make_ref(), case nif_recvfrom(SockRef, RecvRef, BufSz, EFlags) of + {ok, {_Source, _NewData}} = OK -> OK; - {error, eagain} when (Timeout =:= nowait) -> - ?SELECT(recvfrom, RecvRef); + {error, exbusy} = Error when Deadline =:= nowait -> Error; + + {error, exbusy = Reason} -> + %% Internal error: + %% we called recvfrom, got eagain, and called recvfrom again + %% - without waiting for select message + erlang:error(Reason); + {error, eagain} when Deadline =:= nowait -> + ?SELECT(recvfrom, RecvRef); + {error, eagain} -> %% There is nothing just now, but we will be notified when there %% is something to read (a select message). - NewTimeout = next_timeout(TS, Timeout), + Timeout = timeout(Deadline), receive - {?SOCKET_TAG, #socket{ref = SockRef}, select, RecvRef} -> - do_recvfrom(SockRef, BufSz, EFlags, - next_timeout(TS, Timeout)); + {?ESOCK_TAG, #socket{ref = SockRef}, select, RecvRef} -> + do_recvfrom(SockRef, BufSz, EFlags, Deadline); - {?SOCKET_TAG, _Socket, abort, {RecvRef, Reason}} -> + {?ESOCK_TAG, _Socket, abort, {RecvRef, Reason}} -> {error, Reason} - after NewTimeout -> + after Timeout -> cancel(SockRef, recvfrom, RecvRef), {error, timeout} end; + {error, _Reason} = ERROR -> ERROR @@ -2309,7 +2335,7 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> recvmsg(Socket) -> recvmsg(Socket, 0, 0, - ?SOCKET_RECV_FLAGS_DEFAULT, ?SOCKET_RECV_TIMEOUT_DEFAULT). + ?ESOCK_RECV_FLAGS_DEFAULT, ?ESOCK_RECV_TIMEOUT_DEFAULT). -spec recvmsg(Socket, Flags) -> {ok, MsgHdr} | {error, Reason} when Socket :: socket(), @@ -2330,9 +2356,9 @@ recvmsg(Socket) -> Reason :: term(). recvmsg(Socket, Flags) when is_list(Flags) -> - recvmsg(Socket, 0, 0, Flags, ?SOCKET_RECV_TIMEOUT_DEFAULT); + recvmsg(Socket, 0, 0, Flags, ?ESOCK_RECV_TIMEOUT_DEFAULT); recvmsg(Socket, Timeout) -> - recvmsg(Socket, 0, 0, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout). + recvmsg(Socket, 0, 0, ?ESOCK_RECV_FLAGS_DEFAULT, Timeout). -spec recvmsg(Socket, Flags, nowait) -> {ok, MsgHdr} | {select, SelectInfo} | @@ -2357,9 +2383,9 @@ recvmsg(Socket, Timeout) -> recvmsg(Socket, Flags, Timeout) when is_list(Flags) -> recvmsg(Socket, 0, 0, Flags, Timeout); -recvmsg(Socket, BufSz, CtrlSz) when is_integer(BufSz) andalso is_integer(CtrlSz) -> +recvmsg(Socket, BufSz, CtrlSz) when is_integer(BufSz), is_integer(CtrlSz) -> recvmsg(Socket, BufSz, CtrlSz, - ?SOCKET_RECV_FLAGS_DEFAULT, ?SOCKET_RECV_TIMEOUT_DEFAULT). + ?ESOCK_RECV_FLAGS_DEFAULT, ?ESOCK_RECV_TIMEOUT_DEFAULT). -spec recvmsg(Socket, @@ -2386,47 +2412,55 @@ recvmsg(Socket, BufSz, CtrlSz) when is_integer(BufSz) andalso is_integer(CtrlSz) Reason :: term(). recvmsg(#socket{ref = SockRef}, BufSz, CtrlSz, Flags, Timeout) - when (is_integer(BufSz) andalso (BufSz >= 0)) andalso - (is_integer(CtrlSz) andalso (CtrlSz >= 0)) andalso - is_list(Flags) andalso - (is_integer(Timeout) orelse - (Timeout =:= infinity) orelse - (Timeout =:= nowait)) -> - EFlags = enc_recv_flags(Flags), - do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout). - -do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout) -> - TS = timestamp(Timeout), + when is_integer(BufSz), 0 =< BufSz, + is_integer(CtrlSz), 0 =< CtrlSz, + is_list(Flags) -> + try + EFlags = enc_recv_flags(Flags), + Deadline = deadline(Timeout), + do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Deadline) + catch + throw:ERROR -> + ERROR + end. + +do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Deadline) -> + RecvRef = make_ref(), case nif_recvmsg(SockRef, RecvRef, BufSz, CtrlSz, EFlags) of + {ok, _MsgHdr} = OK -> OK; - {error, eagain} when (Timeout =:= nowait) -> - ?SELECT(recvmsg, RecvRef); + {error, exbusy} = Error when Deadline =:= nowait -> Error; + + {error, exbusy = Reason} -> + %% Internal error: + %% we called recvmsg, got eagain, and called recvmsg again + %% - without waiting for select message + erlang:error(Reason); + {error, eagain} when Deadline =:= nowait -> + ?SELECT(recvmsg, RecvRef); + {error, eagain} -> %% There is nothing just now, but we will be notified when there %% is something to read (a select message). - NewTimeout = next_timeout(TS, Timeout), + Timeout = timeout(Deadline), receive - {?SOCKET_TAG, #socket{ref = SockRef}, select, RecvRef} -> - do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, - next_timeout(TS, Timeout)); + {?ESOCK_TAG, #socket{ref = SockRef}, select, RecvRef} -> + do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Deadline); - {?SOCKET_TAG, _Socket, abort, {RecvRef, Reason}} -> + {?ESOCK_TAG, _Socket, abort, {RecvRef, Reason}} -> {error, Reason} - after NewTimeout -> + after Timeout -> cancel(SockRef, recvmsg, RecvRef), {error, timeout} end; - {error, closed} = ERROR -> - do_close(SockRef), - ERROR; {error, _Reason} = ERROR -> ERROR @@ -2435,7 +2469,6 @@ do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout) -> - %% =========================================================================== %% %% close - close a file descriptor @@ -2455,9 +2488,6 @@ do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout) -> Reason :: term(). close(#socket{ref = SockRef}) -> - do_close(SockRef). - -do_close(SockRef) -> case nif_close(SockRef) of ok -> nif_finalize_close(SockRef); @@ -2465,7 +2495,7 @@ do_close(SockRef) -> %% We must wait for the socket_stop callback function to %% complete its work receive - {?SOCKET_TAG, #socket{ref = SockRef}, close, CloseRef} -> + {?ESOCK_TAG, #socket{ref = SockRef}, close, CloseRef} -> nif_finalize_close(SockRef) end; {error, _} = ERROR -> @@ -2487,10 +2517,7 @@ do_close(SockRef) -> shutdown(#socket{ref = SockRef}, How) -> try - begin - EHow = enc_shutdown_how(How), - nif_shutdown(SockRef, EHow) - end + nif_shutdown(SockRef, enc_shutdown_how(How)) catch throw:T -> T; @@ -2559,23 +2586,15 @@ shutdown(#socket{ref = SockRef}, How) -> setopt(#socket{ref = SockRef}, Level, Key, Value) -> try begin - Domain = which_domain(SockRef), - Type = which_type(SockRef), - Protocol = which_protocol(SockRef), + {Domain, Type, Proto} = which_dtp(SockRef), {EIsEncoded, ELevel} = enc_setopt_level(Level), - EKey = enc_setopt_key(Level, Key, Domain, Type, Protocol), - EVal = enc_setopt_value(Level, Key, Value, Domain, Type, Protocol), + EKey = enc_setopt_key(Level, Key, Domain, Type, Proto), + EVal = enc_setopt_value(Level, Key, Value, Domain, Type, Proto), nif_setopt(SockRef, EIsEncoded, ELevel, EKey, EVal) end catch - throw:T -> - T; - %% <WIN32-TEMPORARY> - error:notsup:S -> - erlang:raise(error, notsup, S); - %% </WIN32-TEMPORARY> - error:Reason -> - {error, Reason} % Process more? + throw:ERROR -> + ERROR end. @@ -2635,33 +2654,26 @@ setopt(#socket{ref = SockRef}, Level, Key, Value) -> getopt(#socket{ref = SockRef}, Level, Key) -> try begin - Domain = which_domain(SockRef), - Type = which_type(SockRef), - Protocol = which_protocol(SockRef), + {Domain, Type, Proto} = which_dtp(SockRef), {EIsEncoded, ELevel} = enc_getopt_level(Level), - EKey = enc_getopt_key(Level, Key, Domain, Type, Protocol), + EKey = enc_getopt_key(Level, Key, Domain, Type, Proto), %% We may need to decode the value (for the same reason %% we (may have) needed to encode the value for setopt). case nif_getopt(SockRef, EIsEncoded, ELevel, EKey) of ok -> ok; {ok, EVal} -> - Val = dec_getopt_value(Level, Key, EVal, - Domain, Type, Protocol), + Val = + dec_getopt_value( + Level, Key, EVal, Domain, Type, Proto), {ok, Val}; - {error, _} = ERROR -> - ERROR + {error, _} = E -> + E end end catch - throw:E:_S -> - E; - %% <WIN32-TEMPORARY> - error:notsup:S -> - erlang:raise(error, notsup, S); - %% </WIN32-TEMPORARY> - error:Reason:_Stack -> - {error, Reason} % Process more? + throw:ERROR -> + ERROR end. @@ -2674,43 +2686,77 @@ getopt(#socket{ref = SockRef}, Level, Key) -> which_domain(SockRef) -> case nif_getopt(SockRef, true, - ?SOCKET_OPT_LEVEL_OTP, ?SOCKET_OPT_OTP_DOMAIN) of + ?ESOCK_OPT_LEVEL_OTP, ?ESOCK_OPT_OTP_DOMAIN) of {ok, Domain} -> - Domain; + if + is_atom(Domain) -> + Domain; + is_integer(Domain) -> + invalid_domain(Domain) + end; {error, _} = ERROR -> throw(ERROR) end. --spec which_type(SockRef) -> Type when - SockRef :: reference(), - Type :: type(). - -which_type(SockRef) -> - case nif_getopt(SockRef, true, - ?SOCKET_OPT_LEVEL_OTP, ?SOCKET_OPT_OTP_TYPE) of - {ok, Type} -> - Type; - {error, _} = ERROR -> - throw(ERROR) - end. - --spec which_protocol(SockRef) -> Protocol when - SockRef :: reference(), - Protocol :: protocol(). - -which_protocol(SockRef) -> - case nif_getopt(SockRef, true, - ?SOCKET_OPT_LEVEL_OTP, ?SOCKET_OPT_OTP_PROTOCOL) of - {ok, Proto} -> - Proto; +%%%-spec which_type(SockRef) -> Type when +%%% SockRef :: reference(), +%%% Type :: type(). +%%% +%%%which_type(SockRef) -> +%%% case nif_getopt(SockRef, true, +%%% ?ESOCK_OPT_LEVEL_OTP, ?ESOCK_OPT_OTP_TYPE) of +%%% {ok, Type} -> +%%% if +%%% is_atom(Type) -> +%%% Type; +%%% is_integer(Type) -> +%%% invalid_type(Type) +%%% end; +%%% {error, _} = ERROR -> +%%% throw(ERROR) +%%% end. +%%% +%%%-spec which_protocol(SockRef) -> Protocol when +%%% SockRef :: reference(), +%%% Protocol :: protocol(). +%%% +%%%which_protocol(SockRef) -> +%%% case nif_getopt(SockRef, true, +%%% ?ESOCK_OPT_LEVEL_OTP, ?ESOCK_OPT_OTP_PROTOCOL) of +%%% {ok, Proto} -> +%%% if +%%% is_atom(Proto) -> +%%% Proto; +%%% is_integer(Proto) -> +%%% invalid_protocol(Proto) +%%% end; +%%% {error, _} = ERROR -> +%%% throw(ERROR) +%%% end. + +which_dtp(SockRef) -> + case + nif_getopt( + SockRef, true, ?ESOCK_OPT_LEVEL_OTP, ?ESOCK_OPT_OTP_DTP) + of + {ok, {Domain, Type, Proto} = DTP} -> + if + is_integer(Domain) -> + invalid_domain(Domain); + is_integer(Type) -> + invalid_type(Type); + is_integer(Proto) -> + invalid_protocol(Proto); + is_atom(Domain), is_atom(Type), is_atom(Proto) -> + DTP + end; {error, _} = ERROR -> throw(ERROR) end. - %% =========================================================================== %% %% sockname - return the current address of the socket. @@ -2771,61 +2817,57 @@ cancel(#socket{ref = SockRef}, ?SELECT_INFO(Tag, Ref)) -> %% -spec enc_domain(Domain) -> non_neg_integer() when %% Domain :: domain(). -enc_domain(local) -> ?SOCKET_DOMAIN_LOCAL; -enc_domain(inet) -> ?SOCKET_DOMAIN_INET; -enc_domain(inet6) -> ?SOCKET_DOMAIN_INET6; +enc_domain(local) -> ?ESOCK_DOMAIN_LOCAL; +enc_domain(inet) -> ?ESOCK_DOMAIN_INET; +enc_domain(inet6) -> ?ESOCK_DOMAIN_INET6; enc_domain(Domain) -> invalid_domain(Domain). -%% -spec enc_type(Domain, Type) -> non_neg_integer() when -%% Domain :: domain(), +%% -spec enc_type(Type) -> non_neg_integer() when %% Type :: type(). -%% What combos are valid? -enc_type(_, stream) -> ?SOCKET_TYPE_STREAM; -enc_type(_, dgram) -> ?SOCKET_TYPE_DGRAM; -enc_type(_, raw) -> ?SOCKET_TYPE_RAW; -enc_type(_, seqpacket) -> ?SOCKET_TYPE_SEQPACKET; -enc_type(_, Type) -> invalid_type(Type). +enc_type(stream) -> ?ESOCK_TYPE_STREAM; +enc_type(dgram) -> ?ESOCK_TYPE_DGRAM; +enc_type(raw) -> ?ESOCK_TYPE_RAW; +enc_type(seqpacket) -> ?ESOCK_TYPE_SEQPACKET; +enc_type(Type) -> invalid_type(Type). --spec enc_protocol(Type, Protocol) -> non_neg_integer() | - {raw, non_neg_integer()} when - Type :: type(), +-spec enc_protocol(Protocol) -> non_neg_integer() | + {raw, non_neg_integer()} when Protocol :: protocol(). -enc_protocol(_, default) -> ?SOCKET_PROTOCOL_DEFAULT; -enc_protocol(dgram, ip) -> ?SOCKET_PROTOCOL_IP; -enc_protocol(stream, tcp) -> ?SOCKET_PROTOCOL_TCP; -enc_protocol(dgram, udp) -> ?SOCKET_PROTOCOL_UDP; -enc_protocol(seqpacket, sctp) -> ?SOCKET_PROTOCOL_SCTP; -enc_protocol(dgram, icmp) -> ?SOCKET_PROTOCOL_ICMP; -enc_protocol(raw, icmp) -> ?SOCKET_PROTOCOL_ICMP; -enc_protocol(raw, igmp) -> ?SOCKET_PROTOCOL_IGMP; -enc_protocol(raw, {raw, P} = RAW) when is_integer(P) -> RAW; -enc_protocol(Type, Proto) -> - invalid_protocol(Type, Proto). +enc_protocol(default) -> ?ESOCK_PROTOCOL_DEFAULT; +enc_protocol(ip) -> ?ESOCK_PROTOCOL_IP; +enc_protocol(tcp) -> ?ESOCK_PROTOCOL_TCP; +enc_protocol(udp) -> ?ESOCK_PROTOCOL_UDP; +enc_protocol(sctp) -> ?ESOCK_PROTOCOL_SCTP; +enc_protocol(icmp) -> ?ESOCK_PROTOCOL_ICMP; +enc_protocol(igmp) -> ?ESOCK_PROTOCOL_IGMP; +enc_protocol({raw, P} = RAW) when is_integer(P) -> RAW; +enc_protocol(Proto) -> + invalid_protocol(Proto). -spec enc_send_flags(Flags) -> non_neg_integer() when Flags :: send_flags(). enc_send_flags(Flags) -> - EFlags = [{confirm, ?SOCKET_SEND_FLAG_CONFIRM}, - {dontroute, ?SOCKET_SEND_FLAG_DONTROUTE}, - {eor, ?SOCKET_SEND_FLAG_EOR}, - {more, ?SOCKET_SEND_FLAG_MORE}, - {nosignal, ?SOCKET_SEND_FLAG_NOSIGNAL}, - {oob, ?SOCKET_SEND_FLAG_OOB}], + EFlags = [{confirm, ?ESOCK_SEND_FLAG_CONFIRM}, + {dontroute, ?ESOCK_SEND_FLAG_DONTROUTE}, + {eor, ?ESOCK_SEND_FLAG_EOR}, + {more, ?ESOCK_SEND_FLAG_MORE}, + {nosignal, ?ESOCK_SEND_FLAG_NOSIGNAL}, + {oob, ?ESOCK_SEND_FLAG_OOB}], enc_flags(Flags, EFlags). -spec enc_recv_flags(Flags) -> non_neg_integer() when Flags :: recv_flags(). enc_recv_flags(Flags) -> - EFlags = [{cmsg_cloexec, ?SOCKET_RECV_FLAG_CMSG_CLOEXEC}, - {errqueue, ?SOCKET_RECV_FLAG_ERRQUEUE}, - {oob, ?SOCKET_RECV_FLAG_OOB}, - {peek, ?SOCKET_RECV_FLAG_PEEK}, - {trunc, ?SOCKET_RECV_FLAG_TRUNC}], + EFlags = [{cmsg_cloexec, ?ESOCK_RECV_FLAG_CMSG_CLOEXEC}, + {errqueue, ?ESOCK_RECV_FLAG_ERRQUEUE}, + {oob, ?ESOCK_RECV_FLAG_OOB}, + {peek, ?ESOCK_RECV_FLAG_PEEK}, + {trunc, ?ESOCK_RECV_FLAG_TRUNC}], enc_flags(Flags, EFlags). @@ -2851,19 +2893,19 @@ enc_flags(Flags, EFlags) -> EncodedLevel :: integer(). enc_setopt_level(otp) -> - {true, ?SOCKET_OPT_LEVEL_OTP}; + {true, ?ESOCK_OPT_LEVEL_OTP}; enc_setopt_level(socket) -> - {true, ?SOCKET_OPT_LEVEL_SOCKET}; + {true, ?ESOCK_OPT_LEVEL_SOCKET}; enc_setopt_level(ip) -> - {true, ?SOCKET_OPT_LEVEL_IP}; + {true, ?ESOCK_OPT_LEVEL_IP}; enc_setopt_level(ipv6) -> - {true, ?SOCKET_OPT_LEVEL_IPV6}; + {true, ?ESOCK_OPT_LEVEL_IPV6}; enc_setopt_level(tcp) -> - {true, ?SOCKET_OPT_LEVEL_TCP}; + {true, ?ESOCK_OPT_LEVEL_TCP}; enc_setopt_level(udp) -> - {true, ?SOCKET_OPT_LEVEL_UDP}; + {true, ?ESOCK_OPT_LEVEL_UDP}; enc_setopt_level(sctp) -> - {true, ?SOCKET_OPT_LEVEL_SCTP}; + {true, ?ESOCK_OPT_LEVEL_SCTP}; %% Any option that is of an plain level must be provided as a binary %% already fully encoded! enc_setopt_level(L) when is_integer(L) -> @@ -2889,7 +2931,6 @@ enc_setopt_key(Level, Opt, Domain, Type, Protocol) -> %% encode the value into an more "manageable" type. %% It also handles "aliases" (see linger). --dialyzer({nowarn_function, enc_setopt_value/6}). -spec enc_setopt_value(otp, otp_socket_option(), Value, Domain, Type, Protocol) -> term() when Value :: term(), @@ -2965,6 +3006,8 @@ enc_setopt_value(otp, sndctrlbuf, V, _, _, _) when (V =:= default) -> 0; enc_setopt_value(otp, sndctrlbuf, V, _, _, _) when is_integer(V) andalso (V > 0) -> V; +enc_setopt_value(otp, meta, V, _, _, _) -> + V; enc_setopt_value(otp = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); @@ -3355,12 +3398,6 @@ enc_getopt_key(Level, Opt, Domain, Type, Protocol) -> %% For the most part, we simply let the value pass through, but for some %% values we may need to do an actual decode. %% -%% For some reason dialyzer thinks that the only valid value for Opt to -%% this function is otp_socket_option(). Of course without explaining -%% how it came to that conclusion... And since I know that to be false... -%% - --dialyzer({nowarn_function, dec_getopt_value/6}). %% This string is NULL-terminated, but the general function we use %% in the nif code does not know that. So, deal with it here. @@ -3377,78 +3414,77 @@ dec_getopt_value(_L, _Opt, V, _D, _T, _P) -> %% Most options are usable both for set and get, but some are %% are only available for e.g. get. --spec enc_sockopt_key(Level, Opt, - Direction, +-spec enc_sockopt_key(Level, Opt, Direction, Domain, Type, Protocol) -> non_neg_integer() when Level :: otp, Direction :: set | get, Opt :: otp_socket_option(), Domain :: domain(), Type :: type(), - Protocol :: protocol() - ; (Level, Direction, Opt, + Protocol :: protocol(); + (Level, Opt, Direction, Domain, Type, Protocol) -> non_neg_integer() when Level :: socket, Direction :: set | get, Opt :: socket_option(), Domain :: domain(), Type :: type(), - Protocol :: protocol() - ; (Level, Direction, Opt, + Protocol :: protocol(); + (Level, Opt, Direction, Domain, Type, Protocol) -> non_neg_integer() when Level :: ip, Direction :: set | get, Opt :: ip_socket_option(), Domain :: domain(), Type :: type(), - Protocol :: protocol() - ; (Level, Direction, Opt, + Protocol :: protocol(); + (Level, Opt, Direction, Domain, Type, Protocol) -> non_neg_integer() when Level :: ipv6, Direction :: set | get, Opt :: ipv6_socket_option(), Domain :: domain(), Type :: type(), - Protocol :: protocol() - ; (Level, Direction, Opt, - Domain, Type, Protocol) -> non_neg_integer() when + Protocol :: protocol(); + (Level, Opt, Direction, + Domain, Type, Protocol) -> non_neg_integer() when Level :: tcp, Direction :: set | get, Opt :: tcp_socket_option(), Domain :: domain(), Type :: type(), - Protocol :: protocol() - ; (Level, Direction, Opt, + Protocol :: protocol(); + (Level, Opt, Direction, Domain, Type, Protocol) -> non_neg_integer() when Level :: udp, Direction :: set | get, Opt :: udp_socket_option(), Domain :: domain(), Type :: type(), - Protocol :: protocol() - ; (Level, Direction, Opt, + Protocol :: protocol(); + (Level, Opt, Direction, Domain, Type, Protocol) -> non_neg_integer() when Level :: sctp, Direction :: set | get, Opt :: sctp_socket_option(), Domain :: domain(), Type :: type(), - Protocol :: protocol() - ; (Level, Direction, Opt, - Domain, Type, Protocol) -> non_neg_integer() when + Protocol :: protocol(); + (Level, Opt, Direction, + Domain, Type, Protocol) -> non_neg_integer() when Level :: integer(), Direction :: set, Opt :: integer(), Domain :: domain(), Type :: type(), - Protocol :: protocol() - ; (Level, Direction, Opt, - Domain, Type, Protocol) -> non_neg_integer() when + Protocol :: protocol(); + (Level, Opt, Direction, + Domain, Type, Protocol) -> {NativeOpt, ValueSize} when Level :: integer(), Direction :: get, Opt :: {NativeOpt, ValueSize}, NativeOpt :: integer(), - ValueSize :: non_neg_integer(), + ValueSize :: non_neg_integer() | 'int' | 'bool', Domain :: domain(), Type :: type(), Protocol :: protocol(). @@ -3456,25 +3492,27 @@ dec_getopt_value(_L, _Opt, V, _D, _T, _P) -> %% +++ OTP socket options +++ enc_sockopt_key(otp, debug, _, _, _, _) -> - ?SOCKET_OPT_OTP_DEBUG; + ?ESOCK_OPT_OTP_DEBUG; enc_sockopt_key(otp, iow, _, _, _, _) -> - ?SOCKET_OPT_OTP_IOW; + ?ESOCK_OPT_OTP_IOW; enc_sockopt_key(otp, controlling_process, _, _, _, _) -> - ?SOCKET_OPT_OTP_CTRL_PROC; + ?ESOCK_OPT_OTP_CTRL_PROC; enc_sockopt_key(otp, rcvbuf, _, _, _, _) -> - ?SOCKET_OPT_OTP_RCVBUF; + ?ESOCK_OPT_OTP_RCVBUF; enc_sockopt_key(otp, rcvctrlbuf, _, _, _, _) -> - ?SOCKET_OPT_OTP_RCVCTRLBUF; + ?ESOCK_OPT_OTP_RCVCTRLBUF; enc_sockopt_key(otp, sndctrlbuf, _, _, _, _) -> - ?SOCKET_OPT_OTP_SNDCTRLBUF; + ?ESOCK_OPT_OTP_SNDCTRLBUF; enc_sockopt_key(otp, fd, get = _Dir, _, _, _) -> - ?SOCKET_OPT_OTP_FD; + ?ESOCK_OPT_OTP_FD; +enc_sockopt_key(otp, meta, _, _, _, _) -> + ?ESOCK_OPT_OTP_META; enc_sockopt_key(otp = L, Opt, _, _, _, _) -> not_supported({L, Opt}); %% +++ SOCKET socket options +++ enc_sockopt_key(socket = _L, acceptconn = _Opt, get = _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_ACCEPTCONN; + ?ESOCK_OPT_SOCK_ACCEPTCONN; enc_sockopt_key(socket = L, acceptconn = Opt, Dir, _D, _T, _P) -> not_supported({L, Opt, Dir}); enc_sockopt_key(socket = L, acceptfilter = Opt, _Dir, _D, _T, _P) -> @@ -3483,182 +3521,182 @@ enc_sockopt_key(socket = L, acceptfilter = Opt, _Dir, _D, _T, _P) -> %% Maximum size of buffer for name: IFNAMSIZ %% So, we let the implementation decide. enc_sockopt_key(socket = _L, bindtodevice = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_BINDTODEVICE; + ?ESOCK_OPT_SOCK_BINDTODEVICE; enc_sockopt_key(socket, broadcast = _Opt, _Dir, _D, dgram = _T, _P) -> - ?SOCKET_OPT_SOCK_BROADCAST; + ?ESOCK_OPT_SOCK_BROADCAST; enc_sockopt_key(socket = L, busy_poll = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(socket = _L, debug = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_DEBUG; + ?ESOCK_OPT_SOCK_DEBUG; enc_sockopt_key(socket, domain = _Opt, get = _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_DOMAIN; + ?ESOCK_OPT_SOCK_DOMAIN; enc_sockopt_key(socket, dontroute = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_DONTROUTE; + ?ESOCK_OPT_SOCK_DONTROUTE; enc_sockopt_key(socket = L, error = Opt, get = _Dir, _D, _T, _P) -> not_supported({L, Opt}); %% This is only for connection-oriented sockets, but who are those? %% Type = stream or Protocol = tcp? %% For now, we just let is pass and it will fail later if not ok... enc_sockopt_key(socket, keepalive = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_KEEPALIVE; + ?ESOCK_OPT_SOCK_KEEPALIVE; enc_sockopt_key(socket, linger = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_LINGER; + ?ESOCK_OPT_SOCK_LINGER; enc_sockopt_key(socket = L, mark = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(socket = _L, oobinline = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_OOBINLINE; + ?ESOCK_OPT_SOCK_OOBINLINE; enc_sockopt_key(socket, passcred, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_PASSCRED; + ?ESOCK_OPT_SOCK_PASSCRED; enc_sockopt_key(socket = L, peek_off = Opt, _Dir, local = _D, _T, _P) -> - %% ?SOCKET_OPT_SOCK_PEEK_OFF; + %% ?ESOCK_OPT_SOCK_PEEK_OFF; not_supported({L, Opt}); enc_sockopt_key(socket = L, peercred = Opt, get = _Dir, local = _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(socket, priority = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_PRIORITY; + ?ESOCK_OPT_SOCK_PRIORITY; enc_sockopt_key(socket, protocol = _Opt, get = _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_PROTOCOL; + ?ESOCK_OPT_SOCK_PROTOCOL; enc_sockopt_key(socket, rcvbuf = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_RCVBUF; + ?ESOCK_OPT_SOCK_RCVBUF; enc_sockopt_key(socket = L, rcvbufforce = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); %% May not work on linux. enc_sockopt_key(socket = _L, rcvlowat = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_RCVLOWAT; + ?ESOCK_OPT_SOCK_RCVLOWAT; enc_sockopt_key(socket = _L, rcvtimeo = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_RCVTIMEO; + ?ESOCK_OPT_SOCK_RCVTIMEO; enc_sockopt_key(socket = _L, reuseaddr = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_REUSEADDR; + ?ESOCK_OPT_SOCK_REUSEADDR; enc_sockopt_key(socket = _L, reuseport = _Opt, _Dir, D, _T, _P) when ((D =:= inet) orelse (D =:= inet6)) -> - ?SOCKET_OPT_SOCK_REUSEPORT; + ?ESOCK_OPT_SOCK_REUSEPORT; enc_sockopt_key(socket = L, rxq_ovfl = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(socket = L, setfib = Opt, set = _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(socket = _L, sndbuf = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_SNDBUF; + ?ESOCK_OPT_SOCK_SNDBUF; enc_sockopt_key(socket = L, sndbufforce = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); %% Not changeable on linux. enc_sockopt_key(socket = _L, sndlowat = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_SNDLOWAT; + ?ESOCK_OPT_SOCK_SNDLOWAT; enc_sockopt_key(socket = _L, sndtimeo = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_SNDTIMEO; + ?ESOCK_OPT_SOCK_SNDTIMEO; enc_sockopt_key(socket = _L, timestamp = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_TIMESTAMP; + ?ESOCK_OPT_SOCK_TIMESTAMP; enc_sockopt_key(socket = _L, type = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SOCK_TYPE; + ?ESOCK_OPT_SOCK_TYPE; enc_sockopt_key(socket = L, UnknownOpt, _Dir, _D, _T, _P) -> unknown({L, UnknownOpt}); %% +++ IP socket options +++ enc_sockopt_key(ip = _L, add_membership = _Opt, set = _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_ADD_MEMBERSHIP; + ?ESOCK_OPT_IP_ADD_MEMBERSHIP; enc_sockopt_key(ip = _L, add_source_membership = _Opt, set = _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP; + ?ESOCK_OPT_IP_ADD_SOURCE_MEMBERSHIP; enc_sockopt_key(ip = _L, block_source = _Opt, set = _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_BLOCK_SOURCE; + ?ESOCK_OPT_IP_BLOCK_SOURCE; %% FreeBSD only? %% Only respected on udp and raw ip (unless the hdrincl option has been set). enc_sockopt_key(ip = L, dontfrag = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip = _L, drop_membership = _Opt, set = _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_DROP_MEMBERSHIP; + ?ESOCK_OPT_IP_DROP_MEMBERSHIP; enc_sockopt_key(ip = _L, drop_source_membership = _Opt, set = _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP; + ?ESOCK_OPT_IP_DROP_SOURCE_MEMBERSHIP; %% Linux only? enc_sockopt_key(ip = _L, freebind = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_FREEBIND; + ?ESOCK_OPT_IP_FREEBIND; enc_sockopt_key(ip = _L, hdrincl = _Opt, _Dir, _D, raw = _T, _P) -> - ?SOCKET_OPT_IP_HDRINCL; + ?ESOCK_OPT_IP_HDRINCL; enc_sockopt_key(ip = _L, minttl = _Opt, _Dir, _D, raw = _T, _P) -> - ?SOCKET_OPT_IP_MINTTL; + ?ESOCK_OPT_IP_MINTTL; enc_sockopt_key(ip = _L, msfilter = _Opt, set = _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_MSFILTER; + ?ESOCK_OPT_IP_MSFILTER; enc_sockopt_key(ip = _L, mtu = _Opt, get = _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_MTU; + ?ESOCK_OPT_IP_MTU; enc_sockopt_key(ip = _L, mtu_discover = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_MTU_DISCOVER; + ?ESOCK_OPT_IP_MTU_DISCOVER; enc_sockopt_key(ip = _L, multicast_all = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_MULTICAST_ALL; + ?ESOCK_OPT_IP_MULTICAST_ALL; enc_sockopt_key(ip = _L, multicast_if = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_MULTICAST_IF; + ?ESOCK_OPT_IP_MULTICAST_IF; enc_sockopt_key(ip = _L, multicast_loop = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_MULTICAST_LOOP; + ?ESOCK_OPT_IP_MULTICAST_LOOP; enc_sockopt_key(ip = _L, multicast_ttl = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_MULTICAST_TTL; + ?ESOCK_OPT_IP_MULTICAST_TTL; enc_sockopt_key(ip = _L, nodefrag = _Opt, _Dir, _D, raw = _T, _P) -> - ?SOCKET_OPT_IP_NODEFRAG; + ?ESOCK_OPT_IP_NODEFRAG; enc_sockopt_key(ip = L, options = Opt, _Dir, _D, _T, _P) -> not_supported({Opt, L}); enc_sockopt_key(ip = _L, pktinfo = _Opt, _Dir, _D, dgram = _T, _P) -> - ?SOCKET_OPT_IP_PKTINFO; + ?ESOCK_OPT_IP_PKTINFO; enc_sockopt_key(ip = _L, recvdstaddr = _Opt, _Dir, _D, T, _P) when (T =:= dgram) -> - ?SOCKET_OPT_IP_RECVDSTADDR; + ?ESOCK_OPT_IP_RECVDSTADDR; enc_sockopt_key(ip = _L, recverr = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_RECVERR; + ?ESOCK_OPT_IP_RECVERR; enc_sockopt_key(ip = _L, recvif = _Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> - ?SOCKET_OPT_IP_RECVIF; + ?ESOCK_OPT_IP_RECVIF; enc_sockopt_key(ip = _L, recvopts = _Opt, _Dir, _D, T, _P) when (T =/= stream) -> - ?SOCKET_OPT_IP_RECVOPTS; + ?ESOCK_OPT_IP_RECVOPTS; enc_sockopt_key(ip = _L, recvorigdstaddr = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_RECVORIGDSTADDR; + ?ESOCK_OPT_IP_RECVORIGDSTADDR; enc_sockopt_key(ip, recvtos = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_RECVTOS; + ?ESOCK_OPT_IP_RECVTOS; enc_sockopt_key(ip = _L, recvttl = _Opt, _Dir, _D, T, _P) when (T =/= stream) -> - ?SOCKET_OPT_IP_RECVTTL; + ?ESOCK_OPT_IP_RECVTTL; enc_sockopt_key(ip = _L, retopts = _Opt, _Dir, _D, T, _P) when (T =/= stream) -> - ?SOCKET_OPT_IP_RETOPTS; + ?ESOCK_OPT_IP_RETOPTS; enc_sockopt_key(ip, router_alert = _Opt, _Dir, _D, raw = _T, _P) -> - ?SOCKET_OPT_IP_ROUTER_ALERT; + ?ESOCK_OPT_IP_ROUTER_ALERT; enc_sockopt_key(ip, sendsrcaddr = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_SENDSRCADDR; + ?ESOCK_OPT_IP_SENDSRCADDR; %% On FreeBSD it specifies that this option is only valid %% for stream, dgram and "some" raw sockets... %% No such condition on linux (in the man page)... enc_sockopt_key(ip, tos = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_TOS; + ?ESOCK_OPT_IP_TOS; enc_sockopt_key(ip = _L, transparent = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_TRANSPARENT; + ?ESOCK_OPT_IP_TRANSPARENT; enc_sockopt_key(ip, ttl = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_TTL; + ?ESOCK_OPT_IP_TTL; enc_sockopt_key(ip = _L, unblock_source = _Opt, set = _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IP_UNBLOCK_SOURCE; + ?ESOCK_OPT_IP_UNBLOCK_SOURCE; enc_sockopt_key(ip = L, UnknownOpt, _Dir, _D, _T, _P) -> unknown({L, UnknownOpt}); %% IPv6 socket options enc_sockopt_key(ipv6 = _L, addrform = _Opt, set = _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IPV6_ADDRFORM; + ?ESOCK_OPT_IPV6_ADDRFORM; enc_sockopt_key(ipv6, add_membership = _Opt, set = _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IPV6_ADD_MEMBERSHIP; + ?ESOCK_OPT_IPV6_ADD_MEMBERSHIP; enc_sockopt_key(ipv6 = _L, authhdr = _Opt, _Dir, _D, T, _P) when ((T =:= dgram) orelse (T =:= raw)) -> - ?SOCKET_OPT_IPV6_AUTHHDR; + ?ESOCK_OPT_IPV6_AUTHHDR; enc_sockopt_key(ipv6 = L, auth_level = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, checksum = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6, drop_membership = _Opt, set = _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IPV6_DROP_MEMBERSHIP; + ?ESOCK_OPT_IPV6_DROP_MEMBERSHIP; enc_sockopt_key(ipv6 = _L, dstopts = _Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> - ?SOCKET_OPT_IPV6_DSTOPTS; + ?ESOCK_OPT_IPV6_DSTOPTS; enc_sockopt_key(ipv6 = L, esp_trans_level = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, esp_network_level = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = _L, flowinfo = _Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> - ?SOCKET_OPT_IPV6_FLOWINFO; + ?ESOCK_OPT_IPV6_FLOWINFO; enc_sockopt_key(ipv6, hoplimit = _Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> - ?SOCKET_OPT_IPV6_HOPLIMIT; + ?ESOCK_OPT_IPV6_HOPLIMIT; enc_sockopt_key(ipv6 = _L, hopopts = _Opt, _Dir, _D, T, _P) when ((T =:= dgram) orelse (T =:= raw)) -> - ?SOCKET_OPT_IPV6_HOPOPTS; + ?ESOCK_OPT_IPV6_HOPOPTS; enc_sockopt_key(ipv6 = L, ipcomp_level = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, join_group = Opt, _Dir, _D, _T, _P) -> @@ -3666,44 +3704,44 @@ enc_sockopt_key(ipv6 = L, join_group = Opt, _Dir, _D, _T, _P) -> enc_sockopt_key(ipv6 = L, leave_group = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = _L, mtu = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IPV6_MTU; + ?ESOCK_OPT_IPV6_MTU; enc_sockopt_key(ipv6 = _L, mtu_discover = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IPV6_MTU_DISCOVER; + ?ESOCK_OPT_IPV6_MTU_DISCOVER; enc_sockopt_key(ipv6 = _L, multicast_hops = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IPV6_MULTICAST_HOPS; + ?ESOCK_OPT_IPV6_MULTICAST_HOPS; enc_sockopt_key(ipv6 = _L, multicast_if = _Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> - ?SOCKET_OPT_IPV6_MULTICAST_IF; + ?ESOCK_OPT_IPV6_MULTICAST_IF; enc_sockopt_key(ipv6 = _L, multicast_loop = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IPV6_MULTICAST_LOOP; + ?ESOCK_OPT_IPV6_MULTICAST_LOOP; enc_sockopt_key(ipv6 = L, portrange = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, pktoptions = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = _L, recverr = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IPV6_RECVERR; + ?ESOCK_OPT_IPV6_RECVERR; enc_sockopt_key(ipv6, recvhoplimit = _Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> - ?SOCKET_OPT_IPV6_RECVHOPLIMIT; + ?ESOCK_OPT_IPV6_RECVHOPLIMIT; enc_sockopt_key(ipv6 = _L, Opt, _Dir, _D, T, _P) when ((Opt =:= recvpktinfo) orelse (Opt =:= pktinfo)) andalso ((T =:= dgram) orelse (T =:= raw)) -> - ?SOCKET_OPT_IPV6_RECVPKTINFO; + ?ESOCK_OPT_IPV6_RECVPKTINFO; enc_sockopt_key(ipv6 = _L, recvtclass = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IPV6_RECVTCLASS; + ?ESOCK_OPT_IPV6_RECVTCLASS; enc_sockopt_key(ipv6 = _L, router_alert = _Opt, _Dir, _D, T, _P) when (T =:= raw) -> - ?SOCKET_OPT_IPV6_ROUTER_ALERT; + ?ESOCK_OPT_IPV6_ROUTER_ALERT; enc_sockopt_key(ipv6 = _L, rthdr = _Opt, _Dir, _D, T, _P) when ((T =:= dgram) orelse (T =:= raw)) -> - ?SOCKET_OPT_IPV6_RTHDR; + ?ESOCK_OPT_IPV6_RTHDR; enc_sockopt_key(ipv6 = _L, tclass = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IPV6_TCLASS; + ?ESOCK_OPT_IPV6_TCLASS; enc_sockopt_key(ipv6 = _L, unicast_hops = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IPV6_UNICAST_HOPS; + ?ESOCK_OPT_IPV6_UNICAST_HOPS; enc_sockopt_key(ipv6 = L, use_min_mtu = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = _L, v6only = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_IPV6_V6ONLY; + ?ESOCK_OPT_IPV6_V6ONLY; enc_sockopt_key(ipv6 = L, UnknownOpt, _Dir, _D, _T, _P) -> unknown({L, UnknownOpt}); @@ -3711,9 +3749,9 @@ enc_sockopt_key(ipv6 = L, UnknownOpt, _Dir, _D, _T, _P) -> %% There are other options that would be useful; info, %% but they are difficult to get portable... enc_sockopt_key(tcp, congestion = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_TCP_CONGESTION; + ?ESOCK_OPT_TCP_CONGESTION; enc_sockopt_key(tcp = _L, cork = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_TCP_CORK; + ?ESOCK_OPT_TCP_CORK; enc_sockopt_key(tcp = L, keepidle = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(tcp = L, keepintvl = Opt, _Dir, _D, _T, _P) -> @@ -3721,11 +3759,11 @@ enc_sockopt_key(tcp = L, keepintvl = Opt, _Dir, _D, _T, _P) -> enc_sockopt_key(tcp = L, keepcnt = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(tcp, maxseg = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_TCP_MAXSEG; + ?ESOCK_OPT_TCP_MAXSEG; enc_sockopt_key(tcp = L, md5sig = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(tcp, nodelay = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_TCP_NODELAY; + ?ESOCK_OPT_TCP_NODELAY; enc_sockopt_key(tcp = L, noopt = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(tcp = L, nopush = Opt, _Dir, _D, _T, _P) -> @@ -3739,7 +3777,7 @@ enc_sockopt_key(tcp = L, UnknownOpt, _Dir, _D, _T, _P) -> %% UDP socket options enc_sockopt_key(udp, cork = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_UDP_CORK; + ?ESOCK_OPT_UDP_CORK; enc_sockopt_key(udp = L, UnknownOpt, _Dir, _D, _T, _P) -> unknown({L, UnknownOpt}); @@ -3747,7 +3785,7 @@ enc_sockopt_key(udp = L, UnknownOpt, _Dir, _D, _T, _P) -> enc_sockopt_key(sctp = L, adaption_layer = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = _L, associnfo = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SCTP_ASSOCINFO; + ?ESOCK_OPT_SCTP_ASSOCINFO; enc_sockopt_key(sctp = L, auth_active_key = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, auth_asconf = Opt, _Dir, _D, _T, _P) -> @@ -3759,7 +3797,7 @@ enc_sockopt_key(sctp = L, auth_key = Opt, _Dir, _D, _T, _P) -> enc_sockopt_key(sctp = L, auth_delete_key = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp, autoclose = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SCTP_AUTOCLOSE; + ?ESOCK_OPT_SCTP_AUTOCLOSE; enc_sockopt_key(sctp = L, context = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, default_send_params = Opt, _Dir, _D, _T, _P) -> @@ -3767,11 +3805,11 @@ enc_sockopt_key(sctp = L, default_send_params = Opt, _Dir, _D, _T, _P) -> enc_sockopt_key(sctp = L, delayed_ack_time = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = _L, disable_fragments = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SCTP_DISABLE_FRAGMENTS; + ?ESOCK_OPT_SCTP_DISABLE_FRAGMENTS; enc_sockopt_key(sctp = L, hmac_ident = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = _L, events = _Opt, set = _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SCTP_EVENTS; + ?ESOCK_OPT_SCTP_EVENTS; enc_sockopt_key(sctp = L, explicit_eor = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, fragment_interleave = Opt, _Dir, _D, _T, _P) -> @@ -3779,17 +3817,17 @@ enc_sockopt_key(sctp = L, fragment_interleave = Opt, _Dir, _D, _T, _P) -> enc_sockopt_key(sctp = L, get_peer_addr_info = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = _L, initmsg = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SCTP_INITMSG; + ?ESOCK_OPT_SCTP_INITMSG; enc_sockopt_key(sctp = L, i_want_mapped_v4_addr = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, local_auth_chunks = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = _L, maxseg = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SCTP_MAXSEG; + ?ESOCK_OPT_SCTP_MAXSEG; enc_sockopt_key(sctp = L, maxburst = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp, nodelay = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SCTP_NODELAY; + ?ESOCK_OPT_SCTP_NODELAY; enc_sockopt_key(sctp = L, partial_delivery_point = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, peer_addr_params = Opt, _Dir, _D, _T, _P) -> @@ -3801,11 +3839,11 @@ enc_sockopt_key(sctp = L, primary_addr = Opt, _Dir, _D, _T, _P) -> enc_sockopt_key(sctp = L, reset_streams = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = _L, rtoinfo = _Opt, _Dir, _D, _T, _P) -> - ?SOCKET_OPT_SCTP_RTOINFO; + ?ESOCK_OPT_SCTP_RTOINFO; enc_sockopt_key(sctp = L, set_peer_primary_addr = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, status = Opt, get = _Dir, _D, _T, _P) -> - not_supported({L, Opt}); % ?SOCKET_OPT_SCTP_RTOINFO; + not_supported({L, Opt}); % ?ESOCK_OPT_SCTP_RTOINFO; enc_sockopt_key(sctp = L, use_exp_recvinfo = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, UnknownOpt, _Dir, _D, _T, _P) -> @@ -3828,11 +3866,11 @@ enc_sockopt_key(Level, Opt, _Dir, _Domain, _Type, _Protocol) -> enc_shutdown_how(read) -> - ?SOCKET_SHUTDOWN_HOW_READ; + ?ESOCK_SHUTDOWN_HOW_READ; enc_shutdown_how(write) -> - ?SOCKET_SHUTDOWN_HOW_WRITE; + ?ESOCK_SHUTDOWN_HOW_WRITE; enc_shutdown_how(read_write) -> - ?SOCKET_SHUTDOWN_HOW_READ_WRITE. + ?ESOCK_SHUTDOWN_HOW_READ_WRITE. @@ -3866,8 +3904,8 @@ ensure_sockaddr(#{family := local, path := Path} = SockAddr) (byte_size(Path) > 0) andalso (byte_size(Path) =< 255) -> SockAddr; -ensure_sockaddr(_SockAddr) -> - einval(). +ensure_sockaddr(SockAddr) -> + invalid_address(SockAddr). @@ -3875,19 +3913,29 @@ cancel(SockRef, Op, OpRef) -> case nif_cancel(SockRef, Op, OpRef) of %% The select has already completed {error, select_sent} -> - flush_select_msgs(SockRef, OpRef); + flush_select_msg(SockRef, OpRef), + _ = flush_abort_msg(SockRef, OpRef), + ok; Other -> + _ = flush_abort_msg(SockRef, OpRef), Other end. -flush_select_msgs(SockRef, Ref) -> +flush_select_msg(SockRef, Ref) -> receive - {?SOCKET_TAG, #socket{ref = SockRef}, select, Ref} -> - flush_select_msgs(SockRef, Ref) + {?ESOCK_TAG, #socket{ref = SockRef}, select, Ref} -> + ok after 0 -> ok end. +flush_abort_msg(SockRef, Ref) -> + receive + {?ESOCK_TAG, #socket{ref = SockRef}, abort, {Ref, Reason}} -> + Reason + after 0 -> + ok + end. %% formated_timestamp() -> %% format_timestamp(os:timestamp()). @@ -3915,34 +3963,37 @@ flush_select_msgs(SockRef, Ref) -> %% lists:flatten(FormatDate). -%% A timestamp in ms +deadline(Timeout) -> + case Timeout of + nowait -> Timeout; + infinity -> Timeout; + _ when is_integer(Timeout), 0 =< Timeout -> + timestamp() + Timeout; + _ -> + invalid_timeout(Timeout) + end. -timestamp(nowait = T) -> - T; -timestamp(infinity) -> - undefined; -timestamp(_) -> - timestamp(). +timeout(Deadline) -> + case Deadline of + nowait -> 0; + infinity -> infinity; + _ -> + Now = timestamp(), + if + Now < Deadline -> Deadline - Now; + true -> 0 + end + end. timestamp() -> erlang:monotonic_time(milli_seconds). -next_timeout(_, nowait = Timeout) -> - Timeout; -next_timeout(_, infinity = Timeout) -> - Timeout; -next_timeout(TS, Timeout) -> - NewTimeout = Timeout - tdiff(TS, timestamp()), - if - (NewTimeout > 0) -> - NewTimeout; - true -> - 0 - end. - -tdiff(T1, T2) -> - T2 - T1. +-compile({inline, [bincat/2]}). +bincat(<<>>, <<_/binary>> = B) -> B; +bincat(<<_/binary>> = A, <<>>) -> A; +bincat(<<_/binary>> = A, <<_/binary>> = B) -> + <<A/binary, B/binary>>. %% p(F) -> @@ -3968,43 +4019,45 @@ tdiff(T1, T2) -> -spec invalid_domain(Domain) -> no_return() when Domain :: term(). - invalid_domain(Domain) -> error({invalid_domain, Domain}). -spec invalid_type(Type) -> no_return() when Type :: term(). - invalid_type(Type) -> error({invalid_type, Type}). --spec invalid_protocol(Type, Proto) -> no_return() when - Type :: term(), +-spec invalid_protocol(Proto) -> no_return() when Proto :: term(). +invalid_protocol(Proto) -> + error({invalid_protocol, Proto}). + +-spec invalid_address(SockAddr) -> no_return() when + SockAddr :: term(). +invalid_address(SockAddr) -> + error({invalid_address, SockAddr}). -invalid_protocol(Type, Proto) -> - error({invalid_protocol, {Type, Proto}}). +-spec invalid_timeout(Timeout) -> no_return() when + Timeout :: term(). +invalid_timeout(Timeout) -> + error({invalid_timeout, Timeout}). -spec not_supported(What) -> no_return() when What :: term(). - not_supported(What) -> error({not_supported, What}). -spec unknown(What) -> no_return() when What :: term(). - unknown(What) -> error({unknown, What}). -spec einval() -> no_return(). - einval() -> error(einval). -spec error(Reason) -> no_return() when Reason :: term(). - error(Reason) -> throw({error, Reason}). diff --git a/erts/preloaded/src/socket_registry.erl b/erts/preloaded/src/socket_registry.erl new file mode 100644 index 0000000000..284f90157b --- /dev/null +++ b/erts/preloaded/src/socket_registry.erl @@ -0,0 +1,196 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% ========================================================================= +%% +%% This is a registry process for the socket module. +%% The nif sends info here about all created and deleted socket(s). +%% +%% ========================================================================= + +-module(socket_registry). + +-export([ + start/0, + number_of/0, + which_sockets/1 + ]). + + +-record(esock_info, + { + sock :: socket:socket(), + atime :: integer() % Add time - erlang:monotonic_time(milli_seconds) + }). + + +%% ========================================================================= + +%% This is not a "normal" start function. Instead its the entry +%% function for the socket registry process. +start() -> + erlang:register(?MODULE, self()), + process_flag(trap_exit, true), + loop([]). + +number_of() -> + await_reply( request(number_of) ). + +which_sockets(Filter) when is_function(Filter, 1) -> + await_reply( request({which_sockets, Filter}) ). + + +%% ========================================================================= + +loop(DB) -> + receive + {'$socket', add, Socket} -> + loop( handle_add_socket(DB, Socket) ); + + {'$socket', del, Socket} -> + loop( handle_delete_socket(DB, Socket) ); + + {?MODULE, request, From, ReqId, Req} = _REQ -> + %% erlang:display(REQ), + {NewDB, Reply} = handle_request(DB, Req), + reply(ReqId, From, Reply), + loop(NewDB); + + Msg -> + NewDB = handle_unexpected_msg(DB, Msg), + loop(NewDB) + end. + + +%% ========================================================================= + +handle_add_socket(DB, Sock) -> + [#esock_info{sock = Sock, atime = timestamp()} | DB]. + +handle_delete_socket(DB, Sock) -> + lists:keydelete(Sock, #esock_info.sock, DB). + + +handle_request(DB, number_of) -> + {DB, db_size(DB)}; + +handle_request(DB, {which_sockets, Filter}) -> + {DB, do_which_sockets(DB, Filter)}; + +handle_request(DB, BadRequest) -> + {DB, {error, {bad_request, BadRequest}}}. + + +%% --- + +handle_unexpected_msg(DB, {'EXIT', Pid, Reason}) -> + F = "socket-registry received unexpected exit from ~p:" + "~n ~p", + A = [Pid, Reason], + handle_unexpected_msg(warning, F, A), + DB; +handle_unexpected_msg(DB, X) -> + F = "socket-registry received unexpected:" + "~n ~p", + A = [X], + handle_unexpected_msg(warning, F, A), + DB. + +%% This is "stolen" from the init process. But level is set to warning instead. +%% This "may" not be what you want, but we can always decrease to info instead... +%% Also, we may receive (unexpected) messages that should be classified +%% differently (info, warning, ...)... +%% +%% This is equal to calling logger:[info|warning]/3 which we don't +%% want to do from this process, at least not during +%% system boot. We don't want to call logger:timestamp() +%% either. +%% + +%% handle_unexpected_msg(info, F, A) -> +%% do_handle_unexpected_msg( mk_unexpected_info_msg(F, A) ). +handle_unexpected_msg(warning, F, A) -> + do_handle_unexpected_msg( mk_unexpected_warning_msg(F, A) ). + +do_handle_unexpected_msg(Msg) -> + catch logger ! Msg. + + +%% mk_unexpected_info_msg(F, A) -> +%% mk_unexpected_msg(info, info_msg, F, A). + +mk_unexpected_warning_msg(F, A) -> + mk_unexpected_msg(warning, warning_msg, F, A). + +mk_unexpected_msg(Level, Tag, F, A) -> + Meta = #{pid => self(), + gl => erlang:group_leader(), + time => os:system_time(microsecond), + error_logger => #{tag => Tag}}, + {log, Level, F, A, Meta}. + + +%% --- + +db_size(DB) -> + length(DB). + +do_which_sockets(DB, Filter) -> + try + begin + SocksInfo = + [{Sock, socket:info(Sock)} || #esock_info{sock = Sock} <- DB], + [Sock || {Sock, SockInfo} <- SocksInfo, Filter(SockInfo)] + end + catch + _:_:_ -> + [Sock || #esock_info{sock = Sock} <- DB] + end. + + + +%% ========================================================================= + +request(Req) -> + ReqId = make_ref(), + ReqMsg = {?MODULE, request, self(), ReqId, Req}, + Registry = whoami(), + erlang:send(Registry, ReqMsg), + ReqId. + +reply(ReqId, From, Reply) -> + RepMsg = {?MODULE, reply, self(), ReqId, Reply}, + erlang:send(From, RepMsg). + +await_reply(ReqId) -> + Registry = whoami(), + receive + {?MODULE, reply, Registry, ReqId, Reply} -> + Reply + end. + + +%% ========================================================================= + +whoami() -> + whereis(?MODULE). + +timestamp() -> + erlang:monotonic_time(milli_seconds). diff --git a/erts/vsn.mk b/erts/vsn.mk index da4516530c..1e7f5b3118 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 10.6.1 +VSN = 10.6.4 # Port number 4365 in 4.2 # Port number 4366 in 4.3 diff --git a/lib/asn1/src/Makefile b/lib/asn1/src/Makefile index ba459f6cd3..d9a4197600 100644 --- a/lib/asn1/src/Makefile +++ b/lib/asn1/src/Makefile @@ -190,7 +190,8 @@ asn1rtt_%.$(EMULATOR): asn1rtt_%.erl $(V_ERLC) +debug_info $< $(EVAL_CT_MODULES:%=%.erl): prepare_templates.$(EMULATOR) \ - $(EBIN)/asn1ct_rtt.$(EMULATOR) + $(EBIN)/asn1ct_rtt.$(EMULATOR) \ + $(EBIN)/asn1ct_func.$(EMULATOR) \ # # Dependencies diff --git a/lib/common_test/doc/src/Makefile b/lib/common_test/doc/src/Makefile index b5acdc6f95..ae06572752 100644 --- a/lib/common_test/doc/src/Makefile +++ b/lib/common_test/doc/src/Makefile @@ -74,6 +74,7 @@ XML_CHAPTER_FILES = \ ct_master_chapter.xml \ event_handler_chapter.xml \ ct_hooks_chapter.xml \ + ct_property_test_chapter.xml \ dependencies_chapter.xml \ notes.xml diff --git a/lib/common_test/doc/src/ct_property_test.xml b/lib/common_test/doc/src/ct_property_test.xml index 1e01d9a5d7..1690e9962a 100644 --- a/lib/common_test/doc/src/ct_property_test.xml +++ b/lib/common_test/doc/src/ct_property_test.xml @@ -33,30 +33,39 @@ <file>ct_property_test.xml</file> </header> <module since="OTP 17.3">ct_property_test</module> - <modulesummary>EXPERIMENTAL support in Common Test for calling - property-based tests.</modulesummary> + <modulesummary>Support in Common Test for running property-based tests.</modulesummary> <description> - <p>EXPERIMENTAL support in <c>Common Test</c> for calling property-based - tests.</p> + <p>This module helps running property-based tests in the + <c>Common Test</c> framework. One (or more) of the property testing tools + </p> + <list> + <item><url href="http://www.quviq.com">QuickCheck</url>,</item> + <item><url href="https://proper-testing.github.io">PropEr</url> or</item> + <item><url href="https://github.com/krestenkrab/triq">Triq</url></item> + </list> + <p> + is assumed to be installed. + </p> - <p>This module is a first step to run property-based tests in the - <c>Common Test</c> framework. A property testing tool like QuickCheck - or PropEr is assumed to be installed.</p> - - <p>The idea is to have a <c>Common Test</c> test suite calling a property - testing tool with special property test suites as defined by that tool. - The usual Erlang application directory structure is assumed. The tests - are collected in the <c>test</c> directory of the application. The - <c>test</c> directory has a subdirectory <c>property_test</c>, where - everything needed for the property tests is collected.</p> + <p>The idea with this module is to have a <c>Common Test</c> test suite calling + a property testing tool with special property test suites as defined by that tool. + The tests + are collected in the <c>test</c> directory of the application. The + <c>test</c> directory has a subdirectory <c>property_test</c>, where + everything needed for the property tests are collected. + The usual Erlang application directory structure is assumed. + </p> <p>A typical <c>Common Test</c> test suite using <c>ct_property_test</c> - is organized as follows:</p> + is organized as follows:</p> + + <code> +-module(my_prop_test_SUITE). +-compile(export_all). - <pre> - -include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct.hrl"). all() -> [prop_ftp_case]. @@ -66,51 +75,198 @@ %%%---- test case prop_ftp_case(Config) -> ct_property_test:quickcheck( - ftp_simple_client_server:prop_ftp(Config), + ftp_simple_client_server:prop_ftp(), Config - ).</pre> + ).</code> + <p>and the the property test module (in this example <c>ftp_simple_client_server.erl</c>) + as almost a usual property testing module + (More examples are in <seealso marker="ct_property_test_chapter">the User's Guide</seealso>):</p> + <code> +-module(ftp_simple_client_server). +-export([prop_ftp/0...]). - <warning> - <p>This is experimental code that can be changed or removed anytime - without any warning.</p> - </warning> +-include_lib("common_test/include/ct_property_test.hrl"). +prop_ftp() -> + ?FORALL( .... + </code> </description> <funcs> <func> <name since="OTP 17.3">init_per_suite(Config) -> Config | {skip, Reason}</name> - <fsummary>Initializes Config for property testing.</fsummary> + <fsummary>Initializes and extends Config for property testing.</fsummary> <desc><marker id="init_per_suite-1"/> - <p>Initializes <c>Config</c> for property testing.</p> + <p>Initializes and extends <c>Config</c> for property based testing.</p> <p>This function investigates if support is available for either - Quickcheck, PropEr, or Triq. The options - <c>{property_dir,AbsPath}</c> and <c>{property_test_tool,Tool}</c> - are set in the <c>Config</c> returned.</p> + <url href="http://www.quviq.com">QuickCheck</url>, + <url href="https://proper-testing.github.io">PropEr</url> + or <url href="https://github.com/krestenkrab/triq">Triq</url> and compiles the + properties with the first tool found. + It is supposed to be called in the <c>init_per_suite/1</c> function + in a CommonTest test suite. + </p> + <p>Which tools to check for, and in which order could be set with the option + <c>{prop_tools, list(eqc|proper|triq)}</c> + in the CommonTest configuration <c>Config</c>. The default value is + <c>[eqc, proper, triq]</c> with <c>eqc</c> being the first one searched for. + </p> + <p>If no support is found for any tool, this function returns + <c>{skip, Explanation}</c>. + </p> + <p>If support is found, the option <c>{property_test_tool,ToolModule}</c> with + the selected tool main module name (<c>eqc</c>, <c>proper</c> or <c>triq</c>) + is added to the list <c>Config</c> which then is returned. + </p> + <p>The property tests are assumed to be in a subdirectory named + <c>property_test</c>. + All found Erlang files in that directory are compiled with one of the macros + <c>'EQC'</c>, <c>'PROPER'</c> or <c>'TRIQ'</c> set, depending on which tool + that is first found. This could make parts of the Erlang property tests + code to be included or excluded with the macro directives + <c>-ifdef(Macro).</c> or <c>-ifndef(Macro).</c>. + </p> + <p>The file(s) in the <c>property_test</c> subdirectory could, or should, + include the ct_property_test include file: + </p> + <code> +-include_lib("common_test/include/ct_property_test.hrl"). + </code> + <p>This included file will: + </p> + <list> + <item>Include the correct tool's include file</item> + <item>Set the macro <c>'MOD_eqc'</c> to the correct module name for the + selected tool. That is, the macro <c>'MOD_eqc'</c> is set to either + <c>eqc</c>, <c>proper</c> or <c>triq</c>. + </item> + </list> + </desc> + </func> - <p>The function is intended to be called in function - <c>init_per_suite</c> in the test suite.</p> + <func> + <name since="OTP 17.3">quickcheck(Property, Config) -> true | {fail, Reason}</name> + <fsummary>Calls quickcheck and returns the result in a form suitable for + Common Test.</fsummary> + <desc> + <p>Calls the selected tool's function for running the <c>Property</c>. It is usually and + by historical reasons called quickcheck, and that is why that name is used in + this module (<c>ct_property_test</c>). + </p> + <p>The result is returned in a form suitable for <c>Common Test</c> test suites. + </p> + <p>This function is intended to be called in test cases in test suites. + </p> + </desc> + </func> - <p>The property tests are assumed to be in subdirectory - <c>property_test</c>.</p> + <func> + <name since="">present_result(Module, Cmds, Triple, Config) -> Result</name> + <fsummary>Presents the result of statem property testing</fsummary> + <desc> + <p>Same as <seealso marker="#present_result/5"><c>present_result(Module, Cmds, Triple, Config, [])</c></seealso> + </p> </desc> </func> <func> - <name since="OTP 17.3">quickcheck(Property, Config) -> true | {fail, Reason}</name> - <fsummary>Calls quickcheck and returns the result in a form suitable for - Common Test.</fsummary> - <desc><marker id="quickcheck-2"/> - <p>Calls quickcheck and returns the result in a form suitable for - <c>Common Test</c>.</p> + <name since="">present_result(Module, Cmds, Triple, Config, Options) -> Result</name> + <fsummary>Presents the result of statem property testing</fsummary> + <type> + <v>Module = module()</v> + <d></d> + + <v>Cmds =</v> + <d>the list of commands generated by the property testing tool, for example + by proper:commands/1 or by proper:parallel_commands/1 + </d> + + <v>Triple =</v> + <d>the output from for example proper:run_commands/2 or proper:run_parallel_commands/2</d> + + <v>Config =</v> + <d>the Common Test <seealso marker="common_test#Module:Testcase/1">Config</seealso> in test cases.</d> + + <v>Options = [present_option()]</v> + <v>present_option() = {print_fun, fun(Format,Args)}</v> + <v> | {spec, StatisticsSpec}</v> + <d>The <c>print_fun</c> defines which function to do the actual printout. The default is + <seealso marker="ct#log/2">ct:log/2</seealso>. + The <c>spec</c> defines what statistics are to be printed<!--, see the + <seealso marker="ct_property_test_chapter#spec_present_result">User's Guide</seealso>--> + </d> + + <v>Result = boolean()</v> + <d>Is <c>false</c> if the test failed and is <c>true</c> if the test passed</d> + </type> + <desc> + <p>Presents the result of <i>stateful (statem) property testing</i> using the aggregate function in + PropEr, QuickCheck or other similar property testing tool. + </p> + <p>It is assumed to be called inside the property called by + <seealso marker="#quickcheck/2">quickcheck/2</seealso>:</p> + <code> +... +RunResult = run_parallel_commands(?MODULE, Cmds), +ct_property_test:present_result(?MODULE, Cmds, RunResult, Config) +... + </code> + <p>See the <seealso marker="ct_property_test_chapter#stateful1">User's Guide</seealso> for + an example of the usage and of the default printout. + </p> + <p>The <c>StatisticsSpec</c> is a list of the tuples:</p> + <list> + <item><c>{Title::string(), CollectFun::fun/1}</c></item> + <item><c>{Title::string(), FrequencyFun::/0, CollectFun::fun/1}</c></item> + </list> + <p>Each tuple will produce one table in the order of their places in the list.</p> + <list> + <item><c>Title</c> will be the title of one result table</item> - <p>This function is intended to be called in the test cases in the - test suite.</p> + <item><c>CollectFun</c> is called with one argument: the <c>Cmds</c>. It should return + a list of the values to be counted. The following pre-defined functions exist: + <list> + <item><c>ct_property_test:cmnd_names/1</c> returns a list of commands (function calls) generated in the <c>Cmnd</c> + sequence, without Module, Arguments and other details.</item> + <item><c>ct_property_test:num_calls/1</c> returns a list of the length of commands lists</item> + <item><c>ct_property_test:sequential_parallel/1</c> returns a list with information about sequential and + parallel parts from <c>Tool:parallel_commands/1,2</c></item> + </list> + </item> + + <item><c>FrequencyFun/0</c> returns a fun/1 which is supposed to take a list of items as input, + and return an iolist wich will be printed as the table. Per default, the number of each item is counted + and the percentage is printed for each. The list [a,b,a,a,c] could for example return + <pre> + ["a 60%\n","b 20%\n","c 20%\n"]</pre> + which will be printed by the <c>print_fun</c>. + The default <c>print_fun</c> will print it as: + <pre> + a 60% + b 20% + c 20%</pre> + </item> + </list> + <p>The default <c>StatisticsSpec</c> is:</p> + <list> + <item>For sequential commands: + <code> +[{"Function calls", fun cmnd_names/1}, + {"Length of command sequences", fun print_frequency_ranges/0, + fun num_calls/1}] + </code></item> + <item>For parallel commands: + <code> +[{"Distribution sequential/parallel", fun sequential_parallel/1}, + {"Function calls", fun cmnd_names/1}, + {"Length of command sequences", fun print_frequency_ranges/0, + fun num_calls/1}] + </code></item> + </list> </desc> </func> + </funcs> </erlref> - - diff --git a/lib/common_test/doc/src/ct_property_test_chapter.xml b/lib/common_test/doc/src/ct_property_test_chapter.xml new file mode 100644 index 0000000000..131f3a962d --- /dev/null +++ b/lib/common_test/doc/src/ct_property_test_chapter.xml @@ -0,0 +1,249 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2019</year><year>2019</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + </legalnotice> + + <title>Common Test's Property Testing Support: ct_property_test</title> + <prepared>Hans Nilsson</prepared> + <docno></docno> + <date></date> + <rev></rev> + <file>ct_property_test_chapter.xml</file> + </header> + + <section> + <marker id="general"></marker> + <title>General</title> + <p> + The <em>Common Test Property Testing Support (ct_property_test)</em> + is an aid to run property based testing tools in Common Test test suites. + </p> + <p> + Basic knowledge of property based testing is assumed in the following. + It is also assumed that at least one of the following property based + testing tools is installed and available in the library path: + </p> + <list> + <item><url href="http://www.quviq.com">QuickCheck</url>,</item> + <item><url href="https://proper-testing.github.io">PropEr</url> or</item> + <item><url href="https://github.com/krestenkrab/triq">Triq</url></item> + </list> + </section> + + <section> + <marker id="supported"></marker> + <title>What Is Supported?</title> + <p>The <seealso marker="ct_property_test#">ct_property_test</seealso> module + does the following: + </p> + <list type="bulleted"> + <item>Compiles the files with property tests in the subdirectory <c>property_test</c> + </item> + <item>Tests properties in those files using the first found Property Testing Tool. + </item> + <item>Saves the results - that is the printouts - in the usual Common Test Log + </item> + </list> + </section> + + + <section> + <title>Introductory Example</title> + <p>Assume that we want to test the lists:sort/1 function. + </p> + <p>We need a property to test the function. In normal way, we create + <c>property_test/ct_prop.erl</c> module in the <c>test</c> directory + in our application: + </p> + + <code> +-module(ct_prop). +-export([prop_sort/0]). + +%%% This will include the .hrl file for the installed testing tool: +-include_lib("common_test/include/ct_property_test.hrl"). + +%%% The property we want to check: +%%% For all possibly unsorted lists, +%%% the result of lists:sort/1 is sorted. +prop_sort() -> + ?FORALL(UnSorted, list(), + is_sorted(lists:sort(UnSorted)) + ). + +%%% Function to check that a list is sorted: +is_sorted([]) -> + true; +is_sorted([_]) -> + true; +is_sorted([H1,H2|SortedTail]) when H1 =< H2 -> + is_sorted([H2|SortedTail]); +is_sorted(_) -> + false. + </code> + + <p>We also need a CommonTest test suite: + </p> + <code> +-module(ct_property_test_SUITE). +-compile(export_all). % Only in tests! + +-include_lib("common_test/include/ct.hrl"). + +all() -> [prop_sort + ]. + +%%% First prepare Config and compile the property tests for the found tool: +init_per_suite(Config) -> + ct_property_test:init_per_suite(Config). + +end_per_suite(Config) -> + Config. + +%%%================================================================ +%%% Test suites +%%% +prop_sort(Config) -> + ct_property_test:quickcheck( + ct_prop:prop_sort(), + Config + ). + </code> + + <p>We run it as usual, for example with ct_run in the OS shell:</p> + <pre> +..../test$ ct_run -suite ct_property_test_SUITE +..... +Common Test: Running make in test directories... + +TEST INFO: 1 test(s), 1 case(s) in 1 suite(s) + +Testing lib.common_test.ct_property_test_SUITE: Starting test, 1 test cases + +---------------------------------------------------- +2019-12-18 10:44:46.293 +Found property tester proper +at "/home/X/lib/proper/ebin/proper.beam" + + +---------------------------------------------------- +2019-12-18 10:44:46.294 +Compiling in "/home/..../test/property_test" + Deleted: ["ct_prop.beam"] + ErlFiles: ["ct_prop.erl"] + MacroDefs: [{d,'PROPER'}] + +Testing lib.common_test.ct_property_test_SUITE: TEST COMPLETE, 1 ok, 0 failed of 1 test cases + +.... + </pre> + </section> + + + <section> + <marker id="stateful1"></marker> + <title>A stateful testing example</title> + <p>Assume a test that generates some parallel stateful commands, and runs 300 tests:</p> + <code> +prop_parallel(Config) -> + numtests(300, + ?FORALL(Cmds, parallel_commands(?MODULE), + begin + RunResult = run_parallel_commands(?MODULE, Cmds), + ct_property_test:present_result(?MODULE, Cmds, RunResult, Config) + end)). + </code> + <p>The + <seealso marker="ct_property_test#present_result/4">ct_property_test:present_result/4</seealso> + is a help function for printing some statistics in the CommonTest log file.</p> + <p>Our example test could for example be a simple test of an ftp server, where we perform get, put + and delete requests, some of them in parallel. Per default, the result has three sections: + </p> + <pre> +*** User 2019-12-11 13:28:17.504 *** + +Distribution sequential/parallel + + 57.7% sequential + 28.0% parallel_2 + 14.3% parallel_1 + + + +*** User 2019-12-11 13:28:17.505 *** + +Function calls + + 44.4% get + 39.3% put + 16.3% delete + + + +*** User 2019-12-11 13:28:17.505 *** + +Length of command sequences + +Range : Number in range +-------:---------------- + 0 - 4: 8 2.7% <-- min=3 + 5 - 9: 44 14.7% +10 - 14: 74 24.7% +15 - 19: 60 20.0% <-- mean=18.7 <-- median=16.0 +20 - 24: 38 12.7% +25 - 29: 26 8.7% +30 - 34: 19 6.3% +35 - 39: 19 6.3% +40 - 44: 8 2.7% +45 - 49: 4 1.3% <-- max=47 + ------ + 300 + </pre> + <p>The first part - <i>Distribution sequential/parallel</i> - shows the distribution in the + sequential and parallel part of the result of parallel_commands/1. See any property testing tool for + an explanation of this function. + The table shows that of all commands (get and put in our case), + 57.7% are executed in the sequential part prior to the parallel part, + 28.0% are executed in the first parallel list and the rest in the second parallel list. + </p> + + <p>The second part - <i>Function calls</i> - shows the distribution of the three calls in the + generated command lists. We see that all of the three calls are executed. If it was so that we + thought that we also generated a fourth call, a table like this shows that we failed with that. + </p> + + <p>The third and final part - <i>Length of command sequences</i> - show statistics of the + generated command sequences. We see that the shortest list has three elementes while the longest + has 47 elements. The mean and median values are also shown. Further we could for example see that + only 2.7% of the lists (that is eight lists) only has three or four elements. + </p> + + </section> + + <!--section> + <marker id="spec_present_result"></marker> + <title>The spec for present_result/5</title> + <p>To be written... + <seealso marker="ct_property_test#present_result/5">present_result/5</seealso> + </p> + </section--> +</chapter> diff --git a/lib/common_test/doc/src/ct_slave.xml b/lib/common_test/doc/src/ct_slave.xml index 84e619482d..722ea20979 100644 --- a/lib/common_test/doc/src/ct_slave.xml +++ b/lib/common_test/doc/src/ct_slave.xml @@ -159,7 +159,7 @@ occurs during initialization or startup. Defaults to <c>true</c>. Notice that the node can also be still alive it the boot time-out occurred, but it is not killed in this case.</p></item> - <tag><c>erlang_flags</c></tag> + <tag><c>erl_flags</c></tag> <item><p>Specifies which flags are added to the parameters of the executable <c>erl</c>.</p></item> <tag><c>env</c></tag> diff --git a/lib/common_test/doc/src/part.xml b/lib/common_test/doc/src/part.xml index 000eb06b82..66dcf75258 100644 --- a/lib/common_test/doc/src/part.xml +++ b/lib/common_test/doc/src/part.xml @@ -48,6 +48,7 @@ <xi:include href="dependencies_chapter.xml"/> <xi:include href="ct_hooks_chapter.xml"/> <xi:include href="why_test_chapter.xml"/> + <xi:include href="ct_property_test_chapter.xml"/> </part> diff --git a/lib/common_test/include/ct_property_test.hrl b/lib/common_test/include/ct_property_test.hrl new file mode 100644 index 0000000000..9d5933fde3 --- /dev/null +++ b/lib/common_test/include/ct_property_test.hrl @@ -0,0 +1,40 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2019. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +%% + +-ifndef(CT_PROPERTY_TEST_HRL). + -define(CT_PROPERTY_TEST_HRL, true). + + -ifdef(EQC). + -define(MOD_eqc, eqc). + -include_lib("eqc/include/eqc.hrl"). + -else. + -ifdef(PROPER). + -define(MOD_eqc, proper). + -include_lib("proper/include/proper.hrl"). + -else. + -ifdef(TRIQ). + -define(MOD_eqc, triq). + -include_lib("triq/include/triq.hrl"). + -endif. + -endif. + -endif. + +-endif. diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile index 76689dab8c..ffdef8ec39 100644 --- a/lib/common_test/src/Makefile +++ b/lib/common_test/src/Makefile @@ -96,7 +96,8 @@ HRL_FILES = \ ct_netconfc.hrl EXTERNAL_HRL_FILES = \ ../include/ct.hrl \ - ../include/ct_event.hrl + ../include/ct_event.hrl \ + ../include/ct_property_test.hrl EXTERNAL_INC_PATH = ../include diff --git a/lib/common_test/src/ct_property_test.erl b/lib/common_test/src/ct_property_test.erl index 93642a0970..251a0a4896 100644 --- a/lib/common_test/src/ct_property_test.erl +++ b/lib/common_test/src/ct_property_test.erl @@ -18,26 +18,40 @@ %% %CopyrightEnd% %% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% %%% -%%% WARNING %%% -%%% %%% -%%% This is experimental code which may be changed or removed %%% -%%% anytime without any warning. %%% -%%% %%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -module(ct_property_test). -%% API +%%% API +%% Main functions -export([init_per_suite/1, - quickcheck/2]). + quickcheck/2 + ]). + +%% Result presentation +-export([present_result/4, present_result/5, + title/2, title/3, + sequential_parallel/1, + cmnd_names/1, + num_calls/1, + print_frequency_ranges/0, + print_frequency/0 + ]). +%%% Mandatory include -include_lib("common_test/include/ct.hrl"). +%%%================================================================ +%%% +%%% API +%%% + +%%%---------------------------------------------------------------- +%%% +%%% Search for a property tester in the lib path, and if found, compile +%%% the property tests +%%% init_per_suite(Config) -> - case which_module_exists([eqc,proper,triq]) of + ToolsToCheck = proplists:get_value(prop_tools, Config, [eqc,proper,triq]), + case which_module_exists(ToolsToCheck) of {ok,ToolModule} -> case code:where_is_file(lists:concat([ToolModule,".beam"])) of non_existing -> @@ -66,12 +80,71 @@ init_per_suite(Config) -> {skip, "No property testing tool found"} end. +%%%---------------------------------------------------------------- +%%% +%%% Call the found property tester (if any) +%%% quickcheck(Property, Config) -> Tool = proplists:get_value(property_test_tool,Config), F = function_name(quickcheck, Tool), mk_ct_return( Tool:F(Property), Tool ). +%%%---------------------------------------------------------------- +%%% +%%% Present a nice table of the statem result +%%% +present_result(Module, Cmds, Triple, Config) -> + present_result(Module, Cmds, Triple, Config, []). + +present_result(Module, Cmds, {H,Sf,Result}, Config, Options0) -> + DefSpec = + if + is_tuple(Cmds) -> + [{"Distribution sequential/parallel", fun sequential_parallel/1}]; + is_list(Cmds) -> + [] + end + ++ [{"Function calls", fun cmnd_names/1}, + {"Length of command sequences", fun print_frequency_ranges/0, fun num_calls/1} + ], + Options = add_default_options(Options0, + [{print_fun, fun ct:log/2}, + {spec, DefSpec} + ]), + do_present_result(Module, Cmds, H, Sf, Result, Config, Options). + + +title(Str, Fun) -> + title(Str, Fun, fun io:format/2). + +title(Str, Fun, PrintFun) -> + fun(L) -> PrintFun("~n~s~n~n~s~n", [Str,Fun(L)]) end. + +print_frequency() -> + fun(L) -> + [io_lib:format("~5.1f% ~p~n",[Pcnt,V]) + || {V,_Num,Pcnt} <- + with_percentage(get_frequencies_no_range(L), length(L)) + ] + end. + +print_frequency_ranges() -> + print_frequency_ranges([{ngroups,10}]). + +print_frequency_ranges(Options0) -> + fun([]) -> + io_lib:format('Empty list!~n',[]); + (L ) -> + try + Options = set_default_print_freq_range_opts(Options0, L), + do_print_frequency_ranges(L, Options) + catch + C:E:S -> + ct:pal("~p:~p ~p:~p~n~p~n~p",[?MODULE,?LINE,C,E,S,L]) + end + end. + %%%================================================================ %%% %%% Local functions @@ -155,3 +228,217 @@ macro_def(triq) -> [{d, 'TRIQ'}]. function_name(quickcheck, triq) -> check; function_name(F, _) -> F. + +%%%================================================================ +%%%================================================================ +%%%================================================================ +%%% +%%% Result presentation part +%%% +do_present_result(_Module, Cmds, _H, _Sf, ok, Config, Options) -> + [PrintFun, Spec] = [proplists:get_value(K,Options) || K <- [print_fun,spec]], + Tool = proplists:get_value(property_test_tool,Config), + AGGREGATE = function_name(aggregate, Tool), + lists:foldr(fun({Title, FreqFun, CollecFun}, Result) -> + Tool:AGGREGATE(title(Title, FreqFun(), PrintFun), + CollecFun(Cmds), + Result); + ({Title, CollecFun}, Result) -> + Tool:AGGREGATE(title(Title, print_frequency(), PrintFun), + CollecFun(Cmds), + Result) + end, true, Spec); + +do_present_result(Module, Cmds, H, Sf, Result, _Config, Options) -> + [PrintFun] = [proplists:get_value(K,Options) || K <- [print_fun]], + PrintFun("Module = ~p,~n" + "Commands = ~p,~n" + "History = ~p,~n" + "FinalDynState = ~p,~n" + "Result = ~p", + [Module, Cmds, H, Sf, Result]), + Result == ok. % Proper dislikes non-boolean results while eqc treats non-true as false. + +%%%================================================================ +cmnd_names(Cs) -> traverse_commands(fun cmnd_name/1, Cs). +cmnd_name(L) -> [F || {set,_Var,{call,_Mod,F,_As}} <- L]. + +num_calls(Cs) -> traverse_commands(fun num_call/1, Cs). +num_call(L) -> [length(L)]. + +sequential_parallel(Cs) -> + traverse_commands(fun(L) -> dup_module(L, sequential) end, + fun(L) -> [dup_module(L1, mkmod("parallel",num(L1,L))) || L1<-L] end, + Cs). +dup_module(L, ModName) -> lists:duplicate(length(L), ModName). +mkmod(PfxStr,N) -> list_to_atom(PfxStr++"_"++integer_to_list(N)). + +%% Meta functions for the aggregate functions +traverse_commands(Fun, L) when is_list(L) -> Fun(L); +traverse_commands(Fun, {Seq, ParLs}) -> Fun(lists:append([Seq|ParLs])). + +traverse_commands(Fseq, _Fpar, L) when is_list(L) -> Fseq(L); +traverse_commands(Fseq, Fpar, {Seq, ParLs}) -> lists:append([Fseq(Seq)|Fpar(ParLs)]). + +%%%================================================================ +-define(middle_dot, 0183). + +set_default_print_freq_range_opts(Opts0, L) -> + add_default_options(Opts0, [{ngroups, 10}, + {min, 0}, + {max, max_in_list(L)} + ]). + +add_default_options(Opts0, DefaultOpts) -> + [set_def_opt(Key,DefVal,Opts0) || {Key,DefVal} <- DefaultOpts]. + +set_def_opt(Key, DefaultValue, Opts) -> + {Key, proplists:get_value(Key, Opts, DefaultValue)}. + +max_in_list(L) -> + case lists:last(L) of + Max when is_integer(Max) -> Max; + {Max,_} -> Max + end. + +do_print_frequency_ranges(L0, Options) -> + [N,Min,Max] = [proplists:get_value(K,Options) || K <- [ngroups, min, max]], + L = if + N>Max -> + %% There will be less than the demanded number of classes, + %% insert one last with zero values in it. That will force + %% the generation of N classes. + L0++[{N,0}]; + N=<Max -> + L0 + end, + try + Interval = round((Max-Min)/N), + IntervalLowerLimits = lists:seq(Min,Max,Interval), + Ranges = [{I,I+Interval-1} || I <- IntervalLowerLimits], + Acc0 = [{Rng,0} || Rng <- Ranges], + Fs0 = get_frequencies(L, Acc0), + SumVal = lists:sum([V||{_,V}<-Fs0]), + Fs = with_percentage(Fs0, SumVal), + DistInfo = [{"min", lists:min(L)}, + {"mean", mean(L)}, + {"median", median(L)}, + {"max", lists:max(L)}], + + Npos_value = num_digits(SumVal), + Npos_range = num_digits(Max), + [%% Table heading: + io_lib:format("Range~*s: ~s~n",[2*Npos_range-2,"", "Number in range"]), + %% Line under heading: + io_lib:format("~*c:~*c~n",[2*Npos_range+3,$-, max(16,Npos_value+10),$- ]), + %% Lines with values: + [io_lib:format("~*w - ~*w: ~*w ~5.1f% ~s~n", + [Npos_range,Rlow, + Npos_range,Rhigh, + Npos_value,Val, + Percent, + cond_prt_vals(DistInfo, Interv) + ]) + || {Interv={Rlow,Rhigh},Val,Percent} <- Fs], + %% Line under the table for the total number of values: + io_lib:format('~*c ~*c~n',[2*Npos_range,32, Npos_value+3,$-]), + %% The total number of values: + io_lib:format('~*c ~*w~n',[2*Npos_range,32, Npos_value,SumVal]) + ] + catch + C:E -> + ct:pal('*** Failed printing (~p:~p) for~n~p~n',[C,E,L]) + end. + +cond_prt_vals(LVs, CurrentInterval) -> + [prt_val(Label, Value, CurrentInterval) || {Label,Value} <- LVs]. + +prt_val(Label, Value, CurrentInterval) -> + case in_interval(Value, CurrentInterval) of + true -> + io_lib:format(" <-- ~s=" ++ if + is_float(Value) -> "~.1f"; + true -> "~p" + end, + [Label,Value]); + false -> + "" + end. + +get_frequencies([{I,Num}|T], [{{Lower,Upper},Cnt}|Acc]) when Lower=<I,I=<Upper -> + get_frequencies(T, [{{Lower,Upper},Cnt+Num}|Acc]); +get_frequencies(L=[{I,_Num}|_], [Ah={{_Lower,Upper},_Cnt}|Acc]) when I>Upper -> + [Ah | get_frequencies(L,Acc)]; +get_frequencies([I|T], Acc) when is_integer(I) -> + get_frequencies([{I,1}|T], Acc); +get_frequencies([], Acc) -> + Acc. + +get_frequencies_no_range([]) -> + io_lib:format("No values~n", []); +get_frequencies_no_range(L) -> + [H|T] = lists:sort(L), + get_frequencies_no_range(T, H, 1, []). + +get_frequencies_no_range([H|T], H, N, Acc) -> + get_frequencies_no_range(T, H, N+1, Acc); +get_frequencies_no_range([H1|T], H, N, Acc) -> + get_frequencies_no_range(T, H1, 1, [{H,N}|Acc]); +get_frequencies_no_range([], H, N, Acc) -> + lists:reverse( + lists:keysort(2, [{H,N}|Acc])). + +%% get_frequencies_percent(L) -> +%% with_percentage(get_frequencies_no_range(L), length(L)). + + +with_percentage(Fs, Sum) -> + [{Rng,Val,100*Val/Sum} || {Rng,Val} <- Fs]. + + +num_digits(I) -> 1+trunc(math:log(I)/math:log(10)). + +num(Elem, List) -> length(lists:takewhile(fun(E) -> E /= Elem end, List)) + 1. + +%%%---- Just for naming an operation for readability +is_odd(I) -> (I rem 2) == 1. + +in_interval(Value, {Rlow,Rhigh}) -> + try + Rlow=<round(Value) andalso round(Value)=<Rhigh + catch + _:_ -> false + end. + +%%%================================================================ +%%% Statistical functions + +%%%---- Mean value +mean(L = [X|_]) when is_number(X) -> + lists:sum(L) / length(L); +mean(L = [{_Value,_Weight}|_]) -> + SumOfWeights = lists:sum([W||{_,W}<-L]), + WeightedSum = lists:sum([W*V||{V,W}<-L]), + WeightedSum / SumOfWeights; +mean(_) -> + undefined. + +%%%---- Median +median(L = [X|_]) when is_number(X) -> + Len = length(L), + case is_odd(Len) of + true -> + hd(lists:nthtail(Len div 2, L)); + false -> + %% 1) L has at least one element (the one in the is_number test). + %% 2) Length is even. + %% => Length >= 2 + [M1,M2|_] = lists:nthtail((Len div 2)-1, L), + (M1+M2) / 2 + end; +%% integer Weights... +median(L = [{_Value,_Weight}|_]) -> + median( lists:append([lists:duplicate(W,V) || {V,W} <- L]) ); +median(_) -> + undefined. + diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile index e510b74d6a..2371c4ddd0 100644 --- a/lib/common_test/test/Makefile +++ b/lib/common_test/test/Makefile @@ -76,7 +76,8 @@ MODULES= \ ct_unicode_SUITE \ ct_auto_clean_SUITE \ ct_util_SUITE \ - ct_tc_repeat_SUITE + ct_tc_repeat_SUITE \ + ct_property_test_SUITE ERL_FILES= $(MODULES:%=%.erl) HRL_FILES= test_server_test_lib.hrl @@ -133,6 +134,6 @@ release_tests_spec: $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(COVERFILE) "$(RELSYSDIR)" $(INSTALL_DATA) common_test.spec common_test.cover "$(RELSYSDIR)" chmod -R u+w "$(RELSYSDIR)" - @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) + @tar cf - *_SUITE_data property_test | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/common_test/test/ct_property_test_SUITE.erl b/lib/common_test/test/ct_property_test_SUITE.erl new file mode 100644 index 0000000000..1f8c9e08cf --- /dev/null +++ b/lib/common_test/test/ct_property_test_SUITE.erl @@ -0,0 +1,24 @@ +-module(ct_property_test_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +all() -> [prop_sort + ]. + +%%% First prepare Config and compile the property tests for the found tool: +init_per_suite(Config) -> + ct_property_test:init_per_suite(Config). + +end_per_suite(Config) -> + Config. + +%%%================================================================ +%%% Test suites +%%% +prop_sort(Config) -> + ct_property_test:quickcheck( + ct_prop:prop_sort(), + Config + ). diff --git a/lib/common_test/test/property_test/ct_prop.erl b/lib/common_test/test/property_test/ct_prop.erl new file mode 100644 index 0000000000..67ab3f3e6b --- /dev/null +++ b/lib/common_test/test/property_test/ct_prop.erl @@ -0,0 +1,18 @@ +-module(ct_prop). +-export([prop_sort/0]). + +-include_lib("common_test/include/ct_property_test.hrl"). + +prop_sort() -> + ?FORALL(UnSorted, list(), + is_sorted(lists:sort(UnSorted)) + ). + +is_sorted([]) -> + true; +is_sorted([_]) -> + true; +is_sorted([H1,H2|SortedTail]) when H1 =< H2 -> + is_sorted([H2|SortedTail]); +is_sorted(_) -> + false. diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml index 6919d2310f..dc509bbf60 100644 --- a/lib/compiler/doc/src/notes.xml +++ b/lib/compiler/doc/src/notes.xml @@ -32,6 +32,57 @@ <p>This document describes the changes made to the Compiler application.</p> +<section><title>Compiler 7.5.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Fixed a bug that could cause the compiler to reject + valid code that used the <c>is_map_key/2</c> BIF.</p> + <p> + Own Id: OTP-16452 Aux Id: ERL-1161 </p> + </item> + <item> + <p>Fixed a bug that could cause the compiler to reject + valid code that matched the same map key several + times.</p> + <p> + Own Id: OTP-16456 Aux Id: ERL-1163 </p> + </item> + <item> + <p>The compiler could crash when compiling a convoluted + <c>receive</c> statement.</p> + <p> + Own Id: OTP-16466 Aux Id: ERL-1170 </p> + </item> + <item> + <p>The compiler could crash when a fun was created but + never used.</p> + <p>The compiler could crash when compiling the expression + <c>true = 0 / X</c>.</p> + <p> + Own Id: OTP-16467 Aux Id: ERL-1166, ERL-1167 </p> + </item> + </list> + </section> + +</section> + +<section><title>Compiler 7.5.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Fixed a bug in the compiler that could cause it to + reject valid code.</p> + <p> + Own Id: OTP-16385 Aux Id: ERL-1128 </p> + </item> + </list> + </section> + +</section> + <section><title>Compiler 7.5</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/compiler/src/beam_except.erl b/lib/compiler/src/beam_except.erl index 2b9c1b0cf5..04a5e3430a 100644 --- a/lib/compiler/src/beam_except.erl +++ b/lib/compiler/src/beam_except.erl @@ -131,7 +131,8 @@ fix_block(Is, 0) -> fix_block(Is, Words) -> reverse(fix_block_1(Is, Words)). -fix_block_1([{set,[],[],{alloc,Live,{F1,F2,Needed0,F3}}}|Is], Words) -> +fix_block_1([{set,[],[],{alloc,Live,{F1,F2,Needed0,F3}}}|Is], Words) + when is_integer(Needed0) -> case Needed0 - Words of 0 -> Is; diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl index 74f80ca70e..2d5d3dc457 100644 --- a/lib/compiler/src/beam_jump.erl +++ b/lib/compiler/src/beam_jump.erl @@ -278,7 +278,12 @@ share(Is0) -> share_1([{label,L}=Lbl|Is], Dict0, Lbls0, [_|_]=Seq, Acc) -> case maps:find(Seq, Dict0) of error -> - Dict = maps:put(Seq, L, Dict0), + Dict = case is_shareable(Seq) of + true -> + maps:put(Seq, L, Dict0); + false -> + Dict0 + end, share_1(Is, Dict, Lbls0, [], [[Lbl|Seq]|Acc]); {ok,Label} -> Lbls = maps:put(L, Label, Lbls0), @@ -313,6 +318,13 @@ share_1([I|Is], Dict, Lbls, Seq, Acc) -> share_1(Is, Dict, Lbls, [I], Acc) end. +is_shareable([{'catch',_,_}|_]) -> false; +is_shareable([{catch_end,_}|_]) -> false; +is_shareable([{'try',_,_}|_]) -> false; +is_shareable([{try_case,_}|_]) -> false; +is_shareable([{try_end,_}|_]) -> false; +is_shareable(_) -> true. + clean_non_sharable(Dict0, Lbls0) -> %% We are passing in or out of a 'catch' or 'try' block. Remove %% sequences that should not be shared over the boundaries of the diff --git a/lib/compiler/src/beam_kernel_to_ssa.erl b/lib/compiler/src/beam_kernel_to_ssa.erl index df95749fb3..aa04a75804 100644 --- a/lib/compiler/src/beam_kernel_to_ssa.erl +++ b/lib/compiler/src/beam_kernel_to_ssa.erl @@ -702,12 +702,16 @@ bif_cg(#k_bif{op=#k_remote{mod=#k_atom{val=erlang},name=#k_atom{val=Name}}, internal_cg(make_fun, [Name0,Arity0|As], Rs, _Le, St0) -> #k_atom{val=Name} = Name0, #k_int{val=Arity} = Arity0, - [#k_var{name=Dst0}] = Rs, - {Dst,St} = new_ssa_var(Dst0, St0), - Args = ssa_args(As, St), - Local = #b_local{name=#b_literal{val=Name},arity=Arity}, - MakeFun = #b_set{op=make_fun,dst=Dst,args=[Local|Args]}, - {[MakeFun],St}; + case Rs of + [#k_var{name=Dst0}] -> + {Dst,St} = new_ssa_var(Dst0, St0), + Args = ssa_args(As, St), + Local = #b_local{name=#b_literal{val=Name},arity=Arity}, + MakeFun = #b_set{op=make_fun,dst=Dst,args=[Local|Args]}, + {[MakeFun],St}; + [] -> + {[],St0} + end; internal_cg(bs_init_writable=I, As, [#k_var{name=Dst0}], _Le, St0) -> %% This behaves like a function call. {Dst,St} = new_ssa_var(Dst0, St0), diff --git a/lib/compiler/src/beam_peep.erl b/lib/compiler/src/beam_peep.erl index 5730e9704e..2a9fcf1e52 100644 --- a/lib/compiler/src/beam_peep.erl +++ b/lib/compiler/src/beam_peep.erl @@ -22,7 +22,7 @@ -export([module/2]). --import(lists, [reverse/1,member/2]). +-import(lists, [reverse/1,member/2,usort/1]). -spec module(beam_utils:module_code(), [compile:option()]) -> {'ok',beam_utils:module_code()}. @@ -207,7 +207,7 @@ simplify_has_map_fields(Fail, [Src|Keys0], [{test,has_map_fields,Fail,[Src|Keys1]}|Acc]) -> case are_keys_literals(Keys0) andalso are_keys_literals(Keys1) of true -> - Keys = Keys0 ++ Keys1, + Keys = usort(Keys0 ++ Keys1), {ok,[{test,has_map_fields,Fail,[Src|Keys]}|Acc]}; false -> error diff --git a/lib/compiler/src/beam_ssa_bsm.erl b/lib/compiler/src/beam_ssa_bsm.erl index cb36f1c242..d950ef58ec 100644 --- a/lib/compiler/src/beam_ssa_bsm.erl +++ b/lib/compiler/src/beam_ssa_bsm.erl @@ -520,20 +520,29 @@ cm_register_prior(Src, DstCtx, Lbl, State) -> State#cm{ prior_matches = PriorMatches }. cm_combine_tail(Src, DstCtx, Bool, Acc, State0) -> - SrcCtx = match_context_of(Src, State0#cm.definitions), + SrcCtx0 = match_context_of(Src, State0#cm.definitions), + + {SrcCtx, Renames} = cm_combine_tail_1(Bool, DstCtx, SrcCtx0, + State0#cm.renames), %% We replace the source with a context alias as it normally won't be used %% on the happy path after being matched, and the added cost of conversion %% is negligible if it is. Aliases = maps:put(Src, {0, SrcCtx}, State0#cm.match_aliases), - - Renames0 = State0#cm.renames, - Renames = Renames0#{ Bool => #b_literal{val=true}, DstCtx => SrcCtx }, - State = State0#cm{ match_aliases = Aliases, renames = Renames }, {Acc, State}. +cm_combine_tail_1(Bool, DstCtx, SrcCtx, Renames0) -> + case Renames0 of + #{ SrcCtx := New } -> + cm_combine_tail_1(Bool, DstCtx, New, Renames0); + #{} -> + Renames = Renames0#{ Bool => #b_literal{val=true}, + DstCtx => SrcCtx }, + {SrcCtx, Renames} + end. + %% Lets functions accept match contexts as arguments. The parameter must be %% unused before the bs_start_match instruction, and it must be matched in the %% first block. diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index e83c5f7762..12aaa01b6b 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -601,6 +601,158 @@ valfun_1({jump,{f,Lbl}}, Vst) -> %% The next instruction is never executed. kill_state(SuccVst) end); + +valfun_1(return, Vst) -> + assert_durable_term({x,0}, Vst), + verify_return(Vst), + kill_state(Vst); + +valfun_1({set_tuple_element,Src,Tuple,N}, Vst) -> + I = N + 1, + assert_term(Src, Vst), + assert_type({tuple_element,I}, Tuple, Vst), + %% Manually update the tuple type; we can't rely on the ordinary update + %% helpers as we must support overwriting (rather than just widening or + %% narrowing) known elements, and we can't use extract_term either since + %% the source tuple may be aliased. + {tuple, Sz, Es0} = get_term_type(Tuple, Vst), + Es = set_element_type({integer,I}, get_term_type(Src, Vst), Es0), + override_type({tuple, Sz, Es}, Tuple, Vst); + +%% Match instructions. +valfun_1({select_val,Src,{f,Fail},{list,Choices}}, Vst) -> + assert_term(Src, Vst), + assert_choices(Choices), + validate_select_val(Fail, Choices, Src, Vst); +valfun_1({select_tuple_arity,Tuple,{f,Fail},{list,Choices}}, Vst) -> + assert_type(tuple, Tuple, Vst), + assert_arities(Choices), + validate_select_tuple_arity(Fail, Choices, Tuple, Vst); + +%% New bit syntax matching instructions. +valfun_1({test,bs_start_match3,{f,Fail},Live,[Src],Dst}, Vst) -> + validate_bs_start_match(Fail, Live, bsm_match_state(), Src, Dst, Vst); +valfun_1({test,bs_start_match2,{f,Fail},Live,[Src,Slots],Dst}, Vst) -> + validate_bs_start_match(Fail, Live, bsm_match_state(Slots), Src, Dst, Vst); +valfun_1({test,bs_match_string,{f,Fail},[Ctx,_,_]}, Vst) -> + bsm_validate_context(Ctx, Vst), + branch(Fail, Vst, fun(V) -> V end); +valfun_1({test,bs_skip_bits2,{f,Fail},[Ctx,Src,_,_]}, Vst) -> + bsm_validate_context(Ctx, Vst), + assert_term(Src, Vst), + branch(Fail, Vst, fun(V) -> V end); +valfun_1({test,bs_test_tail2,{f,Fail},[Ctx,_]}, Vst) -> + bsm_validate_context(Ctx, Vst), + branch(Fail, Vst, fun(V) -> V end); +valfun_1({test,bs_test_unit,{f,Fail},[Ctx,_]}, Vst) -> + bsm_validate_context(Ctx, Vst), + branch(Fail, Vst, fun(V) -> V end); +valfun_1({test,bs_skip_utf8,{f,Fail},[Ctx,Live,_]}, Vst) -> + validate_bs_skip_utf(Fail, Ctx, Live, Vst); +valfun_1({test,bs_skip_utf16,{f,Fail},[Ctx,Live,_]}, Vst) -> + validate_bs_skip_utf(Fail, Ctx, Live, Vst); +valfun_1({test,bs_skip_utf32,{f,Fail},[Ctx,Live,_]}, Vst) -> + validate_bs_skip_utf(Fail, Ctx, Live, Vst); +valfun_1({test,bs_get_integer2=Op,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) -> + validate_bs_get(Op, Fail, Ctx, Live, {integer, []}, Dst, Vst); +valfun_1({test,bs_get_float2=Op,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) -> + validate_bs_get(Op, Fail, Ctx, Live, {float, []}, Dst, Vst); +valfun_1({test,bs_get_binary2=Op,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) -> + validate_bs_get(Op, Fail, Ctx, Live, binary, Dst, Vst); +valfun_1({test,bs_get_utf8=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) -> + validate_bs_get(Op, Fail, Ctx, Live, {integer, []}, Dst, Vst); +valfun_1({test,bs_get_utf16=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) -> + validate_bs_get(Op, Fail, Ctx, Live, {integer, []}, Dst, Vst); +valfun_1({test,bs_get_utf32=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) -> + validate_bs_get(Op, Fail, Ctx, Live, {integer, []}, Dst, Vst); +valfun_1({bs_save2,Ctx,SavePoint}, Vst) -> + bsm_save(Ctx, SavePoint, Vst); +valfun_1({bs_restore2,Ctx,SavePoint}, Vst) -> + bsm_restore(Ctx, SavePoint, Vst); +valfun_1({bs_get_position, Ctx, Dst, Live}, Vst0) -> + bsm_validate_context(Ctx, Vst0), + verify_live(Live, Vst0), + verify_y_init(Vst0), + Vst = prune_x_regs(Live, Vst0), + create_term(ms_position, bs_get_position, [Ctx], Dst, Vst, Vst0); +valfun_1({bs_set_position, Ctx, Pos}, Vst) -> + bsm_validate_context(Ctx, Vst), + assert_type(ms_position, Pos, Vst), + Vst; +valfun_1({test,has_map_fields,{f,Lbl},Src,{list,List}}, Vst) -> + assert_type(map, Src, Vst), + assert_unique_map_keys(List), + branch(Lbl, Vst, fun(V) -> V end); +valfun_1({test,is_atom,{f,Lbl},[Src]}, Vst) -> + type_test(Lbl, {atom,[]}, Src, Vst); +valfun_1({test,is_binary,{f,Lbl},[Src]}, Vst) -> + type_test(Lbl, binary, Src, Vst); +valfun_1({test,is_bitstr,{f,Lbl},[Src]}, Vst) -> + type_test(Lbl, binary, Src, Vst); +valfun_1({test,is_boolean,{f,Lbl},[Src]}, Vst) -> + type_test(Lbl, bool, Src, Vst); +valfun_1({test,is_float,{f,Lbl},[Src]}, Vst) -> + type_test(Lbl, {float,[]}, Src, Vst); +valfun_1({test,is_tuple,{f,Lbl},[Src]}, Vst) -> + type_test(Lbl, {tuple,[0],#{}}, Src, Vst); +valfun_1({test,is_integer,{f,Lbl},[Src]}, Vst) -> + type_test(Lbl, {integer,[]}, Src, Vst); +valfun_1({test,is_nonempty_list,{f,Lbl},[Src]}, Vst) -> + type_test(Lbl, cons, Src, Vst); +valfun_1({test,is_number,{f,Lbl},[Src]}, Vst) -> + type_test(Lbl, number, Src, Vst); +valfun_1({test,is_list,{f,Lbl},[Src]}, Vst) -> + type_test(Lbl, list, Src, Vst); +valfun_1({test,is_map,{f,Lbl},[Src]}, Vst) -> + type_test(Lbl, map, Src, Vst); +valfun_1({test,is_nil,{f,Lbl},[Src]}, Vst) -> + %% is_nil is an exact check against the 'nil' value, and should not be + %% treated as a simple type test. + assert_term(Src, Vst), + branch(Lbl, Vst, + fun(FailVst) -> + update_ne_types(Src, nil, FailVst) + end, + fun(SuccVst) -> + update_eq_types(Src, nil, SuccVst) + end); +valfun_1({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst) when is_integer(Sz) -> + assert_type(tuple, Tuple, Vst), + Type = {tuple, Sz, #{}}, + type_test(Lbl, Type, Tuple, Vst); +valfun_1({test,is_tagged_tuple,{f,Lbl},[Src,Sz,Atom]}, Vst) -> + assert_term(Src, Vst), + Type = {tuple, Sz, #{ {integer,1} => Atom }}, + type_test(Lbl, Type, Src, Vst); +valfun_1({test,is_eq_exact,{f,Lbl},[Src,Val]=Ss}, Vst) -> + validate_src(Ss, Vst), + branch(Lbl, Vst, + fun(FailVst) -> + update_ne_types(Src, Val, FailVst) + end, + fun(SuccVst) -> + update_eq_types(Src, Val, SuccVst) + end); +valfun_1({test,is_ne_exact,{f,Lbl},[Src,Val]=Ss}, Vst) -> + validate_src(Ss, Vst), + branch(Lbl, Vst, + fun(FailVst) -> + update_eq_types(Src, Val, FailVst) + end, + fun(SuccVst) -> + update_ne_types(Src, Val, SuccVst) + end); +valfun_1({test,_Op,{f,Lbl},Src}, Vst) -> + %% is_pid, is_reference, et cetera. + validate_src(Src, Vst), + branch(Lbl, Vst, fun(V) -> V end); + +%% Map instructions. +valfun_1({put_map_assoc=Op,{f,Fail},Src,Dst,Live,{list,List}}, Vst) -> + verify_put_map(Op, Fail, Src, Dst, Live, List, Vst); +valfun_1({get_map_elements,{f,Fail},Src,{list,List}}, Vst) -> + verify_get_map(Fail, Src, List, Vst); + valfun_1(I, Vst) -> valfun_2(I, Vst). @@ -687,6 +839,8 @@ valfun_3(I, Vst) -> end. %% Instructions that can cause exceptions. +valfun_4({put_map_exact=Op,{f,Fail},Src,Dst,Live,{list,List}}, Vst) -> + verify_put_map(Op, Fail, Src, Dst, Live, List, Vst); valfun_4({apply,Live}, Vst) -> call(apply, Live+2, Vst); valfun_4({apply_last,Live,_}, Vst) -> @@ -782,10 +936,6 @@ valfun_4({gc_bif,Op,{f,Fail},Live,Ss,Dst}, #vst{current=St0}=Vst0) -> %% registers were pruned before the branch. extract_term(Type, {gc_bif,Op}, Ss, Dst, SuccVst, Vst0) end); -valfun_4(return, Vst) -> - assert_durable_term({x,0}, Vst), - verify_return(Vst), - kill_state(Vst); valfun_4({loop_rec,{f,Fail},Dst}, Vst) -> %% This term may not be part of the root set until remove_message/0 is %% executed. If control transfers to the loop_rec_end/1 instruction, no @@ -813,146 +963,8 @@ valfun_4(timeout, Vst0) -> prune_x_regs(0, Vst); valfun_4(send, Vst) -> call(send, 2, Vst); -valfun_4({set_tuple_element,Src,Tuple,N}, Vst) -> - I = N + 1, - assert_term(Src, Vst), - assert_type({tuple_element,I}, Tuple, Vst), - %% Manually update the tuple type; we can't rely on the ordinary update - %% helpers as we must support overwriting (rather than just widening or - %% narrowing) known elements, and we can't use extract_term either since - %% the source tuple may be aliased. - {tuple, Sz, Es0} = get_term_type(Tuple, Vst), - Es = set_element_type({integer,I}, get_term_type(Src, Vst), Es0), - override_type({tuple, Sz, Es}, Tuple, Vst); -%% Match instructions. -valfun_4({select_val,Src,{f,Fail},{list,Choices}}, Vst) -> - assert_term(Src, Vst), - assert_choices(Choices), - validate_select_val(Fail, Choices, Src, Vst); -valfun_4({select_tuple_arity,Tuple,{f,Fail},{list,Choices}}, Vst) -> - assert_type(tuple, Tuple, Vst), - assert_arities(Choices), - validate_select_tuple_arity(Fail, Choices, Tuple, Vst); - -%% New bit syntax matching instructions. -valfun_4({test,bs_start_match3,{f,Fail},Live,[Src],Dst}, Vst) -> - validate_bs_start_match(Fail, Live, bsm_match_state(), Src, Dst, Vst); -valfun_4({test,bs_start_match2,{f,Fail},Live,[Src,Slots],Dst}, Vst) -> - validate_bs_start_match(Fail, Live, bsm_match_state(Slots), Src, Dst, Vst); -valfun_4({test,bs_match_string,{f,Fail},[Ctx,_,_]}, Vst) -> - bsm_validate_context(Ctx, Vst), - branch(Fail, Vst, fun(V) -> V end); -valfun_4({test,bs_skip_bits2,{f,Fail},[Ctx,Src,_,_]}, Vst) -> - bsm_validate_context(Ctx, Vst), - assert_term(Src, Vst), - branch(Fail, Vst, fun(V) -> V end); -valfun_4({test,bs_test_tail2,{f,Fail},[Ctx,_]}, Vst) -> - bsm_validate_context(Ctx, Vst), - branch(Fail, Vst, fun(V) -> V end); -valfun_4({test,bs_test_unit,{f,Fail},[Ctx,_]}, Vst) -> - bsm_validate_context(Ctx, Vst), - branch(Fail, Vst, fun(V) -> V end); -valfun_4({test,bs_skip_utf8,{f,Fail},[Ctx,Live,_]}, Vst) -> - validate_bs_skip_utf(Fail, Ctx, Live, Vst); -valfun_4({test,bs_skip_utf16,{f,Fail},[Ctx,Live,_]}, Vst) -> - validate_bs_skip_utf(Fail, Ctx, Live, Vst); -valfun_4({test,bs_skip_utf32,{f,Fail},[Ctx,Live,_]}, Vst) -> - validate_bs_skip_utf(Fail, Ctx, Live, Vst); -valfun_4({test,bs_get_integer2=Op,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) -> - validate_bs_get(Op, Fail, Ctx, Live, {integer, []}, Dst, Vst); -valfun_4({test,bs_get_float2=Op,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) -> - validate_bs_get(Op, Fail, Ctx, Live, {float, []}, Dst, Vst); -valfun_4({test,bs_get_binary2=Op,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) -> - validate_bs_get(Op, Fail, Ctx, Live, binary, Dst, Vst); -valfun_4({test,bs_get_utf8=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) -> - validate_bs_get(Op, Fail, Ctx, Live, {integer, []}, Dst, Vst); -valfun_4({test,bs_get_utf16=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) -> - validate_bs_get(Op, Fail, Ctx, Live, {integer, []}, Dst, Vst); -valfun_4({test,bs_get_utf32=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) -> - validate_bs_get(Op, Fail, Ctx, Live, {integer, []}, Dst, Vst); -valfun_4({bs_save2,Ctx,SavePoint}, Vst) -> - bsm_save(Ctx, SavePoint, Vst); -valfun_4({bs_restore2,Ctx,SavePoint}, Vst) -> - bsm_restore(Ctx, SavePoint, Vst); -valfun_4({bs_get_position, Ctx, Dst, Live}, Vst0) -> - bsm_validate_context(Ctx, Vst0), - verify_live(Live, Vst0), - verify_y_init(Vst0), - Vst = prune_x_regs(Live, Vst0), - create_term(ms_position, bs_get_position, [Ctx], Dst, Vst, Vst0); -valfun_4({bs_set_position, Ctx, Pos}, Vst) -> - bsm_validate_context(Ctx, Vst), - assert_type(ms_position, Pos, Vst), - Vst; %% Other test instructions. -valfun_4({test,has_map_fields,{f,Lbl},Src,{list,List}}, Vst) -> - assert_type(map, Src, Vst), - assert_unique_map_keys(List), - branch(Lbl, Vst, fun(V) -> V end); -valfun_4({test,is_atom,{f,Lbl},[Src]}, Vst) -> - type_test(Lbl, {atom,[]}, Src, Vst); -valfun_4({test,is_binary,{f,Lbl},[Src]}, Vst) -> - type_test(Lbl, binary, Src, Vst); -valfun_4({test,is_bitstr,{f,Lbl},[Src]}, Vst) -> - type_test(Lbl, binary, Src, Vst); -valfun_4({test,is_boolean,{f,Lbl},[Src]}, Vst) -> - type_test(Lbl, bool, Src, Vst); -valfun_4({test,is_float,{f,Lbl},[Src]}, Vst) -> - type_test(Lbl, {float,[]}, Src, Vst); -valfun_4({test,is_tuple,{f,Lbl},[Src]}, Vst) -> - type_test(Lbl, {tuple,[0],#{}}, Src, Vst); -valfun_4({test,is_integer,{f,Lbl},[Src]}, Vst) -> - type_test(Lbl, {integer,[]}, Src, Vst); -valfun_4({test,is_nonempty_list,{f,Lbl},[Src]}, Vst) -> - type_test(Lbl, cons, Src, Vst); -valfun_4({test,is_number,{f,Lbl},[Src]}, Vst) -> - type_test(Lbl, number, Src, Vst); -valfun_4({test,is_list,{f,Lbl},[Src]}, Vst) -> - type_test(Lbl, list, Src, Vst); -valfun_4({test,is_map,{f,Lbl},[Src]}, Vst) -> - type_test(Lbl, map, Src, Vst); -valfun_4({test,is_nil,{f,Lbl},[Src]}, Vst) -> - %% is_nil is an exact check against the 'nil' value, and should not be - %% treated as a simple type test. - assert_term(Src, Vst), - branch(Lbl, Vst, - fun(FailVst) -> - update_ne_types(Src, nil, FailVst) - end, - fun(SuccVst) -> - update_eq_types(Src, nil, SuccVst) - end); -valfun_4({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst) when is_integer(Sz) -> - assert_type(tuple, Tuple, Vst), - Type = {tuple, Sz, #{}}, - type_test(Lbl, Type, Tuple, Vst); -valfun_4({test,is_tagged_tuple,{f,Lbl},[Src,Sz,Atom]}, Vst) -> - assert_term(Src, Vst), - Type = {tuple, Sz, #{ {integer,1} => Atom }}, - type_test(Lbl, Type, Src, Vst); -valfun_4({test,is_eq_exact,{f,Lbl},[Src,Val]=Ss}, Vst) -> - validate_src(Ss, Vst), - branch(Lbl, Vst, - fun(FailVst) -> - update_ne_types(Src, Val, FailVst) - end, - fun(SuccVst) -> - update_eq_types(Src, Val, SuccVst) - end); -valfun_4({test,is_ne_exact,{f,Lbl},[Src,Val]=Ss}, Vst) -> - validate_src(Ss, Vst), - branch(Lbl, Vst, - fun(FailVst) -> - update_eq_types(Src, Val, FailVst) - end, - fun(SuccVst) -> - update_ne_types(Src, Val, SuccVst) - end); -valfun_4({test,_Op,{f,Lbl},Src}, Vst) -> - %% is_pid, is_reference, et cetera. - validate_src(Src, Vst), - branch(Lbl, Vst, fun(V) -> V end); valfun_4({bs_add,{f,Fail},[A,B,_],Dst}, Vst) -> assert_term(A, Vst), assert_term(B, Vst), @@ -1061,13 +1073,6 @@ valfun_4({bs_put_utf32,{f,Fail},_,Src}, Vst) -> fun(SuccVst) -> update_type(fun meet/2, {integer,[]}, Src, SuccVst) end); -%% Map instructions. -valfun_4({put_map_assoc=Op,{f,Fail},Src,Dst,Live,{list,List}}, Vst) -> - verify_put_map(Op, Fail, Src, Dst, Live, List, Vst); -valfun_4({put_map_exact=Op,{f,Fail},Src,Dst,Live,{list,List}}, Vst) -> - verify_put_map(Op, Fail, Src, Dst, Live, List, Vst); -valfun_4({get_map_elements,{f,Fail},Src,{list,List}}, Vst) -> - verify_get_map(Fail, Src, List, Vst); valfun_4(_, _) -> error(unknown_instruction). @@ -2824,6 +2829,7 @@ bif_return_type(is_function, [_,_], _) -> bool; bif_return_type(is_integer, [_], _) -> bool; bif_return_type(is_list, [_], _) -> bool; bif_return_type(is_map, [_], _) -> bool; +bif_return_type(is_map_key, [_, _], _) -> bool; bif_return_type(is_number, [_], _) -> bool; bif_return_type(is_pid, [_], _) -> bool; bif_return_type(is_port, [_], _) -> bool; diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index 4939a94a92..eb1f69269c 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -2791,7 +2791,11 @@ opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Sub) -> true -> Let1 = Let0#c_let{vars=Vars0,arg=Arg1,body=Body}, post_opt_let(Let1, Sub) - end + end; + {[],Arg,Body} -> + %% The argument for a sequence must be a single value (not + %% #c_values{}). Therefore, we must keep the let. + post_opt_let(#c_let{vars=[],arg=Arg,body=Body}, Sub) end. %% post_opt_let(Let, Sub) diff --git a/lib/compiler/test/beam_except_SUITE.erl b/lib/compiler/test/beam_except_SUITE.erl index f52239f2a8..c8dfc81969 100644 --- a/lib/compiler/test/beam_except_SUITE.erl +++ b/lib/compiler/test/beam_except_SUITE.erl @@ -130,8 +130,14 @@ coverage(_) -> {'EXIT',{function_clause,[{?MODULE,fake_function_clause,[{a,b},42.0],_}|_]}} = (catch fake_function_clause({a,b})), + {'EXIT',{{badmatch,0.0},_}} = (catch coverage_1(id(42))), + {'EXIT',{badarith,_}} = (catch coverage_1(id(a))), ok. +coverage_1(X) -> + %% ERL-1167: Would crash beam_except. + true = 0 / X. + fake_function_clause(A) -> error(function_clause, [A,42.0]). diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl index fe20fad500..a3f42213e8 100644 --- a/lib/compiler/test/beam_validator_SUITE.erl +++ b/lib/compiler/test/beam_validator_SUITE.erl @@ -36,7 +36,8 @@ val_dsetel/1,bad_tuples/1,bad_try_catch_nesting/1, receive_stacked/1,aliased_types/1,type_conflict/1, infer_on_eq/1,infer_dead_value/1, - receive_marker/1]). + receive_marker/1,safe_instructions/1, + missing_return_type/1]). -include_lib("common_test/include/ct.hrl"). @@ -66,7 +67,8 @@ groups() -> map_field_lists,cover_bin_opt,val_dsetel, bad_tuples,bad_try_catch_nesting, receive_stacked,aliased_types,type_conflict, - infer_on_eq,infer_dead_value,receive_marker]}]. + infer_on_eq,infer_dead_value,receive_marker, + safe_instructions,missing_return_type]}]. init_per_suite(Config) -> test_lib:recompile(?MODULE), @@ -735,6 +737,30 @@ receive_marker(Config) when is_list(Config) -> ok. +%% ERL-1128: the validator erroneously thought that many non-throwing +%% instructions like is_eq_exact could throw. +safe_instructions(Config) when is_list(Config) -> + Errors = do_val(safe_instructions, Config), + + [] = Errors, + + ok. + +missing_return_type(Config) when is_list(Config) -> + %% ERL-1161: the validator didn't know that is_map_key always returns a + %% bool. + Map = #{ hello => there }, + true = mrt_1(true), + false = mrt_1(false), + true = mrt_1(is_map_key(id(hello), Map)), + false = mrt_1(is_map_key(id(there), Map)), + + ok. + +mrt_1(Bool) -> + true = is_boolean(Bool), + Bool. + %%%------------------------------------------------------------------------- transform_remove(Remove, Module) -> diff --git a/lib/compiler/test/beam_validator_SUITE_data/safe_instructions.S b/lib/compiler/test/beam_validator_SUITE_data/safe_instructions.S new file mode 100644 index 0000000000..4266c64741 --- /dev/null +++ b/lib/compiler/test/beam_validator_SUITE_data/safe_instructions.S @@ -0,0 +1,102 @@ +{module, safe_instructions}. %% version = 0 + +{exports, [{module_info,0},{module_info,1},{send_request,1}]}. + +{attributes, []}. + +{labels, 18}. + + +{function, send_request, 1, 2}. + {label,1}. + {line,[{location,"t.erl",6}]}. + {func_info,{atom,t},{atom,send_request},1}. + {label,2}. + {allocate,3,1}. + {init,{y,0}}. + {move,{x,0},{y,1}}. + {'try',{y,2},{f,7}}. + {move,nil,{x,0}}. + {line,[{location,"t.erl",8}]}. + {call,1,{f,9}}. + {move,{x,0},{y,0}}. + {'try',{y,1},{f,3}}. + {line,[{location,"t.erl",9}]}. + {call,0,{f,13}}. + {'%',{type_info,{x,0},{atom,ok}}}. + {try_end,{y,1}}. + {jump,{f,4}}. + {label,3}. + {try_case,{y,1}}. + {label,4}. + {test,is_eq_exact,{f,5},[{y,0},{atom,ok}]}. + {move,{atom,ok},{x,0}}. + {jump,{f,6}}. + {label,5}. + {move,{y,0},{x,0}}. + {label,6}. + {try_end,{y,2}}. + {deallocate,3}. + return. + {label,7}. + {try_case,{y,2}}. + {test_heap,2,0}. + {put_list,{y,1},nil,{x,1}}. + {move,{literal,"error: sending file to ~s"},{x,0}}. + {line,[{location,"t.erl",16}]}. + {call_ext,2,{extfunc,lager,debug,2}}. + {move,{atom,ok},{x,0}}. + {deallocate,3}. + return. + + +{function, ftp_cmds, 1, 9}. + {label,8}. + {line,[{location,"t.erl",21}]}. + {func_info,{atom,t},{atom,ftp_cmds},1}. + {label,9}. + {test,is_nonempty_list,{f,11},[{x,0}]}. + {allocate,1,1}. + {get_list,{x,0},{x,0},{y,0}}. + {line,[{location,"t.erl",23}]}. + {call_fun,0}. + {test,is_eq_exact,{f,10},[{x,0},{atom,ok}]}. + {move,{y,0},{x,0}}. + {call_last,1,{f,9},1}. + {label,10}. + {deallocate,1}. + return. + {label,11}. + {test,is_nil,{f,8},[{x,0}]}. + {move,{atom,ok},{x,0}}. + return. + + +{function, ftp_close, 0, 13}. + {label,12}. + {line,[{location,"t.erl",29}]}. + {func_info,{atom,t},{atom,ftp_close},0}. + {label,13}. + {move,{atom,ok},{x,0}}. + return. + + +{function, module_info, 0, 15}. + {label,14}. + {line,[]}. + {func_info,{atom,t},{atom,module_info},0}. + {label,15}. + {move,{atom,t},{x,0}}. + {line,[]}. + {call_ext_only,1,{extfunc,erlang,get_module_info,1}}. + + +{function, module_info, 1, 17}. + {label,16}. + {line,[]}. + {func_info,{atom,t},{atom,module_info},1}. + {label,17}. + {move,{x,0},{x,1}}. + {move,{atom,t},{x,0}}. + {line,[]}. + {call_ext_only,2,{extfunc,erlang,get_module_info,2}}. diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl index a789b82910..2e91f7b53e 100644 --- a/lib/compiler/test/bs_match_SUITE.erl +++ b/lib/compiler/test/bs_match_SUITE.erl @@ -45,7 +45,8 @@ expression_before_match/1,erl_689/1,restore_on_call/1, restore_after_catch/1,matches_on_parameter/1,big_positions/1, matching_meets_apply/1,bs_start_match2_defs/1, - exceptions_after_match_failure/1, bad_phi_paths/1]). + exceptions_after_match_failure/1, bad_phi_paths/1, + combine_empty_segments/1]). -export([coverage_id/1,coverage_external_ignore/2]). @@ -82,7 +83,8 @@ groups() -> expression_before_match,erl_689,restore_on_call, matches_on_parameter,big_positions, matching_meets_apply,bs_start_match2_defs, - exceptions_after_match_failure,bad_phi_paths]}]. + exceptions_after_match_failure,bad_phi_paths, + combine_empty_segments]}]. init_per_suite(Config) -> @@ -2033,4 +2035,14 @@ bad_phi_paths_1(Arg) -> end, id(B). +combine_empty_segments(_Config) -> + <<0,1,2,3>> = combine_empty_segments_1(<<0,1,2,3>>), + ok. + +combine_empty_segments_1(A) -> + <<B/bits>> = A, + <<C/bits>> = B, + <<D/bits>> = C, + D. + id(I) -> I. diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl index adfebd5158..f3bf716037 100644 --- a/lib/compiler/test/core_fold_SUITE.erl +++ b/lib/compiler/test/core_fold_SUITE.erl @@ -28,7 +28,8 @@ mixed_matching_clauses/1,unnecessary_building/1, no_no_file/1,configuration/1,supplies/1, redundant_stack_frame/1,export_from_case/1, - empty_values/1,cover_letrec_effect/1]). + empty_values/1,cover_letrec_effect/1, + receive_effect/1]). -export([foo/0,foo/1,foo/2,foo/3]). @@ -48,7 +49,8 @@ groups() -> mixed_matching_clauses,unnecessary_building, no_no_file,configuration,supplies, redundant_stack_frame,export_from_case, - empty_values,cover_letrec_effect]}]. + empty_values,cover_letrec_effect, + receive_effect]}]. init_per_suite(Config) -> @@ -641,4 +643,12 @@ cover_letrec_effect(_Config) -> end, ok. +receive_effect(_Config) -> + self() ! whatever, + {} = do_receive_effect(), + ok. + +do_receive_effect() -> + {} = receive _ -> {} = {} end. + id(I) -> I. diff --git a/lib/compiler/test/fun_SUITE.erl b/lib/compiler/test/fun_SUITE.erl index 7fc6195e31..89dea136b3 100644 --- a/lib/compiler/test/fun_SUITE.erl +++ b/lib/compiler/test/fun_SUITE.erl @@ -23,7 +23,7 @@ init_per_group/2,end_per_group/2, test1/1,overwritten_fun/1,otp_7202/1,bif_fun/1, external/1,eep37/1,eep37_dup/1,badarity/1,badfun/1, - duplicated_fun/1]). + duplicated_fun/1,unused_fun/1]). %% Internal exports. -export([call_me/1,dup1/0,dup2/0]). @@ -38,7 +38,7 @@ all() -> groups() -> [{p,[parallel], [test1,overwritten_fun,otp_7202,bif_fun,external,eep37, - eep37_dup,badarity,badfun,duplicated_fun]}]. + eep37_dup,badarity,badfun,duplicated_fun,unused_fun]}]. init_per_suite(Config) -> test_lib:recompile(?MODULE), @@ -277,5 +277,13 @@ duplicated_fun(_Config) -> duplicated_fun_helper(_) -> ok. +%% ERL-1166: beam_kernel_to_ssa would crash if a fun was unused. +unused_fun(_Config) -> + _ = fun() -> ok end, + try id(ok) of + _ -> fun() -> ok end + catch _ -> ok end, + ok. + id(I) -> I. diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl index 440b632381..2bcb6133da 100644 --- a/lib/compiler/test/map_SUITE.erl +++ b/lib/compiler/test/map_SUITE.erl @@ -73,7 +73,8 @@ t_reused_key_variable/1, %% new in OTP 22 - t_mixed_clause/1,cover_beam_trim/1 + t_mixed_clause/1,cover_beam_trim/1, + t_duplicate_keys/1 ]). suite() -> []. @@ -130,7 +131,8 @@ all() -> t_reused_key_variable, %% new in OTP 22 - t_mixed_clause,cover_beam_trim + t_mixed_clause,cover_beam_trim, + t_duplicate_keys ]. groups() -> []. @@ -2191,6 +2193,26 @@ do_cover_beam_trim(Id, OldMax, Max, Id, M) -> #{Id:=Val} = id(M), Val. +t_duplicate_keys(Config) when is_list(Config) -> + Map = #{ gurka => gaffel }, + Map = dup_keys_1(id(Map)), + + ok = dup_keys_1(#{ '__struct__' => ok }), + + ok. + +dup_keys_1(Map) -> + case Map of + #{'__struct__' := _} -> + case Map of + #{'__struct__' := _} -> + ok; + O1 -> + O1 + end; + O2 -> + O2 + end. %% aux diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk index f0bcff01da..87cb90522c 100644 --- a/lib/compiler/vsn.mk +++ b/lib/compiler/vsn.mk @@ -1 +1 @@ -COMPILER_VSN = 7.5 +COMPILER_VSN = 7.5.2 diff --git a/lib/crypto/c_src/api_ng.c b/lib/crypto/c_src/api_ng.c index 002a74724d..5fc161f382 100644 --- a/lib/crypto/c_src/api_ng.c +++ b/lib/crypto/c_src/api_ng.c @@ -261,7 +261,7 @@ static int get_update_args(ErlNifEnv* env, if (!enif_inspect_binary(env, indata_arg, &in_data_bin) ) { *return_term = EXCP_BADARG(env, "Bad 2:nd arg"); - goto err; + goto err0; } ASSERT(in_data_bin.size <= INT_MAX); @@ -294,7 +294,7 @@ static int get_update_args(ErlNifEnv* env, if (!enif_alloc_binary((size_t)in_data_bin.size+block_size, &out_data_bin)) { *return_term = EXCP_ERROR(env, "Can't allocate outdata"); - goto err; + goto err0; } if (!EVP_CipherUpdate(ctx_res->ctx, out_data_bin.data, &out_len, in_data_bin.data, in_data_bin.size)) @@ -318,6 +318,8 @@ static int get_update_args(ErlNifEnv* env, return 1; err: + enif_release_binary(&out_data_bin); + err0: return 0; } diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index 5c1f6af016..ff7479f605 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -772,7 +772,7 @@ Do a complete encrypt or decrypt with an AEAD cipher of the full text. </p> <p>For encryption, set the <c>EncryptFlag</c> to <c>true</c> and set the <c>TagOrTagLength</c> - to the wanted size of the tag, that is, the tag length. If the default length is wanted, the + to the wanted size (in bytes) of the tag, that is, the tag length. If the default length is wanted, the <c>crypto_aead/6</c> form may be used. </p> <p>For decryption, set the <c>EncryptFlag</c> to <c>false</c> and put the tag to be checked diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml index 2d238582b2..7333124709 100644 --- a/lib/crypto/doc/src/notes.xml +++ b/lib/crypto/doc/src/notes.xml @@ -31,6 +31,21 @@ </header> <p>This document describes the changes made to the Crypto application.</p> +<section><title>Crypto 4.6.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Constant time comparisons added.</p> + <p> + Own Id: OTP-16376</p> + </item> + </list> + </section> + +</section> + <section><title>Crypto 4.6.3</title> <section><title>Improvements and New Features</title> @@ -296,6 +311,21 @@ </section> +<section><title>Crypto 4.4.2.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Constant time comparisons added.</p> + <p> + Own Id: OTP-16376</p> + </item> + </list> + </section> + +</section> + <section><title>Crypto 4.4.2.1</title> <section><title>Improvements and New Features</title> @@ -610,6 +640,21 @@ </section> +<section><title>Crypto 4.2.2.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Constant time comparisons added.</p> + <p> + Own Id: OTP-16376</p> + </item> + </list> + </section> + +</section> + <section><title>Crypto 4.2.2.3</title> <section><title>Improvements and New Features</title> diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 965697578d..b19fdadfcd 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -24,6 +24,7 @@ -export([start/0, stop/0, info_lib/0, info_fips/0, supports/0, enable_fips_mode/1, version/0, bytes_to_integer/1]). +-export([equal_const_time/2]). -export([cipher_info/1, hash_info/1]). -export([hash/2, hash_init/1, hash_update/2, hash_final/1]). -export([sign/4, sign/5, verify/5, verify/6]). @@ -552,6 +553,35 @@ enable_fips_mode(_) -> ?nif_stub. %%%================================================================ %%% +%%% Compare in constant time +%%% +%%%================================================================ + +%%% Candidate for a NIF + +equal_const_time(X1, X2) -> + equal_const_time(X1, X2, true). + + +equal_const_time(<<B1,R1/binary>>, <<B2,R2/binary>>, Truth) -> + equal_const_time(R1, R2, Truth and (B1 == B2)); +equal_const_time(<<_,R1/binary>>, <<>>, Truth) -> + equal_const_time(R1, <<>>, Truth and false); +equal_const_time(<<>>, <<>>, Truth) -> + Truth; + +equal_const_time([H1|T1], [H2|T2], Truth) -> + equal_const_time(T1, T2, Truth and (H1 == H2)); +equal_const_time([_|T1], [], Truth) -> + equal_const_time(T1, [], Truth and false); +equal_const_time([], [], Truth) -> + Truth; + +equal_const_time(_, _, _) -> + false. + +%%%================================================================ +%%% %%% Hashing %%% %%%================================================================ diff --git a/lib/crypto/test/crypto_bench_SUITE.erl b/lib/crypto/test/crypto_bench_SUITE.erl index c66a27f0c8..d738902ac8 100644 --- a/lib/crypto/test/crypto_bench_SUITE.erl +++ b/lib/crypto/test/crypto_bench_SUITE.erl @@ -67,7 +67,7 @@ init_per_suite(Config0) -> calibrate([{sec_goal,10} | Config1]) catch _:_ -> - {fail, "Crypto did not start"} + {skip, "Crypto did not start"} end. end_per_suite(_Config) -> diff --git a/lib/crypto/test/crypto_property_test_SUITE.erl b/lib/crypto/test/crypto_property_test_SUITE.erl index 75a3d4872f..bcfd5d3de1 100644 --- a/lib/crypto/test/crypto_property_test_SUITE.erl +++ b/lib/crypto/test/crypto_property_test_SUITE.erl @@ -24,13 +24,28 @@ -include_lib("common_test/include/ct.hrl"). -all() -> [encrypt_decrypt__crypto_one_time, - prop__crypto_init_update +all() -> [encrypt_decrypt_one_time, + init_update, + init_update_multi ]. %%% First prepare Config and compile the property tests for the found tool: init_per_suite(Config) -> - ct_property_test:init_per_suite(Config). + case + try crypto:start() of + ok -> true; + {error, already_started} -> true; + _ -> false + catch + _:_ -> false + end + of + true -> + ct_property_test:init_per_suite(Config); + false -> + {skip, "Crypto did not start"} + end. + end_per_suite(Config) -> Config. @@ -38,13 +53,21 @@ end_per_suite(Config) -> %%%================================================================ %%% Test suites %%% -encrypt_decrypt__crypto_one_time(Config) -> +encrypt_decrypt_one_time(Config) -> ct_property_test:quickcheck( crypto_ng_api:prop__crypto_one_time(), Config ). -prop__crypto_init_update(Config) -> + +init_update(Config) -> ct_property_test:quickcheck( crypto_ng_api:prop__crypto_init_update(), Config ). + +init_update_multi(Config) -> + ct_property_test:quickcheck( + crypto_ng_api_stateful:prop__crypto_init_multi(Config), + Config + ). + diff --git a/lib/crypto/test/engine_SUITE.erl b/lib/crypto/test/engine_SUITE.erl index 41cd132734..572f86e3c9 100644 --- a/lib/crypto/test/engine_SUITE.erl +++ b/lib/crypto/test/engine_SUITE.erl @@ -80,7 +80,7 @@ groups() -> init_per_suite(Config) -> - case {os:type(), crypto:info_lib()} of + try {os:type(), crypto:info_lib()} of {_, [{_,_, <<"OpenSSL 1.0.1s-freebsd 1 Mar 2016">>}]} -> {skip, "Problem with engine on OpenSSL 1.0.1s-freebsd"}; @@ -100,6 +100,8 @@ init_per_suite(Config) -> catch _:_ -> {skip, "Crypto did not start"} end + catch _:_ -> + {skip, "Crypto not loaded"} end. end_per_suite(_Config) -> diff --git a/lib/crypto/test/property_test/crypto_ng_api.erl b/lib/crypto/test/property_test/crypto_ng_api.erl index c3a21b0804..14753a4eba 100644 --- a/lib/crypto/test/property_test/crypto_ng_api.erl +++ b/lib/crypto/test/property_test/crypto_ng_api.erl @@ -23,35 +23,7 @@ -compile(export_all). --proptest(eqc). --proptest([triq,proper]). - --ifndef(EQC). --ifndef(PROPER). --ifndef(TRIQ). -%%-define(EQC,true). --define(PROPER,true). -%%-define(TRIQ,true). --endif. --endif. --endif. - --ifdef(EQC). --include_lib("eqc/include/eqc.hrl"). --define(MOD_eqc,eqc). - --else. --ifdef(PROPER). --include_lib("proper/include/proper.hrl"). --define(MOD_eqc,proper). --else. --ifdef(TRIQ). --define(MOD_eqc,triq). --include_lib("triq/include/triq.hrl"). - --endif. --endif. --endif. +-include_lib("common_test/include/ct_property_test.hrl"). -include("crypto_prop_generators.hrl"). @@ -62,9 +34,12 @@ prop__crypto_one_time() -> numtests(10000, ?FORALL({TextPlain, Cipher, Key, IV}, ?LET(Ciph,cipher(), {text_plain(), Ciph, key(Ciph), iv(Ciph)}), - equal(TextPlain, - full_blocks(TextPlain, Cipher), - decrypt_encrypt_one_time(Cipher, Key, IV, TextPlain)) + begin + R = equal(TextPlain, + full_blocks(TextPlain, Cipher), + decrypt_encrypt_one_time(Cipher, Key, IV, TextPlain)), + prt_inf(Cipher, TextPlain, R) + end ) ). @@ -72,12 +47,25 @@ prop__crypto_init_update() -> numtests(10000, ?FORALL({TextPlain, Cipher, Key, IV}, ?LET(Ciph,cipher(), {text_plain(), Ciph, key(Ciph), iv(Ciph)}), - equal(TextPlain, - full_blocks(TextPlain, Cipher), - decrypt_encrypt_init_update(Cipher, Key, IV, TextPlain)) - ) + begin + R = equal(TextPlain, + full_blocks(TextPlain, Cipher), + decrypt_encrypt_init_update(Cipher, Key, IV, TextPlain)), + prt_inf(Cipher, TextPlain, R) + end) ). +prt_inf(Cipher, TextPlain, R) -> + aggregate(ct_property_test:title("text lengths", + ct_property_test:print_frequency_ranges(), + fun ct:pal/2), + [iolist_size(TextPlain)], + aggregate(ct_property_test:title("ciphers", + ct_property_test:print_frequency(), + fun ct:pal/2), + [Cipher], + R)). + %%%================================================================ %%% Lib diff --git a/lib/crypto/test/property_test/crypto_ng_api_stateful.erl b/lib/crypto/test/property_test/crypto_ng_api_stateful.erl new file mode 100644 index 0000000000..21bbe272dc --- /dev/null +++ b/lib/crypto/test/property_test/crypto_ng_api_stateful.erl @@ -0,0 +1,161 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2017. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(crypto_ng_api_stateful). + +-compile(export_all). + +-include_lib("common_test/include/ct_property_test.hrl"). +-include("crypto_prop_generators.hrl"). + +%%%================================================================ +%%% Properties: + +prop__crypto_init_multi(Config) -> + numtests(300, + ?FORALL(Cmds, parallel_commands(?MODULE), + begin + RunResult = run_parallel_commands(?MODULE, Cmds), + ct_property_test:present_result(?MODULE, Cmds, RunResult, Config) + end)). + +%%%================================================================ +-define(call(F,As), {call,?MODULE,F,As}). + +initial_state() -> + #{}. + + +command(S) -> + frequency( + lists:flatten( + [ + {max(0,5-maps:size(S)), + ?call(init_crypto, ?LET(Ciph, cipher(), + [Ciph, key(Ciph), iv(Ciph), block_size(Ciph)]))}, + + [{10, ?call(encrypt, [oneof(refs(S)), binary()]) + } + || refs(S) =/= []], + + [{30, ?call(decrypt, ?LET({Refs,{_,[CT|_]}}, oneof(Sc), + [Refs, CT])) + } + || Sc <- [[R || {_,{_,Cs}} = R <- maps:to_list(S), + Cs =/= []] + ], + Sc =/= [] ], +[] ])). + + +precondition(S, {call,?MODULE,decrypt,[Refs,CrT]}) -> + %% io:format("precondition(1) ~p Args = ~p, S = ~p", [decrypt,[Refs,CrT],S]), + case maps:get(Refs, S) of + {_BlockSize, [CrT|_]} -> + %% io:format(" (sym) true~n",[]), + true; + {_BlockSize, [CrT|_], _} -> + %% io:format(" (dyn) true~n",[]), + true; + _Other -> + %% io:format(" _Other=~p false~n",[_Other]), + false + end; +precondition(_S, {call,?MODULE,_Name,_Args}) -> + %% io:format("precondition ~p Args = ~p, S = ~p~n", [_Name,_Args,_S]), + true. + + +postcondition(_D, _Call, error) -> + %% io:format("postcondition ~p:~p error _Call = ~p~n",[?MODULE,?LINE,_Call]), + false; +postcondition(D, {call,?MODULE,decrypt,[Refs,_CrT]}, Result) -> + #{Refs := {_BlockSize, _CT, PT}} = D, + Size = size(Result), + <<Expect:Size/binary, _/binary>> = PT, + Expect == Result; +postcondition(_D, _Call, _Result) -> + true. + + +symbolic({var,_}) -> true; +symbolic(_) -> false. + + +next_state(S, Refs, {call,?MODULE,init_crypto,[_Cipher, _Key, _IV, BlockSize]}=_C) -> + case symbolic(Refs) of + true -> + S#{Refs => {BlockSize, []} }; + false -> + S#{Refs => {BlockSize, [], <<>>} } + end; + +next_state(S, Res, {call,?MODULE,encrypt,[Refs,Ptxt]}=_C) -> +%% io:format("next_state enrypt Refs = ~p, S = ~p~n", [Refs,S]), + case S of + #{Refs := {BlockSize, Cs}} -> + S#{Refs := {BlockSize, Cs++[Res]}}; + + #{Refs := {BlockSize, Cs,Ps}} -> + S#{Refs := {BlockSize, Cs++[Res], <<Ps/binary,Ptxt/binary>>}} + end; + +next_state(S, Result, {call,?MODULE,decrypt,[Refs,CrT]}=_C) -> +%% io:format("next_state decrypt Refs = ~p, CrT = ~p, S = ~p~n", [Refs,CrT,S]), + case S of + #{Refs := {BlockSize, [CrT|Cs]}} -> + S#{Refs := {BlockSize, Cs}}; + + #{Refs := {BlockSize, [CrT|Cs], Ps0}} -> + Sz = size(Result), + <<Result:Sz/binary,Ps/binary>> = Ps0, + S#{Refs := {BlockSize,Cs,Ps}} + end. + +%%%================================================================ +-define(ok_error(X), + try X of + __R -> %% io:format("~p:~p ok! Result = ~p~n", [?MODULE,?LINE,__R]), + __R + catch + _CC:_EE:_SS -> + io:format("******* ~p:~p ~p ~p~n~p", [?MODULE,?LINE,_CC,_EE,_SS]), + error + end). + +init_crypto(Cipher, Key, IV, _BlockSize) -> + %% io:format("~p:~p init_crypto~n (~p,~n ~p,~n ~p,~n ~p)", [?MODULE,?LINE,Cipher,Key,IV,_BlockSize]), + ?ok_error({ + crypto:crypto_init(Cipher, Key, IV, true), + crypto:crypto_init(Cipher, Key, IV, false) + }). + +encrypt({EncRef,_DecRef}, PlainText) -> + %% io:format("~p:~p encrypt~n (~p,~n ~p) ", [?MODULE,?LINE,EncRef,PlainText]), + ?ok_error(crypto:crypto_update(EncRef, PlainText)). + +decrypt({_EncRef,DecRef}, CT) -> + %% io:format("~p:~p decrypt~n (~p,~n ~p)", [?MODULE,?LINE,DecRef,CT]), + ?ok_error(crypto:crypto_update(DecRef, CT)). + +%%%---------------------------------------------------------------- +refs(S) -> [Refs || {Refs,_} <- maps:to_list(S)]. + diff --git a/lib/crypto/test/property_test/crypto_prop_generators.erl b/lib/crypto/test/property_test/crypto_prop_generators.erl index 5a53a000f0..3a7e9ecb87 100644 --- a/lib/crypto/test/property_test/crypto_prop_generators.erl +++ b/lib/crypto/test/property_test/crypto_prop_generators.erl @@ -21,37 +21,19 @@ -module(crypto_prop_generators). --compile(export_all). - --proptest(eqc). --proptest([triq,proper]). - --ifndef(EQC). --ifndef(PROPER). --ifndef(TRIQ). -%%-define(EQC,true). --define(PROPER,true). -%%-define(TRIQ,true). --endif. --endif. --endif. - --ifdef(EQC). --include_lib("eqc/include/eqc.hrl"). --define(MOD_eqc,eqc). - --else. --ifdef(PROPER). --include_lib("proper/include/proper.hrl"). --define(MOD_eqc,proper). --else. --ifdef(TRIQ). --define(MOD_eqc,triq). --include_lib("triq/include/triq.hrl"). - --endif. --endif. --endif. +-export([text_plain/0, + cipher/0, + key/1, + iv/1, + iolist/0, + mybinary/1, + non_aead_ciphers/0, + block_size/1, + key_length/1, + iv_length/1 + ]). + +-include_lib("common_test/include/ct_property_test.hrl"). %%%================================================================ %%% Generators diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk index c773529c84..b86553d0a6 100644 --- a/lib/crypto/vsn.mk +++ b/lib/crypto/vsn.mk @@ -1 +1 @@ -CRYPTO_VSN = 4.6.3 +CRYPTO_VSN = 4.6.4 diff --git a/lib/dialyzer/doc/src/typer.xml b/lib/dialyzer/doc/src/typer.xml index 1cfbe94807..524956ed4b 100644 --- a/lib/dialyzer/doc/src/typer.xml +++ b/lib/dialyzer/doc/src/typer.xml @@ -64,7 +64,7 @@ typer [--help] [--version] [--plt PLT] [--edoc] <taglist> - <tag><c>--r</c></tag> + <tag><c>-r</c></tag> <item> <p>Search directories recursively for .erl files below them.</p> </item> diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index 85522c99b2..aa4eb6ad45 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -23,7 +23,7 @@ <copyright> <year>2011</year> -<year>2019</year> +<year>2020</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -1046,8 +1046,9 @@ each node from which requests are sent.</p> <tag><c>&transport_opt;</c></tag> <item> <p> -Any transport option except <c>applications</c> or -<c>capabilities</c>. +Any transport option except <c>applications</c>, +<c>capabilities</c>, <c>transport_config</c>, and +<c>transport_module</c>. Used as defaults for transport configuration, values passed to &add_transport; overriding values configured on the service.</p> </item> @@ -1398,10 +1399,11 @@ Options <c>monitor</c> and <c>link</c> are ignored in the list-valued case. An MFA is applied with an additional term prepended to its argument list, and should return either the pid of the handler process that -invokes <c>diameter_traffic:request/1</c> on the term in order to +invokes <c>diameter_traffic:request/1</c> on the argument in order to process the request, or the atom <c>discard</c>. -The handler process need not be local, but diameter must be started on -the remote node.</p> +The handler process need not be local, and diameter need not be +started on the remote node, but diameter and relevant application +callbacks must be on the code path.</p> <p> Defaults to the empty list.</p> diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml index 8dcba93273..780b0b894a 100644 --- a/lib/diameter/doc/src/notes.xml +++ b/lib/diameter/doc/src/notes.xml @@ -43,6 +43,57 @@ first.</p> <!-- ===================================================================== --> +<section><title>diameter 2.2.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The possibility of choosing a handler process for an + incoming Diameter request with a configured MFA was + documented in OTP 20.0, but counters (with + {traffic_counters, true}) were not incremented when this + process was on a remote node. Counters are now + incremented on the node that configures the transport in + question.</p> + <p> + Introduced in OTP 21.3.</p> + <p> + Own Id: OTP-16457</p> + </item> + <item> + <p> + Transport options differing from those passed to + diameter:add_transport/2 were used in several situations: + when starting a transport process after connect_timer + expiry after an initial connection attempt has failed, + when starting a transport process after a connection has + been accepted, when sending events, when returning + options in diameter:service_info/2, and possibly more. In + particular, the following configuration options to + diameter:add_transport/2 were dropped: avp_dictionaries, + incoming_maxlen, spawn_opt, strict_mbit.</p> + <p> + Moreover, any service options mistakenly passed to + diameter:add_transport/2 were interpreted as such, + instead of being ignored as the documentation states, + with the consequence that outgoing and incoming requests + saw different values of some options, some were always + taken from transport options, and others from service + options.</p> + <p> + diameter:add_transport/2 must be called in new code for + the fix to have effect.</p> + <p> + Introduced in OTP 20.1.</p> + <p> + Own Id: OTP-16459</p> + </item> + </list> + </section> + +</section> + <section><title>diameter 2.2.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/diameter/examples/code/GNUmakefile b/lib/diameter/examples/code/GNUmakefile index f5c2e5f869..9efb966e65 100644 --- a/lib/diameter/examples/code/GNUmakefile +++ b/lib/diameter/examples/code/GNUmakefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2010-2015. All Rights Reserved. +# Copyright Ericsson AB 2010-2020. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,10 +18,10 @@ # %CopyrightEnd% # -EXAMPLES = client server relay # redirect proxy +EXAMPLES = client server relay redirect # proxy CALLBACKS = $(EXAMPLES:%=%_cb) -MODULES = node $(EXAMPLES) $(EXAMPLES:%=%_cb) +MODULES = $(EXAMPLES) $(EXAMPLES:%=%_cb) BEAM = $(MODULES:%=%.beam) diff --git a/lib/diameter/examples/code/README b/lib/diameter/examples/code/README new file mode 100644 index 0000000000..b639849390 --- /dev/null +++ b/lib/diameter/examples/code/README @@ -0,0 +1,105 @@ + +This directory contains small examples of simple Diameter nodes. They +don't do everything a real node should do obviously, but they're a +starting point. + +Each example consists of an interface module with functions to start +and stop a service and add transport, and a corresponding callback +module for the Diameter application the service configures. A real +node might support multiple Diameter applications, either with the +same callback or sharing a common callback, maybe using extra +arguments to distinguish between callbacks for the different +applications. + +The interface functions are named start, stop, connect, and listen; +the client example also has a call function that sends an example +message. Service names should be atoms in these modules (since the +default setting of Origin-Host assumes this), but doesn't need to be +in general. Options are passed directly to diameter:start_service/2 +and diameter:add_transport/2, with some additional convenience options +for the latter; in particular, the atoms tcp and sctp to connect to or +listen on default endpoints (127.0.01:3868), or tuples with protocol +and another endpoint {eg. {tcp, {192,168,1,5}, 3869}. This convenience +makes the simplest usage like this in an Erlang shell: + + diameter:start(). + server:start(). + server:listen(tcp). + client:start(). + client:connect(tcp). + client:call(). + +Or put a relay between the client and server: + + diameter:start(). + server:start(). + server:listen(sctp). + relay:start(). + relay:connect(sctp). + relay:listen(tcp). + client:start(). + client:connect(tcp). + client:call(). + +Most services should probably set the following options, which have +been added to solve various problems over the years, while the +defaults have not been changed for backwards compatibility. + + {decode_format, map} + + Provide decoded messages in #diameter_packet.msg of a + handle_request or handle_answer callback in the form [Name | Avps], + where Name is the atom() name of the message in the (Diameter) + application dictionary in question (eg. 'ACR') and Avps is a map + of AVP values. This avoids compile-time dependencies on the + generated records and their (generally) long-winded names. The + hrl files generated from dictionaries are best avoided. + + {restrict_connections, false} + + Accept multiple connections with the same peer. By default, + diameter will only accept a single connection with a given peer, + which the Diameter RFC can be interpreted as requiring. In + practice, wanting multiple connections to the same peer is + common. + + {string_decode, false} + + Disable the decoding of string-ish Diameter types to Erlang + strings, leaving them as binary(). Strings can be costly if + decoded Diameter messages are passed between processes. + + {strict_mbit, false} + + Relax the interpretation of the M-bit so that an AVP setting + this bit is not regarded as a 5001 error when the message + grammar in question doesn't explicitly list the AVP. Without + this, a Redirect-Host AVP received in a 3006 + (DIAMETER_REDIRECT_INDICATION) answer-message from a redirect + agent will be treated as error, and there have been other + situations in which the default value has caused problems (or at + least surprise). + + {call_mutates_state, false} (on each configured application) + + Avoid pick_peer and subsequent callbacks going through a single + process, which can be a bottleneck. Better to use the tid() of + an ets table as (immutable) state, for example. + +Other options that are particularly useful/necessary in some +situations are: + + pool_size - Create a pool of accepting processes for a listening + transport, to avoid refused connections when many peers + connect simultaneously. Can also be used on a connecting + transport to establish multiple connections with a + single call to diameter:add_transport/2. + + sequence - Ensure unique End-to-End and Hop-by-Hop identifiers over + a cluster of Erlang nodes. + + share_peers - Share peer connections across a cluster of + use_shared_peers Erlang nodes. + + spawn_opt - Replace diameter's spawning of a new handler process for + each request by something else: diameter_dist is an example. diff --git a/lib/diameter/examples/code/client.erl b/lib/diameter/examples/code/client.erl index 0864919cdd..04c91e37fa 100644 --- a/lib/diameter/examples/code/client.erl +++ b/lib/diameter/examples/code/client.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2017. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,120 +18,173 @@ %% %CopyrightEnd% %% +-module(client). + %% -%% An example Diameter client that can sends base protocol RAR +%% An example Diameter client that can sends base protocol ACR %% requests to a connected peer. %% -%% The simplest usage is as follows this to connect to a server -%% listening on the default port on the local host, assuming diameter -%% is already started (eg. diameter:start()). +%% Simplest usage to connect to a server listening on TCP at +%% 127.0.0.1:3868: %% %% client:start(). %% client:connect(tcp). %% client:call(). %% -%% The first call starts the a service with the default name of -%% ?MODULE, the second defines a connecting transport that results in -%% a connection to the peer (if it's listening), the third sends it a -%% RAR and returns the answer. -%% - --module(client). - --include_lib("diameter/include/diameter.hrl"). -export([start/1, %% start a service start/2, %% connect/2, %% add a connecting transport - call/1, %% send using the record encoding - cast/1, %% send using the list encoding and detached + call/2, %% send a request stop/1]). %% stop a service -%% A real application would typically choose an encoding and whether -%% they want the call to return the answer or not. Sending with -%% both the record and list encoding here, one detached and one not, -%% is just for demonstration purposes. %% Convenience functions using the default service name. -export([start/0, connect/1, stop/0, call/0, - cast/0]). + call/1]). -define(DEF_SVC_NAME, ?MODULE). -define(L, atom_to_list). +-define(LOOPBACK, {127,0,0,1}). -%% The service configuration. As in the server example, a client -%% supporting multiple Diameter applications may or may not want to -%% configure a common callback module on all applications. +%% Service configuration. -define(SERVICE(Name), [{'Origin-Host', ?L(Name) ++ ".example.com"}, {'Origin-Realm', "example.com"}, {'Vendor-Id', 0}, {'Product-Name', "Client"}, {'Auth-Application-Id', [0]}, - {string_decode, false}, {decode_format, map}, + {restrict_connections, false}, + {strict_mbit, false}, + {string_decode, false}, {application, [{alias, common}, {dictionary, diameter_gen_base_rfc6733}, - {module, client_cb}]}]). + {module, client_cb}, + {answer_errors, callback}, + {call_mutates_state, false}]}]). -%% start/1 +%% start/2 -start(Name) - when is_atom(Name) -> - start(Name, []); +start(Name, Opts) -> + Defaults = [T || {K,_} = T <- ?SERVICE(Name), + not lists:keymember(K, 1, Opts)], + diameter:start_service(Name, Opts ++ Defaults). -start(Opts) - when is_list(Opts) -> +%% start/1 + +start(Opts) -> start(?DEF_SVC_NAME, Opts). %% start/0 start() -> - start(?DEF_SVC_NAME). + start(?DEF_SVC_NAME, []). -%% start/2 +%% connect/1 -start(Name, Opts) -> - node:start(Name, Opts ++ [T || {K,_} = T <- ?SERVICE(Name), - false == lists:keymember(K, 1, Opts)]). +connect(Opts) -> + connect(?DEF_SVC_NAME, Opts). %% connect/2 +connect(Name, Opts) + when is_list(Opts) -> + diameter:add_transport(Name, {connect, lists:flatmap(fun opts/1, Opts)}); + +%% backwards compatibility with old config +connect(Name, {T, Opts}) -> + connect(Name, [T | Opts]); connect(Name, T) -> - node:connect(Name, T). + connect(Name, [T]). -connect(T) -> - connect(?DEF_SVC_NAME, T). +%% call/2 -%% call/1 +call(Name, #{'Session-Id' := _} = Avps) -> + Defaults = #{'Destination-Realm' => "example.com", + 'Accounting-Record-Type' => 1, %% EVENT_RECORD + 'Accounting-Record-Number' => 0}, + ACR = ['ACR' | maps:merge(Defaults, Avps)], + diameter:call(Name, common, ACR, []); -call(Name) -> - SId = diameter:session_id(?L(Name)), - RAR = ['RAR' | #{'Session-Id' => SId, - 'Auth-Application-Id' => 0, - 'Re-Auth-Request-Type' => 0}], - diameter:call(Name, common, RAR, []). +call(Name, #{} = Avps) -> + call(Name, Avps#{'Session-Id' => diameter:session_id(?L(Name))}); -call() -> - call(?DEF_SVC_NAME). +call(Name, Avps) -> + call(Name, maps:from_list(Avps)). -%% cast/1 +%% call/1 + +call(Avps) -> + call(?DEF_SVC_NAME, Avps). -cast(Name) -> - SId = diameter:session_id(?L(Name)), - RAR = ['RAR', {'Session-Id', SId}, - {'Auth-Application-Id', 0}, - {'Re-Auth-Request-Type', 1}], - diameter:call(Name, common, RAR, [detach]). +%% call/0 -cast() -> - cast(?DEF_SVC_NAME). +call() -> + call(?DEF_SVC_NAME, #{}). %% stop/1 stop(Name) -> - node:stop(Name). + diameter:stop_service(Name). stop() -> stop(?DEF_SVC_NAME). + +%% =========================================================================== + +%% opts/1 +%% +%% Map some terms to transport_module/transport_config pairs as a +%% convenience, pass everything else unmodified. + +opts(T) + when T == any; + T == tcp; + T == sctp -> + opts({T, loopback, 3868}); + +opts({T, RA, RP}) -> + opts({T, [], RA, RP}); + +opts({T, loopback, RA, RP}) -> + opts({T, ?LOOPBACK, RA, RP}); + +opts({T, LA, RA, RP}) + when is_tuple(LA) -> + opts({T, [{ip, LA}], RA, RP}); + +opts({any, Opts, RA, RP}) -> + All = Opts ++ opts(RA, RP), + [{transport_module, diameter_sctp}, + {transport_config, All, 2000}, + {transport_module, diameter_tcp}, + {transport_config, All}]; + +opts({tcp, Opts, RA, RP}) -> + opts({diameter_tcp, Opts, RA, RP}); + +opts({sctp, Opts, RA, RP}) -> + opts({diameter_sctp, Opts, RA, RP}); + +opts({Mod, Opts, loopback, RP}) -> + opts({Mod, Opts, ?LOOPBACK, RP}); + +opts({Mod, Opts, RA, default}) -> + opts({Mod, Opts, RA, 3868}); + +opts({Mod, Opts, RA, RP}) -> + [{transport_module, Mod}, + {transport_config, opts(RA, RP) ++ Opts}]; + +opts(T) -> + [T]. + +%% opts/2 + +opts(loopback, RP) -> + opts(?LOOPBACK, RP); + +opts(RA, RP) -> + [{raddr, RA}, {rport, RP}, {reuseaddr, true}]. diff --git a/lib/diameter/examples/code/client_cb.erl b/lib/diameter/examples/code/client_cb.erl index af2d4d6da7..7721c47a9a 100644 --- a/lib/diameter/examples/code/client_cb.erl +++ b/lib/diameter/examples/code/client_cb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2017. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ -module(client_cb). -include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/include/diameter_gen_base_rfc3588.hrl"). %% diameter callbacks -export([peer_up/3, @@ -50,28 +49,16 @@ pick_peer([Peer | _], _, _SvcName, _State) -> %% prepare_request/3 -prepare_request(#diameter_packet{msg = ['RAR' = T | Avps]}, _, {_, Caps}) -> - #diameter_caps{origin_host = {OH, DH}, - origin_realm = {OR, DR}} +prepare_request(#diameter_packet{msg = [Name | Avps]}, _, {_, Caps}) -> + #diameter_caps{origin_host = {OH, _}, + origin_realm = {OR, _}} = Caps, - - {send, [T | if is_map(Avps) -> - Avps#{'Origin-Host' => OH, - 'Origin-Realm' => OR, - 'Destination-Host' => DH, - 'Destination-Realm' => DR}; - is_list(Avps) -> - [{'Origin-Host', OH}, - {'Origin-Realm', OR}, - {'Destination-Host', DH}, - {'Destination-Realm', DR} - | Avps] - end]}. + {send, [Name | Avps#{'Origin-Host' => OH, 'Origin-Realm' => OR}]}. %% prepare_retransmit/3 -prepare_retransmit(Packet, SvcName, Peer) -> - prepare_request(Packet, SvcName, Peer). +prepare_retransmit(Pkt, _SvcName, _Peer) -> + {send, Pkt}. %% handle_answer/4 @@ -86,4 +73,4 @@ handle_error(Reason, _Request, _SvcName, _Peer) -> %% handle_request/3 handle_request(_Packet, _SvcName, _Peer) -> - erlang:error({unexpected, ?MODULE, ?LINE}). + {answer_message, 3001}. %% DIAMETER_COMMAND_UNSUPPORTED diff --git a/lib/diameter/examples/code/node.erl b/lib/diameter/examples/code/node.erl deleted file mode 100644 index 77810bf893..0000000000 --- a/lib/diameter/examples/code/node.erl +++ /dev/null @@ -1,202 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2010-2018. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - -%% -%% A library module used by the example Diameter nodes. Does little -%% more than provide an alternate/simplified transport configuration. -%% - --module(node). - --export([start/2, - listen/2, - connect/2, - stop/1]). - --export([message/3]). - --type protocol() - :: tcp | sctp. - --type ip_address() - :: default - | inet:ip_address(). - --type server_transport() - :: protocol() - | {protocol(), ip_address(), non_neg_integer()}. - --type server_opts() - :: server_transport() - | {server_transport(), [diameter:transport_opt()]} - | [diameter:transport_opt()]. - --type client_transport() - :: protocol() | any - | {protocol() | any, ip_address(), non_neg_integer()} - | {protocol() | any, ip_address(), ip_address(), non_neg_integer()}. - --type client_opts() - :: client_transport() - | {client_transport(), [diameter:transport_opt()]} - | [diameter:transport_opt()]. - -%% The server_transport() and client_transport() config is just -%% convenience: arbitrary options can be specifed as a -%% [diameter:transport_opt()]. - --define(DEFAULT_PORT, 3868). - -%% --------------------------------------------------------------------------- -%% Interface functions -%% --------------------------------------------------------------------------- - -%% start/2 - --spec start(diameter:service_name(), [diameter:service_opt()]) - -> ok - | {error, term()}. - -start(Name, Opts) - when is_atom(Name), is_list(Opts) -> - diameter:start_service(Name, Opts). - -%% connect/2 - --spec connect(diameter:service_name(), client_opts()) - -> {ok, diameter:transport_ref()} - | {error, term()}. - -connect(Name, Opts) - when is_list(Opts) -> - diameter:add_transport(Name, {connect, Opts}); - -connect(Name, {T, Opts}) -> - connect(Name, Opts ++ client_opts(T)); - -connect(Name, T) -> - connect(Name, [{connect_timer, 5000} | client_opts(T)]). - -%% listen/2 - --spec listen(diameter:service_name(), server_opts()) - -> {ok, diameter:transport_ref()} - | {error, term()}. - -listen(Name, Opts) - when is_list(Opts) -> - diameter:add_transport(Name, {listen, Opts}); - -listen(Name, {T, Opts}) -> - listen(Name, Opts ++ server_opts(T)); - -listen(Name, T) -> - listen(Name, server_opts(T)). - -%% stop/1 - --spec stop(diameter:service_name()) - -> ok - | {error, term()}. - -stop(Name) -> - diameter:stop_service(Name). - -%% --------------------------------------------------------------------------- -%% Internal functions -%% --------------------------------------------------------------------------- - -%% server_opts/1 -%% -%% Return transport options for a listening transport. - -server_opts({T, Addr, Port}) -> - [{transport_module, tmod(T)}, - {transport_config, [{reuseaddr, true}, - {sender, true}, - {message_cb, [fun ?MODULE:message/3, 0]}, - {ip, addr(Addr)}, - {port, Port}]}]; - -server_opts(T) -> - server_opts({T, loopback, ?DEFAULT_PORT}). - -%% client_opts/1 -%% -%% Return transport options for a connecting transport. - -client_opts({T, LA, RA, RP}) - when T == all; %% backwards compatibility - T == any -> - [[S, {C,Os}], T] = [client_opts({P, LA, RA, RP}) || P <- [sctp,tcp]], - [S, {C,Os,2000} | T]; - -client_opts({T, LA, RA, RP}) -> - [{transport_module, tmod(T)}, - {transport_config, [{raddr, addr(RA)}, - {rport, RP}, - {reuseaddr, true} - | ip(LA)]}]; - -client_opts({T, RA, RP}) -> - client_opts({T, default, RA, RP}); - -client_opts(T) -> - client_opts({T, loopback, loopback, ?DEFAULT_PORT}). - -%% --------------------------------------------------------------------------- - -tmod(tcp) -> diameter_tcp; -tmod(sctp) -> diameter_sctp. - -ip(default) -> - []; -ip(loopback) -> - [{ip, {127,0,0,1}}]; -ip(Addr) -> - [{ip, Addr}]. - -addr(loopback) -> - {127,0,0,1}; -addr(A) -> - A. - -%% --------------------------------------------------------------------------- - -%% message/3 -%% -%% Simple message callback that limits the number of concurrent -%% requests on the peer connection in question. - -%% Incoming request. -message(recv, <<_:32, 1:1, _/bits>> = Bin, N) -> - [Bin, N < 32, fun ?MODULE:message/3, N+1]; - -%% Outgoing request. -message(ack, <<_:32, 1:1, _/bits>>, _) -> - []; - -%% Incoming answer or request discarded. -message(ack, _, N) -> - [N =< 32, fun ?MODULE:message/3, N-1]; - -%% Outgoing message or incoming answer. -message(_, Bin, _) -> - [Bin]. diff --git a/lib/diameter/examples/code/redirect.erl b/lib/diameter/examples/code/redirect.erl index 6934e54507..1f9749ba84 100644 --- a/lib/diameter/examples/code/redirect.erl +++ b/lib/diameter/examples/code/redirect.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2016. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -20,52 +20,80 @@ -module(redirect). --include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/include/diameter_gen_base_rfc3588.hrl"). +%% +%% An example Diameter redirect agent. +%% +%% Simplest usage to listen on TCP at 127.0.0.1:3868. +%% +%% redirect:start(). +%% redirect:listen(tcp). +%% +%% Interface. -export([start/1, listen/2, stop/1]). +%% Convenience functions using the default service name. -export([start/0, listen/1, stop/0]). --define(APP_ALIAS, ?MODULE). --define(SVC_NAME, ?MODULE). --define(CALLBACK_MOD, redirect_mod). +-define(DEF_SVC_NAME, ?MODULE). %% The service configuration. -define(SERVICE(Name), [{'Origin-Host', atom_to_list(Name) ++ ".example.com"}, {'Origin-Realm', "example.com"}, {'Vendor-Id', 193}, {'Product-Name', "RedirectAgent"}, - {'Auth-Application-Id', [?DIAMETER_APP_ID_RELAY]}, - {application, [{alias, ?APP_ALIAS}, - {dictionary, ?DIAMETER_DICT_RELAY}, - {module, ?CALLBACK_MOD}]}]). + {'Auth-Application-Id', [16#FFFFFFFF]}, + {decode_format, map}, + {restrict_connections, false}, + {strict_mbit, false}, + {string_decode, false}, + {application, [{alias, redirect}, + {dictionary, diameter_gen_relay}, + {module, redirect_cb}, + {call_mutates_state, false}]}]). + +%% start/0 + +start() -> + start(?DEF_SVC_NAME). %% start/1 start(Name) when is_atom(Name) -> - peer:start(Name, ?SERVICE(Name)). + start(Name, []); -start() -> - start(?SVC_NAME). +start(Opts) + when is_list(Opts) -> + start(?DEF_SVC_NAME, Opts). + +%% start/2 + +start(Name, Opts) -> + Defaults = [T || {K,_} = T <- ?SERVICE(Name), + not lists:keymember(K, 1, Opts)], + diameter:start_service(Name, Opts ++ Defaults). %% listen/2 -listen(Name, T) -> - peer:listen(Name, T). +listen(Name, Opts) -> + server:listen(Name, Opts). + +%% listen/1 -listen(T) -> - listen(?SVC_NAME, T). +listen(Opts) -> + listen(?DEF_SVC_NAME, Opts). %% stop/1 stop(Name) -> - peer:stop(Name). + diameter:stop_service(Name). + +%% stop.0 stop() -> - stop(?SVC_NAME). + stop(?DEF_SVC_NAME). diff --git a/lib/diameter/examples/code/redirect_cb.erl b/lib/diameter/examples/code/redirect_cb.erl index 8325e86391..76ab091be3 100644 --- a/lib/diameter/examples/code/redirect_cb.erl +++ b/lib/diameter/examples/code/redirect_cb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2016. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ -module(redirect_cb). -include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/include/diameter_gen_base_rfc3588.hrl"). %% diameter callbacks -export([peer_up/3, @@ -33,30 +32,71 @@ handle_error/4, handle_request/3]). --define(UNEXPECTED, erlang:error({unexpected, ?MODULE, ?LINE})). +%% Dictionary to encode answer-message AVPs with. +-define(Dict, diameter_gen_base_rfc6733). + +%% Raise an error on callbacks that aren't expected. +-define(ERROR, error({unexpected, ?MODULE, ?LINE})). + +%% peer_up/3 peer_up(_SvcName, _Peer, State) -> State. +%% peer_down/3 + peer_down(_SvcName, _Peer, State) -> State. +%% pick_peer/4 + pick_peer(_, _, _SvcName, _State) -> - ?UNEXPECTED. + false. + +%% prepare_request/3 prepare_request(_, _SvcName, _Peer) -> - ?UNEXPECTED. + ?ERROR. + +%% prepare_retransmit/3 prepare_retransmit(_Packet, _SvcName, _Peer) -> - ?UNEXPECTED. + ?ERROR. + +%% handle_answer/4 handle_answer(_Packet, _Request, _SvcName, _Peer) -> - ?UNEXPECTED. + ?ERROR. + +%% handle_error/4 handle_error(_Reason, _Request, _SvcName, _Peer) -> - ?UNEXPECTED. + ?ERROR. + +%% handle_request/3 -handle_request(#diameter_packet{msg = _, errors = []}, _SvcName, {_, Caps}) -> - #diameter_caps{} +handle_request(#diameter_packet{avps = Avps}, _, {_, Caps}) -> + #diameter_caps{origin_host = {OH, _}, + origin_realm = {OR, _}} = Caps, - discard. %% TODO + + Tail = [#diameter_avp{data = {?Dict, A, V}} + || {A,V} <- [{'Origin-Host', OH}, + {'Origin-Realm', OR}, + {'Result-Code', 3006}, %% DIAMETER_REDIRECT_INDICATION + {'Redirect-Host', <<"aaa://server.example.com:3868">>}]], + + {reply, ['answer-message' | lists:append([session(Avps), Tail])]}. + +%% =========================================================================== + +%% session/1 + +session(Avps) -> + try + [] = [A || #diameter_avp{code = 263, vendor_id = undefined} = A + <- Avps, + throw(A)] + catch + Avp -> [Avp] + end. diff --git a/lib/diameter/examples/code/relay.erl b/lib/diameter/examples/code/relay.erl index 806f79915b..ec53ac01f1 100644 --- a/lib/diameter/examples/code/relay.erl +++ b/lib/diameter/examples/code/relay.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2016. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,81 +18,94 @@ %% %CopyrightEnd% %% +-module(relay). + %% %% An example Diameter relay agent. %% -%% Usage to connect to a server listening on the default port over TCP -%% and to listen on the default port over SCTP is as follows, assuming -%% diameter is already started (eg. diameter:start()). +%% Simplets usage to connect to a server listening on TCP at +%% 127.0.0.1:3868 and listen for connections on TCP at the same +%% endpoint: %% -%% Eg. relay:start(). -%% relay:connect(tcp). -%% relay:listen(sctp). +%% relay:start(). +%% relay:connect(tcp). +%% relay:listen(sctp). %% --module(relay). - +%% Interface. -export([start/1, start/2, listen/2, connect/2, stop/1]). +%% Convenience functions using the default service name. -export([start/0, listen/1, connect/1, stop/0]). +%% Default service name. -define(DEF_SVC_NAME, ?MODULE). -%% The service configuration. +%% Service configuration. -define(SERVICE(Name), [{'Origin-Host', atom_to_list(Name) ++ ".example.com"}, {'Origin-Realm', "example.com"}, {'Vendor-Id', 193}, {'Product-Name', "RelayAgent"}, {'Auth-Application-Id', [16#FFFFFFFF]}, + {decode_format, map}, + {restrict_connections, false}, {string_decode, false}, + {strict_mbit, false}, {application, [{alias, relay}, {dictionary, diameter_gen_relay}, - {module, relay_cb}]}]). + {module, relay_cb}, + {call_mutates_state, false}]}]). -%% start/1 +%% start/2 -start(Name) - when is_atom(Name) -> - start(Name, []). +start(Name, Opts) -> + Defaults = [T || {K,_} = T <- ?SERVICE(Name), + not lists:keymember(K, 1, Opts)], + diameter:start_service(Name, Opts ++ Defaults). %% start/1 -start() -> - start(?DEF_SVC_NAME). +start(Opts) -> + start(?DEF_SVC_NAME, Opts). -%% start/2 +%% start/0 -start(Name, Opts) -> - node:start(Name, Opts ++ [T || {K,_} = T <- ?SERVICE(Name), - false == lists:keymember(K, 1, Opts)]). +start() -> + start(?DEF_SVC_NAME, []). %% listen/2 -listen(Name, T) -> - node:listen(Name, T). +listen(Name, Opts) -> + server:listen(Name, Opts). -listen(T) -> - listen(?DEF_SVC_NAME, T). +%% listen/1 + +listen(Opts) -> + listen(?DEF_SVC_NAME, Opts). %% connect/2 -connect(Name, T) -> - node:connect(Name, T). +connect(Name, Opts) -> + client:connect(Name, Opts). + +%% connect/1 -connect(T) -> - connect(?DEF_SVC_NAME, T). +connect(Opts) -> + connect(?DEF_SVC_NAME, Opts). %% stop/1 stop(Name) -> - node:stop(Name). + diameter:stop_service(Name). + +%% stop/0 stop() -> stop(?DEF_SVC_NAME). diff --git a/lib/diameter/examples/code/relay_cb.erl b/lib/diameter/examples/code/relay_cb.erl index 6df1738143..0d56a14401 100644 --- a/lib/diameter/examples/code/relay_cb.erl +++ b/lib/diameter/examples/code/relay_cb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2016. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -21,48 +21,59 @@ -module(relay_cb). -include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/include/diameter_gen_base_rfc3588.hrl"). %% diameter callbacks -export([peer_up/3, peer_down/3, - pick_peer/5, - prepare_request/4, - prepare_retransmit/4, - handle_answer/5, - handle_error/5, + pick_peer/4, + prepare_request/3, + prepare_retransmit/3, + handle_answer/4, + handle_error/4, handle_request/3]). +%% peer_up/3 + peer_up(_SvcName, _Peer, State) -> State. +%% peer_down/3 + peer_down(_SvcName, _Peer, State) -> State. -%% Returning 'relay' from handle_request causes diameter to resend the -%% incoming request, which leads to pick_peer and prepare_request -%% callbacks as if sending explicitly. The 'extra' argument is -%% appended to the argument list for callbacks following from -%% resending of the request. +%% handle_request/3 + +%% Assume the destination is directly connected; filter +%% correspondingly; don't relay to the sender. +handle_request(_Pkt, _SvcName, {_, Caps}) -> + #diameter_caps{origin_host = {_, OH}} + = Caps, + {relay, [{timeout, 2000}, + {filter, {all, [host, realm, {neg, {host, OH}}]}}]}. -handle_request(_Pkt, _SvcName, _Peer) -> - {relay, [{timeout, 1000}, {extra, [relayed]}]}. +%% pick_peer/4 -%% diameter will filter the sender in the Peers list. -pick_peer([Peer | _], _, _SvcName, _State, relayed) -> +pick_peer([Peer | _], _, _SvcName, _State) -> {ok, Peer}. -prepare_request(Pkt, _SvcName, _Peer, relayed) -> +%% prepare_request/3 + +prepare_request(Pkt, _SvcName, _Peer) -> {send, Pkt}. -prepare_retransmit(Pkt, _SvcName, _Peer, relayed) -> +%% prepare_request/3 + +prepare_retransmit(Pkt, _SvcName, _Peer) -> {send, Pkt}. -%% diameter expects handle_answer to return the diameter_packet record -%% containing the answer when called for a relayed request. +%% handle_answer/4 -handle_answer(Pkt, _Request, _SvcName, _Peer, relayed) -> +%% Relay an answer by returning the first argument. +handle_answer(Pkt, _Request, _SvcName, _Peer) -> Pkt. -handle_error(Reason, _Request, _SvcName, _Peer, relayed) -> +%% handle_error/4 + +handle_error(Reason, _Request, _SvcName, _Peer) -> {error, Reason}. diff --git a/lib/diameter/examples/code/server.erl b/lib/diameter/examples/code/server.erl index a91be70664..6ee49fb678 100644 --- a/lib/diameter/examples/code/server.erl +++ b/lib/diameter/examples/code/server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2015. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,23 +18,20 @@ %% %CopyrightEnd% %% +-module(server). + %% -%% An example Diameter server that can respond to the base protocol -%% RAR sent by the client example. +%% An example Diameter server that answers the base protocol ACR sent +%% by the client example. %% -%% The simplest example to start a server listening on the loopback -%% address (which will serve the example usage given in client.erl) is -%% like this assuming diameter is already started (eg. diameter:start()): +%% Simplest usage to listen on TCP at 127.0.0.1:3868: %% %% server:start(). %% server:listen(tcp). %% -%% The first call starts a service, the second adds a transport listening -%% on the default port. -%% --module(server). +%% Interface. -export([start/1, %% start a service start/2, %% listen/2, %% add a listening transport @@ -45,6 +42,9 @@ listen/1, stop/0]). +%% Internal callback. +-export([message/3]). + -define(DEF_SVC_NAME, ?MODULE). %% The service configuration. In a server supporting multiple Diameter @@ -55,45 +55,110 @@ {'Vendor-Id', 193}, {'Product-Name', "Server"}, {'Auth-Application-Id', [0]}, + {decode_format, map}, {restrict_connections, false}, + {strict_mbit, false}, {string_decode, false}, {application, [{alias, common}, {dictionary, diameter_gen_base_rfc6733}, - {module, server_cb}]}]). + {module, server_cb}, + {call_mutates_state, false}]}]). -%% start/1 +%% start/2 -start(Name) - when is_atom(Name) -> - start(Name, []); +start(Name, Opts) -> + Defaults = [T || {K,_} = T <- ?SERVICE(Name), + not lists:keymember(K, 1, Opts)], + diameter:start_service(Name, Opts ++ Defaults). -start(Opts) - when is_list(Opts) -> +%% start/1 + +start(Opts) -> start(?DEF_SVC_NAME, Opts). %% start/0 start() -> - start(?DEF_SVC_NAME). - -%% start/2 - -start(Name, Opts) -> - node:start(Name, Opts ++ [T || {K,_} = T <- ?SERVICE(Name), - false == lists:keymember(K, 1, Opts)]). + start(?DEF_SVC_NAME, []). %% listen/2 +listen(Name, Opts) + when is_list(Opts) -> + diameter:add_transport(Name, {listen, lists:flatmap(fun opts/1, Opts)}); + +%% backwards compatibility with old config +listen(Name, {T, Opts}) -> + listen(Name, [T | Opts]); listen(Name, T) -> - node:listen(Name, T). + listen(Name, [T]). -listen(T) -> - listen(?DEF_SVC_NAME, T). +%% listen/1 + +listen(Opts) -> + listen(?DEF_SVC_NAME, Opts). %% stop/1 stop(Name) -> - node:stop(Name). + diameter:stop_service(Name). + +%% stop/0 stop() -> stop(?DEF_SVC_NAME). + +%% =========================================================================== + +%% opts/1 +%% +%% Map a 3-tuple a transport_module/transport_config pair as a +%% convenience, pass everything else unmodified. + +opts(T) + when T == tcp; + T == sctp -> + opts({T, loopback, default}); + +opts({tcp, Addr, Port}) -> + opts({diameter_tcp, Addr, Port}); + +opts({sctp, Addr, Port}) -> + opts({diameter_sctp, Addr, Port}); + +opts({Mod, loopback, Port}) -> + opts({Mod, {127,0,0,1}, Port}); + +opts({Mod, Addr, default}) -> + opts({Mod, Addr, 3868}); + +opts({Mod, Addr, Port}) -> + [{transport_module, Mod}, + {transport_config, [{reuseaddr, true}, + {sender, true}, + {message_cb, {?MODULE, message, [0]}}, + {ip, Addr}, + {port, Port}]}]; +opts(T) -> + [T]. + +%% message/3 +%% +%% Simple message callback that limits the number of concurrent +%% requests on the peer connection in question. + +%% Incoming request. +message(recv, <<_:32, 1:1, _/bits>> = Bin, N) -> + [Bin, N < 32, {?MODULE, message, [N+1]}]; + +%% Outgoing request. +message(ack, <<_:32, 1:1, _/bits>>, _) -> + []; + +%% Incoming answer or request discarded. +message(ack, _, N) -> + [N =< 32, {?MODULE, message, [N-1]}]; + +%% Outgoing message or incoming answer. +message(_, Bin, _) -> + [Bin]. diff --git a/lib/diameter/examples/code/server_cb.erl b/lib/diameter/examples/code/server_cb.erl index a2fb8fbda6..5662717c00 100644 --- a/lib/diameter/examples/code/server_cb.erl +++ b/lib/diameter/examples/code/server_cb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2015. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -25,7 +25,6 @@ -module(server_cb). -include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/include/diameter_gen_base_rfc6733.hrl"). %% diameter callbacks -export([peer_up/3, @@ -37,67 +36,87 @@ handle_error/4, handle_request/3]). --define(UNEXPECTED, erlang:error({unexpected, ?MODULE, ?LINE})). +%% Raise an error on callbacks that aren't expected. +-define(ERROR, error({unexpected, ?MODULE, ?LINE})). + +%% peer_up/3 peer_up(_SvcName, _Peer, State) -> State. +%% peer_down/3 + peer_down(_SvcName, _Peer, State) -> State. -pick_peer(_, _, _SvcName, _State) -> - ?UNEXPECTED. +%% pick_peer/3 + +%% Don't let requests be sent, so other request callbacks shouldn't +%% happen. +pick_peer(_LocalCandidates, _RemoteCandidates, _SvcName, _State) -> + false. + +%% prepare_request/3 -prepare_request(_, _SvcName, _Peer) -> - ?UNEXPECTED. +prepare_request(_Packet, _SvcName, _Peer) -> + ?ERROR. + +%% prepare_retransmit/3 prepare_retransmit(_Packet, _SvcName, _Peer) -> - ?UNEXPECTED. + ?ERROR. + +%% handle_answer/4 handle_answer(_Packet, _Request, _SvcName, _Peer) -> - ?UNEXPECTED. + ?ERROR. + +%% handle_error/4 handle_error(_Reason, _Request, _SvcName, _Peer) -> - ?UNEXPECTED. + ?ERROR. + +%% handle_request/3 -%% A request whose decode was successful ... -handle_request(#diameter_packet{msg = Req, errors = []}, _SvcName, {_, Caps}) - when is_record(Req, diameter_base_RAR) -> +%% ACR without decode errors. +handle_request(#diameter_packet{msg = ['ACR' | #{} = Request], + errors = []}, + _SvcName, + {_, Caps}) -> #diameter_caps{origin_host = {OH,_}, origin_realm = {OR,_}} = Caps, - #diameter_base_RAR{'Session-Id' = Id, - 'Re-Auth-Request-Type' = Type} - = Req, - - {reply, #diameter_base_RAA{'Result-Code' = rc(Type), - 'Origin-Host' = OH, - 'Origin-Realm' = OR, - 'Session-Id' = Id}}; - -%% ... or one that wasn't. 3xxx errors are answered by diameter itself -%% but these are 5xxx errors for which we must contruct a reply. -%% diameter will set Result-Code and Failed-AVP's. -handle_request(#diameter_packet{msg = Req}, _SvcName, {_, Caps}) - when is_record(Req, diameter_base_RAR) -> + + #{'Session-Id' := Sid, + 'Accounting-Record-Type' := T, + 'Accounting-Record-Number' := N} + = Request, + + Answer = #{'Result-Code' => 2001, %% DIAMETER_SUCCESS + 'Origin-Host' => OH, + 'Origin-Realm' => OR, + 'Session-Id' => Sid, + 'Accounting-Record-Type' => T, + 'Accounting-Record-Number' => N}, + + {reply, ['ACA' | Answer]}; + +%% ACR with decode errors. +handle_request(#diameter_packet{msg = ['ACR' | #{} = Request]}, + _SvcName, + {_, Caps}) -> #diameter_caps{origin_host = {OH,_}, origin_realm = {OR,_}} = Caps, - #diameter_base_RAR{'Session-Id' = Id} - = Req, - {reply, #diameter_base_RAA{'Origin-Host' = OH, - 'Origin-Realm' = OR, - 'Session-Id' = Id}}; + Answer = maps:merge(maps:with(['Session-Id'], Request), + #{'Origin-Host' => OH, + 'Origin-Realm' => OR}), -%% Answer that any other message is unsupported. + %% Let diameter set Result-Code and Failed-AVP if there were + %% decode errors. + {reply, ['answer-message' | Answer]}; + +%% Answer anything else as unsupported. handle_request(#diameter_packet{}, _SvcName, _) -> {answer_message, 3001}. %% DIAMETER_COMMAND_UNSUPPORTED - -%% Map Re-Auth-Request-Type to Result-Code just for the purpose of -%% generating different answers. - -rc(0) -> - 2001; %% DIAMETER_SUCCESS -rc(_) -> - 5012. %% DIAMETER_UNABLE_TO_COMPLY diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile index 98636ed6e2..d6854cfd27 100644 --- a/lib/diameter/src/Makefile +++ b/lib/diameter/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2010-2018. All Rights Reserved. +# Copyright Ericsson AB 2010-2020. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -210,12 +210,16 @@ realclean: clean PLT = ./otp.plt -plt: +plt: $(PLT) + +$(PLT): dialyzer --build_plt \ --apps erts stdlib kernel \ xmerl ssl public_key crypto \ compiler syntax_tools runtime_tools \ - --output_plt $(PLT) \ + --output_plt $@ \ + --get_warnings \ + --statistics \ --verbose dialyze: opt $(PLT) @@ -224,9 +228,12 @@ dialyze: opt $(PLT) -Wno_improper_lists \ $(EBIN)/diameter_gen_base_rfc3588.$(EMULATOR) \ $(patsubst %, $(EBIN)/%.$(EMULATOR), \ - $(notdir $(RT_MODULES) $(CT_MODULES) $(INFO_MODULES))) + $(notdir $(DICT_YRL) \ + $(RT_MODULES) \ + $(CT_MODULES) \ + $(INFO_MODULES))) # Omit all but the common dictionary module since these -# (diameter_gen_relay in particular) generate warning depending on how +# (diameter_gen_relay in particular) generate warnings depending on how # much of the included diameter_gen.hrl they use. # ---------------------------------------------------- diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl index 7f172e1fa1..2982486a10 100644 --- a/lib/diameter/src/base/diameter.erl +++ b/lib/diameter/src/base/diameter.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2019. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -146,8 +146,9 @@ services() -> %% service_info/2 %% --------------------------------------------------------------------------- --spec service_info(service_name(), atom() | [atom()]) - -> any(). +-spec service_info(service_name(), Item | [Item]) + -> any() + when Item :: atom() | peer_ref(). service_info(SvcName, Option) -> diameter_service:info(SvcName, Option). @@ -351,45 +352,45 @@ call(SvcName, App, Message) -> %% Options common to both start_service/2 and add_transport/2. -type common_opt() - :: {pool_size, pos_integer()} + :: {avp_dictionaries, [module()]} | {capabilities_cb, eval()} | {capx_timeout, 'Unsigned32'()} - | {strict_capx, boolean()} - | {strict_mbit, boolean()} - | {avp_dictionaries, [module()]} + | {connect_timer, 'Unsigned32'()} | {disconnect_cb, eval()} - | {dpr_timeout, 'Unsigned32'()} | {dpa_timeout, 'Unsigned32'()} + | {dpr_timeout, 'Unsigned32'()} | {incoming_maxlen, message_length()} | {length_errors, exit | handle | discard} - | {connect_timer, 'Unsigned32'()} - | {watchdog_timer, 'Unsigned32'() | {module(), atom(), list()}} + | {pool_size, pos_integer()} + | {spawn_opt, list() | mfa()} + | {strict_capx, boolean()} + | {strict_mbit, boolean()} | {watchdog_config, [{okay|suspect, non_neg_integer()}]} - | {spawn_opt, list() | mfa()}. + | {watchdog_timer, 'Unsigned32'() | {module(), atom(), list()}}. %% Options passed to start_service/2 -type service_opt() :: capability() | {application, [application_opt()]} + | {decode_format, decode_format()} | {restrict_connections, restriction()} | {sequence, sequence() | eval()} | {share_peers, remotes()} - | {decode_format, decode_format()} - | {traffic_counters, boolean()} - | {string_decode, boolean()} | {strict_arities, true | strict_arities()} + | {string_decode, boolean()} + | {traffic_counters, boolean()} | {use_shared_peers, remotes()} | common_opt(). -type application_opt() :: {alias, app_alias()} + | {answer_errors, callback|report|discard} + | {call_mutates_state, boolean()} | {dictionary, module()} | {module, app_module()} - | {state, any()} - | {call_mutates_state, boolean()} - | {answer_errors, callback|report|discard} - | {request_errors, answer_3xxx|answer|callback}. + | {request_errors, answer_3xxx|answer|callback} + | {state, any()}. -type app_alias() :: any(). @@ -407,11 +408,11 @@ call(SvcName, App, Message) -> %% Options passed to add_transport/2 -type transport_opt() - :: {transport_module, atom()} + :: {applications, [app_alias()]} + | {capabilities, [capability()]} | {transport_config, any()} | {transport_config, any(), 'Unsigned32'() | infinity} - | {applications, [app_alias()]} - | {capabilities, [capability()]} + | {transport_module, atom()} | common_opt() | {private, any()}. @@ -430,8 +431,8 @@ call(SvcName, App, Message) -> %% Options passed to call/4 -type call_opt() - :: {extra, list()} + :: detach + | {extra, list()} | {filter, peer_filter()} - | {timeout, 'Unsigned32'()} | {peer, peer_ref()} - | detach. + | {timeout, 'Unsigned32'()}. diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index 493a6ab1e3..7f6baf666a 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2018. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -208,7 +208,7 @@ values(Avps) -> encode_avps(_, _, [#diameter_avp{} | _] = Avps, Opts) -> encode_avps(Avps, Opts); -%% ... or as a tuple list or record. +%% ... or as a tuple list, map, or record. encode_avps(Mod, MsgName, Values, Opts) -> Mod:encode_avps(MsgName, Values, Opts). diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl index 36ae4c2276..495e57e456 100644 --- a/lib/diameter/src/base/diameter_config.erl +++ b/lib/diameter/src/base/diameter_config.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2018. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -661,6 +661,9 @@ opt(transport, {transport_module, M}) -> opt(transport, {transport_config, _, Tmo}) -> ?IS_UINT32(Tmo) orelse Tmo == infinity; +opt(transport, {transport_config, _}) -> + true; + opt(transport, {applications, As}) -> is_list(As); @@ -720,15 +723,16 @@ opt(_, {K, _}) when K == disconnect_cb; K == capabilities_cb -> true; -opt(transport, {K, _}) - when K == transport_config; - K == private -> +opt(transport, {private, _}) -> true; -%% Anything else, which is ignored in transport config. This makes -%% options sensitive to spelling mistakes, but arbitrary options are -%% passed by some users as a way to identify transports so can't just -%% do away with it. +%% Anything else is ignored in transport config. This makes options +%% sensitive to spelling mistakes and unintentionally passing service +%% options, but arbitrary options are passed by some users as a way to +%% identify transports (they can be returned by diameter:service_info/2 +%% for example) so can't just do away with it, although silently +%% swallowing service options is at least debatable. The documentation +%% says anything, so accept anything. opt(K, _) -> K == transport. diff --git a/lib/diameter/src/base/diameter_dist.erl b/lib/diameter/src/base/diameter_dist.erl index ed23152b8b..1edca58eb9 100644 --- a/lib/diameter/src/base/diameter_dist.erl +++ b/lib/diameter/src/base/diameter_dist.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2019. All Rights Reserved. +%% Copyright Ericsson AB 2019-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -27,6 +27,21 @@ %% transport configuration, to be able to distribute incoming Diameter %% requests to handler processes (local or remote) in various ways. %% +%% The gen_server implemented here must be started on each node on +%% which {diameter_dist, route_session, _} is configured as a +%% spawn_opt MFA, as well as each node that wants to receive requests. +%% This happens as a consequence of diameter application start, but a +%% minimal solution could start only this server on nodes that should +%% handle requests but not configure transport. (Although the typical +%% case is probably that diameter should be started in any case; for +%% example, to be able to originate requests.) +%% +%% Moreover, attach/1 must be called to communicate the list of +%% services for which the local node is willing to handle requests. +%% The servers on different nodes communicate so that each server +%% knows which nodes are prepared to handle requests for which +%% services. +%% %% spawn_opt callbacks -export([spawn_local/2, diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index cf5e7f21d3..b86dcaf923 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2018. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 77d184cfc7..520a7233cc 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2018. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -735,29 +735,31 @@ init_peers() -> %% Alias, %% TPid} +%% Valid service options are all 2-tuples. service_opts(Opts) -> - remove([{strict_arities, true}, - {avp_dictionaries, []}], - maps:merge(maps:from_list([{monitor, false} | def_opts()]), - maps:from_list(Opts))). + remove([{strict_arities, true}, {avp_dictionaries, []}], + merge(lists:append([[{monitor, false}] | def_opts()]), Opts)). + +merge(List1, List2) -> + maps:merge(maps:from_list(List1), maps:from_list(List2)). remove(List, Map) -> maps:filter(fun(K,V) -> not lists:member({K,V}, List) end, Map). -def_opts() -> %% defaults on the service map - [{share_peers, false}, - {use_shared_peers, false}, - {sequence, {0,32}}, - {restrict_connections, nodes}, - {incoming_maxlen, 16#FFFFFF}, - {strict_arities, true}, - {strict_mbit, true}, - {decode_format, record}, - {avp_dictionaries, []}, - {traffic_counters, true}, - {string_decode, true}, - {spawn_opt, []}]. +def_opts() -> %% defaults on the options map + [[{decode_format, record}, %% service options + {restrict_connections, nodes}, + {sequence, {0,32}}, + {share_peers, false}, + {strict_arities, true}, + {string_decode, true}, + {traffic_counters, true}, + {use_shared_peers, false}], + [{avp_dictionaries, []}, %% common options + {incoming_maxlen, 16#FFFFFF}, + {spawn_opt, []}, + {strict_mbit, true}]]. mref(false = No) -> No; @@ -875,27 +877,46 @@ start(Ref, Type, Opts, N, #state{watchdogT = WatchdogT, = Svc1 = merge_service(Opts, Svc0), Svc = binary_caps(Svc1, SD), - {SOpts, TOpts} = merge_opts(SvcOpts, Opts), - RecvData = diameter_traffic:make_recvdata([SvcName, PeerT, Apps, SOpts]), - T = {TOpts, SOpts, RecvData, Svc}, + {Map, Rest} = merge_opts(SvcOpts, Opts), + RecvData = diameter_traffic:make_recvdata([SvcName, PeerT, Apps, Map]), + T = {Rest, Map, RecvData, Svc}, Rec = #watchdog{type = Type, ref = Ref, - options = TOpts}, - + options = Opts}, %% original options, returned + %% by service_info/2 diameter_lib:fold_n(fun(_,A) -> [wd(Type, Ref, T, WatchdogT, Rec) | A] end, [], N). -merge_opts(SvcOpts, Opts) -> - Keys = [K || {K,_} <- def_opts()], - SO = [T || {K,_} = T <- Opts, lists:member(K, Keys)], - TO = Opts -- SO, - {maps:merge(maps:with(Keys, SvcOpts), maps:from_list(SO)), - TO ++ [T || {K,_} = T <- maps:to_list(SvcOpts), - not lists:member(K, Keys), - not lists:keymember(K, 1, Opts)]}. +%% This is awkward. We have service options that have been passed to +%% diameter:start_service/2 (minus application and capabilities +%% options, removed in diameter_config) and transport options passed +%% to diameter:add_transport/2. The former can include defaults for +%% the latter, but the latter can also contain arbitrary options that +%% are just returned by diameter:service_info/2. There's nothing +%% stopping these arbitrary options from being valid service options, +%% but these aren't interpreted as such. +%% +%% The options are merged (transport defaults have already been merged +%% into the service options in service_opts/1) and split into a map +%% for the service options and a few transport options, and a list for +%% the rest. This is historical convolution. Some options are are +%% pulled out of the list on the way to starting the transport process +%% in diameter_peer_fsm, but more work could probably be done here to +%% simplify things. +%% +%% Transport options are not necessarily 2-tuples: the transport_config +%% 3-tuple means they can't just be turned into a map. +merge_opts(SOpts, TOpts) -> + [SD,TD] = Def = def_opts(), + Keys = [K || L <- Def, {K,_} <- L], + Opts = [T || {K,_} = T <- TOpts, lists:keymember(K, 1, TD)], + {maps:merge(maps:with(Keys, SOpts), maps:from_list(Opts)),%% merge TOpts + TOpts ++ [T || {K,_} = T <- maps:to_list(SOpts), %% append SOpts + not lists:keymember(K, 1, SD), + [] == [A || {A,_} <- TOpts, A == K]]}. binary_caps(Svc, true) -> Svc; diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index 8423e30269..4667bbc3f2 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2019. All Rights Reserved. +%% Copyright Ericsson AB 2013-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -86,6 +86,7 @@ string_decode := boolean(), strict_arities => diameter:strict_arities(), strict_mbit := boolean(), + ordered_encode => boolean(), incoming_maxlen := diameter:message_length()}}). %% Note that incoming_maxlen is currently handled in diameter_peer_fsm, %% so that any message exceeding the maximum is discarded. Retain the @@ -284,12 +285,18 @@ recv(false, false, TPid, Pkt, _, _) -> spawn_request(false, _, _, _, _, _, _) -> %% no transport discard; -%% An MFA should return the pid() of a process in which the argument -%% fun in applied, or the atom 'discard' if the fun is not applied. -%% The latter results in an acknowledgment back to the transport -%% process when appropriate, to ensure that send/recv callbacks can -%% count outstanding requests. Acknowledgement is implicit if the -%% handler process dies (in a handle_request callback for example). +%% An MFA should return the pid() of a process that invokes +%% diameter_traffic:request(ReqT), or the atom 'discard' if the +%% function is not called. The latter results in an acknowledgment +%% back to the transport process when appropriate, to ensure that +%% send/recv callbacks can count outstanding requests. Acknowledgement +%% is implicit if the handler process dies (in a handle_request +%% callback for example). +%% +%% There is no requirement that diameter be started on nodes on which +%% handler processes are spawned, just that diameter and application +%% callbacks are on the code path. (Although the MFA itself may have +%% requirements, as in the case of diameter_dist.) spawn_request(AppT, {M,F,A}, Ack, TPid, Pkt, Dict0, RecvData) -> %% Term to pass to request/1 in an appropriate process. Module %% diameter_dist implements callbacks. @@ -520,7 +527,7 @@ request_cb(noreply, _App, EvalPktFs, EvalFs) -> %% Relay a request to another peer. This is equivalent to doing an %% explicit call/4 with the message in question except that (1) a loop -%% will be detected by examining Route-Record AVP's, (3) a +%% will be detected by examining Route-Record AVP's, (2) a %% Route-Record AVP will be added to the outgoing request and (3) the %% End-to-End Identifier will default to that in the %% #diameter_header{} without the need for an end_to_end_identifier @@ -556,14 +563,7 @@ request_cb(T, App, _, _) -> send_A({reply, Ans}, TPid, App, Dict0, RecvData, Pkt, _Caps, Fs) -> AppDict = App#diameter_app.dictionary, MsgDict = msg_dict(AppDict, Dict0, Ans), - send_answer(Ans, - TPid, - MsgDict, - AppDict, - Dict0, - RecvData, - Pkt, - Fs); + send_answer(Ans, TPid, MsgDict, AppDict, Dict0, RecvData, Pkt, Fs); send_A({call, Opts}, TPid, App, Dict0, RecvData, Pkt, Caps, Fs) -> AppDict = App#diameter_app.dictionary, @@ -667,7 +667,7 @@ is_answer_message(#diameter_packet{msg = Msg}, Dict0) -> is_answer_message([#diameter_header{is_request = R, is_error = E} | _], _) -> E andalso not R; -%% Message sent as a map or tagged avp/value list. +%% Message sent as a map or avp list. is_answer_message([Name | _], _) -> Name == 'answer-message'; @@ -1239,7 +1239,12 @@ is_result(RC, true, _) -> %% incr/2 incr(TPid, Counter) -> - diameter_stats:incr(Counter, TPid, 1). + Node = node(TPid), + if Node == node() -> + diameter_stats:incr(Counter, TPid, 1); + true -> + spawn(Node, diameter_stats, incr, [Counter, TPid, 1]) + end. %% rcc/1 @@ -1866,23 +1871,26 @@ z(#diameter_packet{header = H, bin = Bin, transport_data = T}) -> transport_data = T}. %% send/1 +%% +%% Send from a remote node using a peer connection on this one. Pkt is +%% already stripped. send({TPid, Pkt, #request{handler = Pid} = Req0, SvcName, Timeout, TRef}) -> Req = Req0#request{handler = self()}, - recv(TPid, Pid, TRef, zend_requezt(TPid, Pkt, Req, SvcName, Timeout)). + Pid ! recv(TPid, TRef, send_request(TPid, Pkt, Req, SvcName, Timeout)). -%% recv/4 +%% recv/3 %% %% Relay an answer from a remote node. -recv(TPid, Pid, TRef, {LocalTRef, MRef}) -> +recv(TPid, TRef, {LocalTRef, MRef}) -> receive {answer, _, _, _, _} = A -> - Pid ! A; + A; {'DOWN', MRef, process, _, _} -> - Pid ! {failover, TRef}; + {failover, TRef}; {failover = T, LocalTRef} -> - Pid ! {T, TRef}; + {T, TRef}; T -> exit({timeout, LocalTRef, TPid} = T) end. diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src index bb2a4a8e92..627213637b 100644 --- a/lib/diameter/src/diameter.appup.src +++ b/lib/diameter/src/diameter.appup.src @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2019. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -62,7 +62,15 @@ {"2.1.4.1", [{restart_application, diameter}]}, %% 20.3.8.19 {"2.1.5", [{restart_application, diameter}]}, %% 21.0 {"2.1.6", [{restart_application, diameter}]}, %% 21.1 - {"2.2", [{update, diameter_dist, {advanced, "2.2"}}]} %% 21.3 + {"2.2", [{restart_application, diameter}]}, %% 21.3 + {"2.2.1", [{load_module, diameter}, %% 21.3.5 + {load_module, diameter_codec}, + {update, diameter_config}, + {update, diameter_dist}, + {update, diameter_peer_fsm}, + {update, diameter_service}, + {load_module, diameter_traffic}, + {update, diameter_tcp}]} ], [ {"0.9", [{restart_application, diameter}]}, @@ -106,6 +114,14 @@ {"2.1.4.1", [{restart_application, diameter}]}, {"2.1.5", [{restart_application, diameter}]}, {"2.1.6", [{restart_application, diameter}]}, - {"2.2", [{restart_application, diameter}]} + {"2.2", [{restart_application, diameter}]}, + {"2.2.1", [{load_module, diameter}, + {load_module, diameter_codec}, + {update, diameter_config}, + {update, diameter_dist}, + {update, diameter_peer_fsm}, + {update, diameter_service}, + {load_module, diameter_traffic}, + {update, diameter_tcp}]} ] }. diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk index d16292bb88..cf938785e3 100644 --- a/lib/diameter/src/modules.mk +++ b/lib/diameter/src/modules.mk @@ -1,7 +1,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2010-2019. All Rights Reserved. +# Copyright Ericsson AB 2010-2020. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -98,13 +98,14 @@ BINS = \ # Released files relative to ../examples. EXAMPLES = \ code/GNUmakefile \ - code/node.erl \ code/client.erl \ code/client_cb.erl \ code/server.erl \ code/server_cb.erl \ code/relay.erl \ code/relay_cb.erl \ + code/redirect.erl \ + code/redirect_cb.erl \ dict/rfc4004_mip.dia \ dict/rfc4005_nas.dia \ dict/rfc4006_cc.dia \ diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl index e5e766d2a0..a051aeb156 100644 --- a/lib/diameter/src/transport/diameter_tcp.erl +++ b/lib/diameter/src/transport/diameter_tcp.erl @@ -569,7 +569,11 @@ m({'DOWN', M, process, P, _} = T, #monitor{parent = MRef, %% l/2 %% -%% Transition listener state. +%% Transition listener state. Or not anymore since any message causes +%% the process to exit. + +-spec l(tuple(), #listener{}) + -> no_return(). %% Service process has died. l({'DOWN', _, process, Pid, _} = T, #listener{service = Pid, diff --git a/lib/diameter/test/diameter_dist_SUITE.erl b/lib/diameter/test/diameter_dist_SUITE.erl index b2e4c35b9a..2fda2830ae 100644 --- a/lib/diameter/test/diameter_dist_SUITE.erl +++ b/lib/diameter/test/diameter_dist_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2019. All Rights Reserved. +%% Copyright Ericsson AB 2019-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -160,15 +160,32 @@ ping(Config) -> %% %% Start diameter services. -start(SvcName) - when is_atom(SvcName) -> +%% There's no need to start diameter on a node that only services +%% diameter_dist as a handler of incoming requests, but the +%% diameter_dist server must be started since the servers communicate +%% to determine who services what. The typical case is probably that +%% handler nodes also want to be able to send Diameter requests, in +%% which case the application needs to be started and diameter_dist is +%% started as a part of this, but only start the server here to ensure +%% everything still works as expected. +start({_SvcName, [_, {S1, _}, {S2, _}, _]}) + when node() == S1; %% server1 + node() == S2 -> %% server2 + Mod = diameter_dist, + {ok, _} = gen_server:start({local, Mod}, Mod, _Args = [], _Opts = []), + ok; + +start({SvcName, [{S0, _}, _, _, {C, _}]}) + when node() == S0; %% server0 + node() == C -> %% client ok = diameter:start(), ok = diameter:start_service(SvcName, ?SERVICE((?L(SvcName)))); -start(Config) -> +start(Config) + when is_list(Config) -> Nodes = ?util:read_priv(Config, nodes), [] = [{N,RC} || {N,S} <- Nodes, - RC <- [rpc:call(N, ?MODULE, start, [S])], + RC <- [rpc:call(N, ?MODULE, start, [{S, Nodes}])], RC /= ok]. sequence() -> @@ -194,13 +211,12 @@ origin(Server) -> %% Establish one connection from the client, terminated on the first %% server node, the others handling requests. -connect({?SERVER, Config, [{Node, _} | _]}) -> - if Node == node() -> %% server0 - ?util:write_priv(Config, lref, {Node, ?util:listen(?SERVER, tcp)}); - true -> - diameter_dist:attach([?SERVER]) - end, - ok; +connect({?SERVER, Config, [{Node, _} | _]}) + when Node == node() -> %% server0 + ok = ?util:write_priv(Config, lref, {Node, ?util:listen(?SERVER, tcp)}); + +connect({?SERVER, _Config, _}) -> %% server[12]: register to receive requests + ok = diameter_dist:attach([?SERVER]); connect({?CLIENT, Config, _}) -> ?util:connect(?CLIENT, tcp, ?util:read_priv(Config, lref)), @@ -239,7 +255,18 @@ send(Config) -> send(Config, 0, Dict) -> [{Server0, _} | _] = ?util:read_priv(Config, nodes) , Node = atom_to_binary(Server0, utf8), - {false, _} = {dict:is_key(Node, Dict), dict:to_list(Dict)}; + {false, _} = {dict:is_key(Node, Dict), dict:to_list(Dict)}, + %% Check that counters have been incremented as expected on server0. + [Info] = rpc:call(Server0, diameter, service_info, [?SERVER, connections]), + {[Stats], _} = {[S || {statistics, S} <- Info], Info}, + {[{recv, 1, 100}, {send, 0, 100}], _} + = {[{D,R,N} || T <- [recv, send], + {{{0,275,R}, D}, N} <- Stats, + D == T], + Stats}, + {[{send, 0, 100, 2001}], _} + = {[{D,R,N,C} || {{{0,275,R}, D, {'Result-Code', C}}, N} <- Stats], + Stats}; send(Config, N, Dict) -> #diameter_base_STA{'Result-Code' = ?SUCCESS, diff --git a/lib/diameter/test/diameter_event_SUITE.erl b/lib/diameter/test/diameter_event_SUITE.erl index a291dde6be..6d1d1dfd8f 100644 --- a/lib/diameter/test/diameter_event_SUITE.erl +++ b/lib/diameter/test/diameter_event_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2017. All Rights Reserved. +%% Copyright Ericsson AB 2013-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -28,6 +28,8 @@ -export([suite/0, all/0, + init_per_suite/1, + end_per_suite/1, init_per_testcase/2, end_per_testcase/2]). @@ -85,6 +87,13 @@ all() -> cea_timeout, stop]. +%% Not used, but a convenient place to enable trace. +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + init_per_testcase(Name, Config) -> [{name, Name} | Config]. @@ -109,17 +118,19 @@ start_server(Config) -> %% Connect with matching capabilities and expect the connection to %% come up. up(Config) -> - {Svc, Ref} = connect(Config, [{connect_timer, 5000}, - {watchdog_timer, 15000}]), + {Svc, Ref, T} = connect(Config, [{strict_mbit, false}, + {connect_timer, 5000}, + {watchdog_timer, 15000}]), start = event(Svc), - {up, Ref, {TPid, Caps}, Cfg, #diameter_packet{msg = M}} = event(Svc), + {{up, Ref, {TPid, Caps}, T, #diameter_packet{msg = M}}, _} + = {event(Svc), T}, ['CEA' | #{}] = M, %% assert {watchdog, Ref, _, {initial, okay}, _} = event(Svc), %% Kill the transport process and see that the connection is %% reestablished after a watchdog timeout, not after connect_timer %% expiry. exit(TPid, kill), - {down, Ref, {TPid, Caps}, Cfg} = event(Svc), + {{down, Ref, {TPid, Caps}, T}, _} = {event(Svc), T}, {watchdog, Ref, _, {okay, down}, _} = event(Svc), {reconnect, Ref, _} = event(Svc, 10000, 20000). @@ -127,24 +138,25 @@ up(Config) -> %% to indicate as much and then for the transport to be restarted %% (after connect_timer). down(Config) -> - {Svc, Ref} = connect(Config, [{capabilities, [{'Acct-Application-Id', - [?DICT_ACCT:id()]}]}, - {applications, [?DICT_ACCT]}, - {connect_timer, 5000}, - {watchdog_timer, 20000}]), + {Svc, Ref, T} = connect(Config, [{capabilities, [{'Acct-Application-Id', + [?DICT_ACCT:id()]}]}, + {applications, [?DICT_ACCT]}, + {connect_timer, 5000}, + {watchdog_timer, 20000}]), start = event(Svc), - {closed, Ref, {'CEA', ?NO_COMMON_APP, _, #diameter_packet{msg = M}}, _} - = event(Svc), + {{closed, Ref, {'CEA', ?NO_COMMON_APP, _, #diameter_packet{msg = M}}, T}, + _} + = {event(Svc), T}, ['CEA' | #{}] = M, %% assert {reconnect, Ref, _} = event(Svc, 4000, 10000). %% Connect with matching capabilities but have the server delay its %% CEA and cause the client to timeout. cea_timeout(Config) -> - {Svc, Ref} = connect(Config, [{capx_timeout, ?SERVER_CAPX_TMO div 2}, - {connect_timer, 2*?SERVER_CAPX_TMO}]), + {Svc, Ref, T} = connect(Config, [{capx_timeout, ?SERVER_CAPX_TMO div 2}, + {connect_timer, 2*?SERVER_CAPX_TMO}]), start = event(Svc), - {closed, Ref, {'CEA', timeout}, _} = event(Svc). + {{closed, Ref, {'CEA', timeout}, T}, _} = {event(Svc), T}. stop(_Config) -> ok = diameter:stop(). @@ -168,8 +180,9 @@ connect(Config, Opts) -> Name = Pre ++ uniq() ++ ?CLIENT, diameter:subscribe(Name), ok = start_service(Name, ?SERVICE(Name, [?DICT_COMMON, ?DICT_ACCT])), - {ok, Ref} = diameter:add_transport(Name, opts(Config, Opts)), - {Name, Ref}. + {connect, _} = T = opts(Config, Opts), + {ok, Ref} = diameter:add_transport(Name, T), + {Name, Ref, T}. uniq() -> "-" ++ diameter_util:unique_string(). diff --git a/lib/diameter/test/diameter_examples_SUITE.erl b/lib/diameter/test/diameter_examples_SUITE.erl index ee44ed8dc9..7d47efd527 100644 --- a/lib/diameter/test/diameter_examples_SUITE.erl +++ b/lib/diameter/test/diameter_examples_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2017. All Rights Reserved. +%% Copyright Ericsson AB 2013-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -27,6 +27,8 @@ -export([suite/0, all/0, groups/0, + init_per_suite/1, + end_per_suite/1, init_per_group/2, end_per_group/2]). @@ -85,6 +87,13 @@ groups() -> [{all, [parallel], [{group, P} || P <- ?PROTS]} | [{P, [], Tc} || P <- ?PROTS]]. +%% Not used, but a convenient place to enable trace. +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + init_per_group(all, Config) -> Config; @@ -216,12 +225,14 @@ make_name(Dict) -> %% Compile example code under examples/code. code(Config) -> - Node = slave(compile, here()), - [] = rpc:call(Node, - ?MODULE, - install, - [proplists:get_value(priv_dir, Config)]), - {ok, Node} = ct_slave:stop(compile). + try + [] = rpc:call(slave(compile, here()), + ?MODULE, + install, + [proplists:get_value(priv_dir, Config)]) + after + {ok, _} = ct_slave:stop(compile) + end. %% Compile on another node since the code path may be modified. install(PrivDir) -> diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 47b00c25a2..452bd28333 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2019. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -128,7 +128,7 @@ %% =========================================================================== %% Fraction of shuffle/parallel groups to randomly skip. --define(SKIP, 0.25). +-define(SKIP, 0.90). %% Positive number of testcases from which to select (randomly) from %% tc(), the list of testcases to run, or [] to run all. The random @@ -305,7 +305,8 @@ names() -> S <- ?STRING_DECODES, ST <- ?CALLBACKS, SS <- ?SENDERS, - CS <- ?SENDERS]. + CS <- ?SENDERS, + ?SKIP =< rand:uniform()]. names(Names, []) -> [N || N <- Names, @@ -336,14 +337,9 @@ init_per_group(_) -> init_per_group(Name, Config) when Name == shuffle; Name == parallel -> - case rand:uniform() < ?SKIP of - true -> - {skip, random}; - false -> - start_services(Config), - add_transports(Config), - replace({sleep, Name == parallel}, Config) - end; + start_services(Config), + add_transports(Config), + replace({sleep, Name == parallel}, Config); init_per_group(sctp = Name, Config) -> {_, Sctp} = lists:keyfind(Name, 1, Config), diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk index a8fbca5bc8..e72b6216a7 100644 --- a/lib/diameter/vsn.mk +++ b/lib/diameter/vsn.mk @@ -17,5 +17,5 @@ # %CopyrightEnd% APPLICATION = diameter -DIAMETER_VSN = 2.2.1 +DIAMETER_VSN = 2.2.2 APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN) diff --git a/lib/erl_interface/configure.in b/lib/erl_interface/configure.in index f0e9b2eb3f..230dba25d6 100644 --- a/lib/erl_interface/configure.in +++ b/lib/erl_interface/configure.in @@ -315,6 +315,12 @@ if test "x$GCC" = xyes; then WFLAGS="$WFLAGS -fno-strict-aliasing";; esac CFLAGS="$WERRORFLAGS $CFLAGS" + + # Use -fno-common for gcc, that is link error if multiple definitions of + # global variables are encountered. This is ISO C compliant. + # Until version 10, gcc has had -fcommon as default, which allows and merges + # such dubious duplicates. + LM_TRY_ENABLE_CFLAG([-fno-common], [CFLAGS]) else WFLAGS="" WERRORFLAGS="" diff --git a/lib/erl_interface/src/misc/ei_portio.h b/lib/erl_interface/src/misc/ei_portio.h index 84ebc5039a..5172d085b4 100644 --- a/lib/erl_interface/src/misc/ei_portio.h +++ b/lib/erl_interface/src/misc/ei_portio.h @@ -47,7 +47,7 @@ int ei_writev_fill_ctx_t__(ei_socket_callbacks *cbs, void *ctx, const struct iov int ei_socket_callbacks_have_writev__(ei_socket_callbacks *cbs); #endif -ei_socket_callbacks ei_default_socket_callbacks; +extern ei_socket_callbacks ei_default_socket_callbacks; #define EI_FD_AS_CTX__(FD) \ ((void *) (long) (FD)) diff --git a/lib/eunit/src/eunit_surefire.erl b/lib/eunit/src/eunit_surefire.erl index 2b9f82b075..002a069a92 100644 --- a/lib/eunit/src/eunit_surefire.erl +++ b/lib/eunit/src/eunit_surefire.erl @@ -451,6 +451,11 @@ escape_xml([$< | Tail], Acc, ForAttr) -> escape_xml(Tail, [$;, $t, $l, $& | Acc] escape_xml([$> | Tail], Acc, ForAttr) -> escape_xml(Tail, [$;, $t, $g, $& | Acc], ForAttr); escape_xml([$& | Tail], Acc, ForAttr) -> escape_xml(Tail, [$;, $p, $m, $a, $& | Acc], ForAttr); escape_xml([$" | Tail], Acc, true) -> escape_xml(Tail, [$;, $t, $o, $u, $q, $& | Acc], true); % " +escape_xml([Char | Tail], Acc, ForAttr) when + Char == $\n; Char == $\r; Char == $\t -> escape_xml(Tail, [Char | Acc], ForAttr); +%% Strip C0 control codes which are not allowed in XML 1.0 +escape_xml([Char | Tail], Acc, ForAttr) when + 0 =< Char, Char =< 31 -> escape_xml(Tail, Acc, ForAttr); escape_xml([Char | Tail], Acc, ForAttr) when is_integer(Char) -> escape_xml(Tail, [Char | Acc], ForAttr). %% the input may be utf8 or latin1; the resulting list is unicode diff --git a/lib/eunit/test/Makefile b/lib/eunit/test/Makefile index b6ece61b43..7c1e56c867 100644 --- a/lib/eunit/test/Makefile +++ b/lib/eunit/test/Makefile @@ -22,6 +22,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk MODULES = \ eunit_SUITE \ + tc0 \ tlatin \ tutf8 diff --git a/lib/eunit/test/eunit_SUITE.erl b/lib/eunit/test/eunit_SUITE.erl index 63bdc6c334..b9f4ea4557 100644 --- a/lib/eunit/test/eunit_SUITE.erl +++ b/lib/eunit/test/eunit_SUITE.erl @@ -21,14 +21,16 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, - app_test/1,appup_test/1,eunit_test/1,surefire_utf8_test/1,surefire_latin_test/1]). + app_test/1,appup_test/1,eunit_test/1,surefire_utf8_test/1,surefire_latin_test/1, + surefire_c0_test/1]). -include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [app_test, appup_test, eunit_test, surefire_utf8_test, surefire_latin_test]. + [app_test, appup_test, eunit_test, surefire_utf8_test, surefire_latin_test, + surefire_c0_test]. groups() -> []. @@ -65,11 +67,24 @@ surefire_utf8_test(Config) when is_list(Config) -> check_surefire(tutf8), ok. +surefire_c0_test(Config) when is_list(Config) -> + ok = file:set_cwd(proplists:get_value(priv_dir, Config, ".")), + Chars = check_surefire(tc0), + %% Check that these characters were not stripped + true = lists:member($\n, Chars), + true = lists:member($\r, Chars), + true = lists:member($\t, Chars), + ok. + check_surefire(Module) -> File = "TEST-"++atom_to_list(Module)++".xml", file:delete(File), % ignore test result, some fail on purpose eunit:test(Module, [{report,{eunit_surefire,[{dir,"."}]}}]), {ok, Bin} = file:read_file(File), - [_|_] = unicode:characters_to_list(Bin, unicode), - ok.
\ No newline at end of file + Chars = unicode:characters_to_list(Bin, unicode), + %% Check that unicode decoding succeeded + [_|_] = Chars, + %% Check that file is valid XML + xmerl_scan:file(File), + Chars. diff --git a/lib/eunit/test/tc0.erl b/lib/eunit/test/tc0.erl new file mode 100644 index 0000000000..8c90633fc8 --- /dev/null +++ b/lib/eunit/test/tc0.erl @@ -0,0 +1,14 @@ +-module(tc0). + +-include_lib("eunit/include/eunit.hrl"). + +'c0_bad_output_test_'() -> + [{integer_to_list(C), fun() -> io:format("'~c'", [C]) end} + || C <- lists:seq(0, 31)]. + +'c0_bad_description_test_'() -> + [{[C], fun() -> ok end} + || C <- lists:seq(0, 31)]. + +'c0_bad_name__test'() -> + ok. diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl index 995c961e09..efdaeecca3 100644 --- a/lib/hipe/icode/hipe_beam_to_icode.erl +++ b/lib/hipe/icode/hipe_beam_to_icode.erl @@ -557,21 +557,32 @@ trans_fun([{move,Src,Dst}|Instructions], Env) -> Dst1 = mk_var(Dst), Src1 = trans_arg(Src), [hipe_icode:mk_move(Dst1,Src1) | trans_fun(Instructions,Env)]; -%% -%% try/catch -- THESE ARE KNOWN TO MISCOMPILE, SEE OTP-15949 -%% -trans_fun([{'catch'=Name,_,_}|_], _Env) -> - nyi(Name); -trans_fun([{catch_end=Name,_}|_], _Env) -> - nyi(Name); -trans_fun([{'try'=Name,_,_}|_], _Env) -> - nyi(Name); -trans_fun([{try_end=Name,_}|_], _Env) -> - nyi(Name); -trans_fun([{try_case=Name,_}|_], _Env) -> - nyi(Name); -trans_fun([{try_case_end=Name,_}|_], _Env) -> - nyi(Name); +%%--- catch --- ITS PROCESSING IS POSTPONED +trans_fun([{'catch',N,{_,EndLabel}}|Instructions], Env) -> + NewContLbl = mk_label(new), + [{'catch',N,EndLabel},NewContLbl | trans_fun(Instructions,Env)]; +%%--- catch_end --- ITS PROCESSING IS POSTPONED +trans_fun([{catch_end,_N}=I|Instructions], Env) -> + [I | trans_fun(Instructions,Env)]; +%%--- try --- ITS PROCESSING IS POSTPONED +trans_fun([{'try',N,{_,EndLabel}}|Instructions], Env) -> + NewContLbl = mk_label(new), + [{'try',N,EndLabel},NewContLbl | trans_fun(Instructions,Env)]; +%%--- try_end --- +trans_fun([{try_end,_N}|Instructions], Env) -> + [hipe_icode:mk_end_try() | trans_fun(Instructions,Env)]; +%%--- try_case --- ITS PROCESSING IS POSTPONED +trans_fun([{try_case,_N}=I|Instructions], Env) -> + [I | trans_fun(Instructions,Env)]; +%%--- try_case_end --- +trans_fun([{try_case_end,Arg}|Instructions], Env) -> + BadArg = trans_arg(Arg), + ErrVar = mk_var(new), + Vs = [mk_var(new)], + Atom = hipe_icode:mk_move(ErrVar,hipe_icode:mk_const(try_clause)), + Tuple = hipe_icode:mk_primop(Vs,mktuple,[ErrVar,BadArg]), + Fail = hipe_icode:mk_fail(Vs,error), + [Atom,Tuple,Fail | trans_fun(Instructions,Env)]; %%--- raise --- trans_fun([{raise,{f,0},[Reg1,Reg2],{x,0}}|Instructions], Env) -> V1 = trans_arg(Reg1), @@ -2292,9 +2303,7 @@ fix_catch(Type, Lbl, ContLbl, Code, HandledCatchLbls, Instr) -> TLbl = {Type, Lbl}, case gb_trees:lookup(TLbl, HandledCatchLbls) of {value, Catch} when is_integer(Catch) -> - NewCode = fix_catches(Code, HandledCatchLbls), - Cont = hipe_icode:label_name(ContLbl), - [hipe_icode:mk_begin_try(Catch,Cont),ContLbl | NewCode]; + nyi(unsafe_catch); none -> OldCatch = map_label(Lbl), OldCatchLbl = hipe_icode:mk_label(OldCatch), diff --git a/lib/hipe/icode/hipe_icode_primops.erl b/lib/hipe/icode/hipe_icode_primops.erl index a1f1128124..63b34f23a4 100644 --- a/lib/hipe/icode/hipe_icode_primops.erl +++ b/lib/hipe/icode/hipe_icode_primops.erl @@ -133,6 +133,7 @@ is_safe({hipe_bs_primop, {bs_append, _, _, _, _}}) -> false; is_safe({hipe_bs_primop, {bs_private_append, _, _}}) -> false; is_safe({hipe_bs_primop, bs_init_writable}) -> true; is_safe(build_stacktrace) -> true; +is_safe(raw_raise) -> false; is_safe(#mkfun{}) -> true; is_safe(#unsafe_element{}) -> true; is_safe(#unsafe_update_element{}) -> true; diff --git a/lib/hipe/test/basic_SUITE_data/basic_exceptions.erl b/lib/hipe/test/basic_SUITE_data/basic_exceptions.erl index ba9c03d4ba..9f79231e5a 100644 --- a/lib/hipe/test/basic_SUITE_data/basic_exceptions.erl +++ b/lib/hipe/test/basic_SUITE_data/basic_exceptions.erl @@ -25,6 +25,7 @@ test() -> ok = test_guard_bif(), ok = test_eclectic(), ok = test_raise(), + ok = test_effect(), ok. %%-------------------------------------------------------------------- @@ -675,4 +676,18 @@ do_test_raise_3(Expr) -> erlang:raise(exit, {exception,C,E}, Stk) end. +test_effect() -> + ok = effect_try(2), + {'EXIT',{badarith,_}} = (catch effect_try(bad)), + ok. + +effect_try(X) -> + try + X + 1 + catch + C:E:Stk -> + erlang:raise(C, E, Stk) + end, + ok. + id(I) -> I. diff --git a/lib/kernel/doc/src/erl_epmd.xml b/lib/kernel/doc/src/erl_epmd.xml index 2adbf11a28..31f49a05cb 100644 --- a/lib/kernel/doc/src/erl_epmd.xml +++ b/lib/kernel/doc/src/erl_epmd.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2018</year><year>2018</year> + <year>2018</year><year>2019</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -36,7 +36,7 @@ <p>This module communicates with the EPMD daemon, see <seealso marker="erts:epmd">epmd</seealso>. To implement your own epmd module please see <seealso marker="erts:alt_disco">ERTS User's Guide: How to Implement an - Alternative Service Discovery for Erlang Distribution</seealso></p> + Alternative Node Discovery for Erlang Distribution</seealso></p> </description> <funcs> diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 39da9ef3d6..7cf7ab3b4d 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2018</year> + <year>2004</year><year>2019</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -1048,7 +1048,7 @@ marker="kernel:erl_epmd"><c>erl_epmd</c></seealso> reference manual and ERTS User's Guide <seealso marker="erts:alt_disco">How to Implement an Alternative - Service Discovery for Erlang Distribution</seealso>.</p> + Node Discovery for Erlang Distribution</seealso>.</p> <p> Own Id: OTP-15086 Aux Id: PR-1694 </p> </item> diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 68e1205301..5469d8694c 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -1437,11 +1437,16 @@ error_msg(Format, Args) -> %% This is equal to calling logger:error/3 which we don't want to %% do from code_server. We don't want to call logger:timestamp() %% either. - logger ! {log,error,Format,Args, - #{pid=>self(), - gl=>group_leader(), - time=>os:system_time(microsecond), - error_logger=>#{tag=>error}}}, + _ = try + logger ! {log,error,Format,Args, + #{pid=>self(), + gl=>group_leader(), + time=>os:system_time(microsecond), + error_logger=>#{tag=>error}}} + catch _:_ -> + erlang:display({?MODULE,error}), + erlang:display({Format,Args}) + end, ok. -spec info_msg(io:format(), [term()]) -> 'ok'. @@ -1449,11 +1454,11 @@ info_msg(Format, Args) -> %% This is equal to calling logger:info/3 which we don't want to %% do from code_server. We don't want to call logger:timestamp() %% either. - logger ! {log,info,Format,Args, - #{pid=>self(), - gl=>group_leader(), - time=>os:system_time(microsecond), - error_logger=>#{tag=>info_msg}}}, + catch logger ! {log,info,Format,Args, + #{pid=>self(), + gl=>group_leader(), + time=>os:system_time(microsecond), + error_logger=>#{tag=>info_msg}}}, ok. objfile_extension() -> diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index 09ed31f10c..d8571c01be 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -344,22 +344,11 @@ shutdown(_Module, _Line, _Data, Reason) -> ?shutdown_trace("Net Kernel 2: shutting down connection " "~p:~p, data ~p,reason ~p~n", [_Module,_Line, _Data, Reason]), - flush_down(), exit(Reason). %% Use this line to debug connection. %% Set net_kernel verbose = 1 as well. %% exit({Reason, ?MODULE, _Line, _Data, erlang:timestamp()}). - -flush_down() -> - receive - {From, get_status} -> - From ! {self(), get_status, error}, - flush_down() - after 0 -> - ok - end. - handshake_we_started(#hs_data{request_type=ReqType, other_node=Node, add_flags=AddFlgs0, diff --git a/lib/kernel/src/inet_dns.erl b/lib/kernel/src/inet_dns.erl index 6c98d2aab7..e03f124fe6 100644 --- a/lib/kernel/src/inet_dns.erl +++ b/lib/kernel/src/inet_dns.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -109,8 +109,8 @@ lists_member(H, [H|_]) -> true; lists_member(H, [_|T]) -> lists_member(H, T). - --define(DECODE_ERROR, fmt). % must match a clause in inet_res:query_nss_e?dns +%% must match a clause in inet_res:query_nss_e?dns +-define(DECODE_ERROR, formerr). %% %% Decode a dns buffer. diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl index 6454802b04..7886ef83ac 100644 --- a/lib/kernel/src/inet_res.erl +++ b/lib/kernel/src/inet_res.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2018. All Rights Reserved. +%% Copyright Ericsson AB 1997-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -691,21 +691,22 @@ udp_send(#sock{inet=I}, {A,B,C,D}=IP, Port, Buffer) gen_udp:send(I, IP, Port, Buffer). udp_recv(#sock{inet6=I}, {A,B,C,D,E,F,G,H}=IP, Port, Timeout, Decode) - when ?ip6(A,B,C,D,E,F,G,H), ?port(Port) -> - do_udp_recv(I, IP, Port, Timeout, Decode, time_now(), Timeout); + when ?ip6(A,B,C,D,E,F,G,H), ?port(Port), 0 =< Timeout -> + do_udp_recv(I, IP, Port, Timeout, Decode, deadline(Timeout), Timeout); udp_recv(#sock{inet=I}, {A,B,C,D}=IP, Port, Timeout, Decode) - when ?ip(A,B,C,D), ?port(Port) -> - do_udp_recv(I, IP, Port, Timeout, Decode, time_now(), Timeout). + when ?ip(A,B,C,D), ?port(Port), 0 =< Timeout -> + do_udp_recv(I, IP, Port, Timeout, Decode, deadline(Timeout), Timeout). -do_udp_recv(_I, _IP, _Port, 0, _Decode, _Start, _T) -> +do_udp_recv(_I, _IP, _Port, 0, _Decode, _Deadline, PollCnt) + when PollCnt =< 0 -> timeout; -do_udp_recv(I, IP, Port, Timeout, Decode, Start, T) -> - case gen_udp:recv(I, 0, T) of +do_udp_recv(I, IP, Port, Timeout, Decode, Deadline, PollCnt) -> + case gen_udp:recv(I, 0, Timeout) of {ok,Reply} -> case Decode(Reply) of - false when T =:= 0 -> + false when Timeout =:= 0 -> %% This is a compromize between the hard way i.e - %% in the clause below if NewT becomes 0 bailout + %% in the clause below if Timeout becomes 0 bailout %% immediately and risk that the right reply lies %% ahead after some bad id replies, and the %% forgiving way i.e go on with Timeout 0 until @@ -713,15 +714,12 @@ do_udp_recv(I, IP, Port, Timeout, Decode, Start, T) -> %% which opens for a DOS attack by a malicious %% DNS server flooding with bad id replies causing %% an infinite loop here. - %% - %% Timeout is used as a sanity limit counter - %% just to put an end to the loop. - NewTimeout = erlang:max(0, Timeout - 50), - do_udp_recv(I, IP, Port, NewTimeout, Decode, Start, T); + %% + do_udp_recv( + I, IP, Port, Timeout, Decode, Deadline, PollCnt-50); false -> - Now = time_now(), - NewT = erlang:max(0, Timeout - now_ms(Now, Start)), - do_udp_recv(I, IP, Port, Timeout, Decode, Start, NewT); + T = timeout(Deadline), + do_udp_recv(I, IP, Port, T, Decode, Deadline, PollCnt); Result -> Result end; @@ -758,71 +756,122 @@ udp_close(#sock{inet=I,inet6=I6}) -> %% And that is what the code seems to do, now fixed, hopefully... do_query(_Q, [], _Timer) -> + %% We have no name server to ask, so say nxdomain {error,nxdomain}; do_query(#q{options=#options{retry=Retry}}=Q, NSs, Timer) -> - query_retries(Q, NSs, Timer, Retry, 0, #sock{}). - -query_retries(_Q, _NSs, _Timer, Retry, Retry, S) -> - udp_close(S), - {error,timeout}; -query_retries(_Q, [], _Timer, _Retry, _I, S) -> - udp_close(S), - {error,timeout}; -query_retries(Q, NSs, Timer, Retry, I, S0) -> - case query_nss(Q, NSs, Timer, Retry, I, S0, []) of - {S,{noanswer,ErrNSs}} -> %% remove unreachable nameservers - query_retries(Q, NSs--ErrNSs, Timer, Retry, I+1, S); - {S,Result} -> - udp_close(S), - Result - end. + %% We have at least one name server, + %% so a failure will be a timeout, + %% unless a name server says otherwise + Reason = timeout, + query_retries(Q, NSs, Timer, Retry, 0, #sock{}, Reason). -query_nss(_Q, [], _Timer, _Retry, _I, S, ErrNSs) -> - {S,{noanswer,ErrNSs}}; -query_nss(#q{edns=undefined}=Q, NSs, Timer, Retry, I, S, ErrNSs) -> - query_nss_dns(Q, NSs, Timer, Retry, I, S, ErrNSs); -query_nss(Q, NSs, Timer, Retry, I, S, ErrNSs) -> - query_nss_edns(Q, NSs, Timer, Retry, I, S, ErrNSs). +%% Loop until out of name servers or retries +%% +query_retries(_Q, _NSs, _Timer, Retry, Retry, S, Reason) -> + query_retries_error(S, Reason); +query_retries(_Q, [], _Timer, _Retry, _I, S, Reason) -> + query_retries_error(S, Reason); +query_retries(Q, NSs, Timer, Retry, I, S_0, Reason) -> + query_nss(Q, NSs, Timer, Retry, I, S_0, Reason, NSs). + +%% Loop for all name servers, for each: +%% If EDNS is enabled, try that first, +%% and for selected failures fall back to plain DNS. +%% +query_nss(Q, NSs, Timer, Retry, I, S, Reason, []) -> + %% End of name servers list, do a new retry + query_retries(Q, NSs, Timer, Retry, I+1, S, Reason); +query_nss(#q{edns = undefined}=Q, NSs, Timer, Retry, I, S, Reason, TryNSs) -> + query_nss_dns(Q, NSs, Timer, Retry, I, S, Reason, TryNSs); +query_nss(Q, NSs, Timer, Retry, I, S, Reason, TryNSs) -> + query_nss_edns(Q, NSs, Timer, Retry, I, S, Reason, TryNSs). query_nss_edns( - #q{options=#options{udp_payload_size=PSz}=Options,edns={Id,Buffer}}=Q, - [{IP,Port}=NS|NSs]=NSs0, Timer, Retry, I, S0, ErrNSs) -> - {S,Res}=Reply = - query_ns(S0, Id, Buffer, IP, Port, Timer, Retry, I, Options, PSz), - case Res of - timeout -> {S,{error,timeout}}; % Bailout timeout - {ok,_} -> Reply; - {error,{nxdomain,_}} -> Reply; - {error,{E,_}} when E =:= qfmterror; E =:= notimp; E =:= servfail; - E =:= badvers -> - query_nss_dns(Q, NSs0, Timer, Retry, I, S, ErrNSs); - {error,E} when E =:= fmt; E =:= enetunreach; E =:= econnrefused -> - query_nss(Q, NSs, Timer, Retry, I, S, [NS|ErrNSs]); - _Error -> - query_nss(Q, NSs, Timer, Retry, I, S, ErrNSs) + #q{options = + #options{ + udp_payload_size = PSz}=Options, + edns = {Id,Buffer}}=Q, + NSs, Timer, Retry, I, S_0, Reason, [{IP,Port}=NS|TryNSs]=TryNSs_0) -> + %% + {S,Result} = + query_ns( + S_0, Id, Buffer, IP, Port, Timer, Retry, I, Options, PSz), + case Result of + {error,{E,_}} + when E =:= qfmterror; + E =:= notimp; + E =:= servfail; + E =:= badvers -> + %% The server did not like that, + %% ignore that error and try plain DNS + query_nss_dns( + Q, NSs, Timer, Retry, I, S, Reason, TryNSs_0); + _ -> + query_nss_result( + Q, NSs, Timer, Retry, I, S, Reason, TryNSs, NS, Result) end. query_nss_dns( - #q{dns=Qdns}=Q0, - [{IP,Port}=NS|NSs], Timer, Retry, I, S0, ErrNSs) -> - #q{options=Options,dns={Id,Buffer}}=Q = + #q{dns = Qdns}=Q_0, + NSs, Timer, Retry, I, S_0, Reason, [{IP,Port}=NS|TryNSs]) -> + %% + #q{options = Options, + dns = {Id,Buffer}}=Q = if - is_function(Qdns, 0) -> Q0#q{dns=Qdns()}; - true -> Q0 + is_function(Qdns, 0) -> Q_0#q{dns=Qdns()}; + true -> Q_0 end, - {S,Res}=Reply = + {S,Result} = query_ns( - S0, Id, Buffer, IP, Port, Timer, Retry, I, Options, ?PACKETSZ), - case Res of - timeout -> {S,{error,timeout}}; % Bailout timeout - {ok,_} -> Reply; - {error,{E,_}} when E =:= nxdomain; E =:= qfmterror -> Reply; - {error,E} when E =:= fmt; E =:= enetunreach; E =:= econnrefused -> - query_nss(Q, NSs, Timer, Retry, I, S, [NS|ErrNSs]); - _Error -> - query_nss(Q, NSs, Timer, Retry, I, S, ErrNSs) + S_0, Id, Buffer, IP, Port, Timer, Retry, I, Options, ?PACKETSZ), + query_nss_result( + Q, NSs, Timer, Retry, I, S, Reason, TryNSs, NS, Result). + +query_nss_result(Q, NSs, Timer, Retry, I, S, Reason, TryNSs, NS, Result) -> + case Result of + {ok,_} -> + _ = udp_close(S), + Result; + timeout -> % Out of total time timeout + query_retries_error(S, Reason); % The best reason we have + {error,timeout} -> % Query timeout + %% Try next server, may retry this server later + query_nss(Q, NSs, Timer, Retry, I, S, Reason, TryNSs); + {error,{nxdomain,_}=NewReason} -> + query_retries_error(S, NewReason); % Definite answer + {error,{E,_}=NewReason} + when E =:= qfmterror; + E =:= notimp; + E =:= refused; + E =:= badvers -> + %% The server did not like that. + %% Remove this server from retry list since + %% it will not answer differently on the next retry. + NewNSs = lists:delete(NS, NSs), + query_nss(Q, NewNSs, Timer, Retry, I, S, NewReason, TryNSs); + {error,E=NewReason} + when E =:= formerr; + E =:= enetunreach; + E =:= econnrefused -> + %% Could not decode answer, or network problem. + %% Remove this server from retry list. + NewNSs = lists:delete(NS, NSs), + query_nss(Q, NewNSs, Timer, Retry, I, S, NewReason, TryNSs); + {error,NewReason} -> + %% Try next server, may retry this server later + query_nss(Q, NSs, Timer, Retry, I, S, NewReason, TryNSs) end. +query_retries_error(S, Reason) -> + _ = udp_close(S), + case Reason of + {nxdomain, _} -> + {error, nxdomain}; + _ -> + {error, Reason} + end. + + query_ns(S0, Id, Buffer, IP, Port, Timer, Retry, I, #options{timeout=Tm,usevc=UseVC,verbose=Verbose}, PSz) -> @@ -1035,10 +1084,11 @@ dns_msg(Msg) -> {Type,dns_msg(Fields)} end. --compile({inline, [now_ms/2]}). -now_ms(Int1, Int0) -> - Int1 - Int0. - --compile({inline, [time_now/0]}). -time_now() -> - erlang:monotonic_time(1000). +-compile({inline, [deadline/1, timeout/1]}). +deadline(Timeout) -> % When is the deadline? [ms] + erlang:monotonic_time(1000) + Timeout. +timeout(Deadline) -> % How long to deadline? [ms] >= 0 + case Deadline - erlang:monotonic_time(1000) of + Timeout when 0 =< Timeout -> Timeout; + _ -> 0 + end. diff --git a/lib/kernel/src/logger_config.erl b/lib/kernel/src/logger_config.erl index 5024d20cfe..af8ebfd4e9 100644 --- a/lib/kernel/src/logger_config.erl +++ b/lib/kernel/src/logger_config.erl @@ -41,7 +41,7 @@ delete(Tid,Id) -> allow(Tid,Level,Module) -> LevelInt = level_to_int(Level), - case ets:lookup(Tid,Module) of + try ets:lookup(Tid,Module) of [{Module,{ModLevel,cached}}] when is_integer(ModLevel), LevelInt =< ModLevel -> true; @@ -53,11 +53,17 @@ allow(Tid,Level,Module) -> allow(Tid,Level); _ -> false + catch error:badarg -> + true end. allow(Tid,Level) -> - GlobalLevelInt = ets:lookup_element(Tid,?PRIMARY_KEY,2), - level_to_int(Level) =< GlobalLevelInt. + try ets:lookup_element(Tid,?PRIMARY_KEY,2) of + GlobalLevelInt -> + level_to_int(Level) =< GlobalLevelInt + catch error:badarg -> + true + end. exist(Tid,What) -> ets:member(Tid,table_key(What)). diff --git a/lib/kernel/src/logger_proxy.erl b/lib/kernel/src/logger_proxy.erl index 24b293805c..6ab8e3e4c5 100644 --- a/lib/kernel/src/logger_proxy.erl +++ b/lib/kernel/src/logger_proxy.erl @@ -42,11 +42,16 @@ StringOrReport :: unicode:chardata() | logger:report(), Meta :: logger:metadata(). log(RemoteLog) -> - Olp = persistent_term:get(?MODULE), - case logger_olp:get_pid(Olp) =:= self() of + Olp = persistent_term:get(?MODULE, undefined), + case (Olp =:= undefined) orelse (logger_olp:get_pid(Olp) =:= self()) of true -> %% This happens when the log event comes from the %% emulator, and the group leader is on a remote node. + %% + %% OR + %% + %% when we are to log a remote message before the logger_proxy + %% has started _ = handle_load(RemoteLog, no_state), ok; false -> @@ -112,9 +117,12 @@ init([]) -> %% Log event to send to the node where the group leader of it's client resides handle_load({remote,Node,Log},State) -> - %% If the connection is overloaded (send_nosuspend returns false), - %% we drop the message. - _ = erlang:send_nosuspend({?SERVER,Node},Log), + case erlang:send({?SERVER,Node},Log,[nosuspend]) of + _ok_or_nosuspend -> + %% If the connection is overloaded (send returns nosuspend), + %% we drop the message. + ok + end, State; %% Log event to log on this node handle_load({log,Level,Format,Args,Meta},State) -> diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index 4a68e6676d..e4b214a6c5 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -121,17 +121,23 @@ -define(LISTEN_ID, #listen.listen). -define(ACCEPT_ID, #listen.accept). +-type connection_state() :: pending | up | up_pending. +-type connection_type() :: normal | hidden. + +-include("net_address.hrl"). + +%% Relaxed typing to allow ets:select without Dialyzer complains. -record(connection, { - node, %% remote node name - conn_id, %% Connection identity - state, %% pending | up | up_pending - owner, %% owner pid - ctrlr, %% Controller port or pid - pending_owner, %% possible new owner - address, %% #net_address - waiting = [], %% queued processes - type %% normal | hidden - }). + node :: node() | '_', %% remote node name + conn_id, %% Connection identity + state :: connection_state() | '_', + owner :: pid() | '_', %% owner pid + ctrlr, %% Controller port or pid + pending_owner :: pid() | '_' | undefined, %% possible new owner + address :: #net_address{} | '_', + waiting = [], %% queued processes + type :: connection_type() | '_' +}). -record(barred_connection, { node %% remote node name @@ -151,8 +157,6 @@ %% the connection setup. -define(SETUPTIME, 7000). --include("net_address.hrl"). - %%% BIF -export([dflag_unicode_io/1]). @@ -180,9 +184,31 @@ longnames() -> request(longnames). Reason :: not_allowed | not_found. stop() -> erl_distribution:stop(). -node_info(Node) -> get_node_info(Node). -node_info(Node, Key) -> get_node_info(Node, Key). -nodes_info() -> get_nodes_info(). +-type node_info() :: + {address, #net_address{}} | + {type, connection_type()} | + {in, non_neg_integer()} | + {out, non_neg_integer()} | + {owner, pid()} | + {state, connection_state()}. + +-spec node_info(node()) -> {ok, [node_info()]} | {error, bad_node}. +node_info(Node) -> + get_node_info(Node). + +-spec node_info(node(), address) -> {ok, Address} | {error, bad_node} when Address :: #net_address{}; + (node(), type) -> {ok, Type} | {error, bad_node} when Type :: connection_type(); + (node(), in | out) -> {ok, Bytes} | {error, bad_node} when Bytes :: non_neg_integer(); + (node(), owner) -> {ok, Owner} | {error, bad_node} when Owner :: pid(); + (node(), state) -> {ok, State} | {error, bad_node} when State :: connection_state(). + %(node(), term()) -> {error, invalid_key} | {error, bad_node}. +node_info(Node, Key) -> + get_node_info(Node, Key). + +-spec nodes_info() -> {ok, [{node(), [node_info()]}]}. +nodes_info() -> + get_nodes_info(). + i() -> print_info(). i(Node) -> print_info(Node). @@ -1621,61 +1647,73 @@ connecttime() -> get_node_info(Node) -> case ets:lookup(sys_dist, Node) of - [Conn = #connection{owner = Owner, state = State}] -> - case get_status(Owner, Node, State) of - {ok, In, Out} -> - {ok, [{owner, Owner}, - {state, State}, - {address, Conn#connection.address}, - {type, Conn#connection.type}, - {in, In}, - {out, Out}]}; + [#connection{owner = Owner, state = up, address = Addr, type = Type}] -> + MRef = monitor(process, Owner), + Owner ! {self(), get_status}, + receive + {Owner, get_status, {ok, Read, Write}} -> + demonitor(MRef, [flush]), + {ok, [{owner, Owner}, {state, up}, {address, Addr}, + {type, Type}, {in, Read}, {out, Write}]}; + {'DOWN', MRef, process, Owner, _Info} -> + {error, bad_node} + end; + [#connection{owner = Owner, state = State, address = Addr, type = Type}] -> + {ok, [{owner, Owner}, {state, State}, {address, Addr}, + {type, Type}, {in, 0}, {out, 0}]}; _ -> {error, bad_node} - end; - _ -> - {error, bad_node} end. -%% -%% We can't do monitor_node here incase the node is pending, -%% the monitor_node/2 call hangs until the connection is ready. -%% We will not ask about in/out information either for pending -%% connections as this also would block this call awhile. -%% -get_status(Owner, Node, up) -> - monitor_node(Node, true), - Owner ! {self(), get_status}, - receive - {Owner, get_status, Res} -> - monitor_node(Node, false), - Res; - {nodedown, Node} -> - error - end; -get_status(_, _, _) -> - {ok, 0, 0}. - get_node_info(Node, Key) -> case get_node_info(Node) of - {ok, Info} -> - case lists:keysearch(Key, 1, Info) of - {value, {Key, Value}} -> {ok, Value}; - _ -> {error, invalid_key} - end; - Error -> - Error + {ok, Info} -> + case lists:keyfind(Key, 1, Info) of + {Key, Value} -> + {ok, Value}; + false -> + {error, invalid_key} + end; + {error, bad_node} -> + {error, bad_node} end. + get_nodes_info() -> - Nodes = ets:select(sys_dist, [{#connection{node = '$1', _ = '_'}, [], ['$1']}]), - {ok, lists:filtermap( - fun(Node) -> - case get_node_info(Node) of - {ok, Info} -> {true, {Node, Info}}; - _ -> false - end - end, Nodes)}. + Conns = ets:select(sys_dist, [{#connection{_ = '_'}, [], ['$_']}]), + Info = multi_info(Conns, {self(), get_status}, #{}, []), + {ok, Info}. + +multi_info([], _Msg, PidToRef, NodeInfos) -> + multi_receive(PidToRef, NodeInfos); +multi_info([#connection{owner = Owner, state = up} = Conn | Conns], Msg, PidToRef, NodeInfos) -> + % connection is up, try to figure out in/out bytes + MRef = erlang:monitor(process, Owner), + Owner ! Msg, + multi_info(Conns, Msg, maps:put(Owner, {MRef, Conn}, PidToRef), NodeInfos); +multi_info([#connection{node = Node, owner = Owner, type = Type, + state = State, address = Addr} | Conns], Msg, PidToRef, NodeInfos) -> + % connection is not up: in/out bytes are zero + multi_info(Conns, Msg, PidToRef, [ + {Node, [{owner, Owner}, {state, State}, {address, Addr}, {type, Type}, {in, 0}, {out, 0}]} + | NodeInfos]). + +multi_receive(PidToRef, NodeInfos) when map_size(PidToRef) =:= 0 -> + NodeInfos; +multi_receive(PidToRef, NodeInfos) -> + receive + {DistProc, get_status, {ok, Read, Write}} -> + {{MRef, #connection{node = Node, owner = Owner, type = Type, + state = State, address = Addr}}, NewRefs} = maps:take(DistProc, PidToRef), + erlang:demonitor(MRef, [flush]), + multi_receive(NewRefs, [ + {Node, [{owner, Owner}, {state, State}, {address, Addr}, {type, Type}, {in, Read}, {out, Write}]} + | NodeInfos]); + {'DOWN', _MRef, process, Pid, _Info} -> + % connection went down: reproducing compatible behaviour with + % not showing any information about this connection + multi_receive(maps:remove(Pid, PidToRef), NodeInfos) + end. %% ------------------------------------------------------------ %% Misc. functions diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 6b133f8d6b..131e3fed34 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -28,7 +28,7 @@ delete/1, purge/1, purge_many_exits/0, purge_many_exits/1, soft_purge/1, is_loaded/1, all_loaded/1, load_binary/1, dir_req/1, object_code/1, set_path_file/1, - upgrade/1, + upgrade/0, upgrade/1, sticky_dir/1, pa_pz_option/1, add_del_path/1, dir_disappeared/1, ext_mod_dep/1, clash/1, where_is_file/1, @@ -523,6 +523,9 @@ load_binary(Config) when is_list(Config) -> ok. +upgrade() -> + [{timetrap,{minutes,2}}]. + upgrade(Config) -> DataDir = proplists:get_value(data_dir, Config), diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index 70d8caf478..2720d3cc77 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2019. All Rights Reserved. +%% Copyright Ericsson AB 1998-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -35,7 +35,8 @@ -export([send_to_closed/1, active_n/1, buffer_size/1, binary_passive_recv/1, max_buffer_size/1, bad_address/1, - read_packets/1, open_fd/1, connect/1, implicit_inet6/1, + read_packets/1, recv_poll_after_active_once/1, + open_fd/1, connect/1, implicit_inet6/1, recvtos/1, recvtosttl/1, recvttl/1, recvtclass/1, sendtos/1, sendtosttl/1, sendttl/1, sendtclass/1, local_basic/1, local_unbound/1, @@ -47,7 +48,8 @@ suite() -> all() -> [send_to_closed, buffer_size, binary_passive_recv, max_buffer_size, - bad_address, read_packets, open_fd, connect, + bad_address, read_packets, recv_poll_after_active_once, + open_fd, connect, implicit_inet6, active_n, recvtos, recvtosttl, recvttl, recvtclass, sendtos, sendtosttl, sendttl, sendtclass, @@ -436,6 +438,27 @@ flush() -> end. +%% OTP-16059 +%% UDP recv with timeout 0 corrupts internal state so that after a +%% recv under {active, once} the UDP recv poll wastes incoming data +recv_poll_after_active_once(Config) when is_list(Config) -> + Msg1 = <<"Hej!">>, + Msg2 = <<"Hej igen!">>, + Addr = {127,0,0,1}, + {ok,S1} = gen_udp:open(0, [binary, {active, once}]), + {ok,P1} = inet:port(S1), + {ok,S2} = gen_udp:open(0, [binary, {active, false}]), + {ok,P2} = inet:port(S2), + ok = gen_udp:send(S2, Addr, P1, Msg1), + receive + {udp, S1, Addr, P2, Msg1} -> + {error, timeout} = gen_udp:recv(S1, 0, 0), + ok = gen_udp:send(S2, Addr, P1, Msg2), + receive after 500 -> ok end, % Give the kernel time to deliver + {ok, {Addr, P2, Msg2}} = gen_udp:recv(S1, 0, 0), + ok + end. + %% Test that the 'fd' option works. open_fd(Config) when is_list(Config) -> diff --git a/lib/kernel/test/inet_res_SUITE.erl b/lib/kernel/test/inet_res_SUITE.erl index 6b545fa414..8b3f1aa2a9 100644 --- a/lib/kernel/test/inet_res_SUITE.erl +++ b/lib/kernel/test/inet_res_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2018. All Rights Reserved. +%% Copyright Ericsson AB 2009-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]). -export([basic/1, resolve/1, edns0/1, txt_record/1, files_monitor/1, - last_ms_answer/1]). + last_ms_answer/1, intermediate_error/1]). -export([ gethostbyaddr/0, gethostbyaddr/1, gethostbyaddr_v6/0, gethostbyaddr_v6/1, @@ -64,7 +64,7 @@ suite() -> all() -> [basic, resolve, edns0, txt_record, files_monitor, - last_ms_answer, + last_ms_answer, intermediate_error, gethostbyaddr, gethostbyaddr_v6, gethostbyname, gethostbyname_v6, getaddr, getaddr_v6, ipv4_to_ipv6, host_and_addr]. @@ -91,6 +91,9 @@ zone_dir(TC) -> edns0 -> otptest; files_monitor -> otptest; last_ms_answer -> otptest; + intermediate_error -> + {internal, + #{rcode => ?REFUSED}}; _ -> undefined end. @@ -106,6 +109,9 @@ init_per_testcase(Func, Config) -> inet_db:ins_alt_ns(IP, Port); _ -> ok end, + %% dbg:tracer(), + %% dbg:p(all, c), + %% dbg:tpl(inet_res, query_nss_res, cx), [{nameserver,NsSpec},{res_lookup,Lookup}|Config] catch SkipReason -> @@ -120,6 +126,7 @@ end_per_testcase(_Func, Config) -> inet_db:del_alt_ns(IP, Port); _ -> ok end, + %% dbg:stop(), ns_end(NsSpec, proplists:get_value(priv_dir, Config)). %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -130,15 +137,18 @@ ns(Config) -> NS. ns_init(ZoneDir, PrivDir, DataDir) -> - case os:type() of - {unix,_} when ZoneDir =:= undefined -> undefined; - {unix,_} -> + case {os:type(),ZoneDir} of + {_,{internal,ServerSpec}} -> + ns_start_internal(ServerSpec); + {{unix,_},undefined} -> + undefined; + {{unix,_},otptest} -> PortNum = case {os:type(),os:version()} of {{unix,solaris},{M,V,_}} when M =< 5, V < 10 -> 11895 + rand:uniform(100); _ -> - {ok,S} = gen_udp:open(0, [{reuseaddr,true}]), - {ok,PNum} = inet:port(S), + S = ok(gen_udp:open(0, [{reuseaddr,true}])), + PNum = ok(inet:port(S)), gen_udp:close(S), PNum end, @@ -170,12 +180,43 @@ ns_start(ZoneDir, PrivDir, NS, P) -> ns_start(ZoneDir, PrivDir, NS, P) end. +ns_start_internal(ServerSpec) -> + Parent = self(), + Tag = make_ref(), + {P,Mref} = + spawn_monitor( + fun () -> + _ = process_flag(trap_exit, true), + IP = {127,0,0,1}, + SocketOpts = [{ip,IP},binary,{active,once}], + S = ok(gen_udp:open(0, SocketOpts)), + Port = ok(inet:port(S)), + ParentMref = monitor(process, Parent), + Parent ! {Tag,{IP,Port},self()}, + ns_internal(ServerSpec, ParentMref, Tag, S) + end), + receive + {Tag,_NS,P} = NsSpec -> + demonitor(Mref, [flush]), + NsSpec; + {'DOWN',Mref,_,_,Reason} -> + exit({ns_start_internal,Reason}) + end. + ns_end(undefined, _PrivDir) -> undefined; -ns_end({ZoneDir,_NS,P}, PrivDir) -> +ns_end({ZoneDir,_NS,P}, PrivDir) when is_port(P) -> port_command(P, ["quit",io_lib:nl()]), ns_stop(P), ns_printlog(filename:join([PrivDir,ZoneDir,"named.log"])), - ok. + ok; +ns_end({Tag,_NS,P}, _PrivDir) when is_pid(P) -> + Mref = erlang:monitor(process, P), + P ! Tag, + receive + {'DOWN',Mref,_,_,Reason} -> + Reason = normal, + ok + end. ns_stop(P) -> case ns_collect(P) of @@ -209,6 +250,44 @@ ns_printlog(Fname) -> end. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Internal name server + +ns_internal(ServerSpec, Mref, Tag, S) -> + receive + {'DOWN',Mref,_,_,Reason} -> + exit(Reason); + Tag -> + ok; + {udp,S,IP,Port,Data} -> + Req = ok(inet_dns:decode(Data)), + Resp = ns_internal(ServerSpec, Req), + RespData = inet_dns:encode(Resp), + _ = ok(gen_udp:send(S, IP, Port, RespData)), + _ = ok(inet:setopts(S, [{active,once}])), + ns_internal(ServerSpec, Mref, Tag, S) + end. + +ns_internal(#{rcode := Rcode}, Req) -> + Hdr = inet_dns:msg(Req, header), + Opcode = inet_dns:header(Hdr, opcode), + Id = inet_dns:header(Hdr, id), + Rd = inet_dns:header(Hdr, rd), + %% + Qdlist = inet_dns:msg(Req, qdlist), + inet_dns:make_msg( + [{header, + inet_dns:make_header( + [{id,Id}, + {qr,true}, + {opcode,Opcode}, + {aa,true}, + {tc,false}, + {rd,Rd}, + {ra,false}, + {rcode,Rcode}])}, + {qdlist,Qdlist}]). + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Behaviour modifying nameserver proxy proxy_start(TC, {NS,P}) -> @@ -672,6 +751,23 @@ last_ms_answer(Config) when is_list(Config) -> ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% First name server answers ?REFUSED, second does not answer. +%% Check that we get the error code from the first server. + +intermediate_error(Config) when is_list(Config) -> + NS = ns(Config), + Name = "ns.otptest", + IP = {127,0,0,1}, + %% A "name server" that does not respond + S = ok(gen_udp:open(0, [{ip,IP},{active,false}])), + Port = ok(inet:port(S)), + NSs = [NS,{IP,Port}], + {error,{refused,_}} = + inet_res:resolve(Name, in, a, [{nameservers,NSs},verbose], 500), + _ = gen_udp:close(S), + ok. + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Compatibility tests. Call the inet_SUITE tests, but with %% lookup = [file,dns] instead of [native] @@ -729,3 +825,8 @@ tolower([C|Cs]) when is_integer(C) -> end; tolower([]) -> []. + +-compile({inline,[ok/1]}). +ok(ok) -> ok; +ok({ok,X}) -> X; +ok({error,Reason}) -> error(Reason). diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index a0154b2694..9d35611d93 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2018. All Rights Reserved. +%% Copyright Ericsson AB 1996-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -68,7 +68,7 @@ end_per_group(_GroupName, Config) -> Config. -init_per_testcase(Func, Config) -> +init_per_testcase(_Func, Config) -> Config. end_per_testcase(_Func, _Config) -> @@ -368,7 +368,8 @@ restart(Config) when is_list(Config) -> io:format("SysProcs0=~p~n", [SysProcs0]), [InitPid, PurgerPid, LitCollectorPid, DirtySigNPid, DirtySigHPid, DirtySigMPid, - PrimFilePid] = SysProcs0, + PrimFilePid, + ESockRegPid] = SysProcs0, InitPid = rpc:call(Node, erlang, whereis, [init]), PurgerPid = rpc:call(Node, erlang, whereis, [erts_code_purger]), Procs = rpc:call(Node, erlang, processes, []), @@ -387,7 +388,8 @@ restart(Config) when is_list(Config) -> io:format("SysProcs1=~p~n", [SysProcs1]), [InitPid1, PurgerPid1, LitCollectorPid1, DirtySigNPid1, DirtySigHPid1, DirtySigMPid1, - PrimFilePid1] = SysProcs1, + PrimFilePid1, + ESockRegPid1] = SysProcs1, %% Still the same init process! InitPid1 = rpc:call(Node, erlang, whereis, [init]), @@ -417,6 +419,10 @@ restart(Config) when is_list(Config) -> PrimFileP = pid_to_list(PrimFilePid), PrimFileP = pid_to_list(PrimFilePid1), + %% and same socket_registry helper process! + ESockRegP = pid_to_list(ESockRegPid), + ESockRegP = pid_to_list(ESockRegPid1), + NewProcs0 = rpc:call(Node, erlang, processes, []), NewProcs = NewProcs0 -- SysProcs1, case check_processes(NewProcs, MaxPid) of @@ -444,7 +450,8 @@ restart(Config) when is_list(Config) -> dirty_sig_handler_normal, dirty_sig_handler_high, dirty_sig_handler_max, - prim_file}). + prim_file, + socket_registry}). find_system_processes() -> find_system_procs(processes(), #sys_procs{}). @@ -456,7 +463,8 @@ find_system_procs([], SysProcs) -> SysProcs#sys_procs.dirty_sig_handler_normal, SysProcs#sys_procs.dirty_sig_handler_high, SysProcs#sys_procs.dirty_sig_handler_max, - SysProcs#sys_procs.prim_file]; + SysProcs#sys_procs.prim_file, + SysProcs#sys_procs.socket_registry]; find_system_procs([P|Ps], SysProcs) -> case process_info(P, [initial_call, priority]) of [{initial_call,{erl_init,start,2}},_] -> @@ -483,6 +491,9 @@ find_system_procs([P|Ps], SysProcs) -> [{initial_call,{prim_file,start,0}},_] -> undefined = SysProcs#sys_procs.prim_file, find_system_procs(Ps, SysProcs#sys_procs{prim_file = P}); + [{initial_call,{socket_registry,start,0}},_] -> + undefined = SysProcs#sys_procs.socket_registry, + find_system_procs(Ps, SysProcs#sys_procs{socket_registry = P}); _ -> find_system_procs(Ps, SysProcs) end. diff --git a/lib/kernel/test/logger_proxy_SUITE.erl b/lib/kernel/test/logger_proxy_SUITE.erl index 777531e4ed..bae2bd8d17 100644 --- a/lib/kernel/test/logger_proxy_SUITE.erl +++ b/lib/kernel/test/logger_proxy_SUITE.erl @@ -72,6 +72,7 @@ all() -> [basic, emulator, remote, + remote_disconnect, remote_emulator, config, restart_after, @@ -118,6 +119,19 @@ remote(Config) -> remote(cleanup,_Config) -> ok = logger:remove_handler(?HNAME). +remote_disconnect(Config) -> + {ok,_,Node} = logger_test_lib:setup(Config,[{logger,[{proxy,#{}}]}]), + ok = logger:add_handler(?HNAME,?MODULE,#{config=>self()}), + RemoteGL = rpc:call(Node, erlang, whereis, [user]), + net_kernel:disconnect(Node), + L1 = ?LOC#{ gl => RemoteGL }, logger:notice("Log from ~p; ~p",[?FUNCTION_NAME,?LINE],L1), + ok = ensure(L1), + L2 = ?LOC#{ gl => RemoteGL }, logger:notice([{test_case,?FUNCTION_NAME},{line,?LINE}],L2), + ok = ensure(L2), + ok. +remote_disconnect(cleanup,_Config) -> + ok = logger:remove_handler(?HNAME). + remote_emulator(Config) -> {ok,_,Node} = logger_test_lib:setup(Config,[{logger,[{proxy,#{}}]}]), ok = logger:add_handler(?HNAME,?MODULE,#{config=>self()}), diff --git a/lib/kernel/test/net_SUITE.erl b/lib/kernel/test/net_SUITE.erl index 4f27628bed..1e657a6cdd 100644 --- a/lib/kernel/test/net_SUITE.erl +++ b/lib/kernel/test/net_SUITE.erl @@ -219,6 +219,8 @@ api_b_getifaddrs() -> i("IfAddrs: " "~n ~p", [IfAddrs]), ok; + {error, enotsup = Reason} -> + skip(Reason); {error, Reason} -> ?FAIL(Reason) end. diff --git a/lib/megaco/src/app/megaco.erl b/lib/megaco/src/app/megaco.erl index 2c96ea25d7..a876ff02f8 100644 --- a/lib/megaco/src/app/megaco.erl +++ b/lib/megaco/src/app/megaco.erl @@ -66,7 +66,8 @@ decode_binary_term_id/2, encode_sdp/1, - decode_sdp/1, + decode_sdp/1, + get_sdp_record_from_PropertyGroup/2, versions1/0, versions2/0, print_version_info/0, print_version_info/1, @@ -491,6 +492,18 @@ decode_sdp(PP) -> %%----------------------------------------------------------------- +%% dget_sdp_record_from_PropertyGroup(Type, PG) -> +%% +%% [sdp()]} +%% +%% Get all sdp records of a certain type from a property group +%%----------------------------------------------------------------- + +get_sdp_record_from_PropertyGroup(Type, PG) -> + megaco_sdp:get_sdp_record_from_PropertyGroup(Type, PG). + + +%%----------------------------------------------------------------- %% {ok, Vs} = megaco:versions1(), megaco:format_versions(Vs). print_version_info() -> diff --git a/lib/megaco/test/megaco_codec_flex_lib.erl b/lib/megaco/test/megaco_codec_flex_lib.erl index e322c21dad..566ab0fe47 100644 --- a/lib/megaco/test/megaco_codec_flex_lib.erl +++ b/lib/megaco/test/megaco_codec_flex_lib.erl @@ -60,6 +60,8 @@ init(Config) when is_list(Config) -> %% "~n", [?MODULE, Res]), process_flag(trap_exit, Flag), case Res of + {error, {failed_loading_flex_scanner_driver, Reason}} -> + skip(?F("Failed loading flex driver: ~p", [Reason])); {error, Reason} -> skip(Reason); {ok, FlexConfig} -> diff --git a/lib/megaco/test/megaco_load_SUITE.erl b/lib/megaco/test/megaco_load_SUITE.erl index e5dc5a027a..876e16ecfb 100644 --- a/lib/megaco/test/megaco_load_SUITE.erl +++ b/lib/megaco/test/megaco_load_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2019. All Rights Reserved. +%% Copyright Ericsson AB 2003-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -48,6 +48,7 @@ ]). +-include_lib("common_test/include/ct.hrl"). -include_lib("megaco/include/megaco.hrl"). -include_lib("megaco/include/megaco_message_v1.hrl"). -include("megaco_test_lib.hrl"). @@ -243,20 +244,7 @@ single_user_light_load(suite) -> single_user_light_load(doc) -> []; single_user_light_load(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(tc, single_user_light_load), - put(sname, "TEST"), - i("starting"), - - load_controller(Config, - fun(Env) -> - populate(Env), - exit( single_user_load(5) ) - end), - - i("done", []), - ok. - + try_single_user_load(single_user_light_load, Config, 5). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -266,19 +254,7 @@ single_user_medium_load(suite) -> single_user_medium_load(doc) -> []; single_user_medium_load(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(tc, single_user_medium_load), - put(sname, "TEST"), - i("starting"), - - load_controller(Config, - fun(Env) -> - populate(Env), - exit( single_user_load(15) ) - end), - - i("done", []), - ok. + try_single_user_load(single_user_medium_load, Config, 15). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -288,19 +264,7 @@ single_user_heavy_load(suite) -> single_user_heavy_load(doc) -> []; single_user_heavy_load(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(tc, single_user_heavy_load), - put(sname, "TEST"), - i("starting"), - - load_controller(Config, - fun(Env) -> - populate(Env), - exit( single_user_load(25) ) - end), - - i("done", []), - ok. + try_single_user_load(single_user_heavy_load, Config, 25). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -310,19 +274,7 @@ single_user_extreme_load(suite) -> single_user_extreme_load(doc) -> []; single_user_extreme_load(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(tc, single_user_extreme_load), - put(sname, "TEST"), - i("starting"), - - load_controller(Config, - fun(Env) -> - populate(Env), - exit( single_user_load(100) ) - end), - - i("done", []), - ok. + try_single_user_load(single_user_extreme_load, Config, 100). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -332,19 +284,7 @@ multi_user_light_load(suite) -> multi_user_light_load(doc) -> []; multi_user_light_load(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(tc, multi_user_light_load), - put(sname, "TEST"), - i("starting"), - - load_controller(Config, - fun(Env) -> - populate(Env), - exit( multi_user_load(3,1) ) - end), - - i("done", []), - ok. + try_multi_user_load(multi_user_light_load, Config, 3, 1). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -354,19 +294,7 @@ multi_user_medium_load(suite) -> multi_user_medium_load(doc) -> []; multi_user_medium_load(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(tc, multi_user_medium_load), - put(sname, "TEST"), - i("starting"), - - load_controller(Config, - fun(Env) -> - populate(Env), - exit( multi_user_load(3,5) ) - end), - - i("done", []), - ok. + try_multi_user_load(multi_user_medium_load, Config, 3, 5). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -376,19 +304,7 @@ multi_user_heavy_load(suite) -> multi_user_heavy_load(doc) -> []; multi_user_heavy_load(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(tc, multi_user_heavy_load), - put(sname, "TEST"), - i("starting"), - - load_controller(Config, - fun(Env) -> - populate(Env), - exit( multi_user_load(3,10) ) - end), - - i("done", []), - ok. + try_multi_user_load(multi_user_heavy_load, Config, 3, 10). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -398,19 +314,7 @@ multi_user_extreme_load(suite) -> multi_user_extreme_load(doc) -> []; multi_user_extreme_load(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(tc, multi_user_extreme_load), - put(sname, "TEST"), - i("starting"), - - load_controller(Config, - fun(Env) -> - populate(Env), - exit( multi_user_load(3,15) ) - end), - - i("done", []), - ok. + try_multi_user_load(multi_user_extreme_load, Config, 3, 15). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -462,14 +366,46 @@ load_controller(Config, Fun) when is_list(Config) and is_function(Fun) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -single_user_load(NumLoaders) -> - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("Nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - +try_single_user_load(Name, Config, NumLoaders0) -> + Factor = ?config(megaco_factor, Config), + NumLoaders = + if + (Factor =:= 1) -> + NumLoaders0; + (Factor > NumLoaders0) -> + 1; + true -> + NumLoaders0 div Factor + end, + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("Nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun(State) -> single_user_load(State, Config, NumLoaders) end, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(Name, Pre, Case, Post). + + +single_user_load(State, Config, NumLoaders) -> + i("starting with ~w loader(s)", [NumLoaders]), + Res = load_controller(Config, + fun(Env) -> + populate(Env), + exit( single_user_load(State, NumLoaders) ) + end), + i("done"), + Res. + +single_user_load([MgcNode, MgNode], NumLoaders) -> %% Start the MGC and MGs i("[MGC] start"), MgcMid = {deviceName, "ctrl"}, @@ -532,16 +468,52 @@ single_user_load(NumLoaders) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -multi_user_load(NumUsers, NumLoaders) +try_multi_user_load(Name, Config, NumUsers, NumLoaders0) -> + Factor = ?config(megaco_factor, Config), + NumLoaders = + if + (Factor =:= 1) -> + NumLoaders0; + (Factor > NumLoaders0) -> + 1; + true -> + NumLoaders0 div Factor + end, + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNodes = make_node_names(mg, NumUsers), + d("Nodes: " + "~n MgcNode: ~p" + "~n MgNodes: ~p", [MgcNode, MgNodes]), + Nodes = [MgcNode | MgNodes], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun(State) -> + multi_user_load(State, Config, NumUsers, NumLoaders) + end, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(Name, Pre, Case, Post). + + +multi_user_load(State, Config, NumUsers, NumLoaders) -> + i("starting with ~w loader(s)", [NumLoaders]), + Res = load_controller( + Config, + fun(Env) -> + populate(Env), + exit( multi_user_load(State, NumUsers, NumLoaders) ) + end), + i("done"), + Res. + + +multi_user_load([MgcNode | MgNodes], NumUsers, NumLoaders) when (is_integer(NumUsers) andalso (NumUsers > 1) andalso is_integer(NumLoaders) andalso (NumLoaders >= 1)) -> - MgcNode = make_node_name(mgc), - MgNodes = make_node_names(mg, NumUsers), - d("Nodes: " - "~n MgcNode: ~p" - "~n MgNodes: ~p", [MgcNode, MgNodes]), - ok = megaco_test_lib:start_nodes([MgcNode| MgNodes], ?FILE, ?LINE), - %% Start the MGC and MGs i("[MGC] start"), MgcMid = {deviceName, "ctrl"}, @@ -761,6 +733,15 @@ maybe_display_system_info(_) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +try_tc(TCName, Pre, Case, Post) -> + try_tc(TCName, "TEST", ?TEST_VERBOSITY, Pre, Case, Post). + +try_tc(TCName, Name, Verbosity, Pre, Case, Post) -> + ?TRY_TC(TCName, Name, Verbosity, Pre, Case, Post). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + min(M) -> timer:minutes(M). p(F, A) -> diff --git a/lib/megaco/test/megaco_mess_SUITE.erl b/lib/megaco/test/megaco_mess_SUITE.erl index bb4e3eb1ee..f3dfc053c6 100644 --- a/lib/megaco/test/megaco_mess_SUITE.erl +++ b/lib/megaco/test/megaco_mess_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2019. All Rights Reserved. +%% Copyright Ericsson AB 1999-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -499,6 +499,9 @@ end_per_group(_GroupName, Config) -> %% init_per_testcase(Case, Config) -> + process_flag(trap_exit, true), + + ?ANNOUNCE_CASE_INIT(Case), p("init_per_testcase -> entry with" "~n Config: ~p" @@ -689,31 +692,35 @@ request_and_no_reply(suite) -> request_and_no_reply(doc) -> []; request_and_no_reply(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, request_and_no_reply), - i("starting"), - - MgcNode = make_node_name(mgc), - Mg1Node = make_node_name(mg1), - Mg2Node = make_node_name(mg2), - Mg3Node = make_node_name(mg3), - Mg4Node = make_node_name(mg4), - d("start nodes: " - "~n MgcNode: ~p" - "~n Mg1Node: ~p" - "~n Mg2Node: ~p" - "~n Mg3Node: ~p" - "~n Mg4Node: ~p", - [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]), - Nodes = [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node], - ok = megaco_test_lib:start_nodes(Nodes, ?FILE, ?LINE), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + Mg1Node = make_node_name(mg1), + Mg2Node = make_node_name(mg2), + Mg3Node = make_node_name(mg3), + Mg4Node = make_node_name(mg4), + d("start nodes: " + "~n MgcNode: ~p" + "~n Mg1Node: ~p" + "~n Mg2Node: ~p" + "~n Mg3Node: ~p" + "~n Mg4Node: ~p", + [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]), + Nodes = [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_request_and_no_reply/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(request_and_no_reply, Pre, Case, Post). + +do_request_and_no_reply([MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]) -> %% Start the MGC i("[MGC] start"), ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}], - {ok, Mgc} = - ?MGC_START(MgcNode, {deviceName, "ctrl"}, ET, [], ?MGC_VERBOSITY), + {ok, Mgc} = ?MGC_START(MgcNode, {deviceName, "ctrl"}, ET, [], ?MGC_VERBOSITY), ?SLEEP(?SECONDS(1)), i("[MG] start"), @@ -833,9 +840,31 @@ request_and_no_reply(Config) when is_list(Config) -> d("MG4 user info: ~p", [?MG_USER_INFO(Mg4, all)]), d("MG4 conn info: ~p", [?MG_CONN_INFO(Mg4, all)]), + %% Tell MG4 to stop + i("[MG4] stop"), + ?MG_STOP(Mg4), + + %% Tell MG3 to stop + i("[MG3] stop"), + ?MG_STOP(Mg3), + + %% Tell MG2 to stop + i("[MG2] stop"), + ?MG_STOP(Mg2), + + %% Tell MG1 to stop + i("[MG1] stop"), + ?MG_STOP(Mg1), + + %% Tell Mgc to stop + i("[MGC] stop"), + ?MGC_STOP(Mgc), + + i("done", []), ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% request_and_reply_pending_ack_no_pending(suite) -> @@ -845,20 +874,26 @@ request_and_reply_pending_ack_no_pending(doc) -> "value handle_pending_ack from handle_trans_request when NO " "pending message has been sent"]; request_and_reply_pending_ack_no_pending(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, rar_panp), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - Nodes = [MgcNode, MgNode], - ok = megaco_test_lib:start_nodes(Nodes, ?FILE, ?LINE), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_request_and_reply_pending_ack_no_pending/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)), + ok + end, + try_tc(rar_panp, Pre, Case, Post). + +do_request_and_reply_pending_ack_no_pending([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -871,9 +906,6 @@ request_and_reply_pending_ack_no_pending(Config) when is_list(Config) -> d("[MGC] start the simulation"), {ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq), - %% i("wait some time before starting the MG simulator"), - %% sleep(1000), - i("await MGC ready announcement"), receive announce_mgc -> @@ -1444,19 +1476,25 @@ request_and_reply_pending_ack_one_pending(doc) -> "value handle_pending_ack from handle_trans_request when ONE " "pending message has been sent"]; request_and_reply_pending_ack_one_pending(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, rar_paop), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_request_and_reply_pending_ack_one_pending/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(rar_paop, Pre, Case, Post). + +do_request_and_reply_pending_ack_one_pending([MgcNode, MgNode]) -> d("[MGC] start the simulator"), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -2136,20 +2174,25 @@ single_trans_req_and_reply(doc) -> "The MGC is a megaco instance (megaco event sequence) and the " "MG is emulated (tcp event sequence)"]; single_trans_req_and_reply(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, strar), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_single_trans_req_and_reply/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(strar, Pre, Case, Post). + +do_single_trans_req_and_reply([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -2646,26 +2689,31 @@ single_trans_req_and_reply_sendopts(doc) -> "The MGC is a megaco instance (megaco event sequence) and the " "MG is emulated (tcp event sequence)"]; single_trans_req_and_reply_sendopts(Config) when is_list(Config) -> - %% <CONDITIONAL-SKIP> - Skippable = [{unix, [darwin, linux]}], - Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% </CONDITIONAL-SKIP> - - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, straro), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - - + Pre = fun() -> + %% <CONDITIONAL-SKIP> + Skippable = [{unix, [darwin, linux]}], + Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% </CONDITIONAL-SKIP> + + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_single_trans_req_and_reply_sendopts/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(straro, Pre, Case, Post). + +do_single_trans_req_and_reply_sendopts([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -3208,19 +3256,25 @@ request_and_reply_and_ack(suite) -> request_and_reply_and_ack(doc) -> ["This test case tests that megaco correctly handles three-way-handshake"]; request_and_reply_and_ack(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, raraa), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_request_and_reply_and_ack/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(raraa, Pre, Case, Post). + +do_request_and_reply_and_ack([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -3829,19 +3883,26 @@ request_and_reply_and_no_ack(doc) -> ["This test case tests that megaco handles a failed three-way-handshake," " i.e. when the ack never arrives"]; request_and_reply_and_no_ack(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, rarana), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_request_and_reply_and_no_ack/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(rarana, Pre, Case, Post). + + +do_request_and_reply_and_no_ack([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -4436,19 +4497,25 @@ request_and_reply_and_late_ack(doc) -> ["This test case tests that megaco handles three-way-handshake " "when the ack is late (and requeire a retransmission)"]; request_and_reply_and_late_ack(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, rarala), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_request_and_reply_and_late_ack/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(rarala, Pre, Case, Post). + +do_request_and_reply_and_late_ack([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -5067,26 +5134,31 @@ trans_req_and_reply_and_req(doc) -> "The MGC is a megaco instance (megaco event sequence) and the " "MG is emulated (tcp event sequence)"]; trans_req_and_reply_and_req(Config) when is_list(Config) -> - %% <CONDITIONAL-SKIP> - Skippable = [{unix, [darwin, linux]}], - Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% </CONDITIONAL-SKIP> - - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, trarar), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - - + Pre = fun() -> + %% <CONDITIONAL-SKIP> + Skippable = [{unix, [darwin, linux]}], + Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% </CONDITIONAL-SKIP> + + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_trans_req_and_reply_and_req/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(request_and_no_reply, Pre, Case, Post). + +do_trans_req_and_reply_and_req([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -5730,19 +5802,25 @@ pending_ack_plain(doc) -> "i.e. return with {pending, _} and expect a call to the " "long request function"]; pending_ack_plain(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, pap), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_pending_ack_plain/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(pap, Pre, Case, Post). + +do_pending_ack_plain([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -6423,19 +6501,25 @@ request_and_pending_and_late_reply(doc) -> "i.e. return with {pending, _}. Then, expect the sender " "to keep re-sending the request until the reply is sent."]; request_and_pending_and_late_reply(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, rapalr), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_request_and_pending_and_late_reply/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(rapalr, Pre, Case, Post). + +do_request_and_pending_and_late_reply([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), @@ -7269,19 +7353,25 @@ otp_4359_analyze_encoded_message(RH, ExpErrorCode, M) otp_4836(suite) -> []; otp_4836(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, otp_4836), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_otp_4836/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(otp_4836, Pre, Case, Post). + +do_otp_4836([MgcNode, MgNode]) -> d("start the MGC simulator (generator)"), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), @@ -7522,19 +7612,25 @@ otp_4836_mgc_verify_notify_req_msg(M) -> otp_5805(suite) -> []; otp_5805(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, otp_5805), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_otp_5805/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(otp_5805, Pre, Case, Post). + +do_otp_5805([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -8005,7 +8101,8 @@ otp_5881(Config) when is_list(Config) -> "~n MgcNode: ~p" "~n MgNode: ~p", [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), d("start the MGC simulator (generator)"), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), @@ -8071,6 +8168,10 @@ otp_5881(Config) when is_list(Config) -> i("[MGC] stop generator"), megaco_test_tcp_generator:stop(Mgc), + %% Cleanup + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)), + i("done", []), ok. @@ -8247,19 +8348,25 @@ otp_5881_mgc_verify_notify_req_msg(M) -> otp_5887(suite) -> []; otp_5887(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, otp_5887), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_otp_5887/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(otp_5887, Pre, Case, Post). + +do_otp_5887([MgcNode, MgNode]) -> d("start the MGC simulator (generator)"), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), @@ -8590,19 +8697,25 @@ otp_6253(Config) when is_list(Config) -> otp_6275(suite) -> []; otp_6275(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, otp_6275), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_otp_6275/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(otp_6275, Pre, Case, Post). + +do_otp_6275([MgcNode, MgNode]) -> d("start the MGC simulator (generator)"), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), @@ -9083,21 +9196,27 @@ otp_6276(suite) -> otp_6276(doc) -> "OTP-6276: Cancel when receiving reply raise condition"; otp_6276(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, otp_6276), - i("starting"), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_otp_6276/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(otp_6276, Pre, Case, Post). + +do_otp_6276([MgcNode, MgNode]) -> d("create sequence controller"), CtrlPid = otp_6276_sequence_controller_start(), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), @@ -9762,19 +9881,25 @@ otp_6276(Config) when is_list(Config) -> otp_6442_resend_request1(suite) -> []; otp_6442_resend_request1(Config) when is_list(Config) -> - put(verbosity, debug), - put(sname, "TEST"), - put(tc, otp6442rreq1), - i("starting"), - - MgNode = make_node_name(mg), - d("start (MG) node: ~p", [MgNode]), - ok = megaco_test_lib:start_nodes([MgNode], ?FILE, ?LINE), - - d("[MG] start the simulator "), + Pre = fun() -> + MgNode = make_node_name(mg), + d("start (MG) node: ~p", [MgNode]), + Nodes = [MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_otp_6442_resend_request1/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(otp6442rreq1, Pre, Case, Post). + +do_otp_6442_resend_request1([MgNode]) -> + d("[MG] start the simulator"), {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode), - d("[MG] create the event sequence"), + d("[MG] create the event sequence (~p)", [Mg]), MgMid = {deviceName,"mg"}, MgEvSeq = otp_6442_resend_request1_mg_event_sequence(MgMid), @@ -9978,11 +10103,6 @@ otp_6442_resend_request1_mg_event_sequence(Mid) -> ?otp_6442_resend_request1_mg_verify_service_change_rep_fun(), NotifyReplyVerify = ?otp_6442_resend_request1_mg_verify_notify_rep_fun(), - %% ConnectVerify = - %% otp_6442_resend_request1_mg_verify_handle_connect_fun(), - %% ServiceChangeReplyVerify = - %% otp_6442_resend_request1_mg_verify_service_change_reply_fun(), - %% NotifyReplyVerify = otp_6442_resend_request1_mg_verify_notify_reply_fun(), EvSeq = [ {debug, false}, megaco_start, @@ -10132,15 +10252,21 @@ otp_6442_resend_request1_mg_notify_request_ar(Rid, Tid, Cid) -> otp_6442_resend_request2(suite) -> []; otp_6442_resend_request2(Config) when is_list(Config) -> - put(verbosity, debug), - put(sname, "TEST"), - put(tc, otp6442rreq2), - i("starting"), - - MgNode = make_node_name(mg), - d("start (MG) node: ~p", [MgNode]), - ok = megaco_test_lib:start_nodes([MgNode], ?FILE, ?LINE), - + Pre = fun() -> + MgNode = make_node_name(mg), + d("start (MG) node: ~p", [MgNode]), + Nodes = [MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_otp_6442_resend_request2/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(otp6442rreq2, Pre, Case, Post). + +do_otp_6442_resend_request2([MgNode]) -> d("[MG] start the simulator "), {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode), @@ -10434,15 +10560,21 @@ otp_6442_resend_request2_mg_notify_request_ar(Rid, Tid, Cid) -> otp_6442_resend_reply1(suite) -> []; otp_6442_resend_reply1(Config) when is_list(Config) -> - put(sname, "TEST"), - put(verbosity, debug), - put(tc, otp6442rrep1), - i("starting"), - - MgNode = make_node_name(mg), - d("start (MG) node: ~p", [MgNode]), - ok = megaco_test_lib:start_nodes([MgNode], ?FILE, ?LINE), - + Pre = fun() -> + MgNode = make_node_name(mg), + d("start (MG) node: ~p", [MgNode]), + Nodes = [MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_otp_6442_resend_reply1/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(request_and_no_reply, Pre, Case, Post). + +do_otp_6442_resend_reply1([MgNode]) -> d("[MG] start the simulator "), {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode), @@ -10823,15 +10955,21 @@ otp_6442_resend_reply1_err_desc(T) -> otp_6442_resend_reply2(suite) -> []; otp_6442_resend_reply2(Config) when is_list(Config) -> - put(sname, "TEST"), - put(verbosity, debug), - put(tc, otp6442rrep2), - i("starting"), - - MgNode = make_node_name(mg), - d("start (MG) node: ~p", [MgNode]), - ok = megaco_test_lib:start_nodes([MgNode], ?FILE, ?LINE), - + Pre = fun() -> + MgNode = make_node_name(mg), + d("start (MG) node: ~p", [MgNode]), + Nodes = [MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_otp_6442_resend_reply2/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(otp6442rrep2, Pre, Case, Post). + +do_otp_6442_resend_reply2([MgNode]) -> d("[MG] start the simulator "), {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode), @@ -11323,7 +11461,8 @@ otp_6865_request_and_reply_plain_extra2(Config) when is_list(Config) -> "~n MgcNode: ~p" "~n MgNode: ~p", [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), d("[MGC] start the simulator "), @@ -11371,6 +11510,10 @@ otp_6865_request_and_reply_plain_extra2(Config) when is_list(Config) -> i("stop tc controller"), ok = megaco_tc_controller:stop(), + %% Cleanup + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)), + i("done", []), ok. @@ -12187,19 +12330,25 @@ otp_7189(suite) -> otp_7189(doc) -> "..."; otp_7189(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, otp_7189), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_otp_7189/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(otp_7189, Pre, Case, Post). + +do_otp_7189([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -12705,15 +12854,21 @@ otp_7259(suite) -> otp_7259(doc) -> ["This is a variant of ticket OTP-6442"]; otp_7259(Config) when is_list(Config) -> - put(verbosity, debug), - put(sname, "TEST"), - put(tc, otp7259rr), - i("starting"), - - MgNode = make_node_name(mg), - d("start (MG) node: ~p", [MgNode]), - ok = megaco_test_lib:start_nodes([MgNode], ?FILE, ?LINE), - + Pre = fun() -> + MgNode = make_node_name(mg), + d("start (MG) node: ~p", [MgNode]), + Nodes = [MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_otp_7259/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(otp7259rr, Pre, Case, Post). + +do_otp_7259([MgNode]) -> d("[MG] start the simulator "), {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode), @@ -13138,15 +13293,26 @@ otp_7713(Config) when is_list(Config) -> otp_8183_request1(suite) -> []; otp_8183_request1(Config) when is_list(Config) -> + Pre = fun() -> put(verbosity, debug), put(sname, "TEST"), put(tc, otp8183r1), i("starting"), - MgNode = make_node_name(mg), - d("start (MG) node: ~p", [MgNode]), - ok = megaco_test_lib:start_nodes([MgNode], ?FILE, ?LINE), - + MgNode = make_node_name(mg), + d("start (MG) node: ~p", [MgNode]), + Nodes = [MgNode], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_otp_8183_request1/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(otp8183r1, Pre, Case, Post). + +do_otp_8183_request1([MgNode]) -> d("[MG] start the simulator "), {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode), @@ -13972,6 +14138,46 @@ sleep(X) -> receive after X -> ok end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +to(To, Start) -> + To - (mtime() - Start). + +%% Time in milli seconds +mtime() -> + {A,B,C} = erlang:timestamp(), + A*1000000000+B*1000+(C div 1000). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +try_tc(TCName, Pre, Case, Post) -> + try_tc(TCName, "TEST", ?TEST_VERBOSITY, Pre, Case, Post). + +try_tc(TCName, Name, Verbosity, Pre, Case, Post) -> + ?TRY_TC(TCName, Name, Verbosity, Pre, Case, Post). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% p(F) -> +%% p(F, []). + +p(F, A) -> + p(get(sname), F, A). + +p(S, F, A) when is_list(S) -> + io:format("*** [~s] ~p ~s ***" + "~n " ++ F ++ "~n", + [?FTS(), self(), S | A]); +p(_S, F, A) -> + io:format("*** [~s] ~p *** " + "~n " ++ F ++ "~n", + [?FTS(), self() | A]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + i(F) -> i(F, []). @@ -13995,7 +14201,7 @@ print(Severity, Verbosity, Ts, Tc, P, F, A) -> print(true, TS, TC, P, F, A) -> S = ?F("*** [~s] ~s ~p ~s:~w ***" "~n " ++ F ++ "~n", - [megaco:format_timestamp(TS), P, self(), get(sname), TC | A]), + [?FTS(TS), P, self(), get(sname), TC | A]), io:format("~s", [S]), io:format(user, "~s", [S]); print(_, _, _, _, _, _) -> @@ -14008,33 +14214,3 @@ printable(_,_) -> false. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -to(To, Start) -> - To - (mtime() - Start). - -%% Time in milli seconds -mtime() -> - {A,B,C} = erlang:timestamp(), - A*1000000000+B*1000+(C div 1000). - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%% p(F) -> -%% p(F, []). - -p(F, A) -> - p(get(sname), F, A). - -p(S, F, A) when is_list(S) -> - io:format("*** [~s] ~p ~s ***" - "~n " ++ F ++ "~n", - [?FTS(), self(), S | A]); -p(_S, F, A) -> - io:format("*** [~s] ~p *** " - "~n " ++ F ++ "~n", - [?FTS(), self() | A]). - - diff --git a/lib/megaco/test/megaco_mib_SUITE.erl b/lib/megaco/test/megaco_mib_SUITE.erl index 8763145bbc..e040705b05 100644 --- a/lib/megaco/test/megaco_mib_SUITE.erl +++ b/lib/megaco/test/megaco_mib_SUITE.erl @@ -53,8 +53,8 @@ -include("megaco_test_lib.hrl"). -define(TEST_VERBOSITY, info). % silence | info | debug --define(MGC_VERBOSITY, info). --define(MG_VERBOSITY, info). +-define(MGC_VERBOSITY, debug). +-define(MG_VERBOSITY, debug). -define(LOAD_COUNTER_START, 100). -define(A4444, ["11111111", "00000000", "00000000"]). @@ -292,28 +292,40 @@ connect(suite) -> connect(doc) -> []; connect(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - i("connect -> starting"), - progress("start nodes"), - MgcNode = make_node_name(mgc), - Mg1Node = make_node_name(mg1), - Mg2Node = make_node_name(mg2), - d("connect -> Nodes: " - "~n MgcNode: ~p" - "~n Mg1Node: ~p" - "~n Mg2Node: ~p", [MgcNode, Mg1Node, Mg2Node]), - ok = ?START_NODES([MgcNode, Mg1Node, Mg2Node]), - + Pre = fun() -> + progress("start nodes"), + MgcNode = make_node_name(mgc), + Mg1Node = make_node_name(mg1), + Mg2Node = make_node_name(mg2), + d("connect -> Nodes: " + "~n MgcNode: ~p" + "~n Mg1Node: ~p" + "~n Mg2Node: ~p", [MgcNode, Mg1Node, Mg2Node]), + Nodes = [MgcNode, Mg1Node, Mg2Node], + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_connect/1, + Post = fun(Nodes) -> + progress("stop nodes"), + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(connect, Pre, Case, Post). + +do_connect([MgcNode, Mg1Node, Mg2Node]) -> %% Start the MGC and MGs - ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}], - progress("start MGC"), + + ET = [{text, tcp}, {text, udp}, {binary, tcp}, {binary, udp}], + progress("start MGC (on ~p)", [MgcNode]), {ok, Mgc} = start_mgc(MgcNode, {deviceName, "ctrl"}, ET, ?MGC_VERBOSITY), - progress("start MG1"), + + progress("start MG1 (on ~p) using tcp", [Mg1Node]), {ok, Mg1} = start_mg(Mg1Node, {deviceName, "mg1"}, text, tcp, ?MG_VERBOSITY), - progress("start MG2"), + + progress("start MG2 (on ~p) using udp", [Mg2Node]), {ok, Mg2} = start_mg(Mg2Node, {deviceName, "mg2"}, binary, udp, ?MG_VERBOSITY), @@ -385,7 +397,6 @@ connect(Config) when is_list(Config) -> stop(Mgc), i("connect -> done", []), - progress("done"), ok. @@ -397,27 +408,36 @@ traffic(suite) -> traffic(doc) -> []; traffic(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - i("traffic -> starting"), - progress("start nodes"), - MgcNode = make_node_name(mgc), - Mg1Node = make_node_name(mg1), - Mg2Node = make_node_name(mg2), - Mg3Node = make_node_name(mg3), - Mg4Node = make_node_name(mg4), - d("traffic -> Nodes: " - "~n MgcNode: ~p" - "~n Mg1Node: ~p" - "~n Mg2Node: ~p" - "~n Mg3Node: ~p" - "~n Mg4Node: ~p", - [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]), - ok = ?START_NODES([MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]), - + Pre = fun() -> + progress("start nodes"), + MgcNode = make_node_name(mgc), + Mg1Node = make_node_name(mg1), + Mg2Node = make_node_name(mg2), + Mg3Node = make_node_name(mg3), + Mg4Node = make_node_name(mg4), + Nodes = [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node], + d("traffic -> Nodes: " + "~n MgcNode: ~p" + "~n Mg1Node: ~p" + "~n Mg2Node: ~p" + "~n Mg3Node: ~p" + "~n Mg4Node: ~p", + [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]), + ok = ?START_NODES(Nodes, true), + Nodes + end, + Case = fun do_traffic/1, + Post = fun(Nodes) -> + progress("stop nodes"), + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(traffic, Pre, Case, Post). + +do_traffic([MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]) -> %% Start the MGC and MGs - i("traffic -> start the MGC"), - progress("start MGC"), + i("start the MGC"), + progress("start MGC (on ~p)", [MgcNode]), ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}], {ok, Mgc} = start_mgc(MgcNode, {deviceName, "ctrl"}, ET, ?MGC_VERBOSITY), @@ -550,7 +570,6 @@ traffic(Config) when is_list(Config) -> stop(Mgc), i("traffic -> done", []), - progress("done"), ok. @@ -671,10 +690,12 @@ traffic_connect_mg([{Node, Name, Coding, Trans}|Mg], Acc) -> traffic_connect_mg(Mg, [{Name, Pid}|Acc]). traffic_connect_mg(Node, Name, Coding, Trans) -> + progress("start (and connect) ~s (on ~p) using ~p", [Name, Node, Trans]), Mid = {deviceName, Name}, {ok, Pid} = start_mg(Node, Mid, Coding, Trans, ?MG_VERBOSITY), %% Ask the MGs to do a service change + progress("perform ~s service change", [Name]), {ok, Res} = service_change(Pid), d("traffic_connect_mg -> (~s) service change result: ~p", [Name, Res]), Pid. @@ -831,8 +852,12 @@ mgc_init(Config) -> RH = megaco:user_info(Mid,receive_handle), d("mgc_init -> parse receive info"), ListenTo = mgc_parse_receive_info(RI, RH), - i("mgc_init -> start transport(s)"), + i("mgc_init -> start transport(s) with:" + "~n ListenTo: ~p", [ListenTo]), {Tcp, Udp} = mgc_start_transports(ListenTo), + i("mgc_init -> transport(s) started:" + "~n Tcp: ~p" + "~n Udp: ~p", [Tcp, Udp]), {Mid, Tcp, Udp}. @@ -1247,10 +1272,15 @@ mg_loop(#mg{state = State} = S) -> %% Do a service change {service_change, Parent} when S#mg.parent == Parent, State == initiated -> - i("mg_loop(~p) -> received request to perform service change", - [State]), + i("mg_loop(~p) -> received request to perform service change when:" + "~n Conn Handle: ~p" + "~n Conn Data: ~p", + [State, + S#mg.conn_handle, + megaco:conn_info(S#mg.conn_handle, conn_data)]), Res = mg_service_change(S#mg.conn_handle), - d("mg_loop(~p) -> result: ~p", [State, Res]), + d("mg_loop(~p) -> service change request send result: ~p", + [State, Res]), mg_loop(S#mg{state = connecting}); @@ -1358,9 +1388,12 @@ mg_start_tcp(MgcPort, RH) -> {port, MgcPort}, {receive_handle, RH}, {tcp_options, [{nodelay, true}]}], + i("tcp transport started: attempt (tcp) connect to MGC at:" + "~n ~p", [LocalHost]), case megaco_tcp:connect(Sup, Opts) of {ok, SendHandle, ControlPid} -> PrelMgcMid = preliminary_mid, + i("tcp transport (tcp) connected: attempt (megaco) connect:"), {ok, ConnHandle} = megaco:connect(RH, PrelMgcMid, SendHandle, ControlPid), @@ -1382,12 +1415,18 @@ mg_start_udp(MgcPort, RH) -> %% local host. Try instead to "figure out" tha actual address... LocalAddr = which_local_addr(), Opts = [{port, 0}, {receive_handle, RH}], + i("udp transport started: attempt (udp) open"), case megaco_udp:open(Sup, Opts) of {ok, Handle, ControlPid} -> MgcMid = preliminary_mid, + i("udp transport open: " + "now create send handle with MGC address: " + "~n ~p", [LocalAddr]), SendHandle = megaco_udp:create_send_handle(Handle, LocalAddr, MgcPort), + i("udp transport: attempt (megaco) connect to:" + "~n ~p", [SendHandle]), {ok, ConnHandle} = megaco:connect(RH, MgcMid, SendHandle, ControlPid), @@ -1712,6 +1751,7 @@ get_conf(Key, Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Tries to find a valid local address... which_local_addr() -> case inet:getifaddrs() of {ok, IFs} -> @@ -1722,10 +1762,10 @@ which_local_addr() -> ?SKIP({failed_get_local_addr, Reason}) end. +%% We explicitly skip some interfaces that we know is not "valid" +%% (docker stuff) which_local_addr([]) -> ?SKIP(failed_get_local_addr); -which_local_addr([{"lo" = _IfName, _IfOpts}|IFs]) -> - which_local_addr(IFs); which_local_addr([{"br-" ++ _ = _IfName, _IfOpts}|IFs]) -> which_local_addr(IFs); which_local_addr([{"docker" ++ _ = _IfName, _IfOpts}|IFs]) -> @@ -1738,14 +1778,38 @@ which_local_addr([{_IfName, IfOpts}|IFs]) -> which_local_addr(IFs) end. +which_local_addr2(IfOpts) -> + case if_is_running(IfOpts) of + true -> + which_local_addr3(IfOpts); + false -> + error + end. -which_local_addr2([]) -> +if_is_running(If) -> + lists:keymember(flags, 1, If) andalso + begin + {value, {flags, Flags}} = lists:keysearch(flags, 1, If), + (not lists:member(loopback, Flags)) andalso lists:member(running, Flags) + end. + +which_local_addr3([]) -> error; -which_local_addr2([{addr, Addr}|_]) +which_local_addr3([{addr, Addr}|_]) when (size(Addr) =:= 4) andalso (element(1, Addr) =/= 127) -> {ok, Addr}; -which_local_addr2([_|T]) -> - which_local_addr2(T). +which_local_addr3([_|T]) -> + which_local_addr3(T). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +try_tc(TCName, Pre, Case, Post) -> + try_tc(TCName, "TEST", ?TEST_VERBOSITY, Pre, Case, Post). + +try_tc(TCName, Name, Verbosity, Pre, Case, Post) -> + ?TRY_TC(TCName, Name, Verbosity, Pre, Case, Post). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/megaco/test/megaco_mreq_SUITE.erl b/lib/megaco/test/megaco_mreq_SUITE.erl index 9596389d2b..b53e99a995 100644 --- a/lib/megaco/test/megaco_mreq_SUITE.erl +++ b/lib/megaco/test/megaco_mreq_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2019. All Rights Reserved. +%% Copyright Ericsson AB 2003-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -33,6 +33,7 @@ req_and_rep/1, req_and_pending/1, req_and_cancel/1 + ]). -include_lib("megaco/include/megaco.hrl"). @@ -182,177 +183,198 @@ req_and_rep(suite) -> req_and_rep(doc) -> []; req_and_rep(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - i("req_and_rep -> starting"), - MgcNode = make_node_name(mgc), - Mg1Node = make_node_name(mg1), - Mg2Node = make_node_name(mg2), - Mg3Node = make_node_name(mg3), - Mg4Node = make_node_name(mg4), - d("req_and_rep -> Nodes: " - "~n MgcNode: ~p" - "~n Mg1Node: ~p" - "~n Mg2Node: ~p" - "~n Mg3Node: ~p" - "~n Mg4Node: ~p", - [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]), - ok = megaco_test_lib:start_nodes([MgcNode, - Mg1Node, Mg2Node, Mg3Node, Mg4Node], - ?FILE, ?LINE), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + Mg1Node = make_node_name(mg1), + Mg2Node = make_node_name(mg2), + Mg3Node = make_node_name(mg3), + Mg4Node = make_node_name(mg4), + d("try start nodes: " + "~n MgcNode: ~p" + "~n Mg1Node: ~p" + "~n Mg2Node: ~p" + "~n Mg3Node: ~p" + "~n Mg4Node: ~p", + [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]), + Nodes = [MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_req_and_rep/1, + Post = fun(Nodes) -> + d("stop nodes (in the reverse order):" + "~n ~p", [Nodes]), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(req_and_rep, Pre, Case, Post). + +do_req_and_rep([MgcNode, Mg1Node, Mg2Node, Mg3Node, Mg4Node]) -> %% Start the MGC and MGs - i("req_and_rep -> start the MGC"), + i("start the MGC"), ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}], {ok, Mgc} = ?MGC_START(MgcNode, {deviceName, "ctrl"}, ET, ?MGC_VERBOSITY), - i("req_and_rep -> start and connect the MGs"), + i("start and connect the MGs"), MgConf0 = [{Mg1Node, "mg1", text, tcp, ?MG_VERBOSITY}, {Mg2Node, "mg2", text, udp, ?MG_VERBOSITY}, {Mg3Node, "mg3", binary, tcp, ?MG_VERBOSITY}, {Mg4Node, "mg4", binary, udp, ?MG_VERBOSITY}], - MgConf = req_and_rep_connect_mg(MgConf0, []), + MgConf = rar_connect_mg(MgConf0, []), %% Collect the (initial) MGs statistics - Stats1 = req_and_rep_get_mg_stats(MgConf, []), - d("req_and_rep -> stats for the MGs: ~n~p", [Stats1]), + Stats1 = rar_get_mg_stats(MgConf, []), + d("stats for the MGs: " + "~n ~p", [Stats1]), %% Collect and check the MGC statistics - i("req_and_rep -> collect and check the MGC stats"), + i("collect and check the MGC stats"), {ok, MgcStats1} = ?MGC_GET_STATS(Mgc, 1), - d("req_and_rep -> stats (1) for Mgc: ~n~p~n", [MgcStats1]), + d("stats (1) for Mgc: " + "~n ~p" + "~n", [MgcStats1]), sleep(1000), %% And apply some load - i("req_and_rep -> apply traffic load"), - ok = req_and_rep_apply_load(MgConf), + i("apply traffic load"), + ok = rar_apply_load(MgConf), %% Await completion of load part and the collect traffic - i("req_and_rep -> await load completion"), - ok = req_and_rep_await_load_complete(MgConf), + i("await load completion"), + ok = rar_await_load_complete(MgConf), sleep(1000), - i("req_and_rep -> collect the MGs statistics"), - Stats2 = req_and_rep_get_mg_stats(MgConf, []), - d("req_and_rep -> stats for MGs: ~n~p", [Stats2]), + i("collect the MGs statistics"), + Stats2 = rar_get_mg_stats(MgConf, []), + d("stats for MGs: " + "~n ~p", [Stats2]), - i("req_and_rep -> collect the MGC statistics"), + i("collect the MGC statistics"), {ok, MgcStats2} = ?MGC_GET_STATS(Mgc, 1), - d("req_and_rep -> stats (1) for Mgc: ~n~p~n", [MgcStats2]), + d("stats (1) for Mgc: " + "~n ~p" + "~n", [MgcStats2]), sleep(1000), %% Reset counters - i("req_and_rep -> reset the MGs statistics"), - req_and_rep_reset_mg_stats(MgConf), - Stats3 = req_and_rep_get_mg_stats(MgConf, []), - d("req_and_rep -> stats for the MGs: ~n~p", [Stats3]), - - i("req_and_rep -> reset the MGC statistics"), - req_and_rep_reset_mgc_stats(Mgc), + i("reset the MGs statistics"), + rar_reset_mg_stats(MgConf), + Stats3 = rar_get_mg_stats(MgConf, []), + d("stats for the MGs: " + "~n ~p", [Stats3]), + + i("reset the MGC statistics"), + rar_reset_mgc_stats(Mgc), {ok, MgcStats3} = ?MGC_GET_STATS(Mgc, 1), - d("req_and_rep -> stats (1) for Mgc: ~n~p~n", [MgcStats3]), + d("stats (1) for Mgc: " + "~n ~p" + "~n", [MgcStats3]), sleep(1000), %% Tell MGs to stop - i("req_and_rep -> stop the MGs"), - req_and_rep_stop_mg(MgConf), + i("stop the MGs"), + rar_stop_mg(MgConf), sleep(1000), %% Collect the statistics - i("req_and_rep -> collect the MGC statistics"), + i("collect the MGC statistics"), {ok, MgcStats4} = ?MGC_GET_STATS(Mgc, 1), - d("req_and_rep -> stats (1) for Mgc: ~n~p~n", [MgcStats4]), + d("stats (1) for Mgc: " + "~n ~p", [MgcStats4]), {ok, MgcStats5} = ?MGC_GET_STATS(Mgc, 2), - d("req_and_rep -> stats (2) for Mgc: ~n~p~n", [MgcStats5]), + d("stats (2) for Mgc: " + "~n ~p" + "~n", [MgcStats5]), %% Tell Mgc to stop - i("req_and_rep -> stop the MGC"), + i("stop the MGC"), ?MGC_STOP(Mgc), - i("req_and_rep -> done", []), + i("done", []), ok. -req_and_rep_connect_mg([], Acc) -> +rar_connect_mg([], Acc) -> lists:reverse(Acc); -req_and_rep_connect_mg([{Node, Name, Coding, Trans, Verb}|Mg], Acc) -> - Pid = req_and_rep_connect_mg(Node, Name, Coding, Trans, Verb), - req_and_rep_connect_mg(Mg, [{Name, Pid}|Acc]). +rar_connect_mg([{Node, Name, Coding, Trans, Verb}|Mg], Acc) -> + Pid = rar_connect_mg(Node, Name, Coding, Trans, Verb), + rar_connect_mg(Mg, [{Name, Pid}|Acc]). -req_and_rep_connect_mg(Node, Name, Coding, Trans, Verb) -> +rar_connect_mg(Node, Name, Coding, Trans, Verb) -> Mid = {deviceName, Name}, {ok, Pid} = ?MG_START(Node, Mid, Coding, Trans, Verb), %% Ask the MGs to do a service change Res = ?MG_SERV_CHANGE(Pid), - d("req_and_rep_connect_mg -> (~s) service change result: ~p", [Name,Res]), + d("rar_connect_mg -> (~s) service change result: ~p", [Name,Res]), Pid. -req_and_rep_stop_mg(MGs) -> +rar_stop_mg(MGs) -> [?MG_STOP(Pid) || {_Name, Pid} <- MGs]. -req_and_rep_get_mg_stats([], Acc) -> +rar_get_mg_stats([], Acc) -> lists:reverse(Acc); -req_and_rep_get_mg_stats([{Name, Pid}|Mgs], Acc) -> +rar_get_mg_stats([{Name, Pid}|Mgs], Acc) -> {ok, Stats} = ?MG_GET_STATS(Pid), - d("req_and_rep_get_mg_stats -> stats for ~s: ~n~p~n", [Name, Stats]), - req_and_rep_get_mg_stats(Mgs, [{Name, Stats}|Acc]). + d("rar_get_mg_stats -> stats for ~s: " + "~n ~p" + "~n", [Name, Stats]), + rar_get_mg_stats(Mgs, [{Name, Stats}|Acc]). -req_and_rep_apply_load([]) -> +rar_apply_load([]) -> ok; -req_and_rep_apply_load([{_, MG}|MGs]) -> +rar_apply_load([{_, MG}|MGs]) -> ?MG_APPLY_LOAD(MG,?LOAD_COUNTER_START), - req_and_rep_apply_load(MGs). + rar_apply_load(MGs). -req_and_rep_reset_mg_stats([]) -> +rar_reset_mg_stats([]) -> ok; -req_and_rep_reset_mg_stats([{Name, Pid}|MGs]) -> - d("req_and_rep_reset_mg_stats -> resetting ~s", [Name]), +rar_reset_mg_stats([{Name, Pid}|MGs]) -> + d("rar_reset_mg_stats -> resetting ~s", [Name]), ?MG_RESET_STATS(Pid), - req_and_rep_reset_mg_stats(MGs). + rar_reset_mg_stats(MGs). -req_and_rep_reset_mgc_stats(Mgc) -> - d("req_and_rep_reset_mgc_stats -> resetting ~p", [Mgc]), +rar_reset_mgc_stats(Mgc) -> + d("rar_reset_mgc_stats -> resetting ~p", [Mgc]), ?MGC_RESET_STATS(Mgc). -req_and_rep_await_load_complete([]) -> +rar_await_load_complete([]) -> ok; -req_and_rep_await_load_complete(MGs0) -> +rar_await_load_complete(MGs0) -> receive {load_complete, Pid} -> d("received load_complete from ~p", [Pid]), MGs1 = lists:keydelete(Pid, 2, MGs0), - req_and_rep_await_load_complete(lists:delete(Pid, MGs1)); + rar_await_load_complete(lists:delete(Pid, MGs1)); {'EXIT', Pid, Reason} -> - i("exit signal from ~p: ~p", [Pid, Reason]), + e("exit signal from ~p: ~p", [Pid, Reason]), case lists:keymember(Pid, 2, MGs0) of true -> exit({mg_exit, Pid, Reason}); false -> MGs1 = lists:keydelete(Pid, 2, MGs0), - req_and_rep_await_load_complete(lists:delete(Pid, MGs1)) + rar_await_load_complete(lists:delete(Pid, MGs1)) end end. @@ -364,54 +386,60 @@ req_and_pending(suite) -> req_and_pending(doc) -> []; req_and_pending(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - i("req_and_pending -> starting"), - - MgcNode = make_node_name(mgc), - Mg1Node = make_node_name(mg1), - - d("req_and_pending -> Nodes: " - "~n MgcNode: ~p" - "~n Mg1Node: ~p", - [MgcNode, Mg1Node]), - ok = megaco_test_lib:start_nodes([MgcNode, Mg1Node], - ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("try starting nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_req_and_pending/1, + Post = fun(Nodes) -> + d("stop nodes (in the reverse order):" + "~n ~p", [Nodes]), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(req_and_pending, Pre, Case, Post). + +do_req_and_pending([MgcNode, MgNode]) -> %% Start the MGC and MGs - i("req_and_pending -> start the MGC"), + i("try start the MGC"), ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}], {ok, Mgc} = ?MGC_START(MgcNode, {deviceName, "ctrl"}, ET, ?MGC_VERBOSITY), - i("req_and_pending -> start the MG"), - {ok, Mg1} = - ?MG_START(Mg1Node, {deviceName, "mg1"}, text, tcp, ?MG_VERBOSITY), + i("try start the MG"), + {ok, Mg} = + ?MG_START(MgNode, {deviceName, "mg"}, text, tcp, ?MG_VERBOSITY), - i("req_and_pending -> connect the MG"), - Res1 = ?MG_SERV_CHANGE(Mg1), - d("req_and_pending -> service change result: ~p", [Res1]), + i("connect MG (to MFC)"), + Res1 = ?MG_SERV_CHANGE(Mg), + d("service change result: ~p", [Res1]), sleep(1000), - i("req_and_pending -> change request action to pending"), - {ok, _} = ?MGC_REQ_PEND(Mgc,3500), + i("[MGC] change request action to pending"), + {ok, _} = ?MGC_REQ_PEND(Mgc, 3500), - i("req_and_pending -> send notify request"), - {ok, Res2} = ?MG_NOTIF_RAR(Mg1), - d("req_and_pending -> notify reply: ~p",[Res2]), + i("[MG] send notify request"), + {ok, Res2} = ?MG_NOTIF_RAR(Mg), + d("notify reply: ~p", [Res2]), sleep(1000), - %% Tell MGs to stop - i("req_and_rep -> stop the MGs"), - ?MG_STOP(Mg1), + %% Tell MG to stop + i("stop the MG"), + ?MG_STOP(Mg), %% Tell Mgc to stop - i("req_and_pending -> stop the MGC"), + i("stop the MGC"), ?MGC_STOP(Mgc), - i("req_and_pending -> done", []), + i("done", []), ok. @@ -423,80 +451,91 @@ req_and_cancel(suite) -> req_and_cancel(doc) -> []; req_and_cancel(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - i("req_and_cancel -> starting"), - - MgcNode = make_node_name(mgc), - Mg1Node = make_node_name(mg1), - - d("req_and_cancel -> Nodes: " - "~n MgcNode: ~p" - "~n Mg1Node: ~p", - [MgcNode, Mg1Node]), - ok = megaco_test_lib:start_nodes([MgcNode, Mg1Node], - ?FILE, ?LINE), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("try start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_req_and_cancel/1, + Post = fun(Nodes) -> + d("stop nodes (in the reverse order):" + "~n ~p", [Nodes]), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(req_and_cancel, Pre, Case, Post). + +do_req_and_cancel([MgcNode, MgNode]) -> %% Start the MGC and MGs - i("req_and_cancel -> start the MGC"), + i("start the MGC"), ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}], {ok, Mgc} = ?MGC_START(MgcNode, {deviceName, "ctrl"}, ET, ?MGC_VERBOSITY), - i("req_and_cancel -> start the MG"), - {ok, Mg1} = - ?MG_START(Mg1Node, {deviceName, "mg1"}, text, tcp, ?MG_VERBOSITY), + i("start the MG"), + {ok, Mg} = + ?MG_START(MgNode, {deviceName, "mg"}, text, tcp, ?MG_VERBOSITY), - i("req_and_cancel -> connect the MG"), - Res1 = ?MG_SERV_CHANGE(Mg1), - d("req_and_cancel -> service change result: ~p", [Res1]), + i("connect the MG"), + Res1 = ?MG_SERV_CHANGE(Mg), + d("service change result: ~p", [Res1]), sleep(1000), - i("req_and_cancel -> change request action to pending"), + i("change request action to pending"), {ok, _} = ?MGC_REQ_DISC(Mgc,5000), - i("req_and_cancel -> send notify request"), - ?MG_NOTIF_REQ(Mg1), + i("send notify request"), + ?MG_NOTIF_REQ(Mg), - d("req_and_cancel -> wait some to get it going",[]), + d("wait some to get it going",[]), sleep(1000), - i("req_and_cancel -> now cancel the notify request"), - ok = ?MG_CANCEL(Mg1,req_and_cancel), + i("now cancel the notify request"), + ok = ?MG_CANCEL(Mg, req_and_cancel), - i("req_and_cancel -> now await the notify request result"), - Res2 = ?MG_NOTIF_AR(Mg1), - req_and_cancel_analyze_result(Res2), + i("now await the notify request result"), + Res2 = ?MG_NOTIF_AR(Mg), + rac_analyze_result(Res2), %% Tell MGs to stop - i("req_and_rep -> stop the MGs"), - ?MG_STOP(Mg1), + i("stop the MG"), + ?MG_STOP(Mg), %% Tell Mgc to stop - i("req_and_cancel -> stop the MGC"), + i("stop the MGC"), ?MGC_STOP(Mgc), - i("req_and_cancel -> done", []), - ok.% ?SKIP(not_implemented_yet). + i("done", []), + ok. -req_and_cancel_analyze_result({ok,{_PV,Res}}) -> - i("req_and_cancel -> notify request result: ~n ~p", [Res]), - req_and_cancel_analyze_result2(Res); -req_and_cancel_analyze_result(Unexpected) -> - exit({unexpected_result,Unexpected}). +rac_analyze_result({ok, {_PV,Res}}) -> + i("rac_analyze_result -> notify request result:" + "~n ~p", [Res]), + rac_analyze_result2(Res); +rac_analyze_result(Unexpected) -> + e("rac_analyze_result -> unexpected result: " + "~n ~p", [Unexpected]), + exit({unexpected_result, Unexpected}). -req_and_cancel_analyze_result2({error,{user_cancel,req_and_cancel}}) -> +rac_analyze_result2({error,{user_cancel, req_and_cancel}}) -> ok; -req_and_cancel_analyze_result2([]) -> +rac_analyze_result2([]) -> ok; -req_and_cancel_analyze_result2([{error,{user_cancel,req_and_cancel}}|Res]) -> - req_and_cancel_analyze_result2(Res); -req_and_cancel_analyze_result2([Unknown|_Res]) -> - exit({unknown_result,Unknown}). +rac_analyze_result2([{error,{user_cancel, req_and_cancel}}|Res]) -> + rac_analyze_result2(Res); +rac_analyze_result2([Unknown|_Res]) -> + e("rac_analyze_result2 -> unexpected result: " + "~n ~p", [Unknown]), + exit({unknown_result, Unknown}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -518,36 +557,53 @@ sleep(X) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +try_tc(TCName, Pre, Case, Post) -> + try_tc(TCName, "TEST", ?TEST_VERBOSITY, Pre, Case, Post). + +try_tc(TCName, Name, Verbosity, Pre, Case, Post) -> + ?TRY_TC(TCName, Name, Verbosity, Pre, Case, Post). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + p(F, A) -> io:format("*** [~s] ~p ***" "~n " ++ F ++ "~n", [?FTS(), self() | A]). +%% e(F) -> +%% i(F, []). + +e(F, A) -> + print(info, get(verbosity), "ERROR", get(tc), F, A). + + i(F) -> i(F, []). i(F, A) -> - print(info, get(verbosity), "", F, A). + print(info, get(verbosity), "INFO", get(tc), F, A). %% d(F) -> %% d(F, []). d(F, A) -> - print(debug, get(verbosity), "DBG: ", F, A). + print(debug, get(verbosity), "DBG", get(tc), F, A). printable(_, debug) -> true; printable(info, info) -> true; printable(_,_) -> false. -print(Severity, Verbosity, P, F, A) -> - print(printable(Severity,Verbosity), P, F, A). +print(Severity, Verbosity, P, TC, F, A) -> + print(printable(Severity,Verbosity), P, TC, F, A). -print(true, P, F, A) -> - io:format("*** [~s] ~s ~p ~s ***" +print(true, P, TC, F, A) -> + io:format("*** [~s] [~s] ~p ~s:~w ***" "~n " ++ F ++ "~n", - [?FTS(), P, self(), get(sname) | A]); -print(_, _, _, _) -> + [?FTS(), P, self(), get(sname), TC | A]); +print(_, _, _, _, _) -> ok. diff --git a/lib/megaco/test/megaco_pending_limit_SUITE.erl b/lib/megaco/test/megaco_pending_limit_SUITE.erl index ca802023c2..d5db6e6bc0 100644 --- a/lib/megaco/test/megaco_pending_limit_SUITE.erl +++ b/lib/megaco/test/megaco_pending_limit_SUITE.erl @@ -244,7 +244,9 @@ end_per_group(_Group, Config) -> init_per_testcase(Case, Config) -> process_flag(trap_exit, true), - p("init_per_suite -> entry with" + ?ANNOUNCE_CASE_INIT(Case), + + p("init_per_testcase -> entry with" "~n Config: ~p" "~n Nodes: ~p", [Config, erlang:nodes()]), @@ -284,10 +286,10 @@ sent_timer_late_reply(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MgcNode: ~p" + "~n MgNode: ~p", [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + ok = ?START_NODES([MgcNode, MgNode], true), %% Start the MGC and MGs i("[MGC] start"), @@ -340,6 +342,10 @@ sent_timer_late_reply(Config) when is_list(Config) -> i("[MGC] stop"), ?MGC_STOP(Mgc), + %% Cleanup + d("stop nodes"), + ?STOP_NODES([MgcNode, MgNode]), + i("done", []), ok. @@ -359,10 +365,10 @@ sent_timer_exceeded(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MgcNode: ~p" + "~n MgNode: ~p", [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + ok = ?START_NODES([MgcNode, MgNode], true), %% Start the MGC and MGs i("[MGC] start"), @@ -413,6 +419,10 @@ sent_timer_exceeded(Config) when is_list(Config) -> i("[MGC] stop"), ?MGC_STOP(Mgc), + %% Cleanup + d("stop nodes"), + ?STOP_NODES([MgcNode, MgNode]), + i("done", []), ok. @@ -432,10 +442,10 @@ sent_timer_exceeded_long(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MgcNode: ~p" + "~n MgNode: ~p", [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + ok = ?START_NODES([MgcNode, MgNode], true), %% Start the MGC and MGs i("[MGC] start"), @@ -484,6 +494,10 @@ sent_timer_exceeded_long(Config) when is_list(Config) -> i("[MGC] stop"), ?MGC_STOP(Mgc), + %% Cleanup + d("stop nodes"), + ?STOP_NODES([MgcNode, MgNode]), + i("done", []), ok. @@ -508,10 +522,10 @@ sent_resend_late_reply(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MgcNode: ~p" + "~n MgNode: ~p", [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + ok = ?START_NODES([MgcNode, MgNode], true), %% Start the MGC and MGs i("[MGC] start"), @@ -573,6 +587,10 @@ sent_resend_late_reply(Config) when is_list(Config) -> i("[MGC] stop"), ?MGC_STOP(Mgc), + %% Cleanup + d("stop nodes"), + ?STOP_NODES([MgcNode, MgNode]), + i("done", []), ok. @@ -608,10 +626,10 @@ sent_resend_exceeded(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MgcNode: ~p" + "~n MgNode: ~p", [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + ok = ?START_NODES([MgcNode, MgNode], true), %% Start the MGC and MGs i("[MGC] start"), @@ -668,6 +686,10 @@ sent_resend_exceeded(Config) when is_list(Config) -> i("[MGC] stop"), ?MGC_STOP(Mgc), + %% Cleanup + d("stop nodes"), + ?STOP_NODES([MgcNode, MgNode]), + i("done", []), ok. @@ -703,10 +725,10 @@ sent_resend_exceeded_long(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MgcNode: ~p" + "~n MgNode: ~p", [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + ok = ?START_NODES([MgcNode, MgNode], true), %% Start the MGC and MGs i("[MGC] start"), @@ -764,6 +786,10 @@ sent_resend_exceeded_long(Config) when is_list(Config) -> i("[MGC] stop"), ?MGC_STOP(Mgc), + %% Cleanup + d("stop nodes"), + ?STOP_NODES([MgcNode, MgNode]), + i("done", []), ok. @@ -799,10 +825,10 @@ recv_limit_exceeded1(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MgcNode: ~p" + "~n MgNode: ~p", [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + ok = ?START_NODES([MgcNode, MgNode], true), d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), @@ -842,6 +868,10 @@ recv_limit_exceeded1(Config) when is_list(Config) -> i("[MG] stop generator"), megaco_test_megaco_generator:stop(Mg), + %% Cleanup + d("stop nodes"), + ?STOP_NODES([MgcNode, MgNode]), + i("done", []), ok. @@ -1200,10 +1230,10 @@ otp_4956(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MgcNode: ~p" + "~n MgNode: ~p", [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + ok = ?START_NODES([MgcNode, MgNode], true), d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -1243,6 +1273,10 @@ otp_4956(Config) when is_list(Config) -> i("[MG] stop generator"), megaco_test_tcp_generator:stop(Mg), + %% Cleanup + d("stop nodes"), + ?STOP_NODES([MgcNode, MgNode]), + i("done", []), ok. @@ -1758,10 +1792,10 @@ otp_5310(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MgcNode: ~p" + "~n MgNode: ~p", [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + ok = ?START_NODES([MgcNode, MgNode], true), %% Start the MGC and MGs i("[MGC] start"), @@ -1871,6 +1905,10 @@ otp_5310(Config) when is_list(Config) -> i("[MGC] stop"), ?MGC_STOP(Mgc), + %% Cleanup + d("stop nodes"), + ?STOP_NODES([MgcNode, MgNode]), + i("done", []), ok. @@ -1921,10 +1959,10 @@ otp_5619(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MgcNode: ~p" + "~n MgNode: ~p", [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + ok = ?START_NODES([MgcNode, MgNode], true), %% Start the MGC and MGs i("[MGC] start"), @@ -1989,6 +2027,10 @@ otp_5619(Config) when is_list(Config) -> i("[MGC] stop~n"), ?MGC_STOP(Mgc), + %% Cleanup + d("stop nodes"), + ?STOP_NODES([MgcNode, MgNode]), + i("done", []), ok. diff --git a/lib/megaco/test/megaco_segment_SUITE.erl b/lib/megaco/test/megaco_segment_SUITE.erl index 0d3cc660f2..08b86606de 100644 --- a/lib/megaco/test/megaco_segment_SUITE.erl +++ b/lib/megaco/test/megaco_segment_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2019. All Rights Reserved. +%% Copyright Ericsson AB 2006-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -47,6 +47,7 @@ ]). +-include_lib("common_test/include/ct.hrl"). -include("megaco_test_lib.hrl"). -include_lib("megaco/include/megaco.hrl"). -include_lib("megaco/include/megaco_message_v3.hrl"). @@ -197,18 +198,25 @@ send_segmented_msg_plain1(doc) -> "First plain test that it is possible to send segmented messages. " "Send window = infinity. "; send_segmented_msg_plain1(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, ssmp1), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_send_segmented_msg_plain1/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(ssmp1, Pre, Case, Post). + +do_send_segmented_msg_plain1([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), @@ -792,32 +800,41 @@ ssmp1_mg_notify_reply_ar(Cid, Tid) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - send_segmented_msg_plain2(suite) -> []; send_segmented_msg_plain2(doc) -> "Second plain test that it is possible to send segmented messages. " "Send window = infinity. "; send_segmented_msg_plain2(Config) when is_list(Config) -> - %% <CONDITIONAL-SKIP> - Skippable = [{unix, [linux]}], - Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% </CONDITIONAL-SKIP> - - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, ssmp2), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + %% We leave it commented out as test + %% All the other changes to the framework + %% may have "solved" the issues... + + %% <CONDITIONAL-SKIP> + %% Skippable = [{unix, [linux]}], + %% Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, + %% ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% </CONDITIONAL-SKIP> + + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_send_segmented_msg_plain2/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(ssmp2, Pre, Case, Post). + +do_send_segmented_msg_plain2([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), @@ -1389,26 +1406,31 @@ ssmp2_mg_notify_reply_ar(Cid, Tid) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - send_segmented_msg_plain3(suite) -> []; send_segmented_msg_plain3(doc) -> "Third plain test that it is possible to send segmented messages. " "Send window = 1. "; send_segmented_msg_plain3(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, ssmp3), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_send_segmented_msg_plain3/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(ssmp3, Pre, Case, Post). + +do_send_segmented_msg_plain3([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), @@ -2076,32 +2098,38 @@ ssmp3_mg_notify_reply_ar(Cid, Tid) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - send_segmented_msg_plain4(suite) -> []; send_segmented_msg_plain4(doc) -> "Forth plain test that it is possible to send segmented messages. " "Send window = 3. "; send_segmented_msg_plain4(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, ssmp4), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - + Factor = ?config(megaco_factor, Config), + ct:timetrap(Factor * ?SECS(60)), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun(X) -> do_send_segmented_msg_plain4(Factor, X) end, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(ssmp4, Pre, Case, Post). + +do_send_segmented_msg_plain4(Factor, [MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), d("[MGC] create the event sequence"), - MgcEvSeq = ssmp4_mgc_event_sequence(text, tcp), + MgcEvSeq = ssmp4_mgc_event_sequence(Factor, text, tcp), i("wait some time before starting the MGC simulation"), sleep(1000), @@ -2116,7 +2144,7 @@ send_segmented_msg_plain4(Config) when is_list(Config) -> {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode), d("[MG] create the event sequence"), - MgEvSeq = ssmp4_mg_event_sequence(text, tcp), + MgEvSeq = ssmp4_mg_event_sequence(Factor, text, tcp), i("wait some time before starting the MG simulation"), sleep(1000), @@ -2144,7 +2172,7 @@ send_segmented_msg_plain4(Config) when is_list(Config) -> %% MGC generator stuff %% -ssmp4_mgc_event_sequence(text, tcp) -> +ssmp4_mgc_event_sequence(Factor, text, tcp) -> DecodeFun = ssmp4_mgc_decode_msg_fun(megaco_pretty_text_encoder, []), EncodeFun = ssmp4_mgc_encode_msg_fun(megaco_pretty_text_encoder, []), Mid = {deviceName,"mgc"}, @@ -2213,33 +2241,38 @@ ssmp4_mgc_event_sequence(text, tcp) -> SegmentRep7 = ssmp4_mgc_segment_reply_msg(Mid, TransId, 7, false), SegmentRep8 = ssmp4_mgc_segment_reply_msg(Mid, TransId, 8, true), TransAck = ssmp4_mgc_trans_ack_msg(Mid, TransId), + TO = fun(T) -> Factor*T end, EvSeq = [{debug, true}, + {trigger, "verbosity", + fun() -> + put(verbosity, ?TEST_VERBOSITY) + end}, {decode, DecodeFun}, {encode, EncodeFun}, {listen, 2944}, {expect_accept, any}, - {expect_receive, "service-change-request", {ScrVerifyFun, 5000}}, + {expect_receive, "service-change-request", {ScrVerifyFun, TO(5000)}}, {send, "service-change-reply", ServiceChangeRep}, {expect_nothing, 1000}, {send, "notify request", NotifyReq}, - {expect_receive, "notify reply: segment 1", {NrVerifyFun1, 1000}}, - {expect_receive, "notify reply: segment 2", {NrVerifyFun2, 1000}}, - {expect_receive, "notify reply: segment 3", {NrVerifyFun3, 1000}}, + {expect_receive, "notify reply: segment 1", {NrVerifyFun1, TO(1000)}}, + {expect_receive, "notify reply: segment 2", {NrVerifyFun2, TO(1000)}}, + {expect_receive, "notify reply: segment 3", {NrVerifyFun3, TO(1000)}}, {expect_nothing, 1000}, {send, "segment reply 1", SegmentRep1}, - {expect_receive, "notify reply: segment 4", {NrVerifyFun4, 1000}}, + {expect_receive, "notify reply: segment 4", {NrVerifyFun4, TO(1000)}}, {expect_nothing, 1000}, {send, "segment reply 2", SegmentRep2}, - {expect_receive, "notify reply: segment 5", {NrVerifyFun5, 1000}}, + {expect_receive, "notify reply: segment 5", {NrVerifyFun5, TO(1000)}}, {expect_nothing, 1000}, {send, "segment reply 3", SegmentRep3}, - {expect_receive, "notify reply: segment 6", {NrVerifyFun6, 1000}}, + {expect_receive, "notify reply: segment 6", {NrVerifyFun6, TO(1000)}}, {expect_nothing, 1000}, {send, "segment reply 4", SegmentRep4}, - {expect_receive, "notify reply: segment 7", {NrVerifyFun7, 1000}}, + {expect_receive, "notify reply: segment 7", {NrVerifyFun7, TO(1000)}}, {expect_nothing, 1000}, {send, "segment reply 5", SegmentRep5}, - {expect_receive, "notify reply: segment 8", {NrVerifyFun8, 1000}}, + {expect_receive, "notify reply: segment 8", {NrVerifyFun8, TO(1000)}}, {expect_nothing, 1000}, {send, "segment reply 6", SegmentRep6}, {expect_nothing, 1000}, @@ -2506,7 +2539,7 @@ ssmp4_mgc_trans_ack_msg(Mid, TransId) -> %% %% MG generator stuff %% -ssmp4_mg_event_sequence(text, tcp) -> +ssmp4_mg_event_sequence(Factor, text, tcp) -> Mid = {deviceName,"mg"}, RI = [ {port, 2944}, @@ -2527,7 +2560,8 @@ ssmp4_mg_event_sequence(text, tcp) -> Tid8 = #megaco_term_id{id = ["00000000","00000000","00000008"]}, Tids = [Tid1, Tid2, Tid3, Tid4, Tid5, Tid6, Tid7, Tid8], NotifyReqVerify = ssmp4_mg_verify_notify_request_fun(Tids), - AckVerify = ssmp4_mg_verify_ack_fun(), + AckVerify = ssmp4_mg_verify_ack_fun(), + TO = fun(T) -> Factor*T end, EvSeq = [ {debug, true}, {megaco_trace, disable}, @@ -2548,7 +2582,7 @@ ssmp4_mg_event_sequence(text, tcp) -> {megaco_update_conn_info, max_pdu_size, 128}, {sleep, 1000}, {megaco_callback, handle_trans_request, NotifyReqVerify}, - {megaco_callback, handle_trans_ack, AckVerify, 15000}, + {megaco_callback, handle_trans_ack, AckVerify, TO(15000)}, megaco_stop_user, megaco_stop, {sleep, 1000} @@ -2636,30 +2670,34 @@ ssmp4_mg_verify_notify_request_fun(Tids) -> ssmp4_mg_verify_notify_request( {handle_trans_request, _CH, ?VERSION, ARs}, Tids) - when length(ARs) == length(Tids) -> + when length(ARs) =:= length(Tids) -> (catch ssmp4_mg_do_verify_notify_request(Tids, ARs)); ssmp4_mg_verify_notify_request( {handle_trans_request, _CH, ?VERSION, ARs}, _Tids) -> + e("MG Notify Request verification failed: invalid action requests" + "~n ARs: ~p", [ARs]), {error, {invalid_action_requests, ARs}, ok}; ssmp4_mg_verify_notify_request( {handle_trans_request, CH, V, ARs}, _Tids) -> + e("MG Notify Request verification failed: invalid trans request" + "~n CH: ~p" + "~n V: ~p" + "~n ARs: ~p", [CH, V, ARs]), {error, {invalid_trans_request, {CH, V, ARs}}, ok}; ssmp4_mg_verify_notify_request(Crap, _Tids) -> - io:format("ssmp4_mg_verify_notify_request -> unknown request" - "~n Crap: ~p" - "~n Tids: ~p" - "~n", [Crap, _Tids]), + e("MG Notify Request verification failed: unknown request" + "~n Crap: ~p" + "~n Tids: ~p", [Crap, _Tids]), {error, {unexpected_event, Crap}, ok}. ssmp4_mg_do_verify_notify_request(Tids, ARs) -> - io:format("ssmp4_mg_do_verify_notify_request -> ok" - "~n Tids: ~p" - "~n ARs: ~p" - "~n", [Tids, ARs]), + p("MG Notify Request verification - attempt verify action request(s):" + "~n Tids: ~p" + "~n ARs: ~p", [Tids, ARs]), ActionReplies = ssmp4_mg_do_verify_notify_request_ars(Tids, ARs), - io:format("ssmp4_mg_do_verify_notify_request -> ok" - "~n ActionReplies: ~p" - "~n", [ActionReplies]), + p("MG Notify Request verification - ok" + "~n ActionReplies: ~p" + "~n", [ActionReplies]), Reply = {{handle_ack, ssmp4}, ActionReplies}, {ok, ARs, Reply}. @@ -2673,10 +2711,9 @@ ssmp4_mg_do_verify_notify_request_ars([Tid|Tids], [AR|ARs], Acc) -> ssmp4_mg_do_verify_notify_request_ars(Tids, ARs, [ActionReply|Acc]). ssmp4_mg_do_verify_notify_request_ar(Tid, AR) -> - io:format("ssmp4_mg_do_verify_notify_request_ar -> ok" - "~n Tid: ~p" - "~n AR: ~p" - "~n", [Tid, AR]), + p("ssmp4_mg_do_verify_notify_request_ar -> ok" + "~n Tid: ~p" + "~n AR: ~p", [Tid, AR]), {Cid, CR} = case AR of #'ActionRequest'{contextId = CtxId, @@ -2755,8 +2792,6 @@ ssmp4_mg_notify_reply_ar(Cid, Tid) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - send_segmented_msg_ooo1(suite) -> []; send_segmented_msg_ooo1(doc) -> @@ -2765,20 +2800,27 @@ send_segmented_msg_ooo1(doc) -> "segment reply is sent out-of-order. " "Send window = 3. "; send_segmented_msg_ooo1(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, ssmo1), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - - d("[MGC] start the simulator "), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_send_segmented_msg_ooo1/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(ssmo1, Pre, Case, Post). + +do_send_segmented_msg_ooo1([MgcNode, MgNode]) -> + + d("[MGC] start the simulator"), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), d("[MGC] create the event sequence"), @@ -3436,8 +3478,6 @@ ssmo1_mg_notify_reply_ar(Cid, Tid) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - send_segmented_msg_missing_seg_reply1(suite) -> []; send_segmented_msg_missing_seg_reply1(doc) -> @@ -3446,18 +3486,25 @@ send_segmented_msg_missing_seg_reply1(doc) -> "when a segment reply goes missing. Ack expected. " "Send window = 3. "; send_segmented_msg_missing_seg_reply1(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, ssmmsr1), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_send_segmented_msg_missing_seg_reply1/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(ssmmsr1, Pre, Case, Post). + +do_send_segmented_msg_missing_seg_reply1([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), @@ -3613,6 +3660,10 @@ ssmmsr1_mgc_event_sequence(text, tcp) -> TransAck = ssmmsr1_mgc_trans_ack_msg(Mid, TransId), ReadyForSegments = ssmmsr1_mgc_ready_for_segments_fun(), EvSeq = [{debug, true}, + {trigger, "verbosity", + fun() -> + put(verbosity, ?TEST_VERBOSITY) + end}, {decode, DecodeFun}, {encode, EncodeFun}, {listen, 2944}, @@ -3942,6 +3993,10 @@ ssmmsr1_mg_event_sequence(text, tcp) -> ReadyForSegments = ssmmsr1_mg_ready_for_segments_fun(), EvSeq = [ {debug, true}, + {trigger, + fun() -> + put(verbosity, ?TEST_VERBOSITY) + end}, {megaco_trace, disable}, %% {megaco_trace, max}, megaco_start, @@ -4202,8 +4257,6 @@ ssmmsr1_mg_notify_reply_ar(Cid, Tid) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - send_segmented_msg_missing_seg_reply2(suite) -> []; send_segmented_msg_missing_seg_reply2(doc) -> @@ -4212,18 +4265,25 @@ send_segmented_msg_missing_seg_reply2(doc) -> "when a segment reply goes missing. Ack expected. " "Send window = 1. "; send_segmented_msg_missing_seg_reply2(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, ssmmsr2), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_send_segmented_msg_missing_seg_reply2/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(ssmmsr2, Pre, Case, Post). + +do_send_segmented_msg_missing_seg_reply2([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), @@ -4353,6 +4413,10 @@ ssmmsr2_mgc_event_sequence(text, tcp) -> SegmentRep1 = ssmmsr2_mgc_segment_reply_msg(Mid, TransId, 1, false), ReadyForSegments = ssmmsr2_mgc_ready_for_segments_fun(), EvSeq = [{debug, true}, + {trigger, "verbosity", + fun() -> + put(verbosity, ?TEST_VERBOSITY) + end}, {decode, DecodeFun}, {encode, EncodeFun}, {listen, 2944}, @@ -4661,6 +4725,10 @@ ssmmsr2_mg_event_sequence(text, tcp) -> ReadyForSegments = ssmmsr2_mg_ready_for_segments_fun(), EvSeq = [ {debug, true}, + {trigger, + fun() -> + put(verbosity, ?TEST_VERBOSITY) + end}, {megaco_trace, disable}, %% {megaco_trace, max}, megaco_start, @@ -4952,18 +5020,25 @@ recv_segmented_msg_plain(suite) -> recv_segmented_msg_plain(doc) -> "Received segmented megaco message [plain]"; recv_segmented_msg_plain(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, rsmp), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_recv_segmented_msg_plain/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(rsmp, Pre, Case, Post). + +do_recv_segmented_msg_plain([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), @@ -5621,18 +5696,25 @@ recv_segmented_msg_ooo_seg(suite) -> recv_segmented_msg_ooo_seg(doc) -> "Received segmented megaco message [out-of-order segments]"; recv_segmented_msg_ooo_seg(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, rsmos), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_recv_segmented_msg_ooo_seg/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(rsmos, Pre, Case, Post). + +do_recv_segmented_msg_ooo_seg([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), @@ -6288,18 +6370,25 @@ recv_segmented_msg_missing_seg1(doc) -> "Received segmented megaco message with one segment missing " "using plain integer recv segment timer"; recv_segmented_msg_missing_seg1(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, rsmms1), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_recv_segmented_msg_missing_seg1/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(rsmms1, Pre, Case, Post). + +do_recv_segmented_msg_missing_seg1([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), @@ -6956,18 +7045,25 @@ recv_segmented_msg_missing_seg2(doc) -> "Received segmented megaco message with one segment missing " "using incremental recv segment timer"; recv_segmented_msg_missing_seg2(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, rsmms2), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MgcNode: ~p" + "~n MgNode: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_recv_segmented_msg_missing_seg2/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(rsmms2, Pre, Case, Post). + +do_recv_segmented_msg_missing_seg2([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode), @@ -7828,40 +7924,65 @@ sleep(X) -> receive after X -> ok end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +try_tc(TCName, Pre, Case, Post) -> + try_tc(TCName, "TEST", ?TEST_VERBOSITY, Pre, Case, Post). + +try_tc(TCName, Name, Verbosity, Pre, Case, Post) -> + ?TRY_TC(TCName, Name, Verbosity, Pre, Case, Post). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + p(F, A) -> io:format("*** [~s] ~p ***" "~n " ++ F ++ "~n", [?FTS(), self() | A]). +%% e(F) -> +%% e(F, []). + +e(F, A) -> + print(error, get(verbosity), "ERROR", get(tc), F, A). + + i(F) -> i(F, []). i(F, A) -> - print(info, get(verbosity), get(tc), "INF", F, A). + print(info, get(verbosity), "INFO", get(tc), F, A). d(F) -> d(F, []). d(F, A) -> - print(debug, get(verbosity), get(tc), "DBG", F, A). + print(debug, get(verbosity), "DBG", get(tc), F, A). printable(_, debug) -> true; printable(info, info) -> true; +printable(error, _) -> true; printable(_,_) -> false. -print(Severity, Verbosity, Tc, P, F, A) -> - print(printable(Severity, Verbosity), Tc, P, F, A). +print(Severity, Verbosity, P, TC, F, A) -> + print(printable(Severity, Verbosity), P, TC, F, A). -print(true, Tc, P, F, A) -> - io:format("*** [~s] ~s ~p ~s:~w ***" - "~n " ++ F ++ "~n", - [?FTS(), P, self(), get(sname), Tc | A]); +print(true, P, TC, F, A) when (TC =:= undefined) -> + print(P, "", F, A); +print(true, P, TC, F, A) when is_atom(TC) -> + print(P, ":" ++ atom_to_list(TC), F, A); +print(true, P, TC, F, A) when is_list(TC) -> + print(P, TC, F, A); print(_, _, _, _, _) -> ok. +print(P, TCStr, F, A) -> + io:format("*** [~s] ~s ~p ~s~s ***" + "~n " ++ F ++ "~n", + [?FTS(), P, self(), get(sname), TCStr | A]). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/megaco/test/megaco_tcp_SUITE.erl b/lib/megaco/test/megaco_tcp_SUITE.erl index 5981e34f19..5792c6b380 100644 --- a/lib/megaco/test/megaco_tcp_SUITE.erl +++ b/lib/megaco/test/megaco_tcp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2019. All Rights Reserved. +%% Copyright Ericsson AB 2000-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -30,8 +30,6 @@ -include_lib("megaco/src/tcp/megaco_tcp.hrl"). -include("megaco_test_lib.hrl"). -%% -compile(export_all). - %%---------------------------------------------------------------------- %% External exports @@ -69,14 +67,14 @@ %% Macros %%---------------------------------------------------------------------- +-define(CH, megaco_test_command_handler). +-define(TEST_VERBOSITY, debug). + + %%---------------------------------------------------------------------- %% Records %%---------------------------------------------------------------------- --record(command, {id, desc, cmd}). --record(server, {parent, transport_ref, control_pid, handle}). --record(client, {parent, transport_ref, control_pid, handle}). - %%====================================================================== %% Common Test interface functions @@ -248,17 +246,22 @@ start_and_stop(doc) -> ["This test case sets up a connection and then cloises it. " "No data is sent. "]; start_and_stop(Config) when is_list(Config) -> - put(sname, "start_and_stop"), - p("BEGIN TEST-CASE"), - - process_flag(trap_exit, true), - - p("create nodes"), - ServerNode = make_node_name(server), - ClientNode = make_node_name(client), - Nodes = [ServerNode, ClientNode], - ok = megaco_test_lib:start_nodes(Nodes, ?FILE, ?LINE), - + Pre = fun() -> + p("create nodes"), + ServerNode = make_node_name(server), + ClientNode = make_node_name(client), + Nodes = [ServerNode, ClientNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_start_and_stop/1, + Post = fun(Nodes) -> + p("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(start_and_stop, Pre, Case, Post). + +do_start_and_stop([ServerNode, ClientNode]) -> %% Create command sequences p("create command sequences"), ServerPort = 2944, @@ -275,7 +278,7 @@ start_and_stop(Config) when is_list(Config) -> await_server_listening(Server, Client), - await_command_handler_completion([Server, Client], timer:seconds(20)), + ok = await_command_handler_completion([Server, Client], ?SECS(20)), p("done"), ok. @@ -284,41 +287,41 @@ start_and_stop_server_commands(Port) -> Opts = [{port, Port}], Self = self(), [ - #command{id = 1, - desc = "Command sequence init", - cmd = fun(State) -> - {ok, State#server{parent = Self}} - end}, - - #command{id = 2, - desc = "Start transport", - cmd = fun(State) -> - server_start_transport(State) - end}, - - #command{id = 3, - desc = "Listen", - cmd = fun(State) -> - server_listen(State, Opts) - end}, - - #command{id = 4, - desc = "Notify listening", - cmd = fun(State) -> - server_notify_listening(State) - end}, - - #command{id = 5, - desc = "Await nothing", - cmd = fun(State) -> - server_await_nothing(State, 6000) - end}, - - #command{id = 6, - desc = "Stop", - cmd = fun(State) -> - server_stop_transport(State) - end} + #{id => 1, + desc => "Command sequence init", + cmd => fun(State) -> + {ok, State#{parent => Self}} + end}, + + #{id => 2, + desc => "Start transport", + cmd => fun(State) -> + server_start_transport(State) + end}, + + #{id => 3, + desc => "Listen", + cmd => fun(State) -> + server_listen(State, Opts) + end}, + + #{id => 4, + desc => "Notify listening", + cmd => fun(State) -> + server_notify_listening(State) + end}, + + #{id => 5, + desc => "Await nothing", + cmd => fun(State) -> + server_await_nothing(State, 6000) + end}, + + #{id => 6, + desc => "Stop", + cmd => fun(State) -> + server_stop_transport(State) + end} ]. @@ -327,47 +330,47 @@ start_and_stop_client_commands(ServerPort, ServerHost) -> Opts = [{port, ServerPort}, {host, ServerHost}], Self = self(), [ - #command{id = 1, - desc = "Command sequence init", - cmd = fun(State) -> - {ok, State#client{parent = Self}} - end}, - - #command{id = 2, - desc = "Start transport", - cmd = fun(State) -> - client_start_transport(State) - end}, - - #command{id = 3, - desc = "Await continue", - cmd = fun(State) -> - client_await_continue_signal(State, 5000) - end}, - - #command{id = 4, - desc = "Connect", - cmd = fun(State) -> - client_connect(State, Opts) - end}, - - #command{id = 5, - desc = "Await nothing", - cmd = fun(State) -> - client_await_nothing(State, 5000) - end}, - - #command{id = 6, - desc = "Disconnect", - cmd = fun(State) -> - client_disconnect(State) - end}, - - #command{id = 7, - desc = "Stop transport", - cmd = fun(State) -> - client_stop_transport(State) - end} + #{id => 1, + desc => "Command sequence init", + cmd => fun(State) -> + {ok, State#{parent => Self}} + end}, + + #{id => 2, + desc => "Start transport", + cmd => fun(State) -> + client_start_transport(State) + end}, + + #{id => 3, + desc => "Await continue", + cmd => fun(State) -> + client_await_continue_signal(State, 5000) + end}, + + #{id => 4, + desc => "Connect", + cmd => fun(State) -> + client_connect(State, Opts) + end}, + + #{id => 5, + desc => "Await nothing", + cmd => fun(State) -> + client_await_nothing(State, 5000) + end}, + + #{id => 6, + desc => "Disconnect", + cmd => fun(State) -> + client_disconnect(State) + end}, + + #{id => 7, + desc => "Stop transport", + cmd => fun(State) -> + client_stop_transport(State) + end} ]. @@ -378,17 +381,22 @@ start_and_stop_client_commands(ServerPort, ServerHost) -> sendreceive(suite) -> []; sendreceive(Config) when is_list(Config) -> - put(sname, "sendreceive"), - p("BEGIN TEST-CASE"), - - process_flag(trap_exit, true), - - p("create nodes"), - ServerNode = make_node_name(server), - ClientNode = make_node_name(client), - Nodes = [ServerNode, ClientNode], - ok = megaco_test_lib:start_nodes(Nodes, ?FILE, ?LINE), - + Pre = fun() -> + p("create nodes"), + ServerNode = make_node_name(server), + ClientNode = make_node_name(client), + Nodes = [ServerNode, ClientNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_sendreceive/1, + Post = fun(Nodes) -> + p("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(sendreceive, Pre, Case, Post). + +do_sendreceive([ServerNode, ClientNode]) -> %% Create command sequences p("create command sequences"), ServerPort = 2944, @@ -405,7 +413,7 @@ sendreceive(Config) when is_list(Config) -> await_server_listening(Server, Client), - await_command_handler_completion([Server, Client], timer:seconds(20)), + ok = await_command_handler_completion([Server, Client], ?SECS(20)), p("done"), ok. @@ -414,83 +422,83 @@ sendreceive_server_commands(Port) -> Opts = [{port, Port}], Self = self(), [ - #command{id = 1, - desc = "Command sequence init", - cmd = fun(State) -> - {ok, State#server{parent = Self}} - end}, - - #command{id = 2, - desc = "Start transport", - cmd = fun(State) -> - server_start_transport(State) - end}, - - #command{id = 3, - desc = "Listen", - cmd = fun(State) -> - server_listen(State, Opts) - end}, - - #command{id = 4, - desc = "Notify listening", - cmd = fun(State) -> - server_notify_listening(State) - end}, - - #command{id = 5, - desc = "Await initial message (ping)", - cmd = fun(State) -> - server_await_initial_message(State, "ping", 5000) - end}, - - #command{id = 6, - desc = "Send reply (pong) to initial message", - cmd = fun(State) -> - server_send_message(State, "pong") - end}, - - #command{id = 7, - desc = "Await nothing before sending a message (hejsan)", - cmd = fun(State) -> - server_await_nothing(State, 1000) - end}, - - #command{id = 8, - desc = "Send message (hejsan)", - cmd = fun(State) -> - server_send_message(State, "hejsan") - end}, - - #command{id = 9, - desc = "Await reply (hoppsan) to message", - cmd = fun(State) -> - server_await_message(State, "hoppsan", 1000) - end}, - - #command{id = 10, - desc = "Await nothing before disconnecting", - cmd = fun(State) -> - server_await_nothing(State, 1000) - end}, - - #command{id = 11, - desc = "Disconnect", - cmd = fun(State) -> - server_disconnect(State) - end}, - - #command{id = 12, - desc = "Await nothing before stopping transport", - cmd = fun(State) -> - server_await_nothing(State, 1000) - end}, - - #command{id = 13, - desc = "Stop", - cmd = fun(State) -> - server_stop_transport(State) - end} + #{id => 1, + desc => "Command sequence init", + cmd => fun(State) -> + {ok, State#{parent => Self}} + end}, + + #{id => 2, + desc => "Start transport", + cmd => fun(State) -> + server_start_transport(State) + end}, + + #{id => 3, + desc => "Listen", + cmd => fun(State) -> + server_listen(State, Opts) + end}, + + #{id => 4, + desc => "Notify listening", + cmd => fun(State) -> + server_notify_listening(State) + end}, + + #{id => 5, + desc => "Await initial message (ping)", + cmd => fun(State) -> + server_await_initial_message(State, "ping", 5000) + end}, + + #{id => 6, + desc => "Send reply (pong) to initial message", + cmd => fun(State) -> + server_send_message(State, "pong") + end}, + + #{id => 7, + desc => "Await nothing before sending a message (hejsan)", + cmd => fun(State) -> + server_await_nothing(State, 1000) + end}, + + #{id => 8, + desc => "Send message (hejsan)", + cmd => fun(State) -> + server_send_message(State, "hejsan") + end}, + + #{id => 9, + desc => "Await reply (hoppsan) to message", + cmd => fun(State) -> + server_await_message(State, "hoppsan", 1000) + end}, + + #{id => 10, + desc => "Await nothing before disconnecting", + cmd => fun(State) -> + server_await_nothing(State, 1000) + end}, + + #{id => 11, + desc => "Disconnect", + cmd => fun(State) -> + server_disconnect(State) + end}, + + #{id => 12, + desc => "Await nothing before stopping transport", + cmd => fun(State) -> + server_await_nothing(State, 1000) + end}, + + #{id => 13, + desc => "Stop", + cmd => fun(State) -> + server_stop_transport(State) + end} ]. @@ -498,77 +506,77 @@ sendreceive_client_commands(ServerPort, ServerHost) -> Opts = [{port, ServerPort}, {host, ServerHost}], Self = self(), [ - #command{id = 1, - desc = "Command sequence init", - cmd = fun(State) -> - {ok, State#client{parent = Self}} - end}, - - #command{id = 2, - desc = "Start transport", - cmd = fun(State) -> - client_start_transport(State) - end}, - - #command{id = 3, - desc = "Await continue", - cmd = fun(State) -> - client_await_continue_signal(State, 5000) - end}, - - #command{id = 4, - desc = "Connect", - cmd = fun(State) -> - client_connect(State, Opts) - end}, - - #command{id = 5, - desc = "Send initial message (ping)", - cmd = fun(State) -> - client_send_message(State, "ping") - end}, - - #command{id = 6, - desc = "Await reply (pong) to initial message", - cmd = fun(State) -> - client_await_message(State, "pong", 1000) - end}, - - #command{id = 7, - desc = "Await message (hejsan)", - cmd = fun(State) -> - client_await_message(State, "hejsan", 5000) - end}, - - #command{id = 8, - desc = "Send reply (hoppsan) to message", - cmd = fun(State) -> - client_send_message(State, "hoppsan") - end}, - - #command{id = 9, - desc = "Await nothing before disconnecting", - cmd = fun(State) -> - client_await_nothing(State, 1000) - end}, - - #command{id = 10, - desc = "Disconnect", - cmd = fun(State) -> - client_disconnect(State) - end}, - - #command{id = 11, - desc = "Await nothing before stopping transport", - cmd = fun(State) -> - client_await_nothing(State, 1000) - end}, - - #command{id = 12, - desc = "Stop transport", - cmd = fun(State) -> - client_stop_transport(State) - end} + #{id => 1, + desc => "Command sequence init", + cmd => fun(State) -> + {ok, State#{parent => Self}} + end}, + + #{id => 2, + desc => "Start transport", + cmd => fun(State) -> + client_start_transport(State) + end}, + + #{id => 3, + desc => "Await continue", + cmd => fun(State) -> + client_await_continue_signal(State, 5000) + end}, + + #{id => 4, + desc => "Connect", + cmd => fun(State) -> + client_connect(State, Opts) + end}, + + #{id => 5, + desc => "Send initial message (ping)", + cmd => fun(State) -> + client_send_message(State, "ping") + end}, + + #{id => 6, + desc => "Await reply (pong) to initial message", + cmd => fun(State) -> + client_await_message(State, "pong", 1000) + end}, + + #{id => 7, + desc => "Await message (hejsan)", + cmd => fun(State) -> + client_await_message(State, "hejsan", 5000) + end}, + + #{id => 8, + desc => "Send reply (hoppsan) to message", + cmd => fun(State) -> + client_send_message(State, "hoppsan") + end}, + + #{id => 9, + desc => "Await nothing before disconnecting", + cmd => fun(State) -> + client_await_nothing(State, 1000) + end}, + + #{id => 10, + desc => "Disconnect", + cmd => fun(State) -> + client_disconnect(State) + end}, + + #{id => 11, + desc => "Await nothing before stopping transport", + cmd => fun(State) -> + client_await_nothing(State, 1000) + end}, + + #{id => 12, + desc => "Stop transport", + cmd => fun(State) -> + client_stop_transport(State) + end} ]. @@ -578,17 +586,22 @@ sendreceive_client_commands(ServerPort, ServerHost) -> block_unblock(suite) -> []; block_unblock(Config) when is_list(Config) -> - put(sname, "block_unblock"), - p("BEGIN TEST-CASE"), - - process_flag(trap_exit, true), - - p("create nodes"), - ServerNode = make_node_name(server), - ClientNode = make_node_name(client), - Nodes = [ServerNode, ClientNode], - ok = megaco_test_lib:start_nodes(Nodes, ?FILE, ?LINE), - + Pre = fun() -> + p("create nodes"), + ServerNode = make_node_name(server), + ClientNode = make_node_name(client), + Nodes = [ServerNode, ClientNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_block_unblock/1, + Post = fun(Nodes) -> + p("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(block_unblock, Pre, Case, Post). + +do_block_unblock([ServerNode, ClientNode]) -> %% Create command sequences p("create command sequences"), ServerPort = 2944, @@ -607,7 +620,7 @@ block_unblock(Config) when is_list(Config) -> await_client_blocked(Server, Client), - await_command_handler_completion([Server, Client], timer:seconds(30)), + ok = await_command_handler_completion([Server, Client], ?SECS(30)), p("done"), ok. @@ -616,89 +629,89 @@ block_unblock_server_commands(Port) -> Opts = [{port, Port}], Self = self(), [ - #command{id = 1, - desc = "Command sequence init", - cmd = fun(State) -> - {ok, State#server{parent = Self}} - end}, - - #command{id = 2, - desc = "Start transport", - cmd = fun(State) -> - server_start_transport(State) - end}, - - #command{id = 3, - desc = "Listen", - cmd = fun(State) -> - server_listen(State, Opts) - end}, - - #command{id = 4, - desc = "Notify listening", - cmd = fun(State) -> - server_notify_listening(State) - end}, - - #command{id = 5, - desc = "Await initial message (ping)", - cmd = fun(State) -> - server_await_initial_message(State, "ping", 5000) - end}, - - #command{id = 6, - desc = "Send reply (pong) to initial message", - cmd = fun(State) -> - server_send_message(State, "pong") - end}, - - #command{id = 7, - desc = "Await continue", - cmd = fun(State) -> - server_await_continue_signal(State, 5000) - end}, - - #command{id = 9, - desc = "Await nothing before sending a message (hejsan)", - cmd = fun(State) -> - server_await_nothing(State, 1000) - end}, - - #command{id = 10, - desc = "Send message (hejsan)", - cmd = fun(State) -> - server_send_message(State, "hejsan") - end}, - - #command{id = 11, - desc = "Await reply (hoppsan) to message", - cmd = fun(State) -> - server_await_message(State, "hoppsan", 10000) - end}, - - #command{id = 12, - desc = "Await nothing before disconnecting", - cmd = fun(State) -> - server_await_nothing(State, 1000) - end}, - - #command{id = 13, - desc = "Disconnect", - cmd = fun(State) -> - server_disconnect(State) - end}, - - #command{id = 14, - desc = "Await nothing before stopping transport", - cmd = fun(State) -> - server_await_nothing(State, 1000) - end}, - - #command{id = 15, - desc = "Stop", - cmd = fun(State) -> - server_stop_transport(State) - end} + #{id => 1, + desc => "Command sequence init", + cmd => fun(State) -> + {ok, State#{parent => Self}} + end}, + + #{id => 2, + desc => "Start transport", + cmd => fun(State) -> + server_start_transport(State) + end}, + + #{id => 3, + desc => "Listen", + cmd => fun(State) -> + server_listen(State, Opts) + end}, + + #{id => 4, + desc => "Notify listening", + cmd => fun(State) -> + server_notify_listening(State) + end}, + + #{id => 5, + desc => "Await initial message (ping)", + cmd => fun(State) -> + server_await_initial_message(State, "ping", 5000) + end}, + + #{id => 6, + desc => "Send reply (pong) to initial message", + cmd => fun(State) -> + server_send_message(State, "pong") + end}, + + #{id => 7, + desc => "Await continue", + cmd => fun(State) -> + server_await_continue_signal(State, 5000) + end}, + + #{id => 9, + desc => "Await nothing before sending a message (hejsan)", + cmd => fun(State) -> + server_await_nothing(State, 1000) + end}, + + #{id => 10, + desc => "Send message (hejsan)", + cmd => fun(State) -> + server_send_message(State, "hejsan") + end}, + + #{id => 11, + desc => "Await reply (hoppsan) to message", + cmd => fun(State) -> + server_await_message(State, "hoppsan", 10000) + end}, + + #{id => 12, + desc => "Await nothing before disconnecting", + cmd => fun(State) -> + server_await_nothing(State, 1000) + end}, + + #{id => 13, + desc => "Disconnect", + cmd => fun(State) -> + server_disconnect(State) + end}, + + #{id => 14, + desc => "Await nothing before stopping transport", + cmd => fun(State) -> + server_await_nothing(State, 1000) + end}, + + #{id => 15, + desc => "Stop", + cmd => fun(State) -> + server_stop_transport(State) + end} ]. @@ -706,107 +719,107 @@ block_unblock_client_commands(ServerPort, ServerHost) -> Opts = [{port, ServerPort}, {host, ServerHost}], Self = self(), [ - #command{id = 1, - desc = "Command sequence init", - cmd = fun(State) -> - {ok, State#client{parent = Self}} - end}, - - #command{id = 2, - desc = "Start transport", - cmd = fun(State) -> - client_start_transport(State) - end}, - - #command{id = 3, - desc = "Await continue", - cmd = fun(State) -> - client_await_continue_signal(State, 5000) - end}, - - #command{id = 4, - desc = "Connect", - cmd = fun(State) -> - client_connect(State, Opts) - end}, - - #command{id = 5, - desc = "Send initial message (ping)", - cmd = fun(State) -> - client_send_message(State, "ping") - end}, - - #command{id = 6, - desc = "Await reply (pong) to initial message", - cmd = fun(State) -> - client_await_message(State, "pong", 1000) - end}, - - #command{id = 7, - desc = "Await nothing before blocking", - cmd = fun(State) -> - client_await_nothing(State, 1000) - end}, - - #command{id = 8, - desc = "Block", - cmd = fun(State) -> - client_block(State) - end}, - - #command{id = 9, - desc = "Notify blocked", - cmd = fun(State) -> - client_notify_blocked(State) - end}, - - #command{id = 10, - desc = "Await nothing before unblocking", - cmd = fun(State) -> - client_await_nothing(State, 5000) - end}, - - #command{id = 11, - desc = "Unblock", - cmd = fun(State) -> - client_unblock(State) - end}, - - #command{id = 12, - desc = "Await message (hejsan)", - cmd = fun(State) -> - client_await_message(State, "hejsan", 100) - end}, - - #command{id = 13, - desc = "Send reply (hoppsan) to message", - cmd = fun(State) -> - client_send_message(State, "hoppsan") - end}, - - #command{id = 14, - desc = "Await nothing before disconnecting", - cmd = fun(State) -> - client_await_nothing(State, 1000) - end}, - - #command{id = 15, - desc = "Disconnect", - cmd = fun(State) -> - client_disconnect(State) - end}, - - #command{id = 16, - desc = "Await nothing before stopping transport", - cmd = fun(State) -> - client_await_nothing(State, 1000) - end}, - - #command{id = 17, - desc = "Stop transport", - cmd = fun(State) -> - client_stop_transport(State) - end} + #{id => 1, + desc => "Command sequence init", + cmd => fun(State) -> + {ok, State#{parent => Self}} + end}, + + #{id => 2, + desc => "Start transport", + cmd => fun(State) -> + client_start_transport(State) + end}, + + #{id => 3, + desc => "Await continue", + cmd => fun(State) -> + client_await_continue_signal(State, 5000) + end}, + + #{id => 4, + desc => "Connect", + cmd => fun(State) -> + client_connect(State, Opts) + end}, + + #{id => 5, + desc => "Send initial message (ping)", + cmd => fun(State) -> + client_send_message(State, "ping") + end}, + + #{id => 6, + desc => "Await reply (pong) to initial message", + cmd => fun(State) -> + client_await_message(State, "pong", 1000) + end}, + + #{id => 7, + desc => "Await nothing before blocking", + cmd => fun(State) -> + client_await_nothing(State, 1000) + end}, + + #{id => 8, + desc => "Block", + cmd => fun(State) -> + client_block(State) + end}, + + #{id => 9, + desc => "Notify blocked", + cmd => fun(State) -> + client_notify_blocked(State) + end}, + + #{id => 10, + desc => "Await nothing before unblocking", + cmd => fun(State) -> + client_await_nothing(State, 5000) + end}, + + #{id => 11, + desc => "Unblock", + cmd => fun(State) -> + client_unblock(State) + end}, + + #{id => 12, + desc => "Await message (hejsan)", + cmd => fun(State) -> + client_await_message(State, "hejsan", 100) + end}, + + #{id => 13, + desc => "Send reply (hoppsan) to message", + cmd => fun(State) -> + client_send_message(State, "hoppsan") + end}, + + #{id => 14, + desc => "Await nothing before disconnecting", + cmd => fun(State) -> + client_await_nothing(State, 1000) + end}, + + #{id => 15, + desc => "Disconnect", + cmd => fun(State) -> + client_disconnect(State) + end}, + + #{id => 16, + desc => "Await nothing before stopping transport", + cmd => fun(State) -> + client_await_nothing(State, 1000) + end}, + + #{id => 17, + desc => "Stop transport", + cmd => fun(State) -> + client_stop_transport(State) + end} ]. @@ -956,7 +969,11 @@ await_server_listening(Server, Client) -> "send continue to client [~p]" "~n", [Server, Client]), Client ! {continue, self()}, - ok + ok; + {'EXIT', Server, SReason} -> + exit({server_crash, SReason}); + {'EXIT', Client, CReason} -> + exit({client_crash, CReason}) after 5000 -> %% There is no normal reason why this should take any time. %% Normally, this takes a few milli seconds. So, if we are not @@ -973,7 +990,11 @@ await_client_blocked(Server, Client) -> p("received blocked message from client [~p] => " "send continue to server [~p]~n", [Client, Server]), Server ! {continue, self()}, - ok + ok; + {'EXIT', Client, CReason} -> + exit({client_crash, CReason}); + {'EXIT', Server, SReason} -> + exit({server_crash, SReason}) after 5000 -> %% There is no normal reason why this should take any time. %% Normally, this takes a few milli seconds. So, if we are not @@ -988,18 +1009,18 @@ await_client_blocked(Server, Client) -> %% ------- Server command handler and utility functions ---------- server_start_command_handler(Node, Commands) -> - start_command_handler(Node, Commands, #server{}, "server"). + start_command_handler(Node, Commands, #{}, "server"). -server_start_transport(State) when is_record(State, server) -> +server_start_transport(State) when is_map(State) -> case (catch megaco_tcp:start_transport()) of {ok, Ref} -> - {ok, State#server{transport_ref = Ref}}; + {ok, State#{transport_ref => Ref}}; Error -> Error end. -server_listen(#server{transport_ref = Ref} = State, Options) - when is_record(State, server) andalso is_list(Options) -> +server_listen(#{transport_ref := Ref} = State, Options) + when is_list(Options) -> Opts = [{receive_handle, self()}, {module, ?MODULE} | Options], case (catch megaco_tcp:listen(Ref, Opts)) of ok -> @@ -1008,12 +1029,11 @@ server_listen(#server{transport_ref = Ref} = State, Options) Error end. -server_notify_listening(#server{parent = Parent} = State) - when is_record(State, server) -> +server_notify_listening(#{parent := Parent} = State) -> Parent ! {listening, self()}, {ok, State}. -server_await_continue_signal(#server{parent = Parent} = State, Timeout) -> +server_await_continue_signal(#{parent := Parent} = State, Timeout) -> receive {continue, Parent} -> {ok, State} @@ -1022,11 +1042,11 @@ server_await_continue_signal(#server{parent = Parent} = State, Timeout) -> end. server_await_initial_message(State, InitialMessage, Timeout) - when is_record(State, server) -> + when is_map(State) -> receive {receive_message, {ControlPid, Handle, InitialMessage}} -> - NewState = State#server{control_pid = ControlPid, - handle = Handle}, + NewState = State#{control_pid => ControlPid, + handle => Handle}, {ok, NewState}; Any -> @@ -1037,12 +1057,12 @@ server_await_initial_message(State, InitialMessage, Timeout) {error, timeout} end. -server_send_message(#server{handle = Handle} = State, Message) -> +server_send_message(#{handle := Handle} = State, Message) -> megaco_tcp:send_message(Handle, Message), {ok, State}. server_await_nothing(State, Timeout) - when is_record(State, server) -> + when is_map(State) -> receive Any -> p("received unexpected event: ~p", [Any]), @@ -1052,9 +1072,8 @@ server_await_nothing(State, Timeout) {ok, State} end. - server_await_message(State, ExpectMessage, Timeout) - when is_record(State, server) -> + when is_map(State) -> receive {receive_message, {_, _, ExpectMessage}} -> {ok, State}; @@ -1067,22 +1086,12 @@ server_await_message(State, ExpectMessage, Timeout) {error, timeout} end. -server_disconnect(#server{handle = Handle} = State) +server_disconnect(#{handle := Handle} = State) when (Handle =/= undefined) -> megaco_tcp:close(Handle), - {ok, State#server{handle = undefined}}. - -%% server_block(#server{handle = Handle} = State) -%% when (Handle =/= undefined) -> -%% megaco_tcp:block(Handle), -%% {ok, State}. + {ok, State#{handle => undefined}}. -%% server_unblock(#server{handle = Handle} = State) -%% when (Handle =/= undefined) -> -%% megaco_tcp:unblock(Handle), -%% {ok, State}. - -server_stop_transport(#server{transport_ref = Ref} = State) +server_stop_transport(#{transport_ref := Ref} = State) when (Ref =/= undefined) -> megaco_tcp:stop_transport(Ref), {ok, State}. @@ -1091,28 +1100,28 @@ server_stop_transport(#server{transport_ref = Ref} = State) %% ------- Client command handler and utility functions ---------- client_start_command_handler(Node, Commands) -> - start_command_handler(Node, Commands, #client{}, "client"). + start_command_handler(Node, Commands, #{}, "client"). -client_start_transport(State) when is_record(State, client) -> +client_start_transport(State) when is_map(State) -> case (catch megaco_tcp:start_transport()) of {ok, Ref} -> - {ok, State#client{transport_ref = Ref}}; + {ok, State#{transport_ref => Ref}}; Error -> Error end. -client_connect(#client{transport_ref = Ref} = State, Options) - when is_record(State, client) andalso is_list(Options) -> +client_connect(#{transport_ref := Ref} = State, Options) + when is_list(Options) -> Opts = [{receive_handle, self()}, {module, ?MODULE} | Options], case (catch megaco_tcp:connect(Ref, Opts)) of {ok, Handle, ControlPid} -> - {ok, State#client{control_pid = ControlPid, - handle = Handle}}; + {ok, State#{control_pid => ControlPid, + handle => Handle}}; Error -> Error end. -client_await_continue_signal(#client{parent = Parent} = State, Timeout) -> +client_await_continue_signal(#{parent := Parent} = State, Timeout) -> receive {continue, Parent} -> {ok, State} @@ -1120,12 +1129,12 @@ client_await_continue_signal(#client{parent = Parent} = State, Timeout) -> {error, timeout} end. -client_notify_blocked(#client{parent = Parent} = State) -> +client_notify_blocked(#{parent := Parent} = State) -> Parent ! {blocked, self()}, {ok, State}. client_await_nothing(State, Timeout) - when is_record(State, client) -> + when is_map(State) -> receive Any -> p("received unexpected event: ~p", [Any]), @@ -1134,12 +1143,12 @@ client_await_nothing(State, Timeout) {ok, State} end. -client_send_message(#client{handle = Handle} = State, Message) -> +client_send_message(#{handle := Handle} = State, Message) -> megaco_tcp:send_message(Handle, Message), {ok, State}. client_await_message(State, ExpectMessage, Timeout) - when is_record(State, client) -> + when is_map(State) -> receive {receive_message, {_, _, ExpectMessage}} -> {ok, State}; @@ -1152,22 +1161,22 @@ client_await_message(State, ExpectMessage, Timeout) {error, timeout} end. -client_block(#client{handle = Handle} = State) +client_block(#{handle := Handle} = State) when (Handle =/= undefined) -> megaco_tcp:block(Handle), {ok, State}. -client_unblock(#client{handle = Handle} = State) +client_unblock(#{handle := Handle} = State) when (Handle =/= undefined) -> megaco_tcp:unblock(Handle), {ok, State}. -client_disconnect(#client{handle = Handle} = State) +client_disconnect(#{handle := Handle} = State) when (Handle =/= undefined) -> megaco_tcp:close(Handle), - {ok, State#client{handle = undefined, control_pid = undefined}}. + {ok, State#{handle => undefined, control_pid => undefined}}. -client_stop_transport(#client{transport_ref = Ref} = State) +client_stop_transport(#{transport_ref := Ref} = State) when (Ref =/= undefined) -> megaco_tcp:stop_transport(Ref), {ok, State}. @@ -1176,100 +1185,22 @@ client_stop_transport(#client{transport_ref = Ref} = State) %% -------- Command handler --------- start_command_handler(Node, Commands, State, ShortName) -> - Fun = fun() -> - put(sname, ShortName), - process_flag(trap_exit, true), - Result = (catch command_handler(Commands, State)), - p("command handler terminated with: " - "~n Result: ~p", [Result]), - exit(Result) - end, - erlang:spawn_link(Node, Fun). + ?CH:start(Node, Commands, State, ShortName). -command_handler([], State) -> - p("command_handler -> entry when done with" - "~n State: ~p", [State]), - {ok, State}; -command_handler([#command{id = Id, - desc = Desc, - cmd = Cmd}|Commands], State) -> - p("command_handler -> entry with" - "~n Id: ~p" - "~n Desc: ~p", [Id, Desc]), - case (catch Cmd(State)) of - {ok, NewState} -> - p("command_handler -> cmd ~w ok", [Id]), - command_handler(Commands, NewState); - {error, Reason} -> - p("command_handler -> cmd ~w error: " - "~n Reason: ~p", [Id, Reason]), - {error, {cmd_error, Reason}}; - {'EXIT', Reason} -> - p("command_handler -> cmv ~w exit: " - "~n Reason: ~p", [Id, Reason]), - {error, {cmd_exit, Reason}}; - Error -> - p("command_handler -> cmd ~w failure: " - "~n Error: ~p", [Id, Error]), - {error, {cmd_failure, Error}} - end. +await_command_handler_completion(Pids, Timeout) -> + ?CH:await_completion(Pids, Timeout). -await_command_handler_completion(Pids, Timeout) -> - await_command_handler_completion(Pids, [], [], Timeout). - -await_command_handler_completion([], [], _Good, _Timeout) -> - p("await_command_handler_completion -> entry when done"), - ok; -await_command_handler_completion([], Bad, Good, _Timeout) -> - p("await_command_handler_completion -> entry when done with bad result: " - "~n Bad: ~p" - "~n Good: ~p", [Bad, Good]), - ok; -await_command_handler_completion(Pids, Bad, Good, Timeout) -> - p("await_command_handler_completion -> entry when waiting for" - "~n Pids: ~p" - "~n Bad: ~p" - "~n Good: ~p" - "~n Timeout: ~p", [Pids, Bad, Good, Timeout]), - Begin = ms(), - receive - {'EXIT', Pid, {ok, FinalState}} -> - p("await_command_handler_completion -> " - "received ok EXIT signal from ~p", [Pid]), - case lists:delete(Pid, Pids) of - Pids -> - await_command_handler_completion(Pids, Bad, Good, - Timeout - (ms() - Begin)); - Pids2 -> - p("await_command_handler_completion -> ~p done", [Pid]), - await_command_handler_completion(Pids2, - Bad, - [{Pid, FinalState}|Good], - Timeout - (ms() - Begin)) - end; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - {'EXIT', Pid, {error, Reason}} -> - p("await_command_handler_completion -> " - "received error EXIT signal from ~p", [Pid]), - case lists:delete(Pid, Pids) of - Pids -> - await_command_handler_completion(Pids, Bad, Good, - Timeout - (ms() - Begin)); - Pids2 -> - p("await_command_handler_completion -> ~p done", [Pid]), - await_command_handler_completion(Pids2, - [{Pid, Reason}|Bad], - Good, - Timeout - (ms() - Begin)) - end +try_tc(TCName, Pre, Case, Post) -> + try_tc(TCName, "TEST", ?TEST_VERBOSITY, Pre, Case, Post). - after Timeout -> - p("await_command_handler_completion -> timeout"), - exit({timeout, Pids}) - end. +try_tc(TCName, Name, Verbosity, Pre, Case, Post) -> + ?TRY_TC(TCName, Name, Verbosity, Pre, Case, Post). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% ------- Misc functions -------- @@ -1298,7 +1229,3 @@ p(_S, F, A) -> [?FTS(), self() | A]). -ms() -> - erlang:monotonic_time(milli_seconds). - - diff --git a/lib/megaco/test/megaco_test_command_handler.erl b/lib/megaco/test/megaco_test_command_handler.erl new file mode 100644 index 0000000000..93d6f86e55 --- /dev/null +++ b/lib/megaco/test/megaco_test_command_handler.erl @@ -0,0 +1,193 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2020-2020. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: +%%---------------------------------------------------------------------- +-module(megaco_test_command_handler). + +%%---------------------------------------------------------------------- +%% Include files +%%---------------------------------------------------------------------- +-include("megaco_test_lib.hrl"). + + +%%---------------------------------------------------------------------- +%% External exports +%%---------------------------------------------------------------------- +-export([ + start/4, + await_completion/2, + + print/1, print/2 + ]). + + +%%---------------------------------------------------------------------- +%% Macros +%%---------------------------------------------------------------------- + +%%---------------------------------------------------------------------- +%% Records +%%---------------------------------------------------------------------- + + +%%---------------------------------------------------------------------- +%% Exported functions +%%---------------------------------------------------------------------- + +start(Node, Commands, InitialState, ShortName) + when is_atom(Node) andalso + is_list(Commands) andalso + is_map(InitialState) andalso + is_list(ShortName) -> + Fun = fun() -> + put(sname, ShortName), + process_flag(trap_exit, true), + Result = command_handler(Commands, InitialState), + print("command handler terminated with: " + "~n ~p", [Result]), + exit(Result) + end, + erlang:spawn_link(Node, Fun). + +command_handler([], State) -> + print("command_handler -> entry when done with" + "~n State: ~p", [State]), + {ok, State}; +command_handler([#{id := Id, + desc := Desc, + cmd := Cmd}|Commands], State) + when is_list(Desc) andalso is_function(Cmd, 1) -> + print("command_handler -> [~w] ~s", [Id, Desc]), + try Cmd(State) of + {ok, NewState} -> + print("command_handler -> [~w] cmd ok", [Id]), + command_handler(Commands, NewState); + {skip, _} = SKIP -> + print("command_handler -> [~w] cmd skip (returned)", [Id]), + SKIP; + {error, Reason} -> + print("command_handler -> [~w] cmd error: " + "~n Reason: ~p", [Id, Reason]), + {error, {cmd_error, Reason}} + + catch + throw:{skip, _} = SKIP:_ -> + print("command_handler -> [~w] cmd skip (throw)", [Id]), + SKIP; + exit:{skip, _} = SKIP:_ -> + print("command_handler -> [~w] cmd skip (exit)", [Id]), + SKIP; + C:E:S -> + print("command_handler -> [~w] cmd failure:" + "~n C: ~p" + "~n E: ~p" + "~n S: ~p", [Id, C, E, S]), + {error, {cmd_failure, {C, E, S}}} + end. + + +%% --- Await completion of one or more command handler(s) + +await_completion(Pids, Timeout) -> + await_completion(Pids, [], [], Timeout). + +await_completion([], [], _Good, _Timeout) -> + print("await_completion -> entry when done (success)"), + ok; +await_completion([], Bad, Good, _Timeout) -> + print("await_completion -> entry when done with bad result: " + "~n Bad: ~p" + "~n Good: ~p", [Bad, Good]), + {error, Bad, Good}; +await_completion(Pids, Bad, Good, Timeout) -> + print("await_completion -> entry when waiting for" + "~n Pids: ~p" + "~n Bad: ~p" + "~n Good: ~p" + "~n Timeout: ~p", [Pids, Bad, Good, Timeout]), + Begin = ms(), + receive + {'EXIT', Pid, {ok, FinalState}} -> + case lists:delete(Pid, Pids) of + Pids -> + print("await_completion -> " + "received ok EXIT signal from unknown ~p", [Pid]), + await_completion(Pids, Bad, Good, + Timeout - (ms() - Begin)); + Pids2 -> + print("await_completion -> success from ~p", [Pid]), + await_completion(Pids2, + Bad, + [{Pid, FinalState}|Good], + Timeout - (ms() - Begin)) + end; + + {'EXIT', Pid, {error, Reason}} -> + case lists:delete(Pid, Pids) of + Pids -> + print("await_completion -> " + "received error EXIT signal from unknown ~p", [Pid]), + await_completion(Pids, Bad, Good, + Timeout - (ms() - Begin)); + Pids2 -> + print("await_completion -> failure from ~p", [Pid]), + await_completion(Pids2, + [{Pid, Reason}|Bad], + Good, + Timeout - (ms() - Begin)) + end; + + {'EXIT', Pid, {skip, Reason}} -> + print("await_completion -> skip (exit) from ~p:" + " ~p", [Pid, Reason]), + ?SKIP(Reason) + + after Timeout -> + print("await_completion -> timeout"), + exit({timeout, Pids}) + end. + + + +%% ------- Misc functions -------- + +print(F) -> + print(F, []). + +print(F, A) -> + print(get(sname), F, A). + +print(N, F, A) when is_list(N) -> + io:format("*** [~s] ~p ~s ***" + "~n " ++ F ++ "~n", + [?FTS(), self(), N | A]); +print(_N, F, A) -> + io:format("*** [~s] ~p *** " + "~n " ++ F ++ "~n", + [?FTS(), self() | A]). + + +ms() -> + erlang:monotonic_time(milli_seconds). + + diff --git a/lib/megaco/test/megaco_test_generator.erl b/lib/megaco/test/megaco_test_generator.erl index 8ea7f5ddf7..a7920e7f9e 100644 --- a/lib/megaco/test/megaco_test_generator.erl +++ b/lib/megaco/test/megaco_test_generator.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2019. All Rights Reserved. +%% Copyright Ericsson AB 2007-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -423,7 +423,7 @@ handler_main(Parent, Mod, State, []) -> handler_main(Parent, Mod, State, [Instruction|Instructions]) -> d("handler_main -> entry with" - "~n Instruction: ~p", [Instruction]), + "~n Instruction: ~p", [Instruction]), receive {stop, Parent} -> d("handler_main -> premature stop requested"), @@ -435,6 +435,8 @@ handler_main(Parent, Mod, State, [Instruction|Instructions]) -> Result = (catch Mod:terminate({parent_died, Reason}, State)), exit({parent_died, Reason, Result}) after 0 -> + d("handler_main -> exec: " + "~n Instruction: ~p", [Instruction]), case (catch handler_callback_exec(Mod, State, Instruction)) of {ok, NewState} -> handler_main(Parent, Mod, NewState, Instructions); @@ -535,28 +537,38 @@ debug(F, A) -> debug(get(debug), F, A). debug(true, F, A) -> - print(" DBG", F, A); -debug(_, _F, _A) -> + print(false, " DBG", F, A); +debug(_, _, _) -> ok. +print(Pre, F, A) -> + print(true, Pre, F, A). + + error(F, A) -> - print(" ERROR", F, A). - - -print(P, F, A) -> - print(P, get(name), F, A). - -print([], undefined, F, A) -> - io:format("*** [~s] ~p *** " ++ - "~n " ++ F ++ "~n", - [?FTS(), self() | A]); -print(P, undefined, F, A) -> - io:format("*** [~s] ~p ~s *** " ++ - "~n " ++ F ++ "~n", - [?FTS(), self(), P | A]); -print(P, N, F, A) -> - io:format("*** [~s] ~p ~s~s *** " ++ - "~n " ++ F ++ "~n", - [?FTS(), self(), N, P | A]). + print(true, " ERROR", F, A). + + +print(true = _Verbose, Pre, F, A) -> + FStr = ?F("*** [~s] ~p ~s~s *** " ++ + "~n " ++ F ++ "~n~n", + [?FTS(), self(), string_name(), Pre | A]), + io:format(user, FStr, []), + io:format(standard_io, FStr, []); +print(false = _Verbose, Pre, F, A) -> + FStr = ?F("*** [~s] ~p ~s~s *** " ++ + "~n " ++ F ++ "~n~n", + [?FTS(), self(), string_name(), Pre | A]), + io:format(FStr, []). + +string_name() -> + case get(name) of + N when is_list(N) -> + N; + undefined -> + ""; + N when is_atom(N) -> + atom_to_list(N) + end. diff --git a/lib/megaco/test/megaco_test_generator_lib.erl b/lib/megaco/test/megaco_test_generator_lib.erl index d19728e009..555302d9ea 100644 --- a/lib/megaco/test/megaco_test_generator_lib.erl +++ b/lib/megaco/test/megaco_test_generator_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. +%% Copyright Ericsson AB 2007-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -51,14 +51,24 @@ await_completion(TmrRef, [], OK, ERROR) -> await_completion(TmrRef, Tags, OK, ERROR) -> receive exec_complete_timeout -> + error_msg("Exec complete timeout:" + "~n Tags: ~p" + "~n OK: ~p" + "~n ERROR: ~p", [Tags, OK, ERROR]), {error, {timeout, Tags, OK, ERROR}}; {exec_complete, Tag, ok, Result} -> case lists:delete(Tag, Tags) of Tags -> %% Unknown => ignore + warning_msg("Exec unknown complete with success:" + "~n Tag: ~p" + "~n Result: ~p", [Tag, Result]), await_completion(TmrRef, Tags, OK, ERROR); Tags2 -> + info_msg("Exec complete with success:" + "~n Tag: ~p" + "~n Result: ~p", [Tag, Result]), await_completion(TmrRef, Tags2, [{Tag, Result}|OK], ERROR) end; @@ -66,8 +76,14 @@ await_completion(TmrRef, Tags, OK, ERROR) -> case lists:delete(Tag, Tags) of Tags -> %% Unknown => ignore + warning_msg("Exec unknown complete with failure:" + "~n Tag: ~p" + "~n Reason: ~p", [Tag, Reason]), await_completion(TmrRef, Tags, OK, ERROR); Tags2 -> + error_msg("Exec complete with failure:" + "~n Tag: ~p" + "~n Reason: ~p", [Tag, Reason]), await_completion(TmrRef, Tags2, OK, [{Tag, Reason}|ERROR]) end end. @@ -82,3 +98,14 @@ stop_timer(undefined) -> ok; stop_timer(TmrRef) -> erlang:cancel_timer(TmrRef). + + +info_msg(F, A) -> + error_logger:info_msg(F ++ "~n", A). + +warning_msg(F, A) -> + error_logger:warning_msg(F ++ "~n", A). + +error_msg(F, A) -> + error_logger:error_msg(F ++ "~n", A). + diff --git a/lib/megaco/test/megaco_test_lib.erl b/lib/megaco/test/megaco_test_lib.erl index f45d499199..1e4b7841ee 100644 --- a/lib/megaco/test/megaco_test_lib.erl +++ b/lib/megaco/test/megaco_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2019. All Rights Reserved. +%% Copyright Ericsson AB 1999-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -46,13 +46,18 @@ display_alloc_info/0, display_system_info/1, display_system_info/2, display_system_info/3, + try_tc/6, + prepare_test_case/5, proxy_start/1, proxy_start/2, mk_nodes/1, - start_nodes/3, - start_node/3 + start_nodes/3, start_nodes/4, + start_node/3, start_node/4, + + stop_nodes/3, + stop_node/3 ]). -export([init_per_suite/1, end_per_suite/1, @@ -438,18 +443,6 @@ pprint(F, A) -> init_per_suite(Config) -> - io:format("Host info:" - "~n OS Type: ~p" - "~n OS Version: ~s" - "~n", - [os:type(), - case os:version() of - {Major, Minor, Release} -> - ?F("~w.~w.~w", [Major, Minor, Release]); - Str when is_list(Str) -> - Str - end]), - %% We have some crap machines that causes random test case failures %% for no obvious reason. So, attempt to identify those without actually %% checking for the host name... @@ -485,13 +478,36 @@ init_per_suite(Config) -> %% This version is *not* ok: Skip true end, - COND = [{unix, [{linux, LinuxVersionVerify}, {darwin, DarwinVersionVerify}]}], + %% We are "looking" for a specific machine (a VM) + %% which are *old and crappy" and slow, because it + %% causes a bunch of test cases to fail randomly. + %% But we don not want to test for the host name... + WinVersionVerify = + fun(V) when (V =:= {6,2,9200}) -> + try erlang:system_info(schedulers) of + 2 -> + true; + _ -> + false + catch + _:_:_ -> + true + end; + (_) -> + false + end, + COND = [ + {unix, [{linux, LinuxVersionVerify}, + {darwin, DarwinVersionVerify}]}%% , + %% {win32, [{nt, WinVersionVerify}]} + ], case os_based_skip(COND) of true -> {skip, "Unstable host and/or os (or combo thererof)"}; false -> + Factor = analyze_and_print_host_info(), maybe_start_global_sys_monitor(Config), - Config + [{megaco_factor, Factor} | Config] end. %% We start the global system monitor unless explicitly disabled @@ -547,6 +563,671 @@ end_per_testcase(_Case, Config) -> reset_kill_timer(Config). +%% This function prints various host info, which might be usefull +%% when analyzing the test suite (results). +%% It also returns a "factor" that can be used when deciding +%% the load for some test cases. Such as run time or number of +%% iteraions. This only works for some OSes. +%% +%% We make some calculations on Linux, OpenBSD and FreeBSD. +%% On SunOS we always set the factor to 2 (just to be on the safe side) +%% On all other os:es (mostly windows) we check the number of schedulers, +%% but at least the factor will be 2. +analyze_and_print_host_info() -> + {OsFam, OsName} = os:type(), + Version = + case os:version() of + {Maj, Min, Rel} -> + f("~w.~w.~w", [Maj, Min, Rel]); + VStr -> + VStr + end, + case {OsFam, OsName} of + {unix, linux} -> + analyze_and_print_linux_host_info(Version); + {unix, openbsd} -> + analyze_and_print_openbsd_host_info(Version); + {unix, freebsd} -> + analyze_and_print_freebsd_host_info(Version); + {unix, sunos} -> + analyze_and_print_solaris_host_info(Version); + {win32, nt} -> + analyze_and_print_win_host_info(Version); + _ -> + io:format("OS Family: ~p" + "~n OS Type: ~p" + "~n Version: ~p" + "~n Num Schedulers: ~s" + "~n", [OsFam, OsName, Version, str_num_schedulers()]), + try erlang:system_info(schedulers) of + 1 -> + 10; + 2 -> + 5; + N when (N =< 6) -> + 2; + _ -> + 1 + catch + _:_:_ -> + 10 + end + end. + +str_num_schedulers() -> + try erlang:system_info(schedulers) of + N -> f("~w", [N]) + catch + _:_:_ -> "-" + end. + + +analyze_and_print_linux_host_info(Version) -> + case file:read_file_info("/etc/issue") of + {ok, _} -> + io:format("Linux: ~s" + "~n ~s" + "~n", + [Version, string:trim(os:cmd("cat /etc/issue"))]); + _ -> + io:format("Linux: ~s" + "~n", [Version]) + end, + Factor = + case (catch linux_which_cpuinfo()) of + {ok, {CPU, BogoMIPS}} -> + io:format("CPU: " + "~n Model: ~s" + "~n BogoMIPS: ~s" + "~n Num Schedulers: ~s" + "~n", [CPU, BogoMIPS, str_num_schedulers()]), + %% We first assume its a float, and if not try integer + try list_to_float(string:trim(BogoMIPS)) of + F when F > 4000 -> + 1; + F when F > 1000 -> + 2; + F when F > 500 -> + 3; + _ -> + 5 + catch + _:_:_ -> + try list_to_integer(string:trim(BogoMIPS)) of + I when I > 4000 -> + 1; + I when I > 1000 -> + 2; + I when I > 500 -> + 3; + _ -> + 5 + catch + _:_:_ -> + 5 % Be a "bit" conservative... + end + end; + {ok, CPU} -> + io:format("CPU: " + "~n Model: ~s" + "~n Num Schedulers: ~s" + "~n", [CPU, str_num_schedulers()]), + 2; % Be a "bit" conservative... + _ -> + 5 % Be a "bit" (more) conservative... + end, + %% Check if we need to adjust the factor because of the memory + try linux_which_meminfo() of + AddFactor -> + Factor + AddFactor + catch + _:_:_ -> + Factor + end. + +linux_which_cpuinfo() -> + %% Check for x86 (Intel or AMD) + CPU = + try [string:trim(S) || S <- string:tokens(os:cmd("grep \"model name\" /proc/cpuinfo"), [$:,$\n])] of + ["model name", ModelName | _] -> + ModelName; + _ -> + %% ARM (at least some distros...) + try [string:trim(S) || S <- string:tokens(os:cmd("grep \"Processor\" /proc/cpuinfo"), [$:,$\n])] of + ["Processor", Proc | _] -> + Proc; + _ -> + %% Ok, we give up + throw(noinfo) + catch + _:_:_ -> + throw(noinfo) + end + catch + _:_:_ -> + throw(noinfo) + end, + try [string:trim(S) || S <- string:tokens(os:cmd("grep -i \"bogomips\" /proc/cpuinfo"), [$:,$\n])] of + [_, BMips | _] -> + {ok, {CPU, BMips}}; + _ -> + {ok, CPU} + catch + _:_:_ -> + {ok, CPU} + end. + +%% We *add* the value this return to the Factor. +linux_which_meminfo() -> + try [string:trim(S) || S <- string:tokens(os:cmd("grep MemTotal /proc/meminfo"), [$:])] of + [_, MemTotal] -> + io:format("Memory:" + "~n ~s" + "~n", [MemTotal]), + case string:tokens(MemTotal, [$ ]) of + [MemSzStr, MemUnit] -> + MemSz2 = list_to_integer(MemSzStr), + MemSz3 = + case string:to_lower(MemUnit) of + "kb" -> + MemSz2; + "mb" -> + MemSz2*1024; + "gb" -> + MemSz2*1024*1024; + _ -> + throw(noinfo) + end, + if + (MemSz3 >= 8388608) -> + 0; + (MemSz3 >= 4194304) -> + 1; + (MemSz3 >= 2097152) -> + 3; + true -> + 5 + end; + _X -> + 0 + end; + _ -> + 0 + catch + _:_:_ -> + 0 + end. + + +%% Just to be clear: This is ***not*** scientific... +analyze_and_print_openbsd_host_info(Version) -> + io:format("OpenBSD:" + "~n Version: ~p" + "~n", [Version]), + Extract = + fun(Key) -> + string:tokens(string:trim(os:cmd("sysctl " ++ Key)), [$=]) + end, + try + begin + CPU = + case Extract("hw.model") of + ["hw.model", Model] -> + string:trim(Model); + _ -> + "-" + end, + CPUSpeed = + case Extract("hw.cpuspeed") of + ["hw.cpuspeed", Speed] -> + list_to_integer(Speed); + _ -> + -1 + end, + NCPU = + case Extract("hw.ncpufound") of + ["hw.ncpufound", N] -> + list_to_integer(N); + _ -> + -1 + end, + Memory = + case Extract("hw.physmem") of + ["hw.physmem", PhysMem] -> + list_to_integer(PhysMem) div 1024; + _ -> + -1 + end, + io:format("CPU:" + "~n Model: ~s" + "~n Speed: ~w" + "~n N: ~w" + "~nMemory:" + "~n ~w KB" + "~n", [CPU, CPUSpeed, NCPU, Memory]), + CPUFactor = + if + (CPUSpeed =:= -1) -> + 1; + (CPUSpeed >= 2000) -> + if + (NCPU >= 4) -> + 1; + (NCPU >= 2) -> + 2; + true -> + 3 + end; + true -> + if + (NCPU >= 4) -> + 2; + (NCPU >= 2) -> + 3; + true -> + 4 + end + end, + MemAddFactor = + if + (Memory =:= -1) -> + 0; + (Memory >= 8388608) -> + 0; + (Memory >= 4194304) -> + 1; + (Memory >= 2097152) -> + 2; + true -> + 3 + end, + CPUFactor + MemAddFactor + end + catch + _:_:_ -> + 1 + end. + + +analyze_and_print_freebsd_host_info(Version) -> + io:format("FreeBSD:" + "~n Version: ~p" + "~n", [Version]), + %% This test require that the program 'sysctl' is in the path. + %% First test with 'which sysctl', if that does not work + %% try with 'which /sbin/sysctl'. If that does not work either, + %% we skip the test... + try + begin + SysCtl = + case string:trim(os:cmd("which sysctl")) of + [] -> + case string:trim(os:cmd("which /sbin/sysctl")) of + [] -> + throw(sysctl); + SC2 -> + SC2 + end; + SC1 -> + SC1 + end, + Extract = + fun(Key) -> + string:tokens(string:trim(os:cmd(SysCtl ++ " " ++ Key)), + [$:]) + end, + CPU = analyze_freebsd_cpu(Extract), + CPUSpeed = analyze_freebsd_cpu_speed(Extract), + NCPU = analyze_freebsd_ncpu(Extract), + Memory = analyze_freebsd_memory(Extract), + io:format("CPU:" + "~n Model: ~s" + "~n Speed: ~w" + "~n N: ~w" + "~n Num Schedulers: ~w" + "~nMemory:" + "~n ~w KB" + "~n", + [CPU, CPUSpeed, NCPU, + erlang:system_info(schedulers), Memory]), + CPUFactor = + if + (CPUSpeed =:= -1) -> + 1; + (CPUSpeed >= 2000) -> + if + (NCPU >= 4) -> + 1; + (NCPU >= 2) -> + 2; + true -> + 3 + end; + true -> + if + (NCPU =:= -1) -> + 1; + (NCPU >= 4) -> + 2; + (NCPU >= 2) -> + 3; + true -> + 4 + end + end, + MemAddFactor = + if + (Memory =:= -1) -> + 0; + (Memory >= 8388608) -> + 0; + (Memory >= 4194304) -> + 1; + (Memory >= 2097152) -> + 2; + true -> + 3 + end, + CPUFactor + MemAddFactor + end + catch + _:_:_ -> + io:format("CPU:" + "~n Num Schedulers: ~w" + "~n", [erlang:system_info(schedulers)]), + case erlang:system_info(schedulers) of + 1 -> + 10; + 2 -> + 5; + _ -> + 2 + end + end. + +analyze_freebsd_cpu(Extract) -> + analyze_freebsd_item(Extract, "hw.model", fun(X) -> X end, "-"). + +analyze_freebsd_cpu_speed(Extract) -> + analyze_freebsd_item(Extract, + "hw.clockrate", + fun(X) -> list_to_integer(X) end, + -1). + +analyze_freebsd_ncpu(Extract) -> + analyze_freebsd_item(Extract, + "hw.ncpu", + fun(X) -> list_to_integer(X) end, + -1). + +analyze_freebsd_memory(Extract) -> + analyze_freebsd_item(Extract, + "hw.physmem", + fun(X) -> list_to_integer(X) div 1024 end, + -1). + +analyze_freebsd_item(Extract, Key, Process, Default) -> + try + begin + case Extract(Key) of + [Key, Model] -> + Process(string:trim(Model)); + _ -> + Default + end + end + catch + _:_:_ -> + Default + end. + + +analyze_and_print_solaris_host_info(Version) -> + Release = + case file:read_file_info("/etc/release") of + {ok, _} -> + case [string:trim(S) || S <- string:tokens(os:cmd("cat /etc/release"), [$\n])] of + [Rel | _] -> + Rel; + _ -> + "-" + end; + _ -> + "-" + end, + %% Display the firmware device tree root properties (prtconf -b) + Props = [list_to_tuple([string:trim(PS) || PS <- Prop]) || + Prop <- [string:tokens(S, [$:]) || + S <- string:tokens(os:cmd("prtconf -b"), [$\n])]], + BannerName = case lists:keysearch("banner-name", 1, Props) of + {value, {_, BN}} -> + string:trim(BN); + _ -> + "-" + end, + InstructionSet = + case string:trim(os:cmd("isainfo -k")) of + "Pseudo-terminal will not" ++ _ -> + "-"; + IS -> + IS + end, + PtrConf = [list_to_tuple([string:trim(S) || S <- Items]) || Items <- [string:tokens(S, [$:]) || S <- string:tokens(os:cmd("prtconf"), [$\n])], length(Items) > 1], + SysConf = + case lists:keysearch("System Configuration", 1, PtrConf) of + {value, {_, SC}} -> + SC; + _ -> + "-" + end, + NumPhysProc = + begin + NPPStr = string:trim(os:cmd("psrinfo -p")), + try list_to_integer(NPPStr) of + _ -> + NPPStr + catch + _:_:_ -> + "-" + end + end, + NumProc = try integer_to_list(length(string:tokens(os:cmd("psrinfo"), [$\n]))) of + NPStr -> + NPStr + catch + _:_:_ -> + "-" + end, + MemSz = + case lists:keysearch("Memory size", 1, PtrConf) of + {value, {_, MS}} -> + MS; + _ -> + "-" + end, + io:format("Solaris: ~s" + "~n Release: ~s" + "~n Banner Name: ~s" + "~n Instruction Set: ~s" + "~n CPUs: ~s (~s)" + "~n System Config: ~s" + "~n Memory Size: ~s" + "~n Num Schedulers: ~s" + "~n~n", [Version, Release, BannerName, InstructionSet, + NumPhysProc, NumProc, + SysConf, MemSz, + str_num_schedulers()]), + MemFactor = + try string:tokens(MemSz, [$ ]) of + [SzStr, "Mega" ++ _] -> + try list_to_integer(SzStr) of + Sz when Sz > 8192 -> + 0; + Sz when Sz > 4096 -> + 1; + Sz when Sz > 2048 -> + 2; + _ -> + 5 + catch + _:_:_ -> + 10 + end; + [SzStr, "Giga" ++ _] -> + try list_to_integer(SzStr) of + Sz when Sz > 8 -> + 0; + Sz when Sz > 4 -> + 1; + Sz when Sz > 2 -> + 2; + _ -> + 5 + catch + _:_:_ -> + 10 + end; + _ -> + 10 + catch + _:_:_ -> + 10 + end, + try erlang:system_info(schedulers) of + 1 -> + 10; + 2 -> + 5; + N when (N =< 6) -> + 2; + _ -> + 1 + catch + _:_:_ -> + 10 + end + MemFactor. + + +analyze_and_print_win_host_info(Version) -> + SysInfo = which_win_system_info(), + OsName = win_sys_info_lookup(os_name, SysInfo), + OsVersion = win_sys_info_lookup(os_version, SysInfo), + SysMan = win_sys_info_lookup(system_manufacturer, SysInfo), + NumProcs = win_sys_info_lookup(num_processors, SysInfo), + TotPhysMem = win_sys_info_lookup(total_phys_memory, SysInfo), + io:format("Windows: ~s" + "~n OS Version: ~s (~p)" + "~n System Manufacturer: ~s" + "~n Number of Processor(s): ~s" + "~n Total Physical Memory: ~s" + "~n", [OsName, OsVersion, Version, SysMan, NumProcs, TotPhysMem]), + MemFactor = + try + begin + [MStr, MUnit|_] = + string:tokens(lists:delete($,, TotPhysMem), [$\ ]), + case string:to_lower(MUnit) of + "gb" -> + try list_to_integer(MStr) of + M when M > 8 -> + 0; + M when M > 4 -> + 1; + M when M > 2 -> + 2; + _ -> + 5 + catch + _:_:_ -> + 10 + end; + "mb" -> + try list_to_integer(MStr) of + M when M > 8192 -> + 0; + M when M > 4096 -> + 1; + M when M > 2048 -> + 2; + _ -> + 5 + catch + _:_:_ -> + 10 + end; + _ -> + 10 + end + end + catch + _:_:_ -> + 10 + end, + CPUFactor = + case erlang:system_info(schedulers) of + 1 -> + 10; + 2 -> + 5; + _ -> + 2 + end, + CPUFactor + MemFactor. + +win_sys_info_lookup(Key, SysInfo) -> + win_sys_info_lookup(Key, SysInfo, "-"). + +win_sys_info_lookup(Key, SysInfo, Def) -> + case lists:keysearch(Key, 1, SysInfo) of + {value, {Key, Value}} -> + Value; + false -> + Def + end. + +%% This function only extracts the prop we actually care about! +which_win_system_info() -> + SysInfo = os:cmd("systeminfo"), + try process_win_system_info(string:tokens(SysInfo, [$\r, $\n]), []) + catch + _:_:_ -> + io:format("Failed process System info: " + "~s~n", [SysInfo]), + [] + end. + +process_win_system_info([], Acc) -> + Acc; +process_win_system_info([H|T], Acc) -> + case string:tokens(H, [$:]) of + [Key, Value] -> + case string:to_lower(Key) of + "os name" -> + process_win_system_info(T, + [{os_name, string:trim(Value)}|Acc]); + "os version" -> + process_win_system_info(T, + [{os_version, string:trim(Value)}|Acc]); + "system manufacturer" -> + process_win_system_info(T, + [{system_manufacturer, string:trim(Value)}|Acc]); + "processor(s)" -> + [NumProcStr|_] = string:tokens(Value, [$\ ]), + T2 = lists:nthtail(list_to_integer(NumProcStr), T), + process_win_system_info(T2, + [{num_processors, NumProcStr}|Acc]); + "total physical memory" -> + process_win_system_info(T, + [{total_phys_memory, string:trim(Value)}|Acc]); + _ -> + process_win_system_info(T, Acc) + end; + _ -> + process_win_system_info(T, Acc) + end. + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Set kill timer @@ -578,6 +1259,71 @@ reset_kill_timer(Config) -> end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +try_tc(TCName, Name, Verbosity, Pre, Case, Post) + when is_function(Pre, 0) andalso + is_function(Case, 1) andalso + is_function(Post, 1) -> + process_flag(trap_exit, true), + put(verbosity, Verbosity), + put(sname, Name), + put(tc, TCName), + p("try_tc -> starting: try pre"), + try Pre() of + State -> + p("try_tc -> pre done: try test case"), + try Case(State) of + Res -> + p("try_tc -> test case done: try post"), + (catch Post(State)), + p("try_tc -> done"), + Res + catch + throw:{skip, _} = SKIP:_ -> + p("try_tc -> test case (throw) skip: try post"), + (catch Post(State)), + p("try_tc -> test case (throw) skip: done"), + SKIP; + exit:{skip, _} = SKIP:_ -> + p("try_tc -> test case (exit) skip: try post"), + (catch Post(State)), + p("try_tc -> test case (exit) skip: done"), + SKIP; + C:E:S -> + p("try_tc -> test case failed: try post"), + (catch Post(State)), + case megaco_test_global_sys_monitor:events() of + [] -> + p("try_tc -> test case failed: done"), + exit({case_catched, C, E, S}); + SysEvs -> + p("try_tc -> test case failed with system event(s): " + "~n ~p", [SysEvs]), + {skip, "TC failure with system events"} + end + end + catch + throw:{skip, _} = SKIP:_ -> + p("try_tc -> pre (throw) skip"), + SKIP; + exit:{skip, _} = SKIP:_ -> + p("try_tc -> pre (exit) skip"), + SKIP; + C:E:S -> + case megaco_test_global_sys_monitor:events() of + [] -> + p("try_tc -> pre failed: done"), + exit({pre_catched, C, E, S}); + SysEvs -> + p("try_tc -> pre failed with system event(s): " + "~n ~p", [SysEvs]), + {skip, "TC pre failure with system events"} + end + end. + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% prepare_test_case(Actions, N, Config, File, Line) -> @@ -637,14 +1383,59 @@ node_to_name_and_host(Node) -> start_nodes(Nodes, File, Line) when is_list(Nodes) -> - lists:foreach(fun(N) -> start_node(N, File, Line) end, Nodes). + start_nodes(Nodes, false, File, Line). + +start_nodes(Nodes, Force, File, Line) + when is_list(Nodes) andalso is_boolean(Force) -> + start_nodes(Nodes, Force, File, Line, []). + +start_nodes([], _Force, _File, _Line, _Started) -> + ok; +start_nodes([Node|Nodes], Force, File, Line, Started) -> + try start_node(Node, Force, true, File, Line) of + ok -> + start_nodes(Nodes, Force, File, Line, [Node|Started]) + catch + exit:{skip, _} = SKIP:_ -> + (catch stop_nodes(lists:reverse(Started), File, Line)), + exit(SKIP); + C:E:S -> + (catch stop_nodes(lists:reverse(Started), File, Line)), + erlang:raise(C, E, S) + end. start_node(Node, File, Line) -> + start_node(Node, false, false, File, Line). + +start_node(Node, Force, File, Line) + when is_atom(Node) andalso is_boolean(Force) -> + start_node(Node, Force, false, File, Line). + +start_node(Node, Force, Retry, File, Line) -> case net_adm:ping(Node) of - pong -> + %% Do not require a *new* node + pong when (Force =:= false) -> p("node ~p already running", [Node]), ok; - pang -> + + %% Do require a *new* node, so kill this one and try again + pong when ((Force =:= true) andalso (Retry =:= true)) -> + e("node ~p already running - kill and retry", [Node]), + case stop_node(Node) of + ok -> + start_node(Node, Force, false, File, Line); + error -> + e("node ~p already running - failed kill (no retry)", [Node]), + fatal_skip({node_already_running, Node}, File, Line) + end; + + %% Do require a *new* node, but no retry so give up and fail + pong when (Force =:= true) -> + e("node ~p already running", [Node]), + fatal_skip({node_already_running, Node}, File, Line); + + % Not (yet) running + pang -> [Name, Host] = node_to_name_and_host(Node), Pa = filename:dirname(code:which(?MODULE)), Args = " -pa " ++ Pa ++ @@ -662,17 +1453,67 @@ start_node(Node, File, Line) -> {_, []} = rpc:multicall(global, sync, []), ok; Other -> - p("failed starting node ~p: ~p", [Node, Other]), + e("failed starting node ~p: ~p", [Node, Other]), fatal_skip({cannot_start_node, Node, Other}, File, Line) end end. + + +stop_nodes(Nodes, File, Line) when is_list(Nodes) -> + stop_nodes(Nodes, [], File, Line). + +stop_nodes([], [], _File, _Line) -> + ok; +stop_nodes([], StillRunning, File, Line) -> + e("Failed stopping nodes: " + "~n ~p", [StillRunning]), + fatal_skip({failed_stop_nodes, lists:reverse(StillRunning)}, File, Line); +stop_nodes([Node|Nodes], Acc, File, Line) -> + case stop_node(Node) of + ok -> + stop_nodes(Nodes, Acc, File, Line); + error -> + stop_nodes(Nodes, [Node|Acc], File, Line) + end. +stop_node(Node, File, Line) when is_atom(Node) -> + p("try stop node ~p", [Node]), + case stop_node(Node) of + ok -> + ok; + error -> + fatal_skip({failed_stop_node, Node}, File, Line) + end. + +stop_node(Node) -> + p("try stop node ~p", [Node]), + erlang:monitor_node(Node, true), + rpc:call(Node, erlang, halt, []), + receive + {nodedown, Node} -> + ok + after 10000 -> + e("failed stop node ~p", [Node]), + error + end. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% f(F, A) -> lists:flatten(io_lib:format(F, A)). +e(F, A) -> + print("ERROR", F, A). + +p(F) -> + p(F, []). + p(F, A) -> - io:format("~s ~p " ++ F ++ "~n", [?FTS(), self() | A]). + print("INFO", F, A). + +print(Pre, F, A) -> + io:format("*** [~s] [~s] ~p " ++ F ++ "~n", [?FTS(), Pre, self() | A]). + diff --git a/lib/megaco/test/megaco_test_lib.hrl b/lib/megaco/test/megaco_test_lib.hrl index 4c49f89a8e..2ff887178e 100644 --- a/lib/megaco/test/megaco_test_lib.hrl +++ b/lib/megaco/test/megaco_test_lib.hrl @@ -80,12 +80,19 @@ -define(MULTI_RECEIVE(Expected), ?VERIFY(lists:sort(Expected), lists:sort(?LIB:flush()))). +-define(TRY_TC(TCN, N, V, PRE, CASE, POST), + ?LIB:try_tc(TCN, N, V, PRE, CASE, POST)). + -define(ACQUIRE_NODES(N, Config), ?LIB:prepare_test_case([init, {stop_app, megaco}], N, Config, ?FILE, ?LINE)). --define(START_NODE(Node), ?LIB:start_node(Node, ?FILE, ?LINE)). --define(START_NODES(Nodes), ?LIB:start_nodes(Nodes, ?FILE, ?LINE)). +-define(START_NODE(Node, Force), ?LIB:start_node(Node, Force, ?FILE, ?LINE)). +-define(START_NODE(Node), ?START_NODE(Node, false)). +-define(START_NODES(Nodes, Force), ?LIB:start_nodes(Nodes, Force, ?FILE, ?LINE)). +-define(START_NODES(Nodes), ?START_NODES(Nodes, false)). +-define(STOP_NODE(Node), ?LIB:stop_node(Node, ?FILE, ?LINE)). +-define(STOP_NODES(Nodes), ?LIB:stop_nodes(Nodes, ?FILE, ?LINE)). -define(SLEEP(MSEC), ?LIB:sleep(MSEC)). -define(HOURS(T), ?LIB:hours(T)). diff --git a/lib/megaco/test/megaco_test_megaco_generator.erl b/lib/megaco/test/megaco_test_megaco_generator.erl index 4e0f2e334c..4eedd8d731 100644 --- a/lib/megaco/test/megaco_test_megaco_generator.erl +++ b/lib/megaco/test/megaco_test_megaco_generator.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2019. All Rights Reserved. +%% Copyright Ericsson AB 2007-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -370,12 +370,15 @@ handle_exec({debug, Debug}, State) -> {ok, State}; handle_exec({expect_nothing, To}, State) -> - p("expect_nothing: ~p", [To]), + p("expect nothing: ~p", [To]), receive Any -> + e("received unexpected: " + "~n ~p", [Any]), error({expect_nothing, Any}) after To -> - {ok, State} + p("go nothing (~p) as expected", [To]), + {ok, State} end; handle_exec({megaco_trace, disable}, State) -> @@ -388,17 +391,17 @@ handle_exec({megaco_trace, Level}, State) -> {ok, State}; handle_exec(megaco_start, State) -> - p("megaco_start"), + p("start megaco"), ok = megaco:start(), {ok, State}; handle_exec(megaco_stop, State) -> - p("megaco_stop"), + p("stop megaco"), ok = megaco:stop(), {ok, State}; handle_exec({megaco_start_user, Mid, RecvInfo, Conf}, State) -> - p("megaco_start_user: ~p", [Mid]), + p("start megaco user: ~p", [Mid]), d("megaco_start_user -> start user"), ok = megaco:start_user(Mid, Conf), @@ -424,16 +427,17 @@ handle_exec({megaco_start_user, Mid, RecvInfo, Conf}, State) -> handle_exec(megaco_stop_user, #state{mid = Mid} = State) when Mid /= undefined -> + p("stop megaco user: ~p", [Mid]), megaco_cleanup(State), ok = megaco:stop_user(Mid), {ok, State#state{mid = undefined}}; -handle_exec(start_transport, #state{recv_handle = RH} = State) -> - p("start_transport"), - #megaco_receive_handle{send_mod = TM} = RH, +handle_exec(start_transport, + #state{recv_handle = #megaco_receive_handle{send_mod = TM}} = State) -> + p("start transport ~p", [TM]), case (catch TM:start_transport()) of {ok, Sup} -> - d("start_transport -> Sup: ~p", [Sup]), + d("transport started: Sup: ~p", [Sup]), {ok, State#state{transport_sup = Sup}}; {error, Reason} -> e("failed starting transport (~w): " @@ -448,7 +452,7 @@ handle_exec(start_transport, #state{recv_handle = RH} = State) -> handle_exec({listen, Opts0, MaybeRetry}, #state{recv_handle = RH, port = Port, transport_sup = Pid} = State) when RH#megaco_receive_handle.send_mod =:= megaco_tcp -> - p("listen(tcp)", []), + p("listen(tcp)"), Opts = [{module, ?DELIVER_MOD}, {port, Port}, {receive_handle, RH}, @@ -471,7 +475,7 @@ handle_exec({listen, Opts0, _MaybeRetry}, error({udp_open, Opts0, Else}) end; handle_exec({listen, Opts0, _MaybeRetry}, - #state{recv_handle = RH, port = Port, transport_sup = Pid} = State) + #state{recv_handle = RH, port = Port, transport_sup = Pid} = State) when RH#megaco_receive_handle.send_mod =:= megaco_test_generic_transport -> p("listen(generic)"), Opts = [{module, ?DELIVER_MOD}, {port, Port}, {receive_handle, RH}|Opts0], @@ -487,7 +491,7 @@ handle_exec({connect, Host, Opts0, MaybeRetry}, recv_handle = RH, port = Port} = State) when RH#megaco_receive_handle.send_mod =:= megaco_tcp -> - p("connect[megaco_tcp] to ~p:~p", [Host, Port]), + p("connect(tcp) to ~p:~p", [Host, Port]), PrelMid = preliminary_mid, Opts = [{host, Host}, {port, Port}, @@ -495,7 +499,7 @@ handle_exec({connect, Host, Opts0, MaybeRetry}, {tcp_options, [{nodelay, true}]} | Opts0], case (catch handle_exec_connect_tcp(Host, Opts, Sup, MaybeRetry)) of {ok, SH, ControlPid} -> - d("tcp connected: ~p, ~p", [SH, ControlPid]), + d("connected(tcp): ~p, ~p", [SH, ControlPid]), megaco_connector_start(RH, PrelMid, SH, ControlPid), {ok, State#state{send_handle = SH, ctrl_pid = ControlPid}}; @@ -508,13 +512,13 @@ handle_exec({connect, Host, Opts0, _MaybeRetry}, recv_handle = RH, port = Port} = State) when RH#megaco_receive_handle.send_mod =:= megaco_udp -> - p("connect[megaco_udp] to ~p", [Host]), + p("connect(udp) to ~p", [Host]), PrelMid = preliminary_mid, Opts = [{port, 0}, {receive_handle, RH}|Opts0], d("udp open", []), case (catch megaco_udp:open(Sup, Opts)) of {ok, Handle, ControlPid} -> - d("udp opened: ~p, ~p", [Handle, ControlPid]), + d("opened(udp): ~p, ~p", [Handle, ControlPid]), SH = megaco_udp:create_send_handle(Handle, Host, Port), megaco_connector_start(RH, PrelMid, SH, ControlPid), {ok, State#state{send_handle = SH, @@ -528,12 +532,12 @@ handle_exec({connect, Host, Opts0, _MaybeRetry}, recv_handle = RH, port = Port} = State) when RH#megaco_receive_handle.send_mod =:= megaco_test_generic_transport -> - p("connect[megaco_test_generic_transport] to ~p", [Host]), + p("connect(generic) to ~p", [Host]), PrelMid = preliminary_mid, Opts = [{host, Host}, {port, Port}, {receive_handle, RH}|Opts0], case (catch megaco_test_generic_transport:connect(Sup, Opts)) of {ok, SH, ControlPid} -> - d("generic connected: ~p, ~p", [SH, ControlPid]), + d("connected(generic): ~p, ~p", [SH, ControlPid]), megaco_connector_start(RH, PrelMid, SH, ControlPid), {ok, State#state{send_handle = SH, ctrl_pid = ControlPid}}; @@ -542,13 +546,13 @@ handle_exec({connect, Host, Opts0, _MaybeRetry}, end; handle_exec(megaco_connect, State) -> - p("megaco_connect"), + p("expect megaco_connect"), receive {megaco_connect_result, {ok, CH}} -> - p("megaco connect succeeded: ~p", [CH]), + p("received successful megaco_connect: ~p", [CH]), {ok, State#state{conn_handle = CH}}; {megaco_connect_result, Error} -> - p("megaco connect failed: ~p", [Error]), + p("received failed megaco_connect: ~p", [Error]), #state{result = Res} = State, {ok, State#state{result = [Error|Res]}} end; @@ -557,27 +561,27 @@ handle_exec({megaco_connect, Mid}, #state{recv_handle = RH, send_handle = SH, ctrl_pid = ControlPid} = State) -> - p("megaco_connect: ~p", [Mid]), + p("megaco connect: ~p", [Mid]), megaco_connector_start(RH, Mid, SH, ControlPid), {ok, State}; handle_exec({megaco_user_info, Tag}, #state{mid = Mid, result = Res} = State) when Mid /= undefined -> - p("megaco_user_info: ~w", [Tag]), + p("megaco user-info: ~w", [Tag]), Val = (catch megaco:user_info(Mid, Tag)), d("megaco_user_info: ~p", [Val]), {ok, State#state{result = [Val|Res]}}; handle_exec({megaco_update_user_info, Tag, Val}, #state{mid = Mid} = State) when Mid /= undefined -> - p("megaco_update_user_info: ~w -> ~p", [Tag, Val]), + p("update megaco user-info: ~w -> ~p", [Tag, Val]), ok = megaco:update_user_info(Mid, Tag, Val), {ok, State}; handle_exec({megaco_conn_info, Tag}, #state{conn_handle = CH, result = Res} = State) when CH /= undefined -> - p("megaco_conn_info: ~w", [Tag]), + p("megaco conn-info: ~w", [Tag]), Val = (catch megaco:conn_info(CH, Tag)), d("megaco_conn_info: ~p", [Val]), {ok, State#state{result = [Val|Res]}}; @@ -585,7 +589,7 @@ handle_exec({megaco_conn_info, Tag}, handle_exec({megaco_update_conn_info, Tag, Val}, #state{conn_handle = CH} = State) when CH /= undefined -> - p("megaco_update_conn_info: ~w -> ~p", [Tag, Val]), + p("update megaco conn-info: ~w -> ~p", [Tag, Val]), case megaco:update_conn_info(CH, Tag, Val) of ok -> {ok, State}; @@ -594,15 +598,15 @@ handle_exec({megaco_update_conn_info, Tag, Val}, end; handle_exec(megaco_info, #state{result = Res} = State) -> - p("megaco_info", []), + p("megaco info", []), Val = (catch megaco:info()), d("megaco_info: ~p", [Val]), {ok, State#state{result = [Val|Res]}}; handle_exec({megaco_system_info, Tag, Verify}, #state{result = Res} = State) -> - p("megaco_system_info: ~w", [Tag]), + p("megaco system-info: ~w", [Tag]), Val = (catch megaco:system_info(Tag)), - d("megaco_system_info: ~p", [Val]), + d("megaco system-info: ~p", [Val]), case Verify(Val) of ok -> {ok, State#state{result = [Val|Res]}}; @@ -613,7 +617,10 @@ handle_exec({megaco_system_info, Tag, Verify}, #state{result = Res} = State) -> %% This is either a MG or a MGC which is only connected to one MG handle_exec({megaco_call, ARs, Opts}, #state{conn_handle = CH} = State) when CH /= undefined -> - p("megaco_call"), + p("megaco_call: " + "~n CH: ~p" + "~n ARs: ~p" + "~n Opts: ~p", [CH, ARs, Opts]), {_PV, UserReply} = megaco:call(CH, ARs, Opts), d("megaco_call -> UserReply: ~n~p", [UserReply]), {ok, State}; @@ -624,6 +631,10 @@ handle_exec({megaco_call, RemoteMid, ARs, Opts}, #state{mid = Mid} = State) -> Conns = megaco:user_info(Mid, connections), {value, {_, CH}} = lists:keysearch(RemoteMid, #megaco_conn_handle.remote_mid, Conns), + p("megaco_call: " + "~n CH: ~p" + "~n ARs: ~p" + "~n Opts: ~p", [CH, ARs, Opts]), {_PV, UserReply} = megaco:call(CH, ARs, Opts), d("megaco_call -> UserReply: ~n~p", [UserReply]), {ok, State}; @@ -631,56 +642,65 @@ handle_exec({megaco_call, RemoteMid, ARs, Opts}, #state{mid = Mid} = State) -> %% This is either a MG or a MGC which is only connected to one MG handle_exec({megaco_cast, ARs, Opts}, #state{conn_handle = CH} = State) when CH =/= undefined -> - p("megaco_cast"), + p("megaco_cast: " + "~n CH: ~p" + "~n ARs: ~p", [CH, ARs]), case megaco:cast(CH, ARs, Opts) of ok -> {ok, State}; Error -> - d("failed sending (cast) message: ~n~p", [Error]), + e("failed sending (cast) message: ~n~p", [Error]), #state{result = Acc} = State, {error, State#state{result = [Error|Acc]}} end; handle_exec({megaco_cast, RemoteMid, ARs, Opts}, #state{mid = Mid} = State) -> - p("megaco_cast: ~p", [RemoteMid]), + p("megaco_cast with ~p", [RemoteMid]), %% First we have to find the CH for this Mid Conns = megaco:user_info(Mid, connections), {value, {_, CH}} = lists:keysearch(RemoteMid, #megaco_conn_handle.remote_mid, Conns), + p("megaco_cast: " + "~n CH: ~p" + "~n ARs: ~p" + "~n Opts: ~p", [CH, ARs, Opts]), case megaco:cast(CH, ARs, Opts) of ok -> {ok, State}; Error -> - d("failed sending (cast) message: ~n~p", [Error]), + e("failed sending (cast) message: " + "~n ~p", [Error]), #state{result = Acc} = State, {error, State#state{result = [Error|Acc]}} end; %% Nothing shall happen for atleast Timeout time handle_exec({megaco_callback, nocall, Timeout}, State) -> - p("megaco_callback [~w,~w]", [nocall, Timeout]), + p("expect no megaco_callback for ~w", [Timeout]), receive {handle_megaco_callback, Type, Msg, Pid} -> - d("received unexpected megaco callback: ~n~p", [Msg]), + e("received unexpected megaco callback: ~n~p", [Msg]), #state{result = Res} = State, Err = {unexpected_callback, Type, Msg, Pid}, {error, State#state{result = [Err|Res]}} after Timeout -> + p("got no callback (~p) as expected", [Timeout]), {ok, State} end; handle_exec({megaco_callback, Tag, Verify}, State) when is_function(Verify) -> - p("megaco_callback [~w]", [Tag]), + p("expect megaco_callback ~w", [Tag]), receive {handle_megaco_callback, Type, Msg, Pid} -> - d("received megaco callback: ~n~p", [Msg]), + d("received megaco callback:" + "~n ~p", [Msg]), case Verify(Msg) of {VRes, Res, Reply} -> - d("megaco_callback [~w] ~w",[Tag, VRes]), + d("megaco_callback [~w] ~w", [Tag, VRes]), handle_megaco_callback_reply(Pid, Type, Reply), validate(VRes, Tag, Res, State); {VRes, Delay, Res, Reply} -> - d("megaco_callback [~w] ~w, ~w",[Tag,Delay,VRes]), + d("megaco_callback [~w] ~w, ~w", [Tag,Delay,VRes]), handle_megaco_callback_reply(Pid, Type, Delay, Reply), validate(VRes, Tag, Res, State) end @@ -688,7 +708,7 @@ handle_exec({megaco_callback, Tag, Verify}, State) when is_function(Verify) -> handle_exec({megaco_callback, Tag, {VMod, VFunc, VArgs}}, State) when is_atom(VMod) andalso is_atom(VFunc) andalso is_list(VArgs) -> - p("megaco_callback [~w]", [Tag]), + p("expect megaco_callback ~w", [Tag]), receive {handle_megaco_callback, Type, Msg, Pid} -> d("received megaco callback: ~n~p" @@ -710,7 +730,7 @@ handle_exec({megaco_callback, Tag, {VMod, VFunc, VArgs}}, State) handle_exec({megaco_callback, Tag, Verify, Timeout}, State) when (is_function(Verify) andalso (is_integer(Timeout) andalso (Timeout > 0))) -> - p("megaco_callback [~w]", [Tag]), + p("expect megaco_callback ~w (with ~w)", [Tag, Timeout]), receive {handle_megaco_callback, Type, Msg, Pid} -> d("received megaco callback: ~n~p", [Msg]), @@ -725,22 +745,23 @@ handle_exec({megaco_callback, Tag, Verify, Timeout}, State) validate(VRes, Tag, Res, State) end after Timeout -> + e("megaco_callback ~w timeout", [Tag]), #state{result = Res} = State, Err = {callback_timeout, Tag, Timeout}, {error, State#state{result = [Err|Res]}} end; handle_exec({megaco_callback, Verifiers}, State) -> - p("megaco_callback"), + p("expect megaco_callback(s)"), megaco_callback_verify(Verifiers, State); handle_exec({megaco_cancel, Reason}, #state{conn_handle = CH} = State) -> - p("megaco_cancel [~w]", [Reason]), + p("megaco_cancel: ~w", [Reason]), case megaco:cancel(CH, Reason) of ok -> {ok, State}; Error -> - d("failed cancel: ~n~p", [Error]), + e("failed cancel: ~n~p", [Error]), #state{result = Acc} = State, {error, State#state{result = [Error|Acc]}} end; @@ -1050,32 +1071,37 @@ handle_trans_request_abort(RH, PV, TransNo, Pid, Extra, P) -> handle_megaco_callback_cast(P, Msg, Reply). handle_megaco_callback_cast(P, Msg, Reply) -> - p("handle_megaco_callback_cast -> entry with Msg: ~n~p", [Msg]), + d("handle_megaco_callback_cast -> entry with Msg: ~n~p", [Msg]), P ! {handle_megaco_callback, cast, Msg, self()}, Reply. handle_megaco_callback_call(P, Msg) -> - p("handle_megaco_callback_call -> entry with" + d("handle_megaco_callback_call -> entry with" "~n P: ~p" "~n Msg: ~p", [P, Msg]), P ! {handle_megaco_callback, call, Msg, self()}, receive {handle_megaco_callback_reply, Reply} -> - p("handle_megaco_callback_call -> received reply: ~n~p", [Reply]), + d("handle_megaco_callback_call -> received reply: ~n~p", [Reply]), Reply; {handle_megaco_callback_reply, Delay, Reply} when is_integer(Delay) -> - p("handle_megaco_callback_call -> " + d("handle_megaco_callback_call -> " "received reply [~w]: " "~n ~p", [Delay, Reply]), sleep(Delay), - p("handle_megaco_callback_call -> deliver reply after delay [~w]", + d("handle_megaco_callback_call -> deliver reply after delay [~w]", [Delay]), Reply; + {'EXIT', Pid, Reason} when (Pid =:= P) -> + d("handle_megaco_callback_call -> " + "received unexpected EXIT signal (from ~p): " + "~n Reason: ~p", [Pid, Reason]), + exit({unexpected_EXIT_signal, Pid, Reason}); {'EXIT', SomePid, SomeReason} -> - p("handle_megaco_callback_call -> " - "received unexpected EXIT signal: " - "~n SomePid: ~p" - "~n SomeReason: ~p", [SomePid, SomeReason]), + d("handle_megaco_callback_call -> " + "received unexpected EXIT signal from unknown process: " + "~n Pid: ~p" + "~n Reason: ~p", [SomePid, SomeReason]), exit({unexpected_EXIT_signal, SomePid, SomeReason}) end. diff --git a/lib/megaco/test/megaco_test_mg.erl b/lib/megaco/test/megaco_test_mg.erl index 38883c94f0..5979466785 100644 --- a/lib/megaco/test/megaco_test_mg.erl +++ b/lib/megaco/test/megaco_test_mg.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2019. All Rights Reserved. +%% Copyright Ericsson AB 2003-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -95,9 +95,9 @@ start(Node, Mid, Encoding, Transport, Verbosity) -> start(Node, Mid, Encoding, Transport, Conf, Verbosity) -> d("start mg[~p]: ~p" - "~n Encoding: ~p" - "~n Transport: ~p" - "~n Conf: ~p", [Node, Mid, Encoding, Transport, Conf]), + "~n Encoding: ~p" + "~n Transport: ~p" + "~n Conf: ~p", [Node, Mid, Encoding, Transport, Conf]), RI1 = encoding_config(Encoding), RI2 = transport_config(Transport), {RI3, Conf1} = transport_opts(Conf), @@ -106,17 +106,15 @@ start(Node, Mid, Encoding, Transport, Conf, Verbosity) -> Self = self(), Fun = fun() -> - io:format("LOADER(~p,~p) started~n", [self(),node()]), + i("LOADER(~p,~p) started", [self(),node()]), case (catch mg(Self, Verbosity, Config)) of {'EXIT', Reason} -> - io:format("LOADER(~p,~p) terminating with exit" - "~n~p" - "~n", [self(), node(), Reason]), + e("LOADER(~p,~p) terminating with exit" + "~n ~p", [self(), node(), Reason]), exit(Reason); Else -> - io:format("LOADER(~p,~p) terminating with" - "~n~p" - "~n", [self(), node(), Else]), + i("LOADER(~p,~p) terminating with" + "~n ~p", [self(), node(), Else]), Else end end, @@ -127,12 +125,12 @@ start(Node, Mid, Encoding, Transport, Conf, Verbosity) -> NodePing = net_adm:ping(Node), ProcInfo = (catch proc_info(Pid)), i("start -> " - "~n self(): ~p" - "~n node(): ~p" - "~n net_adm:ping(~p): ~p" - "~n Loader: ~p" - "~n Monitor ref: ~p" - "~n Process info: ~p", + "~n self(): ~p" + "~n node(): ~p" + "~n net_adm:ping(~p): ~p" + "~n Loader: ~p" + "~n Monitor ref: ~p" + "~n Process info: ~p", [self(), node(), Node, NodePing, Pid, @@ -195,12 +193,12 @@ transport_opts(Config) -> await_started(Node, MonRef, Pid) -> i("await_started -> entry with" - "~n MonRef: ~p" - "~n Pid: ~p", [MonRef, Pid]), + "~n MonRef: ~p" + "~n Pid: ~p", [MonRef, Pid]), receive {started, Pid} -> d("await_started ~p - started" - "~n Process info: ~p", [Pid, (catch proc_info(Pid))]), + "~n Process Info: ~p", [Pid, (catch proc_info(Pid))]), true = erlang:monitor_node(Node, false), erlang:demonitor(MonRef), {ok, Pid}; @@ -210,13 +208,13 @@ await_started(Node, MonRef, Pid) -> exit({node_down, Node}); {'DOWN', MonRef, process, Pid, Info} -> - i("await_started ~p - received down signal: ~p", + e("await_started ~p - received down signal: ~p", [Pid, Info]), true = erlang:monitor_node(Node, false), exit({failed_starting, Pid, Info}); {'EXIT', Pid, Reason} -> - i("await_started ~p - received exit signal: ~p", [Pid, Reason]), + e("await_started ~p - received exit signal: ~p", [Pid, Reason]), true = erlang:monitor_node(Node, false), exit({failed_starting, Pid, Reason}) @@ -226,10 +224,10 @@ await_started(Node, MonRef, Pid) -> NodePing = net_adm:ping(Node), ProcInfo = (catch proc_info(Pid)), FlushQ = megaco_test_lib:flush(), - i("await_started ~p - timeout: " - "~n net_adm:ping(~p): ~p" - "~n Process info: ~p" - "~n Messages in my queue: ~p", + e("await_started ~p - timeout: " + "~n net_adm:ping(~p): ~p" + "~n Process info: ~p" + "~n Messages in my queue: ~p", [Pid, Node, NodePing, ProcInfo, FlushQ]), true = erlang:monitor_node(Node, false), exit({error, timeout}) @@ -371,19 +369,21 @@ mg(Parent, Verbosity, Config) -> {'EXIT', normal} -> exit(normal); {'EXIT', Reason} -> - i("mg failed with reason:~n ~p", [Reason]), + e("mg failed with reason:" + "~n ~p", [Reason]), exit(Reason); Else -> - i("mg terminated: ~n ~p", [Else]), + e("mg terminated:" + "~n ~p", [Else]), exit({unexpected, Else}) end end. init(Config) -> d("init -> entry with" - "~n Config: ~p", [Config]), + "~n Config: ~p", [Config]), random_init(), - d("init -> random initiated", []), + d("init -> random initiated"), Mid = get_conf(local_mid, Config), d("init -> Mid: ~p", [Mid]), RI = get_conf(receive_info, Config), @@ -462,8 +462,8 @@ loop(#mg{parent = Parent, mid = Mid} = S) -> i("loop -> enable_test_code: ~p, ~p", [Tag, Fun]), Reply = (catch ets:insert(megaco_test_data, {Tag, Fun})), d("loop -> enable_test_code -> " - "~n Reply: ~p" - "~n ets:tab2list(megaco_test_data): ~p", + "~n Reply: ~p" + "~n ets:tab2list(megaco_test_data): ~p", [Reply,ets:tab2list(megaco_test_data)]), server_reply(Parent, enable_test_code_reply, Reply), loop(evs(S, {enable_test_code, Tag})); @@ -476,13 +476,13 @@ loop(#mg{parent = Parent, mid = Mid} = S) -> %% Give me statistics {statistics, Parent} -> - i("loop -> got request for statistics", []), + i("loop -> got request for statistics"), Stats = do_get_statistics(Mid), server_reply(Parent, statistics_reply, {ok, Stats}), loop(evs(S, stats)); {reset_stats, Parent} -> - i("loop -> got request to reset stats counters", []), + i("loop -> got request to reset stats counters"), do_reset_stats(Mid), server_reply(Parent, reset_stats_ack, ok), loop(evs(S, rst_stats)); @@ -518,11 +518,11 @@ loop(#mg{parent = Parent, mid = Mid} = S) -> %% No server-reply here. Since the service change is %% async, the reply (from the MGC) will come later. {service_change, Parent} -> - i("loop -> received request to perform service change", []), + i("loop -> received request to perform service change"), S1 = case (catch do_service_change(S)) of {ok, MG} -> - d("loop -> service change initiated", []), + d("loop -> service change initiated"), MG; Error -> d("loop -> service change failed: ~p", [Error]), @@ -538,16 +538,16 @@ loop(#mg{parent = Parent, mid = Mid} = S) -> loop(evs(S#mg{group_size = N}, {grp_reqs, N})); {{ack_info, To}, Parent} -> - i("loop -> received request to inform about received ack's ", []), + i("loop -> received request to inform about received ack's"), loop(evs(S#mg{ack_info = To}, {acki, To})); {{rep_info, To}, Parent} -> - i("loop -> received request to inform about received rep's ", []), + i("loop -> received request to inform about received rep's "), loop(evs(S#mg{rep_info = To}, {repi, To})); %% Make a sync-call {notify_request, Parent} -> - i("loop -> received request to send notify request ", []), + i("loop -> received request to send notify request "), {Res, S1} = do_handle_notify_request(S), d("loop -> notify request result: ~p", [Res]), loop(evs(S1, not_req)); @@ -564,7 +564,7 @@ loop(#mg{parent = Parent, mid = Mid} = S) -> %% cancel requests {cancel_request, Reason, Parent} -> - i("loop -> received request to cancel (all) megaco requests ", []), + i("loop -> received request to cancel (all) megaco requests"), Res = do_cancel_requests(Mid, Reason), server_reply(Parent, cancel_request_reply, Res), loop(evs(S, {creq, Reason})); @@ -600,25 +600,28 @@ loop(#mg{parent = Parent, mid = Mid} = S) -> %% Megaco callback messages {request, Request, Mid, From} -> - d("loop -> received megaco request: ~n ~p" - "~n Mid: ~p" - "~n From: ~p", + d("loop -> received megaco request: " + "~n ~p" + "~n Mid: ~p" + "~n From: ~p", [Request, Mid, From]), {Reply, S1} = handle_megaco_request(S, Request), - d("loop -> send (megaco callback) request reply: ~n~p", [Reply]), + d("loop -> send (megaco callback) request reply:" + "~n ~p", [Reply]), From ! {reply, Reply, self()}, loop(evs(S1, {req, {Request, Mid, From}})); {'EXIT', Pid, Reason} -> i("loop -> received exit signal from ~p: " - "~n ~p", [Pid, Reason]), + "~n ~p", [Pid, Reason]), S1 = handle_exit(S, Pid, Reason), loop(evs(S1, {exit, {Pid, Reason}})); Invalid -> - error_msg("received invalid request: ~n~p", [Invalid]), + error_msg("received invalid request: " + "~n ~p", [Invalid]), loop(evs(S, {invalid, Invalid})) end. @@ -793,10 +796,10 @@ do_service_change(#mg{state = State} = MG) -> do_service_change(ConnHandle, Method, EAF, Reason) -> d("send service change using:" - "~n ConnHandle: ~p" - "~n Method: ~p" - "~n EAF: ~p" - "~n Reason: ~p", [ConnHandle, Method, EAF, Reason]), + "~n ConnHandle: ~p" + "~n Method: ~p" + "~n EAF: ~p" + "~n Reason: ~p", [ConnHandle, Method, EAF, Reason]), SCP = cre_serviceChangeParm(Method, [Reason]), TermId = [?megaco_root_termination_id], SCR = cre_serviceChangeReq(TermId, SCP), @@ -816,7 +819,7 @@ do_handle_notify_request(#mg{mid = Mid, {ok, MG#mg{req_handler = Pid}}; do_handle_notify_request(#mg{state = State} = MG) -> d("do_handle_notify_request -> entry with" - "~n State: ~p", [State]), + "~n State: ~p", [State]), {{error, {invalid_state, State}}, MG}. @@ -867,7 +870,7 @@ handle_exit(#mg{parent = Pid} = S, Pid, Reason) -> handle_exit(#mg{parent = Parent, req_handler = Pid} = MG, Pid, Reason) -> error_msg("received unexpected exit from the request handler:" - "~n ~p", [Reason]), + "~n ~p", [Reason]), server_reply(Parent, notify_request_reply, {error, {request_handler_exit, Reason}}), MG#mg{req_handler = undefined}; @@ -875,14 +878,14 @@ handle_exit(#mg{parent = Parent, req_handler = Pid} = MG, Pid, Reason) -> handle_exit(#mg{parent = Parent, mload_info = {Loaders0, Ok, Err}} = MG, Pid, loader_done) -> d("handle_exit(loader_done) -> entry when" - "~n Loaders0: ~p" - "~n Ok: ~p" - "~n Err: ~p", [Loaders0, Ok, Err]), + "~n Loaders0: ~p" + "~n Ok: ~p" + "~n Err: ~p", [Loaders0, Ok, Err]), Loaders = lists:delete(Pid, Loaders0), LoadInfo = case Loaders of [] -> - d("handle_exit -> multi load done", []), + d("handle_exit -> multi load done"), server_reply(Parent, apply_multi_load_ack, {ok, Ok+1, Err}), undefined; _ -> @@ -895,10 +898,10 @@ handle_exit(#mg{parent = Parent, mload_info = {Loaders, Ok, Err}} = MG, Pid, Reason) when length(Loaders) > 0 -> d("handle_exit -> entry when" - "~n Reason: ~p" - "~n Loaders: ~p" - "~n Ok: ~p" - "~n Err: ~p", [Reason, Loaders, Ok, Err]), + "~n Reason: ~p" + "~n Loaders: ~p" + "~n Ok: ~p" + "~n Err: ~p", [Reason, Loaders, Ok, Err]), case lists:delete(Pid, Loaders) of [] -> %% since we cannot be empty prior the delete, @@ -907,15 +910,15 @@ handle_exit(#mg{parent = Parent, mload_info = {Loaders, Ok, Err}} = MG, MG#mg{mload_info = undefined}; Loaders -> %% Could not be this MG, so go on to the next - error_msg("received unexpected exit signal from ~p:~n~p", - [Pid, Reason]); + error_msg("received unexpected exit signal from ~p:" + "~n ~p", [Pid, Reason]); Loaders1 -> %% Not empty, but we removed one MG#mg{mload_info = {Loaders1,Ok,Err+1}} end; handle_exit(_MG, Pid, Reason) -> - error_msg("received unexpected exit signal from ~p:~n~p", - [Pid, Reason]). + error_msg("received unexpected exit signal from ~p:" + "~n ~p", [Pid, Reason]). parse_receive_info(RI, RH) -> @@ -949,10 +952,10 @@ start_transport(_, _, #megaco_receive_handle{send_mod = Mod}, _TO) -> start_tcp(MgcPort, MgcHost, RH, TO) -> d("start tcp transport: " - "~n MGC Port: ~p" - "~n MGC Host: ~p" - "~n Receive handle: ~p" - "~n Transport options: ~p", [MgcPort, MgcHost, RH, TO]), + "~n MGC Port: ~p" + "~n MGC Host: ~p" + "~n Receive handle: ~p" + "~n Transport options: ~p", [MgcPort, MgcHost, RH, TO]), case megaco_tcp:start_transport() of {ok, Sup} -> d("tcp transport started: ~p", [Sup]), @@ -1026,11 +1029,11 @@ megaco_udp_connect(MgcPort, MgcHost, RH, Handle, ControlPid) -> update_load_times(#mg{load_counter = 0} = MG, Times) -> d("update_load_times(0) -> entry with" - "~n Times: ~p", [Times]), + "~n Times: ~p", [Times]), {ok, MG#mg{load_counter = Times}}; update_load_times(#mg{load_counter = N}, Times) -> d("update_load_times(~p) -> entry with" - "~n Times: ~p", [N, Times]), + "~n Times: ~p", [N, Times]), {error, {already_counting, N}}. @@ -1050,14 +1053,14 @@ do_apply_load(#mg{parent = Parent, group_size = Sz, load_counter = N0} = MG, CH) -> d("do_apply_load -> entry with" - "~n Mode: ~p" - "~n Sz: ~p" - "~n N0: ~p", [Mode, Sz, N0]), + "~n Mode: ~p" + "~n Sz: ~p" + "~n N0: ~p", [Mode, Sz, N0]), {NofSent, Actions, ReplyData} = make_notify_request(N0, Sz), d("do_apply_load -> notifications constructed:" - "~n NofSent: ~p" - "~n Actions: ~p" - "~n ReplyData: ~p", [NofSent, Actions, ReplyData]), + "~n NofSent: ~p" + "~n Actions: ~p" + "~n ReplyData: ~p", [NofSent, Actions, ReplyData]), N = N0 - NofSent, case Mode of sync -> @@ -1084,9 +1087,9 @@ do_apply_load(#mg{parent = Parent, start_notify_request_handler(EAF, CH, N) -> d("start_notify_request_handler -> entry with" - "~n EAF: ~p" - "~n CH: ~p" - "~n N: ~p", [EAF, CH, N]), + "~n EAF: ~p" + "~n CH: ~p" + "~n N: ~p", [EAF, CH, N]), Env = get(), spawn_link(?MODULE, notify_request_handler_main, [self(), Env, EAF, CH, N]). @@ -1094,13 +1097,13 @@ notify_request_handler_main(Parent, Env, EAF, CH, N) -> F = fun({Tag, Val}) -> put(Tag, Val) end, lists:foreach(F, Env), d("notify_request_handler_main -> entry with" - "~n Parent: ~p" - "~n EAF: ~p" - "~n CH: ~p" - "~n N: ~p", [Parent, EAF, CH, N]), + "~n Parent: ~p" + "~n EAF: ~p" + "~n CH: ~p" + "~n N: ~p", [Parent, EAF, CH, N]), Res = do_notify_request(EAF, CH, N), d("notify_request_handler_main -> notify complete:" - "~n Res: ~p", [Res]), + "~n Res: ~p", [Res]), Parent ! {notify_request_complete, {ok, Res}, self()}, unlink(Parent), exit(normal). @@ -1220,7 +1223,7 @@ handle_megaco_request(#mg{req_handler = Pid} = MG, {handle_disconnect, _CH, _PV, R}) when is_pid(Pid) -> d("handle_megaco_request(handle_disconnect) -> entry with" - "~n Pid: ~p", [Pid]), + "~n Pid: ~p", [Pid]), Error = {error, {disconnected, R}}, self() ! {notify_request_complete, Error, Pid}, unlink(Pid), @@ -1238,19 +1241,19 @@ handle_megaco_request(#mg{req_handler = Pid} = MG, {handle_message_error, CH, PV, ED}) when is_pid(Pid) -> d("handle_megaco_request(handle_message_error) -> entry with" - "~n Pid: ~p" - "~n CH: ~p" - "~n PV: ~p" - "~n ED: ~p", [Pid, CH, PV, ED]), + "~n Pid: ~p" + "~n CH: ~p" + "~n PV: ~p" + "~n ED: ~p", [Pid, CH, PV, ED]), self() ! {notify_request_complete, ED, Pid}, unlink(Pid), exit(Pid, kill), {no_reply, MG#mg{req_handler = undefined}}; handle_megaco_request(MG, {handle_message_error, CH, PV, ED}) -> d("handle_megaco_request(handle_message_error) -> entry with" - "~n CH: ~p" - "~n PV: ~p" - "~n ED: ~p", [CH, PV, ED]), + "~n CH: ~p" + "~n PV: ~p" + "~n ED: ~p", [CH, PV, ED]), {no_reply, MG}; handle_megaco_request(MG, {handle_trans_request, _CH, _PV, _AR}) -> @@ -1283,31 +1286,31 @@ handle_megaco_request(MG, {handle_trans_ack, _CH, _PV, _AS, _AD}) -> do_handle_trans_reply(#mg{parent = Parent, state = connecting} = MG, CH, _PV, {ok, Rep}, _RD) -> d("do_handle_trans_reply(connecting) -> entry with" - "~n CH: ~p" - "~n Rep: ~p", [CH, Rep]), + "~n CH: ~p" + "~n Rep: ~p", [CH, Rep]), server_reply(Parent, service_change_reply, ok), {ok, MG#mg{state = connected}}; do_handle_trans_reply(#mg{parent = Parent, load_counter = 0} = MG, CH, _PV, {ok, Rep}, _RD) -> d("do_handle_trans_reply(load_counter = 0) -> entry with" - "~n CH: ~p" - "~n Rep: ~p", [CH, Rep, Parent]), + "~n CH: ~p" + "~n Rep: ~p", [CH, Rep, Parent]), handle_trans_reply_verify_act(Rep), server_reply(Parent, load_complete, ok), {ok, MG#mg{reply_counter = 0}}; do_handle_trans_reply(#mg{reply_counter = 0} = MG, CH, _PV, {ok, Rep}, _RD) -> d("do_handle_trans_reply(reply_counter = 0) -> entry with" - "~n CH: ~p" - "~n Rep: ~p", [CH, Rep]), + "~n CH: ~p" + "~n Rep: ~p", [CH, Rep]), handle_trans_reply_verify_act(Rep), apply_load_timer(), {ok, MG}; do_handle_trans_reply(#mg{reply_counter = N} = MG, CH, _PV, {ok, Rep}, _RD) -> d("do_handle_trans_reply(reply_counter = ~p) -> entry with" - "~n CH: ~p" - "~n Rep: ~p", [N, CH, Rep]), + "~n CH: ~p" + "~n Rep: ~p", [N, CH, Rep]), handle_trans_reply_verify_act(Rep), apply_load_timer(), {ok, MG#mg{reply_counter = N-1}}; @@ -1529,11 +1532,18 @@ num2str(N, Val) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% e(F) -> +%% i(F, []). + +e(F, A) -> + print(error, get(verbosity), "ERROR", F, A). + + i(F) -> i(F, []). i(F, A) -> - print(info, get(verbosity), "", F, A). + print(info, get(verbosity), "INFO", F, A). d(F) -> @@ -1543,15 +1553,16 @@ d(F, A) -> print(debug, get(verbosity), "DBG", F, A). +printable(error, _) -> true; printable(_, debug) -> true; printable(info, info) -> true; printable(_,_) -> false. print(Severity, Verbosity, P, F, A) -> - print(printable(Severity,Verbosity), P, F, A). + print(printable(Severity, Verbosity), P, F, A). print(true, P, F, A) -> - io:format("*** [~s] ~s ~p ~s ***" + io:format("*** [~s] [~s] ~p ~s ***" "~n " ++ F ++ "~n~n", [?FTS(), P, self(), get(sname) | A]); print(_, _, _, _) -> diff --git a/lib/megaco/test/megaco_test_mgc.erl b/lib/megaco/test/megaco_test_mgc.erl index a9027ca68e..8a9b182368 100644 --- a/lib/megaco/test/megaco_test_mgc.erl +++ b/lib/megaco/test/megaco_test_mgc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2019. All Rights Reserved. +%% Copyright Ericsson AB 2003-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -90,7 +90,9 @@ start(Node, Mid, ET, Verbosity) -> start(Node, Mid, ET, Conf, Verbosity). start(Node, Mid, ET, Conf, Verbosity) -> - d("start mgc[~p]: ~p", [Node, Mid]), + d("start mgc[~p]: ~p" + "~n ET: ~p" + "~n Conf: ~p", [Node, Mid, ET, Conf]), RI = {receive_info, mk_recv_info(ET)}, Config = [{local_mid, Mid}, RI] ++ Conf, Pid = spawn_link(Node, ?MODULE, mgc, [self(), Verbosity, Config]), @@ -160,13 +162,13 @@ await_started(Pid) -> {ok, Pid}; {'EXIT', Pid, {failed_starting_tcp_listen, {could_not_start_listener, {gen_tcp_listen, eaddrinuse}}}} -> - i("await_started ~p: address already in use", [Pid]), + e("await_started ~p: address already in use", [Pid]), ?SKIP(eaddrinuse); {'EXIT', Pid, Reason} -> - i("await_started ~p: received exit signal: ~p", [Pid, Reason]), + e("await_started ~p: received exit signal: ~p", [Pid, Reason]), exit({failed_starting, Pid, Reason}) after 10000 -> - i("await_started ~p: timeout", [Pid]), + e("await_started ~p: timeout", [Pid]), exit({error, timeout}) end. @@ -365,19 +367,19 @@ loop(S) -> loop(evs(S#mgc{dsi_timer = NewTimer}, {dsi, Time})); {stop, Parent} when S#mgc.parent =:= Parent -> - i("loop -> stopping", []), + i("loop -> stopping"), display_system_info(S#mgc.mid, "at finish "), cancel_timer(S#mgc.dsi_timer), Mid = S#mgc.mid, (catch close_conns(Mid)), megaco:stop_user(Mid), application:stop(megaco), - i("loop -> stopped", []), + i("loop -> stopped"), server_reply(Parent, stopped, ok), done(evs(S, stop), normal); {{disconnect, Reason}, Parent} when S#mgc.parent == Parent -> - i("loop -> disconnecting", []), + i("loop -> disconnecting"), Mid = S#mgc.mid, [Conn|_] = megaco:user_info(Mid, connections), Res = megaco:disconnect(Conn, {self(), Reason}), @@ -485,8 +487,8 @@ loop(S) -> %% Megaco callback messages {request, Request, From} -> - d("loop -> received megaco request from ~p:~n~p", - [From, Request]), + d("loop -> received megaco request from ~p:" + "~n ~p", [From, Request]), {Reply, S1} = handle_megaco_request(Request, S), d("loop -> send request reply: ~n~p", [Reply]), reply(From, Reply), @@ -494,17 +496,17 @@ loop(S) -> {ack_info, To, Parent} when S#mgc.parent == Parent -> - i("loop -> received request to inform about received ack's ", []), + i("loop -> received request to inform about received ack's "), loop(evs(S#mgc{ack_info = To}, {acki, To})); {abort_info, To, Parent} when S#mgc.parent == Parent -> - i("loop -> received request to inform about received aborts ", []), + i("loop -> received request to inform about received aborts "), loop(evs(S#mgc{abort_info = To}, {abi, To})); {req_info, To, Parent} when S#mgc.parent == Parent -> - i("loop -> received request to inform about received req's ", []), + i("loop -> received request to inform about received req's "), loop(evs(S#mgc{req_info = To}, {reqi, To})); @@ -516,16 +518,16 @@ loop(S) -> {'EXIT', Pid, Reason} when S#mgc.tcp_sup =:= Pid -> error_msg("MGC received unexpected exit " - "from TCP transport supervisor (~p):~n~p", - [Pid, Reason]), - i("loop -> [tcp] exiting", []), + "from TCP transport supervisor (~p):" + "~n ~p", [Pid, Reason]), + i("loop -> [tcp] exiting"), display_system_info(S#mgc.mid, "at bad finish (tcp) "), cancel_timer(S#mgc.dsi_timer), Mid = S#mgc.mid, (catch close_conns(Mid)), megaco:stop_user(Mid), application:stop(megaco), - i("loop -> stopped", []), + i("loop -> stopped"), StopReason = {error, {tcp_terminated, Pid, Reason}}, server_reply(S#mgc.parent, stopped, StopReason), done(evs(S, {tcp_sup_exit, Reason}), StopReason); @@ -533,16 +535,16 @@ loop(S) -> {'EXIT', Pid, Reason} when S#mgc.udp_sup =:= Pid -> error_msg("MGC received unexpected exit " - "from UDP transport supervisor (~p):~n~p", - [Pid, Reason]), - i("loop -> [udp] exiting", []), + "from UDP transport supervisor (~p):" + "~n ~p", [Pid, Reason]), + i("loop -> [udp] exiting"), display_system_info(S#mgc.mid, "at bad finish (udp) "), cancel_timer(S#mgc.dsi_timer), Mid = S#mgc.mid, (catch close_conns(Mid)), megaco:stop_user(Mid), application:stop(megaco), - i("loop -> stopped", []), + i("loop -> stopped"), StopReason = {error, {udp_terminated, Pid, Reason}}, server_reply(S#mgc.parent, stopped, StopReason), done(evs(S, {udp_sup_exit, Reason}), StopReason); @@ -619,13 +621,13 @@ parse_receive_info(RI, RH) -> parse_receive_info([], _RH, Transports) -> d("parse_receive_info -> done when" - "~n Transports: ~p", [Transports]), + "~n Transports: ~p", [Transports]), Transports; parse_receive_info([RI|RIs], RH, Transports) -> d("parse_receive_info -> parse receive info"), case (catch parse_receive_info1(RI, RH)) of {error, Reason} -> - i("failed parsing receive info: ~p~n~p", [RI, Reason]), + e("failed parsing receive info: ~p~n~p", [RI, Reason]), exit({failed_parsing_recv_info, RI, Reason}); RH1 -> parse_receive_info(RIs, RH, [RH1|Transports]) @@ -646,9 +648,9 @@ parse_receive_info1(RI, RH) -> encoding_mod = EM, encoding_config = EC}, d("parse_receive_info1 -> " - "~n Transport Opts: ~p" - "~n Port: ~p" - "~n Receive handle: ~p", [TO, TP, RH1]), + "~n Transport Opts: ~p" + "~n Port: ~p" + "~n Receive handle: ~p", [TO, TP, RH1]), {TO, TP, RH1}. @@ -675,19 +677,27 @@ start_transports1([], Tcp, Udp) -> start_transports1([{_TO, _Port, RH}|Transports], Tcp, Udp) when ((RH#megaco_receive_handle.send_mod =:= megaco_tcp) andalso (not is_pid(Tcp))) -> + d("try start tcp transport service"), case megaco_tcp:start_transport() of {ok, Sup} -> + d("tcp transport service started: ~p", [Sup]), start_transports1(Transports, Sup, Udp); Else -> + e("Failed starting TCP transport service:" + "~n ~p", [Else]), throw({error, {failed_starting_tcp_transport, Else}}) end; start_transports1([{_TO, _Port, RH}|Transports], Tcp, Udp) when ((RH#megaco_receive_handle.send_mod =:= megaco_udp) andalso (not is_pid(Udp))) -> + d("try start udp transport servuice"), case megaco_udp:start_transport() of {ok, Sup} -> + d("udp transport started: ~p", [Sup]), start_transports1(Transports, Tcp, Sup); Else -> + e("Failed starting UDP transport service:" + "~n ~p", [Else]), throw({error, {failed_starting_udp_transport, Else}}) end; start_transports1([_|Transports], Tcp, Udp) -> @@ -831,34 +841,34 @@ handle_megaco_request({handle_trans_reply, _CH, _PV, _AR, _RD}, S) -> handle_megaco_request({handle_trans_ack, CH, PV, AS, AD}, #mgc{ack_info = P} = S) when is_pid(P) -> d("handle_megaco_request(handle_trans_ack,~p) -> entry when" - "~n CH: ~p" - "~n PV: ~p" - "~n AS: ~p" - "~n AD: ~p", [P, CH, PV, AS, AD]), + "~n CH: ~p" + "~n PV: ~p" + "~n AS: ~p" + "~n AD: ~p", [P, CH, PV, AS, AD]), P ! {ack_received, self(), AS}, {ok, S}; handle_megaco_request({handle_trans_ack, CH, PV, AS, AD}, S) -> d("handle_megaco_request(handle_trans_ack) -> entry with" - "~n Conn Handle: ~p" - "~n Prot Version: ~p" - "~n Ack Status: ~p" - "~n Ack Data: ~p", [CH, PV, AS, AD]), + "~n Conn Handle: ~p" + "~n Prot Version: ~p" + "~n Ack Status: ~p" + "~n Ack Data: ~p", [CH, PV, AS, AD]), {ok, S}; handle_megaco_request({handle_unexpected_trans, CH, PV, TR}, S) -> d("handle_megaco_request(handle_unexpected_trans) -> entry with" - "~n CH: ~p" - "~n PV: ~p" - "~n TR: ~p", [CH, PV, TR]), + "~n CH: ~p" + "~n PV: ~p" + "~n TR: ~p", [CH, PV, TR]), {ok, S}; handle_megaco_request({handle_trans_request_abort, CH, PV, TI, Handler}, S) -> d("handle_megaco_request(handle_trans_request_abort) -> entry with" - "~n CH: ~p" - "~n PV: ~p" - "~n TI: ~p" - "~n Handler: ~p", [CH, PV, TI, Handler]), + "~n CH: ~p" + "~n PV: ~p" + "~n TI: ~p" + "~n Handler: ~p", [CH, PV, TI, Handler]), Reply = case S#mgc.abort_info of P when is_pid(P) -> @@ -873,8 +883,8 @@ handle_megaco_request({handle_trans_request_abort, CH, PV, TI, Handler}, S) -> do_handle_trans_request(CH, PV, ARs, #mgc{req_action = Action, req_timeout = To} = S) -> d("do_handle_megaco_request(handle_trans_request) -> entry with" - "~n Action: ~p" - "~n To: ~p", [Action, To]), + "~n Action: ~p" + "~n To: ~p", [Action, To]), case handle_act_requests(CH, PV, ARs, Action) of {pending_ignore, ActReqs} -> {{pending, ActReqs}, S#mgc{req_action = ignore}}; @@ -950,8 +960,8 @@ handle_notify_req(CH, PV, CtxId, handle_event(_CH, _PV, _Cid, Tid, EvDesc) -> d("handle_event -> received" - "~n EvDesc: ~p" - "~n Tid: ~p", [EvDesc, Tid]), + "~n EvDesc: ~p" + "~n Tid: ~p", [EvDesc, Tid]), {notifyReply, cre_notifyRep(Tid)}. @@ -1195,20 +1205,24 @@ cancel_timer(Ref) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +e(F, A) -> + print(error, get(verbosity), "ERROR", F, A). + i(F) -> i(F, []). i(F, A) -> - print(info, get(verbosity), "", F, A). + print(info, get(verbosity), "INFO", F, A). d(F) -> d(F, []). d(F, A) -> - print(debug, get(verbosity), "DBG: ", F, A). + print(debug, get(verbosity), "DBG", F, A). +printable(error, _) -> true; printable(_, debug) -> true; printable(info, info) -> true; printable(_,_) -> false. @@ -1222,7 +1236,7 @@ print(_, _, _, _) -> ok. print(P, F, A) -> - io:format("*** [~s] ~s ~p ~s ***" + io:format("*** [~s] [~s] ~p ~s ***" "~n " ++ F ++ "~n~n", [?FTS(), P, self(), get(sname) | A]). diff --git a/lib/megaco/test/megaco_test_tcp_generator.erl b/lib/megaco/test/megaco_test_tcp_generator.erl index ec256f7a87..0edeac6733 100644 --- a/lib/megaco/test/megaco_test_tcp_generator.erl +++ b/lib/megaco/test/megaco_test_tcp_generator.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. +%% Copyright Ericsson AB 2007-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -404,6 +404,7 @@ handle_exec({expect_receive, Desc, {Verify, To}}, e("received unknown message: ~p", [Else]), {error, {expect_receive, {unexpected_message, Else}}} after To -> + e("(expect) receive timeout: ~w", [To]), {error, {expect_receive, timeout}} end; @@ -412,14 +413,14 @@ handle_exec({expect_closed, To}, result = Acc} = State) -> p("expect_closed ~w", [To]), inet:setopts(Sock, [{active, once}]), - p("expect_closed - await closed", []), + d("expect_closed - await closed", []), receive {tcp_closed, Sock} -> p("expect_closed - received closed"), {ok, State#state{connection = undefined, result = [closed|Acc]}} after To -> - e("expect_closed timeout after ~w", [To]), + e("(expect) closed timeout after ~w", [To]), {error, {expect_closed, timeout}} end; @@ -428,13 +429,13 @@ handle_exec({expect_nothing, To}, result = Acc} = State) -> p("expect_nothing ~w", [To]), inet:setopts(Sock, [{active, once}]), - p("expect_nothing - await anything", []), + d("expect_nothing - await anything", []), receive Any -> e("expect_nothing - received: ~p", [Any]), {error, {expect_nothing, Any}} after To -> - p("expect_nothing timeout after ~w", [To]), + p("got nothing (~w) as expected", [To]), {ok, State#state{result = [{nothing, To}|Acc]}} end; @@ -502,6 +503,6 @@ e(F, A) -> megaco_test_generator:error(F, A). p(F ) -> p("", F, []). p(F, A) -> p("", F, A). -p(P, F, A) -> megaco_test_generator:print(P, F, A). +p(P, F, A) -> megaco_test_generator:print(P, F, A). diff --git a/lib/megaco/test/megaco_trans_SUITE.erl b/lib/megaco/test/megaco_trans_SUITE.erl index c1c333a305..1e281987b8 100644 --- a/lib/megaco/test/megaco_trans_SUITE.erl +++ b/lib/megaco/test/megaco_trans_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2019. All Rights Reserved. +%% Copyright Ericsson AB 2003-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -308,19 +308,25 @@ single_ack(suite) -> single_ack(doc) -> []; single_ack(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, single_ack), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_single_ack/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(rsingle_ack, Pre, Case, Post). + +do_single_ack([MgcNode, MgNode]) -> %% Start the MGC and MGs i("[MGC] start"), ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}], @@ -375,26 +381,33 @@ multi_ack_timeout(suite) -> multi_ack_timeout(doc) -> []; multi_ack_timeout(Config) when is_list(Config) -> - %% <CONDITIONAL-SKIP> - Skippable = [win32, {unix, [darwin, linux, sunos]}], % Is there any left? - Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% </CONDITIONAL-SKIP> - - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, multi_ack_timeout), - i("starting"), + Pre = fun() -> + %% <CONDITIONAL-SKIP> + Skippable = [win32, {unix, [darwin, linux, sunos]}], + Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% </CONDITIONAL-SKIP> + + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_multi_ack_timeout/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(multi_ack_timeout, Pre, Case, Post). +do_multi_ack_timeout([MgcNode, MgNode]) -> MaxCount = 20, - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), %% Start the MGC and MGs i("[MGC] start"), @@ -459,19 +472,26 @@ multi_ack_maxcount(suite) -> multi_ack_maxcount(doc) -> []; multi_ack_maxcount(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, multi_ack_maxcount), - i("starting"), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_multi_ack_maxcount/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(multi_ack_maxcount, Pre, Case, Post). + +do_multi_ack_maxcount([MgcNode, MgNode]) -> MaxCount = 10, - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), %% Start the MGC and MGs i("[MGC] start"), @@ -523,7 +543,6 @@ multi_ack_maxcount(Config) when is_list(Config) -> i("wait some time before closing down"), sleep(5000), - %% Tell MG to stop i("[MG] stop"), ?MG_STOP(Mg), @@ -544,20 +563,25 @@ single_trans_req(suite) -> single_trans_req(doc) -> []; single_trans_req(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, single_trans_req), - process_flag(trap_exit, true), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_single_trans_req/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(single_trans_req, Pre, Case, Post). +do_single_trans_req([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -984,19 +1008,25 @@ multi_trans_req_timeout(suite) -> multi_trans_req_timeout(doc) -> []; multi_trans_req_timeout(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, multi_trans_req_timeout), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_multi_trans_req_timeout/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(request_and_no_reply, Pre, Case, Post). +do_multi_trans_req_timeout([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -1438,20 +1468,25 @@ multi_trans_req_maxcount1(suite) -> multi_trans_req_maxcount1(doc) -> "Test that a message is sent when req_maxcount is reached"; multi_trans_req_maxcount1(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, multi_trans_req_maxcount1), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_multi_trans_req_maxcount1/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(multi_trans_req_maxcount1, Pre, Case, Post). + +do_multi_trans_req_maxcount1([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -1895,19 +1930,25 @@ multi_trans_req_maxcount2(doc) -> "Test that the message is sent when req_maxcount is reached " "with a request bigger then maxsize limit"; multi_trans_req_maxcount2(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, multi_trans_req_maxcount2), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_multi_trans_req_maxcount2/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(multi_trans_req_maxcount2, Pre, Case, Post). +do_multi_trans_req_maxcount2([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -2404,19 +2445,30 @@ multi_trans_req_maxsize1(suite) -> multi_trans_req_maxsize1(doc) -> "Test that the message is sent when req_maxsize is reached"; multi_trans_req_maxsize1(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, multi_trans_req_maxsize1), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + put(verbosity, ?TEST_VERBOSITY), + put(sname, "TEST"), + put(tc, multi_trans_req_maxsize1), + i("starting"), + + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_multi_trans_req_maxsize1/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(multi_trans_req_maxsize1, Pre, Case, Post). +do_multi_trans_req_maxsize1([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -2859,19 +2911,25 @@ multi_trans_req_maxsize2(doc) -> "Test that the message is sent when req_maxsize is reached, " "when the 'last' message is bigger then req_maxsize itself"; multi_trans_req_maxsize2(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, multi_trans_req_maxsize2), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_multi_trans_req_maxsize2/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(multi_trans_req_maxsize2, Pre, Case, Post). +do_multi_trans_req_maxsize2([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -3356,19 +3414,25 @@ single_trans_req_and_ack(suite) -> single_trans_req_and_ack(doc) -> []; single_trans_req_and_ack(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, single_trans_req_and_ack), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_single_trans_req_and_ack/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(single_trans_req_and_ack, Pre, Case, Post). +do_single_trans_req_and_ack([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -3839,19 +3903,25 @@ multi_trans_req_and_ack_timeout(suite) -> multi_trans_req_and_ack_timeout(doc) -> []; multi_trans_req_and_ack_timeout(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, multi_trans_req_and_ack_timeout), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_multi_trans_req_and_ack_timeout/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(multi_trans_req_and_ack_timeout, Pre, Case, Post). +do_multi_trans_req_and_ack_timeout([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -4327,24 +4397,34 @@ mtrtaat_err_desc(T) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Transaction Sender +%% +%% + multi_trans_req_and_ack_ackmaxcount(suite) -> []; multi_trans_req_and_ack_ackmaxcount(doc) -> []; multi_trans_req_and_ack_ackmaxcount(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, multi_trans_req_and_ack_ackmaxcount), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_multi_trans_req_and_ack_ackmaxcount/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(multi_trans_req_and_ack_ackmaxcount, Pre, Case, Post). +do_multi_trans_req_and_ack_ackmaxcount([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -4438,6 +4518,9 @@ mtrtaaamc_mgc_event_sequence(text, tcp) -> DiscoVerify = ?mtrtaaamc_mgc_verify_handle_disconnect_fun(), EvSeq = [ {debug, true}, + {trigger, fun() -> + put(verbosity, ?TEST_VERBOSITY) + end}, {megaco_trace, disable}, megaco_start, {megaco_start_user, Mid, RI, []}, @@ -4469,12 +4552,12 @@ mtrtaaamc_mgc_event_sequence(text, tcp) -> mtrtaaamc_mgc_verify_handle_connect({handle_connect, CH, ?VERSION}) -> - io:format("mtrtaaamc_mgc_verify_handle_connect -> ok" - "~n CH: ~p~n", [CH]), + i("MGC Connect verification ok" + "~n ~p~n", [CH]), {ok, CH, ok}; mtrtaaamc_mgc_verify_handle_connect(Else) -> - io:format("mtrtaaamc_mgc_verify_handle_connect -> unknown" - "~n Else: ~p~n", [Else]), + e("MGC Connect verification failed: unknown" + "~n ~p~n", [Else]), {error, Else, ok}. mtrtaaamc_mgc_verify_service_change_req_fun(Mid) -> @@ -4484,8 +4567,9 @@ mtrtaaamc_mgc_verify_service_change_req_fun(Mid) -> mtrtaaamc_mgc_verify_service_change_req( {handle_trans_request, _, ?VERSION, [AR]}, Mid) -> - io:format("mtrtaaamc_mgc_verify_service_change_req -> ok" - "~n AR: ~p~n", [AR]), + i("MGC Service Change Request verification: begin" + "~n AR: ~p" + "~n", [AR]), case AR of #'ActionRequest'{commandRequests = [CR]} -> case CR of @@ -4501,11 +4585,17 @@ mtrtaaamc_mgc_verify_service_change_req( #'ServiceChangeParm'{ serviceChangeMethod = restart, serviceChangeReason = [[$9,$0,$1|_]]} -> + i("MGC Service Change Request " + "verification ok"), Reply = {discard_ack, [mtrtaaamc_mgc_service_change_reply_ar(Mid, 1)]}, {ok, AR, Reply}; _ -> + e("MGC Service Change Request " + "verification failed: invalid SCP" + "~n ~p" + "~n", [Parms]), Err = {invalid_SCP, Parms}, ED = mtrtaaamc_err_desc(Parms), ErrReply = {discard_ack, @@ -4513,32 +4603,50 @@ mtrtaaamc_mgc_verify_service_change_req( {error, Err, ErrReply} end; _ -> + e("MGC Service Change Request " + "verification failed: " + "invalid termination id" + "~n ~p" + "~n", [Tid]), Err = {invalid_termination_id, Tid}, ED = mtrtaaamc_err_desc(Tid), ErrReply = {discard_ack, ED}, {error, Err, ErrReply} end; _ -> + e("MGC Service Change Request verification failed: " + "invalid command" + "~n ~p" + "~n", [Cmd]), Err = {invalid_command, Cmd}, ED = mtrtaaamc_err_desc(Cmd), ErrReply = {discard_ack, ED}, {error, Err, ErrReply} end; _ -> + e("MGC Service Change Request verification failed: " + "invalid command request" + "~n ~p" + "~n", [CR]), Err = {invalid_command_request, CR}, ED = mtrtaaamc_err_desc(CR), ErrReply = {discard_ack, ED}, {error, Err, ErrReply} end; _ -> + e("MGC Service Change Request verification failed: " + "invalid action request" + "~n ~p" + "~n", [AR]), Err = {invalid_action_request, AR}, ED = mtrtaaamc_err_desc(AR), ErrReply = {discard_ack, ED}, {error, Err, ErrReply} end; mtrtaaamc_mgc_verify_service_change_req(Else, _Mid) -> - io:format("mtrtaaamc_mgc_verify_service_change_req -> unknown" - "~n Else: ~p~n", [Else]), + e("MGC Service Change Request verification failed: unknown" + "~n ~p" + "~n", [Else]), ED = mtrtaaamc_err_desc(Else), ErrReply = {discard_ack, ED}, {error, Else, ErrReply}. @@ -4550,9 +4658,11 @@ mtrtaaamc_mgc_verify_notify_request_fun() -> mtrtaaamc_mgc_verify_notify_request( {handle_trans_request, _, ?VERSION, [AR]}) -> - io:format("mtrtaaamc_mgc_verify_notify_request:fun -> ok" - "~n AR: ~p~n", [AR]), + i("MGC Notify Request verification: begin" + "~n AR: ~p" + "~n", [AR]), case AR of + %% *** SLOPPY ACK *** #'ActionRequest'{contextId = 1 = Cid, commandRequests = [CR]} -> #'CommandRequest'{command = Cmd} = CR, @@ -4564,9 +4674,15 @@ mtrtaaamc_mgc_verify_notify_request( observedEventLst = [OE]} = OED, #'ObservedEvent'{eventName = "al/of"} = OE, HandleAck = {handle_sloppy_ack, {kalle, Rid}}, + i("MGC Notify Request verification ok: sloppy ack" + "~n Cid: ~p" + "~n Tid: ~p" + "~n Rid: ~p", [Cid, Tid, Rid]), Reply = {HandleAck, [mtrtaaamc_mgc_notify_reply_ar(Cid, Tid)]}, {ok, AR, Reply}; + + %% *** PROPER ACK *** #'ActionRequest'{contextId = 2 = Cid, commandRequests = [CR]} -> #'CommandRequest'{command = Cmd} = CR, @@ -4574,43 +4690,54 @@ mtrtaaamc_mgc_verify_notify_request( #'NotifyRequest'{terminationID = [Tid], observedEventsDescriptor = OED, errorDescriptor = asn1_NOVALUE} = NR, - #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, + #'ObservedEventsDescriptor'{requestId = _Rid, + observedEventLst = [OE]} = OED, #'ObservedEvent'{eventName = "al/of"} = OE, + i("MGC Notify Request verification ok: discard ack" + "~n Cid: ~p" + "~n Tid: ~p" + "~n Rid: ~p", [Cid, Tid, _Rid]), Reply = {discard_ack, [mtrtaaamc_mgc_notify_reply_ar(Cid, Tid)]}, {ok, AR, Reply}; + _ -> + e("MGC Notify Request verification failed: unexpected AR" + "~n ~p" + "~n", [AR]), ED = mtrtaaamc_err_desc(AR), ErrReply = {discard_ack, ED}, {error, AR, ErrReply} end; mtrtaaamc_mgc_verify_notify_request(Else) -> - io:format("mtrtaaamc_mgc_verify_notify_request:fun -> unknown" - "~n Else: ~p~n", [Else]), + e("MGC Notify Request verification failed: unexpected callback" + "~n ~p" + "~n", [Else]), ED = mtrtaaamc_err_desc(Else), ErrReply = {discard_ack, ED}, {error, Else, ErrReply}. mtrtaaamc_mgc_verify_ack({handle_trans_ack, CH, ?VERSION, ok, {kalle, Rid}}) -> - io:format("mtrtaaamc_mgc_verify_ack -> ok" - "~n CH: ~p" - "~n Rid: ~p" - "~n", [CH, Rid]), + i("MGC Ack verification: ok (kalle)" + "~n CH: ~p" + "~n Rid: ~p" + "~n", [CH, Rid]), {ok, CH, ok}; mtrtaaamc_mgc_verify_ack(Else) -> - io:format("mtrtaaamc_mgc_verify_ack -> unknown" - "~n Else: ~p~n", [Else]), + e("MGC Ack verification failed: unknown" + "~n ~p~n", [Else]), {error, Else, ok}. mtrtaaamc_mgc_verify_handle_disconnect({handle_disconnect, CH, ?VERSION, R}) -> - io:format("mtrtaaamc_mgc_verify_handle_disconnect -> ok" - "~n CH: ~p" - "~n R: ~p" - "~n", [CH, R]), + i("MGC Disconnect verification: ok" + "~n CH: ~p" + "~n R: ~p" + "~n", [CH, R]), {ok, CH, ok}; mtrtaaamc_mgc_verify_handle_disconnect(Else) -> - io:format("mtrtaaamc_mgc_verify_handle_disconnect -> unknown" - "~n Else: ~p~n", [Else]), + e("MGC Disconnect verification failed: unknown" + "~n ~p" + "~n", [Else]), {error, Else, ok}. @@ -4622,26 +4749,11 @@ mtrtaaamc_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -%% mtrtaaamc_mgc_service_change_reply_msg(Mid, TransId, Cid) -> -%% AR = mtrtaaamc_mgc_service_change_reply_ar(Mid, Cid), -%% TRes = cre_transResult([AR]), -%% TR = cre_transReply(TransId, TRes), -%% Trans = cre_transaction(TR), -%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), -%% cre_megacoMessage(Mess). - mtrtaaamc_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -%% mtrtaaamc_mgc_notify_reply(Mid, TransId, Cid, TermId) -> -%% AR = mtrtaaamc_mgc_notify_reply_ar(Cid, TermId), -%% TRes = cre_transResult([AR]), -%% TR = cre_transReply(TransId, TRes), -%% Trans = cre_transaction(TR), -%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), -%% cre_megacoMessage(Mess). %% @@ -4681,6 +4793,9 @@ mtrtaaamc_mg_event_sequence(text, tcp) -> NotifyReplyVerify = ?mtrtaaamc_mg_verify_notify_reply_fun(), EvSeq = [ {debug, true}, + {trigger, fun() -> + put(verbosity, ?TEST_VERBOSITY) + end}, megaco_start, {megaco_start_user, Mid, RI, []}, start_transport, @@ -4704,20 +4819,35 @@ mtrtaaamc_mg_event_sequence(text, tcp) -> {megaco_update_conn_info, trans_ack, true}, {megaco_update_conn_info, trans_req, true}, {megaco_conn_info, all}, + {megaco_conn_info, requests}, {megaco_cast, NR(1,1), []}, + {megaco_conn_info, requests}, {megaco_cast, NR(1,2), []}, + {megaco_conn_info, requests}, {megaco_cast, NR(1,3), []}, + {megaco_conn_info, requests}, {megaco_callback, handle_trans_reply, NotifyReplyVerify}, + {megaco_conn_info, requests}, {megaco_callback, handle_trans_reply, NotifyReplyVerify}, + {megaco_conn_info, requests}, {megaco_callback, handle_trans_reply, NotifyReplyVerify}, + {megaco_conn_info, requests}, {megaco_cast, NR(2,1), []}, + {megaco_conn_info, requests}, {megaco_cast, NR(2,2), []}, + {megaco_conn_info, requests}, {megaco_cast, NR(2,3), []}, + {megaco_conn_info, requests}, {megaco_cast, NR(1,4), [{trans_req,false}]}, + {megaco_conn_info, requests}, {megaco_callback, handle_trans_reply, NotifyReplyVerify}, + {megaco_conn_info, requests}, {megaco_callback, handle_trans_reply, NotifyReplyVerify}, + {megaco_conn_info, requests}, {megaco_callback, handle_trans_reply, NotifyReplyVerify}, + {megaco_conn_info, requests}, {megaco_callback, handle_trans_reply, NotifyReplyVerify}, + {megaco_conn_info, requests}, {sleep, 3000}, megaco_stop_user, megaco_stop, @@ -4726,18 +4856,21 @@ mtrtaaamc_mg_event_sequence(text, tcp) -> EvSeq. mtrtaaamc_mg_verify_handle_connect({handle_connect, CH, ?VERSION}) -> - io:format("mtrtaaamc_mg_verify_handle_connect -> ok" - "~n CH: ~p~n", [CH]), + i("MG Connect verification: ok" + "~n CH: ~p" + "~n", [CH]), {ok, CH, ok}; mtrtaaamc_mg_verify_handle_connect(Else) -> - io:format("mtrtaaamc_mg_verify_handle_connect -> unknown" - "~n Else: ~p~n", [Else]), + e("MG Connect verification failed: unknown" + "~n ~p" + "~n", [Else]), {error, Else, ok}. mtrtaaamc_mg_verify_service_change_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> - io:format("mtrtaaamc_mg_verify_service_change_reply -> ok" - "~n AR: ~p~n", [AR]), + i("MG Service Change Reply verification: begin" + "~n AR: ~p" + "~n", [AR]), case AR of #'ActionReply'{commandReply = [SCR]} -> case SCR of @@ -4751,37 +4884,73 @@ mtrtaaamc_mg_verify_service_change_reply({handle_trans_reply, _CH, ?VERSION, {serviceChangeResParms, #'ServiceChangeResParm'{ serviceChangeMgcId = _RemoteMid}} -> + i("MG Service Change Reply verification ok"), {ok, AR, ok}; {Tag, Val} -> + e("MG Service Change Reply " + "verification failed: " + "invalid service change result" + "~n Tag: ~p" + "~n Val: ~p" + "~n", [Tag, Val]), Err = {invalid_service_change_result, Tag, Val}, {error, Err, ok} end; _ -> + e("MG Service Change Reply verification failed: " + "invalid termination id" + "~n ~p" + "~n", [Tid]), Err = {invalid_termination_id, Tid}, {error, Err, ok} end; {Tag, Val} -> + e("MG Service Change Reply verification failed: " + "invalid command reply" + "~n Tag: ~p" + "~n Val: ~p" + "~n", [Tag, Val]), Err = {invalid_command_reply, Tag, Val}, {error, Err, ok} end; _ -> + e("MG Service Change Reply verification failed: invalid action reply" + "~n ~p" + "~n", [AR]), Err = {invalid_action_reply, AR}, {error, Err, ok} end; mtrtaaamc_mg_verify_service_change_reply(Else) -> - io:format("mtrtaaamc_mg_verify_service_change_reply -> unknown" - "~n Else: ~p~n", [Else]), + e("MG Service Change Reply verification failed -> unknown" + "~n ~p" + "~n", [Else]), {error, Else, ok}. mtrtaaamc_mg_verify_notify_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> - io:format("mtrtaaamc_mg_verify_notify_reply -> ok" - "~n AR: ~p~n", [AR]), + i("MG Notify Reply verification ok:" + "~n ~p~n", [AR]), {ok, AR, ok}; +mtrtaaamc_mg_verify_notify_reply({handle_unexpected_trans, CH, PV, T} = Else) -> + e("MG Notify Reply verification failed: unexpected transaction" + "~n CH: ~p" + "~n PV: ~p" + "~n T: ~p" + "~n", [CH, PV, T]), + {error, Else, ok}; +mtrtaaamc_mg_verify_notify_reply({handle_unexpected_trans, CH, PV, E, T} = Else) -> + e("MG Notify Reply failed: unexpected transaction" + "~n CH: ~p" + "~n PV: ~p" + "~n E: ~p" + "~n T: ~p" + "~n", [CH, PV, E, T]), + {error, Else, ok}; mtrtaaamc_mg_verify_notify_reply(Else) -> - io:format("mtrtaaamc_mg_verify_notify_reply -> unknown" - "~n Else: ~p~n", [Else]), + e("MG Notify Reply failed -> unknown" + "~n ~p" + "~n", [Else]), {error, Else, ok}. mtrtaaamc_mg_service_change_request_ar(_Mid, Cid) -> @@ -4793,13 +4962,6 @@ mtrtaaamc_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -%% mtrtaaamc_mg_service_change_request_msg(Mid, TransId, Cid) -> -%% AR = mtrtaaamc_mg_service_change_request_ar(Mid, Cid), -%% TR = cre_transReq(TransId, [AR]), -%% Trans = cre_transaction(TR), -%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), -%% cre_megacoMessage(Mess). - mtrtaaamc_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), Ev = cre_obsEvent("al/of", TT), @@ -4809,13 +4971,6 @@ mtrtaaamc_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -%% mtrtaaamc_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> -%% AR = mtrtaaamc_mg_notify_request_ar(Rid, TermId, Cid), -%% TR = cre_transReq(TransId, [AR]), -%% Trans = cre_transaction(TR), -%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), -%% cre_megacoMessage(Mess). - %% %% Common functions for the multi_trans_req_timeout test case @@ -4832,19 +4987,25 @@ multi_trans_req_and_ack_reqmaxcount(suite) -> multi_trans_req_and_ack_reqmaxcount(doc) -> []; multi_trans_req_and_ack_reqmaxcount(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, multi_trans_req_and_ack_reqmaxcount), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_multi_trans_req_and_ack_reqmaxcount/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(multi_trans_req_and_ack_reqmaxcount, Pre, Case, Post). +do_multi_trans_req_and_ack_reqmaxcount([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -5235,8 +5396,6 @@ mtrtaarac_mg_verify_handle_connect(Else) -> mtrtaarac_mg_verify_service_change_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> - io:format("mtrtaarac_mg_verify_service_change_reply -> ok" - "~n AR: ~p~n", [AR]), case AR of #'ActionReply'{commandReply = [SCR]} -> case SCR of @@ -5250,37 +5409,55 @@ mtrtaarac_mg_verify_service_change_reply({handle_trans_reply, _CH, ?VERSION, {serviceChangeResParms, #'ServiceChangeResParm'{ serviceChangeMgcId = _RemoteMid}} -> + i("received expected handle_trans_reply " + "(service change) with ok" + "~n AR: ~p", [AR]), {ok, AR, ok}; {Tag, Val} -> + e("received expected handle_trans_reply " + "(service change) with error" + "~n Tag: ~p" + "~n Val: ~p", [Tag, Val]), Err = {invalid_service_change_result, Tag, Val}, {error, Err, ok} end; _ -> + e("received expected handle_trans_reply " + "(service change) with error" + "~n Tid: ~p", [Tid]), Err = {invalid_termination_id, Tid}, {error, Err, ok} end; {Tag, Val} -> + e("received expected handle_trans_reply " + "(action reply) with error" + "~n Tag: ~p" + "~n Val: ~p", [Tag, Val]), Err = {invalid_command_reply, Tag, Val}, {error, Err, ok} end; _ -> + e("received expected handle_trans_reply with error" + "~n AR: ~p", [AR]), Err = {invalid_action_reply, AR}, {error, Err, ok} end; mtrtaarac_mg_verify_service_change_reply(Else) -> - io:format("mtrtaarac_mg_verify_service_change_reply -> unknown" - "~n Else: ~p~n", [Else]), + e("mtrtaarac_mg_verify_service_change_reply -> invalid service change reply" + "~n Expected: handle_trans_reply (service change) with ok" + "~n Received; ~p", [Else]), {error, Else, ok}. mtrtaarac_mg_verify_notify_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> - io:format("mtrtaarac_mg_verify_notify_reply -> ok" - "~n AR: ~p~n", [AR]), + i("received expected handle_notify_reply with ok" + "~n AR: ~p~n", [AR]), {ok, AR, ok}; mtrtaarac_mg_verify_notify_reply(Else) -> - io:format("mtrtaarac_mg_verify_notify_reply -> unknown" - "~n Else: ~p~n", [Else]), + e("MG Notify Reply verification failed: invalid notify reply" + "~n Expected: handle_trans_reply with ok" + "~n Received: ~p", [Else]), {error, Else, ok}. mtrtaarac_mg_service_change_request_ar(_Mid, Cid) -> @@ -5292,13 +5469,6 @@ mtrtaarac_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -%% mtrtaarac_mg_service_change_request_msg(Mid, TransId, Cid) -> -%% AR = mtrtaarac_mg_service_change_request_ar(Mid, Cid), -%% TR = cre_transReq(TransId, [AR]), -%% Trans = cre_transaction(TR), -%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), -%% cre_megacoMessage(Mess). - mtrtaarac_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), Ev = cre_obsEvent("al/of", TT), @@ -5308,13 +5478,6 @@ mtrtaarac_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -%% mtrtaarac_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> -%% AR = mtrtaarac_mg_notify_request_ar(Rid, TermId, Cid), -%% TR = cre_transReq(TransId, [AR]), -%% Trans = cre_transaction(TR), -%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), -%% cre_megacoMessage(Mess). - %% %% Common functions for the multi_trans_req_timeout test case @@ -5333,19 +5496,25 @@ multi_trans_req_and_ack_maxsize1(suite) -> multi_trans_req_and_ack_maxsize1(doc) -> []; multi_trans_req_and_ack_maxsize1(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, multi_trans_req_and_ack_maxsize1), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_multi_trans_req_and_ack_maxsize1/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(multi_trans_req_and_ack_maxsize1, Pre, Case, Post). +do_multi_trans_req_and_ack_maxsize1([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -5831,19 +6000,25 @@ multi_trans_req_and_ack_maxsize2(suite) -> multi_trans_req_and_ack_maxsize2(doc) -> []; multi_trans_req_and_ack_maxsize2(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, multi_trans_req_and_ack_maxsize2), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_multi_trans_req_and_ack_maxsize2/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(multi_trans_req_and_ack_maxsize2, Pre, Case, Post). +do_multi_trans_req_and_ack_maxsize2([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -6372,19 +6547,25 @@ multi_trans_req_and_ack_and_pending(suite) -> multi_trans_req_and_ack_and_pending(doc) -> []; multi_trans_req_and_ack_and_pending(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, mtraaap), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_multi_trans_req_and_ack_and_pending/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(mtraaap, Pre, Case, Post). +do_multi_trans_req_and_ack_and_pending([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -6844,38 +7025,6 @@ mtraaap_mg_verify_service_change_reply(Else) -> "~n Else: ~p~n", [Else]), {error, Else, ok}. -%% mtraaap_mg_verify_notify_request_fun() -> -%% fun(Ev) -> -%% mtraaap_mg_verify_notify_request(Ev) -%% end. - -%% mtraaap_mg_verify_notify_request( -%% {handle_trans_request, _, ?VERSION, [AR]}) -> -%% io:format("mtraaap_mg_verify_notify_request -> ok" -%% "~n AR: ~p~n", [AR]), -%% case AR of -%% #'ActionRequest'{contextId = 1 = Cid, -%% commandRequests = [CR]} -> -%% #'CommandRequest'{command = Cmd} = CR, -%% {notifyReq, NR} = Cmd, -%% #'NotifyRequest'{terminationID = [Tid], -%% observedEventsDescriptor = OED, -%% errorDescriptor = asn1_NOVALUE} = NR, -%% #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, -%% #'ObservedEvent'{eventName = "al/of"} = OE, -%% Reply = {discard_ack, [mtraaap_mg_notify_reply_ar(Cid, Tid)]}, -%% {ok, 3000, AR, Reply}; -%% _ -> -%% ED = mtraaap_err_desc(AR), -%% ErrReply = {discard_ack, ED}, -%% {error, AR, ErrReply} -%% end; -%% mtraaap_mg_verify_notify_request(Else) -> -%% io:format("mtraaap_mg_verify_notify_request:fun -> unknown" -%% "~n Else: ~p~n", [Else]), -%% ED = mtraaap_err_desc(Else), -%% ErrReply = {discard_ack, ED}, -%% {error, Else, ErrReply}. mtraaap_mg_verify_notify_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> @@ -6960,19 +7109,25 @@ multi_trans_req_and_ack_and_reply(suite) -> multi_trans_req_and_ack_and_reply(doc) -> []; multi_trans_req_and_ack_and_reply(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, multi_trans_req_and_ack_and_reply), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_multi_trans_req_and_ack_and_reply/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(multi_trans_req_and_ack_and_reply, Pre, Case, Post). +do_multi_trans_req_and_ack_and_reply([MgcNode, MgNode]) -> d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -7537,23 +7692,27 @@ otp_7192_1(suite) -> otp_7192_1(doc) -> [""]; otp_7192_1(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, otp_7192_1), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_otp_7192_1/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(otp_7192_1, Pre, Case, Post). + +do_otp_7192_1([MgcNode, MgNode]) -> MgMid = {deviceName,"mg"}, - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), - - d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -8109,22 +8268,27 @@ otp_7192_2(suite) -> otp_7192_2(doc) -> []; otp_7192_2(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, otp_7192_2), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - - MgMid = {deviceName,"mg"}, + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_otp_7192_2/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(otp_7192_2, Pre, Case, Post). - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), +do_otp_7192_2([MgcNode, MgNode]) -> + MgMid = {deviceName,"mg"}, d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -8208,7 +8372,7 @@ otp71922_mgc_event_sequence(text, tcp, MgMid) -> {encoding_config, []}, {transport_module, megaco_tcp} ], - Tid = #megaco_term_id{id = ["00000000","00000000","01101101"]}, + Tid = #megaco_term_id{id = ["00000000","00000100","01101101"]}, NR = fun(Cid, Rid) -> [otp71922_mgc_notify_request_ar(Rid, Tid, Cid)] end, @@ -8251,12 +8415,14 @@ otp71922_mgc_event_sequence(text, tcp, MgMid) -> otp71922_mgc_verify_handle_connect({handle_connect, CH, ?VERSION}) -> - io:format("otp71922_mgc_verify_handle_connect -> ok" - "~n CH: ~p~n", [CH]), + i("received expected handle_connect with" + "~n CH: ~p" + "~n => force a 2 second sleep before return", [CH]), {ok, timer:seconds(2), CH, ok}; otp71922_mgc_verify_handle_connect(Else) -> - io:format("otp71922_mgc_verify_handle_connect -> unknown" - "~n Else: ~p~n", [Else]), + e("otp71922_mgc_verify_handle_connect -> invalid handle-connect: " + "~n Extected: handle_connect" + "~n Received: ~p", [Else]), {error, Else, ok}. otp71922_mgc_verify_service_change_req_fun(Mid) -> @@ -8266,8 +8432,8 @@ otp71922_mgc_verify_service_change_req_fun(Mid) -> otp71922_mgc_verify_service_change_req( {handle_trans_request, _, ?VERSION, [AR]}, Mid) -> - io:format("otp71922_mgc_verify_service_change_req -> ok" - "~n AR: ~p~n", [AR]), + i("otp71922_mgc_verify_service_change_req -> ok" + "~n AR: ~p", [AR]), case AR of #'ActionRequest'{commandRequests = [CR]} -> case CR of @@ -8478,8 +8644,11 @@ otp71922_mg_event_sequence(text, tcp, Mid) -> {transport_module, megaco_tcp} ], ServiceChangeReq = [otp71922_mg_service_change_request_ar(Mid, 1)], - Tid = #megaco_term_id{id = ["00000000","00000000","01101101"]}, - NR = fun(Cid, Rid) -> + %% This is so that we can match notify request and reply + Tid1 = #megaco_term_id{id = ["00000000","00000001","01101101"]}, + Tid2 = #megaco_term_id{id = ["00000000","00000010","01101101"]}, + Tid3 = #megaco_term_id{id = ["00000000","00000011","01101101"]}, + NR = fun(Cid, Rid, Tid) -> [otp71922_mg_notify_request_ar(Rid, Tid, Cid)] end, ConnectVerify = ?otp71922_mg_verify_handle_connect_fun(), @@ -8512,20 +8681,20 @@ otp71922_mg_event_sequence(text, tcp, Mid) -> {megaco_update_conn_info, trans_ack, true}, {megaco_update_conn_info, trans_req, true}, {megaco_conn_info, all}, - {megaco_cast, NR(1,1), []}, - {megaco_cast, NR(1,2), []}, - {megaco_cast, NR(1,3), []}, + {megaco_cast, NR(1,1,Tid1), []}, + {megaco_cast, NR(1,2,Tid2), []}, + {megaco_cast, NR(1,3,Tid3), []}, {megaco_callback, handle_trans_reply, NotifyReplyVerify}, {megaco_callback, handle_trans_reply, NotifyReplyVerify}, {megaco_callback, handle_trans_reply, NotifyReplyVerify}, {megaco_update_conn_info, trans_timer, 120000}, - {megaco_cast, NR(2,1), []}, - {megaco_cast, NR(2,2), []}, - {megaco_cast, NR(2,3), []}, + {megaco_cast, NR(2,1,Tid1), []}, + {megaco_cast, NR(2,2,Tid2), []}, + {megaco_cast, NR(2,3,Tid3), []}, {megaco_callback, handle_trans_request, NotifyReqVerify}, - {megaco_callback, handle_trans_reply, NotifyReplyVerify}, - {megaco_callback, handle_trans_reply, NotifyReplyVerify}, - {megaco_callback, handle_trans_reply, NotifyReplyVerify}, + {megaco_callback, handle_trans_reply, NotifyReplyVerify}, + {megaco_callback, handle_trans_reply, NotifyReplyVerify}, + {megaco_callback, handle_trans_reply, NotifyReplyVerify}, {sleep, 3000}, megaco_stop_user, megaco_stop, @@ -8534,18 +8703,17 @@ otp71922_mg_event_sequence(text, tcp, Mid) -> EvSeq. otp71922_mg_verify_handle_connect({handle_connect, CH, ?VERSION}) -> - io:format("otp71922_mg_verify_handle_connect -> ok" - "~n CH: ~p~n", [CH]), + i("received expected handle_connect:" + "~n CH: ~p", [CH]), {ok, CH, ok}; otp71922_mg_verify_handle_connect(Else) -> - io:format("otp71922_mg_verify_handle_connect -> unknown" - "~n Else: ~p~n", [Else]), + e("otp71922_mg_verify_handle_connect -> received unexpected:" + "~n Expected: handle_connect" + "~n Received: ~p", [Else]), {error, Else, ok}. otp71922_mg_verify_service_change_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> - io:format("otp71922_mg_verify_service_change_reply -> ok" - "~n AR: ~p~n", [AR]), case AR of #'ActionReply'{commandReply = [SCR]} -> case SCR of @@ -8559,70 +8727,55 @@ otp71922_mg_verify_service_change_reply({handle_trans_reply, _CH, ?VERSION, {serviceChangeResParms, #'ServiceChangeResParm'{ serviceChangeMgcId = _RemoteMid}} -> + i("received expected handle_trans_reply " + "(service change) with ok" + "~n AR: ~p", [AR]), {ok, AR, ok}; {Tag, Val} -> + e("received expected handle_trans_reply " + "(service change) with error" + "~n Tag: ~p" + "~n Val: ~p", [Tag, Val]), Err = {invalid_service_change_result, Tag, Val}, {error, Err, ok} end; _ -> + e("received expected handle_trans_reply " + "(service change) with error" + "~n Tid: ~p", [Tid]), Err = {invalid_termination_id, Tid}, {error, Err, ok} end; {Tag, Val} -> + e("received expected handle_trans_reply " + "(action reply) with error" + "~n Tag: ~p" + "~n Val: ~p", [Tag, Val]), Err = {invalid_command_reply, Tag, Val}, {error, Err, ok} end; _ -> + e("received expected handle_trans_reply with error" + "~n AR: ~p", [AR]), Err = {invalid_action_reply, AR}, {error, Err, ok} end; otp71922_mg_verify_service_change_reply(Else) -> - io:format("otp71922_mg_verify_service_change_reply -> unknown" - "~n Else: ~p~n", [Else]), + e("invalid service change reply:" + "~n Expected: handle_trans_reply" + "~n Received: ~p", [Else]), {error, Else, ok}. -%% otp71922_mg_verify_notify_request_fun() -> -%% fun(Ev) -> -%% otp71922_mg_verify_notify_request(Ev) -%% end. - -%% otp71922_mg_verify_notify_request( -%% {handle_trans_request, _, ?VERSION, [AR]}) -> -%% io:format("otp71922_mg_verify_notify_request -> ok" -%% "~n AR: ~p~n", [AR]), -%% case AR of -%% #'ActionRequest'{contextId = 1 = Cid, -%% commandRequests = [CR]} -> -%% #'CommandRequest'{command = Cmd} = CR, -%% {notifyReq, NR} = Cmd, -%% #'NotifyRequest'{terminationID = [Tid], -%% observedEventsDescriptor = OED, -%% errorDescriptor = asn1_NOVALUE} = NR, -%% #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, -%% #'ObservedEvent'{eventName = "al/of"} = OE, -%% Reply = {discard_ack, [otp71922_mg_notify_reply_ar(Cid, Tid)]}, -%% {ok, AR, Reply}; -%% _ -> -%% ED = otp71922_err_desc(AR), -%% ErrReply = {discard_ack, ED}, -%% {error, AR, ErrReply} -%% end; -%% otp71922_mg_verify_notify_request(Else) -> -%% io:format("otp71922_mg_verify_notify_request -> unknown" -%% "~n Else: ~p~n", [Else]), -%% ED = otp71922_err_desc(Else), -%% ErrReply = {discard_ack, ED}, -%% {error, Else, ErrReply}. - otp71922_mg_verify_notify_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> - io:format("otp71922_mg_verify_notify_reply -> ok" - "~n AR: ~p~n", [AR]), + i("received expected handle_notify_reply -> ok" + "~n AR: ~p", [AR]), {ok, AR, ok}; otp71922_mg_verify_notify_reply(Else) -> - io:format("otp71922_mg_verify_notify_reply -> unknown" - "~n Else: ~p~n", [Else]), + e("otp71922_mg_verify_notify_reply -> invalid notify reply" + "~n Expected: handle_trans_reply with ok" + "~n Received: ~p", [Else]), {error, Else, ok}. otp71922_mg_service_change_request_ar(_Mid, Cid) -> @@ -8634,18 +8787,6 @@ otp71922_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -%% otp71922_mg_service_change_request_msg(Mid, TransId, Cid) -> -%% AR = otp71922_mg_service_change_request_ar(Mid, Cid), -%% TR = cre_transReq(TransId, [AR]), -%% Trans = cre_transaction(TR), -%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), -%% cre_megacoMessage(Mess). - -%% otp71922_mg_notify_reply_ar(Cid, TermId) -> -%% NR = cre_notifyReply([TermId]), -%% CR = cre_cmdReply(NR), -%% cre_actionReply(Cid, [CR]). - otp71922_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), Ev = cre_obsEvent("al/of", TT), @@ -8655,13 +8796,6 @@ otp71922_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -%% otp71922_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> -%% AR = otp71922_mg_notify_request_ar(Rid, TermId, Cid), -%% TR = cre_transReq(TransId, [AR]), -%% Trans = cre_transaction(TR), -%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), -%% cre_megacoMessage(Mess). - %% %% Common functions for the multi_trans_req_timeout test case @@ -8678,22 +8812,27 @@ otp_7192_3(suite) -> otp_7192_3(doc) -> ["Same as otp_7192_2 but transport is UDP instead of TCP"]; otp_7192_3(Config) when is_list(Config) -> - put(verbosity, ?TEST_VERBOSITY), - put(sname, "TEST"), - put(tc, otp_7192_3), - i("starting"), - - MgcNode = make_node_name(mgc), - MgNode = make_node_name(mg), - d("start nodes: " - "~n MGC Node: ~p" - "~n MG Node: ~p", - [MgcNode, MgNode]), - - MgMid = {deviceName,"mg"}, + Pre = fun() -> + MgcNode = make_node_name(mgc), + MgNode = make_node_name(mg), + d("start nodes: " + "~n MGC Node: ~p" + "~n MG Node: ~p", + [MgcNode, MgNode]), + Nodes = [MgcNode, MgNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_otp_7192_3/1, + Post = fun(Nodes) -> + d("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(otp_7192_3, Pre, Case, Post). - ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), +do_otp_7192_3([MgcNode, MgNode]) -> + MgMid = {deviceName,"mg"}, d("[MGC] start the simulator "), {ok, Mgc} = megaco_test_megaco_generator:start_link("MGC", MgcNode), @@ -9456,6 +9595,16 @@ await_completion(Ids, Timeout) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +try_tc(TCName, Pre, Case, Post) -> + try_tc(TCName, "TEST", ?TEST_VERBOSITY, Pre, Case, Post). + +try_tc(TCName, Name, Verbosity, Pre, Case, Post) -> + ?TRY_TC(TCName, Name, Verbosity, Pre, Case, Post). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + sleep(X) -> receive after X -> ok end. %% error_msg(F,A) -> error_logger:error_msg(F ++ "~n",A). @@ -9472,14 +9621,14 @@ p(F, A) -> %% e(F, []). e(F, A) -> - print(error, "ERR", F, A). + print(error, "ERROR", F, A). i(F) -> i(F, []). i(F, A) -> - print(info, "INF", F, A). + print(info, "INFO", F, A). d(F) -> @@ -9500,11 +9649,13 @@ printable(info, info) -> true; printable(error, _) -> true; printable(_,_) -> false. - print2(true, P, F, A) -> - S = ?F("*** [~s] ~s ~p ~w ***" + S = ?F("*** [~s] ~s ~p~s ***" "~n " ++ F ++ "~n" - "~n", [?FTS(), P, self(), get(tc) | A]), + "~n", [?FTS(), P, self(), case get(tc) of + undefined -> ""; + TC -> " " ++ atom_to_list(TC) + end | A]), io:format("~s", [S]); print2(_, _, _, _) -> ok. diff --git a/lib/megaco/test/megaco_udp_SUITE.erl b/lib/megaco/test/megaco_udp_SUITE.erl index 26783d5faf..05910e50a9 100644 --- a/lib/megaco/test/megaco_udp_SUITE.erl +++ b/lib/megaco/test/megaco_udp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2019. All Rights Reserved. +%% Copyright Ericsson AB 2000-2020 All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -63,14 +63,14 @@ %% Macros %%---------------------------------------------------------------------- +-define(CH, megaco_test_command_handler). +-define(TEST_VERBOSITY, debug). + + %%---------------------------------------------------------------------- %% Records %%---------------------------------------------------------------------- --record(command, {id, desc, cmd}). --record(server, {parent, transport_ref, control_pid, handle, remote}). --record(client, {parent, transport_ref, control_pid, handle, remote}). - %%====================================================================== %% Common Test interface functions @@ -239,17 +239,22 @@ start_and_stop(doc) -> ["This test case sets up a connection and then cloises it. " "No data is sent. "]; start_and_stop(Config) when is_list(Config) -> - put(sname, "start_and_stop"), - p("BEGIN TEST-CASE"), - - process_flag(trap_exit, true), - - p("create nodes"), - ServerNode = make_node_name(server), - ClientNode = make_node_name(client), - Nodes = [ServerNode, ClientNode], - ok = megaco_test_lib:start_nodes(Nodes, ?FILE, ?LINE), - + Pre = fun() -> + p("create nodes"), + ServerNode = make_node_name(server), + ClientNode = make_node_name(client), + Nodes = [ServerNode, ClientNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_start_and_stop/1, + Post = fun(Nodes) -> + p("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(start_and_stop, Pre, Case, Post). + +do_start_and_stop([ServerNode, ClientNode]) -> %% Create command sequences p("create command sequences"), ServerPort = 2944, @@ -279,7 +284,7 @@ start_and_stop(Config) when is_list(Config) -> {error, server_timeout} end, - ok = await_command_handler_completion([Server, Client], timer:seconds(20)), + ok = await_command_handler_completion([Server, Client], ?SECS(20)), p("done"), ok. @@ -288,47 +293,47 @@ start_and_stop_server_commands(Port) -> Opts = [{port, Port}], Self = self(), [ - #command{id = 1, - desc = "Command sequence init", - cmd = fun(State) -> - {ok, State#server{parent = Self}} - end}, - - #command{id = 2, - desc = "Start transport", - cmd = fun(State) -> - server_start_transport(State) - end}, - - #command{id = 3, - desc = "Open", - cmd = fun(State) -> - server_open(State, Opts) - end}, - - #command{id = 4, - desc = "Notify operational", - cmd = fun(State) -> - server_notify_operational(State) - end}, - - #command{id = 5, - desc = "Await nothing", - cmd = fun(State) -> - server_await_nothing(State, 5000) - end}, - - #command{id = 6, - desc = "Close", - cmd = fun(State) -> - server_close(State) - end}, - - #command{id = 7, - desc = "Stop", - cmd = fun(State) -> - server_stop_transport(State) - end} + #{id => 1, + desc => "Command sequence init", + cmd => fun(State) -> + {ok, State#{parent => Self}} + end}, + + #{id => 2, + desc => "Start transport", + cmd => fun(State) -> + server_start_transport(State) + end}, + + #{id => 3, + desc => "Open", + cmd => fun(State) -> + server_open(State, Opts) + end}, + + #{id => 4, + desc => "Notify operational", + cmd => fun(State) -> + server_notify_operational(State) + end}, + + #{id => 5, + desc => "Await nothing", + cmd => fun(State) -> + server_await_nothing(State, 5000) + end}, + + #{id => 6, + desc => "Close", + cmd => fun(State) -> + server_close(State) + end}, + + #{id => 7, + desc => "Stop", + cmd => fun(State) -> + server_stop_transport(State) + end} ]. @@ -336,47 +341,47 @@ start_and_stop_client_commands(ServerPort, _ServerHost) -> Opts = [{port, ServerPort}], Self = self(), [ - #command{id = 1, - desc = "Command sequence init", - cmd = fun(State) -> - {ok, State#client{parent = Self}} - end}, - - #command{id = 2, - desc = "Start transport", - cmd = fun(State) -> - client_start_transport(State) - end}, - - #command{id = 3, - desc = "Open", - cmd = fun(State) -> - client_open(State, Opts) - end}, - - #command{id = 4, - desc = "Await continue", - cmd = fun(State) -> - client_await_continue_signal(State, 5000) - end}, - - #command{id = 5, - desc = "Await nothing", - cmd = fun(State) -> - client_await_nothing(State, 5000) - end}, - - #command{id = 6, - desc = "Close", - cmd = fun(State) -> - client_close(State) - end}, - - #command{id = 7, - desc = "Stop transport", - cmd = fun(State) -> - client_stop_transport(State) - end} + #{id => 1, + desc => "Command sequence init", + cmd => fun(State) -> + {ok, State#{parent => Self}} + end}, + + #{id => 2, + desc => "Start transport", + cmd => fun(State) -> + client_start_transport(State) + end}, + + #{id => 3, + desc => "Open", + cmd => fun(State) -> + client_open(State, Opts) + end}, + + #{id => 4, + desc => "Await continue", + cmd => fun(State) -> + client_await_continue_signal(State, 5000) + end}, + + #{id => 5, + desc => "Await nothing", + cmd => fun(State) -> + client_await_nothing(State, 5000) + end}, + + #{id => 6, + desc => "Close", + cmd => fun(State) -> + client_close(State) + end}, + + #{id => 7, + desc => "Stop transport", + cmd => fun(State) -> + client_stop_transport(State) + end} ]. @@ -394,17 +399,22 @@ sendreceive(suite) -> sendreceive(doc) -> ["Test send and receive with the UDP transport. "]; sendreceive(Config) when is_list(Config) -> - put(sname, "sendreceive"), - p("BEGIN TEST-CASE"), - - process_flag(trap_exit, true), - - p("create nodes"), - ServerNode = make_node_name(server), - ClientNode = make_node_name(client), - Nodes = [ServerNode, ClientNode], - ok = megaco_test_lib:start_nodes(Nodes, ?FILE, ?LINE), - + Pre = fun() -> + p("create nodes"), + ServerNode = make_node_name(server), + ClientNode = make_node_name(client), + Nodes = [ServerNode, ClientNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_sendreceive/1, + Post = fun(Nodes) -> + p("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(sendreceive, Pre, Case, Post). + +do_sendreceive([ServerNode, ClientNode]) -> %% Create command sequences p("create command sequences"), ServerPort = 2944, @@ -434,7 +444,7 @@ sendreceive(Config) when is_list(Config) -> {error, server_timeout} end, - ok = await_command_handler_completion([Server, Client], timer:seconds(20)), + ok = await_command_handler_completion([Server, Client], ?SECS(20)), p("done"), ok. @@ -443,168 +453,167 @@ sendreceive_server_commands(Port) -> Opts = [{port, Port}], Self = self(), [ - #command{id = 1, - desc = "Command sequence init", - cmd = fun(State) -> - {ok, State#server{parent = Self}} - end}, - - #command{id = 2, - desc = "Start transport", - cmd = fun(State) -> - server_start_transport(State) - end}, - - #command{id = 3, - desc = "Open", - cmd = fun(State) -> - server_open(State, Opts) - end}, - - #command{id = 4, - desc = "Notify operational", - cmd = fun(State) -> - server_notify_operational(State) - end}, - - #command{id = 5, - desc = "Await initial message (ping)", - cmd = fun(State) -> - server_await_initial_message(State, "ping", 5000) - end}, - - #command{id = 6, - desc = "Send reply (pong) to initial message", - cmd = fun(State) -> - server_send_message(State, "pong") - end}, - - #command{id = 7, - desc = "Await nothing before sending a message (hejsan)", - cmd = fun(State) -> - server_await_nothing(State, 1000) - end}, - - #command{id = 8, - desc = "Send message (hejsan)", - cmd = fun(State) -> - server_send_message(State, "hejsan") - end}, - - #command{id = 9, - desc = "Await reply (hoppsan) to message", - cmd = fun(State) -> - server_await_message(State, "hoppsan", 1000) - end}, - - #command{id = 10, - desc = "Await nothing before closing", - cmd = fun(State) -> - server_await_nothing(State, 1000) - end}, - - #command{id = 11, - desc = "Close", - cmd = fun(State) -> - server_close(State) - end}, - - #command{id = 12, - desc = "Await nothing before stopping transport", - cmd = fun(State) -> - server_await_nothing(State, 1000) - end}, - - #command{id = 13, - desc = "Stop", - cmd = fun(State) -> - server_stop_transport(State) - end} - + #{id => 1, + desc => "Command sequence init", + cmd => fun(State) -> + {ok, State#{parent => Self}} + end}, + + #{id => 2, + desc => "Start transport", + cmd => fun(State) -> + server_start_transport(State) + end}, + + #{id => 3, + desc => "Open", + cmd => fun(State) -> + server_open(State, Opts) + end}, + + #{id => 4, + desc => "Notify operational", + cmd => fun(State) -> + server_notify_operational(State) + end}, + + #{id => 5, + desc => "Await initial message (ping)", + cmd => fun(State) -> + server_await_initial_message(State, "ping", 5000) + end}, + + #{id => 6, + desc => "Send reply (pong) to initial message", + cmd => fun(State) -> + server_send_message(State, "pong") + end}, + + #{id => 7, + desc => "Await nothing before sending a message (hejsan)", + cmd => fun(State) -> + server_await_nothing(State, 1000) + end}, + + #{id => 8, + desc => "Send message (hejsan)", + cmd => fun(State) -> + server_send_message(State, "hejsan") + end}, + + #{id => 9, + desc => "Await reply (hoppsan) to message", + cmd => fun(State) -> + server_await_message(State, "hoppsan", 1000) + end}, + + #{id => 10, + desc => "Await nothing before closing", + cmd => fun(State) -> + server_await_nothing(State, 1000) + end}, + + #{id => 11, + desc => "Close", + cmd => fun(State) -> + server_close(State) + end}, + + #{id => 12, + desc => "Await nothing before stopping transport", + cmd => fun(State) -> + server_await_nothing(State, 1000) + end}, + + #{id => 13, + desc => "Stop", + cmd => fun(State) -> + server_stop_transport(State) + end} ]. sendreceive_client_commands(ServerPort, ServerHost) -> OwnPort = ServerPort+1, - Opts = [{port, OwnPort}], - Self = self(), + Opts = [{port, OwnPort}], + Self = self(), [ - #command{id = 1, - desc = "Command sequence init", - cmd = fun(State) -> - {ok, State#client{parent = Self}} - end}, - - #command{id = 2, - desc = "Start transport", - cmd = fun(State) -> - client_start_transport(State) - end}, - - #command{id = 3, - desc = "Open", - cmd = fun(State) -> - client_open(State, Opts) - end}, - - #command{id = 4, - desc = "Await continue", - cmd = fun(State) -> - client_await_continue_signal(State, 5000) - end}, - - #command{id = 5, - desc = "Connect", - cmd = fun(State) -> - client_connect(State, ServerHost, ServerPort) - end}, - - #command{id = 6, - desc = "Send initial message (ping)", - cmd = fun(State) -> - client_send_message(State, "ping") - end}, - - #command{id = 7, - desc = "Await reply (pong) to initial message", - cmd = fun(State) -> - client_await_message(State, "pong", 1000) - end}, - - #command{id = 8, - desc = "Await message (hejsan)", - cmd = fun(State) -> - client_await_message(State, "hejsan", 5000) - end}, - - #command{id = 9, - desc = "Send reply (hoppsan) to message", - cmd = fun(State) -> - client_send_message(State, "hoppsan") - end}, - - #command{id = 10, - desc = "Await nothing before closing", - cmd = fun(State) -> - client_await_nothing(State, 1000) - end}, - - #command{id = 11, - desc = "Close", - cmd = fun(State) -> - client_close(State) - end}, - - #command{id = 12, - desc = "Await nothing before stopping transport", - cmd = fun(State) -> - client_await_nothing(State, 1000) - end}, - - #command{id = 13, - desc = "Stop transport", - cmd = fun(State) -> - client_stop_transport(State) - end} + #{id => 1, + desc => "Command sequence init", + cmd => fun(State) -> + {ok, State#{parent => Self}} + end}, + + #{id => 2, + desc => "Start transport", + cmd => fun(State) -> + client_start_transport(State) + end}, + + #{id => 3, + desc => "Open", + cmd => fun(State) -> + client_open(State, Opts) + end}, + + #{id => 4, + desc => "Await continue", + cmd => fun(State) -> + client_await_continue_signal(State, 5000) + end}, + + #{id => 5, + desc => "Connect", + cmd => fun(State) -> + client_connect(State, ServerHost, ServerPort) + end}, + + #{id => 6, + desc => "Send initial message (ping)", + cmd => fun(State) -> + client_send_message(State, "ping") + end}, + + #{id => 7, + desc => "Await reply (pong) to initial message", + cmd => fun(State) -> + client_await_message(State, "pong", 1000) + end}, + + #{id => 8, + desc => "Await message (hejsan)", + cmd => fun(State) -> + client_await_message(State, "hejsan", 5000) + end}, + + #{id => 9, + desc => "Send reply (hoppsan) to message", + cmd => fun(State) -> + client_send_message(State, "hoppsan") + end}, + + #{id => 10, + desc => "Await nothing before closing", + cmd => fun(State) -> + client_await_nothing(State, 1000) + end}, + + #{id => 11, + desc => "Close", + cmd => fun(State) -> + client_close(State) + end}, + + #{id => 12, + desc => "Await nothing before stopping transport", + cmd => fun(State) -> + client_await_nothing(State, 1000) + end}, + + #{id => 13, + desc => "Stop transport", + cmd => fun(State) -> + client_stop_transport(State) + end} ]. @@ -615,17 +624,22 @@ block_unblock(suite) -> block_unblock(doc) -> ["Test the block/unblock functions of the UDP transport. "]; block_unblock(Config) when is_list(Config) -> - put(sname, "block_unblock"), - p("BEGIN TEST-CASE"), - - process_flag(trap_exit, true), - - p("create nodes"), - ServerNode = make_node_name(server), - ClientNode = make_node_name(client), - Nodes = [ServerNode, ClientNode], - ok = megaco_test_lib:start_nodes(Nodes, ?FILE, ?LINE), - + Pre = fun() -> + p("create nodes"), + ServerNode = make_node_name(server), + ClientNode = make_node_name(client), + Nodes = [ServerNode, ClientNode], + ok = ?START_NODES(Nodes), + Nodes + end, + Case = fun do_block_unblock/1, + Post = fun(Nodes) -> + p("stop nodes"), + ?STOP_NODES(lists:reverse(Nodes)) + end, + try_tc(block_unblock, Pre, Case, Post). + +do_block_unblock([ServerNode, ClientNode]) -> %% Create command sequences p("create command sequences"), ServerPort = 2944, @@ -674,7 +688,7 @@ block_unblock(Config) when is_list(Config) -> {error, timeout} end, - ok = await_command_handler_completion([Server, Client], timer:seconds(20)), + ok = await_command_handler_completion([Server, Client], ?SECS(20)), p("done"), ok. @@ -683,198 +697,198 @@ block_unblock_server_commands(Port) -> Opts = [{port, Port}], Self = self(), [ - #command{id = 1, - desc = "Command sequence init", - cmd = fun(State) -> - {ok, State#server{parent = Self}} - end}, - - #command{id = 2, - desc = "Start transport", - cmd = fun(State) -> - server_start_transport(State) - end}, - - #command{id = 3, - desc = "Open", - cmd = fun(State) -> - server_open(State, Opts) - end}, - - #command{id = 4, - desc = "Notify operational", - cmd = fun(State) -> - server_notify_operational(State) - end}, - - #command{id = 5, - desc = "Await initial message (ping)", - cmd = fun(State) -> - server_await_initial_message(State, "ping", 5000) - end}, - - #command{id = 6, - desc = "Send reply (pong) to initial message", - cmd = fun(State) -> - server_send_message(State, "pong") - end}, - - #command{id = 7, - desc = "Await continue", - cmd = fun(State) -> - server_await_continue_signal(State, 5000) - end}, - - #command{id = 8, - desc = "Send message (hejsan)", - cmd = fun(State) -> - server_send_message(State, "hejsan") - end}, - - #command{id = 9, - desc = "Await nothing before receiving (hoppsan) reply", - cmd = fun(State) -> - server_await_nothing(State, 4000) - end}, - - #command{id = 10, - desc = "Await reply (hoppsan) to message", - cmd = fun(State) -> - server_await_message(State, "hoppsan", 2000) - end}, - - #command{id = 11, - desc = "Await nothing before closing", - cmd = fun(State) -> - server_await_nothing(State, 1000) - end}, - - #command{id = 12, - desc = "Close", - cmd = fun(State) -> - server_close(State) - end}, - - #command{id = 13, - desc = "Await nothing before stopping transport", - cmd = fun(State) -> - server_await_nothing(State, 1000) - end}, - - #command{id = 14, - desc = "Stop", - cmd = fun(State) -> - server_stop_transport(State) - end} + #{id => 1, + desc => "Command sequence init", + cmd => fun(State) -> + {ok, State#{parent => Self}} + end}, + + #{id => 2, + desc => "Start transport", + cmd => fun(State) -> + server_start_transport(State) + end}, + + #{id => 3, + desc => "Open", + cmd => fun(State) -> + server_open(State, Opts) + end}, + + #{id => 4, + desc => "Notify operational", + cmd => fun(State) -> + server_notify_operational(State) + end}, + + #{id => 5, + desc => "Await initial message (ping)", + cmd => fun(State) -> + server_await_initial_message(State, "ping", 5000) + end}, + + #{id => 6, + desc => "Send reply (pong) to initial message", + cmd => fun(State) -> + server_send_message(State, "pong") + end}, + + #{id => 7, + desc => "Await continue", + cmd => fun(State) -> + server_await_continue_signal(State, 5000) + end}, + + #{id => 8, + desc => "Send message (hejsan)", + cmd => fun(State) -> + server_send_message(State, "hejsan") + end}, + + #{id => 9, + desc => "Await nothing before receiving (hoppsan) reply", + cmd => fun(State) -> + server_await_nothing(State, 4000) + end}, + + #{id => 10, + desc => "Await reply (hoppsan) to message", + cmd => fun(State) -> + server_await_message(State, "hoppsan", 2000) + end}, + + #{id => 11, + desc => "Await nothing before closing", + cmd => fun(State) -> + server_await_nothing(State, 1000) + end}, + + #{id => 12, + desc => "Close", + cmd => fun(State) -> + server_close(State) + end}, + + #{id => 13, + desc => "Await nothing before stopping transport", + cmd => fun(State) -> + server_await_nothing(State, 1000) + end}, + + #{id => 14, + desc => "Stop", + cmd => fun(State) -> + server_stop_transport(State) + end} ]. block_unblock_client_commands(ServerPort, ServerHost) -> OwnPort = ServerPort+1, - Opts = [{port, OwnPort}], - Self = self(), + Opts = [{port, OwnPort}], + Self = self(), [ - #command{id = 1, - desc = "Command sequence init", - cmd = fun(State) -> - {ok, State#client{parent = Self}} - end}, - - #command{id = 2, - desc = "Start transport", - cmd = fun(State) -> - client_start_transport(State) - end}, - - #command{id = 3, - desc = "Open", - cmd = fun(State) -> - client_open(State, Opts) - end}, - - #command{id = 4, - desc = "Await continue", - cmd = fun(State) -> - client_await_continue_signal(State, 5000) - end}, - - #command{id = 5, - desc = "[pseudo] Connect", - cmd = fun(State) -> - client_connect(State, ServerHost, ServerPort) - end}, - - #command{id = 6, - desc = "Send initial message (ping)", - cmd = fun(State) -> - client_send_message(State, "ping") - end}, - - #command{id = 7, - desc = "Await reply (pong) to initial message", - cmd = fun(State) -> - client_await_message(State, "pong", 1000) - end}, - - #command{id = 8, - desc = "Block", - cmd = fun(State) -> - client_block(State) - end}, - - #command{id = 9, - desc = "Notify blocked", - cmd = fun(State) -> - client_notify_blocked(State) - end}, - - #command{id = 10, - desc = "Await nothing before unblocking", - cmd = fun(State) -> - client_await_nothing(State, 5000) - end}, - - #command{id = 11, - desc = "Unblock", - cmd = fun(State) -> - client_unblock(State) - end}, - - #command{id = 8, - desc = "Await message (hejsan)", - cmd = fun(State) -> - client_await_message(State, "hejsan", 5000) - end}, - - #command{id = 9, - desc = "Send reply (hoppsan) to message", - cmd = fun(State) -> - client_send_message(State, "hoppsan") - end}, - - #command{id = 10, - desc = "Await nothing before closing", - cmd = fun(State) -> - client_await_nothing(State, 1000) - end}, - - #command{id = 11, - desc = "Close", - cmd = fun(State) -> - client_close(State) - end}, - - #command{id = 12, - desc = "Await nothing before stopping transport", - cmd = fun(State) -> - client_await_nothing(State, 1000) - end}, - - #command{id = 13, - desc = "Stop transport", - cmd = fun(State) -> - client_stop_transport(State) - end} + #{id => 1, + desc => "Command sequence init", + cmd => fun(State) -> + {ok, State#{parent => Self}} + end}, + + #{id => 2, + desc => "Start transport", + cmd => fun(State) -> + client_start_transport(State) + end}, + + #{id => 3, + desc => "Open", + cmd => fun(State) -> + client_open(State, Opts) + end}, + + #{id => 4, + desc => "Await continue", + cmd => fun(State) -> + client_await_continue_signal(State, 5000) + end}, + + #{id => 5, + desc => "[pseudo] Connect", + cmd => fun(State) -> + client_connect(State, ServerHost, ServerPort) + end}, + + #{id => 6, + desc => "Send initial message (ping)", + cmd => fun(State) -> + client_send_message(State, "ping") + end}, + + #{id => 7, + desc => "Await reply (pong) to initial message", + cmd => fun(State) -> + client_await_message(State, "pong", 1000) + end}, + + #{id => 8, + desc => "Block", + cmd => fun(State) -> + client_block(State) + end}, + + #{id => 9, + desc => "Notify blocked", + cmd => fun(State) -> + client_notify_blocked(State) + end}, + + #{id => 10, + desc => "Await nothing before unblocking", + cmd => fun(State) -> + client_await_nothing(State, 5000) + end}, + + #{id => 11, + desc => "Unblock", + cmd => fun(State) -> + client_unblock(State) + end}, + + #{id => 8, + desc => "Await message (hejsan)", + cmd => fun(State) -> + client_await_message(State, "hejsan", 5000) + end}, + + #{id => 9, + desc => "Send reply (hoppsan) to message", + cmd => fun(State) -> + client_send_message(State, "hoppsan") + end}, + + #{id => 10, + desc => "Await nothing before closing", + cmd => fun(State) -> + client_await_nothing(State, 1000) + end}, + + #{id => 11, + desc => "Close", + cmd => fun(State) -> + client_close(State) + end}, + + #{id => 12, + desc => "Await nothing before stopping transport", + cmd => fun(State) -> + client_await_nothing(State, 1000) + end}, + + #{id => 13, + desc => "Stop transport", + cmd => fun(State) -> + client_stop_transport(State) + end} ]. @@ -947,35 +961,34 @@ process_received_message(ReceiveHandle, ControlPid, SendHandle, BinMsg) %% ------- Server command handler and utility functions ---------- server_start_command_handler(Node, Commands) -> - start_command_handler(Node, Commands, #server{}, "server"). + start_command_handler(Node, Commands, #{}, "server"). -server_start_transport(State) when is_record(State, server) -> +server_start_transport(State) when is_map(State) -> case (catch megaco_udp:start_transport()) of {ok, Ref} -> - {ok, State#server{transport_ref = Ref}}; + {ok, State#{transport_ref => Ref}}; Error -> Error end. -server_open(#server{transport_ref = Ref} = State, Options) - when is_record(State, server) andalso is_list(Options) -> +server_open(#{transport_ref := Ref} = State, Options) + when is_list(Options) -> Opts = [{receive_handle, self()}, {module, ?MODULE} | Options], case (catch megaco_udp:open(Ref, Opts)) of {ok, Socket, ControlPid} -> - {ok, State#server{handle = {socket, Socket}, % Temporary - control_pid = ControlPid}}; + {ok, State#{handle => {socket, Socket}, % Temporary + control_pid => ControlPid}}; {error, {could_not_open_udp_port, eaddrinuse}} -> {skip, {server, eaddrinuse}}; Error -> Error end. -server_notify_operational(#server{parent = Parent} = State) - when is_record(State, server) -> +server_notify_operational(#{parent := Parent} = State) -> Parent ! {operational, self()}, {ok, State}. -server_await_continue_signal(#server{parent = Parent} = State, Timeout) -> +server_await_continue_signal(#{parent := Parent} = State, Timeout) -> receive {continue, Parent} -> {ok, State} @@ -984,13 +997,13 @@ server_await_continue_signal(#server{parent = Parent} = State, Timeout) -> end. server_await_initial_message(State, InitialMessage, Timeout) - when is_record(State, server) -> + when is_map(State) -> receive {receive_message, {ControlPid, Handle, InitialMessage}} -> p("received expected event with: " "~n ControlPid: ~p" "~n Handle: ~p", [ControlPid, Handle]), - NewState = State#server{handle = Handle}, + NewState = State#{handle => Handle}, {ok, NewState}; Any -> @@ -1001,7 +1014,7 @@ server_await_initial_message(State, InitialMessage, Timeout) {error, timeout} end. -server_send_message(#server{handle = Handle} = State, Message) -> +server_send_message(#{handle := Handle} = State, Message) -> Bin = if is_list(Message) -> list_to_binary(Message); @@ -1012,7 +1025,7 @@ server_send_message(#server{handle = Handle} = State, Message) -> {ok, State}. server_await_nothing(State, Timeout) - when is_record(State, server) -> + when is_map(State) -> receive Any -> p("received unexpected event: ~p", [Any]), @@ -1022,9 +1035,8 @@ server_await_nothing(State, Timeout) {ok, State} end. - server_await_message(State, ExpectMessage, Timeout) - when is_record(State, server) -> + when is_map(State) -> receive {receive_message, {_, _, ExpectMessage}} -> p("received expected message [~p]", [ExpectMessage]), @@ -1038,57 +1050,47 @@ server_await_message(State, ExpectMessage, Timeout) {error, timeout} end. -server_close(#server{handle = {socket, Socket}} = State) -> +server_close(#{handle := {socket, Socket}} = State) -> megaco_udp:close(Socket), - {ok, State#server{handle = undefined, control_pid = undefined}}; -server_close(#server{handle = Handle} = State) + {ok, State#{handle => undefined, control_pid => undefined}}; +server_close(#{handle := Handle} = State) when (Handle =/= undefined) -> megaco_udp:close(Handle), - {ok, State#server{handle = undefined, control_pid = undefined}}. - -%% server_block(#server{handle = Handle} = State) -%% when (Handle =/= undefined) -> -%% megaco_udp:block(Handle), -%% {ok, State}. + {ok, State#{handle => undefined, control_pid => undefined}}. -%% server_unblock(#server{handle = Handle} = State) -%% when (Handle =/= undefined) -> -%% megaco_udp:unblock(Handle), -%% {ok, State}. - -server_stop_transport(#server{transport_ref = Ref} = State) +server_stop_transport(#{transport_ref := Ref} = State) when (Ref =/= undefined) -> megaco_udp:stop_transport(Ref), - {ok, State#server{transport_ref = undefined}}. + {ok, State#{transport_ref => undefined}}. %% ------- Client command handler and utility functions ---------- client_start_command_handler(Node, Commands) -> - start_command_handler(Node, Commands, #client{}, "client"). + start_command_handler(Node, Commands, #{}, "client"). -client_start_transport(State) when is_record(State, client) -> +client_start_transport(State) when is_map(State) -> case (catch megaco_udp:start_transport()) of {ok, Ref} -> - {ok, State#client{transport_ref = Ref}}; + {ok, State#{transport_ref => Ref}}; Error -> Error end. -client_open(#client{transport_ref = Ref} = State, Options) - when is_record(State, client) andalso is_list(Options) -> +client_open(#{transport_ref := Ref} = State, Options) + when is_list(Options) -> Opts = [{receive_handle, self()}, {module, ?MODULE} | Options], case (catch megaco_udp:open(Ref, Opts)) of {ok, Socket, ControlPid} -> - {ok, State#client{handle = {socket, Socket}, - control_pid = ControlPid}}; + {ok, State#{handle => {socket, Socket}, + control_pid => ControlPid}}; {error, {could_not_open_udp_port, eaddrinuse}} -> {skip, {client, eaddrinuse}}; Error -> Error end. -client_await_continue_signal(#client{parent = Parent} = State, Timeout) -> +client_await_continue_signal(#{parent := Parent} = State, Timeout) -> receive {continue, Parent} -> {ok, State} @@ -1096,12 +1098,12 @@ client_await_continue_signal(#client{parent = Parent} = State, Timeout) -> {error, timeout} end. -client_notify_blocked(#client{parent = Parent} = State) -> +client_notify_blocked(#{parent := Parent} = State) -> Parent ! {blocked, self()}, {ok, State}. client_await_nothing(State, Timeout) - when is_record(State, client) -> + when is_map(State) -> receive Any -> p("received unexpected event: ~p", [Any]), @@ -1110,11 +1112,11 @@ client_await_nothing(State, Timeout) {ok, State} end. -client_connect(#client{handle = {socket, Socket}} = State, Host, Port) -> +client_connect(#{handle := {socket, Socket}} = State, Host, Port) -> Handle = megaco_udp:create_send_handle(Socket, Host, Port), - {ok, State#client{handle = Handle}}. + {ok, State#{handle => Handle}}. -client_send_message(#client{handle = Handle} = State, Message) -> +client_send_message(#{handle := Handle} = State, Message) -> Bin = if is_list(Message) -> list_to_binary(Message); @@ -1125,7 +1127,7 @@ client_send_message(#client{handle = Handle} = State, Message) -> {ok, State}. client_await_message(State, ExpectMessage, Timeout) - when is_record(State, client) -> + when is_map(State) -> receive {receive_message, {_, _, ExpectMessage}} -> {ok, State}; @@ -1138,137 +1140,50 @@ client_await_message(State, ExpectMessage, Timeout) {error, timeout} end. -client_block(#client{handle = Handle} = State) +client_block(#{handle := Handle} = State) when (Handle =/= undefined) -> - megaco_udp:block(Handle), + ok = megaco_udp:block(Handle), {ok, State}. -client_unblock(#client{handle = Handle} = State) +client_unblock(#{handle := Handle} = State) when (Handle =/= undefined) -> - megaco_udp:unblock(Handle), + ok = megaco_udp:unblock(Handle), {ok, State}. -client_close(#client{handle = {socket, Socket}} = State) -> +client_close(#{handle := {socket, Socket}} = State) -> megaco_udp:close(Socket), - {ok, State#client{handle = undefined, control_pid = undefined}}; -client_close(#client{handle = Handle} = State) + {ok, State#{handle => undefined, control_pid => undefined}}; +client_close(#{handle := Handle} = State) when (Handle =/= undefined) -> megaco_udp:close(Handle), - {ok, State#client{handle = undefined, control_pid = undefined}}. + {ok, State#{handle => undefined, control_pid => undefined}}. -client_stop_transport(#client{transport_ref = Ref} = State) +client_stop_transport(#{transport_ref := Ref} = State) when (Ref =/= undefined) -> megaco_udp:stop_transport(Ref), - {ok, State#client{transport_ref = undefined}}. + {ok, State#{transport_ref => undefined}}. -%% -------- Command handler --------- +%% -------- Command handler interface --------- start_command_handler(Node, Commands, State, ShortName) -> - Fun = fun() -> - put(sname, ShortName), - process_flag(trap_exit, true), - Result = (catch command_handler(Commands, State)), - p("command handler terminated with: " - "~n Result: ~p", [Result]), - exit(Result) - end, - erlang:spawn_link(Node, Fun). - -command_handler([], State) -> - p("command_handler -> entry when done with" - "~n State: ~p", [State]), - {ok, State}; -command_handler([#command{id = Id, - desc = Desc, - cmd = Cmd}|Commands], State) -> - p("command_handler -> entry with" - "~n Id: ~p" - "~n Desc: ~p", [Id, Desc]), - case (catch Cmd(State)) of - {ok, NewState} -> - p("command_handler -> cmd ~w ok", [Id]), - command_handler(Commands, NewState); - {skip, _} = SKIP -> - p("command_handler -> cmd ~w skip", [Id]), - SKIP; - {error, Reason} -> - p("command_handler -> cmd ~w error: " - "~n Reason: ~p", [Id, Reason]), - {error, {cmd_error, Reason}}; - {'EXIT', Reason} -> - p("command_handler -> cmv ~w exit: " - "~n Reason: ~p", [Id, Reason]), - {error, {cmd_exit, Reason}}; - Error -> - p("command_handler -> cmd ~w failure: " - "~n Error: ~p", [Id, Error]), - {error, {cmd_failure, Error}} - end. + ?CH:start(Node, Commands, State, ShortName). await_command_handler_completion(Pids, Timeout) -> - await_command_handler_completion(Pids, [], [], Timeout). - -await_command_handler_completion([], [], _Good, _Timeout) -> - p("await_command_handler_completion -> entry when done"), - ok; -await_command_handler_completion([], Bad, Good, _Timeout) -> - p("await_command_handler_completion -> entry when done with bad result: " - "~n Bad: ~p" - "~n Good: ~p", [Bad, Good]), - {error, Bad, Good}; -await_command_handler_completion(Pids, Bad, Good, Timeout) -> - p("await_command_handler_completion -> entry when waiting for" - "~n Pids: ~p" - "~n Bad: ~p" - "~n Good: ~p" - "~n Timeout: ~p", [Pids, Bad, Good, Timeout]), - Begin = ms(), - receive - {'EXIT', Pid, {ok, FinalState}} -> - p("await_command_handler_completion -> " - "received ok EXIT signal from ~p", [Pid]), - case lists:delete(Pid, Pids) of - Pids -> - await_command_handler_completion(Pids, Bad, Good, - Timeout - (ms() - Begin)); - Pids2 -> - p("await_command_handler_completion -> ~p done", [Pid]), - await_command_handler_completion(Pids2, - Bad, - [{Pid, FinalState}|Good], - Timeout - (ms() - Begin)) - end; + ?CH:await_completion(Pids, Timeout). - {'EXIT', Pid, {error, Reason}} -> - p("await_command_handler_completion -> " - "received error EXIT signal from ~p", [Pid]), - case lists:delete(Pid, Pids) of - Pids -> - await_command_handler_completion(Pids, Bad, Good, - Timeout - (ms() - Begin)); - Pids2 -> - p("await_command_handler_completion -> ~p done with" - "~n ~p", [Pid, Reason]), - await_command_handler_completion(Pids2, - [{Pid, Reason}|Bad], - Good, - Timeout - (ms() - Begin)) - end; - {'EXIT', Pid, {skip, Reason}} -> - p("await_command_handler_completion -> " - "received skip EXIT signal from ~p with" - "~p", [Pid, Reason]), - ?SKIP(Reason) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +try_tc(TCName, Pre, Case, Post) -> + try_tc(TCName, "TEST", ?TEST_VERBOSITY, Pre, Case, Post). - after Timeout -> - p("await_command_handler_completion -> timeout"), - exit({timeout, Pids}) - end. +try_tc(TCName, Name, Verbosity, Pre, Case, Post) -> + ?TRY_TC(TCName, Name, Verbosity, Pre, Case, Post). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% ------- Misc functions -------- @@ -1297,7 +1212,7 @@ p(_S, F, A) -> [?FTS(), self() | A]). -ms() -> - erlang:monotonic_time(milli_seconds). +%% ms() -> +%% erlang:monotonic_time(milli_seconds). diff --git a/lib/megaco/test/modules.mk b/lib/megaco/test/modules.mk index 3ec3ce7368..55b3003d6d 100644 --- a/lib/megaco/test/modules.mk +++ b/lib/megaco/test/modules.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2019. All Rights Reserved. +# Copyright Ericsson AB 2001-2020. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -33,6 +33,7 @@ TEST_UTIL_MODULES = \ megaco_mess_otp8212_test \ megaco_profile \ megaco_tc_controller \ + megaco_test_command_handler \ megaco_test_global_sys_monitor \ megaco_test_sys_monitor \ megaco_test_generator_lib \ diff --git a/lib/mnesia/src/mnesia_bup.erl b/lib/mnesia/src/mnesia_bup.erl index e57fc5199d..2111c1ce17 100644 --- a/lib/mnesia/src/mnesia_bup.erl +++ b/lib/mnesia/src/mnesia_bup.erl @@ -1121,6 +1121,7 @@ local_uninstall_fallback(Master, FA) -> Bup = FA2#fallback_args.fallback_bup, file:delete(Tmp), Res = file:delete(Bup), + unregister(mnesia_fallback), ?eval_debug_fun({?MODULE, uninstall_fallback2, post_delete}, []), Master ! {self(), Res}, unlink(Master), diff --git a/lib/public_key/asn1/PKIX1Algorithms88.asn1 b/lib/public_key/asn1/PKIX1Algorithms88.asn1 index 6cc6745af6..207ab005a9 100644 --- a/lib/public_key/asn1/PKIX1Algorithms88.asn1 +++ b/lib/public_key/asn1/PKIX1Algorithms88.asn1 @@ -283,4 +283,12 @@ sect571k1 OBJECT IDENTIFIER ::= { ellipticCurve 38 } sect571r1 OBJECT IDENTIFIER ::= { ellipticCurve 39 } + + id-edwards-curve-algs OBJECT IDENTIFIER ::= { 1 3 101 } + + id-X25519 OBJECT IDENTIFIER ::= { id-edwards-curve-algs 110 } + id-X448 OBJECT IDENTIFIER ::= { id-edwards-curve-algs 111 } + id-Ed25519 OBJECT IDENTIFIER ::= { id-edwards-curve-algs 112 } + id-Ed448 OBJECT IDENTIFIER ::= { id-edwards-curve-algs 113 } + END diff --git a/lib/public_key/src/pubkey_cert_records.erl b/lib/public_key/src/pubkey_cert_records.erl index 6a80874df8..1c79f904f3 100644 --- a/lib/public_key/src/pubkey_cert_records.erl +++ b/lib/public_key/src/pubkey_cert_records.erl @@ -113,7 +113,8 @@ supportedPublicKeyAlgorithms(?'id-keyExchangeAlgorithm') -> 'KEA-PublicKey'; supportedPublicKeyAlgorithms(?'id-ecPublicKey') -> 'ECPoint'. supportedCurvesTypes(?'characteristic-two-field') -> characteristic_two_field; -supportedCurvesTypes(?'prime-field') -> prime_field. +supportedCurvesTypes(?'prime-field') -> prime_field; +supportedCurvesTypes(?'id-edwards-curve-algs') -> edwards_curve. namedCurves(?'sect571r1') -> sect571r1; namedCurves(?'sect571k1') -> sect571k1; @@ -148,6 +149,8 @@ namedCurves(?'sect163r1') -> sect163r1; namedCurves(?'sect163k1') -> sect163k1; namedCurves(?'secp256r1') -> secp256r1; namedCurves(?'secp192r1') -> secp192r1; +namedCurves(?'id-X25519') -> x25519; +namedCurves(?'id-X448') -> x448; namedCurves(?'brainpoolP160r1') -> brainpoolP160r1; namedCurves(?'brainpoolP160t1') -> brainpoolP160t1; namedCurves(?'brainpoolP192r1') -> brainpoolP192r1; @@ -162,7 +165,6 @@ namedCurves(?'brainpoolP384r1') -> brainpoolP384r1; namedCurves(?'brainpoolP384t1') -> brainpoolP384t1; namedCurves(?'brainpoolP512r1') -> brainpoolP512r1; namedCurves(?'brainpoolP512t1') -> brainpoolP512t1; - namedCurves(sect571r1) -> ?'sect571r1'; namedCurves(sect571k1) -> ?'sect571k1'; namedCurves(sect409r1) -> ?'sect409r1'; @@ -196,6 +198,8 @@ namedCurves(sect163r1) -> ?'sect163r1'; namedCurves(sect163k1) -> ?'sect163k1'; namedCurves(secp256r1) -> ?'secp256r1'; namedCurves(secp192r1) -> ?'secp192r1'; +namedCurves(x25519) -> ?'id-X25519'; +namedCurves(x448) -> ?'id-X448'; namedCurves(brainpoolP160r1) -> ?'brainpoolP160r1'; namedCurves(brainpoolP160t1) -> ?'brainpoolP160t1'; namedCurves(brainpoolP192r1) -> ?'brainpoolP192r1'; diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 47266c514c..ce5151750d 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -1536,6 +1536,10 @@ ec_curve_spec({ecParameters, ECParams}) -> ec_curve_spec(ECParams); ec_curve_spec({namedCurve, OID}) when is_tuple(OID), is_integer(element(1,OID)) -> ec_curve_spec({namedCurve, pubkey_cert_records:namedCurves(OID)}); +ec_curve_spec({namedCurve, x25519 = Name}) -> + Name; +ec_curve_spec({namedCurve, x448 = Name}) -> + Name; ec_curve_spec({namedCurve, Name}) when is_atom(Name) -> crypto:ec_curve(Name). diff --git a/lib/public_key/test/Makefile b/lib/public_key/test/Makefile index b9beb6d3b9..1ee3b24e3a 100644 --- a/lib/public_key/test/Makefile +++ b/lib/public_key/test/Makefile @@ -32,7 +32,8 @@ MODULES= \ erl_make_certs \ public_key_SUITE \ pbe_SUITE \ - pkits_SUITE + pkits_SUITE \ + pubkey_ssh_SUITE ERL_FILES= $(MODULES:%=%.erl) diff --git a/lib/public_key/test/pubkey_ssh_SUITE.erl b/lib/public_key/test/pubkey_ssh_SUITE.erl new file mode 100644 index 0000000000..afa3741346 --- /dev/null +++ b/lib/public_key/test/pubkey_ssh_SUITE.erl @@ -0,0 +1,417 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2020. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% +-module(pubkey_ssh_SUITE). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("public_key/include/public_key.hrl"). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-define(TIMEOUT, 120000). % 2 min + + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + +suite() -> + []. + +all() -> + [{group, ssh_public_key_decode_encode}, + {group, ssh_hostkey_fingerprint} + ]. + +groups() -> + [{ssh_public_key_decode_encode, [], + [ssh_rsa_public_key, ssh_dsa_public_key, ssh_ecdsa_public_key, + ssh_rfc4716_rsa_comment, ssh_rfc4716_dsa_comment, + ssh_rfc4716_rsa_subject, + ssh_known_hosts, + ssh_auth_keys, ssh1_known_hosts, ssh1_auth_keys, ssh_openssh_public_key_with_comment, + ssh_openssh_public_key_long_header]}, + + {ssh_hostkey_fingerprint, [], + [ssh_hostkey_fingerprint_md5_implicit, + ssh_hostkey_fingerprint_md5, + ssh_hostkey_fingerprint_sha, + ssh_hostkey_fingerprint_sha256, + ssh_hostkey_fingerprint_sha384, + ssh_hostkey_fingerprint_sha512, + ssh_hostkey_fingerprint_list]} + ]. +%%------------------------------------------------------------------- +init_per_suite(Config) -> + application:stop(crypto), + try crypto:start() of + ok -> + application:start(asn1), + Config + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + application:stop(asn1), + application:stop(crypto). + +%%------------------------------------------------------------------- +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. +%%------------------------------------------------------------------- +init_per_testcase(ssh_hostkey_fingerprint_md5_implicit, Config) -> + init_fingerprint_testcase([md5], Config); + +init_per_testcase(ssh_hostkey_fingerprint_md5, Config) -> + init_fingerprint_testcase([md5], Config); + +init_per_testcase(ssh_hostkey_fingerprint_sha, Config) -> + init_fingerprint_testcase([sha], Config); + +init_per_testcase(ssh_hostkey_fingerprint_sha256, Config) -> + init_fingerprint_testcase([sha256], Config); + +init_per_testcase(ssh_hostkey_fingerprint_sha384, Config) -> + init_fingerprint_testcase([sha384], Config); + +init_per_testcase(ssh_hostkey_fingerprint_sha512, Config) -> + init_fingerprint_testcase([sha512], Config); + +init_per_testcase(ssh_hostkey_fingerprint_list , Config) -> + init_fingerprint_testcase([sha,md5], Config); + +init_per_testcase(_, Config) -> + init_common_per_testcase(Config). + + +init_fingerprint_testcase(Algs, Config) -> + Hashs = proplists:get_value(hashs, crypto:supports(), []), + case Algs -- Hashs of + [] -> init_common_per_testcase(Config); + UnsupportedAlgs -> {skip,{UnsupportedAlgs,not_supported}} + end. + +init_common_per_testcase(Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ct:timetrap(?TIMEOUT), + [{watchdog, Dog} | Config]. + + +end_per_testcase(_TestCase, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +ssh_rsa_public_key(Config) when is_list(Config) -> + Datadir = proplists:get_value(data_dir, Config), + + {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_rsa_pub")), + [{PubKey, Attributes1}] = public_key:ssh_decode(RSARawSsh2, public_key), + [{PubKey, Attributes1}] = public_key:ssh_decode(RSARawSsh2, rfc4716_public_key), + + {ok, RSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_rsa_pub")), + [{PubKey, Attributes2}] = public_key:ssh_decode(RSARawOpenSsh, public_key), + [{PubKey, Attributes2}] = public_key:ssh_decode(RSARawOpenSsh, openssh_public_key), + + %% Can not check EncodedSSh == RSARawSsh2 and EncodedOpenSsh + %% = RSARawOpenSsh as line breakpoints may differ + + EncodedSSh = public_key:ssh_encode([{PubKey, Attributes1}], rfc4716_public_key), + EncodedOpenSsh = public_key:ssh_encode([{PubKey, Attributes2}], openssh_public_key), + + [{PubKey, Attributes1}] = + public_key:ssh_decode(EncodedSSh, public_key), + [{PubKey, Attributes2}] = + public_key:ssh_decode(EncodedOpenSsh, public_key). + +%%-------------------------------------------------------------------- +ssh_dsa_public_key(Config) when is_list(Config) -> + Datadir = proplists:get_value(data_dir, Config), + + {ok, DSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_dsa_pub")), + [{PubKey, Attributes1}] = public_key:ssh_decode(DSARawSsh2, public_key), + [{PubKey, Attributes1}] = public_key:ssh_decode(DSARawSsh2, rfc4716_public_key), + + {ok, DSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_dsa_pub")), + [{PubKey, Attributes2}] = public_key:ssh_decode(DSARawOpenSsh, public_key), + [{PubKey, Attributes2}] = public_key:ssh_decode(DSARawOpenSsh, openssh_public_key), + + %% Can not check EncodedSSh == DSARawSsh2 and EncodedOpenSsh + %% = DSARawOpenSsh as line breakpoints may differ + + EncodedSSh = public_key:ssh_encode([{PubKey, Attributes1}], rfc4716_public_key), + EncodedOpenSsh = public_key:ssh_encode([{PubKey, Attributes2}], openssh_public_key), + + [{PubKey, Attributes1}] = + public_key:ssh_decode(EncodedSSh, public_key), + [{PubKey, Attributes2}] = + public_key:ssh_decode(EncodedOpenSsh, public_key). + +%%-------------------------------------------------------------------- +ssh_ecdsa_public_key(Config) when is_list(Config) -> + Datadir = proplists:get_value(data_dir, Config), + + {ok, ECDSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_ecdsa_pub")), + [{PubKey, Attributes1}] = public_key:ssh_decode(ECDSARawSsh2, public_key), + [{PubKey, Attributes1}] = public_key:ssh_decode(ECDSARawSsh2, rfc4716_public_key), + + {ok, ECDSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_ecdsa_pub")), + [{PubKey, Attributes2}] = public_key:ssh_decode(ECDSARawOpenSsh, public_key), + [{PubKey, Attributes2}] = public_key:ssh_decode(ECDSARawOpenSsh, openssh_public_key), + + %% Can not check EncodedSSh == ECDSARawSsh2 and EncodedOpenSsh + %% = ECDSARawOpenSsh as line breakpoints may differ + + EncodedSSh = public_key:ssh_encode([{PubKey, Attributes1}], rfc4716_public_key), + EncodedOpenSsh = public_key:ssh_encode([{PubKey, Attributes2}], openssh_public_key), + + [{PubKey, Attributes1}] = + public_key:ssh_decode(EncodedSSh, public_key), + [{PubKey, Attributes2}] = + public_key:ssh_decode(EncodedOpenSsh, public_key). + +%%-------------------------------------------------------------------- +ssh_rfc4716_rsa_comment(Config) when is_list(Config) -> + Datadir = proplists:get_value(data_dir, Config), + + {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_rsa_comment_pub")), + [{#'RSAPublicKey'{} = PubKey, Attributes}] = + public_key:ssh_decode(RSARawSsh2, public_key), + + Headers = proplists:get_value(headers, Attributes), + + Value = proplists:get_value("Comment", Headers, undefined), + true = Value =/= undefined, + RSARawSsh2 = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key). + +%%-------------------------------------------------------------------- +ssh_rfc4716_dsa_comment(Config) when is_list(Config) -> + Datadir = proplists:get_value(data_dir, Config), + + {ok, DSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_dsa_comment_pub")), + [{{_, #'Dss-Parms'{}} = PubKey, Attributes}] = + public_key:ssh_decode(DSARawSsh2, public_key), + + Headers = proplists:get_value(headers, Attributes), + + Value = proplists:get_value("Comment", Headers, undefined), + true = Value =/= undefined, + + %% Can not check Encoded == DSARawSsh2 as line continuation breakpoints may differ + Encoded = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key), + [{PubKey, Attributes}] = + public_key:ssh_decode(Encoded, public_key). + +%%-------------------------------------------------------------------- +ssh_rfc4716_rsa_subject(Config) when is_list(Config) -> + Datadir = proplists:get_value(data_dir, Config), + + {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_subject_pub")), + [{#'RSAPublicKey'{} = PubKey, Attributes}] = + public_key:ssh_decode(RSARawSsh2, public_key), + + Headers = proplists:get_value(headers, Attributes), + + Value = proplists:get_value("Subject", Headers, undefined), + true = Value =/= undefined, + + %% Can not check Encoded == RSARawSsh2 as line continuation breakpoints may differ + Encoded = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key), + [{PubKey, Attributes}] = + public_key:ssh_decode(Encoded, public_key). + +%%-------------------------------------------------------------------- +ssh_known_hosts(Config) when is_list(Config) -> + Datadir = proplists:get_value(data_dir, Config), + + {ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "known_hosts")), + [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2}, + {#'RSAPublicKey'{}, Attributes3}, {#'RSAPublicKey'{}, Attributes4}] = Decoded = + public_key:ssh_decode(SshKnownHosts, known_hosts), + + Comment1 = undefined, + Comment2 = "foo@bar.com", + Comment3 = "Comment with whitespaces", + Comment4 = "foo@bar.com Comment with whitespaces", + + Comment1 = proplists:get_value(comment, Attributes1, undefined), + Comment2 = proplists:get_value(comment, Attributes2), + Comment3 = proplists:get_value(comment, Attributes3), + Comment4 = proplists:get_value(comment, Attributes4), + + Value1 = proplists:get_value(hostnames, Attributes1, undefined), + Value2 = proplists:get_value(hostnames, Attributes2, undefined), + true = (Value1 =/= undefined) and (Value2 =/= undefined), + + Encoded = public_key:ssh_encode(Decoded, known_hosts), + Decoded = public_key:ssh_decode(Encoded, known_hosts). + +%%-------------------------------------------------------------------- +ssh1_known_hosts(Config) when is_list(Config) -> + Datadir = proplists:get_value(data_dir, Config), + + {ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "ssh1_known_hosts")), + [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2},{#'RSAPublicKey'{}, Attributes3}] + = Decoded = public_key:ssh_decode(SshKnownHosts, known_hosts), + + Value1 = proplists:get_value(hostnames, Attributes1, undefined), + Value2 = proplists:get_value(hostnames, Attributes2, undefined), + true = (Value1 =/= undefined) and (Value2 =/= undefined), + + Comment ="dhopson@VMUbuntu-DSH comment with whitespaces", + Comment = proplists:get_value(comment, Attributes3), + + Encoded = public_key:ssh_encode(Decoded, known_hosts), + Decoded = public_key:ssh_decode(Encoded, known_hosts). + +%%-------------------------------------------------------------------- +ssh_auth_keys(Config) when is_list(Config) -> + Datadir = proplists:get_value(data_dir, Config), + + {ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "auth_keys")), + [{#'RSAPublicKey'{}, Attributes1}, {{_, #'Dss-Parms'{}}, Attributes2}, + {#'RSAPublicKey'{}, Attributes3}, {{_, #'Dss-Parms'{}}, Attributes4} + ] = Decoded = + public_key:ssh_decode(SshAuthKeys, auth_keys), + + Value1 = proplists:get_value(options, Attributes1, undefined), + true = Value1 =/= undefined, + + Comment1 = Comment2 = "dhopson@VMUbuntu-DSH", + Comment3 = Comment4 ="dhopson@VMUbuntu-DSH comment with whitespaces", + + Comment1 = proplists:get_value(comment, Attributes1), + Comment2 = proplists:get_value(comment, Attributes2), + Comment3 = proplists:get_value(comment, Attributes3), + Comment4 = proplists:get_value(comment, Attributes4), + + Encoded = public_key:ssh_encode(Decoded, auth_keys), + Decoded = public_key:ssh_decode(Encoded, auth_keys). + +%%-------------------------------------------------------------------- +ssh1_auth_keys(Config) when is_list(Config) -> + Datadir = proplists:get_value(data_dir, Config), + + {ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "ssh1_auth_keys")), + [{#'RSAPublicKey'{}, Attributes1}, + {#'RSAPublicKey'{}, Attributes2}, {#'RSAPublicKey'{}, Attributes3}, + {#'RSAPublicKey'{}, Attributes4}, {#'RSAPublicKey'{}, Attributes5}] = Decoded = + public_key:ssh_decode(SshAuthKeys, auth_keys), + + Value1 = proplists:get_value(bits, Attributes2, undefined), + Value2 = proplists:get_value(bits, Attributes3, undefined), + true = (Value1 =/= undefined) and (Value2 =/= undefined), + + Comment2 = Comment3 = "dhopson@VMUbuntu-DSH", + Comment4 = Comment5 ="dhopson@VMUbuntu-DSH comment with whitespaces", + + undefined = proplists:get_value(comment, Attributes1, undefined), + Comment2 = proplists:get_value(comment, Attributes2), + Comment3 = proplists:get_value(comment, Attributes3), + Comment4 = proplists:get_value(comment, Attributes4), + Comment5 = proplists:get_value(comment, Attributes5), + + Encoded = public_key:ssh_encode(Decoded, auth_keys), + Decoded = public_key:ssh_decode(Encoded, auth_keys). + +%%-------------------------------------------------------------------- +ssh_openssh_public_key_with_comment(Config) when is_list(Config) -> + Datadir = proplists:get_value(data_dir, Config), + + {ok, DSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_dsa_with_comment_pub")), + [{{_, #'Dss-Parms'{}}, _}] = public_key:ssh_decode(DSARawOpenSsh, openssh_public_key). + +%%-------------------------------------------------------------------- +ssh_openssh_public_key_long_header(Config) when is_list(Config) -> + Datadir = proplists:get_value(data_dir, Config), + + {ok,RSARawOpenSsh} = file:read_file(filename:join(Datadir, "ssh_rsa_long_header_pub")), + [{#'RSAPublicKey'{}, _}] = Decoded = public_key:ssh_decode(RSARawOpenSsh, public_key), + + Encoded = public_key:ssh_encode(Decoded, rfc4716_public_key), + Decoded = public_key:ssh_decode(Encoded, rfc4716_public_key). + +%%-------------------------------------------------------------------- +%% Check of different host keys left to later +ssh_hostkey_fingerprint_md5_implicit(_Config) -> + Expected = "4b:0b:63:de:0f:a7:3a:ab:2c:cc:2d:d1:21:37:1d:3a", + Expected = public_key:ssh_hostkey_fingerprint(ssh_hostkey(rsa)). + +%%-------------------------------------------------------------------- +%% Check of different host keys left to later +ssh_hostkey_fingerprint_md5(_Config) -> + Expected = "MD5:4b:0b:63:de:0f:a7:3a:ab:2c:cc:2d:d1:21:37:1d:3a", + Expected = public_key:ssh_hostkey_fingerprint(md5, ssh_hostkey(rsa)). + +%%-------------------------------------------------------------------- +%% Since this kind of fingerprint is not available yet on standard +%% distros, we do like this instead. The Expected is generated with: +%% $ openssh-7.3p1/ssh-keygen -E sha1 -lf <file> +%% 2048 SHA1:Soammnaqg06jrm2jivMSnzQGlmk none@example.org (RSA) +ssh_hostkey_fingerprint_sha(_Config) -> + Expected = "SHA1:Soammnaqg06jrm2jivMSnzQGlmk", + Expected = public_key:ssh_hostkey_fingerprint(sha, ssh_hostkey(rsa)). + +%%-------------------------------------------------------------------- +%% Since this kind of fingerprint is not available yet on standard +%% distros, we do like this instead. +ssh_hostkey_fingerprint_sha256(_Config) -> + Expected = "SHA256:T7F1BahkJWR7iJO8+rpzWOPbp7LZP4MlNrDExdNYOvY", + Expected = public_key:ssh_hostkey_fingerprint(sha256, ssh_hostkey(rsa)). + +%%-------------------------------------------------------------------- +%% Since this kind of fingerprint is not available yet on standard +%% distros, we do like this instead. +ssh_hostkey_fingerprint_sha384(_Config) -> + Expected = "SHA384:QhkLoGNI4KXdPvC//HxxSCP3uTQVADqxdajbgm+Gkx9zqz8N94HyP1JmH8C4/aEl", + Expected = public_key:ssh_hostkey_fingerprint(sha384, ssh_hostkey(rsa)). + +%%-------------------------------------------------------------------- +%% Since this kind of fingerprint is not available yet on standard +%% distros, we do like this instead. +ssh_hostkey_fingerprint_sha512(_Config) -> + Expected = "SHA512:ezUismvm3ADQQb6Nm0c1DwQ6ydInlJNfsnSQejFkXNmABg1Aenk9oi45CXeBOoTnlfTsGG8nFDm0smP10PBEeA", + Expected = public_key:ssh_hostkey_fingerprint(sha512, ssh_hostkey(rsa)). + +%%-------------------------------------------------------------------- +%% Since this kind of fingerprint is not available yet on standard +%% distros, we do like this instead. +ssh_hostkey_fingerprint_list(_Config) -> + Expected = ["SHA1:Soammnaqg06jrm2jivMSnzQGlmk", + "MD5:4b:0b:63:de:0f:a7:3a:ab:2c:cc:2d:d1:21:37:1d:3a"], + Expected = public_key:ssh_hostkey_fingerprint([sha,md5], ssh_hostkey(rsa)). + +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- +ssh_hostkey(rsa) -> + [{PKdecoded,_}] = + public_key:ssh_decode( + <<"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDYXcYmsyJBstl4EfFYzfQJmSiUE162zvSGSoMYybShYOI6rnnyvvihfw8Aml+2gZ716F2tqG48FQ/yPZEGWNPMrCejPpJctaPWhpNdNMJ8KFXSEgr5bY2mEpa19DHmuDeXKzeJJ+X7s3fVdYc4FMk5731KIW6Huf019ZnTxbx0VKG6b1KAJBg3vpNsDxEMwQ4LFMB0JHVklOTzbxmpaeULuIxvl65A+eGeFVeo2Q+YI9UnwY1vSgmc9Azwy8Ie9Z0HpQBN5I7Uc5xnknT8V6xDhgNfXEfzsgsRdDfZLECt1WO/1gP9wkosvAGZWt5oG8pbNQWiQdFq536ck8WQD9WD none@example.org">>, + public_key), + PKdecoded. + diff --git a/lib/public_key/test/public_key_SUITE_data/auth_keys b/lib/public_key/test/pubkey_ssh_SUITE_data/auth_keys index 8be7357a06..8be7357a06 100644 --- a/lib/public_key/test/public_key_SUITE_data/auth_keys +++ b/lib/public_key/test/pubkey_ssh_SUITE_data/auth_keys diff --git a/lib/public_key/test/public_key_SUITE_data/known_hosts b/lib/public_key/test/pubkey_ssh_SUITE_data/known_hosts index 3c3af68178..3c3af68178 100644 --- a/lib/public_key/test/public_key_SUITE_data/known_hosts +++ b/lib/public_key/test/pubkey_ssh_SUITE_data/known_hosts diff --git a/lib/public_key/test/public_key_SUITE_data/openssh_dsa_pub b/lib/public_key/test/pubkey_ssh_SUITE_data/openssh_dsa_pub index a765ba8189..a765ba8189 100644 --- a/lib/public_key/test/public_key_SUITE_data/openssh_dsa_pub +++ b/lib/public_key/test/pubkey_ssh_SUITE_data/openssh_dsa_pub diff --git a/lib/public_key/test/public_key_SUITE_data/openssh_dsa_with_comment_pub b/lib/public_key/test/pubkey_ssh_SUITE_data/openssh_dsa_with_comment_pub index d5a34a3f78..d5a34a3f78 100644 --- a/lib/public_key/test/public_key_SUITE_data/openssh_dsa_with_comment_pub +++ b/lib/public_key/test/pubkey_ssh_SUITE_data/openssh_dsa_with_comment_pub diff --git a/lib/public_key/test/public_key_SUITE_data/openssh_ecdsa_pub b/lib/public_key/test/pubkey_ssh_SUITE_data/openssh_ecdsa_pub index a49b4264b8..a49b4264b8 100644 --- a/lib/public_key/test/public_key_SUITE_data/openssh_ecdsa_pub +++ b/lib/public_key/test/pubkey_ssh_SUITE_data/openssh_ecdsa_pub diff --git a/lib/public_key/test/public_key_SUITE_data/openssh_rsa_pub b/lib/public_key/test/pubkey_ssh_SUITE_data/openssh_rsa_pub index 0a0838db40..0a0838db40 100644 --- a/lib/public_key/test/public_key_SUITE_data/openssh_rsa_pub +++ b/lib/public_key/test/pubkey_ssh_SUITE_data/openssh_rsa_pub diff --git a/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh1_auth_keys index ac3d61b4c7..ac3d61b4c7 100644 --- a/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys +++ b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh1_auth_keys diff --git a/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh1_known_hosts index 835b16ab67..835b16ab67 100644 --- a/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts +++ b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh1_known_hosts diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_comment_pub b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh2_dsa_comment_pub index ca5089dbd7..ca5089dbd7 100644 --- a/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_comment_pub +++ b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh2_dsa_comment_pub diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_pub b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh2_dsa_pub index a5e38be81a..a5e38be81a 100644 --- a/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_pub +++ b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh2_dsa_pub diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_ecdsa_pub b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh2_ecdsa_pub index 702e5c4fde..702e5c4fde 100644 --- a/lib/public_key/test/public_key_SUITE_data/ssh2_ecdsa_pub +++ b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh2_ecdsa_pub diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_comment_pub b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh2_rsa_comment_pub index e4d446147c..e4d446147c 100644 --- a/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_comment_pub +++ b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh2_rsa_comment_pub diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_pub b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh2_rsa_pub index 761088b517..761088b517 100644 --- a/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_pub +++ b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh2_rsa_pub diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_subject_pub b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh2_subject_pub index 8b8ccda8ba..8b8ccda8ba 100644 --- a/lib/public_key/test/public_key_SUITE_data/ssh2_subject_pub +++ b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh2_subject_pub diff --git a/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_comment_pub b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh_rsa_long_comment_pub index 7b42ced93e..7b42ced93e 100644 --- a/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_comment_pub +++ b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh_rsa_long_comment_pub diff --git a/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_header_pub b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh_rsa_long_header_pub index 7b42ced93e..7b42ced93e 100644 --- a/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_header_pub +++ b/lib/public_key/test/pubkey_ssh_SUITE_data/ssh_rsa_long_header_pub diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index 878489eb0f..5acb61575e 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -40,7 +40,6 @@ suite() -> all() -> [app, appup, {group, pem_decode_encode}, - {group, ssh_public_key_decode_encode}, encrypt_decrypt, {group, sign_verify}, pkix, pkix_countryname, pkix_emailaddress, pkix_path_validation, @@ -53,14 +52,7 @@ all() -> pkix_verify_hostname_options, pkix_test_data_all_default, pkix_test_data, - short_cert_issuer_hash, short_crl_issuer_hash, - ssh_hostkey_fingerprint_md5_implicit, - ssh_hostkey_fingerprint_md5, - ssh_hostkey_fingerprint_sha, - ssh_hostkey_fingerprint_sha256, - ssh_hostkey_fingerprint_sha384, - ssh_hostkey_fingerprint_sha512, - ssh_hostkey_fingerprint_list + short_cert_issuer_hash, short_crl_issuer_hash ]. groups() -> @@ -70,13 +62,6 @@ groups() -> ec_pem_encode_generated, gen_ec_param_prime_field, gen_ec_param_char_2_field ]}, - {ssh_public_key_decode_encode, [], - [ssh_rsa_public_key, ssh_dsa_public_key, ssh_ecdsa_public_key, - ssh_rfc4716_rsa_comment, ssh_rfc4716_dsa_comment, - ssh_rfc4716_rsa_subject, - ssh_known_hosts, - ssh_auth_keys, ssh1_known_hosts, ssh1_auth_keys, ssh_openssh_public_key_with_comment, - ssh_openssh_public_key_long_header]}, {sign_verify, [], [rsa_sign_verify, dsa_sign_verify]} ]. %%------------------------------------------------------------------- @@ -118,24 +103,11 @@ init_per_testcase(gen_ec_param_char_2_field=TC, Config) -> init_per_testcase(TestCase, Config) -> case TestCase of - ssh_hostkey_fingerprint_md5_implicit -> init_fingerprint_testcase([md5], Config); - ssh_hostkey_fingerprint_md5 -> init_fingerprint_testcase([md5], Config); - ssh_hostkey_fingerprint_sha -> init_fingerprint_testcase([sha], Config); - ssh_hostkey_fingerprint_sha256 -> init_fingerprint_testcase([sha256], Config); - ssh_hostkey_fingerprint_sha384 -> init_fingerprint_testcase([sha384], Config); - ssh_hostkey_fingerprint_sha512 -> init_fingerprint_testcase([sha512], Config); - ssh_hostkey_fingerprint_list -> init_fingerprint_testcase([sha,md5], Config); - ec_pem_encode_generated -> init_ec_pem_encode_generated(Config); + ec_pem_encode_generated -> + init_ec_pem_encode_generated(Config); _ -> init_common_per_testcase(Config) end. -init_fingerprint_testcase(Algs, Config) -> - Hashs = proplists:get_value(hashs, crypto:supports(), []), - case Algs -- Hashs of - [] -> init_common_per_testcase(Config); - UnsupportedAlgs -> {skip,{UnsupportedAlgs,not_supported}} - end. - init_common_per_testcase(Config0) -> Config = lists:keydelete(watchdog, 1, Config0), Dog = ct:timetrap(?TIMEOUT), @@ -404,313 +376,6 @@ cert_pem(Config) when is_list(Config) -> asn1_encode_decode(Entry2). %%-------------------------------------------------------------------- -ssh_rsa_public_key() -> - [{doc, "ssh rsa public key decode/encode"}]. -ssh_rsa_public_key(Config) when is_list(Config) -> - Datadir = proplists:get_value(data_dir, Config), - - {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_rsa_pub")), - [{PubKey, Attributes1}] = public_key:ssh_decode(RSARawSsh2, public_key), - [{PubKey, Attributes1}] = public_key:ssh_decode(RSARawSsh2, rfc4716_public_key), - - {ok, RSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_rsa_pub")), - [{PubKey, Attributes2}] = public_key:ssh_decode(RSARawOpenSsh, public_key), - [{PubKey, Attributes2}] = public_key:ssh_decode(RSARawOpenSsh, openssh_public_key), - - %% Can not check EncodedSSh == RSARawSsh2 and EncodedOpenSsh - %% = RSARawOpenSsh as line breakpoints may differ - - EncodedSSh = public_key:ssh_encode([{PubKey, Attributes1}], rfc4716_public_key), - EncodedOpenSsh = public_key:ssh_encode([{PubKey, Attributes2}], openssh_public_key), - - [{PubKey, Attributes1}] = - public_key:ssh_decode(EncodedSSh, public_key), - [{PubKey, Attributes2}] = - public_key:ssh_decode(EncodedOpenSsh, public_key). - -%%-------------------------------------------------------------------- - -ssh_dsa_public_key() -> - [{doc, "ssh dsa public key decode/encode"}]. -ssh_dsa_public_key(Config) when is_list(Config) -> - Datadir = proplists:get_value(data_dir, Config), - - {ok, DSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_dsa_pub")), - [{PubKey, Attributes1}] = public_key:ssh_decode(DSARawSsh2, public_key), - [{PubKey, Attributes1}] = public_key:ssh_decode(DSARawSsh2, rfc4716_public_key), - - {ok, DSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_dsa_pub")), - [{PubKey, Attributes2}] = public_key:ssh_decode(DSARawOpenSsh, public_key), - [{PubKey, Attributes2}] = public_key:ssh_decode(DSARawOpenSsh, openssh_public_key), - - %% Can not check EncodedSSh == DSARawSsh2 and EncodedOpenSsh - %% = DSARawOpenSsh as line breakpoints may differ - - EncodedSSh = public_key:ssh_encode([{PubKey, Attributes1}], rfc4716_public_key), - EncodedOpenSsh = public_key:ssh_encode([{PubKey, Attributes2}], openssh_public_key), - - [{PubKey, Attributes1}] = - public_key:ssh_decode(EncodedSSh, public_key), - [{PubKey, Attributes2}] = - public_key:ssh_decode(EncodedOpenSsh, public_key). - -%%-------------------------------------------------------------------- - -ssh_ecdsa_public_key() -> - [{doc, "ssh ecdsa public key decode/encode"}]. -ssh_ecdsa_public_key(Config) when is_list(Config) -> - Datadir = proplists:get_value(data_dir, Config), - - {ok, ECDSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_ecdsa_pub")), - [{PubKey, Attributes1}] = public_key:ssh_decode(ECDSARawSsh2, public_key), - [{PubKey, Attributes1}] = public_key:ssh_decode(ECDSARawSsh2, rfc4716_public_key), - - {ok, ECDSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_ecdsa_pub")), - [{PubKey, Attributes2}] = public_key:ssh_decode(ECDSARawOpenSsh, public_key), - [{PubKey, Attributes2}] = public_key:ssh_decode(ECDSARawOpenSsh, openssh_public_key), - - %% Can not check EncodedSSh == ECDSARawSsh2 and EncodedOpenSsh - %% = ECDSARawOpenSsh as line breakpoints may differ - - EncodedSSh = public_key:ssh_encode([{PubKey, Attributes1}], rfc4716_public_key), - EncodedOpenSsh = public_key:ssh_encode([{PubKey, Attributes2}], openssh_public_key), - - [{PubKey, Attributes1}] = - public_key:ssh_decode(EncodedSSh, public_key), - [{PubKey, Attributes2}] = - public_key:ssh_decode(EncodedOpenSsh, public_key). - -%%-------------------------------------------------------------------- -ssh_rfc4716_rsa_comment() -> - [{doc, "Test comment header and rsa key"}]. -ssh_rfc4716_rsa_comment(Config) when is_list(Config) -> - Datadir = proplists:get_value(data_dir, Config), - - {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_rsa_comment_pub")), - [{#'RSAPublicKey'{} = PubKey, Attributes}] = - public_key:ssh_decode(RSARawSsh2, public_key), - - Headers = proplists:get_value(headers, Attributes), - - Value = proplists:get_value("Comment", Headers, undefined), - true = Value =/= undefined, - RSARawSsh2 = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key). - -%%-------------------------------------------------------------------- -ssh_rfc4716_dsa_comment() -> - [{doc, "Test comment header and dsa key"}]. -ssh_rfc4716_dsa_comment(Config) when is_list(Config) -> - Datadir = proplists:get_value(data_dir, Config), - - {ok, DSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_dsa_comment_pub")), - [{{_, #'Dss-Parms'{}} = PubKey, Attributes}] = - public_key:ssh_decode(DSARawSsh2, public_key), - - Headers = proplists:get_value(headers, Attributes), - - Value = proplists:get_value("Comment", Headers, undefined), - true = Value =/= undefined, - - %% Can not check Encoded == DSARawSsh2 as line continuation breakpoints may differ - Encoded = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key), - [{PubKey, Attributes}] = - public_key:ssh_decode(Encoded, public_key). - -%%-------------------------------------------------------------------- -ssh_rfc4716_rsa_subject() -> - [{doc, "Test another header value than comment"}]. -ssh_rfc4716_rsa_subject(Config) when is_list(Config) -> - Datadir = proplists:get_value(data_dir, Config), - - {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_subject_pub")), - [{#'RSAPublicKey'{} = PubKey, Attributes}] = - public_key:ssh_decode(RSARawSsh2, public_key), - - Headers = proplists:get_value(headers, Attributes), - - Value = proplists:get_value("Subject", Headers, undefined), - true = Value =/= undefined, - - %% Can not check Encoded == RSARawSsh2 as line continuation breakpoints may differ - Encoded = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key), - [{PubKey, Attributes}] = - public_key:ssh_decode(Encoded, public_key). - -%%-------------------------------------------------------------------- -ssh_known_hosts() -> - [{doc, "ssh known hosts file encode/decode"}]. -ssh_known_hosts(Config) when is_list(Config) -> - Datadir = proplists:get_value(data_dir, Config), - - {ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "known_hosts")), - [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2}, - {#'RSAPublicKey'{}, Attributes3}, {#'RSAPublicKey'{}, Attributes4}] = Decoded = - public_key:ssh_decode(SshKnownHosts, known_hosts), - - Comment1 = undefined, - Comment2 = "foo@bar.com", - Comment3 = "Comment with whitespaces", - Comment4 = "foo@bar.com Comment with whitespaces", - - Comment1 = proplists:get_value(comment, Attributes1, undefined), - Comment2 = proplists:get_value(comment, Attributes2), - Comment3 = proplists:get_value(comment, Attributes3), - Comment4 = proplists:get_value(comment, Attributes4), - - Value1 = proplists:get_value(hostnames, Attributes1, undefined), - Value2 = proplists:get_value(hostnames, Attributes2, undefined), - true = (Value1 =/= undefined) and (Value2 =/= undefined), - - Encoded = public_key:ssh_encode(Decoded, known_hosts), - Decoded = public_key:ssh_decode(Encoded, known_hosts). - -%%-------------------------------------------------------------------- - -ssh1_known_hosts() -> - [{doc, "ssh (ver 1) known hosts file encode/decode"}]. -ssh1_known_hosts(Config) when is_list(Config) -> - Datadir = proplists:get_value(data_dir, Config), - - {ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "ssh1_known_hosts")), - [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2},{#'RSAPublicKey'{}, Attributes3}] - = Decoded = public_key:ssh_decode(SshKnownHosts, known_hosts), - - Value1 = proplists:get_value(hostnames, Attributes1, undefined), - Value2 = proplists:get_value(hostnames, Attributes2, undefined), - true = (Value1 =/= undefined) and (Value2 =/= undefined), - - Comment ="dhopson@VMUbuntu-DSH comment with whitespaces", - Comment = proplists:get_value(comment, Attributes3), - - Encoded = public_key:ssh_encode(Decoded, known_hosts), - Decoded = public_key:ssh_decode(Encoded, known_hosts). - -%%-------------------------------------------------------------------- -ssh_auth_keys() -> - [{doc, "ssh authorized keys file encode/decode"}]. -ssh_auth_keys(Config) when is_list(Config) -> - Datadir = proplists:get_value(data_dir, Config), - - {ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "auth_keys")), - [{#'RSAPublicKey'{}, Attributes1}, {{_, #'Dss-Parms'{}}, Attributes2}, - {#'RSAPublicKey'{}, Attributes3}, {{_, #'Dss-Parms'{}}, Attributes4} - ] = Decoded = - public_key:ssh_decode(SshAuthKeys, auth_keys), - - Value1 = proplists:get_value(options, Attributes1, undefined), - true = Value1 =/= undefined, - - Comment1 = Comment2 = "dhopson@VMUbuntu-DSH", - Comment3 = Comment4 ="dhopson@VMUbuntu-DSH comment with whitespaces", - - Comment1 = proplists:get_value(comment, Attributes1), - Comment2 = proplists:get_value(comment, Attributes2), - Comment3 = proplists:get_value(comment, Attributes3), - Comment4 = proplists:get_value(comment, Attributes4), - - Encoded = public_key:ssh_encode(Decoded, auth_keys), - Decoded = public_key:ssh_decode(Encoded, auth_keys). - -%%-------------------------------------------------------------------- -ssh1_auth_keys() -> - [{doc, "ssh (ver 1) authorized keys file encode/decode"}]. -ssh1_auth_keys(Config) when is_list(Config) -> - Datadir = proplists:get_value(data_dir, Config), - - {ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "ssh1_auth_keys")), - [{#'RSAPublicKey'{}, Attributes1}, - {#'RSAPublicKey'{}, Attributes2}, {#'RSAPublicKey'{}, Attributes3}, - {#'RSAPublicKey'{}, Attributes4}, {#'RSAPublicKey'{}, Attributes5}] = Decoded = - public_key:ssh_decode(SshAuthKeys, auth_keys), - - Value1 = proplists:get_value(bits, Attributes2, undefined), - Value2 = proplists:get_value(bits, Attributes3, undefined), - true = (Value1 =/= undefined) and (Value2 =/= undefined), - - Comment2 = Comment3 = "dhopson@VMUbuntu-DSH", - Comment4 = Comment5 ="dhopson@VMUbuntu-DSH comment with whitespaces", - - undefined = proplists:get_value(comment, Attributes1, undefined), - Comment2 = proplists:get_value(comment, Attributes2), - Comment3 = proplists:get_value(comment, Attributes3), - Comment4 = proplists:get_value(comment, Attributes4), - Comment5 = proplists:get_value(comment, Attributes5), - - Encoded = public_key:ssh_encode(Decoded, auth_keys), - Decoded = public_key:ssh_decode(Encoded, auth_keys). - -%%-------------------------------------------------------------------- -ssh_openssh_public_key_with_comment() -> - [{doc, "Test that emty lines and lines starting with # are ignored"}]. -ssh_openssh_public_key_with_comment(Config) when is_list(Config) -> - Datadir = proplists:get_value(data_dir, Config), - - {ok, DSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_dsa_with_comment_pub")), - [{{_, #'Dss-Parms'{}}, _}] = public_key:ssh_decode(DSARawOpenSsh, openssh_public_key). - -%%-------------------------------------------------------------------- -ssh_openssh_public_key_long_header() -> - [{doc, "Test that long headers are handled"}]. -ssh_openssh_public_key_long_header(Config) when is_list(Config) -> - Datadir = proplists:get_value(data_dir, Config), - - {ok,RSARawOpenSsh} = file:read_file(filename:join(Datadir, "ssh_rsa_long_header_pub")), - [{#'RSAPublicKey'{}, _}] = Decoded = public_key:ssh_decode(RSARawOpenSsh, public_key), - - Encoded = public_key:ssh_encode(Decoded, rfc4716_public_key), - Decoded = public_key:ssh_decode(Encoded, rfc4716_public_key). - -%%-------------------------------------------------------------------- -%% Check of different host keys left to later -ssh_hostkey_fingerprint_md5_implicit(_Config) -> - Expected = "4b:0b:63:de:0f:a7:3a:ab:2c:cc:2d:d1:21:37:1d:3a", - Expected = public_key:ssh_hostkey_fingerprint(ssh_hostkey(rsa)). - -%%-------------------------------------------------------------------- -%% Check of different host keys left to later -ssh_hostkey_fingerprint_md5(_Config) -> - Expected = "MD5:4b:0b:63:de:0f:a7:3a:ab:2c:cc:2d:d1:21:37:1d:3a", - Expected = public_key:ssh_hostkey_fingerprint(md5, ssh_hostkey(rsa)). - -%%-------------------------------------------------------------------- -%% Since this kind of fingerprint is not available yet on standard -%% distros, we do like this instead. The Expected is generated with: -%% $ openssh-7.3p1/ssh-keygen -E sha1 -lf <file> -%% 2048 SHA1:Soammnaqg06jrm2jivMSnzQGlmk none@example.org (RSA) -ssh_hostkey_fingerprint_sha(_Config) -> - Expected = "SHA1:Soammnaqg06jrm2jivMSnzQGlmk", - Expected = public_key:ssh_hostkey_fingerprint(sha, ssh_hostkey(rsa)). - -%%-------------------------------------------------------------------- -%% Since this kind of fingerprint is not available yet on standard -%% distros, we do like this instead. -ssh_hostkey_fingerprint_sha256(_Config) -> - Expected = "SHA256:T7F1BahkJWR7iJO8+rpzWOPbp7LZP4MlNrDExdNYOvY", - Expected = public_key:ssh_hostkey_fingerprint(sha256, ssh_hostkey(rsa)). - -%%-------------------------------------------------------------------- -%% Since this kind of fingerprint is not available yet on standard -%% distros, we do like this instead. -ssh_hostkey_fingerprint_sha384(_Config) -> - Expected = "SHA384:QhkLoGNI4KXdPvC//HxxSCP3uTQVADqxdajbgm+Gkx9zqz8N94HyP1JmH8C4/aEl", - Expected = public_key:ssh_hostkey_fingerprint(sha384, ssh_hostkey(rsa)). - -%%-------------------------------------------------------------------- -%% Since this kind of fingerprint is not available yet on standard -%% distros, we do like this instead. -ssh_hostkey_fingerprint_sha512(_Config) -> - Expected = "SHA512:ezUismvm3ADQQb6Nm0c1DwQ6ydInlJNfsnSQejFkXNmABg1Aenk9oi45CXeBOoTnlfTsGG8nFDm0smP10PBEeA", - Expected = public_key:ssh_hostkey_fingerprint(sha512, ssh_hostkey(rsa)). - -%%-------------------------------------------------------------------- -%% Since this kind of fingerprint is not available yet on standard -%% distros, we do like this instead. -ssh_hostkey_fingerprint_list(_Config) -> - Expected = ["SHA1:Soammnaqg06jrm2jivMSnzQGlmk", - "MD5:4b:0b:63:de:0f:a7:3a:ab:2c:cc:2d:d1:21:37:1d:3a"], - Expected = public_key:ssh_hostkey_fingerprint([sha,md5], ssh_hostkey(rsa)). - -%%-------------------------------------------------------------------- encrypt_decrypt() -> [{doc, "Test public_key:encrypt_private and public_key:decrypt_public"}]. encrypt_decrypt(Config) when is_list(Config) -> @@ -1413,13 +1078,6 @@ incorrect_emailaddress_pkix_cert() -> -ssh_hostkey(rsa) -> - [{PKdecoded,_}] = - public_key:ssh_decode( - <<"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDYXcYmsyJBstl4EfFYzfQJmSiUE162zvSGSoMYybShYOI6rnnyvvihfw8Aml+2gZ716F2tqG48FQ/yPZEGWNPMrCejPpJctaPWhpNdNMJ8KFXSEgr5bY2mEpa19DHmuDeXKzeJJ+X7s3fVdYc4FMk5731KIW6Huf019ZnTxbx0VKG6b1KAJBg3vpNsDxEMwQ4LFMB0JHVklOTzbxmpaeULuIxvl65A+eGeFVeo2Q+YI9UnwY1vSgmc9Azwy8Ie9Z0HpQBN5I7Uc5xnknT8V6xDhgNfXEfzsgsRdDfZLECt1WO/1gP9wkosvAGZWt5oG8pbNQWiQdFq536ck8WQD9WD none@example.org">>, - public_key), - PKdecoded. - hardcode_rsa_key() -> #'RSAPrivateKey'{ version = 'two-prime', diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl index c5d4b9bef3..99b796e24a 100644 --- a/lib/sasl/src/systools_make.erl +++ b/lib/sasl/src/systools_make.erl @@ -47,7 +47,7 @@ -compile({inline,[{badarg,2}]}). -ifdef(USE_ESOCK). --define(ESOCK_SOCKET_MODS, [socket]). +-define(ESOCK_SOCKET_MODS, [socket, socket_registry]). -define(ESOCK_NET_MODS, [prim_net]). -else. -define(ESOCK_SOCKET_MODS, []). diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index e376330809..dd1c535d01 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -80,6 +80,32 @@ </section> + <section><title>SNMP 5.4.3.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Its now possible to remove selected varbinds (from the + final message) when sending a notification. This is done + by setting the 'value' (in the varbind(s) of the varbinds + list) to '?NOTIFICATION_IGNORE_VB_VALUE'.</p> + <p> + Own Id: OTP-16349 Aux Id: ERIERL-444 </p> + </item> + <item> + <p> + Its now possible to specify that an oid shall be + "truncated" (trailing ".0" to be removed) when sending an + notification.</p> + <p> + Own Id: OTP-16360 Aux Id: ERIERL-451 </p> + </item> + </list> + </section> + +</section> + <section><title>SNMP 5.4.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/snmp/doc/src/snmp_agent_netif.xml b/lib/snmp/doc/src/snmp_agent_netif.xml index a8dea5ab7b..66a1bd900f 100644 --- a/lib/snmp/doc/src/snmp_agent_netif.xml +++ b/lib/snmp/doc/src/snmp_agent_netif.xml @@ -36,7 +36,7 @@ <image file="snmp_agent_netif_1.gif"> <icaption>The Purpose of Agent Net if</icaption> </image> - <p>The Network Interface (Net if) process delivers SNMP PDUs to a + <p>The Network Interface (Net If) process delivers SNMP PDUs to a master agent, and receives SNMP PDUs from the master agent. The most common behaviour of a Net if process is that is receives bytes from a network, decodes them into an SNMP PDU, which it sends to a master @@ -70,7 +70,7 @@ <marker id="messages"></marker> <title>Messages</title> <p>The section <em>Messages</em> describes mandatory messages, which - Net if must send and be able to receive. + Net If must send and be able to receive. </p> <p>In this section an <c>Address</c> field is a <c>{Domain, Addr}</c> tuple where <c>Domain</c> is diff --git a/lib/snmp/doc/src/snmp_app.xml b/lib/snmp/doc/src/snmp_app.xml index 54a7eafe76..978aff59b1 100644 --- a/lib/snmp/doc/src/snmp_app.xml +++ b/lib/snmp/doc/src/snmp_app.xml @@ -4,7 +4,7 @@ <appref> <header> <copyright> - <year>1997</year><year>2019</year> + <year>1997</year><year>2020</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -594,7 +594,7 @@ in the snmp_config file! <tag><marker id="manager_server"></marker> <c><![CDATA[server() = [server_opt()] <optional>]]></c></tag> <item> - <p><c>server_opt() = {timeout, server_timeout()} | {verbosity, verbosity()} | {cbproxy, server_cbproxy()}</c></p> + <p><c>server_opt() = {timeout, server_timeout()} | {verbosity, verbosity()} | {cbproxy, server_cbproxy()} | {netif_sup, server_nis()}</c></p> <p>Specifies the options for the manager server process.</p> <p>Default is <c>silence</c>.</p> </item> @@ -641,6 +641,36 @@ in the snmp_config file! <p>Default is <c>temporary</c>.</p> </item> + <tag><marker id="manager_server_nis"></marker> + <c><![CDATA[server_nis() = none (default) | {PingTO, PongTO} <optional>]]></c></tag> + <item> + <p>This option specifies if the server should actively supervise the + net-if process. + Note that this will only work if the used net-if process actually supports + the protocol. See + <seealso marker="snmpm_network_interface">snmpm_network_interface</seealso> behaviour for more info. </p> + <taglist> + <tag><marker id="manager_server_nis_none"></marker> + <c><![CDATA[none (default)]]></c></tag> + <item> + <p>No active supervision of the net-if process. </p> + </item> + + <tag><marker id="manager_server_nis_active"></marker> + <c><![CDATA[{PingTO :: pos_integer(), PongTO :: pos_integer()}]]></c></tag> + <item> + <p>The <c>PingTO</c> time specifies the between a successful ping + (or start) and the time when a ping message is to be sent to the net-if + process (basically the time between ping). </p> + <p>The <c>PongTO</c> time specifies how long time the net-if process + has to respond to a ping message, with a <em>pong</em> message. + Its starts counting when the ping message has been sent.</p> + <p>Both times are in milli seconds.</p> + </item> + </taglist> + <p>Default is <c>none</c>.</p> + </item> + <tag><marker id="manager_config"></marker> <c><![CDATA[manager_config() = [manager_config_opt()] <mandatory>]]></c></tag> <item> diff --git a/lib/snmp/doc/src/snmp_config.xml b/lib/snmp/doc/src/snmp_config.xml index d615edcec0..79c6703c94 100644 --- a/lib/snmp/doc/src/snmp_config.xml +++ b/lib/snmp/doc/src/snmp_config.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2016</year> + <year>1997</year><year>2020</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -612,7 +612,7 @@ in so far as it will be converted to the new format if found. <tag><marker id="manager_server"></marker> <c><![CDATA[server() = [server_opt()] <optional>]]></c></tag> <item> - <p><c>server_opt() = {timeout, server_timeout()} | {verbosity, verbosity()} | {cbproxy, server_cbproxy()}</c></p> + <p><c>server_opt() = {timeout, server_timeout()} | {verbosity, verbosity()} | {cbproxy, server_cbproxy()} | {netif_sup, server_nis()}</c></p> <p>Specifies the options for the manager server process.</p> <p>Default is <c>silence</c>.</p> </item> @@ -659,6 +659,40 @@ in so far as it will be converted to the new format if found. <p>Default is <c>temporary</c>.</p> </item> + <tag><marker id="manager_server_nis"></marker> + <c><![CDATA[server_nis() = none (default) | {PingTO, PongTO} <optional>]]></c></tag> + <item> + <p>This option specifies if the server should actively supervise the + net-if process. + Note that this will only work if the used net-if process actually supports + the protocol. See + <seealso marker="snmpm_network_interface">snmpm_network_interface</seealso> behaviour for more info. </p> + <taglist> + <tag><marker id="manager_server_nis_none"></marker> + <c><![CDATA[none (default)]]></c></tag> + <item> + <p>No active supervision of the net-if process. </p> + </item> + + <tag><marker id="manager_server_nis_active"></marker> + <c><![CDATA[{PingTO :: pos_integer(), PongTO :: pos_integer()}]]></c></tag> + <item> + <p>The <c>PingTO</c> time specifies the between a successful ping + (or start) and the time when a + <seealso marker="snmp_manager_netif#im_ping">ping</seealso> + message is to be sent to the net-if + process (basically the time between ping:s). </p> + <p>The <c>PongTO</c> time specifies how long time the net-if process + has to respond to a ping message, with a + <seealso marker="snmp_manager_netif#om_pong">pong</seealso> + message. + It starts counting when the ping message has been sent.</p> + <p>Both times are in milli seconds.</p> + </item> + </taglist> + <p>Default is <c>none</c>.</p> + </item> + <tag><marker id="manager_config"></marker> <c><![CDATA[manager_config() = [manager_config_opt()] <mandatory>]]></c></tag> <item> diff --git a/lib/snmp/doc/src/snmp_manager_netif.xml b/lib/snmp/doc/src/snmp_manager_netif.xml index 0dfcdbda0d..9825f3f3bd 100644 --- a/lib/snmp/doc/src/snmp_manager_netif.xml +++ b/lib/snmp/doc/src/snmp_manager_netif.xml @@ -37,7 +37,7 @@ <icaption>The Purpose of Manager Net if</icaption> </image> - <p>The Network Interface (Net if) process delivers SNMP PDUs to the + <p>The Network Interface (Net If) process delivers SNMP PDUs to the manager server, and receives SNMP PDUs from the manager server. The most common behaviour of a Net if process is that is receives request PDU from the manager server, encodes the PDU into bytes @@ -54,131 +54,186 @@ both uses UDP as the transport protocol i.e the transport domains <c>transportDomainUdpIpv4</c> and/or <c>transportDomainUdpIpv6</c>. The difference between the two modules is that the latter is - "multi-threaded", i.e. for each message/request a new process + "multi-threaded", i.e. for each message/request a new process is created that processes the message/request and then exits. </p> + <p>There is a <c>server</c> config option, + <seealso marker="snmp_config#manager_server_nis">netif_sup</seealso> + that enables "active" Net If supervision. This is very simple mechanism. + The (supervising) process simply sends a + <seealso marker="#im_ping">ping</seealso> message and expects a + <seealso marker="#om_pong">pong</seealso> message response + (withing a specific time). + The interval between each <c>ping/pong</c> exhange is user configurable. + As is the time that is allowed for the + <seealso marker="#om_pong">pong</seealso> + message to arrive. + Both the NetIf module(s) provided with the app supports active supervision. + If a NetIf module/process is used which do not implement this, then + the server cannot be configured with active supervision. </p> + <p>It is also possible to write your own Net if process and this section describes how to do that.</p> <section> <marker id="mandatory_functions"></marker> <title>Mandatory Functions</title> - <p>A Net if process must implement the SNMP manager + <p>A Net If process must implement the SNMP manager <seealso marker="snmpm_network_interface">network interface behaviour</seealso>. </p> </section> <section> <title>Messages</title> - <p>The section <em>Messages</em> describes mandatory messages, which - Net if must send to the manager server process. - </p> + <p>The section <em>Messages</em> describes mandatory (with exception + for the ping/pong messages) messages, + which Net If must send to the manager server process. </p> <p>In this section a <c>Domain</c> field is the transport domain i.e one of <c>transportDomainUdpIpv4</c> or <c>transportDomainUdpIpv6</c>, and an <c>Addr</c> field is an <c>{</c><seealso marker="kernel:inet#type-ip_address"><c>IpAddr</c></seealso><c>,IpPort}</c> tuple.</p> - <p>Net if must send the following message when it receives an - SNMP PDU from the network that is aimed for the MasterAgent: - </p> - <pre> + <section> + <marker id="outgoing_messages"></marker> + <title>Outgoing Messages</title> + + <p>Net if must send the following message when it receives an + SNMP PDU from the network that is aimed for the MasterAgent: </p> + <pre> Server ! {snmp_pdu, Pdu, Domain, Addr} - </pre> - <list type="bulleted"> - <item> - <p><c>Pdu</c> is an SNMP PDU record, as defined in - <c>snmp_types.hrl</c>, with the SNMP request.</p> - </item> - <item> - <p><c>Domain</c> is the source transport domain. </p> - </item> - <item> - <p><c>Addr</c> is the source address. </p> - </item> - </list> - <pre> + </pre> + <list type="bulleted"> + <item> + <p><c>Pdu</c> is an SNMP PDU record, as defined in + <c>snmp_types.hrl</c>, with the SNMP request.</p> + </item> + <item> + <p><c>Domain</c> is the source transport domain. </p> + </item> + <item> + <p><c>Addr</c> is the source address. </p> + </item> + </list> + + <pre> Server ! {snmp_trap, Trap, Domain, Addr} - </pre> - <list type="bulleted"> - <item> - <p><c>Trap</c> is either an SNMP pdu record or an trappdu record, - as defined in <c>snmp_types.hrl</c>, with the SNMP request.</p> - </item> - <item> - <p><c>Domain</c> is the source transport domain. </p> - </item> - <item> - <p><c>Addr</c> is the source address. </p> - </item> - </list> - <pre> + </pre> + <list type="bulleted"> + <item> + <p><c>Trap</c> is either an SNMP pdu record or an trappdu record, + as defined in <c>snmp_types.hrl</c>, with the SNMP request.</p> + </item> + <item> + <p><c>Domain</c> is the source transport domain. </p> + </item> + <item> + <p><c>Addr</c> is the source address. </p> + </item> + </list> + + <pre> Server ! {snmp_inform, Ref, Pdu, PduMS, Domain, Addr} - </pre> - <list type="bulleted"> - <item> - <p><c>Ref</c> is either the atom <c>ignore</c> or something - that can be used to identify the inform-request (e.g. request-id). - <c>ignore</c> is used if the response (acknowledgment) to the - inform-request has already been sent (this means that the server - will not make the call to the - <seealso marker="snmpm_network_interface#inform_response">inform_response</seealso> - function). See the - <seealso marker="snmp_config#manager_irb">inform request behaviour</seealso> - configuration option for more info.</p> - </item> - <item> - <p><c>Pdu</c> is an SNMP PDU record, as defined in - <c>snmp_types.hrl</c>, with the SNMP request.</p> - </item> - <item> - <p><c>Domain</c> is the source transport domain. </p> - </item> - <item> - <p><c>Addr</c> is the source address. </p> - </item> - </list> - <pre> + </pre> + <list type="bulleted"> + <item> + <p><c>Ref</c> is either the atom <c>ignore</c> or something + that can be used to identify the inform-request (e.g. request-id). + <c>ignore</c> is used if the response (acknowledgment) to the + inform-request has already been sent (this means that the server + will not make the call to the + <seealso marker="snmpm_network_interface#inform_response">inform_response</seealso> + function). See the + <seealso marker="snmp_config#manager_irb">inform request behaviour</seealso> + configuration option for more info.</p> + </item> + <item> + <p><c>Pdu</c> is an SNMP PDU record, as defined in + <c>snmp_types.hrl</c>, with the SNMP request.</p> + </item> + <item> + <p><c>Domain</c> is the source transport domain. </p> + </item> + <item> + <p><c>Addr</c> is the source address. </p> + </item> + </list> + + <pre> Server ! {snmp_report, Data, Domain, Addr} - </pre> - <list type="bulleted"> - <item> - <p><c>Data</c> is either <c>{ok, Pdu}</c> or - <c>{error, ReqId, ReasonInfo, Pdu}</c>. Which one is used depends - on the return value from the MPD - <seealso marker="snmpm_mpd#process_msg">process_msg</seealso> function. If the MsgData is <c>ok</c>, - the first is used, and if it is <c>{error, ReqId, Reason}</c> - the latter is used.</p> - </item> - <item> - <p><c>Pdu</c> is an SNMP PDU record, as defined in - <c>snmp_types.hrl</c>, with the SNMP request.</p> - </item> - <item> - <p><c>ReqId</c> is an integer.</p> - </item> - <item> - <p><c>ReasonInfo</c> is a term().</p> - </item> - <item> - <p><c>Domain</c> is the source transport domain. </p> - </item> - <item> - <p><c>Addr</c> is the source address. </p> - </item> - </list> + </pre> + <list type="bulleted"> + <item> + <p><c>Data</c> is either <c>{ok, Pdu}</c> or + <c>{error, ReqId, ReasonInfo, Pdu}</c>. Which one is used depends + on the return value from the MPD + <seealso marker="snmpm_mpd#process_msg">process_msg</seealso> function. If the MsgData is <c>ok</c>, + the first is used, and if it is <c>{error, ReqId, Reason}</c> + the latter is used.</p> + </item> + <item> + <p><c>Pdu</c> is an SNMP PDU record, as defined in + <c>snmp_types.hrl</c>, with the SNMP request.</p> + </item> + <item> + <p><c>ReqId</c> is an integer.</p> + </item> + <item> + <p><c>ReasonInfo</c> is a term().</p> + </item> + <item> + <p><c>Domain</c> is the source transport domain. </p> + </item> + <item> + <p><c>Addr</c> is the source address. </p> + </item> + </list> + + <marker id="om_pong"></marker> + <pre> +Supervisor ! {pong, self()} + </pre> + <list type="bulleted"> + <item> + <p><c>Supervisor</c> is the process that sent the + <seealso marker="#im_ping">ping</seealso> message (see below). </p> + </item> + </list> + </section> <section> - <title>Notes</title> - <p>Since the Net if process is responsible for encoding and - decoding of SNMP messages, it must also update the relevant - counters in the SNMP group in MIB-II. It can use the functions - in the module <c>snmpm_mpd</c> for this purpose (refer to the - Reference Manual, section <c>snmp</c>, module <c>snmpm_mpd</c> - for more details). - </p> - <p>There are also some useful functions for encoding and - decoding of SNMP messages in the module <c>snmp_pdus</c>. - </p> + <marker id="incoming_messages"></marker> + <title>Incoming Messages</title> + <p>This section describes the incoming messages which a Net If + process may choose to respond to. </p> + + <list type="bulleted"> + <item> + <marker id="im_ping"></marker> + <p><c>{ping, Supervisor}</c></p> + <p>This message is sent to the Net If process by a process that + has been configured to perfor "active supervision" of the Net If + process. The Net If process should respond immediately with + a <seealso marker="#om_pong">pong</seealso> message. </p> + <list type="bulleted"> + <item> + <p><c>Supervisor</c> is a <c>pid()</c>. </p> + </item> + </list> + </item> + </list> </section> </section> + + <section> + <title>Notes</title> + <p>Since the Net if process is responsible for encoding and + decoding of SNMP messages, it must also update the relevant + counters in the SNMP group in MIB-II. It can use the functions + in the module <c>snmpm_mpd</c> for this purpose (refer to the + Reference Manual, section <c>snmp</c>, module <c>snmpm_mpd</c> + for more details). </p> + + <p>There are also some useful functions for encoding and + decoding of SNMP messages in the module <c>snmp_pdus</c>. </p> + </section> </chapter> diff --git a/lib/snmp/doc/src/snmp_pdus.xml b/lib/snmp/doc/src/snmp_pdus.xml index f403b6edf4..4b00dcb7f0 100644 --- a/lib/snmp/doc/src/snmp_pdus.xml +++ b/lib/snmp/doc/src/snmp_pdus.xml @@ -125,22 +125,7 @@ <p>Decodes a list of bytes into an SNMP UsmSecurityParameters</p> </desc> </func> - <func> - <name since="">enc_encrypted_scoped_pdu(EncryptedScopedPdu) -> [byte()]</name> - <fsummary>Encode an encrypted SNMP scopedPDU</fsummary> - <type> - <v>EncryptedScopedPdu = [byte()]</v> - </type> - <desc> - <p>Encodes an encrypted SNMP ScopedPdu into an OCTET STRING - that can be used as the <c>data</c> field in a - <c>message</c> record, that later can be encoded with a call - to <c>enc_message_only/1</c>. - </p> - <p>This function should be used whenever the <c>ScopedPDU</c> - is encrypted.</p> - </desc> - </func> + <func> <name since="">enc_message(Message) -> [byte()]</name> <fsummary>Encode an SNMP Message</fsummary> diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml index c45df98ee0..71308dcf80 100644 --- a/lib/snmp/doc/src/snmpm.xml +++ b/lib/snmp/doc/src/snmpm.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2004</year><year>2018</year> + <year>2004</year><year>2020</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -1487,6 +1487,21 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 verbosity <c>silence</c>, nothing is printed. The higher the verbosity, the more is printed.</p> + <marker id="restart"></marker> + </desc> + </func> + + <func> + <name since="">restart(Ref) -> void()</name> + <fsummary>Restart the indicated process</fsummary> + <type> + <v>Ref = net_if</v> + </type> + <desc> + <p>Restart the indicated process (<c>Ref</c>). Note that its not + without risk to restart a process, and should therefor be used + with care. </p> + <marker id="format_reason"></marker> </desc> </func> diff --git a/lib/snmp/doc/src/snmpm_mpd.xml b/lib/snmp/doc/src/snmpm_mpd.xml index b024f8d294..721e579470 100644 --- a/lib/snmp/doc/src/snmpm_mpd.xml +++ b/lib/snmp/doc/src/snmpm_mpd.xml @@ -48,7 +48,7 @@ <funcs> <func> - <name since="">init_mpd(Vsns) -> mpd_state()</name> + <name since="">init(Vsns) -> mpd_state()</name> <fsummary>Initialize the MPD module</fsummary> <type> <v>Vsns = [Vsn]</v> diff --git a/lib/snmp/doc/src/snmpm_user.xml b/lib/snmp/doc/src/snmpm_user.xml index c961300490..fe0ac0b0d2 100644 --- a/lib/snmp/doc/src/snmpm_user.xml +++ b/lib/snmp/doc/src/snmpm_user.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2004</year><year>2016</year> + <year>2004</year><year>2020</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -96,7 +96,7 @@ snmp_v1_trap_info() :: {Enteprise :: snmp:oid(), <name since="">handle_error(ReqId, Reason, UserData) -> void()</name> <fsummary>Handle error</fsummary> <type> - <v>ReqId = integer()</v> + <v>ReqId = netif | integer()</v> <v>Reason = {unexpected_pdu, SnmpInfo} | {invalid_sec_info, SecInfo, SnmpInfo} | {empty_message, Addr, Port} | term()</v> <v>SnmpInfo = snmp_gen_info()</v> <v>SecInfo = term()</v> @@ -115,7 +115,11 @@ snmp_v1_trap_info() :: {Enteprise :: snmp:oid(), <p>If <c>ReqId</c> is less then 0, it means that this information was not available to the manager (that info was never retrieved before the message was discarded). </p> - <p>For <c>SnmpInfo</c> see handle_agent below.</p> + <p>For <c>SnmpInfo</c> see handle_agent below.</p> + <p>Note that there is a special case when the value of <c>ReqId</c> + has the value of the atom <c>netif</c>. This means that the NetIF + process has suffered a "fatal" error and been restarted. + With possible loss of traffic! </p> <marker id="handle_agent"></marker> </desc> diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl index 8e60cecaf9..c0859f6fd8 100644 --- a/lib/snmp/src/manager/snmpm.erl +++ b/lib/snmp/src/manager/snmpm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2019. All Rights Reserved. +%% Copyright Ericsson AB 2004-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -89,8 +89,9 @@ system_start_time/0, sys_up_time/0, - info/0, - verbosity/2 + info/0, info/1, + verbosity/2, + restart/1 ]). -export([format_reason/1, format_reason/2]). @@ -297,6 +298,9 @@ oid_to_type(Oid) -> info() -> snmpm_server:info(). +info(Key) -> + proplists:get_value(Key, info(), {error, not_found}). + %% -- Verbosity -- @@ -316,6 +320,16 @@ verbosity(all, V) -> snmpm_server:verbosity(note_store, V). +%% -- Restart -- + +%% Restart various component processes in the manager +%% Note that the effects of this is diffiult to +%% predict, so it should be use with *caution*! + +restart(net_if = What) -> + snmpm_server:restart(What). + + %% -- Users -- %% Register the 'user'. diff --git a/lib/snmp/src/manager/snmpm_config.erl b/lib/snmp/src/manager/snmpm_config.erl index fc6ddc5738..decda09022 100644 --- a/lib/snmp/src/manager/snmpm_config.erl +++ b/lib/snmp/src/manager/snmpm_config.erl @@ -151,6 +151,7 @@ -define(SERVER_OPT_GCT_DEFAULT, 30000). -define(SERVER_OPT_MT_DEFAULT, true). -define(SERVER_OPT_CBP_DEFAULT, temporary). % permanent +-define(SERVER_OPT_NIS_DEFAULT, none). %% -define(DEF_ADDR_TAG, default_addr_tag). -define(DEFAULT_TARGETNAME, default_agent). @@ -1115,15 +1116,17 @@ do_init(Opts) -> end, %% -- Server (optional) -- - ServerOpts = get_opt(server, Opts, []), + ServerOpts = get_opt(server, Opts, []), ServerVerb = get_opt(verbosity, ServerOpts, ?SERVER_OPT_VERB_DEFAULT), ServerGct = get_opt(timeout, ServerOpts, ?SERVER_OPT_GCT_DEFAULT), ServerMt = get_opt(multi_threaded, ServerOpts, ?SERVER_OPT_MT_DEFAULT), ServerCBP = get_opt(cbproxy, ServerOpts, ?SERVER_OPT_CBP_DEFAULT), + ServerNIS = get_opt(netif_sup, ServerOpts, ?SERVER_OPT_NIS_DEFAULT), ets:insert(snmpm_config_table, {server_verbosity, ServerVerb}), ets:insert(snmpm_config_table, {server_timeout, ServerGct}), ets:insert(snmpm_config_table, {server_multi_threaded, ServerMt}), ets:insert(snmpm_config_table, {server_cbproxy, ServerCBP}), + ets:insert(snmpm_config_table, {server_nis, ServerNIS}), %% -- Mibs (optional) -- ?vdebug("initiate mini mib", []), @@ -1132,7 +1135,7 @@ do_init(Opts) -> init_mini_mib(Mibs), %% -- Net-if (optional) -- - ?vdebug("net_if options", []), + ?vdebug("net-if options", []), NetIfIrb = case get_opt(inform_request_behaviour, Opts, ?IRB_DEFAULT) of user -> @@ -1421,6 +1424,9 @@ verify_server_opts([{multi_threaded, MT}|Opts]) when is_boolean(MT) -> verify_server_opts([{cbproxy, CBP}|Opts]) -> verify_server_cbproxy(CBP), verify_server_opts(Opts); +verify_server_opts([{netif_sup, NIS}|Opts]) -> + verify_server_nis(NIS), + verify_server_opts(Opts); verify_server_opts([Opt|_]) -> error({invalid_server_option, Opt}). @@ -1436,6 +1442,17 @@ verify_server_cbproxy(permanent) -> verify_server_cbproxy(CBP) -> error({invalid_server_cbproxy, CBP}). +verify_server_nis(none) -> + ok; +verify_server_nis({PingTo, PongTo} = V) when is_integer(PingTo) andalso + (PingTo > 0) andalso + is_integer(PongTo) andalso + (PongTo > 0) -> + ok; +verify_server_nis(NIS) -> + error({invalid_server_netif_sup, NIS}). + + verify_net_if_opts([]) -> ok; verify_net_if_opts([{module, Mod}|Opts]) -> diff --git a/lib/snmp/src/manager/snmpm_net_if.erl b/lib/snmp/src/manager/snmpm_net_if.erl index c2e5c6d2f0..0d57bc944a 100644 --- a/lib/snmp/src/manager/snmpm_net_if.erl +++ b/lib/snmp/src/manager/snmpm_net_if.erl @@ -58,6 +58,14 @@ %% -define(VMODULE,"NET_IF"). -include("snmp_verbosity.hrl"). +%% This is for debugging!! +-ifdef(snmp_debug). +-define(allow_exec, true). +-else. +-define(allow_exec, false). +-endif. + + -record(state, { server, @@ -67,7 +75,8 @@ log, irb = auto, % auto | {user, integer()} irgc, - filter + filter, + allow_exec = ?allow_exec }). -record(transport, @@ -92,6 +101,7 @@ -define(ATL_SEQNO_MAX, 2147483647). + %%%------------------------------------------------------------------- %%% API %%%------------------------------------------------------------------- @@ -520,6 +530,13 @@ handle_call(info, _From, State) -> Reply = get_info(State), {reply, Reply, State}; +%% This is for debugging!! +handle_call({exec, F}, _From, #state{allow_exec = true} = State) + when is_function(F, 0) -> + ?vlog("[call] exec", []), + {reply, F(), State}; + + handle_call(Req, From, State) -> warning_msg("received unknown request (from ~p): ~n~p", [Req, From]), {reply, {error, {invalid_request, Req}}, State}. @@ -556,6 +573,13 @@ handle_cast(filter_reset, State) -> reset_counters(), {noreply, State}; +%% This is for debugging!! +handle_cast({exec, F}, #state{allow_exec = true} = State) when is_function(F, 0) -> + ?vlog("[cast] exec", []), + F(), + {noreply, State}; + + handle_cast(Msg, State) -> warning_msg("received unknown message: ~n~p", [Msg]), {noreply, State}. @@ -597,6 +621,20 @@ handle_info({disk_log, _Node, Log, Info}, State) -> handle_info({'DOWN', _, _, _, _} = Info, State) -> handle_info_down(Info, State); + +handle_info({ping, Pid}, State) -> + ?vdebug("received ping message from ~p", [Pid]), + Pid ! {pong, self()}, + {noreply, State}; + + +%% This is for debugging!! +handle_info({exec, F}, #state{allow_exec = true} = State) when is_function(F, 0) -> + ?vlog("[info] exec", []), + F(), + {noreply, State}; + + handle_info(Info, State) -> handle_info_unknown(Info, State). diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl index fe2c22d9ba..1a8d09ab9b 100644 --- a/lib/snmp/src/manager/snmpm_server.erl +++ b/lib/snmp/src/manager/snmpm_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2019. All Rights Reserved. +%% Copyright Ericsson AB 2004-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -45,13 +45,13 @@ %% discovery/2, discovery/3, discovery/4, discovery/5, discovery/6, - %% system_info_updated/2, get_log_type/0, set_log_type/1, reconfigure/0, info/0, - verbosity/1, verbosity/2 + verbosity/1, verbosity/2, + restart/1 ]). @@ -77,6 +77,9 @@ %% GCT exports -export([gct_init/1, gct/2]). +%% NIS exports +-export([nis_init/2, nis_loop/1]). + %% CallBack Proxy exports -export([cbproxy_loop/1, do_handle_error/4, @@ -157,6 +160,24 @@ net_if, net_if_mod, net_if_ref, + + %% NetIF supervision + %% This (config) option defines if/how the "server" + %% shall supervise the net-if process. + %% And by "supervise" we don't meant in the way a + %% *supervisor" supervisor. This is "active" supervision + %% (basically, send ping and expect pong back). + %% There are two alternatives: + %% none - No supervision (default) + %% {PingInterval, PongTimeout} + %% PingInterval :: pos_integer() + %% Time between a successful test and the next. + %% PongInterval :: pos_integer() + %% Time the NetIF process has to answer a ping + %% + nis :: none | {pos_integer(), pos_integer()}, + nis_pid :: undefined | pid(), % Pid of the process doing the actual sup + req, %% ???? Last request id in outgoing message oid, %% ???? Last oid in request outgoing message mini_mib, @@ -492,6 +513,9 @@ cancel_async_request(UserId, ReqId) -> call({cancel_async_request, UserId, ReqId}). +info() -> + call(info). + verbosity(Verbosity) -> case ?vvalidate(Verbosity) of Verbosity -> @@ -500,9 +524,6 @@ verbosity(Verbosity) -> {error, {invalid_verbosity, Verbosity}} end. -info() -> - call(info). - verbosity(net_if = Ref, Verbosity) -> verbosity2(Ref, Verbosity); verbosity(note_store = Ref, Verbosity) -> @@ -516,9 +537,10 @@ verbosity2(Ref, Verbosity) -> {error, {invalid_verbosity, Verbosity}} end. -%% Target -> all | server | net_if -%% system_info_updated(Target, What) -> -%% call({system_info_updated, Target, What}). + +restart(net_if = What) -> + cast({restart, What}). + get_log_type() -> call(get_log_type). @@ -590,6 +612,15 @@ do_init() -> {NoteStore, NoteStoreRef} = do_init_note_store(Prio), {NetIf, NetIfModule, NetIfRef} = do_init_net_if(NoteStore), + %% -- (maybe) Start the NetIF "supervisor" -- + {NIS, NISPid} = + case snmpm_config:system_info(server_nis) of + {ok, none = V} -> + {V, undefined}; + {ok, {PingTO, PongTO} = V} -> + {V, nis_start(NetIf, PingTO, PongTO)} + end, + MiniMIB = snmpm_config:make_mini_mib(), State = #state{mini_mib = MiniMIB, gct = GCT, @@ -598,6 +629,8 @@ do_init() -> net_if = NetIf, net_if_mod = NetIfModule, net_if_ref = NetIfRef, + nis = NIS, + nis_pid = NISPid, cbproxy = CBProxy, cbproxy_pid = CBPPid}, ?vlog("started", []), @@ -624,7 +657,7 @@ do_init_note_store(Prio) -> end. do_init_net_if(NoteStore) -> - ?vdebug("try start net if", []), + ?vdebug("try start net-if", []), {ok, NetIfModule} = snmpm_config:system_info(net_if_module), case snmpm_misc_sup:start_net_if(NetIfModule, NoteStore) of {ok, Pid} -> @@ -638,6 +671,7 @@ do_init_net_if(NoteStore) -> throw({error, {failed_starting_net_if, Reason}}) end. + %% --------------------------------------------------------------------- %% --------------------------------------------------------------------- @@ -1017,6 +1051,13 @@ handle_call(Req, _From, State) -> {reply, {error, unknown_request}, State}. +handle_cast({restart, net_if}, + #state{net_if = Pid} = State) -> + ?vlog("received net_if (~p) restart message", [Pid]), + %% We will get an exit signel/message, which will trigger a (re-)start + exit(Pid, kill), + {noreply, State}; + handle_cast(Msg, State) -> warning_msg("received unknown message: ~n~p", [Msg]), {noreply, State}. @@ -1077,19 +1118,18 @@ handle_info(gc_timeout, #state{gct = GCT} = State) -> {noreply, State}; -handle_info({'DOWN', _MonRef, process, Pid, _Reason}, - #state{note_store = NoteStore, - net_if = Pid} = State) -> - ?vlog("received 'DOWN' message regarding net_if", []), - {NetIf, _, Ref} = do_init_net_if(NoteStore), - {noreply, State#state{net_if = NetIf, net_if_ref = Ref}}; +handle_info({'DOWN', _MonRef, process, Pid, Reason}, + #state{net_if = Pid} = State) -> + ?vlog("received 'DOWN' message regarding net_if (~p)", [Pid]), + NewState = handle_netif_down(State, Reason), + {noreply, NewState}; handle_info({'DOWN', _MonRef, process, Pid, _Reason}, #state{note_store = Pid, net_if = NetIf, net_if_mod = Mod} = State) -> - ?vlog("received 'DOWN' message regarding note_store", []), + ?vlog("received 'DOWN' message regarding note_store (~p)", [Pid]), {ok, Prio} = snmpm_config:system_info(prio), {NoteStore, Ref} = do_init_note_store(Prio), Mod:note_store(NetIf, NoteStore), @@ -1118,8 +1158,17 @@ handle_info({'EXIT', Pid, Reason}, #state{cbproxy_pid = Pid} = State) -> {noreply, State#state{cbproxy_pid = NewCBP}}; +handle_info({'EXIT', Pid, Reason}, #state{net_if = NetIF, + nis = {PingTO, PongTO}, + nis_pid = Pid} = State) -> + warning_msg("NetIF (active) supervisor (~w) process crashed: " + "~n ~p", [Pid, Reason]), + NewNIS = nis_start(NetIF, PingTO, PongTO), + {noreply, State#state{nis_pid = NewNIS}}; + + handle_info(Info, State) -> - warning_msg("received unknown info: ~n~p", [Info]), + warning_msg("received unknown info: ~n ~p", [Info]), {noreply, State}. @@ -1146,8 +1195,9 @@ code_change(_Vsn, #state{gct = Pid} = State0, _Extra) -> %% Terminate %%---------------------------------------------------------- -terminate(Reason, #state{gct = GCT, cbproxy = CBP}) -> +terminate(Reason, #state{nis_pid = NIS, gct = GCT, cbproxy = CBP}) -> ?vdebug("terminate: ~p",[Reason]), + nis_stop(NIS), cbproxy_stop(CBP), gct_stop(GCT), snmpm_misc_sup:stop_note_store(), @@ -3119,7 +3169,17 @@ handle_invalid_result(Func, Args, InvalidResult) -> "~n Invalid result: ~p", [Func, Args, InvalidResult]). - + +handle_netif_down(#state{note_store = NoteStore, + nis_pid = NIS} = State, + Reason) -> + %% Srart a new NetIF + {NetIF, _, Ref} = do_init_net_if(NoteStore), + %% Inform the (active) supervisor + nis_netif(NIS, NetIF), + netif_down_inform_users(Reason), + State#state{net_if = NetIF, net_if_ref = Ref}. + handle_down(MonRef) -> (catch do_handle_down(MonRef)). @@ -3608,6 +3668,258 @@ new_timeout(T1, T2) -> %%---------------------------------------------------------------------- +%% NetIF Supervisor +%% +%% The NIS process "supervises" the NetIF process. That does *not* mean +%% that it takes on the role of a standard erlang 'supervisor' process. +%% The NIS is instead a process that "actively" supervises by means +%% of "ping" messages, which the supervised process must respond to +%% (with a pong message) *in time*. +%% If it does not, NIS assumes that it has hung, and kills it. +%% The server process then restarts the NetIF process and informs NIS. +%%---------------------------------------------------------------------- + +nis_start(NetIF, PingTO, PongTO) -> + ?vdebug("start nis process (~w, ~w)", [PingTO, PongTO]), + State = #{parent => self(), + netif_pid => NetIF, + ping_to => PingTO, + ping_tref => undefined, + ping_start => undefined, % Time when ping sent + ping_max => undefined, % Max roundtrip time + pong_to => PongTO, + pong_tref => undefined, + kill_cnt => 0}, + proc_lib:start_link(?MODULE, nis_init, [State, get(verbosity)]). + +nis_stop(NIS) when is_pid(NIS) -> + NIS ! {?MODULE, self(), stop}; +nis_stop(_) -> + ok. + + +nis_info(NIS) when is_pid(NIS) -> + NIS ! {?MODULE, self(), info}, + receive + {?MODULE, NIS, {info, Info}} -> + Info + after 1000 -> + [] + end; +nis_info(_) -> + []. + + +nis_netif(NIS, NetIF) when is_pid(NIS) andalso is_pid(NetIF) -> + NIS ! {?MODULE, self(), {netif, NetIF}}; +nis_netif(_, _) -> + ok. + + +nis_init(#{parent := Parent, + netif_pid := NetIF, + ping_to := PingTO} = State, + Verbosity) -> + put(verbosity, Verbosity), + put(sname, mnis), + ?vlog("starting"), + MRef = erlang:monitor(process, NetIF), + TRef = erlang:start_timer(PingTO, self(), ping_timeout), + erlang:register(snmpm_server_nis, self()), + proc_lib:init_ack(Parent, self()), + ?vlog("started"), + nis_loop(State#{netif_mref => MRef, + ping_tref => TRef}). + +%% The NetIF is dead. Its up to the server to restart it and inform us +nis_loop(#{parent := Parent, + netif_pid := undefined, + ping_tref := undefined, + pong_tref := undefined} = State) -> + receive + {?MODULE, Parent, stop} -> + ?vlog("[idle] stop received"), + nis_handle_stop(State), + exit(normal); + + {?MODULE, Pid, info} -> + ?vtrace("[idle] info received"), + Info = nis_handle_info(State), + Pid ! {?MODULE, self(), {info, Info}}, + ?MODULE:nis_loop(State); + + {?MODULE, Parent, {netif, NetIF}} -> + ?vlog("[idle] (new) netif started: ~p => start ping timer", [NetIF]), + MRef = erlang:monitor(process, NetIF), + PingTO = maps:get(ping_to, State), + TRef = erlang:start_timer(PingTO, self(), ping_timeout), + ?MODULE:nis_loop(State#{netif_pid => NetIF, + netif_mref => MRef, + ping_max => undefined, + ping_tref => TRef}); + + _Any -> + ?MODULE:nis_loop(State) + + after 5000 -> + %% This is for code upgrade + ?MODULE:nis_loop(State) + end; + +%% PING timer running (waiting for ping-timeout) +nis_loop(#{parent := Parent, + netif_pid := NetIF, + netif_mref := MRef, + ping_tref := PingTRef, + pong_tref := undefined} = State) when is_pid(NetIF) andalso + (PingTRef =/= undefined) -> + receive + {'DOWN', MRef, process, NetIF, _} -> + ?vlog("[ping] netif died => cancel ping timer"), + erlang:cancel_timer(PingTRef), + ?MODULE:nis_loop(State#{netif_pid => undefined, + netif_mref => undefined, + ping_tref => undefined}); + + {?MODULE, Parent, stop} -> + ?vlog("[ping] stop received"), + nis_handle_stop(State), + exit(normal); + + {?MODULE, Pid, info} -> + ?vtrace("[ping] info received"), + Info = nis_handle_info(State), + Pid ! {?MODULE, self(), {info, Info}}, + ?MODULE:nis_loop(State); + + %% Time to ping NetIF + {timeout, PingTRef, ping_timeout} -> + ?vlog("[ping] (ping-) timer timeout => send ping and start pong timer"), + NetIF ! {ping, self()}, + PongTO = maps:get(pong_to, State), + TRef = erlang:start_timer(PongTO, self(), pong_timeout), + ?MODULE:nis_loop(State#{ping_tref => undefined, + ping_start => us(), + pong_tref => TRef}); + + _Any -> + ?MODULE:nis_loop(State) + + after 5000 -> + %% This is for code upgrade + ?MODULE:nis_loop(State) + end; + +%% PONG timer running (waiting for pong message) +nis_loop(#{parent := Parent, + netif_pid := NetIF, + netif_mref := MRef, + ping_tref := undefined, + pong_tref := PongTRef} = State) when is_pid(NetIF) andalso + (PongTRef =/= undefined) -> + receive + {'DOWN', MRef, process, NetIF, _} -> + ?vlog("[pong] netif died => cancel pong timer"), + erlang:cancel_timer(PongTRef), + ?MODULE:nis_loop(State#{netif_pid => undefined, + netif_mref => undefined, + pong_tref => undefined}); + + {?MODULE, Parent, stop} -> + ?vlog("[pong] stop received"), + nis_handle_stop(State), + exit(normal); + + {?MODULE, Pid, info} -> + ?vlog("[pong] info received"), + Info = nis_handle_info(State), + Pid ! {?MODULE, self(), {info, Info}}, + ?MODULE:nis_loop(State); + + {pong, NetIF} -> + ?vlog("[pong] received pong => cancel pong timer, start ping timer"), + T = us(), + erlang:cancel_timer(PongTRef), + Start = maps:get(ping_start, State), + Max = maps:get(ping_max, State), + RT = T - Start, + NewMax = + if + (Max =:= undefined) -> + ?vtrace("[pong] Max: ~w", [RT]), + RT; + (RT > Max) -> + ?vtrace("[pong] New Max: ~w", [RT]), + RT; + true -> + Max + end, + PingTO = maps:get(ping_to, State), + PingTRef = erlang:start_timer(PingTO, self(), ping_timeout), + ?MODULE:nis_loop(State#{ping_tref => PingTRef, + ping_start => undefined, + ping_max => NewMax, + pong_tref => undefined}); + + %% Time to kill NetIF + {timeout, PongTRef, pong_timeout} -> + ?vinfo("[pong] (pong-) timer timeout => kill NetIF (~p)", [NetIF]), + nis_handle_pong_timeout(NetIF, MRef), + KCnt = maps:get(kill_cnt, State), + ?MODULE:nis_loop(State#{netif_pid => undefined, + netif_mref => undefined, + pong_tref => undefined, + kill_cnt => KCnt + 1}); + + _Any -> + ?MODULE:nis_loop(State) + + after 5000 -> + %% This is for code upgrade + ?MODULE:nis_loop(State) + end. + + +nis_handle_info(#{ping_max := undefined, kill_cnt := KCnt}) -> + [{kcnt, KCnt}]; +nis_handle_info(#{ping_max := Max, kill_cnt := KCnt}) -> + [{max, Max}, {kcnt, KCnt}]. + + +nis_handle_stop(#{pong_tref := PongTRef, + ping_tref := PingTRef}) -> + cancel_timer(PongTRef), + cancel_timer(PingTRef). + +%% Inform all users that the netif process has been killed +nis_handle_pong_timeout(NetIF, MRef) -> + erlang:demonitor(MRef, [flush]), + exit(NetIF, kill), + netif_down_inform_users({pong, killed}), + ok. + + +%%---------------------------------------------------------------------- + +%% Inform all users that "something" fatal happened to the NetIF process. +netif_down_inform_users(Reason) -> + InformUser = fun(UserId) -> + spawn(fun() -> + case snmpm_config:user_info(UserId) of + {ok, UserMod, UserData} -> + UserMod:handle_error(netif, + Reason, + UserData); + {error, _} -> + ok + end, + exit(normal) + end) + end, + lists:foreach(InformUser, snmpm_config:which_users()). + + +%%---------------------------------------------------------------------- maybe_delete(false, ReqId) -> ets:delete(snmpm_request_table, ReqId); @@ -3651,6 +3963,9 @@ is_started(#state{net_if = _Pid, net_if_mod = _Mod}) -> %%---------------------------------------------------------------------- +cast(Req) -> + gen_server:cast(?SERVER, Req). + call(Req) -> call(Req, infinity). @@ -3672,15 +3987,16 @@ get_info(#state{gct = GCT, net_if = NI, net_if_mod = NIMod, note_store = NS, + nis_pid = NIS, cbproxy = CBP}) -> - Info = [{server, server_info(GCT, CBP)}, + Info = [{server, server_info(GCT, CBP, NIS)}, {config, config_info()}, {net_if, net_if_info(NI, NIMod)}, {note_store, note_store_info(NS)}, {stats_counters, get_stats_counters()}], Info. -server_info(GCT, CBP) -> +server_info(GCT, CBP, NIS) -> {CBPInfo, CBPSz} = if (CBP =:= permanent) -> @@ -3689,12 +4005,20 @@ server_info(GCT, CBP) -> true -> {[], []} end, + {NISInfo, NISSz} = + case NIS of + undefined -> + {[], []}; + _ -> + {[{nis, nis_info(NIS)}], + [{nis, proc_mem(NIS)}]} + end, ProcSize = proc_mem(self()), GCTSz = proc_mem(GCT), RTSz = tab_size(snmpm_request_table), MTSz = tab_size(snmpm_monitor_table), - [{process_memory, [{server, ProcSize}, {gct, GCTSz}] ++ CBPSz}, - {db_memory, [{request, RTSz}, {monitor, MTSz}]}] ++ CBPInfo. + [{process_memory, [{server, ProcSize}, {gct, GCTSz}] ++ CBPSz ++ NISSz}, + {db_memory, [{request, RTSz}, {monitor, MTSz}]}] ++ CBPInfo ++ NISInfo. proc_mem(P) when is_pid(P) -> case (catch erlang:process_info(P, memory)) of @@ -3744,3 +4068,6 @@ note_store_info(Pid) -> %%---------------------------------------------------------------------- + +us() -> + erlang:monotonic_time(micro_seconds). diff --git a/lib/snmp/src/manager/snmpm_user.erl b/lib/snmp/src/manager/snmpm_user.erl index c0100d372f..9d0077449a 100644 --- a/lib/snmp/src/manager/snmpm_user.erl +++ b/lib/snmp/src/manager/snmpm_user.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -41,7 +41,7 @@ %% An "asynchronous" error has been detected -callback handle_error( - ReqId :: integer(), + ReqId :: netif | integer(), Reason :: {unexpected_pdu, SnmpInfo :: snmp_gen_info()} | {invalid_sec_info, SecInfo :: term(), diff --git a/lib/snmp/src/misc/snmp_verbosity.erl b/lib/snmp/src/misc/snmp_verbosity.erl index 9b2676d048..a42a1acb3a 100644 --- a/lib/snmp/src/misc/snmp_verbosity.erl +++ b/lib/snmp/src/misc/snmp_verbosity.erl @@ -138,6 +138,7 @@ image_of_sname(mns) -> "M-NOTE-STORE"; image_of_sname(mnif) -> "M-NET-IF"; image_of_sname(mnifl) -> "M-NET-IF-LOGGER"; image_of_sname(mnifw) -> io_lib:format("M-NET-IF-worker(~p)", [self()]); +image_of_sname(mnis) -> "M-NIS"; image_of_sname(mconf) -> "M-CONF"; image_of_sname(lc) -> io_lib:format("LOG-CONVERTER(~p)", [self()]); diff --git a/lib/snmp/src/misc/snmp_verbosity.hrl b/lib/snmp/src/misc/snmp_verbosity.hrl index c79f4c3e88..6aef28a854 100644 --- a/lib/snmp/src/misc/snmp_verbosity.hrl +++ b/lib/snmp/src/misc/snmp_verbosity.hrl @@ -24,39 +24,59 @@ -ifdef(VMODULE). +-define(vinfo(F), ?vinfo(F, [])). -define(vinfo(F,A), snmp_verbosity:print(get(verbosity),info, ?VMODULE,F,A)). +-define(vlog(F), ?vlog(F, [])). -define(vlog(F,A), snmp_verbosity:print(get(verbosity),log, ?VMODULE,F,A)). +-define(vdebug(F), ?vdebug(F, [])). -define(vdebug(F,A),snmp_verbosity:print(get(verbosity),debug,?VMODULE,F,A)). +-define(vtrace(F), ?vtrace(F, [])). -define(vtrace(F,A),snmp_verbosity:print(get(verbosity),trace,?VMODULE,F,A)). -else. +-define(vinfo(F), ?vinfo(F, [])). -define(vinfo(F,A), snmp_verbosity:print(get(verbosity),info, F,A)). +-define(vlog(F), ?vlog(F, [])). -define(vlog(F,A), snmp_verbosity:print(get(verbosity),log, F,A)). +-define(vdebug(F), ?vdebug(F, [])). -define(vdebug(F,A),snmp_verbosity:print(get(verbosity),debug,F,A)). +-define(vtrace(F), ?vtrace(F, [])). -define(vtrace(F,A),snmp_verbosity:print(get(verbosity),trace,F,A)). -endif. -define(vvalidate(V), snmp_verbosity:validate(V)). +-define(vinfoc(F), ?vinfoc(F, [])). -define(vinfoc(F,A), snmp_verbosity:printc(get(verbosity),info, F,A)). +-define(vlogc(F), ?vlogc(F, [])). -define(vlogc(F,A), snmp_verbosity:printc(get(verbosity),log, F,A)). +-define(vdebugc(F), ?vdebug(F, [])). -define(vdebugc(F,A),snmp_verbosity:printc(get(verbosity),debug,F,A)). +-define(vtracec(F), ?vtracec(F, [])). -define(vtracec(F,A),snmp_verbosity:printc(get(verbosity),trace,F,A)). -else. -define(vvalidate(V),ok). --define(vinfo(F,A),ok). --define(vlog(F,A),ok). --define(vdebug(F,A),ok). --define(vtrace(F,A),ok). - --define(vinfoc(F,A),ok). --define(vlogc(F,A),ok). +-define(vinfo(F), ok). +-define(vinfo(F,A), ok). +-define(vlog(F), ok). +-define(vlog(F,A), ok). +-define(vdebug(F), ok). +-define(vdebug(F,A), ok). +-define(vtrace(F), ok). +-define(vtrace(F,A), ok). + +-define(vinfoc(F), ok). +-define(vinfoc(F,A), ok). +-define(vlogc(F), ok). +-define(vlogc(F,A), ok). +-define(vdebugc(F), ok). -define(vdebugc(F,A),ok). +-define(vtracec(F), ok). -define(vtracec(F,A),ok). -endif. diff --git a/lib/snmp/test/Makefile b/lib/snmp/test/Makefile index a9a88ccbfa..0e24506c5d 100644 --- a/lib/snmp/test/Makefile +++ b/lib/snmp/test/Makefile @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2016. All Rights Reserved. +# Copyright Ericsson AB 1997-2020. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -89,14 +89,8 @@ ifeq ($(SNMP_DEBUG),) # SNMP_DEBUG = d endif -ifeq ($(SNMP_DEBUG),e) - SNMP_FLAGS += -Dsnmp_error -endif -ifeq ($(SNMP_DEBUG),l) - SNMP_FLAGS += -Dsnmp_error -Dsnmp_log -endif ifeq ($(SNMP_DEBUG),d) - SNMP_FLAGS += -Dsnmp_error -Dsnmp_log -Dsnmp_debug + SNMP_FLAGS += -Dsnmp_debug endif ifeq ($(DONT_USE_TS),true) @@ -175,7 +169,7 @@ emakebuild: $(EMAKEFILE) targets: mib $(EMAKEFILE) erl -make -old_targets: mib $(TARGET_FILES) $(TEST_SERVER_TARGETS) +old_targets: mib $(TARGET_FILES) $(EMAKEFILE): Makefile $(MAKE_EMAKE) $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' > $(EMAKEFILE) diff --git a/lib/snmp/test/modules.mk b/lib/snmp/test/modules.mk index a1554815d9..ba0aea21dd 100644 --- a/lib/snmp/test/modules.mk +++ b/lib/snmp/test/modules.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2019. All Rights Reserved. +# Copyright Ericsson AB 2004-2020. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,14 +35,14 @@ SUITE_MODULES = \ snmp_to_snmpnet_SUITE TEST_UTIL_MODULES = \ - snmp_agent_test_get \ + snmp_test_lib \ snmp_agent_test_lib \ + snmp_agent_test_get \ snmp_manager_user \ snmp_manager_user_old \ snmp_manager_user_test_lib \ snmp_test_global_sys_monitor \ snmp_test_sys_monitor \ - snmp_test_lib \ snmp_test_manager \ snmp_test_mgr \ snmp_test_mgr_misc \ @@ -52,10 +52,6 @@ TEST_UTIL_MODULES = \ test1 \ test2 -TEST_SERVER_MODULES = \ - snmp_test_server \ - snmp_test_suite - MODULES = \ $(TEST_UTIL_MODULES) \ $(SUITE_MODULES) diff --git a/lib/snmp/test/snmp_agent_SUITE.erl b/lib/snmp/test/snmp_agent_SUITE.erl index 6159aa9dda..2a6cfbedbe 100644 --- a/lib/snmp/test/snmp_agent_SUITE.erl +++ b/lib/snmp/test/snmp_agent_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2019. All Rights Reserved. +%% Copyright Ericsson AB 2003-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -481,22 +481,22 @@ -define(expect1(What), - snmp_agent_test_lib:expect(?MODULE, ?LINE, + ?ALIB:expect(?MODULE, ?LINE, What)). -define(expect2(What, ExpVBs), - snmp_agent_test_lib:expect(?MODULE, ?LINE, + ?ALIB:expect(?MODULE, ?LINE, What, ExpVBs)). -define(expect3(Err, Idx, ExpVBs), - snmp_agent_test_lib:expect(?MODULE, ?LINE, + ?ALIB:expect(?MODULE, ?LINE, Err, Idx, ExpVBs)). -define(expect4(Err, Idx, ExpVBs, To), - snmp_agent_test_lib:expect(?MODULE, ?LINE, + ?ALIB:expect(?MODULE, ?LINE, Err, Idx, ExpVBs, To)). -define(expect5(Type, Ent, Gen, Spec, ExpVBs), - snmp_agent_test_lib:expect(?MODULE, ?LINE, + ?ALIB:expect(?MODULE, ?LINE, Type, Ent, Gen, Spec, ExpVBs)). -define(expect6(Type, Ent, Gen, Spec, ExpVBs, To), - snmp_agent_test_lib:expect(?MODULE, ?LINE, + ?ALIB:expect(?MODULE, ?LINE, Type, Ent, Gen, Spec, ExpVBs, To)). @@ -578,9 +578,9 @@ all_cases() -> init_per_suite(Config0) when is_list(Config0) -> - p("init_per_suite -> entry with" - "~n Config: ~p" - "~n Nodes: ~p", [Config0, erlang:nodes()]), + ?IPRINT("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), case ?LIB:init_per_suite(Config0) of {skip, _} = SKIP -> @@ -601,33 +601,34 @@ init_per_suite(Config0) when is_list(Config0) -> snmp_test_mgr_counter_server:start(), - p("init_per_suite -> end when" - "~n Config: ~p" - "~n Nodes: ~p", [Config4, erlang:nodes()]), + ?IPRINT("init_per_suite -> end when" + "~n Config: ~p" + "~n Nodes: ~p", [Config4, erlang:nodes()]), Config4 end. end_per_suite(Config0) when is_list(Config0) -> - p("end_per_suite -> entry with" - "~n Config0: ~p" - "~n Nodes: ~p", [Config0, erlang:nodes()]), + ?IPRINT("end_per_suite -> entry with" + "~n Config0: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), case snmp_test_mgr_counter_server:stop() of {ok, Counters} -> - p("end_per_suite -> sucessfully stopped counter server" - "~n Counters: ~p", [Counters]); + ?IPRINT("end_per_suite -> sucessfully stopped counter server" + "~n Counters: ~p", [Counters]); {error, Reason} -> - p("end_per_suite -> failed stopping counter server" - "~n Reason: ~p", [Reason]) + ?IPRINT("end_per_suite -> failed stopping counter server" + "~n Reason: ~p", [Reason]) end, snmp_test_sys_monitor:stop(), Config1 = ?LIB:end_per_suite(Config0), - p("end_per_suite -> end when" - "~n Nodes: ~p", [erlang:nodes()]), + ?IPRINT("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), + Config1. @@ -739,7 +740,7 @@ init_per_group_ipv6(GroupName, Config, Init) -> false -> %% Even if this host supports IPv6 we don't use it unless its %% one of the configured/supported IPv6 hosts... - case (?HAS_SUPPORT_IPV6() andalso ?IS_IPV6_HOST()) of + case ?HAS_SUPPORT_IPV6() of true -> Init( snmp_test_lib:init_group_top_dir( @@ -813,18 +814,35 @@ end_per_group(_GroupName, Config) -> %% ----- Init Per TestCase ----- %% +%% T is in number of minutes +wd_start(T, Config) -> + Factor = case ?config(snmp_factor, Config) of + F when (F > 0) -> + F-1; + _ -> + 0 + end, + Dog = ?WD_START(?MINS(T + Factor)), + [{watchdog, Dog} | Config ]. + + +wd_stop(Config) -> + Dog = ?config(watchdog, Config), + ?WD_STOP(Dog), + lists:keydelete(Dog, 2, Config). + init_per_testcase(Case, Config) when is_list(Config) -> - p("init_per_testcase -> entry with" - "~n Config: ~p" - "~n Nodes: ~p", [Config, erlang:nodes()]), + ?IPRINT("init_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), Result = init_per_testcase1(Case, Config), snmp_test_global_sys_monitor:reset_events(), - p("init_per_testcase -> done when" - "~n Result: ~p" - "~n Nodes: ~p", [Result, erlang:nodes()]), + ?IPRINT("init_per_testcase -> done when" + "~n Result: ~p" + "~n Nodes: ~p", [Result, erlang:nodes()]), Result. init_per_testcase1(otp8395 = Case, Config) when is_list(Config) -> @@ -841,8 +859,7 @@ init_per_testcase1(otp_7157 = _Case, Config) when is_list(Config) -> ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), - Dog = ?WD_START(?MINS(1)), - [{watchdog, Dog} | Config ]; + wd_start(1, Config); init_per_testcase1(Case, Config) when ((Case =:= otp_16092_simple_start_and_stop1) orelse (Case =:= otp_16092_simple_start_and_stop2) orelse @@ -852,30 +869,26 @@ init_per_testcase1(Case, Config) ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), - Dog = ?WD_START(?MINS(1)), - init_per_testcase2(Case, [{watchdog, Dog} | Config]); + init_per_testcase2(Case, wd_start(1, Config)); init_per_testcase1(v2_inform_i = _Case, Config) when is_list(Config) -> ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), - Dog = ?WD_START(?MINS(10)), - [{watchdog, Dog} | Config ]; + wd_start(10, Config); init_per_testcase1(v3_inform_i = _Case, Config) when is_list(Config) -> ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), - Dog = ?WD_START(?MINS(10)), - [{watchdog, Dog} | Config ]; + wd_start(10, Config); init_per_testcase1(_Case, Config) when is_list(Config) -> ?DBG("init_per_testcase -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), - Dog = ?WD_START(?MINS(6)), - [{watchdog, Dog}| Config ]. + wd_start(6, Config). init_per_testcase2(Case, Config) -> - ?DBG("end_per_testcase2 -> entry with" + ?DBG("init_per_testcase2 -> entry with" "~n Case: ~p" "~n Config: ~p", [Case, Config]), @@ -911,20 +924,48 @@ init_per_testcase2(Case, Config) -> %% ---- End Per TestCase ---- end_per_testcase(Case, Config) when is_list(Config) -> - p("end_per_testcase -> entry with" - "~n Config: ~p" - "~n Nodes: ~p", [Config, erlang:nodes()]), - display_log(Config), + ?IPRINT("end_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", + [Config, erlang:nodes()]), + + ?IPRINT("system events during test: " + "~n ~p", [snmp_test_global_sys_monitor:events()]), + + %% On some hosts, this operation can take a long time. + %% So long, that the timetrap expires and the test case + %% will be "failed". + %% So, wrap it in a process and for a successful test case, + %% give it 30 seconds, then kill it. If the test case has + %% already failed, we will want to get as much of the logs + %% as possible. So, set no timeout (infinity) and let the + %% test framework take care of things... + DisplayLogTimeout = + case ?config(tc_status, Config) of + ok -> + ?SECS(30); + _ -> + infinity + end, + Flag = process_flag(trap_exit, true), + Pid = spawn_link(fun() -> display_log(Config), exit(normal) end), + receive + {'EXIT', Pid, _} -> + process_flag(trap_exit, Flag), + ok + after DisplayLogTimeout -> + ?WPRINT("Display Log process fail to complete in time (~w msec): " + "kill it", [DisplayLogTimeout]), + process_flag(trap_exit, Flag), + exit(Pid, kill) + end, - p("system events during test: " - "~n ~p", [snmp_test_global_sys_monitor:events()]), - Result = end_per_testcase1(Case, Config), - p("end_per_testcase -> done with" - "~n Result: ~p" - "~n Nodes: ~p", [Result, erlang:nodes()]), + ?IPRINT("end_per_testcase -> done with" + "~n Result: ~p" + "~n Nodes: ~p", [Result, erlang:nodes()]), Result. end_per_testcase1(otp8395, Config) when is_list(Config) -> @@ -935,9 +976,7 @@ end_per_testcase1(_Case, Config) when is_list(Config) -> ?DBG("end_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), - Dog = ?config(watchdog, Config), - ?WD_STOP(Dog), - Config. + wd_stop(Config). @@ -975,37 +1014,37 @@ end_per_testcase1(_Case, Config) when is_list(Config) -> init_all(Conf) -> ?DISPLAY_SUITE_INFO(), - snmp_agent_test_lib:init_all(Conf). + ?ALIB:init_all(Conf). finish_all(Conf) -> - snmp_agent_test_lib:finish_all(Conf). + ?ALIB:finish_all(Conf). start_v1_agent(Config) -> - snmp_agent_test_lib:start_v1_agent(Config). + ?ALIB:start_v1_agent(Config). start_v1_agent(Config, Opts) -> - snmp_agent_test_lib:start_v1_agent(Config, Opts). + ?ALIB:start_v1_agent(Config, Opts). start_v2_agent(Config) -> - snmp_agent_test_lib:start_v2_agent(Config). + ?ALIB:start_v2_agent(Config). start_v2_agent(Config, Opts) -> - snmp_agent_test_lib:start_v2_agent(Config, Opts). + ?ALIB:start_v2_agent(Config, Opts). %% start_v3_agent(Config) -> -%% snmp_agent_test_lib:start_v3_agent(Config). +%% ?ALIB:start_v3_agent(Config). start_v3_agent(Config, Opts) -> - snmp_agent_test_lib:start_v3_agent(Config, Opts). + ?ALIB:start_v3_agent(Config, Opts). start_bilingual_agent(Config) -> - snmp_agent_test_lib:start_bilingual_agent(Config). + ?ALIB:start_bilingual_agent(Config). start_multi_threaded_agent(Config) when is_list(Config) -> - snmp_agent_test_lib:start_mt_agent(Config). + ?ALIB:start_mt_agent(Config). stop_agent(Config) -> - snmp_agent_test_lib:stop_agent(Config). + ?ALIB:stop_agent(Config). create_tables(SaNode) -> @@ -1148,28 +1187,28 @@ varm_mib_storage_mnesia_cases() -> [msm_varm_mib_start]. init_mib_storage_ets(Config) when is_list(Config) -> - ?LOG("init_mib_storage_ets -> entry", []), + ?IPRINT("init_mib_storage_ets -> entry"), MibStorage = {mib_storage, [{module, snmpa_mib_storage_ets}]}, init_ms(Config, [MibStorage]). init_mib_storage_dets(Config) when is_list(Config) -> - ?LOG("init_mib_storage_dets -> entry", []), + ?IPRINT("init_mib_storage_dets -> entry"), ?line AgentDbDir = ?GCONF(agent_db_dir, Config), MibStorage = {mib_storage, [{module, snmpa_mib_storage_dets}, {options, [{dir, AgentDbDir}]}]}, init_ms(Config, [MibStorage]). init_mib_storage_mnesia(Config) when is_list(Config) -> - ?LOG("init_mib_storage_mnesia -> entry", []), + ?IPRINT("init_mib_storage_mnesia -> entry"), ?line AgentNode = ?GCONF(snmp_master, Config), MibStorage = {mib_storage, [{module, snmpa_mib_storage_mnesia}, {options, [{nodes, [AgentNode]}]}]}, init_ms(Config, [MibStorage]). init_ms(Config, Opts) when is_list(Config) -> - ?LOG("init_ms -> entry with" - "~n Config: ~p" - "~n Opts: ~p", [Config, Opts]), + ?IPRINT("init_ms -> entry with" + "~n Config: ~p" + "~n Opts: ~p", [Config, Opts]), ?line SaNode = ?GCONF(snmp_sa, Config), ?line create_tables(SaNode), ?line AgentConfDir = ?GCONF(agent_conf_dir, Config), @@ -1206,11 +1245,16 @@ init_size_check_ms(Config, Opts) when is_list(Config) -> ok -> case ?CRYPTO_SUPPORT() of {no, Reason} -> + ?WPRINT("crypto support not sufficient:" + "~n ~p", [Reason]), ?SKIP({unsupported_encryption, Reason}); yes -> + ?IPRINT("crypto started"), ok end; {error, Reason} -> + ?IPRINT("crypto not started:" + "~n ~p", [Reason]), ?SKIP({failed_starting_crypto, Reason}) end, create_tables(SaNode), @@ -1222,7 +1266,7 @@ init_size_check_ms(Config, Opts) when is_list(Config) -> [{vsn, v3} | start_v3_agent(Config, Opts)]. init_varm_mib_storage_dets(Config) when is_list(Config) -> - ?LOG("init_varm_mib_storage_dets -> entry", []), + ?IPRINT("init_varm_mib_storage_dets -> entry"), ?line SaNode = ?GCONF(snmp_sa, Config), ?line create_tables(SaNode), ?line AgentDbDir = ?GCONF(agent_db_dir, Config), @@ -1243,7 +1287,7 @@ init_varm_mib_storage_dets(Config) when is_list(Config) -> [{vsn, v1}, {agent_opts, Opts} | Config]. init_varm_mib_storage_mnesia(Config) when is_list(Config) -> - ?LOG("init_varm_mib_storage_mnesia -> entry", []), + ?IPRINT("init_varm_mib_storage_mnesia -> entry"), ?line SaNode = ?GCONF(snmp_sa, Config), ?line create_tables(SaNode), ?line AgentConfDir = ?GCONF(agent_conf_dir, Config), @@ -1264,7 +1308,7 @@ init_varm_mib_storage_mnesia(Config) when is_list(Config) -> [{vsn, v1}, {agent_opts, Opts} | Config]. finish_mib_storage_ets(Config) when is_list(Config) -> - ?LOG("finish_mib_storage_ets -> entry", []), + ?IPRINT("finish_mib_storage_ets -> entry"), delete_tables(), C1 = stop_agent(Config), delete_files(C1), @@ -1272,7 +1316,7 @@ finish_mib_storage_ets(Config) when is_list(Config) -> lists:keydelete(agent_opts, 1, C2). finish_mib_storage_dets(Config) when is_list(Config) -> - ?LOG("finish_mib_storage_dets -> entry", []), + ?IPRINT("finish_mib_storage_dets -> entry"), delete_tables(), C1 = stop_agent(Config), delete_files(C1), @@ -1280,7 +1324,7 @@ finish_mib_storage_dets(Config) when is_list(Config) -> lists:keydelete(agent_opts, 1, C2). finish_mib_storage_mnesia(Config) when is_list(Config) -> - ?LOG("finish_mib_storage_mnesia -> entry", []), + ?IPRINT("finish_mib_storage_mnesia -> entry"), delete_tables(), delete_mib_storage_mnesia_tables(), C1 = stop_agent(Config), @@ -1289,7 +1333,7 @@ finish_mib_storage_mnesia(Config) when is_list(Config) -> lists:keydelete(agent_opts, 1, C2). finish_varm_mib_storage_dets(Config) when is_list(Config) -> - ?LOG("finish_varm_mib_storage_dets -> entry", []), + ?IPRINT("finish_varm_mib_storage_dets -> entry"), delete_tables(), %% C1 = stop_agent(Config), % In case something went wrong... delete_files(Config), @@ -1297,7 +1341,7 @@ finish_varm_mib_storage_dets(Config) when is_list(Config) -> lists:keydelete(agent_opts, 1, C2). finish_varm_mib_storage_mnesia(Config) when is_list(Config) -> - ?LOG("finish_varm_mib_storage_mnesia -> entry", []), + ?IPRINT("finish_varm_mib_storage_mnesia -> entry"), delete_tables(), delete_mib_storage_mnesia_tables(), %% C1 = stop_agent(Config), % In case something went wrong... @@ -1408,7 +1452,7 @@ ms_size_check(suite) -> []; ms_size_check(Config) when is_list(Config) -> ?P(ms_size_check), init_case(Config), - ?LOG("mib server size check...", []), + ?IPRINT("mib server size check..."), ?line load_master("Test2"), ?line load_master("TestTrap"), @@ -1449,7 +1493,7 @@ ms_size_check(Config) when is_list(Config) -> varm_mib_start(suite) -> []; varm_mib_start(Config) when is_list(Config) -> ?P(varm_mib_start), - ?LOG("varm_mib_start -> entry", []), + ?IPRINT("varm_mib_start -> entry"), init_case(Config), %% Start the agent @@ -1694,24 +1738,64 @@ app_dir(App) -> end. create_local_db_dir(Config) when is_list(Config) -> - ?P(create_local_db_dir), - DataDir = snmp_test_lib:lookup(data_dir, Config), - UName = erlang:unique_integer([positive]), - T = {UName, UName, UName}, - [As,Bs,Cs] = [integer_to_list(I) || I <- tuple_to_list(T)], - DbDir = filename:join([DataDir, As, Bs, Cs]), - ok = del_dir(DbDir, 3), - Name = list_to_atom(atom_to_list(create_local_db_dir) - ++"-"++As++"-"++Bs++"-"++Cs), - Pa = filename:dirname(code:which(?MODULE)), - {ok,Node} = ?t:start_node(Name, slave, [{args, "-pa " ++ Pa}]), + Pre = fun() -> + DataDir = snmp_test_lib:lookup(data_dir, Config), + T = {erlang:unique_integer([positive]), + erlang:unique_integer([positive]), + erlang:unique_integer([positive])}, + [As,Bs,Cs] = [integer_to_list(I) || I <- tuple_to_list(T)], + DbDir = filename:join([DataDir, As, Bs, Cs]), + Name = list_to_atom(atom_to_list(create_local_db_dir) + ++"_"++As++"_"++Bs++"_"++Cs), + ?IPRINT("try ensuring db-dir does not exist"), + try del_dir(DbDir, 3) of + ok -> + ok + catch + C:E:S -> + ?WPRINT("Failed pre db-dir delete: " + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + throw({skip, "Failed pre db-dir cleanup"}) + end, + ?IPRINT("try start node ~p", [Name]), + case ?ALIB:start_node(Name) of + {ok, Node} -> + {DbDir, Node}; + {error, Reason} -> + ?WPRINT("Failed starting node ~p:" + "~n ~p", [Reason]), + throw({skip, ?F("Failed starting node ~p", [Name])}) + end + end, + Case = fun do_create_local_db_dir/1, + Post = fun({DbDir, Node}) -> + ?IPRINT("try stop node ~p", [Node]), + ?ALIB:stop_node(Node), + ?IPRINT("try delete db-dir"), + try del_dir(DbDir, 3) + catch + C:E:S -> + ?WPRINT("Failed post db-dir delete: " + "~n DbDir ~s" + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [DbDir, C, E, S]), + ok + end + end, + ?TC_TRY(create_local_db_dir, Pre, Case, Post). +do_create_local_db_dir({DbDir, Node}) -> + ?P(create_local_db_dir), %% first start with a nonexisting DbDir Fun1 = fun() -> false = filelib:is_dir(DbDir), process_flag(trap_exit,true), {error, {error, {failed_open_dets, {file_error, _, _}}}} = - snmpa_local_db:start_link(normal, DbDir, [{verbosity,trace}]), + snmpa_local_db:start_link(normal, DbDir, + [{verbosity, trace}]), false = filelib:is_dir(DbDir), {ok, not_found} end, @@ -1729,21 +1813,21 @@ create_local_db_dir(Config) when is_list(Config) -> {ok, found} end, {ok, found} = nodecall(Node, Fun2), - %% cleanup - ?t:stop_node(Node), - ok = del_dir(DbDir, 3), ok. nodecall(Node, Fun) -> Parent = self(), - Ref = make_ref(), - spawn_link(Node, - fun() -> - Res = Fun(), - unlink(Parent), - Parent ! {Ref, Res} - end), + Ref = make_ref(), + Pid = spawn_link(Node, + fun() -> + Res = Fun(), + unlink(Parent), + Parent ! {Ref, Res} + end), receive + %% Just so we are not left hanging + {'EXIT', Pid, Reason} -> + Reason; {Ref, Res} -> Res end. @@ -1992,11 +2076,16 @@ init_v3(Config) when is_list(Config) -> ok -> case ?CRYPTO_SUPPORT() of {no, Reason} -> + ?WPRINT("crypto support not sufficient:" + "~n ~p", [Reason]), ?SKIP({unsupported_encryption, Reason}); yes -> + ?IPRINT("crypto started"), ok end; {error, Reason} -> + ?IPRINT("crypto not started:" + "~n ~p", [Reason]), ?SKIP({failed_starting_crypto, Reason}) end, SaNode = ?config(snmp_sa, Config), @@ -2046,7 +2135,7 @@ finish_mt(Config) when is_list(Config) -> %% This one *must* be run first in each case. init_case(Config) -> - snmp_agent_test_lib:init_case(Config). + ?ALIB:init_case(Config). load_master(Mib) -> @@ -2079,10 +2168,10 @@ unload_mibs(Mibs) -> ok = snmpa:unload_mibs(snmp_master_agent, Mibs). start_subagent(SaNode, RegTree, Mib) -> - snmp_agent_test_lib:start_subagent(SaNode, RegTree, Mib). + ?ALIB:start_subagent(SaNode, RegTree, Mib). stop_subagent(SA) -> - snmp_agent_test_lib:stop_subagent(SA). + ?ALIB:stop_subagent(SA). %%----------------------------------------------------------------- @@ -2116,7 +2205,7 @@ simple(Config) when is_list(Config) -> try_test(simple_standard_test), - p("done"), + ?IPRINT("done"), ok. simple_2(X) -> ?P(simple_2), simple(X). @@ -2143,7 +2232,7 @@ big(Config) when is_list(Config) -> {SaNode, _MgrNode, _MibDir} = init_case(Config), - ?P1("Starting subagent..."), + ?NPRINT("Starting subagent..."), ?line pong = net_adm:ping(SaNode), ?line {ok, SA} = start_subagent(SaNode, ?klas1, "Klas1"), @@ -2172,7 +2261,7 @@ big2(Config) when is_list(Config) -> %% v2 equivalent of the mibs. {SaNode, _MgrNode, _MibDir} = init_case(Config), - ?P1("Starting subagent..."), + ?NPRINT("Starting subagent..."), ?line pong = net_adm:ping(SaNode), ?line {ok, SA} = start_subagent(SaNode, ?klas1, "Klas1-v2"), @@ -2278,7 +2367,7 @@ opaque_3(X) -> ?P(opaque_2), opaque(X). change_target_addr_config(suite) -> []; change_target_addr_config(Config) when is_list(Config) -> ?P(change_target_addr_config), - ?LOG("change_target_addr_config -> entry",[]), + ?IPRINT("change_target_addr_config -> entry"), init_case(Config), put(sname,snmp_suite), @@ -2286,43 +2375,43 @@ change_target_addr_config(Config) when is_list(Config) -> MA = whereis(snmp_master_agent), - ?LOG("change_target_addr_config -> load TestTrap",[]), + ?IPRINT("change_target_addr_config -> load TestTrap"), ?line load_master("TestTrap"), - ?LOG("change_target_addr_config -> set trace verbosity for local_db",[]), + ?IPRINT("change_target_addr_config -> set trace verbosity for local_db"), ?line snmpa:verbosity(local_db,trace), %% First send some traps that will arive att the original manager - ?LOG("change_target_addr_config -> send trap",[]), + ?IPRINT("change_target_addr_config -> send trap"), try_test(ma_trap1, [MA]), - ?LOG("change_target_addr_config -> set silence verbosity for local_db",[]), + ?IPRINT("change_target_addr_config -> set silence verbosity for local_db"), ?line snmpa:verbosity(local_db, silence), %% Start new dummy listener - ?LOG("change_target_addr_config -> start dummy manager",[]), + ?IPRINT("change_target_addr_config -> start dummy manager"), ?line {ok,Pid,NewPort} = dummy_manager_start(MA), %% Reconfigure - ?LOG("change_target_addr_config -> reconfigure",[]), + ?IPRINT("change_target_addr_config -> reconfigure"), AgentConfDir = ?config(agent_conf_dir, Config), ?line rewrite_target_addr_conf(AgentConfDir, NewPort), ?line snmp_target_mib:reconfigure(AgentConfDir), %% Send the trap again - ?LOG("change_target_addr_config -> send trap again",[]), + ?IPRINT("change_target_addr_config -> send trap again"), catch dummy_manager_send_trap2(Pid), - ?LOG("change_target_addr_config -> await trap ack",[]), + ?IPRINT("change_target_addr_config -> await trap ack"), catch dummy_manager_await_trap2_ack(), - ?LOG("change_target_addr_config -> stop dummy manager",[]), + ?IPRINT("change_target_addr_config -> stop dummy manager"), ?line ok = dummy_manager_stop(Pid), - ?LOG("change_target_addr_config -> reset target address config",[]), + ?IPRINT("change_target_addr_config -> reset target address config"), ?line reset_target_addr_conf(AgentConfDir), - ?LOG("change_target_addr_config -> unload TestTrap",[]), + ?IPRINT("change_target_addr_config -> unload TestTrap"), ?line unload_master("TestTrap"). @@ -2338,11 +2427,15 @@ await_dummy_manager_started(Pid) -> ?DBG("dummy_manager_start -> acknowledge received with" "~n Port: ~p",[Port]), {ok,Pid,Port}; + {'EXIT', Pid, Reason} -> + ?EPRINT("dummy manager terminated: " + "~n ~p", [Reason]), {error, Pid, Reason}; + _O -> - ?LOG("dummy_manager_start -> received unknown message:" - "~n ~p",[_O]), + ?NPRINT("dummy_manager_start -> received unknown message:" + "~n ~p", [_O]), await_dummy_manager_started(Pid) end. @@ -2354,7 +2447,7 @@ dummy_manager_stop(Pid) -> ?DBG("dummy_manager_stop -> acknowledge received",[]), ok after 10000 -> - ?ERR("dummy_manager_stop -> timeout",[]), + ?EPRINT("dummy_manager_stop -> timeout"), timeout end. @@ -2366,7 +2459,7 @@ dummy_manager_await_trap2_ack() -> ?DBG("dummy_manager_await_trap2 -> entry",[]), receive {received_trap, _Trap} -> - ?LOG("dummy_manager_await_trap2 -> received trap: ~p", [_Trap]), + ?IPRINT("dummy_manager_await_trap2 -> received trap: ~p", [_Trap]), %% Note: %% Without this sleep the v2_inform_i testcase failes! There %% is no relation between these two test cases as far as I @@ -2374,10 +2467,10 @@ dummy_manager_await_trap2_ack() -> ?SLEEP(60000), ok; _O -> - ?ERR("dummy_manager_await_trap2 -> unexpected message: ~p",[_O]), + ?WPRINT("dummy_manager_await_trap2 -> unexpected message: ~p",[_O]), ok after 10000 -> - ?ERR("dummy_manager_await_trap2 -> timeout",[]), + ?EPRINT("dummy_manager_await_trap2 -> timeout",[]), timeout end. @@ -2393,18 +2486,18 @@ dummy_manager_init(Parent,MA) -> dummy_manager_loop(Parent,S,MA). dummy_manager_loop(P,S,MA) -> - ?LOG("dummy_manager_loop -> ready for receive",[]), + ?IPRINT("dummy_manager_loop -> ready for receive"), receive {send_trap,Trap} -> - ?LOG("dummy_manager_loop -> received trap send request" - "~n Trap: ~p",[Trap]), + ?IPRINT("dummy_manager_loop -> received trap send request" + "~n Trap: ~p", [Trap]), snmpa:send_trap(MA, Trap, "standard trap"), dummy_manager_loop(P,S,MA); {udp, _UdpId, _Ip, _UdpPort, Bytes} -> - ?LOG("dummy_manager_loop -> received upd message" - "~n from: ~p:~p" - "~n size: ~p", - [_Ip, _UdpPort, dummy_manager_message_sz(Bytes)]), + ?IPRINT("dummy_manager_loop -> received upd message" + "~n from: ~p:~p" + "~n size: ~p", + [_Ip, _UdpPort, dummy_manager_message_sz(Bytes)]), R = dummy_manager_handle_message(Bytes), ?DBG("dummy_manager_loop -> R: ~p", [R]), P ! R, @@ -2415,30 +2508,31 @@ dummy_manager_loop(P,S,MA) -> gen_udp:close(S), exit(normal); _O -> - ?LOG("dummy_manager_loop -> received unknown message:" - "~n ~p", [_O]), + ?WPRINT("dummy_manager_loop -> received unknown message:" + "~n ~p", [_O]), dummy_manager_loop(P, S, MA) end. --ifdef(snmp_log). +%% -ifdef(snmp_log). dummy_manager_message_sz(B) when is_binary(B) -> size(B); dummy_manager_message_sz(L) when is_list(L) -> length(L); dummy_manager_message_sz(_) -> undefined. --endif. +%% -endif. dummy_manager_handle_message(Bytes) -> case (catch snmp_pdus:dec_message(Bytes)) of {'EXIT',Reason} -> - ?ERR("dummy_manager_handle_message -> " - "failed decoding message only:~n ~p",[Reason]), - {error,Reason}; + ?EPRINT("dummy_manager_handle_message -> " + "failed decoding message only:" + "~n ~p", [Reason]), + {error, Reason}; M -> ?DBG("dummy_manager_handle_message -> decoded message:" - "~n ~p",[M]), - {received_trap,M} + "~n ~p", [M]), + {received_trap, M} end. @@ -2465,19 +2559,20 @@ subagent(Config) when is_list(Config) -> ?line {ok, SA} = start_subagent(SaNode, ?klas1, "Klas1"), try_test(load_test_sa), - ?P1("Testing unregister subagent..."), + ?NPRINT("Testing unregister subagent..."), MA = whereis(snmp_master_agent), rpc:call(SaNode, snmp, unregister_subagent, [MA, SA]), try_test(unreg_test), - ?P1("Loading previous subagent mib in master and testing..."), + ?NPRINT("Loading previous subagent mib in master and testing..."), ?line ok = snmpa:load_mib(MA, join(MibDir, "Klas1")), try_test(load_test), - ?P1("Unloading previous subagent mib in master and testing..."), + ?NPRINT("Unloading previous subagent mib in master and testing..."), ?line ok = snmpa:unload_mib(MA, join(MibDir, "Klas1")), try_test(unreg_test), - ?P1("Testing register subagent..."), + + ?NPRINT("Testing register subagent..."), rpc:call(SaNode, snmp, register_subagent, [MA, ?klas1, SA]), try_test(load_test_sa), @@ -2503,14 +2598,14 @@ mnesia(Config) when is_list(Config) -> ?P(mnesia), {SaNode, _MgrNode, _MibDir} = init_case(Config), - ?P1("Starting subagent with mnesia impl..."), + ?NPRINT("Starting subagent with mnesia impl..."), {ok, SA} = start_subagent(SaNode, ?klas2, "Klas2"), ?line load_master("OLD-SNMPEA-MIB"), ?line init_old(), try_test(big_test_2), - ?P1("Testing unregister subagent..."), + ?NPRINT("Testing unregister subagent..."), MA = whereis(snmp_master_agent), rpc:call(SaNode, snmp, unregister_subagent, [MA, SA]), try_test(unreg_test), @@ -2577,7 +2672,7 @@ mul_get(Config) when is_list(Config) -> ?P(mul_get), init_case(Config), - ?P1("Testing multiple get..."), + ?NPRINT("Testing multiple get..."), try_test(do_mul_get). mul_get_2(X) -> ?P(mul_get_2), mul_get(X). @@ -2590,7 +2685,7 @@ mul_get_err(Config) when is_list(Config) -> ?P(mul_get_err), init_case(Config), - ?P1("Testing multiple get with error..."), + ?NPRINT("Testing multiple get with error..."), try_test(do_mul_get_err). mul_get_err_2(X) -> ?P(mul_get_err_2), mul_get_err(X). @@ -2603,7 +2698,7 @@ mul_next(Config) when is_list(Config) -> ?P(mul_next), init_case(Config), - ?P1("Testing multiple next..."), + ?NPRINT("Testing multiple next..."), try_test(do_mul_next). mul_next_2(X) -> ?P(mul_next_2), mul_next(X). @@ -2616,7 +2711,7 @@ mul_next_err(Config) when is_list(Config) -> ?P(mul_next_err), init_case(Config), - ?P1("Testing multiple next..."), + ?NPRINT("Testing multiple next..."), try_test(do_mul_next_err). mul_next_err_2(X) -> ?P(mul_next_err_2), mul_next_err(X). @@ -2629,7 +2724,7 @@ mul_set(Config) when is_list(Config) -> ?P(mul_set), init_case(Config), - ?P1("Testing multiple set..."), + ?NPRINT("Testing multiple set..."), try_test(do_mul_set). mul_set_2(X) -> ?P(mul_set_2), mul_set(X). @@ -2642,7 +2737,7 @@ mul_set_err(Config) when is_list(Config) -> ?P(mul_set_err), init_case(Config), - ?P1("Testing multiple set with error..."), + ?NPRINT("Testing multiple set with error..."), try_test(do_mul_set_err). mul_set_err_2(X) -> ?P(mul_set_err_2), mul_set_err(X). @@ -2656,30 +2751,30 @@ sa_register(Config) when is_list(Config) -> {SaNode, _MgrNode, MibDir} = init_case(Config), ?DBG("sa_register -> start subagent", []), - ?P1("start subagent..."), + ?NPRINT("start subagent..."), ?line {ok, SA} = start_subagent(SaNode, ?klas1, "Klas1"), ?DBG("sa_register -> unregister subagent", []), - ?P1("Testing unregister subagent (2)..."), + ?NPRINT("Testing unregister subagent (2)..."), MA = whereis(snmp_master_agent), rpc:call(SaNode, snmp, unregister_subagent, [MA, ?klas1]), try_test(unreg_test), - ?P1("Unloading Klas1..."), + ?NPRINT("Unloading Klas1..."), ?DBG("sa_register -> unload mibs", []), snmpa:unload_mib(SA, join(MibDir, "Klas1")), - ?P1("Loading SA-MIB..."), + ?NPRINT("Loading SA-MIB..."), ?DBG("sa_register -> unload mibs", []), snmpa:load_mib(SA, join(MibDir, "SA-MIB")), - ?P1("register subagent..."), + ?NPRINT("register subagent..."), ?DBG("sa_register -> register subagent", []), rpc:call(SaNode, snmp, register_subagent, [MA, ?sa, SA]), try_test(sa_mib), - ?P1("stop subagent..."), + ?NPRINT("stop subagent..."), ?DBG("sa_register -> stop subagent", []), ?line stop_subagent(SA). @@ -2696,32 +2791,32 @@ v1_trap(Config) when is_list(Config) -> trap1(Config) -> {SaNode, _MgrNode, _MibDir} = init_case(Config), - ?P1("start subagent..."), + ?NPRINT("start subagent..."), ?line {ok, SA} = start_subagent(SaNode, ?sa, "SA-MIB"), - ?P1("Testing trap sending from master agent..."), + ?NPRINT("Testing trap sending from master agent..."), MA = whereis(snmp_master_agent), - ?P1("load TestTrap & TestTrapv2..."), + ?NPRINT("load TestTrap & TestTrapv2..."), ?line load_master("TestTrap"), ?line load_master("TestTrapv2"), - ?P1("Testing trap sending from master-agent..."), + ?NPRINT("Testing trap sending from master-agent..."), try_test(ma_trap1, [MA]), try_test(ma_trap2, [MA]), try_test(ma_v2_2_v1_trap, [MA]), try_test(ma_v2_2_v1_trap2, [MA]), - ?P1("Testing trap sending from subagent..."), + ?NPRINT("Testing trap sending from subagent..."), try_test(sa_trap1, [SA]), try_test(sa_trap2, [SA]), try_test(sa_trap3, [SA]), - ?P1("unload TestTrap & TestTrapv2..."), + ?NPRINT("unload TestTrap & TestTrapv2..."), ?line unload_master("TestTrap"), ?line unload_master("TestTrapv2"), - ?P1("stop subagent..."), + ?NPRINT("stop subagent..."), ?line stop_subagent(SA). v2_trap(suite) -> []; @@ -2732,17 +2827,17 @@ v2_trap(Config) when is_list(Config) -> trap2(Config) -> {SaNode, _MgrNode, _MibDir} = init_case(Config), - ?P1("start subagent..."), + ?NPRINT("start subagent..."), ?line {ok, SA} = start_subagent(SaNode, ?sa, "SA-MIB"), - ?P1("Testing trap sending from master agent..."), + ?NPRINT("Testing trap sending from master agent..."), MA = whereis(snmp_master_agent), - ?P1("load TestTrap & TestTrapv2..."), + ?NPRINT("load TestTrap & TestTrapv2..."), ?line load_master("TestTrap"), ?line load_master("TestTrapv2"), - ?P1("Testing trap sending from master-agent..."), + ?NPRINT("Testing trap sending from master-agent..."), try_test(ma_v2_trap1, [MA]), try_test(ma_v2_trap2, [MA]), try_test(ma_v1_2_v2_trap, [MA]), @@ -2750,16 +2845,16 @@ trap2(Config) -> try_test(sa_mib), - ?P1("Testing trap sending from subagent..."), + ?NPRINT("Testing trap sending from subagent..."), try_test(sa_v1_2_v2_trap1, [SA]), try_test(sa_v1_2_v2_trap2, [SA]), try_test(sa_v1_2_v2_trap3, [SA]), - ?P1("unload TestTrap & TestTrapv2..."), + ?NPRINT("unload TestTrap & TestTrapv2..."), ?line unload_master("TestTrap"), ?line unload_master("TestTrapv2"), - ?P1("stop subagent..."), + ?NPRINT("stop subagent..."), ?line stop_subagent(SA). v3_trap(suite) -> []; @@ -2811,18 +2906,18 @@ inform_i(Config) -> MA = whereis(snmp_master_agent), - ?P1("load TestTrap & TestTrapv2..."), + ?NPRINT("load TestTrap & TestTrapv2..."), ?line load_master("TestTrap"), ?line load_master("TestTrapv2"), - ?P1("Testing inform sending from master agent... " + ?NPRINT("Testing inform sending from master agent... " "~nNOTE! This test takes a few minutes (10) to complete."), try_test(ma_v2_inform1, [MA]), try_test(ma_v2_inform2, [MA]), try_test(ma_v2_inform3, [MA]), - ?P1("unload TestTrap & TestTrapv2..."), + ?NPRINT("unload TestTrap & TestTrapv2..."), ?line unload_master("TestTrap"), ?line unload_master("TestTrapv2"), ok. @@ -2843,26 +2938,26 @@ sa_error(Config) when is_list(Config) -> ?P(sa_error), {SaNode, _MgrNode, _MibDir} = init_case(Config), - ?P1("load OLD-SNMPEA-MIB..."), + ?NPRINT("load OLD-SNMPEA-MIB..."), ?line load_master("OLD-SNMPEA-MIB"), ?line init_old(), - ?P1("start subagent..."), + ?NPRINT("start subagent..."), ?line {ok, SA} = start_subagent(SaNode, ?sa, "SA-MIB"), - ?P1("Testing sa bad value (is_set_ok)..."), + ?NPRINT("Testing sa bad value (is_set_ok)..."), try_test(sa_errs_bad_value), - ?P1("Testing sa gen err (set)..."), + ?NPRINT("Testing sa gen err (set)..."), try_test(sa_errs_gen_err), - ?P1("Testing too big..."), + ?NPRINT("Testing too big..."), try_test(sa_too_big), - ?P1("unload OLD-SNMPEA-MIB..."), + ?NPRINT("unload OLD-SNMPEA-MIB..."), ?line unload_master("OLD-SNMPEA-MIB"), - ?P1("stop subagent..."), + ?NPRINT("stop subagent..."), stop_subagent(SA). sa_error_2(X) -> @@ -2886,35 +2981,35 @@ next_across_sa(Config) when is_list(Config) -> {SaNode, _MgrNode, MibDir} = init_case(Config), MA = whereis(snmp_master_agent), - ?P1("start subagent (1)..."), + ?NPRINT("start subagent (1)..."), ?line {ok, SA} = start_subagent(SaNode, ?sa, "SA-MIB"), - ?P1("Loading another subagent mib (Klas1)..."), + ?NPRINT("Loading another subagent mib (Klas1)..."), ?line ok = snmpa:load_mib(SA, MibDir ++ "Klas1"), - ?P1("register subagent..."), + ?NPRINT("register subagent..."), rpc:call(SaNode, snmp, register_subagent, [MA, ?klas1, SA]), - ?P1("Load test subagent..."), + ?NPRINT("Load test subagent..."), try_test(load_test_sa), - ?P1("Testing next across subagent (endOfMibView from SA)..."), + ?NPRINT("Testing next across subagent (endOfMibView from SA)..."), try_test(next_across_sa_test), - ?P1("Unloading mib (Klas1)"), + ?NPRINT("Unloading mib (Klas1)"), snmpa:unload_mib(SA, join(MibDir, "Klas1")), rpc:call(SaNode, snmp, unregister_subagent, [MA, ?klas1]), try_test(unreg_test), - ?P1("Starting another subagent (2) "), + ?NPRINT("Starting another subagent (2) "), ?line {ok, SA2} = start_subagent(SaNode, ?klas1, "Klas1"), - ?P1("Testing next across subagent (wrong prefix from SA)..."), + ?NPRINT("Testing next across subagent (wrong prefix from SA)..."), try_test(next_across_sa_test), - ?P1("stop subagent (1)..."), + ?NPRINT("stop subagent (1)..."), stop_subagent(SA), - ?P1("stop subagent (2)..."), + ?NPRINT("stop subagent (2)..."), stop_subagent(SA2). next_across_sa_2(X) -> @@ -2939,27 +3034,27 @@ undo(Config) when is_list(Config) -> MA = whereis(snmp_master_agent), - ?P1("start subagent (1)..."), + ?NPRINT("start subagent (1)..."), ?line {ok, SA} = start_subagent(SaNode, ?sa, "SA-MIB"), - ?P1("Load Klas3 & Klas4..."), + ?NPRINT("Load Klas3 & Klas4..."), ?line ok = snmpa:load_mib(MA, join(MibDir, "Klas3")), ?line ok = snmpa:load_mib(MA, join(MibDir, "Klas4")), - ?P1("Testing undo phase at master agent..."), + ?NPRINT("Testing undo phase at master agent..."), try_test(undo_test), try_test(api_test2), - ?P1("Unload Klas3..."), + ?NPRINT("Unload Klas3..."), ?line ok = snmpa:unload_mib(MA, join(MibDir, "Klas3")), - ?P1("Testing bad return values from instrum. funcs..."), + ?NPRINT("Testing bad return values from instrum. funcs..."), try_test(bad_return), - ?P1("Unload Klas4..."), + ?NPRINT("Unload Klas4..."), ?line ok = snmpa:unload_mib(MA, join(MibDir, "Klas4")), - ?P1("Testing undo phase at subagent..."), + ?NPRINT("Testing undo phase at subagent..."), ?line ok = snmpa:load_mib(SA, join(MibDir, "Klas3")), ?line ok = snmpa:load_mib(SA, join(MibDir, "Klas4")), ?line ok = snmpa:register_subagent(MA, ?klas3, SA), @@ -2967,11 +3062,11 @@ undo(Config) when is_list(Config) -> try_test(undo_test), try_test(api_test3), - ?P1("Testing undo phase across master/subagents..."), + ?NPRINT("Testing undo phase across master/subagents..."), try_test(undo_test), try_test(api_test3), - ?P1("stop subagent..."), + ?NPRINT("stop subagent..."), stop_subagent(SA). undo_2(X) -> @@ -2995,12 +3090,12 @@ v1_processing(Config) when is_list(Config) -> ?DBG("v1_processing -> entry", []), init_case(Config), - ?P1("Load Test2..."), + ?NPRINT("Load Test2..."), ?line load_master("Test2"), try_test(v1_proc), - ?P1("Unload Test2..."), + ?NPRINT("Unload Test2..."), ?line unload_master("Test2"). %% Req. Test2 @@ -3009,12 +3104,12 @@ v2_processing(Config) when is_list(Config) -> ?P(v2_processing), init_case(Config), - ?P1("Load Test2..."), + ?NPRINT("Load Test2..."), ?line load_master("Test2"), try_test(v2_proc), - ?P1("Unload Test2..."), + ?NPRINT("Unload Test2..."), ?line unload_master("Test2"). %% Req. Test2 @@ -3023,12 +3118,12 @@ v3_processing(Config) when is_list(Config) -> ?P(v3_processing), init_case(Config), - ?P1("Load Test2..."), + ?NPRINT("Load Test2..."), ?line load_master("Test2"), try_test(v2_proc), % same as v2! - ?P1("Unload Test2..."), + ?NPRINT("Unload Test2..."), ?line unload_master("Test2"). @@ -3101,7 +3196,7 @@ v3_md5_auth(Config) when is_list(Config) -> ?P(v3_md5_auth), init_case(Config), - ?P1("Testing MD5 authentication...takes a few seconds..."), + ?NPRINT("Testing MD5 authentication...takes a few seconds..."), AgentConfDir = ?config(agent_conf_dir, Config), ?line rewrite_target_params_conf(AgentConfDir, "authMD5", authNoPriv), @@ -3128,7 +3223,7 @@ v3_sha_auth(Config) when is_list(Config) -> ?P(v3_sha_auth), init_case(Config), - ?P1("Testing SHA authentication...takes a few seconds..."), + ?NPRINT("Testing SHA authentication...takes a few seconds..."), AgentConfDir = ?config(agent_conf_dir, Config), ?line rewrite_target_params_conf(AgentConfDir, "authSHA", authNoPriv), @@ -3155,7 +3250,7 @@ v3_des_priv(Config) when is_list(Config) -> ?P(v3_des_priv), init_case(Config), - ?P1("Testing DES encryption...takes a few seconds..."), + ?NPRINT("Testing DES encryption...takes a few seconds..."), AgentConfDir = ?config(agent_conf_dir, Config), ?line rewrite_target_params_conf(AgentConfDir, "privDES", authPriv), @@ -3772,10 +3867,10 @@ big_test() -> %% Req. system group, Klas2, OLD-SNMPEA-MIB big_test_2() -> - ?P1("Testing simple next/get/set @ master agent (2)..."), + ?NPRINT("Testing simple next/get/set @ master agent (2)..."), simple_standard_test(), - ?P1("Testing simple next/get/set @ subagent (2)..."), + ?NPRINT("Testing simple next/get/set @ subagent (2)..."), gn([[klas2]]), ?line ?expect1([{[fname2,0], ""}]), g([[fname2,0]]), @@ -3787,7 +3882,7 @@ big_test_2() -> otp_1298_test(), - ?P1("Testing next from last object in master to subagent (2)..."), + ?NPRINT("Testing next from last object in master to subagent (2)..."), gn([[?v1_2(sysServices, sysORLastChange),0]]), ?line ?expect1([{[fname2,0], "test set"}]), gn([[1,1], [?v1_2(sysServices, sysORLastChange),0]]), @@ -3796,7 +3891,7 @@ big_test_2() -> table_test(), - ?P1("Adding one row in subagent table (2)"), + ?NPRINT("Adding one row in subagent table (2)"), _FTab = [friendsEntry2], s([{[friendsEntry2, [2, 3]], s, "kompis3"}, {[friendsEntry2, [3, 3]], i, ?createAndGo}]), @@ -3809,7 +3904,7 @@ big_test_2() -> s([{[friendsEntry2, [3, 3]], i, ?destroy}]), ?line ?expect1([{[friendsEntry2, [3, 3]], ?destroy}]), - ?P1("Adding two rows in subagent table with special INDEX (2)"), + ?NPRINT("Adding two rows in subagent table with special INDEX (2)"), s([{[kompissEntry2, [1, 3]], s, "kompis3"}, {[kompissEntry2, [2, 3]], i, ?createAndGo}]), ?line ?expect1([{[kompissEntry2, [1, 3]], "kompis3"}, @@ -3838,7 +3933,7 @@ big_test_2() -> %% Req. Test1 multi_threaded_test() -> - ?P1("Testing multi threaded agent..."), + ?NPRINT("Testing multi threaded agent..."), g([[multiStr,0]]), Pid = get_multi_pid(), g([[sysUpTime,0]]), @@ -3865,7 +3960,7 @@ multi_threaded_test() -> %% Req. Test1, TestTrapv2 mt_trap_test(MA) -> - ?P1("Testing trap-sending with multi threaded agent..."), + ?NPRINT("Testing trap-sending with multi threaded agent..."), ?DBG("mt_trap_test(01) -> issue testTrapv22 (standard trap)", []), snmpa:send_trap(MA, testTrapv22, "standard trap"), ?DBG("mt_trap_test(02) -> await v2trap", []), @@ -3911,7 +4006,7 @@ get_multi_pid(N) -> %% Req. Test1 types_v2_test() -> - ?P1("Testing v2 types..."), + ?NPRINT("Testing v2 types..."), s([{[bits1,0], 2#10}]), ?line ?expect1([{[bits1,0], ?str(2#10)}]), @@ -3938,8 +4033,9 @@ types_v2_test() -> %% Req. Test1 implied_test(MA) -> - ?LOG("implied_test -> start",[]), - ?P1("Testing IMPLIED..."), + ?IPRINT("implied_test -> start"), + + ?NPRINT("Testing IMPLIED..."), snmpa:verbosity(MA, trace), @@ -3997,13 +4093,14 @@ implied_test(MA) -> snmpa:verbosity(MA, log), - ?LOG("implied_test -> done", []). + ?IPRINT("implied_test -> done", []), + ok. %% Req. Test1 sparse_table_test() -> - ?P1("Testing sparse table..."), + ?NPRINT("Testing sparse table..."), %% Create two rows, check that they are get-nexted in correct order. Idx1 = 1, @@ -4033,11 +4130,12 @@ sparse_table_test() -> %% Req. Test1 cnt_64_test(MA) -> - ?LOG("start cnt64 test (~p)",[MA]), + ?IPRINT("start cnt64 test (~p)", [MA]), snmpa:verbosity(MA, trace), - ?LOG("start cnt64 test",[]), - ?P1("Testing Counter64, and at the same time, " - "RowStatus is not last column"), + ?IPRINT("start cnt64 test"), + + ?NPRINT("Testing Counter64, and at the same time, " + "RowStatus is not last column"), ?DBG("get cnt64",[]), g([[cnt64,0]]), @@ -4096,7 +4194,7 @@ cnt_64_test(MA) -> %% Req. Test1 opaque_test() -> - ?P1("Testing Opaque datatype..."), + ?NPRINT("Testing Opaque datatype..."), g([[opaqueObj,0]]), ?line ?expect1([{[opaqueObj,0], "opaque-data"}]). @@ -4238,7 +4336,7 @@ do_mul_next_err() -> %% Req. system group, Klas1, OLD-SNMPEA-MIB do_mul_set() -> - ?P1("Adding one row in subagent table, and one in master table"), + ?NPRINT("Adding one row in subagent table, and one in master table"), NewKeyc3 = [intCommunityEntry,[3],get(mip),is("test")], NewKeyc4 = [intCommunityEntry,[4],get(mip),is("test")], NewKeyc5 = [intCommunityEntry,[5],get(mip),is("test")], @@ -4272,7 +4370,7 @@ do_mul_set_err() -> NewKeyc3 = [intCommunityEntry,[3],get(mip),is("test")], NewKeyc4 = [intCommunityEntry,[4],get(mip),is("test")], NewKeyc5 = [intCommunityEntry,[5],get(mip),is("test")], - ?P1("Adding one row in subagent table, and one in master table"), + ?NPRINT("Adding one row in subagent table, and one in master table"), s([{[friendsEntry, [2, 3]], s, "kompis3"}, {NewKeyc3, 2}, {[sysUpTime,0], 45}, % sysUpTime (readOnly) @@ -4435,14 +4533,14 @@ ma_v2_inform1(MA) -> "~n with receiver: ~p", [_Addr]), ok; {snmp_targets, T, Addrs} -> - ?ERR("ma_v2_inform1 -> " - "received unexpected snmp_targets" - "~n with receivers: ~n ~p",[Addrs]), + ?EPRINT("ma_v2_inform1 -> " + "received unexpected snmp_targets" + "~n with receivers: ~n ~p",[Addrs]), {error, {bad_addrs, Addrs}} after 5000 -> - ?ERR("ma_v2_inform1 -> " - "timeout awaiting snmp_targets [~w]",[T]), + ?EPRINT("ma_v2_inform1 -> " + "timeout awaiting snmp_targets [~w]",[T]), {error, snmp_targets_timeout} end end, @@ -4456,16 +4554,16 @@ ma_v2_inform1(MA) -> "[with manager response] from: ~n ~p", [_Addr]), ok; {snmp_notification, Tag03, {no_response, _Addr}} -> - ?ERR("ma_v2_inform1 -> " - "received unexpected snmp_notification " - "[without manager response] from: ~n ~p", - [_Addr]), + ?EPRINT("ma_v2_inform1 -> " + "received unexpected snmp_notification " + "[without manager response] from: ~n ~p", + [_Addr]), {error, no_response} after 20000 -> - ?ERR("ma_v2_inform1 -> " - "timeout awaiting snmp_notification [~p]", - [Tag03]), + ?EPRINT("ma_v2_inform1 -> " + "timeout awaiting snmp_notification [~p]", + [Tag03]), {error, snmp_notification_timeout} end end, @@ -4490,9 +4588,9 @@ ma_v2_inform1(MA) -> fun() -> receive {snmp_notification, Tag07, {got_response, _Addr}} -> - ?ERR("ma_v2_inform1 -> " - "received unexpected snmp_notification " - "[with manager response] from: ~n ~p", [_Addr]), + ?EPRINT("ma_v2_inform1 -> " + "received unexpected snmp_notification " + "[with manager response] from: ~n ~p", [_Addr]), {error, got_response}; {snmp_notification, Tag07, {no_response, _Addr}} -> ?DBG("ma_v2_inform1 -> " @@ -4502,9 +4600,9 @@ ma_v2_inform1(MA) -> ok after 240000 -> - ?ERR("ma_v2_inform1 -> " - "timeout awaiting snmp_notification [~p]", - [Tag07]), + ?EPRINT("ma_v2_inform1 -> " + "timeout awaiting snmp_notification [~p]", + [Tag07]), {error, snmp_notification_timeout} end end, @@ -4554,26 +4652,28 @@ ma_v2_inform2(MA) -> %% Await callback(s) CmdAwaitDeliveryCallback = fun(Kind, Ref, Tag) -> - io:format("CmdAwaitDeliveryCallback -> entry with" - "~n Kind: ~p" - "~n Ref: ~p" - "~n Tag: ~p" - "~n", [Kind, Ref, Tag]), + ?IPRINT("CmdAwaitDeliveryCallback -> entry with" + "~n Kind: ~p" + "~n Ref: ~p" + "~n Tag: ~p", [Kind, Ref, Tag]), receive {Kind, Ref, ok} -> - io:format("CmdAwaitDeliveryCallback(~p,~p) -> received expected result: ok" - "~n", [Tag, Ref]), + ?IPRINT("CmdAwaitDeliveryCallback(~p,~p) -> " + "received expected result: ok" + "~n", [Tag, Ref]), ok; {Kind, Ref, Error} -> - io:format("CmdAwaitDeliveryCallback(~p,~p) -> received unexpected result: " - "~n Error: ~p" - "~n", [Tag, Ref, Error]), + ?IPRINT("CmdAwaitDeliveryCallback(~p,~p) -> " + "received unexpected result: " + "~n Error: ~p" + "~n", [Tag, Ref, Error]), {error, {unexpected_response, Error}} after 240000 -> - ?ERR("ma_v2_inform2 -> " - "timeout awaiting got_response for snmp_notification [~p]", - [Tag]), + ?EPRINT("ma_v2_inform2 -> " + "timeout awaiting got_response for " + "snmp_notification [~p]", + [Tag]), {error, snmp_notification_timeout} end end, @@ -4645,25 +4745,27 @@ ma_v2_inform3(MA) -> %% Await callback(s) CmdAwaitDeliveryCallback = fun(Kind, Ref, Tag) -> - io:format("CmdAwaitDeliveryCallback -> entry with" - "~n Kind: ~p" - "~n Ref: ~p" - "~n Tag: ~p" - "~n", [Kind, Ref, Tag]), + ?IPRINT("CmdAwaitDeliveryCallback -> entry with" + "~n Kind: ~p" + "~n Ref: ~p" + "~n Tag: ~p", [Kind, Ref, Tag]), receive {Kind, Ref, ok} -> - io:format("CmdAwaitDeliveryCallback(~p,~p) -> received expected result: ok" - "~n", [Tag, Ref]), + ?IPRINT("CmdAwaitDeliveryCallback(~p,~p) -> " + "received expected result: ok" + "~n", [Tag, Ref]), ok; {Kind, Ref, Error} -> - io:format("CmdAwaitDeliveryCallback(~p,~p) -> received unexpected result: " - "~n Error: ~p" - "~n", [Tag, Ref, Error]), + ?IPRINT("CmdAwaitDeliveryCallback(~p,~p) -> " + "received unexpected result: " + "~n Error: ~p" + "~n", [Tag, Ref, Error]), {error, {unexpected_response, Error}} after 240000 -> - ?ERR("ma_v2_inform3 -> " - "timeout awaiting got_response for snmp_notification [~p]", + ?EPRINT("ma_v2_inform3 -> " + "timeout awaiting got_response for " + "snmp_notification [~p]", [Tag]), {error, snmp_notification_timeout} end @@ -4746,16 +4848,16 @@ delivery_info(Tag, Address, DeliveryResult, Extra) -> command_handler([]) -> ok; command_handler([{_No, _Desc, Cmd}|Rest]) -> - ?LOG("command_handler -> command ~w: ~n ~s", [_No, _Desc]), + ?IPRINT("command_handler -> command ~w: ~n ~s", [_No, _Desc]), case (catch Cmd()) of ok -> - ?LOG("command_handler -> ~w: ok", [_No]), + ?IPRINT("command_handler -> ~w: ok", [_No]), command_handler(Rest); {error, Reason} -> - ?ERR("command_handler -> ~w error: ~n~p", [_No, Reason]), + ?EPRINT("command_handler -> ~w error: ~n~p", [_No, Reason]), ?line ?FAIL(Reason); Error -> - ?ERR("command_handler -> ~w unexpected: ~n~p", [_No, Error]), + ?EPRINT("command_handler -> ~w unexpected: ~n~p", [_No, Error]), ?line ?FAIL({unexpected_command_result, Error}) end. @@ -5033,7 +5135,7 @@ standard_mibs2_cases() -> snmpv2_mib_2(suite) -> []; snmpv2_mib_2(Config) when is_list(Config) -> ?P(snmpv2_mib_2), - ?LOG("snmpv2_mib_2 -> start",[]), + ?IPRINT("snmpv2_mib_2 -> start"), init_case(Config), ?DBG("snmpv2_mib_2 -> standard mib init",[]), @@ -5072,7 +5174,7 @@ snmpv2_mib_2(Config) when is_list(Config) -> "then disable auth traps",[]), try_test(snmpv2_mib_test_finish, [], [{community, "bad community"}]), - ?LOG("snmpv2_mib_2 -> done", []), + ?IPRINT("snmpv2_mib_2 -> done"), ok. @@ -5190,7 +5292,7 @@ snmp_community_mib_2(X) -> ?P(snmp_community_mib_2), snmp_community_mib(X). %% Req. SNMP-COMMUNITY-MIB snmp_community_mib_test() -> - ?INF("NOT YET IMPLEMENTED", []), + ?NPRINT("NOT YET IMPLEMENTED"), nyi. @@ -5215,41 +5317,69 @@ snmp_framework_mib_3(Config) when is_list(Config) -> %% Req. SNMP-FRAMEWORK-MIB +%% snmpEngineID in number of seconds. +%% In theory, the Engine Time diff of the engine, should be exactly +%% the same as the number of seconds we sleep (5 in this case). +%% But because, on some (slow or/and high loaded) hosts, the actual +%% time we sleep could be a lot larger (due to, for instance, scheduling). +%% Therefor we must take that into account when we check if the +%% Engine Time diff (between the two checks) is acceptably. snmp_framework_mib_test() -> + Sleep = 5, ?line ["agentEngine"] = get_req(1, [[snmpEngineID,0]]), T1 = snmp_misc:now(ms), ?line [EngineTime] = get_req(2, [[snmpEngineTime,0]]), T2 = snmp_misc:now(ms), - ?SLEEP(5000), + ?SLEEP(?SECS(Sleep)), T3 = snmp_misc:now(ms), ?line [EngineTime2] = get_req(3, [[snmpEngineTime,0]]), T4 = snmp_misc:now(ms), - ?PRINT2("snmp_framework_mib -> time(s): " - "~n EngineTime 1: ~p" - "~n Time to acquire: ~w ms" - "~n EngineTime 2: ~p" - "~n Time to acquire: ~w ms" - "~n => (5 sec sleep between get(snmpEngineTime))" - "~n Total time to acquire: ~w ms", - [EngineTime, T2-T1, EngineTime2, T4-T3, T4-T1]), - if - (EngineTime+7) < EngineTime2 -> - ?line ?FAIL({too_large_diff, EngineTime, EngineTime2}); - (EngineTime+4) > EngineTime2 -> - ?line ?FAIL({too_large_diff, EngineTime, EngineTime2}); - true -> - ok + + %% Ok, we tried to sleep 5 seconds, but how long did we actually sleep + ASleep = ((T3-T2) + 500) div 1000, + EngineTimeDiff = EngineTime2 - EngineTime, + HighEngineTime = EngineTime + ASleep + 2, + LowEngineTime = EngineTime + ASleep - 1, + + ?IPRINT("snmp_framework_mib -> time(s): " + "~n EngineTime 1: ~p" + "~n Time to acquire: ~w msec" + "~n EngineTime 2: ~p" + "~n Time to acquire: ~w msec" + "~n => Total time to acquire: ~w msec" + "~n Sleep between get(snmpEngineTime): ~w (~w) sec" + "~n Engine Time Diff: ~w sec" + "~n Success if:" + "~n ~w (low) =< Engine Time 2 =< ~w (high)", + [EngineTime, T2-T1, + EngineTime2, T4-T3, + T4-T1, ASleep, Sleep, EngineTimeDiff, LowEngineTime, HighEngineTime]), + + if + (HighEngineTime < EngineTime2) -> + ?EPRINT("snmp_framework_mib -> (High) Engine Time diff (~w) too large: " + "~n ~w < ~w", + [EngineTimeDiff, HighEngineTime, EngineTime2]), + ?line ?FAIL({too_large_diff, EngineTime, EngineTime2}); + (LowEngineTime > EngineTime2) -> + ?EPRINT("snmp_framework_mib -> (Low) Engine Time diff (~w) too large: " + "~n ~w > ~w", + [EngineTimeDiff, LowEngineTime, EngineTime2]), + ?line ?FAIL({too_large_diff, EngineTime, EngineTime2}); + true -> + ok end, + T5 = snmp_misc:now(ms), ?line case get_req(4, [[snmpEngineBoots,0]]) of [Boots] when is_integer(Boots) -> T6 = snmp_misc:now(ms), - ?PRINT2("snmp_framework_mib -> " + ?IPRINT("snmp_framework_mib -> " "~n boots: ~p" "~n Time to acquire: ~w ms", [Boots, T6-T5]), ok; Else -> - ?PRINT2("snmp_framework_mib -> failed get proper boots:" + ?EPRINT("snmp_framework_mib -> failed get proper boots:" "~n ~p", [Else]), ?FAIL({invalid_boots, Else}) end, @@ -5339,7 +5469,7 @@ snmp_target_mib_2(X) -> ?P(snmp_target_mib_2), snmp_target_mib(X). snmp_target_mib_3(X) -> ?P(snmp_target_mib_3), snmp_target_mib(X). snmp_target_mib_test() -> - ?INF("NOT YET IMPLEMENTED", []), + ?NPRINT("NOT YET IMPLEMENTED"), nyi. snmp_notification_mib(suite) -> []; @@ -5357,7 +5487,7 @@ snmp_notification_mib_3(X) -> ?P(snmp_notification_mib_3), snmp_notification_mib(X). snmp_notification_mib_test() -> - ?INF("NOT YET IMPLEMENTED", []), + ?NPRINT("NOT YET IMPLEMENTED"), nyi. @@ -5393,7 +5523,7 @@ snmp_view_based_acm_mib_3(X) -> snmp_view_based_acm_mib() -> snmpa:verbosity(net_if,trace), snmpa:verbosity(master_agent,trace), - ?LOG("start snmp_view_based_acm_mib test",[]), + ?IPRINT("start snmp_view_based_acm_mib test"), %% The user "no-rights" is present in USM, and is mapped to security %% name 'no-rights", which is not present in VACM. %% So, we'll add rights for it, try them and delete them. @@ -5790,7 +5920,7 @@ usm_bad() -> loop_mib_1(suite) -> []; loop_mib_1(Config) when is_list(Config) -> ?P(loop_mib_1), - ?LOG("loop_mib_1 -> initiate case",[]), + ?IPRINT("loop_mib_1 -> initiate case"), {_SaNode, _MgrNode, _MibDir} = init_case(Config), ?DBG("loop_mib_1 -> ~n" @@ -5827,14 +5957,14 @@ loop_mib_1(Config) when is_list(Config) -> ?line unload_master("SNMP-VIEW-BASED-ACM-MIB"), %% snmpa:verbosity(master_agent,log), %% snmpa:verbosity(mib_server,silence), - ?LOG("loop_mib_1 -> done",[]), + ?IPRINT("loop_mib_1 -> done"), ok. loop_mib_2(suite) -> []; loop_mib_2(Config) when is_list(Config) -> ?P(loop_mib_2), - ?LOG("loop_mib_2 -> initiate case",[]), + ?IPRINT("loop_mib_2 -> initiate case"), {_SaNode, _MgrNode, _MibDir} = init_case(Config), ?DBG("do_loop_mib_2 -> ~n" "\tSaNode: ~p~n" @@ -5857,14 +5987,14 @@ loop_mib_2(Config) when is_list(Config) -> ?line unload_master("SNMP-NOTIFICATION-MIB"), ?line unload_master("SNMP-FRAMEWORK-MIB"), ?line unload_master("SNMP-VIEW-BASED-ACM-MIB"), - ?LOG("loop_mib_2 -> done",[]), + ?IPRINT("loop_mib_2 -> done"), ok. loop_mib_3(suite) -> []; loop_mib_3(Config) when is_list(Config) -> ?P(loop_mib_3), - ?LOG("loop_mib_3 -> initiate case",[]), + ?IPRINT("loop_mib_3 -> initiate case"), {_SaNode, _MgrNode, _MibDir} = init_case(Config), ?DBG("loop_mib_3 -> ~n" "\tSaNode: ~p~n" @@ -5883,7 +6013,7 @@ loop_mib_3(Config) when is_list(Config) -> ?line unload_master("SNMP-NOTIFICATION-MIB"), ?line unload_master("SNMP-VIEW-BASED-ACM-MIB"), ?line unload_master("SNMP-USER-BASED-SM-MIB"), - ?LOG("loop_mib_3 -> done",[]), + ?IPRINT("loop_mib_3 -> done"), ok. @@ -6378,7 +6508,7 @@ otp_1366_2(X) -> ?P(otp_1366_2), otp_1366(X). otp_1366_3(X) -> ?P(otp_1366_3), otp_1366(X). otp_1366_test() -> - ?INF("NOT YET IMPLEMENTED", []), + ?NPRINT("NOT YET IMPLEMENTED"), 'NYI'. @@ -6397,7 +6527,7 @@ otp_2776_2(X) -> ?P(otp_2776_2), otp_2776(X). otp_2776_3(X) -> ?P(otp_2776_3), otp_2776(X). otp_2776_test() -> - io:format("Testing bug reported in ticket OTP-2776...~n"), + ?NPRINT("Testing bug reported in ticket OTP-2776..."), Dt01_valid = [19,98,9,1,1,0,23,0,43,0,0], Dt02_valid = [19,98,9,1,0,0,0,0,43,0,0], % This is what is fixed: 00:00 @@ -6716,10 +6846,11 @@ otp_7157(Config) -> %% ts:run(snmp, snmp_agent_test, [batch]). otp_7157_test(MA) -> - ?LOG("start otp_7157_test test (~p)",[MA]), + ?IPRINT("start otp_7157_test test (~p)", [MA]), snmpa:verbosity(MA, trace), - ?LOG("start otp_7157_test test",[]), - ?P1("Testing that varbinds in traps/notifications are not reordered"), + ?IPRINT("start otp_7157_test test"), + + ?NPRINT("Testing that varbinds in traps/notifications are not reordered"), ?DBG("send cntTrap",[]), snmpa:send_trap(MA, cntTrap, "standard trap"), @@ -6757,10 +6888,16 @@ otp_16092_simple_start_and_stop1(Config) -> ?P(otp_16092_simple_start_and_stop1), ?DBG("otp_16092_simple_start_and_stop1 -> entry", []), - otp_16092_simple_start_and_stop(Config, default, success), + TC = fun() -> + otp_16092_simple_start_and_stop(Config, default, success) + end, - ?DBG("otp_16092_simple_start_and_stop1 -> done", []), - ok. + Result = otp_16092_try(TC), + + ?DBG("otp_16092_simple_start_and_stop1 -> done: " + "~n ~p", [Result]), + + Result. otp_16092_simple_start_and_stop2(suite) -> []; @@ -6768,10 +6905,16 @@ otp_16092_simple_start_and_stop2(Config) -> ?P(otp_16092_simple_start_and_stop2), ?DBG("otp_16092_simple_start_and_stop2 -> entry", []), - otp_16092_simple_start_and_stop(Config, [], success), + TC = fun() -> + otp_16092_simple_start_and_stop(Config, [], success) + end, - ?DBG("otp_16092_simple_start_and_stop2 -> done", []), - ok. + Result = otp_16092_try(TC), + + ?DBG("otp_16092_simple_start_and_stop2 -> done: " + "~n ~p", [Result]), + + Result. otp_16092_simple_start_and_stop3(suite) -> []; @@ -6779,10 +6922,18 @@ otp_16092_simple_start_and_stop3(Config) -> ?P(otp_16092_simple_start_and_stop3), ?DBG("otp_16092_simple_start_and_stop3 -> entry", []), - otp_16092_simple_start_and_stop(Config, 'this-should-be-ignored', success), + TC = fun() -> + otp_16092_simple_start_and_stop(Config, + 'this-should-be-ignored', + success) + end, - ?DBG("otp_16092_simple_start_and_stop3 -> done", []), - ok. + Result = otp_16092_try(TC), + + ?DBG("otp_16092_simple_start_and_stop3 -> done: " + "~n ~p", [Result]), + + Result. otp_16092_simple_start_and_stop4(suite) -> []; @@ -6790,29 +6941,35 @@ otp_16092_simple_start_and_stop4(Config) -> ?P(otp_16092_simple_start_and_stop4), ?DBG("otp_16092_simple_start_and_stop4 -> entry", []), - otp_16092_simple_start_and_stop(Config, ['this-should-fail'], failure), + TC = fun() -> + otp_16092_simple_start_and_stop(Config, + ['this-should-fail'], + failure) + end, + + Result = otp_16092_try(TC), - ?DBG("otp_16092_simple_start_and_stop4 -> done", []), - ok. + ?DBG("otp_16092_simple_start_and_stop4 -> done: " + "~n ", [Result]), + + Result. +otp_16092_try(TC) -> + try TC() of + Any -> + Any + catch + _:{skip, _} = SKIP -> + SKIP + end. + otp_16092_simple_start_and_stop(Config, ESO, Expected) -> ?line ConfDir = ?config(agent_conf_dir, Config), ?line DbDir = ?config(agent_db_dir, Config), - p("try start agent node~n"), - p(user, "try start agent node~n"), - Node = case ?ALIB:start_node(agent_16092) of - {ok, N} -> - p("agent node ~p started~n", [N]), - p(user, "agent node ~p started~n", [N]), - N; - {error, Reason} -> - e("Failed starting agent node: " - "~n ~p" - "~n", [Reason]), - ?SKIP({failed_starting_node, Reason}) - end, + ?NPRINT("try start agent node"), + {ok, Node} = ?ALIB:start_node(agent_16092), Vsns = [v1], IP = tuple_to_list(?config(ip, Config)), @@ -6841,30 +6998,29 @@ otp_16092_simple_start_and_stop(Config, ESO, Expected) -> {config, ConfOpts}, {net_if, NiOpts}], - otp16092_try_start_and_stop_agent(Node, Opts, Expected), - p("try stop agent node ~p~n", [Node]), - p(user, "try stop agent node ~p~n", [Node]), + ?NPRINT("try stop agent node ~p", [Node]), ?ALIB:stop_node(Node), ?SLEEP(1000), - p("done~n"), - p(user, "done~n"), + ?NPRINT("done"), ok. - + otp16092_try_start_and_stop_agent(Node, Opts, Expected) -> - i("try start snmp (agent) supervisor (on ~p) - expect ~p~n", [Node, Expected]), + ?IPRINT("try start snmp (agent) supervisor (on ~p) - expect ~p", + [Node, Expected]), case start_standalone_agent(Node, Opts) of Pid when is_pid(Pid) andalso (Expected =:= success) -> - i("Expected success starting snmp (agent) supervisor~n"), + ?IPRINT("Expected success starting snmp (agent) supervisor"), ?SLEEP(1000), stop_standalone_agent(Pid), ok; Pid when is_pid(Pid) andalso (Expected =:= failure) -> - e("Unexpected success starting snmp (agent) supervisor: (~p)~n", [Pid]), + ?EPRINT("Unexpected success starting snmp (agent) supervisor: (~p)", + [Pid]), ?SLEEP(1000), stop_standalone_agent(Pid), ?FAIL('unexpected-start-success'); @@ -6875,18 +7031,18 @@ otp16092_try_start_and_stop_agent(Node, Opts, Expected) -> {shutdown, {failed_to_start_child, snmpa_agent, {net_if_error, Reason}}}}} when (Expected =:= failure) -> - p("Expected (shutdown, net-if) error starting " - "snmp (agent) supervisor (on ~p):" - "~n ~p", [Node, Reason]), + ?IPRINT("Expected (shutdown, net-if) error starting " + "snmp (agent) supervisor (on ~p):" + "~n ~p", [Node, Reason]), ok; {error, {shutdown, Reason}} when (Expected =:= failure) -> - p("Expected (shutdown) error starting " - "snmp (agent) supervisor (on ~p):" - "~n ~p", [Node, Reason]), + ?IPRINT("Expected (shutdown) error starting " + "snmp (agent) supervisor (on ~p):" + "~n ~p", [Node, Reason]), ok; {error, Reason} when (Expected =:= failure) -> - p("Expected error starting snmp (agent) supervisor (on ~p):" - "~n ~p", [Node, Reason]), + ?IPRINT("Expected error starting snmp (agent) supervisor (on ~p):" + "~n ~p", [Node, Reason]), ok; {badrpc, @@ -6896,23 +7052,24 @@ otp16092_try_start_and_stop_agent(Node, Opts, Expected) -> {shutdown, {failed_to_start_child, snmpa_agent, {net_if_error, Reason}}}}}}} when (Expected =:= failure) -> - p("Expected (badrpc, shutdown, net-if) error starting " - "snmp (agent) supervisor (on ~p):" - "~n ~p", [Node, Reason]), + ?IPRINT("Expected (badrpc, shutdown, net-if) error starting " + "snmp (agent) supervisor (on ~p):" + "~n ~p", [Node, Reason]), ok; {badrpc, {'EXIT', {shutdown, Reason}}} when (Expected =:= failure) -> - p("Expected (badrpc, shutdown) error starting " - "snmp (agent) supervisor (on ~p):" - "~n ~p", [Node, Reason]), + ?IPRINT("Expected (badrpc, shutdown) error starting " + "snmp (agent) supervisor (on ~p):" + "~n ~p", [Node, Reason]), ok; {badrpc, {'EXIT', Reason}} when (Expected =:= failure) -> - p("Expected (badrpc) error starting snmp (agent) supervisor (on ~p):" - "~n ~p", [Node, Reason]), + ?IPRINT("Expected (badrpc) error starting snmp (agent) supervisor " + "(on ~p):" + "~n ~p", [Node, Reason]), ok; {badrpc, Reason} = BADRPC -> - e("Bad RPC to node ~p failed:" - "~n ~p", [Node, Reason]), + ?WPRINT("Bad RPC to node ~p failed:" + "~n ~p", [Node, Reason]), ?SKIP({BADRPC, Node}) end, @@ -7006,9 +7163,7 @@ otp8395({init, Config}) when is_list(Config) -> %% Create watchdog %% - Dog = ?WD_START(?MINS(1)), - - [{watchdog, Dog} | Config2]; + wd_start(1, Config2); otp8395({fin, Config}) when is_list(Config) -> ?DBG("otp8395(fin) -> entry with" @@ -7047,9 +7202,7 @@ otp8395({fin, Config}) when is_list(Config) -> ?DBG("otp8395(fin) -> stop manager node", []), stop_node(ManagerNode), - Dog = ?config(watchdog, Config), - ?WD_STOP(Dog), - lists:keydelete(watchdog, 1, Config); + wd_stop(Config); otp8395(doc) -> "OTP-8395 - ATL with sequence numbering. "; @@ -7102,7 +7255,7 @@ otp9884({fin, Config}) when is_list(Config) -> fin_v1_agent(Config); otp9884(doc) -> - "OTP-9884 - Simlutaneous backup call should not work. "; + "OTP-9884 - Simultaneous backup(s) call should not work. "; otp9884(Config) when is_list(Config) -> ?DBG("otp9884 -> entry with" @@ -7111,10 +7264,15 @@ otp9884(Config) when is_list(Config) -> AgentNode = ?config(agent_node, Config), [AgentBkpDir1, AgentBkpDir2] = ?config(agent_backup_dirs, Config), Self = self(), - timer:apply_after(1000, - ?MODULE, otp9884_backup, [AgentNode, Self, first, AgentBkpDir1]), - timer:apply_after(1000, - ?MODULE, otp9884_backup, [AgentNode, Self, second, AgentBkpDir2]), + timer:apply_after(1000, + ?MODULE, + otp9884_backup, + [AgentNode, Self, first, AgentBkpDir1]), + timer:apply_after(1000, + ?MODULE, + otp9884_backup, + [AgentNode, Self, second, AgentBkpDir2]), + otp9884_await_backup_started(), otp9884_await_backup_completion(undefined, undefined), ?DBG("otp9884 -> done", []), @@ -7122,20 +7280,47 @@ otp9884(Config) when is_list(Config) -> otp9884_backup(Node, Pid, Tag, Dir) -> - io:format("[~w] call backup function~n", [Tag]), + ?IPRINT("[~w] backup - await continue", [Tag]), + Pid ! {otp9884_backup_started, Tag, self()}, + receive + {otp9884_backup_continue, Tag, Pid} -> + ok + end, + ?IPRINT("[~w] backup start", [Tag]), Res = rpc:call(Node, snmpa, backup, [Dir]), - io:format("[~w] backup result: ~p~n", [Tag, Res]), + ?IPRINT("[~w] backup result: ~p", [Tag, Res]), Pid ! {otp9884_backup_complete, Tag, Res}. + +otp9884_await_backup_started() -> + otp9884_await_backup_started(undefined, undefined). + +otp9884_await_backup_started(First, Second) + when is_pid(First) andalso is_pid(Second) -> + ?IPRINT("otp9884_await_backup_started -> order first continue"), + First ! {otp9884_backup_continue, first, self()}, + ?IPRINT("otp9884_await_backup_started -> order second continue"), + Second ! {otp9884_backup_continue, second, self()}, + ok; +otp9884_await_backup_started(First, Second) -> + receive + {otp9884_backup_started, first, Pid} when (First =:= undefined) -> + ?IPRINT("otp9884_await_backup_started -> received started from first"), + otp9884_await_backup_started(Pid, Second); + {otp9884_backup_started, second, Pid} when (Second =:= undefined) -> + ?IPRINT("otp9884_await_backup_started -> received started from second"), + otp9884_await_backup_started(First, Pid) + end. + otp9884_await_backup_completion(ok, Second) when ((Second =/= ok) andalso (Second =/= undefined)) -> - io:format("otp9884_await_backup_completion -> " - "first backup succeed and second failed (~p)~n", [Second]), + ?IPRINT("otp9884_await_backup_completion -> " + "first backup succeed and second failed (~p)", [Second]), ok; otp9884_await_backup_completion(First, ok) when ((First =/= ok) andalso (First =/= undefined)) -> - io:format("otp9884_await_backup_completion -> " - "second backup succeed and first failed (~p)~n", [First]), + ?IPRINT("otp9884_await_backup_completion -> " + "second backup succeed and first failed (~p)", [First]), ok; otp9884_await_backup_completion(First, Second) when (((First =:= undefined) andalso (Second =:= undefined)) @@ -7143,22 +7328,28 @@ otp9884_await_backup_completion(First, Second) ((First =:= undefined) andalso (Second =/= undefined)) orelse ((First =/= undefined) andalso (Second =:= undefined))) -> - io:format("otp9884_await_backup_completion -> await complete messages~n", []), + ?IPRINT("otp9884_await_backup_completion -> await complete messages"), receive {otp9884_backup_complete, first, Res} -> - io:format("otp9884_await_backup_completion -> " - "received complete message for first: ~p~n", [Res]), + ?IPRINT("otp9884_await_backup_completion -> " + "received complete message for first: ~p", [Res]), otp9884_await_backup_completion(Res, Second); {otp9884_backup_complete, second, Res} -> - io:format("otp9884_await_backup_completion -> " - "received complete message for second: ~p~n", [Res]), + ?IPRINT("otp9884_await_backup_completion -> " + "received complete message for second: ~p", [Res]), otp9884_await_backup_completion(First, Res) after 10000 -> %% we have waited long enough + ?EPRINT("otp9884_await_backup_completion -> timeout"), throw({error, {timeout, First, Second}}) end; otp9884_await_backup_completion(First, Second) -> + ?EPRINT("Bad Completion: " + "~n First: ~p" + "~n Second: ~p", [First, Second]), throw({error, {bad_completion, First, Second}}). + + %%----------------------------------------------------------------- agent_log_validation(Node) -> @@ -7382,16 +7573,16 @@ verify_subinfo(Info0, [Key|Keys]) -> is(S) -> [length(S) | S]. try_test(Func) -> - ?P2("try test ~w...", [Func]), - snmp_agent_test_lib:try_test(?MODULE, Func). + ?NPRINT("try test ~w...", [Func]), + ?ALIB:try_test(?MODULE, Func). try_test(Func, A) -> - ?P2("try test ~w...", [Func]), - snmp_agent_test_lib:try_test(?MODULE, Func, A). + ?NPRINT("try test ~w...", [Func]), + ?ALIB:try_test(?MODULE, Func, A). try_test(Func, A, Opts) -> - ?P2("try test ~w...", [Func]), - snmp_agent_test_lib:try_test(?MODULE, Func, A, Opts). + ?NPRINT("try test ~w...", [Func]), + ?ALIB:try_test(?MODULE, Func, A, Opts). %% Test manager wrapperfunctions: @@ -7402,77 +7593,80 @@ gb(NR, MR, Oids) -> snmp_test_mgr:gb(NR, MR, Oids). s(VAV) -> snmp_test_mgr:s(VAV). get_req(Id, Vars) -> - snmp_agent_test_lib:get_req(Id, Vars). + ?ALIB:get_req(Id, Vars). get_next_req(Vars) -> - snmp_agent_test_lib:get_next_req(Vars). + ?ALIB:get_next_req(Vars). start_node(Name) -> - snmp_agent_test_lib:start_node(Name). + ?ALIB:start_node(Name). +stop_node(undefined) -> + ok; stop_node(Node) -> - snmp_agent_test_lib:stop_node(Node). + ?ALIB:stop_node(Node). %%%----------------------------------------------------------------- %%% Configuration %%%----------------------------------------------------------------- delete_files(Config) -> - snmp_agent_test_lib:delete_files(Config). + ?ALIB:delete_files(Config). config(Vsns, MgrDir, AgentDir, MIp, AIp) -> - snmp_agent_test_lib:config(Vsns, MgrDir, AgentDir, MIp, AIp). + ?ALIB:config(Vsns, MgrDir, AgentDir, MIp, AIp). config(Vsns, MgrDir, AgentDir, MIp, AIp, IpFamily) -> - snmp_agent_test_lib:config(Vsns, MgrDir, AgentDir, MIp, AIp, IpFamily). + ?ALIB:config(Vsns, MgrDir, AgentDir, MIp, AIp, IpFamily). update_usm(Vsns, Dir) -> - snmp_agent_test_lib:update_usm(Vsns, Dir). + ?ALIB:update_usm(Vsns, Dir). update_usm_mgr(Vsns, Dir) -> - snmp_agent_test_lib:update_usm_mgr(Vsns, Dir). + ?ALIB:update_usm_mgr(Vsns, Dir). rewrite_usm_mgr(Dir, ShaKey, DesKey) -> - snmp_agent_test_lib:rewrite_usm_mgr(Dir, ShaKey, DesKey). + ?ALIB:rewrite_usm_mgr(Dir, ShaKey, DesKey). reset_usm_mgr(Dir) -> - snmp_agent_test_lib:reset_usm_mgr(Dir). + ?ALIB:reset_usm_mgr(Dir). update_vacm(Vsn, Dir) -> - snmp_agent_test_lib:update_vacm(Vsn, Dir). + ?ALIB:update_vacm(Vsn, Dir). write_community_conf(Dir, Conf) -> - snmp_agent_test_lib:write_community_conf(Dir, Conf). + ?ALIB:write_community_conf(Dir, Conf). write_target_addr_conf(Dir, Conf) -> - snmp_agent_test_lib:write_target_addr_conf(Dir, Conf). + ?ALIB:write_target_addr_conf(Dir, Conf). rewrite_target_addr_conf(Dir, NewPort) -> - snmp_agent_test_lib:rewrite_target_addr_conf(Dir, NewPort). + ?ALIB:rewrite_target_addr_conf(Dir, NewPort). reset_target_addr_conf(Dir) -> - snmp_agent_test_lib:reset_target_addr_conf(Dir). + ?ALIB:reset_target_addr_conf(Dir). write_target_params_conf(Dir, Vsns) -> - snmp_agent_test_lib:write_target_params_conf(Dir, Vsns). + ?ALIB:write_target_params_conf(Dir, Vsns). rewrite_target_params_conf(Dir, SecName, SecLevel) -> - snmp_agent_test_lib:rewrite_target_params_conf(Dir, SecName, SecLevel). + ?ALIB:rewrite_target_params_conf(Dir, SecName, SecLevel). reset_target_params_conf(Dir) -> - snmp_agent_test_lib:reset_target_params_conf(Dir). + ?ALIB:reset_target_params_conf(Dir). write_notify_conf(Dir) -> - snmp_agent_test_lib:write_notify_conf(Dir). + ?ALIB:write_notify_conf(Dir). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% copy_file(From, To) -> - snmp_agent_test_lib:copy_file(From, To). + ?ALIB:copy_file(From, To). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -7485,25 +7679,25 @@ display_log(Config) -> LogDir = Dir, Mibs = [], OutFile = join(LogDir, "snmpa_log.txt"), - p("~n" - "=========================" - " < Audit Trail Log > " - "=========================" - "~n"), + ?IPRINT("~n" + "=========================" + " < Audit Trail Log > " + "=========================" + "~n"), rcall(Node, snmpa, log_to_txt, [LogDir, Mibs, OutFile]), rcall(Node, snmpa, log_to_io, [LogDir, Mibs]), - p("~n" - "=========================" - " < / Audit Trail Log > " - "=========================" - "~n", []); + ?IPRINT("~n" + "=========================" + " < / Audit Trail Log > " + "=========================" + "~n"); false -> - p("display_log -> no agent node found"), + ?IPRINT("display_log -> no agent node found"), ok end; false -> - p("display_log -> no agent log dir found: " - "~n ~p", [Config]), + ?IPRINT("display_log -> no agent log dir found: " + "~n ~p", [Config]), ok end. @@ -7518,8 +7712,7 @@ display_memory_usage() -> SSMU = display_symbolic_store_memory_usage(Info), LDBMU = display_local_db_memory_usage(Info), MSMU = display_mib_server_memory_usage(Info), - ?INF("Memory usage: ~n" ++ - AMU ++ NIMU ++ NSMU ++ SSMU ++ LDBMU ++ MSMU, []), + ?NPRINT("Memory usage: ~n" ++ AMU ++ NIMU ++ NSMU ++ SSMU ++ LDBMU ++ MSMU), ok. display_agent_memory_usage(Info) -> @@ -7536,62 +7729,57 @@ display_agent_memory_usage(Info) -> lists_key1search([db_memory,community_cache], AgentInfo), VacmSize = lists_key1search([db_memory,vacm], AgentInfo), - lists:flatten( - io_lib:format(" Agent memory usage: " - "~n Master process memory size: ~p" - "~n Worker process memory size: ~p" - "~n Set-worker process memory size: ~p" - "~n Agent tab size: ~p" - "~n Community cache size: ~p" - "~n Vacm tab size: ~p" - "~n", - [ProcMem, WProcMem, SWProcMem, - TabSize, CCSize, VacmSize])). + ?F(" Agent memory usage: " + "~n Master process memory size: ~p" + "~n Worker process memory size: ~p" + "~n Set-worker process memory size: ~p" + "~n Agent tab size: ~p" + "~n Community cache size: ~p" + "~n Vacm tab size: ~p" + "~n", + [ProcMem, WProcMem, SWProcMem, + TabSize, CCSize, VacmSize]). display_net_if_memory_usage(Info) -> NiInfo = lists_key1search(net_if, Info), ProcMem = lists_key1search(process_memory, NiInfo), - lists:flatten( - io_lib:format(" Net if memory usage: " - "~n Process memory size: ~p" - "~n",[ProcMem])). + ?F(" Net if memory usage: " + "~n Process memory size: ~p" + "~n", [ProcMem]). display_note_store_memory_usage(Info) -> NsInfo = lists_key1search(note_store, Info), ProcMem = lists_key1search([process_memory,notes], NsInfo), ProcTmrMem = lists_key1search([process_memory,timer], NsInfo), TabSize = lists_key1search([db_memory,notes], NsInfo), - lists:flatten( - io_lib:format(" Note store memory usage: " - "~n Notes process memory size: ~p" - "~n Timer process memory size: ~p" - "~n Notes tab size: ~p" - "~n", - [ProcMem, ProcTmrMem, TabSize])). + ?F(" Note store memory usage: " + "~n Notes process memory size: ~p" + "~n Timer process memory size: ~p" + "~n Notes tab size: ~p" + "~n", + [ProcMem, ProcTmrMem, TabSize]). display_symbolic_store_memory_usage(Info) -> SsInfo = lists_key1search(symbolic_store, Info), ProcMem = lists_key1search(process_memory, SsInfo), DbMem = lists_key1search(db_memory, SsInfo), - lists:flatten( - io_lib:format(" Symbolic store memory usage: " - "~n Process memory size: ~p" - "~n DB size: ~p" - "~n", - [ProcMem, DbMem])). + ?F(" Symbolic store memory usage: " + "~n Process memory size: ~p" + "~n DB size: ~p" + "~n", + [ProcMem, DbMem]). display_local_db_memory_usage(Info) -> LdInfo = lists_key1search(local_db, Info), ProcMem = lists_key1search(process_memory, LdInfo), EtsSize = lists_key1search([db_memory,ets], LdInfo), DetsSize = lists_key1search([db_memory,dets], LdInfo), - lists:flatten( - io_lib:format(" Local DB memory usage: " - "~n Process memory size: ~p" - "~n DB [ets] size: ~p" - "~n DB [dets] size: ~p" - "~n", - [ProcMem, EtsSize, DetsSize])). + ?F(" Local DB memory usage: " + "~n Process memory size: ~p" + "~n DB [ets] size: ~p" + "~n DB [dets] size: ~p" + "~n", + [ProcMem, EtsSize, DetsSize]). display_mib_server_memory_usage(Info) -> MibInfo = lists_key1search(mib_server, Info), @@ -7600,15 +7788,14 @@ display_mib_server_memory_usage(Info) -> MibDbSize = lists_key1search([db_memory,mib], MibInfo), NodeDbSize = lists_key1search([db_memory,node], MibInfo), TreeDbSize = lists_key1search([db_memory,tree], MibInfo), - lists:flatten( - io_lib:format(" MIB server memory usage: " - "~n Process memory size: ~p" - "~n Tree size: ~p" - "~n Mib db size: ~p" - "~n Node db size: ~p" - "~n Tree db size: ~p" - "~n", - [ProcMem, TreeSize, MibDbSize, NodeDbSize, TreeDbSize])). + ?F(" MIB server memory usage: " + "~n Process memory size: ~p" + "~n Tree size: ~p" + "~n Mib db size: ~p" + "~n Node db size: ~p" + "~n Tree db size: ~p" + "~n", + [ProcMem, TreeSize, MibDbSize, NodeDbSize, TreeDbSize]). lists_key1search([], Res) -> Res; @@ -7630,56 +7817,6 @@ lists_key1search(Key, List) when is_atom(Key) -> %% ------ -join(Parts) -> - filename:join(Parts). - -join(Dir, File) -> - filename:join(Dir, File). - - -%% ------ - -rcall(Node, Mod, Func, Args) -> - case rpc:call(Node, Mod, Func, Args) of - {badrpc, nodedown} -> - ?FAIL({rpc_failure, Node}); - Else -> - Else - end. - - -%% ------ - -%% e(F) -> -%% e(F, []). - -e(F, A) -> - p(user, "<ERROR> " ++ F, A), - p(standard_io, "<ERROR> " ++ F, A). - -i(F) -> - i(F, []). -i(F, A) -> - p(user, F, A), - p(standard_io, F, A). - -p(F) -> - p(F, []). - -p(Dev, F) when is_atom(Dev) -> - p(Dev, F, []); -p(F, A) -> - p(standard_io, F, A). - -p(Dev, F, A) -> - io:format(Dev, - "*** [~s] ***" - "~n" ++ F ++ "~n", [formated_timestamp()|A]). - -formated_timestamp() -> - snmp_test_lib:formated_timestamp(). - - init_v1_agent(Config) -> %% -- %% Start nodes @@ -7750,9 +7887,7 @@ init_v1_agent(Config) -> %% Create watchdog %% - Dog = ?WD_START(?MINS(1)), - - [{watchdog, Dog} | Config2]. + wd_start(1, Config2). fin_v1_agent(Config) -> AgentNode = ?config(agent_node, Config), @@ -7787,10 +7922,7 @@ fin_v1_agent(Config) -> %% stop_node(ManagerNode), - Dog = ?config(watchdog, Config), - ?WD_STOP(Dog), - lists:keydelete(watchdog, 1, Config). - + wd_stop(Config). config_ipfamily(Config) -> @@ -7800,3 +7932,25 @@ config_ipfamily(Config) -> Value -> Value end. + + +%% ------ + +join(Parts) -> + filename:join(Parts). + +join(Dir, File) -> + filename:join(Dir, File). + + +%% ------ + +rcall(Node, Mod, Func, Args) -> + case rpc:call(Node, Mod, Func, Args) of + {badrpc, nodedown} -> + ?FAIL({rpc_failure, Node}); + Else -> + Else + end. + + diff --git a/lib/snmp/test/snmp_agent_conf_SUITE.erl b/lib/snmp/test/snmp_agent_conf_SUITE.erl index 889af630a4..1f0ad3708b 100644 --- a/lib/snmp/test/snmp_agent_conf_SUITE.erl +++ b/lib/snmp/test/snmp_agent_conf_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2019. All Rights Reserved. +%% Copyright Ericsson AB 2003-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -101,7 +101,7 @@ init_per_testcase(_Case, Config) when is_list(Config) -> Config. end_per_testcase(_Case, Config) when is_list(Config) -> - ?PRINT2("system events during test: " + ?IPRINT("system events during test: " "~n ~p", [snmp_test_global_sys_monitor:events()]), Config. diff --git a/lib/snmp/test/snmp_agent_mibs_SUITE.erl b/lib/snmp/test/snmp_agent_mibs_SUITE.erl index 9545dfdcc9..150e015554 100644 --- a/lib/snmp/test/snmp_agent_mibs_SUITE.erl +++ b/lib/snmp/test/snmp_agent_mibs_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2019. All Rights Reserved. +%% Copyright Ericsson AB 2003-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -67,6 +67,9 @@ ]). +-define(ALIB, snmp_agent_test_lib). + + %%====================================================================== %% Common Test interface functions %%====================================================================== @@ -166,27 +169,18 @@ init_per_testcase(Case, Config0) when is_list(Config0) -> init_per_testcase2(size_check_ets2_bad_file1, Config) when is_list(Config) -> DbDir = ?config(db_dir, Config), - %% Create a ad file + %% Create a bad file ok = file:write_file(join(DbDir, "snmpa_symbolic_store.db"), "calvin and hoppes play chess"), Config; init_per_testcase2(size_check_ets3_bad_file1, Config) when is_list(Config) -> DbDir = ?config(db_dir, Config), - %% Create a ad file + %% Create a bad file ok = file:write_file(join(DbDir, "snmpa_symbolic_store.db"), "calvin and hoppes play chess"), Config; init_per_testcase2(size_check_mnesia, Config) when is_list(Config) -> - DbDir = ?config(db_dir, Config), - try - begin - mnesia_start([{dir, DbDir}]), - Config - end - catch - throw:{skip, _} = SKIP -> - SKIP - end; + Config; init_per_testcase2(cache_test, Config) when is_list(Config) -> Min = timer:minutes(5), Timeout = @@ -205,7 +199,7 @@ init_per_testcase2(_Case, Config) when is_list(Config) -> end_per_testcase(Case, Config) when is_list(Config) -> - ?PRINT2("system events during test: " + ?IPRINT("system events during test: " "~n ~p", [snmp_test_global_sys_monitor:events()]), end_per_testcase1(Case, Config). @@ -301,7 +295,8 @@ size_check_ets1(suite) -> []; size_check_ets1(Config) when is_list(Config) -> MibStorage = [{module, snmpa_mib_storage_ets}], - do_size_check([{mib_storage, MibStorage}|Config]). + do_size_check(size_check_ets1, + [{mib_storage, MibStorage}|Config]). size_check_ets2(suite) -> []; @@ -309,7 +304,8 @@ size_check_ets2(Config) when is_list(Config) -> Dir = ?config(db_dir, Config), MibStorage = [{module, snmpa_mib_storage_ets}, {options, [{dir, Dir}]}], - do_size_check([{mib_storage, MibStorage}|Config]). + do_size_check(size_check_ets2, + [{mib_storage, MibStorage}|Config]). size_check_ets2_bad_file1(suite) -> []; @@ -319,7 +315,8 @@ size_check_ets2_bad_file1(Config) when is_list(Config) -> MibStorage = [{module, snmpa_mib_storage_ets}, {options, [{dir, Dir}, {action, clear}]}], - do_size_check([{mib_storage, MibStorage}|Config]). + do_size_check(size_check_ets2_bad_file1, + [{mib_storage, MibStorage}|Config]). size_check_ets3(suite) -> []; @@ -328,7 +325,8 @@ size_check_ets3(Config) when is_list(Config) -> MibStorage = [{module, snmpa_mib_storage_ets}, {options, [{dir, Dir}, {checksum, true}]}], - do_size_check([{mib_storage, MibStorage}|Config]). + do_size_check(size_check_ets3, + [{mib_storage, MibStorage}|Config]). size_check_ets3_bad_file1(suite) -> []; @@ -339,7 +337,8 @@ size_check_ets3_bad_file1(Config) when is_list(Config) -> {options, [{dir, Dir}, {action, clear}, {checksum, true}]}], - do_size_check([{mib_storage, MibStorage}|Config]). + do_size_check(size_check_ets3_bad_file1, + [{mib_storage, MibStorage}|Config]). size_check_dets(suite) -> []; @@ -347,18 +346,72 @@ size_check_dets(Config) when is_list(Config) -> Dir = ?config(db_dir, Config), MibStorage = [{module, snmpa_mib_storage_dets}, {options, [{dir, Dir}]}], - do_size_check([{mib_storage, MibStorage}|Config]). + do_size_check(size_check_dets, + [{mib_storage, MibStorage}|Config]). size_check_mnesia(suite) -> []; size_check_mnesia(Config) when is_list(Config) -> MibStorage = [{module, snmpa_mib_storage_mnesia}, - {options, [{nodes, [node()]}]}], - do_size_check([{mib_storage, MibStorage}|Config]). + {options, [{nodes, []}]}], + DbDir = ?config(db_dir, Config), + Init = fun() -> mnesia_start([{dir, DbDir}]), ok end, + do_size_check(size_check_mnesia, + Init, + [{mib_storage, MibStorage}|Config]). + +do_size_check(Name, Config) -> + Init = fun() -> ok end, + do_size_check(Name, Init, Config). + +do_size_check(Name, Init, Config) -> + Pre = fun() -> + {ok, Node} = ?ALIB:start_node(unique(Name)), + ok = run_on(Node, Init), + Node + end, + Case = fun(Node) -> + monitor_node(Node, true), + Pid = spawn_link(Node, fun() -> do_size_check(Config) end), + receive + {nodedown, Node} = N -> + exit(N); + {'EXIT', Pid, normal} -> + monitor_node(Node, false), + ok; + {'EXIT', Pid, ok} -> + monitor_node(Node, false), + ok; + {'EXIT', Pid, Reason} -> + monitor_node(Node, false), + exit(Reason) + end + end, + Post = fun({Node, _}) -> + ?STOP_NODE(Node) + end, + ?TC_TRY(Name, Pre, Case, Post). + +run_on(Node, F) when is_atom(Node) andalso is_function(F, 0) -> + monitor_node(Node, true), + Pid = spawn_link(Node, F), + receive + {nodedown, Node} = N -> + exit(N); + {'EXIT', Pid, normal} -> + monitor_node(Node, false), + ok; + {'EXIT', Pid, Reason} -> + monitor_node(Node, false), + Reason + end. + +unique(PreName) -> + list_to_atom(?F("~w_~w", [PreName, erlang:system_time(millisecond)])). do_size_check(Config) -> - ?DBG("do_size_check -> start with" - "~n Config: ~p", [Config]), + ?IPRINT("do_size_check -> start with" + "~n Config: ~p", [Config]), Prio = normal, Verbosity = trace, @@ -404,7 +457,7 @@ do_size_check(Config) -> ?DBG("do_size_check -> stop symbolic store", []), ?line sym_stop(), - ?DBG("do_size_check -> done", []), + ?IPRINT("do_size_check -> done", []), ok. @@ -635,7 +688,7 @@ mnesia_start(Opts, Nodes) -> %% We can accept mnesia beeing loaded but *not* started. %% If its started it *may* contain data that will invalidate %% this test case. - ?PRINT2("mnesia_start -> try load mnesia when:" + ?IPRINT("mnesia_start -> try load mnesia when:" "~n Loaded: ~p" "~n Running: ~p", [apps_loaded(), apps_running()]), ?line ok = case application:load(mnesia) of @@ -647,12 +700,12 @@ mnesia_start(Opts, Nodes) -> ERROR end, F = fun({Key, Val}) -> - ?PRINT2("mnesia_start -> try set mnesia env: " + ?IPRINT("mnesia_start -> try set mnesia env: " "~n ~p -> ~p", [Key, Val]), ?line application_controller:set_env(mnesia, Key, Val) end, lists:foreach(F, Opts), - ?PRINT2("mnesia_start -> create mnesia schema on ~p", [Nodes]), + ?IPRINT("mnesia_start -> create mnesia schema on ~p", [Nodes]), ?line case mnesia:create_schema(Nodes) of ok -> ok; @@ -662,7 +715,7 @@ mnesia_start(Opts, Nodes) -> throw({skip, ?F("Failed create mnesia schema: ~p", [SchemaReason])}) end, - ?PRINT2("mnesia_start -> start mnesia", []), + ?IPRINT("mnesia_start -> start mnesia", []), ?line case application:start(mnesia) of ok -> ok; @@ -672,19 +725,19 @@ mnesia_start(Opts, Nodes) -> throw({skip, ?F("Failed starting mnesia: ~p", [StartReason])}) end, - ?PRINT2("mnesia_start -> mnesia started", []), + ?IPRINT("mnesia_start -> mnesia started", []), ok. mnesia_stop() -> - ?PRINT2("mnesia_stop -> try stop mnesia when:" + ?IPRINT("mnesia_stop -> try stop mnesia when:" "~n Loaded: ~p" "~n Running: ~p", [apps_loaded(), apps_running()]), application:stop(mnesia), - ?PRINT2("mnesia_stop -> try unload mnesia when" + ?IPRINT("mnesia_stop -> try unload mnesia when" "~n Loaded: ~p" "~n Running: ~p", [apps_loaded(), apps_running()]), application:unload(mnesia), - ?PRINT2("mnesia_stop -> done when:" + ?IPRINT("mnesia_stop -> done when:" "~n Loaded: ~p" "~n Running: ~p", [apps_loaded(), apps_running()]), ok. @@ -856,19 +909,19 @@ display_memory_usage(MibsPid) -> MibDbSize = key1search([db_memory,mib], MibsInfo), NodeDbSize = key1search([db_memory,node], MibsInfo), TreeDbSize = key1search([db_memory,tree], MibsInfo), - ?INF("Symbolic store memory usage: " - "~n Process memory size: ~p" - "~n Db size: ~p" - "~n" - "~nMib server memory usage: " - "~n Tree size: ~p" - "~n Process memory size: ~p" - "~n Mib db size: ~p" - "~n Node db size: ~p" - "~n Tree db size: ~p" - "~n", - [SymProcSize, DbSize, - TreeSize, MibsProcMem, MibDbSize, NodeDbSize, TreeDbSize]). + ?IPRINT("Symbolic store memory usage: " + "~n Process memory size: ~p" + "~n Db size: ~p" + "~n" + "~nMib server memory usage: " + "~n Tree size: ~p" + "~n Process memory size: ~p" + "~n Mib db size: ~p" + "~n Node db size: ~p" + "~n Tree db size: ~p" + "~n", + [SymProcSize, DbSize, + TreeSize, MibsProcMem, MibDbSize, NodeDbSize, TreeDbSize]). key1search([], Res) -> Res; diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl index c0da47dc4c..6a4c582f36 100644 --- a/lib/snmp/test/snmp_agent_test_lib.erl +++ b/lib/snmp/test/snmp_agent_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2019. All Rights Reserved. +%% Copyright Ericsson AB 2005-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -122,8 +122,8 @@ init_all(Config) when is_list(Config) -> - ?LOG("init_all -> entry with" - "~n Config: ~p",[Config]), + ?IPRINT("init_all -> entry with" + "~n Config: ~p",[Config]), %% -- %% Start nodes @@ -300,7 +300,7 @@ try_test(TcRunMod, TcRunFunc, TcRunArgs, TcRunOpts) -> %% process as well. tc_try(N, M, F, A) -> - ?PRINT2("tc_try -> entry with" + ?IPRINT("tc_try -> entry with" "~n N: ~p" "~n M: ~p" "~n F: ~p" @@ -312,32 +312,32 @@ tc_try(N, M, F, A) -> get()]), case net_adm:ping(N) of pong -> - ?PRINT2("tc_try -> ~p still running - start runner~n", [N]), + ?IPRINT("tc_try -> ~p still running - start runner~n", [N]), OldFlag = trap_exit(true), % Make sure we catch it Runner = spawn_link(N, ?MODULE, tc_wait, [self(), get(), M, F, A]), await_tc_runner_started(Runner, OldFlag), await_tc_runner_done(Runner, OldFlag); pang -> - ?EPRINT2("tc_try -> ~p *not* running~n", [N]), + ?WPRINT("tc_try -> ~p *not* running~n", [N]), skip({node_not_running, N}) end. await_tc_runner_started(Runner, OldFlag) -> - ?PRINT2("await tc-runner (~p) start ack~n", [Runner]), + ?IPRINT("await tc-runner (~p) start ack~n", [Runner]), receive {'EXIT', Runner, Reason} -> - ?EPRINT2("TC runner start failed: " - "~n ~p~n", [Reason]), + ?EPRINT("TC runner start failed: " + "~n ~p~n", [Reason]), exit({tx_runner_start_failed, Reason}); {tc_runner_started, Runner} -> - ?PRINT2("TC runner start acknowledged~n"), + ?IPRINT("TC runner start acknowledged~n"), ok after 10000 -> %% We should *really* not have to wait this long, but... trap_exit(OldFlag), unlink_and_flush_exit(Runner), - RunnerInfo = process_info(Runner), - ?EPRINT2("TC runner start timeout: " - "~n ~p", [RunnerInfo]), + RunnerInfo = ?PINFO(Runner), + ?EPRINT("TC runner start timeout: " + "~n ~p", [RunnerInfo]), %% If we don't get a start ack within 10 seconds, we are f*ed exit(Runner, kill), exit({tc_runner_start, timeout, RunnerInfo}) @@ -352,18 +352,18 @@ await_tc_runner_done(Runner, OldFlag) -> SysEvs = snmp_test_global_sys_monitor:events(), if (SysEvs =:= []) -> - ?EPRINT2("TC runner failed: " - "~n ~p~n", [Reason]), + ?EPRINT("TC runner failed: " + "~n ~p~n", [Reason]), exit({tx_runner_failed, Reason}); true -> - ?EPRINT2("TC runner failed when we got system events: " - "~n Reason: ~p" - "~n Sys Events: ~p" - "~n", [Reason, SysEvs]), + ?WPRINT("TC runner failed when we got system events: " + "~n Reason: ~p" + "~n Sys Events: ~p" + "~n", [Reason, SysEvs]), skip([{reason, Reason}, {system_events, SysEvs}]) end; {tc_runner_done, Runner, {'EXIT', {skip, Reason}}, Loc} -> - ?PRINT2("call -> done with skip: " + ?WPRINT("call -> done with skip: " "~n Reason: ~p" "~n Loc: ~p" "~n", [Reason, Loc]), @@ -372,7 +372,7 @@ await_tc_runner_done(Runner, OldFlag) -> put(test_server_loc, Loc), skip(Reason); {tc_runner_done, Runner, {'EXIT', Rn}, Loc} -> - ?PRINT2("call -> done with exit: " + ?EPRINT("call -> done with exit: " "~n Rn: ~p" "~n Loc: ~p" "~n", [Rn, Loc]), @@ -409,7 +409,7 @@ unlink_and_flush_exit(Pid) -> end. tc_wait(From, Env, M, F, A) -> - ?PRINT2("tc_wait -> entry with" + ?IPRINT("tc_wait -> entry with" "~n From: ~p" "~n Env: ~p" "~n M: ~p" @@ -417,9 +417,9 @@ tc_wait(From, Env, M, F, A) -> "~n A: ~p", [From, Env, M, F, A]), From ! {tc_runner_started, self()}, lists:foreach(fun({K,V}) -> put(K,V) end, Env), - ?PRINT2("tc_wait -> env set - now run tc~n"), + ?IPRINT("tc_wait -> env set - now run tc~n"), Res = (catch apply(M, F, A)), - ?PRINT2("tc_wait -> tc run done: " + ?IPRINT("tc_wait -> tc run done: " "~n ~p" "~n", [Res]), From ! {tc_runner_done, self(), Res, get(test_server_loc)}, @@ -437,7 +437,7 @@ tc_wait(From, Env, M, F, A) -> end. tc_run(Mod, Func, Args, Opts) -> - ?PRINT2("tc_run -> entry with" + ?IPRINT("tc_run -> entry with" "~n Mod: ~p" "~n Func: ~p" "~n Args: ~p" @@ -456,7 +456,7 @@ tc_run(Mod, Func, Args, Opts) -> ?DBG("tc_run -> Crypto: ~p", [_CryptoRes]), StdM = join(code:priv_dir(snmp), "mibs") ++ "/", Vsn = get(vsn), - ?PRINT2("tc_run -> config:" + ?IPRINT("tc_run -> config:" "~n M: ~p" "~n Vsn: ~p" "~n Dir: ~p" @@ -469,7 +469,7 @@ tc_run(Mod, Func, Args, Opts) -> "~n", [M,Vsn,Dir,User,SecLevel,EngineID,CtxEngineID,Community,StdM]), case snmp_test_mgr:start([%% {agent, snmp_test_lib:hostname()}, {packet_server_debug, true}, - {debug, true}, + {debug, false}, {agent, get(master_host)}, {ipfamily, get(ipfamily)}, {agent_udp, 4000}, @@ -487,7 +487,7 @@ tc_run(Mod, Func, Args, Opts) -> {ok, _Pid} -> case (catch apply(Mod, Func, Args)) of {'EXIT', {skip, Reason}} -> - ?EPRINT2("apply skip detected: " + ?WPRINT("apply skip detected: " "~n ~p", [Reason]), (catch snmp_test_mgr:stop()), ?SKIP(Reason); @@ -500,11 +500,11 @@ tc_run(Mod, Func, Args, Opts) -> (catch snmp_test_mgr:stop()), if (SysEvs =:= []) -> - ?EPRINT2("TC runner failed: " - "~n ~p~n", [Reason]), + ?EPRINT("TC runner failed: " + "~n ~p~n", [Reason]), ?FAIL({apply_failed, {Mod, Func, Args}, Reason}); true -> - ?EPRINT2("apply exit catched when we got system events: " + ?WPRINT("apply exit catched when we got system events: " "~n Reason: ~p" "~n Sys Events: ~p" "~n", [Reason, SysEvs]), @@ -516,14 +516,14 @@ tc_run(Mod, Func, Args, Opts) -> end; {error, Reason} -> - ?EPRINT2("Failed starting (test) manager: " - "~n ~p", [Reason]), + ?EPRINT("Failed starting (test) manager: " + "~n ~p", [Reason]), (catch snmp_test_mgr:stop()), ?line ?FAIL({mgr_start_error, Reason}); Err -> - ?EPRINT2("Failed starting (test) manager: " - "~n ~p", [Err]), + ?EPRINT("Failed starting (test) manager: " + "~n ~p", [Err]), (catch snmp_test_mgr:stop()), ?line ?FAIL({mgr_start_failure, Err}) end. @@ -570,10 +570,10 @@ start_agent(Config, Vsns) -> start_agent(Config, Vsns, []). start_agent(Config, Vsns, Opts) -> - ?LOG("start_agent -> entry (~p) with" - "~n Config: ~p" - "~n Vsns: ~p" - "~n Opts: ~p", [node(), Config, Vsns, Opts]), + ?IPRINT("start_agent -> entry (~p) with" + "~n Config: ~p" + "~n Vsns: ~p" + "~n Opts: ~p", [node(), Config, Vsns, Opts]), ?line AgentLogDir = ?config(agent_log_dir, Config), ?line AgentConfDir = ?config(agent_conf_dir, Config), @@ -603,17 +603,17 @@ start_agent(Config, Vsns, Opts) -> process_flag(trap_exit,true), - ?PRINT2("start_agent -> try start snmp app supervisor", []), + ?IPRINT("start_agent -> try start snmp app supervisor", []), {ok, AppSup} = snmp_app_sup:start_link(), unlink(AppSup), ?DBG("start_agent -> snmp app supervisor: ~p", [AppSup]), - ?PRINT2("start_agent -> try start master agent",[]), + ?IPRINT("start_agent -> try start master agent",[]), ?line Sup = start_sup(Env), ?line unlink(Sup), ?DBG("start_agent -> snmp supervisor: ~p", [Sup]), - ?PRINT2("start_agent -> try (rpc) start sub agent on ~p", [SaNode]), + ?IPRINT("start_agent -> try (rpc) start sub agent on ~p", [SaNode]), ?line SaDir = ?config(sa_dir, Config), ?line {ok, Sub} = start_sub_sup(SaNode, SaDir), ?DBG("start_agent -> done", []), @@ -813,36 +813,33 @@ merge_agent_options([{Key, _Value} = Opt|Opts], Options) -> stop_agent(Config) when is_list(Config) -> - ?PRINT2("stop_agent -> entry with" + ?IPRINT("stop_agent -> entry with" "~n Config: ~p",[Config]), %% Stop the sub-agent (the agent supervisor) {SubSup, SubPar} = ?config(snmp_sub, Config), - ?PRINT2("stop_agent -> attempt to stop sub agent (~p)" + ?IPRINT("stop_agent -> attempt to stop sub agent (~p)" "~n Sub Sup info: " "~n ~p" "~n Sub Par info: " "~n ~p", - [SubSup, - (catch process_info(SubSup)), - (catch process_info(SubPar))]), + [SubSup, ?PINFO(SubSup), ?PINFO(SubPar)]), stop_sup(SubSup, SubPar), Config2 = lists:keydelete(snmp_sub, 1, Config), %% Stop the master-agent (the top agent supervisor) {MasterSup, MasterPar} = ?config(snmp_sup, Config), - ?PRINT2("stop_agent -> attempt to stop master agent (~p)" + ?IPRINT("stop_agent -> attempt to stop master agent (~p)" "~n Master Sup: " "~n ~p" "~n Master Par: " "~n ~p" "~n Agent Info: " "~n ~p", - [MasterSup, - (catch process_info(MasterSup)), - (catch process_info(MasterPar)), + [MasterSup, + ?PINFO(MasterSup), ?PINFO(MasterPar), agent_info(MasterSup)]), stop_sup(MasterSup, MasterPar), Config3 = lists:keydelete(snmp_sup, 1, Config2), @@ -850,25 +847,24 @@ stop_agent(Config) when is_list(Config) -> %% Stop the top supervisor (of the snmp app) AppSup = ?config(snmp_app_sup, Config), - ?PRINT2("stop_agent -> attempt to app sup ~p" + ?IPRINT("stop_agent -> attempt to app sup ~p" "~n App Sup: ~p", - [AppSup, - (catch process_info(AppSup))]), + [AppSup, ?PINFO(AppSup)]), Config4 = lists:keydelete(snmp_app_sup, 1, Config3), - ?PRINT2("stop_agent -> done", []), + ?IPRINT("stop_agent -> done", []), Config4. start_sup(Env) -> case (catch snmp_app_sup:start_agent(normal, Env)) of {ok, S} -> - ?DBG("start_agent -> started, Sup: ~p",[S]), + ?DBG("start_agent -> started, Sup: ~p", [S]), S; Else -> - ?DBG("start_agent -> unknown result: ~n~p",[Else]), + ?EPRINT("start_agent -> unknown result: ~n~p", [Else]), %% Get info about the apps we depend on ?FAIL({start_failed, Else, ?IS_MNESIA_RUNNING()}) end. @@ -876,18 +872,18 @@ start_sup(Env) -> stop_sup(Pid, _) when (node(Pid) =:= node()) -> case (catch process_info(Pid)) of PI when is_list(PI) -> - ?LOG("stop_sup -> attempt to stop ~p", [Pid]), + ?IPRINT("stop_sup -> attempt to stop ~p", [Pid]), Ref = erlang:monitor(process, Pid), exit(Pid, kill), await_stopped(Pid, Ref); {'EXIT', _Reason} -> - ?LOG("stop_sup -> ~p not running", [Pid]), + ?IPRINT("stop_sup -> ~p not running", [Pid]), ok end; stop_sup(Pid, _) -> - ?LOG("stop_sup -> attempt to stop ~p", [Pid]), + ?IPRINT("stop_sup -> attempt to stop ~p", [Pid]), Ref = erlang:monitor(process, Pid), - ?LOG("stop_sup -> Ref: ~p", [Ref]), + ?IPRINT("stop_sup -> Ref: ~p", [Ref]), exit(Pid, kill), await_stopped(Pid, Ref). @@ -897,7 +893,7 @@ await_stopped(Pid, Ref) -> ?DBG("received down message for ~p", [Pid]), ok after 10000 -> - ?INF("await_stopped -> timeout for ~p",[Pid]), + ?EPRINT("await_stopped -> timeout for ~p",[Pid]), erlang:demonitor(Ref), ?FAIL({failed_stop,Pid}) end. @@ -1076,7 +1072,7 @@ io_format_expect(F) -> io_format_expect(F, []). io_format_expect(F, A) -> - ?PRINT2("EXPECT " ++ F, A). + ?IPRINT("EXPECT " ++ F, A). do_expect(Expect) when is_atom(Expect) -> @@ -1518,7 +1514,7 @@ get_req(Id, Vars) -> get_next_req(Vars) -> ?DBG("get_next_req -> entry with" - "~n Vars: ~p",[Vars]), + "~n Vars: ~p", [Vars]), snmp_test_mgr:gn(Vars), ?DBG("get_next_req -> await response",[]), Response = snmp_test_mgr:receive_response(), @@ -1529,42 +1525,37 @@ get_next_req(Vars) -> %% --- start and stop nodes --- start_node(Name) -> - ?LOG("start_node -> entry with" - "~n Name: ~p" - "~n when" - "~n hostname of this node: ~p", - [Name, list_to_atom(?HOSTNAME(node()))]), + ?IPRINT("start_node -> entry with" + "~n Name: ~p" + "~n when" + "~n hostname of this node: ~p", + [Name, list_to_atom(?HOSTNAME(node()))]), + Pa = filename:dirname(code:which(?MODULE)), - ?DBG("start_node -> Pa: ~p",[Pa]), - - Args = case init:get_argument('CC_TEST') of - {ok, [[]]} -> - " -pa /clearcase/otp/libraries/snmp/ebin "; - {ok, [[Path]]} -> - " -pa " ++ Path; - error -> - "" - end, - %% Do not use start_link!!! (the proc that calls this one is tmp) - ?DBG("start_node -> Args: ~p~n", [Args]), - A = Args ++ " -pa " ++ Pa ++ + ?DBG("start_node -> Pa: ~p", [Pa]), + + A = " -pa " ++ Pa ++ " -s " ++ atom_to_list(snmp_test_sys_monitor) ++ " start" ++ " -s global sync", - case (catch ?START_NODE(Name, A)) of + case ?START_NODE(Name, A) of {ok, Node} -> - %% Tell the test_server to not clean up things it never started. - ?DBG("start_node -> Node: ~p",[Node]), + ?DBG("start_node -> Node: ~p", [Node]), global:sync(), {ok, Node}; + {error, Reason} -> + ?WPRINT("start_node -> failed starting node ~p:" + "~n Reason: ~p", [Name, Reason]), + ?line ?SKIP({failed_start_node, Reason}); Else -> - ?ERR("start_node -> failed with(other): Else: ~p",[Else]), + ?EPRINT("start_node -> failed starting node ~p:" + "~n ~p", [Name, Else]), ?line ?FAIL(Else) end. stop_node(Node) -> - ?LOG("stop_node -> Node: ~p",[Node]), - rpc:cast(Node, erlang, halt, []). + ?IPRINT("stop_node -> Node: ~p", [Node]), + ?STOP_NODE(Node). %%%----------------------------------------------------------------- @@ -1575,14 +1566,14 @@ config(Vsns, MgrDir, AgentConfDir, MIp, AIp) -> config(Vsns, MgrDir, AgentConfDir, MIp, AIp, inet). config(Vsns, MgrDir, AgentConfDir, MIp, AIp, IpFamily) -> - ?LOG("config -> entry with" - "~n Vsns: ~p" - "~n MgrDir: ~p" - "~n AgentConfDir: ~p" - "~n MIp: ~p" - "~n AIp: ~p" - "~n IpFamily: ~p", - [Vsns, MgrDir, AgentConfDir, MIp, AIp, IpFamily]), + ?IPRINT("config -> entry with" + "~n Vsns: ~p" + "~n MgrDir: ~p" + "~n AgentConfDir: ~p" + "~n MIp: ~p" + "~n AIp: ~p" + "~n IpFamily: ~p", + [Vsns, MgrDir, AgentConfDir, MIp, AIp, IpFamily]), ?line {Domain, ManagerAddr} = case IpFamily of inet6 -> @@ -1753,8 +1744,8 @@ rewrite_target_addr_conf(Dir, NewPort) -> {ok, _} -> ok; {error, _R} -> - ?ERR("failure reading file info of " - "target address config file: ~p", [_R]), + ?WPRINT("failure reading file info of " + "target address config file: ~p", [_R]), ok end, @@ -1778,10 +1769,10 @@ rewrite_target_addr_conf_check(O) -> rewrite_target_addr_conf2(NewPort, {Name, Ip, _Port, Timeout, Retry, "std_trap", EngineId}) -> - ?LOG("rewrite_target_addr_conf2 -> entry with std_trap",[]), + ?IPRINT("rewrite_target_addr_conf2 -> entry with std_trap",[]), {Name,Ip,NewPort,Timeout,Retry,"std_trap",EngineId}; rewrite_target_addr_conf2(_NewPort,O) -> - ?LOG("rewrite_target_addr_conf2 -> entry with " + ?IPRINT("rewrite_target_addr_conf2 -> entry with " "~n O: ~p",[O]), O. @@ -1835,12 +1826,12 @@ display_memory_usage() -> MibDbSize = key1search([db_memory,mib], Info), NodeDbSize = key1search([db_memory,node], Info), TreeDbSize = key1search([db_memory,tree], Info), - ?INF("Memory usage: " - "~n Tree size: ~p" - "~n Process memory size: ~p" - "~n Mib db size: ~p" - "~n Node db size: ~p" - "~n Tree db size: ~p", + ?IPRINT("Memory usage: " + "~n Tree size: ~p" + "~n Process memory size: ~p" + "~n Mib db size: ~p" + "~n Node db size: ~p" + "~n Tree db size: ~p", [TreeSize, ProcMem, MibDbSize, NodeDbSize, TreeDbSize]). key1search([], Res) -> @@ -1876,51 +1867,3 @@ join(Dir, File) -> skip(R) -> exit({skip, R}). -%% await_pdu(To) -> -%% await_response(To, pdu). -%% -%% await_trap(To) -> -%% await_response(To, trap). -%% -%% await_any(To) -> -%% await_response(To, any). -%% -%% -%% await_response(To, What) -> -%% await_response(To, What, []). -%% -%% await_response(To, What, Stuff) when is_integer(To) andalso (To >= 0) -> -%% T = t(), -%% receive -%% {snmp_pdu, PDU} when is_record(Trap, pdu) andalso (What =:= pdu) -> -%% {ok, PDU}; -%% {snmp_pdu, Trap} is_when record(Trap, trappdu) andalso (What =:= trap) -> -%% {ok, Trap}; -%% Any when What =:= any -> -%% {ok, Any}; -%% Any -> -%% %% Recalc time -%% NewTo = To - (t() - T) -%% await_reponse(NewTo, What, [{NewTo, Any}|Stuff]) -%% after To -> -%% {error, {timeout, Stuff}} -%% end; -%% await_response(_, Stuff) -> -%% {error, {timeout, Stuff}}. -%% -%% -%% t() -> -%% {A,B,C} = os:timestamp(), -%% A*1000000000+B*1000+(C div 1000). -%% -%% -%% timeout() -> -%% timeout(os:type()). -%% -%% timeout(_) -> 3500. - - -%% Time in milli seconds -%% t() -> -%% {A,B,C} = os:timestamp(), -%% A*1000000000+B*1000+(C div 1000). diff --git a/lib/snmp/test/snmp_compiler_SUITE.erl b/lib/snmp/test/snmp_compiler_SUITE.erl index aeb055742e..f4ba914919 100644 --- a/lib/snmp/test/snmp_compiler_SUITE.erl +++ b/lib/snmp/test/snmp_compiler_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2019. All Rights Reserved. +%% Copyright Ericsson AB 2003-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -106,26 +106,48 @@ tickets_cases() -> init_per_suite(Config0) when is_list(Config0) -> - ?DBG("init_per_suite -> entry with" - "~n Config0: ~p", [Config0]), + ?IPRINT("init_per_suite -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), - Config1 = snmp_test_lib:init_suite_top_dir(?MODULE, Config0), - Config2 = snmp_test_lib:fix_data_dir(Config1), + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; - %% Mib-dirs - %% data_dir is trashed by the test-server / common-test - %% so there is no point in fixing it... - MibDir = snmp_test_lib:lookup(data_dir, Config2), - StdMibDir = filename:join([code:priv_dir(snmp), "mibs"]), + Config1 when is_list(Config1) -> + Config2 = snmp_test_lib:init_suite_top_dir(?MODULE, Config1), + Config3 = snmp_test_lib:fix_data_dir(Config2), - [{mib_dir, MibDir}, {std_mib_dir, StdMibDir} | Config2]. + %% Mib-dirs + %% data_dir is trashed by the test-server / common-test + %% so there is no point in fixing it... + MibDir = snmp_test_lib:lookup(data_dir, Config3), + StdMibDir = filename:join([code:priv_dir(snmp), "mibs"]), -end_per_suite(Config) when is_list(Config) -> + Config4 = [{mib_dir, MibDir}, {std_mib_dir, StdMibDir} | Config3], + + %% We need a monitor on this node also + snmp_test_sys_monitor:start(), - ?DBG("end_per_suite -> entry with" - "~n Config: ~p", [Config]), + snmp_test_mgr_counter_server:start(), - Config. + ?IPRINT("init_per_suite -> end when" + "~n Config: ~p", [Config4]), + + Config4 + end. + + +end_per_suite(Config0) when is_list(Config0) -> + ?IPRINT("end_per_suite -> entry with" + "~n Config0: ~p", [Config0]), + + snmp_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + ?IPRINT("end_per_suite -> end"), + + Config1. %% @@ -165,7 +187,8 @@ end_per_testcase(_Case, Config) when is_list(Config) -> description(suite) -> []; description(Config) when is_list(Config) -> put(tname,desc), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Dir = ?config(case_top_dir, Config), Filename = join(Dir,"test"), @@ -179,14 +202,14 @@ description(Config) when is_list(Config) -> {warnings, false}, {description, false}]), MIB1 = read_mib(MibBinName), - %% io:format("description -> MIB1: ~n~p~n", [MIB1]), + %% ?IPRINT("description -> MIB1: ~n~p~n", [MIB1]), check_mib(MIB1#mib.mes, Oid, undefined), ?line {ok,_} = snmpc:compile(MibSrcName, [{outdir, Dir}, {group_check, false}, {warnings, false}, {description, true}]), MIB2 = read_mib(MibBinName), - %% io:format("description -> MIB2: ~n~p~n", [MIB2]), + %% ?IPRINT("description -> MIB2: ~n~p~n", [MIB2]), check_mib(MIB2#mib.mes, Oid, Desctext), %% Cleanup @@ -200,7 +223,8 @@ description(Config) when is_list(Config) -> oid_conflicts(suite) -> []; oid_conflicts(Config) when is_list(Config) -> put(tname,oid_conflicts), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Dir = ?config(case_top_dir, Config), Mib = join(Dir,"TESTv2.mib"), @@ -232,7 +256,8 @@ agent_capabilities(suite) -> []; agent_capabilities(Config) when is_list(Config) -> put(tname,agent_capabilities), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), SnmpPrivDir = which_priv_dir(snmp), SnmpMibsDir = join(SnmpPrivDir, "mibs"), @@ -253,11 +278,10 @@ agent_capabilities(Config) when is_list(Config) -> ?line {ok, Mib2} = snmp_misc:read_mib(MibFile2), MEDiff = Mib2#mib.mes -- Mib1#mib.mes, %% This is a rather pathetic test, but it is somthing... - io:format("agent_capabilities -> " - "~n MEDiff: ~p" - "~n Mib1: ~p" - "~n Mib2: ~p" - "~n", [MEDiff, Mib1, Mib2]), + ?IPRINT("agent_capabilities -> " + "~n MEDiff: ~p" + "~n Mib1: ~p" + "~n Mib2: ~p", [MEDiff, Mib1, Mib2]), case length(MEDiff) of 2 -> ok; @@ -272,8 +296,9 @@ agent_capabilities(Config) when is_list(Config) -> module_compliance(suite) -> []; module_compliance(Config) when is_list(Config) -> - put(tname,module_compliance), - p("starting with Config: ~p~n", [Config]), + put(tname, module_compliance), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), SnmpPrivDir = which_priv_dir(snmp), SnmpMibsDir = join(SnmpPrivDir, "mibs"), @@ -294,11 +319,10 @@ module_compliance(Config) when is_list(Config) -> ?line {ok, Mib2} = snmp_misc:read_mib(MibFile2), MEDiff = Mib2#mib.mes -- Mib1#mib.mes, %% This is a rather pathetic test, but it is somthing... - io:format("module_compliance -> " - "~n MEDiff: ~p" - "~n Mib1: ~p" - "~n Mib2: ~p" - "~n", [MEDiff, Mib1, Mib2]), + ?IPRINT("module_compliance -> " + "~n MEDiff: ~p" + "~n Mib1: ~p" + "~n Mib2: ~p", [MEDiff, Mib1, Mib2]), case length(MEDiff) of 1 -> ok; @@ -314,7 +338,8 @@ warnings_as_errors(suite) -> ["OTP-9437"]; warnings_as_errors(Config) when is_list(Config) -> put(tname,warnings_as_errors), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Dir = ?config(case_top_dir, Config), MibDir = ?config(mib_dir, Config), MibFile = join(MibDir, "OTP8574-MIB.mib"), @@ -338,14 +363,16 @@ otp_6150(suite) -> []; otp_6150(Config) when is_list(Config) -> put(tname, otp6150), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Dir = ?config(case_top_dir, Config), MibDir = ?config(mib_dir, Config), MibFile = join(MibDir, "ERICSSON-TOP-MIB.mib"), ?line {ok, Mib} = snmpc:compile(MibFile, [{outdir, Dir}, {verbosity, trace}]), - io:format("otp_6150 -> Mib: ~n~p~n", [Mib]), + ?IPRINT("otp_6150 -> Mib: " + "~n ~p", [Mib]), ok. @@ -355,29 +382,30 @@ otp_8574(suite) -> []; otp_8574(Config) when is_list(Config) -> put(tname, otp8574), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Dir = ?config(case_top_dir, Config), MibDir = ?config(mib_dir, Config), MibFile = join(MibDir, "OTP8574-MIB.mib"), - p("ensure compile fail without relaxed assign check"), + ?IPRINT("ensure compile fail without relaxed assign check"), case snmpc:compile(MibFile, [{group_check, false}, {outdir, Dir}]) of {error, compilation_failed} -> - p("with relaxed assign check MIB compiles with warning"), + ?IPRINT("with relaxed assign check MIB compiles with warning"), case snmpc:compile(MibFile, [{group_check, false}, {outdir, Dir}, relaxed_row_name_assign_check]) of {ok, _Mib} -> ok; {error, Reason} -> - p("unexpected compile failure: " - "~n Reason: ~p", [Reason]), + ?EPRINT("unexpected compile failure: " + "~n Reason: ~p", [Reason]), exit({unexpected_compile_failure, Reason}) end; {ok, _} -> - p("unexpected compile success"), + ?EPRINT("unexpected compile success"), exit(unexpected_compile_success) end. @@ -388,7 +416,8 @@ otp_8595(suite) -> []; otp_8595(Config) when is_list(Config) -> put(tname, otp8595), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Dir = ?config(case_top_dir, Config), MibDir = ?config(mib_dir, Config), @@ -397,7 +426,7 @@ otp_8595(Config) when is_list(Config) -> snmpc:compile(MibFile, [{outdir, Dir}, {verbosity, trace}, {group_check, false}]), - p("Mib: ~n~p~n", [Mib]), + ?IPRINT("Mib: ~n~p~n", [Mib]), ok. @@ -407,14 +436,16 @@ otp_10799(suite) -> []; otp_10799(Config) when is_list(Config) -> put(tname, otp10799), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Dir = ?config(case_top_dir, Config), MibDir = ?config(mib_dir, Config), MibFile = join(MibDir, "OTP10799-MIB.mib"), ?line {ok, Mib} = snmpc:compile(MibFile, [{outdir, Dir}, {verbosity, trace}]), - p("Mib: ~n~p~n", [Mib]), + ?IPRINT("Mib: " + "~n ~p", [Mib]), ok. @@ -424,7 +455,8 @@ otp_10808(suite) -> []; otp_10808(Config) when is_list(Config) -> put(tname, otp10808), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Dir = ?config(case_top_dir, Config), MibDir = ?config(mib_dir, Config), @@ -433,7 +465,8 @@ otp_10808(Config) when is_list(Config) -> snmpc:compile(MibFile, [{outdir, Dir}, {verbosity, trace}, {group_check, false}]), - p("Mib: ~n~p~n", [Mib]), + ?IPRINT("Mib: " + "~n ~p", [Mib]), ok. @@ -443,7 +476,8 @@ otp_14145(suite) -> []; otp_14145(Config) when is_list(Config) -> put(tname, otp14145), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Dir = ?config(case_top_dir, Config), MibDir = ?config(mib_dir, Config), @@ -454,7 +488,8 @@ otp_14145(Config) when is_list(Config) -> {verbosity, trace}, {group_check, false}, module_compliance]), - p("Mib: ~n~p~n", [MibBin]), + ?IPRINT("Mib: " + "~n ~p", [MibBin]), MIB = read_mib(MibBin), Oid = [1,3,6,1,2,1,67,4], check_mib(MIB#mib.mes, Oid, undefined), @@ -467,7 +502,8 @@ otp_13014(suite) -> []; otp_13014(Config) when is_list(Config) -> put(tname, otp13014), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Dir = ?config(case_top_dir, Config), MibDir = ?config(mib_dir, Config), @@ -478,7 +514,8 @@ otp_13014(Config) when is_list(Config) -> {verbosity, log}, {group_check, false}, module_compliance]), - p("Mib: ~n~p~n", [MibBin]), + ?IPRINT("Mib: " + "~n ~p", [MibBin]), #mib{mes = MEs} = read_mib(MibBin), Oid = [1,0,8802,1,1,2,1,1,7], #me{ @@ -494,20 +531,23 @@ otp_13014(Config) when is_list(Config) -> TableInfo, ok. + %%====================================================================== otp_14196(suite) -> []; otp_14196(Config) when is_list(Config) -> put(tname, otp14196), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Dir = ?config(case_top_dir, Config), MibDir = ?config(mib_dir, Config), MibFile = join(MibDir, "OTP14196-MIB.mib"), ?line {ok, Mib} = snmpc:compile(MibFile, [{outdir, Dir}, {verbosity, trace}]), - p("Mib: ~n~p~n", [Mib]), + ?IPRINT("Mib: " + "~n ~p", [Mib]), ok. @@ -517,7 +557,8 @@ augments_extra_info(suite) -> []; augments_extra_info(Config) when is_list(Config) -> put(tname, augments_extra_info), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Dir = ?config(case_top_dir, Config), MibDir = ?config(mib_dir, Config), @@ -527,28 +568,30 @@ augments_extra_info(Config) when is_list(Config) -> snmpc:compile(Test2File, [{outdir, Dir}, {verbosity, silence}, {group_check, false}]), - io:format("Test2BinFile: ~n~p~n", [Test2BinFile]), + ?IPRINT("Test2BinFile: " + "~n ~p", [Test2BinFile]), ?line {ok, Test3BinFile} = snmpc:compile(Test3File, [{i, [MibDir]}, {outdir, Dir}, {verbosity, silence}, {group_check, true}]), - io:format("Test3BinFile: ~n~p~n", [Test3BinFile]), + ?IPRINT("Test3BinFile: " + "~n ~p", [Test3BinFile]), {ok, Test3Mib} = snmp_misc:read_mib(Test3BinFile), - io:format("Test3Mib: ~n~p~n", [Test3Mib]), + ?IPRINT("Test3Mib: " + "~n ~p", [Test3Mib]), %% There is only one table in this mib #mib{table_infos = [{TableName, TI}]} = Test3Mib, - io:format("TableName: ~p" - "~n Table Info: ~p" - "~n", [TableName, TI]), + ?IPRINT("TableName: ~p" + "~n Table Info: ~p", [TableName, TI]), #table_info{nbr_of_cols = 4, defvals = DefVals, not_accessible = [2,4], index_types = {augments, {tEntry, undefined}}, first_accessible = 1} = TI, - io:format("Table info: ~p" - "~n DefVals: ~p" - "~n", [TableName, DefVals]), + ?IPRINT("Table info: ~p" + "~n DefVals: ~p" + "~n", [TableName, DefVals]), ok. @@ -750,21 +793,3 @@ which_priv_dir(App) -> join(A,B) -> filename:join(A,B). - -%% ------ - -%% p(F) -> -%% p(F, []). - -p(F) -> - p(F, []). - -p(F, A) -> - p(get(tname), F, A). - -p(TName, F, A) -> - io:format("*** [~w][~s] ***" - "~n" ++ F ++ "~n", [TName, formated_timestamp()|A]). - -formated_timestamp() -> - snmp_test_lib:formated_timestamp(). diff --git a/lib/snmp/test/snmp_conf_SUITE.erl b/lib/snmp/test/snmp_conf_SUITE.erl index bd8375b400..7d60485060 100644 --- a/lib/snmp/test/snmp_conf_SUITE.erl +++ b/lib/snmp/test/snmp_conf_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2019. All Rights Reserved. +%% Copyright Ericsson AB 2003-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -97,11 +97,34 @@ groups() -> %% ----- %% -init_per_suite(Config) when is_list(Config) -> - Config. +init_per_suite(Config0) when is_list(Config0) -> + ?IPRINT("init_per_suite -> entry with" + "~n Config0: ~p", [Config0]), -end_per_suite(Config) when is_list(Config) -> - Config. + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + %% We need a monitor on this node also + snmp_test_sys_monitor:start(), + + ?IPRINT("init_per_suite -> end when" + "~n Config: ~p", [Config1]), + + Config1 + end. + +end_per_suite(Config0) when is_list(Config0) -> + ?IPRINT("end_per_suite -> entry with" + "~n Config0: ~p", [Config0]), + + snmp_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + ?IPRINT("end_per_suite -> end"), + + Config1. @@ -136,7 +159,7 @@ end_per_testcase(_Case, Config) when is_list(Config) -> check_mandatory(suite) -> []; check_mandatory(Config) when is_list(Config) -> ?P(check_mandatory), - %% d("check_mandatory -> entry"), + %% ?IPRINT("check_mandatory -> entry"), A1 = [{a, hej}, {b, hopp}, {c, 10}, {d, 10101}, {f, 10.88}], B1 = [{a, {value, hejsan}}, {b, mandatory}, @@ -696,8 +719,3 @@ read_files(Config) when is_list(Config) -> %% Internal functions %%====================================================================== -% d(F) -> -% d(F, []). - -% d(F, A) -> -% io:format("~w:" ++ F ++ "~n", [?MODULE|A]). diff --git a/lib/snmp/test/snmp_log_SUITE.erl b/lib/snmp/test/snmp_log_SUITE.erl index 6f9e7f5e49..b63c9e3b34 100644 --- a/lib/snmp/test/snmp_log_SUITE.erl +++ b/lib/snmp/test/snmp_log_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2019. All Rights Reserved. +%% Copyright Ericsson AB 2003-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -67,7 +67,7 @@ %% Internal exports %%---------------------------------------------------------------------- -export([ - log_writer_main/5, + log_writer_main/6, log_reader_main/1, next_seqno/2 ]). @@ -175,7 +175,7 @@ init_per_testcase(Case, Config) when is_list(Config) -> end_per_testcase(_Case, Config) when is_list(Config) -> - ?PRINT2("system events during test: " + ?IPRINT("system events during test: " "~n ~p", [snmp_test_global_sys_monitor:events()]), %% Leave the dirs created above (enable debugging of the test case(s)) @@ -191,7 +191,7 @@ end_per_testcase(_Case, Config) when is_list(Config) -> open_and_close(suite) -> []; open_and_close(Config) when is_list(Config) -> - p(open_and_close), + ?P(open_and_close), put(sname,open_and_close), put(verbosity,trace), Dir = ?config(log_dir, Config), @@ -213,7 +213,7 @@ open_write_and_close1(suite) -> open_write_and_close1(doc) -> "Open a plain (no sequence-numbering) log file"; open_write_and_close1(Config) when is_list(Config) -> - p(open_write_and_close1), + ?P(open_write_and_close1), put(sname,open_write_and_close1), put(verbosity,trace), ?DBG("open_write_and_close1 -> start", []), @@ -232,7 +232,7 @@ open_write_and_close2(suite) -> open_write_and_close2(doc) -> "Open a log file with sequence-numbering explicitly disabled"; open_write_and_close2(Config) when is_list(Config) -> - p(open_write_and_close2), + ?P(open_write_and_close2), put(sname,open_write_and_close2), put(verbosity,trace), ?DBG("open_write_and_close2 -> start", []), @@ -251,7 +251,7 @@ open_write_and_close3(suite) -> open_write_and_close3(doc) -> "Open a log file with sequence-numbering using MFA"; open_write_and_close3(Config) when is_list(Config) -> - p(open_write_and_close3), + ?P(open_write_and_close3), put(sname,open_write_and_close3), put(verbosity,trace), ?DBG("open_write_and_close2 -> start", []), @@ -272,7 +272,7 @@ open_write_and_close4(suite) -> open_write_and_close4(doc) -> "Open a log file with sequence-numbering using fun"; open_write_and_close4(Config) when is_list(Config) -> - p(open_write_and_close4), + ?P(open_write_and_close4), put(sname,open_write_and_close4), put(verbosity,trace), ?DBG("open_write_and_close2 -> start", []), @@ -371,7 +371,7 @@ log_to_io1(suite) -> []; log_to_io1(doc) -> "Log to io from the same process that opened " "and wrote the log"; log_to_io1(Config) when is_list(Config) -> - p(log_to_io1), + ?P(log_to_io1), put(sname,l2i1), put(verbosity,debug), ?DBG("log_to_io1 -> start", []), @@ -426,18 +426,20 @@ log_to_io2(doc) -> "Log to io from a different process than which " "opened and wrote the log"; log_to_io2(Config) when is_list(Config) -> process_flag(trap_exit, true), - p(log_to_io2), + ?P(log_to_io2), put(sname, l2i2), put(verbosity,debug), ?DBG("log_to_io2 -> start", []), Dir = ?config(log_dir, Config), + Factor = ?config(snmp_factor, Config), Name = "snmp_test_l2i2", File = join(Dir, "snmp_test_l2i2.log"), Size = {1024, 10}, Repair = true, ?DBG("log_to_io2 -> create log writer process", []), - ?line {ok, Log, Logger} = log_writer_start(Name, File, Size, Repair), + ?line {ok, Log, Logger} = + log_writer_start(Name, File, Size, Repair, Factor), ?DBG("log_to_io2 -> create log reader process", []), ?line {ok, Reader} = log_reader_start(), @@ -485,7 +487,7 @@ log_to_io2(Config) when is_list(Config) -> log_to_txt1(suite) -> []; log_to_txt1(Config) when is_list(Config) -> - p(log_to_txt1), + ?P(log_to_txt1), put(sname,l2t1), put(verbosity,debug), ?DBG("log_to_txt1 -> start", []), @@ -503,7 +505,7 @@ log_to_txt1(Config) when is_list(Config) -> log_to_txt2(suite) -> []; log_to_txt2(Config) when is_list(Config) -> - p(log_to_txt2), + ?P(log_to_txt2), put(sname,l2t2), put(verbosity,debug), ?DBG("log_to_txt2 -> start", []), @@ -640,11 +642,12 @@ log_to_txt3(doc) -> "opened and wrote the log"; log_to_txt3(Config) when is_list(Config) -> process_flag(trap_exit, true), - p(log_to_txt3), + ?P(log_to_txt3), put(sname,l2t3), put(verbosity,debug), ?DBG("log_to_txt3 -> start", []), Dir = ?config(log_dir, Config), + Factor = ?config(snmp_factor, Config), Name = "snmp_test_l2t3", LogFile = join(Dir, "snmp_test_l2t3.log"), TxtFile = join(Dir, "snmp_test_l2t3.txt"), @@ -656,7 +659,8 @@ log_to_txt3(Config) when is_list(Config) -> Mibs = [join(StdMibDir, "SNMPv2-MIB")], ?DBG("log_to_txt3 -> create log writer process", []), - ?line {ok, Log, Logger} = log_writer_start(Name, LogFile, Size, Repair), + ?line {ok, Log, Logger} = + log_writer_start(Name, LogFile, Size, Repair, Factor), ?DBG("log_to_txt3 -> create log reader process", []), ?line {ok, Reader} = log_reader_start(), @@ -679,9 +683,7 @@ log_to_txt3(Config) when is_list(Config) -> R = snmp_log:log_to_txt(Log, LogFile, Dir, Mibs, TxtFile), T2 = snmp_misc:now(ms), - io:format(user, - "Time converting file: ~w ms~n", - [T2 - T1]), + ?IPRINT("Time converting file: ~w ms", [T2 - T1]), {R, I} end), @@ -693,9 +695,9 @@ log_to_txt3(Config) when is_list(Config) -> ?DBG("log_to_txt3 -> text file size: ~p", [FileSize]), validate_size(FileSize); {Error, Info} -> - ?DBG("log_to_txt3 -> log to txt failed: " - "~n Error: ~p" - "~n Info: ~p", [Error, Info]), + ?EPRINT("log to txt failed: " + "~n Error: ~p" + "~n Info: ~p", [Error, Info]), ?line ?FAIL({log_lo_txt_failed, Error, Info}) end, @@ -705,7 +707,7 @@ log_to_txt3(Config) when is_list(Config) -> ?DBG("log_to_txt3 -> instruct the log reader to stop", []), ?line log_reader_stop(Reader), - ?DBG("log_to_txt3 -> done", []), + ?IPRINT("log_to_txt3 -> done", []), ok. @@ -730,9 +732,9 @@ validate_size(A, B) -> %% Internal functions %%====================================================================== -log_writer_start(Name, File, Size, Repair) -> +log_writer_start(Name, File, Size, Repair, Factor) -> Pid = spawn_link(?MODULE, log_writer_main, - [Name, File, Size, Repair, self()]), + [Name, File, Size, Repair, self(), Factor]), receive {log, Log, Pid} -> {ok, Log, Pid}; @@ -750,7 +752,7 @@ log_writer_stop(Pid) -> receive {'EXIT', Pid, normal} -> _T2 = snmp_misc:now(ms), - ?DBG("it took ~w ms to stop the writer", [_T2 - _T1]), + ?IPRINT("it took ~w ms to stop the writer", [_T2 - _T1]), ok after 60000 -> Msg = receive Any -> Any after 0 -> nothing end, @@ -767,7 +769,7 @@ log_writer_sleep(Pid, Time) -> receive {sleeping, Pid} -> _T2 = snmp_misc:now(ms), - ?DBG("it took ~w ms to put the writer to sleep", [_T2 - _T1]), + ?IPRINT("it took ~w ms to put the writer to sleep", [_T2 - _T1]), ok; {'EXIT', Pid, Reason} -> {error, Reason} @@ -777,13 +779,16 @@ log_writer_sleep(Pid, Time) -> exit({failed_put_writer_to_sleep, timeout, Msg, Info}) end. -log_writer_main(Name, File, Size, Repair, P) -> +log_writer_main(Name, File, Size, Repair, P, Factor) -> process_flag(trap_exit, true), - %% put(sname,log_writer), - %% put(verbosity,trace), + put(tname, "LOG-WRITER"), {ok, Log} = snmp_log:create(Name, File, Size, Repair), P ! {log, Log, self()}, - Msgs = lists:flatten(lists:duplicate(10, messages())), + Msgs = lists:flatten(lists:duplicate(if + (Factor > 10) -> 1; + true -> 10 div Factor + end, + messages())), Addr = ?LOCALHOST(), Port = 162, Logger = fun(Packet) -> @@ -797,39 +802,34 @@ log_writer_main(Name, File, Size, Repair, P) -> log_writer(Log, BatchLogger, P). log_writer(Log, Fun, P) -> - lp("entry"), + ?IPRINT("entry"), receive {stop, P} -> - lp("received stop request"), + ?IPRINT("received stop request"), ok = snmp_log:close(Log), exit(normal); {info, P} -> - lp("received info request"), + ?IPRINT("received info request"), {ok, Info} = snmp_log:info(Log), display_info(Info), log_writer(Log, Fun, P); {sleep, Time, P} -> - lp("received sleep (~w) request", [Time]), + ?IPRINT("received sleep (~w) request", [Time]), P ! {sleeping, self()}, ?SLEEP(Time), - lp("done sleeping"), + ?IPRINT("done sleeping"), log_writer(Log, Fun, P); ELSE -> - io:format("ERROR:logger - received unknown message: " - "~n ~p~n", [ELSE]), + ?EPRINT("Received unknown message: " + "~n ~p", [ELSE]), log_writer(Log, Fun, P) after 1000 -> - lp("log some messages"), + ?IPRINT("log some messages"), To = lists:duplicate(100, 100), lists:foreach(Fun, To), log_writer(Log, Fun, P) end. -lp(F) -> - lp(F, []). - -lp(F, A) -> - io:format(user,"writer [~w] " ++ F ++ "~n", [self()|A]). %% -- @@ -839,7 +839,7 @@ log_reader_start() -> receive {started, Pid} -> _T2 = snmp_misc:now(ms), - ?DBG("it took ~w ms to start the reader", [_T2 - _T1]), + ?IPRINT("it took ~w ms to start the reader", [_T2 - _T1]), {ok, Pid}; {'EXIT', Pid, Reason} -> {error, Reason} @@ -853,7 +853,7 @@ log_reader_stop(Pid) -> receive {'EXIT', Pid, normal} -> _T2 = snmp_misc:now(ms), - ?DBG("it took ~w ms to put the reader to eleep", [_T2 - _T1]), + ?IPRINT("it took ~w ms to stop the reader", [_T2 - _T1]), ok after 1000 -> Msg = receive Any -> Any after 0 -> nothing end, @@ -868,35 +868,28 @@ log_reader_log_to(Pid, LogToFun) when is_function(LogToFun) -> end. log_reader_main(P) -> - put(sname,log_reader), - put(verbosity,trace), + put(tname, "LOG-READER"), P ! {started, self()}, log_reader(P). log_reader(P) -> - rp("entry"), + ?IPRINT("entry"), receive {stop, P} -> - rp("received stop request"), + ?IPRINT("received stop request"), exit(normal); {log_to, F, P} -> - rp("received log_to request"), + ?IPRINT("received log_to request"), Res = F(), - rp("done with log_to - sending reply"), + ?IPRINT("done with log_to - sending reply"), P ! {log_to_reply, Res, self()}, log_reader(P); ELSE -> - io:format("ERROR:reader - received unknown message: " - "~n ~p~n", [ELSE]), + ?EPRINT("Received unknown message: " + "~n ~p", [ELSE]), log_reader(P) end. -rp(F) -> - rp(F, []). - -rp(F, A) -> - io:format(user, "reader [~w] " ++ F ++ "~n", [self()|A]). - %%====================================================================== @@ -1115,23 +1108,6 @@ varbinds([{Oid, Type, Value, Idx}|T], Acc) -> org_index = Idx}, varbinds(T, [Varbind|Acc]). -% enc_message('version-3' = Vsn, Community, Pdu) -> -% ScopedPDU = #scopedPdu{contextEngineID = ContextEngineID, -% contextName = ContextName, -% data = Pdu}, -% NUsmSecParams = -% UsmSecParams#usmSecurityParameters{msgAuthenticationParameters = -% AuthParams}, -% SecBytes = snmp_pdus:enc_usm_security_parameters(NUsmSecParams), -% V3Hdr = #v3_hdr{msgID = MsgID, -% msgMaxSize = AgentMS, -% msgFlags = snmp_misc:mk_msg_flags(Type, SecLevel), -% msgSecurityParameters = SecBytes -% msgSecurityModel = MsgSecurityModel}, -% Msg = #message{version = Vsn, vsn_hdr = V3Hdr, -% data = ScopedPDUBytes}, -% snmp_pdus:enc_message_only(Message2); - enc_message(Vsn, Community, Pdu) -> PduBytes = snmp_pdus:enc_pdu(Pdu), Msg = #message{version = Vsn, @@ -1144,14 +1120,13 @@ display_info(Info) -> CurrentFile = get_info(current_file, Info, -1), NoItems = get_info(no_current_items, Info, -1), NoBytes = get_info(no_current_bytes, Info, -1), - io:format(user, "Disk log info: " - "~n Number of filled since opened: ~p" - "~n Number of filled since last info: ~p" - "~n Current file: ~p" - "~n Number of items in file: ~p" - "~n Number of bytes in file: ~p" - "~n", - [SinceOpened, SinceLastInfo, CurrentFile, NoItems, NoBytes]). + ?NPRINT("Disk log info: " + "~n Number of filled since opened: ~p" + "~n Number of filled since last info: ~p" + "~n Current file: ~p" + "~n Number of items in file: ~p" + "~n Number of bytes in file: ~p", + [SinceOpened, SinceLastInfo, CurrentFile, NoItems, NoBytes]). get_info(Key, Info, Def) -> case lists:keysearch(Key, 1, Info) of @@ -1164,5 +1139,3 @@ get_info(Key, Info, Def) -> join(D, F) -> filename:join(D, F). -p(Case) -> - io:format(user, "test case: ~w~n", [Case]). diff --git a/lib/snmp/test/snmp_manager_SUITE.erl b/lib/snmp/test/snmp_manager_SUITE.erl index 98aa8336fb..9d8e2efacf 100644 --- a/lib/snmp/test/snmp_manager_SUITE.erl +++ b/lib/snmp/test/snmp_manager_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2019. All Rights Reserved. +%% Copyright Ericsson AB 2003-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -337,31 +337,34 @@ ipv6_tests() -> init_per_suite(Config0) when is_list(Config0) -> - ?DBG("init_per_suite -> entry with" - "~n Config0: ~p", [Config0]), + ?IPRINT("init_per_suite -> entry with" + "~n Config0: ~p", [Config0]), - case snmp_test_lib:init_per_suite(Config0) of + case ?LIB:init_per_suite(Config0) of {skip, _} = SKIP -> SKIP; Config1 -> + ?IPRINT("init_per_suite -> common init done" + "~n Config1: ~p", [Config1]), + %% Preferably this test SUITE should be divided into groups %% so that if crypto does not work only v3 tests that %% need crypto will be skipped, but as this is only a %% problem with one legacy test machine, we will procrastinate %% until we have a more important reason to fix this. - case snmp_test_lib:crypto_start() of + case ?LIB:crypto_start() of ok -> %% We need one on this node also snmp_test_sys_monitor:start(), - Config2 = snmp_test_lib:init_suite_top_dir(?MODULE, Config1), - Config3 = snmp_test_lib:fix_data_dir(Config2), + Config2 = ?LIB:init_suite_top_dir(?MODULE, Config1), + Config3 = ?LIB:fix_data_dir(Config2), %% Mib-dirs %% data_dir is trashed by the test-server / common-test %% so there is no point in fixing it... - MibDir = snmp_test_lib:lookup(data_dir, Config3), + MibDir = ?LIB:lookup(data_dir, Config3), StdMibDir = filename:join([code:priv_dir(snmp), "mibs"]), [{mib_dir, MibDir}, {std_mib_dir, StdMibDir} | Config3]; @@ -372,15 +375,15 @@ init_per_suite(Config0) when is_list(Config0) -> end_per_suite(Config0) when is_list(Config0) -> - p("end_per_suite -> entry with" - "~n Config0: ~p" - "~n Nodes: ~p", [Config0, erlang:nodes()]), + ?IPRINT("end_per_suite -> entry with" + "~n Config0: ~p" + "~n Nodes: ~p", [Config0, erlang:nodes()]), snmp_test_sys_monitor:stop(), - Config1 = snmp_test_lib:end_per_suite(Config0), + Config1 = ?LIB:end_per_suite(Config0), - p("end_per_suite -> end when" - "~n Nodes: ~p", [erlang:nodes()]), + ?IPRINT("end_per_suite -> end when" + "~n Nodes: ~p", [erlang:nodes()]), Config1. @@ -389,11 +392,11 @@ end_per_suite(Config0) when is_list(Config0) -> %% init_per_group(request_tests_mt = GroupName, Config) -> - snmp_test_lib:init_group_top_dir( + ?LIB:init_group_top_dir( GroupName, [{manager_net_if_module, snmpm_net_if_mt} | Config]); init_per_group(event_tests_mt = GroupName, Config) -> - snmp_test_lib:init_group_top_dir( + ?LIB:init_group_top_dir( GroupName, [{manager_net_if_module, snmpm_net_if_mt} | Config]); init_per_group(ipv6_mt = GroupName, Config) -> @@ -402,7 +405,7 @@ init_per_group(ipv6_mt = GroupName, Config) -> init_per_group(ipv6 = GroupName, Config) -> init_per_group_ipv6(GroupName, Config); init_per_group(GroupName, Config) -> - snmp_test_lib:init_group_top_dir(GroupName, Config). + ?LIB:init_group_top_dir(GroupName, Config). init_per_group_ipv6(GroupName, Config) -> @@ -429,9 +432,9 @@ init_per_group_ipv6(GroupName, Config) -> false -> %% Even if this host supports IPv6 we don't use it unless its %% one of the configures/supported IPv6 hosts... - case (?HAS_SUPPORT_IPV6() andalso ?IS_IPV6_HOST()) of + case ?HAS_SUPPORT_IPV6() of true -> - ipv6_init(snmp_test_lib:init_group_top_dir(GroupName, Config)); + ipv6_init(?LIB:init_group_top_dir(GroupName, Config)); false -> {skip, "Host does not support IPv6"} end @@ -450,8 +453,10 @@ end_per_group(_GroupName, Config) -> %% init_per_testcase(Case, Config) when is_list(Config) -> - p(Case, "init_per_testcase begin when" - "~n Nodes: ~p~n~n", [erlang:nodes()]), + + ?IPRINT("init_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", [Config, erlang:nodes()]), snmp_test_global_sys_monitor:reset_events(), @@ -469,15 +474,19 @@ init_per_testcase(Case, Config) when is_list(Config) -> C:{skip, _} = E:_ when ((C =:= throw) orelse (C =:= exit)) -> E; + exit:{suite_failed, {{Reason, _CS},_MFA}, Mod, Line}:_ -> + {skip, {Reason, Mod, Line}}; + exit:{suite_failed, Reason, Mod, Line}:_ -> + {skip, {Reason, Mod, Line}}; C:E:_ when ((C =:= throw) orelse (C =:= exit)) -> {skip, {catched, C, E}} end end, - p(Case, "init_per_testcase end when" - "~n Nodes: ~p" - "~n Result: ~p" - "~n~n", [Result, erlang:nodes()]), + ?IPRINT("init_per_testcase end when" + "~n Nodes: ~p" + "~n Result: ~p" + "~n~n", [erlang:nodes(), Result]), Result. init_per_testcase2(Case, Config) -> @@ -524,10 +533,14 @@ init_per_testcase2(Case, Config) -> InformSwarm when (InformSwarm =:= inform_swarm_cbp_def) orelse (InformSwarm =:= inform_swarm_cbp_temp) orelse (InformSwarm =:= inform_swarm_cbp_perm) -> - ?MINS(60); + case ?config(snmp_factor, Config) of + N when is_integer(N) -> ?MINS(2*N); + _ -> ?MINS(2) + end; _ -> ?MINS(1) end, + ?IPRINT("Set test case timetrap: ~p", [TO]), ct:timetrap(TO), Conf = [{ipfamily, Family}, @@ -606,12 +619,12 @@ init_per_testcase3(Case, Config) -> (Case =:= inform_swarm_cbp_temp) orelse (Case =:= inform_swarm_cbp_perm) -> Verb = [{manager_config_verbosity, silence}, - {manager_note_store_verbosity, silence}, - {manager_server_verbosity, info}, - {manager_net_if_verbosity, info}, - {agent_verbosity, info}, - {agent_net_if_verbosity, info}], - Verb ++ Config; + {manager_note_store_verbosity, silence}, + {manager_server_verbosity, info}, + {manager_net_if_verbosity, info}, + {agent_verbosity, info}, + {agent_net_if_verbosity, info}], + Verb ++ Config; Case =:= otp8395_1 -> [{manager_atl_seqno, true} | Config]; true -> @@ -667,20 +680,21 @@ init_per_testcase_fail_agent_cleanup(Conf) -> (catch fin_agent(Conf)). end_per_testcase(Case, Config) when is_list(Config) -> - p(Case, "end_per_testcase begin when" - "~n Nodes: ~p~n~n", [erlang:nodes()]), + ?IPRINT("end_per_testcase -> entry with" + "~n Config: ~p" + "~n Nodes: ~p", + [Config, erlang:nodes()]), - ?PRINT2("system events during test: " + ?IPRINT("system events during test: " "~n ~p", [snmp_test_global_sys_monitor:events()]), - %% Dog = ?config(watchdog, Config), - %% ?WD_STOP(Dog), - %% Conf1 = lists:keydelete(watchdog, 1, Config), Conf1 = Config, Conf2 = end_per_testcase2(Case, Conf1), - p(Case, "end_per_testcase end when" - "~n Nodes: ~p~n~n", [erlang:nodes()]), + ?IPRINT("end_per_testcase -> done with" + "~n Condif: ~p" + "~n Nodes: ~p", [Conf2, erlang:nodes()]), + Conf2. end_per_testcase2(Case, Config) -> @@ -760,7 +774,8 @@ simple_start_and_stop1(Config) when is_list(Config) -> fun() -> do_simple_start_and_stop1(Config) end). do_simple_start_and_stop1(Config) -> - p("starting with Config: ~n~p", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), ConfDir = ?config(manager_conf_dir, Config), DbDir = ?config(manager_db_dir, Config), @@ -771,12 +786,12 @@ do_simple_start_and_stop1(Config) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try starting manager"), + ?IPRINT("try starting manager"), ok = snmpm:start_link(Opts), ?SLEEP(1000), - p("manager started, now try to stop"), + ?IPRINT("manager started, now try to stop"), ok = snmpm:stop(), ?SLEEP(1000), @@ -788,13 +803,18 @@ do_simple_start_and_stop1(Config) -> simple_start_and_stop2(suite) -> []; simple_start_and_stop2(Config) when is_list(Config) -> - ?TC_TRY(simple_start_and_stop2, - fun() -> do_simple_start_and_stop2(Config) end). - -do_simple_start_and_stop2(Config) -> - p("starting with Config: ~p~n", [Config]), - - ManagerNode = start_manager_node(), + Pre = fun() -> + ManagerNode = start_manager_node(), + [ManagerNode] + end, + Case = fun(State) -> do_simple_start_and_stop2(State, Config) end, + Post = fun([ManagerNode]) -> stop_node(ManagerNode) end, + ?TC_TRY(simple_start_and_stop2, Pre, Case, Post). + +do_simple_start_and_stop2([ManagerNode], Config) -> + ?IPRINT("starting with Config: " + "~n ~p" + "~n", [Config]), ConfDir = ?config(manager_conf_dir, Config), DbDir = ?config(manager_db_dir, Config), @@ -807,27 +827,24 @@ do_simple_start_and_stop2(Config) -> {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try load snmp application"), + ?IPRINT("try load snmp application"), ?line ok = load_snmp(ManagerNode), - p("try set manager env for the snmp application"), + ?IPRINT("try set manager env for the snmp application"), ?line ok = set_mgr_env(ManagerNode, Opts), - p("try starting snmp application (with only manager)"), + ?IPRINT("try starting snmp application (with only manager)"), ?line ok = start_snmp(ManagerNode), - p("started"), + ?IPRINT("started"), ?SLEEP(1000), - p("try stopping snmp application (with only manager)"), + ?IPRINT("try stopping snmp application (with only manager)"), ?line ok = stop_snmp(ManagerNode), ?SLEEP(1000), - - stop_node(ManagerNode), - - ?SLEEP(1000), + ?IPRINT("end"), ok. @@ -840,7 +857,9 @@ simple_start_and_stop3(Config) when is_list(Config) -> fun() -> do_simple_start_and_stop3(Config) end). do_simple_start_and_stop3(Config) -> - p("starting with Config: ~n~p", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), + ConfDir = ?config(manager_conf_dir, Config), DbDir = ?config(manager_db_dir, Config), @@ -851,17 +870,19 @@ do_simple_start_and_stop3(Config) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try starting manager"), + ?IPRINT("try starting manager"), try snmpm:start_link(Opts) of ok -> + (catch snmpm:stop()), ?FAIL('unexpected-success') catch _:_:_ -> - p("expected start failure"), + ?IPRINT("expected start failure"), ok end, ?SLEEP(1000), + ?IPRINT("end"), ok. @@ -874,7 +895,8 @@ simple_start_and_monitor_crash1(Config) when is_list(Config) -> fun() -> do_simple_start_and_monitor_crash1(Config) end). do_simple_start_and_monitor_crash1(Config) -> - p("starting with Config: ~n~p", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), ConfDir = ?config(manager_conf_dir, Config), DbDir = ?config(manager_db_dir, Config), @@ -886,15 +908,15 @@ do_simple_start_and_monitor_crash1(Config) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try starting manager"), + ?IPRINT("try starting manager"), ok = snmpm:start(Opts), ?SLEEP(1000), - p("create the monitor"), + ?IPRINT("create the monitor"), Ref = snmpm:monitor(), - p("make sure it has not already crashed..."), + ?IPRINT("make sure it has not already crashed..."), receive {'DOWN', Ref, process, Obj1, Reason1} -> ?FAIL({unexpected_down, Obj1, Reason1}) @@ -902,20 +924,28 @@ do_simple_start_and_monitor_crash1(Config) -> ok end, - p("stop the manager"), + ?IPRINT("stop the manager"), ok = snmpm:stop(), - p("await the down-message"), + ?IPRINT("await the down-message"), receive {'DOWN', Ref, process, Obj2, Reason2} -> - p("received expected down-message: " - "~n Obj2: ~p" - "~n Reason2: ~p", - [Obj2, Reason2]), + ?IPRINT("received expected down-message: " + "~n Obj2: ~p" + "~n Reason2: ~p", + [Obj2, Reason2]), ok after 1000 -> + %% The manager is an entire process tree and we can't + %% wait for all of them. Instead, we assume that if + %% we deal with the top supervisor, all the other procs + %% will also follow... + ?ENSURE_NOT_RUNNING(snmpm_supervisor, + fun() -> snmpm:stop() end, + 1000), ?FAIL(timeout) end, + ?IPRINT("end"), ok. @@ -924,11 +954,22 @@ do_simple_start_and_monitor_crash1(Config) -> simple_start_and_monitor_crash2(suite) -> []; simple_start_and_monitor_crash2(Config) when is_list(Config) -> + Cond = fun() -> case os:type() of + {unix, netbsd} -> + {skip, "Unstable on NetBSD"}; + _ -> + ok + end + end, + Pre = fun() -> undefined end, + Case = fun(_) -> do_simple_start_and_monitor_crash2(Config) end, + Post = fun(_) -> ok end, ?TC_TRY(simple_start_and_monitor_crash2, - fun() -> do_simple_start_and_monitor_crash2(Config) end). + Cond, Pre, Case, Post). do_simple_start_and_monitor_crash2(Config) -> - p("starting with Config: ~n~p", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), ConfDir = ?config(manager_conf_dir, Config), DbDir = ?config(manager_db_dir, Config), @@ -941,15 +982,15 @@ do_simple_start_and_monitor_crash2(Config) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try starting manager"), + ?IPRINT("try starting manager"), ok = snmpm:start(Opts), ?SLEEP(1000), - p("create the monitor"), + ?IPRINT("create the monitor"), Ref = snmpm:monitor(), - p("make sure it has not already crashed..."), + ?IPRINT("make sure it has not already crashed..."), receive {'DOWN', Ref, process, Obj1, Reason1} -> ?FAIL({unexpected_down, Obj1, Reason1}) @@ -957,16 +998,16 @@ do_simple_start_and_monitor_crash2(Config) -> ok end, - p("crash the manager"), + ?IPRINT("crash the manager"), simulate_crash(), - p("await the down-message"), + ?IPRINT("await the down-message"), receive {'DOWN', Ref, process, Obj2, Reason2} -> - p("received expected down-message: " - "~n Obj2: ~p" - "~n Reason2: ~p", - [Obj2, Reason2]), + ?IPRINT("received expected down-message: " + "~n Obj2: ~p" + "~n Reason2: ~p", + [Obj2, Reason2]), ok after 1000 -> ?FAIL(timeout) @@ -993,16 +1034,17 @@ simulate_crash(?MAX_KILLS, _) -> ok end; simulate_crash(NumKills, Pid) when (NumKills < ?MAX_KILLS) and is_pid(Pid) -> - p("similate_crash -> ~w, ~p", [NumKills, Pid]), + ?IPRINT("similate_crash -> ~w, ~p", [NumKills, Pid]), Ref = erlang:monitor(process, Pid), exit(Pid, kill), receive {'DOWN', Ref, process, _Object, _Info} -> - p("received expected 'DOWN' message"), + ?IPRINT("received expected 'DOWN' message"), simulate_crash(NumKills + 1, server_pid()) after 1000 -> case server_pid() of P when is_pid(P) -> + ?EPRINT("received expected 'DOWN' message"), exit({error, {no_down_from_server, P}}); _ -> ok @@ -1021,7 +1063,8 @@ notify_started01(Config) when is_list(Config) -> fun() -> do_notify_started01(Config) end). do_notify_started01(Config) -> - p("starting with Config: ~n~p", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), ConfDir = ?config(manager_conf_dir, Config), DbDir = ?config(manager_db_dir, Config), @@ -1033,11 +1076,11 @@ do_notify_started01(Config) -> {note_store, [{verbosity, silence}]}, {config, [{verbosity, log}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("request start notification (1)"), + ?IPRINT("request start notification (1)"), Pid1 = snmpm:notify_started(10000), receive {snmpm_start_timeout, Pid1} -> - p("received expected start timeout"), + ?IPRINT("received expected start timeout"), ok; Any1 -> ?FAIL({unexpected_message, Any1}) @@ -1045,17 +1088,17 @@ do_notify_started01(Config) -> ?FAIL({unexpected_timeout, Pid1}) end, - p("request start notification (2)"), + ?IPRINT("request start notification (2)"), Pid2 = snmpm:notify_started(10000), - p("start the snmpm starter"), + ?IPRINT("start the snmpm starter"), Pid = snmpm_starter(Opts, 5000), - p("await the start notification"), + ?IPRINT("await the start notification"), Ref = receive {snmpm_started, Pid2} -> - p("received started message -> create the monitor"), + ?IPRINT("received started message -> create the monitor"), snmpm:monitor(); Any2 -> ?FAIL({unexpected_message, Any2}) @@ -1063,7 +1106,7 @@ do_notify_started01(Config) -> ?FAIL({unexpected_timeout, Pid2}) end, - p("[~p] make sure it has not already crashed...", [Ref]), + ?IPRINT("[~p] make sure it has not already crashed...", [Ref]), receive {'DOWN', Ref, process, Obj1, Reason1} -> ?FAIL({unexpected_down, Obj1, Reason1}) @@ -1071,22 +1114,22 @@ do_notify_started01(Config) -> ok end, - p("stop the manager"), + ?IPRINT("stop the manager"), Pid ! {stop, self()}, %ok = snmpm:stop(), - p("await the down-message"), + ?IPRINT("await the down-message"), receive {'DOWN', Ref, process, Obj2, Reason2} -> - p("received expected down-message: " - "~n Obj2: ~p" - "~n Reason2: ~p", - [Obj2, Reason2]), + ?IPRINT("received expected down-message: " + "~n Obj2: ~p" + "~n Reason2: ~p", + [Obj2, Reason2]), ok after 5000 -> ?FAIL(down_timeout) end, - p("end"), + ?IPRINT("end"), ok. @@ -1135,7 +1178,8 @@ notify_started02_cond(Config) -> ?NON_PC_TC_MAYBE_SKIP(Config, Condition). do_notify_started02(Config) -> - p("starting with Config: ~n~p", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), ConfDir = ?config(manager_conf_dir, Config), DbDir = ?config(manager_db_dir, Config), @@ -1147,11 +1191,11 @@ do_notify_started02(Config) -> {note_store, [{verbosity, silence}]}, {config, [{verbosity, debug}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("start snmpm client process"), + ?IPRINT("start snmpm client process"), NumIterations = 5, Pid1 = ns02_client_start(NumIterations), - p("start snmpm ctrl (starter) process"), + ?IPRINT("start snmpm ctrl (starter) process"), Pid2 = ns02_ctrl_start(Opts, NumIterations), %% On a reasonably fast machine, one iteration takes approx 4 seconds. @@ -1173,7 +1217,8 @@ do_notify_started02(Config) -> ?SKIP(Reason) end, - p("await snmpm client process exit (max ~p+10000 msec)", [ApproxStartTime]), + ?IPRINT("await snmpm client process exit (max ~p+10000 msec)", + [ApproxStartTime]), receive %% We take this opportunity to check if we got a skip from %% the ctrl process. @@ -1191,7 +1236,7 @@ do_notify_started02(Config) -> ?FAIL(timeout) end, - p("await snmpm starter process exit"), + ?IPRINT("await snmpm starter process exit"), receive {'EXIT', Pid2, normal} -> ok; @@ -1205,7 +1250,7 @@ do_notify_started02(Config) -> ?FAIL(timeout) end, - p("end"), + ?IPRINT("end"), ok. @@ -1218,8 +1263,8 @@ ns02_client_await_approx_runtime(Pid) -> {?MODULE, client_time, Time} -> {ok, Time}; {'EXIT', Pid, Reason} -> - p("client (~p) failed: " - "~n ~p", [Pid, Reason]), + ?EPRINT("client (~p) failed: " + "~n ~p", [Pid, Reason]), {error, Reason} after 30000 -> @@ -1231,48 +1276,48 @@ ns02_client_await_approx_runtime(Pid) -> ns02_client(Parent, N) when is_pid(Parent) -> put(tname, ns02_client), - p("starting"), + ?IPRINT("starting"), ns02_client_loop(Parent, dummy, snmpm:notify_started(?NS_TIMEOUT), snmp_misc:now(ms), undefined, N). ns02_client_loop(_Parent, _Ref, _Pid, _Begin, _End, 0) -> - %% p("loop -> done"), + %% ?IPRINT("loop -> done"), exit(normal); ns02_client_loop(Parent, Ref, Pid, Begin, End, N) when is_pid(Parent) andalso is_integer(Begin) andalso is_integer(End) -> - %% p("loop -> [~w] inform parent: ~w, ~w => ~w", [N, Begin, End, End-Begin]), + %% ?IPRINT("loop -> [~w] inform parent: ~w, ~w => ~w", [N, Begin, End, End-Begin]), Parent ! {?MODULE, client_time, N*(End-Begin)}, ns02_client_loop(undefined, Ref, Pid, snmp_misc:now(ms), undefined, N); ns02_client_loop(Parent, Ref, Pid, Begin, End, N) when is_integer(Begin) andalso is_integer(End) -> - %% p("loop -> [~w] entry when" + %% ?IPRINT("loop -> [~w] entry when" %% "~n Ref: ~p" %% "~n Pid: ~p" %% "~n Begin: ~p" %% "~n End: ~p", [N, Ref, Pid, Begin, End]), ns02_client_loop(Parent, Ref, Pid, snmp_misc:now(ms), undefined, N); ns02_client_loop(Parent, Ref, Pid, Begin, End, N) -> - %% p("loop(await message) -> [~w] entry when" + %% ?IPRINT("loop(await message) -> [~w] entry when" %% "~n Ref: ~p" %% "~n Pid: ~p" %% "~n Begin: ~p" %% "~n End: ~p", [N, Ref, Pid, Begin, End]), receive {snmpm_started, Pid} -> - p("received expected started message (~w)", [N]), + ?IPRINT("received expected started message (~w)", [N]), ns02_client_loop(Parent, snmpm:monitor(), dummy, Begin, End, N); {snmpm_start_timeout, Pid} -> - p("unexpected timout"), + ?EPRINT("unexpected timeout"), ?FAIL({unexpected_start_timeout, Pid}); {'DOWN', Ref, process, Obj, Reason} -> - p("received expected DOWN message (~w) with" - "~n Obj: ~p" - "~n Reason: ~p", [N, Obj, Reason]), + ?IPRINT("received expected DOWN message (~w) with" + "~n Obj: ~p" + "~n Reason: ~p", [N, Obj, Reason]), ns02_client_loop(Parent, dummy, snmpm:notify_started(?NS_TIMEOUT), Begin, snmp_misc:now(ms), @@ -1284,7 +1329,7 @@ ns02_ctrl_start(Opts, N) -> ns02_ctrl(Opts, N) -> put(tname, ns02_ctrl), - p("starting"), + ?IPRINT("starting"), ns02_ctrl_loop(Opts, N). @@ -1294,36 +1339,36 @@ ns02_ctrl(Opts, N) -> %% So, we try to monitor each start attempt. We allow 5 sec (just %% to give slow boxes a chance). ns02_ctrl_loop(_Opts, 0) -> - p("done"), + ?IPRINT("done"), exit(normal); ns02_ctrl_loop(Opts, N) -> - p("entry when N: ~p", [N]), + ?IPRINT("entry when N: ~p", [N]), ?SLEEP(2000), - p("start manager"), + ?IPRINT("start manager"), TS1 = erlang:system_time(millisecond), {StarterPid, StarterMRef} = erlang:spawn_monitor(fun() -> exit(snmpm:start(Opts)) end), receive {'DOWN', StarterMRef, process, StarterPid, ok} -> TS2 = erlang:system_time(millisecond), - p("manager started: ~w ms", [TS2-TS1]), + ?IPRINT("manager started: ~w ms", [TS2-TS1]), ok after 5000 -> - p("manager (~p) start timeout - kill", [StarterPid]), + ?EPRINT("manager (~p) start timeout - kill", [StarterPid]), exit(StarterPid, kill), exit({skip, start_timeout}) end, ?SLEEP(2000), - p("stop manager"), + ?IPRINT("stop manager"), ?SLEEP(100), % Give the verbosity to take effect... TS3 = erlang:system_time(millisecond), case snmpm:stop(5000) of ok -> TS4 = erlang:system_time(millisecond), - p("manager stopped: ~p ms", [TS4-TS3]), + ?IPRINT("manager stopped: ~p ms", [TS4-TS3]), ok; {error, timeout} -> - p("manager stop timeout - kill (cleanup) and skip"), + ?EPRINT("manager stop timeout - kill (cleanup) and skip"), exit(whereis(snmpm_supervisor), kill), exit({skip, stop_timeout}) end, @@ -1335,37 +1380,44 @@ ns02_ctrl_loop(Opts, N) -> info(suite) -> []; info(Config) when is_list(Config) -> - ?TC_TRY(info, - fun() -> do_info(Config) end). - -do_info(Config) -> - p("starting with Config: ~n~p", [Config]), - - ConfDir = ?config(manager_conf_dir, Config), - DbDir = ?config(manager_db_dir, Config), + Pre = fun() -> + ConfDir = ?config(manager_conf_dir, Config), + DbDir = ?config(manager_db_dir, Config), + + write_manager_conf(ConfDir), + + Opts = [{server, [{verbosity, trace}]}, + {net_if, [{verbosity, trace}]}, + {note_store, [{verbosity, trace}]}, + {config, [{verbosity, trace}, + {dir, ConfDir}, + {db_dir, DbDir}]}], + ?IPRINT("try starting manager"), + ok = snmpm:start(Opts), + ?SLEEP(1000), + ok + end, + Case = fun(_) -> do_info(Config) end, + Post = fun(_) -> + ?IPRINT("info verified, now try to stop"), + snmpm:stop(), + ?SLEEP(1000), + ok + end, + ?TC_TRY(info, Pre, Case, Post). - write_manager_conf(ConfDir), - - Opts = [{server, [{verbosity, trace}]}, - {net_if, [{verbosity, trace}]}, - {note_store, [{verbosity, trace}]}, - {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - - p("try starting manager"), - ok = snmpm:start(Opts), - ?SLEEP(1000), +do_info(Config) -> + ?IPRINT("starting with Config: " + "~n ~p", [Config]), - p("manager started, now get info"), + ?IPRINT("get info"), Info = snmpm:info(), - p("got info, now verify: ~n~p", [Info]), + ?IPRINT("got info, now verify: " + "~n ~p", [Info]), ok = verify_info( Info ), - p("info verified, now try to stop"), - ok = snmpm:stop(), - - ?SLEEP(1000), - + ?IPRINT("end"), ok. verify_info(Info) when is_list(Info) -> @@ -1405,13 +1457,18 @@ verify_info([{Key, SubKeys}|Keys], Info) -> register_user1(suite) -> []; register_user1(Config) when is_list(Config) -> - ?TC_TRY(register_user1, - fun() -> do_register_user1(Config) end). - -do_register_user1(Config) -> - p("starting with Config: ~p~n", [Config]), - - ManagerNode = start_manager_node(), + Pre = fun() -> + ManagerNode = start_manager_node(), + [ManagerNode] + end, + Case = fun(State) -> do_register_user1(State, Config) end, + Post = fun([ManagerNode]) -> stop_node(ManagerNode) end, + ?TC_TRY(register_user1, Pre, Case, Post). + +do_register_user1([ManagerNode], Config) -> + ?IPRINT("starting with Config: " + "~n ~p" + "~n", [Config]), ConfDir = ?config(manager_conf_dir, Config), DbDir = ?config(manager_db_dir, Config), @@ -1424,64 +1481,60 @@ do_register_user1(Config) -> {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("load snmp application"), + ?IPRINT("load snmp application"), ?line ok = load_snmp(ManagerNode), - p("set manager env for the snmp application"), + ?IPRINT("set manager env for the snmp application"), ?line ok = set_mgr_env(ManagerNode, Opts), - p("starting snmp application (with only manager)"), + ?IPRINT("starting snmp application (with only manager)"), ?line ok = start_snmp(ManagerNode), - p("started"), + ?IPRINT("started"), ?SLEEP(1000), - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("try register user(s)"), + ?IPRINT("try register user(s)"), ?line ok = mgr_register_user(ManagerNode, calvin, snmpm_user_default, [self(), "various misc info"]), Users1 = mgr_which_users(ManagerNode), - p("users: ~p~n", [Users1]), + ?IPRINT("users: ~p~n", [Users1]), ?line ok = verify_users(Users1, [calvin]), - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), ?line ok = mgr_register_user(ManagerNode, hobbe, snmpm_user_default, {"misc info", self()}), Users2 = mgr_which_users(ManagerNode), - p("users: ~p~n", [Users2]), + ?IPRINT("users: ~p~n", [Users2]), ?line ok = verify_users(Users2, [calvin, hobbe]), - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("try unregister user(s)"), + ?IPRINT("try unregister user(s)"), ?line ok = mgr_unregister_user(ManagerNode, calvin), Users3 = mgr_which_users(ManagerNode), - p("users: ~p~n", [Users3]), + ?IPRINT("users: ~p~n", [Users3]), ?line ok = verify_users(Users3, [hobbe]), - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), ?line ok = mgr_unregister_user(ManagerNode, hobbe), Users4 = mgr_which_users(ManagerNode), - p("users: ~p~n", [Users4]), + ?IPRINT("users: ~p~n", [Users4]), ?line ok = verify_users(Users4, []), - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), ?SLEEP(1000), - p("stop snmp application (with only manager)"), + ?IPRINT("stop snmp application (with only manager)"), ?line ok = stop_snmp(ManagerNode), ?SLEEP(1000), - stop_node(ManagerNode), - - ?SLEEP(1000), - ok. verify_users([], []) -> @@ -1504,13 +1557,18 @@ register_agent_old(doc) -> register_agent_old(suite) -> []; register_agent_old(Config) when is_list(Config) -> - ?TC_TRY(register_agent_old, - fun() -> do_register_agent_old(Config) end). - -do_register_agent_old(Config) -> - p("starting with Config: ~p~n", [Config]), - - ManagerNode = start_manager_node(), + Pre = fun() -> + ManagerNode = start_manager_node(), + [ManagerNode] + end, + Case = fun(State) -> do_register_agent_old(State, Config) end, + Post = fun([ManagerNode]) -> stop_node(ManagerNode) end, + ?TC_TRY(register_agent_old, Pre, Case, Post). + +do_register_agent_old([ManagerNode], Config) -> + ?IPRINT("starting with Config: " + "~n ~p" + "~n", [Config]), ConfDir = ?config(manager_conf_dir, Config), DbDir = ?config(manager_db_dir, Config), @@ -1523,104 +1581,105 @@ do_register_agent_old(Config) -> {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("load snmp application"), + ?IPRINT("load snmp application"), ?line ok = load_snmp(ManagerNode), - p("set manager env for the snmp application"), + ?IPRINT("set manager env for the snmp application"), ?line ok = set_mgr_env(ManagerNode, Opts), - p("starting snmp application (with only manager)"), + ?IPRINT("starting snmp application (with only manager)"), ?line ok = start_snmp(ManagerNode), - p("started"), + ?IPRINT("started"), ?SLEEP(1000), - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("register user(s) user_alfa & user_beta"), + ?IPRINT("register user(s) user_alfa & user_beta"), ?line ok = mgr_register_user(ManagerNode, user_alfa, snmpm_user_default, []), ?line ok = mgr_register_user(ManagerNode, user_beta, snmpm_user_default, []), - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("register agent(s)"), + ?IPRINT("register agent(s)"), ?line ok = mgr_register_agent(ManagerNode, user_alfa, 5000, []), ?line ok = mgr_register_agent(ManagerNode, user_alfa, 5001, []), ?line ok = mgr_register_agent(ManagerNode, user_beta, 5002, []), ?line ok = mgr_register_agent(ManagerNode, user_beta, 5003, []), - p("verify all agent(s): expect 4"), + ?IPRINT("verify all agent(s): expect 4"), case mgr_which_agents(ManagerNode) of Agents1 when length(Agents1) =:= 4 -> - p("all agents: ~p~n", [Agents1]), + ?IPRINT("all agents: ~p~n", [Agents1]), ok; Agents1 -> ?FAIL({agent_registration_failure, Agents1}) end, - p("verify user_alfa agent(s)"), + ?IPRINT("verify user_alfa agent(s)"), case mgr_which_agents(ManagerNode, user_alfa) of Agents2 when length(Agents2) =:= 2 -> - p("calvin agents: ~p~n", [Agents2]), + ?IPRINT("calvin agents: ~p", [Agents2]), ok; Agents2 -> ?FAIL({agent_registration_failure, Agents2}) end, - p("verify user_beta agent(s)"), + ?IPRINT("verify user_beta agent(s)"), case mgr_which_agents(ManagerNode, user_beta) of Agents3 when length(Agents3) =:= 2 -> - p("hobbe agents: ~p~n", [Agents3]), + ?IPRINT("hobbe agents: ~p", [Agents3]), ok; Agents3 -> ?FAIL({agent_registration_failure, Agents3}) end, - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: " + "~n ~p", [mgr_info(ManagerNode)]), - p("unregister user user_alfa"), + ?IPRINT("unregister user user_alfa"), ?line ok = mgr_unregister_user(ManagerNode, user_alfa), - p("verify all agent(s): expect 2"), + ?IPRINT("verify all agent(s): expect 2"), case mgr_which_agents(ManagerNode) of Agents4 when length(Agents4) =:= 2 -> - p("all agents: ~p~n", [Agents4]), + ?IPRINT("all agents: ~p", [Agents4]), ok; Agents4 -> ?FAIL({agent_unregistration_failure, Agents4}) end, - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: " + "~n ~p~n", [mgr_info(ManagerNode)]), - p("unregister user_beta agents"), + ?IPRINT("unregister user_beta agents"), ?line ok = mgr_unregister_agent(ManagerNode, user_beta, 5002), ?line ok = mgr_unregister_agent(ManagerNode, user_beta, 5003), - p("verify all agent(s): expect 0"), + ?IPRINT("verify all agent(s): expect 0"), case mgr_which_agents(ManagerNode) of [] -> ok; Agents5 -> - p("all agents: ~p~n", [Agents5]), + ?IPRINT("all agents: ~p~n", [Agents5]), ?FAIL({agent_unregistration_failure, Agents5}) end, - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: " + "~n ~p", [mgr_info(ManagerNode)]), - p("unregister user hobbe"), + ?IPRINT("unregister user hobbe"), ?line ok = mgr_unregister_user(ManagerNode, user_beta), - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: " + "~n ~p", [mgr_info(ManagerNode)]), ?SLEEP(1000), - p("stop snmp application (with only manager)"), + ?IPRINT("stop snmp application (with only manager)"), ?line ok = stop_snmp(ManagerNode), ?SLEEP(1000), - - stop_node(ManagerNode), - - ?SLEEP(1000), + ?IPRINT("end"), ok. @@ -1632,19 +1691,22 @@ register_agent2(doc) -> register_agent2(suite) -> []; register_agent2(Config) when is_list(Config) -> - ?TC_TRY(register_agent2, - fun() -> do_register_agent2(Config) end). - -do_register_agent2(Config) -> - p("starting with Config: ~p~n", [Config]), - - ManagerNode = start_manager_node(), - - ConfDir = ?config(manager_conf_dir, Config), - DbDir = ?config(manager_db_dir, Config), + Pre = fun() -> + ManagerNode = start_manager_node(), + [ManagerNode] + end, + Case = fun(State) -> do_register_agent2(State, Config) end, + Post = fun([ManagerNode]) -> stop_node(ManagerNode) end, + ?TC_TRY(register_agent2, Pre, Case, Post). + +do_register_agent2([ManagerNode], Config) -> + ?IPRINT("starting with Config: " + "~n ~p", [Config]), + + ConfDir = ?config(manager_conf_dir, Config), + DbDir = ?config(manager_db_dir, Config), LocalHost = snmp_test_lib:localhost(), - write_manager_conf(ConfDir), Opts = [{server, [{verbosity, trace}]}, @@ -1652,28 +1714,27 @@ do_register_agent2(Config) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - - p("load snmp application"), + ?IPRINT("load snmp application"), ?line ok = load_snmp(ManagerNode), - p("set manager env for the snmp application"), + ?IPRINT("set manager env for the snmp application"), ?line ok = set_mgr_env(ManagerNode, Opts), - p("starting snmp application (with only manager)"), + ?IPRINT("starting snmp application (with only manager)"), ?line ok = start_snmp(ManagerNode), - p("started"), + ?IPRINT("started"), ?SLEEP(1000), - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("register user(s) user_alfa & user_beta"), + ?IPRINT("register user(s) user_alfa & user_beta"), ?line ok = mgr_register_user(ManagerNode, user_alfa, snmpm_user_default, []), ?line ok = mgr_register_user(ManagerNode, user_beta, snmpm_user_default, []), - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("register agent(s)"), + ?IPRINT("register agent(s)"), TargetName1 = "agent1", ?line ok = mgr_register_agent(ManagerNode, user_alfa, TargetName1, [{address, LocalHost}, @@ -1695,79 +1756,75 @@ do_register_agent2(Config) -> {port, 5004}, {engine_id, "agentEngineId-4"}]), - p("verify all agent(s): expect 4"), + ?IPRINT("verify all agent(s): expect 4"), case mgr_which_agents(ManagerNode) of Agents1 when length(Agents1) =:= 4 -> - p("all agents: ~p~n", [Agents1]), + ?IPRINT("all agents: ~p~n", [Agents1]), ok; Agents1 -> ?FAIL({agent_registration_failure, Agents1}) end, - p("verify user_alfa agent(s)"), + ?IPRINT("verify user_alfa agent(s)"), case mgr_which_agents(ManagerNode, user_alfa) of Agents2 when length(Agents2) =:= 2 -> - p("calvin agents: ~p~n", [Agents2]), + ?IPRINT("calvin agents: ~p~n", [Agents2]), ok; Agents2 -> ?FAIL({agent_registration_failure, Agents2}) end, - p("verify user_beta agent(s)"), + ?IPRINT("verify user_beta agent(s)"), case mgr_which_agents(ManagerNode, user_beta) of Agents3 when length(Agents3) =:= 2 -> - p("hobbe agents: ~p~n", [Agents3]), + ?IPRINT("hobbe agents: ~p~n", [Agents3]), ok; Agents3 -> ?FAIL({agent_registration_failure, Agents3}) end, - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("unregister user user_alfa"), + ?IPRINT("unregister user user_alfa"), ?line ok = mgr_unregister_user(ManagerNode, user_alfa), - p("verify all agent(s): expect 2"), + ?IPRINT("verify all agent(s): expect 2"), case mgr_which_agents(ManagerNode) of Agents4 when length(Agents4) =:= 2 -> - p("all agents: ~p~n", [Agents4]), + ?IPRINT("all agents: ~p~n", [Agents4]), ok; Agents4 -> ?FAIL({agent_unregistration_failure, Agents4}) end, - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("unregister user_beta agents"), + ?IPRINT("unregister user_beta agents"), ?line ok = mgr_unregister_agent(ManagerNode, user_beta, TargetName3), ?line ok = mgr_unregister_agent(ManagerNode, user_beta, TargetName4), - p("verify all agent(s): expect 0"), + ?IPRINT("verify all agent(s): expect 0"), case mgr_which_agents(ManagerNode) of [] -> ok; Agents5 -> - p("all agents: ~p~n", [Agents5]), + ?IPRINT("all agents: ~p~n", [Agents5]), ?FAIL({agent_unregistration_failure, Agents5}) end, - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("unregister user user_beta"), + ?IPRINT("unregister user user_beta"), ?line ok = mgr_unregister_user(ManagerNode, user_beta), - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), ?SLEEP(1000), - p("stop snmp application (with only manager)"), + ?IPRINT("stop snmp application (with only manager)"), ?line ok = stop_snmp(ManagerNode), ?SLEEP(1000), - stop_node(ManagerNode), - - ?SLEEP(1000), - ok. @@ -1779,16 +1836,20 @@ register_agent3(doc) -> register_agent3(suite) -> []; register_agent3(Config) when is_list(Config) -> - ?TC_TRY(register_agent3, - fun() -> do_register_agent3(Config) end). - -do_register_agent3(Config) -> - p("starting with Config: ~p~n", [Config]), - - ManagerNode = start_manager_node(), - - ConfDir = ?config(manager_conf_dir, Config), - DbDir = ?config(manager_db_dir, Config), + Pre = fun() -> + ManagerNode = start_manager_node(), + [ManagerNode] + end, + Case = fun(State) -> do_register_agent3(State, Config) end, + Post = fun([ManagerNode]) -> stop_node(ManagerNode) end, + ?TC_TRY(register_agent3, Pre, Case, Post). + +do_register_agent3([ManagerNode], Config) -> + ?IPRINT("starting with Config: " + "~n ~p", [Config]), + + ConfDir = ?config(manager_conf_dir, Config), + DbDir = ?config(manager_db_dir, Config), LocalHost = snmp_test_lib:localhost(), @@ -1800,27 +1861,27 @@ do_register_agent3(Config) -> {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("load snmp application"), + ?IPRINT("load snmp application"), ?line ok = load_snmp(ManagerNode), - p("set manager env for the snmp application"), + ?IPRINT("set manager env for the snmp application"), ?line ok = set_mgr_env(ManagerNode, Opts), - p("starting snmp application (with only manager)"), + ?IPRINT("starting snmp application (with only manager)"), ?line ok = start_snmp(ManagerNode), - p("started"), + ?IPRINT("started"), ?SLEEP(1000), - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("register user(s) user_alfa & user_beta"), + ?IPRINT("register user(s) user_alfa & user_beta"), ?line ok = mgr_register_user(ManagerNode, user_alfa, snmpm_user_default, []), ?line ok = mgr_register_user(ManagerNode, user_beta, snmpm_user_default, []), - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("register agent(s)"), + ?IPRINT("register agent(s)"), TargetName1 = "agent2", ?line ok = mgr_register_agent(ManagerNode, user_alfa, TargetName1, [{tdomain, transportDomainUdpIpv4}, @@ -1840,7 +1901,7 @@ do_register_agent3(Config) -> {address, LocalHost}, {port, 5003}, {engine_id, "agentEngineId-3"}]), - p("Expected registration failure: ~p", [Reason4]), + ?IPRINT("Expected registration failure: ~p", [Reason4]), TargetName4 = "agent5", ?line {error, {unknown_domain, _} = Reason5} = mgr_register_agent(ManagerNode, user_beta, TargetName4, @@ -1848,77 +1909,73 @@ do_register_agent3(Config) -> {address, LocalHost}, {port, 5004}, {engine_id, "agentEngineId-4"}]), - p("Expected registration failure: ~p", [Reason5]), + ?IPRINT("Expected registration failure: ~p", [Reason5]), - p("verify all agent(s): expect 2"), + ?IPRINT("verify all agent(s): expect 2"), case mgr_which_agents(ManagerNode) of Agents1 when length(Agents1) =:= 2 -> - p("all agents: ~p~n", [Agents1]), + ?IPRINT("all agents: ~p~n", [Agents1]), ok; Agents1 -> ?FAIL({agent_registration_failure, Agents1}) end, - p("verify user_alfa agent(s)"), + ?IPRINT("verify user_alfa agent(s)"), case mgr_which_agents(ManagerNode, user_alfa) of Agents2 when length(Agents2) =:= 2 -> - p("calvin agents: ~p~n", [Agents2]), + ?IPRINT("calvin agents: ~p~n", [Agents2]), ok; Agents2 -> ?FAIL({agent_registration_failure, Agents2}) end, - p("verify user_beta agent(s)"), + ?IPRINT("verify user_beta agent(s)"), case mgr_which_agents(ManagerNode, user_beta) of Agents3 when length(Agents3) =:= 0 -> - p("hobbe agents: ~p~n", [Agents3]), + ?IPRINT("hobbe agents: ~p~n", [Agents3]), ok; Agents3 -> ?FAIL({agent_registration_failure, Agents3}) end, - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("unregister user user_alfa"), + ?IPRINT("unregister user user_alfa"), ?line ok = mgr_unregister_user(ManagerNode, user_alfa), - p("verify all agent(s): expect 0"), + ?IPRINT("verify all agent(s): expect 0"), case mgr_which_agents(ManagerNode) of Agents4 when length(Agents4) =:= 0 -> - p("all agents: ~p~n", [Agents4]), + ?IPRINT("all agents: ~p~n", [Agents4]), ok; Agents4 -> ?FAIL({agent_unregistration_failure, Agents4}) end, - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("verify all agent(s): expect 0"), + ?IPRINT("verify all agent(s): expect 0"), case mgr_which_agents(ManagerNode) of [] -> ok; Agents5 -> - p("all agents: ~p~n", [Agents5]), + ?EPRINT("all agents: ~p~n", [Agents5]), ?FAIL({agent_unregistration_failure, Agents5}) end, - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("unregister user user_beta"), + ?IPRINT("unregister user user_beta"), ?line ok = mgr_unregister_user(ManagerNode, user_beta), - p("manager info: ~p~n", [mgr_info(ManagerNode)]), + ?IPRINT("manager info: ~p~n", [mgr_info(ManagerNode)]), ?SLEEP(1000), - p("stop snmp application (with only manager)"), + ?IPRINT("stop snmp application (with only manager)"), ?line ok = stop_snmp(ManagerNode), ?SLEEP(1000), - stop_node(ManagerNode), - - ?SLEEP(1000), - ok. @@ -1932,26 +1989,28 @@ simple_sync_get2(Config) when is_list(Config) -> fun() -> do_simple_sync_get2(Config) end). do_simple_sync_get2(Config) -> - p("starting with Config: ~n~p", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Get = fun(Node, TargetName, Oids) -> mgr_user_sync_get(Node, TargetName, Oids) end, PostVerify = fun() -> ok end, - do_simple_sync_get2(Config, Get, PostVerify), + Res = do_simple_sync_get2(Config, Get, PostVerify), display_log(Config), - ok. + Res. do_simple_sync_get2(Config, Get, PostVerify) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Node = ?config(manager_node, Config), TargetName = ?config(manager_agent_target_name, Config), - p("issue get-request without loading the mib"), + ?IPRINT("issue get-request without loading the mib"), Oids1 = [?sysObjectID_instance, ?sysDescr_instance, ?sysUpTime_instance], ?line ok = do_simple_sync_get2(Node, TargetName, Oids1, Get, PostVerify), - p("issue get-request after first loading the mibs"), + ?IPRINT("issue get-request after first loading the mibs"), ?line ok = mgr_user_load_mib(Node, std_mib()), Oids2 = [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]], ?line ok = do_simple_sync_get2(Node, TargetName, Oids2, Get, PostVerify), @@ -1973,17 +2032,19 @@ do_simple_sync_get2(Node, TargetName, Oids, Get, PostVerify) value = SysDescr}, #varbind{oid = ?sysUpTime_instance, value = SysUpTime}]} -> - p("expected result from get: " - "~n SysObjectID: ~p" - "~n SysDescr: ~s" - "~n SysUpTime: ~w", - [SysObjectID, SysDescr, SysUpTime]), + ?IPRINT("expected result from get: " + "~n SysObjectID: ~p" + "~n SysDescr: ~s" + "~n SysUpTime: ~w", + [SysObjectID, SysDescr, SysUpTime]), PostVerify(); {noError, 0, Vbs} -> - p("unexpected varbinds: ~n~p", [Vbs]), + ?EPRINT("unexpected varbinds: " + "~n ~p", [Vbs]), {error, {unexpected_vbs, Vbs}}; Else -> - p("unexpected reply: ~n~p", [Else]), + ?EPRINT("unexpected reply: " + "~n ~p", [Else]), {error, {unexpected_response, Else}} end, ok. @@ -1999,7 +2060,8 @@ simple_sync_get3(Config) when is_list(Config) -> fun() -> do_simple_sync_get3(Config) end). do_simple_sync_get3(Config) -> - p("starting with Config: ~n~p", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Self = self(), Msg = simple_sync_get3, Fun = fun() -> Self ! Msg end, @@ -2018,9 +2080,9 @@ do_simple_sync_get3(Config) -> ok end end, - do_simple_sync_get2(Config, Get, PostVerify), + Res = do_simple_sync_get2(Config, Get, PostVerify), display_log(Config), - ok. + Res. @@ -2028,7 +2090,7 @@ do_simple_sync_get3(Config) -> %%====================================================================== sag_verify({noError, 0, _Vbs}, any) -> - p("verified [any]"), + ?IPRINT("verified [any]"), ok; sag_verify({noError, 0, Vbs}, Exp) -> ?DBG("verified first stage ok: " @@ -2046,13 +2108,13 @@ sag_verify_vbs(Vbs, []) -> sag_verify_vbs([], Exp) -> {error, {expected_vbs, Exp}}; sag_verify_vbs([#varbind{oid = Oid}|Vbs], [any|Exp]) -> - p("verified [any] oid ~w", [Oid]), + ?IPRINT("verified [any] oid ~w", [Oid]), sag_verify_vbs(Vbs, Exp); sag_verify_vbs([#varbind{oid = Oid, value = Value}|Vbs], [Oid|Exp]) -> - p("verified oid ~w [~p]", [Oid, Value]), + ?IPRINT("verified oid ~w [~p]", [Oid, Value]), sag_verify_vbs(Vbs, Exp); sag_verify_vbs([#varbind{oid = Oid, value = Value}|Vbs], [{Oid,Value}|Exp]) -> - p("verified oid ~w and ~p", [Oid, Value]), + ?IPRINT("verified oid ~w and ~p", [Oid, Value]), sag_verify_vbs(Vbs, Exp); sag_verify_vbs([Vb|_], [E|_]) -> {error, {unexpected_vb, Vb, E}}. @@ -2068,7 +2130,8 @@ simple_async_get2(Config) when is_list(Config) -> fun() -> do_simple_async_get2(Config) end). do_simple_async_get2(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), TargetName = ?config(manager_agent_target_name, Config), @@ -2127,13 +2190,17 @@ do_simple_async_sync_get2(MgrInfo, AgentInfo, Get, PostVerify) end} ], - p("manager info when starting test: ~n~p", [MgrInfo()]), - p("agent info when starting test: ~n~p", [AgentInfo()]), + ?IPRINT("manager info when starting test: " + "~n ~p", [MgrInfo()]), + ?IPRINT("agent info when starting test: " + "~n ~p", [AgentInfo()]), ?line ok = async_exec(Requests, []), - p("manager info when ending test: ~n~p", [MgrInfo()]), - p("agent info when ending test: ~n~p", [AgentInfo()]), + ?IPRINT("manager info when ending test: " + "~n ~p", [MgrInfo()]), + ?IPRINT("agent info when ending test: " + "~n ~p", [AgentInfo()]), ok. @@ -2151,7 +2218,8 @@ simple_async_get3(Config) when is_list(Config) -> fun() -> do_simple_async_get3(Config) end). do_simple_async_get3(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), TargetName = ?config(manager_agent_target_name, Config), @@ -2167,9 +2235,9 @@ do_simple_async_get3(Config) -> PostVerify = fun(ok) -> receive Msg -> ok end; (Error) -> Error end, - do_simple_async_sync_get2(Config, MgrNode, AgentNode, Get, PostVerify), + Res = do_simple_async_sync_get2(Config, MgrNode, AgentNode, Get, PostVerify), display_log(Config), - ok. + Res. async_g_exec3(Node, TargetName, Oids, SendOpts) -> mgr_user_async_get2(Node, TargetName, Oids, SendOpts). @@ -2219,15 +2287,16 @@ simple_sync_get_next2(Config) when is_list(Config) -> fun() -> do_simple_sync_get_next2(Config) end). do_simple_sync_get_next2(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), GetNext = fun(Node, TargetName, Oids) -> mgr_user_sync_get_next(Node, TargetName, Oids) end, PostVerify = fun(Res) -> Res end, - do_simple_sync_get_next2(Config, GetNext, PostVerify), + Res = do_simple_sync_get_next2(Config, GetNext, PostVerify), display_log(Config), - ok. + Res. do_simple_sync_get_next2(Config, GetNext, PostVerify) @@ -2322,7 +2391,7 @@ do_simple_sync_get_next2(Config, GetNext, PostVerify) do_simple_get_next(N, Node, TargetName, Oids, Verify, GetNext, PostVerify) -> - p("issue get-next command ~w", [N]), + ?IPRINT("issue get-next command ~w", [N]), case GetNext(Node, TargetName, Oids) of {ok, Reply, _Rem} -> ?DBG("get-next ok:" @@ -2344,7 +2413,8 @@ simple_sync_get_next3(suite) -> []; simple_sync_get_next3(Config) when is_list(Config) -> process_flag(trap_exit, true), put(tname, ssgn3), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Self = self(), Msg = simple_sync_get_next3, Fun = fun() -> Self ! Msg end, @@ -2374,7 +2444,8 @@ simple_async_get_next2(Config) when is_list(Config) -> fun() -> do_simple_async_get_next2(Config) end). do_simple_async_get_next2(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -2388,9 +2459,9 @@ do_simple_async_get_next2(Config) -> async_gn_exec2(MgrNode, TargetName, Oids) end, PostVerify = fun(Res) -> Res end, - do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify), + Res = do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify), display_log(Config), - ok. + Res. do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify) when is_function(GetNext, 1) andalso is_function(PostVerify, 1) -> @@ -2461,13 +2532,17 @@ do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify) end} ], - p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]), - p("agent info when starting test: ~n~p", [agent_info(AgentNode)]), + ?IPRINT("manager info when starting test: " + "~n ~p", [mgr_info(MgrNode)]), + ?IPRINT("agent info when starting test: " + "~n ~p", [agent_info(AgentNode)]), ?line ok = async_exec(Requests, []), - p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]), - p("agent info when ending test: ~n~p", [agent_info(AgentNode)]), + ?IPRINT("manager info when ending test: " + "~n ~p", [mgr_info(MgrNode)]), + ?IPRINT("agent info when ending test: " + "~n ~p", [agent_info(AgentNode)]), ok. @@ -2505,7 +2580,8 @@ simple_async_get_next3(Case, Config) when is_list(Config) -> do_simple_async_get_next3(Config) -> %% process_flag(trap_exit, true), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -2532,9 +2608,9 @@ do_simple_async_get_next3(Config) -> (Error) -> Error end, - do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify), + Res = do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify), display_log(Config), - ok. + Res. async_gn_exec3(Node, TargetName, Oids, SendOpts) -> mgr_user_async_get_next2(Node, TargetName, Oids, SendOpts). @@ -2563,16 +2639,17 @@ simple_sync_set2(Config) when is_list(Config) -> fun() -> do_simple_sync_set2(Config) end). do_simple_sync_set2(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Set = fun(Node, TargetName, VAVs) -> mgr_user_sync_set(Node, TargetName, VAVs) end, PostVerify = fun() -> ok end, - do_simple_sync_set2(Config, Set, PostVerify), + Res = do_simple_sync_set2(Config, Set, PostVerify), display_log(Config), - ok. + Res. do_simple_sync_set2(Config, Set, PostVerify) when is_function(Set, 3) andalso is_function(PostVerify, 0) -> @@ -2580,7 +2657,7 @@ do_simple_sync_set2(Config, Set, PostVerify) Node = ?config(manager_node, Config), TargetName = ?config(manager_agent_target_name, Config), - p("issue set-request without loading the mib"), + ?IPRINT("issue set-request without loading the mib"), Val11 = "Arne Anka", Val12 = "Stockholm", VAVs1 = [ @@ -2589,7 +2666,7 @@ do_simple_sync_set2(Config, Set, PostVerify) ], ?line ok = do_simple_set2(Node, TargetName, VAVs1, Set, PostVerify), - p("issue set-request after first loading the mibs"), + ?IPRINT("issue set-request after first loading the mibs"), ?line ok = mgr_user_load_mib(Node, std_mib()), Val21 = "Sune Anka", Val22 = "Gothenburg", @@ -2619,7 +2696,8 @@ do_simple_set2(Node, TargetName, VAVs, Set, PostVerify) -> {noError, 0, Vbs} -> {error, {unexpected_vbs, Vbs}}; Else -> - p("unexpected reply: ~n~p", [Else]), + ?EPRINT("unexpected reply: " + "~n ~p", [Else]), {error, {unexpected_response, Else}} end, ok. @@ -2635,7 +2713,8 @@ simple_sync_set3(Config) when is_list(Config) -> fun() -> do_simple_sync_set3(Config) end). do_simple_sync_set3(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p", [Config]), Self = self(), Msg = simple_sync_set3, @@ -2651,15 +2730,15 @@ do_simple_sync_set3(Config) -> end, PostVerify = fun() -> receive Msg -> ok end end, - do_simple_sync_set2(Config, Set, PostVerify), + Res = do_simple_sync_set2(Config, Set, PostVerify), display_log(Config), - ok. + Res. %%====================================================================== sas_verify({noError, 0, _Vbs}, any) -> - p("verified [any]"), + ?IPRINT("verified [any]"), ok; sas_verify({noError, 0, Vbs}, Expected) -> ?DBG("verified stage 1: " @@ -2676,13 +2755,13 @@ sas_verify_vbs(Vbs, []) -> sas_verify_vbs([], Exp) -> {error, {expected_vbs, Exp}}; sas_verify_vbs([#varbind{oid = Oid}|Vbs], [any|Exp]) -> - p("verified [any] oid ~w", [Oid]), + ?IPRINT("verified [any] oid ~w", [Oid]), sas_verify_vbs(Vbs, Exp); sas_verify_vbs([#varbind{oid = Oid, value = Value}|Vbs], [Oid|Exp]) -> - p("verified oid ~w [~p]", [Oid, Value]), + ?IPRINT("verified oid ~w [~p]", [Oid, Value]), sas_verify_vbs(Vbs, Exp); sas_verify_vbs([#varbind{oid = Oid, value = Value}|Vbs], [{Oid,Value}|Exp]) -> - p("verified oid ~w and ~p", [Oid, Value]), + ?IPRINT("verified oid ~w and ~p", [Oid, Value]), sas_verify_vbs(Vbs, Exp); sas_verify_vbs([Vb|_], [E|_]) -> {error, {unexpected_vb, Vb, E}}. @@ -2698,7 +2777,9 @@ simple_async_set2(Config) when is_list(Config) -> fun() -> do_simple_async_set2(Config) end). do_simple_async_set2(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p" + "~n", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -2715,9 +2796,9 @@ do_simple_async_set2(Config) -> end, PostVerify = fun(Res) -> Res end, - do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify), + Res = do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify), display_log(Config), - ok. + Res. do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify) -> Requests = @@ -2748,13 +2829,17 @@ do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify) -> end} ], - p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]), - p("agent info when starting test: ~n~p", [agent_info(AgentNode)]), + ?IPRINT("manager info when starting test: " + "~n ~p", [mgr_info(MgrNode)]), + ?IPRINT("agent info when starting test: " + "~n ~p", [agent_info(AgentNode)]), ?line ok = async_exec(Requests, []), - p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]), - p("agent info when ending test: ~n~p", [agent_info(AgentNode)]), + ?IPRINT("manager info when ending test: " + "~n ~p", [mgr_info(MgrNode)]), + ?IPRINT("agent info when ending test: " + "~n ~p", [agent_info(AgentNode)]), ok. @@ -2788,7 +2873,8 @@ simple_async_set3(Case, Config) -> fun() -> do_simple_async_set3(Config) end). do_simple_async_set3(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p~n", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -2816,9 +2902,9 @@ do_simple_async_set3(Config) -> (Res) -> Res end, - do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify), + Res = do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify), display_log(Config), - ok. + Res. async_s_exec3(Node, TargetName, VAVs, SendOpts) -> mgr_user_async_set2(Node, TargetName, VAVs, SendOpts). @@ -2871,7 +2957,8 @@ simple_sync_get_bulk2(Config) when is_list(Config) -> fun() -> do_simple_sync_get_bulk2(Config) end). do_simple_sync_get_bulk2(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p~n", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -2884,9 +2971,9 @@ do_simple_sync_get_bulk2(Config) -> end, PostVerify = fun(Res) -> Res end, - do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify), + Res = do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify), display_log(Config), - ok. + Res. do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify) -> %% -- 1 -- @@ -2986,7 +3073,7 @@ do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify) -> %% -- 11 -- ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2), - p("TCnt2: ~p", [TCnt2]), + ?IPRINT("TCnt2: ~p", [TCnt2]), VF11 = fun(X) -> verify_ssgb_reply2(X, [{fl([TCnt2,2]), 100}, @@ -3005,7 +3092,7 @@ do_simple_get_bulk2(N, when is_function(Verify, 1) andalso is_function(GetBulk, 3) andalso is_function(PostVerify) -> - p("issue get-bulk command ~w", [N]), + ?IPRINT("issue get-bulk command ~w", [N]), case GetBulk(NonRep, MaxRep, Oids) of {ok, Reply, _Rem} -> ?DBG("get-bulk ok:" @@ -3029,7 +3116,8 @@ simple_sync_get_bulk3(Config) when is_list(Config) -> fun() -> do_simple_sync_get_bulk3(Config) end). do_simple_sync_get_bulk3(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p~n", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -3053,9 +3141,9 @@ do_simple_sync_get_bulk3(Config) -> (Res) -> Res end, - do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify), + Res = do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify), display_log(Config), - ok. + Res. %%====================================================================== @@ -3068,7 +3156,8 @@ simple_async_get_bulk2(Config) when is_list(Config) -> fun() -> do_simple_async_get_bulk2(Config) end). do_simple_async_get_bulk2(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~p ~n", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -3085,9 +3174,9 @@ do_simple_async_get_bulk2(Config) -> end, PostVerify = fun(Res) -> Res end, - do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify), + Res = do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify), display_log(Config), - ok. + Res. do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify) -> %% We re-use the verification functions from the ssgb test-case @@ -3200,13 +3289,17 @@ do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify) -> VF10} ], - p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]), - p("agent info when starting test: ~n~p", [agent_info(AgentNode)]), + ?IPRINT("manager info when starting test: " + "~n ~p", [mgr_info(MgrNode)]), + ?IPRINT("agent info when starting test: " + "~n ~p", [agent_info(AgentNode)]), ?line ok = async_exec(Requests, []), - p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]), - p("agent info when ending test: ~n~p", [agent_info(AgentNode)]), + ?IPRINT("manager info when ending test: " + "~n ~p", [mgr_info(MgrNode)]), + ?IPRINT("agent info when ending test: " + "~n ~p", [agent_info(AgentNode)]), ok. @@ -3230,7 +3323,8 @@ simple_async_get_bulk3(Case, Config) -> do_simple_async_get_bulk3(Config) -> process_flag(trap_exit, true), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p~n", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -3258,9 +3352,9 @@ do_simple_async_get_bulk3(Config) -> (Res) -> Res end, - do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify), + Res = do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify), display_log(Config), - ok. + Res. async_gb_exec3(Node, TargetName, {NR, MR, Oids}, SendOpts) -> mgr_user_async_get_bulk2(Node, TargetName, NR, MR, Oids, SendOpts). @@ -3296,7 +3390,9 @@ misc_async2(Config) when is_list(Config) -> fun() -> do_misc_async2(Config) end). do_misc_async2(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p" + "~n", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -3465,13 +3561,17 @@ do_misc_async2(Config) -> end} ], - p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]), - p("agent info when starting test: ~n~p", [agent_info(AgentNode)]), + ?IPRINT("manager info when starting test: " + "~n ~p", [mgr_info(MgrNode)]), + ?IPRINT("agent info when starting test: " + "~n ~p", [agent_info(AgentNode)]), ?line ok = async_exec(Requests, []), - p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]), - p("agent info when ending test: ~n~p", [agent_info(AgentNode)]), + ?IPRINT("manager info when ending test: " + "~n ~p", [mgr_info(MgrNode)]), + ?IPRINT("agent info when ending test: " + "~n ~p", [agent_info(AgentNode)]), display_log(Config), ok. @@ -3497,45 +3597,47 @@ collect_traps(0, TrapInfo) -> collect_traps(N, Acc) -> receive {async_event, _From, {trap, TrapInfo}} -> - p("collect_traps -> received trap: ~n ~p", [TrapInfo]), + ?IPRINT("collect_traps -> received trap: " + "~n ~p", [TrapInfo]), collect_traps(N-1, [TrapInfo|Acc]) after 10000 -> - p("collect_traps -> still awaiting ~w trap(s) - giving up", [N]), + ?WPRINT("collect_traps -> still awaiting ~w trap(s) - giving up", [N]), Acc end. verify_traps([], []) -> - p("verify_traps -> done"), + ?IPRINT("verify_traps -> done"), ok; verify_traps([], Verifiers) -> - p("verify_traps -> done when ~w verifiers remain", [length(Verifiers)]), + ?IPRINT("verify_traps -> done when ~w verifiers remain", [length(Verifiers)]), {error, {failed_verify, [Id || {Id, _} <- Verifiers]}}; verify_traps([Trap|Traps], Verifiers0) -> - p("verify_traps -> entry"), + ?IPRINT("verify_traps -> entry"), case verify_trap(Trap, Verifiers0) of {ok, Id} -> - p("verify_traps -> trap verified: ~p", [Id]), + ?IPRINT("verify_traps -> trap verified: ~p", [Id]), Verifiers = lists:keydelete(Id, 1, Verifiers0), verify_traps(Traps, Verifiers); error -> - p("verify_traps -> failed verifying trap: ~n ~p", [Trap]), + ?EPRINT("verify_traps -> failed verifying trap: " + "~n ~p", [Trap]), {error, {failed_verifying_trap, Trap}} end. verify_trap(Trap, []) -> - p("verify_trap -> could not verify trap:" - "~n Trap: ~p", [Trap]), + ?EPRINT("verify_trap -> could not verify trap:" + "~n Trap: ~p", [Trap]), error; verify_trap(Trap, [{Id, Verifier}|Verifiers]) -> - p("verify_trap -> entry with" - "~n Id: ~p" - "~n Trap: ~p", [Id, Trap]), + ?IPRINT("verify_trap -> entry with" + "~n Id: ~p" + "~n Trap: ~p", [Id, Trap]), case Verifier(Trap) of ok -> - p("verify_trap -> verified"), + ?IPRINT("verify_trap -> verified"), {ok, Id}; {error, _} -> - p("verify_trap -> not verified"), + ?NPRINT("verify_trap -> not verified"), verify_trap(Trap, Verifiers) end. @@ -3548,7 +3650,9 @@ trap1(Config) when is_list(Config) -> fun() -> do_trap1(Config) end). do_trap1(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p" + "~n", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -3569,32 +3673,33 @@ do_trap1(Config) -> fun(Ent, Gen, Spec, ExpVBs, Trap) -> case Trap of {Ent, Gen, Spec, _Timestamp, VBs} -> - p("trap info as expected"), + ?IPRINT("trap info as expected"), case (catch validate_vbs(MgrNode, ExpVBs, VBs)) of ok -> - p("valid trap"), + ?IPRINT("valid trap"), ok; Error -> - p("invalid trap: ~n Error: ~p", [Error]), + ?EPRINT("invalid trap: " + "~n ~p", [Error]), Error end; {Enteprise, Generic, Spec, Timestamp, VBs} -> - p("unepxected v1 trap info:" - "~n Enteprise: ~p" - "~n Generic: ~p" - "~n Spec: ~p" - "~n Timestamp: ~p" - "~n VBs: ~p", - [Enteprise, Generic, Spec, Timestamp, VBs]), + ?EPRINT("unepxected v1 trap info:" + "~n Enteprise: ~p" + "~n Generic: ~p" + "~n Spec: ~p" + "~n Timestamp: ~p" + "~n VBs: ~p", + [Enteprise, Generic, Spec, Timestamp, VBs]), ExpTrap = {Ent, Gen, Spec, ignore, ExpVBs}, Reason = {unexpected_trap, {ExpTrap, Trap}}, {error, Reason}; {Err, Idx, VBs} -> - p("unexpected trap info: " - "~n Err: ~p" - "~n Idx: ~p" - "~n VBs: ~p", [Err, Idx, VBs]), + ?EPRINT("unexpected trap info: " + "~n Err: ~p" + "~n Idx: ~p" + "~n VBs: ~p", [Err, Idx, VBs]), Reason = {unexpected_status, {Err, Idx, VBs}}, {error, Reason} end @@ -3605,24 +3710,25 @@ do_trap1(Config) -> fun(ExpVBs, Trap) -> case Trap of {noError, 0, VBs0} -> - p("trap info as expected: ~n~p", [VBs0]), + ?IPRINT("trap info as expected: " + "~n ~p", [VBs0]), %% The first two are a timestamp and oid [_,_|VBs] = VBs0, case (catch validate_vbs(MgrNode, ExpVBs, VBs)) of ok -> - p("valid trap"), + ?IPRINT("valid trap"), ok; Error -> - p("invalid trap: ~n Error: ~p", - [Error]), + ?EPRINT("invalid trap: " + "~n ~p", [Error]), Error end; {Err, Idx, VBs} -> - p("unexpected error status: " - "~n Err: ~p" - "~n Idx: ~p" - "~n VBs: ~p", [Err, Idx, VBs]), + ?EPRINT("unexpected error status: " + "~n Err: ~p" + "~n Idx: ~p" + "~n VBs: ~p", [Err, Idx, VBs]), Reason = {unexpected_status, {Err, Idx, VBs}}, {error, Reason} end @@ -3633,8 +3739,10 @@ do_trap1(Config) -> %% Collect various info about the manager and the agent Cmd1 = fun() -> - p("manager info: ~n~p", [mgr_info(MgrNode)]), - p("agent info: ~n~p", [agent_info(AgentNode)]), + ?IPRINT("manager info: " + "~n ~p", [mgr_info(MgrNode)]), + ?IPRINT("agent info: " + "~n ~p", [agent_info(AgentNode)]), ok end, @@ -3690,9 +3798,9 @@ do_trap1(Config) -> {5, "Manager and agent info after test completion", Cmd1} ], - command_handler(Commands), + Res = command_handler(Commands), display_log(Config), - ok. + Res. %%====================================================================== @@ -3703,7 +3811,9 @@ trap2(Config) when is_list(Config) -> fun() -> do_trap2(Config) end). do_trap2(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p" + "~n", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -3724,32 +3834,32 @@ do_trap2(Config) -> fun(Ent, Gen, Spec, ExpVBs, Trap) -> case Trap of {Ent, Gen, Spec, _Timestamp, VBs} -> - p("trap info as expected"), + ?IPRINT("trap info as expected"), case (catch validate_vbs(MgrNode, ExpVBs, VBs)) of ok -> - p("valid trap"), + ?IPRINT("valid trap"), ok; Error -> - p("invalid trap: ~n Error: ~p", [Error]), + ?EPRINT("invalid trap: ~n Error: ~p", [Error]), Error end; {Enteprise, Generic, Spec, Timestamp, VBs} -> - p("unepxected v1 trap info:" - "~n Enteprise: ~p" - "~n Generic: ~p" - "~n Spec: ~p" - "~n Timestamp: ~p" - "~n VBs: ~p", - [Enteprise, Generic, Spec, Timestamp, VBs]), + ?EPRINT("unepxected v1 trap info:" + "~n Enteprise: ~p" + "~n Generic: ~p" + "~n Spec: ~p" + "~n Timestamp: ~p" + "~n VBs: ~p", + [Enteprise, Generic, Spec, Timestamp, VBs]), ExpTrap = {Ent, Gen, Spec, ignore, ExpVBs}, Reason = {unexpected_trap, {ExpTrap, Trap}}, {error, Reason}; {Err, Idx, VBs} -> - p("unexpected trap info: " - "~n Err: ~p" - "~n Idx: ~p" - "~n VBs: ~p", [Err, Idx, VBs]), + ?EPRINT("unexpected trap info: " + "~n Err: ~p" + "~n Idx: ~p" + "~n VBs: ~p", [Err, Idx, VBs]), Reason = {unexpected_status, {Err, Idx, VBs}}, {error, Reason} end @@ -3760,24 +3870,24 @@ do_trap2(Config) -> fun(ExpVBs, Trap) -> case Trap of {noError, 0, VBs0} -> - p("trap info as expected: ~n~p", [VBs0]), + ?IPRINT("trap info as expected: ~n~p", [VBs0]), %% The first two are a timestamp and oid [_,_|VBs] = VBs0, case (catch validate_vbs(MgrNode, ExpVBs, VBs)) of ok -> - p("valid trap"), + ?IPRINT("valid trap"), ok; Error -> - p("invalid trap: ~n Error: ~p", - [Error]), + ?EPRINT("invalid trap: " + "~n ~p", [Error]), Error end; {Err, Idx, VBs} -> - p("unexpected error status: " - "~n Err: ~p" - "~n Idx: ~p" - "~n VBs: ~p", [Err, Idx, VBs]), + ?EPRINT("unexpected error status: " + "~n Err: ~p" + "~n Idx: ~p" + "~n VBs: ~p", [Err, Idx, VBs]), Reason = {unexpected_status, {Err, Idx, VBs}}, {error, Reason} end @@ -3787,8 +3897,10 @@ do_trap2(Config) -> %% Collect various info about the manager and the agent Cmd1 = fun() -> - p("manager info: ~n~p", [mgr_info(MgrNode)]), - p("agent info: ~n~p", [agent_info(AgentNode)]), + ?IPRINT("manager info: " + "~n ~p", [mgr_info(MgrNode)]), + ?IPRINT("agent info: " + "~n ~p", [agent_info(AgentNode)]), ok end, @@ -3885,9 +3997,9 @@ do_trap2(Config) -> {7, "Manager and agent info after test completion", Cmd1} ], - command_handler(Commands), + Res = command_handler(Commands), display_log(Config), - ok. + Res. %%====================================================================== @@ -3898,7 +4010,9 @@ inform1(Config) when is_list(Config) -> fun() -> do_inform1(Config) end). do_inform1(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p" + "~n", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -3917,9 +4031,12 @@ do_inform1(Config) -> Cmd1 = fun() -> - p("manager info: ~n~p", [mgr_info(MgrNode)]), - p("manager system info: ~n~p", [mgr_sys_info(MgrNode)]), - p("agent info: ~n~p", [agent_info(AgentNode)]), + ?IPRINT("manager info: " + "~n ~p", [mgr_info(MgrNode)]), + ?IPRINT("manager system info: " + "~n ~p", [mgr_sys_info(MgrNode)]), + ?IPRINT("agent info: " + "~n ~p", [agent_info(AgentNode)]), ok end, @@ -3933,26 +4050,26 @@ do_inform1(Config) -> fun() -> receive {async_event, From, {inform, Pid, Inform}} -> - p("received inform"), + ?IPRINT("received inform"), case Inform of {noError, 0, VBs} when is_list(VBs) -> case (catch validate_testTrapv22_vbs(MgrNode, VBs)) of ok -> - p("valid inform"), + ?IPRINT("valid inform"), Pid ! {handle_inform_no_response, From}, ok; Error -> - p("invalid inform: ~n Error: ~p", - [Error]), + ?EPRINT("invalid inform: " + "~n ~p", [Error]), Error end; {Err, Idx, VBs} -> - p("unexpected error status: " - "~n Err: ~p" - "~n Idx: ~p" - "~n VBs: ~p", [Err, Idx, VBs]), + ?EPRINT("unexpected error status: " + "~n Err: ~p" + "~n Idx: ~p" + "~n VBs: ~p", [Err, Idx, VBs]), Reason = {unexpected_status, {Err, Idx, VBs}}, {error, Reason} end @@ -3970,25 +4087,25 @@ do_inform1(Config) -> fun() -> receive {async_event, From, {inform, Pid, Inform}} -> - p("received inform"), + ?IPRINT("received inform"), case Inform of {noError, 0, VBs} when is_list(VBs) -> case (catch validate_testTrapv22_vbs(MgrNode, VBs)) of ok -> - p("valid inform"), + ?IPRINT("valid inform"), Pid ! {handle_inform_response, From}, ok; Error -> - p("invalid inform: ~n Error: ~p", - [Error]), + ?EPRINT("invalid inform: " + "~n ~p", [Error]), Error end; {Err, Idx, VBs} -> - p("unexpected error status: " - "~n Err: ~p" - "~n Idx: ~p" - "~n VBs: ~p", [Err, Idx, VBs]), + ?EPRINT("unexpected error status: " + "~n Err: ~p" + "~n Idx: ~p" + "~n VBs: ~p", [Err, Idx, VBs]), Reason = {unexpected_status, {Err, Idx, VBs}}, {error, Reason} end @@ -4014,9 +4131,9 @@ do_inform1(Config) -> {6, "Manager and agent info after test completion", Cmd1} ], - command_handler(Commands), + Res = command_handler(Commands), display_log(Config), - ok. + Res. %%====================================================================== @@ -4027,7 +4144,9 @@ inform2(Config) when is_list(Config) -> fun() -> do_inform2(Config) end). do_inform2(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p" + "~n", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -4047,8 +4166,10 @@ do_inform2(Config) -> Cmd1 = fun() -> - p("manager info: ~n~p", [mgr_info(MgrNode)]), - p("agent info: ~n~p", [agent_info(AgentNode)]), + ?IPRINT("manager info: " + "~n ~p", [mgr_info(MgrNode)]), + ?IPRINT("agent info: " + "~n ~p", [agent_info(AgentNode)]), ok end, @@ -4066,7 +4187,7 @@ do_inform2(Config) -> fun() -> receive {snmp_targets, inform2_tag1, Addrs} -> - p("sent inform to ~p", [Addrs]), + ?IPRINT("sent inform to ~p", [Addrs]), ok after 10000 -> receive @@ -4082,26 +4203,26 @@ do_inform2(Config) -> fun() -> receive {async_event, From, {inform, Pid, Inform}} -> - p("received inform"), + ?IPRINT("received inform"), case Inform of {noError, 0, VBs} when is_list(VBs) -> case (catch validate_testTrapv22_vbs(MgrNode, VBs)) of ok -> - p("valid inform"), + ?IPRINT("valid inform"), Pid ! {handle_inform_no_response, From}, ok; Error -> - p("invalid inform: ~n Error: ~p", - [Error]), + ?IPRINT("invalid inform: " + "~n ~p", [Error]), Error end; {Err, Idx, VBs} -> - p("unexpected error status: " - "~n Err: ~p" - "~n Idx: ~p" - "~n VBs: ~p", [Err, Idx, VBs]), + ?EPRINT("unexpected error status: " + "~n Err: ~p" + "~n Idx: ~p" + "~n VBs: ~p", [Err, Idx, VBs]), Reason = {unexpected_status, {Err, Idx, VBs}}, {error, Reason} end @@ -4119,25 +4240,25 @@ do_inform2(Config) -> fun() -> receive {async_event, From, {inform, Pid, Inform}} -> - p("received inform"), + ?IPRINT("received inform"), case Inform of {noError, 0, VBs} when is_list(VBs) -> case (catch validate_testTrapv22_vbs(MgrNode, VBs)) of ok -> - p("valid inform"), + ?IPRINT("valid inform"), Pid ! {handle_inform_response, From}, ok; Error -> - p("invalid inform: ~n Error: ~p", - [Error]), + ?EPRINT("invalid inform: " + "~n ~p", [Error]), Error end; {Err, Idx, VBs} -> - p("unexpected error status: " - "~n Err: ~p" - "~n Idx: ~p" - "~n VBs: ~p", [Err, Idx, VBs]), + ?EPRINT("unexpected error status: " + "~n Err: ~p" + "~n Idx: ~p" + "~n VBs: ~p", [Err, Idx, VBs]), Reason = {unexpected_status, {Err, Idx, VBs}}, {error, Reason} end @@ -4155,14 +4276,14 @@ do_inform2(Config) -> fun() -> receive {snmp_notification, inform2_tag1, {got_response, Addr}} -> - p("received expected \"got response\" notification " - "from: " - "~n ~p", [Addr]), + ?IPRINT("received expected \"got response\" notification " + "from: " + "~n ~p", [Addr]), ok; {snmp_notification, inform2_tag1, {no_response, Addr}} -> - e("Received unexpected \"no response\" " - "notification from: " - "~n ~p", [Addr]), + ?EPRINT("Received unexpected \"no response\" " + "notification from: " + "~n ~p", [Addr]), {error, no_response} after 10000 -> receive @@ -4188,9 +4309,9 @@ do_inform2(Config) -> {8, "Manager and agent info after test completion", Cmd1} ], - command_handler(Commands), + Res = command_handler(Commands), display_log(Config), - ok. + Res. %%====================================================================== @@ -4201,7 +4322,8 @@ inform3(Config) when is_list(Config) -> fun() -> do_inform3(Config) end). do_inform3(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p~n", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -4219,8 +4341,10 @@ do_inform3(Config) -> Cmd1 = fun() -> - p("manager info: ~n~p", [mgr_info(MgrNode)]), - p("agent info: ~n~p", [agent_info(AgentNode)]), + ?IPRINT("manager info: " + "~n ~p", [mgr_info(MgrNode)]), + ?IPRINT("agent info: " + "~n ~p", [agent_info(AgentNode)]), ok end, @@ -4238,7 +4362,7 @@ do_inform3(Config) -> fun() -> receive {snmp_targets, inform3_tag1, [_Addr]} -> - p("received inform-sent acknowledgement", []), + ?IPRINT("received inform-sent ack"), ok after 10000 -> receive @@ -4254,26 +4378,25 @@ do_inform3(Config) -> fun() -> receive {async_event, From, {inform, Pid, Inform}} -> - p("received inform"), + ?IPRINT("received inform"), case Inform of {noError, 0, VBs} when is_list(VBs) -> case (catch validate_testTrapv22_vbs(MgrNode, VBs)) of ok -> - p("valid inform"), - Pid ! {handle_inform_no_response, - From}, + ?IPRINT("valid inform"), + Pid ! {handle_inform_no_response, From}, ok; Error -> - p("invalid inform: ~n Error: ~p", - [Error]), + ?EPRINT("invalid inform: " + "~n ~p", [Error]), Error end; {Err, Idx, VBs} -> - p("unexpected error status: " - "~n Err: ~p" - "~n Idx: ~p" - "~n VBs: ~p", [Err, Idx, VBs]), + ?EPRINT("unexpected error status: " + "~n Err: ~p" + "~n Idx: ~p" + "~n VBs: ~p", [Err, Idx, VBs]), Reason = {unexpected_status, {Err, Idx, VBs}}, {error, Reason} end @@ -4291,15 +4414,15 @@ do_inform3(Config) -> fun() -> receive {snmp_notification, inform3_tag1, {no_response, Addr}} -> - p("received expected \"no response\" notification " - "from: " - "~n ~p", [Addr]), + ?IPRINT("received expected \"no response\" notification " + "from: " + "~n ~p", [Addr]), ok; {snmp_notification, inform3_tag1, {got_response, Addr}} -> - e("Received unexpected \"got response\" " - "notification from: " - "~n ~p", - [Addr]), + ?EPRINT("Received unexpected \"got response\" " + "notification from: " + "~n ~p", + [Addr]), {error, {got_response, Addr}} after 120000 -> receive @@ -4326,9 +4449,9 @@ do_inform3(Config) -> {9, "Manager and agent info after test completion", Cmd1} ], - command_handler(Commands), + Res = command_handler(Commands), display_log(Config), - ok. + Res. %%====================================================================== @@ -4339,7 +4462,8 @@ inform4(Config) when is_list(Config) -> fun() -> do_inform4(Config) end). do_inform4(Config) -> - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~n ~p~n", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -4357,8 +4481,10 @@ do_inform4(Config) -> Cmd1 = fun() -> - p("manager info: ~n~p", [mgr_info(MgrNode)]), - p("agent info: ~n~p", [agent_info(AgentNode)]), + ?IPRINT("manager info: " + "~n ~p", [mgr_info(MgrNode)]), + ?IPRINT("agent info: " + "~n ~p", [agent_info(AgentNode)]), ok end, @@ -4372,13 +4498,13 @@ do_inform4(Config) -> fun() -> receive {async_event, From, {inform, Pid, Inform}} -> - p("received inform"), + ?IPRINT("received inform"), case Inform of {noError, 0, VBs} when is_list(VBs) -> case (catch validate_testTrapv22_vbs(MgrNode, VBs)) of ok -> - p("valid inform"), + ?IPRINT("valid inform"), %% Actually, as we have %% configured the manager in %% this test case (irb = auto) @@ -4386,15 +4512,15 @@ do_inform4(Config) -> Pid ! {handle_inform_response, From}, ok; Error -> - p("invalid inform: ~n Error: ~p", - [Error]), + ?EPRINT("invalid inform: " + "~n ~p", [Error]), Error end; {Err, Idx, VBs} -> - p("unexpected error status: " - "~n Err: ~p" - "~n Idx: ~p" - "~n VBs: ~p", [Err, Idx, VBs]), + ?EPRINT("unexpected error status: " + "~n Err: ~p" + "~n Idx: ~p" + "~n VBs: ~p", [Err, Idx, VBs]), Reason = {unexpected_status, {Err, Idx, VBs}}, {error, Reason} end @@ -4413,11 +4539,11 @@ do_inform4(Config) -> %% fun() -> %% receive %% {async_event, _ReqId, {error, Reason}} -> -%% p("received error"), +%% ?IPRINT("received error"), %% case Reason of %% {failed_processing_message, %% {securityError, usmStatsUnknownEngineIDs}} -> -%% p("expected error"), +%% ?IPRINT("expected error"), %% ok; %% _ -> %% p("unexpected error: " @@ -4446,9 +4572,9 @@ do_inform4(Config) -> {6, "Manager and agent info after test completion", Cmd1} ], - command_handler(Commands), + Res = command_handler(Commands), display_log(Config), - ok. + Res. %%====================================================================== @@ -4473,7 +4599,8 @@ inform_swarm(Case, Config) -> do_inform_swarm(Config) -> %% process_flag(trap_exit, true), - p("starting with Config: ~p~n", [Config]), + ?IPRINT("starting with Config: " + "~p ~n", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -4502,7 +4629,7 @@ do_inform_swarm(Config) -> Seqs = lists:seq(1, NumInforms), lists:foreach( fun(N) -> - p("send notification ~w", [N]), + ?IPRINT("send notification ~w", [N]), agent_send_notif(AgentNode, testTrapv22, {{inform2_tag1, N}, Collector}, @@ -4513,11 +4640,11 @@ do_inform_swarm(Config) -> if N rem 100 == 0 -> Sleep = 1000, - p("sleep ~w [~w]", [Sleep, N]), + ?IPRINT("sleep ~w [~w]", [Sleep, N]), ?SLEEP(Sleep); N rem 10 == 0 -> Sleep = 100, - p("sleep ~w [~w]", [Sleep, N]), + ?IPRINT("sleep ~w [~w]", [Sleep, N]), ?SLEEP(Sleep); true -> ok @@ -4529,8 +4656,10 @@ do_inform_swarm(Config) -> Cmd1 = fun() -> - p("manager info: ~n~p", [mgr_info(MgrNode)]), - p("agent info: ~n~p", [agent_info(AgentNode)]), + ?IPRINT("manager info: " + "~n ~p", [mgr_info(MgrNode)]), + ?IPRINT("agent info: " + "~n ~p", [agent_info(AgentNode)]), ok end, @@ -4553,13 +4682,13 @@ do_inform_swarm(Config) -> {5, "Manager and agent info after test completion", Cmd1} ], - command_handler(Commands), + Res = command_handler(Commands), display_log(Config), - ok. + Res. inform_swarm_collector(N) -> - inform_swarm_collector(N, 0, 0, 0, 10000). + inform_swarm_collector(N, 0, 0, 0, ?SECS(60)). %% Note that we need to deal with re-transmissions! %% That is, the agent did not receive the ack in time, @@ -4575,60 +4704,65 @@ inform_swarm_collector(N, SentAckCnt, RecvCnt, RespCnt, _) when ((N == SentAckCnt) and (N == RespCnt) and (N =< RecvCnt)) -> - p("inform_swarm_collector -> done when" - "~n N: ~w" - "~n SentAckCnt: ~w" - "~n RecvCnt: ~w" - "~n RespCnt: ~w", [N, SentAckCnt, RecvCnt, RespCnt]), + ?IPRINT("inform_swarm_collector -> done when" + "~n N: ~w" + "~n SentAckCnt: ~w" + "~n RecvCnt: ~w" + "~n RespCnt: ~w", [N, SentAckCnt, RecvCnt, RespCnt]), ok; inform_swarm_collector(N, SentAckCnt, RecvCnt, RespCnt, Timeout) -> - p("inform_swarm_collector -> entry with" - "~n N: ~w" - "~n SentAckCnt: ~w" - "~n RecvCnt: ~w" - "~n RespCnt: ~w", [N, SentAckCnt, RecvCnt, RespCnt]), + %% ?IPRINT("inform_swarm_collector -> entry with" + %% "~n N: ~w" + %% "~n SentAckCnt: ~w" + %% "~n RecvCnt: ~w" + %% "~n RespCnt: ~w", [N, SentAckCnt, RecvCnt, RespCnt]), receive {snmp_targets, {inform2_tag1, Id}, [_Addr]} -> - p("received inform-sent acknowledgement for ~w", [Id]), + ?IPRINT("received inform-sent acknowledgement for ~w", [Id]), inform_swarm_collector(N, SentAckCnt+1, RecvCnt, RespCnt, Timeout); %% The manager has received the actual inform {async_event, From, {inform, Pid, Inform}} -> - p("received inform"), + ?IPRINT("received inform"), case Inform of {noError, 0, VBs} when is_list(VBs) -> Pid ! {handle_inform_response, From}, inform_swarm_collector(N, SentAckCnt, RecvCnt+1, RespCnt, Timeout); {Err, Idx, VBs} -> - e("Unexpected error status: " - "~n Err: ~p" - "~n Idx: ~p" - "~n VBs: ~p", [Err, Idx, VBs]), + ?EPRINT("Unexpected error status: " + "~n Err: ~p" + "~n Idx: ~p" + "~n VBs: ~p", [Err, Idx, VBs]), Reason = {unexpected_status, {Err, Idx, VBs}}, {error, Reason} end; %% The agent has received ack from the manager {snmp_notification, {inform2_tag1, Id}, {got_response, Addr}} -> - p("received expected \"got response\" for ~w " - "notification from: " - "~n ~p", - [Id, Addr]), + ?IPRINT("received expected \"got response\" for ~w " + "notification from: " + "~n ~p", [Id, Addr]), inform_swarm_collector(N, SentAckCnt, RecvCnt, RespCnt+1, Timeout); %% The agent did not received ack from the manager in time {snmp_notification, inform2_tag1, {no_response, Addr}} -> - e("Received expected \"no response\" notification " - "from: " - "~n ~p", [Addr]), + ?EPRINT("Received expected \"no response\" notification " + "from: " + "~n ~p", [Addr]), Reason = {no_response, Addr, {N, SentAckCnt, RecvCnt, RespCnt}}, {error, Reason} after Timeout -> %% Give up when we have been dead in the water for Timeout ms + ?EPRINT("timeout when" + "~n N: ~p" + "~n SentAckCnt: ~p" + "~n RecvCnt: ~p" + "~n RespCnt: ~p", + [N, SentAckCnt, RecvCnt, RespCnt]), {error, {timeout, N, SentAckCnt, RecvCnt, RespCnt}} end. @@ -4646,48 +4780,54 @@ report(Config) when is_list(Config) -> otp8015_1(doc) -> ["OTP-8015:1 - testing the new api-function."]; otp8015_1(suite) -> []; otp8015_1(Config) when is_list(Config) -> - ?TC_TRY(otp8015_1, - fun() -> do_otp8015_1(Config) end). + Pre = fun() -> + ConfDir = ?config(manager_conf_dir, Config), + DbDir = ?config(manager_db_dir, Config), + + write_manager_conf(ConfDir), + + Opts = [{server, [{verbosity, trace}]}, + {net_if, [{verbosity, trace}]}, + {note_store, [{verbosity, trace}]}, + {config, [{verbosity, trace}, + {dir, ConfDir}, + {db_dir, DbDir}]}], + + ?IPRINT("starting manager"), + ok = snmpm:start_link(Opts), + + ?SLEEP(1000), + ok + end, + Case = fun(_) -> do_otp8015_1(Config) end, + Post = fun(_) -> + ?IPRINT("stop manager"), + ok = snmpm:stop(), + ?SLEEP(1000), + ok + end, + ?TC_TRY(otp8015_1, Pre, Case, Post). do_otp8015_1(Config) -> - p("starting with Config: ~p~n", [Config]), - - ConfDir = ?config(manager_conf_dir, Config), - DbDir = ?config(manager_db_dir, Config), - - write_manager_conf(ConfDir), - - Opts = [{server, [{verbosity, trace}]}, - {net_if, [{verbosity, trace}]}, - {note_store, [{verbosity, trace}]}, - {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - - p("starting manager"), - ok = snmpm:start_link(Opts), - - ?SLEEP(1000), + ?IPRINT("starting with Config: " + "~n ~p" + "~n", [Config]), snmpm:load_mib(std_mib()), snmpm:load_mib(test_trap_mib(Config)), - p("manager started, now sleep some"), + ?IPRINT("manager started, now sleep some"), ?SLEEP(1000), - p("loaded mibs: ~p", [snmpm:which_mibs()]), + ?IPRINT("loaded mibs: ~p", [snmpm:which_mibs()]), - p("get some type(s) from the mibs"), + ?IPRINT("get some type(s) from the mibs"), {ok, 'Counter32'} = snmpm:oid_to_type(?snmpOutTraps), - {ok, [IfIndex]} = snmpm:name_to_oid(ifIndex), - {ok, 'INTEGER'} = snmpm:oid_to_type(IfIndex), + {ok, [IfIndex]} = snmpm:name_to_oid(ifIndex), + {ok, 'INTEGER'} = snmpm:oid_to_type(IfIndex), - - p("stop manager"), - ok = snmpm:stop(), - - ?SLEEP(1000), - - p("end"), + ?IPRINT("end"), ok. @@ -4708,27 +4848,30 @@ do_otp8395_1(Config) -> %%====================================================================== async_exec([], Acc) -> - p("all async request's sent => now await reponses"), + ?IPRINT("all async request's sent => now await responses"), async_verify(async_collector(Acc, [])); async_exec([{Id, Data, Exec, Ver}|Reqs], Acc) -> - p("issue async request ~w", [Id]), + ?IPRINT("issue async request ~w", [Id]), ?line {ok, ReqId} = Exec(Data), async_exec(Reqs, [{ReqId, Id, Ver}|Acc]). async_collector([], Acc) -> - p("received replies for all requests - now sort"), + ?IPRINT("received replies for all requests - now sort"), lists:keysort(1, Acc); async_collector(Expected, Acc) -> receive {async_event, ReqId, Reply} -> - p("received async event with request-id ~w", [ReqId]), + ?IPRINT("received async event with request-id ~w", [ReqId]), case lists:keysearch(ReqId, 1, Expected) of {value, {_, Id, Ver}} -> - p("event was for request ~w", [Id]), + ?IPRINT("event was for request ~w", [Id]), Expected2 = lists:keydelete(ReqId, 1, Expected), async_collector(Expected2, [{Id, Ver, Reply}|Acc]); false -> % Duplicate reply? + ?EPRINT("unexpected async event: " + "~n ReqId: ~p" + "~n Reply: ~p", [ReqId, Reply]), ?FAIL({unexpected_async_event, ReqId, Reply}) end after 10000 -> @@ -4738,7 +4881,7 @@ async_collector(Expected, Acc) -> async_verify([]) -> ok; async_verify([{Id, Verify, Reply}|Replies]) -> - p("verify reply ~w", [Id]), + ?IPRINT("verify reply ~w", [Id]), Verify(Reply), async_verify(Replies). @@ -4795,17 +4938,19 @@ purify_oid(Node, Oid) -> command_handler([]) -> ok; command_handler([{No, Desc, Cmd}|Cmds]) -> - p("command_handler -> command ~w: " - "~n ~s", [No, Desc]), + ?IPRINT("command_handler -> command ~w: " + "~n ~s", [No, Desc]), case (catch Cmd()) of ok -> - p("command_handler -> ~w: ok",[No]), + ?IPRINT("command_handler -> ~w: ok", [No]), command_handler(Cmds); {error, Reason} -> - e("Command_handler -> ~w error: ~n~p",[No, Reason]), + ?EPRINT("Command_handler -> ~w error: " + "~n ~p", [No, Reason]), ?line ?FAIL({command_failed, No, Reason}); Error -> - e("Command_handler -> ~w unexpected: ~n~p",[No, Error]), + ?EPRINT("Command_handler -> ~w unexpected: " + "~n ~p", [No, Error]), ?line ?FAIL({unexpected_command_result, No, Error}) end. @@ -4814,9 +4959,9 @@ command_handler([{No, Desc, Cmd}|Cmds]) -> init_manager(AutoInform, Config) -> - ?LOG("init_manager -> entry with" - "~n AutoInform: ~p" - "~n Config: ~p", [AutoInform, Config]), + ?IPRINT("init_manager -> entry with" + "~n AutoInform: ~p" + "~n Config: ~p", [AutoInform, Config]), %% -- %% Start node @@ -4856,11 +5001,18 @@ init_manager(AutoInform, Config) -> start_manager(Node, Vsns, Conf) end catch + C:{suite_failed, Reason, _M, _L} = E:S when (C =:= exit) -> + ?EPRINT("Failure during manager start (suite-failed):" + "~n Reason: ~p" + "~n StackTrace: ~p", [Reason, S]), + %% And now, *try* to cleanup + (catch stop_node(Node)), + erlang:raise(C, E, S); C:E:S -> - p("Failure during manager start: " - "~n Error Class: ~p" - "~n Error: ~p" - "~n StackTrace: ~p", [C, E, S]), + ?EPRINT("Failure during manager start: " + "~n Error Class: ~p" + "~n Error: ~p" + "~n StackTrace: ~p", [C, E, S]), %% And now, *try* to cleanup (catch stop_node(Node)), ?FAIL({failed_starting_manager, C, E, S}) @@ -4871,19 +5023,19 @@ fin_manager(Config) -> StopMgrRes = stop_manager(Node), StopCryptoRes = fin_crypto(Node), StopNode = stop_node(Node), - p("fin_manager -> stop apps and (mgr node ~p) node results: " - "~n SNMP Mgr: ~p" - "~n Crypto: ~p" - "~n Node: ~p", - [Node, StopMgrRes, StopCryptoRes, StopNode]), + ?IPRINT("fin_manager -> stop apps and (mgr node ~p) node results: " + "~n SNMP Mgr: ~p" + "~n Crypto: ~p" + "~n Node: ~p", + [Node, StopMgrRes, StopCryptoRes, StopNode]), Config. %% -- Misc agent functions -- init_agent(Config) -> - ?LOG("init_agent -> entry with" - "~n Config: ~p", [Config]), + ?IPRINT("init_agent -> entry with" + "~n Config: ~p", [Config]), %% -- %% Retrieve some dir's @@ -4937,11 +5089,18 @@ init_agent(Config) -> start_agent(Node, Vsns, Conf) end catch + C:{suite_failed, Reason, _M, _L} = E:S when (C =:= exit) -> + ?EPRINT("Failure during agent start (suite-failed):" + "~n Reason: ~p" + "~n StackTrace: ~p", [Reason, S]), + %% And now, *try* to cleanup + (catch stop_node(Node)), + erlang:raise(C, E, S); C:E:S -> - p("Failure during agent start: " - "~n Error Class: ~p" - "~n Error: ~p" - "~n StackTrace: ~p", [C, E, S]), + ?EPRINT("Failure during agent start: " + "~n Error Class: ~p" + "~n Error: ~p" + "~n StackTrace: ~p", [C, E, S]), %% And now, *try* to cleanup (catch stop_node(Node)), ?FAIL({failed_starting_agent, C, E, S}) @@ -4949,17 +5108,17 @@ init_agent(Config) -> fin_agent(Config) -> - Node = ?config(agent_node, Config), + Node = ?config(agent_node, Config), StopAgentRes = stop_agent(Node), StopCryptoRes = fin_crypto(Node), StopMnesiaRes = fin_mnesia(Node), StopNode = stop_node(Node), - p("fin_agent -> stop apps and (agent node ~p) node results: " - "~n SNMP Agent: ~p" - "~n Crypto: ~p" - "~n Mnesia: ~p" - "~n Node: ~p", - [Node, StopAgentRes, StopCryptoRes, StopMnesiaRes, StopNode]), + ?IPRINT("fin_agent -> stop apps and (agent node ~p) node results: " + "~n SNMP Agent: ~p" + "~n Crypto: ~p" + "~n Mnesia: ~p" + "~n Node: ~p", + [Node, StopAgentRes, StopCryptoRes, StopMnesiaRes, StopNode]), Config. init_mnesia(Node, Dir, MnesiaDebug) @@ -5025,8 +5184,8 @@ load_app(Node, App) -> ({error, {already_loaded, LoadedApp}}) when (LoadedApp =:= App) -> ok; ({error, Reason}) -> - p("failed loading app ~w on ~p: " - "~n ~p", [App, Node, Reason]), + ?EPRINT("failed loading app ~w on ~p: " + "~n ~p", [App, Node, Reason]), ?FAIL({failed_load, Node, App, Reason}) end, do_load_app(Node, App, VerifySuccess). @@ -5046,8 +5205,8 @@ start_app(Node, App) -> ({error, {already_started, LoadedApp}}) when (LoadedApp =:= App) -> ok; ({error, Reason}) -> - p("failed starting app ~w on ~p: " - "~n ~p", [App, Node, Reason]), + ?EPRINT("failed starting app ~w on ~p: " + "~n Reason: ~p", [App, Node, Reason]), ?FAIL({failed_start, Node, App, Reason}) end, start_app(Node, App, VerifySuccess). @@ -5065,8 +5224,8 @@ stop_app(Node, App) -> ({error, {not_started, LoadedApp}}) when (LoadedApp =:= App) -> ok; ({error, Reason}) -> - p("failed stopping app ~w on ~p: " - "~n ~p", [App, Node, Reason]), + ?EPRINT("failed stopping app ~w on ~p: " + "~n ~p", [App, Node, Reason]), ?FAIL({failed_stop, Node, App, Reason}) end, stop_app(Node, App, VerifySuccess). @@ -5082,11 +5241,11 @@ set_app_env(Node, App, Key, Val) -> VerifySuccess = fun(ok) -> ok; ({error, Reason}) -> - p("failed setting app ~w env on ~p" - "~n Key: ~p" - "~n Val: ~p" - "~n Reason: ~p" - "~n ~p", [App, Node, Key, Val, Reason]), + ?EPRINT("failed setting app ~w env on ~p" + "~n Key: ~p" + "~n Val: ~p" + "~n Reason: ~p", + [App, Node, Key, Val, Reason]), ?FAIL({failed_set_app_env, Node, App, Key, Val, Reason}) end, @@ -5488,12 +5647,13 @@ start_manager(Node, Vsns, Conf0, _Opts) -> ConfigVerbosity = get_opt(manager_config_verbosity, Conf0, trace), NoteStoreVerbosity = get_opt(manager_note_store_verbosity, Conf0, log), - ServerVerbosity = get_opt(manager_server_verbosity, Conf0, trace), NetIfVerbosity = get_opt(manager_net_if_verbosity, Conf0, trace), AtlSeqNo = get_opt(manager_atl_seqno, Conf0, false), + ServerVerbosity = get_opt(manager_server_verbosity, Conf0, trace), CBP = get_opt(manager_server_cbproxy, Conf0, temporary), + NIS = get_opt(manager_server_nis, Conf0, none), NetIfConf = case get_opt(manager_net_if_module, Conf0, no_module) of @@ -5516,12 +5676,22 @@ start_manager(Node, Vsns, Conf0, _Opts) -> {verbosity, ConfigVerbosity}]}, {note_store, [{verbosity, NoteStoreVerbosity}]}, {server, [{verbosity, ServerVerbosity}, - {cbproxy, CBP}]}, + {cbproxy, CBP}, + {netif_sup, NIS}]}, {net_if, NetIfConf}], ?line ok = set_mgr_env(Node, Env), - ?line ok = start_snmp(Node), - + ?line ok = try start_snmp(Node) of + ok -> + ok; + {error, Reason} -> + ?FAIL({failed_start_manager, Reason}) + catch + exit:{suite_failed, {failed_start, _, _, Reason}, _M, _L}:_ -> + ?FAIL({failed_start_manager, Reason}); + C:E:S -> + erlang:raise(C, E, S) + end, Conf0. stop_manager(Node) -> @@ -5577,7 +5747,17 @@ start_agent(Node, Vsns, Conf0, _Opts) -> {multi_threaded, true}], ?line ok = set_agent_env(Node, Env), - ?line ok = start_snmp(Node), + ?line try start_snmp(Node) of + ok -> + ok; + {error, Reason} -> + ?FAIL({failed_start_agent, Reason}) + catch + exit:{suite_failed, {failed_start, _, _, Reason}, _M, _L}:_ -> + ?FAIL({failed_start_agent, Reason}); + C:E:S -> + erlang:raise(C, E, S) + end, Conf0. stop_agent(Node) -> @@ -5641,7 +5821,7 @@ start_node(Name, Retry) -> global:sync(), Node; {error, timeout} -> - e("Failed starting node ~p: timeout", [Name]), + ?EPRINT("Failed starting node ~p: timeout", [Name]), ?line ?FAIL({error_starting_node, Name, timeout}); {error, {already_running, Node}} when (Retry =:= true) -> %% Ouch @@ -5650,24 +5830,24 @@ start_node(Name, Retry) -> %% timeout) but actually succeeded. Regardless, we don't know %% the state of this node, so (try) stop it and then (re-) try %% start again. - e("Failed starting node ~p: Already Running - try stop", [Node]), + ?WPRINT("Failed starting node ~p: Already Running - try stop", [Node]), case ?STOP_NODE(Node) of true -> - p("Successfully stopped old node ~p", [Node]), + ?IPRINT("Successfully stopped old node ~p", [Node]), start_node(Name, false); false -> - e("Failed stop old node ~p", [Node]), + ?EPRINT("Failed stop old node ~p", [Node]), ?line ?FAIL({error_starting_node, Node, Retry, already_running}) end; {error, {already_running, Node}} -> - e("Failed starting node ~p: Already Running", [Node]), + ?EPRINT("Failed starting node ~p: Already Running", [Node]), ?line ?FAIL({error_starting_node, Node, Retry, already_running}); {error, Reason} -> - e("Failed starting node ~p: ~p", [Name, Reason]), + ?EPRINT("Failed starting node ~p: ~p", [Name, Reason]), ?line ?FAIL({error_starting_node, Name, Reason}) catch exit:{suite_failed, Reason} -> - e("(suite) Failed starting node ~p: ~p", [Name, Reason]), + ?EPRINT("(suite) Failed starting node ~p: ~p", [Name, Reason]), ?line ?FAIL({failed_starting_node, Name, Reason}) end. @@ -5846,24 +6026,24 @@ display_log(Config) -> LogDir = Dir, Mibs = [], OutFile = j(LogDir, "snmpm_log.txt"), - p("~n" - "=========================" - " < Audit Trail Log > " - "=========================" - "~n"), + ?IPRINT("~n" + "=========================" + " < Audit Trail Log > " + "=========================" + "~n"), rcall(Node, snmpm, log_to_txt, [LogDir, Mibs, OutFile]), rcall(Node, snmpm, log_to_io, [LogDir, Mibs]), - p("~n" - "=========================" - " < / Audit Trail Log > " - "=========================" - "~n"); + ?IPRINT("~n" + "=========================" + " < / Audit Trail Log > " + "=========================" + "~n"); false -> - p("display_log -> no manager node found"), + ?IPRINT("display_log -> no manager node found"), ok end; false -> - p("display_log -> no manager log dir found"), + ?IPRINT("display_log -> no manager log dir found"), ok end. @@ -5914,32 +6094,5 @@ rcall(Node, Mod, Func, Args) -> %% ------ -%% Time in milli sec -%% t() -> -%% {A,B,C} = os:timestamp(), -%% A*1000000000+B*1000+(C div 1000). - - -%% ------ - -e(F, A) -> - p("<ERROR> " ++ F, A). - -p(F) -> - p(F, []). - -p(F, A) -> - p(get(tname), F, A). - -p(TName, F, A) -> - io:format("*** [~w][~s] ***" - "~n " ++ F ++ "~n", [TName, formated_timestamp()|A]). - -formated_timestamp() -> - snmp_test_lib:formated_timestamp(). - -%% p(TName, F, A) -> -%% io:format("~w -> " ++ F ++ "~n", [TName|A]). - ipv6_init(Config) when is_list(Config) -> [{ipfamily, inet6} | Config]. diff --git a/lib/snmp/test/snmp_manager_config_SUITE.erl b/lib/snmp/test/snmp_manager_config_SUITE.erl index b5c1894294..9a7b485a60 100644 --- a/lib/snmp/test/snmp_manager_config_SUITE.erl +++ b/lib/snmp/test/snmp_manager_config_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2019. All Rights Reserved. +%% Copyright Ericsson AB 2004-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ %%---------------------------------------------------------------------- -module(snmp_manager_config_SUITE). + %%---------------------------------------------------------------------- %% Include files %%---------------------------------------------------------------------- @@ -97,6 +98,7 @@ ]). + %%---------------------------------------------------------------------- %% Internal exports %%---------------------------------------------------------------------- @@ -219,24 +221,40 @@ otp_8395_cases() -> otp_8395_4 ]. + init_per_suite(Config0) when is_list(Config0) -> - ?DBG("init_per_suite -> entry with" - "~n Config0: ~p", [Config0]), + ?IPRINT("init_per_suite -> entry with" + "~n Config0: ~p", [Config0]), - Config1 = snmp_test_lib:init_suite_top_dir(?MODULE, Config0), + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; - ?DBG("init_per_suite -> done when" - "~n Config1: ~p", [Config1]), + Config1 -> - Config1. + Config2 = snmp_test_lib:init_suite_top_dir(?MODULE, Config1), -end_per_suite(Config) when is_list(Config) -> + %% We need one on this node also + snmp_test_sys_monitor:start(), - ?DBG("end_per_suite -> entry with" - "~n Config: ~p", [Config]), + ?IPRINT("init_per_suite -> end when" + "~n Config: ~p", [Config2]), - Config. + Config2 + end. + +end_per_suite(Config0) when is_list(Config0) -> + + ?IPRINT("end_per_suite -> entry with" + "~n Config0: ~p", [Config0]), + + snmp_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + ?IPRINT("end_per_suite -> end"), + + Config1. init_per_group(_GroupName, Config) -> @@ -247,11 +265,16 @@ end_per_group(_GroupName, Config) -> init_per_testcase(Case, Config) when is_list(Config) -> - p("init_per_testcase -> Case: ~p", [Case]), + ?IPRINT("init_per_testcase -> entry with" + "~n Config: ~p", [Config]), + + snmp_test_global_sys_monitor:reset_events(), + SuiteTopDir = ?config(snmp_suite_top_dir, Config), CaseTopDir = filename:join(SuiteTopDir, atom_to_list(Case)), ?line ok = file:make_dir(CaseTopDir), - p("init_per_testcase -> CaseTopDir: ~p", [CaseTopDir]), + + ?IPRINT("init_per_testcase -> CaseTopDir: ~p", [CaseTopDir]), MgrTopDir = filename:join(CaseTopDir, "manager/"), ?line ok = file:make_dir(MgrTopDir), MgrConfDir = filename:join(MgrTopDir, "conf/"), @@ -265,15 +288,26 @@ init_per_testcase(Case, Config) when is_list(Config) -> end, MgrLogDir = filename:join(MgrTopDir, "log/"), ?line ok = file:make_dir(MgrLogDir), - [{case_top_dir, CaseTopDir}, - {manager_dir, MgrTopDir}, - {manager_conf_dir, MgrConfDir}, - {manager_db_dir, MgrDbDir}, - {manager_log_dir, MgrLogDir} | Config]. + Config1 = [{case_top_dir, CaseTopDir}, + {manager_dir, MgrTopDir}, + {manager_conf_dir, MgrConfDir}, + {manager_db_dir, MgrDbDir}, + {manager_log_dir, MgrLogDir} | Config], + + ?IPRINT("init_per_testcase -> done when" + "~n Config1: ~p", [Config1]), + Config1. + + +end_per_testcase(_Case, Config) when is_list(Config) -> + + ?IPRINT("end_per_testcase -> entry with" + "~n Config: ~p", [Config]), + + ?IPRINT("system events during test: " + "~n ~p", [snmp_test_global_sys_monitor:events()]), -end_per_testcase(Case, Config) when is_list(Config) -> - p("end_per_testcase -> Case: ~p", [Case]), %% The cleanup is removed due to some really discusting NFS behaviour... %% Also, it can always be useful to retain "all the stuff" after %% the test case in case of debugging... @@ -293,8 +327,8 @@ simple_start_and_stop(doc) -> "Start the snmp manager config process with the \n" "minimum setof options (config dir)."; simple_start_and_stop(Conf) when is_list(Conf) -> - put(tname,ssas), - p("start"), + put(tname, "SIME-START_AND_STOP"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), DbDir = ?config(manager_db_dir, Conf), @@ -319,9 +353,9 @@ start_without_mandatory_opts1(doc) -> "Start the snmp manager config process with some of the \n" "mandatory options missing."; start_without_mandatory_opts1(Conf) when is_list(Conf) -> - put(tname,swomo1), - put(verbosity,trace), - p("start"), + put(tname, "START-WO-MAND-OPTS-1"), + put(verbosity, trace), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), DbDir = ?config(manager_db_dir, Conf), @@ -330,12 +364,12 @@ start_without_mandatory_opts1(Conf) when is_list(Conf) -> %% config, but no dir: - p("config option, but no dir"), + ?IPRINT("config option, but no dir"), Opts = [{priority, normal}, {config, [{verbosity, trace}, {db_dir, DbDir}]}, {mibs, []}], ?line {error, {missing_mandatory,dir}} = config_start(Opts), - p("done"), + ?IPRINT("done"), ok. @@ -348,9 +382,9 @@ start_without_mandatory_opts2(doc) -> "Start the snmp manager config process with some of the \n" "mandatory options missing."; start_without_mandatory_opts2(Conf) when is_list(Conf) -> - put(tname,swomo2), + put(tname, "START-WO-MAND-OPTS-2"), put(verbosity,trace), - p("start"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), @@ -358,13 +392,13 @@ start_without_mandatory_opts2(Conf) when is_list(Conf) -> %% Second set of options (no config): - p("no config option"), + ?IPRINT("no config option"), Opts = [{priority, normal}, {mibs, []}], ?line {error, {missing_mandatory,config,[dir, db_dir]}} = config_start(Opts), - p("done"), + ?IPRINT("done"), ok. @@ -377,8 +411,9 @@ start_with_all_valid_opts(doc) -> "Start the snmp manager config process with the \n" "complete set of all the valid options."; start_with_all_valid_opts(Conf) when is_list(Conf) -> + put(tname, "START-W-ALL-VALID-OPTS"), put(tname,swavo), - p("start"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), DbDir = ?config(manager_db_dir, Conf), @@ -389,16 +424,22 @@ start_with_all_valid_opts(Conf) when is_list(Conf) -> %% Third set of options (no versions): - p("all options"), + ?IPRINT("all options"), NetIfOpts = [{module, snmpm_net_if}, {verbosity, trace}, {options, [{recbuf, 30000}, {bind_to, false}, {no_reuse, false}]}], - ServerOpts = [{timeout, 10000}, {verbosity, trace}], - NoteStoreOpts = [{timeout, 20000}, {verbosity, trace}], - ConfigOpts = [{dir, ConfDir}, {verbosity, trace}, - {db_dir, DbDir}, {db_init_error, create}], + ServerOpts = [{timeout, ?SECS(10)}, + {verbosity, trace}, + {cbproxy, permanent}, + {netif_sup, {?SECS(60), ?SECS(5)}}], + NoteStoreOpts = [{timeout, ?SECS(20)}, + {verbosity, trace}], + ConfigOpts = [{dir, ConfDir}, + {verbosity, trace}, + {db_dir, DbDir}, + {db_init_error, create}], Mibs = [join(StdMibDir, "SNMP-NOTIFICATION-MIB"), join(StdMibDir, "SNMP-USER-BASED-SM-MIB")], Prio = normal, @@ -418,7 +459,7 @@ start_with_all_valid_opts(Conf) when is_list(Conf) -> ?line {ok, _Pid} = config_start(Opts), ?line ok = config_stop(), - p("done"), + ?IPRINT("done"), ok. @@ -431,8 +472,8 @@ start_with_unknown_opts(doc) -> "Start the snmp manager config process when some of\n" "the options are unknown."; start_with_unknown_opts(Conf) when is_list(Conf) -> - put(tname,swuo), - p("start"), + put(tname, "START-W-UNKNOWN-OPTS"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), DbDir = ?config(manager_db_dir, Conf), @@ -443,7 +484,7 @@ start_with_unknown_opts(Conf) when is_list(Conf) -> %% Third set of options (no versions): - p("all options"), + ?IPRINT("all options"), NetIfOpts = [{module, snmpm_net_if}, {verbosity, trace}, {options, [{recbuf, 30000}, @@ -471,7 +512,10 @@ start_with_unknown_opts(Conf) when is_list(Conf) -> {versions, Vsns}], ?line {ok, _Pid} = config_start(Opts), - p("done"), + ?IPRINT("(config) started - now stop"), + ?line ok = config_stop(), + + ?IPRINT("done"), ok. @@ -484,8 +528,8 @@ start_with_incorrect_opts(doc) -> "Start the snmp manager config process when some of\n" "the options has incorrect values."; start_with_incorrect_opts(Conf) when is_list(Conf) -> - put(tname,swio), - p("start"), + put(tname, "START-W-INCORRECT-OPTS"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), DbDir = ?config(manager_db_dir, Conf), @@ -496,135 +540,135 @@ start_with_incorrect_opts(Conf) when is_list(Conf) -> ConfigOpts = [{verbosity,trace}, {dir, ConfDir}, {db_dir, DbDir}], - p("net-if - incorrect module"), + ?IPRINT("net-if - incorrect module"), NetIfOpts1 = [{module, snmpm_user}], %% Behaviour check will fail Opts01 = [{config, ConfigOpts}, {versions, [v1]}, {net_if, NetIfOpts1}], ?line {error, Reason01} = config_start(Opts01), - p("net-if (module) res: ~p", [Reason01]), + ?IPRINT("net-if (module) res: ~p", [Reason01]), - p("net-if - incorrect verbosity"), + ?IPRINT("net-if - incorrect verbosity"), NetIfOpts2 = [{verbosity, invalid_verbosity}], Opts02 = [{config, ConfigOpts}, {versions, [v1]}, {net_if, NetIfOpts2}], ?line {error, Reason02} = config_start(Opts02), - p("net-if (verbosity) res: ~p", [Reason02]), + ?IPRINT("net-if (verbosity) res: ~p", [Reason02]), - p("net-if - incorrect options"), + ?IPRINT("net-if - incorrect options"), NetIfOpts3 = [{options, invalid_options}], Opts03 = [{config, ConfigOpts}, {versions, [v1]}, {net_if, NetIfOpts3}], ?line {error, Reason03} = config_start(Opts03), - p("net-if (options) res: ~p", [Reason03]), + ?IPRINT("net-if (options) res: ~p", [Reason03]), - p("server - incorrect timeout (1)"), + ?IPRINT("server - incorrect timeout (1)"), ServerOpts1 = [{timeout, invalid_timeout}], Opts08 = [{config, ConfigOpts}, {versions, [v1]}, {server, ServerOpts1}], ?line {error, Reason08} = config_start(Opts08), - p("server (timeout) res: ~p", [Reason08]), + ?IPRINT("server (timeout) res: ~p", [Reason08]), - p("server - incorrect timeout (2)"), + ?IPRINT("server - incorrect timeout (2)"), ServerOpts2 = [{timeout, 0}], Opts09 = [{config, ConfigOpts}, {versions, [v1]}, {server, ServerOpts2}], ?line {error, Reason09} = config_start(Opts09), - p("server (timeout) res: ~p", [Reason09]), + ?IPRINT("server (timeout) res: ~p", [Reason09]), - p("server - incorrect timeout (3)"), + ?IPRINT("server - incorrect timeout (3)"), ServerOpts3 = [{timeout, -1000}], Opts10 = [{config, ConfigOpts}, {versions, [v1]}, {server, ServerOpts3}], ?line {error, Reason10} = config_start(Opts10), - p("server (timeout) res: ~p", [Reason10]), + ?IPRINT("server (timeout) res: ~p", [Reason10]), - p("server - incorrect verbosity"), + ?IPRINT("server - incorrect verbosity"), ServerOpts4 = [{verbosity, invalid_verbosity}], Opts11 = [{config, ConfigOpts}, {versions, [v1]}, {server, ServerOpts4}], ?line {error, Reason11} = config_start(Opts11), - p("server (verbosity) res: ~p", [Reason11]), + ?IPRINT("server (verbosity) res: ~p", [Reason11]), - p("note-store - incorrect timeout (1)"), + ?IPRINT("note-store - incorrect timeout (1)"), NoteStoreOpts1 = [{timeout, invalid_timeout}], Opts12 = [{config, ConfigOpts}, {versions, [v1]}, {note_store, NoteStoreOpts1}], ?line {error, Reason12} = config_start(Opts12), - p("note-store (timeout) res: ~p", [Reason12]), + ?IPRINT("note-store (timeout) res: ~p", [Reason12]), - p("note-store - incorrect timeout (2)"), + ?IPRINT("note-store - incorrect timeout (2)"), NoteStoreOpts2 = [{timeout, 0}], Opts13 = [{config, ConfigOpts}, {versions, [v1]}, {note_store, NoteStoreOpts2}], ?line {error, Reason13} = config_start(Opts13), - p("note-store (timeout) res: ~p", [Reason13]), + ?IPRINT("note-store (timeout) res: ~p", [Reason13]), - p("note-store - incorrect timeout (3)"), + ?IPRINT("note-store - incorrect timeout (3)"), NoteStoreOpts3 = [{timeout, -2000}], Opts14 = [{config, ConfigOpts}, {versions, [v1]}, {note_store, NoteStoreOpts3}], ?line {error, Reason14} = config_start(Opts14), - p("note-store (timeout) res: ~p", [Reason14]), + ?IPRINT("note-store (timeout) res: ~p", [Reason14]), - p("note-store - incorrect verbosity"), + ?IPRINT("note-store - incorrect verbosity"), NoteStoreOpts4 = [{timeout, 20000}, {verbosity, invalid_verbosity}], Opts15 = [{config, ConfigOpts}, {versions, [v1]}, {note_store, NoteStoreOpts4}], ?line {error, Reason15} = config_start(Opts15), - p("note-store (verbosity) res: ~p", [Reason15]), + ?IPRINT("note-store (verbosity) res: ~p", [Reason15]), - p("config - incorrect dir (1)"), + ?IPRINT("config - incorrect dir (1)"), ConfigOpts1 = [{dir, invalid_dir}], Opts16 = [{config, ConfigOpts1}, {versions, [v1]}], ?line {error, Reason16} = config_start(Opts16), - p("config (dir) res: ~p", [Reason16]), + ?IPRINT("config (dir) res: ~p", [Reason16]), - p("config - incorrect dir (2)"), + ?IPRINT("config - incorrect dir (2)"), ConfigOpts2 = [{dir, "/invalid/dir"}], Opts17 = [{config, ConfigOpts2}, {versions, [v1]}], ?line {error, Reason17} = config_start(Opts17), - p("config (dir) res: ~p", [Reason17]), + ?IPRINT("config (dir) res: ~p", [Reason17]), - p("config - incorrect verbosity"), + ?IPRINT("config - incorrect verbosity"), ConfigOpts3 = [{dir, ConfDir}, {verbosity, invalid_verbosity}], Opts18 = [{config, ConfigOpts3}, {versions, [v1]}], ?line {error, Reason18} = config_start(Opts18), - p("config (verbosity) res: ~p", [Reason18]), + ?IPRINT("config (verbosity) res: ~p", [Reason18]), - p("mibs - incorrect mibs (1)"), + ?IPRINT("mibs - incorrect mibs (1)"), Mibs1 = invalid_mibs, Opts19 = [{config, ConfigOpts}, {versions, [v1]}, {mibs, Mibs1}], ?line {error, Reason19} = config_start(Opts19), - p("mibs (mibs) res: ~p", [Reason19]), + ?IPRINT("mibs (mibs) res: ~p", [Reason19]), - p("mibs - incorrect mibs (2)"), + ?IPRINT("mibs - incorrect mibs (2)"), Mibs2 = [join(StdMibDir, "INVALID-MIB")], Opts20 = [{config, ConfigOpts}, {versions, [v1]}, {mibs, Mibs2}], ?line {error, Reason20} = config_start(Opts20), - p("mibs (mibs) res: ~p", [Reason20]), + ?IPRINT("mibs (mibs) res: ~p", [Reason20]), - p("prio - incorrect prio"), + ?IPRINT("prio - incorrect prio"), Prio1 = invalid_prio, Opts21 = [{config, ConfigOpts}, {versions, [v1]}, {priority, Prio1}], ?line {error, Reason21} = config_start(Opts21), - p("prio (prio) res: ~p", [Reason21]), + ?IPRINT("prio (prio) res: ~p", [Reason21]), - p("atl - incorrect type"), + ?IPRINT("atl - incorrect type"), ATL1 = [{type, invalid_type}, {dir, LogDir}, {size, {10,10240}}, @@ -633,9 +677,9 @@ start_with_incorrect_opts(Conf) when is_list(Conf) -> {versions, [v1]}, {audit_trail_log, ATL1}], ?line {error, Reason22} = config_start(Opts22), - p("atl (type) res: ~p", [Reason22]), + ?IPRINT("atl (type) res: ~p", [Reason22]), - p("atl - incorrect dir (1)"), + ?IPRINT("atl - incorrect dir (1)"), ATL2 = [{type, read_write}, {dir, invalid_dir}, {size, {10,10240}}, @@ -644,9 +688,9 @@ start_with_incorrect_opts(Conf) when is_list(Conf) -> {versions, [v1]}, {audit_trail_log, ATL2}], ?line {error, Reason23} = config_start(Opts23), - p("atl (dir) res: ~p", [Reason23]), + ?IPRINT("atl (dir) res: ~p", [Reason23]), - p("atl - incorrect dir (2)"), + ?IPRINT("atl - incorrect dir (2)"), ATL3 = [{type, read_write}, {dir, "/invalid/dir"}, {size, {10,10240}}, @@ -655,9 +699,9 @@ start_with_incorrect_opts(Conf) when is_list(Conf) -> {versions, [v1]}, {audit_trail_log, ATL3}], ?line {error, Reason24} = config_start(Opts24), - p("atl (dir) res: ~p", [Reason24]), + ?IPRINT("atl (dir) res: ~p", [Reason24]), - p("atl - incorrect size (1)"), + ?IPRINT("atl - incorrect size (1)"), ATL4 = [{type, read_write}, {dir, LogDir}, {size, invalid_size}, @@ -666,9 +710,9 @@ start_with_incorrect_opts(Conf) when is_list(Conf) -> {versions, [v1]}, {audit_trail_log, ATL4}], ?line {error, Reason25} = config_start(Opts25), - p("atl (size) res: ~p", [Reason25]), + ?IPRINT("atl (size) res: ~p", [Reason25]), - p("atl - incorrect size (2)"), + ?IPRINT("atl - incorrect size (2)"), ATL5 = [{type, read_write}, {dir, LogDir}, {size, {10,invalid_file_size}}, @@ -677,9 +721,9 @@ start_with_incorrect_opts(Conf) when is_list(Conf) -> {versions, [v1]}, {audit_trail_log, ATL5}], ?line {error, Reason26} = config_start(Opts26), - p("atl (size) res: ~p", [Reason26]), + ?IPRINT("atl (size) res: ~p", [Reason26]), - p("atl - incorrect size (3)"), + ?IPRINT("atl - incorrect size (3)"), ATL6 = [{type, read_write}, {dir, LogDir}, {size, {invalid_file_num,10240}}, @@ -688,9 +732,9 @@ start_with_incorrect_opts(Conf) when is_list(Conf) -> {versions, [v1]}, {audit_trail_log, ATL6}], ?line {error, Reason27} = config_start(Opts27), - p("atl (size) res: ~p", [Reason27]), + ?IPRINT("atl (size) res: ~p", [Reason27]), - p("atl - incorrect repair"), + ?IPRINT("atl - incorrect repair"), ATL7 = [{type, read_write}, {dir, LogDir}, {size, {10,10240}}, @@ -699,23 +743,23 @@ start_with_incorrect_opts(Conf) when is_list(Conf) -> {versions, [v1]}, {audit_trail_log, ATL7}], ?line {error, Reason28} = config_start(Opts28), - p("atl (repair) res: ~p", [Reason28]), + ?IPRINT("atl (repair) res: ~p", [Reason28]), - p("version - incorrect versions (1)"), + ?IPRINT("version - incorrect versions (1)"), Vsns1 = invalid_vsns, Opts29 = [{config, ConfigOpts}, {versions, Vsns1}], ?line {error, Reason29} = config_start(Opts29), - p("versions (versions) res: ~p", [Reason29]), + ?IPRINT("versions (versions) res: ~p", [Reason29]), - p("version - incorrect versions (2)"), + ?IPRINT("version - incorrect versions (2)"), Vsns2 = [v1,v2,v3,v9], Opts30 = [{config, ConfigOpts}, {versions, Vsns2}], ?line {error, Reason30} = config_start(Opts30), - p("versions (versions) res: ~p", [Reason30]), + ?IPRINT("versions (versions) res: ~p", [Reason30]), - p("done"), + ?IPRINT("done"), ok. @@ -727,8 +771,8 @@ start_with_invalid_manager_conf_file1(suite) -> []; start_with_invalid_manager_conf_file1(doc) -> "Start with invalid manager config file (1)."; start_with_invalid_manager_conf_file1(Conf) when is_list(Conf) -> - put(tname,swimcf), - p("start"), + put(tname, "START-W-INV-MGR-CONF-FILE-1"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), DbDir = ?config(manager_db_dir, Conf), @@ -737,142 +781,142 @@ start_with_invalid_manager_conf_file1(Conf) when is_list(Conf) -> {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], %% -- - p("write manager config file with invalid IP address (1)"), + ?IPRINT("write manager config file with invalid IP address (1)"), write_manager_conf(ConfDir, "arne-anka", "4001", "500", "\"bmkEngine\""), ?line {error, Reason11} = config_start(Opts), - p("start failed (as expected): ~p", [Reason11]), + ?IPRINT("start failed (as expected): ~p", [Reason11]), ?line {failed_reading, _, _, 1, {parse_error, _}} = Reason11, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write manager config file with invalid IP address (2)"), + ?IPRINT("write manager config file with invalid IP address (2)"), write_manager_conf(ConfDir, "arne_anka", "4001", "500", "\"bmkEngine\""), ?line {error, Reason12} = config_start(Opts), - p("start failed (as expected): ~p", [Reason12]), + ?IPRINT("start failed (as expected): ~p", [Reason12]), ?line {failed_check, _, _, 2, {bad_address, _}} = Reason12, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write manager config file with invalid IP address (3)"), + ?IPRINT("write manager config file with invalid IP address (3)"), write_manager_conf(ConfDir, "9999", "4001", "500", "\"bmkEngine\""), ?line {error, Reason13} = config_start(Opts), - p("start failed (as expected): ~p", [Reason13]), + ?IPRINT("start failed (as expected): ~p", [Reason13]), ?line {failed_check, _, _, 2, {bad_address, _}} = Reason13, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write manager config file with invalid port (2)"), + ?IPRINT("write manager config file with invalid port (2)"), write_manager_conf(ConfDir, "[134,138,177,189]", "kalle-anka", "500", "\"bmkEngine\""), ?line {error, Reason21} = config_start(Opts), - p("start failed (as expected): ~p", [Reason21]), + ?IPRINT("start failed (as expected): ~p", [Reason21]), ?line {failed_reading, _, _, 2, {parse_error, _}} = Reason21, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write manager config file with invalid port (1)"), + ?IPRINT("write manager config file with invalid port (1)"), write_manager_conf(ConfDir, "[134,138,177,189]", "-1", "500", "\"bmkEngine\""), ?line {error, Reason22} = config_start(Opts), - p("start failed (as expected): ~p", [Reason22]), + ?IPRINT("start failed (as expected): ~p", [Reason22]), io:format("Reason22: ~p~n", [Reason22]), ?line {failed_check, _, _, 3, {bad_port, _}} = Reason22, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write manager config file with invalid port (3)"), + ?IPRINT("write manager config file with invalid port (3)"), write_manager_conf(ConfDir, "[134,138,177,189]", "\"kalle-anka\"", "500", "\"bmkEngine\""), ?line {error, Reason23} = config_start(Opts), - p("start failed (as expected): ~p", [Reason23]), + ?IPRINT("start failed (as expected): ~p", [Reason23]), ?line {failed_check, _, _, 3, {bad_port, _}} = Reason23, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write manager config file with invalid EngineID (1)"), + ?IPRINT("write manager config file with invalid EngineID (1)"), write_manager_conf(ConfDir, "[134,138,177,189]", "4001", "500", "bmkEngine"), ?line {error, Reason31} = config_start(Opts), - p("start failed (as expected): ~p", [Reason31]), + ?IPRINT("start failed (as expected): ~p", [Reason31]), ?line {failed_check, _, _, 5, {invalid_string, _}} = Reason31, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write manager config file with invalid EngineID (2)"), + ?IPRINT("write manager config file with invalid EngineID (2)"), write_manager_conf(ConfDir, "[134,138,177,189]", "4001", "500", "{1,2,3}"), ?line {error, Reason32} = config_start(Opts), - p("start failed (as expected): ~p", [Reason32]), + ?IPRINT("start failed (as expected): ~p", [Reason32]), ?line {failed_check, _, _, 5, {invalid_string, _}} = Reason32, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write manager config file with invalid EngineID (3)"), + ?IPRINT("write manager config file with invalid EngineID (3)"), write_manager_conf(ConfDir, "[134,138,177,189]", "4001", "500", "10101"), ?line {error, Reason33} = config_start(Opts), - p("start failed (as expected): ~p", [Reason33]), + ?IPRINT("start failed (as expected): ~p", [Reason33]), ?line {failed_check, _, _, 5, {invalid_string, _}} = Reason33, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write manager config file with invalid MMS (1)"), + ?IPRINT("write manager config file with invalid MMS (1)"), write_manager_conf(ConfDir, "[134,138,177,189]", "4001", "483", "\"bmkEngine\""), ?line {error, Reason41} = config_start(Opts), - p("start failed (as expected): ~p", [Reason41]), + ?IPRINT("start failed (as expected): ~p", [Reason41]), ?line {failed_check, _, _, 4, {invalid_integer, _}} = Reason41, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write manager config file with invalid MMS (2)"), + ?IPRINT("write manager config file with invalid MMS (2)"), write_manager_conf(ConfDir, "[134,138,177,189]", "4001", "-1", "\"bmkEngine\""), ?line {error, Reason42} = config_start(Opts), - p("start failed (as expected): ~p", [Reason42]), + ?IPRINT("start failed (as expected): ~p", [Reason42]), ?line {failed_check, _, _, 4, {invalid_integer, _}} = Reason42, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write manager config file with invalid MMS (3)"), + ?IPRINT("write manager config file with invalid MMS (3)"), write_manager_conf(ConfDir, "[134,138,177,189]", "4001", "\"kalle-anka\"", "\"bmkEngine\""), ?line {error, Reason43} = config_start(Opts), - p("start failed (as expected): ~p", [Reason43]), + ?IPRINT("start failed (as expected): ~p", [Reason43]), ?line {failed_check, _, _, 4, {invalid_integer, _}} = Reason43, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write manager config file with invalid MMS (4)"), + ?IPRINT("write manager config file with invalid MMS (4)"), write_manager_conf(ConfDir, "[134,138,177,189]", "4001", "kalle_anka", "\"bmkEngine\""), ?line {error, Reason44} = config_start(Opts), - p("start failed (as expected): ~p", [Reason44]), + ?IPRINT("start failed (as expected): ~p", [Reason44]), ?line {failed_check, _, _, 4, {invalid_integer, _}} = Reason44, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write manager config file with unknown option"), + ?IPRINT("write manager config file with unknown option"), write_manager_conf(ConfDir, "{kalle, anka}."), ?line {error, Reason51} = config_start(Opts), - p("start failed (as expected): ~p", [Reason51]), + ?IPRINT("start failed (as expected): ~p", [Reason51]), ?line {failed_check, _, _, 1, {unknown_config, _}} = Reason51, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write manager config file with unknown option"), + ?IPRINT("write manager config file with unknown option"), write_manager_conf(ConfDir, "kalle_anka."), ?line {error, Reason52} = config_start(Opts), - p("start failed (as expected): ~p", [Reason52]), + ?IPRINT("start failed (as expected): ~p", [Reason52]), ?line {failed_check, _, _, 1, {unknown_config, _}} = Reason52, - await_config_not_running(), + config_ensure_not_running(), - p("done"), + ?IPRINT("done"), ok. @@ -884,8 +928,8 @@ start_with_invalid_users_conf_file1(suite) -> []; start_with_invalid_users_conf_file1(doc) -> "Start with invalid users config file."; start_with_invalid_users_conf_file1(Conf) when is_list(Conf) -> - put(tname,swiucf), - p("start"), + put(tname, "START-W-INV-USER-CONF-FILE-1"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), DbDir = ?config(manager_db_dir, Conf), @@ -899,74 +943,74 @@ start_with_invalid_users_conf_file1(Conf) when is_list(Conf) -> write_manager_conf(ConfDir), %% -- - p("write users config file with invalid module (1)"), + ?IPRINT("write users config file with invalid module (1)"), write_users_conf(ConfDir, [{"kalle", "kalle", "dummy"}]), ?line {error, Reason11} = config_start(Opts), - p("start failed (as expected): ~p", [Reason11]), + ?IPRINT("start failed (as expected): ~p", [Reason11]), ?line {failed_check, _, _, _, {bad_module, kalle}} = Reason11, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write users config file with invalid module (1)"), + ?IPRINT("write users config file with invalid module (1)"), write_users_conf(ConfDir, [{"kalle", "snmpm", "dummy"}]), ?line {error, Reason12} = config_start(Opts), - p("start failed (as expected): ~p", [Reason12]), + ?IPRINT("start failed (as expected): ~p", [Reason12]), ?line {failed_check, _, _, _, {bad_module, _}} = Reason12, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write users config file with invalid module (2)"), + ?IPRINT("write users config file with invalid module (2)"), write_users_conf(ConfDir, [{"kalle1", "10101", "dummy"}]), ?line {error, Reason13} = config_start(Opts), - p("start failed (as expected): ~p", [Reason13]), + ?IPRINT("start failed (as expected): ~p", [Reason13]), ?line {failed_check, _, _, _, {bad_module, _}} = Reason13, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write users config file with invalid user tuple (1)"), + ?IPRINT("write users config file with invalid user tuple (1)"), write_users_conf2(ConfDir, "{kalle, snmpm_user_default}."), ?line {error, Reason21} = config_start(Opts), - p("start failed (as expected): ~p", [Reason21]), + ?IPRINT("start failed (as expected): ~p", [Reason21]), ?line {failed_check, _, _, _, {bad_user_config, _}} = Reason21, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write users config file with invalid user tuple (2)"), + ?IPRINT("write users config file with invalid user tuple (2)"), write_users_conf2(ConfDir, "{kalle, snmpm_user_default, kalle, [], olle}."), ?line {error, Reason22} = config_start(Opts), - p("start failed (as expected): ~p", [Reason22]), + ?IPRINT("start failed (as expected): ~p", [Reason22]), ?line {failed_check, _, _, _, {bad_user_config, _}} = Reason22, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write users config file with invalid user tuple (3)"), + ?IPRINT("write users config file with invalid user tuple (3)"), write_users_conf2(ConfDir, "snmpm_user_default."), ?line {error, Reason23} = config_start(Opts), - p("start failed (as expected): ~p", [Reason23]), + ?IPRINT("start failed (as expected): ~p", [Reason23]), ?line {failed_check, _, _, _, {bad_user_config, _}} = Reason23, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write users config file with invalid user tuple (4)"), + ?IPRINT("write users config file with invalid user tuple (4)"), write_users_conf2(ConfDir, "[kalle, snmpm_user_default, kalle]."), ?line {error, Reason24} = config_start(Opts), - p("start failed (as expected): ~p", [Reason24]), + ?IPRINT("start failed (as expected): ~p", [Reason24]), ?line {failed_check, _, _, _, {bad_user_config, _}} = Reason24, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write users config file with invalid user agent default config (1)"), + ?IPRINT("write users config file with invalid user agent default config (1)"), write_users_conf2(ConfDir, "{kalle, snmpm_user_default, kalle, olle}."), ?line {error, Reason31} = config_start(Opts), - p("start failed (as expected): ~p", [Reason31]), + ?IPRINT("start failed (as expected): ~p", [Reason31]), ?line {failed_check, _, _, _, {bad_default_agent_config, _}} = Reason31, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("write users config file with invalid user agent default config (2)"), + ?IPRINT("write users config file with invalid user agent default config (2)"), write_users_conf2(ConfDir, "{kalle, snmpm_user_default, kalle, [olle]}."), ?line {error, Reason32} = config_start(Opts), - p("start failed (as expected): ~p", [Reason32]), + ?IPRINT("start failed (as expected): ~p", [Reason32]), %% ?line {failed_check, _, _, _, {bad_default_agent_config, _}} = Reason32, case Reason32 of {failed_check, _, _, _, {bad_default_agent_config, _}} -> @@ -974,9 +1018,9 @@ start_with_invalid_users_conf_file1(Conf) when is_list(Conf) -> {A, B, C, D} -> exit({bad_error, A, B, C, D}) end, - await_config_not_running(), + config_ensure_not_running(), - p("done"), + ?IPRINT("done"), ok. @@ -988,8 +1032,8 @@ start_with_invalid_agents_conf_file1(suite) -> []; start_with_invalid_agents_conf_file1(doc) -> "Start with invalid agents config file."; start_with_invalid_agents_conf_file1(Conf) when is_list(Conf) -> - put(tname, swiacf), - p("start"), + put(tname, "START-W-INV-AGS-CONF-FILE-1"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), DbDir = ?config(manager_db_dir, Conf), @@ -1009,375 +1053,391 @@ start_with_invalid_agents_conf_file1(Conf) when is_list(Conf) -> "any", "\"initial\"", "noAuthNoPriv"}, %% -- - p("[test 11] write agents config file with invalid user (1)"), + ?IPRINT("[test 11] write agents config file with invalid user (1)"), Agent11 = setelement(1, Agent0, "kalle-anka"), write_agents_conf(ConfDir, [Agent11]), case config_start(Opts) of {error, Reason11} -> - p("start failed (as expected): ~p", [Reason11]), + ?IPRINT("start failed (as expected): ~p", [Reason11]), ?line {failed_reading, _, _, _, {parse_error, _}} = Reason11, - await_config_not_running(); + config_ensure_not_running(); OK_11 -> + config_ensure_not_running(), exit({error, {unexpected_success, "11", OK_11}}) end, %% -- - p("[test 21] write agents config file with invalid target name (1)"), + ?IPRINT("[test 21] write agents config file with invalid target name (1)"), Agent21 = setelement(2, Agent0, "targ-hobbes"), write_agents_conf(ConfDir, [Agent21]), case config_start(Opts) of {error, Reason21} -> - p("start failed (as expected): ~p", [Reason21]), + ?IPRINT("start failed (as expected): ~p", [Reason21]), ?line {failed_reading, _, _, _, {parse_error, _}} = Reason21, - await_config_not_running(); + config_ensure_not_running(); OK_21 -> + config_ensure_not_running(), exit({error, {unexpected_success, "21", OK_21}}) end, %% -- - p("[test 22] write agents config file with invalid target name (2)"), + ?IPRINT("[test 22] write agents config file with invalid target name (2)"), Agent22 = setelement(2, Agent0, "targ_hobbes"), write_agents_conf(ConfDir, [Agent22]), case config_start(Opts) of {error, Reason22} -> - p("start failed (as expected): ~p", [Reason22]), + ?IPRINT("start failed (as expected): ~p", [Reason22]), ?line {failed_check, _, _, _, {invalid_string, _}} = Reason22, - await_config_not_running(); + config_ensure_not_running(); OK_22 -> + config_ensure_not_running(), exit({error, {unexpected_success, "22", OK_22}}) end, %% -- - p("[test 23] write agents config file with invalid target name (3)"), + ?IPRINT("[test 23] write agents config file with invalid target name (3)"), Agent23 = setelement(2, Agent0, "10101"), write_agents_conf(ConfDir, [Agent23]), case config_start(Opts) of {error, Reason23} -> - p("start failed (as expected): ~p", [Reason23]), + ?IPRINT("start failed (as expected): ~p", [Reason23]), ?line {failed_check, _, _, _, {invalid_string, _}} = Reason23, - await_config_not_running(); + config_ensure_not_running(); OK_23 -> + config_ensure_not_running(), exit({error, {unexpected_success, "23", OK_23}}) end, %% -- - p("[test 31] write agents config file with invalid community (1)"), + ?IPRINT("[test 31] write agents config file with invalid community (1)"), Agent31 = setelement(3, Agent0, "targ-hobbes"), write_agents_conf(ConfDir, [Agent31]), case config_start(Opts) of {error, Reason31} -> - p("start failed (as expected): ~p", [Reason31]), + ?IPRINT("start failed (as expected): ~p", [Reason31]), ?line {failed_reading, _, _, _, {parse_error, _}} = Reason31, - await_config_not_running(); + config_ensure_not_running(); OK_31 -> + config_ensure_not_running(), exit({error, {unexpected_success, "31", OK_31}}) end, %% -- - p("[test 32] write agents config file with invalid community (2)"), + ?IPRINT("[test 32] write agents config file with invalid community (2)"), Agent32 = setelement(3, Agent0, "targ_hobbes"), write_agents_conf(ConfDir, [Agent32]), case config_start(Opts) of {error, Reason32} -> - p("start failed (as expected): ~p", [Reason32]), + ?IPRINT("start failed (as expected): ~p", [Reason32]), ?line {failed_check, _, _, _, {invalid_string, _}} = Reason32, - await_config_not_running(); + config_ensure_not_running(); OK_32 -> + config_ensure_not_running(), exit({error, {unexpected_success, "32", OK_32}}) end, %% -- - p("[test 33] write agents config file with invalid community (3)"), + ?IPRINT("[test 33] write agents config file with invalid community (3)"), Agent33 = setelement(3, Agent0, "10101"), write_agents_conf(ConfDir, [Agent33]), case config_start(Opts) of {error, Reason33} -> - p("start failed (as expected): ~p", [Reason33]), + ?IPRINT("start failed (as expected): ~p", [Reason33]), ?line {failed_check, _, _, _, {invalid_string, _}} = Reason33, - await_config_not_running(); + config_ensure_not_running(); OK_33 -> + config_ensure_not_running(), exit({error, {unexpected_success, "33", OK_33}}) end, %% -- - p("[test 51] write agents config file with invalid ip (1)"), + ?IPRINT("[test 51] write agents config file with invalid ip (1)"), Agent51 = setelement(4, Agent0, "kalle_anka"), write_agents_conf(ConfDir, [Agent51]), case config_start(Opts) of {error, Reason51} -> - p("start failed (as expected): ~p", [Reason51]), + ?IPRINT("start failed (as expected): ~p", [Reason51]), ?line {failed_check, _, _, _, {bad_domain, _}} = Reason51, - await_config_not_running(); + config_ensure_not_running(); OK_51 -> + config_ensure_not_running(), exit({error, {unexpected_success, "51", OK_51}}) end, %% -- - p("[test 52] write agents config file with invalid ip (2)"), + ?IPRINT("[test 52] write agents config file with invalid ip (2)"), Agent52 = setelement(4, Agent0, "10101"), write_agents_conf(ConfDir, [Agent52]), case config_start(Opts) of {error, Reason52} -> - p("start failed (as expected): ~p", [Reason52]), + ?IPRINT("start failed (as expected): ~p", [Reason52]), ?line {failed_check, _, _, _, {bad_address, _}} = Reason52, - await_config_not_running(); + config_ensure_not_running(); OK_52 -> + config_ensure_not_running(), exit({error, {unexpected_success, "52", OK_52}}) end, %% -- - p("[test 53] write agents config file with invalid ip (3)"), + ?IPRINT("[test 53] write agents config file with invalid ip (3)"), Agent53 = setelement(4, Agent0, "[192,168,0]"), write_agents_conf(ConfDir, [Agent53]), case config_start(Opts) of {error, Reason53} -> - p("start failed (as expected): ~p", [Reason53]), + ?IPRINT("start failed (as expected): ~p", [Reason53]), ?line {failed_check, _, _, _, {bad_address, _}} = Reason53, - await_config_not_running(); + config_ensure_not_running(); OK_53 -> + config_ensure_not_running(), exit({error, {unexpected_success, "53", OK_53}}) end, %% -- - p("[test 54] write agents config file with invalid ip (4)"), + ?IPRINT("[test 54] write agents config file with invalid ip (4)"), Agent54 = setelement(4, Agent0, "[192,168,0,100,99]"), write_agents_conf(ConfDir, [Agent54]), case config_start(Opts) of {error, Reason54} -> - p("start failed (as expected): ~p", [Reason54]), + ?IPRINT("start failed (as expected): ~p", [Reason54]), ?line {failed_check, _, _, _, {bad_address, _}} = Reason54, - await_config_not_running(); + config_ensure_not_running(); OK_54 -> + config_ensure_not_running(), exit({error, {unexpected_success, "54", OK_54}}) end, %% -- - p("[test 55] write agents config file with invalid ip (5)"), + ?IPRINT("[test 55] write agents config file with invalid ip (5)"), Agent55 = setelement(4, Agent0, "[192,168,0,arne]"), write_agents_conf(ConfDir, [Agent55]), ?line {error, Reason55} = config_start(Opts), - p("start failed (as expected): ~p", [Reason55]), + ?IPRINT("start failed (as expected): ~p", [Reason55]), ?line {failed_check, _, _, _, {bad_address, _}} = Reason55, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 61] write agents config file with invalid port (1)"), + ?IPRINT("[test 61] write agents config file with invalid port (1)"), Agent61 = setelement(5, Agent0, "kalle_anka"), write_agents_conf(ConfDir, [Agent61]), ?line {error, Reason61} = config_start(Opts), - p("start failed (as expected): ~p", [Reason61]), + ?IPRINT("start failed (as expected): ~p", [Reason61]), ?line {failed_check, _, _, _, {bad_address, _}} = Reason61, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 62] write agents config file with invalid port (2)"), + ?IPRINT("[test 62] write agents config file with invalid port (2)"), Agent62 = setelement(5, Agent0, "-1"), write_agents_conf(ConfDir, [Agent62]), ?line {error, Reason62} = config_start(Opts), - p("start failed (as expected): ~p", [Reason62]), + ?IPRINT("start failed (as expected): ~p", [Reason62]), ?line {failed_check, _, _, _, {bad_address, _}} = Reason62, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 63] write agents config file with invalid port (3)"), + ?IPRINT("[test 63] write agents config file with invalid port (3)"), Agent63 = setelement(5, Agent0, "\"100\""), write_agents_conf(ConfDir, [Agent63]), ?line {error, Reason63} = config_start(Opts), - p("start failed (as expected): ~p", [Reason63]), + ?IPRINT("start failed (as expected): ~p", [Reason63]), ?line {failed_check, _, _, _, {bad_address, _}} = Reason63, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 71] write agents config file with invalid engine-id (1)"), + ?IPRINT("[test 71] write agents config file with invalid engine-id (1)"), Agent71 = setelement(6, Agent0, "kalle_anka"), write_agents_conf(ConfDir, [Agent71]), ?line {error, Reason71} = config_start(Opts), - p("start failed (as expected): ~p", [Reason71]), + ?IPRINT("start failed (as expected): ~p", [Reason71]), ?line {failed_check, _, _, _, {invalid_string, _}} = Reason71, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 72] write agents config file with invalid engine-id (2)"), + ?IPRINT("[test 72] write agents config file with invalid engine-id (2)"), Agent72 = setelement(6, Agent0, "10101"), write_agents_conf(ConfDir, [Agent72]), ?line {error, Reason72} = config_start(Opts), - p("start failed (as expected): ~p", [Reason72]), + ?IPRINT("start failed (as expected): ~p", [Reason72]), ?line {failed_check, _, _, _, {invalid_string, _}} = Reason72, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 81] write agents config file with invalid timeout (1)"), + ?IPRINT("[test 81] write agents config file with invalid timeout (1)"), Agent81 = setelement(7, Agent0, "kalle_anka"), write_agents_conf(ConfDir, [Agent81]), ?line {error, Reason81} = config_start(Opts), - p("start failed (as expected): ~p", [Reason81]), + ?IPRINT("start failed (as expected): ~p", [Reason81]), ?line {failed_check, _, _, _, {invalid_timer, _}} = Reason81, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 82] write agents config file with invalid timeout (2)"), + ?IPRINT("[test 82] write agents config file with invalid timeout (2)"), Agent82 = setelement(7, Agent0, "-1"), write_agents_conf(ConfDir, [Agent82]), ?line {error, Reason82} = config_start(Opts), - p("start failed (as expected): ~p", [Reason82]), + ?IPRINT("start failed (as expected): ~p", [Reason82]), ?line {failed_check, _, _, _, {invalid_timer, _}} = Reason82, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 83] write agents config file with invalid timeout (3)"), + ?IPRINT("[test 83] write agents config file with invalid timeout (3)"), Agent83 = setelement(7, Agent0, "{1000, 1, 10, kalle}"), write_agents_conf(ConfDir, [Agent83]), ?line {error, Reason83} = config_start(Opts), - p("start failed (as expected): ~p", [Reason83]), + ?IPRINT("start failed (as expected): ~p", [Reason83]), ?line {failed_check, _, _, _, {invalid_timer, _}} = Reason83, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 84] write agents config file with invalid timeout (4)"), + ?IPRINT("[test 84] write agents config file with invalid timeout (4)"), Agent84 = setelement(7, Agent0, "{1000, -1, 10, 10}"), write_agents_conf(ConfDir, [Agent84]), ?line {error, Reason84} = config_start(Opts), - p("start failed (as expected): ~p", [Reason84]), + ?IPRINT("start failed (as expected): ~p", [Reason84]), ?line {failed_check, _, _, _, {invalid_timer, _}} = Reason84, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 85] write agents config file with invalid timeout (5)"), + ?IPRINT("[test 85] write agents config file with invalid timeout (5)"), Agent85 = setelement(7, Agent0, "{1000, 1, -100, 10}"), write_agents_conf(ConfDir, [Agent85]), ?line {error, Reason85} = config_start(Opts), - p("start failed (as expected): ~p", [Reason85]), + ?IPRINT("start failed (as expected): ~p", [Reason85]), ?line {failed_check, _, _, _, {invalid_timer, _}} = Reason85, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 86] write agents config file with invalid timeout (6)"), + ?IPRINT("[test 86] write agents config file with invalid timeout (6)"), Agent86 = setelement(7, Agent0, "{1000, 1, 100, -1}"), write_agents_conf(ConfDir, [Agent86]), ?line {error, Reason86} = config_start(Opts), - p("start failed (as expected): ~p", [Reason86]), + ?IPRINT("start failed (as expected): ~p", [Reason86]), ?line {failed_check, _, _, _, {invalid_timer, _}} = Reason86, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 91] write agents config file with invalid max-message-size (1)"), + ?IPRINT("[test 91] write agents config file with invalid max-message-size (1)"), Agent91 = setelement(8, Agent0, "483"), write_agents_conf(ConfDir, [Agent91]), ?line {error, Reason91} = config_start(Opts), - p("start failed (as expected): ~p", [Reason91]), + ?IPRINT("start failed (as expected): ~p", [Reason91]), ?line {failed_check, _, _, _, {invalid_packet_size, _}} = Reason91, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 92] write agents config file with invalid max-message-size (2)"), + ?IPRINT("[test 92] write agents config file with invalid max-message-size (2)"), Agent92 = setelement(8, Agent0, "kalle_anka"), write_agents_conf(ConfDir, [Agent92]), ?line {error, Reason92} = config_start(Opts), - p("start failed (as expected): ~p", [Reason92]), + ?IPRINT("start failed (as expected): ~p", [Reason92]), ?line {failed_check, _, _, _, {invalid_packet_size, _}} = Reason92, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test A1] write agents config file with invalid version (1)"), + ?IPRINT("[test A1] write agents config file with invalid version (1)"), AgentA1 = setelement(9, Agent0, "1"), write_agents_conf(ConfDir, [AgentA1]), ?line {error, ReasonA1} = config_start(Opts), - p("start failed (as expected): ~p", [ReasonA1]), + ?IPRINT("start failed (as expected): ~p", [ReasonA1]), ?line {failed_check, _, _, _, {bad_version, _}} = ReasonA1, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test A2] write agents config file with invalid version (2)"), + ?IPRINT("[test A2] write agents config file with invalid version (2)"), AgentA2 = setelement(9, Agent0, "v30"), write_agents_conf(ConfDir, [AgentA2]), ?line {error, ReasonA2} = config_start(Opts), - p("start failed (as expected): ~p", [ReasonA2]), + ?IPRINT("start failed (as expected): ~p", [ReasonA2]), ?line {failed_check, _, _, _, {bad_version, _}} = ReasonA2, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test B1] write agents config file with invalid sec-model (1)"), + ?IPRINT("[test B1] write agents config file with invalid sec-model (1)"), AgentB1 = setelement(10, Agent0, "\"any\""), write_agents_conf(ConfDir, [AgentB1]), ?line {error, ReasonB1} = config_start(Opts), - p("start failed (as expected): ~p", [ReasonB1]), + ?IPRINT("start failed (as expected): ~p", [ReasonB1]), ?line {failed_check, _, _, _, {invalid_sec_model, _}} = ReasonB1, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test B2] write agents config file with invalid sec-model (2)"), + ?IPRINT("[test B2] write agents config file with invalid sec-model (2)"), AgentB2 = setelement(10, Agent0, "v3"), write_agents_conf(ConfDir, [AgentB2]), ?line {error, ReasonB2} = config_start(Opts), - p("start failed (as expected): ~p", [ReasonB2]), + ?IPRINT("start failed (as expected): ~p", [ReasonB2]), ?line {failed_check, _, _, _, {invalid_sec_model, _}} = ReasonB2, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test C1] write agents config file with invalid sec-name (1)"), + ?IPRINT("[test C1] write agents config file with invalid sec-name (1)"), AgentC1 = setelement(11, Agent0, "initial"), write_agents_conf(ConfDir, [AgentC1]), case config_start(Opts) of {error, ReasonC1} -> - p("start failed (as expected): ~p", [ReasonC1]), + ?IPRINT("start failed (as expected): ~p", [ReasonC1]), ?line {failed_check, _, _, _, {bad_sec_name, _}} = ReasonC1, - await_config_not_running(); + config_ensure_not_running(); OK_C1 -> + config_ensure_not_running(), exit({error, {unexpected_success, "C1", OK_C1}}) end, %% -- - p("[test C2] write agents config file with invalid sec-name (2)"), + ?IPRINT("[test C2] write agents config file with invalid sec-name (2)"), AgentC2 = setelement(11, Agent0, "10101"), write_agents_conf(ConfDir, [AgentC2]), case config_start(Opts) of {error, ReasonC2} -> - p("start failed (as expected): ~p", [ReasonC2]), + ?IPRINT("start failed (as expected): ~p", [ReasonC2]), ?line {failed_check, _, _, _, {bad_sec_name, _}} = ReasonC2, - await_config_not_running(); + config_ensure_not_running(); OK_C2 -> + config_ensure_not_running(), exit({error, {unexpected_success, "C2", OK_C2}}) end, %% -- - p("[test D1] write agents config file with invalid sec-level (1)"), + ?IPRINT("[test D1] write agents config file with invalid sec-level (1)"), AgentD1 = setelement(12, Agent0, "\"noAuthNoPriv\""), write_agents_conf(ConfDir, [AgentD1]), case config_start(Opts) of {error, ReasonD1} -> - p("start failed (as expected): ~p", [ReasonD1]), + ?IPRINT("start failed (as expected): ~p", [ReasonD1]), ?line {failed_check, _, _, _, {invalid_sec_level, _}} = ReasonD1, - await_config_not_running(); + config_ensure_not_running(); OK_D1 -> + config_ensure_not_running(), exit({error, {unexpected_success, "D1", OK_D1}}) end, %% -- - p("[test D2] write agents config file with invalid sec-level (2)"), + ?IPRINT("[test D2] write agents config file with invalid sec-level (2)"), AgentD2 = setelement(12, Agent0, "99"), write_agents_conf(ConfDir, [AgentD2]), case config_start(Opts) of {error, ReasonD2} -> - p("start failed (as expected): ~p", [ReasonD2]), + ?IPRINT("start failed (as expected): ~p", [ReasonD2]), ?line {failed_check, _, _, _, {invalid_sec_level, _}} = ReasonD2, - await_config_not_running(); + config_ensure_not_running(); OK_D2 -> + config_ensure_not_running(), exit({error, {unexpected_success, "D2", OK_D2}}) end, %% -- - p("[test E1] write agents config file with invalid agent (1)"), + ?IPRINT("[test E1] write agents config file with invalid agent (1)"), write_agents_conf2(ConfDir, "{swiacf, \"targ-hobbes\"}."), case config_start(Opts) of {error, ReasonE1} -> - p("start failed (as expected): ~p", [ReasonE1]), + ?IPRINT("start failed (as expected): ~p", [ReasonE1]), ?line {failed_check, _, _, _, {bad_agent_config, _}} = ReasonE1, - await_config_not_running(); + config_ensure_not_running(); OK_E1 -> + config_ensure_not_running(), exit({error, {unexpected_success, "E1", OK_E1}}) end, - p("done"), + ?IPRINT("done"), ok. @@ -1389,8 +1449,8 @@ start_with_invalid_usm_conf_file1(suite) -> []; start_with_invalid_usm_conf_file1(doc) -> "Start with invalid usm config file."; start_with_invalid_usm_conf_file1(Conf) when is_list(Conf) -> - put(tname,swiusmcf), - p("start"), + put(tname, "START-W-INV-USM-CONF-FILE-1"), + ?IPRINT("start"), process_flag(trap_exit, true), case ?CRYPTO_START() of @@ -1424,179 +1484,179 @@ start_with_invalid_usm_conf_file1(Conf) when is_list(Conf) -> "usmNoPrivProtocol", "[]"}, %% -- - p("[test 11] write usm config file with invalid engine-id (1)"), + ?IPRINT("[test 11] write usm config file with invalid engine-id (1)"), Usm11 = setelement(1, Usm0, "kalle-anka"), write_usm_conf(ConfDir, [Usm11]), ?line {error, Reason11} = config_start(Opts), - p("start failed (as expected): ~p", [Reason11]), + ?IPRINT("start failed (as expected): ~p", [Reason11]), ?line {failed_reading, _, _, _, {parse_error, _}} = Reason11, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 12] write usm config file with invalid engine-id (2)"), + ?IPRINT("[test 12] write usm config file with invalid engine-id (2)"), Usm12 = setelement(1, Usm0, "kalle_anka"), write_usm_conf(ConfDir, [Usm12]), ?line {error, Reason12} = config_start(Opts), - p("start failed (as expected): ~p", [Reason12]), + ?IPRINT("start failed (as expected): ~p", [Reason12]), ?line {failed_check, _, _, _, {bad_usm_engine_id, _}} = Reason12, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 13] write usm config file with invalid engine-id (3)"), + ?IPRINT("[test 13] write usm config file with invalid engine-id (3)"), Usm13 = setelement(1, Usm1, "10101"), write_usm_conf(ConfDir, [Usm13]), ?line {error, Reason13} = config_start(Opts), - p("start failed (as expected): ~p", [Reason13]), + ?IPRINT("start failed (as expected): ~p", [Reason13]), ?line {failed_check, _, _, _, {bad_usm_engine_id, _}} = Reason13, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 21] write usm config file with invalid user-name (1)"), + ?IPRINT("[test 21] write usm config file with invalid user-name (1)"), Usm21 = setelement(2, Usm0, "kalle_anka"), write_usm_conf(ConfDir, [Usm21]), ?line {error, Reason21} = config_start(Opts), - p("start failed (as expected): ~p", [Reason21]), + ?IPRINT("start failed (as expected): ~p", [Reason21]), ?line {failed_check, _, _, _, {bad_usm_user_name, _}} = Reason21, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 22] write usm config file with invalid user-name (1)"), + ?IPRINT("[test 22] write usm config file with invalid user-name (1)"), Usm22 = setelement(2, Usm1, "10101"), write_usm_conf(ConfDir, [Usm22]), ?line {error, Reason22} = config_start(Opts), - p("start failed (as expected): ~p", [Reason22]), + ?IPRINT("start failed (as expected): ~p", [Reason22]), ?line {failed_check, _, _, _, {bad_usm_user_name, _}} = Reason22, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 31] write usm config file with invalid sec-name (1)"), + ?IPRINT("[test 31] write usm config file with invalid sec-name (1)"), Usm31 = setelement(3, Usm1, "kalle_anka"), write_usm_conf(ConfDir, [Usm31]), ?line {error, Reason31} = config_start(Opts), - p("start failed (as expected): ~p", [Reason31]), + ?IPRINT("start failed (as expected): ~p", [Reason31]), ?line {failed_check, _, _, _, {bad_usm_sec_name, _}} = Reason31, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 32] write usm config file with invalid sec-name (2)"), + ?IPRINT("[test 32] write usm config file with invalid sec-name (2)"), Usm32 = setelement(3, Usm1, "10101"), write_usm_conf(ConfDir, [Usm32]), ?line {error, Reason32} = config_start(Opts), - p("start failed (as expected): ~p", [Reason32]), + ?IPRINT("start failed (as expected): ~p", [Reason32]), ?line {failed_check, _, _, _, {bad_usm_sec_name, _}} = Reason32, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 41] write usm config file with invalid auth-protocol (1)"), + ?IPRINT("[test 41] write usm config file with invalid auth-protocol (1)"), Usm41 = setelement(3, Usm0, "\"usmNoAuthProtocol\""), write_usm_conf(ConfDir, [Usm41]), ?line {error, Reason41} = config_start(Opts), - p("start failed (as expected): ~p", [Reason41]), + ?IPRINT("start failed (as expected): ~p", [Reason41]), ?line {failed_check, _, _, _, {invalid_auth_protocol, _}} = Reason41, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 42] write usm config file with invalid auth-protocol (2)"), + ?IPRINT("[test 42] write usm config file with invalid auth-protocol (2)"), Usm42 = setelement(3, Usm0, "kalle"), write_usm_conf(ConfDir, [Usm42]), ?line {error, Reason42} = config_start(Opts), - p("start failed (as expected): ~p", [Reason42]), + ?IPRINT("start failed (as expected): ~p", [Reason42]), ?line {failed_check, _, _, _, {invalid_auth_protocol, _}} = Reason42, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 43] write usm config file with invalid auth-protocol (3)"), + ?IPRINT("[test 43] write usm config file with invalid auth-protocol (3)"), Usm43 = setelement(3, Usm0, "10101"), write_usm_conf(ConfDir, [Usm43]), ?line {error, Reason43} = config_start(Opts), - p("start failed (as expected): ~p", [Reason43]), + ?IPRINT("start failed (as expected): ~p", [Reason43]), ?line {failed_check, _, _, _, {invalid_auth_protocol, _}} = Reason43, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 51] write usm config file with invalid auth-key (1)"), + ?IPRINT("[test 51] write usm config file with invalid auth-key (1)"), Usm51 = setelement(3, Usm0, "usmHMACMD5AuthProtocol"), write_usm_conf(ConfDir, [Usm51]), ?line {error, Reason51} = config_start(Opts), - p("start failed (as expected): ~p", [Reason51]), + ?IPRINT("start failed (as expected): ~p", [Reason51]), ?line {failed_check, _, _, _, {invalid_auth_key, _, _}} = Reason51, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 52] write usm config file with invalid auth-key (2)"), + ?IPRINT("[test 52] write usm config file with invalid auth-key (2)"), Usm52 = setelement(4, Usm51, "[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5]"), write_usm_conf(ConfDir, [Usm52]), ?line {error, Reason52} = config_start(Opts), - p("start failed (as expected): ~p", [Reason52]), + ?IPRINT("start failed (as expected): ~p", [Reason52]), ?line {failed_check, _, _, _, {invalid_auth_key, _, 15}} = Reason52, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 53] write usm config file with invalid auth-key (3)"), + ?IPRINT("[test 53] write usm config file with invalid auth-key (3)"), Usm53 = setelement(4, Usm51, "[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7]"), write_usm_conf(ConfDir, [Usm53]), ?line {error, Reason53} = config_start(Opts), - p("start failed (as expected): ~p", [Reason53]), + ?IPRINT("start failed (as expected): ~p", [Reason53]), ?line {failed_check, _, _, _, {invalid_auth_key, _, 17}} = Reason53, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 54] write usm config file with invalid auth-key (4)"), + ?IPRINT("[test 54] write usm config file with invalid auth-key (4)"), Usm54 = setelement(4, Usm51, "[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,kalle]"), write_usm_conf(ConfDir, [Usm54]), ?line maybe_start_crypto(), %% Make sure it's started... ?line {error, Reason54} = config_start(Opts), ?line ok = maybe_stop_crypto(), - p("start failed (as expected): ~p", [Reason54]), + ?IPRINT("start failed (as expected): ~p", [Reason54]), ?line {failed_check, _, _, _, {invalid_auth_key, _}} = Reason54, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 55] write usm config file with invalid auth-key (5)"), + ?IPRINT("[test 55] write usm config file with invalid auth-key (5)"), Usm55 = setelement(4, Usm51, "arne_anka"), write_usm_conf(ConfDir, [Usm55]), ?line {error, Reason55} = config_start(Opts), - p("start failed (as expected): ~p", [Reason55]), + ?IPRINT("start failed (as expected): ~p", [Reason55]), ?line {failed_check, _, _, _, {invalid_auth_key, _}} = Reason55, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 56] write usm config file with invalid auth-key (6)"), + ?IPRINT("[test 56] write usm config file with invalid auth-key (6)"), Usm56 = setelement(4, Usm51, "10101"), write_usm_conf(ConfDir, [Usm56]), ?line {error, Reason56} = config_start(Opts), - p("start failed (as expected): ~p", [Reason56]), + ?IPRINT("start failed (as expected): ~p", [Reason56]), ?line {failed_check, _, _, _, {invalid_auth_key, _}} = Reason56, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 57] write usm config file with invalid auth-key (7)"), + ?IPRINT("[test 57] write usm config file with invalid auth-key (7)"), Usm57 = setelement(3, Usm0, "usmHMACSHAAuthProtocol"), write_usm_conf(ConfDir, [Usm57]), ?line {error, Reason57} = config_start(Opts), - p("start failed (as expected): ~p", [Reason57]), + ?IPRINT("start failed (as expected): ~p", [Reason57]), ?line {failed_check, _, _, _, {invalid_auth_key, _, _}} = Reason57, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 58] write usm config file with invalid auth-key (8)"), + ?IPRINT("[test 58] write usm config file with invalid auth-key (8)"), Usm58 = setelement(4, Usm57, "[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6]"), write_usm_conf(ConfDir, [Usm58]), ?line {error, Reason58} = config_start(Opts), - p("start failed (as expected): ~p", [Reason58]), + ?IPRINT("start failed (as expected): ~p", [Reason58]), ?line {failed_check, _, _, _, {invalid_auth_key, _, 16}} = Reason58, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 59] write usm config file with invalid auth-key (9)"), + ?IPRINT("[test 59] write usm config file with invalid auth-key (9)"), Usm59 = setelement(4, Usm57, "[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,ka]"), write_usm_conf(ConfDir, [Usm59]), ?line ok = maybe_start_crypto(), ?line {error, Reason59} = config_start(Opts), ?line ok = maybe_stop_crypto(), - p("start failed (as expected): ~p", [Reason59]), + ?IPRINT("start failed (as expected): ~p", [Reason59]), ?line {failed_check, _, _, _, {invalid_auth_key, _}} = Reason59, - await_config_not_running(), + config_ensure_not_running(), %% -- %% <CRYPTO-MODIFICATIONS> @@ -1604,16 +1664,16 @@ start_with_invalid_usm_conf_file1(Conf) when is_list(Conf) -> %% explicitly (all of it is as of R14 implemented with NIFs). case (catch crypto:version()) of {'EXIT', {undef, _}} -> - p("[test 5A] write usm config file with valid auth-key " + ?IPRINT("[test 5A] write usm config file with valid auth-key " "when crypto not started (10)"), Usm5A = setelement(4, Usm57, "[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0]"), write_usm_conf(ConfDir, [Usm5A]), ?line {error, Reason5A} = config_start(Opts), - p("start failed (as expected): ~p", [Reason5A]), + ?IPRINT("start failed (as expected): ~p", [Reason5A]), ?line {failed_check, _, _, _, {unsupported_crypto, _}} = Reason5A, - await_config_not_running(); + config_ensure_not_running(); _ -> %% This function is only present in version 2.0 or greater. %% The crypto app no longer needs to be explicitly started @@ -1622,87 +1682,87 @@ start_with_invalid_usm_conf_file1(Conf) when is_list(Conf) -> %% </CRYPTO-MODIFICATIONS> %% -- - p("[test 61] write usm config file with invalid priv-protocol (1)"), + ?IPRINT("[test 61] write usm config file with invalid priv-protocol (1)"), Usm61 = setelement(5, Usm0, "\"usmNoPrivProtocol\""), write_usm_conf(ConfDir, [Usm61]), ?line {error, Reason61} = config_start(Opts), - p("start failed (as expected): ~p", [Reason61]), + ?IPRINT("start failed (as expected): ~p", [Reason61]), ?line {failed_check, _, _, _, {invalid_priv_protocol, _}} = Reason61, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 62] write usm config file with invalid priv-protocol (2)"), + ?IPRINT("[test 62] write usm config file with invalid priv-protocol (2)"), Usm62 = setelement(5, Usm0, "kalle"), write_usm_conf(ConfDir, [Usm62]), ?line {error, Reason62} = config_start(Opts), - p("start failed (as expected): ~p", [Reason62]), + ?IPRINT("start failed (as expected): ~p", [Reason62]), ?line {failed_check, _, _, _, {invalid_priv_protocol, _}} = Reason62, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 63] write usm config file with invalid priv-protocol (3)"), + ?IPRINT("[test 63] write usm config file with invalid priv-protocol (3)"), Usm63 = setelement(5, Usm0, "10101"), write_usm_conf(ConfDir, [Usm63]), ?line {error, Reason63} = config_start(Opts), - p("start failed (as expected): ~p", [Reason63]), + ?IPRINT("start failed (as expected): ~p", [Reason63]), ?line {failed_check, _, _, _, {invalid_priv_protocol, _}} = Reason63, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 71] write usm config file with invalid priv-key (1)"), + ?IPRINT("[test 71] write usm config file with invalid priv-key (1)"), Usm71 = setelement(5, Usm0, "usmDESPrivProtocol"), write_usm_conf(ConfDir, [Usm71]), ?line {error, Reason71} = config_start(Opts), - p("start failed (as expected): ~p", [Reason71]), + ?IPRINT("start failed (as expected): ~p", [Reason71]), ?line {failed_check, _, _, _, {invalid_priv_key, _, _}} = Reason71, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 72] write usm config file with invalid priv-key (2)"), + ?IPRINT("[test 72] write usm config file with invalid priv-key (2)"), Usm72 = setelement(6, Usm71, "[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5]"), write_usm_conf(ConfDir, [Usm72]), ?line {error, Reason72} = config_start(Opts), - p("start failed (as expected): ~p", [Reason72]), + ?IPRINT("start failed (as expected): ~p", [Reason72]), ?line {failed_check, _, _, _, {invalid_priv_key, _, 15}} = Reason72, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 73] write usm config file with invalid priv-key (3)"), + ?IPRINT("[test 73] write usm config file with invalid priv-key (3)"), Usm73 = setelement(6, Usm71, "[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7]"), write_usm_conf(ConfDir, [Usm73]), ?line {error, Reason73} = config_start(Opts), - p("start failed (as expected): ~p", [Reason73]), + ?IPRINT("start failed (as expected): ~p", [Reason73]), ?line {failed_check, _, _, _, {invalid_priv_key, _, 17}} = Reason73, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 74] write usm config file with invalid priv-key (4)"), + ?IPRINT("[test 74] write usm config file with invalid priv-key (4)"), Usm74 = setelement(6, Usm71, "[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,kalle]"), write_usm_conf(ConfDir, [Usm74]), ?line ok = maybe_start_crypto(), ?line {error, Reason74} = config_start(Opts), ?line ok = maybe_stop_crypto(), - p("start failed (as expected): ~p", [Reason74]), + ?IPRINT("start failed (as expected): ~p", [Reason74]), ?line {failed_check, _, _, _, {invalid_priv_key, _}} = Reason74, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 75] write usm config file with invalid priv-key (5)"), + ?IPRINT("[test 75] write usm config file with invalid priv-key (5)"), Usm75 = setelement(6, Usm71, "arne_anka"), write_usm_conf(ConfDir, [Usm75]), ?line {error, Reason75} = config_start(Opts), - p("start failed (as expected): ~p", [Reason75]), + ?IPRINT("start failed (as expected): ~p", [Reason75]), ?line {failed_check, _, _, _, {invalid_priv_key, _}} = Reason75, - await_config_not_running(), + config_ensure_not_running(), %% -- - p("[test 76] write usm config file with invalid priv-key (6)"), + ?IPRINT("[test 76] write usm config file with invalid priv-key (6)"), Usm76 = setelement(6, Usm71, "10101"), write_usm_conf(ConfDir, [Usm76]), ?line {error, Reason76} = config_start(Opts), - p("start failed (as expected): ~p", [Reason76]), + ?IPRINT("start failed (as expected): ~p", [Reason76]), ?line {failed_check, _, _, _, {invalid_priv_key, _}} = Reason76, - await_config_not_running(), + config_ensure_not_running(), %% -- %% <CRYPTO-MODIFICATIONS> @@ -1710,14 +1770,14 @@ start_with_invalid_usm_conf_file1(Conf) when is_list(Conf) -> %% explicitly (all of it is as of R14 implemented with NIFs). case (catch crypto:version()) of {'EXIT', {undef, _}} -> - p("[test 77] write usm config file with valid priv-key " + ?IPRINT("[test 77] write usm config file with valid priv-key " "when crypto not started (7)"), Usm77 = setelement(6, Usm71, "[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6]"), write_usm_conf(ConfDir, [Usm77]), ?line {error, Reason77} = config_start(Opts), - p("start failed (as expected): ~p", [Reason77]), + ?IPRINT("start failed (as expected): ~p", [Reason77]), ?line {failed_check, _, _, _, {unsupported_crypto, _}} = Reason77, - await_config_not_running(); + config_ensure_not_running(); _ -> %% This function is only present in version 2.0 or greater. %% The crypto app no longer needs to be explicitly started @@ -1726,14 +1786,14 @@ start_with_invalid_usm_conf_file1(Conf) when is_list(Conf) -> %% </CRYPTO-MODIFICATIONS> %% -- - p("[test 78] write usm config file with invalid usm (1)"), + ?IPRINT("[test 78] write usm config file with invalid usm (1)"), write_usm_conf2(ConfDir, "{\"bmkEngine\", \"swiusmcf\"}."), ?line {error, Reason81} = config_start(Opts), - p("start failed (as expected): ~p", [Reason81]), + ?IPRINT("start failed (as expected): ~p", [Reason81]), ?line {failed_check, _, _, _, {bad_usm_config, _}} = Reason81, - await_config_not_running(), + config_ensure_not_running(), - p("done"), + ?IPRINT("done"), ok. @@ -1746,28 +1806,28 @@ start_with_create_db_and_dir_opt(doc) -> "Start the snmp manager config process with the\n" "create_db_and_dir option."; start_with_create_db_and_dir_opt(Conf) when is_list(Conf) -> - put(tname, swcdado), - p("start"), + put(tname, "START-W-CRE-DB-AND-DIR-OPT"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), DbDir = ?config(manager_db_dir, Conf), true = not filelib:is_dir(DbDir) and not filelib:is_file(DbDir), write_manager_conf(ConfDir), - p("verify nonexistent db_dir"), + ?IPRINT("verify nonexistent db_dir"), ConfigOpts01 = [{verbosity,trace}, {dir, ConfDir}, {db_dir, DbDir}], {error, Reason01} = config_start([{config, ConfigOpts01}]), - p("nonexistent db_dir res: ~p", [Reason01]), + ?IPRINT("nonexistent db_dir res: ~p", [Reason01]), {invalid_conf_db_dir, _, not_found} = Reason01, - p("verify nonexistent db_dir gets created"), + ?IPRINT("verify nonexistent db_dir gets created"), ConfigOpts02 = [{db_init_error, create_db_and_dir} | ConfigOpts01], {ok, _Pid} = config_start([{config, ConfigOpts02}]), true = filelib:is_dir(DbDir), - p("verified: nonexistent db_dir was correctly created"), + ?IPRINT("verified: nonexistent db_dir was correctly created"), ok = config_stop(), - p("done"), + ?IPRINT("done"), ok. %% @@ -1780,8 +1840,8 @@ simple_system_op(doc) -> "Access some of the known system info and some \n" "system info that does not exist."; simple_system_op(Conf) when is_list(Conf) -> - put(tname,sso), - p("start"), + put(tname, "SIMPLE-SYS-OP"), + ?IPRINT("start"), ConfDir = ?config(manager_conf_dir, Conf), DbDir = ?config(manager_db_dir, Conf), @@ -1790,21 +1850,21 @@ simple_system_op(Conf) when is_list(Conf) -> Opts = [{versions, [v1]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("start config"), + ?IPRINT("start config"), ?line {ok, _Pid} = config_start(Opts), - p("retreive various configs"), + ?IPRINT("retreive various configs"), ?line {ok, _Time} = snmpm_config:system_start_time(), ?line {ok, _EngineId} = snmpm_config:get_engine_id(), ?line {ok, _MMS} = snmpm_config:get_engine_max_message_size(), - p("attempt to retreive nonexisting"), + ?IPRINT("attempt to retreive nonexisting"), ?line {error, not_found} = snmpm_config:system_info(kalle), ?line ok = config_stop(), - await_config_not_running(), + config_ensure_not_running(), - p("done"), + ?IPRINT("done"), ok. @@ -1822,8 +1882,8 @@ register_user_using_file(suite) -> []; register_user_using_file(doc) -> "Register user using the 'users.conf' file."; register_user_using_file(Conf) when is_list(Conf) -> - put(tname,ruufi), - p("start"), + put(tname, "REG-USER-USING-FILE"), + ?IPRINT("start"), process_flag(trap_exit, true), _ConfDir = ?config(manager_conf_dir, Conf), _DbDir = ?config(manager_db_dir, Conf), @@ -1838,8 +1898,8 @@ register_user_using_function(suite) -> []; register_user_using_function(doc) -> "Register user using the API (function)."; register_user_using_function(Conf) when is_list(Conf) -> - put(tname,ruufu), - p("start"), + put(tname, "REG-USER-USING-FUNC"), + ?IPRINT("start"), process_flag(trap_exit, true), _ConfDir = ?config(manager_conf_dir, Conf), _DbDir = ?config(manager_db_dir, Conf), @@ -1854,8 +1914,8 @@ register_user_failed_using_function1(suite) -> []; register_user_failed_using_function1(doc) -> "Register user failed using incorrect arguments to API (function)."; register_user_failed_using_function1(Conf) when is_list(Conf) -> - put(tname,rufufu1), - p("start"), + put(tname, "REG-USER-FAIL-USING-FUNC-1"), + ?IPRINT("start"), process_flag(trap_exit, true), _ConfDir = ?config(manager_conf_dir, Conf), _DbDir = ?config(manager_db_dir, Conf), @@ -1872,13 +1932,29 @@ register_user_failed_using_function1(Conf) when is_list(Conf) -> %% --- %% +%% This test case tests that we can "register" agents using a config file. +%% So, starting the config process is part of the actual test, but even +%% if the test fails, we want to make sure the config process is actually +%% stop'ed. So, we put config stop in the post. + register_agent_using_file(suite) -> []; register_agent_using_file(doc) -> "Register agents using the 'agents'conf' file."; register_agent_using_file(Conf) when is_list(Conf) -> - put(tname,raufi), - p("start"), + put(tname, "REG-AG-USING-FILE"), process_flag(trap_exit, true), + Pre = fun() -> ok end, + Case = fun(_) -> do_register_agent_using_file(Conf) end, + Post = fun(_) -> + ?IPRINT("stop config process"), + ?line ok = snmpm_config:stop(), + config_ensure_not_running(), + ok + end, + ?TC_TRY(register_agent_using_file, Pre, Case, Post). + +do_register_agent_using_file(Conf) -> + ?IPRINT("start"), ConfDir = ?config(manager_conf_dir, Conf), DbDir = ?config(manager_db_dir, Conf), @@ -1887,11 +1963,11 @@ register_agent_using_file(Conf) when is_list(Conf) -> %% -- - p("write manager config file"), + ?IPRINT("write manager config file"), write_manager_conf(ConfDir), %% -- - p("write users config file"), + ?IPRINT("write users config file"), UserId1 = raufi1, UserId1Str = str(UserId1), UserId2 = raufi2, @@ -1901,7 +1977,7 @@ register_agent_using_file(Conf) when is_list(Conf) -> write_users_conf(ConfDir, [User1, User2]), %% -- - p("write agents config file"), + ?IPRINT("write agents config file"), AgentAddr1 = [192,168,0,101], AgentAddr1Str = str(AgentAddr1), AgentPort1 = 162, @@ -1929,38 +2005,38 @@ register_agent_using_file(Conf) when is_list(Conf) -> write_agents_conf(ConfDir, [Agent1Str, Agent2Str]), %% -- - p("start the config process"), + ?IPRINT("start the config process"), ?line {ok, _Pid} = config_start(Opts), %% -- - p("which agents"), + ?IPRINT("which agents"), ?line [_, _] = All = snmpm_config:which_agents(), - p("all agents: ~n ~p", [All]), + ?IPRINT("all agents: ~n ~p", [All]), ?line [A1] = snmpm_config:which_agents(UserId1), - p("agents belonging to ~w: ~n ~p", [UserId1, A1]), + ?IPRINT("agents belonging to ~w: ~n ~p", [UserId1, A1]), ?line [A2] = snmpm_config:which_agents(UserId2), - p("agents belonging to ~w: ~n ~p", [UserId2, A2]), + ?IPRINT("agents belonging to ~w: ~n ~p", [UserId2, A2]), %% -- - p("All info for agent <~w,~w>", [AgentAddr1, AgentPort1]), + ?IPRINT("All info for agent <~w,~w>", [AgentAddr1, AgentPort1]), ?line {ok, AllInfo1} = snmpm_config:agent_info(AgentAddr1, AgentPort1, all), - p("all agent info for agent: ~n ~p", [AllInfo1]), + ?IPRINT("all agent info for agent: ~n ~p", [AllInfo1]), %% -- - p("EngineID (~p) for agent <~w,~w>", [EngineID1, AgentAddr1, AgentPort1]), + ?IPRINT("EngineID (~p) for agent <~w,~w>", [EngineID1, AgentAddr1, AgentPort1]), ?line {ok, EngineID1} = snmpm_config:agent_info(AgentAddr1, AgentPort1, engine_id), %% -- - p("All info for agent <~w,~w>", [AgentAddr2, AgentPort2]), + ?IPRINT("All info for agent <~w,~w>", [AgentAddr2, AgentPort2]), ?line {ok, AllInfo2} = snmpm_config:agent_info(AgentAddr2, AgentPort2, all), - p("all agent info for agent: ~n ~p", [AllInfo2]), + ?IPRINT("all agent info for agent: ~n ~p", [AllInfo2]), %% -- - p("EngineID (~p) for agent <~w,~w>", [EngineID2, AgentAddr2, AgentPort2]), + ?IPRINT("EngineID (~p) for agent <~w,~w>", [EngineID2, AgentAddr2, AgentPort2]), ?line {ok, EngineID2} = snmpm_config:agent_info(AgentAddr2, AgentPort2, engine_id), @@ -1968,7 +2044,7 @@ register_agent_using_file(Conf) when is_list(Conf) -> ?line {ok, MMS2} = snmpm_config:agent_info(AgentAddr2, AgentPort2, max_message_size), NewMMS21 = 2048, - p("try update agent info max-message-size to ~w for agent <~w,~w>", + ?IPRINT("try update agent info max-message-size to ~w for agent <~w,~w>", [NewMMS21, AgentAddr2, AgentPort2]), ?line ok = snmpm_config:update_agent_info(UserId2, AgentAddr2, AgentPort2, max_message_size, NewMMS21), @@ -1976,34 +2052,29 @@ register_agent_using_file(Conf) when is_list(Conf) -> snmpm_config:agent_info(AgentAddr2, AgentPort2, max_message_size), %% -- - p("try (and fail) to update agent info max-message-size to ~w " + ?IPRINT("try (and fail) to update agent info max-message-size to ~w " "for agent <~w,~w> " "with user ~w (not owner)", [NewMMS21, AgentAddr2, AgentPort2, UserId1]), ?line {error, Reason01} = snmpm_config:update_agent_info(UserId1, AgentAddr2, AgentPort2, max_message_size, NewMMS21), - p("expected failure. Reason01: ~p", [Reason01]), + ?IPRINT("expected failure. Reason01: ~p", [Reason01]), ?line {ok, NewMMS21} = snmpm_config:agent_info(AgentAddr2, AgentPort2, max_message_size), %% -- NewMMS22 = 400, - p("try (and fail) to update agent info max-message-size to ~w " + ?IPRINT("try (and fail) to update agent info max-message-size to ~w " "for agent <~w,~w>", [NewMMS22, AgentAddr2, AgentPort2]), ?line {error, Reason02} = snmpm_config:update_agent_info(UserId1, AgentAddr2, AgentPort2, max_message_size, NewMMS22), - p("expected failure. Reason02: ~p", [Reason02]), + ?IPRINT("expected failure. Reason02: ~p", [Reason02]), %% -- - p("stop config process"), - ?line ok = snmpm_config:stop(), - await_config_not_running(), - - %% -- - p("done"), + ?IPRINT("done"), ok. @@ -2015,8 +2086,8 @@ register_agent_using_function(suite) -> []; register_agent_using_function(doc) -> "Register agents using the API (function)."; register_agent_using_function(Conf) when is_list(Conf) -> - put(tname,raufu), - p("start"), + put(tname, "REG-AG-USING-FUNC"), + ?IPRINT("start"), process_flag(trap_exit, true), _ConfDir = ?config(manager_conf_dir, Conf), _DbDir = ?config(manager_db_dir, Conf), @@ -2032,8 +2103,8 @@ register_agent_failed_using_function1(doc) -> "Register agents failng using the API (function) with incorrect " "config (1)."; register_agent_failed_using_function1(Conf) when is_list(Conf) -> - put(tname,rafuf1), - p("start"), + put(tname, "REG-AG-FAIL-USING-FUNC-1"), + ?IPRINT("start"), process_flag(trap_exit, true), _ConfDir = ?config(manager_conf_dir, Conf), _DbDir = ?config(manager_db_dir, Conf), @@ -2054,8 +2125,8 @@ register_usm_user_using_file(suite) -> []; register_usm_user_using_file(doc) -> "Register usm user using the 'usm.conf' file."; register_usm_user_using_file(Conf) when is_list(Conf) -> - put(tname,ruuufi), - p("start"), + put(tname, "REG-USM-USER-USING-FILE"), + ?IPRINT("start"), process_flag(trap_exit, true), case ?CRYPTO_START() of @@ -2077,11 +2148,11 @@ register_usm_user_using_file(Conf) when is_list(Conf) -> {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], %% -- - p("write manager config file"), + ?IPRINT("write manager config file"), write_manager_conf(ConfDir), %% -- - p("write usm user config file"), + ?IPRINT("write usm user config file"), SecEngineID = "loctzp's engine", SecName1 = "samu_auth1", UserName1 = SecName1, @@ -2101,31 +2172,31 @@ register_usm_user_using_file(Conf) when is_list(Conf) -> write_usm_conf(ConfDir, [UsmUser1, UsmUser2]), %% -- - p("start the config process"), + ?IPRINT("start the config process"), ?line {ok, _Pid} = config_start(Opts), %% -- - p("lookup 1 (ok)"), + ?IPRINT("lookup 1 (ok)"), ?line {ok, #usm_user{name = UserName1} = User1} = snmpm_config:get_usm_user_from_sec_name(SecEngineID, SecName1), - p("User: ~p", [User1]), + ?IPRINT("User: ~p", [User1]), - p("lookup 2 (ok)"), + ?IPRINT("lookup 2 (ok)"), ?line {ok, #usm_user{name = UserName2} = User2} = snmpm_config:get_usm_user_from_sec_name(SecEngineID, SecName2), - p("User: ~p", [User2]), + ?IPRINT("User: ~p", [User2]), - p("lookup 3 (error)"), + ?IPRINT("lookup 3 (error)"), ?line {error, not_found} = snmpm_config:get_usm_user_from_sec_name(SecEngineID, SecName2 ++ "_1"), %% -- - p("stop config process"), + ?IPRINT("stop config process"), ?line ok = snmpm_config:stop(), - await_config_not_running(), + config_ensure_not_running(), %% -- - p("done"), + ?IPRINT("done"), ok. @@ -2137,8 +2208,8 @@ register_usm_user_using_function(suite) -> []; register_usm_user_using_function(doc) -> "Register usm user using the API (function)."; register_usm_user_using_function(Conf) when is_list(Conf) -> - put(tname,ruuufu), - p("start"), + put(tname, "REG-USM-USER-USING-FUNC"), + ?IPRINT("start"), process_flag(trap_exit, true), case ?CRYPTO_START() of @@ -2160,18 +2231,18 @@ register_usm_user_using_function(Conf) when is_list(Conf) -> {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], %% -- - p("write manager config file"), + ?IPRINT("write manager config file"), write_manager_conf(ConfDir), %% -- - p("start the config process"), + ?IPRINT("start the config process"), ?line {ok, _Pid} = config_start(Opts), %% -- - p("register usm user's"), + ?IPRINT("register usm user's"), EngineID = "loctzp's engine", - p("register user 1 (ok)"), + ?IPRINT("register user 1 (ok)"), UserName1 = "samu_auth1", SecName1 = UserName1, UsmConfig1 = [{sec_name, SecName1}, @@ -2179,11 +2250,11 @@ register_usm_user_using_function(Conf) when is_list(Conf) -> {auth_key, [1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6]}, {priv, usmNoPrivProtocol}], ?line ok = snmpm_config:register_usm_user(EngineID, UserName1, UsmConfig1), - p("try register user 1 again (error)"), + ?IPRINT("try register user 1 again (error)"), ?line {error, {already_registered, EngineID, UserName1}} = snmpm_config:register_usm_user(EngineID, UserName1, UsmConfig1), - p("register user 2 (ok)"), + ?IPRINT("register user 2 (ok)"), UserName2 = "samu_auth2", SecName2 = UserName2, UsmConfig2 = [{auth, usmHMACMD5AuthProtocol}, @@ -2191,7 +2262,7 @@ register_usm_user_using_function(Conf) when is_list(Conf) -> {priv, usmNoPrivProtocol}], ?line ok = snmpm_config:register_usm_user(EngineID, UserName2, UsmConfig2), - p("register user 3 (ok)"), + ?IPRINT("register user 3 (ok)"), UserName3 = "samu3", SecName3 = "samu_auth3", UsmConfig3 = [{sec_name, SecName3}, @@ -2200,32 +2271,32 @@ register_usm_user_using_function(Conf) when is_list(Conf) -> {priv, usmNoPrivProtocol}], ?line ok = snmpm_config:register_usm_user(EngineID, UserName3, UsmConfig3), - p("lookup 1 (ok)"), + ?IPRINT("lookup 1 (ok)"), ?line {ok, #usm_user{name = UserName1} = User1} = snmpm_config:get_usm_user_from_sec_name(EngineID, SecName1), - p("User: ~p", [User1]), + ?IPRINT("User: ~p", [User1]), - p("lookup 2 (ok)"), + ?IPRINT("lookup 2 (ok)"), ?line {ok, #usm_user{name = UserName2} = User2} = snmpm_config:get_usm_user_from_sec_name(EngineID, SecName2), - p("User: ~p", [User2]), + ?IPRINT("User: ~p", [User2]), - p("lookup 3 (ok)"), + ?IPRINT("lookup 3 (ok)"), ?line {ok, #usm_user{name = UserName3} = User3} = snmpm_config:get_usm_user_from_sec_name(EngineID, SecName3), - p("User: ~p", [User3]), + ?IPRINT("User: ~p", [User3]), - p("lookup 4 (error)"), + ?IPRINT("lookup 4 (error)"), ?line {error, not_found} = snmpm_config:get_usm_user_from_sec_name(EngineID, SecName3 ++ "_1"), %% -- - p("stop config process"), + ?IPRINT("stop config process"), ?line ok = snmpm_config:stop(), - await_config_not_running(), + config_ensure_not_running(), %% -- - p("done"), + ?IPRINT("done"), ok. @@ -2237,8 +2308,8 @@ register_usm_user_failed_using_function1(suite) -> []; register_usm_user_failed_using_function1(doc) -> "Register usm user failed using incorrect arguments to API (function)."; register_usm_user_failed_using_function1(Conf) when is_list(Conf) -> - put(tname,ruufufu1), - p("start"), + put(tname, "REG-USM-USER-FAIL-USING-FUNC"), + ?IPRINT("start"), process_flag(trap_exit, true), case ?CRYPTO_START() of @@ -2266,8 +2337,8 @@ update_usm_user_info(suite) -> []; update_usm_user_info(doc) -> "Update usm user info."; update_usm_user_info(Conf) when is_list(Conf) -> - put(tname,ruufufu1), - p("start"), + put(tname, "UPD-USM-USER-INFO"), + ?IPRINT("start"), process_flag(trap_exit, true), case ?CRYPTO_START() of @@ -2301,8 +2372,8 @@ create_and_increment(suite) -> []; create_and_increment(doc) -> "Craete and increment counters."; create_and_increment(Conf) when is_list(Conf) -> - put(tname,cai), - p("start"), + put(tname, "CRE-AND-INC"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), @@ -2317,10 +2388,6 @@ create_and_increment(Conf) when is_list(Conf) -> %% Random init ?SNMP_RAND_SEED(), - %% rand:seed(exrop, - %% {erlang:phash2([node()]), - %% erlang:monotonic_time(), - %% erlang:unique_integer()}), StartVal = rand:uniform(2147483647), IncVal = 42, @@ -2330,7 +2397,7 @@ create_and_increment(Conf) when is_list(Conf) -> ?line EndVal = snmpm_config:incr_counter(test_id, IncVal), ?line ok = snmpm_config:stop(), - await_config_not_running(), + config_ensure_not_running(), ok. @@ -2348,8 +2415,8 @@ stats_create_and_increment(suite) -> []; stats_create_and_increment(doc) -> "Create and increment statistics counters."; stats_create_and_increment(Conf) when is_list(Conf) -> - put(tname,scai), - p("start"), + put(tname, "STATS-CRE-AND-INC"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), @@ -2362,26 +2429,26 @@ stats_create_and_increment(Conf) when is_list(Conf) -> ?line {ok, _Pid} = snmpm_config:start_link(Opts), - p("stats table (1): ~p", [ets:tab2list(snmpm_stats_table)]), + ?IPRINT("stats table (1): ~p", [ets:tab2list(snmpm_stats_table)]), ?line 0 = snmpm_config:maybe_cre_stats_counter(stats1, 0), - p("stats table (2): ~p", [ets:tab2list(snmpm_stats_table)]), + ?IPRINT("stats table (2): ~p", [ets:tab2list(snmpm_stats_table)]), ?line ok = snmpm_config:maybe_cre_stats_counter(stats1, 0), - p("stats table (3): ~p", [ets:tab2list(snmpm_stats_table)]), + ?IPRINT("stats table (3): ~p", [ets:tab2list(snmpm_stats_table)]), ?line 1 = snmpm_config:maybe_cre_stats_counter(stats2, 1), - p("stats table (4): ~p", [ets:tab2list(snmpm_stats_table)]), + ?IPRINT("stats table (4): ~p", [ets:tab2list(snmpm_stats_table)]), ?line 10 = snmpm_config:cre_stats_counter(stats3, 10), - p("stats table (5): ~p", [ets:tab2list(snmpm_stats_table)]), + ?IPRINT("stats table (5): ~p", [ets:tab2list(snmpm_stats_table)]), Stats1Inc = fun() -> snmpm_config:incr_stats_counter(stats1, 1) end, ?line 10 = loop(10, -1, Stats1Inc), - p("stats table (6): ~p", [ets:tab2list(snmpm_stats_table)]), + ?IPRINT("stats table (6): ~p", [ets:tab2list(snmpm_stats_table)]), ?line ok = snmpm_config:reset_stats_counter(stats1), ?line 10 = loop(10, -1, Stats1Inc), ?line ok = snmpm_config:stop(), - await_config_not_running(), + config_ensure_not_running(), ok. @@ -2403,46 +2470,46 @@ otp_7219(suite) -> otp_7219(doc) -> "Test-case for ticket OTP-7219"; otp_7219(Config) when is_list(Config) -> - put(tname, otp7219), - p("start"), + put(tname, "OTP-7219"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Config), DbDir = ?config(manager_db_dir, Config), - p("write manager configuration"), + ?IPRINT("write manager configuration"), write_manager_conf(ConfDir), Opts1 = [{versions, [v1]}, {inform_request_behaviour, user}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("start manager config"), + ?IPRINT("start manager config"), ?line {ok, _Pid1} = snmpm_config:start_link(Opts1), - p("get some manager config"), + ?IPRINT("get some manager config"), {ok, {user, _}} = snmpm_config:system_info(net_if_irb), - p("stop manager config"), + ?IPRINT("stop manager config"), ?line ok = snmpm_config:stop(), - await_config_not_running(), + config_ensure_not_running(), IRB_TO = 15322, Opts2 = [{versions, [v1]}, {inform_request_behaviour, {user, IRB_TO}}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("start manager config"), + ?IPRINT("start manager config"), ?line {ok, _Pid2} = snmpm_config:start_link(Opts2), - p("get some manager config"), + ?IPRINT("get some manager config"), {ok, {user, IRB_TO}} = snmpm_config:system_info(net_if_irb), - p("stop manager config"), + ?IPRINT("stop manager config"), ?line ok = snmpm_config:stop(), - await_config_not_running(), + config_ensure_not_running(), - p("done"), + ?IPRINT("done"), ok. @@ -2452,8 +2519,8 @@ otp_8395_1(suite) -> []; otp_8395_1(doc) -> "OTP-8395(1)"; otp_8395_1(Conf) when is_list(Conf) -> - put(tname, otp_8395_1), - p("start"), + put(tname, "OTP-8395-1"), + ?IPRINT("start"), process_flag(trap_exit, true), otp8395(Conf, false, ok), ok. @@ -2462,8 +2529,8 @@ otp_8395_2(suite) -> []; otp_8395_2(doc) -> "OTP-8395(2)"; otp_8395_2(Conf) when is_list(Conf) -> - put(tname, otp_8395_2), - p("start"), + put(tname, "OTP-8395-2"), + ?IPRINT("start"), process_flag(trap_exit, true), otp8395(Conf, true, ok), ok. @@ -2472,8 +2539,8 @@ otp_8395_3(suite) -> []; otp_8395_3(doc) -> "OTP-8395(3)"; otp_8395_3(Conf) when is_list(Conf) -> - put(tname, otp_8395_3), - p("start"), + put(tname, "OTP-8395-3"), + ?IPRINT("start"), process_flag(trap_exit, true), otp8395(Conf, gurka, error), ok. @@ -2487,7 +2554,7 @@ otp8395(Conf, SeqNoVal, Expect) -> write_manager_conf(ConfDir), %% Third set of options (no versions): - p("all options"), + ?IPRINT("all options"), NetIfOpts = [{module, snmpm_net_if}, {verbosity, trace}, {options, [{recbuf, 30000}, @@ -2526,7 +2593,7 @@ otp8395(Conf, SeqNoVal, Expect) -> Error when (Expect =:= ok) -> exit({unexpected_failed_starting_config, SeqNoVal, Error}) end, - p("done"), + ?IPRINT("done"), ok. @@ -2534,8 +2601,8 @@ otp_8395_4(suite) -> []; otp_8395_4(doc) -> "OTP-8395(4)"; otp_8395_4(Conf) when is_list(Conf) -> - put(tname, otp_8395_4), - p("start"), + put(tname, "OTP-8395-4"), + ?IPRINT("start"), process_flag(trap_exit, true), snmp:print_version_info(), @@ -2548,7 +2615,7 @@ otp_8395_4(Conf) when is_list(Conf) -> write_manager_conf(ConfDir), %% Third set of options (no versions): - p("all options"), + ?IPRINT("all options"), NetIfOpts = [{module, snmpm_net_if}, {verbosity, trace}, {options, [{recbuf, 30000}, @@ -2595,7 +2662,7 @@ otp_8395_4(Conf) when is_list(Conf) -> ?line ok = config_stop(), - p("done"), + ?IPRINT("done"), ok. @@ -2607,52 +2674,17 @@ otp8395_incr_counter(Counter, Initial, Increment, Max) -> %% Internal functions %%====================================================================== -await_config_not_running() -> - await_not_running(snmpm_config, 5). - -await_not_running(Name, 0) -> - p("await_not_running -> done waiting for ~w to die - try kill it", [Name]), - %% Ok, we tried it the nice way, now use brute force - await_killed(Name, 5); -await_not_running(Name, N) when N > 0 -> - p("await_not_running -> is process ~w still running (~w)", [Name, N]), - case erlang:whereis(Name) of - undefined -> - p("await_not_running -> no such (~w) process - sleep some",[Name]), - ?SLEEP(1000), - p("await_not_running -> no such (~w) process - done", [Name]), - ok; - Pid when is_pid(Pid) -> - p("~w process still running", [Name]), - ?SLEEP(500), - await_not_running(Name, N-1) - end. - -await_killed(Name, 0) -> - p("await_killed -> could not kill ~w => giving up", [Name]), - exit({error, {failed_terminating, Name}}); -await_killed(Name, N) when N > 0 -> - p("await_killed -> is process ~w still running (~w)", [Name, N]), - case whereis(Name) of - undefined -> - p("await_killed -> no such (~w) process - sleep some", [Name]), - ?SLEEP(1000), - p("await_killed -> no such (~w) process - done", [Name]), - ok; - Pid when is_pid(Pid) -> - p("await_killed -> ~w still running - try kill it", [Name]), - exit(Pid, kill), - ?SLEEP(1000), - await_killed(Name, N-1) - end. - - config_start(Opts) -> (catch snmpm_config:start_link(Opts)). config_stop() -> (catch snmpm_config:stop()). +config_ensure_not_running() -> + ?ENSURE_NOT_RUNNING(snmpm_config, + fun() -> snmpm_config:stop() end, + 1000). + %% ------ @@ -2809,20 +2841,5 @@ verify_dir_existing(DirName, Dir) -> %% ------ str(X) -> - lists:flatten(io_lib:format("~w", [X])). - - -%% ------ - -p(F) -> - p(F, []). - -p(F, A) -> - p(get(tname), F, A). - -p(TName, F, A) -> - io:format("*** [~s] ***" - " ~w -> " ++ F ++ "~n", [formated_timestamp(),TName|A]). + ?F("~w", [X]). -formated_timestamp() -> - snmp_test_lib:formated_timestamp(). diff --git a/lib/snmp/test/snmp_manager_user_SUITE.erl b/lib/snmp/test/snmp_manager_user_SUITE.erl index 353f1b21f3..eca0d8a4f9 100644 --- a/lib/snmp/test/snmp_manager_user_SUITE.erl +++ b/lib/snmp/test/snmp_manager_user_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2019. All Rights Reserved. +%% Copyright Ericsson AB 2004-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -143,8 +143,8 @@ tickets_cases() -> init_per_suite(Config0) when is_list(Config0) -> - p("init_per_suite -> entry with" - "~n Config0: ~p", [Config0]), + ?IPRINT("init_per_suite -> entry with" + "~n Config0: ~p", [Config0]), case ?LIB:init_per_suite(Config0) of {skip, _} = SKIP -> @@ -153,19 +153,23 @@ init_per_suite(Config0) when is_list(Config0) -> snmp_test_sys_monitor:start(), Config2 = snmp_test_lib:init_suite_top_dir(?MODULE, Config1), - p("init_per_suite -> done when" - "~n Config: ~p", [Config2]), + ?IPRINT("init_per_suite -> done when" + "~n Config: ~p", [Config2]), Config2 end. end_per_suite(Config) when is_list(Config) -> - p("end_per_suite -> entry with" - "~n Config: ~p", [Config]), + ?IPRINT("end_per_suite -> entry with" + "~n Config: ~p", [Config]), snmp_test_sys_monitor:stop(), - ?LIB:end_per_suite(Config). + Config1 = ?LIB:end_per_suite(Config), + + ?IPRINT("end_per_suite -> end"), + + Config1. @@ -186,14 +190,15 @@ end_per_group(_GroupName, Config) -> %% init_per_testcase(Case, Config) when is_list(Config) -> - p("init_per_testcase -> Case: ~p", [Case]), + ?IPRINT("init_per_testcase -> entry with" + "~n Config: ~p", [Config]), snmp_test_global_sys_monitor:reset_events(), SuiteTopDir = ?config(snmp_suite_top_dir, Config), CaseTopDir = filename:join(SuiteTopDir, atom_to_list(Case)), ?line ok = file:make_dir(CaseTopDir), - p("init_per_testcase -> CaseTopDir: ~p", [CaseTopDir]), + ?IPRINT("init_per_testcase -> CaseTopDir: ~p", [CaseTopDir]), MgrTopDir = filename:join(CaseTopDir, "manager/"), ?line ok = file:make_dir(MgrTopDir), MgrConfDir = filename:join(MgrTopDir, "conf/"), @@ -202,18 +207,27 @@ init_per_testcase(Case, Config) when is_list(Config) -> ?line ok = file:make_dir(MgrDbDir), MgrLogDir = filename:join(MgrTopDir, "log/"), ?line ok = file:make_dir(MgrLogDir), - [{case_top_dir, CaseTopDir}, - {manager_dir, MgrTopDir}, - {manager_conf_dir, MgrConfDir}, - {manager_db_dir, MgrDbDir}, - {manager_log_dir, MgrLogDir} | Config]. + Config1 = [{case_top_dir, CaseTopDir}, + {manager_dir, MgrTopDir}, + {manager_conf_dir, MgrConfDir}, + {manager_db_dir, MgrDbDir}, + {manager_log_dir, MgrLogDir} | Config], + + ?IPRINT("init_per_testcase -> done when" + "~n Config: ~p", [Config1]), + + Config1. + + +end_per_testcase(_Case, Config) when is_list(Config) -> -end_per_testcase(Case, Config) when is_list(Config) -> - p("end_per_testcase -> Case: ~p", [Case]), + ?IPRINT("end_per_testcase -> entry with" + "~n Config: ~p", + [Config]), - p("system events during test: " - "~n ~p~n", [snmp_test_global_sys_monitor:events()]), + ?IPRINT("system events during test: ~p", + [snmp_test_global_sys_monitor:events()]), Config. @@ -228,7 +242,7 @@ simple_register_and_unregister1(doc) -> "Start a user, register and unregister the user."; simple_register_and_unregister1(Conf) when is_list(Conf) -> put(tname,srar1), - p("start"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), @@ -241,7 +255,7 @@ simple_register_and_unregister1(Conf) when is_list(Conf) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try starting manager"), + ?IPRINT("try starting manager"), ok = snmpm:start_link(Opts), ?SLEEP(1000), @@ -251,23 +265,23 @@ simple_register_and_unregister1(Conf) when is_list(Conf) -> ?line Pid = start_user(), ?line [] = Users1 = which_users(), - p("Users1: ~p", [Users1]), + ?IPRINT("Users1: ~p", [Users1]), ?line ok = register_user(Pid, Id), ?line [Id] = Users2 = which_users(), - p("Users2: ~p", [Users2]), + ?IPRINT("Users2: ~p", [Users2]), ?line ok = unregister_user(Pid), ?line [] = Users3 = which_users(), - p("Users3: ~p", [Users3]), + ?IPRINT("Users3: ~p", [Users3]), ?line stop_user(Pid), ?line ok = snmpm:stop(), - p("end"), + ?IPRINT("end"), ok. @@ -279,7 +293,7 @@ simple_register_and_unregister2(doc) -> "register 2 users (per that process) and unregister the 2 users."; simple_register_and_unregister2(Conf) when is_list(Conf) -> put(tname,srar2), - p("start"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), @@ -292,7 +306,7 @@ simple_register_and_unregister2(Conf) when is_list(Conf) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try starting manager"), + ?IPRINT("try starting manager"), ok = snmpm:start_link(Opts), ?SLEEP(1000), @@ -303,7 +317,7 @@ simple_register_and_unregister2(Conf) when is_list(Conf) -> ?line Pid = start_user(), ?line [] = Users1 = which_users(), - p("Users1: ~p", [Users1]), + ?IPRINT("Users1: ~p", [Users1]), ?line ok = register_user(Pid, Id1), ?line ok = register_user(Pid, Id2), @@ -316,20 +330,20 @@ simple_register_and_unregister2(Conf) when is_list(Conf) -> Else -> ?FAIL({invalid_users, Else}) end, - p("Users2: ~p", [Users2]), + ?IPRINT("Users2: ~p", [Users2]), ?line ok = unregister_user(Pid, Id1), ?line ok = unregister_user(Pid, Id2), ?line [] = Users3 = which_users(), - p("Users3: ~p", [Users3]), + ?IPRINT("Users3: ~p", [Users3]), ?line stop_user(Pid), ?line ok = snmpm:stop(), - p("end"), + ?IPRINT("end"), ok. @@ -341,7 +355,7 @@ simple_register_and_unregister3(doc) -> "register one users per process and unregister the 2 users."; simple_register_and_unregister3(Conf) when is_list(Conf) -> put(tname,srar2), - p("start"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), @@ -354,7 +368,7 @@ simple_register_and_unregister3(Conf) when is_list(Conf) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try starting manager"), + ?IPRINT("try starting manager"), ok = snmpm:start_link(Opts), ?SLEEP(1000), @@ -366,7 +380,7 @@ simple_register_and_unregister3(Conf) when is_list(Conf) -> ?line Pid2 = start_user(), ?line [] = Users1 = which_users(), - p("Users1: ~p", [Users1]), + ?IPRINT("Users1: ~p", [Users1]), ?line ok = register_user(Pid1, Id1), ?line ok = register_user(Pid2, Id2), @@ -379,21 +393,21 @@ simple_register_and_unregister3(Conf) when is_list(Conf) -> Else -> ?FAIL({invalid_users, Else}) end, - p("Users2: ~p", [Users2]), + ?IPRINT("Users2: ~p", [Users2]), ?line ok = unregister_user(Pid1, Id1), ?line ok = unregister_user(Pid2, Id2), ?line [] = Users3 = which_users(), - p("Users3: ~p", [Users3]), + ?IPRINT("Users3: ~p", [Users3]), ?line stop_user(Pid1), ?line stop_user(Pid2), ?line ok = snmpm:stop(), - p("end"), + ?IPRINT("end"), ok. @@ -404,7 +418,7 @@ register_and_crash1(doc) -> "Start a user, register and crash user."; register_and_crash1(Conf) when is_list(Conf) -> put(tname,racau1), - p("start"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), @@ -417,7 +431,7 @@ register_and_crash1(Conf) when is_list(Conf) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try starting manager"), + ?IPRINT("try starting manager"), ?line ok = snmpm:start_link(Opts), ?SLEEP(1000), @@ -427,22 +441,22 @@ register_and_crash1(Conf) when is_list(Conf) -> ?line Pid = start_user(), ?line [] = Users1 = which_users(), - p("Users1: ~p", [Users1]), + ?IPRINT("Users1: ~p", [Users1]), ?line ok = register_user(Pid, Id), ?line [Id] = Users2 = which_users(), - p("Users2: ~p", [Users2]), + ?IPRINT("Users2: ~p", [Users2]), ?line ok = simulate_crash(Pid), ?line [Id] = Users3 = which_users(), - p("Users3: ~p", [Users3]), + ?IPRINT("Users3: ~p", [Users3]), - p("stop manager"), + ?IPRINT("stop manager"), ?line ok = snmpm:stop(), - p("end"), + ?IPRINT("end"), ok. @@ -454,7 +468,7 @@ register_and_crash2(doc) -> "register 2 users (per that process) and crash the process."; register_and_crash2(Conf) when is_list(Conf) -> put(tname,racau2), - p("start"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), @@ -467,7 +481,7 @@ register_and_crash2(Conf) when is_list(Conf) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try starting manager"), + ?IPRINT("try starting manager"), ?line ok = snmpm:start_link(Opts), ?SLEEP(1000), @@ -478,7 +492,7 @@ register_and_crash2(Conf) when is_list(Conf) -> ?line Pid = start_user(), ?line [] = Users1 = which_users(), - p("Users1: ~p", [Users1]), + ?IPRINT("Users1: ~p", [Users1]), ?line ok = register_user(Pid, Id1), ?line ok = register_user(Pid, Id2), @@ -491,7 +505,7 @@ register_and_crash2(Conf) when is_list(Conf) -> Else1 -> ?FAIL({invalid_users, Else1}) end, - p("Users2: ~p", [Users2]), + ?IPRINT("Users2: ~p", [Users2]), ?line ok = simulate_crash(Pid), @@ -503,12 +517,12 @@ register_and_crash2(Conf) when is_list(Conf) -> Else2 -> ?FAIL({invalid_users, Else2}) end, - p("Users3: ~p", [Users3]), + ?IPRINT("Users3: ~p", [Users3]), - p("stop manager"), + ?IPRINT("stop manager"), ?line ok = snmpm:stop(), - p("end"), + ?IPRINT("end"), ok. @@ -521,7 +535,7 @@ register_and_crash3(doc) -> "crash the first user process."; register_and_crash3(Conf) when is_list(Conf) -> %% put(tname,rac3), - %% p("start"), + %% ?IPRINT("start"), %% process_flag(trap_exit, true), ?SKIP(not_yet_implemented). @@ -534,7 +548,7 @@ register_request_and_crash1(doc) -> "register user, send request and crash user."; register_request_and_crash1(Conf) when is_list(Conf) -> %% put(tname,rrac1), - %% p("start"), + %% ?IPRINT("start"), %% process_flag(trap_exit, true), ?SKIP(not_yet_implemented). @@ -548,7 +562,7 @@ register_request_and_crash2(doc) -> "send a request for each user and crash the single user process."; register_request_and_crash2(Conf) when is_list(Conf) -> %% put(tname,rrac2), - %% p("start"), + %% ?IPRINT("start"), %% process_flag(trap_exit, true), ?SKIP(not_yet_implemented). @@ -562,7 +576,7 @@ register_request_and_crash3(doc) -> "send a request for each user and crash the first user process."; register_request_and_crash3(Conf) when is_list(Conf) -> %% put(tname,rrac3), - %% p("start"), + %% ?IPRINT("start"), %% process_flag(trap_exit, true), ?SKIP(not_yet_implemented). @@ -574,7 +588,7 @@ simple_register_monitor_and_unregister1(doc) -> "Start a user, register-link and unregister the user."; simple_register_monitor_and_unregister1(Conf) when is_list(Conf) -> put(tname,srlau1), - p("start"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), @@ -587,41 +601,41 @@ simple_register_monitor_and_unregister1(Conf) when is_list(Conf) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try starting manager"), + ?IPRINT("try starting manager"), ok = snmpm:start_link(Opts), ?SLEEP(1000), Id = make_ref(), - p("start user"), + ?IPRINT("start user"), ?line Pid = start_user(), - p("get users (=0)"), + ?IPRINT("get users (=0)"), ?line [] = Users1 = which_users(), - p("Users1: ~p", [Users1]), + ?IPRINT("Users1: ~p", [Users1]), - p("register monitored user"), + ?IPRINT("register monitored user"), ?line ok = register_user_monitor(Pid, Id), - p("get users (=1)"), + ?IPRINT("get users (=1)"), ?line [Id] = Users2 = which_users(), - p("Users2: ~p", [Users2]), + ?IPRINT("Users2: ~p", [Users2]), - p("unregister monitored user"), + ?IPRINT("unregister monitored user"), ?line unregister_user(Pid), - p("get users (=0)"), + ?IPRINT("get users (=0)"), ?line [] = Users3 = which_users(), - p("Users3: ~p", [Users3]), + ?IPRINT("Users3: ~p", [Users3]), - p("start user"), + ?IPRINT("start user"), ?line stop_user(Pid), - p("stop manager"), + ?IPRINT("stop manager"), ?line ok = snmpm:stop(), - p("end"), + ?IPRINT("end"), ok. @@ -634,7 +648,7 @@ simple_register_monitor_and_unregister2(doc) -> "unregister the 2 users."; simple_register_monitor_and_unregister2(Conf) when is_list(Conf) -> put(tname,srlau2), - p("start"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), @@ -647,7 +661,7 @@ simple_register_monitor_and_unregister2(Conf) when is_list(Conf) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try starting manager"), + ?IPRINT("try starting manager"), ok = snmpm:start_link(Opts), ?SLEEP(1000), @@ -658,7 +672,7 @@ simple_register_monitor_and_unregister2(Conf) when is_list(Conf) -> ?line Pid = start_user(), ?line [] = Users1 = which_users(), - p("Users1: ~p", [Users1]), + ?IPRINT("Users1: ~p", [Users1]), ?line ok = register_user_monitor(Pid, Id1), ?line ok = register_user_monitor(Pid, Id2), @@ -671,19 +685,19 @@ simple_register_monitor_and_unregister2(Conf) when is_list(Conf) -> Else -> ?FAIL({invalid_users, Else}) end, - p("Users2: ~p", [Users2]), + ?IPRINT("Users2: ~p", [Users2]), ?line ok = unregister_user(Pid, Id1), ?line ok = unregister_user(Pid, Id2), ?line [] = Users3 = which_users(), - p("Users3: ~p", [Users3]), + ?IPRINT("Users3: ~p", [Users3]), ?line stop_user(Pid), ?line ok = snmpm:stop(), - p("end"), + ?IPRINT("end"), ok. @@ -697,7 +711,7 @@ simple_register_monitor_and_unregister3(doc) -> "unregister the 2 users."; simple_register_monitor_and_unregister3(Conf) when is_list(Conf) -> put(tname,srlau3), - p("start"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), @@ -710,7 +724,7 @@ simple_register_monitor_and_unregister3(Conf) when is_list(Conf) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try starting manager"), + ?IPRINT("try starting manager"), ok = snmpm:start_link(Opts), ?SLEEP(1000), @@ -721,7 +735,7 @@ simple_register_monitor_and_unregister3(Conf) when is_list(Conf) -> ?line Pid = start_user(), ?line [] = Users1 = which_users(), - p("Users1: ~p", [Users1]), + ?IPRINT("Users1: ~p", [Users1]), ?line ok = register_user(Pid, Id1), ?line ok = register_user_monitor(Pid, Id2), @@ -734,18 +748,18 @@ simple_register_monitor_and_unregister3(Conf) when is_list(Conf) -> Else -> ?FAIL({invalid_users, Else}) end, - p("Users2: ~p", [Users2]), + ?IPRINT("Users2: ~p", [Users2]), ?line unregister_user(Pid), ?line [] = Users3 = which_users(), - p("Users3: ~p", [Users3]), + ?IPRINT("Users3: ~p", [Users3]), ?line stop_user(Pid), ?line ok = snmpm:stop(), - p("end"), + ?IPRINT("end"), ok. @@ -756,7 +770,7 @@ register_monitor_and_crash1(doc) -> "Start a user, register-monitor and crash the user."; register_monitor_and_crash1(Conf) when is_list(Conf) -> put(tname,rlac1), - p("start"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), @@ -769,7 +783,7 @@ register_monitor_and_crash1(Conf) when is_list(Conf) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try starting manager"), + ?IPRINT("try starting manager"), ?line ok = snmpm:start_link(Opts), ?SLEEP(1000), @@ -779,24 +793,24 @@ register_monitor_and_crash1(Conf) when is_list(Conf) -> ?line Pid = start_user(), ?line [] = Users1 = which_users(), - p("Users1: ~p", [Users1]), + ?IPRINT("Users1: ~p", [Users1]), ?line ok = register_user_monitor(Pid, Id), ?line [Id] = Users2 = which_users(), - p("Users2: ~p", [Users2]), + ?IPRINT("Users2: ~p", [Users2]), ?line ok = simulate_crash(Pid), ?SLEEP(1000), ?line [] = Users3 = which_users(), - p("Users3: ~p", [Users3]), + ?IPRINT("Users3: ~p", [Users3]), - p("stop manager"), + ?IPRINT("stop manager"), ?line ok = snmpm:stop(), - p("end"), + ?IPRINT("end"), ok. @@ -809,7 +823,7 @@ register_monitor_and_crash2(doc) -> "and crash the single user process."; register_monitor_and_crash2(Conf) when is_list(Conf) -> put(tname,rlac2), - p("start"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), @@ -822,7 +836,7 @@ register_monitor_and_crash2(Conf) when is_list(Conf) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try starting manager"), + ?IPRINT("try starting manager"), ?line ok = snmpm:start_link(Opts), ?SLEEP(1000), @@ -833,7 +847,7 @@ register_monitor_and_crash2(Conf) when is_list(Conf) -> ?line Pid = start_user(), ?line [] = Users1 = which_users(), - p("Users1: ~p", [Users1]), + ?IPRINT("Users1: ~p", [Users1]), ?line ok = register_user_monitor(Pid, Id1), ?line ok = register_user_monitor(Pid, Id2), @@ -846,19 +860,19 @@ register_monitor_and_crash2(Conf) when is_list(Conf) -> Else -> ?FAIL({invalid_users, Else}) end, - p("Users2: ~p", [Users2]), + ?IPRINT("Users2: ~p", [Users2]), ?line ok = simulate_crash(Pid), ?SLEEP(1000), ?line [] = Users3 = which_users(), - p("Users3: ~p", [Users3]), + ?IPRINT("Users3: ~p", [Users3]), - p("stop manager"), + ?IPRINT("stop manager"), ?line ok = snmpm:stop(), - p("end"), + ?IPRINT("end"), ok. @@ -901,7 +915,7 @@ register_monitor_and_crash3(Conf) when is_list(Conf) -> ?NON_PC_TC_MAYBE_SKIP(Conf, Condition), %% </CONDITIONAL-SKIP> - p("start"), + ?IPRINT("start"), ConfDir = ?config(manager_conf_dir, Conf), DbDir = ?config(manager_db_dir, Conf), @@ -913,7 +927,7 @@ register_monitor_and_crash3(Conf) when is_list(Conf) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try starting manager"), + ?IPRINT("try starting manager"), ?line ok = snmpm:start_link(Opts), ?SLEEP(1000), @@ -924,7 +938,7 @@ register_monitor_and_crash3(Conf) when is_list(Conf) -> ?line Pid = start_user(), ?line [] = Users1 = which_users(), - p("Users1: ~p", [Users1]), + ?IPRINT("Users1: ~p", [Users1]), ?line ok = register_user(Pid, Id1), ?line ok = register_user_monitor(Pid, Id2), @@ -937,19 +951,19 @@ register_monitor_and_crash3(Conf) when is_list(Conf) -> Else -> ?FAIL({invalid_users, Else}) end, - p("Users2: ~p", [Users2]), + ?IPRINT("Users2: ~p", [Users2]), ?line ok = simulate_crash(Pid), ?SLEEP(1000), ?line [Id1] = Users3 = which_users(), - p("Users3: ~p", [Users3]), + ?IPRINT("Users3: ~p", [Users3]), - p("stop manager"), + ?IPRINT("stop manager"), ?line ok = snmpm:stop(), - p("end"), + ?IPRINT("end"), ok. @@ -962,7 +976,7 @@ register_monitor_and_crash4(doc) -> "and crash the first user process."; register_monitor_and_crash4(Conf) when is_list(Conf) -> put(tname,rlac4), - p("start"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), @@ -975,7 +989,7 @@ register_monitor_and_crash4(Conf) when is_list(Conf) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("start manager"), + ?IPRINT("start manager"), ?line ok = snmpm:start_link(Opts), ?SLEEP(1000), @@ -983,12 +997,12 @@ register_monitor_and_crash4(Conf) when is_list(Conf) -> Id1 = make_ref(), Id2 = make_ref(), - p("start user processes"), + ?IPRINT("start user processes"), ?line Pid1 = start_user(), ?line Pid2 = start_user(), ?line [] = Users1 = which_users(), - p("Users1: ~p", [Users1]), + ?IPRINT("Users1: ~p", [Users1]), ?line ok = register_user_monitor(Pid1, Id1), ?line ok = register_user_monitor(Pid2, Id2), @@ -1001,21 +1015,21 @@ register_monitor_and_crash4(Conf) when is_list(Conf) -> Else -> ?FAIL({invalid_users, Else}) end, - p("Users2: ~p", [Users2]), + ?IPRINT("Users2: ~p", [Users2]), ?line ok = simulate_crash(Pid1), ?SLEEP(1000), ?line [Id2] = Users3 = which_users(), - p("Users3: ~p", [Users3]), + ?IPRINT("Users3: ~p", [Users3]), ?line stop_user(Pid2), - p("stop manager"), + ?IPRINT("stop manager"), ?line ok = snmpm:stop(), - p("end"), + ?IPRINT("end"), ok. @@ -1030,7 +1044,7 @@ register_monitor_and_crash5(doc) -> "and crash the first user process."; register_monitor_and_crash5(Conf) when is_list(Conf) -> put(tname,rlac4), - p("start"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), @@ -1043,7 +1057,7 @@ register_monitor_and_crash5(Conf) when is_list(Conf) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("start manager"), + ?IPRINT("start manager"), ?line ok = snmpm:start_link(Opts), ?SLEEP(1000), @@ -1051,12 +1065,12 @@ register_monitor_and_crash5(Conf) when is_list(Conf) -> Id1 = gurka, %% make_ref(), Id2 = tomat, %% make_ref(), - p("start user processes"), + ?IPRINT("start user processes"), ?line Pid1 = start_user(), ?line Pid2 = start_user(), ?line [] = Users1 = which_users(), - p("Users1: ~p", [Users1]), + ?IPRINT("Users1: ~p", [Users1]), ?line ok = register_user_monitor(Pid1, Id1), ?line ok = register_user_monitor(Pid2, Id2), @@ -1090,9 +1104,9 @@ register_monitor_and_crash5(Conf) when is_list(Conf) -> U3 -> ?FAIL({invalid_users, U3}) end, - p("Users2: ~p", [Users2]), + ?IPRINT("Users2: ~p", [Users2]), - p("verify all agent(s): expect 2"), + ?IPRINT("verify all agent(s): expect 2"), ?line Agents1 = case which_agents() of [TargetName1, TargetName2] = A1 -> A1; @@ -1101,25 +1115,25 @@ register_monitor_and_crash5(Conf) when is_list(Conf) -> A3 -> ?FAIL({invalid_agents, A3}) end, - p("Agents1: ~p", [Agents1]), + ?IPRINT("Agents1: ~p", [Agents1]), ?line ok = simulate_crash(Pid1), - p("wait some time"), + ?IPRINT("wait some time"), ?SLEEP(1000), ?line [Id2] = Users3 = which_users(), - p("Users3: ~p", [Users3]), + ?IPRINT("Users3: ~p", [Users3]), ?line [TargetName2] = Agents2 = which_agents(), - p("Agents2: ~p", [Agents2]), + ?IPRINT("Agents2: ~p", [Agents2]), ?line stop_user(Pid2), - p("stop manager"), + ?IPRINT("stop manager"), ?line ok = snmpm:stop(), - p("end"), + ?IPRINT("end"), ok. @@ -1132,7 +1146,7 @@ register_monitor_request_and_crash1(doc) -> "send request and crash the user."; register_monitor_request_and_crash1(Conf) when is_list(Conf) -> %% put(tname,rlrac1), - %% p("start"), + %% ?IPRINT("start"), %% process_flag(trap_exit, true), ?SKIP(not_yet_implemented). @@ -1146,7 +1160,7 @@ register_monitor_request_and_crash2(doc) -> "send a request for each user and crash the single user process."; register_monitor_request_and_crash2(Conf) when is_list(Conf) -> %% put(tname,rlrac2), - %% p("start"), + %% ?IPRINT("start"), %% process_flag(trap_exit, true), ?SKIP(not_yet_implemented). @@ -1160,7 +1174,7 @@ register_monitor_request_and_crash3(doc) -> "send a request for each user and crash the single user process."; register_monitor_request_and_crash3(Conf) when is_list(Conf) -> %% put(tname,rlrac3), - %% p("start"), + %% ?IPRINT("start"), %% process_flag(trap_exit, true), ?SKIP(not_yet_implemented). @@ -1176,7 +1190,7 @@ register_monitor_request_and_crash4(doc) -> "crash the first user process."; register_monitor_request_and_crash4(Conf) when is_list(Conf) -> %% put(tname,rlrac4), - %% p("start"), + %% ?IPRINT("start"), %% process_flag(trap_exit, true), ?SKIP(not_yet_implemented). @@ -1189,7 +1203,7 @@ otp7902(doc) -> "OTP-7902 - Start old user and make sure it wors."; otp7902(Conf) when is_list(Conf) -> put(tname, otp7902), - p("start"), + ?IPRINT("start"), process_flag(trap_exit, true), ConfDir = ?config(manager_conf_dir, Conf), @@ -1202,27 +1216,27 @@ otp7902(Conf) when is_list(Conf) -> {note_store, [{verbosity, trace}]}, {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], - p("try starting manager"), + ?IPRINT("try starting manager"), ok = snmpm:start_link(Opts), ?SLEEP(1000), ?line [] = Users1 = which_users(), - p("Users1: ~p", [Users1]), + ?IPRINT("Users1: ~p", [Users1]), ?line ok = snmp_manager_user_old:start(), ?line [_] = Users2 = which_users(), - p("Users2: ~p", [Users2]), + ?IPRINT("Users2: ~p", [Users2]), ?line ok = snmp_manager_user_old:stop(), ?line [] = Users3 = which_users(), - p("Users3: ~p", [Users3]), + ?IPRINT("Users3: ~p", [Users3]), ?line ok = snmpm:stop(), - p("end"), + ?IPRINT("end"), ok. @@ -1306,15 +1320,3 @@ write_conf_file(Dir, File, Str) -> ?line ok = io:format(Fd, "~s", [Str]), file:close(Fd). - -%% ------ - -p(F) -> - p(F, []). - -p(F, A) -> - p(get(tname), F, A). - -p(TName, F, A) -> - io:format("~w -> " ++ F ++ "~n", [TName|A]). - diff --git a/lib/snmp/test/snmp_note_store_SUITE.erl b/lib/snmp/test/snmp_note_store_SUITE.erl index 832c03029e..3b6d9f9fcd 100644 --- a/lib/snmp/test/snmp_note_store_SUITE.erl +++ b/lib/snmp/test/snmp_note_store_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2019. All Rights Reserved. +%% Copyright Ericsson AB 2004-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -81,11 +81,34 @@ groups() -> %% ----- %% -init_per_suite(Config) when is_list(Config) -> - Config. +init_per_suite(Config0) when is_list(Config0) -> + ?IPRINT("init_per_suite -> entry with" + "~n Config: ~p", [Config0]), -end_per_suite(Config) when is_list(Config) -> - Config. + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + %% We need a monitor on this node also + snmp_test_sys_monitor:start(), + + ?IPRINT("init_per_suite -> end when" + "~n Config1: ~p", [Config1]), + + Config1 + end. + +end_per_suite(Config0) when is_list(Config0) -> + ?IPRINT("end_per_suite -> entry with" + "~n Config: ~p", [Config0]), + + snmp_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + ?IPRINT("end_per_suite -> end"), + + Config1. @@ -106,9 +129,24 @@ end_per_group(_GroupName, Config) -> %% init_per_testcase(_Case, Config) when is_list(Config) -> + ?IPRINT("init_per_testcase -> entry with" + "~n Config: ~p", [Config]), + + snmp_test_global_sys_monitor:reset_events(), + + ?IPRINT("init_per_testcase -> end"), + Config. end_per_testcase(_Case, Config) when is_list(Config) -> + + ?IPRINT("end_per_testcase -> entry with" + "~n Config: ~p", + [Config]), + + ?IPRINT("system events during test: ~p", + [snmp_test_global_sys_monitor:events()]), + Config. @@ -143,42 +181,55 @@ notes(doc) -> ["Testing that it does what it is actually supposed to do, " "namilly to handle notes. "]; notes(Config) when is_list(Config) -> - - {ok, Handler, Pid} = note_store_handler_start(), - - io:format("sleep some before we begin the tests~n", []), - ?SLEEP(timer:seconds(1)), + Pre = fun() -> + ?IPRINT("try start note-store"), + case note_store_handler_start() of + {ok, Handler, Pid} -> + ?IPRINT("started - sleep some before we begin the tests"), + ?SLEEP(?SECS(1)), + {Handler, Pid}; + {error, Reason} -> + {skip, ?F("Failed starting note-store: ~p", [Reason])} + end + end, + Case = fun(State) -> do_notes(State, Config) end, + Post = fun({Handler, _Pid}) -> + note_store_handler_stop(Handler) + end, + ?TC_TRY(notes, Pre, Case, Post). + +do_notes({_, Pid}, _Config) -> %% Default lifetime is infinity. A note with lifetime %% infinity is permanent - io:format("create permanent note sune~n", []), + ?IPRINT("create permanent note: sune"), true = snmp_note_store:set_note(Pid, sune, 10), 10 = snmp_note_store:get_note(Pid, sune), 10 = snmp_note_store:get_note(Pid, sune), %% Lifetime is in 1/100 sec ticks, so 500 equals 5 seconds - io:format("create 5 sec note kalle~n", []), + ?IPRINT("create 5 sec note kalle"), true = snmp_note_store:set_note(Pid, 500, kalle, hobbe), - io:format("wait 1 sec~n", []), + ?IPRINT("wait 1 sec"), ?SLEEP(timer:seconds(1)), - io:format("get note kalle~n", []), + ?IPRINT("get note kalle"), hobbe = snmp_note_store:get_note(Pid, kalle), - io:format("wait 5 sec~n", []), + ?IPRINT("wait 5 sec"), ?SLEEP(timer:seconds(5)), - io:format("get note kalle again (now it should not exist)~n", []), + ?IPRINT("get note kalle again (now it should not exist)"), undefined = snmp_note_store:get_note(Pid, kalle), - io:format("create 5 sec note kalle~n", []), + ?IPRINT("create 5 sec note kalle"), true = snmp_note_store:set_note(Pid, 500, kalle, hobbe), - io:format("wait 6 sec (to allow timer to clean up note)~n", []), + ?IPRINT("wait 6 sec (to allow timer to clean up note)"), ?SLEEP(timer:seconds(6)), - io:format("get note kalle - should not exist~n", []), + ?IPRINT("get note kalle - should not exist"), undefined = snmp_note_store:get_note(Pid, kalle), - io:format("read the permanent note sune again~n", []), + ?IPRINT("read the permanent note sune again"), 10 = snmp_note_store:get_note(Pid, sune), - note_store_handler_stop(Handler), + ?IPRINT("done"), ok. @@ -190,47 +241,65 @@ info(suite) -> info(doc) -> ["Testing that we can retreive process info."]; info(Config) when is_list(Config) -> - - Prio = normal, - Mod = ?MODULE, - Opts = [{verbosity, trace}], - {ok, Pid} = snmp_note_store:start_link(Prio, Mod, Opts), + Pre = fun() -> + ?IPRINT("try start note-store"), + Prio = normal, + Mod = ?MODULE, + Opts = [{verbosity, trace}], + case snmp_note_store:start_link(Prio, Mod, Opts) of + {ok, Pid} -> + ?IPRINT("note-store started: ~p", [Pid]), + Pid; + {error, Reason} -> + {skip, ?F("Failed starting note-store: ~p", [Reason])} + end + end, + Case = fun(State) -> do_info(State, Config) end, + Post = fun(Pid) -> + ?IPRINT("attempt stop note-store"), + snmp_note_store:stop(Pid) + end, + ?TC_TRY(info, Pre, Case, Post). + +do_info(Pid, _Config) -> %% Get the info: + ?IPRINT("get initial info"), Info = snmp_note_store:info(Pid), - io:format("Info: ~p~n", [Info]), + ?IPRINT("Info: " + "~n ~p", [Info]), %% Verify content - io:format("get process memory~n", []), + ?IPRINT("verify content: get notes process memory"), {value, {process_memory, ProcMem}} = lists:keysearch(process_memory, 1, Info), - io:format("get notes process memory~n", []), + ?IPRINT("get notes process memory"), {value, {notes, NotesProcMem}} = lists:keysearch(notes, 1, ProcMem), - io:format("verify notes process memory~n", []), + ?IPRINT("verify notes process memory"), if is_integer(NotesProcMem) andalso (NotesProcMem > 0) -> ok; true -> throw({error, {bad_notes_proc_memery, NotesProcMem}}) end, - io:format("get timer process memory~n", []), + ?IPRINT("verify content: get timer process memory"), {value, {timer, TmrProcMem}} = lists:keysearch(timer, 1, ProcMem), - io:format("verify timer process memory~n", []), + ?IPRINT("verify content: timer process memory"), if is_integer(TmrProcMem) andalso (TmrProcMem > 0) -> ok; true -> throw({error, {bad_timer_proc_memery, TmrProcMem}}) end, - io:format("get db memory~n", []), + ?IPRINT("verify content: get db memory"), {value, {db_memory, DbMem}} = lists:keysearch(db_memory, 1, Info), - io:format("get notes db memory~n", []), + ?IPRINT("verify content: get notes db memory"), {value, {notes, NotesDbMem}} = lists:keysearch(notes, 1, DbMem), - io:format("verify notes db memory~n", []), + ?IPRINT("verify content: notes db memory"), if is_integer(NotesDbMem) andalso (NotesDbMem > 0) -> ok; @@ -238,9 +307,7 @@ info(Config) when is_list(Config) -> throw({error, {bad_notes_db_memery, NotesDbMem}}) end, - - snmp_note_store:stop(Pid), - + ?IPRINT("done"), ok. @@ -251,23 +318,38 @@ garbage_in(suite) -> garbage_in(doc) -> ["Test that the process handles garbage sent to it."]; garbage_in(Config) when is_list(Config) -> - - io:format("start note_store server~n", []), - Prio = normal, - Mod = ?MODULE, - Opts = [{verbosity, trace}], - {ok, Pid} = snmp_note_store:start_link(Prio, Mod, Opts), - - io:format("issue bad request~n", []), + Pre = fun() -> + ?IPRINT("try start note-store"), + Prio = normal, + Mod = ?MODULE, + Opts = [{verbosity, trace}], + case snmp_note_store:start_link(Prio, Mod, Opts) of + {ok, Pid} -> + ?IPRINT("note-store started: ~p", [Pid]), + Pid; + {error, Reason} -> + {skip, ?F("Failed starting note-store: ~p", [Reason])} + end + end, + Case = fun(State) -> do_garbage_in(State, Config) end, + Post = fun(Pid) -> + ?IPRINT("attempt stop note-store"), + snmp_note_store:stop(Pid) + end, + ?TC_TRY(garbage_in, Pre, Case, Post). + +do_garbage_in(Pid, _Config) -> + + ?IPRINT("issue bad request"), {error, _} = gen_server:call(Pid, bad_request), - io:format("cast bad message~n", []), + ?IPRINT("cast bad message"), gen_server:cast(Pid, bad_message), - io:format("bang bad info~n", []), + ?IPRINT("bang bad info"), Pid ! bad_info, - io:format("verify note_Store server still alive and kicking~n", []), + ?IPRINT("verify note-store server still alive and kicking"), Info = snmp_note_store:info(Pid), if is_list(Info) andalso (length(Info) > 0) -> @@ -276,10 +358,7 @@ garbage_in(Config) when is_list(Config) -> throw({error, {bad_info, Info}}) end, - io:format("stop note_store server~n", []), - snmp_note_store:stop(Pid), - - io:format("done~n", []), + ?IPRINT("done"), ok. diff --git a/lib/snmp/test/snmp_pdus_SUITE.erl b/lib/snmp/test/snmp_pdus_SUITE.erl index f49e2eb602..d70e7aebd0 100644 --- a/lib/snmp/test/snmp_pdus_SUITE.erl +++ b/lib/snmp/test/snmp_pdus_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2019. All Rights Reserved. +%% Copyright Ericsson AB 2003-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -75,11 +75,34 @@ tickets_cases() -> %% ----- %% -init_per_suite(Config) when is_list(Config) -> - Config. +init_per_suite(Config0) when is_list(Config0) -> + ?IPRINT("init_per_suite -> entry with" + "~n Config: ~p", [Config0]), -end_per_suite(Config) when is_list(Config) -> - Config. + case ?LIB:init_per_suite(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 when is_list(Config1) -> + %% We need a monitor on this node also + snmp_test_sys_monitor:start(), + + ?IPRINT("init_per_suite -> end when" + "~n Config1: ~p", [Config1]), + + Config1 + end. + +end_per_suite(Config0) when is_list(Config0) -> + ?IPRINT("end_per_suite -> entry with" + "~n Config: ~p", [Config0]), + + snmp_test_sys_monitor:stop(), + Config1 = ?LIB:end_per_suite(Config0), + + ?IPRINT("end_per_suite -> end"), + + Config1. @@ -100,9 +123,24 @@ end_per_group(_GroupName, Config) -> %% init_per_testcase(_Case, Config) when is_list(Config) -> + ?IPRINT("init_per_testcase -> entry with" + "~n Config: ~p", [Config]), + + snmp_test_global_sys_monitor:reset_events(), + + ?IPRINT("init_per_testcase -> end"), + Config. end_per_testcase(_Case, Config) when is_list(Config) -> + + ?IPRINT("end_per_testcase -> entry with" + "~n Config: ~p", + [Config]), + + ?IPRINT("system events during test: ~p", + [snmp_test_global_sys_monitor:events()]), + Config. @@ -114,7 +152,7 @@ end_per_testcase(_Case, Config) when is_list(Config) -> otp7575(suite) -> []; otp7575(doc) -> ["OTP-7575 - Message version"]; otp7575(Config) when is_list(Config) -> - io:format("attempt to decode message with valid version~n", []), + ?IPRINT("attempt to decode message with valid version"), MsgWithOkVersion = <<48,39,2,1,0,4,6,112,117,98,108,105,99,160,26,2,2,1,49,2,1,0,2,1,0,48,14,48,12,6,8,43,6,1,2,1,1,5,0,5,0>>, case (catch dec_message(MsgWithOkVersion)) of Msg when is_record(Msg, message) -> @@ -123,7 +161,7 @@ otp7575(Config) when is_list(Config) -> exit({unexpected_decode_result, 1, Unexpected1}) end, - io:format("attempt to decode message with bad version~n", []), + ?IPRINT("attempt to decode message with bad version"), MsgWithBadVersion = <<48,48,2,10,1,1,1,1,1,1,1,1,1,1,4,6,112,117,98,108,105,99,160,26,2,2,1,49,2,1,0,2,1,0,48,14,48,12,6,8,43,6,1,2,1,1,5,0,5,0>>, case (catch dec_message(MsgWithBadVersion)) of {'EXIT', {bad_version, BadVersion}} when is_integer(BadVersion) -> @@ -132,7 +170,7 @@ otp7575(Config) when is_list(Config) -> exit({unexpected_decode_result, 2, Unexpected2}) end, - io:format("attempt to decode message with very bad version~n", []), + ?IPRINT("attempt to decode message with very bad version"), MsgWithVeryBadVersion = <<48,49,2,11,1,1,1,1,1,1,1,1,1,1,1,4,6,112,117,98,108,105,99,160,26,2,2,1,49,2,1,0,2,1,0,48,14,48,12,6,8,43,6,1,2,1,1,5,0,5,0>>, case (catch dec_message(MsgWithVeryBadVersion)) of {'EXIT', {bad_version, {VersionSize, MaxVersionSize}}} when (VersionSize > MaxVersionSize) -> @@ -140,7 +178,7 @@ otp7575(Config) when is_list(Config) -> Unexpected3 -> exit({unexpected_decode_result, 3, Unexpected3}) end, - io:format("done~n", []), + ?IPRINT("done"), ok. @@ -148,52 +186,52 @@ otp8563(suite) -> []; otp8563(doc) -> ["OTP-8563 - Counter64"]; otp8563(Config) when is_list(Config) -> Val1 = 16#7fffffffffffffff, - io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]), + ?IPRINT("try encode and decode value 1: ~w (0x~.16b)", [Val1, Val1]), Enc1 = snmp_pdus:enc_value('Counter64', Val1), - io:format(" => ~w~n", [Enc1]), + ?IPRINT(" => ~w", [Enc1]), {{'Counter64', Val1}, []} = snmp_pdus:dec_value(Enc1), Val2 = Val1 + 1, - io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]), + ?IPRINT("try encode and decode value 2: ~w (0x~.16b)", [Val2, Val2]), Enc2 = snmp_pdus:enc_value('Counter64', Val2), - io:format(" => ~w~n", [Enc2]), + ?IPRINT(" => ~w", [Enc2]), {{'Counter64', Val2}, []} = snmp_pdus:dec_value(Enc2), Val3 = Val2 + 1, - io:format("try encode and decode value 3: ~w (0x~.16b)~n", [Val3, Val3]), + ?IPRINT("try encode and decode value 3: ~w (0x~.16b)", [Val3, Val3]), Enc3 = snmp_pdus:enc_value('Counter64', Val3), - io:format(" => ~w~n", [Enc3]), + ?IPRINT(" => ~w", [Enc3]), {{'Counter64', Val3}, []} = snmp_pdus:dec_value(Enc3), Val4 = 16#fffffffffffffffe, - io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]), + ?IPRINT("try encode and decode value 4: ~w (0x~.16b)", [Val4, Val4]), Enc4 = snmp_pdus:enc_value('Counter64', Val4), - io:format(" => ~w~n", [Enc4]), + ?IPRINT(" => ~w", [Enc4]), {{'Counter64', Val4}, []} = snmp_pdus:dec_value(Enc4), Val5 = Val4 + 1, - io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]), + ?IPRINT("try encode and decode value 5: ~w (0x~.16b)", [Val5, Val5]), Enc5 = snmp_pdus:enc_value('Counter64', Val5), - io:format(" => ~w~n", [Enc5]), + ?IPRINT(" => ~w", [Enc5]), {{'Counter64', Val5}, []} = snmp_pdus:dec_value(Enc5), Val6 = 16#ffffffffffffffff + 1, - io:format("try and fail to encode value 6: ~w (0x~.16b)~n", [Val6, Val6]), + ?IPRINT("try and fail to encode value 6: ~w (0x~.16b)", [Val6, Val6]), case (catch snmp_pdus:enc_value('Counter64', Val6)) of {'EXIT', {error, {bad_counter64, Val6}}} -> ok; Unexpected6 -> - io:format(" => ~w~n", [Unexpected6]), + ?IPRINT(" => ~w", [Unexpected6]), exit({unexpected_encode_result, Unexpected6, Val6}) end, Val7 = -1, - io:format("try and fail to encode value 7: ~w~n", [Val7]), + ?IPRINT("try and fail to encode value 7: ~w", [Val7]), case (catch snmp_pdus:enc_value('Counter64', Val7)) of {'EXIT', {error, {bad_counter64, Val7}}} -> ok; Unexpected7 -> - io:format(" => ~w~n", [Unexpected7]), + ?IPRINT(" => ~w", [Unexpected7]), exit({unexpected_encode_result, Unexpected7, Val7}) end, @@ -204,70 +242,70 @@ otp9022(suite) -> []; otp9022(doc) -> ["OTP-9022 - Counter32"]; otp9022(Config) when is_list(Config) -> Val0 = 2908389204, - io:format("try encode and decode value 0: ~w (0x~.16b)~n", [Val0, Val0]), + ?IPRINT("try encode and decode value 0: ~w (0x~.16b)", [Val0, Val0]), Enc0 = snmp_pdus:enc_value('Counter32', Val0), - io:format(" => ~w~n", [Enc0]), + ?IPRINT(" => ~w", [Enc0]), {{'Counter32', Val0}, []} = snmp_pdus:dec_value(Enc0), Val1 = 0, - io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]), + ?IPRINT("try encode and decode value 1: ~w (0x~.16b)", [Val1, Val1]), Enc1 = snmp_pdus:enc_value('Counter32', Val1), - io:format(" => ~w~n", [Enc1]), + ?IPRINT(" => ~w", [Enc1]), {{'Counter32', Val1}, []} = snmp_pdus:dec_value(Enc1), Val2 = Val1 + 1, - io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]), + ?IPRINT("try encode and decode value 2: ~w (0x~.16b)", [Val2, Val2]), Enc2 = snmp_pdus:enc_value('Counter32', Val2), - io:format(" => ~w~n", [Enc2]), + ?IPRINT(" => ~w", [Enc2]), {{'Counter32', Val2}, []} = snmp_pdus:dec_value(Enc2), Val3 = 16#7ffffffe, - io:format("try encode and decode value 3: ~w (0x~.16b)~n", [Val3, Val3]), + ?IPRINT("try encode and decode value 3: ~w (0x~.16b)", [Val3, Val3]), Enc3 = snmp_pdus:enc_value('Counter32', Val3), - io:format(" => ~w~n", [Enc3]), + ?IPRINT(" => ~w", [Enc3]), {{'Counter32', Val3}, []} = snmp_pdus:dec_value(Enc3), Val4 = Val3 + 1, - io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]), + ?IPRINT("try encode and decode value 4: ~w (0x~.16b)", [Val4, Val4]), Enc4 = snmp_pdus:enc_value('Counter32', Val4), - io:format(" => ~w~n", [Enc4]), + ?IPRINT(" => ~w", [Enc4]), {{'Counter32', Val4}, []} = snmp_pdus:dec_value(Enc4), Val5 = Val4 + 1, - io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]), + ?IPRINT("try encode and decode value 5: ~w (0x~.16b)", [Val5, Val5]), Enc5 = snmp_pdus:enc_value('Counter32', Val5), - io:format(" => ~w~n", [Enc5]), + ?IPRINT(" => ~w", [Enc5]), {{'Counter32', Val5}, []} = snmp_pdus:dec_value(Enc5), Val6 = 16#fffffffe, - io:format("try encode and decode value 6: ~w (0x~.16b)~n", [Val6, Val6]), + ?IPRINT("try encode and decode value 6: ~w (0x~.16b)", [Val6, Val6]), Enc6 = snmp_pdus:enc_value('Counter32', Val6), - io:format(" => ~w~n", [Enc6]), + ?IPRINT(" => ~w", [Enc6]), {{'Counter32', Val6}, []} = snmp_pdus:dec_value(Enc6), Val7 = Val6 + 1, - io:format("try encode and decode value 7: ~w (0x~.16b)~n", [Val7, Val7]), + ?IPRINT("try encode and decode value 7: ~w (0x~.16b)", [Val7, Val7]), Enc7 = snmp_pdus:enc_value('Counter32', Val7), - io:format(" => ~w~n", [Enc7]), + ?IPRINT(" => ~w", [Enc7]), {{'Counter32', Val7}, []} = snmp_pdus:dec_value(Enc7), Val8 = 16#ffffffff + 1, - io:format("try and fail to encode value 8: ~w (0x~.16b)~n", [Val8, Val8]), + ?IPRINT("try and fail to encode value 8: ~w (0x~.16b)", [Val8, Val8]), case (catch snmp_pdus:enc_value('Counter32', Val8)) of {'EXIT', {error, {bad_counter32, Val8}}} -> ok; Unexpected8 -> - io:format(" => ~w~n", [Unexpected8]), + ?IPRINT(" => ~w", [Unexpected8]), exit({unexpected_encode_result, Unexpected8, Val8}) end, Val9 = -1, - io:format("try and fail to encode value 9: ~w~n", [Val9]), + ?IPRINT("try and fail to encode value 9: ~w", [Val9]), case (catch snmp_pdus:enc_value('Counter32', Val9)) of {'EXIT', {error, {bad_counter32, Val9}}} -> ok; Unexpected9 -> - io:format(" => ~w~n", [Unexpected9]), + ?IPRINT(" => ~w", [Unexpected9]), exit({unexpected_encode_result, Unexpected9, Val9}) end, @@ -278,74 +316,74 @@ otp10132(suite) -> []; otp10132(doc) -> ["OTP-10132 - TimeTicks"]; otp10132(Config) when is_list(Config) -> Val0 = 2159001034, - io:format("try encode and decode value 0: ~w (0x~.16b)~n", [Val0, Val0]), + ?IPRINT("try encode and decode value 0: ~w (0x~.16b)", [Val0, Val0]), Enc0 = snmp_pdus:enc_value('TimeTicks', Val0), - io:format(" => ~w~n", [Enc0]), + ?IPRINT(" => ~w", [Enc0]), {{'TimeTicks', Val0}, []} = snmp_pdus:dec_value(Enc0), Val1 = 0, - io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]), + ?IPRINT("try encode and decode value 1: ~w (0x~.16b)", [Val1, Val1]), Enc1 = snmp_pdus:enc_value('TimeTicks', Val1), - io:format(" => ~w~n", [Enc1]), + ?IPRINT(" => ~w", [Enc1]), {{'TimeTicks', Val1}, []} = snmp_pdus:dec_value(Enc1), Val2 = Val1 + 1, - io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]), + ?IPRINT("try encode and decode value 2: ~w (0x~.16b)", [Val2, Val2]), Enc2 = snmp_pdus:enc_value('TimeTicks', Val2), - io:format(" => ~w~n", [Enc2]), + ?IPRINT(" => ~w", [Enc2]), {{'TimeTicks', Val2}, []} = snmp_pdus:dec_value(Enc2), Val3 = 16#7ffffffe, - io:format("try encode and decode value 3: ~w (0x~.16b)~n", [Val3, Val3]), + ?IPRINT("try encode and decode value 3: ~w (0x~.16b)", [Val3, Val3]), Enc3 = snmp_pdus:enc_value('TimeTicks', Val3), - io:format(" => ~w~n", [Enc3]), + ?IPRINT(" => ~w", [Enc3]), {{'TimeTicks', Val3}, []} = snmp_pdus:dec_value(Enc3), Val4 = Val3 + 1, - io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]), + ?IPRINT("try encode and decode value 4: ~w (0x~.16b)", [Val4, Val4]), Enc4 = snmp_pdus:enc_value('TimeTicks', Val4), - io:format(" => ~w~n", [Enc4]), + ?IPRINT(" => ~w", [Enc4]), {{'TimeTicks', Val4}, []} = snmp_pdus:dec_value(Enc4), Val5 = Val4 + 1, - io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]), + ?IPRINT("try encode and decode value 5: ~w (0x~.16b)", [Val5, Val5]), Enc5 = snmp_pdus:enc_value('TimeTicks', Val5), - io:format(" => ~w~n", [Enc5]), + ?IPRINT(" => ~w", [Enc5]), {{'TimeTicks', Val5}, []} = snmp_pdus:dec_value(Enc5), Val6 = 16#fffffffe, - io:format("try encode and decode value 6: ~w (0x~.16b)~n", [Val6, Val6]), + ?IPRINT("try encode and decode value 6: ~w (0x~.16b)", [Val6, Val6]), Enc6 = snmp_pdus:enc_value('TimeTicks', Val6), - io:format(" => ~w~n", [Enc6]), + ?IPRINT(" => ~w", [Enc6]), {{'TimeTicks', Val6}, []} = snmp_pdus:dec_value(Enc6), Val7 = Val6 + 1, - io:format("try encode and decode value 7: ~w (0x~.16b)~n", [Val7, Val7]), + ?IPRINT("try encode and decode value 7: ~w (0x~.16b)", [Val7, Val7]), Enc7 = snmp_pdus:enc_value('TimeTicks', Val7), - io:format(" => ~w~n", [Enc7]), + ?IPRINT(" => ~w", [Enc7]), {{'TimeTicks', Val7}, []} = snmp_pdus:dec_value(Enc7), Val8 = Val7 + 1, - io:format("try and fail to encode value 8: ~w (0x~.16b)~n", [Val8, Val8]), + ?IPRINT("try and fail to encode value 8: ~w (0x~.16b)", [Val8, Val8]), case (catch snmp_pdus:enc_value('TimeTicks', Val8)) of {'EXIT', {error, {bad_timeticks, Val8}}} -> ok; Unexpected8 -> - io:format(" => ~w~n", [Unexpected8]), + ?IPRINT(" => ~w", [Unexpected8]), exit({unexpected_encode_result, Unexpected8, Val8}) end, Val9 = -1, - io:format("try and fail to encode value 9: ~w~n", [Val9]), + ?IPRINT("try and fail to encode value 9: ~w", [Val9]), case (catch snmp_pdus:enc_value('TimeTicks', Val9)) of {'EXIT', {error, {bad_timeticks, Val9}}} -> ok; Unexpected9 -> - io:format(" => ~w~n", [Unexpected9]), + ?IPRINT(" => ~w", [Unexpected9]), exit({unexpected_encode_result, Unexpected9, Val9}) end, - io:format("done~n", []), + ?IPRINT("done"), ok. diff --git a/lib/snmp/test/snmp_test_lib.erl b/lib/snmp/test/snmp_test_lib.erl index d2edd636cc..3e445174ba 100644 --- a/lib/snmp/test/snmp_test_lib.erl +++ b/lib/snmp/test/snmp_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2019. All Rights Reserved. +%% Copyright Ericsson AB 2002-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -23,12 +23,14 @@ -include_lib("kernel/include/file.hrl"). --export([tc_try/2, tc_try/3]). +-export([tc_try/2, tc_try/3, + tc_try/4, tc_try/5]). -export([hostname/0, hostname/1, localhost/0, localhost/1, os_type/0, sz/1, display_suite_info/1]). --export([non_pc_tc_maybe_skip/4, os_based_skip/1, - has_support_ipv6/0, has_support_ipv6/1, - is_ipv6_host/0, is_ipv6_host/1]). +-export([non_pc_tc_maybe_skip/4, + os_based_skip/1, + has_support_ipv6/0 + ]). -export([init_per_suite/1, end_per_suite/1, init_suite_top_dir/2, init_group_top_dir/2, init_testcase_top_dir/2, fix_data_dir/1, @@ -40,57 +42,136 @@ -export([ping/1, local_nodes/0, nodes_on/1]). -export([start_node/2, stop_node/1]). -export([is_app_running/1, - is_crypto_running/0, is_mnesia_running/0, is_snmp_running/0]). + is_crypto_running/0, is_mnesia_running/0, is_snmp_running/0, + ensure_not_running/3]). -export([crypto_start/0, crypto_support/0]). -export([watchdog/3, watchdog_start/1, watchdog_start/2, watchdog_stop/1]). -export([del_dir/1]). --export([cover/1]). --export([f/2, p/2, print1/2, print2/2, print/5, formated_timestamp/0]). +-export([f/2, formated_timestamp/0]). +-export([p/2, print1/2, print2/2, print/5]). +-export([eprint/2, wprint/2, nprint/2, iprint/2]). + + +-define(SKIP(R), skip(R, ?MODULE, ?LINE)). %% ---------------------------------------------------------------------- %% Run test-case %% -%% *** tc_try/2,3 *** -%% Case: Basically the test case name -%% TCCondFun: A fun that is evaluated before the actual test case -%% The point of this is that it can performs checks to -%% see if we shall run the test case at all. -%% For instance, the test case may only work in specific -%% conditions. -%% FCFun: The test case fun -tc_try(Case, TCFun) -> - tc_try(Case, fun() -> ok end, TCFun). - -tc_try(Case, TCCondFun, TCFun) - when is_atom(Case) andalso - is_function(TCCondFun, 0) andalso - is_function(TCFun, 0) -> +%% *** tc_try/2,3,4,5 *** +%% Case: Basically the test case name +%% TCCond: A fun that is evaluated before the actual test case +%% The point of this is that it can performs checks to +%% see if we shall run the test case at all. +%% For instance, the test case may only work in specific +%% conditions. +%% Pre: A fun that is nominally part of the test case +%% but is an initiation that must be "undone". This is +%% done by the Post fun (regardless if the TC is successfull +%% or not). Example: Starts a couple of nodes, +%% TC: The test case fun +%% Post: A fun that undo what was done by the Pre fun. +%% Example: Stops the nodes created by the Pre function. +tc_try(Case, TC) -> + tc_try(Case, fun() -> ok end, TC). + +tc_try(Case, TCCond, TC0) when is_function(TC0, 0) -> + Pre = fun() -> undefined end, + TC = fun(_) -> TC0() end, + Post = fun(_) -> ok end, + tc_try(Case, TCCond, Pre, TC, Post). + +tc_try(Case, Pre, TC, Post) + when is_atom(Case) andalso + is_function(Pre, 0) andalso + is_function(TC, 1) andalso + is_function(Post, 1) -> + TCCond = fun() -> ok end, + tc_try(Case, TCCond, Pre, TC, Post). + +tc_try(Case, TCCond, Pre, TC, Post) + when is_atom(Case) andalso + is_function(TCCond, 0) andalso + is_function(Pre, 0) andalso + is_function(TC, 1) andalso + is_function(Post, 1) -> tc_begin(Case), - try TCCondFun() of + try TCCond() of ok -> - try - begin - TCFun(), - sleep(seconds(1)), - tc_end("ok") - end + tc_print("starting: try pre"), + try Pre() of + State -> + tc_print("pre done: try test case"), + try + begin + TC(State), + sleep(seconds(1)), + tc_print("test case done: try post"), + (catch Post(State)), + tc_end("ok") + end + catch + C:{skip, _} = SKIP when (C =:= throw) orelse + (C =:= exit) -> + tc_print("test case (~w) skip: try post", [C]), + (catch Post(State)), + tc_end( f("skipping(catched,~w,tc)", [C]) ), + SKIP; + C:E:S -> + %% We always check the system events + %% before we accept a failure. + %% We do *not* run the Post here because it might + %% generate sys events itself... + case snmp_test_global_sys_monitor:events() of + [] -> + tc_print("test case failed: try post"), + (catch Post(State)), + tc_end( f("failed(catched,~w,tc)", [C]) ), + erlang:raise(C, E, S); + SysEvs -> + tc_print("System Events received during tc: " + "~n ~p" + "~nwhen tc failed:" + "~n C: ~p" + "~n E: ~p" + "~n S: ~p", + [SysEvs, C, E, S]), + (catch Post(State)), + tc_end( f("skipping(catched-sysevs,~w,tc)", + [C]) ), + SKIP = {skip, "TC failure with system events"}, + SKIP + end + end catch - C:{skip, _} = SKIP when ((C =:= throw) orelse (C =:= exit)) -> - tc_end( f("skipping(catched,~w,tc)", [C]) ), + C:{skip, _} = SKIP when (C =:= throw) orelse + (C =:= exit) -> + tc_end( f("skipping(catched,~w,tc-pre)", [C]) ), SKIP; C:E:S -> - %% We always check the system events before we accept a failure + %% We always check the system events + %% before we accept a failure case snmp_test_global_sys_monitor:events() of [] -> - tc_end( f("failed(catched,~w,tc)", [C]) ), - erlang:raise(C, E, S); + tc_print("tc-pre failed: auto-skip" + "~n C: ~p" + "~n E: ~p" + "~n S: ~p", + [C, E, S]), + tc_end( f("auto-skip(catched,~w,tc-pre)", [C]) ), + SKIP = {skip, f("TC-Pre failure (~w)", [C])}, + SKIP; SysEvs -> - tc_print("System Events received: " - "~n ~p", [SysEvs], "", ""), - tc_end( f("skipping(catched-sysevs,~w,tc)", [C]) ), - SKIP = {skip, "TC failure with system events"}, + tc_print("System Events received: " + "~n ~p" + "~nwhen tc-pre failed:" + "~n C: ~p" + "~n E: ~p" + "~n S: ~p", + [SysEvs, C, E, S], "", ""), + tc_end( f("skipping(catched-sysevs,~w,tc-pre)", [C]) ), + SKIP = {skip, "TC-Pre failure with system events"}, SKIP end end; @@ -142,14 +223,21 @@ tc_end(Result) when is_list(Result) -> "", "----------------------------------------------------~n~n"), ok. +tc_print(F) -> + tc_print(F, [], "", ""). + +tc_print(F, A) -> + tc_print(F, A, "", ""). + tc_print(F, Before, After) -> tc_print(F, [], Before, After). tc_print(F, A, Before, After) -> Name = tc_which_name(), FStr = f("*** [~s][~s][~p] " ++ F ++ "~n", - [formated_timestamp(),Name,self()|A]), - io:format(user, Before ++ FStr ++ After, []). + [formated_timestamp(), Name, self() | A]), + io:format(user, Before ++ FStr ++ After, []), + io:format(standard_io, Before ++ FStr ++ After, []). tc_which_name() -> case tc_get_name() of @@ -406,37 +494,113 @@ os_based_skip_check(OsName, OsNames) -> end. -%% A basic test to check if current host supports IPv6 +%% A modern take on the "Check if our host handle IPv6" question. +%% has_support_ipv6() -> - case inet:gethostname() of - {ok, Hostname} -> - has_support_ipv6(Hostname); + case os:type() of + {win32, _} -> + %% We do not yet have support for windows in the socket nif, + %% so for windows we need to use the old style... + old_has_support_ipv6(); _ -> - false + socket:supports(ipv6) andalso has_valid_ipv6_address() end. -has_support_ipv6(Hostname) -> - case inet:getaddr(Hostname, inet6) of - {ok, Addr} when (size(Addr) =:= 8) andalso - (element(1, Addr) =/= 0) andalso - (element(1, Addr) =/= 16#fe80) -> - true; +has_valid_ipv6_address() -> + case net:getifaddrs(fun(#{addr := #{family := inet6}, + flags := Flags}) -> + not lists:member(loopback, Flags); + (_) -> + false + end) of + {ok, [#{addr := #{addr := LocalAddr}}|_]} -> + %% At least one valid address, we pick the first... + try validate_ipv6_address(LocalAddr) + catch + _:_:_ -> + false + end; {ok, _} -> false; {error, _} -> false end. - -is_ipv6_host() -> +validate_ipv6_address(LocalAddr) -> + Domain = inet6, + ServerSock = + case socket:open(Domain, dgram, udp) of + {ok, SS} -> + SS; + {error, R2} -> + ?SKIP(f("(server) socket open failed: ~p", [R2])) + end, + LocalSA = #{family => Domain, addr => LocalAddr}, + ServerPort = + case socket:bind(ServerSock, LocalSA) of + {ok, P1} -> + P1; + {error, R3} -> + socket:close(ServerSock), + ?SKIP(f("(server) socket bind failed: ~p", [R3])) + end, + ServerSA = LocalSA#{port => ServerPort}, + ClientSock = + case socket:open(Domain, dgram, udp) of + {ok, CS} -> + CS; + {error, R4} -> + ?SKIP(f("(client) socket open failed: ~p", [R4])) + end, + case socket:bind(ClientSock, LocalSA) of + {ok, _} -> + ok; + {error, R5} -> + socket:close(ServerSock), + socket:close(ClientSock), + ?SKIP(f("(client) socket bind failed: ~p", [R5])) + end, + case socket:sendto(ClientSock, <<"hejsan">>, ServerSA) of + ok -> + ok; + {error, R6} -> + socket:close(ServerSock), + socket:close(ClientSock), + ?SKIP(f("failed socket sendto test: ~p", [R6])) + end, + case socket:recvfrom(ServerSock) of + {ok, {_, <<"hejsan">>}} -> + socket:close(ServerSock), + socket:close(ClientSock), + true; + {error, R7} -> + socket:close(ServerSock), + socket:close(ClientSock), + ?SKIP(f("failed socket recvfrom test: ~p", [R7])) + end. + + + + +old_has_support_ipv6() -> case inet:gethostname() of {ok, Hostname} -> - is_ipv6_host(Hostname); - {error, _} -> + old_has_support_ipv6(Hostname) andalso old_is_ipv6_host(Hostname); + _ -> false end. -is_ipv6_host(Hostname) -> +old_has_support_ipv6(Hostname) -> + case inet:getaddr(Hostname, inet6) of + {ok, Addr} when (size(Addr) =:= 8) andalso + (element(1, Addr) =/= 0) andalso + (element(1, Addr) =/= 16#fe80) -> + true; + _ -> + false + end. + +old_is_ipv6_host(Hostname) -> case ct:require(ipv6_hosts) of ok -> lists:member(list_to_atom(Hostname), ct:get_config(ipv6_hosts)); @@ -445,6 +609,7 @@ is_ipv6_host(Hostname) -> end. + %% ---------------------------------------------------------------- %% Test suite utility functions %% @@ -486,8 +651,9 @@ init_per_suite(Config) -> true -> {skip, "Unstable host and/or os (or combo thererof)"}; false -> + Factor = analyze_and_print_host_info(), snmp_test_global_sys_monitor:start(), - Config + [{snmp_factor, Factor} | Config] end. @@ -625,6 +791,692 @@ skip(Reason, Module, Line) -> exit({skip, String}). +%% This function prints various host info, which might be usefull +%% when analyzing the test suite (results). +%% It also returns a "factor" that can be used when deciding +%% the load for some test cases. Such as run time or number of +%% iteraions. This only works for some OSes. +%% +%% We make some calculations on Linux, OpenBSD and FreeBSD. +%% On SunOS we always set the factor to 2 (just to be on the safe side) +%% On all other os:es (mostly windows) we check the number of schedulers, +%% but at least the factor will be 2. +analyze_and_print_host_info() -> + {OsFam, OsName} = os:type(), + Version = + case os:version() of + {Maj, Min, Rel} -> + f("~w.~w.~w", [Maj, Min, Rel]); + VStr -> + VStr + end, + case {OsFam, OsName} of + {unix, linux} -> + analyze_and_print_linux_host_info(Version); + {unix, openbsd} -> + analyze_and_print_openbsd_host_info(Version); + {unix, freebsd} -> + analyze_and_print_freebsd_host_info(Version); + {unix, sunos} -> + analyze_and_print_solaris_host_info(Version); + {win32, nt} -> + analyze_and_print_win_host_info(Version); + _ -> + io:format("OS Family: ~p" + "~n OS Type: ~p" + "~n Version: ~p" + "~n Num Schedulers: ~s" + "~n", [OsFam, OsName, Version, str_num_schedulers()]), + try erlang:system_info(schedulers) of + 1 -> + 10; + 2 -> + 5; + N when (N =< 6) -> + 2; + _ -> + 1 + catch + _:_:_ -> + 10 + end + end. + +analyze_and_print_linux_host_info(Version) -> + case file:read_file_info("/etc/issue") of + {ok, _} -> + io:format("Linux: ~s" + "~n ~s" + "~n", + [Version, string:trim(os:cmd("cat /etc/issue"))]); + _ -> + io:format("Linux: ~s" + "~n", [Version]) + end, + Factor = + case (catch linux_which_cpuinfo()) of + {ok, {CPU, BogoMIPS}} -> + io:format("CPU: " + "~n Model: ~s" + "~n BogoMIPS: ~s" + "~n", [CPU, BogoMIPS]), + %% We first assume its a float, and if not try integer + try list_to_float(string:trim(BogoMIPS)) of + F when F > 1000 -> + 1; + F when F > 1000 -> + 2; + _ -> + 3 + catch + _:_:_ -> + %% + try list_to_integer(string:trim(BogoMIPS)) of + I when I > 1000 -> + 1; + I when I > 1000 -> + 2; + _ -> + 3 + catch + _:_:_ -> + 1 + end + end; + _ -> + 1 + end, + %% Check if we need to adjust the factor because of the memory + try linux_which_meminfo() of + AddFactor -> + io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]), + Factor + AddFactor + catch + _:_:_ -> + io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]), + Factor + end. + +linux_which_cpuinfo() -> + %% Check for x86 (Intel or AMD) + CPU = + try [string:trim(S) || S <- string:tokens(os:cmd("grep \"model name\" /proc/cpuinfo"), [$:,$\n])] of + ["model name", ModelName | _] -> + ModelName; + _ -> + %% ARM (at least some distros...) + try [string:trim(S) || S <- string:tokens(os:cmd("grep \"Processor\" /proc/cpuinfo"), [$:,$\n])] of + ["Processor", Proc | _] -> + Proc; + _ -> + %% Ok, we give up + throw(noinfo) + catch + _:_:_ -> + throw(noinfo) + end + catch + _:_:_ -> + throw(noinfo) + end, + try [string:trim(S) || S <- string:tokens(os:cmd("grep -i \"bogomips\" /proc/cpuinfo"), [$:,$\n])] of + [_, BMips | _] -> + {ok, {CPU, BMips}}; + _ -> + {ok, CPU} + catch + _:_:_ -> + {ok, CPU} + end. + +%% We *add* the value this return to the Factor. +linux_which_meminfo() -> + try [string:trim(S) || S <- string:tokens(os:cmd("grep MemTotal /proc/meminfo"), [$:])] of + [_, MemTotal] -> + io:format("Memory:" + "~n ~s" + "~n", [MemTotal]), + case string:tokens(MemTotal, [$ ]) of + [MemSzStr, MemUnit] -> + MemSz2 = list_to_integer(MemSzStr), + MemSz3 = + case string:to_lower(MemUnit) of + "kb" -> + MemSz2; + "mb" -> + MemSz2*1024; + "gb" -> + MemSz2*1024*1024; + _ -> + throw(noinfo) + end, + if + (MemSz3 >= 8388608) -> + 0; + (MemSz3 >= 4194304) -> + 1; + (MemSz3 >= 2097152) -> + 2; + true -> + 3 + end; + _X -> + 0 + end; + _ -> + 0 + catch + _:_:_ -> + 0 + end. + + +%% Just to be clear: This is ***not*** scientific... +analyze_and_print_openbsd_host_info(Version) -> + io:format("OpenBSD:" + "~n Version: ~p" + "~n", [Version]), + Extract = + fun(Key) -> + string:tokens(string:trim(os:cmd("sysctl " ++ Key)), [$=]) + end, + try + begin + CPU = + case Extract("hw.model") of + ["hw.model", Model] -> + string:trim(Model); + _ -> + "-" + end, + CPUSpeed = + case Extract("hw.cpuspeed") of + ["hw.cpuspeed", Speed] -> + list_to_integer(Speed); + _ -> + -1 + end, + NCPU = + case Extract("hw.ncpufound") of + ["hw.ncpufound", N] -> + list_to_integer(N); + _ -> + -1 + end, + Memory = + case Extract("hw.physmem") of + ["hw.physmem", PhysMem] -> + list_to_integer(PhysMem) div 1024; + _ -> + -1 + end, + io:format("CPU:" + "~n Model: ~s" + "~n Speed: ~w" + "~n N: ~w" + "~nMemory:" + "~n ~w KB" + "~n", [CPU, CPUSpeed, NCPU, Memory]), + io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]), + CPUFactor = + if + (CPUSpeed =:= -1) -> + 1; + (CPUSpeed >= 2000) -> + if + (NCPU >= 4) -> + 1; + (NCPU >= 2) -> + 2; + true -> + 3 + end; + true -> + if + (NCPU >= 4) -> + 2; + (NCPU >= 2) -> + 3; + true -> + 4 + end + end, + MemAddFactor = + if + (Memory =:= -1) -> + 0; + (Memory >= 8388608) -> + 0; + (Memory >= 4194304) -> + 1; + (Memory >= 2097152) -> + 2; + true -> + 3 + end, + CPUFactor + MemAddFactor + end + catch + _:_:_ -> + io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]), + 1 + end. + + +analyze_and_print_freebsd_host_info(Version) -> + io:format("FreeBSD:" + "~n Version: ~p" + "~n", [Version]), + %% This test require that the program 'sysctl' is in the path. + %% First test with 'which sysctl', if that does not work + %% try with 'which /sbin/sysctl'. If that does not work either, + %% we skip the test... + try + begin + SysCtl = + case string:trim(os:cmd("which sysctl")) of + [] -> + case string:trim(os:cmd("which /sbin/sysctl")) of + [] -> + throw(sysctl); + SC2 -> + SC2 + end; + SC1 -> + SC1 + end, + Extract = + fun(Key) -> + string:tokens(string:trim(os:cmd(SysCtl ++ " " ++ Key)), + [$:]) + end, + CPU = analyze_freebsd_cpu(Extract), + CPUSpeed = analyze_freebsd_cpu_speed(Extract), + NCPU = analyze_freebsd_ncpu(Extract), + Memory = analyze_freebsd_memory(Extract), + io:format("CPU:" + "~n Model: ~s" + "~n Speed: ~w" + "~n N: ~w" + "~n Num Schedulers: ~s" + "~nMemory:" + "~n ~w KB" + "~n", + [CPU, CPUSpeed, NCPU, str_num_schedulers(), Memory]), + io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]), + CPUFactor = + if + (CPUSpeed =:= -1) -> + 1; + (CPUSpeed >= 2000) -> + if + (NCPU >= 4) -> + 1; + (NCPU >= 2) -> + 2; + true -> + 3 + end; + true -> + if + (NCPU =:= -1) -> + 1; + (NCPU >= 4) -> + 2; + (NCPU >= 2) -> + 3; + true -> + 4 + end + end, + MemAddFactor = + if + (Memory =:= -1) -> + 0; + (Memory >= 8388608) -> + 0; + (Memory >= 4194304) -> + 1; + (Memory >= 2097152) -> + 2; + true -> + 3 + end, + CPUFactor + MemAddFactor + end + catch + _:_:_ -> + io:format("CPU:" + "~n Num Schedulers: ~s" + "~n", [str_num_schedulers()]), + io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]), + case erlang:system_info(schedulers) of + 1 -> + 10; + 2 -> + 5; + _ -> + 2 + end + end. + + +analyze_freebsd_cpu(Extract) -> + analyze_freebsd_item(Extract, "hw.model", fun(X) -> X end, "-"). + +analyze_freebsd_cpu_speed(Extract) -> + analyze_freebsd_item(Extract, + "hw.clockrate", + fun(X) -> list_to_integer(X) end, + -1). + +analyze_freebsd_ncpu(Extract) -> + analyze_freebsd_item(Extract, + "hw.ncpu", + fun(X) -> list_to_integer(X) end, + -1). + +analyze_freebsd_memory(Extract) -> + analyze_freebsd_item(Extract, + "hw.physmem", + fun(X) -> list_to_integer(X) div 1024 end, + -1). + +analyze_freebsd_item(Extract, Key, Process, Default) -> + try + begin + case Extract(Key) of + [Key, Model] -> + Process(string:trim(Model)); + _ -> + Default + end + end + catch + _:_:_ -> + Default + end. + + +analyze_and_print_solaris_host_info(Version) -> + Release = + case file:read_file_info("/etc/release") of + {ok, _} -> + case [string:trim(S) || S <- string:tokens(os:cmd("cat /etc/release"), [$\n])] of + [Rel | _] -> + Rel; + _ -> + "-" + end; + _ -> + "-" + end, + %% Display the firmware device tree root properties (prtconf -b) + Props = [list_to_tuple([string:trim(PS) || PS <- Prop]) || + Prop <- [string:tokens(S, [$:]) || + S <- string:tokens(os:cmd("prtconf -b"), [$\n])]], + BannerName = case lists:keysearch("banner-name", 1, Props) of + {value, {_, BN}} -> + string:trim(BN); + _ -> + "-" + end, + InstructionSet = + case string:trim(os:cmd("isainfo -k")) of + "Pseudo-terminal will not" ++ _ -> + "-"; + IS -> + IS + end, + PtrConf = [list_to_tuple([string:trim(S) || S <- Items]) || Items <- [string:tokens(S, [$:]) || S <- string:tokens(os:cmd("prtconf"), [$\n])], length(Items) > 1], + SysConf = + case lists:keysearch("System Configuration", 1, PtrConf) of + {value, {_, SC}} -> + SC; + _ -> + "-" + end, + %% Because we count the lines of the output (which may contain + %% any number of extra crap lines) we need to ensure we only + %% count the "proper" stdout. So send it to a tmp file first + %% and then count its number of lines... + NumPhysCPU = + try + begin + File1 = f("/tmp/psrinfo_p.~s.~w", [os:getpid(), os:system_time()]), + os:cmd("psrinfo -p > " ++ File1), + string:trim(os:cmd("cat " ++ File1)) + end + catch + _:_:_ -> + "-" + end, + %% Because we count the lines of the output (which may contain + %% any number of extra crap lines) we need to ensure we only + %% count the "proper" stdout. So send it to a tmp file first + %% and then count its number of lines... + NumVCPU = + try + begin + File2 = f("/tmp/psrinfo.~s.~w", [os:getpid(), os:system_time()]), + os:cmd("psrinfo > " ++ File2), + [NumVCPUStr | _] = string:tokens(os:cmd("wc -l " ++ File2), [$\ ]), + NumVCPUStr + end + catch + _:_:_ -> + "-" + end, + MemSz = + case lists:keysearch("Memory size", 1, PtrConf) of + {value, {_, MS}} -> + MS; + _ -> + "-" + end, + io:format("Solaris: ~s" + "~n Release: ~s" + "~n Banner Name: ~s" + "~n Instruction Set: ~s" + "~n CPUs: ~s (~s)" + "~n System Config: ~s" + "~n Memory Size: ~s" + "~n Num Schedulers: ~s" + "~n~n", [Version, Release, BannerName, InstructionSet, + NumPhysCPU, NumVCPU, + SysConf, MemSz, + str_num_schedulers()]), + io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]), + MemFactor = + try string:tokens(MemSz, [$ ]) of + [SzStr, "Mega" ++ _] -> + try list_to_integer(SzStr) of + Sz when Sz > 8192 -> + 0; + Sz when Sz > 4096 -> + 1; + Sz when Sz > 2048 -> + 2; + _ -> + 5 + catch + _:_:_ -> + 10 + end; + [SzStr, "Giga" ++ _] -> + try list_to_integer(SzStr) of + Sz when Sz > 8 -> + 0; + Sz when Sz > 4 -> + 1; + Sz when Sz > 2 -> + 2; + _ -> + 5 + catch + _:_:_ -> + 10 + end; + _ -> + 10 + catch + _:_:_ -> + 10 + end, + try erlang:system_info(schedulers) of + 1 -> + 10; + 2 -> + 5; + N when (N =< 6) -> + 2; + _ -> + 1 + catch + _:_:_ -> + 10 + end + MemFactor. + + + +analyze_and_print_win_host_info(Version) -> + SysInfo = which_win_system_info(), + OsName = win_sys_info_lookup(os_name, SysInfo), + OsVersion = win_sys_info_lookup(os_version, SysInfo), + SysMan = win_sys_info_lookup(system_manufacturer, SysInfo), + SysMod = win_sys_info_lookup(system_model, SysInfo), + NumProcs = win_sys_info_lookup(num_processors, SysInfo), + TotPhysMem = win_sys_info_lookup(total_phys_memory, SysInfo), + io:format("Windows: ~s" + "~n OS Version: ~s (~p)" + "~n System Manufacturer: ~s" + "~n System Model: ~s" + "~n Number of Processor(s): ~s" + "~n Total Physical Memory: ~s" + "~n Num Schedulers: ~s" + "~n~n", [OsName, OsVersion, Version, + SysMan, SysMod, NumProcs, TotPhysMem, + str_num_schedulers()]), + io:format("TS Scale Factor: ~w~n", [timetrap_scale_factor()]), + MemFactor = + try + begin + [MStr, MUnit|_] = + string:tokens(lists:delete($,, TotPhysMem), [$\ ]), + case string:to_lower(MUnit) of + "gb" -> + try list_to_integer(MStr) of + M when M > 8 -> + 0; + M when M > 4 -> + 1; + M when M > 2 -> + 2; + _ -> + 5 + catch + _:_:_ -> + 10 + end; + "mb" -> + try list_to_integer(MStr) of + M when M > 8192 -> + 0; + M when M > 4096 -> + 1; + M when M > 2048 -> + 2; + _ -> + 5 + catch + _:_:_ -> + 10 + end; + _ -> + 10 + end + end + catch + _:_:_ -> + 10 + end, + CPUFactor = + case erlang:system_info(schedulers) of + 1 -> + 10; + 2 -> + 5; + _ -> + 2 + end, + CPUFactor + MemFactor. + +win_sys_info_lookup(Key, SysInfo) -> + win_sys_info_lookup(Key, SysInfo, "-"). + +win_sys_info_lookup(Key, SysInfo, Def) -> + case lists:keysearch(Key, 1, SysInfo) of + {value, {Key, Value}} -> + Value; + false -> + Def + end. + +%% This function only extracts the prop we actually care about! +which_win_system_info() -> + SysInfo = os:cmd("systeminfo"), + try process_win_system_info(string:tokens(SysInfo, [$\r, $\n]), []) + catch + _:_:_ -> + io:format("Failed process System info: " + "~s~n", [SysInfo]), + [] + end. + +process_win_system_info([], Acc) -> + Acc; +process_win_system_info([H|T], Acc) -> + case string:tokens(H, [$:]) of + [Key, Value] -> + case string:to_lower(Key) of + "os name" -> + process_win_system_info(T, + [{os_name, string:trim(Value)}|Acc]); + "os version" -> + process_win_system_info(T, + [{os_version, string:trim(Value)}|Acc]); + "system manufacturer" -> + process_win_system_info(T, + [{system_manufacturer, string:trim(Value)}|Acc]); + "system model" -> + process_win_system_info(T, + [{system_model, string:trim(Value)}|Acc]); + "processor(s)" -> + [NumProcStr|_] = string:tokens(Value, [$\ ]), + T2 = lists:nthtail(list_to_integer(NumProcStr), T), + process_win_system_info(T2, + [{num_processors, NumProcStr}|Acc]); + "total physical memory" -> + process_win_system_info(T, + [{total_phys_memory, string:trim(Value)}|Acc]); + _ -> + process_win_system_info(T, Acc) + end; + _ -> + process_win_system_info(T, Acc) + end. + + + +str_num_schedulers() -> + try erlang:system_info(schedulers) of + N -> f("~w", [N]) + catch + _:_:_ -> "-" + end. + + + %% ---------------------------------------------------------------- %% Time related function %% @@ -717,15 +1569,16 @@ is_snmp_running() -> is_app_running(snmp). crypto_start() -> - case (catch crypto:start()) of + try crypto:start() of ok -> ok; {error, {already_started,crypto}} -> - ok; - {'EXIT', Reason} -> - {error, {exit, Reason}}; - Else -> - Else + ok + catch + exit:{undef, [{crypto, start, [], []} | _]}:_ -> + {error, no_crypto}; + C:E:S -> + {error, {C, E, S}} end. crypto_support() -> @@ -745,8 +1598,74 @@ crypto_support([Func|Funcs], Acc) -> is_crypto_supported(Func) -> snmp_misc:is_crypto_supported(Func). - - + + +%% This function ensures that a *named* process on the local node is not running. +%% It does so by: +%% 1) Wait for 'Timeout' msec +%% 2) If 1 did not work, issue 'stop' and then wait 'Timeout' msec +%% 3) And finally, if 2 did not work, issue exit(kill). +ensure_not_running(Name, Stopper, Timeout) + when is_atom(Name) andalso + is_function(Stopper, 0) andalso + is_integer(Timeout) -> + ensure_not_running(whereis(Name), Name, Stopper, Timeout). + +ensure_not_running(Pid, Name, Stopper, Timeout) when is_pid(Pid) -> + MRef = erlang:monitor(process, Pid), + try + begin + ensure_not_running_wait(Pid, MRef, Timeout), + ensure_not_running_stop(Pid, MRef, Stopper, Timeout), + ensure_not_running_kill(Pid, MRef, Timeout), + exit({failed_ensure_not_running, Name}) + end + catch + throw:ok -> + sleep(1000), + ok + end; +ensure_not_running(_, _, _, _) -> + iprint("ensure_not_running -> not running", []), + sleep(1000), % This should not actually be necessary! + ok. + + +ensure_not_running_wait(Pid, MRef, Timeout) -> + receive + {'DOWN', MRef, process, Pid, _Info} -> + iprint("ensure_not_running_wait -> died peacefully", []), + throw(ok) + after Timeout -> + wprint("ensure_not_running_wait -> giving up", []), + ok + end. + +ensure_not_running_stop(Pid, MRef, Stopper, Timeout) -> + %% Spawn a stop'er process + StopPid = spawn(Stopper), + receive + {'DOWN', MRef, process, Pid, _Info} -> + nprint("ensure_not_running_stop -> dead (stopped)", []), + throw(ok) + after Timeout -> + wprint("ensure_not_running_stop -> giving up", []), + exit(StopPid, kill), + ok + end. + +ensure_not_running_kill(Pid, MRef, Timeout) -> + exit(Pid, kill), + receive + {'DOWN', MRef, process, Pid, _Info} -> + nprint("ensure_not_running_kill -> dead (killed)", []), + throw(ok) + after Timeout -> + wprint("ensure_not_running_kill -> giving up", []), + ok + end. + + %% ---------------------------------------------------------------- %% Watchdog functions %% @@ -875,19 +1794,6 @@ del_file_or_dir(FileOrDir) -> %% ---------------------------------------------------------------------- -%% cover functions -%% - -cover([Suite, Case] = Args) when is_atom(Suite) andalso is_atom(Case) -> - Mods0 = cover:compile_directory("../src"), - Mods1 = [Mod || {ok, Mod} <- Mods0], - snmp_test_server:t(Args), - Files0 = [cover:analyse_to_file(Mod) || Mod <- Mods1], - [io:format("Cover output: ~s~n", [File]) || {ok, File} <- Files0], - ok. - - -%% ---------------------------------------------------------------------- %% (debug) Print functions %% @@ -933,3 +1839,49 @@ print(Prefix, Module, Line, Format, Args) -> formated_timestamp() -> snmp_misc:formated_timestamp(). + +%% ---------------------------------------------------------------------- +%% +%% General purpose print functions +%% ERROR, WARNING and NOTICE are written both to 'user' and 'standard_io'. +%% INFO only to 'standard_io'. +%% +%% Should we also allow for (optional) a "short name" (sname)? +%% + +%% ERROR print (both to user and standard_io) +eprint(F, A) -> + Str = format_print("ERROR", F, A), + io:format(user, "~s~n", [Str]), + io:format(standard_io, "~s~n", [Str]). + +%% WARNING print (both to user and standard_io) +wprint(F, A) -> + Str = format_print("WARNING", F, A), + io:format(user, "~s~n", [Str]), + io:format(standard_io, "~s~n", [Str]). + +%% NOTICE print (both to user and standard_io) +nprint(F, A) -> + Str = format_print("NOTICE", F, A), + io:format(user, "~s~n", [Str]), + io:format(standard_io, "~s~n", [Str]). + +%% INFO print (only to user) +iprint(F, A) -> + Str = format_print("INFO", F, A), + io:format(standard_io, "~s~n", [Str]). + +format_print(Prefix, F, A) -> + format_print(get(tname), Prefix, F, A). + +format_print(undefined, Prefix, F, A) -> + f("*** [~s] ~s ~p ~p *** ~n" ++ F ++ "~n", + [formated_timestamp(), Prefix, node(), self() | A]); +format_print(TName, Prefix, F, A) when is_atom(TName) -> + format_print(atom_to_list(TName), Prefix, F, A); +format_print(TName, Prefix, F, A) when is_list(TName) -> + f("*** [~s] ~s ~s ~p ~p *** ~n" ++ F ++ "~n", + [formated_timestamp(), Prefix, TName, node(), self() | A]). + + diff --git a/lib/snmp/test/snmp_test_lib.hrl b/lib/snmp/test/snmp_test_lib.hrl index c91b6d1859..f4863c9a1e 100644 --- a/lib/snmp/test/snmp_test_lib.hrl +++ b/lib/snmp/test/snmp_test_lib.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2019. All Rights Reserved. +%% Copyright Ericsson AB 2002-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -43,17 +43,18 @@ %% - Test case macros - --define(TC_TRY(C, TC), ?LIB:tc_try(C, TC)). --define(TC_TRY(C, TCCond, TC), ?LIB:tc_try(C, TCCond, TC)). + +-define(TC_TRY(C, TC), ?LIB:tc_try(C, TC)). +-define(TC_TRY(C, TCCond, TC), ?LIB:tc_try(C, TCCond, TC)). +-define(TC_TRY(C, Pre, TC, Post), ?LIB:tc_try(C, Pre, TC, Post)). +-define(TC_TRY(C, TCCond, Pre, TC, Post), ?LIB:tc_try(C, TCCond, Pre, TC, Post)). + -define(OS_BASED_SKIP(Skippable), ?LIB:os_based_skip(Skippable)). -define(NON_PC_TC_MAYBE_SKIP(Config, Condition), ?LIB:non_pc_tc_maybe_skip(Config, Condition, ?MODULE, ?LINE)). -define(SKIP(Reason), ?LIB:skip(Reason, ?MODULE, ?LINE)). -define(FAIL(Reason), ?LIB:fail(Reason, ?MODULE, ?LINE)). --define(IS_IPV6_HOST(), ?LIB:is_ipv6_host()). --define(IS_IPV6_HOST(H), ?LIB:is_ipv6_host(H)). -define(HAS_SUPPORT_IPV6(), ?LIB:has_support_ipv6()). --define(HAS_SUPPORT_IPV6(H), ?LIB:has_support_ipv6(H)). %% - Time macros - @@ -83,6 +84,10 @@ -define(FLUSH(), ?LIB:flush_mqueue()). -define(ETRAP_GET(), ?LIB:trap_exit()). -define(ETRAP_SET(O), ?LIB:trap_exit(O)). +-define(PINFO(__P__), try process_info(__P__) + catch _:_:_ -> + {not_running, __P__} + end). %% - Node utility macros - @@ -104,6 +109,9 @@ -define(CRYPTO_SUPPORT(), ?LIB:crypto_support()). +-define(ENSURE_NOT_RUNNING(N, S, T), ?LIB:ensure_not_running(N, S, T)). + + %% - Dir macros - -define(DEL_DIR(D), ?LIB:del_dir(D)). @@ -111,55 +119,33 @@ %% - Print macros +%% Used for indicating the start of a test case -define(P(C), ?LIB:p(?MODULE, C)). --define(P1(F), ?LIB:p(F, [])). --define(P2(F, A), ?LIB:p(F, A)). --define(F(F, A), ?LIB:f(F, A)). --ifdef(snmp_debug). --ifndef(snmp_log). --define(snmp_log,true). --endif. --ifndef(snmp_error). --define(snmp_error,true). --endif. --else. --ifdef(snmp_log). --ifndef(snmp_error). --define(snmp_error,true). --endif. --endif. --endif. +%% Takes a format call (such as io:format) and produces a printable string +-define(F(F, A), ?LIB:f(F, A)). -ifdef(snmp_debug). --define(DBG(F,A), ?PRINT("DBG", F, A)). +-define(DBG(F,A), ?IPRINT(F, A)). -else. -define(DBG(F,A), ok). -endif. --ifdef(snmp_log). --define(LOG(F,A), ?PRINT("LOG", F, A)). --else. --define(LOG(F,A), ok). --endif. - --ifdef(snmp_error). --define(ERR(F,A), ?PRINT("ERR", F, A)). --else. --define(ERR(F,A), ok). --endif. - --define(INF(F,A), ?PRINT("INF", F, A)). +%% ERROR print +-define(EPRINT(F), ?LIB:eprint(F, [])). +-define(EPRINT(F, A), ?LIB:eprint(F, A)). --define(PRINT(P,F,A), ?LIB:print(P, ?MODULE, ?LINE, F, A)). +%% WARNING print +-define(WPRINT(F), ?LIB:wprint(F, [])). +-define(WPRINT(F, A), ?LIB:wprint(F, A)). --define(PRINT1(F, A), ?LIB:print1(F, A)). --define(PRINT1(F), ?PRINT1(F, [])). --define(EPRINT1(F, A), ?PRINT1("<ERROR> " ++ F, A)). +%% NOTICE print +-define(NPRINT(F), ?LIB:nprint(F, [])). +-define(NPRINT(F, A), ?LIB:nprint(F, A)). --define(PRINT2(F, A), ?LIB:print2(F, A)). --define(PRINT2(F), ?PRINT2(F, [])). --define(EPRINT2(F, A), ?PRINT2("<ERROR> " ++ F, A)). +%% INFO print +-define(IPRINT(F), ?LIB:iprint(F, [])). +-define(IPRINT(F, A), ?LIB:iprint(F, A)). -define(FTS(), snmp_misc:formated_timestamp()). -define(FTS(TS), snmp_misc:format_timestamp(TS)). diff --git a/lib/snmp/test/snmp_test_mgr.erl b/lib/snmp/test/snmp_test_mgr.erl index f50147a852..0a80860ed1 100644 --- a/lib/snmp/test/snmp_test_mgr.erl +++ b/lib/snmp/test/snmp_test_mgr.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2019. All Rights Reserved. +%% Copyright Ericsson AB 1996-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -191,40 +191,41 @@ receive_trap(Timeout) -> %% {mibs, List of Filenames}, {trap_udp, UDPPort (default 5000)}, %%---------------------------------------------------------------------- init({Options, CallerPid}) -> + %% This causes "verbosity printouts" to print (from level debug) + %% in the modules we "borrow" from the agent code (burr,,,) + %% With "our" name (mgr). put(sname, mgr), - put(verbosity, debug), + put(verbosity, debug), + %% Make use of the "test name" print "feature" + put(tname, "MGR"), ?SNMP_RAND_SEED(), - %% rand:seed(exrop, - %% {erlang:phash2([node()]), - %% erlang:monotonic_time(), - %% erlang:unique_integer()}), case (catch is_options_ok(Options)) of true -> put(debug, get_value(debug, Options, false)), - d("init -> (~p) extract options",[self()]), + d("init -> extract options"), PacksDbg = get_value(packet_server_debug, Options, false), - print("[~w] ~p -> PacksDbg: ~p~n", [?MODULE, self(), PacksDbg]), + ?IPRINT("init -> PacksDbg: ~p", [PacksDbg]), RecBufSz = get_value(recbuf, Options, 1024), - print("[~w] ~p -> RecBufSz: ~p~n", [?MODULE, self(), RecBufSz]), + ?IPRINT("init -> RecBufSz: ~p", [RecBufSz]), Mibs = get_value(mibs, Options, []), - print("[~w] ~p -> Mibs: ~p~n", [?MODULE, self(), Mibs]), + ?IPRINT("init -> Mibs: ~p", [Mibs]), Udp = get_value(agent_udp, Options, 4000), - print("[~w] ~p -> Udp: ~p~n", [?MODULE, self(), Udp]), + ?IPRINT("init -> Udp: ~p", [Udp]), User = get_value(user, Options, "initial"), - print("[~w] ~p -> User: ~p~n", [?MODULE, self(), User]), + ?IPRINT("init -> User: ~p", [User]), EngineId = get_value(engine_id, Options, "agentEngine"), - print("[~w] ~p -> EngineId: ~p~n", [?MODULE, self(), EngineId]), + ?IPRINT("init -> EngineId: ~p", [EngineId]), CtxEngineId = get_value(context_engine_id, Options, EngineId), - print("[~w] ~p -> CtxEngineId: ~p~n", [?MODULE, self(), CtxEngineId]), + ?IPRINT("init -> CtxEngineId: ~p", [CtxEngineId]), TrapUdp = get_value(trap_udp, Options, 5000), - print("[~w] ~p -> TrapUdp: ~p~n", [?MODULE, self(), TrapUdp]), + ?IPRINT("init -> TrapUdp: ~p", [TrapUdp]), Dir = get_value(dir, Options, "."), - print("[~w] ~p -> Dir: ~p~n", [?MODULE, self(), Dir]), + ?IPRINT("init -> Dir: ~p", [Dir]), SecLevel = get_value(sec_level, Options, noAuthNoPriv), - print("[~w] ~p -> SecLevel: ~p~n", [?MODULE, self(), SecLevel]), + ?IPRINT("init -> SecLevel: ~p", [SecLevel]), MiniMIB = snmp_mini_mib:create(Mibs), - d("[~w] ~p -> MiniMIB: " - "~n ~p", [?MODULE, self(), MiniMIB]), + d("init -> MiniMIB: " + "~n ~p", [MiniMIB]), Version = case lists:member(v2, Options) of true -> 'version-2'; false -> @@ -233,41 +234,38 @@ init({Options, CallerPid}) -> false -> 'version-1' end end, - print("[~w] ~p -> Version: ~p~n", [?MODULE, self(), Version]), + ?IPRINT("init -> Version: ~p", [Version]), Com = case Version of 'version-3' -> get_value(context, Options, ""); _ -> get_value(community, Options, "public") end, - print("[~w] ~p -> Com: ~p~n", [?MODULE, self(), Com]), + ?IPRINT("init -> Com: ~p", [Com]), VsnHdrD = {Com, User, EngineId, CtxEngineId, mk_seclevel(SecLevel)}, - print("[~w] ~p -> VsnHdrD: ~p~n", [?MODULE, self(), VsnHdrD]), + ?IPRINT("init -> VsnHdrD: ~p", [VsnHdrD]), IpFamily = get_value(ipfamily, Options, inet), - print("[~w] ~p -> IpFamily: ~p~n", [?MODULE, self(), IpFamily]), + ?IPRINT("init -> IpFamily: ~p", [IpFamily]), AgIp = case snmp_misc:assq(agent, Options) of {value, Addr} when is_tuple(Addr) andalso (size(Addr) =:= 4) andalso (IpFamily =:= inet) -> - print("[~w] ~p -> Addr: ~p~n", - [?MODULE, self(), Addr]), + ?IPRINT("init -> Addr: ~p", [Addr]), Addr; {value, Addr} when is_tuple(Addr) andalso (size(Addr) =:= 8) andalso (IpFamily =:= inet6) -> - print("[~w] ~p -> Addr: ~p~n", - [?MODULE, self(), Addr]), + ?IPRINT("init -> Addr: ~p", [Addr]), Addr; {value, Host} when is_list(Host) -> - print("[~w] ~p -> Host: ~p~n", - [?MODULE, self(), Host]), + ?IPRINT("init -> Host: ~p", [Host]), {ok, Ip} = snmp_misc:ip(Host, IpFamily), Ip end, - print("[~w] ~p -> AgIp: ~p~n", [?MODULE, self(), AgIp]), + ?IPRINT("init -> AgIp: ~p", [AgIp]), Quiet = lists:member(quiet, Options), - print("[~w] ~p -> Quiet: ~p~n", [?MODULE, self(), Quiet]), + ?IPRINT("init -> Quiet: ~p", [Quiet]), PackServ = start_packet_server( Quiet, Options, CallerPid, AgIp, Udp, TrapUdp, @@ -692,16 +690,16 @@ echo_pdu(PDU, MiniMIB) -> %% Test Sequence %%---------------------------------------------------------------------- echo_errors({error, Id, {ExpectedFormat, ExpectedData}, {Format, Data}})-> - io:format("* Unexpected Behaviour * Id: ~w.~n" - " Expected: " ++ ExpectedFormat ++ "~n" - " Got: " ++ Format ++ "~n", - [Id] ++ ExpectedData ++ Data), + ?IPRINT("*** Unexpected Behaviour *** Id: ~w.~n" + " Expected: " ++ ExpectedFormat ++ "~n" + " Got: " ++ Format ++ "~n", + [Id] ++ ExpectedData ++ Data), {error, Id, {ExpectedFormat, ExpectedData}, {Format, Data}}; echo_errors(ok) -> ok; echo_errors({ok, Val}) -> {ok, Val}. get_response_impl(Id, ExpVars) -> - ?PRINT2("await response ~w with" + ?IPRINT("await response ~w with" "~n Expected Varbinds: ~p", [Id, ExpVars]), PureVars = find_pure_oids2(ExpVars), @@ -710,7 +708,7 @@ get_response_impl(Id, ExpVars) -> error_status = noError, error_index = 0, varbinds = VBs} -> - ?PRINT2("received expected response pdu (~w) - match vars" + ?IPRINT("received expected response pdu (~w) - match vars" "~n Expected VBs: ~p" "~n Received VBs: ~p", [Id, PureVars, VBs]), @@ -720,10 +718,10 @@ get_response_impl(Id, ExpVars) -> request_id = ReqId, error_status = Err2, error_index = Index2} -> - ?EPRINT2("received unexpected response pdu: ~w, ~w, ~w" - "~n Received Error: ~p" - "~n Received Index: ~p", - [Type2, Id, ReqId, Err2, Index2]), + ?EPRINT("received unexpected response pdu: ~w, ~w, ~w" + "~n Received Error: ~p" + "~n Received Index: ~p", + [Type2, Id, ReqId, Err2, Index2]), {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w", @@ -732,8 +730,8 @@ get_response_impl(Id, ExpVars) -> [Type2, Err2, Index2]}}; {error, Reason} -> - ?EPRINT2("unexpected receive pdu error: ~w" - "~n ~p", [Id, Reason]), + ?EPRINT("unexpected receive pdu error: ~w" + "~n ~p", [Id, Reason]), format_reason(Id, Reason) end. @@ -743,81 +741,81 @@ get_response_impl(Id, ExpVars) -> %% Returns: ok | {error, Id, {ExpectedFormat, ExpectedData}, {Format, Data}} %%---------------------------------------------------------------------- expect_impl(Id, any) -> - ?PRINT2("await ~w pdu (any)", [Id]), + ?IPRINT("await ~w pdu (any)", [Id]), case receive_response() of PDU when is_record(PDU, pdu) -> - ?PRINT2("received expected pdu (~w)", [Id]), + ?IPRINT("received expected pdu (~w)", [Id]), ok; {error, Reason} -> - ?EPRINT1("unexpected receive error: ~w" - "~n ~p", [Id, Reason]), + ?EPRINT("unexpected receive error: ~w" + "~n ~p", [Id, Reason]), format_reason(Id, Reason) end; expect_impl(Id, return) -> - ?PRINT2("await ~w pdu", [Id]), + ?IPRINT("await ~w pdu", [Id]), case receive_response() of PDU when is_record(PDU, pdu) -> - ?PRINT2("received expected pdu (~w)", [Id]), + ?IPRINT("received expected pdu (~w)", [Id]), {ok, PDU}; {error, Reason} -> - ?EPRINT1("unexpected receive error: ~w" - "~n ~p", [Id, Reason]), + ?EPRINT("unexpected receive error: ~w" + "~n ~p", [Id, Reason]), format_reason(Id, Reason) end; expect_impl(Id, trap) -> - ?PRINT2("await ~w trap", [Id]), + ?IPRINT("await ~w trap", [Id]), case receive_trap(3500) of PDU when is_record(PDU, trappdu) -> - ?PRINT2("received expected trap (~w)", [Id]), + ?IPRINT("received expected trap (~w)", [Id]), ok; {error, Reason} -> - ?EPRINT1("unexpected receive error: ~w" - "~n ~p", [Id, Reason]), + ?EPRINT("unexpected receive error: ~w" + "~n ~p", [Id, Reason]), format_reason(Id, Reason) end; expect_impl(Id, timeout) -> - ?PRINT2("await ~w nothing", [Id]), + ?IPRINT("await ~w nothing", [Id]), receive X -> - ?EPRINT1("received unexpected message: ~w" - "~n ~p", - [Id, X]), + ?EPRINT("received unexpected message: ~w" + "~n ~p", + [Id, X]), {error, Id, {"Timeout", []}, {"Message ~w", [X]}} after 3500 -> ok end; expect_impl(Id, Err) when is_atom(Err) -> - ?PRINT2("await ~w with" + ?IPRINT("await ~w with" "~n Err: ~p", [Id, Err]), case receive_response() of #pdu{error_status = Err} -> - ?PRINT2("received pdu with expected error status (~w, ~w)", + ?IPRINT("received pdu with expected error status (~w, ~w)", [Id, Err]), ok; #pdu{type = Type2, request_id = ReqId, error_status = Err2} -> - ?EPRINT1("received pdu with unexpected error status: ~w, ~w, ~w" - "~n Expected Error: ~p" - "~n Received Error: ~p", - [Type2, Id, ReqId, Err, Err2]), + ?EPRINT("received pdu with unexpected error status: ~w, ~w, ~w" + "~n Expected Error: ~p" + "~n Received Error: ~p", + [Type2, Id, ReqId, Err, Err2]), {error, Id, {"ErrorStatus: ~w, RequestId: ~w", [Err,ReqId]}, {"ErrorStatus: ~w", [Err2]}}; {error, Reason} -> - ?EPRINT1("unexpected receive error: ~w" - "~n ~p", [Id, Reason]), + ?EPRINT("unexpected receive error: ~w" + "~n ~p", [Id, Reason]), format_reason(Id, Reason) end; expect_impl(Id, ExpectedVarbinds) when is_list(ExpectedVarbinds) -> - ?PRINT2("await ~w with" + ?IPRINT("await ~w with" "~n ExpectedVarbinds: ~p", [Id, ExpectedVarbinds]), PureVars = find_pure_oids(ExpectedVarbinds), @@ -826,7 +824,7 @@ expect_impl(Id, ExpectedVarbinds) when is_list(ExpectedVarbinds) -> error_status = noError, error_index = 0, varbinds = VBs} -> - ?PRINT2("received expected response pdu (~w) - check varbinds" + ?IPRINT("received expected response pdu (~w) - check varbinds" "~n Expected VBs: ~p" "~n Received VBs: ~p", [Id, PureVars, VBs]), @@ -836,22 +834,22 @@ expect_impl(Id, ExpectedVarbinds) when is_list(ExpectedVarbinds) -> request_id = ReqId, error_status = Err2, error_index = Index2} -> - ?EPRINT1("received unexpected pdu: ~w, ~w, ~w" - "~n Received Error: ~p" - "~n Received Index: ~p", + ?EPRINT("received unexpected pdu: ~w, ~w, ~w" + "~n Received Error: ~p" + "~n Received Index: ~p", [Type2, Id, ReqId, Err2, Index2]), {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w", ['get-response', noError, 0, ReqId]}, {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}}; {error, Reason} -> - ?EPRINT1("unexpected receive error: ~w" - "~n ~p", [Id, Reason]), + ?EPRINT("unexpected receive error: ~w" + "~n ~p", [Id, Reason]), format_reason(Id, Reason) end. expect_impl(Id, v2trap, ExpectedVarbinds) when is_list(ExpectedVarbinds) -> - ?PRINT2("await v2 trap ~w with" + ?IPRINT("await v2 trap ~w with" "~n ExpectedVarbinds: ~p", [Id, ExpectedVarbinds]), PureVars = find_pure_oids(ExpectedVarbinds), @@ -860,7 +858,7 @@ expect_impl(Id, v2trap, ExpectedVarbinds) when is_list(ExpectedVarbinds) -> error_status = noError, error_index = 0, varbinds = VBs} -> - ?PRINT2("received expected v2 trap (~w) - check varbinds" + ?IPRINT("received expected v2 trap (~w) - check varbinds" "~n Expected VBs: ~p" "~n Received VBs: ~p", [Id, PureVars, VBs]), @@ -870,22 +868,22 @@ expect_impl(Id, v2trap, ExpectedVarbinds) when is_list(ExpectedVarbinds) -> request_id = ReqId, error_status = Err2, error_index = Index2} -> - ?EPRINT1("received unexpected pdu: ~w, ~w, ~w" - "~n Received Error: ~p" - "~n Received Index: ~p", - [Type2, Id, ReqId, Err2, Index2]), + ?EPRINT("received unexpected pdu: ~w, ~w, ~w" + "~n Received Error: ~p" + "~n Received Index: ~p", + [Type2, Id, ReqId, Err2, Index2]), {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w", ['snmpv2-trap', noError, 0, ReqId]}, {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}}; {error, Reason} -> - ?EPRINT1("unexpected receive error: ~w" - "~n ~p", [Id, Reason]), + ?EPRINT("unexpected receive error: ~w" + "~n ~p", [Id, Reason]), format_reason(Id, Reason) end; expect_impl(Id, report, ExpectedVarbinds) when is_list(ExpectedVarbinds) -> - ?PRINT2("await report ~w with" + ?IPRINT("await report ~w with" "~n ExpectedVarbinds: ~p", [Id, ExpectedVarbinds]), PureVBs = find_pure_oids(ExpectedVarbinds), @@ -894,7 +892,7 @@ expect_impl(Id, report, ExpectedVarbinds) when is_list(ExpectedVarbinds) -> error_status = noError, error_index = 0, varbinds = VBs} -> - ?PRINT2("received expected report (~w) - check varbinds" + ?IPRINT("received expected report (~w) - check varbinds" "~n Expected VBs: ~p" "~n Received VBs: ~p", [Id, PureVBs, VBs]), @@ -904,23 +902,23 @@ expect_impl(Id, report, ExpectedVarbinds) when is_list(ExpectedVarbinds) -> request_id = ReqId, error_status = Err2, error_index = Index2} -> - ?EPRINT1("received unexpected pdu: ~w, ~w, ~w" - "~n Received Error: ~p" - "~n Received Index: ~p", - [Type2, Id, ReqId, Err2, Index2]), + ?EPRINT("received unexpected pdu: ~w, ~w, ~w" + "~n Received Error: ~p" + "~n Received Index: ~p", + [Type2, Id, ReqId, Err2, Index2]), {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w", [report, noError, 0, ReqId]}, {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}}; {error, Reason} -> - ?EPRINT1("unexpected receive error: ~w" - "~n ~p", [Id, Reason]), + ?EPRINT("unexpected receive error: ~w" + "~n ~p", [Id, Reason]), format_reason(Id, Reason) end; expect_impl(Id, {inform, Reply}, ExpectedVarbinds) when is_list(ExpectedVarbinds) -> - ?PRINT2("await inform ~w with" + ?IPRINT("await inform ~w with" "~n Reply: ~p" "~n ExpectedVarbinds: ~p", [Id, Reply, ExpectedVarbinds]), @@ -931,20 +929,20 @@ expect_impl(Id, {inform, Reply}, ExpectedVarbinds) error_status = noError, error_index = 0, varbinds = VBs} -> - ?PRINT2("received inform (~w) - check varbinds" + ?IPRINT("received inform (~w) - check varbinds" "~n Expected VBs: ~p" "~n Received VBs: ~p", [Id, PureVBs, VBs]), case check_vars(Id, PureVBs, VBs) of ok when (Reply == true) -> - ?PRINT2("varbinds ok (~w) - send ok inform response", [Id]), + ?IPRINT("varbinds ok (~w) - send ok inform response", [Id]), RespPDU = Resp#pdu{type = 'get-response', error_status = noError, error_index = 0}, ?MODULE:rpl(RespPDU), ok; ok when (element(1, Reply) == error) -> - ?PRINT2("varbinds ok (~w) - send error inform response", [Id]), + ?IPRINT("varbinds ok (~w) - send error inform response", [Id]), {error, Status, Index} = Reply, RespPDU = Resp#pdu{type = 'get-response', error_status = Status, @@ -952,13 +950,12 @@ expect_impl(Id, {inform, Reply}, ExpectedVarbinds) ?MODULE:rpl(RespPDU), ok; ok when (Reply == false) -> - ?PRINT2("varbinds ok (~w) - don't send inform response", [Id]), + ?IPRINT("varbinds ok (~w) - don't send inform response", [Id]), ok; Else -> - ?EPRINT1("unexpected varbinds (~w)", [Id]), - io:format("expect_impl(~w, inform) -> " - "~n Else: ~p" - "~n", [Id, Else]), + ?EPRINT("unexpected (inform) varbinds (~w):" + "~n VBs: ~p" + "~n ~p", [Id, VBs, Else]), Else end; @@ -966,22 +963,22 @@ expect_impl(Id, {inform, Reply}, ExpectedVarbinds) request_id = ReqId, error_status = Err2, error_index = Index2} -> - ?EPRINT1("received unexpected pdu: ~w, ~w, ~w" - "~n Received Error: ~p" - "~n Received Index: ~p", - [Type2, Id, ReqId, Err2, Index2]), + ?EPRINT("received unexpected pdu: ~w, ~w, ~w" + "~n Received Error: ~p" + "~n Received Index: ~p", + [Type2, Id, ReqId, Err2, Index2]), {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w", ['inform-request', noError, 0, ReqId]}, {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}}; {error, Reason} -> - ?EPRINT1("unexpected receive error: ~w" - "~n ~p", [Id, Reason]), + ?EPRINT("unexpected receive error: ~w" + "~n ~p", [Id, Reason]), format_reason(Id, Reason) end. expect_impl(Id, Err, Index, any = _ExpectedVarbinds) -> - ?PRINT2("await response ~w with" + ?IPRINT("await response ~w with" "~n Err: ~p" "~n Index: ~p" "~n ExpectedVarbinds: ~p", @@ -990,13 +987,13 @@ expect_impl(Id, Err, Index, any = _ExpectedVarbinds) -> #pdu{type = 'get-response', error_status = Err, error_index = Index} -> - ?PRINT2("received expected response pdu (~w, ~w, ~w)", + ?IPRINT("received expected response pdu (~w, ~w, ~w)", [Id, Err, Index]), ok; #pdu{type = 'get-response', error_status = Err} when (Index == any) -> - ?PRINT2("received expected response pdu (~w, ~w)", + ?IPRINT("received expected response pdu (~w, ~w)", [Id, Err]), ok; @@ -1006,14 +1003,14 @@ expect_impl(Id, Err, Index, any = _ExpectedVarbinds) -> error_index = Idx} when is_list(Index) -> case lists:member(Idx, Index) of true -> - ?PRINT2("received expected response pdu (~w, ~w, ~w)", + ?IPRINT("received expected response pdu (~w, ~w, ~w)", [Id, Err, Idx]), ok; false -> - ?EPRINT1("received response pdu with unexpected index (~w, ~w):" - "~n Expected Index: ~p" - "~n Received Index: ~p", - [Id, Err, Index, Idx]), + ?EPRINT("received response pdu with unexpected index (~w, ~w):" + "~n Expected Index: ~p" + "~n Received Index: ~p", + [Id, Err, Index, Idx]), {error, Id, {"ErrStat: ~w, Idx: ~w, RequestId: ~w", [Err, Index, ReqId]}, {"ErrStat: ~w, Idx: ~w", [Err, Idx]}} @@ -1023,22 +1020,24 @@ expect_impl(Id, Err, Index, any = _ExpectedVarbinds) -> request_id = ReqId, error_status = Err2, error_index = Index2} -> - ?EPRINT1("received unexpected response pdu: ~w, ~w, ~w" - "~n Expected Error: ~p" - "~n Received Error: ~p" - "~n Expected Index: ~p" - "~n Received Index: ~p", - [Type2, Id, ReqId, Err, Err2, Index, Index2]), + ?EPRINT("received unexpected response pdu: ~w, ~w, ~w" + "~n Expected Error: ~p" + "~n Received Error: ~p" + "~n Expected Index: ~p" + "~n Received Index: ~p", + [Type2, Id, ReqId, Err, Err2, Index, Index2]), {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w", ['get-response', Err, Index, ReqId]}, {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}}; {error, Reason} -> + ?EPRINT("unexpected (receive) response: " + "~n ~p", [Reason]), format_reason(Id, Reason) end; expect_impl(Id, Err, Index, ExpectedVarbinds) -> - ?PRINT2("await response ~w with" + ?IPRINT("await response ~w with" "~n Err: ~p" "~n Index: ~p" "~n ExpectedVarbinds: ~p", @@ -1049,7 +1048,7 @@ expect_impl(Id, Err, Index, ExpectedVarbinds) -> error_status = Err, error_index = Index, varbinds = VBs} -> - ?PRINT2("received expected response pdu (~w, ~w, ~w) - check varbinds" + ?IPRINT("received expected response pdu (~w, ~w, ~w) - check varbinds" "~n Expected VBs: ~p" "~n Received VBs: ~p", [Id, Err, Index, PureVBs, VBs]), @@ -1058,7 +1057,7 @@ expect_impl(Id, Err, Index, ExpectedVarbinds) -> #pdu{type = 'get-response', error_status = Err, varbinds = VBs} when (Index == any) -> - ?PRINT2("received expected response pdu (~w, ~w) - check varbinds" + ?IPRINT("received expected response pdu (~w, ~w) - check varbinds" "~n Expected VBs: ~p" "~n Received VBs: ~p", [Id, Err, PureVBs, VBs]), @@ -1071,18 +1070,18 @@ expect_impl(Id, Err, Index, ExpectedVarbinds) -> varbinds = VBs} when is_list(Index) -> case lists:member(Idx, Index) of true -> - ?PRINT2("received expected pdu (~w, ~w, ~w) - check varbinds" + ?IPRINT("received expected pdu (~w, ~w, ~w) - check varbinds" "~n Expected VBs: ~p" "~n Received VBs: ~p", [Id, Err, Idx, PureVBs, VBs]), check_vars(Id, PureVBs, VBs); false -> - ?EPRINT1("received response pdu with unexpected index (~w, ~w):" - "~n Expected Index: ~p" - "~n Received Index: ~p" - "~n Expected VBs: ~p" - "~n Received VBs: ~p", - [Id, Err, Index, Idx, PureVBs, VBs]), + ?EPRINT("received response pdu with unexpected index (~w, ~w):" + "~n Expected Index: ~p" + "~n Received Index: ~p" + "~n Expected VBs: ~p" + "~n Received VBs: ~p", + [Id, Err, Index, Idx, PureVBs, VBs]), {error,Id, {"ErrStat: ~w, Idx: ~w, Varbinds: ~w, RequestId: ~w", [Err,Index,PureVBs,ReqId]}, @@ -1095,15 +1094,15 @@ expect_impl(Id, Err, Index, ExpectedVarbinds) -> error_status = Err2, error_index = Index2, varbinds = VBs} -> - ?EPRINT1("received unexpected response pdu: ~w, ~w, ~w" - "~n Expected Error: ~p" - "~n Received Error: ~p" - "~n Expected Index: ~p" - "~n Received Index: ~p" - "~n Expected VBs: ~p" - "~n Received VBs: ~p", - [Type2, Id, ReqId, - Err, Err2, Index, Index2, PureVBs, VBs]), + ?EPRINT("received unexpected response pdu: ~w, ~w, ~w" + "~n Expected Error: ~p" + "~n Received Error: ~p" + "~n Expected Index: ~p" + "~n Received Index: ~p" + "~n Expected VBs: ~p" + "~n Received VBs: ~p", + [Type2, Id, ReqId, + Err, Err2, Index, Index2, PureVBs, VBs]), {error,Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, Varbinds: ~w, RequestId: ~w", ['get-response',Err,Index,PureVBs,ReqId]}, @@ -1111,13 +1110,13 @@ expect_impl(Id, Err, Index, ExpectedVarbinds) -> [Type2,Err2,Index2,VBs]}}; {error, Reason} -> - ?EPRINT1("unexpected receive pdu error: ~w" - "~n ~p", [Id, Reason]), + ?EPRINT("unexpected receive pdu error: ~w" + "~n ~p", [Id, Reason]), format_reason(Id, Reason) end. expect_impl(Id, trap, Enterp, Generic, Specific, ExpectedVarbinds) -> - ?PRINT2("await trap pdu ~w with" + ?IPRINT("await trap pdu ~w with" "~n Enterprise: ~p" "~n Generic: ~p" "~n Specific: ~p" @@ -1130,7 +1129,7 @@ expect_impl(Id, trap, Enterp, Generic, Specific, ExpectedVarbinds) -> generic_trap = Generic, specific_trap = Specific, varbinds = VBs} -> - ?PRINT2("received expected trap pdu - check varbinds" + ?IPRINT("received expected trap pdu - check varbinds" "~n Expected VBs: ~p" "~n Received VBs: ~p", [PureVBs, VBs]), @@ -1140,20 +1139,20 @@ expect_impl(Id, trap, Enterp, Generic, Specific, ExpectedVarbinds) -> generic_trap = G2, specific_trap = Spec2, varbinds = VBs} -> - ?EPRINT1("received unexpected trap pdu: ~w" - "~n Expected Enterprise: ~p" - "~n Received Enterprise: ~p" - "~n Expected Generic: ~p" - "~n Received Generic: ~p" - "~n Expected Specific: ~p" - "~n Received Specific: ~p" - "~n Expected VBs: ~p" - "~n Received VBs: ~p", - [Id, - PureE, Ent2, - Generic, G2, - Specific, Spec2, - PureVBs, VBs]), + ?EPRINT("received unexpected trap pdu: ~w" + "~n Expected Enterprise: ~p" + "~n Received Enterprise: ~p" + "~n Expected Generic: ~p" + "~n Received Generic: ~p" + "~n Expected Specific: ~p" + "~n Received Specific: ~p" + "~n Expected VBs: ~p" + "~n Received VBs: ~p", + [Id, + PureE, Ent2, + Generic, G2, + Specific, Spec2, + PureVBs, VBs]), {error, Id, {"Enterprise: ~w, Generic: ~w, Specific: ~w, Varbinds: ~w", [PureE, Generic, Specific, ExpectedVarbinds]}, @@ -1161,8 +1160,8 @@ expect_impl(Id, trap, Enterp, Generic, Specific, ExpectedVarbinds) -> [Ent2, G2, Spec2, VBs]}}; {error, Reason} -> - ?EPRINT1("unexpected receive trap pdu error: ~w" - "~n ~p", [Id, Reason]), + ?EPRINT("unexpected receive trap pdu error: ~w" + "~n ~p", [Id, Reason]), format_reason(Id, Reason) end. @@ -1252,15 +1251,11 @@ sizeOf(L) when is_list(L) -> sizeOf(B) when is_binary(B) -> size(B). +d(F) -> d(F, []). d(F, A) -> d(get(debug), F, A). d(true, F, A) -> - print(F, A); + ?IPRINT(F, A); d(_,_F,_A) -> ok. -print(F, A) -> - ?PRINT2("MGR " ++ F, A). - -%% formated_timestamp() -> -%% snmp_test_lib:formated_timestamp(). diff --git a/lib/snmp/test/snmp_test_mgr_misc.erl b/lib/snmp/test/snmp_test_mgr_misc.erl index 6608a88c00..738f45a1b0 100644 --- a/lib/snmp/test/snmp_test_mgr_misc.erl +++ b/lib/snmp/test/snmp_test_mgr_misc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2019. All Rights Reserved. +%% Copyright Ericsson AB 1996-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -102,13 +102,21 @@ init_packet( Parent, SnmpMgr, AgentIp, UdpPort, TrapUdp, VsnHdr, Version, Dir, BufSz, DbgOptions, IpFamily) -> + %% This causes "verbosity printouts" to print (from the + %% specified level) in the modules we "borrow" from the + %% agent code (burr,,,). + %% With "our" name (mgr_misc). put(sname, mgr_misc), init_debug(DbgOptions), + %% Make use of the "test name" print "feature" + put(tname, "MGR-MISC"), + ?IPRINT("starting"), UdpOpts = [{recbuf,BufSz}, {reuseaddr, true}, IpFamily], {ok, UdpId} = gen_udp:open(TrapUdp, UdpOpts), put(msg_id, 1), init_usm(Version, Dir), proc_lib:init_ack(Parent, self()), + ?IPRINT("started"), packet_loop(SnmpMgr, UdpId, AgentIp, UdpPort, VsnHdr, Version, []). init_debug(Dbg) when is_atom(Dbg) -> @@ -601,43 +609,51 @@ set_pdu(Msg, RePdu) -> Msg#message{data = RePdu}. +%% Disgustingly, we borrow stuff from the agent, including the +%% local-db. Also, disgustingly, the local-db may actually not +%% have died yet. But since we actually *need* a clean local-db, +%% we must make sure its dead before we try to start the new +%% instance... init_usm('version-3', Dir) -> - ?vlog("init_usm -> create (and init) fake \"agent\" table", []), + ?IPRINT("init_usm -> create (and init) fake \"agent\" table", []), ets:new(snmp_agent_table, [set, public, named_table]), ets:insert(snmp_agent_table, {agent_mib_storage, persistent}), %% The local-db process may *still* be running (from a previous %% test case), on the way down, but not yet dead. - %% Either way, before we start it, make sure its dead and *gone*! + %% Either way, before we try to start it, make sure its old instance + %% dead and *gone*! %% How do we do that without getting hung up? Calling the stop %% function, will not do since it uses Timeout=infinity. - ?vlog("init_usm -> ensure (old) fake local-db is dead", []), + ?IPRINT("init_usm -> ensure (old) fake local-db is dead", []), ensure_local_db_dead(), - ?vlog("init_usm -> try start fake local-db", []), + ?IPRINT("init_usm -> try start fake local-db", []), case snmpa_local_db:start_link(normal, Dir, [{sname, "MGR-LOCAL-DB"}, {verbosity, trace}]) of {ok, Pid} -> - ?vlog("started: ~p" - "~n ~p", [Pid, process_info(Pid)]); + ?IPRINT("init_usm -> local-db started: ~p" + "~n ~p", [Pid, process_info(Pid)]); {error, {already_started, Pid}} -> LDBInfo = process_info(Pid), - ?vlog("already started: ~p" - "~n ~p", [Pid, LDBInfo]), + ?EPRINT("init_usm -> local-db already started: ~p" + "~n ~p", [Pid, LDBInfo]), ?FAIL({still_running, snmpa_local_db, LDBInfo}); {error, Reason} -> + ?EPRINT("init_usm -> failed start local-db: " + "~n ~p", [Reason]), ?FAIL({failed_starting, snmpa_local_db, Reason}) end, NameDb = snmpa_agent:db(snmpEngineID), - ?vlog("init_usm -> try set manager engine-id", []), + ?IPRINT("init_usm -> try set manager engine-id"), R = snmp_generic:variable_set(NameDb, "mgrEngine"), snmp_verbosity:print(info, info, "init_usm -> engine-id set result: ~p", [R]), - ?vlog("init_usm -> try set engine boots (framework-mib)", []), + ?IPRINT("init_usm -> try set engine boots (framework-mib)"), snmp_framework_mib:set_engine_boots(1), - ?vlog("init_usm -> try set engine time (framework-mib)", []), + ?IPRINT("init_usm -> try set engine time (framework-mib)"), snmp_framework_mib:set_engine_time(1), - ?vlog("init_usm -> try usm (mib) reconfigure", []), + ?IPRINT("init_usm -> try usm (mib) reconfigure"), snmp_user_based_sm_mib:reconfigure(Dir), - ?vlog("init_usm -> done", []), + ?IPRINT("init_usm -> done"), ok; init_usm(_Vsn, _Dir) -> ok. @@ -659,27 +675,28 @@ ensure_dead(Pid, Timeout) when is_pid(Pid) -> ok end; ensure_dead(_, _) -> - ?vlog("ensure_dead -> already dead", []), + ?IPRINT("ensure_dead -> already dead", []), ok. ensure_dead_wait(Pid, MRef, Timeout) -> receive {'DOWN', MRef, process, Pid, _Info} -> - ?vlog("ensure_dead_wait -> died peacefully", []), + ?IPRINT("ensure_dead_wait -> died peacefully"), throw(ok) after Timeout -> - ?vlog("ensure_dead_wait -> giving up", []), + ?WPRINT("ensure_dead_wait -> giving up"), ok end. ensure_dead_stop(Pid, MRef, Timeout) -> + %% Spawn a stop'er process StopPid = spawn(fun() -> snmpa_local_db:stop() end), receive {'DOWN', MRef, process, Pid, _Info} -> - ?vlog("ensure_dead -> dead (stopped)", []), + ?NPRINT("ensure_dead -> dead (stopped)"), throw(ok) after Timeout -> - ?vlog("ensure_dead_stop -> giving up", []), + ?WPRINT("ensure_dead_stop -> giving up"), exit(StopPid, kill), ok end. @@ -688,10 +705,10 @@ ensure_dead_kill(Pid, MRef, Timeout) -> exit(Pid, kill), receive {'DOWN', MRef, process, Pid, _Info} -> - ?vlog("ensure_dead -> dead (killed)", []), + ?NPRINT("ensure_dead -> dead (killed)"), throw(ok) after Timeout -> - ?vlog("ensure_dead_kill -> giving up", []), + ?WPRINT("ensure_dead_kill -> giving up"), ok end. @@ -891,10 +908,7 @@ d(F) -> d(F, []). d(F,A) -> d(get(debug), F, A). d(true, F, A) -> - print(F, A); + ?IPRINT(F, A); d(_,_F,_A) -> ok. -print(F, A) -> - ?PRINT2("MGR_PS " ++ F, A). - diff --git a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl index 09f20ad48a..76a1967513 100644 --- a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl +++ b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2014-2018. All Rights Reserved. +%% Copyright Ericsson AB 2014-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -130,16 +130,27 @@ snmpd_cases() -> %% ----- %% -init_per_suite(Config) -> - try - begin - Config2 = ?LIB:init_per_suite(netsnmp_init(Config)), - snmp_test_sys_monitor:start(), - Config2 - end - catch - throw:{skip, _} = SKIP -> - SKIP +init_per_suite(Config0) -> + ?IPRINT("init_per_suite -> entry with" + "~n Config: ~p", [Config0]), + + case netsnmp_init(Config0) of + {skip, _} = SKIP -> + SKIP; + + Config1 -> + case ?LIB:init_per_suite(Config1) of + {skip, _} = SKIP -> + SKIP; + + Config2 when is_list(Config2) -> + snmp_test_sys_monitor:start(), + + ?IPRINT("init_per_suite -> end when" + "~n Config: ~p", [Config2]), + + Config2 + end end. netsnmp_init(Config) -> @@ -150,10 +161,10 @@ netsnmp_init(Config) -> [{agent_port, ?AGENT_PORT}, {manager_port, ?MANAGER_PORT} | Config]; false -> - throw({skip, "Buggy NetSNMP"}) + {skip, "Buggy NetSNMP"} end; false -> - throw({skip, "No NetSNMP"}) + {skip, "No NetSNMP"} end. has_netsnmp() -> @@ -172,9 +183,15 @@ netsnmp_check(RE) -> end_per_suite(Config) -> + ?IPRINT("end_per_suite -> entry with" + "~n Config: ~p", [Config]), + snmp_test_sys_monitor:stop(), - ?LIB:end_per_suite(Config). + ?LIB:end_per_suite(Config), + ?IPRINT("end_per_suite -> end"), + + Config. %% %% ----- @@ -208,7 +225,7 @@ init_per_group(_, Config) -> Config. init_per_group_ipv6(Families, Config) -> - case ?LIB:has_support_ipv6() of + case ?HAS_SUPPORT_IPV6() of true -> init_per_group_ip(Families, Config); false -> @@ -256,17 +273,27 @@ end_per_group(_GroupName, Config) -> %% init_per_testcase(_Case, Config) -> + ?IPRINT("init_per_testcase -> entry with" + "~n Config: ~p", [Config]), snmp_test_global_sys_monitor:reset_events(), - Dog = ct:timetrap(20000), + Dog = ct:timetrap(?SECS(20)), application:stop(snmp), application:unload(snmp), - [{watchdog, Dog} | Config]. + Config1 = [{watchdog, Dog} | Config], + + ?IPRINT("init_per_testcase -> done when" + "~n Config: ~p", [Config1]), + + Config1. end_per_testcase(_, Config) -> - ?PRINT2("system events during test: " + ?IPRINT("end_per_testcase -> entry with" + "~n Config: ~p", [Config]), + + ?IPRINT("system events during test: " "~n ~p", [snmp_test_global_sys_monitor:events()]), case application:stop(snmp) of @@ -281,6 +308,10 @@ end_per_testcase(_, Config) -> E2 -> ct:pal("application:unload(snmp) -> ~p", [E2]) end, + + ?IPRINT("end_per_testcase -> done with" + "~n Config: ~p", [Config]), + Config. find_executable(Exec, Config) -> diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index b5a718c0b9..e79cbaa336 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -30,6 +30,35 @@ <file>notes.xml</file> </header> +<section><title>Ssh 4.8.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed that <c>ssh_connection:send</c> could allocate a + large amount of memory if given an iolist() as input + data.</p> + <p> + Own Id: OTP-16373</p> + </item> + <item> + <p> + Safe atom conversions.</p> + <p> + Own Id: OTP-16375</p> + </item> + <item> + <p> + Constant time comparisons added.</p> + <p> + Own Id: OTP-16376</p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 4.8.1</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -235,6 +264,35 @@ </section> +<section><title>Ssh 4.7.6.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed that <c>ssh_connection:send</c> could allocate a + large amount of memory if given an iolist() as input + data.</p> + <p> + Own Id: OTP-16373</p> + </item> + <item> + <p> + Safe atom conversions.</p> + <p> + Own Id: OTP-16375</p> + </item> + <item> + <p> + Constant time comparisons added.</p> + <p> + Own Id: OTP-16376</p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 4.7.6.2</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -610,6 +668,50 @@ </section> </section> +<section><title>Ssh 4.6.9.7</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed possible hanging in <c>ssh_sftp:stop_channel/1</c>.</p> + <p> + Own Id: OTP-16507 Aux Id: ERIERL-470 </p> + </item> + </list> + </section> + +</section> + +<section><title>Ssh 4.6.9.6</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed that <c>ssh_connection:send</c> could allocate a + large amount of memory if given an iolist() as input + data.</p> + <p> + Own Id: OTP-16373</p> + </item> + <item> + <p> + Safe atom conversions.</p> + <p> + Own Id: OTP-16375</p> + </item> + <item> + <p> + Constant time comparisons added.</p> + <p> + Own Id: OTP-16376</p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 4.6.9.5</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml index aa0fbe376c..862f79ac56 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -1196,6 +1196,41 @@ </desc> </func> +<!-- SET_SOCK_OPTS/2 --> + <func> + <name name="set_sock_opts" arity="2" since=""/> + <fsummary>Set tcp socket options on connections</fsummary> + <desc> + <p>Sets tcp socket options on the tcp-socket below an ssh connection.</p> + <p>This function calls the + <seealso marker="kernel:inet#setopts/2">inet:setopts/2</seealso>, read that documentation and + for <seealso marker="kernel:gen_tcp#type-option">gen_tcp:option()</seealso>. + All gen_tcp socket options except <c>active</c>, <c>deliver</c>, <c>mode</c> and <c>packet</c> + are allowed. The excluded options are reserved by the SSH application. + </p> + <warning> + <p>This is an extremly dangerous function. You use it on your own risk.</p> + <p>Some options are OS and OS version dependent. + Do not use it unless you know what effect your option values will have + on an TCP stream.</p> + <p>Some values may destroy the functionality of the SSH protocol. + </p> + </warning> + </desc> + </func> + +<!-- GET_SOCK_OPTS/2 --> + <func> + <name name="get_sock_opts" arity="2" since=""/> + <fsummary>Get tcp socket options on connections</fsummary> + <desc> + <p>Get tcp socket option values of the tcp-socket below an ssh connection.</p> + <p>This function calls the + <seealso marker="kernel:inet#getopts/2">inet:getopts/2</seealso>, read that documentation. + </p> + </desc> + </func> + <!-- DEAMON/1,2,3 --> <func> <name since="">daemon(Port | TcpSocket) -> Result</name> diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile index 9627b70eeb..f5c520f2f0 100644 --- a/lib/ssh/src/Makefile +++ b/lib/ssh/src/Makefile @@ -39,7 +39,13 @@ RELSYSDIR = $(RELEASE_PATH)/lib/ssh-$(VSN) # Behaviour (api) modules are first so they are compiled when # the compiler reaches a callback module using them. -BEHAVIOUR_MODULES= \ +# The $(BEHAVIOUR_MODULES_1) has a behaviour used in one or more +# of the $(BEHAVIOUR_MODULES_2) + +BEHAVIOUR_MODULES_1= \ + ssh_dbg + +BEHAVIOUR_MODULES_2= \ ssh_client_key_api \ ssh_daemon_channel \ ssh_server_channel \ @@ -59,7 +65,6 @@ MODULES= \ ssh_connection \ ssh_connection_handler \ ssh_connection_sup \ - ssh_dbg \ ssh_file \ ssh_info \ ssh_io \ @@ -83,12 +88,15 @@ HRL_FILES = ERL_FILES= \ $(MODULES:%=%.erl) \ - $(BEHAVIOUR_MODULES:%=%.erl) + $(BEHAVIOUR_MODULES_1:%=%.erl) \ + $(BEHAVIOUR_MODULES_2:%=%.erl) TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) -BEHAVIOUR_TARGET_FILES= $(BEHAVIOUR_MODULES:%=$(EBIN)/%.$(EMULATOR)) +BEHAVIOUR_TARGET_FILES_1= $(BEHAVIOUR_MODULES_1:%=$(EBIN)/%.$(EMULATOR)) +BEHAVIOUR_TARGET_FILES_2= $(BEHAVIOUR_MODULES_2:%=$(EBIN)/%.$(EMULATOR)) +BEHAVIOUR_TARGET_FILES= $(BEHAVIOUR_TARGET_FILES_1) $(BEHAVIOUR_TARGET_FILES_2) APP_FILE= ssh.app APPUP_FILE= ssh.appup @@ -115,7 +123,8 @@ ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \ # Targets # ---------------------------------------------------- -$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES) +$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES_2) +$(BEHAVIOUR_TARGET_FILES_2): $(BEHAVIOUR_TARGET_FILES_1) debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src index 2193c14611..21e3604400 100644 --- a/lib/ssh/src/ssh.app.src +++ b/lib/ssh/src/ssh.app.src @@ -44,10 +44,9 @@ {env, []}, {mod, {ssh_app, []}}, {runtime_dependencies, [ - "crypto-4.5", + "crypto-4.6.4", "erts-9.0", "kernel-5.3", "public_key-1.6.1", "stdlib-3.4.1" ]}]}. - diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index c93b796078..355b40eea8 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -35,6 +35,7 @@ channel_info/3, daemon/1, daemon/2, daemon/3, daemon_info/1, daemon_info/2, + set_sock_opts/2, get_sock_opts/2, default_algorithms/0, chk_algos_opts/1, stop_listener/1, stop_listener/2, stop_listener/3, @@ -560,6 +561,25 @@ chk_algos_opts(Opts) -> {error, {non_algo_opts_found,OtherOps}} end. + +%%-------------------------------------------------------------------- +-spec set_sock_opts(ConnectionRef, SocketOptions) -> + ok | {error, inet:posix()} when + ConnectionRef :: connection_ref(), + SocketOptions :: [gen_tcp:option()] . +%%-------------------------------------------------------------------- +set_sock_opts(ConnectionRef, SocketOptions) -> + ssh_connection_handler:set_sock_opts(ConnectionRef, SocketOptions). + +%%-------------------------------------------------------------------- +-spec get_sock_opts(ConnectionRef, SocketGetOptions) -> + ok | {error, inet:posix()} when + ConnectionRef :: connection_ref(), + SocketGetOptions :: [gen_tcp:option_name()] . +%%-------------------------------------------------------------------- +get_sock_opts(ConnectionRef, SocketGetOptions) -> + ssh_connection_handler:get_sock_opts(ConnectionRef, SocketGetOptions). + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl index 86c447f695..a9d81f7252 100644 --- a/lib/ssh/src/ssh.hrl +++ b/lib/ssh/src/ssh.hrl @@ -87,6 +87,9 @@ -define(Empint(X), (ssh_bits:mpint(X))/binary ). -define(Ebinary(X), ?STRING(X) ). +%% Other macros +-define(to_binary(X), (try iolist_to_binary(X) catch _:_ -> unicode:characters_to_binary(X) end) ). + %% Cipher details -define(SSH_CIPHER_NONE, 0). -define(SSH_CIPHER_3DES, 3). diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl index 11ce80354e..dae34f858f 100644 --- a/lib/ssh/src/ssh_acceptor.erl +++ b/lib/ssh/src/ssh_acceptor.erl @@ -33,7 +33,8 @@ %% spawn export -export([acceptor_init/5, acceptor_loop/6]). --export([dbg_trace/3]). +-behaviour(ssh_dbg). +-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]). -define(SLEEP_TIME, 200). @@ -202,18 +203,21 @@ handle_error(Reason) -> %%%# Tracing %%%# -dbg_trace(points, _, _) -> [connections]; +ssh_dbg_trace_points() -> [connections]. -dbg_trace(flags, connections, _) -> [c]; -dbg_trace(on, connections, _) -> dbg:tp(?MODULE, acceptor_init, 5, x), - dbg:tpl(?MODULE, handle_connection, 5, x); -dbg_trace(off, connections, _) -> dbg:ctp(?MODULE, acceptor_init, 5), - dbg:ctp(?MODULE, handle_connection, 5); -dbg_trace(format, connections, {call, {?MODULE,acceptor_init, - [_Parent, Port, Address, _Opts, _AcceptTimeout]}}) -> +ssh_dbg_flags(connections) -> [c]. + +ssh_dbg_on(connections) -> dbg:tp(?MODULE, acceptor_init, 5, x), + dbg:tpl(?MODULE, handle_connection, 5, x). + +ssh_dbg_off(connections) -> dbg:ctp(?MODULE, acceptor_init, 5), + dbg:ctp(?MODULE, handle_connection, 5). + +ssh_dbg_format(connections, {call, {?MODULE,acceptor_init, + [_Parent, Port, Address, _Opts, _AcceptTimeout]}}) -> [io_lib:format("Starting LISTENER on ~s:~p\n", [ntoa(Address),Port]) ]; -dbg_trace(format, connections, {return_from, {?MODULE,handle_connection,5}, {error,Error}}) -> +ssh_dbg_format(connections, {return_from, {?MODULE,handle_connection,5}, {error,Error}}) -> ["Starting connection to server failed:\n", io_lib:format("Error = ~p", [Error]) ]. diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 9632168e65..b9813b6b5c 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -462,7 +462,7 @@ check_password(User, Password, Opts, Ssh) -> case ?GET_OPT(pwdfun, Opts) of undefined -> Static = get_password_option(Opts, User), - {Password == Static, Ssh}; + {crypto:equal_const_time(Password,Static), Ssh}; Checker when is_function(Checker,2) -> {Checker(User, Password), Ssh}; diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl index 7f04fcb804..0345eaf0eb 100644 --- a/lib/ssh/src/ssh_cli.erl +++ b/lib/ssh/src/ssh_cli.erl @@ -33,7 +33,8 @@ %% ssh_server_channel callbacks -export([init/1, handle_ssh_msg/2, handle_msg/2, terminate/2]). --export([dbg_trace/3]). +-behaviour(ssh_dbg). +-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]). %% state -record(state, { @@ -651,12 +652,15 @@ not_zero(A, _) -> %%%# Tracing %%%# -dbg_trace(points, _, _) -> [terminate]; +ssh_dbg_trace_points() -> [terminate]. -dbg_trace(flags, terminate, _) -> [c]; -dbg_trace(on, terminate, _) -> dbg:tp(?MODULE, terminate, 2, x); -dbg_trace(off, terminate, _) -> dbg:ctpg(?MODULE, terminate, 2); -dbg_trace(format, terminate, {call, {?MODULE,terminate, [Reason, State]}}) -> +ssh_dbg_flags(terminate) -> [c]. + +ssh_dbg_on(terminate) -> dbg:tp(?MODULE, terminate, 2, x). + +ssh_dbg_off(terminate) -> dbg:ctpg(?MODULE, terminate, 2). + +ssh_dbg_format(terminate, {call, {?MODULE,terminate, [Reason, State]}}) -> ["Cli Terminating:\n", io_lib:format("Reason: ~p,~nState:~n~s", [Reason, wr_record(State)]) ]. diff --git a/lib/ssh/src/ssh_client_channel.erl b/lib/ssh/src/ssh_client_channel.erl index 3bd1e1fdf1..605f71b8ac 100644 --- a/lib/ssh/src/ssh_client_channel.erl +++ b/lib/ssh/src/ssh_client_channel.erl @@ -72,7 +72,8 @@ cache_info/2, cache_find/2, get_print_info/1]). --export([dbg_trace/3]). +-behaviour(ssh_dbg). +-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]). -record(state, { cm, @@ -388,64 +389,64 @@ adjust_window(_) -> %%%# Tracing %%%# -dbg_trace(points, _, _) -> [terminate, channels, channel_events]; +ssh_dbg_trace_points() -> [terminate, channels, channel_events]. +ssh_dbg_flags(channels) -> [c]; +ssh_dbg_flags(terminate) -> [c]; +ssh_dbg_flags(channel_events) -> [c]. -dbg_trace(flags, channels, A) -> [c] ++ dbg_trace(flags, terminate, A); -dbg_trace(on, channels, A) -> dbg:tp(?MODULE, init, 1, x), - dbg_trace(on, terminate, A); -dbg_trace(off, channels, A) -> dbg:ctpg(?MODULE, init, 1), - dbg_trace(off, terminate, A); -dbg_trace(format, channels, {call, {?MODULE,init, [[KVs]]}}) -> +ssh_dbg_on(terminate) -> dbg:tp(?MODULE, terminate, 2, x); +ssh_dbg_on(channels) -> dbg:tp(?MODULE, init, 1, x), + ssh_dbg_on(terminate); +ssh_dbg_on(channel_events) -> dbg:tp(?MODULE, handle_call, 3, x), + dbg:tp(?MODULE, handle_cast, 2, x), + dbg:tp(?MODULE, handle_info, 2, x). + +ssh_dbg_off(terminate) -> dbg:ctpg(?MODULE, terminate, 2); +ssh_dbg_off(channels) -> dbg:ctpg(?MODULE, init, 1), + ssh_dbg_off(terminate); +ssh_dbg_off(channel_events) -> dbg:ctpg(?MODULE, handle_call, 3), + dbg:ctpg(?MODULE, handle_cast, 2), + dbg:ctpg(?MODULE, handle_info, 2). + +ssh_dbg_format(channels, {call, {?MODULE,init, [[KVs]]}}) -> ["Server Channel Starting:\n", io_lib:format("Connection: ~p, ChannelId: ~p, CallBack: ~p\nCallBack init args = ~p", [proplists:get_value(K,KVs) || K <- [cm, channel_id, channel_cb]] ++ [channel_cb_init_args(KVs)]) ]; -dbg_trace(format, channels, {return_from, {?MODULE,init,1}, {stop,Reason}}) -> +ssh_dbg_format(channels, {return_from, {?MODULE,init,1}, {stop,Reason}}) -> ["Server Channel Start FAILED!\n", io_lib:format("Reason = ~p", [Reason]) ]; -dbg_trace(format, channels, F) -> - dbg_trace(format, terminate, F); - - -dbg_trace(flags, terminate, _) -> [c]; -dbg_trace(on, terminate, _) -> dbg:tp(?MODULE, terminate, 2, x); -dbg_trace(off, terminate, _) -> dbg:ctpg(?MODULE, terminate, 2); -dbg_trace(format, terminate, {call, {?MODULE,terminate, [Reason, State]}}) -> +ssh_dbg_format(channels, F) -> + ssh_dbg_format(terminate, F); +ssh_dbg_format(terminate, {call, {?MODULE,terminate, [Reason, State]}}) -> ["Server Channel Terminating:\n", io_lib:format("Reason: ~p,~nState:~n~s", [Reason, wr_record(State)]) ]; -dbg_trace(flags, channel_events, _) -> [c]; -dbg_trace(on, channel_events, _) -> dbg:tp(?MODULE, handle_call, 3, x), - dbg:tp(?MODULE, handle_cast, 2, x), - dbg:tp(?MODULE, handle_info, 2, x); -dbg_trace(off, channel_events, _) -> dbg:ctpg(?MODULE, handle_call, 3), - dbg:ctpg(?MODULE, handle_cast, 2), - dbg:ctpg(?MODULE, handle_info, 2); -dbg_trace(format, channel_events, {call, {?MODULE,handle_call, [Call,From,State]}}) -> +ssh_dbg_format(channel_events, {call, {?MODULE,handle_call, [Call,From,State]}}) -> [hdr("is called", State), io_lib:format("From: ~p~nCall: ~p~n", [From, Call]) ]; -dbg_trace(format, channel_events, {return_from, {?MODULE,handle_call,3}, Ret}) -> +ssh_dbg_format(channel_events, {return_from, {?MODULE,handle_call,3}, Ret}) -> ["Server Channel call returned:\n", io_lib:format("~p~n", [ssh_dbg:reduce_state(Ret)]) ]; -dbg_trace(format, channel_events, {call, {?MODULE,handle_cast, [Cast,State]}}) -> +ssh_dbg_format(channel_events, {call, {?MODULE,handle_cast, [Cast,State]}}) -> [hdr("got cast", State), io_lib:format("Cast: ~p~n", [Cast]) ]; -dbg_trace(format, channel_events, {return_from, {?MODULE,handle_cast,2}, Ret}) -> +ssh_dbg_format(channel_events, {return_from, {?MODULE,handle_cast,2}, Ret}) -> ["Server Channel cast returned:\n", io_lib:format("~p~n", [ssh_dbg:reduce_state(Ret)]) ]; -dbg_trace(format, channel_events, {call, {?MODULE,handle_info, [Info,State]}}) -> +ssh_dbg_format(channel_events, {call, {?MODULE,handle_info, [Info,State]}}) -> [hdr("got info", State), io_lib:format("Info: ~p~n", [Info]) ]; -dbg_trace(format, channel_events, {return_from, {?MODULE,handle_info,2}, Ret}) -> +ssh_dbg_format(channel_events, {return_from, {?MODULE,handle_info,2}, Ret}) -> ["Server Channel info returned:\n", io_lib:format("~p~n", [ssh_dbg:reduce_state(Ret)]) ]. diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl index 73aeda8efc..380faeb11e 100644 --- a/lib/ssh/src/ssh_connection.erl +++ b/lib/ssh/src/ssh_connection.erl @@ -417,12 +417,7 @@ channel_data(ChannelId, DataType, Data0, From) -> case ssh_client_channel:cache_lookup(Cache, ChannelId) of #channel{remote_id = Id, sent_close = false} = Channel0 -> - Data = - try iolist_to_binary(Data0) - catch - _:_ -> - unicode:characters_to_binary(Data0) - end, + Data = ?to_binary(Data0), {SendList, Channel} = update_send_window(Channel0#channel{flow_control = From}, DataType, Data, Connection), diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index ad5a54a2e2..e8c0d88e59 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -57,7 +57,8 @@ channel_info/3, adjust_window/3, close/2, disconnect/4, - get_print_info/1 + get_print_info/1, + set_sock_opts/2, get_sock_opts/2 ]). -type connection_ref() :: ssh:connection_ref(). @@ -74,7 +75,8 @@ renegotiate/1, alg/1 % Export intended for test cases ]). --export([dbg_trace/3]). +-behaviour(ssh_dbg). +-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]). -define(send_disconnect(Code, DetailedText, StateName, State), @@ -320,6 +322,31 @@ close(ConnectionHandler, ChannelId) -> ok end. + +%%-------------------------------------------------------------------- +%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +set_sock_opts(ConnectionRef, SocketOptions) -> + try lists:foldr(fun({Name,_Val}, Acc) -> + case lists:member(Name, [active, deliver, mode, packet]) of + true -> [Name|Acc]; + false -> Acc + end + end, [], SocketOptions) + of + [] -> + call(ConnectionRef, {set_sock_opts,SocketOptions}); + Bad -> + {error, {not_allowed,Bad}} + catch + _:_ -> + {error, badarg} + end. + +%%-------------------------------------------------------------------- +%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +get_sock_opts(ConnectionRef, SocketGetOptions) -> + call(ConnectionRef, {get_sock_opts,SocketGetOptions}). + %%==================================================================== %% Test support %%==================================================================== @@ -1204,6 +1231,20 @@ handle_event({call,From}, {info, ChannelPid}, _, D) -> end, [], cache(D)), {keep_state_and_data, [{reply, From, {ok,Result}}]}; +handle_event({call,From}, {set_sock_opts,SocketOptions}, _StateName, D) -> + Result = try inet:setopts(D#data.socket, SocketOptions) + catch + _:_ -> {error, badarg} + end, + {keep_state_and_data, [{reply,From,Result}]}; + +handle_event({call,From}, {get_sock_opts,SocketGetOptions}, _StateName, D) -> + Result = try inet:getopts(D#data.socket, SocketGetOptions) + catch + _:_ -> {error, badarg} + end, + {keep_state_and_data, [{reply,From,Result}]}; + handle_event({call,From}, stop, _StateName, D0) -> {Repls,D} = send_replies(ssh_connection:handle_stop(D0#data.connection_state), D0), {stop_and_reply, normal, [{reply,From,ok}|Repls], D}; @@ -2284,14 +2325,37 @@ update_inet_buffers(Socket) -> %%%# Tracing %%%# -dbg_trace(points, _, _) -> [terminate, disconnect, connections, connection_events, renegotiation]; - -dbg_trace(flags, connections, A) -> [c] ++ dbg_trace(flags, terminate, A); -dbg_trace(on, connections, A) -> dbg:tp(?MODULE, init_connection_handler, 3, x), - dbg_trace(on, terminate, A); -dbg_trace(off, connections, A) -> dbg:ctpg(?MODULE, init_connection_handler, 3), - dbg_trace(off, terminate, A); -dbg_trace(format, connections, {call, {?MODULE,init_connection_handler, [Role, Sock, Opts]}}) -> +ssh_dbg_trace_points() -> [terminate, disconnect, connections, connection_events, renegotiation]. + +ssh_dbg_flags(connections) -> [c | ssh_dbg_flags(terminate)]; +ssh_dbg_flags(renegotiation) -> [c]; +ssh_dbg_flags(connection_events) -> [c]; +ssh_dbg_flags(terminate) -> [c]; +ssh_dbg_flags(disconnect) -> [c]. + +ssh_dbg_on(connections) -> dbg:tp(?MODULE, init_connection_handler, 3, x), + ssh_dbg_on(terminate); +ssh_dbg_on(connection_events) -> dbg:tp(?MODULE, handle_event, 4, x); +ssh_dbg_on(renegotiation) -> dbg:tpl(?MODULE, init_renegotiate_timers, 2, x), + dbg:tpl(?MODULE, pause_renegotiate_timers, 2, x), + dbg:tpl(?MODULE, check_data_rekeying_dbg, 2, x), + dbg:tpl(?MODULE, start_rekeying, 2, x); +ssh_dbg_on(terminate) -> dbg:tp(?MODULE, terminate, 3, x); +ssh_dbg_on(disconnect) -> dbg:tpl(?MODULE, send_disconnect, 7, x). + + +ssh_dbg_off(disconnect) -> dbg:ctpl(?MODULE, send_disconnect, 7); +ssh_dbg_off(terminate) -> dbg:ctpg(?MODULE, terminate, 3); +ssh_dbg_off(renegotiation) -> dbg:ctpl(?MODULE, init_renegotiate_timers, 2), + dbg:ctpl(?MODULE, pause_renegotiate_timers, 2), + dbg:ctpl(?MODULE, check_data_rekeying_dbg, 2), + dbg:ctpl(?MODULE, start_rekeying, 2); +ssh_dbg_off(connection_events) -> dbg:ctpg(?MODULE, handle_event, 4); +ssh_dbg_off(connections) -> dbg:ctpg(?MODULE, init_connection_handler, 3), + ssh_dbg_off(terminate). + + +ssh_dbg_format(connections, {call, {?MODULE,init_connection_handler, [Role, Sock, Opts]}}) -> DefaultOpts = ssh_options:handle_options(Role,[]), ExcludedKeys = [internal_options, user_options], NonDefaultOpts = @@ -2312,41 +2376,29 @@ dbg_trace(format, connections, {call, {?MODULE,init_connection_handler, [Role, S [Sock,inet:ntoa(IPp),Portp,inet:ntoa(IPs),Ports, NonDefaultOpts]) ]; -dbg_trace(format, connections, F) -> - dbg_trace(format, terminate, F); +ssh_dbg_format(connections, F) -> + ssh_dbg_format(terminate, F); -dbg_trace(flags, connection_events, _) -> [c]; -dbg_trace(on, connection_events, _) -> dbg:tp(?MODULE, handle_event, 4, x); -dbg_trace(off, connection_events, _) -> dbg:ctpg(?MODULE, handle_event, 4); -dbg_trace(format, connection_events, {call, {?MODULE,handle_event, [EventType, EventContent, State, _Data]}}) -> +ssh_dbg_format(connection_events, {call, {?MODULE,handle_event, [EventType, EventContent, State, _Data]}}) -> ["Connection event\n", io_lib:format("EventType: ~p~nEventContent: ~p~nState: ~p~n", [EventType, EventContent, State]) ]; -dbg_trace(format, connection_events, {return_from, {?MODULE,handle_event,4}, Ret}) -> +ssh_dbg_format(connection_events, {return_from, {?MODULE,handle_event,4}, Ret}) -> ["Connection event result\n", io_lib:format("~p~n", [event_handler_result(Ret)]) ]; -dbg_trace(flags, renegotiation, _) -> [c]; -dbg_trace(on, renegotiation, _) -> dbg:tpl(?MODULE, init_renegotiate_timers, 2, x), - dbg:tpl(?MODULE, pause_renegotiate_timers, 2, x), - dbg:tpl(?MODULE, check_data_rekeying_dbg, 2, x), - dbg:tpl(?MODULE, start_rekeying, 2, x); -dbg_trace(off, renegotiation, _) -> dbg:ctpl(?MODULE, init_renegotiate_timers, 2), - dbg:ctpl(?MODULE, pause_renegotiate_timers, 2), - dbg:ctpl(?MODULE, check_data_rekeying_dbg, 2), - dbg:ctpl(?MODULE, start_rekeying, 2); -dbg_trace(format, renegotiation, {call, {?MODULE,init_renegotiate_timers,[_State,D]}}) -> +ssh_dbg_format(renegotiation, {call, {?MODULE,init_renegotiate_timers,[_State,D]}}) -> ["Renegotiation init\n", io_lib:format("rekey_limit: ~p ({ms,bytes})~ncheck_data_size: ~p (ms)~n", [?GET_OPT(rekey_limit, (D#data.ssh_params)#ssh.opts), ?REKEY_DATA_TIMOUT]) ]; -dbg_trace(format, renegotiation, {call, {?MODULE,pause_renegotiate_timers,[_State,_D]}}) -> +ssh_dbg_format(renegotiation, {call, {?MODULE,pause_renegotiate_timers,[_State,_D]}}) -> ["Renegotiation pause\n"]; -dbg_trace(format, renegotiation, {call, {?MODULE,start_rekeying,[_Role,_D]}}) -> +ssh_dbg_format(renegotiation, {call, {?MODULE,start_rekeying,[_Role,_D]}}) -> ["Renegotiation start rekeying\n"]; -dbg_trace(format, renegotiation, {call, {?MODULE,check_data_rekeying_dbg,[SentSinceRekey, MaxSent]}}) -> +ssh_dbg_format(renegotiation, {call, {?MODULE,check_data_rekeying_dbg,[SentSinceRekey, MaxSent]}}) -> ["Renegotiation check data sent\n", io_lib:format("TotalSentSinceRekey: ~p~nMaxBeforeRekey: ~p~nStartRekey: ~p~n", [SentSinceRekey, MaxSent, SentSinceRekey >= MaxSent]) @@ -2354,10 +2406,7 @@ dbg_trace(format, renegotiation, {call, {?MODULE,check_data_rekeying_dbg,[SentSi -dbg_trace(flags, terminate, _) -> [c]; -dbg_trace(on, terminate, _) -> dbg:tp(?MODULE, terminate, 3, x); -dbg_trace(off, terminate, _) -> dbg:ctpg(?MODULE, terminate, 3); -dbg_trace(format, terminate, {call, {?MODULE,terminate, [Reason, StateName, D]}}) -> +ssh_dbg_format(terminate, {call, {?MODULE,terminate, [Reason, StateName, D]}}) -> ExtraInfo = try {conn_info(peer,D), @@ -2390,11 +2439,8 @@ dbg_trace(format, terminate, {call, {?MODULE,terminate, [Reason, StateName, D]}} ] end; -dbg_trace(flags, disconnect, _) -> [c]; -dbg_trace(on, disconnect, _) -> dbg:tpl(?MODULE, send_disconnect, 7, x); -dbg_trace(off, disconnect, _) -> dbg:ctpl(?MODULE, send_disconnect, 7); -dbg_trace(format, disconnect, {call,{?MODULE,send_disconnect, - [Code, Reason, DetailedText, Module, Line, StateName, _D]}}) -> +ssh_dbg_format(disconnect, {call,{?MODULE,send_disconnect, + [Code, Reason, DetailedText, Module, Line, StateName, _D]}}) -> ["Disconnecting:\n", io_lib:format(" Module = ~p, Line = ~p, StateName = ~p,~n" " Code = ~p, Reason = ~p,~n" diff --git a/lib/ssh/src/ssh_dbg.erl b/lib/ssh/src/ssh_dbg.erl index 43ac4c0ccf..7b7f9909ac 100644 --- a/lib/ssh/src/ssh_dbg.erl +++ b/lib/ssh/src/ssh_dbg.erl @@ -80,6 +80,15 @@ -define(CALL_TIMEOUT, 15000). % 3x the default +-type trace_point() :: atom(). +-type trace_points() :: [trace_point()]. + +-callback ssh_dbg_trace_points() -> trace_points(). +-callback ssh_dbg_flags(trace_point()) -> [atom()]. +-callback ssh_dbg_on(trace_point() | trace_points()) -> term(). +-callback ssh_dbg_off(trace_point() | trace_points()) -> term(). +-callback ssh_dbg_format(trace_point(), term()) -> iolist(). + %%%================================================================ -define(ALL_DBG_TYPES, get_all_dbg_types()). @@ -170,13 +179,13 @@ init(_) -> %%%---------------------------------------------------------------- handle_call({switch,on,Types}, _From, D) -> NowOn = lists:usort(Types ++ D#data.types_on), - call_modules(on, Types, NowOn), + call_modules(on, Types), {reply, {ok,NowOn}, D#data{types_on = NowOn}}; handle_call({switch,off,Types}, _From, D) -> StillOn = D#data.types_on -- Types, - call_modules(off, Types, StillOn), - call_modules(on, StillOn, StillOn), + call_modules(off, Types), + call_modules(on, StillOn), {reply, {ok,StillOn}, D#data{types_on = StillOn}}; handle_call(get_on, _From, D) -> @@ -202,52 +211,54 @@ handle_info(C, D) -> ssh_modules_with_trace() -> {ok,AllSshModules} = application:get_key(ssh, modules), [M || M <- AllSshModules, - lists:member({dbg_trace,3}, M:module_info(exports))]. + {behaviour,Bs} <- M:module_info(attributes), + lists:member(?MODULE, Bs) + ]. %%%---------------------------------------------------------------- get_all_trace_flags() -> - get_all_trace_flags(ssh_modules_with_trace()). - -get_all_trace_flags(Modules) -> lists:usort( - lists:flatten( - lists:foldl( - fun(Type, Acc) -> - call_modules(flags, Type, undefined, Acc, Modules) - end, [timestamp], ?ALL_DBG_TYPES))). + lists:flatten([timestamp | call_modules(flags, ?ALL_DBG_TYPES)] + )). %%%---------------------------------------------------------------- get_all_dbg_types() -> lists:usort( lists:flatten( - call_modules(points, undefined) )). + call_modules(points) )). %%%---------------------------------------------------------------- -call_modules(Cmnd, Type) -> - call_modules(Cmnd, Type, undefined). - -call_modules(Cmnd, Type, Arg) -> - call_modules(Cmnd, Type, Arg, []). +call_modules(points) -> + F = fun(Mod) -> Mod:ssh_dbg_trace_points() end, + fold_modules(F, [], ssh_modules_with_trace()). + +call_modules(Cmnd, Types) when is_list(Types) -> + F = case Cmnd of + flags -> fun(Type) -> + fun(Mod) -> Mod:ssh_dbg_flags(Type) end + end; + on -> fun(Type) -> + fun(Mod) -> Mod:ssh_dbg_on(Type) end + end; + off -> fun(Type) -> + fun(Mod) -> Mod:ssh_dbg_off(Type) end + end + end, + lists:foldl(fun(T, Acc) -> + fold_modules(F(T), Acc, ssh_modules_with_trace()) + end, [], Types). -call_modules(Cmnd, Type, Arg, Acc0) -> - call_modules(Cmnd, Type, Arg, Acc0, ssh_modules_with_trace()). -call_modules(Cmnd, Types, Arg, Acc0, Modules) when is_list(Types) -> - lists:foldl( - fun(Type, Acc) -> - call_modules(Cmnd, Type, Arg, Acc, Modules) - end, Acc0, Types); -call_modules(Cmnd, Type, Arg, Acc0, Modules) -> - lists:foldl( - fun(Mod, Acc) -> - try Mod:dbg_trace(Cmnd, Type, Arg) - of - Result -> [Result|Acc] - catch - _:_ -> Acc - end - end, Acc0, Modules). +fold_modules(F, Acc0, Modules) -> + lists:foldl( + fun(Mod, Acc) -> + try F(Mod) of + Result -> [Result|Acc] + catch + _:_ -> Acc + end + end, Acc0, Modules). %%%---------------------------------------------------------------- switch(X, Type) when is_atom(Type) -> @@ -314,7 +325,9 @@ try_all_types_in_all_modules(TypesOn, Arg, WriteFun, Acc0) -> lists:foldl( fun(SshMod,Acc) -> try WriteFun("~n~s ~p ~s~n", - [lists:flatten(TS), PID, lists:flatten(SshMod:dbg_trace(format,Type,INFO))], + [lists:flatten(TS), + PID, + lists:flatten(SshMod:ssh_dbg_format(Type, INFO))], Acc) catch _:_ -> Acc diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl index 7c86a81108..47cbec1513 100644 --- a/lib/ssh/src/ssh_message.erl +++ b/lib/ssh/src/ssh_message.erl @@ -32,7 +32,9 @@ -export([encode/1, decode/1, decode_keyboard_interactive_prompts/2]). --export([dbg_trace/3]). +-behaviour(ssh_dbg). +-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]). + ucl(B) -> try unicode:characters_to_list(B) of @@ -606,34 +608,36 @@ encode_signature({ed_pub, ed448,_}, _SigAlg, Signature) -> %%%# Tracing %%%# -dbg_trace(points, _, _) -> [ssh_messages, raw_messages]; +ssh_dbg_trace_points() -> [ssh_messages, raw_messages]. + +ssh_dbg_flags(ssh_messages) -> [c]; +ssh_dbg_flags(raw_messages) -> [c]. -dbg_trace(flags, ssh_messages, _) -> [c]; -dbg_trace(on, ssh_messages, _) -> dbg:tp(?MODULE,encode,1,x), - dbg:tp(?MODULE,decode,1,x); -dbg_trace(off, ssh_messages, _) -> dbg:ctpg(?MODULE,encode,1), - dbg:ctpg(?MODULE,decode,1); +ssh_dbg_on(P) when P==ssh_messages ; + P==raw_messages -> + dbg:tp(?MODULE,encode,1,x), + dbg:tp(?MODULE,decode,1,x). -dbg_trace(flags, raw_messages, A) -> dbg_trace(flags, ssh_messages, A); -dbg_trace(on, raw_messages, A) -> dbg_trace(on, ssh_messages, A); -dbg_trace(off, raw_messages, A) -> dbg_trace(off, ssh_messages, A); +ssh_dbg_off(P) when P==ssh_messages ; + P==raw_messages -> + dbg:ctpg(?MODULE,encode,1), + dbg:ctpg(?MODULE,decode,1). -dbg_trace(format, ssh_messages, {call,{?MODULE,encode,[Msg]}}) -> +ssh_dbg_format(ssh_messages, {call,{?MODULE,encode,[Msg]}}) -> Name = string:to_upper(atom_to_list(element(1,Msg))), ["Going to send ",Name,":\n", wr_record(ssh_dbg:shrink_bin(Msg)) ]; -dbg_trace(format, ssh_messages, {return_from,{?MODULE,decode,1},Msg}) -> +ssh_dbg_format(ssh_messages, {return_from,{?MODULE,decode,1},Msg}) -> Name = string:to_upper(atom_to_list(element(1,Msg))), ["Received ",Name,":\n", wr_record(ssh_dbg:shrink_bin(Msg)) ]; - -dbg_trace(format, raw_messages, {call,{?MODULE,decode,[BytesPT]}}) -> +ssh_dbg_format(raw_messages, {call,{?MODULE,decode,[BytesPT]}}) -> ["Received plain text bytes (shown after decryption):\n", io_lib:format("~p",[BytesPT]) ]; -dbg_trace(format, raw_messages, {return_from,{?MODULE,encode,1},BytesPT}) -> +ssh_dbg_format(raw_messages, {return_from,{?MODULE,encode,1},BytesPT}) -> ["Going to send plain text bytes (shown before encryption):\n", io_lib:format("~p",[BytesPT]) ]. diff --git a/lib/ssh/src/ssh_sftp.erl b/lib/ssh/src/ssh_sftp.erl index 28df85dfa4..11d48bb1e5 100644 --- a/lib/ssh/src/ssh_sftp.erl +++ b/lib/ssh/src/ssh_sftp.erl @@ -52,7 +52,8 @@ %% TODO: Should be placed elsewhere ssh_sftpd should not call functions in ssh_sftp! -export([info_to_attr/1, attr_to_info/1]). --export([dbg_trace/3]). +-behaviour(ssh_dbg). +-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]). -record(state, { @@ -818,7 +819,7 @@ write_file(Pid, Name, List) -> Timeout :: timeout(), Error :: {error, reason()}. write_file(Pid, Name, List, FileOpTimeout) when is_list(List) -> - write_file(Pid, Name, list_to_binary(List), FileOpTimeout); + write_file(Pid, Name, to_bin(List), FileOpTimeout); write_file(Pid, Name, Bin, FileOpTimeout) -> case open(Pid, Name, [write, binary], FileOpTimeout) of {ok, Handle} -> @@ -1621,7 +1622,7 @@ lseek_pos(_, _, _) -> %%%================================================================ %%% -to_bin(Data) when is_list(Data) -> list_to_binary(Data); +to_bin(Data) when is_list(Data) -> ?to_binary(Data); to_bin(Data) when is_binary(Data) -> Data. @@ -1833,12 +1834,15 @@ format_channel_start_error(Reason) -> %%%# Tracing %%%# -dbg_trace(points, _, _) -> [terminate]; +ssh_dbg_trace_points() -> [terminate]. -dbg_trace(flags, terminate, _) -> [c]; -dbg_trace(on, terminate, _) -> dbg:tp(?MODULE, terminate, 2, x); -dbg_trace(off, terminate, _) -> dbg:ctpg(?MODULE, terminate, 2); -dbg_trace(format, terminate, {call, {?MODULE,terminate, [Reason, State]}}) -> +ssh_dbg_flags(terminate) -> [c]. + +ssh_dbg_on(terminate) -> dbg:tp(?MODULE, terminate, 2, x). + +ssh_dbg_off(terminate) -> dbg:ctpg(?MODULE, terminate, 2). + +ssh_dbg_format(terminate, {call, {?MODULE,terminate, [Reason, State]}}) -> ["Sftp Terminating:\n", io_lib:format("Reason: ~p,~nState:~n~s", [Reason, wr_record(State)]) ]. diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl index bf921f0ff3..b8dc905d4d 100644 --- a/lib/ssh/src/ssh_sftpd.erl +++ b/lib/ssh/src/ssh_sftpd.erl @@ -38,7 +38,9 @@ -export([init/1, handle_ssh_msg/2, handle_msg/2, terminate/2]). --export([dbg_trace/3]). +-behaviour(ssh_dbg). +-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]). + -record(state, { xf, % [{channel,ssh_xfer states}...] @@ -950,12 +952,15 @@ maybe_increase_recv_window(ConnectionManager, ChannelId, Options) -> %%%# Tracing %%%# -dbg_trace(points, _, _) -> [terminate]; +ssh_dbg_trace_points() -> [terminate]. + +ssh_dbg_flags(terminate) -> [c]. + +ssh_dbg_on(terminate) -> dbg:tp(?MODULE, terminate, 2, x). + +ssh_dbg_off(terminate) -> dbg:ctpg(?MODULE, terminate, 2). -dbg_trace(flags, terminate, _) -> [c]; -dbg_trace(on, terminate, _) -> dbg:tp(?MODULE, terminate, 2, x); -dbg_trace(off, terminate, _) -> dbg:ctpg(?MODULE, terminate, 2); -dbg_trace(format, terminate, {call, {?MODULE,terminate, [Reason, State]}}) -> +ssh_dbg_format(terminate, {call, {?MODULE,terminate, [Reason, State]}}) -> ["SftpD Terminating:\n", io_lib:format("Reason: ~p,~nState:~n~s", [Reason, wr_record(State)]) ]. diff --git a/lib/ssh/src/ssh_shell.erl b/lib/ssh/src/ssh_shell.erl index cdc9a6df5b..39329875d8 100644 --- a/lib/ssh/src/ssh_shell.erl +++ b/lib/ssh/src/ssh_shell.erl @@ -35,7 +35,8 @@ %% Spawn export -export([input_loop/2]). --export([dbg_trace/3]). +-behaviour(ssh_dbg). +-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]). -record(state, { @@ -188,12 +189,15 @@ get_ancestors() -> %%%# Tracing %%%# -dbg_trace(points, _, _) -> [terminate]; +ssh_dbg_trace_points() -> [terminate]. -dbg_trace(flags, terminate, _) -> [c]; -dbg_trace(on, terminate, _) -> dbg:tp(?MODULE, terminate, 2, x); -dbg_trace(off, terminate, _) -> dbg:ctpg(?MODULE, terminate, 2); -dbg_trace(format, terminate, {call, {?MODULE,terminate, [Reason, State]}}) -> +ssh_dbg_flags(terminate) -> [c]. + +ssh_dbg_on(terminate) -> dbg:tp(?MODULE, terminate, 2, x). + +ssh_dbg_off(terminate) -> dbg:ctpg(?MODULE, terminate, 2). + +ssh_dbg_format(terminate, {call, {?MODULE,terminate, [Reason, State]}}) -> ["Shell Terminating:\n", io_lib:format("Reason: ~p,~nState:~n~s", [Reason, wr_record(State)]) ]. diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index d1a3d513d1..9a4ae5bbc6 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -55,7 +55,8 @@ get_host_key/2, call_KeyCb/3]). --export([dbg_trace/3]). +-behaviour(ssh_dbg). +-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]). %%% For test suites -export([pack/3, adjust_algs_for_peer_version/2]). @@ -233,7 +234,7 @@ is_valid_mac(_, _ , #ssh{recv_mac_size = 0}) -> true; is_valid_mac(Mac, Data, #ssh{recv_mac = Algorithm, recv_mac_key = Key, recv_sequence = SeqNum}) -> - Mac == mac(Algorithm, Key, SeqNum, Data). + crypto:equal_const_time(Mac, mac(Algorithm, Key, SeqNum, Data)). format_version({Major,Minor}, SoftwareVersion) -> "SSH-" ++ integer_to_list(Major) ++ "." ++ @@ -1097,9 +1098,21 @@ alg_final(rcv, SSH0) -> select_all(CL, SL) when length(CL) + length(SL) < ?MAX_NUM_ALGORITHMS -> - A = CL -- SL, %% algortihms only used by client + %% algortihms only used by client + %% NOTE: an algorithm occuring more than once in CL will still be present + %% in CLonly. This is not a problem for nice clients. + CLonly = CL -- SL, + %% algorithms used by client and server (client pref) - lists:map(fun(ALG) -> list_to_atom(ALG) end, (CL -- A)); + lists:foldr(fun(ALG, Acc) -> + try [list_to_existing_atom(ALG) | Acc] + catch + %% If an malicious client uses the same non-existing algorithm twice, + %% we will end up here + _:_ -> Acc + end + end, [], (CL -- CLonly)); + select_all(CL, SL) -> Error = lists:concat(["Received too many algorithms (",length(CL),"+",length(SL)," >= ",?MAX_NUM_ALGORITHMS,")."]), ?DISCONNECT(?SSH_DISCONNECT_PROTOCOL_ERROR, @@ -1548,7 +1561,7 @@ decrypt(#ssh{decrypt = 'chacha20-poly1305@openssh.com', %% The length is already decrypted and used to divide the input %% Check the mac (important that it is timing-safe): PolyKey = crypto:crypto_one_time(chacha20, K2, <<0:8/unit:8,Seq:8/unit:8>>, <<0:32/unit:8>>, false), - case equal_const_time(Ctag, crypto:poly1305(PolyKey, <<AAD/binary,Ctext/binary>>)) of + case crypto:equal_const_time(Ctag, crypto:poly1305(PolyKey, <<AAD/binary,Ctext/binary>>)) of true -> %% MAC is ok, decode IV2 = <<1:8/little-unit:8, Seq:8/unit:8>>, @@ -1800,7 +1813,10 @@ valid_key_sha_alg(_, _) -> false. valid_key_sha_alg_ec(OID, Alg) -> Curve = public_key:oid2ssh_curvename(OID), - Alg == list_to_atom("ecdsa-sha2-" ++ binary_to_list(Curve)). + try Alg == list_to_existing_atom("ecdsa-sha2-" ++ binary_to_list(Curve)) + catch + _:_ -> false + end. -dialyzer({no_match, public_algo/1}). @@ -1811,7 +1827,10 @@ public_algo({ed_pub, ed25519,_}) -> 'ssh-ed25519'; public_algo({ed_pub, ed448,_}) -> 'ssh-ed448'; public_algo({#'ECPoint'{},{namedCurve,OID}}) -> Curve = public_key:oid2ssh_curvename(OID), - list_to_atom("ecdsa-sha2-" ++ binary_to_list(Curve)). + try list_to_existing_atom("ecdsa-sha2-" ++ binary_to_list(Curve)) + catch + _:_ -> undefined + end. sha('ssh-rsa') -> sha; @@ -1845,7 +1864,7 @@ sha('curve25519-sha256@libssh.org' ) -> sha256; sha('curve448-sha512') -> sha512; sha(x25519) -> sha256; sha(x448) -> sha512; -sha(Str) when is_list(Str), length(Str)<50 -> sha(list_to_atom(Str)). +sha(Str) when is_list(Str), length(Str)<50 -> sha(list_to_existing_atom(Str)). mac_key_bytes('hmac-sha1') -> 20; @@ -1953,18 +1972,6 @@ same(Algs) -> [{client2server,Algs}, {server2client,Algs}]. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Compare two binaries in a timing safe maner. -%%% The time spent in comparing should not be different depending on where in the binaries they differ. -%%% This is to avoid a certain side-channel attac. -equal_const_time(X1, X2) -> equal_const_time(X1, X2, true). - -equal_const_time(<<B1,R1/binary>>, <<B2,R2/binary>>, Truth) -> - equal_const_time(R1, R2, Truth and (B1 == B2)); -equal_const_time(<<>>, <<>>, Truth) -> - Truth; -equal_const_time(_, _, _) -> - false. - %%%-------- Remove CR, LF and following characters from a line trim_tail(Str) -> @@ -1977,34 +1984,43 @@ trim_tail(Str) -> %%%# Tracing %%%# -dbg_trace(points, _, _) -> [alg, ssh_messages, raw_messages, hello]; +ssh_dbg_trace_points() -> [alg, ssh_messages, raw_messages, hello]. + +ssh_dbg_flags(alg) -> [c]; +ssh_dbg_flags(hello) -> [c]; +ssh_dbg_flags(raw_messages) -> ssh_dbg_flags(hello); +ssh_dbg_flags(ssh_messages) -> ssh_dbg_flags(hello). + + +ssh_dbg_on(alg) -> dbg:tpl(?MODULE,select_algorithm,4,x); +ssh_dbg_on(hello) -> dbg:tp(?MODULE,hello_version_msg,1,x), + dbg:tp(?MODULE,handle_hello_version,1,x); +ssh_dbg_on(raw_messages) -> ssh_dbg_on(hello); +ssh_dbg_on(ssh_messages) -> ssh_dbg_on(hello). -dbg_trace(flags, hello, _) -> [c]; -dbg_trace(on, hello, _) -> dbg:tp(?MODULE,hello_version_msg,1,x), - dbg:tp(?MODULE,handle_hello_version,1,x); -dbg_trace(off, hello, _) -> dbg:ctpg(?MODULE,hello_version_msg,1), - dbg:ctpg(?MODULE,handle_hello_version,1); -dbg_trace(C, raw_messages, A) -> dbg_trace(C, hello, A); -dbg_trace(C, ssh_messages, A) -> dbg_trace(C, hello, A); +ssh_dbg_off(alg) -> dbg:ctpl(?MODULE,select_algorithm,4); +ssh_dbg_off(hello) -> dbg:ctpg(?MODULE,hello_version_msg,1), + dbg:ctpg(?MODULE,handle_hello_version,1); +ssh_dbg_off(raw_messages) -> ssh_dbg_off(hello); +ssh_dbg_off(ssh_messages) -> ssh_dbg_off(hello). -dbg_trace(flags, alg, _) -> [c]; -dbg_trace(on, alg, _) -> dbg:tpl(?MODULE,select_algorithm,4,x); -dbg_trace(off, alg, _) -> dbg:ctpl(?MODULE,select_algorithm,4); -dbg_trace(format, hello, {return_from,{?MODULE,hello_version_msg,1},Hello}) -> + +ssh_dbg_format(hello, {return_from,{?MODULE,hello_version_msg,1},Hello}) -> ["Going to send hello message:\n", Hello ]; -dbg_trace(format, hello, {call,{?MODULE,handle_hello_version,[Hello]}}) -> +ssh_dbg_format(hello, {call,{?MODULE,handle_hello_version,[Hello]}}) -> ["Received hello message:\n", Hello ]; - -dbg_trace(format, alg, {return_from,{?MODULE,select_algorithm,4},{ok,Alg}}) -> +ssh_dbg_format(alg, {return_from,{?MODULE,select_algorithm,4},{ok,Alg}}) -> ["Negotiated algorithms:\n", wr_record(Alg) - ]. + ]; +ssh_dbg_format(raw_messages, X) -> ssh_dbg_format(hello, X); +ssh_dbg_format(ssh_messages, X) -> ssh_dbg_format(hello, X). ?wr_record(alg). diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl index b071b38be2..2292beaf68 100644 --- a/lib/ssh/src/ssh_xfer.erl +++ b/lib/ssh/src/ssh_xfer.erl @@ -93,7 +93,7 @@ write(XF,ReqID, Handle, Offset, Data) -> is_binary(Data) -> Data; is_list(Data) -> - unicode:characters_to_binary(Data) + ?to_binary(Data) end, xf_request(XF,?SSH_FXP_WRITE, [?uint32(ReqID), @@ -233,7 +233,7 @@ xf_request(XF, Op, Arg) -> is_binary(Arg) -> Arg; is_list(Arg) -> - list_to_binary(Arg) + ?to_binary(Arg) end, Size = 1+size(Data), ssh_connection:send(CM, Channel, [<<?UINT32(Size), Op, Data/binary>>]). @@ -243,7 +243,7 @@ xf_send_reply(#ssh_xfer{cm = CM, channel = Channel}, Op, Arg) -> is_binary(Arg) -> Arg; is_list(Arg) -> - list_to_binary(Arg) + ?to_binary(Arg) end, Size = 1 + size(Data), ssh_connection:send(CM, Channel, [<<?UINT32(Size), Op, Data/binary>>]). diff --git a/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl b/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl index f4b521356f..dfc94f830c 100644 --- a/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl +++ b/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl @@ -23,37 +23,7 @@ -compile(export_all). --proptest(eqc). --proptest([triq,proper]). - --ifndef(EQC). --ifndef(PROPER). --ifndef(TRIQ). --define(EQC,true). -%%-define(PROPER,true). -%%-define(TRIQ,true). --endif. --endif. --endif. - --ifdef(EQC). --include_lib("eqc/include/eqc.hrl"). --define(MOD_eqc,eqc). - --else. --ifdef(PROPER). --include_lib("proper/include/proper.hrl"). --define(MOD_eqc,proper). - --else. --ifdef(TRIQ). --define(MOD_eqc,triq). --include_lib("triq/include/triq.hrl"). - --endif. --endif. --endif. - +-include_lib("common_test/include/ct_property_test.hrl"). %%% Properties: diff --git a/lib/ssh/test/property_test/ssh_eqc_client_server.erl b/lib/ssh/test/property_test/ssh_eqc_client_server.erl index acb0faa0c7..4c890c9dd2 100644 --- a/lib/ssh/test/property_test/ssh_eqc_client_server.erl +++ b/lib/ssh/test/property_test/ssh_eqc_client_server.erl @@ -23,8 +23,6 @@ -compile(export_all). --proptest([proper]). - -ifndef(PROPER). -else. %% Only use proper @@ -35,10 +33,9 @@ %% However, with access to eqc it ought to be quite easy to re-enable eqc by %% studying the diff. --include_lib("proper/include/proper.hrl"). --define(MOD_eqc,proper). - -include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_property_test.hrl"). + %% Limit the testing time on CI server... this needs to be improved in % from total budget. -define(TESTINGTIME(Prop), eqc:testing_time(30,Prop)). @@ -102,20 +99,20 @@ %% To be called as eqc:quickcheck( ssh_eqc_client_server:prop_seq() ). prop_seq() -> error_logger:tty(false), - ?TESTINGTIME(do_prop_seq(?SSH_DIR)). + ?TESTINGTIME(do_prop_seq(?SSH_DIR,[])). %% To be called from a common_test test suite prop_seq(CT_Config) -> error_logger:tty(false), - do_prop_seq(full_path(?SSH_DIR, CT_Config)). + do_prop_seq(full_path(?SSH_DIR, CT_Config), CT_Config). -do_prop_seq(DataDir) -> +do_prop_seq(DataDir, CT_Config) -> setup_rsa(DataDir), ?FORALL(Cmds,commands(?MODULE), begin {H,Sf,Result} = run_commands(?MODULE,Cmds,[{data_dir,DataDir}]), - present_result(?MODULE, Cmds, {H,Sf,Result}, Result==ok) + ct_property_test:present_result(?MODULE, Cmds, {H,Sf,Result}, CT_Config, []) end). full_path(SSHdir, CT_Config) -> @@ -124,37 +121,37 @@ full_path(SSHdir, CT_Config) -> %%%---- prop_parallel() -> error_logger:tty(false), - ?TESTINGTIME(do_prop_parallel(?SSH_DIR)). + ?TESTINGTIME(do_prop_parallel(?SSH_DIR,[])). %% To be called from a common_test test suite prop_parallel(CT_Config) -> error_logger:tty(false), - do_prop_parallel(full_path(?SSH_DIR, CT_Config)). + do_prop_parallel(full_path(?SSH_DIR, CT_Config), CT_Config). -do_prop_parallel(DataDir) -> +do_prop_parallel(DataDir, CT_Config) -> setup_rsa(DataDir), ?FORALL(Cmds,parallel_commands(?MODULE), begin {H,Sf,Result} = run_parallel_commands(?MODULE,Cmds,[{data_dir,DataDir}]), - present_result(?MODULE, Cmds, {H,Sf,Result}, Result==ok) + ct_property_test:present_result(?MODULE, Cmds, {H,Sf,Result}, CT_Config, []) end). %%%---- %% prop_parallel_multi() -> -%% ?TESTINGTIME(do_prop_parallel_multi(?SSH_DIR)). +%% ?TESTINGTIME(do_prop_parallel_multi(?SSH_DIR, [])). %% %% To be called from a common_test test suite %% prop_parallel_multi(CT_Config) -> -%% do_prop_parallel_multi(full_path(?SSH_DIR, CT_Config)). +%% do_prop_parallel_multi(full_path(?SSH_DIR, CT_Config), CT_Config). -%% do_prop_parallel_multi(DataDir) -> +%% do_prop_parallel_multi(DataDir, CT_Config) -> %% setup_rsa(DataDir), %% ?FORALL(Repetitions,?SHRINK(1,[10]), %% ?FORALL(Cmds,parallel_commands(?MODULE), %% ?ALWAYS(Repetitions, %% begin %% {H,Sf,Result} = run_parallel_commands(?MODULE,Cmds,[{data_dir,DataDir}]), -%% present_result(?MODULE, Cmds, {H,Sf,Result}, Result==ok) +%% ct_property_test:present_result(?MODULE, Cmds, {H,Sf,Result}, CT_Config, []) %% end))). %%%================================================================ @@ -476,178 +473,6 @@ is_ok(_) -> true. ensure_string({A,B,C,D}) -> lists:flatten(io_lib:format("~w.~w.~w.~w",[A,B,C,D])); ensure_string(X) -> X. -%%%---------------------------------------------------------------- -present_result(_Module, Cmds, _Triple, true) -> - aggregate(with_title("Distribution sequential/parallel"), sequential_parallel(Cmds), - aggregate(with_title("Function calls"), cmnd_names(Cmds), - aggregate(with_title("Message sizes"), empty_msgs(Cmds), - aggregate(print_frequencies(), message_sizes(Cmds), - aggregate(title("Length of command sequences",print_frequencies()), num_calls(Cmds), - true))))); - -present_result(Module, Cmds, Triple, false) -> - pretty_comands(Module, Cmds, Triple, [{show_states,true}], false), - false. % Proper dislikes non-boolean results while eqc treats non-true as false. - -pretty_comands(Module, Cmds, Triple, Opts, Bool) -> - ct:log("Module = ~p,~n Cmds = ~p,~n Triple = ~p,~n Opts = ~p,~n Bool = ~p",[Module, Cmds, Triple, Opts, Bool]). - - - -cmnd_names(Cs) -> traverse_commands(fun cmnd_name/1, Cs). -cmnd_name(L) -> [F || {set,_Var,{call,_Mod,F,_As}} <- L]. - -empty_msgs(Cs) -> traverse_commands(fun empty_msg/1, Cs). -empty_msg(L) -> [empty || {set,_,{call,_,ssh_send,[_,_,Msg]}} <- L, - size(Msg)==0]. - -message_sizes(Cs) -> traverse_commands(fun message_size/1, Cs). -message_size(L) -> [size(Msg) || {set,_,{call,_,ssh_send,[_,_,Msg]}} <- L]. - -num_calls(Cs) -> traverse_commands(fun num_call/1, Cs). -num_call(L) -> [length(L)]. - -sequential_parallel(Cs) -> - traverse_commands(fun(L) -> dup_module(L, sequential) end, - fun(L) -> [dup_module(L1, mkmod("parallel",num(L1,L))) || L1<-L] end, - Cs). -dup_module(L, ModName) -> lists:duplicate(length(L), ModName). -mkmod(PfxStr,N) -> list_to_atom(PfxStr++"_"++integer_to_list(N)). - -%% Meta functions for the aggregate functions -traverse_commands(Fun, L) when is_list(L) -> Fun(L); -traverse_commands(Fun, {Seq, ParLs}) -> Fun(lists:append([Seq|ParLs])). - -traverse_commands(Fseq, _Fpar, L) when is_list(L) -> Fseq(L); -traverse_commands(Fseq, Fpar, {Seq, ParLs}) -> lists:append([Fseq(Seq)|Fpar(ParLs)]). - -%%%---------------- -%% PrintMethod([{term(), int()}]) -> any(). -print_frequencies() -> print_frequencies(10). - -print_frequencies(Ngroups) -> fun([]) -> io:format('Empty list!~n',[]); - (L ) -> - try - M = lists:last(L), - Max = if is_integer(M) -> M; - is_tuple(M) -> element(1,L) - end, - print_frequencies(L,Ngroups,0,Max) - catch - C:E:S -> - ct:pal("~p:~p ~p:~p~n~p~n~p",[?MODULE,?LINE,C,E,S,L]) - end - end. - - -print_frequencies(Ngroups, MaxValue) -> fun(L) -> print_frequencies(L,Ngroups,0,MaxValue) end. - -print_frequencies(L, N, Min, Max) when N>Max -> print_frequencies(L++[{N,0}], N, Min, N); -print_frequencies(L, N, Min, Max0) -> - try - Interval = round((Max0-Min)/N), - Max = Max0 + (Max0 rem Interval), - IntervalUpperLimits = - lists:reverse( - [Max | tl(lists:reverse(lists:seq(Min,Max,Interval)))] - ), - {Acc0,_} = lists:mapfoldl(fun(Upper,Lower) -> - {{{Lower,Upper},0}, Upper+1} - end, hd(IntervalUpperLimits), tl(IntervalUpperLimits)), - Fs0 = get_frequencies(L, Acc0), - SumVal = lists:sum([V||{_,V}<-Fs0]), - Fs = with_percentage(Fs0, SumVal), - Mean = mean(L), - Median = median(L), - Npos_value = num_digits(SumVal), - Npos_range = num_digits(Max), - io:format("Range~*s: ~s~n",[2*Npos_range-2,"", "Number in range"]), - io:format("~*c:~*c~n",[2*Npos_range+3,$-, max(16,Npos_value+10),$- ]), - [begin - io:format("~*w - ~*w: ~*w ~5.1f%",[Npos_range,Rlow, - Npos_range,Rhigh, - Npos_value,Val, - Percent]), - [io:format(" <-- mean=~.1f",[Mean]) || in_interval(Mean, Interval)], - [io:format(" <-- median=" ++ - if - is_float(Median) -> "~.1f"; - true -> "~p" - end, [Median]) || in_interval(Median, Interval)], - io:nl() - end - || {Interval={Rlow,Rhigh},Val,Percent} <- Fs], - io:format('~*c ~*c~n',[2*Npos_range,32,Npos_value+2,$-]), - io:format('~*c ~*w~n',[2*Npos_range,32,Npos_value,SumVal]) - catch - C:E -> - io:format('*** Faild printing (~p:~p) for~n~p~n',[C,E,L]) - end. - -get_frequencies([{I,Num}|T], [{{Lower,Upper},Cnt}|Acc]) when Lower=<I,I=<Upper -> - get_frequencies(T, [{{Lower,Upper},Cnt+Num}|Acc]); -get_frequencies(L=[{I,_Num}|_], [Ah={{_Lower,Upper},_Cnt}|Acc]) when I>Upper -> - [Ah | get_frequencies(L,Acc)]; -get_frequencies([I|T], Acc) when is_integer(I) -> - get_frequencies([{I,1}|T], Acc); -get_frequencies([], Acc) -> - Acc. - -with_percentage(Fs, Sum) -> - [{Rng,Val,100*Val/Sum} || {Rng,Val} <- Fs]. - - -title(Str, Fun) -> - fun(L) -> - io:format('~s~n',[Str]), - Fun(L) - end. - -num_digits(I) -> 1+trunc(math:log(I)/math:log(10)). - -num(Elem, List) -> length(lists:takewhile(fun(E) -> E /= Elem end, List)) + 1. - -%%%---- Just for naming an operation for readability -is_odd(I) -> (I rem 2) == 1. - -in_interval(Value, {Rlow,Rhigh}) -> - try - Rlow=<round(Value) andalso round(Value)=<Rhigh - catch - _:_ -> false - end. - -%%%================================================================ -%%% Statistical functions - -%%%---- Mean value -mean(L = [X|_]) when is_number(X) -> - lists:sum(L) / length(L); -mean(L = [{_Value,_Weight}|_]) -> - SumOfWeights = lists:sum([W||{_,W}<-L]), - WeightedSum = lists:sum([W*V||{V,W}<-L]), - WeightedSum / SumOfWeights; -mean(_) -> - undefined. - -%%%---- Median -median(L = [X|_]) when is_number(X) -> - case is_odd(length(L)) of - true -> - hd(lists:nthtail(length(L) div 2, L)); - false -> - %% 1) L has at least on element (the when test). - %% 2) Length is even. - %% => Length >= 2 - [M1,M2|_] = lists:nthtail((length(L) div 2)-1, L), - (M1+M2) / 2 - end; -%% integer Weights... -median(L = [{_Value,_Weight}|_]) -> - median( lists:append([lists:duplicate(W,V) || {V,W} <- L]) ); -median(_) -> - undefined. - %%%================================================================ %%% The rest is taken and modified from ssh_test_lib.erl setup_rsa(Dir) -> diff --git a/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl b/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl index 165274241c..6470fa5f3d 100644 --- a/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl +++ b/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl @@ -23,36 +23,7 @@ -compile(export_all). --proptest(eqc). --proptest([triq,proper]). - --ifndef(EQC). --ifndef(PROPER). --ifndef(TRIQ). --define(EQC,true). -%%-define(PROPER,true). -%%-define(TRIQ,true). --endif. --endif. --endif. - --ifdef(EQC). --include_lib("eqc/include/eqc.hrl"). --define(MOD_eqc,eqc). - --else. --ifdef(PROPER). --include_lib("proper/include/proper.hrl"). --define(MOD_eqc,proper). - --else. --ifdef(TRIQ). --define(MOD_eqc,triq). --include_lib("triq/include/triq.hrl"). - --endif. --endif. --endif. +-include_lib("common_test/include/ct_property_test.hrl"). %% Public key records: -include_lib("public_key/include/public_key.hrl"). diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index 30d03f842f..386730af17 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -79,7 +79,8 @@ groups() -> packet_size, ssh_info_print, {group, login_bad_pwd_no_retry}, - shell_exit_status + shell_exit_status, + setopts_getopts ]}, {ssh_renegotiate_SUITE, [?PARALLEL], [rekey0, @@ -866,7 +867,7 @@ daemon_already_started(Config) when is_list(Config) -> %%% Test that a failed daemon start does not leave the port open daemon_error_closes_port(Config) -> GoodSystemDir = proplists:get_value(data_dir, Config), - Port = ssh_test_lib:inet_port(), + Port = inet_port(), {error,_} = ssh_test_lib:daemon(Port, []), % No system dir case ssh_test_lib:daemon(Port, [{system_dir, GoodSystemDir}]) of {error,eaddrinuse} -> @@ -1440,6 +1441,42 @@ shell_exit_status(Config) when is_list(Config) -> %%---------------------------------------------------------------------------- +setopts_getopts(Config) -> + process_flag(trap_exit, true), + SystemDir = proplists:get_value(data_dir, Config), + UserDir = proplists:get_value(priv_dir, Config), + + ShellFun = fun (_User) -> spawn(fun() -> ok end) end, + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, UserDir}, + {user_passwords, [{"vego", "morot"}]}, + {shell, ShellFun}, + {failfun, fun ssh_test_lib:failfun/2}]), + ConnectionRef = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user_dir, UserDir}, + {user, "vego"}, + {password, "morot"}, + {user_interaction, false}]), + %% Test get_sock_opts + {ok,[{active,once},{deliver,term},{mode,binary},{packet,0}]} = + ssh:get_sock_opts(ConnectionRef, [active, deliver, mode, packet]), + + %% Test to set forbidden opts + {error,{not_allowed,[active,deliver,mode,packet]}} = + ssh:set_sock_opts(ConnectionRef, [{active,once},{deliver,term},{mode,binary},{packet,0}]), + + %% Test to set some other opt + {ok,[{delay_send,DS0}]} = + ssh:get_sock_opts(ConnectionRef, [delay_send]), + DS1 = not DS0, + ok = ssh:set_sock_opts(ConnectionRef, [{delay_send,DS1}]), + {ok,[{delay_send,DS1}]} = + ssh:get_sock_opts(ConnectionRef, [delay_send]), + + ssh:stop_daemon(Pid). + +%%---------------------------------------------------------------------------- %%% Idle timeout test rekey0() -> [{timetrap,{seconds,90}}]. rekey1() -> [{timetrap,{seconds,90}}]. @@ -1681,9 +1718,7 @@ renegotiate1(Config) -> {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}, {preferred_algorithms,Algs}]), - RPort = ssh_test_lib:inet_port(), - {ok,RelayPid} = ssh_relay:start_link({0,0,0,0}, RPort, Host, DPort), - + {ok,RelayPid,_,RPort} = ssh_relay:start_link({0,0,0,0}, 0, Host, DPort), ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]), {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef), @@ -1721,8 +1756,7 @@ renegotiate2(Config) -> {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}, {preferred_algorithms,Algs}]), - RPort = ssh_test_lib:inet_port(), - {ok,RelayPid} = ssh_relay:start_link({0,0,0,0}, RPort, Host, DPort), + {ok,RelayPid,_,RPort} = ssh_relay:start_link({0,0,0,0}, 0, Host, DPort), ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]), {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef), @@ -1931,3 +1965,8 @@ new_do_shell_prompt(IO, N, Op, Str, More) -> new_do_shell(IO, N, [{Op,Str}|More]). %%-------------------------------------------------------------------- +inet_port() -> + {ok, Socket} = gen_tcp:listen(0, [{reuseaddr, true}]), + {ok, Port} = inet:port(Socket), + gen_tcp:close(Socket), + Port. diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl index c187a5e4b5..42d5c736c8 100644 --- a/lib/ssh/test/ssh_connection_SUITE.erl +++ b/lib/ssh/test/ssh_connection_SUITE.erl @@ -95,7 +95,10 @@ sock() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> - ?CHECK_CRYPTO(Config). + ?CHECK_CRYPTO( + [{ptty_supported, ssh_test_lib:ptty_supported()} + | Config] + ). end_per_suite(_Config) -> catch ssh:stop(), @@ -117,7 +120,7 @@ end_per_group(_, Config) -> Config. %%-------------------------------------------------------------------- -init_per_testcase(_TestCase, Config) -> +init_per_testcase(TestCase, Config) -> %% To make sure we start clean as it is not certain that %% end_per_testcase will be run! end_per_testcase(Config), @@ -347,7 +350,11 @@ ptty_alloc_default(Config) when is_list(Config) -> ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, {user_interaction, false}]), {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), - success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, []), + Expect = case proplists:get_value(ptty_supported, Config) of + true -> success; + false -> failure + end, + Expect = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, []), ssh:close(ConnectionRef). %%-------------------------------------------------------------------- @@ -358,8 +365,12 @@ ptty_alloc(Config) when is_list(Config) -> ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, {user_interaction, false}]), {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), - success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, - [{term, os:getenv("TERM", ?DEFAULT_TERMINAL)}, {width, 70}, {height, 20}]), + Expect = case proplists:get_value(ptty_supported, Config) of + true -> success; + false -> failure + end, + Expect = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, + [{term, os:getenv("TERM", ?DEFAULT_TERMINAL)}, {width, 70}, {height, 20}]), ssh:close(ConnectionRef). @@ -371,8 +382,12 @@ ptty_alloc_pixel(Config) when is_list(Config) -> ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, {user_interaction, false}]), {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), - success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, - [{term, os:getenv("TERM", ?DEFAULT_TERMINAL)}, {pixel_widh, 630}, {pixel_hight, 470}]), + Expect = case proplists:get_value(ptty_supported, Config) of + true -> success; + false -> failure + end, + Expect = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, + [{term, os:getenv("TERM", ?DEFAULT_TERMINAL)}, {pixel_widh, 630}, {pixel_hight, 470}]), ssh:close(ConnectionRef). %%-------------------------------------------------------------------- diff --git a/lib/ssh/test/ssh_relay.erl b/lib/ssh/test/ssh_relay.erl index 763130358b..3bb4cb2071 100644 --- a/lib/ssh/test/ssh_relay.erl +++ b/lib/ssh/test/ssh_relay.erl @@ -28,8 +28,7 @@ }). -record(state, { - local_addr, - local_port, + sockname, peer_addr, peer_port, lpid, @@ -92,11 +91,17 @@ release_next(Srv, Dir, TriggerDir) -> %% @doc %% Starts the server %% -%% @spec start_link() -> {ok, Pid} | ignore | {error, Error} +%% @spec start_link() -> {ok, Pid, Host, Port} | ignore | {error, Error} %% @end %%-------------------------------------------------------------------- start_link(ListenAddr, ListenPort, PeerAddr, PeerPort) -> - gen_server:start_link(?MODULE, [ListenAddr, ListenPort, PeerAddr, PeerPort], []). + case gen_server:start_link(?MODULE, [ListenAddr, ListenPort, PeerAddr, PeerPort], []) of + {ok,Pid} -> + {ok,{Host,Port}} = gen_server:call(Pid, get_sockname), + {ok, Pid, Host, Port}; + Other -> + Other + end. stop(Srv) -> unlink(Srv), @@ -126,11 +131,11 @@ init([ListenAddr, ListenPort, PeerAddr, PeerPort | _Options]) -> end, case gen_tcp:listen(ListenPort, [{reuseaddr, true}, {backlog, 1}, {active, false}, binary | IfAddr]) of {ok, LSock} -> + {ok, SName} = inet:sockname(LSock), Parent = self(), {LPid, _LMod} = spawn_monitor(fun() -> listen(Parent, LSock) end), - S = #state{local_addr = ListenAddr, - local_port = ListenPort, - lpid = LPid, + S = #state{sockname = SName, + lpid = LPid, peer_addr = ssh_test_lib:ntoa( ssh_test_lib:mangle_connect_address(PeerAddr)), peer_port = PeerPort @@ -174,6 +179,8 @@ handle_call({release, Dir}, _From, State) -> end; handle_call({release_next, _Dir, _TriggerDir}, _From, State) -> {reply, {error, nyi}, State}; +handle_call(get_sockname, _From, State) -> + {reply, {ok,State#state.sockname}, State}; handle_call(Request, _From, State) -> Reply = {unhandled, Request}, diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl index c2f9c0eba8..4ecec4e79d 100644 --- a/lib/ssh/test/ssh_sftp_SUITE.erl +++ b/lib/ssh/test/ssh_sftp_SUITE.erl @@ -100,29 +100,32 @@ groups() -> init_per_group(not_unicode, Config) -> ct:comment("Begin ~p",[grps(Config)]), - DataDir = proplists:get_value(data_dir, Config), + {ok,TestDataDir} = + make_data_sub_dir(Config, "test_data", "test_data_not_unicode"), [{user, "Alladin"}, {passwd, "Sesame"}, {data, <<"Hello world!">>}, + {data_pos, ["Hej ","hopp"]}, {filename, "sftp.txt"}, {testfile, "test.txt"}, {linktest, "link_test.txt"}, {tar_filename, "sftp_tar_test.tar"}, {tar_F1_txt, "f1.txt"}, - {datadir_tar, filename:join(DataDir,"sftp_tar_test_data")} + {datadir_tar, TestDataDir} | Config]; init_per_group(unicode, Config) -> - case (file:native_name_encoding() == utf8) - andalso ("四" == [22235]) - of + case have_unicode_support() of true -> ct:comment("Begin ~p",[grps(Config)]), - DataDir = proplists:get_value(data_dir, Config), + {ok,TestDataDir} = + make_data_sub_dir(Config, "test_data", "test_data_高兴_unicöde"), + populate_non_unicode(TestDataDir), NewConfig = [{user, "åke高兴"}, {passwd, "ärlig日本じん"}, - {data, <<"foobar å 一二三四いちにさんち">>}, + {data, "foobar å 一二三四いちにさんち"}, + {data_pos, ["中","国"]}, {filename, "sftp瑞点.txt"}, {testfile, "testハンス.txt"}, {linktest, "link_test語.txt"}, @@ -130,14 +133,14 @@ init_per_group(unicode, Config) -> {tar_F1_txt, "F一.txt"}, {tar_F3_txt, "f3.txt"}, {tar_F4_txt, "g四.txt"}, - {datadir_tar, filename:join(DataDir,"sftp_tar_test_data_高兴")} - | lists:foldl(fun(K,Cf) -> lists:keydelete(K,1,Cf) end, - Config, - [user, passwd, data, - filename, testfile, linktest, - tar_filename, tar_F1_txt, datadir_tar - ] - ) + {datadir_tar, TestDataDir} + | Config %% lists:foldl(fun(K,Cf) -> lists:keydelete(K,1,Cf) end, + %% Config, + %% [user, passwd, data, + %% filename, testfile, linktest, + %% tar_filename, tar_F1_txt, datadir_tar + %% ] + %% ) ], FN = fn(proplists:get_value(tar_F1_txt,NewConfig), NewConfig), case catch file:read_file(FN) of @@ -178,7 +181,8 @@ init_per_group(openssh_server, Config) -> {ok, _ChannelPid, Connection} -> [{peer, {_HostName,{IPx,Portx}}}] = ssh:connection_info(Connection,[peer]), ssh:close(Connection), - [{peer, {fmt_host(IPx),Portx}}, {group, openssh_server} | Config]; + [{w2l, fun w2l/1}, + {peer, {fmt_host(IPx),Portx}}, {group, openssh_server} | Config]; {error,"Key exchange failed"} -> {skip, "openssh server doesn't support the tested kex algorithm"}; Other -> @@ -330,15 +334,16 @@ open_close_file() -> [{doc, "Test API functions open/3 and close/2"}]. open_close_file(Config) when is_list(Config) -> FileName = proplists:get_value(filename, Config), - + SftpFileName = w2l(Config, FileName), + ct:log("FileName = ~p~nSftpFileName = ~p", [FileName, SftpFileName]), {Sftp, _} = proplists:get_value(sftp, Config), - ok = open_close_file(Sftp, FileName, [read]), - ok = open_close_file(Sftp, FileName, [write]), - ok = open_close_file(Sftp, FileName, [write, creat]), - ok = open_close_file(Sftp, FileName, [write, trunc]), - ok = open_close_file(Sftp, FileName, [append]), - ok = open_close_file(Sftp, FileName, [read, binary]). + ok = open_close_file(Sftp, SftpFileName, [read]), + ok = open_close_file(Sftp, SftpFileName, [write]), + ok = open_close_file(Sftp, SftpFileName, [write, creat]), + ok = open_close_file(Sftp, SftpFileName, [write, trunc]), + ok = open_close_file(Sftp, SftpFileName, [append]), + ok = open_close_file(Sftp, SftpFileName, [read, binary]). open_close_file(Server, File, Mode) -> {ok, Handle} = ssh_sftp:open(Server, File, Mode), @@ -349,21 +354,24 @@ open_close_dir() -> [{doc, "Test API functions opendir/2 and close/2"}]. open_close_dir(Config) when is_list(Config) -> PrivDir = proplists:get_value(sftp_priv_dir, Config), + SftpPrivDir = w2l(Config, PrivDir), {Sftp, _} = proplists:get_value(sftp, Config), FileName = proplists:get_value(filename, Config), + SftpFileName = w2l(Config, FileName), - {ok, Handle} = ssh_sftp:opendir(Sftp, PrivDir), + {ok, Handle} = ssh_sftp:opendir(Sftp, SftpPrivDir), ok = ssh_sftp:close(Sftp, Handle), - {error, _} = ssh_sftp:opendir(Sftp, FileName). + {error, _} = ssh_sftp:opendir(Sftp, SftpFileName). %%-------------------------------------------------------------------- read_file() -> [{doc, "Test API funtion read_file/2"}]. read_file(Config) when is_list(Config) -> FileName = proplists:get_value(filename, Config), + SftpFileName = w2l(Config, FileName), {Sftp, _} = proplists:get_value(sftp, Config), - {ok, Data} = ssh_sftp:read_file(Sftp, FileName), - {ok, Data} = ssh_sftp:read_file(Sftp, FileName), + {ok, Data} = ssh_sftp:read_file(Sftp, SftpFileName), + {ok, Data} = ssh_sftp:read_file(Sftp, SftpFileName), {ok, Data} = file:read_file(FileName). %%-------------------------------------------------------------------- @@ -371,8 +379,10 @@ read_dir() -> [{doc,"Test API function list_dir/2"}]. read_dir(Config) when is_list(Config) -> PrivDir = proplists:get_value(sftp_priv_dir, Config), + SftpPrivDir = w2l(Config, PrivDir), + {Sftp, _} = proplists:get_value(sftp, Config), - {ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir), + {ok, Files} = ssh_sftp:list_dir(Sftp, SftpPrivDir), ct:log("sftp list dir: ~p~n", [Files]). %%-------------------------------------------------------------------- @@ -380,31 +390,34 @@ write_file() -> [{doc, "Test API function write_file/2"}]. write_file(Config) when is_list(Config) -> FileName = proplists:get_value(filename, Config), + SftpFileName = w2l(Config, FileName), {Sftp, _} = proplists:get_value(sftp, Config), - - Data = list_to_binary("Hej hopp!"), - ok = ssh_sftp:write_file(Sftp, FileName, [Data]), - {ok, Data} = file:read_file(FileName). + Data = proplists:get_value(data, Config), + Expected = unicode:characters_to_binary(Data), + ok = ssh_sftp:write_file(Sftp, SftpFileName, [Data]), + {ok, Expected} = file:read_file(FileName). %%-------------------------------------------------------------------- write_file_iolist() -> [{doc, "Test API function write_file/2 with iolists"}]. write_file_iolist(Config) when is_list(Config) -> FileName = proplists:get_value(filename, Config), + SftpFileName = w2l(Config, FileName), {Sftp, _} = proplists:get_value(sftp, Config), - Data = list_to_binary("Hej hopp!"), + Data = proplists:get_value(data, Config), + DataB = unicode:characters_to_binary(Data), lists:foreach( fun(D) -> - ok = ssh_sftp:write_file(Sftp, FileName, [D]), - Expected = if is_binary(D) -> D; - is_list(D) -> list_to_binary(D) + ok = ssh_sftp:write_file(Sftp, SftpFileName, [D]), + Expected = try iolist_to_binary(D) + catch _:_ -> unicode:characters_to_binary(D) end, {ok, Expected} = file:read_file(FileName) end, [Data, [Data,Data], [[Data],[Data]], [[[Data]],[[[[Data]],Data]]], - [[[[Data]],Data],binary_to_list(Data)], - [[[[Data]],Data],[[binary_to_list(Data)],[[binary_to_list(Data)]]]] + [[[[Data]],Data],DataB], + [[[[Data]],Data],[[DataB],[[DataB]]]] ]). %%-------------------------------------------------------------------- @@ -412,53 +425,63 @@ write_big_file() -> [{doc, "Test API function write_file/2 with big data"}]. write_big_file(Config) when is_list(Config) -> FileName = proplists:get_value(filename, Config), + SftpFileName = w2l(Config, FileName), {Sftp, _} = proplists:get_value(sftp, Config), - Data = list_to_binary(lists:duplicate(750000,"a")), - ok = ssh_sftp:write_file(Sftp, FileName, [Data]), - {ok, Data} = file:read_file(FileName). + Data = [lists:duplicate(750000,"a"), + proplists:get_value(data, Config)], + ok = ssh_sftp:write_file(Sftp, SftpFileName, Data), + Expected = unicode:characters_to_binary(Data), + {ok, Expected} = file:read_file(FileName). %%-------------------------------------------------------------------- sftp_read_big_file() -> [{doc, "Test API function read_file/2 with big data"}]. sftp_read_big_file(Config) when is_list(Config) -> FileName = proplists:get_value(filename, Config), + SftpFileName = w2l(Config, FileName), {Sftp, _} = proplists:get_value(sftp, Config), - Data = list_to_binary(lists:duplicate(750000,"a")), - ct:log("Data size to write is ~p bytes",[size(Data)]), - ok = ssh_sftp:write_file(Sftp, FileName, [Data]), - {ok, Data} = ssh_sftp:read_file(Sftp, FileName). + Data = [lists:duplicate(750000,"a"), + proplists:get_value(data, Config)], + ok = ssh_sftp:write_file(Sftp, SftpFileName, Data), + Expected = unicode:characters_to_binary(Data), + {ok, Expected} = ssh_sftp:read_file(Sftp, SftpFileName). %%-------------------------------------------------------------------- remove_file() -> [{doc,"Test API function delete/2"}]. remove_file(Config) when is_list(Config) -> PrivDir = proplists:get_value(sftp_priv_dir, Config), + SftpPrivDir = w2l(Config, PrivDir), FileName = proplists:get_value(filename, Config), + SftpFileName = w2l(Config, FileName), {Sftp, _} = proplists:get_value(sftp, Config), - {ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir), + {ok, Files} = ssh_sftp:list_dir(Sftp, SftpPrivDir), true = lists:member(filename:basename(FileName), Files), - ok = ssh_sftp:delete(Sftp, FileName), - {ok, NewFiles} = ssh_sftp:list_dir(Sftp, PrivDir), + ok = ssh_sftp:delete(Sftp, SftpFileName), + {ok, NewFiles} = ssh_sftp:list_dir(Sftp, SftpPrivDir), false = lists:member(filename:basename(FileName), NewFiles), - {error, no_such_file} = ssh_sftp:delete(Sftp, FileName). + {error, no_such_file} = ssh_sftp:delete(Sftp, SftpFileName). %%-------------------------------------------------------------------- rename_file() -> [{doc, "Test API function rename_file/2"}]. rename_file(Config) when is_list(Config) -> PrivDir = proplists:get_value(sftp_priv_dir, Config), + SftpPrivDir = w2l(Config, PrivDir), FileName = proplists:get_value(filename, Config), + SftpFileName = w2l(Config, FileName), NewFileName = proplists:get_value(testfile, Config), + SftpNewFileName = w2l(Config, NewFileName), {Sftp, _} = proplists:get_value(sftp, Config), - {ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir), + {ok, Files} = ssh_sftp:list_dir(Sftp, SftpPrivDir), ct:log("FileName: ~p, Files: ~p~n", [FileName, Files]), true = lists:member(filename:basename(FileName), Files), false = lists:member(filename:basename(NewFileName), Files), - ok = ssh_sftp:rename(Sftp, FileName, NewFileName), - {ok, NewFiles} = ssh_sftp:list_dir(Sftp, PrivDir), + ok = ssh_sftp:rename(Sftp, SftpFileName, SftpNewFileName), + {ok, NewFiles} = ssh_sftp:list_dir(Sftp, SftpPrivDir), ct:log("FileName: ~p, Files: ~p~n", [FileName, NewFiles]), false = lists:member(filename:basename(FileName), NewFiles), @@ -469,14 +492,17 @@ mk_rm_dir() -> [{doc,"Test API functions make_dir/2, del_dir/2"}]. mk_rm_dir(Config) when is_list(Config) -> PrivDir = proplists:get_value(sftp_priv_dir, Config), + SftpPrivDir = w2l(Config, PrivDir), {Sftp, _} = proplists:get_value(sftp, Config), DirName = filename:join(PrivDir, "test"), - ok = ssh_sftp:make_dir(Sftp, DirName), - ok = ssh_sftp:del_dir(Sftp, DirName), + SftpDirName = w2l(Config, DirName), + ok = ssh_sftp:make_dir(Sftp, SftpDirName), + ok = ssh_sftp:del_dir(Sftp, SftpDirName), NewDirName = filename:join(PrivDir, "foo/bar"), - {error, _} = ssh_sftp:make_dir(Sftp, NewDirName), - {error, _} = ssh_sftp:del_dir(Sftp, PrivDir). + SftpNewDirName = w2l(Config, NewDirName), + {error, _} = ssh_sftp:make_dir(Sftp, SftpNewDirName), + {error, _} = ssh_sftp:del_dir(Sftp, SftpPrivDir). %%-------------------------------------------------------------------- links() -> @@ -488,10 +514,12 @@ links(Config) when is_list(Config) -> _ -> {Sftp, _} = proplists:get_value(sftp, Config), FileName = proplists:get_value(filename, Config), + SftpFileName = w2l(Config, FileName), LinkFileName = proplists:get_value(linktest, Config), + SftpLinkFileName = w2l(Config, LinkFileName), - ok = ssh_sftp:make_symlink(Sftp, LinkFileName, FileName), - {ok, FileName} = ssh_sftp:read_link(Sftp, LinkFileName) + ok = ssh_sftp:make_symlink(Sftp, SftpLinkFileName, SftpFileName), + {ok, FileName} = ssh_sftp:read_link(Sftp, SftpLinkFileName) end. %%-------------------------------------------------------------------- @@ -499,9 +527,10 @@ retrieve_attributes() -> [{doc, "Test API function read_file_info/3"}]. retrieve_attributes(Config) when is_list(Config) -> FileName = proplists:get_value(filename, Config), + SftpFileName = w2l(Config, FileName), {Sftp, _} = proplists:get_value(sftp, Config), - {ok, FileInfo} = ssh_sftp:read_file_info(Sftp, FileName), + {ok, FileInfo} = ssh_sftp:read_file_info(Sftp, SftpFileName), {ok, NewFileInfo} = file:read_file_info(FileName), %% TODO comparison. There are some differences now is that ok? @@ -512,13 +541,14 @@ set_attributes() -> [{doc,"Test API function write_file_info/3"}]. set_attributes(Config) when is_list(Config) -> FileName = proplists:get_value(testfile, Config), + SftpFileName = w2l(Config, FileName), {Sftp, _} = proplists:get_value(sftp, Config), {ok,Fd} = file:open(FileName, write), io:put_chars(Fd,"foo"), - ok = ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=8#400}), + ok = ssh_sftp:write_file_info(Sftp, SftpFileName, #file_info{mode=8#400}), {error, eacces} = file:write_file(FileName, "hello again"), - ok = ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=8#600}), + ok = ssh_sftp:write_file_info(Sftp, SftpFileName, #file_info{mode=8#600}), ok = file:write_file(FileName, "hello again"). %%-------------------------------------------------------------------- @@ -530,23 +560,24 @@ file_owner_access(Config) when is_list(Config) -> {skip, "Not a relevant test on Windows"}; _ -> FileName = proplists:get_value(filename, Config), + SftpFileName = w2l(Config, FileName), {Sftp, _} = proplists:get_value(sftp, Config), {ok, #file_info{mode = InitialMode}} = ssh_sftp:read_file_info(Sftp, FileName), - ok = ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=8#000}), - {ok, #file_info{access = none}} = ssh_sftp:read_file_info(Sftp, FileName), + ok = ssh_sftp:write_file_info(Sftp, SftpFileName, #file_info{mode=8#000}), + {ok, #file_info{access = none}} = ssh_sftp:read_file_info(Sftp, SftpFileName), - ok = ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=8#400}), - {ok, #file_info{access = read}} = ssh_sftp:read_file_info(Sftp, FileName), + ok = ssh_sftp:write_file_info(Sftp, SftpFileName, #file_info{mode=8#400}), + {ok, #file_info{access = read}} = ssh_sftp:read_file_info(Sftp, SftpFileName), - ok = ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=8#200}), - {ok, #file_info{access = write}} = ssh_sftp:read_file_info(Sftp, FileName), + ok = ssh_sftp:write_file_info(Sftp, SftpFileName, #file_info{mode=8#200}), + {ok, #file_info{access = write}} = ssh_sftp:read_file_info(Sftp, SftpFileName), - ok = ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=8#600}), - {ok, #file_info{access = read_write}} = ssh_sftp:read_file_info(Sftp, FileName), + ok = ssh_sftp:write_file_info(Sftp, SftpFileName, #file_info{mode=8#600}), + {ok, #file_info{access = read_write}} = ssh_sftp:read_file_info(Sftp, SftpFileName), - ok = ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=InitialMode}), + ok = ssh_sftp:write_file_info(Sftp, SftpFileName, #file_info{mode=InitialMode}), ok end. @@ -558,7 +589,8 @@ async_read(Config) when is_list(Config) -> {Sftp, _} = proplists:get_value(sftp, Config), FileName = proplists:get_value(filename, Config), - {ok, Handle} = ssh_sftp:open(Sftp, FileName, [read]), + SftpFileName = w2l(Config, FileName), + {ok, Handle} = ssh_sftp:open(Sftp, SftpFileName, [read]), {async, Ref} = ssh_sftp:aread(Sftp, Handle, 20), receive @@ -576,13 +608,15 @@ async_write() -> async_write(Config) when is_list(Config) -> {Sftp, _} = proplists:get_value(sftp, Config), FileName = proplists:get_value(testfile, Config), - {ok, Handle} = ssh_sftp:open(Sftp, FileName, [write]), - Data = list_to_binary("foobar"), + SftpFileName = w2l(Config, FileName), + {ok, Handle} = ssh_sftp:open(Sftp, SftpFileName, [write]), + Data = proplists:get_value(data, Config), + Expected = unicode:characters_to_binary(Data), {async, Ref} = ssh_sftp:awrite(Sftp, Handle, Data), receive {async_reply, Ref, ok} -> - {ok, Data} = file:read_file(FileName); + {ok, Expected} = file:read_file(FileName); Msg -> ct:fail(Msg) end. @@ -593,11 +627,12 @@ position() -> [{doc, "Test API functions position/3"}]. position(Config) when is_list(Config) -> FileName = proplists:get_value(testfile, Config), + SftpFileName = w2l(Config, FileName), {Sftp, _} = proplists:get_value(sftp, Config), Data = list_to_binary("1234567890"), - ok = ssh_sftp:write_file(Sftp, FileName, [Data]), - {ok, Handle} = ssh_sftp:open(Sftp, FileName, [read]), + ok = ssh_sftp:write_file(Sftp, SftpFileName, Data), + {ok, Handle} = ssh_sftp:open(Sftp, SftpFileName, [read]), {ok, 3} = ssh_sftp:position(Sftp, Handle, {bof, 3}), {ok, "4"} = ssh_sftp:read(Sftp, Handle, 1), @@ -622,17 +657,21 @@ pos_read() -> [{doc,"Test API functions pread/3 and apread/3"}]. pos_read(Config) when is_list(Config) -> FileName = proplists:get_value(testfile, Config), + SftpFileName = w2l(Config, FileName), {Sftp, _} = proplists:get_value(sftp, Config), - Data = list_to_binary("Hej hopp!"), - ok = ssh_sftp:write_file(Sftp, FileName, [Data]), + Data = proplists:get_value(data_pos, Config), + [Expect1,Expect2] = Es = [binary_to_list(unicode:characters_to_binary(D)) || D <- Data], + [Len1,Len2] = [length(E) || E <- Es], + ok = ssh_sftp:write_file(Sftp, SftpFileName, Data), - {ok, Handle} = ssh_sftp:open(Sftp, FileName, [read]), - {async, Ref} = ssh_sftp:apread(Sftp, Handle, {bof, 5}, 4), + {ok,Read} = file:read_file(FileName), + ct:log("File: <~ts>~n<~p>~nExpect1: <~p>~nExpect2: <~p>", [Read, Read, Expect1, Expect2]), - NewData = "opp!", + {ok,Handle} = ssh_sftp:open(Sftp, SftpFileName, [read]), + {async,Ref} = ssh_sftp:apread(Sftp, Handle, {bof, Len1}, Len2), receive - {async_reply, Ref, {ok, NewData}} -> + {async_reply, Ref, {ok,Expect2}} -> ok; Msg -> ct:fail(Msg) @@ -640,21 +679,20 @@ pos_read(Config) when is_list(Config) -> 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, - NewData1 = "hopp", - - {ok, NewData1} = ssh_sftp:pread(Sftp, Handle, {bof, 4}, 4). + {ok,Expect1} = ssh_sftp:pread(Sftp, Handle, {bof,0}, Len1). %%-------------------------------------------------------------------- pos_write() -> [{doc,"Test API functions pwrite/4 and apwrite/4"}]. pos_write(Config) when is_list(Config) -> FileName = proplists:get_value(testfile, Config), + SftpFileName = w2l(Config, FileName), {Sftp, _} = proplists:get_value(sftp, Config), - {ok, Handle} = ssh_sftp:open(Sftp, FileName, [write]), + {ok, Handle} = ssh_sftp:open(Sftp, SftpFileName, [write]), Data = list_to_binary("Bye,"), - ok = ssh_sftp:write_file(Sftp, FileName, [Data]), + ok = ssh_sftp:write_file(Sftp, SftpFileName, [Data]), NewData = list_to_binary(" see you tomorrow"), {async, Ref} = ssh_sftp:apwrite(Sftp, Handle, {bof, 4}, NewData), @@ -667,10 +705,11 @@ pos_write(Config) when is_list(Config) -> 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, - ok = ssh_sftp:pwrite(Sftp, Handle, eof, list_to_binary("!")), + LastData = proplists:get_value(data, Config), + ok = ssh_sftp:pwrite(Sftp, Handle, eof, LastData), - NewData1 = list_to_binary("Bye, see you tomorrow!"), - {ok, NewData1} = ssh_sftp:read_file(Sftp, FileName). + NewData1 = unicode:characters_to_binary("Bye, see you tomorrow" ++ LastData), + {ok, NewData1} = ssh_sftp:read_file(Sftp, SftpFileName). %%-------------------------------------------------------------------- start_channel_sock(Config) -> @@ -697,25 +736,27 @@ start_channel_sock(Config) -> %% Test that the channel is usable FileName = proplists:get_value(filename, Config), - ok = open_close_file(ChPid1, FileName, [read]), - ok = open_close_file(ChPid1, FileName, [write]), + SftpFileName = w2l(Config, FileName), + + ok = open_close_file(ChPid1, SftpFileName, [read]), + ok = open_close_file(ChPid1, SftpFileName, [write]), %% Try to open a second channel on the Connection {ok, ChPid2} = ssh_sftp:start_channel(Conn, Opts), - ok = open_close_file(ChPid1, FileName, [read]), - ok = open_close_file(ChPid2, FileName, [read]), + ok = open_close_file(ChPid1, SftpFileName, [read]), + ok = open_close_file(ChPid2, SftpFileName, [read]), %% Test that the second channel still works after closing the first one ok = ssh_sftp:stop_channel(ChPid1), - ok = open_close_file(ChPid2, FileName, [write]), + ok = open_close_file(ChPid2, SftpFileName, [write]), %% Test the Connection survives that all channels are closed ok = ssh_sftp:stop_channel(ChPid2), {ok, ChPid3} = ssh_sftp:start_channel(Conn, Opts), - ok = open_close_file(ChPid3, FileName, [write]), + ok = open_close_file(ChPid3, SftpFileName, [write]), %% Test that a closed channel really is closed - {error, closed} = ssh_sftp:open(ChPid2, FileName, [write]), + {error, closed} = ssh_sftp:open(ChPid2, SftpFileName, [write]), ok = ssh_sftp:stop_channel(ChPid3), %% Test that the socket is closed when the Connection closes @@ -749,17 +790,19 @@ version_option(Config) when is_list(Config) -> create_empty_tar(Config) -> ChPid2 = proplists:get_value(channel_pid2, Config), TarFileName = proplists:get_value(tar_filename, Config), - {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + SftpTarFileName = w2l(Config, TarFileName), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write]), erl_tar:close(Handle), {ChPid,_} = proplists:get_value(sftp,Config), {ok, #file_info{type=regular}} = - ssh_sftp:read_file_info(ChPid, TarFileName). + ssh_sftp:read_file_info(ChPid, SftpTarFileName). %%-------------------------------------------------------------------- files_to_tar(Config) -> ChPid2 = proplists:get_value(channel_pid2, Config), TarFileName = proplists:get_value(tar_filename, Config), - {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + SftpTarFileName = w2l(Config, TarFileName), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write]), F1 = proplists:get_value(tar_F1_txt, Config), ok = erl_tar:add(Handle, fn(F1,Config), F1, [verbose]), ok = erl_tar:add(Handle, fn("f2.txt",Config), "f2.txt", [verbose]), @@ -770,7 +813,8 @@ files_to_tar(Config) -> ascii_filename_ascii_contents_to_tar(Config) -> ChPid2 = proplists:get_value(channel_pid2, Config), TarFileName = proplists:get_value(tar_filename, Config), - {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + SftpTarFileName = w2l(Config, TarFileName), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write]), ok = erl_tar:add(Handle, fn("f2.txt",Config), "f2.txt", [verbose]), ok = erl_tar:close(Handle), chk_tar(["f2.txt"], Config). @@ -783,7 +827,8 @@ ascii_filename_unicode_contents_to_tar(Config) -> Fn -> ChPid2 = proplists:get_value(channel_pid2, Config), TarFileName = proplists:get_value(tar_filename, Config), - {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + SftpTarFileName = w2l(Config, TarFileName), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write]), ok = erl_tar:add(Handle, fn(Fn,Config), Fn, [verbose]), ok = erl_tar:close(Handle), chk_tar([Fn], Config) @@ -797,7 +842,8 @@ unicode_filename_ascii_contents_to_tar(Config) -> Fn -> ChPid2 = proplists:get_value(channel_pid2, Config), TarFileName = proplists:get_value(tar_filename, Config), - {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + SftpTarFileName = w2l(Config, TarFileName), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write]), ok = erl_tar:add(Handle, fn(Fn,Config), Fn, [verbose]), ok = erl_tar:close(Handle), chk_tar([Fn], Config) @@ -807,7 +853,8 @@ unicode_filename_ascii_contents_to_tar(Config) -> big_file_to_tar(Config) -> ChPid2 = proplists:get_value(channel_pid2, Config), TarFileName = proplists:get_value(tar_filename, Config), - {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + SftpTarFileName = w2l(Config, TarFileName), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write]), ok = erl_tar:add(Handle, fn("big.txt",Config), "big.txt", [verbose]), ok = erl_tar:close(Handle), chk_tar(["big.txt"], Config). @@ -817,7 +864,8 @@ big_file_to_tar(Config) -> files_chunked_to_tar(Config) -> ChPid2 = proplists:get_value(channel_pid2, Config), TarFileName = proplists:get_value(tar_filename, Config), - {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + SftpTarFileName = w2l(Config, TarFileName), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write]), F1 = proplists:get_value(tar_F1_txt, Config), ok = erl_tar:add(Handle, fn(F1,Config), F1, [verbose,{chunks,2}]), ok = erl_tar:close(Handle), @@ -827,7 +875,8 @@ files_chunked_to_tar(Config) -> directory_to_tar(Config) -> ChPid2 = proplists:get_value(channel_pid2, Config), TarFileName = proplists:get_value(tar_filename, Config), - {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + SftpTarFileName = w2l(Config, TarFileName), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write]), ok = erl_tar:add(Handle, fn("d1",Config), "d1", [verbose]), ok = erl_tar:close(Handle), chk_tar(["d1"], Config). @@ -836,7 +885,8 @@ directory_to_tar(Config) -> binaries_to_tar(Config) -> ChPid2 = proplists:get_value(channel_pid2, Config), TarFileName = proplists:get_value(tar_filename, Config), - {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + SftpTarFileName = w2l(Config, TarFileName), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write]), Bin = <<"A binary">>, ok = erl_tar:add(Handle, Bin, "b1", [verbose]), ok = erl_tar:close(Handle), @@ -850,7 +900,8 @@ null_crypto_tar(Config) -> Cend = fun(Bin,_CState) -> {ok,Bin} end, C = {Cinit,Cenc,Cend}, TarFileName = proplists:get_value(tar_filename, Config), - {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write,{crypto,C}]), + SftpTarFileName = w2l(Config, TarFileName), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write,{crypto,C}]), Bin = <<"A binary">>, F1 = proplists:get_value(tar_F1_txt, Config), ok = erl_tar:add(Handle, Bin, "b1", [verbose]), @@ -868,7 +919,8 @@ simple_crypto_tar_small(Config) -> Cend = fun(Bin,_CState) -> {ok,stuff(Bin)} end, C = {Cinit,Cenc,Cend}, TarFileName = proplists:get_value(tar_filename, Config), - {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write,{crypto,C}]), + SftpTarFileName = w2l(Config, TarFileName), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write,{crypto,C}]), Bin = <<"A binary">>, F1 = proplists:get_value(tar_F1_txt, Config), ok = erl_tar:add(Handle, Bin, "b1", [verbose]), @@ -885,7 +937,8 @@ simple_crypto_tar_big(Config) -> Cend = fun(Bin,_CState) -> {ok,stuff(Bin)} end, C = {Cinit,Cenc,Cend}, TarFileName = proplists:get_value(tar_filename, Config), - {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write,{crypto,C}]), + SftpTarFileName = w2l(Config, TarFileName), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write,{crypto,C}]), Bin = <<"A binary">>, F1 = proplists:get_value(tar_F1_txt, Config), ok = erl_tar:add(Handle, Bin, "b1", [verbose]), @@ -906,7 +959,8 @@ read_tar(Config) -> {"b2",list_to_binary(lists:duplicate(750000,"a"))} ]), TarFileName = proplists:get_value(tar_filename, Config), - {ok,HandleWrite} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + SftpTarFileName = w2l(Config, TarFileName), + {ok,HandleWrite} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write]), [ok = erl_tar:add(HandleWrite, Bin, Name, [verbose]) || {Name,Bin} <- NameBins], ok = erl_tar:close(HandleWrite), @@ -929,7 +983,8 @@ read_null_crypto_tar(Config) -> Cr = {Cinitr,Cdec}, TarFileName = proplists:get_value(tar_filename, Config), - {ok,HandleWrite} = ssh_sftp:open_tar(ChPid2, TarFileName, [write,{crypto,Cw}]), + SftpTarFileName = w2l(Config, TarFileName), + {ok,HandleWrite} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write,{crypto,Cw}]), [ok = erl_tar:add(HandleWrite, Bin, Name, [verbose]) || {Name,Bin} <- NameBins], ok = erl_tar:close(HandleWrite), @@ -953,7 +1008,8 @@ read_crypto_tar(Config) -> Cr = {Cinitr,Cdec}, TarFileName = proplists:get_value(tar_filename, Config), - {ok,HandleWrite} = ssh_sftp:open_tar(ChPid2, TarFileName, [write,{crypto,Cw}]), + SftpTarFileName = w2l(Config, TarFileName), + {ok,HandleWrite} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write,{crypto,Cw}]), [ok = erl_tar:add(HandleWrite, Bin, Name, [verbose]) || {Name,Bin} <- NameBins], ok = erl_tar:close(HandleWrite), @@ -993,7 +1049,9 @@ aes_cbc256_crypto_tar(Config) -> Cw = {Cinitw,Cenc,Cendw}, TarFileName = proplists:get_value(tar_filename, Config), - {ok,HandleWrite} = ssh_sftp:open_tar(ChPid2, TarFileName, [write,{crypto,Cw}]), + SftpTarFileName = w2l(Config, TarFileName), + + {ok,HandleWrite} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write,{crypto,Cw}]), [ok = erl_tar:add(HandleWrite, Bin, Name, [verbose]) || {Name,Bin} <- NameBins], ok = erl_tar:close(HandleWrite), @@ -1036,7 +1094,9 @@ aes_ctr_stream_crypto_tar(Config) -> Cw = {Cinitw,Cenc,Cendw}, TarFileName = proplists:get_value(tar_filename, Config), - {ok,HandleWrite} = ssh_sftp:open_tar(ChPid2, TarFileName, [write,{crypto,Cw}]), + SftpTarFileName = w2l(Config, TarFileName), + + {ok,HandleWrite} = ssh_sftp:open_tar(ChPid2, SftpTarFileName, [write,{crypto,Cw}]), [ok = erl_tar:add(HandleWrite, Bin, Name, [verbose]) || {Name,Bin} <- NameBins], ok = erl_tar:close(HandleWrite), @@ -1046,41 +1106,27 @@ aes_ctr_stream_crypto_tar(Config) -> %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- -oldprep(Config) -> - DataDir = proplists:get_value(data_dir, Config), - TestFile = proplists:get_value(filename, Config), - TestFile1 = proplists:get_value(testfile, Config), - TestLink = proplists:get_value(linktest, Config), - TarFileName = proplists:get_value(tar_filename, Config), - - file:delete(TestFile), - file:delete(TestFile1), - file:delete(TestLink), - file:delete(TarFileName), - - %% Initial config - FileName = filename:join(DataDir, "sftp.txt"), - file:copy(FileName, TestFile), - Mode = 8#00400 bor 8#00200 bor 8#00040, % read & write owner, read group - {ok, FileInfo} = file:read_file_info(TestFile), - ok = file:write_file_info(TestFile, - FileInfo#file_info{mode = Mode}). - -prepare(Config0) -> +old_prepare(Config0) -> PrivDir = proplists:get_value(priv_dir, Config0), Dir = filename:join(PrivDir, ssh_test_lib:random_chars(10)), file:make_dir(Dir), + ct:log("~p:~p created the directory~nsftp_priv_dir = ~p", [?MODULE,?LINE,Dir]), Keys = [filename, testfile, linktest, tar_filename], Config1 = foldl_keydelete(Keys, Config0), Config2 = lists:foldl(fun({Key,Name}, ConfAcc) -> - [{Key, filename:join(Dir,Name)} | ConfAcc] + [{Key, filename:join(Dir,Name)} | ConfAcc] end, Config1, lists:zip(Keys, [proplists:get_value(K,Config0) || K<-Keys])), + catch ct:log("~p:~p Prepared filenames (Key -> Value):~n~ts", + [?MODULE,?LINE, + [io_lib:format("~p -> ~ts~n", [K,V]) || {K,V} <- Config2, + lists:member(K, Keys)]]), + DataDir = proplists:get_value(data_dir, Config2), FilenameSrc = filename:join(DataDir, "sftp.txt"), FilenameDst = proplists:get_value(filename, Config2), @@ -1088,6 +1134,49 @@ prepare(Config0) -> [{sftp_priv_dir,Dir} | Config2]. +have_unicode_support() -> (file:native_name_encoding() == utf8) andalso ("四" == [22235]). + + +make_data_sub_dir(Config, SubDir) -> + make_data_sub_dir(Config, SubDir, SubDir). + +make_data_sub_dir(Config, SubDirSrc, SubDirDst) -> + SrcDir = filename:join(proplists:get_value(data_dir, Config), + SubDirSrc + ), + DstDir = filename:join(proplists:get_value(data_dir, Config), + SubDirDst), + ssh_test_lib:copy_recursive(SrcDir, DstDir), + {ok,DstDir}. + + +prepare(Config0) -> + SrcDir = proplists:get_value(datadir_tar,Config0), + DstDir = filename:join(proplists:get_value(priv_dir,Config0), ssh_test_lib:random_chars(10)), + ssh_test_lib:copy_recursive(SrcDir, DstDir), + Keys = [filename, + testfile, + linktest, + tar_filename], + Config1 = foldl_keydelete(Keys, Config0), + Config2 = lists:foldl(fun({Key,Name}, ConfAcc) -> + [{Key, filename:join(DstDir,Name)} | ConfAcc] + end, + Config1, + lists:zip(Keys, [proplists:get_value(K,Config0) || K<-Keys])), + + catch ct:log("~p:~p Prepared filenames (Key -> Value):~n~ts", + [?MODULE,?LINE, + [io_lib:format("~p -> ~ts~n", [K,V]) || {K,V} <- Config2, + lists:member(K, Keys)]]), + + DataDir = proplists:get_value(data_dir, Config2), + FilenameSrc = filename:join(DataDir, "sftp.txt"), + FilenameDst = proplists:get_value(filename, Config2), + {ok,_} = file:copy(FilenameSrc, FilenameDst), + [{sftp_priv_dir,DstDir} | Config2]. + + foldl_keydelete(Keys, L) -> lists:foldl(fun(K,E) -> lists:keydelete(K,1,E) end, L, @@ -1102,9 +1191,10 @@ chk_tar(Items, Config, Opts) -> chk_tar(Items, TarFileName, Config, Opts). chk_tar(Items, TarFileName, Config, Opts) when is_list(Opts) -> + SftpTarFileName = w2l(Config, TarFileName), tar_size(TarFileName, Config), {ChPid,_} = proplists:get_value(sftp,Config), - {ok,HandleRead} = ssh_sftp:open_tar(ChPid, TarFileName, [read|Opts]), + {ok,HandleRead} = ssh_sftp:open_tar(ChPid, SftpTarFileName, [read|Opts]), {ok,NameValueList} = erl_tar:extract(HandleRead,[memory,verbose]), ok = erl_tar:close(HandleRead), case {lists:sort(expand_items(Items,Config)), lists:sort(NameValueList)} of @@ -1145,8 +1235,9 @@ analyze_report([], []) -> "". tar_size(TarFileName, Config) -> + SftpTarFileName = w2l(Config, TarFileName), {ChPid,_} = proplists:get_value(sftp,Config), - {ok,Data} = ssh_sftp:read_file(ChPid, TarFileName), + {ok,Data} = ssh_sftp:read_file(ChPid, SftpTarFileName), io:format('Tar file ~p is~n ~p bytes.~n',[TarFileName, size(Data)]). expand_items(Items, Config) -> @@ -1178,3 +1269,22 @@ fn(Name, Config) -> fmt_host({A,B,C,D}) -> lists:concat([A,".",B,".",C,".",D]); fmt_host(S) -> S. + +%%%---------------------------------------------------------------- +populate_non_unicode(Dir) -> + file:delete("f1.txt"), + ok = file:write_file(filename:join(Dir,"f3.txt"), + unicode:characters_to_binary("你好")), + ok = file:write_file(filename:join(Dir,"F一.txt"), + unicode:characters_to_binary("你好")), + ok = file:write_file(filename:join(Dir,"g四.txt"), + <<"How are you?">>). + +w2l(P) -> + ssh_test_lib:winpath_to_linuxpath(P). + +w2l(Config, P) -> + W2L = proplists:get_value(w2l, Config, fun(X) -> X end), + W2L(P). + + diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/F一.txt b/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/F一.txt deleted file mode 100644 index e6076a05b5..0000000000 --- a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/F一.txt +++ /dev/null @@ -1 +0,0 @@ -你好 diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/big.txt b/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/big.txt deleted file mode 100644 index f597b69d4c..0000000000 --- a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/big.txt +++ /dev/null @@ -1,16384 +0,0 @@ -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. -All work and no play makes Jack a dull boy. diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/d1/f1 b/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/d1/f1 deleted file mode 100644 index 1bafa9761e..0000000000 --- a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/d1/f1 +++ /dev/null @@ -1 +0,0 @@ -And hi from the subdirectory too! diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/d1/f2 b/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/d1/f2 deleted file mode 100644 index 8566adaeef..0000000000 --- a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/d1/f2 +++ /dev/null @@ -1 +0,0 @@ -one more file diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/f2.txt b/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/f2.txt deleted file mode 100644 index d18c6b11fc..0000000000 --- a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/f2.txt +++ /dev/null @@ -1 +0,0 @@ -How are you? diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/f3.txt b/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/f3.txt deleted file mode 100644 index e6076a05b5..0000000000 --- a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/f3.txt +++ /dev/null @@ -1 +0,0 @@ -你好 diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/g四.txt b/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/g四.txt deleted file mode 100644 index d18c6b11fc..0000000000 --- a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/g四.txt +++ /dev/null @@ -1 +0,0 @@ -How are you? diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data/big.txt b/lib/ssh/test/ssh_sftp_SUITE_data/test_data/big.txt index f597b69d4c..f597b69d4c 100644 --- a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data/big.txt +++ b/lib/ssh/test/ssh_sftp_SUITE_data/test_data/big.txt diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data/d1/f1 b/lib/ssh/test/ssh_sftp_SUITE_data/test_data/d1/f1 index 1bafa9761e..1bafa9761e 100644 --- a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data/d1/f1 +++ b/lib/ssh/test/ssh_sftp_SUITE_data/test_data/d1/f1 diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data/d1/f2 b/lib/ssh/test/ssh_sftp_SUITE_data/test_data/d1/f2 index 8566adaeef..8566adaeef 100644 --- a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data/d1/f2 +++ b/lib/ssh/test/ssh_sftp_SUITE_data/test_data/d1/f2 diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data/f1.txt b/lib/ssh/test/ssh_sftp_SUITE_data/test_data/f1.txt index 137d409d7b..137d409d7b 100644 --- a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data/f1.txt +++ b/lib/ssh/test/ssh_sftp_SUITE_data/test_data/f1.txt diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data/f2.txt b/lib/ssh/test/ssh_sftp_SUITE_data/test_data/f2.txt index d18c6b11fc..d18c6b11fc 100644 --- a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data/f2.txt +++ b/lib/ssh/test/ssh_sftp_SUITE_data/test_data/f2.txt diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl index a0e3d809be..73bfc13eef 100644 --- a/lib/ssh/test/ssh_sup_SUITE.erl +++ b/lib/ssh/test/ssh_sup_SUITE.erl @@ -58,11 +58,10 @@ end_per_group(_GroupName, Config) -> init_per_suite(Config) -> ?CHECK_CRYPTO( begin - Port = ssh_test_lib:inet_port(node()), PrivDir = proplists:get_value(priv_dir, Config), UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth file:make_dir(UserDir), - [{userdir, UserDir},{port, Port}, {host, "localhost"}, {host_ip, any} | Config] + [{userdir, UserDir} | Config] end). end_per_suite(_) -> @@ -136,13 +135,11 @@ sshc_subtree(Config) when is_list(Config) -> sshd_subtree() -> [{doc, "Make sure the sshd subtree is correct"}]. sshd_subtree(Config) when is_list(Config) -> - HostIP = proplists:get_value(host_ip, Config), - Port = proplists:get_value(port, Config), SystemDir = proplists:get_value(data_dir, Config), - {ok,Daemon} = ssh:daemon(HostIP, Port, [{system_dir, SystemDir}, - {failfun, fun ssh_test_lib:failfun/2}, - {user_passwords, - [{?USER, ?PASSWD}]}]), + {Daemon, HostIP, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {failfun, fun ssh_test_lib:failfun/2}, + {user_passwords, + [{?USER, ?PASSWD}]}]), ct:log("Expect HostIP=~p, Port=~p, Daemon=~p",[HostIP,Port,Daemon]), ?wait_match([{{server,ssh_system_sup, ListenIP, Port, ?DEFAULT_PROFILE}, @@ -151,7 +148,7 @@ sshd_subtree(Config) when is_list(Config) -> supervisor:which_children(sshd_sup), [ListenIP,Daemon]), true = ssh_test_lib:match_ip(HostIP, ListenIP), - check_sshd_system_tree(Daemon, Config), + check_sshd_system_tree(Daemon, HostIP, Port, Config), ssh:stop_daemon(HostIP, Port), ct:sleep(?WAIT_FOR_SHUTDOWN), ?wait_match([], supervisor:which_children(sshd_sup)). @@ -160,16 +157,14 @@ sshd_subtree(Config) when is_list(Config) -> sshd_subtree_profile() -> [{doc, "Make sure the sshd subtree using profile option is correct"}]. sshd_subtree_profile(Config) when is_list(Config) -> - HostIP = proplists:get_value(host_ip, Config), - Port = proplists:get_value(port, Config), Profile = proplists:get_value(profile, Config), SystemDir = proplists:get_value(data_dir, Config), - {ok, Daemon} = ssh:daemon(HostIP, Port, [{system_dir, SystemDir}, - {failfun, fun ssh_test_lib:failfun/2}, - {user_passwords, - [{?USER, ?PASSWD}]}, - {profile, Profile}]), + {Daemon, HostIP, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {failfun, fun ssh_test_lib:failfun/2}, + {user_passwords, + [{?USER, ?PASSWD}]}, + {profile, Profile}]), ct:log("Expect HostIP=~p, Port=~p, Profile=~p, Daemon=~p",[HostIP,Port,Profile,Daemon]), ?wait_match([{{server,ssh_system_sup, ListenIP,Port,Profile}, Daemon, supervisor, @@ -177,7 +172,7 @@ sshd_subtree_profile(Config) when is_list(Config) -> supervisor:which_children(sshd_sup), [ListenIP,Daemon]), true = ssh_test_lib:match_ip(HostIP, ListenIP), - check_sshd_system_tree(Daemon, Config), + check_sshd_system_tree(Daemon, HostIP, Port, Config), ssh:stop_daemon(HostIP, Port, Profile), ct:sleep(?WAIT_FOR_SHUTDOWN), ?wait_match([], supervisor:which_children(sshd_sup)). @@ -354,9 +349,7 @@ chk_empty_con_daemon(Daemon) -> %%------------------------------------------------------------------------- %% Help functions %%------------------------------------------------------------------------- -check_sshd_system_tree(Daemon, Config) -> - Host = proplists:get_value(host, Config), - Port = proplists:get_value(port, Config), +check_sshd_system_tree(Daemon, Host, Port, Config) -> UserDir = proplists:get_value(userdir, Config), {ok, Client} = ssh:connect(Host, Port, [{silently_accept_hosts, true}, {user_interaction, false}, diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl index 036820fa8d..83481e6c33 100644 --- a/lib/ssh/test/ssh_test_lib.erl +++ b/lib/ssh/test/ssh_test_lib.erl @@ -28,6 +28,7 @@ -include_lib("public_key/include/public_key.hrl"). -include_lib("common_test/include/ct.hrl"). -include_lib("ssh/src/ssh_transport.hrl"). +-include_lib("kernel/include/file.hrl"). -include("ssh_test_lib.hrl"). %%%---------------------------------------------------------------- @@ -353,12 +354,6 @@ receive_exec_result(Data, ConnectionRef, ChannelId) -> expected = receive_exec_result(Closed). -inet_port()-> - {ok, Socket} = gen_tcp:listen(0, [{reuseaddr, true}]), - {ok, Port} = inet:port(Socket), - gen_tcp:close(Socket), - Port. - setup_ssh_auth_keys(RSAFile, DSAFile, Dir) -> Entries = ssh_file_entry(RSAFile) ++ ssh_file_entry(DSAFile), AuthKeys = public_key:ssh_encode(Entries , auth_keys), @@ -611,19 +606,12 @@ del_dirs(Dir) -> ok end. -inet_port(Node) -> - {Port, Socket} = do_inet_port(Node), - rpc:call(Node, gen_tcp, close, [Socket]), - Port. - -do_inet_port(Node) -> - {ok, Socket} = rpc:call(Node, gen_tcp, listen, [0, [{reuseaddr, true}]]), - {ok, Port} = rpc:call(Node, inet, port, [Socket]), - {Port, Socket}. - openssh_sanity_check(Config) -> ssh:start(), - case ssh:connect("localhost", 22, [{password,""}]) of + case ssh:connect("localhost", 22, [{password,""}, + {silently_accept_hosts, true}, + {user_interaction, false} + ]) of {ok, Pid} -> ssh:close(Pid), ssh:stop(), @@ -1124,3 +1112,68 @@ report(Comment, Line) -> crypto:info_lib(), crypto:info_fips(), crypto:supports()]). + +%%%---------------------------------------------------------------- +lc_name_in(Names) -> + case inet:gethostname() of + {ok,Name} -> + lists:member(string:to_lower(Name), Names); + Other -> + ct:log("~p:~p inet:gethostname() returned ~p", [?MODULE,?LINE,Other]), + false + end. + +ptty_supported() -> not lc_name_in([]). %%["fobi"]). + +%%%---------------------------------------------------------------- +has_WSL() -> + os:getenv("WSLENV") =/= false. % " =/= false" =/= "== true" :) + +winpath_to_linuxpath(Path) -> + case {has_WSL(), Path} of + {true, [_,$:|WithoutWinInit]} -> + "/mnt/c" ++ WithoutWinInit; + _ -> + Path + end. + +%%%---------------------------------------------------------------- +copy_recursive(Src, Dst) -> + {ok,S} = file:read_file_info(Src), + case S#file_info.type of + directory -> + %%ct:log("~p:~p copy dir ~ts -> ~ts", [?MODULE,?LINE,Src,Dst]), + {ok,Names} = file:list_dir(Src), + mk_dir_path(Dst), + %%ct:log("~p:~p Names = ~p", [?MODULE,?LINE,Names]), + lists:foreach(fun(Name) -> + copy_recursive(filename:join(Src, Name), + filename:join(Dst, Name)) + end, Names); + _ -> + %%ct:log("~p:~p copy file ~ts -> ~ts", [?MODULE,?LINE,Src,Dst]), + {ok,_NumBytesCopied} = file:copy(Src, Dst) + end. + +%%%---------------------------------------------------------------- +%% Make a directory even if parts of the path does not exist + +mk_dir_path(DirPath) -> + case file:make_dir(DirPath) of + {error,eexist} -> + %%ct:log("~p:~p dir exists ~ts", [?MODULE,?LINE,DirPath]), + ok; + {error,enoent} -> + %%ct:log("~p:~p try make dirname of ~ts", [?MODULE,?LINE,DirPath]), + case mk_dir_path( filename:dirname(DirPath) ) of + ok -> + %%ct:log("~p:~p redo ~ts", [?MODULE,?LINE,DirPath]), + file:make_dir(DirPath); + Error -> + %%ct:log("~p:~p return Error ~p ~ts", [?MODULE,?LINE,Error,DirPath]), + Error + end; + Other -> + %%ct:log("~p:~p return Other ~p ~ts", [?MODULE,?LINE,Other,DirPath]), + Other + end. diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl index cf64ac8dab..4f06bd3d65 100644 --- a/lib/ssh/test/ssh_to_openssh_SUITE.erl +++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl @@ -62,7 +62,10 @@ init_per_suite(Config) -> {error,econnrefused} -> {skip,"No openssh deamon (econnrefused)"}; _ -> - ssh_test_lib:openssh_sanity_check(Config) + ssh_test_lib:openssh_sanity_check( + [{ptty_supported, ssh_test_lib:ptty_supported()} + | Config] + ) end ). @@ -118,14 +121,26 @@ erlang_shell_client_openssh_server(Config) when is_list(Config) -> Prev = lists:usort(supervisor:which_children(sshc_sup)), Shell = ssh_test_lib:start_shell(?SSH_DEFAULT_PORT, IO), IO ! {input, self(), "echo Hej\n"}, - receive_data("Hej", undefined), - IO ! {input, self(), "exit\n"}, - receive_logout(), - receive_normal_exit(Shell), - %% Check that the connection is closed: - ct:log("Expects ~p", [Prev]), - ?wait_match(Prev, lists:usort(supervisor:which_children(sshc_sup))). - + case proplists:get_value(ptty_supported, Config) of + true -> + ct:log("~p:~p ptty supported", [?MODULE,?LINE]), + receive_data("Hej", undefined), + IO ! {input, self(), "exit\n"}, + receive_logout(), + receive_normal_exit(Shell), + %% Check that the connection is closed: + ct:log("Expects ~p", [Prev]), + ?wait_match(Prev, lists:usort(supervisor:which_children(sshc_sup))); + false -> + ct:log("~p:~p ptty unsupported", [?MODULE,?LINE]), + receive_exit(Shell, + fun({{badmatch,failure}, + [{ssh,shell,_,_} | _]}) -> true; + (_) -> + false + end) + end. + %%-------------------------------------------------------------------- %% Test that the server could redirect stdin and stdout from/to an %% OpensSSH client when handling an exec request @@ -168,6 +183,7 @@ exec_direct_with_io_in_sshc(Config) when is_list(Config) -> ct:pal("Cmd = ~p~n",[Cmd]), case os:cmd(Cmd) of "? {ciao,\"oaic\"}" -> ok; + "'? '{ciao,\"oaic\"}" -> ok; % WSL "{ciao,\"oaic\"}? " -> ok; % Could happen if the client sends the piped % input before receiving the prompt ("? "). Other -> ct:fail("Received ~p",[Other]) @@ -239,14 +255,14 @@ receive_data(Data, Conn) -> Lines = string:tokens(binary_to_list(Info), "\r\n "), case lists:member(Data, Lines) of true -> - ct:log("Expected result ~p found in lines: ~p~n", [Data,Lines]), + ct:log("~p:~p Expected result ~p found in lines: ~p~n", [?MODULE,?LINE,Data,Lines]), ok; false -> - ct:log("Extra info: ~p~n", [Info]), + ct:log("~p:~p Extra info: ~p~n", [?MODULE,?LINE,Info]), receive_data(Data, Conn) end; Other -> - ct:log("Unexpected: ~p",[Other]), + ct:log("~p:~p Unexpected: ~p",[?MODULE,?LINE,Other]), receive_data(Data, Conn) after 30000 -> @@ -275,18 +291,32 @@ receive_logout() -> 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. + receive_normal_exit(Shell) -> + receive_exit(Shell, fun(Reason) -> Reason == normal end). + + +receive_exit(Shell, F) when is_function(F,1) -> receive - {'EXIT', Shell, normal} -> - ok; - <<"\r\n">> -> - receive_normal_exit(Shell); - Other -> - ct:fail({unexpected_msg, Other}) - after + {'EXIT', Shell, Reason} -> + case F(Reason) of + true -> + ok; + false -> + ct:fail({unexpected_exit, Reason}) + end; + + <<"\r\n">> -> + receive_normal_exit(Shell); + + Other -> + ct:fail({unexpected_msg, Other}) + + after 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. + extra_logout() -> receive <<"logout">> -> diff --git a/lib/ssh/test/ssh_upgrade_SUITE.erl b/lib/ssh/test/ssh_upgrade_SUITE.erl index 7b9b109fa1..de9d7b2b1c 100644 --- a/lib/ssh/test/ssh_upgrade_SUITE.erl +++ b/lib/ssh/test/ssh_upgrade_SUITE.erl @@ -149,8 +149,7 @@ setup_server_client(#state{config=Config} = State) -> SFTP = ssh_sftpd:subsystem_spec([{root,FtpRootDir},{cwd,FtpRootDir}]), - {Server,Host,Port} = ssh_test_lib:daemon(ssh_test_lib:inet_port(), % when lower rel is 18.x - [{system_dir,DataDir}, + {Server,Host,Port} = ssh_test_lib:daemon([{system_dir,DataDir}, {user_passwords,[{"hej","hopp"}]}, {subsystems,[SFTP]}]), diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index a205da67fc..64b0bda0b4 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,4 +1,4 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 4.8.1 +SSH_VSN = 4.8.2 APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 3671bfbdb6..6360f5977e 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -27,6 +27,65 @@ </header> <p>This document describes the changes made to the SSL application.</p> +<section><title>SSL 9.5.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Enhance error handling, all ALERTS shall be handled + gracefully and not cause a crash.</p> + <p> + Own Id: OTP-16413 Aux Id: ERL-1136 </p> + </item> + <item> + <p> + Enhance alert logging, in some places the role indication + of the alert origin was missing. So the log would say + undefined instead of client or server.</p> + <p> + Own Id: OTP-16424</p> + </item> + <item> + <p> + Two different optimizations did not work together and + resulted in the possible breakage of connections using + stream ciphers (that is RC4). Reworked the implementation + to avoid this.</p> + <p> + Own Id: OTP-16426 Aux Id: ERL-1136 </p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 9.5.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix the handling of GREASE values sent by web browsers + when establishing TLS 1.3 connections. This change + improves handling of GREASE values in various protocol + elements sent in a TLS 1.3 ClientHello.</p> + <p> + Own Id: OTP-16388 Aux Id: ERL-1130 </p> + </item> + <item> + <p> + Correct DTLS listen emulation, could cause problems with + opening a new DTLS listen socket for a port previously + used by a now closed DTLS listen socket.</p> + <p> + Own Id: OTP-16396 Aux Id: ERL-1118 </p> + </item> + </list> + </section> + +</section> + <section><title>SSL 9.5.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 7424566adb..27b8a3f457 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -348,20 +348,35 @@ <datatype> <name name="cipher_suites"/> <desc> - <p>Supported cipher suites. The function - <c>cipher_suites/2</c> can be used to find all ciphers that - are supported by default. <c>cipher_suites(all, 'tlsv1.2')</c> can be - called to find all available cipher suites. Pre-Shared Key - (<url href="http://www.ietf.org/rfc/rfc4279.txt">RFC - 4279</url> and <url - href="http://www.ietf.org/rfc/rfc5487.txt">RFC 5487</url>), - Secure Remote Password (<url - href="http://www.ietf.org/rfc/rfc5054.txt">RFC 5054</url>), - RC4, 3DES, DES cipher suites, and anonymous cipher suites only work if - explicitly enabled by this option; they are supported/enabled - by the peer also. Anonymous cipher suites are supported for - testing purposes only and are not be used when security - matters.</p> + <p>A list of cipher suites that should be supported</p> + + <p>The function <seealso + marker="#cipher_suites-2"> ssl:cipher_suites/2 </seealso> + can be used to find all cipher suites that + are supported by default and all cipher suites that may be configured.</p> + + <p>If you compose your own <c>cipher_suites()</c> make + sure they are filtered for cryptolib support + <seealso + marker="#filter_cipher_suites-2"> ssl:filter_cipher_suites/2 </seealso> + Additionaly the functions <seealso + marker="#append_cipher_suites-2"> ssl:append_cipher_suites/2 </seealso> + , <seealso + marker="#prepend_cipher_suites-2"> ssl:prepend_cipher_suites/2</seealso>, + <seealso marker="#suite_to_str/1">ssl:suite_to_str/1</seealso>, + <seealso marker="#str_to_suite/1">ssl:str_to_suite/1</seealso>, + and <seealso marker="#suite_to_openssl_str/1">ssl:suite_to_openssl_str/1</seealso> + also exist to help creating customized cipher suite lists.</p> + + <note><p>Note that TLS-1.3 and TLS-1.2 cipher suites are not overlapping + sets of cipher suites so to support both these versions cipher + suites from both versions need to be included. If supporting + TLS-1.3 versions prior to TLS-1.2 can not be supported. </p></note> + + <p>Non-default cipher suites including anonymous cipher suites (PRE TLS-1.3) are supported for + interop/testing purposes and may be used by adding them to your cipher suite list. + Note that they must also be supported/enabled by the peer to actually be used. + </p> </desc> </datatype> @@ -747,6 +762,21 @@ fun(srp, Username :: string(), UserState :: term()) -> </desc> </datatype> + <datatype> + <name name="key_update_at"/> + <desc><p>Configures the maximum amount of bytes that can be sent on a TLS 1.3 connection + before an automatic key update is performed.</p> + <p>There are cryptographic limits on the amount of plaintext which can be safely + encrypted under a given set of keys. The current default ensures that data integrity + will not be breached with probability greater than 1/2^57. For more information see + <url href="http://www.isg.rhul.ac.uk/~kp/TLS-AEbounds.pdf">Limits on Authenticated + Encryption Use in TLS</url>.</p> + <warning><p>The default value of this option shall provide the above mentioned security + guarantees and it shall be reasonable for most applications (~353 TB).</p> + </warning> + </desc> + </datatype> + <datatype_title>TLS/DTLS OPTION DESCRIPTIONS - CLIENT</datatype_title> <datatype> @@ -1258,6 +1288,17 @@ fun(srp, Username :: string(), UserState :: term()) -> <desc><p>Returns all default or all supported (except anonymous), or all anonymous cipher suites for a TLS version</p> + + <note><p>The cipher suites returned by this function are the + cipher suites that the OTP ssl application can support provided + that they are supported by the cryptolib linked with the OTP + crypto application. Use <seealso + marker="#filter_cipher_suites-2"> ssl:filter_cipher_suites(Suites, + []).</seealso> to filter the list for the current + cryptolib. Note that cipher suites may be filtered out because + they are too old or too new depending on the + cryptolib</p></note> + </desc> </func> @@ -1424,10 +1465,17 @@ fun(srp, Username :: string(), UserState :: term()) -> <name since="OTP 20.3" name="filter_cipher_suites" arity="2" /> <fsummary></fsummary> <desc><p>Removes cipher suites if any of the filter functions - returns false for any part of the cipher suite. This function - also calls default filter functions to make sure the cipher - suites are supported by crypto. If no filter function is supplied for some - part the default behaviour is fun(Algorithm) -> true.</p> + returns false for any part of the cipher suite. If no filter function is supplied for some + part the default behaviour regards it as if there was a filter function that returned true. + + For examples see <seealso marker="ssl:using_ssl#customizing-cipher-suits"> Customizing cipher suits </seealso> + + Additionaly this function also filters the cipher suites to + exclude cipher suites not supported by the cryptolib used by the + OTP crypto application. That is calling + ssl:filter_cipher_suites(Suites, []) will be equivalent to only + applying the filters for cryptolib support. + </p> </desc> </func> @@ -1624,7 +1672,21 @@ fun(srp, Username :: string(), UserState :: term()) -> is still active using the previously negotiated session.</p> </desc> </func> - + + <func> + <name since="OTP 22.3" name="update_keys" arity="2" /> + <fsummary>Initiates a key update.</fsummary> + <desc><p>There are cryptographic limits on the amount of plaintext which can be safely + encrypted under a given set of keys. If the amount of data surpasses those limits, a key + update is triggered and a new set of keys are installed. + See also the option <seealso marker="ssl:ssl#type-key_update_at">key_update_at</seealso>.</p> + <p>This function can be used to explicitly start a key update on a TLS 1.3 connection. + There are two types of the key update: if <em>Type</em> is set to <em>write</em>, only the writing + key is updated; if <em>Type</em> is set to <em>read_write</em>, both the reading and writing keys + are updated.</p> + </desc> + </func> + <func> <name since="" name="send" arity="2" /> <fsummary>Writes data to a socket.</fsummary> diff --git a/lib/ssl/doc/src/standards_compliance.xml b/lib/ssl/doc/src/standards_compliance.xml index 98542a797b..553ee79a39 100644 --- a/lib/ssl/doc/src/standards_compliance.xml +++ b/lib/ssl/doc/src/standards_compliance.xml @@ -1721,14 +1721,14 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.3</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>Server</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.3</em></cell> </row> <row> @@ -1824,8 +1824,8 @@ </url> </cell> <cell align="left" valign="middle"><em></em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.3</em></cell> </row> <row> diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index a658fe0306..85ba4f0556 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -908,12 +908,13 @@ handle_client_hello(#client_hello{client_version = ClientVersion} = Hello, %% raw data from socket, unpack records handle_info({Protocol, _, _, _, Data}, StateName, - #state{static_env = #static_env{data_tag = Protocol}} = State0) -> + #state{static_env = #static_env{role = Role, + data_tag = Protocol}} = State0) -> case next_dtls_record(Data, StateName, State0) of {Record, State} -> next_event(StateName, Record, State); #alert{} = Alert -> - ssl_connection:handle_normal_shutdown(Alert, StateName, State0), + ssl_connection:handle_normal_shutdown(Alert#alert{role = Role}, StateName, State0), {stop, {shutdown, own_alert}, State0} end; @@ -925,8 +926,10 @@ handle_info({PassiveTag, Socket}, StateName, State#state{protocol_specific = PS#{active_n_toggle => true}}); handle_info({CloseTag, Socket}, StateName, - #state{static_env = #static_env{socket = Socket, - close_tag = CloseTag}, + #state{static_env = #static_env{ + role = Role, + socket = Socket, + close_tag = CloseTag}, connection_env = #connection_env{negotiated_version = Version}, socket_options = #socket_options{active = Active}, protocol_buffers = #protocol_buffers{dtls_cipher_texts = CTs}, @@ -947,7 +950,8 @@ handle_info({CloseTag, Socket}, StateName, %%invalidate_session(Role, Host, Port, Session) ok end, - ssl_connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), + Alert = ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY, transport_closed), + ssl_connection:handle_normal_shutdown(Alert#alert{role = Role}, StateName, State), {stop, {shutdown, transport_closed}, State}; true -> %% Fixes non-delivery of final DTLS record in {active, once}. diff --git a/lib/ssl/src/dtls_listener_sup.erl b/lib/ssl/src/dtls_listener_sup.erl index 2c43c215be..dae7e48487 100644 --- a/lib/ssl/src/dtls_listener_sup.erl +++ b/lib/ssl/src/dtls_listener_sup.erl @@ -52,7 +52,7 @@ lookup_listner(Port) -> [{Port, {Owner, Handler}}] -> case erlang:is_process_alive(Handler) of true -> - case erlang:is_process_alive(Owner) of + case (Owner =/= undefined) andalso erlang:is_process_alive(Owner) of true -> {error, already_listening}; false -> @@ -75,7 +75,7 @@ register_listner(OwnerAndListner, Port) -> %%% Supervisor callback %%%========================================================================= init(_O) -> - ets:new(dtls_listener_sup, [named_table, public]), + ets:new(dtls_listener_sup, [named_table, public, set]), RestartStrategy = simple_one_for_one, MaxR = 0, MaxT = 3600, diff --git a/lib/ssl/src/dtls_packet_demux.erl b/lib/ssl/src/dtls_packet_demux.erl index cc3efa33ed..11ad659ff5 100644 --- a/lib/ssl/src/dtls_packet_demux.erl +++ b/lib/ssl/src/dtls_packet_demux.erl @@ -32,6 +32,7 @@ accept/2, sockname/1, close/1, + new_owner/1, get_all_opts/1, set_all_opts/2, get_sock_opts/2, @@ -76,16 +77,23 @@ accept(PacketSocket, Accepter) -> sockname(PacketSocket) -> call(PacketSocket, sockname). + close(PacketSocket) -> call(PacketSocket, close). + +new_owner(PacketSocket) -> + call(PacketSocket, new_owner). + get_sock_opts(PacketSocket, SplitSockOpts) -> call(PacketSocket, {get_sock_opts, SplitSockOpts}). get_all_opts(PacketSocket) -> call(PacketSocket, get_all_opts). + set_sock_opts(PacketSocket, Opts) -> call(PacketSocket, {set_sock_opts, Opts}). set_all_opts(PacketSocket, Opts) -> call(PacketSocket, {set_all_opts, Opts}). + getstat(PacketSocket, Opts) -> call(PacketSocket, {getstat, Opts}). @@ -143,6 +151,8 @@ handle_call(close, _, #state{dtls_processes = Processes, end, queue:to_list(Accepters)), {reply, ok, State#state{close = true, accepters = queue:new()}} end; +handle_call(new_owner, _, State) -> + {reply, ok, State#state{close = false, first = true}}; handle_call({get_sock_opts, {SocketOptNames, EmOptNames}}, _, #state{listener = Socket, emulated_options = EmOpts} = State) -> case get_socket_opts(Socket, SocketOptNames) of diff --git a/lib/ssl/src/dtls_socket.erl b/lib/ssl/src/dtls_socket.erl index 81d9f3dcca..7c72c45bca 100644 --- a/lib/ssl/src/dtls_socket.erl +++ b/lib/ssl/src/dtls_socket.erl @@ -33,7 +33,9 @@ peername/2, sockname/2, port/2, - close/2]). + close/2, + close/1 + ]). -export([emulated_options/0, emulated_options/1, @@ -56,6 +58,7 @@ listen(Port, #config{transport_info = TransportInfo, dtls_listener_sup:register_listner({self(), Listner0}, Port), Result0; {ok, Listner0} = Result0 -> + dtls_packet_demux:new_owner(Listner0), dtls_packet_demux:set_all_opts(Listner0, {Options, emulated_socket_options(EmOpts0, #socket_options{}), SslOpts}), dtls_listener_sup:register_listner({self(), Listner0}, Port), Result0; @@ -96,6 +99,10 @@ connect(Address, Port, #config{transport_info = {Transport, _, _, _, _} = CbInfo Error end. +close(#sslsocket{pid = {dtls, #config{dtls_handler = {Pid, Port}}}}) -> + dtls_listener_sup:register_listner({undefined, Pid}, Port), + dtls_packet_demux:close(Pid). + close(_, dtls) -> ok; close(gen_udp, {_Client, _Socket}) -> diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src index c45e6bcf9a..63ccaabd74 100644 --- a/lib/ssl/src/ssl.app.src +++ b/lib/ssl/src/ssl.app.src @@ -74,5 +74,5 @@ {applications, [crypto, public_key, kernel, stdlib]}, {env, []}, {mod, {ssl_app, []}}, - {runtime_dependencies, ["stdlib-3.5","public_key-1.5","kernel-6.0", + {runtime_dependencies, ["stdlib-3.5","public_key-@OTP-16528@","kernel-6.0", "erts-10.0","crypto-4.2", "inets-5.10.7"]}]}. diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index af6c50f0c1..ee1664a82f 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -87,6 +87,7 @@ groups/1, format_error/1, renegotiate/1, + update_keys/2, prf/5, negotiated_protocol/1, connection_information/1, @@ -313,7 +314,8 @@ {padding_check, padding_check()} | {beast_mitigation, beast_mitigation()} | {ssl_imp, ssl_imp()} | - {session_tickets, session_tickets()}. + {session_tickets, session_tickets()} | + {key_update_at, key_update_at()}. -type protocol() :: tls | dtls. -type handshake_completion() :: hello | full. @@ -356,6 +358,7 @@ -type client_session_tickets() :: disabled | manual | auto. -type server_session_tickets() :: disabled | stateful | stateless. -type session_tickets() :: client_session_tickets() | server_session_tickets(). +-type key_update_at() :: pos_integer(). -type bloom_filter_window_size() :: integer(). -type bloom_filter_hash_functions() :: integer(). -type bloom_filter_bits() :: integer(). @@ -786,8 +789,8 @@ handshake_cancel(Socket) -> %%-------------------------------------------------------------------- close(#sslsocket{pid = [Pid|_]}) when is_pid(Pid) -> ssl_connection:close(Pid, {close, ?DEFAULT_TIMEOUT}); -close(#sslsocket{pid = {dtls, #config{dtls_handler = {Pid, _}}}}) -> - dtls_packet_demux:close(Pid); +close(#sslsocket{pid = {dtls, #config{dtls_handler = {_, _}}}} = DTLSListen) -> + dtls_socket:close(DTLSListen); close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_,_,_,_}}}}) -> Transport:close(ListenSocket). @@ -1351,6 +1354,28 @@ renegotiate(#sslsocket{pid = {dtls,_}}) -> renegotiate(#sslsocket{pid = {Listen,_}}) when is_port(Listen) -> {error, enotconn}. + +%%--------------------------------------------------------------- +-spec update_keys(SslSocket, Type) -> ok | {error, reason()} when + SslSocket :: sslsocket(), + Type :: write | read_write. +%% +%% Description: Initiate a key update. +%%-------------------------------------------------------------------- +update_keys(#sslsocket{pid = [Pid, Sender |_]}, Type0) when is_pid(Pid) andalso + is_pid(Sender) andalso + (Type0 =:= write orelse + Type0 =:= read_write) -> + Type = case Type0 of + write -> + update_not_requested; + read_write -> + update_requested + end, + tls_connection:send_key_update(Sender, Type); +update_keys(_, Type) -> + {error, {illegal_parameter, Type}}. + %%-------------------------------------------------------------------- -spec prf(SslSocket, Secret, Label, Seed, WantedLength) -> {ok, binary()} | {error, reason()} when @@ -1643,6 +1668,13 @@ handle_option(honor_ecc_order = Option, Value0, OptionsMap, #{role := Role}) -> handle_option(keyfile = Option, unbound, #{certfile := CertFile} = OptionsMap, _Env) -> Value = validate_option(Option, CertFile), OptionsMap#{Option => Value}; +handle_option(key_update_at = Option, unbound, OptionsMap, #{rules := Rules}) -> + Value = validate_option(Option, default_value(Option, Rules)), + OptionsMap#{Option => Value}; +handle_option(key_update_at = Option, Value0, #{versions := Versions} = OptionsMap, _Env) -> + assert_option_dependency(Option, versions, Versions, ['tlsv1.3']), + Value = validate_option(Option, Value0), + OptionsMap#{Option => Value}; handle_option(next_protocol_selector = Option, unbound, OptionsMap, #{rules := Rules}) -> Value = default_value(Option, Rules), OptionsMap#{Option => Value}; @@ -1973,6 +2005,9 @@ validate_option(keyfile, Value) when is_binary(Value) -> Value; validate_option(keyfile, Value) when is_list(Value), Value =/= "" -> binary_filename(Value); +validate_option(key_update_at, Value) when is_integer(Value) andalso + Value > 0 -> + Value; validate_option(password, Value) when is_list(Value) -> Value; @@ -2540,7 +2575,8 @@ include_security_info([Item | Items]) -> end. server_name_indication_default(Host) when is_list(Host) -> - Host; + %% SNI should not contain a trailing dot that a hostname may + string:strip(Host, right, $.); server_name_indication_default(_) -> undefined. diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl index d4b0cb1f14..33b91b09e7 100644 --- a/lib/ssl/src/ssl_alert.erl +++ b/lib/ssl/src/ssl_alert.erl @@ -201,8 +201,8 @@ description_txt(?UNSUPPORTED_EXTENSION) -> "Unsupported Extension"; description_txt(?CERTIFICATE_UNOBTAINABLE) -> "Certificate Unobtainable"; -description_txt(?UNRECOGNISED_NAME) -> - "Unrecognised Name"; +description_txt(?UNRECOGNIZED_NAME) -> + "Unrecognized Name"; description_txt(?BAD_CERTIFICATE_STATUS_RESPONSE) -> "Bad Certificate Status Response"; description_txt(?BAD_CERTIFICATE_HASH_VALUE) -> @@ -272,8 +272,8 @@ description_atom(?UNSUPPORTED_EXTENSION) -> unsupported_extension; description_atom(?CERTIFICATE_UNOBTAINABLE) -> certificate_unobtainable; -description_atom(?UNRECOGNISED_NAME) -> - unrecognised_name; +description_atom(?UNRECOGNIZED_NAME) -> + unrecognized_name; description_atom(?BAD_CERTIFICATE_STATUS_RESPONSE) -> bad_certificate_status_response; description_atom(?BAD_CERTIFICATE_HASH_VALUE) -> diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl index 0edc4bf8e4..6e562f0f33 100644 --- a/lib/ssl/src/ssl_alert.hrl +++ b/lib/ssl/src/ssl_alert.hrl @@ -107,7 +107,7 @@ -define(MISSING_EXTENSION, 109). -define(UNSUPPORTED_EXTENSION, 110). -define(CERTIFICATE_UNOBTAINABLE, 111). --define(UNRECOGNISED_NAME, 112). +-define(UNRECOGNIZED_NAME, 112). -define(BAD_CERTIFICATE_STATUS_RESPONSE, 113). -define(BAD_CERTIFICATE_HASH_VALUE, 114). -define(UNKNOWN_PSK_IDENTITY, 115). diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 20a080dc2b..b62a1c4546 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -53,7 +53,8 @@ %% Alert and close handling -export([handle_own_alert/4, handle_alert/3, handle_normal_shutdown/3, - handle_trusted_certs_db/1]). + handle_trusted_certs_db/1, + maybe_invalidate_session/6]). %% Data handling -export([read_application_data/2, internal_renegotiation/2]). @@ -325,6 +326,7 @@ dist_handshake_complete(ConnectionPid, DHandle) -> prf(ConnectionPid, Secret, Label, Seed, WantedLength) -> call(ConnectionPid, {prf, Secret, Label, Seed, WantedLength}). + %%==================================================================== %% Alert and close handling %%==================================================================== @@ -442,6 +444,13 @@ handle_alert(#alert{level = ?WARNING} = Alert, StateName, Alert#alert{role = opposite_role(Role)}), Connection:next_event(StateName, no_record, State). +maybe_invalidate_session(undefined,_, _, _, _, _) -> + ok; +maybe_invalidate_session({3, 4},_, _, _, _, _) -> + ok; +maybe_invalidate_session({3, N}, Type, Role, Host, Port, Session) when N < 4 -> + maybe_invalidate_session(Type, Role, Host, Port, Session). + %%==================================================================== %% Data handling %%==================================================================== @@ -1342,18 +1351,22 @@ handle_common_event(internal, {handshake, {#hello_request{}, _}}, StateName, when StateName =/= connection -> keep_state_and_data; handle_common_event(internal, {handshake, {Handshake, Raw}}, StateName, - #state{handshake_env = #handshake_env{tls_handshake_history = Hist0}} = State0, + #state{handshake_env = #handshake_env{tls_handshake_history = Hist0}, + connection_env = #connection_env{negotiated_version = Version}} = State0, Connection) -> PossibleSNI = Connection:select_sni_extension(Handshake), %% This function handles client SNI hello extension when Handshake is %% a client_hello, which needs to be determined by the connection callback. %% In other cases this is a noop - State = #state{handshake_env = HsEnv} = handle_sni_extension(PossibleSNI, State0), - - Hist = ssl_handshake:update_handshake_history(Hist0, Raw), - {next_state, StateName, State#state{handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}}, - [{next_event, internal, Handshake}]}; + case handle_sni_extension(PossibleSNI, State0) of + #state{handshake_env = HsEnv} = State -> + Hist = ssl_handshake:update_handshake_history(Hist0, Raw), + {next_state, StateName, State#state{handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}}, + [{next_event, internal, Handshake}]}; + #alert{} = Alert -> + handle_own_alert(Alert, Version, StateName, State0) + end; handle_common_event(internal, {protocol_record, TLSorDTLSRecord}, StateName, State, Connection) -> Connection:handle_protocol_record(TLSorDTLSRecord, StateName, State); handle_common_event(timeout, hibernate, _, _, _) -> @@ -1492,24 +1505,34 @@ handle_call(_,_,_,_,_) -> handle_info({ErrorTag, Socket, econnaborted}, StateName, #state{static_env = #static_env{role = Role, + host = Host, + port = Port, socket = Socket, transport_cb = Transport, error_tag = ErrorTag, trackers = Trackers, protocol_cb = Connection}, - start_or_recv_from = StartFrom - } = State) when StateName =/= connection -> + handshake_env = #handshake_env{renegotiation = Type}, + connection_env = #connection_env{negotiated_version = Version}, + session = Session, + start_or_recv_from = StartFrom + } = State) when StateName =/= connection -> + + maybe_invalidate_session(Version, Type, Role, Host, Port, Session), Pids = Connection:pids(State), alert_user(Pids, Transport, Trackers,Socket, - StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role, StateName, Connection), + StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role, StateName, Connection), {stop, {shutdown, normal}, State}; -handle_info({ErrorTag, Socket, Reason}, StateName, #state{static_env = #static_env{socket = Socket, - error_tag = ErrorTag}, +handle_info({ErrorTag, Socket, Reason}, StateName, #state{static_env = #static_env{ + role = Role, + socket = Socket, + error_tag = ErrorTag}, ssl_options = #{log_level := Level}} = State) -> ssl_logger:log(info, Level, #{description => "Socket error", reason => [{error_tag, ErrorTag}, {description, Reason}]}, ?LOCATION), - handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), + Alert = ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY, {transport_error, Reason}), + handle_normal_shutdown(Alert#alert{role = Role}, StateName, State), {stop, {shutdown,normal}, State}; handle_info({'DOWN', MonitorRef, _, _, Reason}, _, @@ -2797,9 +2820,11 @@ ssl_options_list([{Key, Value}|T], Acc) -> handle_active_option(false, connection = StateName, To, Reply, State) -> hibernate_after(StateName, State, [{reply, To, Reply}]); -handle_active_option(_, connection = StateName, To, _Reply, #state{connection_env = #connection_env{terminated = true}, +handle_active_option(_, connection = StateName, To, _Reply, #state{static_env = #static_env{role = Role}, + connection_env = #connection_env{terminated = true}, user_data_buffer = {_,0,_}} = State) -> - handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY, all_data_deliverd), StateName, + Alert = ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY, all_data_deliverd), + handle_normal_shutdown(Alert#alert{role = Role}, StateName, State#state{start_or_recv_from = To}), {stop,{shutdown, peer_close}, State}; handle_active_option(_, connection = StateName0, To, Reply, #state{static_env = #static_env{protocol_cb = Connection}, @@ -3004,6 +3029,11 @@ log_alert(Level, Role, ProtocolName, StateName, Alert) -> alert => Alert, alerter => peer}, Alert#alert.where). +maybe_invalidate_session({false, first}, server = Role, Host, Port, Session) -> + invalidate_session(Role, Host, Port, Session); +maybe_invalidate_session(_, _, _, _, _) -> + ok. + invalidate_session(client, Host, Port, Session) -> ssl_manager:invalidate_session(Host, Port, Session); invalidate_session(server, _, Port, Session) -> @@ -3011,9 +3041,16 @@ invalidate_session(server, _, Port, Session) -> handle_sni_extension(undefined, State) -> State; -handle_sni_extension(#sni{hostname = Hostname}, #state{static_env = #static_env{role = Role} = InitStatEnv0, - handshake_env = HsEnv, - connection_env = CEnv} = State0) -> +handle_sni_extension(#sni{hostname = Hostname}, State) -> + case is_sni_value(Hostname) of + true -> + handle_sni_extension(Hostname, State); + false -> + ?ALERT_REC(?FATAL, ?UNRECOGNIZED_NAME, {sni_included_trailing_dot, Hostname}) + end; +handle_sni_extension(Hostname, #state{static_env = #static_env{role = Role} = InitStatEnv0, + handshake_env = HsEnv, + connection_env = CEnv} = State0) -> NewOptions = update_ssl_options_from_sni(State0#state.ssl_options, Hostname), case NewOptions of undefined -> @@ -3070,3 +3107,11 @@ no_records(Extensions) -> maps:map(fun(_, Value) -> ssl_handshake:extension_value(Value) end, Extensions). + +is_sni_value(Hostname) -> + case hd(lists:reverse(Hostname)) of + $. -> + false; + _ -> + true + end. diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index ecbe905d28..3b33af95d0 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -402,6 +402,12 @@ certificate_verify(Signature, PublicKeyInfo, Version, %%-------------------------------------------------------------------- verify_signature(_Version, _Hash, {_HashAlgo, anon}, _Signature, _) -> true; +verify_signature({3, Minor}, Hash, {HashAlgo, rsa_pss_rsae}, Signature, {?rsaEncryption, PubKey, _PubKeyParams}) + when Minor >= 3 -> + public_key:verify({digest, Hash}, HashAlgo, Signature, PubKey, + [{rsa_padding, rsa_pkcs1_pss_padding}, + {rsa_pss_saltlen, -1}, + {rsa_mgf1_md, HashAlgo}]); verify_signature({3, Minor}, Hash, {HashAlgo, rsa}, Signature, {?rsaEncryption, PubKey, _PubKeyParams}) when Minor >= 3 -> public_key:verify({digest, Hash}, HashAlgo, Signature, PubKey); @@ -2356,6 +2362,20 @@ dec_server_key_params(Len, Keys, Version) -> <<Params:Len/bytes, Signature/binary>> = Keys, dec_server_key_signature(Params, Signature, Version). +dec_server_key_signature(Params, <<?BYTE(8), ?BYTE(SignAlgo), + ?UINT16(0)>>, {Major, Minor}) + when Major == 3, Minor >= 3 -> + <<?UINT16(Scheme0)>> = <<?BYTE(8), ?BYTE(SignAlgo)>>, + Scheme = ssl_cipher:signature_scheme(Scheme0), + {Hash, Sign, _} = ssl_cipher:scheme_to_components(Scheme), + {Params, {Hash, Sign}, <<>>}; +dec_server_key_signature(Params, <<?BYTE(8), ?BYTE(SignAlgo), + ?UINT16(Len), Signature:Len/binary>>, {Major, Minor}) + when Major == 3, Minor >= 3 -> + <<?UINT16(Scheme0)>> = <<?BYTE(8), ?BYTE(SignAlgo)>>, + Scheme = ssl_cipher:signature_scheme(Scheme0), + {Hash, Sign, _} = ssl_cipher:scheme_to_components(Scheme), + {Params, {Hash, Sign}, Signature}; dec_server_key_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo), ?UINT16(0)>>, {Major, Minor}) when Major == 3, Minor >= 3 -> @@ -2464,8 +2484,17 @@ decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), when Version =:= {3,4} -> SignSchemeListLen = Len - 2, <<?UINT16(SignSchemeListLen), SignSchemeList/binary>> = ExtData, - SignSchemes = [ssl_cipher:signature_scheme(SignScheme) || - <<?UINT16(SignScheme)>> <= SignSchemeList], + %% Ignore unknown signature algorithms + Fun = fun(Elem) -> + case ssl_cipher:signature_scheme(Elem) of + unassigned -> + false; + Value -> + {true, Value} + end + end, + SignSchemes= lists:filtermap(Fun, [SignScheme || + <<?UINT16(SignScheme)>> <= SignSchemeList]), decode_extensions(Rest, Version, MessageType, Acc#{signature_algs => #signature_algorithms{ @@ -2475,8 +2504,17 @@ decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_CERT_EXT), ?UINT16(Len), ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) -> SignSchemeListLen = Len - 2, <<?UINT16(SignSchemeListLen), SignSchemeList/binary>> = ExtData, - SignSchemes = [ssl_cipher:signature_scheme(SignScheme) || - <<?UINT16(SignScheme)>> <= SignSchemeList], + %% Ignore unknown signature algorithms + Fun = fun(Elem) -> + case ssl_cipher:signature_scheme(Elem) of + unassigned -> + false; + Value -> + {true, Value} + end + end, + SignSchemes= lists:filtermap(Fun, [SignScheme || + <<?UINT16(SignScheme)>> <= SignSchemeList]), decode_extensions(Rest, Version, MessageType, Acc#{signature_algs_cert => #signature_algorithms_cert{ @@ -2651,11 +2689,18 @@ decode_client_shares(ClientShares) -> %% decode_client_shares(<<>>, Acc) -> lists:reverse(Acc); -decode_client_shares(<<?UINT16(Group),?UINT16(Len),KeyExchange:Len/binary,Rest/binary>>, Acc) -> - decode_client_shares(Rest, [#key_share_entry{ - group = tls_v1:enum_to_group(Group), - key_exchange= KeyExchange - }|Acc]). +decode_client_shares(<<?UINT16(Group0),?UINT16(Len),KeyExchange:Len/binary,Rest/binary>>, Acc) -> + case tls_v1:enum_to_group(Group0) of + undefined -> + %% Ignore key_share with unknown group + decode_client_shares(Rest, Acc); + Group -> + decode_client_shares(Rest, [#key_share_entry{ + group = Group, + key_exchange= KeyExchange + }|Acc]) + end. + decode_next_protocols({next_protocol_negotiation, Protocols}) -> decode_protocols(Protocols, []). @@ -2681,7 +2726,10 @@ decode_psk_key_exchange_modes(<<>>, Acc) -> decode_psk_key_exchange_modes(<<?BYTE(?PSK_KE), Rest/binary>>, Acc) -> decode_psk_key_exchange_modes(Rest, [psk_ke|Acc]); decode_psk_key_exchange_modes(<<?BYTE(?PSK_DHE_KE), Rest/binary>>, Acc) -> - decode_psk_key_exchange_modes(Rest, [psk_dhe_ke|Acc]). + decode_psk_key_exchange_modes(Rest, [psk_dhe_ke|Acc]); +%% Ignore unknown PskKeyExchangeModes +decode_psk_key_exchange_modes(<<?BYTE(_), Rest/binary>>, Acc) -> + decode_psk_key_exchange_modes(Rest, Acc). decode_psk_identities(Identities) -> diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index ceca206605..d661de323c 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -109,6 +109,12 @@ -define('24H_in_msec', 86400000). -define('24H_in_sec', 86400). +%% https://tools.ietf.org/html/rfc8446#section-5.5 +%% Limits on Key Usage +%% http://www.isg.rhul.ac.uk/~kp/TLS-AEbounds.pdf +%% Number of records * Record length +%% 2^24.5 * 2^14 = 2^38.5 +-define(KEY_USAGE_LIMIT_AES_GCM, 388736063997). %% This map stores all supported options with default values and %% list of dependencies: @@ -144,6 +150,7 @@ key => {undefined, [versions]}, keyfile => {undefined, [versions, certfile]}, + key_update_at => {?KEY_USAGE_LIMIT_AES_GCM, [versions]}, log_level => {notice, [versions]}, max_handshake_size => {?DEFAULT_MAX_HANDSHAKE_SIZE, [versions]}, next_protocol_selector => {undefined, [versions]}, diff --git a/lib/ssl/src/ssl_logger.erl b/lib/ssl/src/ssl_logger.erl index 9ee0c23aaa..66de98c53a 100644 --- a/lib/ssl/src/ssl_logger.erl +++ b/lib/ssl/src/ssl_logger.erl @@ -244,6 +244,11 @@ parse_handshake(Direction, #new_session_ticket{} = NewSessionTicket) -> Header = io_lib:format("~s Post-Handshake, NewSessionTicket", [header_prefix(Direction)]), Message = io_lib:format("~p", [?rec_info(new_session_ticket, NewSessionTicket)]), + {Header, Message}; +parse_handshake(Direction, #key_update{} = KeyUpdate) -> + Header = io_lib:format("~s Post-Handshake, KeyUpdate", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(key_update, KeyUpdate)]), {Header, Message}. diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl index e37e3f714f..fb2f31b83c 100644 --- a/lib/ssl/src/ssl_record.hrl +++ b/lib/ssl/src/ssl_record.hrl @@ -67,6 +67,7 @@ compression_algorithm, % unit 8 master_secret, % opaque 48 resumption_master_secret, + application_traffic_secret, client_random, % opaque 32 server_random, % opaque 32 exportable % boolean @@ -74,7 +75,7 @@ -define(INITIAL_BYTES, 5). --define(MAX_SEQENCE_NUMBER, 18446744073709551615). %% (1 bsl 64) - 1 = 18446744073709551615 +-define(MAX_SEQUENCE_NUMBER, 18446744073709551615). %% (1 bsl 64) - 1 = 18446744073709551615 %% Sequence numbers cannot wrap so when max is about to be reached we should renegotiate. %% We will renegotiate a little before so that there will be sequence numbers left %% for the rehandshake and a little data. Currently we decided to renegotiate a little more @@ -152,6 +153,7 @@ -define(MAX_PLAIN_TEXT_LENGTH, 16384). -define(MAX_COMPRESSED_LENGTH, (?MAX_PLAIN_TEXT_LENGTH+1024)). -define(MAX_CIPHER_TEXT_LENGTH, (?MAX_PLAIN_TEXT_LENGTH+2048)). +-define(TLS13_MAX_CIPHER_TEXT_LENGTH, (?MAX_PLAIN_TEXT_LENGTH+256)). %% -record(protocol_version, { %% major, % unit 8 diff --git a/lib/ssl/src/tls_client_ticket_store.erl b/lib/ssl/src/tls_client_ticket_store.erl index 6343b9cf0b..7386b0363f 100644 --- a/lib/ssl/src/tls_client_ticket_store.erl +++ b/lib/ssl/src/tls_client_ticket_store.erl @@ -294,7 +294,7 @@ store_ticket(#state{db = Db0, max = Max} = State, Ticket, HKDF, SNI, PSK) -> true -> Db0 end, - Key = erlang:monotonic_time(), + Key = {erlang:monotonic_time(), erlang:unique_integer([monotonic])}, Db = gb_trees:insert(Key, #data{hkdf = HKDF, sni = SNI, diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index ed11cfd985..7257e1dea5 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -88,7 +88,7 @@ %% gen_statem callbacks -export([callback_mode/0, terminate/3, code_change/4, format_status/2]). --export([encode_handshake/4]). +-export([encode_handshake/4, send_key_update/2, update_cipher_key/2]). -define(DIST_CNTRL_SPAWN_OPTS, [{priority, max}]). @@ -221,7 +221,7 @@ activate_socket(#state{protocol_specific = #{active_n_toggle := true, active_n : next_record(State, CipherTexts, ConnectionStates, Check) -> next_record(State, CipherTexts, ConnectionStates, Check, []). %% -next_record(#state{connection_env = #connection_env{negotiated_version = Version}} = State, +next_record(#state{connection_env = #connection_env{negotiated_version = {3,4} = Version}} = State, [CT|CipherTexts], ConnectionStates0, Check, Acc) -> case tls_record:decode_cipher_text(Version, CT, ConnectionStates0, Check) of {#ssl_tls{type = ?APPLICATION_DATA, fragment = Fragment}, ConnectionStates} -> @@ -242,10 +242,41 @@ next_record(#state{connection_env = #connection_env{negotiated_version = Version %% Not ?APPLICATION_DATA but we have accumulated fragments %% -> build an ?APPLICATION_DATA record with concatenated fragments %% and forget about decrypting this record - we'll decrypt it again next time + %% Will not work for stream ciphers next_record_done(State, [CT|CipherTexts], ConnectionStates0, #ssl_tls{type = ?APPLICATION_DATA, fragment = iolist_to_binary(lists:reverse(Acc))}); #alert{} = Alert -> Alert + end; +next_record(#state{connection_env = #connection_env{negotiated_version = Version}} = State, + [#ssl_tls{type = ?APPLICATION_DATA} = CT |CipherTexts], ConnectionStates0, Check, Acc) -> + case tls_record:decode_cipher_text(Version, CT, ConnectionStates0, Check) of + {#ssl_tls{type = ?APPLICATION_DATA, fragment = Fragment}, ConnectionStates} -> + case CipherTexts of + [] -> + %% End of cipher texts - build and deliver an ?APPLICATION_DATA record + %% from the accumulated fragments + next_record_done(State, [], ConnectionStates, + #ssl_tls{type = ?APPLICATION_DATA, + fragment = iolist_to_binary(lists:reverse(Acc, [Fragment]))}); + [_|_] -> + next_record(State, CipherTexts, ConnectionStates, Check, [Fragment|Acc]) + end; + #alert{} = Alert -> + Alert + end; +next_record(State, CipherTexts, ConnectionStates, _, [_|_] = Acc) -> + next_record_done(State, CipherTexts, ConnectionStates, + #ssl_tls{type = ?APPLICATION_DATA, + fragment = iolist_to_binary(lists:reverse(Acc))}); +next_record(#state{connection_env = #connection_env{negotiated_version = Version}} = State, + [CT|CipherTexts], ConnectionStates0, Check, []) -> + case tls_record:decode_cipher_text(Version, CT, ConnectionStates0, Check) of + {Record, ConnectionStates} -> + %% Singelton non-?APPLICATION_DATA record - deliver + next_record_done(State, CipherTexts, ConnectionStates, Record); + #alert{} = Alert -> + Alert end. next_record_done(#state{protocol_buffers = Buffers} = State, CipherTexts, ConnectionStates, Record) -> @@ -256,14 +287,14 @@ next_record_done(#state{protocol_buffers = Buffers} = State, CipherTexts, Connec next_event(StateName, Record, State) -> next_event(StateName, Record, State, []). %% -next_event(StateName, no_record, State0, Actions) -> +next_event(StateName, no_record, #state{static_env = #static_env{role = Role}} = State0, Actions) -> case next_record(StateName, State0) of {no_record, State} -> ssl_connection:hibernate_after(StateName, State, Actions); {Record, State} -> next_event(StateName, Record, State, Actions); #alert{} = Alert -> - ssl_connection:handle_normal_shutdown(Alert, StateName, State0), + ssl_connection:handle_normal_shutdown(Alert#alert{role = Role}, StateName, State0), {stop, {shutdown, own_alert}, State0} end; next_event(StateName, #ssl_tls{} = Record, State, Actions) -> @@ -278,23 +309,21 @@ handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, Stat case ssl_connection:read_application_data(Data, State0) of {stop, _, _} = Stop-> Stop; - {Record, #state{start_or_recv_from = Caller} = State1} -> + {Record, #state{start_or_recv_from = Caller} = State} -> TimerAction = case Caller of undefined -> %% Passive recv complete cancel timer [{{timeout, recv}, infinity, timeout}]; _ -> [] end, - {next_state, StateName, State, Actions} = next_event(StateName, Record, State1, TimerAction), - ssl_connection:hibernate_after(StateName, State, Actions) + next_event(StateName, Record, State, TimerAction) end; handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, State0) -> case ssl_connection:read_application_data(Data, State0) of {stop, _, _} = Stop-> Stop; - {Record, State1} -> - {next_state, StateName, State, Actions} = next_event(StateName, Record, State1), - ssl_connection:hibernate_after(StateName, State, Actions) + {Record, State} -> + next_event(StateName, Record, State) end; %%% TLS record protocol level handshake messages handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, fragment = Data}, @@ -387,7 +416,6 @@ renegotiate(#state{static_env = #static_env{role = server, send_handshake(Handshake, State) -> send_handshake_flight(queue_handshake(Handshake, State)). - queue_handshake(Handshake, #state{handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv, connection_env = #connection_env{negotiated_version = Version}, flight_buffer = Flight0, @@ -706,7 +734,7 @@ hello(internal, #server_hello{} = Hello, [{next_event, internal, Hello}]} end; hello(info, Event, State) -> - gen_info(Event, ?FUNCTION_NAME, State); + handle_info(Event, ?FUNCTION_NAME, State); hello(Type, Event, State) -> gen_handshake(?FUNCTION_NAME, Type, Event, State). @@ -753,15 +781,19 @@ connection({call, From}, {user_renegotiate, WriteState}, [{next_event,{call, From}, renegotiate}]}; connection({call, From}, {close, {Pid, _Timeout}}, - #state{connection_env = #connection_env{terminated = closed} =CEnv} = State) -> + #state{connection_env = #connection_env{terminated = closed} = CEnv, + protocol_specific = PS} = State) -> {next_state, downgrade, State#state{connection_env = - CEnv#connection_env{terminated = true, - downgrade = {Pid, From}}}, + CEnv#connection_env{terminated = true, + downgrade = {Pid, From}}, + protocol_specific = PS#{active_n_toggle => true, + active_n => 1} + }, [{next_event, internal, ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY)}]}; connection({call, From}, {close,{Pid, Timeout}}, #state{connection_states = ConnectionStates, - protocol_specific = #{sender := Sender}, + protocol_specific = #{sender := Sender} = PS, connection_env = CEnv } = State0) -> case tls_sender:downgrade(Sender, Timeout) of @@ -775,7 +807,10 @@ connection({call, From}, ConnectionStates#{current_write => Write}}), {next_state, downgrade, State#state{connection_env = CEnv#connection_env{downgrade = {Pid, From}, - terminated = true}}, + terminated = true}, + protocol_specific = PS#{active_n_toggle => true, + active_n => 1} + }, [{timeout, Timeout, downgrade}]}; {error, timeout} -> {stop_and_reply, {shutdown, downgrade_fail}, [{reply, From, {error, timeout}}]} @@ -850,6 +885,16 @@ connection(internal, #new_session_ticket{} = NewSessionTicket, State) -> handle_new_session_ticket(NewSessionTicket, State), next_event(?FUNCTION_NAME, no_record, State); +connection(internal, #key_update{} = KeyUpdate, State0) -> + %% TLS 1.3 + case handle_key_update(KeyUpdate, State0) of + {ok, State} -> + next_event(?FUNCTION_NAME, no_record, State); + {error, State, Alert} -> + ssl_connection:handle_own_alert(Alert, {3,4}, connection, State), + next_event(?FUNCTION_NAME, no_record, State) + end; + connection(Type, Event, State) -> ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE). @@ -1077,6 +1122,7 @@ initialize_tls_sender(#state{static_env = #static_env{ connection_env = #connection_env{negotiated_version = Version}, socket_options = SockOpts, ssl_options = #{renegotiate_at := RenegotiateAt, + key_update_at := KeyUpdateAt, log_level := LogLevel}, connection_states = #{current_write := ConnectionWriteState}, protocol_specific = #{sender := Sender}}) -> @@ -1088,6 +1134,7 @@ initialize_tls_sender(#state{static_env = #static_env{ transport_cb => Transport, negotiated_version => Version, renegotiate_at => RenegotiateAt, + key_update_at => KeyUpdateAt, log_level => LogLevel}, tls_sender:initialize(Sender, Init). @@ -1151,8 +1198,24 @@ handle_info({PassiveTag, Socket}, StateName, next_event(StateName, no_record, State#state{protocol_specific = PS#{active_n_toggle => true}}); handle_info({CloseTag, Socket}, StateName, - #state{static_env = #static_env{socket = Socket, close_tag = CloseTag}, + #state{static_env = #static_env{ + role = Role, + host = Host, + port = Port, + socket = Socket, + close_tag = CloseTag}, + handshake_env = #handshake_env{renegotiation = Type}, connection_env = #connection_env{negotiated_version = Version}, + session = Session} = State) when StateName =/= connection -> + ssl_connection:maybe_invalidate_session(Version, Type, Role, Host, Port, Session), + Alert = ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY, transport_closed), + ssl_connection:handle_normal_shutdown(Alert#alert{role = Role}, StateName, State), + {stop, {shutdown, transport_closed}, State}; +handle_info({CloseTag, Socket}, StateName, + #state{static_env = #static_env{ + role = Role, + socket = Socket, + close_tag = CloseTag}, socket_options = #socket_options{active = Active}, protocol_buffers = #protocol_buffers{tls_cipher_texts = CTs}, user_data_buffer = {_,BufferSize,_}, @@ -1165,18 +1228,18 @@ handle_info({CloseTag, Socket}, StateName, case (Active == false) andalso ((CTs =/= []) or (BufferSize =/= 0)) of false -> - case Version of - {1, N} when N >= 1 -> - ok; - _ -> - %% As invalidate_sessions here causes performance issues, - %% we will conform to the widespread implementation - %% practice and go aginst the spec - %%invalidate_session(Role, Host, Port, Session) - ok - end, - - ssl_connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), + %% As invalidate_sessions here causes performance issues, + %% we will conform to the widespread implementation + %% practice and go aginst the spec + %% case Version of + %% {3, N} when N >= 1 -> + %% ok; + %% _ -> + %% invalidate_session(Role, Host, Port, Session) + %% ok + %% end, + Alert = ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY, transport_closed), + ssl_connection:handle_normal_shutdown(Alert#alert{role = Role}, StateName, State), {stop, {shutdown, transport_closed}, State}; true -> %% Fixes non-delivery of final TLS record in {active, once}. @@ -1405,6 +1468,50 @@ handle_new_session_ticket(#new_session_ticket{ticket_nonce = Nonce} = NewSession tls_client_ticket_store:store_ticket(NewSessionTicket, HKDF, SNI, PSK). +handle_key_update(#key_update{request_update = update_not_requested}, State0) -> + %% Update read key in connection + {ok, update_cipher_key(current_read, State0)}; +handle_key_update(#key_update{request_update = update_requested}, + #state{protocol_specific = #{sender := Sender}} = State0) -> + %% Update read key in connection + State1 = update_cipher_key(current_read, State0), + %% Send key_update and update sender's write key + case send_key_update(Sender, update_not_requested) of + ok -> + {ok, State1}; + {error, Reason} -> + {error, State1, ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason)} + end. + + +update_cipher_key(ConnStateName, #state{connection_states = CS0} = State0) -> + CS = update_cipher_key(ConnStateName, CS0), + State0#state{connection_states = CS}; +update_cipher_key(ConnStateName, CS0) -> + #{security_parameters := SecParams0, + cipher_state := CipherState0} = ConnState0 = maps:get(ConnStateName, CS0), + HKDF = SecParams0#security_parameters.prf_algorithm, + CipherSuite = SecParams0#security_parameters.cipher_suite, + ApplicationTrafficSecret0 = SecParams0#security_parameters.application_traffic_secret, + ApplicationTrafficSecret = tls_v1:update_traffic_secret(HKDF, ApplicationTrafficSecret0), + + %% Calculate traffic keys + #{cipher := Cipher} = ssl_cipher_format:suite_bin_to_map(CipherSuite), + {Key, IV} = tls_v1:calculate_traffic_keys(HKDF, Cipher, ApplicationTrafficSecret), + + SecParams = SecParams0#security_parameters{application_traffic_secret = ApplicationTrafficSecret}, + CipherState = CipherState0#cipher_state{key = Key, iv = IV}, + ConnState = ConnState0#{security_parameters => SecParams, + cipher_state => CipherState, + sequence_number => 0}, + CS0#{ConnStateName => ConnState}. + + +send_key_update(Sender, Type) -> + KeyUpdate = tls_handshake_1_3:key_update(Type), + tls_sender:send_post_handshake(Sender, KeyUpdate). + + %% Send ticket data to user as opaque binary send_ticket_data(User, NewSessionTicket, HKDF, SNI, PSK) -> Timestamp = erlang:system_time(seconds), diff --git a/lib/ssl/src/tls_connection_1_3.erl b/lib/ssl/src/tls_connection_1_3.erl index 6fb96b1f5a..b424f8e9e7 100644 --- a/lib/ssl/src/tls_connection_1_3.erl +++ b/lib/ssl/src/tls_connection_1_3.erl @@ -191,7 +191,8 @@ wait_finished(internal, ssl_connection:handle_own_alert(Alert, {3,4}, finished, State0); State1 -> {Record, State} = ssl_connection:prepare_connection(State1, Module), - tls_connection:next_event(connection, Record, State) + tls_connection:next_event(connection, Record, State, + [{{timeout, handshake}, cancel}]) end; wait_finished(Type, Msg, State, Connection) -> ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). @@ -217,8 +218,6 @@ wait_ee(internal, #change_cipher_spec{}, State, _Module) -> tls_connection:next_event(?FUNCTION_NAME, no_record, State); wait_ee(internal, #encrypted_extensions{} = EE, State0, _Module) -> case tls_handshake_1_3:do_wait_ee(EE, State0) of - #alert{} = Alert -> - ssl_connection:handle_own_alert(Alert, {3,4}, wait_ee, State0); {State1, NextState} -> tls_connection:next_event(NextState, no_record, State1) end; diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 474cbd621b..b6bdba4cb6 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -411,6 +411,8 @@ get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length), ssl_logger:debug(LogLevel, inbound, 'handshake', Handshake), get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc]) catch + throw:#alert{} = Alert -> + throw(Alert); _:_ -> throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, handshake_decode_error)) end; diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index dfd82f5b64..8c5d652035 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -39,7 +39,8 @@ %% Create handshake messages -export([certificate/5, certificate_verify/4, - encrypted_extensions/1]). + encrypted_extensions/1, + key_update/1]). -export([do_start/2, do_negotiated/2, @@ -189,7 +190,7 @@ certificate(OwnCert, CertDbHandle, CertDbRef, _CRContext, Role) -> certificate_request_context = <<>>, certificate_list = CertList}}; {error, Error} when Role =:= server -> - {error, {no_suitable_certificates, Error}}; + {error, ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {no_suitable_certificates, Error})}; {error, _Error} when Role =:= client -> %% The client MUST send a Certificate message if and only if the server %% has requested client authentication via a CertificateRequest message @@ -229,9 +230,8 @@ certificate_verify(PrivateKey, SignatureScheme, algorithm = SignatureScheme, signature = Signature }}; - {error, badarg} -> - {error, badarg} - + {error, #alert{} = Alert} -> + {error, Alert} end. @@ -251,6 +251,10 @@ finished(#state{connection_states = ConnectionStates, }. +key_update(Type) -> + #key_update{request_update = Type}. + + %%==================================================================== %% Encode handshake %%==================================================================== @@ -291,7 +295,8 @@ encode_handshake(#new_session_ticket{ encode_handshake(#end_of_early_data{}) -> {?END_OF_EARLY_DATA, <<>>}; encode_handshake(#key_update{request_update = Update}) -> - {?KEY_UPDATE, <<?BYTE(Update)>>}; + EncUpdate = encode_key_update(Update), + {?KEY_UPDATE, <<EncUpdate/binary>>}; encode_handshake(HandshakeMsg) -> ssl_handshake:encode_handshake(HandshakeMsg, {3,4}). @@ -361,7 +366,7 @@ decode_handshake(?NEW_SESSION_TICKET, <<?UINT32(LifeTime), ?UINT32(Age), decode_handshake(?END_OF_EARLY_DATA, _) -> #end_of_early_data{}; decode_handshake(?KEY_UPDATE, <<?BYTE(Update)>>) -> - #key_update{request_update = Update}; + #key_update{request_update = decode_key_update(Update)}; decode_handshake(Tag, HandshakeMsg) -> ssl_handshake:decode_handshake({3,4}, Tag, HandshakeMsg). @@ -408,6 +413,26 @@ encode_signature(Signature) -> Size = byte_size(Signature), <<?UINT16(Size), Signature/binary>>. +encode_key_update(update_not_requested) -> + <<?BYTE(0)>>; +encode_key_update(update_requested) -> + <<?BYTE(1)>>. + +%% enum { +%% update_not_requested(0), update_requested(1), (255) +%% } KeyUpdateRequest; +%% +%% request_update: Indicates whether the recipient of the KeyUpdate +%% should respond with its own KeyUpdate. If an implementation +%% receives any other value, it MUST terminate the connection with an +%% "illegal_parameter" alert. +decode_key_update(0) -> + update_not_requested; +decode_key_update(1) -> + update_requested; +decode_key_update(N) -> + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER, {request_update,N})). + decode_cert_entries(Entries) -> decode_cert_entries(Entries, []). @@ -421,6 +446,7 @@ decode_cert_entries(<<?UINT24(DSize), Data:DSize/binary, ?UINT16(Esize), BinExts encode_extensions(Exts)-> ssl_handshake:encode_extensions(extensions_list(Exts)). + decode_extensions(Exts, MessageType) -> ssl_handshake:decode_extensions(Exts, {3,4}, MessageType). @@ -467,7 +493,7 @@ sign(THash, Context, HashAlgo, #'ECPrivateKey'{} = PrivateKey) -> {ok, Signature} catch error:badarg -> - {error, badarg} + {error, ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, badarg)} end; sign(THash, Context, HashAlgo, PrivateKey) -> Content = build_content(Context, THash), @@ -482,7 +508,7 @@ sign(THash, Context, HashAlgo, PrivateKey) -> {ok, Signature} catch error:badarg -> - {error, badarg} + {error, ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, badarg)} end. @@ -493,7 +519,7 @@ verify(THash, Context, HashAlgo, Signature, {?'id-ecPublicKey', PublicKey, Publi {ok, Result} catch error:badarg -> - {error, badarg} + {error, ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, badarg)} end; verify(THash, Context, HashAlgo, Signature, {?rsaEncryption, PublicKey, _PubKeyParams}) -> Content = build_content(Context, THash), @@ -508,7 +534,7 @@ verify(THash, Context, HashAlgo, Signature, {?rsaEncryption, PublicKey, _PubKeyP {ok, Result} catch error:badarg -> - {error, badarg} + {error, ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, badarg)} end. @@ -606,18 +632,8 @@ do_start(#client_hello{cipher_suites = ClientCiphers, Maybe(session_resumption(NextStateTuple, PSK)) end catch - {Ref, {insufficient_security, no_suitable_groups}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); - {Ref, illegal_parameter} -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); - {Ref, no_suitable_cipher} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher); - {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); - {Ref, {insufficient_security, no_suitable_public_key}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key); - {Ref, no_application_protocol} -> - ?ALERT_REC(?FATAL, ?NO_APPLICATION_PROTOCOL) + {Ref, #alert{} = Alert} -> + Alert end; %% TLS Client do_start(#server_hello{cipher_suite = SelectedCipherSuite, @@ -696,8 +712,8 @@ do_start(#server_hello{cipher_suite = SelectedCipherSuite, {State, wait_sh} catch - {Ref, {illegal_parameter, Reason}} -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER, Reason) + {Ref, #alert{} = Alert} -> + Alert end. @@ -757,10 +773,8 @@ do_negotiated({start_handshake, PSK0}, {State9, NextState} catch - {Ref, badarg} -> - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {digitally_sign, badarg}); - {Ref, {no_suitable_certificates, Reason}} -> - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {no_suitable_certificates, Reason}) + {Ref, #alert{} = Alert} -> + Alert end. @@ -769,17 +783,9 @@ do_wait_cert(#certificate_1_3{} = Certificate, State0) -> try Maybe(process_certificate(Certificate, State0)) catch - {Ref, {certificate_required, State}} -> - {?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required), State}; - {Ref, {{certificate_unknown, Reason}, State}} -> - {?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, Reason), State}; - {Ref, {{internal_error, Reason}, State}} -> - {?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason), State}; - {Ref, {{handshake_failure, Reason}, State}} -> - {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason), State}; + {Ref, #alert{} = Alert} -> + {Alert, State0}; {Ref, {#alert{} = Alert, State}} -> - {Alert, State}; - {#alert{} = Alert, State} -> {Alert, State} end. @@ -790,12 +796,8 @@ do_wait_cv(#certificate_verify_1_3{} = CertificateVerify, State0) -> State1 = Maybe(verify_signature_algorithm(State0, CertificateVerify)), Maybe(verify_certificate_verify(State1, CertificateVerify)) catch - {Ref, {{bad_certificate, Reason}, State}} -> - {?ALERT_REC(?FATAL, ?BAD_CERTIFICATE, {bad_certificate, Reason}), State}; - {Ref, {badarg, State}} -> - {?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {verify, badarg}), State}; - {Ref, {{handshake_failure, Reason}, State}} -> - {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {handshake_failure, Reason}), State} + {Ref, {#alert{} = Alert, State}} -> + {Alert, State} end. %% TLS Server @@ -804,29 +806,30 @@ do_wait_finished(#finished{verify_data = VerifyData}, {Ref,Maybe} = maybe(), try - Maybe(validate_client_finished(State0, VerifyData)), + Maybe(validate_finished(State0, VerifyData)), State1 = calculate_traffic_secrets(State0), State2 = maybe_calculate_resumption_master_secret(State1), + State3 = forget_master_secret(State2), %% Configure traffic keys - State3 = ssl_record:step_encryption_state(State2), + State4 = ssl_record:step_encryption_state(State3), %% Send session ticket - maybe_send_session_ticket(State3) + maybe_send_session_ticket(State4) catch - {Ref, decrypt_error} -> - ?ALERT_REC(?FATAL, ?DECRYPT_ERROR, decrypt_error) + {Ref, #alert{} = Alert} -> + Alert end; %% TLS Client -do_wait_finished(#finished{verify_data = _VerifyData}, +do_wait_finished(#finished{verify_data = VerifyData}, #state{static_env = #static_env{role = client}} = State0) -> {Ref,Maybe} = maybe(), try - %% Maybe(validate_client_finished(State0, VerifyData)), + Maybe(validate_finished(State0, VerifyData)), %% Maybe send Certificate + CertificateVerify State1 = Maybe(maybe_queue_cert_cert_cv(State0)), @@ -841,17 +844,14 @@ do_wait_finished(#finished{verify_data = _VerifyData}, State4 = calculate_traffic_secrets(State3), State5 = maybe_calculate_resumption_master_secret(State4), + State6 = forget_master_secret(State5), %% Configure traffic keys - ssl_record:step_encryption_state(State5) + ssl_record:step_encryption_state(State6) catch - {Ref, decrypt_error} -> - ?ALERT_REC(?FATAL, ?DECRYPT_ERROR, decrypt_error); - {Ref, badarg} -> - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {digitally_sign, badarg}); - {Ref, {no_suitable_certificates, Reason}} -> - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {no_suitable_certificates, Reason}) + {Ref, #alert{} = Alert} -> + Alert end. @@ -909,16 +909,8 @@ do_wait_sh(#server_hello{cipher_suite = SelectedCipherSuite, catch {Ref, {State, StateName, ServerHello}} -> {State, StateName, ServerHello}; - {Ref, {insufficient_security, no_suitable_groups}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); - {Ref, illegal_parameter} -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); - {Ref, no_suitable_cipher} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher); - {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); - {Ref, {insufficient_security, no_suitable_public_key}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key) + {Ref, #alert{} = Alert} -> + Alert end. @@ -939,16 +931,6 @@ do_wait_ee(#encrypted_extensions{extensions = Extensions}, State0) -> {State1, wait_cert_cr} catch - {Ref, {insufficient_security, no_suitable_groups}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); - {Ref, illegal_parameter} -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); - {Ref, no_suitable_cipher} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher); - {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); - {Ref, {insufficient_security, no_suitable_public_key}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key); {Ref, {State, StateName}} -> {State, StateName} end. @@ -959,14 +941,8 @@ do_wait_cert_cr(#certificate_1_3{} = Certificate, State0) -> try Maybe(process_certificate(Certificate, State0)) catch - {Ref, {certificate_required, _State}} -> - ?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required); - {Ref, {{certificate_unknown, Reason}, _State}} -> - ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, Reason); - {Ref, {{internal_error, Reason}, _State}} -> - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason); - {Ref, {{handshake_failure, Reason}, _State}} -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason); + {Ref, #alert{} = Alert} -> + {Alert, State0}; {Ref, {#alert{} = Alert, State}} -> {Alert, State} end; @@ -975,30 +951,11 @@ do_wait_cert_cr(#certificate_request_1_3{} = CertificateRequest, State0) -> try Maybe(process_certificate_request(CertificateRequest, State0)) catch - {Ref, {certificate_required, _State}} -> - ?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required); - {Ref, {{certificate_unknown, Reason}, _State}} -> - ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, Reason); - {Ref, {illegal_parameter, Reason}} -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER, Reason); - {Ref, {{internal_error, Reason}, _State}} -> - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason); - {Ref, {{handshake_failure, Reason}, _State}} -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason) + {Ref, #alert{} = Alert} -> + {Alert, State0} end. - -%% TODO: Remove this function! -%% not_implemented(State, Reason) -> -%% {error, {not_implemented, State, Reason}}. - -%% not_implemented(update_secrets, State0, Reason) -> -%% State1 = calculate_traffic_secrets(State0), -%% State = ssl_record:step_encryption_state(State1), -%% {error, {not_implemented, State, Reason}}. - - %% For reasons of backward compatibility with middleboxes (see %% Appendix D.4), the HelloRetryRequest message uses the same structure %% as the ServerHello, but with Random set to the special value of the @@ -1056,8 +1013,8 @@ maybe_queue_cert_cert_cv(#state{connection_states = _ConnectionStates0, State = Maybe(maybe_queue_cert_verify(Certificate, State1)), {ok, State} catch - {Ref, badarg} -> - {error, badarg} + {Ref, #alert{} = Alert} -> + {error, Alert} end. @@ -1076,25 +1033,25 @@ maybe_queue_cert_verify(_Certificate, CertificateVerify = Maybe(certificate_verify(CertPrivateKey, SignatureScheme, State, client)), {ok, tls_connection:queue_handshake(CertificateVerify, State)} catch - {Ref, badarg} -> - {error, badarg} + {Ref, #alert{} = Alert} -> + {error, Alert} end. %% Recipients of Finished messages MUST verify that the contents are %% correct and if incorrect MUST terminate the connection with a %% "decrypt_error" alert. -validate_client_finished(#state{connection_states = ConnectionStates, - handshake_env = - #handshake_env{ - tls_handshake_history = {Messages0, _}}}, VerifyData) -> +validate_finished(#state{connection_states = ConnectionStates, + handshake_env = + #handshake_env{ + tls_handshake_history = {Messages0, _}}}, VerifyData) -> #{security_parameters := SecParamsR, - cipher_state := #cipher_state{finished_key = FinishedKey}} = + cipher_state := #cipher_state{finished_key = FinishedKey}} = ssl_record:current_connection_state(ConnectionStates, read), #security_parameters{prf_algorithm = HKDFAlgo} = SecParamsR, - %% Drop the client's finished message, it is not part of the handshake context - %% when the client calculates its finished message. + %% Drop the peer's finished message, it is not part of the handshake context + %% when the client/server calculates its finished message. [_|Messages] = Messages0, ControlData = tls_v1:finished_verify_data(FinishedKey, HKDFAlgo, Messages), @@ -1104,7 +1061,7 @@ validate_client_finished(#state{connection_states = ConnectionStates, compare_verify_data(Data, Data) -> ok; compare_verify_data(_, _) -> - {error, decrypt_error}. + {error, ?ALERT_REC(?FATAL, ?DECRYPT_ERROR, decrypt_error)}. send_hello_retry_request(#state{connection_states = ConnectionStates0} = State0, @@ -1239,7 +1196,7 @@ process_certificate(#certificate_1_3{ %% secrets. State1 = calculate_traffic_secrets(State0), State = ssl_record:step_encryption_state(State1), - {error, {certificate_required, State}}; + {error, {?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required), State}}; process_certificate(#certificate_1_3{certificate_list = Certs0}, #state{ssl_options = #{signature_algs := SignAlgs, @@ -1271,8 +1228,8 @@ process_certificate(#certificate_1_3{certificate_list = Certs0}, false -> State1 = calculate_traffic_secrets(State0), State = ssl_record:step_encryption_state(State1), - {error, {{handshake_failure, - "Client certificate uses unsupported signature algorithm"}, State}} + {error, {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, + "Client certificate uses unsupported signature algorithm"), State}} end. @@ -1336,9 +1293,9 @@ validate_certificate_chain(Certs, CertDbHandle, CertDbRef, catch error:{badmatch,{error, {asn1, Asn1Reason}}} -> %% ASN-1 decode of certificate somehow failed - {error, {certificate_unknown, {failed_to_decode_certificate, Asn1Reason}}}; + {error, ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, {failed_to_decode_certificate, Asn1Reason})}; error:OtherReason -> - {error, {internal_error, {unexpected_error, OtherReason}}} + {error, ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {unexpected_error, OtherReason})} end. @@ -1425,6 +1382,7 @@ calculate_handshake_secrets(PublicKey, PrivateKey, SelectedGroup, PSK, WriteFinishedKey = tls_v1:finished_key(ServerHSTrafficSecret, HKDFAlgo), update_pending_connection_states(State0, HandshakeSecret, undefined, + undefined, undefined, ReadKey, ReadIV, ReadFinishedKey, WriteKey, WriteIV, WriteFinishedKey). @@ -1451,7 +1409,7 @@ get_pre_shared_key(manual = SessionTickets, UseTicket, HKDFAlgo, SelectedIdentit undefined -> %% full handshake, default PSK {ok, binary:copy(<<0>>, ssl_cipher:hash_size(HKDFAlgo))}; illegal_parameter -> - {error, illegal_parameter}; + {error, ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)}; {_, PSK} -> {ok, PSK} end; @@ -1463,7 +1421,7 @@ get_pre_shared_key(auto = SessionTickets, UseTicket, HKDFAlgo, SelectedIdentity) {ok, binary:copy(<<0>>, ssl_cipher:hash_size(HKDFAlgo))}; illegal_parameter -> tls_client_ticket_store:unlock_tickets(self(), UseTicket), - {error, illegal_parameter}; + {error, ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)}; {Key, PSK} -> tls_client_ticket_store:remove_tickets([Key]), %% Remove single-use ticket tls_client_ticket_store:unlock_tickets(self(), UseTicket -- [Key]), @@ -1511,6 +1469,7 @@ calculate_traffic_secrets(#state{ {WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ServerAppTrafficSecret0), update_pending_connection_states(State0, MasterSecret, undefined, + ClientAppTrafficSecret0, ServerAppTrafficSecret0, ReadKey, ReadIV, undefined, WriteKey, WriteIV, undefined). @@ -1563,17 +1522,36 @@ maybe_calculate_resumption_master_secret(#state{ update_resumption_master_secret(State, RMS). +forget_master_secret(#state{connection_states = + #{pending_read := PendingRead, + pending_write := PendingWrite, + current_read := CurrentRead, + current_write := CurrentWrite} = CS} = State) -> + State#state{connection_states = CS#{pending_read => overwrite_master_secret(PendingRead), + pending_write => overwrite_master_secret(PendingWrite), + current_read => overwrite_master_secret(CurrentRead), + current_write => overwrite_master_secret(CurrentWrite)}}. + + +overwrite_master_secret(ConnectionState = #{security_parameters := SecurityParameters0}) -> + SecurityParameters = SecurityParameters0#security_parameters{master_secret = {master_secret, <<0>>}}, + ConnectionState#{security_parameters => SecurityParameters}. + + update_pending_connection_states(#state{ static_env = #static_env{role = server}, connection_states = CS = #{pending_read := PendingRead0, pending_write := PendingWrite0}} = State, HandshakeSecret, ResumptionMasterSecret, + ClientAppTrafficSecret, ServerAppTrafficSecret, ReadKey, ReadIV, ReadFinishedKey, WriteKey, WriteIV, WriteFinishedKey) -> PendingRead = update_connection_state(PendingRead0, HandshakeSecret, ResumptionMasterSecret, + ClientAppTrafficSecret, ReadKey, ReadIV, ReadFinishedKey), PendingWrite = update_connection_state(PendingWrite0, HandshakeSecret, ResumptionMasterSecret, + ServerAppTrafficSecret, WriteKey, WriteIV, WriteFinishedKey), State#state{connection_states = CS#{pending_read => PendingRead, pending_write => PendingWrite}}; @@ -1583,22 +1561,27 @@ update_pending_connection_states(#state{ CS = #{pending_read := PendingRead0, pending_write := PendingWrite0}} = State, HandshakeSecret, ResumptionMasterSecret, + ClientAppTrafficSecret, ServerAppTrafficSecret, ReadKey, ReadIV, ReadFinishedKey, WriteKey, WriteIV, WriteFinishedKey) -> PendingRead = update_connection_state(PendingRead0, HandshakeSecret, ResumptionMasterSecret, + ServerAppTrafficSecret, WriteKey, WriteIV, WriteFinishedKey), PendingWrite = update_connection_state(PendingWrite0, HandshakeSecret, ResumptionMasterSecret, + ClientAppTrafficSecret, ReadKey, ReadIV, ReadFinishedKey), State#state{connection_states = CS#{pending_read => PendingRead, pending_write => PendingWrite}}. update_connection_state(ConnectionState = #{security_parameters := SecurityParameters0}, - HandshakeSecret, ResumptionMasterSecret, Key, IV, FinishedKey) -> + HandshakeSecret, ResumptionMasterSecret, + ApplicationTrafficSecret, Key, IV, FinishedKey) -> %% Store secret SecurityParameters = SecurityParameters0#security_parameters{ master_secret = HandshakeSecret, - resumption_master_secret = ResumptionMasterSecret}, + resumption_master_secret = ResumptionMasterSecret, + application_traffic_secret = ApplicationTrafficSecret}, ConnectionState#{security_parameters => SecurityParameters, cipher_state => cipher_init(Key, IV, FinishedKey)}. @@ -1751,8 +1734,8 @@ verify_signature_algorithm(#state{ false -> State1 = calculate_traffic_secrets(State0), State = ssl_record:step_encryption_state(State1), - {error, {{handshake_failure, - "CertificateVerify uses unsupported signature algorithm"}, State}} + {error, {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, + "CertificateVerify uses unsupported signature algorithm"), State}} end. @@ -1796,11 +1779,12 @@ verify_certificate_verify(#state{ {ok, false} -> State1 = calculate_traffic_secrets(State0), State = ssl_record:step_encryption_state(State1), - {error, {{handshake_failure, "Failed to verify CertificateVerify"}, State}}; - {error, badarg} -> + {error, {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, + "Failed to verify CertificateVerify"), State}}; + {error, #alert{} = Alert} -> State1 = calculate_traffic_secrets(State0), State = ssl_record:step_encryption_state(State1), - {error, {badarg, State}} + {error, {Alert, State}} end. @@ -1822,7 +1806,7 @@ peer_context_string(client) -> %% server MUST abort the handshake with a "handshake_failure" or an %% "insufficient_security" alert. select_common_groups(_, []) -> - {error, {insufficient_security, no_suitable_groups}}; + {error, ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups)}; select_common_groups(ServerGroups, ClientGroups) -> Fun = fun(E) -> lists:member(E, ClientGroups) end, case lists:filter(Fun, ServerGroups) of @@ -1853,7 +1837,7 @@ select_common_groups(ServerGroups, ClientGroups) -> validate_client_key_share(_ ,[]) -> ok; validate_client_key_share([], _) -> - {error, illegal_parameter}; + {error, ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)}; validate_client_key_share([G|ClientGroups], [{_, G, _}|ClientShares]) -> validate_client_key_share(ClientGroups, ClientShares); validate_client_key_share([_|ClientGroups], [_|_] = ClientShares) -> @@ -1861,6 +1845,8 @@ validate_client_key_share([_|ClientGroups], [_|_] = ClientShares) -> %% Verify that selected group is offered by the client. +validate_server_key_share([], _) -> + {error, ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)}; validate_server_key_share([G|_ClientGroups], {_, G, _}) -> ok; validate_server_key_share([_|ClientGroups], {_, _, _} = ServerKeyShare) -> @@ -1868,17 +1854,17 @@ validate_server_key_share([_|ClientGroups], {_, _, _} = ServerKeyShare) -> validate_selected_group(SelectedGroup, [SelectedGroup|_]) -> - {error, {illegal_parameter, - "Selected group sent by the server shall not correspond to a group" - " which was provided in the key_share extension"}}; + {error, ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER, + "Selected group sent by the server shall not correspond to a group" + " which was provided in the key_share extension")}; validate_selected_group(SelectedGroup, ClientGroups) -> case lists:member(SelectedGroup, ClientGroups) of true -> ok; false -> - {error, {illegal_parameter, - "Selected group sent by the server shall correspond to a group" - " which was provided in the supported_groups extension"}} + {error, ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER, + "Selected group sent by the server shall correspond to a group" + " which was provided in the supported_groups extension")} end. @@ -1930,7 +1916,7 @@ get_server_public_key({key_share_entry, Group, PublicKey}) -> handle_alpn(undefined, _) -> {ok, undefined}; handle_alpn([], _) -> - {error, no_application_protocol}; + {error, ?ALERT_REC(?FATAL, ?NO_APPLICATION_PROTOCOL)}; handle_alpn([_|_], undefined) -> {ok, undefined}; handle_alpn([ServerProtocol|T], ClientProtocols) -> @@ -1943,7 +1929,7 @@ handle_alpn([ServerProtocol|T], ClientProtocols) -> select_cipher_suite(_, [], _) -> - {error, no_suitable_cipher}; + {error, ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher)}; %% If honor_cipher_order is set to true, use the server's preference for %% cipher suite selection. select_cipher_suite(true, ClientCiphers, ServerCiphers) -> @@ -1966,7 +1952,7 @@ validate_cipher_suite(Cipher, ClientCiphers) -> true -> ok; false -> - {error, illegal_parameter} + {error, ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)} end. @@ -1992,9 +1978,9 @@ check_cert_sign_algo(SignAlgo, SignHash, _, ClientSignAlgsCert) -> %% DSA keys are not supported by TLS 1.3 select_sign_algo(dsa, _ClientSignAlgs, _ServerSignAlgs) -> - {error, {insufficient_security, no_suitable_public_key}}; + {error, ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key)}; select_sign_algo(_, [], _) -> - {error, {insufficient_security, no_suitable_signature_algorithm}}; + {error, ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm)}; select_sign_algo(PublicKeyAlgo, [C|ClientSignAlgs], ServerSignAlgs) -> {_, S, _} = ssl_cipher:scheme_to_components(C), %% RSASSA-PKCS1-v1_5 and Legacy algorithms are not defined for use in signed @@ -2017,7 +2003,7 @@ select_sign_algo(PublicKeyAlgo, [C|ClientSignAlgs], ServerSignAlgs) -> do_check_cert_sign_algo(_, _, []) -> - {error, {insufficient_security, no_suitable_signature_algorithm}}; + {error, ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm)}; do_check_cert_sign_algo(SignAlgo, SignHash, [Scheme|T]) -> {Hash, Sign, _Curve} = ssl_cipher:scheme_to_components(Scheme), case compare_sign_algos(SignAlgo, SignHash, Sign, Hash) of @@ -2208,32 +2194,32 @@ create_binders(Context, [{_, _, _, PSK, _, HKDF}|T], Acc) -> %% PskBinderEntry binders<33..2^16-1>; %% } OfferedPsks; truncate_client_hello(HelloBin0) -> - HelloBin1 = remove_binders(HelloBin0), - {Truncated, _} = split_binary(HelloBin1, size(HelloBin1) - 2), + <<?BYTE(Type), ?UINT24(_Length), Body/binary>> = HelloBin0, + CH0 = #client_hello{ + extensions = #{pre_shared_key := PSK0} = Extensions0} = + tls_handshake:decode_handshake({3,4}, Type, Body), + #pre_shared_key_client_hello{offered_psks = OfferedPsks0} = PSK0, + OfferedPsks = OfferedPsks0#offered_psks{binders = []}, + PSK = PSK0#pre_shared_key_client_hello{offered_psks = OfferedPsks}, + Extensions = Extensions0#{pre_shared_key => PSK}, + CH = CH0#client_hello{extensions = Extensions}, + + %% Decoding a ClientHello from an another TLS implementation can contain + %% unsupported extensions and thus executing decoding and encoding on + %% the input can result in a different handshake binary. + %% The original length of the binders can still be determined by + %% re-encoding the original ClientHello and using its size as reference + %% when we substract the size of the truncated binary. + TruncatedSize = iolist_size(tls_handshake:encode_handshake(CH, {3,4})), + RefSize = iolist_size(tls_handshake:encode_handshake(CH0, {3,4})), + BindersSize = RefSize - TruncatedSize, + + %% Return the truncated ClientHello by cutting of the binders from the original + %% ClientHello binary. + {Truncated, _} = split_binary(HelloBin0, size(HelloBin0) - BindersSize - 2), Truncated. -remove_binders(Binary0) -> - OrigSize = byte_size(Binary0), - HashSize256 = ssl_cipher:hash_size(sha256), - HashSize384 = ssl_cipher:hash_size(sha384), - HashSize512 = ssl_cipher:hash_size(sha512), - - NewSize256 = OrigSize - HashSize256 - 1, - NewSize384 = OrigSize - HashSize384 - 1, - NewSize512 = OrigSize - HashSize512 - 1, - case Binary0 of - <<Binary:NewSize256/binary,?BYTE(HashSize256),_:HashSize256/binary>> -> - remove_binders(Binary); - <<Binary:NewSize384/binary,?BYTE(HashSize384),_:HashSize384/binary>> -> - remove_binders(Binary); - <<Binary:NewSize512/binary,?BYTE(HashSize512),_:HashSize512/binary>> -> - remove_binders(Binary); - Else -> - Else - end. - - %% The PskBinderEntry is computed in the same way as the Finished %% message (Section 4.4.4) but with the BaseKey being the binder_key %% derived via the key schedule from the corresponding PSK which is diff --git a/lib/ssl/src/tls_handshake_1_3.hrl b/lib/ssl/src/tls_handshake_1_3.hrl index cb28ca78f6..94674c3a57 100644 --- a/lib/ssl/src/tls_handshake_1_3.hrl +++ b/lib/ssl/src/tls_handshake_1_3.hrl @@ -255,7 +255,8 @@ #certificate_request_1_3{} | #certificate_1_3{} | #certificate_verify_1_3{} | - #new_session_ticket{}. + #new_session_ticket{} | + #key_update{}. -export_type([tls_handshake_1_3/0]). diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index e8040b461d..538c3e44fb 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -38,7 +38,7 @@ %% Encoding TLS records -export([encode_handshake/3, encode_alert_record/3, encode_change_cipher_spec/2, encode_data/3]). --export([encode_plain_text/4]). +-export([encode_plain_text/4, split_iovec/1]). %% Decoding -export([decode_cipher_text/4]). @@ -395,6 +395,12 @@ hello_version([Highest|_]) when Highest >= {3,3} -> hello_version(Versions) -> lowest_protocol_version(Versions). +split_iovec([]) -> + []; +split_iovec(Data) -> + {Part,Rest} = split_iovec(Data, ?MAX_PLAIN_TEXT_LENGTH, []), + [Part|split_iovec(Rest)]. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -498,8 +504,9 @@ validate_tls_record_length(_Versions, Q, _SslOpts, Acc, Type, Version, undefined validate_tls_record_length(Versions, {_,Size0,_} = Q0, #{log_level := LogLevel} = SslOpts, Acc, Type, Version, Length) -> + Max = max_len(Versions), if - Length =< ?MAX_CIPHER_TEXT_LENGTH -> + Length =< Max -> if Length =< Size0 -> %% Complete record @@ -630,12 +637,6 @@ split_iovec(Data, Version, BCA, zero_n) split_iovec(Data, _Version, _BCA, _BeatMitigation) -> split_iovec(Data). -split_iovec([]) -> - []; -split_iovec(Data) -> - {Part,Rest} = split_iovec(Data, ?MAX_PLAIN_TEXT_LENGTH, []), - [Part|split_iovec(Rest)]. -%% split_iovec([Bin|Data] = Bin_Data, SplitSize, Acc) -> BinSize = byte_size(Bin), if @@ -671,4 +672,7 @@ sufficient_tlsv1_2_crypto_support() -> CryptoSupport = crypto:supports(), proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)). - +max_len([{3,4}|_])-> + ?TLS13_MAX_CIPHER_TEXT_LENGTH; +max_len(_) -> + ?MAX_CIPHER_TEXT_LENGTH. diff --git a/lib/ssl/src/tls_record_1_3.erl b/lib/ssl/src/tls_record_1_3.erl index d713062284..52e3c06a3b 100644 --- a/lib/ssl/src/tls_record_1_3.erl +++ b/lib/ssl/src/tls_record_1_3.erl @@ -47,7 +47,7 @@ encode_handshake(Frag, ConnectionStates) -> case iolist_size(Frag) of N when N > ?MAX_PLAIN_TEXT_LENGTH -> %% TODO: Consider padding here - Data = split_bin(iolist_to_binary(Frag), ?MAX_PLAIN_TEXT_LENGTH), + Data = tls_record:split_iovec(Frag), encode_iolist(?HANDSHAKE, Data, ConnectionStates); _ -> encode_plain_text(?HANDSHAKE, Frag, ConnectionStates) @@ -64,13 +64,13 @@ encode_alert_record(#alert{level = Level, description = Description}, encode_plain_text(?ALERT, <<?BYTE(Level), ?BYTE(Description)>>, ConnectionStates). %%-------------------------------------------------------------------- --spec encode_data(binary(), ssl_record:connection_states()) -> +-spec encode_data(iolist(), ssl_record:connection_states()) -> {iolist(), ssl_record:connection_states()}. %% %% Description: Encodes data to send on the ssl-socket. %%-------------------------------------------------------------------- encode_data(Frag, ConnectionStates) -> - Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH, {3,4}), + Data = tls_record:split_iovec(Frag), encode_iolist(?APPLICATION_DATA, Data, ConnectionStates). encode_plain_text(Type, Data0, #{current_write := Write0} = ConnectionStates) -> @@ -180,21 +180,6 @@ decode_cipher_text(#ssl_tls{type = Type}, _) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -split_bin(Bin, ChunkSize) -> - split_bin(Bin, ChunkSize, []). -split_bin(Bin, ChunkSize, _) -> - do_split_bin(Bin, ChunkSize, []). - -do_split_bin(<<>>, _, Acc) -> - lists:reverse(Acc); -do_split_bin(Bin, ChunkSize, Acc) -> - case Bin of - <<Chunk:ChunkSize/binary, Rest/binary>> -> - do_split_bin(Rest, ChunkSize, [Chunk | Acc]); - _ -> - lists:reverse(Acc, [Bin]) - end. - inner_plaintext(Type, Data, Length) -> #inner_plaintext{ content = Data, diff --git a/lib/ssl/src/tls_sender.erl b/lib/ssl/src/tls_sender.erl index 50811ef3dd..790746658e 100644 --- a/lib/ssl/src/tls_sender.erl +++ b/lib/ssl/src/tls_sender.erl @@ -26,18 +26,20 @@ -include("ssl_alert.hrl"). -include("ssl_handshake.hrl"). -include("ssl_api.hrl"). +-include("ssl_record.hrl"). +-include("tls_handshake_1_3.hrl"). %% API --export([start/0, start/1, initialize/2, send_data/2, send_alert/2, +-export([start/0, start/1, initialize/2, send_data/2, + send_post_handshake/2, send_alert/2, send_and_ack_alert/2, setopts/2, renegotiate/1, peer_renegotiate/1, downgrade/2, - update_connection_state/3, dist_tls_socket/1, dist_handshake_complete/3]). + update_connection_state/3, + dist_tls_socket/1, dist_handshake_complete/3]). %% gen_statem callbacks -export([callback_mode/0, init/1, terminate/3, code_change/4]). -export([init/3, connection/3, handshake/3, death_row/3]). --define(SERVER, ?MODULE). - -record(static, {connection_pid, role, @@ -47,6 +49,8 @@ transport_cb, negotiated_version, renegotiate_at, + key_update_at, %% TLS 1.3 + bytes_sent, %% TLS 1.3 connection_monitor, dist_handle, log_level @@ -94,6 +98,13 @@ send_data(Pid, AppData) -> call(Pid, {application_data, AppData}). %%-------------------------------------------------------------------- +-spec send_post_handshake(pid(), #key_update{}) -> ok | {error, term()}. +%% Description: Send post handshake data +%%-------------------------------------------------------------------- +send_post_handshake(Pid, HandshakeData) -> + call(Pid, {post_handshake_data, HandshakeData}). + +%%-------------------------------------------------------------------- -spec send_alert(pid(), #alert{}) -> _. %% Description: TLS connection process wants to send an Alert %% in the connection state. @@ -204,6 +215,7 @@ init({call, From}, {Pid, #{current_write := WriteState, transport_cb := Transport, negotiated_version := Version, renegotiate_at := RenegotiateAt, + key_update_at := KeyUpdateAt, log_level := LogLevel}}, #data{connection_states = ConnectionStates, static = Static0} = StateData0) -> Monitor = erlang:monitor(process, Pid), @@ -218,6 +230,8 @@ init({call, From}, {Pid, #{current_write := WriteState, transport_cb = Transport, negotiated_version = Version, renegotiate_at = RenegotiateAt, + key_update_at = KeyUpdateAt, + bytes_sent = 0, log_level = LogLevel}}, {next_state, handshake, StateData, [{reply, From, ok}]}; init(_, _, _) -> @@ -239,6 +253,8 @@ connection({call, From}, {application_data, AppData}, Data -> send_application_data(Data, From, ?FUNCTION_NAME, StateData) end; +connection({call, From}, {post_handshake_data, HSData}, StateData) -> + send_post_handshake_data(HSData, From, ?FUNCTION_NAME, StateData); connection({call, From}, {ack_alert, #alert{} = Alert}, StateData0) -> StateData = send_tls_alert(Alert, StateData0), {next_state, ?FUNCTION_NAME, StateData, @@ -275,7 +291,8 @@ connection({call, From}, {dist_handshake_complete, _Node, DHandle}, end]}; connection(internal, {application_packets, From, Data}, StateData) -> send_application_data(Data, From, ?FUNCTION_NAME, StateData); -%% +connection(internal, {post_handshake_data, From, HSData}, StateData) -> + send_post_handshake_data(HSData, From, ?FUNCTION_NAME, StateData); connection(cast, #alert{} = Alert, StateData0) -> StateData = send_tls_alert(Alert, StateData0), {next_state, ?FUNCTION_NAME, StateData}; @@ -324,11 +341,14 @@ handshake({call, _}, _, _) -> {keep_state_and_data, [postpone]}; handshake(internal, {application_packets,_,_}, _) -> {keep_state_and_data, [postpone]}; -handshake(cast, {new_write, WritesState, Version}, - #data{connection_states = ConnectionStates, static = Static} = StateData) -> +handshake(cast, {new_write, WriteState, Version}, + #data{connection_states = ConnectionStates, + static = #static{key_update_at = KeyUpdateAt0} = Static} = StateData) -> + KeyUpdateAt = key_update_at(Version, WriteState, KeyUpdateAt0), {next_state, connection, - StateData#data{connection_states = ConnectionStates#{current_write => WritesState}, - static = Static#static{negotiated_version = Version}}}; + StateData#data{connection_states = ConnectionStates#{current_write => WriteState}, + static = Static#static{negotiated_version = Version, + key_update_at = KeyUpdateAt}}}; handshake(info, dist_data, _) -> {keep_state_and_data, [postpone]}; handshake(info, tick, _) -> @@ -422,30 +442,111 @@ send_application_data(Data, From, StateName, negotiated_version = Version, transport_cb = Transport, renegotiate_at = RenegotiateAt, + key_update_at = KeyUpdateAt, + bytes_sent = BytesSent, log_level = LogLevel}, connection_states = ConnectionStates0} = StateData0) -> - case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of - true -> + case time_to_rekey(Version, Data, ConnectionStates0, RenegotiateAt, KeyUpdateAt, BytesSent) of + key_update -> + KeyUpdate = tls_handshake_1_3:key_update(update_requested), + {keep_state_and_data, [{next_event, internal, {post_handshake_data, From, KeyUpdate}}, + {next_event, internal, {key_update, From}}, + {next_event, internal, {application_packets, From, Data}}]}; + renegotiate -> ssl_connection:internal_renegotiation(Pid, ConnectionStates0), {next_state, handshake, StateData0, [{next_event, internal, {application_packets, From, Data}}]}; + chunk_and_key_update -> + KeyUpdate = tls_handshake_1_3:key_update(update_requested), + %% Prevent infinite loop of key updates + {Chunk, Rest} = chunk_data(Data, KeyUpdateAt), + {keep_state_and_data, [{next_event, internal, {post_handshake_data, From, KeyUpdate}}, + {next_event, internal, {application_packets, From, Chunk}}, + {next_event, internal, {application_packets, From, Rest}}]}; false -> {Msgs, ConnectionStates} = tls_record:encode_data(Data, Version, ConnectionStates0), StateData = StateData0#data{connection_states = ConnectionStates}, case tls_socket:send(Transport, Socket, Msgs) of ok when DistHandle =/= undefined -> ssl_logger:debug(LogLevel, outbound, 'record', Msgs), - {next_state, StateName, StateData, []}; + StateData1 = update_bytes_sent(Version, StateData, Data), + {next_state, StateName, StateData1, []}; Reason when DistHandle =/= undefined -> {next_state, death_row, StateData, [{state_timeout, 5000, Reason}]}; ok -> ssl_logger:debug(LogLevel, outbound, 'record', Msgs), - {next_state, StateName, StateData, [{reply, From, ok}]}; + StateData1 = update_bytes_sent(Version, StateData, Data), + {next_state, StateName, StateData1, [{reply, From, ok}]}; Result -> {next_state, StateName, StateData, [{reply, From, Result}]} end end. +%% TLS 1.3 Post Handshake Data +send_post_handshake_data(Handshake, From, StateName, + #data{static = #static{socket = Socket, + dist_handle = DistHandle, + negotiated_version = Version, + transport_cb = Transport, + log_level = LogLevel}, + connection_states = ConnectionStates0} = StateData0) -> + BinHandshake = tls_handshake:encode_handshake(Handshake, Version), + {Encoded, ConnectionStates} = + tls_record:encode_handshake(BinHandshake, Version, ConnectionStates0), + ssl_logger:debug(LogLevel, outbound, 'handshake', Handshake), + StateData1 = StateData0#data{connection_states = ConnectionStates}, + case tls_socket:send(Transport, Socket, Encoded) of + ok when DistHandle =/= undefined -> + ssl_logger:debug(LogLevel, outbound, 'record', Encoded), + StateData = maybe_update_cipher_key(StateData1, Handshake), + {next_state, StateName, StateData, []}; + Reason when DistHandle =/= undefined -> + {next_state, death_row, StateData1, [{state_timeout, 5000, Reason}]}; + ok -> + ssl_logger:debug(LogLevel, outbound, 'record', Encoded), + StateData = maybe_update_cipher_key(StateData1, Handshake), + {next_state, StateName, StateData, [{reply, From, ok}]}; + Result -> + {next_state, StateName, StateData1, [{reply, From, Result}]} + end. + +maybe_update_cipher_key(#data{connection_states = ConnectionStates0, + static = Static0} = StateData, #key_update{}) -> + ConnectionStates = tls_connection:update_cipher_key(current_write, ConnectionStates0), + Static = Static0#static{bytes_sent = 0}, + StateData#data{connection_states = ConnectionStates, + static = Static}; +maybe_update_cipher_key(StateData, _) -> + StateData. + +update_bytes_sent(Version, StateData, _) when Version < {3,4} -> + StateData; +%% Count bytes sent in TLS 1.3 for AES-GCM +update_bytes_sent(_, #data{static = #static{key_update_at = seq_num_wrap}} = StateData, _) -> + StateData; %% Chacha20-Poly1305 +update_bytes_sent(_, #data{static = #static{bytes_sent = Sent} = Static} = StateData, Data) -> + StateData#data{static = Static#static{bytes_sent = Sent + iolist_size(Data)}}. %% AES-GCM + +%% For AES-GCM, up to 2^24.5 full-size records (about 24 million) may be +%% encrypted on a given connection while keeping a safety margin of +%% approximately 2^-57 for Authenticated Encryption (AE) security. For +%% ChaCha20/Poly1305, the record sequence number would wrap before the +%% safety limit is reached. +key_update_at(Version, #{security_parameters := + #security_parameters{ + bulk_cipher_algorithm = CipherAlgo}}, KeyUpdateAt) + when Version >= {3,4} -> + case CipherAlgo of + ?AES_GCM -> + KeyUpdateAt; + ?CHACHA20_POLY1305 -> + seq_num_wrap; + ?AES_CCM -> + KeyUpdateAt + end; +key_update_at(_, _, KeyUpdateAt) -> + KeyUpdateAt. + -compile({inline, encode_packet/2}). encode_packet(Packet, Data) -> Len = iolist_size(Data), @@ -463,9 +564,29 @@ encode_packet(Packet, Data) -> set_opts(SocketOptions, [{packet, N}]) -> SocketOptions#socket_options{packet = N}. -time_to_renegotiate(_Data, - #{current_write := #{sequence_number := Num}}, - RenegotiateAt) -> +time_to_rekey(Version, _Data, + #{current_write := #{sequence_number := ?MAX_SEQUENCE_NUMBER}}, + _, _, _) when Version >= {3,4} -> + key_update; +time_to_rekey(Version, _Data, _, _, seq_num_wrap, _) when Version >= {3,4} -> + false; +time_to_rekey(Version, Data, _, _, KeyUpdateAt, BytesSent) when Version >= {3,4} -> + DataSize = iolist_size(Data), + case (BytesSent + DataSize) > KeyUpdateAt of + true -> + %% Handle special case that causes an invite loop of key updates. + case DataSize > KeyUpdateAt of + true -> + chunk_and_key_update; + false -> + key_update + end; + false -> + false + end; +time_to_rekey(_, _Data, + #{current_write := #{sequence_number := Num}}, + RenegotiateAt, _, _) -> %% We could do test: %% is_time_to_renegotiate((erlang:byte_size(_Data) div @@ -473,10 +594,14 @@ time_to_renegotiate(_Data, %% have a some what lower renegotiateAt and a much cheaper test is_time_to_renegotiate(Num, RenegotiateAt). +chunk_data(Data, Size) -> + {Chunk, Rest} = split_binary(iolist_to_binary(Data), Size), + {[Chunk], [Rest]}. + is_time_to_renegotiate(N, M) when N < M-> false; is_time_to_renegotiate(_,_) -> - true. + renegotiate. call(FsmPid, Event) -> try gen_statem:call(FsmPid, Event) diff --git a/lib/ssl/src/tls_server_session_ticket.erl b/lib/ssl/src/tls_server_session_ticket.erl index fc3904366e..a1e8ec4331 100644 --- a/lib/ssl/src/tls_server_session_ticket.erl +++ b/lib/ssl/src/tls_server_session_ticket.erl @@ -221,7 +221,7 @@ stateful_ticket_store(Ref, NewSessionTicket, Hash, Psk, max := Max, ref_index := Index0} = Stateful} = State0) -> - Id = erlang:monotonic_time(), + Id = {erlang:monotonic_time(), erlang:unique_integer([monotonic])}, StatefulTicket = {NewSessionTicket, Hash, Psk}, case gb_trees:size(Tree0) of Max -> @@ -288,7 +288,7 @@ stateful_usable_ticket(Key, Prf, Binder, HandshakeHist, Tree) -> false end. -stateful_living_ticket(TimeStamp, +stateful_living_ticket({TimeStamp,_}, #new_session_ticket{ticket_lifetime = LifeTime}) -> Now = erlang:monotonic_time(), Lived = erlang:convert_time_unit(Now-TimeStamp, native, seconds), diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl index 381793c65d..38594151d9 100644 --- a/lib/ssl/src/tls_v1.erl +++ b/lib/ssl/src/tls_v1.erl @@ -884,7 +884,9 @@ oid_to_enum(?secp384r1) -> 24; oid_to_enum(?secp521r1) -> 25; oid_to_enum(?brainpoolP256r1) -> 26; oid_to_enum(?brainpoolP384r1) -> 27; -oid_to_enum(?brainpoolP512r1) -> 28. +oid_to_enum(?brainpoolP512r1) -> 28; +oid_to_enum(?'id-X25519') -> 29; +oid_to_enum(?'id-X448') -> 30. enum_to_oid(1) -> ?sect163k1; enum_to_oid(2) -> ?sect163r1; @@ -914,5 +916,7 @@ enum_to_oid(25) -> ?secp521r1; enum_to_oid(26) -> ?brainpoolP256r1; enum_to_oid(27) -> ?brainpoolP384r1; enum_to_oid(28) -> ?brainpoolP512r1; +enum_to_oid(29) -> ?'id-X25519'; +enum_to_oid(30) -> ?'id-X448'; enum_to_oid(_) -> undefined. diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile index cb52a6d3d5..e53f54130f 100644 --- a/lib/ssl/test/Makefile +++ b/lib/ssl/test/Makefile @@ -66,6 +66,8 @@ MODULES = \ ssl_dist_bench_SUITE \ ssl_engine_SUITE\ ssl_handshake_SUITE \ + ssl_key_update_SUITE \ + openssl_key_update_SUITE \ ssl_npn_hello_SUITE \ ssl_packet_SUITE \ ssl_payload_SUITE \ @@ -73,6 +75,7 @@ MODULES = \ ssl_session_SUITE \ ssl_session_cache_SUITE \ ssl_session_ticket_SUITE \ + openssl_session_ticket_SUITE \ openssl_session_SUITE \ ssl_ECC_SUITE \ ssl_ECC_openssl_SUITE \ diff --git a/lib/ssl/test/dtls_api_SUITE.erl b/lib/ssl/test/dtls_api_SUITE.erl index a89e754219..b3bf3b1528 100644 --- a/lib/ssl/test/dtls_api_SUITE.erl +++ b/lib/ssl/test/dtls_api_SUITE.erl @@ -39,7 +39,8 @@ groups() -> api_tests() -> [ dtls_listen_owner_dies, - dtls_listen_close + dtls_listen_close, + dtls_listen_reopen ]. init_per_suite(Config0) -> @@ -114,7 +115,7 @@ dtls_listen_owner_dies(Config) when is_list(Config) -> {ok, LSocket} = ssl:listen(Port, [{protocol, dtls} | ServerOpts]), spawn(fun() -> {ok, ASocket} = ssl:transport_accept(LSocket), - _ = ssl:handshake(ASocket), + {ok, Socket} = ssl:handshake(ASocket), receive {ssl, Socket, "from client"} -> ssl:send(Socket, "from server"), @@ -141,6 +142,49 @@ dtls_listen_close(Config) when is_list(Config) -> {ok, ListenSocket} = ssl:listen(Port, [{protocol, dtls} | ServerOpts]), ok = ssl:close(ListenSocket). + +dtls_listen_reopen() -> + [{doc, "Test that you close a DTLS 'listner' socket and open a new one for the same port"}]. + +dtls_listen_reopen(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Port = ssl_test_lib:inet_port(ServerNode), + {ok, LSocket0} = ssl:listen(Port, [{protocol, dtls} | ServerOpts]), + spawn(fun() -> + {ok, ASocket} = ssl:transport_accept(LSocket0), + {ok, Socket} = ssl:handshake(ASocket), + receive + {ssl, Socket, "from client"} -> + ssl:send(Socket, "from server 1"), + ssl:close(Socket) + end + end), + {ok, Client1} = ssl:connect(Hostname, Port, ClientOpts), + ok = ssl:close(LSocket0), + {ok, LSocket1} = ssl:listen(Port, [{protocol, dtls} | ServerOpts]), + spawn(fun() -> + {ok, ASocket} = ssl:transport_accept(LSocket1), + {ok, Socket} = ssl:handshake(ASocket), + receive + {ssl, Socket, "from client"} -> + ssl:send(Socket, "from server 2"), + ssl:close(Socket) + end + end), + {ok, Client2} = ssl:connect(Hostname, Port, [{protocol, dtls} | ClientOpts]), + ssl:send(Client2, "from client"), + ssl:send(Client1, "from client"), + receive + {ssl, Client1, "from server 1"} -> + ssl:close(Client1) + end, + receive + {ssl, Client2, "from server 2"} -> + ssl:close(Client2) + end. %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- diff --git a/lib/ssl/test/openssl_alpn_SUITE.erl b/lib/ssl/test/openssl_alpn_SUITE.erl index 3813562cda..0e3e3daee3 100644 --- a/lib/ssl/test/openssl_alpn_SUITE.erl +++ b/lib/ssl/test/openssl_alpn_SUITE.erl @@ -46,7 +46,9 @@ all() -> {group, 'dtlsv1.2'}, {group, 'dtlsv1'}]; false -> - [{group, 'tlsv1.2'}, + [ + {group, 'tlsv1.3'}, + {group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}] end. diff --git a/lib/ssl/test/openssl_key_update_SUITE.erl b/lib/ssl/test/openssl_key_update_SUITE.erl new file mode 100644 index 0000000000..4963f0bb30 --- /dev/null +++ b/lib/ssl/test/openssl_key_update_SUITE.erl @@ -0,0 +1,134 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2020-2020. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(openssl_key_update_SUITE). + +%% Callback functions +-export([all/0, + groups/0, + init_per_suite/1, + end_per_suite/1, + init_per_group/2, + end_per_group/2, + init_per_testcase/2, + end_per_testcase/2]). + +%% Testcases +-export([openssl_client_explicit_key_update/0, + openssl_client_explicit_key_update/1, + openssl_server_explicit_key_update/0, + openssl_server_explicit_key_update/1]). + +-include_lib("common_test/include/ct.hrl"). + +all() -> + [{group, 'tlsv1.3'}]. + +groups() -> + [{'tlsv1.3', [], tls_1_3_tests()}]. + +tls_1_3_tests() -> + [openssl_client_explicit_key_update, + openssl_server_explicit_key_update]. + +init_per_suite(Config0) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + case proplists:get_bool(ecdh, proplists:get_value(public_keys, crypto:supports())) of + true -> + ssl_test_lib:make_ecdsa_cert(Config0); + false -> + {skip, "Missing EC crypto support"} + end + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:unload(ssl), + application:stop(crypto). + +init_per_group(GroupName, Config) -> + ssl_test_lib:init_per_group_openssl(GroupName, Config). + +end_per_group(GroupName, Config) -> + ssl_test_lib:end_per_group(GroupName, Config). + +init_per_testcase(_TestCase, Config) -> + ssl_test_lib:ct_log_supported_protocol_versions(Config), + ct:timetrap({seconds, 10}), + Config. + +end_per_testcase(_TestCase, Config) -> + Config. + + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +openssl_client_explicit_key_update() -> + [{doc,"Test ssl:update_key/2 between openssl s_client and erlang server."}]. + +openssl_client_explicit_key_update(Config) -> + Data = "123456789012345", %% 15 bytes + + Server = ssl_test_lib:start_server(erlang, [{log_level, debug}], Config), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client(openssl, [{port, Port}], Config), + ssl_test_lib:send_recv_result_active(Client, Server, Data), + + %% TODO s_client can hang after sending special commands e.g "k", "K" + %% ssl_test_lib:update_keys(Client, write), + %% ssl_test_lib:update_keys(Client, read_write), + ssl_test_lib:update_keys(Server, write), + ssl_test_lib:update_keys(Server, read_write), + + ssl_test_lib:send_recv_result_active(Client, Server, Data), + + ssl_test_lib:close(Client), + ssl_test_lib:close(Server). + +openssl_server_explicit_key_update() -> + [{doc,"Test ssl:update_key/2 between ssl client and s_server."}]. + +openssl_server_explicit_key_update(Config) -> + Data = "123456789012345", %% 15 bytes + + Server = ssl_test_lib:start_server(openssl, [], Config), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client(erlang, [{port, Port}, + {log_level, debug}, + {versions, ['tlsv1.2','tlsv1.3']}],Config), + ssl_test_lib:send_recv_result_active(Server, Client, Data), + + ssl_test_lib:update_keys(Client, write), + ssl_test_lib:update_keys(Client, read_write), + ssl_test_lib:update_keys(Server, write), + ssl_test_lib:update_keys(Server, read_write), + + ssl_test_lib:send_recv_result_active(Client, Server, Data), + + ssl_test_lib:close(Client), + ssl_test_lib:close(Server). diff --git a/lib/ssl/test/openssl_session_ticket_SUITE.erl b/lib/ssl/test/openssl_session_ticket_SUITE.erl new file mode 100644 index 0000000000..775048e355 --- /dev/null +++ b/lib/ssl/test/openssl_session_ticket_SUITE.erl @@ -0,0 +1,409 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-2020. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(openssl_session_ticket_SUITE). + +%% Callback functions +-export([all/0, + groups/0, + init_per_suite/1, + end_per_suite/1, + init_per_group/2, + end_per_group/2, + init_per_testcase/2, + end_per_testcase/2]). + +%% Testcases +-export([openssl_server_basic/0, + openssl_server_basic/1, + openssl_server_hrr/0, + openssl_server_hrr/1, + openssl_server_hrr_multiple_tickets/0, + openssl_server_hrr_multiple_tickets/1, + openssl_client_basic/0, + openssl_client_basic/1, + openssl_client_hrr/0, + openssl_client_hrr/1]). + +-include("tls_handshake.hrl"). + +-include_lib("common_test/include/ct.hrl"). + +-define(SLEEP, 500). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- +all() -> + [ + {group, 'tlsv1.3'} + ]. + +groups() -> + [{'tlsv1.3', [], [{group, stateful}, + {group, stateless}, + {group, openssl_server}]}, + {openssl_server, [], [openssl_server_basic, + openssl_server_hrr, + openssl_server_hrr_multiple_tickets + ]}, + {stateful, [], session_tests()}, + {stateless, [], session_tests()}]. + +session_tests() -> + [openssl_client_basic, + openssl_client_hrr]. + +init_per_suite(Config0) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + ssl_test_lib:make_rsa_cert(Config0) + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). + +init_per_group(stateful, Config) -> + [{server_ticket_mode, stateful} | proplists:delete(server_ticket_mode, Config)]; +init_per_group(stateless, Config) -> + [{server_ticket_mode, stateless} | proplists:delete(server_ticket_mode, Config)]; +init_per_group(GroupName, Config) -> + ssl_test_lib:init_per_group_openssl(GroupName, Config). + +end_per_group(GroupName, Config) -> + ssl_test_lib:end_per_group(GroupName, Config). + +init_per_testcase(_TestCase, Config) -> + ssl:stop(), + application:load(ssl), + ssl:start(), + ct:timetrap({seconds, 15}), + Config. + +end_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +openssl_server_basic() -> + [{doc,"Test session resumption with session tickets (erlang client - openssl server)"}]. +openssl_server_basic(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Version = 'tlsv1.3', + Port = ssl_test_lib:inet_port(node()), + CertFile = proplists:get_value(certfile, ServerOpts), + CACertFile = proplists:get_value(cacertfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + + %% Configure session tickets + ClientOpts = [{session_tickets, auto}, {log_level, debug}, + {versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + + Exe = "openssl", + Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-cert", CertFile,"-key", KeyFile, "-CAfile", CACertFile, "-msg", "-debug"], + + OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), + + ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), + + %% Store ticket from first connection + Client0 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, + verify_active_session_resumption, + [false, no_reply]}}, + {from, self()}, {options, ClientOpts}]), + %% Wait for session ticket + ct:sleep(100), + + %% Close previous connection as s_server can only handle one at a time + ssl_test_lib:close(Client0), + + %% Use ticket + Client1 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, + verify_active_session_resumption, + [true, no_reply]}}, + {from, self()}, + {options, ClientOpts}]), + process_flag(trap_exit, false), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close_port(OpensslPort), + ssl_test_lib:close(Client1). + +openssl_client_basic() -> + [{doc,"Test session resumption with session tickets (openssl client - erlang server)"}]. +openssl_client_basic(Config) when is_list(Config) -> + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + TicketFile0 = filename:join([proplists:get_value(priv_dir, Config), "session_ticket0"]), + TicketFile1 = filename:join([proplists:get_value(priv_dir, Config), "session_ticket1"]), + ServerTicketMode = proplists:get_value(server_ticket_mode, Config), + + Data = "Hello world", + + %% Configure session tickets + ServerOpts = [{session_tickets, ServerTicketMode}, {log_level, debug}, + {versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0], + + Server0 = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + verify_active_session_resumption, + [false]}}, + {options, ServerOpts}]), + + Version = 'tlsv1.3', + Port0 = ssl_test_lib:inet_port(Server0), + + Exe = "openssl", + Args0 = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname) + ++ ":" ++ integer_to_list(Port0), + ssl_test_lib:version_flag(Version), + "-sess_out", TicketFile0], + + OpenSslPort0 = ssl_test_lib:portable_open_port(Exe, Args0), + + true = port_command(OpenSslPort0, Data), + + ssl_test_lib:check_result(Server0, ok), + + Server0 ! {listen, {mfa, {ssl_test_lib, + verify_active_session_resumption, + [true]}}}, + + %% Wait for session ticket + ct:sleep(100), + + Args1 = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname) + ++ ":" ++ integer_to_list(Port0), + ssl_test_lib:version_flag(Version), + "-sess_in", TicketFile0, + "-sess_out", TicketFile1], + + OpenSslPort1 = ssl_test_lib:portable_open_port(Exe, Args1), + + true = port_command(OpenSslPort1, Data), + + ssl_test_lib:check_result(Server0, ok), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close(Server0), + ssl_test_lib:close_port(OpenSslPort0), + ssl_test_lib:close_port(OpenSslPort1). + +openssl_server_hrr() -> + [{doc,"Test session resumption with session tickets and hello_retry_request (erlang client - openssl server)"}]. +openssl_server_hrr(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Version = 'tlsv1.3', + Port = ssl_test_lib:inet_port(node()), + CertFile = proplists:get_value(certfile, ServerOpts), + CACertFile = proplists:get_value(cacertfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + + %% Configure session tickets + ClientOpts = [{session_tickets, auto}, {log_level, debug}, + {versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups,[secp256r1, x25519]}|ClientOpts0], + + Exe = "openssl", + Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-cert", CertFile, + "-key", KeyFile, + "-CAfile", CACertFile, + "-groups", "X448:X25519", + "-msg", "-debug"], + + OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), + + ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), + + %% Store ticket from first connection + Client0 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, + verify_active_session_resumption, + [false, no_reply]}}, + {from, self()}, {options, ClientOpts}]), + %% Wait for session ticket + ct:sleep(100), + + %% Close previous connection as s_server can only handle one at a time + ssl_test_lib:close(Client0), + + %% Use ticket + Client1 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, + verify_active_session_resumption, + [true, no_reply]}}, + {from, self()}, + {options, ClientOpts}]), + process_flag(trap_exit, false), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close_port(OpensslPort), + ssl_test_lib:close(Client1). + +openssl_client_hrr() -> + [{doc,"Test session resumption with session tickets and hello_retry_request (openssl client - erlang server)"}]. +openssl_client_hrr(Config) when is_list(Config) -> + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + TicketFile0 = filename:join([proplists:get_value(priv_dir, Config), "session_ticket0"]), + TicketFile1 = filename:join([proplists:get_value(priv_dir, Config), "session_ticket1"]), + ServerTicketMode = proplists:get_value(server_ticket_mode, Config), + + Data = "Hello world", + + %% Configure session tickets + ServerOpts = [{session_tickets, ServerTicketMode}, {log_level, debug}, + {versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups,[x448, x25519]}|ServerOpts0], + + Server0 = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + verify_active_session_resumption, + [false]}}, + {options, ServerOpts}]), + + Version = 'tlsv1.3', + Port0 = ssl_test_lib:inet_port(Server0), + + Exe = "openssl", + Args0 = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname) + ++ ":" ++ integer_to_list(Port0), + ssl_test_lib:version_flag(Version), + "-groups", "P-256:X25519", + "-sess_out", TicketFile0], + + OpenSslPort0 = ssl_test_lib:portable_open_port(Exe, Args0), + + true = port_command(OpenSslPort0, Data), + + ssl_test_lib:check_result(Server0, ok), + + Server0 ! {listen, {mfa, {ssl_test_lib, + verify_active_session_resumption, + [true]}}}, + + %% Wait for session ticket + ct:sleep(100), + + Args1 = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname) + ++ ":" ++ integer_to_list(Port0), + ssl_test_lib:version_flag(Version), + "-groups", "P-256:X25519", + "-sess_in", TicketFile0, + "-sess_out", TicketFile1], + + OpenSslPort1 = ssl_test_lib:portable_open_port(Exe, Args1), + + true = port_command(OpenSslPort1, Data), + + ssl_test_lib:check_result(Server0, ok), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close(Server0), + ssl_test_lib:close_port(OpenSslPort0), + ssl_test_lib:close_port(OpenSslPort1). + +openssl_server_hrr_multiple_tickets() -> + [{doc,"Test session resumption with multiple session tickets and hello_retry_request (erlang client - openssl server)"}]. +openssl_server_hrr_multiple_tickets(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Version = 'tlsv1.3', + Port = ssl_test_lib:inet_port(node()), + CertFile = proplists:get_value(certfile, ServerOpts), + CACertFile = proplists:get_value(cacertfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + + %% Configure session tickets + ClientOpts = [{session_tickets, manual}, {log_level, debug}, + {versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups,[secp256r1, x25519]}|ClientOpts0], + + Exe = "openssl", + Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-cert", CertFile, + "-key", KeyFile, + "-CAfile", CACertFile, + "-groups", "X448:X25519", + "-msg", "-debug"], + + OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), + + ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), + + %% Store ticket from first connection + Client0 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, + verify_active_session_resumption, + [false, no_reply, {tickets, 2}]}}, + {from, self()}, {options, ClientOpts}]), + + Tickets0 = ssl_test_lib:check_tickets(Client0), + + ct:pal("Received tickets: ~p~n", [Tickets0]), + + %% Close previous connection as s_server can only handle one at a time + ssl_test_lib:close(Client0), + + %% Use tickets + Client1 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, + verify_active_session_resumption, + [true, no_reply, no_tickets]}}, + {from, self()}, + {options, [{use_ticket, Tickets0}|ClientOpts]}]), + + process_flag(trap_exit, false), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close_port(OpensslPort), + ssl_test_lib:close(Client1). diff --git a/lib/ssl/test/openssl_sni_SUITE.erl b/lib/ssl/test/openssl_sni_SUITE.erl index 26f08e36c0..ebae0e2683 100644 --- a/lib/ssl/test/openssl_sni_SUITE.erl +++ b/lib/ssl/test/openssl_sni_SUITE.erl @@ -38,7 +38,8 @@ all() -> %% Note: SNI not supported in sslv3 case ssl_test_lib:openssl_sane_dtls() of true -> - [{group, 'tlsv1.2'}, + [{group, 'tlsv1.3'}, + {group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'} %% Seems broken in openssl @@ -46,7 +47,8 @@ all() -> %%{group, 'dtlsv1'} ]; false -> - [{group, 'tlsv1.2'}, + [{group, 'tlsv1.3'}, + {group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}] end. @@ -54,7 +56,8 @@ all() -> groups() -> case ssl_test_lib:openssl_sane_dtls() of true -> - [{'tlsv1.2', [], sni_tests()}, + [{'tlsv1.3', [], sni_tests()}, + {'tlsv1.2', [], sni_tests()}, {'tlsv1.1', [], sni_tests()}, {'tlsv1', [], sni_tests()} %% Seems broken in openssl @@ -62,7 +65,8 @@ groups() -> %%{'dtlsv1', [], sni_tests()} ]; false -> - [{'tlsv1.2', [], sni_tests()}, + [{'tlsv1.3', [], sni_tests()}, + {'tlsv1.2', [], sni_tests()}, {'tlsv1.1', [], sni_tests()}, {'tlsv1', [], sni_tests()} ] diff --git a/lib/ssl/test/property_test/ssl_eqc_handshake.erl b/lib/ssl/test/property_test/ssl_eqc_handshake.erl index 9ae267a2d3..f19a74898d 100644 --- a/lib/ssl/test/property_test/ssl_eqc_handshake.erl +++ b/lib/ssl/test/property_test/ssl_eqc_handshake.erl @@ -591,7 +591,7 @@ server_hello_selected_version() -> #server_hello_selected_version{selected_version = {3,4}}. request_update() -> - oneof([?UPDATE_NOT_REQUESTED, ?UPDATE_REQUESTED]). + oneof([update_not_requested, update_requested]). certificate_chain()-> Conf = cert_conf(), diff --git a/lib/ssl/test/ssl_alert_SUITE.erl b/lib/ssl/test/ssl_alert_SUITE.erl index a5b9fe9b0e..6b02df759f 100644 --- a/lib/ssl/test/ssl_alert_SUITE.erl +++ b/lib/ssl/test/ssl_alert_SUITE.erl @@ -59,7 +59,7 @@ alerts(Config) when is_list(Config) -> ?DECRYPT_ERROR, ?EXPORT_RESTRICTION, ?PROTOCOL_VERSION, ?INSUFFICIENT_SECURITY, ?INTERNAL_ERROR, ?USER_CANCELED, ?NO_RENEGOTIATION, ?UNSUPPORTED_EXTENSION, ?CERTIFICATE_UNOBTAINABLE, - ?UNRECOGNISED_NAME, ?BAD_CERTIFICATE_STATUS_RESPONSE, + ?UNRECOGNIZED_NAME, ?BAD_CERTIFICATE_STATUS_RESPONSE, ?BAD_CERTIFICATE_HASH_VALUE, ?UNKNOWN_PSK_IDENTITY, 255 %% Unsupported/unknow alert will result in a description too ], diff --git a/lib/ssl/test/ssl_cipher_suite_SUITE.erl b/lib/ssl/test/ssl_cipher_suite_SUITE.erl index e598d662e9..855533cc3d 100644 --- a/lib/ssl/test/ssl_cipher_suite_SUITE.erl +++ b/lib/ssl/test/ssl_cipher_suite_SUITE.erl @@ -32,6 +32,7 @@ %%-------------------------------------------------------------------- all() -> [ + {group, 'tlsv1.3'}, {group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}, @@ -42,6 +43,7 @@ all() -> groups() -> [ + {'tlsv1.3', [], tls_1_3_kex()}, {'tlsv1.2', [], kex()}, {'tlsv1.1', [], kex()}, {'tlsv1', [], kex()}, @@ -60,6 +62,7 @@ groups() -> ecdhe_rsa_aes_256_gcm, ecdhe_rsa_chacha20_poly1305 ]}, + {ecdhe_1_3_rsa_cert, [], tls_1_3_cipher_suites()}, {ecdhe_ecdsa, [],[ecdhe_ecdsa_rc4_128, ecdhe_ecdsa_3des_ede_cbc, ecdhe_ecdsa_aes_128_cbc, @@ -127,6 +130,17 @@ groups() -> ]} ]. + +tls_1_3_kex() -> + [{group, ecdhe_1_3_rsa_cert}]. + +tls_1_3_cipher_suites() -> + [aes_256_gcm_sha384, + aes_128_gcm_sha256, + chacha20_poly1305_sha256, + aes_128_ccm_sha256 + ]. + kex() -> rsa() ++ ecdsa() ++ dss() ++ anonymous(). @@ -186,7 +200,13 @@ end_per_suite(_Config) -> ssl:stop(), application:stop(crypto). - +init_per_group(GroupName, Config) when GroupName == ecdhe_1_3_rsa_cert -> + case proplists:get_bool(ecdh, proplists:get_value(public_keys, crypto:supports())) of + true -> + init_certs(GroupName, Config); + false -> + {skip, "Missing EC crypto support"} + end; init_per_group(GroupName, Config) when GroupName == ecdh_anon; GroupName == ecdhe_rsa; GroupName == ecdhe_psk -> @@ -318,6 +338,53 @@ init_per_testcase(TestCase, Config) when TestCase == psk_aes_256_ccm_8; _ -> {skip, "Missing AES_256_CCM crypto support"} end; +init_per_testcase(aes_256_gcm_sha384, Config) -> + SupCiphers = proplists:get_value(ciphers, crypto:supports()), + SupHashs = proplists:get_value(hashs, crypto:supports()), + case (lists:member(aes_256_gcm, SupCiphers)) andalso + (lists:member(sha384, SupHashs)) + of + true -> + ct:timetrap({seconds, 5}), + Config; + _ -> + {skip, "Missing AES_256_GCM_SHA384 crypto support"} + end; +init_per_testcase(aes_128_gcm_sha256, Config) -> + SupCiphers = proplists:get_value(ciphers, crypto:supports()), + SupHashs = proplists:get_value(hashs, crypto:supports()), + case (lists:member(aes_256_gcm, SupCiphers)) andalso + (lists:member(sha256, SupHashs)) + of + true -> + ct:timetrap({seconds, 5}), + Config; + _ -> + {skip, "Missing AES_128_GCM_SHA256 crypto support"} + end; +init_per_testcase(chacha20_poly1305_sha256, Config) -> + SupCiphers = proplists:get_value(ciphers, crypto:supports()), + SupHashs = proplists:get_value(hashs, crypto:supports()), + case (lists:member(chacha20_poly1305, SupCiphers)) andalso + (lists:member(sha256, SupHashs)) + of + true -> + ct:timetrap({seconds, 5}), + Config; + _ -> + {skip, "Missing chacha20_poly1305_sha256 crypto support"} + end; +init_per_testcase(aes_128_ccm_sha256, Config) -> + SupCiphers = proplists:get_value(ciphers, crypto:supports()), + SupHashs = proplists:get_value(hashs, crypto:supports()), + case (lists:member(aes_128_ccm, SupCiphers)) andalso + (lists:member(sha256, SupHashs)) of + true -> + ct:timetrap({seconds, 5}), + Config; + _ -> + {skip, "Missing AES_128_CCM_SHA256 crypto support"} + end; init_per_testcase(TestCase, Config) -> Cipher = ssl_test_lib:test_cipher(TestCase, Config), SupCiphers = proplists:get_value(ciphers, crypto:supports()), @@ -335,7 +402,6 @@ end_per_testcase(_TestCase, Config) -> %%-------------------------------------------------------------------- %% Initializtion ------------------------------------------ %%-------------------------------------------------------------------- - init_certs(srp_rsa, Config) -> DefConf = ssl_test_lib:default_cert_chain_conf(), CertChainConf = ssl_test_lib:gen_conf(rsa, rsa, DefConf, DefConf), @@ -367,6 +433,14 @@ init_certs(rsa, Config) -> [{tls_config, #{server_config => ServerOpts, client_config => ClientOpts}} | proplists:delete(tls_config, Config)]; +init_certs(ecdhe_1_3_rsa_cert, Config) -> + ClientExt = x509_test:extensions([{key_usage, [digitalSignature]}]), + {ClientOpts, ServerOpts} = ssl_test_lib:make_rsa_cert_chains([{server_chain, + [[],[],[{extensions, ClientExt}]]}], + Config, "_peer_rsa_digitalsign"), + [{tls_config, #{server_config => ServerOpts, + client_config => ClientOpts}} | + proplists:delete(tls_config, Config)]; init_certs(dhe_dss, Config) -> DefConf = ssl_test_lib:default_cert_chain_conf(), CertChainConf = ssl_test_lib:gen_conf(dsa, dsa, DefConf, DefConf), @@ -427,6 +501,22 @@ init_certs(_GroupName, Config) -> %% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- +aes_256_gcm_sha384(Config) when is_list(Config)-> + Version = ssl_test_lib:protocol_version(Config), + cipher_suite_test(ssl:str_to_suite("TLS_AES_256_GCM_SHA384"), Version, Config). + +aes_128_gcm_sha256(Config) when is_list(Config) -> + Version = ssl_test_lib:protocol_version(Config), + cipher_suite_test(ssl:str_to_suite("TLS_AES_128_GCM_SHA256"), Version, Config). + +chacha20_poly1305_sha256(Config) when is_list(Config) -> + Version = ssl_test_lib:protocol_version(Config), + cipher_suite_test(ssl:str_to_suite("TLS_CHACHA20_POLY1305_SHA256"), Version, Config). + +aes_128_ccm_sha256(Config) when is_list(Config) -> + Version = ssl_test_lib:protocol_version(Config), + cipher_suite_test(ssl:str_to_suite("TLS_AES_128_CCM_SHA256"), Version, Config). + %%-------------------------------------------------------------------- %% SRP -------------------------------------------------------- %%-------------------------------------------------------------------- @@ -775,3 +865,4 @@ test_ciphers(Kex, Cipher, Version) -> (_) -> false end}]). + diff --git a/lib/ssl/test/ssl_key_update_SUITE.erl b/lib/ssl/test/ssl_key_update_SUITE.erl new file mode 100644 index 0000000000..2816f1a39e --- /dev/null +++ b/lib/ssl/test/ssl_key_update_SUITE.erl @@ -0,0 +1,136 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2020-2020. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(ssl_key_update_SUITE). + +%% Callback functions +-export([all/0, + groups/0, + init_per_suite/1, + end_per_suite/1, + init_per_group/2, + end_per_group/2, + init_per_testcase/2, + end_per_testcase/2]). + +%% Testcases +-export([key_update_at/0, + key_update_at/1, + explicit_key_update/0, + explicit_key_update/1]). + +-include_lib("common_test/include/ct.hrl"). + +all() -> + [{group, 'tlsv1.3'}]. + +groups() -> + [{'tlsv1.3', [], tls_1_3_tests()}]. + +tls_1_3_tests() -> + [key_update_at, + explicit_key_update]. + +init_per_suite(Config0) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + case proplists:get_bool(ecdh, proplists:get_value(public_keys, crypto:supports())) of + true -> + ssl_test_lib:make_ecdsa_cert(Config0); + false -> + {skip, "Missing EC crypto support"} + end + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:unload(ssl), + application:stop(crypto). + +init_per_group(GroupName, Config) -> + ssl_test_lib:init_per_group(GroupName, Config). + +end_per_group(GroupName, Config) -> + ssl_test_lib:end_per_group(GroupName, Config). + +init_per_testcase(_TestCase, Config) -> + ssl_test_lib:ct_log_supported_protocol_versions(Config), + ct:timetrap({seconds, 10}), + Config. + +end_per_testcase(_TestCase, Config) -> + Config. + + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +key_update_at() -> + [{doc,"Test option 'key_update_at' between erlang client and erlang server."}]. + +key_update_at(Config) -> + %% {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Data = "123456789012345", %% 15 bytes + + Server = ssl_test_lib:start_server(erlang, [{log_level, debug}, + {key_update_at, 15}], Config), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client(erlang, [{port, Port}, + {log_level, debug}, + {key_update_at, 15}], Config), + %% Sending bytes over limit triggers key update + ssl_test_lib:send(Client, Data), + Data = ssl_test_lib:check_active_receive(Server, Data), + %% TODO check if key has been updated (needs debug logging of secrets) + + %% Test mechanism to prevent infinite loop of key updates + BigData = binary:copy(<<"1234567890">>, 10), %% 100 bytes + ok = ssl_test_lib:send(Client, BigData), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +explicit_key_update() -> + [{doc,"Test ssl:update_key/2 between erlang client and erlang server."}]. + +explicit_key_update(Config) -> + Data = "123456789012345", %% 15 bytes + + Server = ssl_test_lib:start_server(erlang, [{log_level, debug}], Config), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client(erlang, [{port, Port}, {log_level, debug}], Config), + ssl_test_lib:send_recv_result_active(Client, Server, Data), + + ssl_test_lib:update_keys(Client, write), + ssl_test_lib:update_keys(Client, read_write), + ssl_test_lib:send_recv_result_active(Client, Server, Data), + + ssl_test_lib:update_keys(Server, write), + ssl_test_lib:update_keys(Server, read_write), + ssl_test_lib:send_recv_result_active(Client, Server, Data), + %% TODO check if key has been updated (needs debug logging of secrets) + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index b71be64787..99cf19f3ef 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -50,6 +50,7 @@ %%-------------------------------------------------------------------- all() -> [ + {group, 'tlsv1.3'}, {group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}, @@ -59,7 +60,8 @@ all() -> ]. groups() -> - [{'tlsv1.2', [], socket_packet_tests() ++ protocol_packet_tests()}, + [{'tlsv1.3', [], socket_packet_tests() ++ protocol_packet_tests()}, + {'tlsv1.2', [], socket_packet_tests() ++ protocol_packet_tests()}, {'tlsv1.1', [], socket_packet_tests() ++ protocol_packet_tests()}, {'tlsv1', [], socket_packet_tests() ++ protocol_packet_tests()}, {'sslv3', [], socket_packet_tests() ++ protocol_packet_tests()}, @@ -2009,7 +2011,8 @@ packet(Config, Data, Send, Recv, Quantity, Packet, Active) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, Send ,[Data, Quantity]}}, - {options, [{packet, Packet}, {nodelay, true}| ServerOpts]}]), + {options, [{packet, Packet}, {nodelay, true}| ServerOpts] + ++ ssl_test_lib:bigger_buffers()}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, @@ -2018,7 +2021,7 @@ packet(Config, Data, Send, Recv, Quantity, Packet, Active) -> {options, [{active, Active}, {nodelay, true}, {packet, Packet} | - ClientOpts]}]), + ClientOpts] ++ ssl_test_lib:bigger_buffers()}]), ssl_test_lib:check_result(Client, ok), diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl index 91043408b7..4374d9ff47 100644 --- a/lib/ssl/test/ssl_payload_SUITE.erl +++ b/lib/ssl/test/ssl_payload_SUITE.erl @@ -27,11 +27,13 @@ -define(TIMEOUT, 600000). + %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- all() -> [ + {group, 'tlsv1.3'}, {group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}, @@ -40,6 +42,7 @@ all() -> groups() -> [ + {'tlsv1.3', [], payload_tests()}, {'tlsv1.2', [], payload_tests()}, {'tlsv1.1', [], payload_tests()}, {'tlsv1', [], payload_tests()}, @@ -523,7 +526,7 @@ server_echos_passive( [{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, echoer, [Length]}}, - {options, [{active, false}, {mode, binary} | ServerOpts]}]), + {options, [{active, false}, {mode, binary} | ServerOpts] ++ ssl_test_lib:bigger_buffers()}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client( @@ -531,7 +534,7 @@ server_echos_passive( {host, Hostname}, {from, self()}, {mfa, {?MODULE, sender, [Data]}}, - {options, [{active, false}, {mode, binary} | ClientOpts]}]), + {options, [{active, false}, {mode, binary} | ClientOpts] ++ ssl_test_lib:bigger_buffers() }]), %% ssl_test_lib:check_result(Server, ok, Client, ok), %% @@ -569,7 +572,7 @@ server_echos_active_once( [{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, echoer_active_once, [Length]}}, - {options, [{active, once}, {mode, binary} | ServerOpts]}]), + {options, [{active, once}, {mode, binary} | ServerOpts] ++ ssl_test_lib:bigger_buffers()}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client( @@ -577,7 +580,7 @@ server_echos_active_once( {host, Hostname}, {from, self()}, {mfa, {?MODULE, sender_active_once, [Data]}}, - {options, [{active, once}, {mode, binary} | ClientOpts]}]), + {options, [{active, once}, {mode, binary} | ClientOpts] ++ ssl_test_lib:bigger_buffers() }]), %% ssl_test_lib:check_result(Server, ok, Client, ok), %% @@ -593,7 +596,7 @@ server_echos_active( [{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, echoer_active, [Length]}}, - {options, [{active, true}, {mode, binary} | ServerOpts]}]), + {options, [{active, true}, {mode, binary} | ServerOpts] ++ ssl_test_lib:bigger_buffers()}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client( @@ -601,7 +604,7 @@ server_echos_active( {host, Hostname}, {from, self()}, {mfa, {?MODULE, sender_active, [Data]}}, - {options, [{active, true}, {mode, binary} | ClientOpts]}]), + {options, [{active, true}, {mode, binary} | ClientOpts] ++ ssl_test_lib:bigger_buffers()}]), %% ssl_test_lib:check_result(Server, ok, Client, ok), %% @@ -616,7 +619,7 @@ client_echos_passive( [{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, sender, [Data]}}, - {options, [{active, false}, {mode, binary} | ServerOpts]}]), + {options, [{active, false}, {mode, binary} | ServerOpts] ++ ssl_test_lib:bigger_buffers()}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client( @@ -624,7 +627,7 @@ client_echos_passive( {host, Hostname}, {from, self()}, {mfa, {?MODULE, echoer, [Length]}}, - {options, [{active, false}, {mode, binary} | ClientOpts]}]), + {options, [{active, false}, {mode, binary} | ClientOpts] ++ ssl_test_lib:bigger_buffers()}]), %% ssl_test_lib:check_result(Server, ok, Client, ok), %% @@ -640,7 +643,7 @@ client_echos_passive_chunk( [{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, sender, [Data]}}, - {options, [{active, false}, {mode, binary} | ServerOpts]}]), + {options, [{active, false}, {mode, binary} | ServerOpts] ++ ssl_test_lib:bigger_buffers()}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client( @@ -648,7 +651,7 @@ client_echos_passive_chunk( {host, Hostname}, {from, self()}, {mfa, {?MODULE, echoer_chunk, [Length]}}, - {options, [{active, false}, {mode, binary} | ClientOpts]}]), + {options, [{active, false}, {mode, binary} | ClientOpts] ++ ssl_test_lib:bigger_buffers()}]), %% ssl_test_lib:check_result(Server, ok, Client, ok), %% @@ -664,7 +667,7 @@ client_echos_active_once( [{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, sender_active_once, [Data]}}, - {options, [{active, once}, {mode, binary} | ServerOpts]}]), + {options, [{active, once}, {mode, binary} | ServerOpts] ++ ssl_test_lib:bigger_buffers()}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client( @@ -672,7 +675,7 @@ client_echos_active_once( {host, Hostname}, {from, self()}, {mfa, {?MODULE, echoer_active_once, [Length]}}, - {options,[{active, once}, {mode, binary} | ClientOpts]}]), + {options,[{active, once}, {mode, binary} | ClientOpts] ++ ssl_test_lib:bigger_buffers()}]), %% ssl_test_lib:check_result(Server, ok, Client, ok), %% @@ -687,7 +690,7 @@ client_echos_active( [{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, sender_active, [Data]}}, - {options, [{active, true}, {mode, binary} | ServerOpts]}]), + {options, [{active, true}, {mode, binary} | ServerOpts] ++ ssl_test_lib:bigger_buffers()}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client( @@ -695,7 +698,7 @@ client_echos_active( {host, Hostname}, {from, self()}, {mfa, {?MODULE, echoer_active, [Length]}}, - {options, [{active, true}, {mode, binary} | ClientOpts]}]), + {options, [{active, true}, {mode, binary} | ClientOpts] ++ ssl_test_lib:bigger_buffers()}]), % ssl_test_lib:check_result(Server, ok, Client, ok), %% @@ -811,3 +814,4 @@ echo_active(Socket, Size) -> echo_active(Socket, Size - byte_size(Data)) end. + diff --git a/lib/ssl/test/ssl_renegotiate_SUITE.erl b/lib/ssl/test/ssl_renegotiate_SUITE.erl index ef3f9ebb52..921604458a 100644 --- a/lib/ssl/test/ssl_renegotiate_SUITE.erl +++ b/lib/ssl/test/ssl_renegotiate_SUITE.erl @@ -115,6 +115,18 @@ end_per_group(GroupName, Config) -> Config end. +init_per_testcase(TestCase, Config) when TestCase == renegotiate_dos_mitigate_active; + TestCase == renegotiate_dos_mitigate_passive; + TestCase == renegotiate_dos_mitigate_absolute -> + ct:timetrap({seconds, 160}), + Config; +init_per_testcase(_, Config) -> + ct:timetrap({seconds, 15}), + Config. + +end_per_testcase(_, Config) -> + Config. + %%-------------------------------------------------------------------- %% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- diff --git a/lib/ssl/test/ssl_session_SUITE.erl b/lib/ssl/test/ssl_session_SUITE.erl index aa79698a72..f8dd633ed4 100644 --- a/lib/ssl/test/ssl_session_SUITE.erl +++ b/lib/ssl/test/ssl_session_SUITE.erl @@ -25,6 +25,7 @@ -compile(export_all). -include("tls_handshake.hrl"). +-include("ssl_record.hrl"). -include_lib("common_test/include/ct.hrl"). -include_lib("public_key/include/public_key.hrl"). @@ -48,11 +49,11 @@ all() -> groups() -> [{'dtlsv1.2', [], session_tests()}, {'dtlsv1', [], session_tests()}, - {'tlsv1.3', [], session_tests()}, - {'tlsv1.2', [], session_tests()}, - {'tlsv1.1', [], session_tests()}, - {'tlsv1', [], session_tests()}, - {'sslv3', [], session_tests()} + {'tlsv1.3', [], session_tests() ++ tls_session_tests()}, + {'tlsv1.2', [], session_tests() ++ tls_session_tests()}, + {'tlsv1.1', [], session_tests() ++ tls_session_tests()}, + {'tlsv1', [], session_tests() ++ tls_session_tests()}, + {'sslv3', [], session_tests() ++ tls_session_tests()} ]. session_tests() -> @@ -62,6 +63,8 @@ session_tests() -> no_reuses_session_server_restart_new_cert, no_reuses_session_server_restart_new_cert_file]. +tls_session_tests() -> + [session_table_stable_size_on_tcp_close]. init_per_suite(Config0) -> catch crypto:stop(), @@ -372,6 +375,177 @@ no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) -> ssl_test_lib:close(Server1), ssl_test_lib:close(Client1). +session_table_stable_size_on_tcp_close() -> + [{doc, "Check that new sessions are cleanup when connection is closed abruptly during first handshake"}]. + +session_table_stable_size_on_tcp_close(Config) when is_list(Config)-> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop] = StatusInfo, + State = ssl_test_lib:state(Prop), + ServerCache = element(3, State), + + N = ets:info(ServerCache, size), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, [{reuseaddr, true} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + faulty_client(Hostname, Port), + check_table_did_not_grow(ServerCache, N). + + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- +check_table_did_not_grow(ServerCache, N) -> + ct:sleep(500), + check_table_did_not_grow(ServerCache, N, 10). + +check_table_did_not_grow(_, _, 0) -> + ct:fail(table_grew); +check_table_did_not_grow(ServerCache, N, Tries) -> + case ets:info(ServerCache, size) of + N -> + ok; + _ -> + ct:sleep(500), + check_table_did_not_grow(ServerCache, N, Tries -1) + end. + +faulty_client(Host, Port) -> + {ok, Sock} = gen_tcp:connect(Host, Port, [], 10000), + Random = crypto:strong_rand_bytes(32), + CH = client_hello(Random), + CHBin = encode_client_hello(CH, Random), + gen_tcp:send(Sock, CHBin), + ct:sleep(100), + gen_tcp:close(Sock). + + +server(LOpts, Port) -> + {ok, LSock} = ssl:listen(Port, LOpts), + Pid = spawn_link(?MODULE, accept_loop, [LSock]), + ssl:controlling_process(LSock, Pid), + Pid. + +accept_loop(Sock) -> + {ok, CSock} = ssl:transport_accept(Sock), + _ = ssl:handshake(CSock), + accept_loop(Sock). + + +encode_client_hello(CH, Random) -> + HSBin = tls_handshake:encode_handshake(CH, {3,3}), + CS = connection_states(Random), + {Encoded, _} = tls_record:encode_handshake(HSBin, {3,3}, CS), + Encoded. + +client_hello(Random) -> + CipherSuites = [<<0,255>>, <<"À,">>, <<"À0">>, <<"À$">>, <<"À(">>, + <<"À.">>, <<"À2">>, <<"À&">>, <<"À*">>, <<0,159>>, + <<0,163>>, <<0,107>>, <<0,106>>, <<"À+">>, <<"À/">>, + <<"À#">>, <<"À'">>, <<"À-">>, <<"À1">>, <<"À%">>, + <<"À)">>, <<0,158>>, <<0,162>>, <<0,103>>, <<0,64>>, + <<"À\n">>, <<192,20>>, <<0,57>>, <<0,56>>, <<192,5>>, + <<192,15>>, <<"À\t">>, <<192,19>>, <<0,51>>, <<0,50>>, + <<192,4>>, <<192,14>>], + Extensions = #{alpn => undefined, + ec_point_formats => + {ec_point_formats, + [0]}, + elliptic_curves => + {elliptic_curves, + [{1,3,132,0,39}, + {1,3,132,0,38}, + {1,3,132,0,35}, + {1,3,36,3,3,2, + 8,1,1,13}, + {1,3,132,0,36}, + {1,3,132,0,37}, + {1,3,36,3,3,2, + 8,1,1,11}, + {1,3,132,0,34}, + {1,3,132,0,16}, + {1,3,132,0,17}, + {1,3,36,3,3,2, + 8,1,1,7}, + {1,3,132,0,10}, + {1,2,840, + 10045,3,1,7}, + {1,3,132,0,3}, + {1,3,132,0,26}, + {1,3,132,0,27}, + {1,3,132,0,32}, + {1,3,132,0,33}, + {1,3,132,0,24}, + {1,3,132,0,25}, + {1,3,132,0,31}, + {1,2,840, + 10045,3,1,1}, + {1,3,132,0,1}, + {1,3,132,0,2}, + {1,3,132,0,15}, + {1,3,132,0,9}, + {1,3,132,0,8}, + {1,3,132,0, + 30}]}, + next_protocol_negotiation => + undefined, + renegotiation_info => + {renegotiation_info, + undefined}, + signature_algs => + {hash_sign_algos, + [{sha512,ecdsa}, + {sha512,rsa}, + {sha384,ecdsa}, + {sha384,rsa}, + {sha256,ecdsa}, + {sha256,rsa}, + {sha224,ecdsa}, + {sha224,rsa}, + {sha,ecdsa}, + {sha,rsa}, + {sha,dsa}]}, + sni => + {sni, + "localhost"}, + srp => + undefined}, + + #client_hello{client_version = {3,3}, + random = Random, + session_id = crypto:strong_rand_bytes(32), + cipher_suites = CipherSuites, + compression_methods = [0], + extensions = Extensions + }. + +connection_states(Random) -> + #{current_write => + #{beast_mitigation => one_n_minus_one,cipher_state => undefined, + client_verify_data => undefined,compression_state => undefined, + mac_secret => undefined,secure_renegotiation => undefined, + security_parameters => + #security_parameters{ + cipher_suite = <<0,0>>, + connection_end = 1, + bulk_cipher_algorithm = 0, + cipher_type = 0, + iv_size = 0, + key_size = 0, + key_material_length = 0, + expanded_key_material_length = 0, + mac_algorithm = 0, + prf_algorithm = 0, + hash_size = 0, + compression_algorithm = 0, + master_secret = undefined, + resumption_master_secret = undefined, + client_random = Random, + server_random = undefined, + exportable = undefined}, + sequence_number => 0,server_verify_data => undefined}}. diff --git a/lib/ssl/test/ssl_session_ticket_SUITE.erl b/lib/ssl/test/ssl_session_ticket_SUITE.erl index 96b0fb5c2a..3d41b59223 100644 --- a/lib/ssl/test/ssl_session_ticket_SUITE.erl +++ b/lib/ssl/test/ssl_session_ticket_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2019. All Rights Reserved. +%% Copyright Ericsson AB 2007-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,11 +18,27 @@ %% %CopyrightEnd% %% -%% -module(ssl_session_ticket_SUITE). -%% Note: This directive should only be used in test suites. --compile(export_all). +%% Callback functions +-export([all/0, + groups/0, + init_per_suite/1, + end_per_suite/1, + init_per_group/2, + end_per_group/2, + init_per_testcase/2, + end_per_testcase/2]). + +%% Testcases +-export([basic/0, + basic/1, + hello_retry_request/0, + hello_retry_request/1, + multiple_tickets/0, + multiple_tickets/1, + multiple_tickets_2hash/0, + multiple_tickets_2hash/1]). -include("tls_handshake.hrl"). @@ -40,21 +56,15 @@ all() -> ]. groups() -> - [{'tlsv1.3', [], [{group, stateful}, {group, stateless}, {group, openssl_server}]}, - {openssl_server, [], [erlang_client_openssl_server_basic, - erlang_client_openssl_server_hrr, - erlang_client_openssl_server_hrr_multiple_tickets - ]}, + [{'tlsv1.3', [], [{group, stateful}, {group, stateless}]}, {stateful, [], session_tests()}, {stateless, [], session_tests()}]. session_tests() -> - [erlang_client_erlang_server_basic, - openssl_client_erlang_server_basic, - erlang_client_erlang_server_hrr, - openssl_client_erlang_server_hrr, - erlang_client_erlang_server_multiple_tickets, - erlang_client_erlang_server_multiple_tickets_2hash]. + [basic, + hello_retry_request, + multiple_tickets, + multiple_tickets_2hash]. init_per_suite(Config0) -> catch crypto:stop(), @@ -75,27 +85,10 @@ init_per_group(stateful, Config) -> init_per_group(stateless, Config) -> [{server_ticket_mode, stateless} | proplists:delete(server_ticket_mode, Config)]; init_per_group(GroupName, Config) -> - ssl_test_lib:clean_tls_version(Config), - case ssl_test_lib:is_tls_version(GroupName) andalso ssl_test_lib:sufficient_crypto_support(GroupName) of - true -> - ssl_test_lib:init_tls_version(GroupName, Config); - _ -> - case ssl_test_lib:sufficient_crypto_support(GroupName) of - true -> - ssl:start(), - Config; - false -> - {skip, "Missing crypto support"} - end - end. + ssl_test_lib:init_per_group(GroupName, Config). end_per_group(GroupName, Config) -> - case ssl_test_lib:is_tls_version(GroupName) of - true -> - ssl_test_lib:clean_tls_version(Config); - false -> - Config - end. + ssl_test_lib:end_per_group(GroupName, Config). init_per_testcase(_, Config) -> ssl:stop(), @@ -111,10 +104,9 @@ end_per_testcase(_TestCase, Config) -> %% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- - -erlang_client_erlang_server_basic() -> +basic() -> [{doc,"Test session resumption with session tickets (erlang client - erlang server)"}]. -erlang_client_erlang_server_basic(Config) when is_list(Config) -> +basic(Config) when is_list(Config) -> ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -166,127 +158,9 @@ erlang_client_erlang_server_basic(Config) when is_list(Config) -> ssl_test_lib:close(Server0), ssl_test_lib:close(Client1). - -erlang_client_openssl_server_basic() -> - [{doc,"Test session resumption with session tickets (erlang client - openssl server)"}]. -erlang_client_openssl_server_basic(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Version = 'tlsv1.3', - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - CACertFile = proplists:get_value(cacertfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - - %% Configure session tickets - ClientOpts = [{session_tickets, auto}, {log_level, debug}, - {versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], - - Exe = "openssl", - Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), - "-cert", CertFile,"-key", KeyFile, "-CAfile", CACertFile, "-msg", "-debug"], - - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - %% Store ticket from first connection - Client0 = ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, - verify_active_session_resumption, - [false, no_reply]}}, - {from, self()}, {options, ClientOpts}]), - %% Wait for session ticket - ct:sleep(100), - - %% Close previous connection as s_server can only handle one at a time - ssl_test_lib:close(Client0), - - %% Use ticket - Client1 = ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, - verify_active_session_resumption, - [true, no_reply]}}, - {from, self()}, - {options, ClientOpts}]), - process_flag(trap_exit, false), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - ssl_test_lib:close(Client1). - - -openssl_client_erlang_server_basic() -> - [{doc,"Test session resumption with session tickets (openssl client - erlang server)"}]. -openssl_client_erlang_server_basic(Config) when is_list(Config) -> - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - TicketFile0 = filename:join([proplists:get_value(priv_dir, Config), "session_ticket0"]), - TicketFile1 = filename:join([proplists:get_value(priv_dir, Config), "session_ticket1"]), - ServerTicketMode = proplists:get_value(server_ticket_mode, Config), - - Data = "Hello world", - - %% Configure session tickets - ServerOpts = [{session_tickets, ServerTicketMode}, {log_level, debug}, - {versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0], - - Server0 = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - verify_active_session_resumption, - [false]}}, - {options, ServerOpts}]), - - Version = 'tlsv1.3', - Port0 = ssl_test_lib:inet_port(Server0), - - Exe = "openssl", - Args0 = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname) - ++ ":" ++ integer_to_list(Port0), - ssl_test_lib:version_flag(Version), - "-sess_out", TicketFile0], - - OpenSslPort0 = ssl_test_lib:portable_open_port(Exe, Args0), - - true = port_command(OpenSslPort0, Data), - - ssl_test_lib:check_result(Server0, ok), - - Server0 ! {listen, {mfa, {ssl_test_lib, - verify_active_session_resumption, - [true]}}}, - - %% Wait for session ticket - ct:sleep(100), - - Args1 = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname) - ++ ":" ++ integer_to_list(Port0), - ssl_test_lib:version_flag(Version), - "-sess_in", TicketFile0, - "-sess_out", TicketFile1], - - OpenSslPort1 = ssl_test_lib:portable_open_port(Exe, Args1), - - true = port_command(OpenSslPort1, Data), - - ssl_test_lib:check_result(Server0, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server0), - ssl_test_lib:close_port(OpenSslPort0), - ssl_test_lib:close_port(OpenSslPort1). - - -erlang_client_erlang_server_hrr() -> +hello_retry_request() -> [{doc,"Test session resumption with session tickets and hello_retry_request (erlang client - erlang server)"}]. -erlang_client_erlang_server_hrr(Config) when is_list(Config) -> +hello_retry_request(Config) when is_list(Config) -> ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -340,135 +214,9 @@ erlang_client_erlang_server_hrr(Config) when is_list(Config) -> ssl_test_lib:close(Server0), ssl_test_lib:close(Client1). - -erlang_client_openssl_server_hrr() -> - [{doc,"Test session resumption with session tickets and hello_retry_request (erlang client - openssl server)"}]. -erlang_client_openssl_server_hrr(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Version = 'tlsv1.3', - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - CACertFile = proplists:get_value(cacertfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - - %% Configure session tickets - ClientOpts = [{session_tickets, auto}, {log_level, debug}, - {versions, ['tlsv1.2','tlsv1.3']}, - {supported_groups,[secp256r1, x25519]}|ClientOpts0], - - Exe = "openssl", - Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), - "-cert", CertFile, - "-key", KeyFile, - "-CAfile", CACertFile, - "-groups", "X448:X25519", - "-msg", "-debug"], - - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - %% Store ticket from first connection - Client0 = ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, - verify_active_session_resumption, - [false, no_reply]}}, - {from, self()}, {options, ClientOpts}]), - %% Wait for session ticket - ct:sleep(100), - - %% Close previous connection as s_server can only handle one at a time - ssl_test_lib:close(Client0), - - %% Use ticket - Client1 = ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, - verify_active_session_resumption, - [true, no_reply]}}, - {from, self()}, - {options, ClientOpts}]), - process_flag(trap_exit, false), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - ssl_test_lib:close(Client1). - - -openssl_client_erlang_server_hrr() -> - [{doc,"Test session resumption with session tickets and hello_retry_request (openssl client - erlang server)"}]. -openssl_client_erlang_server_hrr(Config) when is_list(Config) -> - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - TicketFile0 = filename:join([proplists:get_value(priv_dir, Config), "session_ticket0"]), - TicketFile1 = filename:join([proplists:get_value(priv_dir, Config), "session_ticket1"]), - ServerTicketMode = proplists:get_value(server_ticket_mode, Config), - - Data = "Hello world", - - %% Configure session tickets - ServerOpts = [{session_tickets, ServerTicketMode}, {log_level, debug}, - {versions, ['tlsv1.2','tlsv1.3']}, - {supported_groups,[x448, x25519]}|ServerOpts0], - - Server0 = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - verify_active_session_resumption, - [false]}}, - {options, ServerOpts}]), - - Version = 'tlsv1.3', - Port0 = ssl_test_lib:inet_port(Server0), - - Exe = "openssl", - Args0 = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname) - ++ ":" ++ integer_to_list(Port0), - ssl_test_lib:version_flag(Version), - "-groups", "P-256:X25519", - "-sess_out", TicketFile0], - - OpenSslPort0 = ssl_test_lib:portable_open_port(Exe, Args0), - - true = port_command(OpenSslPort0, Data), - - ssl_test_lib:check_result(Server0, ok), - - Server0 ! {listen, {mfa, {ssl_test_lib, - verify_active_session_resumption, - [true]}}}, - - %% Wait for session ticket - ct:sleep(100), - - Args1 = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname) - ++ ":" ++ integer_to_list(Port0), - ssl_test_lib:version_flag(Version), - "-groups", "P-256:X25519", - "-sess_in", TicketFile0, - "-sess_out", TicketFile1], - - OpenSslPort1 = ssl_test_lib:portable_open_port(Exe, Args1), - - true = port_command(OpenSslPort1, Data), - - ssl_test_lib:check_result(Server0, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server0), - ssl_test_lib:close_port(OpenSslPort0), - ssl_test_lib:close_port(OpenSslPort1). - - -erlang_client_erlang_server_multiple_tickets() -> +multiple_tickets() -> [{doc,"Test session resumption with multiple session tickets (erlang client - erlang server)"}]. -erlang_client_erlang_server_multiple_tickets(Config) when is_list(Config) -> +multiple_tickets(Config) when is_list(Config) -> ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -524,72 +272,9 @@ erlang_client_erlang_server_multiple_tickets(Config) when is_list(Config) -> ssl_test_lib:close(Server0), ssl_test_lib:close(Client1). - -erlang_client_openssl_server_hrr_multiple_tickets() -> - [{doc,"Test session resumption with multiple session tickets and hello_retry_request (erlang client - openssl server)"}]. -erlang_client_openssl_server_hrr_multiple_tickets(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Version = 'tlsv1.3', - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - CACertFile = proplists:get_value(cacertfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - - %% Configure session tickets - ClientOpts = [{session_tickets, manual}, {log_level, debug}, - {versions, ['tlsv1.2','tlsv1.3']}, - {supported_groups,[secp256r1, x25519]}|ClientOpts0], - - Exe = "openssl", - Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), - "-cert", CertFile, - "-key", KeyFile, - "-CAfile", CACertFile, - "-groups", "X448:X25519", - "-msg", "-debug"], - - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - %% Store ticket from first connection - Client0 = ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, - verify_active_session_resumption, - [false, no_reply, {tickets, 2}]}}, - {from, self()}, {options, ClientOpts}]), - - Tickets0 = ssl_test_lib:check_tickets(Client0), - - ct:pal("Received tickets: ~p~n", [Tickets0]), - - %% Close previous connection as s_server can only handle one at a time - ssl_test_lib:close(Client0), - - %% Use tickets - Client1 = ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, - verify_active_session_resumption, - [true, no_reply, no_tickets]}}, - {from, self()}, - {options, [{use_ticket, Tickets0}|ClientOpts]}]), - - process_flag(trap_exit, false), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - ssl_test_lib:close(Client1). - - -erlang_client_erlang_server_multiple_tickets_2hash() -> +multiple_tickets_2hash() -> [{doc,"Test session resumption with multiple session tickets with 2 different hash algorithms (erlang client - erlang server)"}]. -erlang_client_erlang_server_multiple_tickets_2hash(Config) when is_list(Config) -> +multiple_tickets_2hash(Config) when is_list(Config) -> ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), diff --git a/lib/ssl/test/ssl_sni_SUITE.erl b/lib/ssl/test/ssl_sni_SUITE.erl index e68ea2c99d..e58a4642fb 100644 --- a/lib/ssl/test/ssl_sni_SUITE.erl +++ b/lib/ssl/test/ssl_sni_SUITE.erl @@ -60,7 +60,9 @@ sni_tests() -> ip_fallback, no_ip_fallback, dns_name_reuse, - customize_hostname_check]. + customize_hostname_check, + sni_no_trailing_dot, + hostname_trailing_dot]. init_per_suite(Config0) -> catch crypto:stop(), @@ -288,6 +290,58 @@ customize_hostname_check(Config) when is_list(Config) -> ]), ssl_test_lib:check_client_alert(Server, Client1, handshake_failure). +sni_no_trailing_dot() -> + [{doc,"Test that sni may not include a triling dot"}]. +sni_no_trailing_dot(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_cert_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{log_level, debug} | ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{log_level, debug}, + {server_name_indication, Hostname ++ "."} |ClientOpts]}]), + ssl_test_lib:check_server_alert(Server, Client, unrecognized_name). + +hostname_trailing_dot() -> + [{doc,"Test that fallback sni removes trailing dot of hostname"}]. + +hostname_trailing_dot(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname0} = ssl_test_lib:run_where(Config), + + case trailing_dot_hostname(Hostname0) of + {ok, Hostname} -> + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client); + {skip, _ } = Skip -> + Skip + end. + %%-------------------------------------------------------------------- %% Internal Functions ------------------------------------------------ %%-------------------------------------------------------------------- @@ -426,3 +480,17 @@ host_name(undefined, Hostname) -> Hostname; host_name(Hostname, _) -> Hostname. + +trailing_dot_hostname(HostName) -> + case lists:member($., HostName) of + true -> + case lists:last(HostName) =/= $. of + true -> + {ok, HostName ++ "."}; + false -> + {ok, HostName} + end; + _ -> + {skip, "Trailing dot conf not possible"} + end. + diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index b4b493fbe2..206c4c8b32 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -32,6 +32,92 @@ -define(SLEEP, 1000). -define(DEFAULT_CURVE, secp256r1). +%%==================================================================== +%% API +%%==================================================================== +start_client(erlang, Options, Config) -> + start_client(Options, Config); +start_client(openssl, Options, Config) -> + start_openssl_client(Options, Config); +start_client(Type, _Args, _Config) -> + {error, unsupported_client_type, Type}. + +start_server(erlang, Options, Config) -> + start_server(Options, Config); +start_server(openssl, Options, Config) -> + start_openssl_server(Options, Config); +start_server(Type, _Args, _Config) -> + {error, unsupported_server_type, Type}. + +%% Test +send_recv_result_active(Peer1, Peer2, Data) -> + ok = ssl_test_lib:send(Peer1, Data), + Data = ssl_test_lib:check_active_receive(Peer2, Data), + ok = ssl_test_lib:send(Peer2, Data), + Data = ssl_test_lib:check_active_receive(Peer1, Data). + +%% Certs +init_ecdsa_certs(Config) -> + DefConf = ssl_test_lib:default_cert_chain_conf(), + CertChainConf = ssl_test_lib:gen_conf(ecdsa, ecdsa, DefConf, DefConf), + #{server_config := ServerOpts, + client_config := ClientOpts} + = public_key:pkix_test_data(CertChainConf), + [{tls_config, #{server_config => ServerOpts, + client_config => ClientOpts}} | + proplists:delete(tls_config, Config)]. + +%% Options +get_server_opts(Config) -> + SOpts = proplists:get_value(server_ecdsa_opts, Config), + ssl_test_lib:ssl_options(SOpts, Config). + +get_client_opts(Config) -> + COpts = proplists:get_value(client_ecdsa_opts, Config), + ssl_test_lib:ssl_options(COpts, Config). + +%% Default callback functions +init_per_group(GroupName, Config) -> + clean_tls_version(Config), + case is_tls_version(GroupName) andalso sufficient_crypto_support(GroupName) of + true -> + init_tls_version(GroupName, Config); + _ -> + case sufficient_crypto_support(GroupName) of + true -> + ssl:start(), + Config; + false -> + {skip, "Missing crypto support"} + end + end. + +init_per_group_openssl(GroupName, Config) -> + case is_tls_version(GroupName) of + true -> + case check_sane_openssl_version(GroupName) of + true -> + [{version, GroupName}|init_tls_version(GroupName, Config)]; + false -> + {skip, "Missing openssl support"} + end; + _ -> + ssl:start(), + Config + end. + +end_per_group(GroupName, Config) -> + case is_tls_version(GroupName) of + true -> + clean_tls_version(Config); + false -> + Config + end. + +%%==================================================================== +%% Internal functions +%%==================================================================== + %% For now always run locally run_where(_) -> ClientNode = node(), @@ -59,6 +145,18 @@ normalize_loopback({127,_,_,_}, client) -> normalize_loopback(Address, _) -> Address. + +start_server(Args0, Config) -> + {_, ServerNode, _} = ssl_test_lib:run_where(Config), + ServerOpts = ssl_test_lib:get_server_opts(Config), + Node = proplists:get_value(node, Args0, ServerNode), + Port = proplists:get_value(port, Args0, 0), + Args = [{from, self()}, + {node, Node}, + {port, Port}, + {options, ServerOpts} | Args0], + start_server(Args). +%% start_server(Args) -> Node = proplists:get_value(node, Args), Result = spawn_link(Node, ?MODULE, run_server, [Args]), @@ -108,17 +206,59 @@ do_run_server(_, ok = Result, Opts) -> do_run_server(ListenSocket, AcceptSocket, Opts) -> Pid = proplists:get_value(from, Opts), Transport = proplists:get_value(transport, Opts, ssl), - {Module, Function, Args} = proplists:get_value(mfa, Opts), - ct:log("~p:~p~nServer: apply(~p,~p,~p)~n", - [?MODULE,?LINE, Module, Function, [AcceptSocket | Args]]), - case apply(Module, Function, [AcceptSocket | Args]) of + MFA = proplists:get_value(mfa, Opts), + case server_apply_mfa(AcceptSocket, MFA) of no_result_msg -> ok; Msg -> ct:log("~p:~p~nServer Msg: ~p ~n", [?MODULE,?LINE, Msg]), Pid ! {self(), Msg} end, + do_run_server_core(ListenSocket, AcceptSocket, Opts, Transport, Pid). + +server_apply_mfa(_, undefined) -> + no_result_msg; +server_apply_mfa(AcceptSocket, {Module, Function, Args}) -> + ct:log("~p:~p~nServer: apply(~p,~p,~p)~n", + [?MODULE,?LINE, Module, Function, [AcceptSocket | Args]]), + apply(Module, Function, [AcceptSocket | Args]). + +client_apply_mfa(_, undefined) -> + no_result_msg; +client_apply_mfa(AcceptSocket, {Module, Function, Args}) -> + ct:log("~p:~p~nClient: apply(~p,~p,~p)~n", + [?MODULE,?LINE, Module, Function, [AcceptSocket | Args]]), + apply(Module, Function, [AcceptSocket | Args]). + + +do_run_server_core(ListenSocket, AcceptSocket, Opts, Transport, Pid) -> receive + {data, Data} -> + ct:log("[server] Send: ~p~n", [Data]), + case Transport:send(AcceptSocket, Data) of + ok -> + Pid ! {self(), ok}; + {error, Reason} -> + Pid ! {self(), Reason} + end, + do_run_server_core(ListenSocket, AcceptSocket, Opts, Transport, Pid); + {active_receive, Data} -> + case active_recv(AcceptSocket, length(Data)) of + ReceivedData -> + ct:log("[server] Received: ~p~n", [Data]), + Pid ! {self(), ReceivedData} + end, + do_run_server_core(ListenSocket, AcceptSocket, Opts, Transport, Pid); + {update_keys, Type} -> + case ssl:update_keys(AcceptSocket, Type) of + ok -> + ct:log("[server] Update keys: ~p", [Type]), + Pid ! {self(), ok}; + {error, Reason} -> + ct:log("[server] Update keys failed: ~p", [Type]), + Pid ! {self(), Reason} + end, + do_run_server_core(ListenSocket, AcceptSocket, Opts, Transport, Pid); listen -> run_server(ListenSocket, Opts); {listen, MFA} -> @@ -337,7 +477,233 @@ remove_close_msg(ReconnectTimes) -> {ssl_closed, _} -> remove_close_msg(ReconnectTimes -1) end. - + + +start_openssl_server(Args0, Config) -> + {_, ServerNode, _} = ssl_test_lib:run_where(Config), + ServerOpts = ssl_test_lib:get_server_opts(Config), + Node = proplists:get_value(node, Args0, ServerNode), + Port = proplists:get_value(port, Args0, 0), + Args = [{from, self()}, {port, Port}] ++ ServerOpts ++ Args0, + Result = spawn_link(Node, ?MODULE, init_openssl_server, [lists:delete(return_socket, Args)]), + receive + {started, Socket} -> + case lists:member(return_socket, Args) of + true -> {Result, Socket}; + false -> Result + end; + {start_failed, Reason} -> + {start_failed, Reason} + end. + +init_openssl_server(Options) -> + {ok, Version} = application:get_env(ssl,protocol_version), + %% Port = proplists:get_value(port, Options), + Port = inet_port(node()), + Pid = proplists:get_value(from, Options), + + Exe = "openssl", + Ciphers = proplists:get_value(ciphers, Options, ssl:cipher_suites(default,Version)), + Groups0 = proplists:get_value(groups, Options), + CertArgs = openssl_cert_options(Options, server), + Exe = "openssl", + + Args = case Groups0 of + undefined -> + ["s_server", "-accept", integer_to_list(Port), cipher_flag(Version), + ciphers(Ciphers, Version), + ssl_test_lib:version_flag(Version)] ++ CertArgs ++ ["-msg", "-debug"]; + Group -> + ["s_server", "-accept", integer_to_list(Port), cipher_flag(Version), + ciphers(Ciphers, Version), "-groups", Group, + ssl_test_lib:version_flag(Version)] ++ CertArgs ++ ["-msg", "-debug"] + end, + SslPort = ssl_test_lib:portable_open_port(Exe, Args), + Pid ! {started, Port}, + Pid ! {self(), {port, Port}}, + case openssl_server_started(SslPort) of + true -> + openssl_server_loop(Pid, SslPort, Args); + false -> + {error, openssl_server} + end. + +openssl_server_started(_Port) -> + receive + {Port, {data, Data}} -> + ct:log("~p:~p~n Openssl~n ~s~n",[?MODULE,?LINE, Data]), + verify_openssl_server_started(Port, Data) + after + 5000 -> + false + end. + +openssl_server_loop(Pid, SslPort, Args) -> + receive + {data, Data} -> + case port_command(SslPort, Data, [nosuspend]) of + true -> + ct:log("[openssl server] Send data: ~p~n", [Data]), + Pid ! {self(), ok}; + _Else -> + ct:log("[openssl server] Send failed, data: ~p~n", [Data]), + Pid ! {self(), {error, port_command_failed}} + end, + openssl_server_loop(Pid, SslPort, Args); + {active_receive, Data} -> + case active_recv(SslPort, length(Data)) of + ReceivedData -> + ct:log("[openssl server] Received: ~p~n", [Data]), + Pid ! {self(), ReceivedData} + end, + openssl_server_loop(Pid, SslPort, Args); + {update_keys, Type} -> + case Type of + write -> + ct:log("[openssl server] Update keys: ~p", [Type]), + true = port_command(SslPort, "k", [nosuspend]), + Pid ! {self(), ok}; + read_write -> + ct:log("[openssl server] Update keys: ~p", [Type]), + true = port_command(SslPort, "K", [nosuspend]), + Pid ! {self(), ok} + end, + openssl_server_loop(Pid, SslPort, Args); + close -> + ct:log("~p:~p~n[openssl server] Server closing~n", [?MODULE,?LINE]), + port_close(SslPort); + {ssl_closed, _Socket} -> + %% TODO + ok + end. + +start_openssl_client(Args0, Config) -> + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + ClientOpts = ssl_test_lib:get_client_opts(Config), + Node = proplists:get_value(node, Args0, ClientNode), + Args = [{from, self()}, + {host, Hostname}, + {options, ClientOpts} | Args0], + + Result = spawn_link(Node, ?MODULE, init_openssl_client, [lists:delete(return_socket, Args)]), + receive + {connected, Socket} -> + case lists:member(return_socket, Args) of + true -> {Result, Socket}; + false -> Result + end; + {connect_failed, Reason} -> + {connect_failed, Reason} + end. + +init_openssl_client(Options) -> + {ok, Version} = application:get_env(ssl,protocol_version), + Port = proplists:get_value(port, Options), + Pid = proplists:get_value(from, Options), + + Exe = "openssl", + Ciphers = proplists:get_value(ciphers, Options, ssl:cipher_suites(default,Version)), + Groups0 = proplists:get_value(groups, Options), + CertArgs = openssl_cert_options(Options, client), + Exe = "openssl", + Args0 = case Groups0 of + undefined -> + ["s_client", "-verify", "2", "-port", integer_to_list(Port), cipher_flag(Version), + ciphers(Ciphers, Version), + ssl_test_lib:version_flag(Version)] ++ CertArgs ++ ["-msg", "-debug"]; + Group -> + ["s_client", "-verify", "2", "-port", integer_to_list(Port), cipher_flag(Version), + ciphers(Ciphers, Version), "-groups", Group, + ssl_test_lib:version_flag(Version)] ++ CertArgs ++ ["-msg", "-debug"] + end, + Args = maybe_force_ipv4(Args0), + SslPort = ssl_test_lib:portable_open_port(Exe, Args), + case openssl_client_started(SslPort) of + true -> + openssl_client_loop(Pid, SslPort, Args); + false -> + {error, openssl_client} + end. + +openssl_client_started(Port) -> + receive + {Port, {data, Data}} -> + ct:log("~p:~p~n Openssl~n ~s~n",[?MODULE,?LINE, Data]), + verify_openssl_client_started(Port, Data) + after + 5000 -> + false + end. + +verify_openssl_server_started(Port, Data) -> + case re:run(Data, ".*CIPHER is.*") of + nomatch -> + openssl_server_started(Port); + {match, _} -> + true + end. + +verify_openssl_client_started(Port, Data) -> + case re:run(Data, ".*New, TLSv\\d[.]\\d, Cipher is.*") of + nomatch -> + openssl_client_started(Port); + {match, _} -> + true + end. + +openssl_client_loop(Pid, SslPort, Args) -> + Pid ! {connected, SslPort}, + openssl_client_loop_core(Pid, SslPort, Args). + +openssl_client_loop_core(Pid, SslPort, Args) -> + receive + {data, Data} -> + case port_command(SslPort, Data, [nosuspend]) of + true -> + ct:log("[openssl client] Send data: ~p~n", [Data]), + Pid ! {self(), ok}; + _Else -> + ct:log("[openssl client] Send failed, data: ~p~n", [Data]), + Pid ! {self(), {error, port_command_failed}} + end, + openssl_client_loop_core(Pid, SslPort, Args); + {active_receive, Data} -> + case active_recv(SslPort, length(Data)) of + ReceivedData -> + ct:log("[openssl client] Received: ~p~n", [Data]), + Pid ! {self(), ReceivedData} + end, + openssl_client_loop_core(Pid, SslPort, Args); + {update_keys, Type} -> + case Type of + write -> + ct:log("[openssl client] Update keys: ~p", [Type]), + true = port_command(SslPort, "k", [nosuspend]), + Pid ! {self(), ok}; + read_write -> + ct:log("[openssl client] Update keys: ~p", [Type]), + true = port_command(SslPort, "K", [nosuspend]), + Pid ! {self(), ok} + end, + openssl_client_loop_core(Pid, SslPort, Args); + close -> + ct:log("~p:~p~nClient closing~n", [?MODULE,?LINE]), + port_close(SslPort); + {ssl_closed, _Socket} -> + %% TODO + ok + end. + +start_client(Args0, Config) -> + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + ServerOpts = ssl_test_lib:get_server_opts(Config), + Node = proplists:get_value(node, Args0, ServerNode), + Args = [{from, self()}, + {host, Hostname}, + {node, Node}, + {options, ServerOpts} | Args0], + start_client(Args). +%% start_client(Args) -> Node = proplists:get_value(node, Args), Result = spawn_link(Node, ?MODULE, run_client_init, [lists:delete(return_socket, Args)]), @@ -380,25 +746,15 @@ client_loop(_Node, Host, Port, Pid, Transport, Options, Opts) -> %% In special cases we want to know the client port, it will %% be indicated by sending {port, 0} in options list! send_selected_port(Pid, proplists:get_value(port, Options), Socket), - {Module, Function, Args} = proplists:get_value(mfa, Opts), - ct:log("~p:~p~nClient: apply(~p,~p,~p)~n", - [?MODULE,?LINE, Module, Function, [Socket | Args]]), - case apply(Module, Function, [Socket | Args]) of + MFA = proplists:get_value(mfa, Opts), + case client_apply_mfa(Socket, MFA) of no_result_msg -> ok; Msg -> ct:log("~p:~p~nClient Msg: ~p ~n", [?MODULE,?LINE, Msg]), Pid ! {self(), Msg} end, - receive - close -> - ct:log("~p:~p~nClient closing~n", [?MODULE,?LINE]), - Transport:close(Socket); - {ssl_closed, Socket} -> - ok; - {gen_tcp, closed} -> - ok - end; + client_loop_core(Socket, Pid, Transport); {error, econnrefused = Reason} -> case get(retries) of N when N < 5 -> @@ -426,6 +782,43 @@ client_loop(_Node, Host, Port, Pid, Transport, Options, Opts) -> Pid ! {connect_failed, Reason} end. +client_loop_core(Socket, Pid, Transport) -> + receive + {data, Data} -> + ct:log("[client] Send: ~p~n", [Data]), + case Transport:send(Socket, Data) of + ok -> + Pid ! {self(), ok}; + {error, Reason} -> + Pid ! {self(), Reason} + end, + client_loop_core(Socket, Pid, Transport); + {active_receive, Data} -> + case active_recv(Socket, length(Data)) of + ReceivedData -> + ct:log("[client] Received: ~p~n", [Data]), + Pid ! {self(), ReceivedData} + end, + client_loop_core(Socket, Pid, Transport); + {update_keys, Type} -> + case ssl:update_keys(Socket, Type) of + ok -> + ct:log("[client] Update keys: ~p", [Type]), + Pid ! {self(), ok}; + {error, Reason} -> + ct:log("[client] Update keys failed: ~p", [Type]), + Pid ! {self(), Reason} + end, + client_loop_core(Socket, Pid, Transport); + close -> + ct:log("~p:~p~nClient closing~n", [?MODULE,?LINE]), + Transport:close(Socket); + {ssl_closed, Socket} -> + ok; + {gen_tcp, closed} -> + ok + end. + client_cont_loop(_Node, Host, Port, Pid, Transport, Options, cancel, _Opts) -> case Transport:connect(Host, Port, Options) of {ok, Socket, _} -> @@ -1622,7 +2015,7 @@ common_ciphers(crypto) -> ssl:cipher_suites(); common_ciphers(openssl) -> OpenSslSuites = - string:tokens(string:strip(os:cmd("openssl ciphers"), right, $\n), ":"), + string:tokens(string:strip(portable_cmd("openssl", ["ciphers"]), right, $\n), ":"), [ssl_cipher_format:suite_bin_to_map(S) || S <- ssl_cipher:suites(tls_record:highest_protocol_version([])), lists:member(ssl_cipher_format:suite_map_to_openssl_str(ssl_cipher_format:suite_bin_to_map(S)), OpenSslSuites) @@ -1686,7 +2079,7 @@ openssl_ecdh_rsa_suites() -> end, Ciphers). openssl_filter(FilterStr) -> - Ciphers = string:tokens(os:cmd("openssl ciphers"), ":"), + Ciphers = string:tokens(portable_cmd("openssl", ["ciphers"]), ":"), lists:filter(fun(Str) -> string_regex_filter(Str, FilterStr) end, Ciphers). @@ -1952,6 +2345,37 @@ send_recv_result_active(Socket) -> Data = active_recv(Socket, length(Data)), ok. +send_recv_result_active(Socket, Data) -> + ssl:send(Socket, Data), + Data = active_recv(Socket, length(Data)), + ok. + +send(Pid, Data) -> + Pid ! {data, Data}, + receive + {Pid, ok} -> + ok; + {Pid, Reason} -> + {error, Reason} + end. + +check_active_receive(Pid, Data) -> + Pid ! {active_receive, Data}, + receive + {Pid, Data} -> + %% ct:log("Received: ~p~n", [Data]), + Data + end. + +update_keys(Pid, Type) -> + Pid ! {update_keys, Type}, + receive + {Pid, ok} -> + ok; + {Pid, Reason} -> + {error, Reason} + end. + send_recv_result_active_once(Socket) -> Data = "Hello world", ssl:send(Socket, Data), @@ -1973,7 +2397,10 @@ verify_active_session_resumption(Socket, SessionResumption, WaitForReply, Ticket Expected = boolean_to_log_msg(SessionResumption), Got = boolean_to_log_msg(Got0), ct:fail("~p:~p~nFailed to verify session resumption! (expected ~p, got ~p)", - [?MODULE, ?LINE, Expected, Got]) + [?MODULE, ?LINE, Expected, Got]); + {error, Reason} -> + ct:fail("~p:~p~nFailed to verify session resumption! Reason: ~p", + [?MODULE, ?LINE, Reason]) end, Data = "Hello world", @@ -2025,12 +2452,23 @@ active_recv(Socket, N) -> active_recv(_Socket, 0, Acc) -> Acc; +active_recv(_Socket, N, Acc) when N < 0 -> + {_, T} = lists:split(0 - N, Acc), + T; active_recv(Socket, N, Acc) -> receive {ssl, Socket, Bytes} -> + active_recv(Socket, N-length(Bytes), Acc ++ Bytes); + {Socket, {data, Bytes0}} -> + Bytes = filter_openssl_debug_data(Bytes0), active_recv(Socket, N-length(Bytes), Acc ++ Bytes) end. +filter_openssl_debug_data(Bytes) -> + re:replace(Bytes, + "(read.*\n|write to.*\n|[\\dabcdefABCDEF]{4,4} -.*\n|>>> .*\n|<<< .*\n| \\d\\d.*\n|KEYUPDATE\n|.*Read BLOCK\n)*", + "", [{return, list}]). + active_once_recv(_Socket, 0) -> ok; active_once_recv(Socket, N) -> @@ -2071,7 +2509,7 @@ active_once_disregard(Socket, N) -> end. is_ipv6_supported() -> - case os:cmd("openssl version") of + case portable_cmd("openssl", ["version"]) of "OpenSSL 0.9.8" ++ _ -> % Does not support IPv6 false; "OpenSSL 1.0" ++ _ -> % Does not support IPv6 @@ -2081,7 +2519,7 @@ is_ipv6_supported() -> end. is_sane_ecc(openssl) -> - case os:cmd("openssl version") of + case portable_cmd("openssl", ["version"]) of "OpenSSL 1.0.0a" ++ _ -> % Known bug in openssl %% manifests as SSL_CHECK_SERVERHELLO_TLSEXT:tls invalid ecpointformat list false; @@ -2121,7 +2559,7 @@ is_sane_oppenssl_client() -> end. is_fips(openssl) -> - VersionStr = os:cmd("openssl version"), + VersionStr = portable_cmd("openssl",["version"]), case re:split(VersionStr, "fips") of [_] -> false; @@ -2155,7 +2593,7 @@ cipher_restriction(Config0) -> end. openssl_dsa_support() -> - case os:cmd("openssl version") of + case portable_cmd("openssl", ["version"]) of "LibreSSL 2.6.1" ++ _ -> true; "LibreSSL 2.6.2" ++ _ -> @@ -2184,7 +2622,7 @@ openssl_dsa_support() -> %% Acctual support is tested elsewhere, this is to exclude some LibreSSL and OpenSSL versions openssl_sane_dtls() -> - case os:cmd("openssl version") of + case portable_cmd("openssl", ["version"]) of "OpenSSL 0." ++ _ -> false; "OpenSSL 1.0.1s-freebsd" ++ _ -> @@ -2203,7 +2641,7 @@ openssl_sane_dtls() -> false end. openssl_sane_client_cert() -> - case os:cmd("openssl version") of + case portable_cmd("openssl", ["version"]) of "LibreSSL 2.5.2" ++ _ -> true; "LibreSSL 2.4" ++ _ -> @@ -2225,9 +2663,7 @@ openssl_sane_client_cert() -> check_sane_openssl_version(Version) -> case supports_ssl_tls_version(Version) of true -> - case {Version, os:cmd("openssl version")} of - {'sslv3', "OpenSSL 1.0.2" ++ _} -> - false; + case {Version, portable_cmd("openssl",["version"])} of {'dtlsv1', "OpenSSL 0" ++ _} -> false; {'dtlsv1.2', "OpenSSL 0" ++ _} -> @@ -2260,9 +2696,10 @@ check_sane_openssl_version(Version) -> false -> false end. -check_sane_openssl_renegotaite(Config, Version) when Version == 'tlsv1.1'; - Version == 'tlsv1.2' -> - case os:cmd("openssl version") of +check_sane_openssl_renegotaite(Config, Version) when Version == 'tlsv1'; + Version == 'tlsv1.1'; + Version == 'tlsv1.2' -> + case portable_cmd("openssl", ["version"]) of "OpenSSL 1.0.1c" ++ _ -> {skip, "Known renegotiation bug in OpenSSL"}; "OpenSSL 1.0.1b" ++ _ -> @@ -2271,7 +2708,9 @@ check_sane_openssl_renegotaite(Config, Version) when Version == 'tlsv1.1'; {skip, "Known renegotiation bug in OpenSSL"}; "OpenSSL 1.0.1 " ++ _ -> {skip, "Known renegotiation bug in OpenSSL"}; - _ -> + "LibreSSL 3.0.2" ++ _ -> + {skip, "Known renegotiation bug in OpenSSL"}; + _ -> check_sane_openssl_renegotaite(Config) end; check_sane_openssl_renegotaite(Config, 'sslv3') -> @@ -2422,26 +2861,45 @@ portable_open_port(Exe, Args) -> open_port({spawn_executable, AbsPath}, [{args, Args}, stderr_to_stdout]). -supports_ssl_tls_version(sslv2 = Version) -> - case os:cmd("openssl version") of - "OpenSSL 1" ++ _ -> - false; - %% Appears to be broken - "OpenSSL 0.9.8.o" ++ _ -> - false; - _ -> - VersionFlag = version_flag(Version), - Exe = "openssl", - Args = ["s_client", VersionFlag], - [{trap_exit, Trap}] = process_info(self(), [trap_exit]), - process_flag(trap_exit, true), - Port = ssl_test_lib:portable_open_port(Exe, Args), - Bool = do_supports_ssl_tls_version(Port, ""), - consume_port_exit(Port), - process_flag(trap_exit, Trap), - Bool - end; +portable_cmd(Exe, Args) -> + AbsPath = os:find_executable(Exe), + ct:pal("open_port({spawn_executable, ~p}, [{args, ~p}, stderr_to_stdout]).", [AbsPath, Args]), + Port = open_port({spawn_executable, AbsPath}, + [{args, Args}, stderr_to_stdout]), + receive + {Port, {data, Data}} -> + catch erlang:port_close(Port), + Data + end. + +supports_ssl_tls_version(Version) when Version == sslv2; + Version == sslv3 -> + case ubuntu_legacy_support() of + true -> + case portable_cmd("openssl", ["version"]) of + "OpenSSL 1.1" ++ _ -> + false; + "OpenSSL 1" ++ _ -> + Version =/= sslv2; + %% Appears to be broken + "OpenSSL 0.9.8.o" ++ _ -> + false; + _ -> + VersionFlag = version_flag(Version), + Exe = "openssl", + Args = ["s_client", VersionFlag], + [{trap_exit, Trap}] = process_info(self(), [trap_exit]), + process_flag(trap_exit, true), + Port = ssl_test_lib:portable_open_port(Exe, Args), + Bool = do_supports_ssl_tls_version(Port, ""), + consume_port_exit(Port), + process_flag(trap_exit, Trap), + Bool + end; + false -> + false + end; supports_ssl_tls_version(Version) -> VersionFlag = version_flag(Version), Exe = "openssl", @@ -2467,6 +2925,20 @@ do_supports_ssl_tls_version(Port, Acc) -> true end. +ubuntu_legacy_support() -> + case os:type() of + {unix, linux} -> + Issue = os:cmd("more /etc/issue"), + case re:run(Issue, "Ubuntu 1[6-9]+", [global]) of + nomatch -> + true; + _ -> + false + end; + _ -> + true + end. + ssl_options(Option, Config) when is_atom(Option) -> ProtocolOpts = proplists:get_value(protocol_opts, Config, []), Opts = proplists:get_value(Option, Config, []), @@ -2896,3 +3368,15 @@ openssl_sane_dtls_session_reuse() -> _-> openssl_sane_dtls() end. + +-define(BIG_BUF, 10000000). +%% Workaround data delivery issues on solaris | openbsd when kernel buffers are small +bigger_buffers() -> + case os:type() of + {unix,sunos} -> + [{recbuf, ?BIG_BUF},{sndbuf, ?BIG_BUF}]; + {unix,openbsd} -> + [{recbuf, ?BIG_BUF},{sndbuf, ?BIG_BUF}]; + _ -> + [] + end. diff --git a/lib/ssl/test/tls_1_3_record_SUITE.erl b/lib/ssl/test/tls_1_3_record_SUITE.erl index 26d7694e16..521e9a4fb8 100644 --- a/lib/ssl/test/tls_1_3_record_SUITE.erl +++ b/lib/ssl/test/tls_1_3_record_SUITE.erl @@ -81,7 +81,7 @@ encode_decode(_Config) -> <<128,229,186,211,62,127,182,20,62,166,233,23,135,64,121, 3,104,251,214,161,253,31,3,2,232,37,8,221,189,72,64,218, 121,41,112,148,254,34,68,164,228,60,161,201,132,55,56, - 157>>}, undefined, + 157>>}, undefined, undefined, undefined, <<92,24,205,75,244,60,136,212,250,32,214,20,37,3,213,87,61,207, 147,61,168,145,177,118,160,153,33,53,48,108,191,174>>, @@ -107,7 +107,7 @@ encode_decode(_Config) -> <<128,229,186,211,62,127,182,20,62,166,233,23,135,64,121, 3,104,251,214,161,253,31,3,2,232,37,8,221,189,72,64,218, 121,41,112,148,254,34,68,164,228,60,161,201,132,55,56, - 157>>}, undefined, + 157>>}, undefined, undefined, undefined, <<92,24,205,75,244,60,136,212,250,32,214,20,37,3,213,87,61,207, 147,61,168,145,177,118,160,153,33,53,48,108,191,174>>, @@ -744,14 +744,12 @@ encode_decode(_Config) -> %% PRK (32 octets): 7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 b0 bf %% da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c %% - RMS = hexstr2bin("7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 b0 bf + _RMS = hexstr2bin("7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 b0 bf da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c"), %% Verify calculation of resumption master secret that is used to create %% the pre shared key in '0-RTT'. - Temp = tls_v1:resumption_master_secret(HKDFAlgo, {master_secret, MasterSecret}, CHSF), - erlang:display({rms, RMS}), - erlang:display({new_rms, Temp}), + _Temp = tls_v1:resumption_master_secret(HKDFAlgo, {master_secret, MasterSecret}, CHSF), %% {server} derive secret "tls13 exp master": %% diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index ac79ee5fdc..2b2859b6b6 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 9.5.1 +SSL_VSN = 9.5.3 diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml index dabce02b3d..c12ad2deef 100644 --- a/lib/stdlib/doc/src/gen_statem.xml +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2016</year><year>2019</year> + <year>2016</year><year>2020</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -99,6 +99,14 @@ </seealso> were added. </item> + <item> + In OTP 22.3 the possibility to change the callback module + with actions + <seealso marker="#type-action"><c>change_callback_module</c></seealso>, + <seealso marker="#type-action"><c>push_callback_module</c></seealso> and + <seealso marker="#type-action"><c>pop_callback_module</c></seealso>, + was added. + </item> </list> </note> <p> @@ -119,6 +127,7 @@ Reply from other state than the request, <c>sys</c> traceable </item> <item>Multiple <c>sys</c> traceable replies</item> + <item>Changing the callback module</item> </list> @@ -129,15 +138,17 @@ </p> <list type="bulleted"> <item> - <p>One for finite-state machines + <p> + One for finite-state machines (<seealso marker="gen_fsm"><c>gen_fsm</c></seealso> like), which requires the state to be an atom and uses that state as - the name of the current callback function + the name of the current callback function. </p> </item> <item> - <p>One without restriction on the state data type - that uses one callback function for all states + <p> + One that allows the state to be any term and + that uses one callback function for all states. </p> </item> </list> @@ -150,7 +161,7 @@ </seealso> <c>gen_fsm</c> to <c>gen_statem</c>. </p> <p> - A generic state machine process (<c>gen_statem</c>) implemented + A generic state machine server process (<c>gen_statem</c>) implemented using this module has a standard set of interface functions and includes functionality for tracing and error reporting. It also fits into an OTP supervision tree. For more information, see @@ -626,9 +637,12 @@ handle_event(_, _, State, Data) -> If the <seealso marker="#type-callback_mode"><em>callback mode</em></seealso> is <c>state_functions</c>, - the state must be of this type. + the state must be an atom. After a <em>state change</em> (<c>NextState =/= State</c>), all postponed events are retried. + Note that the state <c>terminate</c> is not possible + to use since it would collide with the optional callback function + <seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso>. </p> </desc> </datatype> @@ -707,10 +721,9 @@ handle_event(_, _, State, Data) -> <name name="callback_mode"/> <desc> <p> - The <em>callback mode</em> is selected when starting the - <c>gen_statem</c> and after code change - using the return value from - <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>. + The <em>callback mode</em> is selected + with the return value from + <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>: </p> <taglist> <tag><c>state_functions</c></tag> @@ -732,6 +745,18 @@ handle_event(_, _, State, Data) -> </p> </item> </taglist> + <p> + The function + <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso> + is called when starting the <c>gen_statem</c>, + after code change + and after changing the callback module with any of the actions + <seealso marker="#type-action"><c>change_callback_module</c></seealso>, + <seealso marker="#type-action"><c>push_callback_module</c></seealso> or + <seealso marker="#type-action"><c>pop_callback_module</c></seealso>. + The result is cached for subsequent calls to + <seealso marker="#state callback">state callbacks</seealso>. + </p> </desc> </datatype> <datatype> @@ -1168,6 +1193,74 @@ handle_event(_, _, State, Data) -> an event inserted this way from any external event. </p> </item> + <tag> + <c>change_callback_module</c> + </tag> + <item> + <p> + Changes the callback module to + <c><anno>NewModule</anno></c> + which will be used when calling all subsequent + <seealso marker="#state callback">state callbacks</seealso>. + </p> + <p> + The <c>gen_statem</c> engine will find out the + <seealso marker="#type-callback_mode"> + <em>callback mode</em> + </seealso> + of <c><anno>NewModule</anno></c> by calling + <seealso marker="#Module:callback_mode/0"> + <c>NewModule:callback_mode/0</c> + </seealso> + before the next + <seealso marker="#state callback">state callback</seealso>. + </p> + <p> + Changing the callback module does not affect the + <em>state transition</em> in any way, + it only changes which module that handles the events. + Be aware that all relevant callback functions in + <c><anno>NewModule</anno></c> + such as the + <seealso marker="#state callback">state callback</seealso>, + <seealso marker="#Module:code_change/4"><c><anno>NewModule</anno>:code_change/4</c></seealso>, + <seealso marker="#Module:format_status/2"> + <c><anno>NewModule</anno>:format_status/2</c> + </seealso> + and + <seealso marker="#Module:terminate/3"> + <c><anno>NewModule</anno>:terminate/3</c> + </seealso> + must be able to handle the state and data + from the old module. + </p> + </item> + <tag> + <c>push_callback_module</c><br /> + </tag> + <item> + <p> + Pushes the current callback module + to the top of an internal stack of callback modules + and changes the callback module to + <c><anno>NewModule</anno></c>. + Otherwise like + <c>{change_callback_module, NewModule}</c> + above. + </p> + </item> + <tag> + <c>pop_callback_module</c> + </tag> + <item> + Pops the top module from the internal stack of + callback modules and changes the callback module + to be the popped module. + If the stack is empty the server fails. + Otherwise like + <c>{change_callback_module, NewModule}</c> + above. + </item> </taglist> </desc> </datatype> @@ -1917,7 +2010,9 @@ handle_event(_, _, State, Data) -> <funcs> <func> <name since="OTP 19.1">Module:callback_mode() -> CallbackMode</name> - <fsummary>Update the internal state during upgrade/downgrade.</fsummary> + <fsummary> + Define the callback mode for the callback module. + </fsummary> <type> <v> CallbackMode = @@ -1933,10 +2028,11 @@ handle_event(_, _, State, Data) -> <seealso marker="#type-callback_mode"><em>callback mode</em></seealso> of the callback module. The value is cached by <c>gen_statem</c> for efficiency reasons, so this function is only called - once after server start and after code change, + once after server start, after code change, + and after changing the callback module, but before the first <seealso marker="#state callback"><em>state callback</em></seealso> - in the current code version is called. + in the current callback module's code version is called. More occasions may be added in future versions of <c>gen_statem</c>. </p> @@ -1945,9 +2041,16 @@ handle_event(_, _, State, Data) -> <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso> returns or when <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso> - is called. Code change happens when + is called. + Code change happens when <seealso marker="#Module:code_change/4"><c>Module:code_change/4</c></seealso> returns. + A change of the callback module happens when a + <seealso marker="#state callback"><em>state callback</em></seealso> + returns any of the actions + <seealso marker="#type-action"><c>change_callback_module</c></seealso>, + <seealso marker="#type-action"><c>push_callback_module</c></seealso> or + <seealso marker="#type-action"><c>pop_callback_module</c></seealso>. </p> <p> The <c>CallbackMode</c> is either just @@ -1970,7 +2073,9 @@ handle_event(_, _, State, Data) -> <name since="OTP 19.0">Module:code_change(OldVsn, OldState, OldData, Extra) -> Result </name> - <fsummary>Update the internal state during upgrade/downgrade.</fsummary> + <fsummary> + Update the internal state during upgrade/downgrade. + </fsummary> <type> <v>OldVsn = Vsn | {down,Vsn}</v> <v> Vsn = term()</v> @@ -2052,13 +2157,44 @@ handle_event(_, _, State, Data) -> will not be honoured, most probably causing a server crash. </p> + <p> + If the server changes callback module using any of the actions + <seealso marker="#type-action"><c>change_callback_module</c></seealso>, + <seealso marker="#type-action"><c>push_callback_module</c></seealso> or + <seealso marker="#type-action"><c>pop_callback_module</c></seealso>, + be aware that it is always the current callback module that + will get this callback call. That the current callback module + handles the current state and data update should be no surprise, + but it must be able to handle even parts of + the state and data that it is not familiar with, + somehow. + </p> + <p> + In the supervisor + <seealso marker="doc/design_principles:sup_princ#child-specification">child specification</seealso> + there is a list of modules which is recommended to contain + only the callback module. + For a <c>gen_statem</c> with multiple callback modules + there is no real need to list all of them, + it may not even be possible since the list could change + after code upgrade. + If this list would contain only the start callback module, + as recommended, what is important is to upgrade <em>that</em> module + whenever a <em>synchronized code replacement</em> is done. + Then the release handler concludes that an upgrade + that upgrades <em>that</em> module needs to suspend, + code change, and resume any server whose child specification + declares that it is using <em>that</em> module. + And again; the <em>current</em> callback module will get the + <c>Module:code_change/4</c> call. + </p> </desc> </func> <func> <name since="OTP 19.0">Module:init(Args) -> Result(StateType)</name> <fsummary> - Initializing process and internal state. + Initialize process and internal state. </fsummary> <type> <v>Args = term()</v> @@ -2101,8 +2237,9 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre> <name since="OTP 19.0">Module:format_status(Opt, [PDict,State,Data]) -> Status </name> - <fsummary>Optional function for providing a term describing the - current <c>gen_statem</c> status.</fsummary> + <fsummary> + Describe the current <c>gen_statem</c> status (optional). + </fsummary> <type> <v>Opt = normal | terminate</v> <v>PDict = [{Key, Value}]</v> diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml index fb24bb63b5..3df13cee27 100644 --- a/lib/stdlib/doc/src/notes.xml +++ b/lib/stdlib/doc/src/notes.xml @@ -31,6 +31,43 @@ </header> <p>This document describes the changes made to the STDLIB application.</p> +<section><title>STDLIB 3.11.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>A directory traversal vulnerability has been + eliminated in erl_tar. erl_tar will now refuse to extract + symlinks that points outside the targeted extraction + directory and will return + <c>{error,{Path,unsafe_symlink}}</c>. (Thanks to Eric + Meadows-Jönsson for the bug report and for suggesting a + fix.)</p> + <p> + Own Id: OTP-16441</p> + </item> + </list> + </section> + +</section> + +<section><title>STDLIB 3.11.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The <c>ets:update_counter/4</c> core dumped when given an + ordered_set with write_concurrency enabled and an invalid + position. This bug has been fixed.</p> + <p> + Own Id: OTP-16378 Aux Id: ERL-1125 </p> + </item> + </list> + </section> + +</section> + <section><title>STDLIB 3.11</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -529,6 +566,26 @@ </section> +<section><title>STDLIB 3.8.2.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>A directory traversal vulnerability has been + eliminated in erl_tar. erl_tar will now refuse to extract + symlinks that points outside the targeted extraction + directory and will return + <c>{error,{Path,unsafe_symlink}}</c>. (Thanks to Eric + Meadows-Jönsson for the bug report and for suggesting a + fix.)</p> + <p> + Own Id: OTP-16441</p> + </item> + </list> + </section> + +</section> + <section><title>STDLIB 3.8.2.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/stdlib/doc/src/queue.xml b/lib/stdlib/doc/src/queue.xml index 89cce6d85b..46a3dc5d19 100644 --- a/lib/stdlib/doc/src/queue.xml +++ b/lib/stdlib/doc/src/queue.xml @@ -52,7 +52,7 @@ is to be regarded as opaque by other modules. Any code assuming knowledge of the format is running on thin ice.</p> - <p>All operations has an amortized O(1) running time, except + <p>All operations have an amortized O(1) running time, except <seealso marker="#filter/2"><c>filter/2</c></seealso>, <seealso marker="#join/2"><c>join/2</c></seealso>, <seealso marker="#len/1"><c>len/1</c></seealso>, diff --git a/lib/stdlib/src/calendar.erl b/lib/stdlib/src/calendar.erl index 3a8fe2211b..ef6d1882e6 100644 --- a/lib/stdlib/src/calendar.erl +++ b/lib/stdlib/src/calendar.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2019. All Rights Reserved. +%% Copyright Ericsson AB 1996-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -369,7 +369,7 @@ rfc3339_to_system_time(DateTimeString, Options) -> IsFractionChar = fun(C) -> C >= $0 andalso C =< $9 orelse C =:= $. end, {FractionStr, UtcOffset} = lists:splitwith(IsFractionChar, TimeStr), Time = datetime_to_system_time(DateTime), - Secs = Time - offset_adjustment(Time, second, UtcOffset), + Secs = Time - offset_string_adjustment(Time, second, UtcOffset), check(DateTimeString, Options, Secs), ScaledEpoch = erlang:convert_time_unit(Secs, second, Unit), ScaledEpoch + copy_sign(fraction(Unit, FractionStr), ScaledEpoch). @@ -689,13 +689,13 @@ offset(OffsetOption, Secs0) when OffsetOption =:= ""; offset(OffsetOption, _Secs) -> OffsetOption. +offset_adjustment(Time, Unit, "") -> + local_offset(Time, Unit); offset_adjustment(Time, Unit, OffsetString) when is_list(OffsetString) -> offset_string_adjustment(Time, Unit, OffsetString); offset_adjustment(_Time, Unit, Offset) when is_integer(Offset) -> erlang:convert_time_unit(Offset, Unit, second). -offset_string_adjustment(Time, Unit, "") -> - local_offset(Time, Unit); offset_string_adjustment(_Time, _Unit, "Z") -> 0; offset_string_adjustment(_Time, _Unit, "z") -> diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 4ad94f2507..739f786321 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2018. All Rights Reserved. +%% Copyright Ericsson AB 1996-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -27,10 +27,10 @@ attribute attr_val function function_clauses function_clause clause_args clause_guard clause_body expr expr_100 expr_150 expr_160 expr_200 expr_300 expr_400 expr_500 -expr_600 expr_700 expr_800 +expr_600 expr_650 expr_700 expr_800 expr_max pat_expr pat_expr_200 pat_expr_300 pat_expr_400 pat_expr_500 -pat_expr_600 pat_expr_700 pat_expr_800 +pat_expr_600 pat_expr_650 pat_expr_700 pat_expr_800 pat_expr_max map_pat_expr record_pat_expr pat_argument_list pat_exprs list tail @@ -251,10 +251,12 @@ expr_500 -> expr_500 mult_op expr_600 : ?mkop2('$1', '$2', '$3'). expr_500 -> expr_600 : '$1'. -expr_600 -> prefix_op expr_700 : +expr_600 -> prefix_op expr_600 : ?mkop1('$1', '$2'). -expr_600 -> map_expr : '$1'. -expr_600 -> expr_700 : '$1'. +expr_600 -> expr_650 : '$1'. + +expr_650 -> map_expr : '$1'. +expr_650 -> expr_700 : '$1'. expr_700 -> function_call : '$1'. expr_700 -> record_expr : '$1'. @@ -298,10 +300,12 @@ pat_expr_500 -> pat_expr_500 mult_op pat_expr_600 : ?mkop2('$1', '$2', '$3'). pat_expr_500 -> pat_expr_600 : '$1'. -pat_expr_600 -> prefix_op pat_expr_700 : +pat_expr_600 -> prefix_op pat_expr_600 : ?mkop1('$1', '$2'). -pat_expr_600 -> map_pat_expr : '$1'. -pat_expr_600 -> pat_expr_700 : '$1'. +pat_expr_600 -> pat_expr_650 : '$1'. + +pat_expr_650 -> map_pat_expr : '$1'. +pat_expr_650 -> pat_expr_700 : '$1'. pat_expr_700 -> record_pat_expr : '$1'. pat_expr_700 -> pat_expr_800 : '$1'. @@ -732,7 +736,7 @@ Erlang code. -type af_template() :: abstract_expr(). --type af_qualifier_seq() :: [af_qualifier()]. +-type af_qualifier_seq() :: [af_qualifier(), ...]. -type af_qualifier() :: af_generator() | af_filter(). @@ -749,7 +753,7 @@ Erlang code. -type af_try() :: {'try', anno(), - af_body() | [], + af_body(), af_clause_seq() | [], af_clause_seq() | [], af_body() | []}. @@ -765,7 +769,10 @@ Erlang code. -type af_remote_fun() :: {'fun', anno(), {'function', module(), function_name(), arity()}} - | {'fun', anno(), {'function', af_atom(), af_atom(), af_integer()}}. + | {'fun', anno(), {'function', + af_atom() | af_variable(), + af_atom() | af_variable(), + af_integer() | af_variable()}}. -type af_fun() :: {'fun', anno(), {'clauses', af_clause_seq()}}. @@ -795,8 +802,8 @@ Erlang code. | af_record_creation(af_guard_test()) | af_record_index() | af_record_field_access(af_guard_test()) - | af_map_creation(abstract_expr()) - | af_map_update(abstract_expr()) + | af_map_creation(af_guard_test()) + | af_map_update(af_guard_test()) | af_guard_call() | af_remote_guard_call(). @@ -812,7 +819,7 @@ Erlang code. -type af_assoc_exact(T) :: {'map_field_exact', anno(), T, T}. --type af_guard_call() :: {'call', anno(), function_name(), [af_guard_test()]}. +-type af_guard_call() :: {'call', anno(), af_atom(), [af_guard_test()]}. -type af_remote_guard_call() :: {'call', anno(), @@ -841,7 +848,7 @@ Erlang code. -type af_record_field(T) :: {'record_field', anno(), af_field_name(), T}. -type af_map_pattern() :: - {'map', anno(), [af_assoc_exact(abstract_expr())]}. + {'map', anno(), [af_assoc_exact(af_pattern())]}. -type abstract_type() :: af_annotated_type() | af_atom() @@ -903,7 +910,8 @@ Erlang code. -type af_tuple_type() :: {'type', anno(), 'tuple', 'any'} | {'type', anno(), 'tuple', [abstract_type()]}. --type af_type_union() :: {'type', anno(), 'union', [abstract_type()]}. +-type af_type_union() :: + {'type', anno(), 'union', [abstract_type(), ...]}. % at least two -type af_type_variable() :: {'var', anno(), atom()}. % except '_' @@ -911,7 +919,7 @@ Erlang code. {'user_type', anno(), type_name(), [abstract_type()]}. -type af_function_type_list() :: [af_constrained_function_type() | - af_function_type()]. + af_function_type(), ...]. -type af_constrained_function_type() :: {'type', anno(), 'bounded_fun', [af_function_type() | % [Ft, Fc] @@ -921,7 +929,7 @@ Erlang code. {'type', anno(), 'fun', [{'type', anno(), 'product', [abstract_type()]} | abstract_type()]}. --type af_function_constraint() :: [af_constraint()]. +-type af_function_constraint() :: [af_constraint(), ...]. -type af_constraint() :: {'type', anno(), 'constraint', [af_lit_atom('is_subtype') | @@ -1584,7 +1592,7 @@ max_prec() -> 900. -spec type_inop_prec(type_inop()) -> {prec(), prec(), prec()}. type_inop_prec('=') -> {150,100,100}; -type_inop_prec('::') -> {160,150,150}; +type_inop_prec('::') -> {150,150,160}; type_inop_prec('|') -> {180,170,170}; type_inop_prec('..') -> {300,200,300}; type_inop_prec('+') -> {400,400,500}; diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 255c0ae81f..daa172af50 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2019. All Rights Reserved. +%% Copyright Ericsson AB 1996-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -322,9 +322,11 @@ ltype(T) -> ltype(T, 0). ltype({ann_type,_Line,[V,T]}, Prec) -> - {_L,P,_R} = type_inop_prec('::'), - E = typed(lexpr(V, options(none)), T), - maybe_paren(P, Prec, E); + {L,P,R} = type_inop_prec('::'), + Vl = ltype(V, L), + Tr = ltype(T, R), + El = {list,[{cstep,[Vl,' ::'],Tr}]}, + maybe_paren(P, Prec, El); ltype({paren_type,_Line,[T]}, P) -> %% Generated before Erlang/OTP 18. ltype(T, P); @@ -410,8 +412,7 @@ field_type({type,_Line,field_type,[Name,Type]}, _Prec) -> typed(lexpr(Name, options(none)), Type). typed(B, Type) -> - {_L,_P,R} = type_inop_prec('::'), - {list,[{cstep,[B,' ::'],ltype(Type, R)}]}. + {list,[{cstep,[B,' ::'],ltype(Type)}]}. tuple_type([], _) -> leaf("{}"); @@ -540,11 +541,13 @@ lexpr({nil,_}, _, _) -> '[]'; lexpr({cons,_,H,T}, _, Opts) -> list(T, [H], Opts); lexpr({lc,_,E,Qs}, _Prec, Opts) -> - Lcl = {list,[{step,[lexpr(E, Opts),leaf(" ||")],lc_quals(Qs, Opts)}]}, + P = max_prec(), + Lcl = {list,[{step,[lexpr(E, P, Opts),leaf(" ||")],lc_quals(Qs, Opts)}]}, {list,[{seq,$[,[],[[]],[{force_nl,leaf(" "),[Lcl]}]},$]]}; %% {list,[{step,$[,Lcl},$]]}; lexpr({bc,_,E,Qs}, _Prec, Opts) -> - Lcl = {list,[{step,[lexpr(E, Opts),leaf(" ||")],lc_quals(Qs, Opts)}]}, + P = max_prec(), + Lcl = {list,[{step,[lexpr(E, P, Opts),leaf(" ||")],lc_quals(Qs, Opts)}]}, {list,[{seq,'<<',[],[[]],[{force_nl,leaf(" "),[Lcl]}]},'>>']}; %% {list,[{step,'<<',Lcl},'>>']}; lexpr({tuple,_,Elts}, _, Opts) -> @@ -564,14 +567,16 @@ lexpr({record, _, Name, Fs}, Prec, Opts) -> lexpr({record_field, _, Rec, Name, F}, Prec, Opts) -> {L,P,R} = inop_prec('#'), Rl = lexpr(Rec, L, Opts), - Nl = [$#,{atom,Name},$.], + Sep = hash_after_integer(Rec, [$#]), + Nl = [Sep,{atom,Name},$.], El = [Rl,Nl,lexpr(F, R, Opts)], maybe_paren(P, Prec, El); lexpr({record, _, Rec, Name, Fs}, Prec, Opts) -> {L,P,_R} = inop_prec('#'), Rl = lexpr(Rec, L, Opts), + Sep = hash_after_integer(Rec, []), Nl = record_name(Name), - El = {first,[Rl,Nl],record_fields(Fs, Opts)}, + El = {first,[Rl,Sep,Nl],record_fields(Fs, Opts)}, maybe_paren(P, Prec, El); lexpr({record_field, _, {atom,_,''}, F}, Prec, Opts) -> {_L,P,R} = inop_prec('.'), @@ -588,7 +593,8 @@ lexpr({map, _, Fs}, Prec, Opts) -> lexpr({map, _, Map, Fs}, Prec, Opts) -> {L,P,_R} = inop_prec('#'), Rl = lexpr(Map, L, Opts), - El = {first,[Rl,$#],map_fields(Fs, Opts)}, + Sep = hash_after_integer(Map, [$#]), + El = {first,[Rl|Sep],map_fields(Fs, Opts)}, maybe_paren(P, Prec, El); lexpr({block,_,Es}, _, Opts) -> {list,[{step,'begin',body(Es, Opts)},{reserved,'end'}]}; @@ -652,20 +658,20 @@ lexpr({'try',_,Es,Scs,Ccs,As}, _, Opts) -> true -> {step,{list,[{step,'try',body(Es, Opts)},{reserved,'of'}]}, cr_clauses(Scs, Opts)} - end, + end] ++ if Ccs =:= [] -> []; true -> - {step,'catch',try_clauses(Ccs, Opts)} - end, + [{step,'catch',try_clauses(Ccs, Opts)}] + end ++ if As =:= [] -> []; true -> - {step,'after',body(As, Opts)} - end, - {reserved,'end'}]}; + [{step,'after',body(As, Opts)}] + end ++ + [{reserved,'end'}]}; lexpr({'catch',_,Expr}, Prec, Opts) -> {P,R} = preop_prec('catch'), El = {list,[{step,'catch',lexpr(Expr, R, Opts)}]}, @@ -718,6 +724,17 @@ lexpr(HookExpr, Precedence, #options{hook = {Mod,Func,Eas}}) lexpr(HookExpr, Precedence, #options{hook = Func, opts = Options}) -> {hook,HookExpr,Precedence,Func,Options}. +%% An integer is separated from the following '#' by a space, which +%% erl_scan can handle. +hash_after_integer({integer, _, _}, C) -> + [$\s|C]; +hash_after_integer({'fun',_,{function, _, _}}, C) -> + [$\s|C]; +hash_after_integer({'fun',_,{function, _, _, _}}, C) -> + [$\s|C]; +hash_after_integer(_, C) -> + C. + call(Name, Args, Prec, Opts) -> {F,P} = func_prec(), Item = {first,lexpr(Name, F, Opts),args(Args, Opts)}, @@ -839,12 +856,6 @@ cr_clause({clause,_,[T],G,B}, Opts) -> try_clauses(Cs, Opts) -> clauses(fun try_clause/2, Opts, Cs). -try_clause({clause,_,[{tuple,_,[{atom,_,throw},V,S]}],G,B}, Opts) -> - El = lexpr(V, 0, Opts), - Sl = stack_backtrace(S, [El], Opts), - Gl = guard_when(Sl, G, Opts), - Bl = body(B, Opts), - {step,Gl,Bl}; try_clause({clause,_,[{tuple,_,[C,V,S]}],G,B}, Opts) -> Cs = lexpr(C, 0, Opts), El = lexpr(V, 0, Opts), @@ -972,7 +983,7 @@ frmt(Item, I, PP) -> %%% - {prefer_nl,Sep,IPs}: forces linebreak between Is unlesss negative %%% indentation. %%% - {atom,A}: an atom -%%% - {singleton_atom_type,A}: an singleton atom type +%%% - {singleton_atom_type,A}: a singleton atom type %%% - {char,C}: a character %%% - {string,S}: a string. %%% - {value,T}: a term. diff --git a/lib/stdlib/src/erl_tar.erl b/lib/stdlib/src/erl_tar.erl index 591aea2f83..78cdd02307 100644 --- a/lib/stdlib/src/erl_tar.erl +++ b/lib/stdlib/src/erl_tar.erl @@ -1616,7 +1616,8 @@ write_extracted_element(#tar_header{name=Name0}=Header, Bin, Opts) -> create_extracted_dir(Name1, Opts); symlink -> read_verbose(Opts, "x ~ts~n", [Name0]), - create_symlink(Name1, Header#tar_header.linkname, Opts); + LinkName = safe_link_name(Header, Opts), + create_symlink(Name1, LinkName, Opts); Device when Device =:= char orelse Device =:= block -> %% char/block devices will be created as empty files %% and then have their major/minor device set later @@ -1644,6 +1645,52 @@ make_safe_path(Path, #read_opts{cwd=Cwd}) -> filename:absname(SafePath, Cwd) end. +safe_link_name(#tar_header{linkname=Path}, #read_opts{cwd=Cwd}) -> + case safe_relative_path_links(Path, Cwd) of + unsafe -> + throw({error,{Path,unsafe_symlink}}); + SafePath -> + SafePath + end. + +safe_relative_path_links(Path, Cwd) -> + case filename:pathtype(Path) of + relative -> safe_relative_path_links(filename:split(Path), Cwd, [], ""); + _ -> unsafe + end. + +safe_relative_path_links([Segment|Segments], Cwd, PrevSegments, Acc) -> + AccSegment = join(Acc, Segment), + case lists:member(AccSegment, PrevSegments) of + true -> + unsafe; + false -> + case file:read_link(join(Cwd, AccSegment)) of + {ok, LinkPath} -> + case filename:pathtype(LinkPath) of + relative -> + safe_relative_path_links(filename:split(LinkPath) ++ Segments, + Cwd, [AccSegment|PrevSegments], Acc); + _ -> + unsafe + end; + + {error, _} -> + case filename:safe_relative_path(join(Acc, Segment)) of + unsafe -> + unsafe; + NewAcc -> + safe_relative_path_links(Segments, Cwd, + [AccSegment|PrevSegments], NewAcc) + end + end + end; +safe_relative_path_links([], _Cwd, _PrevSegments, Acc) -> + Acc. + +join([], Path) -> Path; +join(Left, Right) -> filename:join(Left, Right). + create_regular(Name, NameInArchive, Bin, Opts) -> case write_extracted_file(Name, Bin, Opts) of not_written -> diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl index 105b2a4577..2cf30e10ec 100644 --- a/lib/stdlib/src/gen_statem.erl +++ b/lib/stdlib/src/gen_statem.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2016-2019. All Rights Reserved. +%% Copyright Ericsson AB 2016-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -149,6 +149,9 @@ {'next_event', % Insert event as the next to handle EventType :: event_type(), EventContent :: term()} | + {'change_callback_module', NewModule :: module()} | + {'push_callback_module', NewModule :: module()} | + 'pop_callback_module' | enter_action(). -type enter_action() :: 'hibernate' | % Set the hibernate option @@ -337,12 +340,13 @@ terminate/3, % Has got a default implementation code_change/4, % Only needed by advanced soft upgrade %% - state_name/3, % Example for callback_mode() =:= state_functions: + state_name/3, % Just an example callback; + %% for callback_mode() =:= state_functions %% there has to be a StateName/3 callback function - %% for every StateName in your state machine but the state name - %% 'state_name' does of course not have to be used. + %% for every StateName in your state machine, + %% but not one has to be named 'state_name' %% - handle_event/4 % For callback_mode() =:= handle_event_function + handle_event/4 % Only for callback_mode() =:= handle_event_function ]). @@ -421,7 +425,7 @@ timeout_event_type(Type) -> {callback_mode = undefined :: callback_mode() | undefined, state_enter = false :: boolean(), parent :: pid(), - module :: atom(), + modules :: [module()], name :: atom() | pid(), hibernate_after = infinity :: timeout() }). @@ -689,12 +693,12 @@ enter( P = #params{ parent = Parent, - module = Module, + modules = [Module], name = Name, hibernate_after = HibernateAfterTimeout}, S = #state{state_data = {State,Data}}, Debug_1 = ?sys_debug(Debug, Name, {enter,State}), - loop_callback_mode( + loop_state_callback( P, Debug_1, S, Q, {State,Data}, %% Tunneling Actions through CallbackEvent here... %% Special path to go to action handling, after first @@ -726,7 +730,7 @@ init_it(Starter, Parent, ServerRef, Module, Args, Opts) -> proc_lib:init_ack(Starter, {error,Reason}), error_info( Class, Reason, Stacktrace, Debug, - #params{parent = Parent, name = Name, module = Module}, + #params{parent = Parent, name = Name, modules = [Module]}, #state{}, []), erlang:raise(Class, Reason, Stacktrace) end. @@ -762,7 +766,7 @@ init_result( proc_lib:init_ack(Starter, {error,Error}), error_info( error, Error, ?STACKTRACE(), Debug, - #params{parent = Parent, name = Name, module = Module}, + #params{parent = Parent, name = Name, modules = [Module]}, #state{}, []), exit(Error) end. @@ -783,7 +787,7 @@ system_terminate(Reason, Parent, Debug, {P,S}) -> update_parent(P, Parent), Debug, S, []). system_code_change( - {#params{module = Module} = P, + {#params{modules = [Module | _]} = P, #state{state_data = {State,Data}} = S}, _Mod, OldVsn, Extra) -> case @@ -814,7 +818,7 @@ system_replace_state( format_status( Opt, [PDict,SysState,Parent,Debug, - {#params{name = Name} = P, + {#params{name = Name, modules = Modules} = P, #state{postponed = Postponed, timers = Timers} = S}]) -> Header = gen:format_status_header("Status for state machine", Name), Log = sys:get_log(Debug), @@ -822,6 +826,7 @@ format_status( {data, [{"Status",SysState}, {"Parent",Parent}, + {"Modules",Modules}, {"Time-outs",list_timeouts(Timers)}, {"Logged Events",Log}, {"Postponed",Postponed}]} | @@ -1046,79 +1051,7 @@ loop_event_handler( %% restored when looping back to loop/3 or loop_event/5. %% Q = [Event|Events], - loop_callback_mode(P, Debug, S, Q, State_Data, Event). - -%% Figure out the callback mode -%% -loop_callback_mode( - #params{callback_mode = undefined} = P, Debug, S, - Q, State_Data, CallbackEvent) -> - %% - Module = P#params.module, - try Module:callback_mode() of - CallbackMode -> - loop_callback_mode_result( - P, Debug, S, - Q, State_Data, CallbackEvent, - CallbackMode, listify(CallbackMode), undefined, false) - catch - CallbackMode -> - loop_callback_mode_result( - P, Debug, S, - Q, State_Data, CallbackEvent, - CallbackMode, listify(CallbackMode), undefined, false); - Class:Reason:Stacktrace -> - terminate( - Class, Reason, Stacktrace, P, Debug, S, Q) - end; -loop_callback_mode(P, Debug, S, Q, State_Data, CallbackEvent) -> - loop_state_callback(P, Debug, S, Q, State_Data, CallbackEvent). - -%% Check the result of Module:callback_mode() -%% -loop_callback_mode_result( - P, Debug, S, Q, State_Data, CallbackEvent, - CallbackMode, [H|T], NewCallbackMode, NewStateEnter) -> - %% - case callback_mode(H) of - true -> - loop_callback_mode_result( - P, Debug, S, Q, State_Data, CallbackEvent, - CallbackMode, T, H, NewStateEnter); - false -> - case state_enter(H) of - true -> - loop_callback_mode_result( - P, Debug, S, Q, State_Data, CallbackEvent, - CallbackMode, T, NewCallbackMode, true); - false -> - terminate( - error, - {bad_return_from_callback_mode,CallbackMode}, - ?STACKTRACE(), - P, Debug, S, Q) - end - end; -loop_callback_mode_result( - P, Debug, S, Q, State_Data, CallbackEvent, - CallbackMode, [], NewCallbackMode, NewStateEnter) -> - %% - case NewCallbackMode of - undefined -> - terminate( - error, - {bad_return_from_callback_mode,CallbackMode}, - ?STACKTRACE(), - P, Debug, S, Q); - _ -> - P_1 = - P#params{ - callback_mode = NewCallbackMode, - state_enter = NewStateEnter}, - loop_state_callback( - P_1, Debug, S, Q, State_Data, CallbackEvent) - end. - + loop_state_callback(P, Debug, S, Q, State_Data, Event). %% Make a state enter call to the state function, we loop back here %% from further down if state enter calls are enabled @@ -1149,7 +1082,33 @@ loop_state_callback(P, Debug, S, Q, State_Data, CallbackEvent) -> StateCall, CallbackEvent). %% loop_state_callback( - #params{callback_mode = CallbackMode, module = Module} = P, + #params{callback_mode = undefined, modules = [Module | _]} = P, + Debug, S, Q, State_Data, + NextEventsR, Hibernate, TimeoutsR, Postpone, + StateCall, CallbackEvent) -> + %% + %% Figure out the callback mode + %% + try Module:callback_mode() of + CallbackMode -> + loop_callback_mode_result( + P, Debug, S, Q, State_Data, + NextEventsR, Hibernate, TimeoutsR, Postpone, + StateCall, CallbackEvent, + CallbackMode, listify(CallbackMode), undefined, false) + catch + CallbackMode -> + loop_callback_mode_result( + P, Debug, S, Q, State_Data, + NextEventsR, Hibernate, TimeoutsR, Postpone, + StateCall, CallbackEvent, + CallbackMode, listify(CallbackMode), undefined, false); + Class:Reason:Stacktrace -> + terminate( + Class, Reason, Stacktrace, P, Debug, S, Q) + end; +loop_state_callback( + #params{callback_mode = CallbackMode, modules = [Module | _]} = P, Debug, S, Q, {State,Data} = State_Data, NextEventsR, Hibernate, TimeoutsR, Postpone, StateCall, {Type,Content}) -> @@ -1186,6 +1145,61 @@ loop_state_callback( NextEventsR, Hibernate, TimeoutsR, Postpone, CallEnter, StateCall, Actions). +%% Check the result of Module:callback_mode() +%% +loop_callback_mode_result( + P, Debug, S, Q, State_Data, + NextEventsR, Hibernate, TimeoutsR, Postpone, + StateCall, CallbackEvent, + CallbackMode, [H|T], NewCallbackMode, NewStateEnter) -> + %% + case callback_mode(H) of + true -> + loop_callback_mode_result( + P, Debug, S, Q, State_Data, + NextEventsR, Hibernate, TimeoutsR, Postpone, + StateCall, CallbackEvent, + CallbackMode, T, H, NewStateEnter); + false -> + case state_enter(H) of + true -> + loop_callback_mode_result( + P, Debug, S, Q, State_Data, + NextEventsR, Hibernate, TimeoutsR, Postpone, + StateCall, CallbackEvent, + CallbackMode, T, NewCallbackMode, true); + false -> + terminate( + error, + {bad_return_from_callback_mode,CallbackMode}, + ?STACKTRACE(), + P, Debug, S, Q) + end + end; +loop_callback_mode_result( + P, Debug, S, Q, State_Data, + NextEventsR, Hibernate, TimeoutsR, Postpone, + StateCall, CallbackEvent, + CallbackMode, [], NewCallbackMode, NewStateEnter) -> + %% + case NewCallbackMode of + undefined -> + terminate( + error, + {bad_return_from_callback_mode,CallbackMode}, + ?STACKTRACE(), + P, Debug, S, Q); + _ -> + P_1 = + P#params{ + callback_mode = NewCallbackMode, + state_enter = NewStateEnter}, + loop_state_callback( + P_1, Debug, S, Q, State_Data, + NextEventsR, Hibernate, TimeoutsR, Postpone, + StateCall, CallbackEvent) + end. + %% Process the result from the state function %% loop_state_callback_result( @@ -1434,91 +1448,70 @@ loop_actions_list( P, Debug, S, Q, NextState_NewData, NextEventsR, Hibernate, TimeoutsR, Postpone, CallEnter, StateCall, Actions, Type, Content); - %% - Timeout -> - loop_actions_timeout( - P, Debug, S, Q, NextState_NewData, - NextEventsR, Hibernate, TimeoutsR, Postpone, - CallEnter, StateCall, Actions, Timeout) - end. - -%% Process a reply action -%% -loop_actions_reply( - P, Debug, S, Q, NextState_NewData, - NextEventsR, Hibernate, TimeoutsR, Postpone, - CallEnter, StateCall, Actions, - From, Reply) -> - %% - case from(From) of - true -> - %% No need for a separate ?not_sys_debug clause here - %% since the external call to erlang:'!'/2 in reply/2 - %% will cause swap out of all live registers anyway - reply(From, Reply), - Debug_1 = ?sys_debug(Debug, P#params.name, {out,Reply,From}), - loop_actions_list( - P, Debug_1, S, Q, NextState_NewData, - NextEventsR, Hibernate, TimeoutsR, Postpone, - CallEnter, StateCall, Actions); - false -> - terminate( - error, - {bad_action_from_state_function,{reply,From,Reply}}, - ?STACKTRACE(), P, Debug, - S#state{ - state_data = NextState_NewData, - hibernate = Hibernate}, - Q) - end. - -%% Process a next_event action -%% -loop_actions_next_event( - P, Debug, S, Q, NextState_NewData, - NextEventsR, Hibernate, TimeoutsR, Postpone, - CallEnter, StateCall, Actions, Type, Content) -> - case event_type(Type) of - true when StateCall -> - NextEvent = {Type,Content}, - case Debug of - ?not_sys_debug -> + %% + {Tag, NewModule} + when Tag =:= change_callback_module, is_atom(NewModule); + Tag =:= push_callback_module, is_atom(NewModule) -> + if + StateCall -> + NewModules = + case Tag of + change_callback_module -> + [NewModule | tl(P#params.modules)]; + push_callback_module -> + [NewModule | P#params.modules] + end, + P_1 = + P#params{ + callback_mode = undefined, modules = NewModules}, loop_actions_list( - P, Debug, S, Q, NextState_NewData, - [NextEvent|NextEventsR], - Hibernate, TimeoutsR, Postpone, + P_1, Debug, S, Q, NextState_NewData, + NextEventsR, Hibernate, TimeoutsR, Postpone, CallEnter, StateCall, Actions); - _ -> - Name = P#params.name, - {State,_Data} = S#state.state_data, - Debug_1 = - sys_debug(Debug, Name, {in,{Type,Content},State}), + true -> + terminate( + error, + {bad_state_enter_action_from_state_function,Action}, + ?STACKTRACE(), P, Debug, + S#state{ + state_data = NextState_NewData, + hibernate = Hibernate}, + Q) + end; + pop_callback_module when tl(P#params.modules) =/= [] -> + if + StateCall -> + NewModules = tl(P#params.modules), + P_1 = + P#params{ + callback_mode = undefined, + modules = NewModules}, loop_actions_list( - P, Debug_1, S, Q, NextState_NewData, - [NextEvent|NextEventsR], - Hibernate, TimeoutsR, Postpone, - CallEnter, StateCall, Actions) - end; + P_1, Debug, S, Q, NextState_NewData, + NextEventsR, Hibernate, TimeoutsR, Postpone, + CallEnter, StateCall, Actions); + true -> + terminate( + error, + {bad_state_enter_action_from_state_function,Action}, + ?STACKTRACE(), P, Debug, + S#state{ + state_data = NextState_NewData, + hibernate = Hibernate}, + Q) + end; + %% _ -> - terminate( - error, - {if - StateCall -> - bad_action_from_state_function; - true -> - bad_state_enter_action_from_state_function - end, - {next_event,Type,Content}}, - ?STACKTRACE(), P, Debug, - S#state{ - state_data = NextState_NewData, - hibernate = Hibernate}, - Q) + loop_actions_list( + P, Debug, S, Q, NextState_NewData, + NextEventsR, Hibernate, TimeoutsR, Postpone, + CallEnter, StateCall, Actions, Action) end. -%% Process a timeout action, or also any unrecognized action +%% Process all other actions, i.e timeout actions, +%% all others are unrecognized %% -loop_actions_timeout( +loop_actions_list( P, Debug, S, Q, NextState_NewData, NextEventsR, Hibernate, TimeoutsR, Postpone, CallEnter, StateCall, Actions, @@ -1585,7 +1578,7 @@ loop_actions_timeout( hibernate = Hibernate}, Q) end; -loop_actions_timeout( +loop_actions_list( P, Debug, S, Q, NextState_NewData, NextEventsR, Hibernate, TimeoutsR, Postpone, CallEnter, StateCall, Actions, @@ -1610,7 +1603,7 @@ loop_actions_timeout( hibernate = Hibernate}, Q) end; -loop_actions_timeout( +loop_actions_list( P, Debug, S, Q, NextState_NewData, NextEventsR, Hibernate, TimeoutsR, Postpone, CallEnter, StateCall, Actions, @@ -1634,7 +1627,7 @@ loop_actions_timeout( hibernate = Hibernate}, Q) end; -loop_actions_timeout( +loop_actions_list( P, Debug, S, Q, NextState_NewData, NextEventsR, Hibernate, TimeoutsR, Postpone, CallEnter, StateCall, Actions, @@ -1659,6 +1652,80 @@ loop_actions_timeout( Q) end. +%% Process a reply action +%% +loop_actions_reply( + P, Debug, S, Q, NextState_NewData, + NextEventsR, Hibernate, TimeoutsR, Postpone, + CallEnter, StateCall, Actions, + From, Reply) -> + %% + case from(From) of + true -> + %% No need for a separate ?not_sys_debug clause here + %% since the external call to erlang:'!'/2 in reply/2 + %% will cause swap out of all live registers anyway + reply(From, Reply), + Debug_1 = ?sys_debug(Debug, P#params.name, {out,Reply,From}), + loop_actions_list( + P, Debug_1, S, Q, NextState_NewData, + NextEventsR, Hibernate, TimeoutsR, Postpone, + CallEnter, StateCall, Actions); + false -> + terminate( + error, + {bad_action_from_state_function,{reply,From,Reply}}, + ?STACKTRACE(), P, Debug, + S#state{ + state_data = NextState_NewData, + hibernate = Hibernate}, + Q) + end. + +%% Process a next_event action +%% +loop_actions_next_event( + P, Debug, S, Q, NextState_NewData, + NextEventsR, Hibernate, TimeoutsR, Postpone, + CallEnter, StateCall, Actions, Type, Content) -> + case event_type(Type) of + true when StateCall -> + NextEvent = {Type,Content}, + case Debug of + ?not_sys_debug -> + loop_actions_list( + P, Debug, S, Q, NextState_NewData, + [NextEvent|NextEventsR], + Hibernate, TimeoutsR, Postpone, + CallEnter, StateCall, Actions); + _ -> + Name = P#params.name, + {State,_Data} = S#state.state_data, + Debug_1 = + sys_debug(Debug, Name, {in,{Type,Content},State}), + loop_actions_list( + P, Debug_1, S, Q, NextState_NewData, + [NextEvent|NextEventsR], + Hibernate, TimeoutsR, Postpone, + CallEnter, StateCall, Actions) + end; + _ -> + terminate( + error, + {if + StateCall -> + bad_action_from_state_function; + true -> + bad_state_enter_action_from_state_function + end, + {next_event,Type,Content}}, + ?STACKTRACE(), P, Debug, + S#state{ + state_data = NextState_NewData, + hibernate = Hibernate}, + Q) + end. + %% Do the state transition %% loop_state_transition( @@ -2236,7 +2303,7 @@ do_reply_then_terminate( terminate( Class, Reason, Stacktrace, - #params{module = Module} = P, Debug, + #params{modules = [Module | _]} = P, Debug, #state{state_data = {State,Data}} = S, Q) -> case erlang:function_exported(Module, terminate, 3) of true -> @@ -2277,6 +2344,7 @@ error_info( Class, Reason, Stacktrace, Debug, #params{ name = Name, + modules = Modules, callback_mode = CallbackMode, state_enter = StateEnter} = P, #state{ @@ -2288,6 +2356,7 @@ error_info( name=>Name, queue=>Q, postponed=>Postponed, + modules=>Modules, callback_mode=>CallbackMode, state_enter=>StateEnter, state=>format_status(terminate, get(), P, S), @@ -2327,6 +2396,7 @@ format_log(#{label:={gen_statem,terminate}, name:=Name, queue:=Q, postponed:=Postponed, + modules:=Modules, callback_mode:=CallbackMode, state_enter:=StateEnter, state:=FmtData, @@ -2373,6 +2443,7 @@ format_log(#{label:={gen_statem,terminate}, end ++ "** When server state = ~tp~n" ++ "** Reason for termination = ~w:~tp~n" ++ + "** Callback modules = ~p~n" ++ "** Callback mode = ~p~n" ++ case Q of [_,_|_] -> "** Queued = ~tp~n"; @@ -2401,6 +2472,7 @@ format_log(#{label:={gen_statem,terminate}, end] ++ [error_logger:limit_term(FmtData), Class,error_logger:limit_term(FixedReason), + error_logger:limit_term(Modules), CBMode] ++ case Q of [_|[_|_] = Events] -> [error_logger:limit_term(Events)]; @@ -2438,7 +2510,7 @@ format_client_log({_Pid,{Name,Stacktrace}}) -> %% Call Module:format_status/2 or return a default value format_status( Opt, PDict, - #params{module = Module}, + #params{modules = [Module | _]}, #state{state_data = {State,Data} = State_Data}) -> case erlang:function_exported(Module, format_status, 2) of true -> diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl index 74efe5c513..2114b17a79 100644 --- a/lib/stdlib/src/ms_transform.erl +++ b/lib/stdlib/src/ms_transform.erl @@ -356,11 +356,6 @@ clause({clause,Line,H0,G0,B0},Bound) -> copy({call,Line,{remote,_Line2,{atom,_Line3,ets},{atom,_Line4,fun2ms}}, As0},Bound) -> {transform_call(ets,Line,As0,Bound),Bound}; -copy({call,Line,{remote,_Line2,{record_field,_Line3, - {atom,_Line4,''},{atom,_Line5,ets}}, - {atom,_Line6,fun2ms}}, As0},Bound) -> - %% Packages... - {transform_call(ets,Line,As0,Bound),Bound}; copy({call,Line,{remote,_Line2,{atom,_Line3,dbg},{atom,_Line4,fun2ms}}, As0},Bound) -> {transform_call(dbg,Line,As0,Bound),Bound}; diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl index c73cf22943..9bb48cb157 100644 --- a/lib/stdlib/src/shell.erl +++ b/lib/stdlib/src/shell.erl @@ -1496,8 +1496,8 @@ catch_exception(Bool) -> PromptFunc :: 'default' | {module(),atom()}, PromptFunc2 :: 'default' | {module(),atom()}. -prompt_func(String) -> - set_env(stdlib, shell_prompt_func, String, ?DEF_PROMPT_FUNC). +prompt_func(PromptFunc) -> + set_env(stdlib, shell_prompt_func, PromptFunc, ?DEF_PROMPT_FUNC). -spec strings(Strings) -> Strings2 when Strings :: boolean(), diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src index 2bff791c93..6ade386159 100644 --- a/lib/stdlib/src/stdlib.app.src +++ b/lib/stdlib/src/stdlib.app.src @@ -108,7 +108,6 @@ dets]}, {applications, [kernel]}, {env, []}, - {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-10.6","crypto-3.3", + {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-10.6.2","crypto-3.3", "compiler-5.0"]} ]}. - diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src index 20d93f1fee..c9c060f575 100644 --- a/lib/stdlib/src/stdlib.appup.src +++ b/lib/stdlib/src/stdlib.appup.src @@ -29,6 +29,9 @@ {"%VSN%", [{<<"^3\\.10$">>,[restart_new_emulator]}, {<<"^3\\.10\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^3\\.11$">>,[restart_new_emulator]}, + {<<"^3\\.11\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^3\\.11\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, {<<"^3\\.5$">>,[restart_new_emulator]}, {<<"^3\\.5\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, {<<"^3\\.5\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, @@ -47,6 +50,9 @@ {<<"^3\\.9\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}], [{<<"^3\\.10$">>,[restart_new_emulator]}, {<<"^3\\.10\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^3\\.11$">>,[restart_new_emulator]}, + {<<"^3\\.11\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^3\\.11\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, {<<"^3\\.5$">>,[restart_new_emulator]}, {<<"^3\\.5\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, {<<"^3\\.5\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, diff --git a/lib/stdlib/src/uri_string.erl b/lib/stdlib/src/uri_string.erl index 18ee8375c7..315e5700eb 100644 --- a/lib/stdlib/src/uri_string.erl +++ b/lib/stdlib/src/uri_string.erl @@ -297,10 +297,7 @@ NormalizedURI :: uri_string() | error(). normalize(URIMap) -> - try normalize(URIMap, []) - catch - throw:{error, Atom, RestData} -> {error, Atom, RestData} - end. + normalize(URIMap, []). -spec normalize(URI, Options) -> NormalizedURI when @@ -309,20 +306,32 @@ normalize(URIMap) -> NormalizedURI :: uri_string() | uri_map() | error(). normalize(URIMap, []) when is_map(URIMap) -> - recompose(normalize_map(URIMap)); + try recompose(normalize_map(URIMap)) + catch + throw:{error, Atom, RestData} -> {error, Atom, RestData} + end; normalize(URIMap, [return_map]) when is_map(URIMap) -> - normalize_map(URIMap); + try normalize_map(URIMap) + catch + throw:{error, Atom, RestData} -> {error, Atom, RestData} + end; normalize(URIString, []) -> case parse(URIString) of Value when is_map(Value) -> - recompose(normalize_map(Value)); + try recompose(normalize_map(Value)) + catch + throw:{error, Atom, RestData} -> {error, Atom, RestData} + end; Error -> Error end; normalize(URIString, [return_map]) -> case parse(URIString) of Value when is_map(Value) -> - normalize_map(Value); + try normalize_map(Value) + catch + throw:{error, Atom, RestData} -> {error, Atom, RestData} + end; Error -> Error end. diff --git a/lib/stdlib/test/calendar_SUITE.erl b/lib/stdlib/test/calendar_SUITE.erl index 224c0d5625..bea5a217db 100644 --- a/lib/stdlib/test/calendar_SUITE.erl +++ b/lib/stdlib/test/calendar_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2019. All Rights Reserved. +%% Copyright Ericsson AB 1997-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -223,6 +223,7 @@ rfc3339(Config) when is_list(Config) -> {'EXIT', _} = (catch do_format_z(253402300799+1, [])), {'EXIT', _} = (catch do_parse("9999-12-31T23:59:60Z", [])), {'EXIT', _} = (catch do_format_z(253402300799*1000000000+999999999+1, Ns)), + {'EXIT', _} = (catch do_parse("2010-04-11T22:35:41", [])), % OTP-16514 253402300799 = do_parse("9999-12-31T23:59:59Z", []), "0000-01-01T00:00:00Z" = test_parse("0000-01-01T00:00:00.0+00:00"), diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl index 2436c8091c..c7556f6f7e 100644 --- a/lib/stdlib/test/erl_eval_SUITE.erl +++ b/lib/stdlib/test/erl_eval_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2018. All Rights Reserved. +%% Copyright Ericsson AB 1998-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -48,7 +48,8 @@ zero_width/1, eep37/1, eep43/1, - otp_15035/1]). + otp_15035/1, + otp_16439/1]). %% %% Define to run outside of test server @@ -88,7 +89,7 @@ all() -> otp_6539, otp_6543, otp_6787, otp_6977, otp_7550, otp_8133, otp_10622, otp_13228, otp_14826, funs, try_catch, eval_expr_5, zero_width, - eep37, eep43, otp_15035]. + eep37, eep43, otp_15035, otp_16439]. groups() -> []. @@ -1666,6 +1667,17 @@ otp_15035(Config) when is_list(Config) -> {e, d}), ok. +otp_16439(Config) when is_list(Config) -> + check(fun() -> + - 5 end, "+ - 5.", -5), + check(fun() -> - + - 5 end, "- + - 5.", 5), + check(fun() -> case 7 of - - 7 -> seven end end, + "case 7 of - - 7 -> seven end.", seven), + + {ok,Ts,_} = erl_scan:string("- #{}. "), + {ok,[{op,1,'-',{map,1,[]}}]} = erl_parse:parse_exprs(Ts), + + ok. + %% Check the string in different contexts: as is; in fun; from compiled code. check(F, String, Result) -> check1(F, String, Result), diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index c0cfd26925..48152243f8 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2019. All Rights Reserved. +%% Copyright Ericsson AB 2006-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -52,7 +52,8 @@ otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1, otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1, otp_10302/1, otp_10820/1, otp_11100/1, otp_11861/1, pr_1014/1, - otp_13662/1, otp_14285/1, otp_15592/1, otp_15751/1, otp_15755/1]). + otp_13662/1, otp_14285/1, otp_15592/1, otp_15751/1, otp_15755/1, + otp_16435/1]). %% Internal export. -export([ehook/6]). @@ -82,7 +83,7 @@ groups() -> [otp_6321, otp_6911, otp_6914, otp_8150, otp_8238, otp_8473, otp_8522, otp_8567, otp_8664, otp_9147, otp_10302, otp_10820, otp_11100, otp_11861, pr_1014, otp_13662, - otp_14285, otp_15592, otp_15751, otp_15755]}]. + otp_14285, otp_15592, otp_15751, otp_15755, otp_16435]}]. init_per_suite(Config) -> Config. @@ -1189,36 +1190,34 @@ otp_15592(_Config) -> ok. otp_15751(_Config) -> - ok = pp_expr(<<"try foo:bar() - catch - Reason : Stacktrace -> - {Reason, Stacktrace} - end">>), - ok = pp_expr(<<"try foo:bar() - catch - throw: Reason : Stacktrace -> - {Reason, Stacktrace} - end">>), - ok = pp_expr(<<"try foo:bar() - catch - Reason : _ -> - Reason - end">>), - ok = pp_expr(<<"try foo:bar() - catch - throw: Reason : _ -> - Reason - end">>), - ok = pp_expr(<<"try foo:bar() - catch - Reason -> - Reason - end">>), - ok = pp_expr(<<"try foo:bar() - catch - throw: Reason -> - Reason - end">>), + Check = fun(L) -> + ok = pp_expr(L), + remove_indentation(flat_parse_and_pp_expr(L, 0, [])) + end, + "try foo:bar() catch Reason:Stacktrace -> {Reason, Stacktrace} end" = + Check("try foo:bar() + catch Reason:Stacktrace -> {Reason, Stacktrace} end"), + + "try foo:bar() catch throw:Reason:Stacktrace -> {Reason, Stacktrace} end" = + Check("try foo:bar() + catch throw:Reason:Stacktrace -> {Reason, Stacktrace} end"), + + "try foo:bar() catch Reason:_ -> Reason end" = + Check("try foo:bar() + catch Reason:_ -> Reason end"), + + "try foo:bar() catch throw:Reason -> Reason end" = % ":_" removed + Check("try foo:bar() + catch throw:Reason:_-> Reason end"), + + "try foo:bar() catch throw:Reason -> Reason end" = % "throw:" added + Check("try foo:bar() + catch Reason -> Reason end"), + + "try foo:bar() catch throw:Reason -> Reason end" = + Check("try foo:bar() + catch throw:Reason -> Reason end"), + ok. otp_15755(_Config) -> @@ -1261,6 +1260,45 @@ otp_15755(_Config) -> " 'sf s sdf', [], {}, {[]}}.", [])), ok. +otp_16435(_Config) -> + CheckF = fun(S) -> S = lists:flatten(parse_and_pp_forms(S, [])) end, + CheckF("-type t() :: A :: integer().\n"), + CheckF("-type t() :: A :: (B :: integer()).\n"), + CheckF("-type t() :: {A :: (B :: integer())}.\n"), + CheckF("-record(r,{f :: {A :: (B :: integer())}}).\n"), + CheckF("-record(r,{f = 3 :: {A :: (B :: integer())}}).\n"), + CheckF("-type t() :: #r{f :: A :: (B :: integer())}.\n"), + CheckF("-spec t(X) -> X when X :: Y :: (Z :: #r{}).\n"), + + CheckF("f() ->\n << \n (catch <<1:4>>) ||\n" + " A <- []\n >>.\n"), + CheckF("f() ->\n [ \n (catch foo) ||\n A <- []\n ].\n"), + + + Check = fun(S) -> S = flat_parse_and_pp_expr(S, 0, []) end, + Check("5 #r4.f1"), + Check("17 #{[] => true}"), + Check("0 #r1{f2 = foo}"), + Check("fun foo:bar/17 #{}"), + Check("fun a/2 #{}"), + + Check("try foo:bar() of\n" + " a ->\n" + " b\n" + "after\n" + " d\n" + "end"), + + Check("try foo:bar() of\n" + " a ->\n" + " b\n" + "catch\n" + " _:_ ->\n" + " c\n" + "end"), + + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% compile(Config, Tests) -> diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index b7fe664f12..7f349c1bb5 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -50,6 +50,7 @@ fixtable_next/1, fixtable_insert/1, rename/1, rename_unnamed/1, evil_rename/1, update_element/1, update_counter/1, evil_update_counter/1, partly_bound/1, match_heavy/1]). -export([update_counter_with_default/1]). +-export([update_counter_with_default_bad_pos/1]). -export([update_counter_table_growth/1]). -export([member/1]). -export([memory/1]). @@ -131,7 +132,9 @@ all() -> interface_equality, fixtable_next, fixtable_insert, rename, rename_unnamed, evil_rename, update_element, update_counter, evil_update_counter, - update_counter_with_default, partly_bound, + update_counter_with_default, + update_counter_with_default_bad_pos, + partly_bound, update_counter_table_growth, match_heavy, {group, fold}, member, t_delete_object, select_bound_chunk, @@ -2376,35 +2379,76 @@ update_counter_with_default_do(Opts) -> T1 = ets_new(a, [set | Opts]), %% Insert default object. 3 = ets:update_counter(T1, foo, 2, {beaufort,1}), + 1 = ets:info(T1, size), %% Increment. 5 = ets:update_counter(T1, foo, 2, {cabecou,1}), + 1 = ets:info(T1, size), %% Increment with list. [9] = ets:update_counter(T1, foo, [{2,4}], {camembert,1}), + 1 = ets:info(T1, size), %% Same with non-immediate key. 3 = ets:update_counter(T1, {foo,bar}, 2, {{chaource,chevrotin},1}), + 2 = ets:info(T1, size), 5 = ets:update_counter(T1, {foo,bar}, 2, {{cantal,comté},1}), + 2 = ets:info(T1, size), [9] = ets:update_counter(T1, {foo,bar}, [{2,4}], {{emmental,de,savoie},1}), + 2 = ets:info(T1, size), + %% default counter is not an integer. + {'EXIT',{badarg,_}} = (catch ets:update_counter(T1, qux, 3, {saint,félicien})), + 2 = ets:info(T1, size), + %% No third element in default value. + {'EXIT',{badarg,_}} = (catch ets:update_counter(T1, qux, [{3,1}], {roquefort,1})), + 2 = ets:info(T1, size), + %% Same with ordered set. T2 = ets_new(b, [ordered_set | Opts]), 3 = ets:update_counter(T2, foo, 2, {maroilles,1}), + 1 = ets:info(T2, size), 5 = ets:update_counter(T2, foo, 2, {mimolette,1}), + 1 = ets:info(T2, size), [9] = ets:update_counter(T2, foo, [{2,4}], {morbier,1}), + 1 = ets:info(T2, size), 3 = ets:update_counter(T2, {foo,bar}, 2, {{laguiole},1}), + 2 = ets:info(T2, size), 5 = ets:update_counter(T2, {foo,bar}, 2, {{saint,nectaire},1}), + 2 = ets:info(T2, size), [9] = ets:update_counter(T2, {foo,bar}, [{2,4}], {{rocamadour},1}), + 2 = ets:info(T2, size), %% Arithmetically-equal keys. 3 = ets:update_counter(T2, 1.0, 2, {1,1}), + 3 = ets:info(T2, size), 5 = ets:update_counter(T2, 1, 2, {1,1}), + 3 = ets:info(T2, size), 7 = ets:update_counter(T2, 1, 2, {1.0,1}), + 3 = ets:info(T2, size), %% Same with reversed type difference. 3 = ets:update_counter(T2, 2, 2, {2.0,1}), + 4 = ets:info(T2, size), 5 = ets:update_counter(T2, 2.0, 2, {2.0,1}), + 4 = ets:info(T2, size), 7 = ets:update_counter(T2, 2.0, 2, {2,1}), - %% bar is not an integer. + 4 = ets:info(T2, size), + %% default counter is not an integer. {'EXIT',{badarg,_}} = (catch ets:update_counter(T1, qux, 3, {saint,félicien})), + 4 = ets:info(T2, size), %% No third element in default value. {'EXIT',{badarg,_}} = (catch ets:update_counter(T1, qux, [{3,1}], {roquefort,1})), + 4 = ets:info(T2, size), + ok. +%% ERL-1125 +update_counter_with_default_bad_pos(Config) when is_list(Config) -> + repeat_for_all_ord_set_table_types(fun update_counter_with_default_bad_pos_do/1). + +update_counter_with_default_bad_pos_do(Opts) -> + T = ets_new(a, Opts), + 0 = ets:info(T, size), + ok = try ets:update_counter(T, 101065, {1, 1}, {101065, 0}) + catch + error:badarg -> ok; + Class:Reason -> {Class, Reason} + end, + 0 = ets:info(T, size), ok. update_counter_table_growth(_Config) -> diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl index aa4d258cbf..296973370c 100644 --- a/lib/stdlib/test/gen_statem_SUITE.erl +++ b/lib/stdlib/test/gen_statem_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2016-2019. All Rights Reserved. +%% Copyright Ericsson AB 2016-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -68,7 +68,8 @@ tcs(sys) -> error_format_status, terminate_crash_format, get_state, replace_state]; tcs(undef_callbacks) -> - [undef_code_change, undef_terminate1, undef_terminate2]. + [undef_code_change, undef_terminate1, undef_terminate2, + pop_too_many]. init_per_suite(Config) -> Config. @@ -83,9 +84,7 @@ init_per_group(GroupName, Config) GroupName =:= sys_handle_event -> [{callback_mode,handle_event_function}|Config]; init_per_group(undef_callbacks, Config) -> - DataDir = ?config(data_dir, Config), - StatemPath = filename:join(DataDir, "oc_statem.erl"), - {ok, oc_statem} = compile:file(StatemPath), + compile_oc_statem(Config), Config; init_per_group(_GroupName, Config) -> Config. @@ -98,6 +97,9 @@ init_per_testcase(_CaseName, Config) -> %%% dbg:tracer(), %%% dbg:p(all, c), %%% dbg:tpl(gen_statem, cx), +%%% dbg:tpl(gen_statem, loop_receive, cx), +%%% dbg:tpl(gen_statem, loop_state_callback, cx), +%%% dbg:tpl(gen_statem, loop_callback_mode_result, cx), %%% dbg:tpl(proc_lib, cx), %%% dbg:tpl(gen, cx), %%% dbg:tpl(sys, cx), @@ -107,6 +109,12 @@ end_per_testcase(_CaseName, Config) -> %%% dbg:stop(), Config. +compile_oc_statem(Config) -> + DataDir = ?config(data_dir, Config), + StatemPath = filename:join(DataDir, "oc_statem.erl"), + {ok, oc_statem} = compile:file(StatemPath), + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -define(EXPECT_FAILURE(Code, Reason), try begin Code end of @@ -1722,7 +1730,7 @@ enter_loop(Reg1, Reg2, Opts) -> end. undef_code_change(_Config) -> - {ok, Statem} = gen_statem:start(oc_statem, [], []), + {ok, Statem} = gen_statem:start(oc_statem, [], [{debug, [trace]}]), {error, {'EXIT', {undef, [{oc_statem, code_change, [_, _, _, _], _}|_]}}} = fake_upgrade(Statem, oc_statem). @@ -1735,7 +1743,7 @@ fake_upgrade(Pid, Mod) -> Ret. undef_terminate1(_Config) -> - {ok, Statem} = gen_statem:start(oc_statem, [], []), + {ok, Statem} = gen_statem:start(oc_statem, [], [{debug,[trace]}]), MRef = monitor(process, Statem), ok = gen_statem:stop(Statem), verify_down(Statem, MRef, normal), @@ -1743,7 +1751,7 @@ undef_terminate1(_Config) -> undef_terminate2(_Config) -> Reason = {error, test}, - {ok, Statem} = oc_statem:start(), + {ok, Statem} = oc_statem:start([{debug,[trace]}]), MRef = monitor(process, Statem), ok = gen_statem:stop(Statem, Reason, infinity), verify_down(Statem, MRef, Reason). @@ -1769,6 +1777,54 @@ verify_down(Statem, MRef, Reason) -> ct:fail(default_terminate_failed) end. + +pop_too_many(_Config) -> + _ = process_flag(trap_exit, true), + + Machine = + #{init => + fun () -> + {ok,start,undefined} + end, + start => + fun ({call, From}, {change_callback_module, _Module} = Action, + undefined = _Data) -> + {keep_state_and_data, + [Action, + {reply,From,ok}]}; + ({call, From}, {verify, ?MODULE}, + undefined = _Data) -> + {keep_state_and_data, + [{reply,From,ok}]}; + ({call, From}, pop_callback_module = Action, + undefined = _Data) -> + {keep_state_and_data, + [Action, + {reply,From,ok}]} + end}, + {ok, STM} = + gen_statem:start_link( + ?MODULE, + {map_statem, Machine, []}, + [{debug, [trace]}]), + + ok = gen_statem:call(STM, {change_callback_module, oc_statem}), + ok = gen_statem:call(STM, {push_callback_module, ?MODULE}), + ok = gen_statem:call(STM, {verify, ?MODULE}), + ok = gen_statem:call(STM, pop_callback_module), + BadAction = {bad_action_from_state_function, pop_callback_module}, + {{BadAction, _}, + {gen_statem,call,[STM,pop_callback_module,infinity]}} = + ?EXPECT_FAILURE(gen_statem:call(STM, pop_callback_module), Reason), + + receive + {'EXIT', STM, {BadAction, _}} -> + ok; + Other -> + ct:fail({surprise, Other}) + end. + + %% Test the order for multiple {next_event,T,C} next_events(Config) -> {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []), diff --git a/lib/stdlib/test/gen_statem_SUITE_data/oc_statem.erl b/lib/stdlib/test/gen_statem_SUITE_data/oc_statem.erl index 27c9e0718d..1bcd08867f 100644 --- a/lib/stdlib/test/gen_statem_SUITE_data/oc_statem.erl +++ b/lib/stdlib/test/gen_statem_SUITE_data/oc_statem.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2017. All Rights Reserved. +%% Copyright Ericsson AB 2017-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -22,16 +22,31 @@ -behaviour(gen_statem). %% API --export([start/0]). +-export([start/1]). %% gen_statem callbacks --export([init/1, callback_mode/0]). +-export([init/1, callback_mode/0, handle_event/4]). -start() -> - gen_statem:start({local, ?MODULE}, ?MODULE, [], []). +start(Opts) -> + gen_statem:start({local, ?MODULE}, ?MODULE, [], Opts). init([]) -> - {ok, state_name, #{}}. + {ok, start, #{}}. callback_mode() -> - handle_event_function. + [handle_event_function, state_enter]. + +handle_event(enter, start, start, _Data) -> + keep_state_and_data; +handle_event( + {call,From}, {push_callback_module,NewModule} = Action, + start, _Data) -> + {keep_state_and_data, + [Action, + {reply,From,ok}]}; +handle_event( + {call,From}, pop_callback_module = Action, + start, _Data) -> + {keep_state_and_data, + [Action, + {reply,From,ok}]}. diff --git a/lib/stdlib/test/tar_SUITE.erl b/lib/stdlib/test/tar_SUITE.erl index 32a33283d1..fb2b7dc45d 100644 --- a/lib/stdlib/test/tar_SUITE.erl +++ b/lib/stdlib/test/tar_SUITE.erl @@ -578,19 +578,22 @@ extract_from_open_file(Config) when is_list(Config) -> symlinks(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), Dir = filename:join(PrivDir, "symlinks"), + VulnerableDir = filename:join(PrivDir, "vulnerable_symlinks"), ok = file:make_dir(Dir), + ok = file:make_dir(VulnerableDir), ABadSymlink = filename:join(Dir, "bad_symlink"), - PointsTo = "/a/definitely/non_existing/path", - Res = case make_symlink("/a/definitely/non_existing/path", ABadSymlink) of + PointsTo = "a/definitely/non_existing/path", + Res = case make_symlink("a/definitely/non_existing/path", ABadSymlink) of {error, enotsup} -> {skip, "Symbolic links not supported on this platform"}; ok -> symlinks(Dir, "bad_symlink", PointsTo), - long_symlink(Dir) + long_symlink(Dir), + symlink_vulnerability(VulnerableDir) end, %% Clean up. - delete_files([Dir]), + delete_files([Dir,VulnerableDir]), verify_ports(Config), Res. @@ -678,7 +681,7 @@ long_symlink(Dir) -> ok = file:set_cwd(Dir), AFile = "long_symlink", - RequiresPAX = "/tmp/aarrghh/this/path/is/far/longer/than/one/hundred/characters/which/is/the/maximum/number/of/characters/allowed", + RequiresPAX = "tmp/aarrghh/this/path/is/far/longer/than/one/hundred/characters/which/is/the/maximum/number/of/characters/allowed", ok = file:make_symlink(RequiresPAX, AFile), ok = erl_tar:create(Tar, [AFile], [verbose]), false = is_ustar(Tar), @@ -690,6 +693,23 @@ long_symlink(Dir) -> {ok, RequiresPAX} = file:read_link(AFile), ok. +symlink_vulnerability(Dir) -> + ok = file:set_cwd(Dir), + ok = file:make_dir("tar"), + ok = file:set_cwd("tar"), + ok = file:make_symlink("..", "link"), + ok = file:write_file("../file", <<>>), + ok = erl_tar:create("../my.tar", ["link","link/file"]), + ok = erl_tar:tt("../my.tar"), + + ok = file:set_cwd(Dir), + delete_files(["file","tar"]), + ok = file:make_dir("tar"), + ok = file:set_cwd("tar"), + {error,{"..",unsafe_symlink}} = erl_tar:extract("../my.tar"), + + ok. + init(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), ok = file:set_cwd(PrivDir), diff --git a/lib/stdlib/test/uri_string_SUITE.erl b/lib/stdlib/test/uri_string_SUITE.erl index 55ba75bd5f..0829fcd915 100644 --- a/lib/stdlib/test/uri_string_SUITE.erl +++ b/lib/stdlib/test/uri_string_SUITE.erl @@ -29,6 +29,7 @@ normalize_pct_encoded_userinfo/1, normalize_pct_encoded_query/1, normalize_pct_encoded_fragment/1, + normalize_pct_encoded_negative/1, parse_binary_fragment/1, parse_binary_host/1, parse_binary_host_ipv4/1, parse_binary_host_ipv6/1, parse_binary_path/1, parse_binary_pct_encoded_fragment/1, parse_binary_pct_encoded_query/1, @@ -86,6 +87,7 @@ all() -> normalize_pct_encoded_userinfo, normalize_pct_encoded_query, normalize_pct_encoded_fragment, + normalize_pct_encoded_negative, parse_binary_scheme, parse_binary_userinfo, parse_binary_pct_encoded_userinfo, @@ -1139,6 +1141,16 @@ normalize_pct_encoded_fragment(_Config) -> #{host := "example.com", path := "/", fragment := "合気道"} = uri_string:normalize("//example.com/#%E5%90%88%E6%B0%97%E9%81%93", [return_map]). +normalize_pct_encoded_negative(_Config) -> + {error,invalid_utf8,<<0,0,0,246>>} = + uri_string:normalize(#{host => "%00%00%00%F6",path => []}, [return_map]), + {error,invalid_utf8,<<0,0,0,246>>} = + uri_string:normalize(#{host => "%00%00%00%F6",path => []}, []), + {error,invalid_utf8,<<0,0,0,246>>} = + uri_string:normalize("//%00%00%00%F6", [return_map]), + {error,invalid_utf8,<<0,0,0,246>>} = + uri_string:normalize("//%00%00%00%F6", []). + interop_query_utf8(_Config) -> Q = uri_string:compose_query([{"foo bar","1"}, {"合", "2"}]), Uri = uri_string:recompose(#{path => "/", query => Q}), diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk index 6f74c2cd06..dae9e1fd64 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -1 +1 @@ -STDLIB_VSN = 3.11 +STDLIB_VSN = 3.11.2 diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el index 838c41a090..dafb9d56ac 100644 --- a/lib/tools/emacs/erlang.el +++ b/lib/tools/emacs/erlang.el @@ -4,7 +4,7 @@ ;; Author: Anders Lindgren ;; Keywords: erlang, languages, processes ;; Date: 2011-12-11 -;; Version: 2.8.3 +;; Version: 2.8.4 ;; Package-Requires: ((emacs "24.3")) ;; %CopyrightBegin% @@ -87,7 +87,7 @@ "The Erlang programming language." :group 'languages) -(defconst erlang-version "2.8.3" +(defconst erlang-version "2.8.4" "The version number of Erlang mode.") (defcustom erlang-root-dir nil @@ -2890,7 +2890,7 @@ Value is list (stack token-start token-type in-what)." ((looking-at "-type\\s \\|-opaque\\s ") (if stack (forward-char 1) - (erlang-push (list 'icr token (current-column)) stack) + (erlang-push (list 'type token (current-column)) stack) (forward-char 6))) ((looking-at "-spec\\s ") (if stack @@ -2933,7 +2933,7 @@ Value is list (stack token-start token-type in-what)." (erlang-pop stack) (if (and (eq (car (car stack)) 'fun) (or (eq (car (car (last stack))) 'spec) - (eq (car (car (cdr stack))) '::))) ;; -type() + (eq (car (car (last stack))) 'type))) ;; -type() ;; Inside fun type def ') closes fun definition (erlang-pop stack))) ((eq (car (car stack)) 'icr) @@ -2995,7 +2995,9 @@ Return nil if inside string, t if in a comment." (- (+ previous erlang-argument-indent) 1)))) (t (nth 2 stack-top)))) - ((= (following-char) ?,) + ((looking-at "||") + (erlang-indent-element stack-top indent-point token)) + ((memq (following-char) '(?, ?|)) ;; a comma at the start of the line: line up with opening parenthesis. (min (nth 2 stack-top) (erlang-indent-element stack-top indent-point token))) @@ -3033,9 +3035,9 @@ Return nil if inside string, t if in a comment." (save-excursion (goto-char (nth 1 stack-top)) (if (and erlang-icr-indent - (looking-at "\\(if\\|case\\|receive\\)[^_a-zA-Z0-9]")) + (looking-at "\\(if\\|case\\|receive\\|try\\)[^_a-zA-Z0-9]")) (+ (nth 2 stack-top) erlang-icr-indent) - (if (looking-at "\\(case\\|receive\\)[^_a-zA-Z0-9]") + (if (looking-at "\\(case\\|receive\\|try\\)[^_a-zA-Z0-9]") (+ (nth 2 stack-top) erlang-indent-level) (skip-chars-forward "a-z") (skip-chars-forward " \t") diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl index 2b3af417b6..80af13a4c3 100644 --- a/lib/tools/src/cover.erl +++ b/lib/tools/src/cover.erl @@ -1722,7 +1722,10 @@ bool_switch(E, T, F, AllVars, AuxVarN) -> {'case',Line,E, [{clause,Line,[{atom,Line,true}],[],[T]}, {clause,Line,[{atom,Line,false}],[],[F]}, - {clause,Line,[AuxVar],[], + %% Mark the next clause as compiler-generated to suppress + %% a warning if the case expression is an obvious boolean + %% value. + {clause,erl_anno:set_generated(true, Line),[AuxVar],[], [{call,Line, {remote,Line,{atom,Line,erlang},{atom,Line,error}}, [{tuple,Line,[{atom,Line,badarg},AuxVar]}]}]}]}. diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl index 36d4828861..369fbb2d42 100644 --- a/lib/tools/src/fprof.erl +++ b/lib/tools/src/fprof.erl @@ -2782,6 +2782,8 @@ parsify({A, B, C}) -> {parsify(A), parsify(B), parsify(C)}; parsify(Tuple) when is_tuple(Tuple) -> list_to_tuple(parsify(tuple_to_list(Tuple))); +parsify(Map) when is_map(Map) -> + maps:from_list(parsify(maps:to_list(Map))); parsify(Pid) when is_pid(Pid) -> erlang:pid_to_list(Pid); parsify(Port) when is_port(Port) -> diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl index 462767f430..0de73bc3d7 100644 --- a/lib/tools/test/cover_SUITE.erl +++ b/lib/tools/test/cover_SUITE.erl @@ -37,7 +37,7 @@ all() -> dont_reconnect_after_stop, stop_node_after_disconnect, export_import, otp_5031, otp_6115, otp_8270, otp_10979_hanging_node, otp_14817, - local_only, startup_race], + local_only, startup_race, otp_16476], case whereis(cover_server) of undefined -> [coverage,StartStop ++ NoStartStop]; @@ -1803,6 +1803,17 @@ startup_race_1([]) -> cover:stop(), ok. +otp_16476(Config) when is_list(Config) -> + Mod = obvious_booleans, + Dir = filename:join(proplists:get_value(data_dir, Config), + ?FUNCTION_NAME), + ok = file:set_cwd(Dir), + {ok, Mod} = compile:file(Mod, [debug_info]), + {ok, Mod} = cover:compile(Mod), + ok = Mod:Mod(), + ok = cover:stop(), + ok. + %%--Auxiliary------------------------------------------------------------ analyse_expr(Expr, Config) -> diff --git a/lib/tools/test/cover_SUITE_data/otp_16476/obvious_booleans.erl b/lib/tools/test/cover_SUITE_data/otp_16476/obvious_booleans.erl new file mode 100644 index 0000000000..1f383be0a5 --- /dev/null +++ b/lib/tools/test/cover_SUITE_data/otp_16476/obvious_booleans.erl @@ -0,0 +1,17 @@ +-module(obvious_booleans). +-export([?MODULE/0]). +-compile([warnings_as_errors]). + +?MODULE() -> + true = both_ok(ok, ok), + false = both_ok(ok, nok), + true = one_ok(ok, nok), + true = one_ok(nok, ok), + false = one_ok(nok, nok), + ok. + +both_ok(A, B) -> + A =:= ok andalso B =:= ok. + +one_ok(A, B) -> + A =:= ok orelse B =:= ok. diff --git a/lib/tools/test/emacs_SUITE.erl b/lib/tools/test/emacs_SUITE.erl index 42a1f395ec..9a0caa0867 100644 --- a/lib/tools/test/emacs_SUITE.erl +++ b/lib/tools/test/emacs_SUITE.erl @@ -214,11 +214,12 @@ emacs(EmacsCmds) when is_list(EmacsCmds) -> "--directory ", dquote(emacs_dir()), " ", "--eval \"(require 'erlang-start)\" " | EmacsCmds], + io:format("Cmd: ~ts~n", [Cmd]), Res0 = os:cmd(Cmd ++ " ; echo $?"), Rows = string:lexemes(Res0, ["\r\n", $\n]), Res = lists:last(Rows), Output = string:join(lists:droplast(Rows), "\n"), - io:format("Cmd ~ts:~n => ~s ~ts~n", [Cmd, Res, Output]), + io:format(" => ~s ~ts~n", [Res, Output]), "0" = Res, Output. diff --git a/lib/tools/test/emacs_SUITE_data/type_specs b/lib/tools/test/emacs_SUITE_data/type_specs index f9b15d7914..a72e90cf57 100644 --- a/lib/tools/test/emacs_SUITE_data/type_specs +++ b/lib/tools/test/emacs_SUITE_data/type_specs @@ -66,6 +66,30 @@ , b :: any() }. + +-type combined() :: { atom(), + atom() + , integer() + } + | [ atom() | + atom() + | integer() + ]. + +-type a_list1() :: [ atom() | + t() + | tuple() + ]. + + +-type a_list_with_fun() :: + %% ERL-1140 + [ atom() | + fun() + | tuple() + ]. + + %% Spec -spec t1(FooBar :: t99()) -> t99(); diff --git a/lib/tools/test/fprof_SUITE.erl b/lib/tools/test/fprof_SUITE.erl index ae0e7253ad..cdb207c1e2 100644 --- a/lib/tools/test/fprof_SUITE.erl +++ b/lib/tools/test/fprof_SUITE.erl @@ -27,7 +27,7 @@ %% Test suites -export([stack_seq/1, tail_seq/1, create_file_slow/1, spawn_simple/1, imm_tail_seq/1, imm_create_file_slow/1, imm_compile/1, - cpu_create_file_slow/1, unicode/1]). + cpu_create_file_slow/1, unicode/1, parsify_maps/1]). %% Other exports -export([create_file_slow/2]). @@ -59,7 +59,7 @@ all() -> false -> [stack_seq, tail_seq, create_file_slow, spawn_simple, imm_tail_seq, imm_create_file_slow, imm_compile, - cpu_create_file_slow, unicode] + cpu_create_file_slow, unicode, parsify_maps] end. @@ -544,6 +544,35 @@ unicode(Config) when is_list(Config) -> ok = fprof:profile(dump, AnalysisFile), ok = fprof:analyse(dest, AnalysisFile). +parsify_maps(Config) when is_list(Config) -> + Pid = self(), + Ref = make_ref(), + Port = hd(erlang:ports()), + Fun = fun () -> ok end, + M = #{pid => Pid, Pid => pid, + ref => Ref, Ref => ref, + port => Port, Port => port, + a_fun => Fun, Fun => a_fun}, + io:format("M = ~p~n", [M]), + L = [{tuple, M}, M, #{my_map => M, M => my_map}], + PL = fprof:parsify(L), + [{tuple, PM}, PM, PMap] = PL, + #{my_map := PM, PM := my_map} = PMap, + io:format("PM = ~p~n", [PM]), + LPid = pid_to_list(Pid), + LRef = ref_to_list(Ref), + LPort = port_to_list(Port), + LFun = erlang:fun_to_list(Fun), + LPid = maps:get(pid, PM), + pid = maps:get(LPid, PM), + LRef = maps:get(ref, PM), + ref = maps:get(LRef, PM), + LPort = maps:get(port, PM), + port = maps:get(LPort, PM), + LFun = maps:get(a_fun, PM), + a_fun = maps:get(LFun, PM), + ok. + %%%--------------------------------------------------------------------- %%% Functions to test %%%--------------------------------------------------------------------- diff --git a/make/otp_version_tickets_in_merge b/make/otp_version_tickets_in_merge index f04e1d49f5..e69de29bb2 100644 --- a/make/otp_version_tickets_in_merge +++ b/make/otp_version_tickets_in_merge @@ -1,6 +0,0 @@ -OTP-16314 -OTP-16349 -OTP-16357 -OTP-16359 -OTP-16360 -OTP-16361 diff --git a/otp_versions.table b/otp_versions.table index 47fdc5db8d..e9547c7ead 100644 --- a/otp_versions.table +++ b/otp_versions.table @@ -1,5 +1,13 @@ +OTP-22.2.8 : diameter-2.2.2 # asn1-5.0.9 common_test-1.18.1 compiler-7.5.2 crypto-4.6.4 debugger-4.2.8 dialyzer-4.1.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.11 erl_interface-3.13.1 erts-10.6.4 et-1.6.4 eunit-2.4 ftp-1.0.4 hipe-3.19.2 inets-7.1.2 jinterface-1.10.1 kernel-6.5.1 megaco-3.18.7 mnesia-4.16.2 observer-2.9.3 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7.1 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 snmp-5.4.5 ssh-4.8.2 ssl-9.5.3 stdlib-3.11.2 syntax_tools-2.2.1 tftp-1.0.2 tools-3.3 wx-1.9 xmerl-1.3.23 : +OTP-22.2.7 : compiler-7.5.2 # asn1-5.0.9 common_test-1.18.1 crypto-4.6.4 debugger-4.2.8 dialyzer-4.1.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.11 erl_interface-3.13.1 erts-10.6.4 et-1.6.4 eunit-2.4 ftp-1.0.4 hipe-3.19.2 inets-7.1.2 jinterface-1.10.1 kernel-6.5.1 megaco-3.18.7 mnesia-4.16.2 observer-2.9.3 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7.1 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 snmp-5.4.5 ssh-4.8.2 ssl-9.5.3 stdlib-3.11.2 syntax_tools-2.2.1 tftp-1.0.2 tools-3.3 wx-1.9 xmerl-1.3.23 : +OTP-22.2.6 : erts-10.6.4 # asn1-5.0.9 common_test-1.18.1 compiler-7.5.1 crypto-4.6.4 debugger-4.2.8 dialyzer-4.1.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.11 erl_interface-3.13.1 et-1.6.4 eunit-2.4 ftp-1.0.4 hipe-3.19.2 inets-7.1.2 jinterface-1.10.1 kernel-6.5.1 megaco-3.18.7 mnesia-4.16.2 observer-2.9.3 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7.1 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 snmp-5.4.5 ssh-4.8.2 ssl-9.5.3 stdlib-3.11.2 syntax_tools-2.2.1 tftp-1.0.2 tools-3.3 wx-1.9 xmerl-1.3.23 : +OTP-22.2.5 : erts-10.6.3 stdlib-3.11.2 # asn1-5.0.9 common_test-1.18.1 compiler-7.5.1 crypto-4.6.4 debugger-4.2.8 dialyzer-4.1.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.11 erl_interface-3.13.1 et-1.6.4 eunit-2.4 ftp-1.0.4 hipe-3.19.2 inets-7.1.2 jinterface-1.10.1 kernel-6.5.1 megaco-3.18.7 mnesia-4.16.2 observer-2.9.3 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7.1 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 snmp-5.4.5 ssh-4.8.2 ssl-9.5.3 syntax_tools-2.2.1 tftp-1.0.2 tools-3.3 wx-1.9 xmerl-1.3.23 : +OTP-22.2.4 : ssl-9.5.3 # asn1-5.0.9 common_test-1.18.1 compiler-7.5.1 crypto-4.6.4 debugger-4.2.8 dialyzer-4.1.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.11 erl_interface-3.13.1 erts-10.6.2 et-1.6.4 eunit-2.4 ftp-1.0.4 hipe-3.19.2 inets-7.1.2 jinterface-1.10.1 kernel-6.5.1 megaco-3.18.7 mnesia-4.16.2 observer-2.9.3 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7.1 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 snmp-5.4.5 ssh-4.8.2 stdlib-3.11.1 syntax_tools-2.2.1 tftp-1.0.2 tools-3.3 wx-1.9 xmerl-1.3.23 : +OTP-22.2.3 : compiler-7.5.1 ssl-9.5.2 # asn1-5.0.9 common_test-1.18.1 crypto-4.6.4 debugger-4.2.8 dialyzer-4.1.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.11 erl_interface-3.13.1 erts-10.6.2 et-1.6.4 eunit-2.4 ftp-1.0.4 hipe-3.19.2 inets-7.1.2 jinterface-1.10.1 kernel-6.5.1 megaco-3.18.7 mnesia-4.16.2 observer-2.9.3 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7.1 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 snmp-5.4.5 ssh-4.8.2 stdlib-3.11.1 syntax_tools-2.2.1 tftp-1.0.2 tools-3.3 wx-1.9 xmerl-1.3.23 : +OTP-22.2.2 : crypto-4.6.4 erts-10.6.2 ssh-4.8.2 stdlib-3.11.1 # asn1-5.0.9 common_test-1.18.1 compiler-7.5 debugger-4.2.8 dialyzer-4.1.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.11 erl_interface-3.13.1 et-1.6.4 eunit-2.4 ftp-1.0.4 hipe-3.19.2 inets-7.1.2 jinterface-1.10.1 kernel-6.5.1 megaco-3.18.7 mnesia-4.16.2 observer-2.9.3 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7.1 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 snmp-5.4.5 ssl-9.5.1 syntax_tools-2.2.1 tftp-1.0.2 tools-3.3 wx-1.9 xmerl-1.3.23 : OTP-22.2.1 : erts-10.6.1 snmp-5.4.5 ssl-9.5.1 # asn1-5.0.9 common_test-1.18.1 compiler-7.5 crypto-4.6.3 debugger-4.2.8 dialyzer-4.1.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.11 erl_interface-3.13.1 et-1.6.4 eunit-2.4 ftp-1.0.4 hipe-3.19.2 inets-7.1.2 jinterface-1.10.1 kernel-6.5.1 megaco-3.18.7 mnesia-4.16.2 observer-2.9.3 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7.1 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 ssh-4.8.1 stdlib-3.11 syntax_tools-2.2.1 tftp-1.0.2 tools-3.3 wx-1.9 xmerl-1.3.23 : OTP-22.2 : common_test-1.18.1 compiler-7.5 crypto-4.6.3 debugger-4.2.8 dialyzer-4.1.1 erl_docgen-0.11 erl_interface-3.13.1 erts-10.6 eunit-2.4 ftp-1.0.4 hipe-3.19.2 inets-7.1.2 kernel-6.5.1 megaco-3.18.7 mnesia-4.16.2 observer-2.9.3 public_key-1.7.1 snmp-5.4.4 ssh-4.8.1 ssl-9.5 stdlib-3.11 tftp-1.0.2 tools-3.3 wx-1.9 xmerl-1.3.23 # asn1-5.0.9 diameter-2.2.1 edoc-0.11 eldap-1.2.8 et-1.6.4 jinterface-1.10.1 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 syntax_tools-2.2.1 : +OTP-22.1.8.1 : snmp-5.4.3.1 # asn1-5.0.9 common_test-1.18 compiler-7.4.9 crypto-4.6.2 debugger-4.2.7 dialyzer-4.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.10 erl_interface-3.13 erts-10.5.6 et-1.6.4 eunit-2.3.8 ftp-1.0.3 hipe-3.19.1 inets-7.1.1 jinterface-1.10.1 kernel-6.5 megaco-3.18.6 mnesia-4.16.1 observer-2.9.2 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 ssh-4.8 ssl-9.4 stdlib-3.10 syntax_tools-2.2.1 tftp-1.0.1 tools-3.2.1 wx-1.8.9 xmerl-1.3.22 : OTP-22.1.8 : erts-10.5.6 # asn1-5.0.9 common_test-1.18 compiler-7.4.9 crypto-4.6.2 debugger-4.2.7 dialyzer-4.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.10 erl_interface-3.13 et-1.6.4 eunit-2.3.8 ftp-1.0.3 hipe-3.19.1 inets-7.1.1 jinterface-1.10.1 kernel-6.5 megaco-3.18.6 mnesia-4.16.1 observer-2.9.2 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 snmp-5.4.3 ssh-4.8 ssl-9.4 stdlib-3.10 syntax_tools-2.2.1 tftp-1.0.1 tools-3.2.1 wx-1.8.9 xmerl-1.3.22 : OTP-22.1.7 : compiler-7.4.9 erts-10.5.5 # asn1-5.0.9 common_test-1.18 crypto-4.6.2 debugger-4.2.7 dialyzer-4.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.10 erl_interface-3.13 et-1.6.4 eunit-2.3.8 ftp-1.0.3 hipe-3.19.1 inets-7.1.1 jinterface-1.10.1 kernel-6.5 megaco-3.18.6 mnesia-4.16.1 observer-2.9.2 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 snmp-5.4.3 ssh-4.8 ssl-9.4 stdlib-3.10 syntax_tools-2.2.1 tftp-1.0.1 tools-3.2.1 wx-1.8.9 xmerl-1.3.22 : OTP-22.1.6 : compiler-7.4.8 crypto-4.6.2 erts-10.5.4 snmp-5.4.3 # asn1-5.0.9 common_test-1.18 debugger-4.2.7 dialyzer-4.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.10 erl_interface-3.13 et-1.6.4 eunit-2.3.8 ftp-1.0.3 hipe-3.19.1 inets-7.1.1 jinterface-1.10.1 kernel-6.5 megaco-3.18.6 mnesia-4.16.1 observer-2.9.2 odbc-2.12.4 os_mon-2.5.1 parsetools-2.1.8 public_key-1.7 reltool-0.8 runtime_tools-1.14 sasl-3.4.1 ssh-4.8 ssl-9.4 stdlib-3.10 syntax_tools-2.2.1 tftp-1.0.1 tools-3.2.1 wx-1.8.9 xmerl-1.3.22 : @@ -17,6 +25,9 @@ OTP-22.0.3 : compiler-7.4.2 dialyzer-4.0.1 erts-10.4.2 ssl-9.3.2 stdlib-3.9.2 # OTP-22.0.2 : compiler-7.4.1 crypto-4.5.1 erts-10.4.1 stdlib-3.9.1 # asn1-5.0.9 common_test-1.17.3 debugger-4.2.7 dialyzer-4.0 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 ssl-9.3.1 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 : OTP-22.0.1 : ssl-9.3.1 # asn1-5.0.9 common_test-1.17.3 compiler-7.4 crypto-4.5 debugger-4.2.7 dialyzer-4.0 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 erts-10.4 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 stdlib-3.9 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 : OTP-22.0 : asn1-5.0.9 common_test-1.17.3 compiler-7.4 crypto-4.5 debugger-4.2.7 dialyzer-4.0 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 erts-10.4 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 ssl-9.3 stdlib-3.9 syntax_tools-2.2 tools-3.2 wx-1.8.8 xmerl-1.3.21 # diameter-2.2.1 et-1.6.4 eunit-2.3.7 ftp-1.0.2 parsetools-2.1.8 tftp-1.0.1 : +OTP-21.3.8.14 : erts-10.3.5.10 # asn1-5.0.8 common_test-1.17.2.1 compiler-7.3.2 crypto-4.4.2.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 ftp-1.0.2.2 hipe-3.18.3 inets-7.0.7.2 jinterface-1.9.1 kernel-6.3.1.3 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6.1 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6.3 ssl-9.2.3.5 stdlib-3.8.2.3 syntax_tools-2.1.7.1 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 : +OTP-21.3.8.13 : erts-10.3.5.9 stdlib-3.8.2.3 # asn1-5.0.8 common_test-1.17.2.1 compiler-7.3.2 crypto-4.4.2.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 ftp-1.0.2.2 hipe-3.18.3 inets-7.0.7.2 jinterface-1.9.1 kernel-6.3.1.3 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6.1 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6.3 ssl-9.2.3.5 syntax_tools-2.1.7.1 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 : +OTP-21.3.8.12 : crypto-4.4.2.2 erts-10.3.5.8 ssh-4.7.6.3 # asn1-5.0.8 common_test-1.17.2.1 compiler-7.3.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 ftp-1.0.2.2 hipe-3.18.3 inets-7.0.7.2 jinterface-1.9.1 kernel-6.3.1.3 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6.1 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssl-9.2.3.5 stdlib-3.8.2.2 syntax_tools-2.1.7.1 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 : OTP-21.3.8.11 : erts-10.3.5.7 ftp-1.0.2.2 # asn1-5.0.8 common_test-1.17.2.1 compiler-7.3.2 crypto-4.4.2.1 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 hipe-3.18.3 inets-7.0.7.2 jinterface-1.9.1 kernel-6.3.1.3 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6.1 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6.2 ssl-9.2.3.5 stdlib-3.8.2.2 syntax_tools-2.1.7.1 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 : OTP-21.3.8.10 : ftp-1.0.2.1 ssh-4.7.6.2 # asn1-5.0.8 common_test-1.17.2.1 compiler-7.3.2 crypto-4.4.2.1 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 erts-10.3.5.6 et-1.6.4 eunit-2.3.7 hipe-3.18.3 inets-7.0.7.2 jinterface-1.9.1 kernel-6.3.1.3 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6.1 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssl-9.2.3.5 stdlib-3.8.2.2 syntax_tools-2.1.7.1 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 : OTP-21.3.8.9 : inets-7.0.7.2 # asn1-5.0.8 common_test-1.17.2.1 compiler-7.3.2 crypto-4.4.2.1 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 erts-10.3.5.6 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 jinterface-1.9.1 kernel-6.3.1.3 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6.1 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6.1 ssl-9.2.3.5 stdlib-3.8.2.2 syntax_tools-2.1.7.1 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 : @@ -61,6 +72,8 @@ OTP-21.0.3 : erts-10.0.3 # asn1-5.0.6 common_test-1.16 compiler-7.2.2 crypto-4.3 OTP-21.0.2 : compiler-7.2.2 erts-10.0.2 public_key-1.6.1 stdlib-3.5.1 # asn1-5.0.6 common_test-1.16 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 megaco-3.18.3 mnesia-4.15.4 observer-2.8 odbc-2.12.1 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 snmp-5.2.11 ssh-4.7 ssl-9.0 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 : OTP-21.0.1 : compiler-7.2.1 erts-10.0.1 # asn1-5.0.6 common_test-1.16 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 megaco-3.18.3 mnesia-4.15.4 observer-2.8 odbc-2.12.1 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 public_key-1.6 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 snmp-5.2.11 ssh-4.7 ssl-9.0 stdlib-3.5 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 : OTP-21.0 : asn1-5.0.6 common_test-1.16 compiler-7.2 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 erts-10.0 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 mnesia-4.15.4 observer-2.8 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 public_key-1.6 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 ssh-4.7 ssl-9.0 stdlib-3.5 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 # megaco-3.18.3 odbc-2.12.1 snmp-5.2.11 : +OTP-20.3.8.26 : erts-9.3.3.15 ssh-4.6.9.7 # asn1-5.0.5.2 common_test-1.15.4.4 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.4 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4.1 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.2 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11.2 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.2 tools-2.11.2.2 wx-1.8.3 xmerl-1.3.16.1 : +OTP-20.3.8.25 : crypto-4.2.2.4 erts-9.3.3.14 ssh-4.6.9.6 # asn1-5.0.5.2 common_test-1.15.4.4 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4.1 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.2 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11.2 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.2 tools-2.11.2.2 wx-1.8.3 xmerl-1.3.16.1 : OTP-20.3.8.24 : common_test-1.15.4.4 erts-9.3.3.13 ssh-4.6.9.5 # asn1-5.0.5.2 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.3 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4.1 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.2 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11.2 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.2 tools-2.11.2.2 wx-1.8.3 xmerl-1.3.16.1 : OTP-20.3.8.23 : crypto-4.2.2.3 erts-9.3.3.12 snmp-5.2.11.2 syntax_tools-2.1.4.2 # asn1-5.0.5.2 common_test-1.15.4.3 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4.1 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.2 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 ssh-4.6.9.4 ssl-8.2.6.4 stdlib-3.4.5.1 tools-2.11.2.2 wx-1.8.3 xmerl-1.3.16.1 : OTP-20.3.8.22 : common_test-1.15.4.3 erts-9.3.3.11 tools-2.11.2.2 # asn1-5.0.5.2 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4.1 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.2 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11.1 ssh-4.6.9.4 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 wx-1.8.3 xmerl-1.3.16.1 : diff --git a/scripts/bundle-otp b/scripts/bundle-otp index aa1f166732..df82ff4fc5 100755 --- a/scripts/bundle-otp +++ b/scripts/bundle-otp @@ -6,8 +6,8 @@ if [ "$TRAVIS_PULL_REQUEST" = "false" -a "$TRAVIS_REPO_SLUG" != "erlang/otp" ]; exit 0 fi -OTP_META_FILE=$ERL_TOP/${TRAVIS_TAG}-bundle.txt -OTP_FILE=$ERL_TOP/${TRAVIS_TAG}-bundle.tar.gz +OTP_META_FILE=$ERL_TOP/${TRAVIS_TAG}.0-bundle.txt +OTP_FILE=$ERL_TOP/${TRAVIS_TAG}.0-bundle.tar.gz REPOSITORIES="otp,$TRAVIS_TAG corba,.*" diff --git a/system/doc/design_principles/distributed_applications.xml b/system/doc/design_principles/distributed_applications.xml index a1a0149eb5..62b7882f25 100644 --- a/system/doc/design_principles/distributed_applications.xml +++ b/system/doc/design_principles/distributed_applications.xml @@ -61,7 +61,7 @@ <list type="bulleted"> <item>Specifies where the application <c>Application = atom()</c> can execute.</item> - <item>><c>NodeDesc = [Node | {Node,...,Node}]</c> is a list of + <item><c>NodeDesc = [Node | {Node,...,Node}]</c> is a list of node names in priority order. The order between nodes in a tuple is undefined.</item> <item><c>Timeout = integer()</c> specifies how many milliseconds diff --git a/system/doc/design_principles/release_handling.xml b/system/doc/design_principles/release_handling.xml index 027a71c59f..0bcd7dc1b2 100644 --- a/system/doc/design_principles/release_handling.xml +++ b/system/doc/design_principles/release_handling.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2016</year> + <year>2003</year><year>2020</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -59,7 +59,7 @@ <p><em>Step 2</em>) The release is transferred to and installed at target environment. For information of how to install the first target system, see - <seealso marker="system_principles:create_target">System Principles</seealso>.</p> + <seealso marker="doc/system_principles:create_target">System Principles</seealso>.</p> <p><em>Step 3</em>) Modifications, for example, error corrections, are made to the code in the development environment.</p> <p><em>Step 4</em>) At some point, it is time to make a new version diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml index 06bffc72ee..3cfbd3ec22 100644 --- a/system/doc/design_principles/statem.xml +++ b/system/doc/design_principles/statem.xml @@ -183,6 +183,23 @@ State(S) x Event(E) -> Actions(A), State(S')</pre> handles the system messages, and calls the <em>callback module</em> with machine specific events. </p> + <p> + The <em>callback module</em> can be changed for a running server + using any of the + <seealso marker="#Transition Actions">transition actions</seealso> + <seealso marker="stdlib:gen_statem#type-action"><c>{change_callback_module, NewModule}</c></seealso>, + <seealso marker="stdlib:gen_statem#type-action"><c>{push_callback_module, NewModule}</c></seealso> or + <seealso marker="stdlib:gen_statem#type-action"><c>pop_callback_module</c></seealso>. + Note that this is a pretty esotheric thing to do... + The origin for this feature is a protocol that after + version negotiation branches off into quite different + state machines depending on the protocol version. + There <i>might</i> be other use cases. + <i>Beware</i> that the new callback module + completely replaces the previous behaviour module, + so all relevant callback functions has to handle + the state and data from the previous callback module. + </p> </section> <!-- =================================================================== --> @@ -216,8 +233,10 @@ State(S) x Event(E) -> Actions(A), State(S')</pre> </item> </taglist> <p> - The <em>callback mode</em> is selected at server start - and may be changed with a code upgrade/downgrade. + The <em>callback mode</em> is a property of + the <em>callback module</em> and is set at server start. + It may be changed due to a code upgrade/downgrade, + or when changing the <em>callback module</em>. </p> <p> See the section @@ -631,6 +650,57 @@ State(S) x Event(E) -> Actions(A), State(S')</pre> Generate the next event to handle, see section <seealso marker="#Inserted Events">Inserted Events</seealso>. </item> + <tag> + <seealso marker="stdlib:gen_statem#type-action"> + <c>{change_callback_module, NewModule}</c> + </seealso> + </tag> + <item> + Change the + <seealso marker="#Callback Module"> + <em>callback module</em> + </seealso> + for the running server. + This can be done during any <em>state transition</em>, + whether it is a <em>state change</em> or not, + but it can <i>not</i> be done from a + <seealso marker="#State Enter Calls"><em>state enter call</em></seealso>. + </item> + <tag> + <seealso marker="stdlib:gen_statem#type-action"> + <c>{push_callback_module, NewModule}</c> + </seealso> + </tag> + <item> + Push the current <em>callback module</em> + to the top of an internal stack of callback modules + and set the new + <seealso marker="#Callback Module"> + <em>callback module</em> + </seealso> + for the running server. + Otherwise like + <c>{change_callback_module, NewModule}</c> + above. + </item> + <tag> + <seealso marker="stdlib:gen_statem#type-action"> + <c>pop_callback_module</c> + </seealso> + </tag> + <item> + Pop the top module from + the internal stack of callback modules + and set it to be the new + <seealso marker="#Callback Module"> + <em>callback module</em> + </seealso> + for the running server. + If the stack is empty the server fails. + Otherwise like + <c>{change_callback_module, NewModule}</c> + above. + </item> </taglist> <p> For details, see the <c>gen_statem(3)</c> @@ -812,8 +882,10 @@ StateName(EventType, EventContent, Data) -> <seealso marker="#Transition Actions">State Transition Actions</seealso>. You may not change the state, <seealso marker="#Postponing Events">postpone</seealso> - this non-event, or - <seealso marker="#Inserted Events">insert any events</seealso>. + this non-event, + <seealso marker="#Inserted Events">insert any events</seealso>, + or change the + <seealso marker="#Callback Module"><em>callback module</em></seealso>. </p> <p> The first state that is entered diff --git a/system/doc/reference_manual/expressions.xml b/system/doc/reference_manual/expressions.xml index baa1269470..4277c21fdb 100644 --- a/system/doc/reference_manual/expressions.xml +++ b/system/doc/reference_manual/expressions.xml @@ -1414,9 +1414,11 @@ end</code> the patterns <c>Pattern</c> are sequentially matched against the result in the same way as for a <seealso marker="#case">case</seealso> expression, except that if - the matching fails, a <c>try_clause</c> run-time error occurs.</p> - <p>An exception occurring during the evaluation of <c>Body</c> is - not caught.</p> + the matching fails, a <c>try_clause</c> run-time error occurs instead of a + <c>case_clause</c>.</p> + <p>Only exceptions occurring during the evaluation of <c>Exprs</c> can be + caught by the <c>catch</c> section. Exceptions occurring in a <c>Body</c> + or due to a failed match are not caught.</p> <p>The <c>try</c> expression can also be augmented with an <c>after</c> section, intended to be used for cleanup with side effects:</p> |