commit:     2855289d40697769e513f2e2c3b919a91bb905fd
Author:     Jaco Kroon <jaco <AT> uls <DOT> co <DOT> za>
AuthorDate: Sat May 22 14:35:47 2021 +0000
Commit:     Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Sat May 22 15:09:45 2021 +0000
URL:        https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=2855289d

net-misc/asterisk: 13.38.2 revision bump to fix shutdown deadlock

This is a version bump to update the func_lock patch to a newer version
to incorporate the fixes/improvements from:

https://gerrit.asterisk.org/c/asterisk/+/15942 (CLI: locks show)
https://gerrit.asterisk.org/c/asterisk/+/15943 (unload memory corruption)
https://gerrit.asterisk.org/c/asterisk/+/15944 (error path ref counting)
https://gerrit.asterisk.org/c/asterisk/+/15945 (ast_module_ref usage)

Package-Manager: Portage-3.0.18, Repoman-3.0.2
Signed-off-by: Jaco Kroon <jaco <AT> uls.co.za>
Closes: https://github.com/gentoo/gentoo/pull/20929
Signed-off-by: Sam James <sam <AT> gentoo.org>

 net-misc/asterisk/asterisk-13.38.2-r3.ebuild       | 348 +++++++++++++++++
 .../asterisk-13.38.2-r3-func_lock-fix-races.patch  | 421 +++++++++++++++++++++
 2 files changed, 769 insertions(+)

diff --git a/net-misc/asterisk/asterisk-13.38.2-r3.ebuild 
b/net-misc/asterisk/asterisk-13.38.2-r3.ebuild
new file mode 100644
index 00000000000..c41ddbf6577
--- /dev/null
+++ b/net-misc/asterisk/asterisk-13.38.2-r3.ebuild
@@ -0,0 +1,348 @@
+# Copyright 1999-2021 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=7
+
+LUA_COMPAT=( lua5-{1..4} )
+
+inherit autotools linux-info lua-single systemd toolchain-funcs tmpfiles
+
+DESCRIPTION="Asterisk: A Modular Open Source PBX System"
+HOMEPAGE="https://www.asterisk.org/";
+SRC_URI="https://downloads.asterisk.org/pub/telephony/asterisk/releases/${P}.tar.gz";
+LICENSE="GPL-2"
+SLOT="0/${PV%%.*}"
+KEYWORDS="~amd64 ~arm ~arm64 ~ppc ~ppc64 ~x86"
+
+IUSE_VOICEMAIL_STORAGE=(
+       +voicemail_storage_file
+       voicemail_storage_odbc
+       voicemail_storage_imap
+)
+IUSE="${IUSE_VOICEMAIL_STORAGE[*]} alsa blocks bluetooth calendar +caps 
cluster curl dahdi debug doc freetds gtalk http iconv ilbc ldap lua mysql newt 
odbc oss pjproject portaudio postgres radius selinux snmp span speex srtp +ssl 
static statsd syslog systemd vorbis xmpp"
+REQUIRED_USE="gtalk? ( xmpp )
+       lua? ( ${LUA_REQUIRED_USE} )
+       ^^ ( ${IUSE_VOICEMAIL_STORAGE[*]//+/} )
+       voicemail_storage_odbc? ( odbc )
+"
+
+PATCHES=(
+       "${FILESDIR}/${PN}-historic-no-var-run-install.patch"
+       "${FILESDIR}/${PN}-13.38.1-r1-autoconf-lua-version.patch"
+       "${FILESDIR}/${PN}-13.38.2-r3-func_lock-fix-races.patch"
+       "${FILESDIR}/${PN}-13.18.1-r2-autoconf-2.70.patch"
+       "${FILESDIR}/${PN}-13.38.2-r1-menuselect-exitcodes.patch"
+       "${FILESDIR}/${PN}-13.38.2-r2-func_odbc_minargs-ARGC.patch"
+)
+
+DEPEND="acct-user/asterisk
+       acct-group/asterisk
+       dev-db/sqlite:3
+       dev-libs/popt
+       dev-libs/jansson
+       dev-libs/libedit
+       dev-libs/libxml2:2
+       dev-libs/libxslt
+       sys-libs/zlib
+       alsa? ( media-libs/alsa-lib )
+       bluetooth? ( net-wireless/bluez:= )
+       calendar? (
+               net-libs/neon:=
+               dev-libs/libical:=
+               dev-libs/iksemel
+       )
+       caps? ( sys-libs/libcap )
+       blocks? ( sys-libs/blocksruntime )
+       cluster? ( sys-cluster/corosync )
+       curl? ( net-misc/curl )
+       dahdi? (
+               net-libs/libpri
+               net-misc/dahdi-tools
+       )
+       freetds? ( dev-db/freetds )
+       gtalk? ( dev-libs/iksemel )
+       http? ( dev-libs/gmime:2.6 )
+       iconv? ( virtual/libiconv )
+       ilbc? ( media-libs/libilbc )
+       ldap? ( net-nds/openldap )
+       lua? ( ${LUA_DEPS} )
+       mysql? ( dev-db/mysql-connector-c:= )
+       newt? ( dev-libs/newt )
+       odbc? ( dev-db/unixODBC )
+       pjproject? ( net-libs/pjproject:= )
+       portaudio? ( media-libs/portaudio )
+       postgres? ( dev-db/postgresql:* )
+       radius? ( net-dialup/freeradius-client )
+       snmp? ( net-analyzer/net-snmp:= )
+       span? ( media-libs/spandsp )
+       speex? (
+               media-libs/speex
+               media-libs/speexdsp
+       )
+       srtp? ( net-libs/libsrtp:0 )
+       ssl? (
+               dev-libs/openssl:0=
+       )
+       systemd? ( sys-apps/systemd )
+       !systemd? ( !sys-apps/systemd )
+       vorbis? (
+               media-libs/libogg
+               media-libs/libvorbis
+       )
+       voicemail_storage_imap? ( virtual/imap-c-client )
+       xmpp? ( dev-libs/iksemel )
+"
+
+RDEPEND="${DEPEND}
+       net-misc/asterisk-core-sounds
+       net-misc/asterisk-extra-sounds
+       net-misc/asterisk-moh-opsound
+       selinux? ( sec-policy/selinux-asterisk )
+       syslog? ( virtual/logger )"
+
+BDEPEND="dev-libs/libxml2:2
+       virtual/pkgconfig"
+
+QA_DT_NEEDED="/usr/lib.*/libasteriskssl[.]so[.][0-9]\+"
+
+_make_args=(
+       "NOISY_BUILD=yes"
+       "ASTDBDIR=\$(ASTDATADIR)/astdb"
+       "OPTIMIZE="
+       "DEBUG="
+       "DESTDIR=${D}"
+       "CONFIG_SRC=configs/samples"
+       "CONFIG_EXTEN=.sample"
+)
+
+pkg_pretend() {
+       CONFIG_CHECK="~!NF_CONNTRACK_SIP"
+       local WARNING_NF_CONNTRACK_SIP="SIP (NAT) connection tracking is 
enabled. Some users
+       have reported that this module dropped critical SIP packets in their 
deployments. You
+       may want to disable it if you see such problems."
+       check_extra_config
+
+       [[ "${MERGE_TYPE}" == binary ]] && return
+
+       if tc-is-clang; then
+               use blocks || die "CC=clang requires USE=blocks"
+       else
+               use blocks && die "USE=blocks can only be used with CC=clang"
+       fi
+}
+
+pkg_setup() {
+       use lua && lua-single_pkg_setup
+}
+
+src_prepare() {
+       default
+       AT_M4DIR="autoconf third-party third-party/pjproject 
third-party/jansson" eautoreconf
+}
+
+src_configure() {
+       local vmst
+       local copt cstate
+
+       econf \
+               LUA_VERSION="${ELUA#lua}" \
+               --libdir="/usr/$(get_libdir)" \
+               --localstatedir="/var" \
+               --with-crypto \
+               --with-gsm=internal \
+               --with-popt \
+               --with-z \
+               --with-libedit \
+               $(use_with caps cap) \
+               $(use_with lua lua) \
+               $(use_with http gmime) \
+               $(use_with newt) \
+               $(use_with pjproject) \
+               $(use_with portaudio) \
+               $(use_with ssl)
+
+       _menuselect() {
+               menuselect/menuselect "$@" || die "menuselect $* failed."
+       }
+
+       _use_select() {
+               local state=$(usex "$1" enable disable)
+               shift # remove use from parameters
+
+               while [[ -n $1 ]]; do
+                       _menuselect --${state} "$1" menuselect.makeopts
+                       shift
+               done
+       }
+
+       # Blank out sounds/sounds.xml file to prevent
+       # asterisk from installing sounds files (we pull them in via
+       # asterisk-{core,extra}-sounds and asterisk-moh-opsound.
+       >"${S}"/sounds/sounds.xml
+
+       # That NATIVE_ARCH chatter really is quite bothersome
+       sed -i 's/NATIVE_ARCH=/NATIVE_ARCH=0/' build_tools/menuselect-deps || 
die "Unable to squelch noisy build system"
+
+       # Compile menuselect binary for optional components
+       emake "${_make_args[@]}" menuselect.makeopts
+
+       # Disable BUILD_NATIVE (bug #667498)
+       _menuselect --disable build_native menuselect.makeopts
+
+       # Broken functionality is forcibly disabled (bug #360143)
+       _menuselect --disable chan_misdn menuselect.makeopts
+       _menuselect --disable chan_ooh323 menuselect.makeopts
+
+       # Utility set is forcibly enabled (bug #358001)
+       _menuselect --enable smsq menuselect.makeopts
+       _menuselect --enable streamplayer menuselect.makeopts
+       _menuselect --enable aelparse menuselect.makeopts
+       _menuselect --enable astman menuselect.makeopts
+
+       # this is connected, otherwise it would not find
+       # ast_pktccops_gate_alloc symbol
+       _menuselect --enable chan_mgcp menuselect.makeopts
+       _menuselect --enable res_pktccops menuselect.makeopts
+
+       # SSL is forcibly enabled, IAX2 & DUNDI are expected to be available
+       _menuselect --enable pbx_dundi menuselect.makeopts
+       _menuselect --enable func_aes menuselect.makeopts
+       _menuselect --enable chan_iax2 menuselect.makeopts
+
+       # SQlite3 is now the main database backend, enable related features
+       _menuselect --enable cdr_sqlite3_custom menuselect.makeopts
+       _menuselect --enable cel_sqlite3_custom menuselect.makeopts
+
+       # The others are based on USE-flag settings
+       _use_select alsa         chan_alsa
+       _use_select bluetooth    chan_mobile
+       _use_select calendar     res_calendar 
res_calendar_{caldav,ews,exchange,icalendar}
+       _use_select cluster      res_corosync
+       _use_select curl         func_curl res_config_curl res_curl
+       _use_select dahdi        app_dahdiras app_meetme chan_dahdi codec_dahdi 
res_timing_dahdi
+       _use_select freetds      {cdr,cel}_tds
+       _use_select gtalk        chan_motif
+       _use_select http         res_http_post
+       _use_select iconv        func_iconv
+       _use_select ilbc         codec_ilbc format_ilbc
+       _use_select ldap         res_config_ldap
+       _use_select lua          pbx_lua
+       _use_select mysql        app_mysql cdr_mysql res_config_mysql
+       _use_select odbc         cdr_adaptive_odbc res_config_odbc 
{cdr,cel,res,func}_odbc
+       _use_select oss          chan_oss
+       _use_select postgres     {cdr,cel}_pgsql res_config_pgsql
+       _use_select radius       {cdr,cel}_radius
+       _use_select snmp         res_snmp
+       _use_select span         res_fax_spandsp
+       _use_select speex        {codec,func}_speex
+       _use_select srtp         res_srtp
+       _use_select statsd       res_statsd res_{endpoint,chan}_stats
+       _use_select syslog       cdr_syslog
+       _use_select vorbis       format_ogg_vorbis
+       _use_select xmpp         res_xmpp
+
+       # Voicemail storage ...
+       for vmst in "${IUSE_VOICEMAIL_STORAGE[@]}"; do
+               if use "${vmst#+}"; then
+                       _menuselect --enable "$(echo "${vmst##*_}" | tr 
'[:lower:]' '[:upper:]')_STORAGE" menuselect.makeopts
+               fi
+       done
+
+       if use debug; then
+               for o in DONT_OPTIMIZE DEBUG_FD_LEAKS MALLOC_DEBUG 
BETTER_BACKTRACES; do
+                       _menuselect --enable $o menuselect.makeopts
+               done
+       fi
+
+       if [[ -n "${GENTOO_ASTERISK_CUSTOM_MENUSELECT:+yes}" ]]; then
+               for copt in ${GENTOO_ASTERISK_CUSTOM_MENUSELECT}; do
+                       cstate=--enable
+                       [[ "${copt}" == -* ]] && cstate=--disable
+                       ebegin "Custom option ${copt#[-+]} ${cstate:2}d"
+                       _menuselect ${cstate} "${copt#[-+]}"
+                       eend $?
+               done
+       fi
+}
+
+src_compile() {
+       emake "${_make_args[@]}"
+}
+
+src_install() {
+       local d
+
+       dodir "/usr/$(get_libdir)/pkgconfig"
+       diropts -m 0750 -o root -g asterisk
+       dodir   /etc/asterisk
+
+       emake "${_make_args[@]}" install install-configs
+
+       fowners asterisk: /var/lib/asterisk/astdb
+
+       if use radius; then
+               insinto /etc/radiusclient/
+               doins contrib/dictionary.digium
+       fi
+
+       # keep directories
+       diropts -m 0750 -o asterisk -g root
+       keepdir 
/var/spool/asterisk/{system,tmp,meetme,monitor,dictate,voicemail,recording}
+       diropts -m 0750 -o asterisk -g asterisk
+       keepdir /var/log/asterisk/{cdr-csv,cdr-custom}
+
+       newinitd "${FILESDIR}"/initd-13.32.0-r1 asterisk
+       newconfd "${FILESDIR}"/confd-13.32.0 asterisk
+
+       systemd_dounit "${FILESDIR}"/asterisk.service
+       newtmpfiles "${FILESDIR}"/asterisk.tmpfiles2.conf asterisk.conf
+       systemd_install_serviced "${FILESDIR}"/asterisk.service.conf
+
+       # Reset diropts else dodoc uses it for doc installations.
+       diropts -m0755
+
+       # install the upgrade documentation
+       dodoc UPGRADE* BUGS CREDITS
+
+       # install extra documentation
+       use doc && dodoc doc/*.{txt,pdf}
+
+       # install logrotate snippet; bug #329281
+       #
+       insinto /etc/logrotate.d
+       newins "${FILESDIR}/1.6.2/asterisk.logrotate4" asterisk
+
+       # Asterisk installs a few folders that's empty by design,
+       # but still required.  This finds them, and marks them for
+       # portage.
+       while read d < <(find "${ED}"/var -type d -empty || die "Find 
failed."); do
+               keepdir "${d#${ED}}"
+       done
+}
+
+pkg_postinst() {
+       if [ -z "${REPLACING_VERSIONS}" ]; then
+               elog "Asterisk Wiki: https://wiki.asterisk.org/wiki/";
+               elog "Gentoo VoIP IRC Channel: #gentoo-voip @ irc.freenode.net"
+       elif [ "$(ver_cut 1 "${REPLACING_VERSIONS}")" != "$(ver_cut 1)" ]; then
+               elog "You are updating from Asterisk $(ver_cut 1 
"${REPLACING_VERSIONS}") upgrade document:"
+               elog 
"https://wiki.asterisk.org/wiki/display/AST/Upgrading+to+Asterisk+$(ver_cut 1)"
+               elog "Gentoo VoIP IRC Channel: #gentoo-voip @ irc.freenode.net"
+       fi
+
+       if [[ -n "${GENTOO_ASTERISK_CUSTOM_MENUSELECT:+yes}" ]]; then
+               ewarn "You are using GENTOO_ASTERISK_CUSTOM_MENUSELECT, this 
should only be used"
+               ewarn "for debugging, for anything else, please file a bug on 
https://bugs.gentoo.org";
+       fi
+
+       if [[ -f /var/lib/asterisk/astdb.sqlite3 ]]; then
+               ewarn "Default astdb location has changed from 
/var/lib/asterisk to /var/lib/asterisk/astdb"
+               ewarn "You still have a /var/lib/asterisk/astdb.sqlite file.  
You need to either set"
+               ewarn "astdbdir in /etc/asterisk/asterisk.conf to 
/var/lib/asterisk or follow these"
+               ewarn "steps to migrate:"
+               ewarn "1.  /etc/init.d/asterisk stop"
+               ewarn "2.  mv /var/lib/asterisk/astdb.sqlite 
/var/lib/asterisk/astdb/"
+               ewarn "3.  /etc/init.d/asterisk start"
+               ewarn "This update was done partly for security reasons so that 
/var/lib/asterisk can be root owned."
+       fi
+}

diff --git 
a/net-misc/asterisk/files/asterisk-13.38.2-r3-func_lock-fix-races.patch 
b/net-misc/asterisk/files/asterisk-13.38.2-r3-func_lock-fix-races.patch
new file mode 100644
index 00000000000..3d3c7b101d4
--- /dev/null
+++ b/net-misc/asterisk/files/asterisk-13.38.2-r3-func_lock-fix-races.patch
@@ -0,0 +1,421 @@
+From b35211fd58afcf430a0d95a243dc7a086d72b2b8 Mon Sep 17 00:00:00 2001
+From: Jaco Kroon <j...@uls.co.za>
+Date: Fri, 21 May 2021 20:11:59 +0200
+Subject: [PATCH] Replacement patch for v13.
+
+Change-Id: I30236d7d7229f317c681fb7c6d7944d6108acd08
+---
+ funcs/func_lock.c | 234 +++++++++++++++++++++++++---------------------
+ 1 file changed, 126 insertions(+), 108 deletions(-)
+
+diff --git a/funcs/func_lock.c b/funcs/func_lock.c
+index a006a574eb..c472504f52 100644
+--- a/funcs/func_lock.c
++++ b/funcs/func_lock.c
+@@ -44,6 +44,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ #include "asterisk/linkedlists.h"
+ #include "asterisk/astobj2.h"
+ #include "asterisk/utils.h"
++#include "asterisk/cli.h"
+ 
+ /*** DOCUMENTATION
+       <function name="LOCK" language="en_US">
+@@ -112,7 +113,6 @@ static AST_LIST_HEAD_STATIC(locklist, lock_frame);
+ static void lock_free(void *data);
+ static void lock_fixup(void *data, struct ast_channel *oldchan, struct 
ast_channel *newchan);
+ static int unloading = 0;
+-static pthread_t broker_tid = AST_PTHREADT_NULL;
+ 
+ static const struct ast_datastore_info lock_info = {
+       .type = "MUTEX",
+@@ -126,8 +126,8 @@ struct lock_frame {
+       ast_cond_t cond;
+       /*! count is needed so if a recursive mutex exits early, we know how 
many times to unlock it. */
+       unsigned int count;
+-      /*! Container of requesters for the named lock */
+-      struct ao2_container *requesters;
++      /*! Count of waiting of requesters for the named lock */
++      unsigned int requesters;
+       /*! who owns us */
+       struct ast_channel *owner;
+       /*! name of the lock */
+@@ -149,14 +149,19 @@ static void lock_free(void *data)
+       while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) {
+               /* Only unlock if we own the lock */
+               if (clframe->channel == clframe->lock_frame->owner) {
++                      ast_mutex_lock(&clframe->lock_frame->mutex);
+                       clframe->lock_frame->count = 0;
+                       clframe->lock_frame->owner = NULL;
++                      ast_cond_signal(&clframe->lock_frame->cond);
++                      ast_mutex_unlock(&clframe->lock_frame->mutex);
+               }
+               ast_free(clframe);
+       }
+       AST_LIST_UNLOCK(oldlist);
+       AST_LIST_HEAD_DESTROY(oldlist);
+       ast_free(oldlist);
++
++      ast_module_unref(ast_module_info->self);
+ }
+ 
+ static void lock_fixup(void *data, struct ast_channel *oldchan, struct 
ast_channel *newchan)
+@@ -175,54 +180,11 @@ static void lock_fixup(void *data, struct ast_channel 
*oldchan, struct ast_chann
+               if (clframe->lock_frame->owner == oldchan) {
+                       clframe->lock_frame->owner = newchan;
+               }
+-              /* We don't move requesters, because the thread stack is 
different */
+               clframe->channel = newchan;
+       }
+       AST_LIST_UNLOCK(list);
+ }
+ 
+-static void *lock_broker(void *unused)
+-{
+-      struct lock_frame *frame;
+-      struct timespec forever = { 1000000, 0 };
+-      for (;;) {
+-              int found_requester = 0;
+-
+-              /* Test for cancel outside of the lock */
+-              pthread_testcancel();
+-              AST_LIST_LOCK(&locklist);
+-
+-              AST_LIST_TRAVERSE(&locklist, frame, entries) {
+-                      if (ao2_container_count(frame->requesters)) {
+-                              found_requester++;
+-                              ast_mutex_lock(&frame->mutex);
+-                              if (!frame->owner) {
+-                                      ast_cond_signal(&frame->cond);
+-                              }
+-                              ast_mutex_unlock(&frame->mutex);
+-                      }
+-              }
+-
+-              AST_LIST_UNLOCK(&locklist);
+-              pthread_testcancel();
+-
+-              /* If there are no requesters, then wait for a signal */
+-              if (!found_requester) {
+-                      nanosleep(&forever, NULL);
+-              } else {
+-                      sched_yield();
+-              }
+-      }
+-      /* Not reached */
+-      return NULL;
+-}
+-
+-static int ast_channel_cmp_cb(void *obj, void *arg, int flags)
+-{
+-      struct ast_channel *chan = obj, *cmp_args = arg;
+-      return strcasecmp(ast_channel_name(chan), ast_channel_name(cmp_args)) ? 
0 : CMP_MATCH;
+-}
+-
+ static int get_lock(struct ast_channel *chan, char *lockname, int trylock)
+ {
+       struct ast_datastore *lock_store = ast_channel_datastore_find(chan, 
&lock_info, NULL);
+@@ -234,7 +196,12 @@ static int get_lock(struct ast_channel *chan, char 
*lockname, int trylock)
+       struct timeval now;
+ 
+       if (!lock_store) {
+-              ast_debug(1, "Channel %s has no lock datastore, so we're 
allocating one.\n", ast_channel_name(chan));
++              if (unloading) {
++                      ast_log(LOG_ERROR, "%sLOCK has no datastore and 
func_lock is unloading, failing.\n",
++                                      trylock ? "TRY" : "");
++                      return -1;
++              }
++
+               lock_store = ast_datastore_alloc(&lock_info, NULL);
+               if (!lock_store) {
+                       ast_log(LOG_ERROR, "Unable to allocate new datastore.  
No locks will be obtained.\n");
+@@ -253,6 +220,9 @@ static int get_lock(struct ast_channel *chan, char 
*lockname, int trylock)
+               lock_store->data = list;
+               AST_LIST_HEAD_INIT(list);
+               ast_channel_datastore_add(chan, lock_store);
++
++              /* We cannot unload until this channel has released the 
lock_store */
++              ast_module_ref(ast_module_info->self);
+       } else
+               list = lock_store->data;
+ 
+@@ -266,6 +236,9 @@ static int get_lock(struct ast_channel *chan, char 
*lockname, int trylock)
+ 
+       if (!current) {
+               if (unloading) {
++                      ast_log(LOG_ERROR,
++                              "Lock doesn't exist whilst unloading.  %sLOCK 
will fail.\n",
++                              trylock ? "TRY" : "");
+                       /* Don't bother */
+                       AST_LIST_UNLOCK(&locklist);
+                       return -1;
+@@ -292,17 +265,12 @@ static int get_lock(struct ast_channel *chan, char 
*lockname, int trylock)
+                       AST_LIST_UNLOCK(&locklist);
+                       return -1;
+               }
+-              current->requesters = 
ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
+-                      NULL, ast_channel_cmp_cb);
+-              if (!current->requesters) {
+-                      ast_mutex_destroy(&current->mutex);
+-                      ast_cond_destroy(&current->cond);
+-                      ast_free(current);
+-                      AST_LIST_UNLOCK(&locklist);
+-                      return -1;
+-              }
+               AST_LIST_INSERT_TAIL(&locklist, current, entries);
+       }
++      /* Add to requester list */
++      ast_mutex_lock(&current->mutex);
++      current->requesters++;
++      ast_mutex_unlock(&current->mutex);
+       AST_LIST_UNLOCK(&locklist);
+ 
+       /* Found lock or created one - now find or create the corresponding 
link in the channel */
+@@ -315,7 +283,13 @@ static int get_lock(struct ast_channel *chan, char 
*lockname, int trylock)
+ 
+       if (!clframe) {
+               if (unloading) {
++                      ast_log(LOG_ERROR,
++                              "Busy unloading.  %sLOCK will fail.\n",
++                              trylock ? "TRY" : "");
+                       /* Don't bother */
++                      ast_mutex_lock(&current->mutex);
++                      current->requesters--;
++                      ast_mutex_unlock(&current->mutex);
+                       AST_LIST_UNLOCK(list);
+                       return -1;
+               }
+@@ -324,6 +298,9 @@ static int get_lock(struct ast_channel *chan, char 
*lockname, int trylock)
+                       ast_log(LOG_ERROR,
+                               "Unable to allocate channel lock frame.  %sLOCK 
will fail.\n",
+                               trylock ? "TRY" : "");
++                      ast_mutex_lock(&current->mutex);
++                      current->requesters--;
++                      ast_mutex_unlock(&current->mutex);
+                       AST_LIST_UNLOCK(list);
+                       return -1;
+               }
+@@ -339,44 +316,44 @@ static int get_lock(struct ast_channel *chan, char 
*lockname, int trylock)
+        * the same amount, before we'll release this one.
+        */
+       if (current->owner == chan) {
++              /* We're not a requester, we already have it */
++              ast_mutex_lock(&current->mutex);
++              current->requesters--;
++              ast_mutex_unlock(&current->mutex);
+               current->count++;
+               return 0;
+       }
+ 
+-      /* Okay, we have both frames, so now we need to try to lock.
+-       *
+-       * Locking order: always lock locklist first.  We need the
+-       * locklist lock because the broker thread counts whether
+-       * there are requesters with the locklist lock held, and we
+-       * need to hold it, so that when we send our signal, below,
+-       * to wake up the broker thread, it definitely will see that
+-       * a requester exists at that point in time.  Otherwise, we
+-       * could add to the requesters after it has already seen that
+-       * that lock is unoccupied and wait forever for another signal.
+-       */
+-      AST_LIST_LOCK(&locklist);
+-      ast_mutex_lock(&current->mutex);
+-      /* Add to requester list */
+-      ao2_link(current->requesters, chan);
+-      pthread_kill(broker_tid, SIGURG);
+-      AST_LIST_UNLOCK(&locklist);
+-
+       /* Wait up to three seconds from now for LOCK. */
+       now = ast_tvnow();
+       timeout.tv_sec = now.tv_sec + 3;
+       timeout.tv_nsec = now.tv_usec * 1000;
+ 
+-      if (!current->owner
+-              || (!trylock
+-                      && !(res = ast_cond_timedwait(&current->cond, 
&current->mutex, &timeout)))) {
+-              res = 0;
++      ast_mutex_lock(&current->mutex);
++
++      res = 0;
++      while (!trylock && !res && current->owner) {
++              res = ast_cond_timedwait(&current->cond, &current->mutex, 
&timeout);
++      }
++      if (current->owner) {
++              ast_log(LOG_ERROR, "%sLOCK failed to obtain lock %s.\n", 
trylock ? "TRY" : "",
++                              lockname);
++              /* timeout;
++               * trylock; or
++               * cond_timedwait failed.
++               *
++               * either way, we fail to obtain the lock.
++               */
++              res = -1;
++      } else {
+               current->owner = chan;
+               current->count++;
+-      } else {
+-              res = -1;
++              res = 0;
+       }
+       /* Remove from requester list */
+-      ao2_unlink(current->requesters, chan);
++      current->requesters--;
++      if (res && unloading)
++              ast_cond_signal(&current->cond);
+       ast_mutex_unlock(&current->mutex);
+ 
+       return res;
+@@ -400,7 +377,7 @@ static int unlock_read(struct ast_channel *chan, const 
char *cmd, char *data, ch
+       }
+ 
+       if (!(list = lock_store->data)) {
+-              ast_debug(1, "This should NEVER happen\n");
++              ast_log(LOG_ERROR, "Datastore's data member is NULL ... this 
should be impossible.");
+               ast_copy_string(buf, "0", len);
+               return 0;
+       }
+@@ -419,12 +396,17 @@ static int unlock_read(struct ast_channel *chan, const 
char *cmd, char *data, ch
+ 
+       if (!clframe) {
+               /* We didn't have this lock in the first place */
++              ast_log(LOG_WARNING, "Attempting to UNLOCK(%s) - a lock this 
channel never owned.\n",
++                              data);
+               ast_copy_string(buf, "0", len);
+               return 0;
+       }
+ 
+       if (--clframe->lock_frame->count == 0) {
++              ast_mutex_lock(&clframe->lock_frame->mutex);
+               clframe->lock_frame->owner = NULL;
++              ast_cond_signal(&clframe->lock_frame->cond);
++              ast_mutex_unlock(&clframe->lock_frame->mutex);
+       }
+ 
+       ast_copy_string(buf, "1", len);
+@@ -455,6 +437,37 @@ static int trylock_read(struct ast_channel *chan, const 
char *cmd, char *data, c
+       return 0;
+ }
+ 
++static char *handle_cli_locks_show(struct ast_cli_entry *e, int cmd, struct 
ast_cli_args *a)
++{
++      int c = 0;
++      struct lock_frame* current;
++      switch (cmd) {
++      case CLI_INIT:
++              e->command = "locks show";
++              e->usage =
++                      "Usage: locks show\n"
++                      "       List all locks known to func_lock, along with 
their current status.\n";
++              return NULL;
++      case CLI_GENERATE:
++              return NULL;
++      }
++
++      ast_cli(a->fd, "func_lock locks:\n");
++      ast_cli(a->fd, "%-40s Requesters Owner\n", "Name");
++      AST_LIST_LOCK(&locklist);
++      AST_LIST_TRAVERSE(&locklist, current, entries) {
++              ast_mutex_lock(&current->mutex);
++              ast_cli(a->fd, "%-40s %-10d %s\n", current->name, 
current->requesters,
++                              current->owner ? 
ast_channel_name(current->owner) : "(unlocked)");
++              ast_mutex_unlock(&current->mutex);
++              c++;
++      }
++      AST_LIST_UNLOCK(&locklist);
++      ast_cli(a->fd, "%d total locks listed.\n", c);
++
++      return 0;
++}
++
+ static struct ast_custom_function lock_function = {
+       .name = "LOCK",
+       .read = lock_read,
+@@ -473,6 +486,8 @@ static struct ast_custom_function unlock_function = {
+       .read_max = 2,
+ };
+ 
++static struct ast_cli_entry cli_locks_show = 
AST_CLI_DEFINE(handle_cli_locks_show, "List func_lock locks.");
++
+ static int unload_module(void)
+ {
+       struct lock_frame *current;
+@@ -480,34 +495,43 @@ static int unload_module(void)
+       /* Module flag */
+       unloading = 1;
+ 
++      /* Make it impossible for new requesters to be added
++       * NOTE:  channels could already be in get_lock() */
++      ast_custom_function_unregister(&lock_function);
++      ast_custom_function_unregister(&trylock_function);
++
++      ast_cli_unregister(&cli_locks_show);
++
+       AST_LIST_LOCK(&locklist);
+       while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
+-              /* If any locks are currently in use, then we cannot unload 
this module */
+-              if (current->owner || ao2_container_count(current->requesters)) 
{
+-                      /* Put it back */
+-                      AST_LIST_INSERT_HEAD(&locklist, current, entries);
+-                      AST_LIST_UNLOCK(&locklist);
+-                      unloading = 0;
+-                      return -1;
++              int warned = 0;
++              ast_mutex_lock(&current->mutex);
++              while (current->owner || current->requesters) {
++                      if (!warned) {
++                              ast_log(LOG_WARNING, "Waiting for %d requesters 
for %s lock %s.\n",
++                                              current->requesters, 
current->owner ? "locked" : "unlocked",
++                                              current->name);
++                              warned = 1;
++                      }
++                      /* either the mutex is locked, or other parties are 
currently in get_lock,
++                       * we need to wait for all of those to clear first */
++                      ast_cond_wait(&current->cond, &current->mutex);
+               }
++              ast_mutex_unlock(&current->mutex);
++              /* At this point we know:
++               * 1. the lock has been released,
++               * 2. there are no requesters (nor should any be able to sneak 
in).
++               */
+               ast_mutex_destroy(&current->mutex);
+-              ao2_ref(current->requesters, -1);
++              ast_cond_destroy(&current->cond);
+               ast_free(current);
+       }
++      AST_LIST_UNLOCK(&locklist);
++      AST_LIST_HEAD_DESTROY(&locklist);
+ 
+-      /* No locks left, unregister functions */
+-      ast_custom_function_unregister(&lock_function);
+-      ast_custom_function_unregister(&trylock_function);
++      /* At this point we can safely stop access to UNLOCK */
+       ast_custom_function_unregister(&unlock_function);
+ 
+-      if (broker_tid != AST_PTHREADT_NULL) {
+-              pthread_cancel(broker_tid);
+-              pthread_kill(broker_tid, SIGURG);
+-              pthread_join(broker_tid, NULL);
+-      }
+-
+-      AST_LIST_UNLOCK(&locklist);
+-
+       return 0;
+ }
+ 
+@@ -516,13 +540,7 @@ static int load_module(void)
+       int res = ast_custom_function_register_escalating(&lock_function, 
AST_CFE_READ);
+       res |= ast_custom_function_register_escalating(&trylock_function, 
AST_CFE_READ);
+       res |= ast_custom_function_register_escalating(&unlock_function, 
AST_CFE_READ);
+-
+-      if (ast_pthread_create_background(&broker_tid, NULL, lock_broker, 
NULL)) {
+-              ast_log(LOG_ERROR, "Failed to start lock broker thread. 
Unloading func_lock module.\n");
+-              broker_tid = AST_PTHREADT_NULL;
+-              unload_module();
+-              return AST_MODULE_LOAD_DECLINE;
+-      }
++      res |= ast_cli_register(&cli_locks_show);
+ 
+       return res;
+ }
+-- 
+2.26.3
+

Reply via email to