transport dynamic modules
authorJeremy Harris <jgh146exb@wizmail.org>
Thu, 15 Aug 2024 13:53:22 +0000 (14:53 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Thu, 15 Aug 2024 13:53:22 +0000 (14:53 +0100)
21 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/NewStuff
src/scripts/Configure-Makefile
src/scripts/MakeLinks
src/scripts/drivers-Makefile [new file with mode: 0755]
src/scripts/routers-Makefile [deleted file]
src/src/EDITME
src/src/drtables.c
src/src/globals.h
src/src/route.c
src/src/routers/Makefile
src/src/transport.c
src/src/transports/Makefile
src/src/transports/appendfile.c
src/src/transports/autoreply.c
src/src/transports/lmtp.c
src/src/transports/pipe.c
src/src/transports/queuefile.c
src/src/transports/smtp.c
test/confs/0000
test/runtest

index 84597da830f56e7f0286787f13ce71607fcc0726..9fbf7a2db31bf9b29a21d872a97c5d9790928cc8 100644 (file)
@@ -2072,6 +2072,7 @@ withdrawn.
 .section "Dynamically loaded module support" "SECTdynamicmodules"
 .cindex "lookup modules"
 .cindex "router modules"
 .section "Dynamically loaded module support" "SECTdynamicmodules"
 .cindex "lookup modules"
 .cindex "router modules"
+.cindex "transport modules"
 .cindex "dynamic modules"
 .cindex ".so building"
 On some platforms, Exim supports not compiling all lookup types directly into
 .cindex "dynamic modules"
 .cindex ".so building"
 On some platforms, Exim supports not compiling all lookup types directly into
@@ -2083,7 +2084,7 @@ dependencies.
 Most, but not all, lookup types can be built this way.
 
 .new
 Most, but not all, lookup types can be built this way.
 
 .new
-Similarly, router drivers can be built as external modules.
+Similarly, router and transport drivers can be built as external modules.
 This permits a smaller exim binary, growing only as needed for the
 runtime cofiguration.
 .wen
 This permits a smaller exim binary, growing only as needed for the
 runtime cofiguration.
 .wen
index bb7f2290e201a4c4683873c354b7d5a43b18b3a4..be0f0c679643daa6745039c77ad88e437deaf59b 100644 (file)
@@ -14,8 +14,8 @@ Version 4.98
 
  3. Events smtp:fail:protocol and smtp:fail:syntax
 
 
  3. Events smtp:fail:protocol and smtp:fail:syntax
 
- 4. JSON lookup support, and all the router driver except manualroute,
-    can now be built as lodable modules
+ 4. JSON lookup support, all the router drivers except manualroute, and all the transport
+    drivers except smtp can now be built as lodable modules
 
 Version 4.98
 ------------
 
 Version 4.98
 ------------
index 3b0528940ff311a0f73e6d2f86b04830f409006e..20e686b51d84f6c058731d4740d321ee36564afb 100755 (executable)
@@ -10,6 +10,7 @@
 
 LC_ALL=C
 export LC_ALL
 
 LC_ALL=C
 export LC_ALL
+set -e
 
 
 # First off, get the OS type, and check that there is a make file for it.
 
 
 # First off, get the OS type, and check that there is a make file for it.
@@ -291,12 +292,16 @@ rm -f $mftt
 cp ../src/lookups/Makefile $look_mf_pre
 ../scripts/lookups-Makefile
 
 cp ../src/lookups/Makefile $look_mf_pre
 ../scripts/lookups-Makefile
 
-class="routers"
-cp ../src/$class/Makefile $class/Makefile.predynamic
-../scripts/$class-Makefile
-mv routers/Makefile.postdynamic $class/Makefile
-rm $class/Makefile.predynamic
-class=
+while read class classdef names
+do
+  cp ../src/$class/Makefile $class/Makefile.predynamic
+  CLASS=$class CLASSDEF=$classdef DRNAMES="$names" ../scripts/drivers-Makefile
+  mv $class/Makefile.postdynamic $class/Makefile
+  rm $class/Makefile.predynamic
+done <<-END
+ routers ROUTER                ACCEPT DNSLOOKUP IPLITERAL IPLOOKUP MANUALROUTE QUERYPROGRAM REDIRECT
+ transports TRANSPORT  APPENDFILE AUTOREPLY LMTP PIPE QUEUEFILE SMTP
+END
 
 # See if there is a definition of EXIM_PERL in what we have built so far.
 # If so, run Perl to find the default values for PERL_CC, PERL_CCOPTS,
 
 # See if there is a definition of EXIM_PERL in what we have built so far.
 # If so, run Perl to find the default values for PERL_CC, PERL_CCOPTS,
index f413c625edb11a1bd763a97089f372d4771d86e3..998b73bf9f7e199c5c97fb20a19125337bdb6ae9 100755 (executable)
@@ -63,7 +63,8 @@ cd ..
 d="transports"
 mkdir $d
 cd $d
 d="transports"
 mkdir $d
 cd $d
-for f in README Makefile appendfile.h appendfile.c autoreply.h \
+# Makefile is generated
+for f in README appendfile.h appendfile.c autoreply.h \
   autoreply.c lmtp.h lmtp.c pipe.h pipe.c queuefile.c queuefile.h \
   smtp.h smtp.c smtp_socks.c tf_maildir.c tf_maildir.h
 do
   autoreply.c lmtp.h lmtp.c pipe.h pipe.c queuefile.c queuefile.h \
   smtp.h smtp.c smtp_socks.c tf_maildir.c tf_maildir.h
 do
diff --git a/src/scripts/drivers-Makefile b/src/scripts/drivers-Makefile
new file mode 100755 (executable)
index 0000000..0c3da19
--- /dev/null
@@ -0,0 +1,176 @@
+#! /bin/sh
+
+# Copyright (c) The Exim Maintainers 1995 - 2024
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+set -e
+class=${CLASS:?}
+classdef=${CLASSDEF:?}
+drnames="${DRNAMES:?}"
+
+# We turn the configure-built build-$foo/$class/Makefile.predynamic into Makefile.
+# This is used for router and transport drivers, called from scripts/Configure-Makefile.
+
+# We always re-exec ourselves at least once, because it's the cleanest and
+# most portable way to turn on various features we expect of POSIX sh.
+if [ -z "$EXIM_DRIVER_MAKEFILE_ADJUSTED" ]
+then
+  SHELL=/bin/sh
+  EXIM_DRIVER_MAKEFILE_ADJUSTED=yes
+  export EXIM_DRIVER_MAKEFILE_ADJUSTED
+
+  # Solaris sh and tr are problematic until we get xpg4 variants
+  if [ -x /usr/xpg4/bin/sh ]
+  then
+    PATH="/usr/xpg4/bin:$PATH"
+    export PATH
+    SHELL=/usr/xpg4/bin/sh
+    export SHELL
+  fi
+
+  # IRIX uses /bin/ksh for sh but in a compatibility mode unless $_XPG == 1,
+  # where said compatibility mode disables $(...)
+  _XPG=1
+  export _XPG
+
+  # We need the _right_ tr, so must do that first; but if a shell which
+  # we're more confident is sane is available, let's try that.  Mostly,
+  # the problem is that "local" is not actually in "the" standard, it's
+  # just in every not-insane shell.  Though arguably, there are no shells
+  # with POSIX-ish syntax which qualify as "not insane".
+  for b in /bin/dash /bin/bash /usr/local/bin/bash
+  do
+    if [ -x "$b" ]
+    then
+      SHELL="$b"
+      break
+    fi
+  done
+  # if we get a report of a system with zsh but not bash, we can add that
+  # to the list, but be sure to enable sh_word_split in that case.
+
+  exec "$SHELL" "$0" "$@"
+fi
+
+input=$class/Makefile.predynamic
+target=$class/Makefile.postdynamic
+defs_source=Makefile-t
+tag_marker='MAGIC-TAG-MODS-OBJ-RULES-GO-HERE'
+
+tab='  '
+
+# We rely on tr(1) for translating case below.  Some people export
+# values of LC_CTYPE and LC_COLLATE which apparently break our assumptions.
+# We're a script expecting certain output based on known inputs and not dealing
+# with UTF8, so we should be safe doing this:
+LC_ALL=C
+export LC_ALL
+
+if [ -f "$defs_source" ]
+then
+  :
+  # we are happy
+else
+  echo >&2 "$0: ERROR: MISSING FILE '${defs_source}'"
+  echo >&2 "$0: SHOULD HAVE BEEN CALLED FROM scripts/Configure-Makefile"
+  exit 1
+fi
+
+# nb: do not permit leading whitespace for this, as CFLAGS_DYNAMIC is exported
+# to the lookups subdir via a line with leading whitespace which otherwise
+# matches
+if grep -q "^CFLAGS_DYNAMIC[ $tab?:]*=" "$defs_source"
+then
+  # we have a definition, we're good to go
+  echo >&2 ">>> Creating $class/Makefile for building dynamic modules"
+  enable_dynamic=yes
+else
+  echo >&2 ">>> Creating $class/Makefile without dynamic module support"
+  enable_dynamic=''
+  # We always do something now, since there should always be a lookup,
+  # and now we need to run in order to put the OBJ=$(OBJ)+ rules in.  So we
+  # continue on.
+fi
+
+# For the want_ checks, we need to let the user override values from the make
+# command-line, not just check the Makefile.
+
+want_dynamic() {
+  local dyn_name="$1"
+  local re="(${classdef}|EXPERIMENTAL)_${dyn_name}[ $tab]*=[ $tab]*2"
+  env | grep -E -q "^$re"
+  if [ $? -eq 0 ]; then return 0; fi
+  grep -E -q "^[ $tab]*$re" "$defs_source"
+}
+
+want_at_all() {
+  local want_name="$1"
+  local re="(${classdef}|EXPERIMENTAL)_${want_name}[ $tab]*=[ $tab]*."
+  env | grep -E -q "^$re"
+  if [ $? -eq 0 ]; then return 0; fi
+  grep -E -q "^[ $tab]*$re" "$defs_source"
+}
+
+# The values of these variables will be emitted into the Makefile.
+
+MODS=""
+OBJ=""
+
+emit_module_rule() {
+  local name="$1"
+  local mod_name pkgconf
+  if [ "${name%:*}" = "$name" ]
+  then
+    # Square brackets are redundant but benign for POSIX compliant tr,
+    # however Solaris /usr/bin/tr requires them. Sometimes Solaris
+    # gets installed without a complete set of xpg4 tools, sigh.
+    mod_name=$(echo $name | tr [A-Z] [a-z])
+  else
+    mod_name="${name#*:}"
+    name="${name%:*}"
+  fi
+
+  if want_dynamic "$name"
+  then
+    if [ -z "$enable_dynamic" ]; then
+      echo >&2 "Missing CFLAGS_DYNAMIC prevents building dynamic $name"
+      exit 1
+    fi
+    MODS="${MODS} ${mod_name}.so"
+#    pkgconf=$(grep "^${classdef}_${name}_PC" "$defs_source")
+#    if [ $? -eq 0 ]; then
+#      pkgconf=$(echo $pkgconf | sed 's/^.*= *//')
+#      echo "${classdef}_${mod_name}_INCLUDE = $(pkg-config --cflags $pkgconf)"
+#      echo "${classdef}_${mod_name}_LIBS = $(pkg-config --libs $pkgconf)"
+#    else
+#      grep "^${classdef}_${name}_" "$defs_source"
+#      echo "${classdef}_${mod_name}_INCLUDE = \$(${classdef}_${name}_INCLUDE)"
+#      echo "${classdef}_${mod_name}_LIBS = \$(${classdef}_${name}_LIBS)"
+#    fi
+  elif want_at_all "$name"
+  then
+    OBJ="${OBJ} ${mod_name}.o"
+  fi
+}
+
+rm -f "$target"
+exec 5>&1
+exec > "$target"
+
+sed -n "1,/$tag_marker/p" < "$input"
+
+for name_mod in $drnames
+do
+  emit_module_rule $name_mod
+done
+
+echo "MODS = $MODS"
+echo "OBJ = $OBJ"
+
+sed -n "/$tag_marker/,\$p" < "$input"
+
+exec >&5
+
+# Configure-Makefile will move $target into place
+
+# vim: set ft=sh sw=2 :
diff --git a/src/scripts/routers-Makefile b/src/scripts/routers-Makefile
deleted file mode 100755 (executable)
index 5bb6a78..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-#! /bin/sh
-
-# Copyright (c) The Exim Maintainers 1995 - 2021
-# SPDX-License-Identifier: GPL-2.0-or-later
-
-# We turn the configure-built build-$foo/routers/Makefile.predynamic into Makefile
-
-# We always re-exec ourselves at least once, because it's the cleanest and
-# most portable way to turn on various features we expect of POSIX sh.
-if [ -z "$EXIM_ROUTER_MAKEFILE_ADJUSTED" ]
-then
-  SHELL=/bin/sh
-  EXIM_ROUTER_MAKEFILE_ADJUSTED=yes
-  export EXIM_ROUTER_MAKEFILE_ADJUSTED
-
-  # Solaris sh and tr are problematic until we get xpg4 variants
-  if [ -x /usr/xpg4/bin/sh ]
-  then
-    PATH="/usr/xpg4/bin:$PATH"
-    export PATH
-    SHELL=/usr/xpg4/bin/sh
-    export SHELL
-  fi
-
-  # IRIX uses /bin/ksh for sh but in a compatibility mode unless $_XPG == 1,
-  # where said compatibility mode disables $(...)
-  _XPG=1
-  export _XPG
-
-  # We need the _right_ tr, so must do that first; but if a shell which
-  # we're more confident is sane is available, let's try that.  Mostly,
-  # the problem is that "local" is not actually in "the" standard, it's
-  # just in every not-insane shell.  Though arguably, there are no shells
-  # with POSIX-ish syntax which qualify as "not insane".
-  for b in /bin/dash /bin/bash /usr/local/bin/bash
-  do
-    if [ -x "$b" ]
-    then
-      SHELL="$b"
-      break
-    fi
-  done
-  # if we get a report of a system with zsh but not bash, we can add that
-  # to the list, but be sure to enable sh_word_split in that case.
-
-  exec "$SHELL" "$0" "$@"
-fi
-
-input=routers/Makefile.predynamic
-target=routers/Makefile.postdynamic
-defs_source=Makefile-t
-tag_marker='MAGIC-TAG-MODS-OBJ-RULES-GO-HERE'
-
-tab='  '
-
-# We rely on tr(1) for translating case below.  Some people export
-# values of LC_CTYPE and LC_COLLATE which apparently break our assumptions.
-# We're a script expecting certain output based on known inputs and not dealing
-# with UTF8, so we should be safe doing this:
-LC_ALL=C
-export LC_ALL
-
-if [ -f "$defs_source" ]
-then
-  :
-  # we are happy
-else
-  echo >&2 "$0: ERROR: MISSING FILE '${defs_source}'"
-  echo >&2 "$0: SHOULD HAVE BEEN CALLED FROM scripts/Configure-Makefile"
-  exit 1
-fi
-
-# nb: do not permit leading whitespace for this, as CFLAGS_DYNAMIC is exported
-# to the lookups subdir via a line with leading whitespace which otherwise
-# matches
-if grep -q "^CFLAGS_DYNAMIC[ $tab?:]*=" "$defs_source"
-then
-  # we have a definition, we're good to go
-  echo >&2 ">>> Creating routers/Makefile for building dynamic modules"
-  enable_dynamic=yes
-else
-  echo >&2 ">>> Creating routers/Makefile without dynamic module support"
-  enable_dynamic=''
-  # We always do something now, since there should always be a lookup,
-  # and now we need to run in order to put the OBJ=$(OBJ)+ rules in.  So we
-  # continue on.
-fi
-
-# For the want_ checks, we need to let the user override values from the make
-# command-line, not just check the Makefile.
-
-want_dynamic() {
-  local dyn_name="$1"
-  local re="ROUTER_${dyn_name}[ $tab]*=[ $tab]*2"
-  env | grep -q "^$re"
-  if [ $? -eq 0 ]; then return 0; fi
-  grep -q "^[ $tab]*$re" "$defs_source"
-}
-
-want_at_all() {
-  local want_name="$1"
-  local re="ROUTER_${want_name}[ $tab]*=[ $tab]*."
-  env | grep -q "^$re"
-  if [ $? -eq 0 ]; then return 0; fi
-  grep -q "^[ $tab]*$re" "$defs_source"
-}
-
-# Adapted want_at_all above to work for EXPERIMENTAL features
-want_experimental() {
-  local want_name="$1"
-  local re="EXPERIMENTAL_${want_name}[ $tab]*=[ $tab]*."
-  env | grep -q "^$re"
-  if [ $? -eq 0 ]; then return 0; fi
-  grep -q "^[ $tab]*$re" "$defs_source"
-}
-
-# The values of these variables will be emitted into the Makefile.
-
-MODS=""
-OBJ=""
-
-emit_module_rule() {
-  local name="$1"
-  local mod_name pkgconf
-  if [ "${name%:*}" = "$name" ]
-  then
-    # Square brackets are redundant but benign for POSIX compliant tr,
-    # however Solaris /usr/bin/tr requires them. Sometimes Solaris
-    # gets installed without a complete set of xpg4 tools, sigh.
-    mod_name=$(echo $name | tr [A-Z] [a-z])
-  else
-    mod_name="${name#*:}"
-    name="${name%:*}"
-  fi
-
-  if want_dynamic "$name"
-  then
-    if [ -z "$enable_dynamic" ]; then
-      echo >&2 "Missing CFLAGS_DYNAMIC prevents building dynamic $name"
-      exit 1
-    fi
-    MODS="${MODS} ${mod_name}.so"
-#    pkgconf=$(grep "^ROUTER_${name}_PC" "$defs_source")
-#    if [ $? -eq 0 ]; then
-#      pkgconf=$(echo $pkgconf | sed 's/^.*= *//')
-#      echo "ROUTER_${mod_name}_INCLUDE = $(pkg-config --cflags $pkgconf)"
-#      echo "ROUTER_${mod_name}_LIBS = $(pkg-config --libs $pkgconf)"
-#    else
-#      grep "^ROUTER_${name}_" "$defs_source"
-#      echo "ROUTER_${mod_name}_INCLUDE = \$(ROUTER_${name}_INCLUDE)"
-#      echo "ROUTER_${mod_name}_LIBS = \$(ROUTER_${name}_LIBS)"
-#    fi
-  elif want_at_all "$name"
-  then
-    OBJ="${OBJ} ${mod_name}.o"
-  fi
-}
-
-rm -f "$target"
-exec 5>&1
-exec > "$target"
-
-sed -n "1,/$tag_marker/p" < "$input"
-
-for name_mod in \
-    ACCEPT DNSLOOKUP IPLITERAL IPLOOKUP MANUALROUTE QUERYPROGRAM REDIRECT
-do
-  emit_module_rule $name_mod
-done
-
-echo "MODS = $MODS"
-echo "OBJ = $OBJ"
-
-sed -n "/$tag_marker/,\$p" < "$input"
-
-exec >&5
-
-# Configure-Makefile will move $target into place
-
-# vim: set ft=sh sw=2 :
index 5e755692fd5b878abf14b4b2e067a630ea6f6960..820793032de4fb087dc8ad3c41a167cfa5502b7c 100644 (file)
@@ -344,6 +344,15 @@ ROUTER_REDIRECT=yes
 # file. By commenting out those you know you don't want to use, you can make
 # the binary a bit smaller. If you are unsure, leave all of these included for
 # now.
 # file. By commenting out those you know you don't want to use, you can make
 # the binary a bit smaller. If you are unsure, leave all of these included for
 # now.
+#
+# If set to "2" instead of "yes" then the corresponding driver will be
+# built as a module and must be installed into LOOKUP_MODULE_DIR (the name
+# is historic).
+# You need to add -export-dynamic -rdynamic to EXTRALIBS. You may also need to
+# add -ldl to EXTRALIBS so that dlopen() is available to Exim. You need to
+# define CFLAGS_DYNAIC and LOOKUP_MODULE_DIR below so the builds are done right,
+# and so the exim binary actually loads dynamic lookup modules.
+# The smtp transport cannot be built as a module.
 
 TRANSPORT_APPENDFILE=yes
 TRANSPORT_AUTOREPLY=yes
 
 TRANSPORT_APPENDFILE=yes
 TRANSPORT_AUTOREPLY=yes
index b514090966b7f2843dbf9ae14052ad9c9af83d5e..026f2ff97b712b7d71b5a682747e155d57fffe56 100644 (file)
@@ -230,134 +230,11 @@ exim binary. */
 
 #include "routers/rf_functions.h"
 
 
 #include "routers/rf_functions.h"
 
-#ifdef TRANSPORT_APPENDFILE
-# include "transports/appendfile.h"
-#endif
-
-#ifdef TRANSPORT_AUTOREPLY
-# include "transports/autoreply.h"
-#endif
-
-#ifdef TRANSPORT_LMTP
-# include "transports/lmtp.h"
-#endif
-
-#ifdef TRANSPORT_PIPE
-# include "transports/pipe.h"
-#endif
-
-#ifdef EXPERIMENTAL_QUEUEFILE
-# include "transports/queuefile.h"
-#endif
-
-#ifdef TRANSPORT_SMTP
-# include "transports/smtp.h"
-#endif
-
 
 router_info * routers_available = NULL;
 
 router_info * routers_available = NULL;
+transport_info * transports_available = NULL;
 
 
 
 
-transport_info * transports_available_newlist = NULL;
-transport_info transports_available_oldarray[] = {
-#ifdef TRANSPORT_APPENDFILE
-  {
-  .drinfo = {
-    .driver_name =     US"appendfile",
-    .options =         appendfile_transport_options,
-    .options_count =   &appendfile_transport_options_count,
-    .options_block =   &appendfile_transport_option_defaults,       /* private options defaults */
-    .options_len =     sizeof(appendfile_transport_options_block),
-    .init =            appendfile_transport_init,
-    },
-  .code =              appendfile_transport_entry,
-  .tidyup =            NULL,
-  .closedown =         NULL,
-  .local =             TRUE
-  },
-#endif
-#ifdef TRANSPORT_AUTOREPLY
-  {
-  .drinfo = {
-    .driver_name =     US"autoreply",
-    .options =         autoreply_transport_options,
-    .options_count =   &autoreply_transport_options_count,
-    .options_block =   &autoreply_transport_option_defaults,
-    .options_len =     sizeof(autoreply_transport_options_block),
-    .init =            autoreply_transport_init,
-    },
-  .code =              autoreply_transport_entry,
-  .tidyup =            NULL,
-  .closedown =         NULL,
-  .local =             TRUE
-  },
-#endif
-#ifdef TRANSPORT_LMTP
-  {
-  .drinfo = {
-    .driver_name =     US"lmtp",
-    .options =         lmtp_transport_options,
-    .options_count =   &lmtp_transport_options_count,
-    .options_block =   &lmtp_transport_option_defaults,
-    .options_len =     sizeof(lmtp_transport_options_block),
-    .init =            lmtp_transport_init,
-    },
-  .code =              lmtp_transport_entry,
-  .tidyup =            NULL,
-  .closedown =         NULL,
-  .local =             TRUE
-  },
-#endif
-#ifdef TRANSPORT_PIPE
-  {
-  .drinfo = {
-    .driver_name =     US"pipe",
-    .options =         pipe_transport_options,
-    .options_count =   &pipe_transport_options_count,
-    .options_block =   &pipe_transport_option_defaults,
-    .options_len =     sizeof(pipe_transport_options_block),
-    .init =            pipe_transport_init,
-    },
-  .code =              pipe_transport_entry,
-  .tidyup =            NULL,
-  .closedown =         NULL,
-  .local =             TRUE
-  },
-#endif
-#ifdef EXPERIMENTAL_QUEUEFILE
-  {
-  .drinfo = {
-    .driver_name =     US"queuefile",
-    .options =         queuefile_transport_options,
-    .options_count =   &queuefile_transport_options_count,
-    .options_block =   &queuefile_transport_option_defaults,
-    .options_len =     sizeof(queuefile_transport_options_block),
-    .init =            queuefile_transport_init,
-    },
-  .code =              queuefile_transport_entry,
-  .tidyup =            NULL,
-  .closedown =         NULL,
-  .local =             TRUE
-  },
-#endif
-#ifdef TRANSPORT_SMTP
-  {
-  .drinfo = {
-    .driver_name =     US"smtp",
-    .options =         smtp_transport_options,
-    .options_count =   &smtp_transport_options_count,
-    .options_block =   &smtp_transport_option_defaults,
-    .options_len =     sizeof(smtp_transport_options_block),
-    .init =            smtp_transport_init,
-    },
-  .code =              smtp_transport_entry,
-  .tidyup =            NULL,
-  .closedown =         smtp_transport_closedown,
-  .local =             FALSE
-  },
-#endif
-  { .drinfo = { .driver_name = US"" }}
-};
 
 #ifndef MACRO_PREDEF
 
 
 #ifndef MACRO_PREDEF
 
@@ -429,35 +306,69 @@ return g;
 gstring *
 transport_show_supported(gstring * g)
 {
 gstring *
 transport_show_supported(gstring * g)
 {
-g = string_cat(g, US"Transports:");
-#ifdef TRANSPORT_APPENDFILE
-  g = string_cat(g, US" appendfile");
-  #ifdef SUPPORT_MAILDIR
-    g = string_cat(g, US"/maildir");   /* damn these subclasses */
-  #endif
-  #ifdef SUPPORT_MAILSTORE
-    g = string_cat(g, US"/mailstore");
-  #endif
-  #ifdef SUPPORT_MBX
-    g = string_cat(g, US"/mbx");
-  #endif
-#endif
-#ifdef TRANSPORT_AUTOREPLY
-  g = string_cat(g, US" autoreply");
-#endif
-#ifdef TRANSPORT_LMTP
-  g = string_cat(g, US" lmtp");
-#endif
-#ifdef TRANSPORT_PIPE
-  g = string_cat(g, US" pipe");
-#endif
-#ifdef EXPERIMENTAL_QUEUEFILE
-  g = string_cat(g, US" queuefile");
-#endif
-#ifdef TRANSPORT_SMTP
-  g = string_cat(g, US" smtp");
+uschar * b = US""              /* static-build transportnames */
+#if defined(TRANSPORT_APPENDFILE) && TRANSPORT_APPENDFILE!=2
+  " appendfile"
+ifdef SUPPORT_MAILDIR
+    "/maildir"
+endif
+ifdef SUPPORT_MAILSTORE
+    "/mailstore"
+endif
+ifdef SUPPORT_MBX
+    "/mbx"
+endif
+#endif
+#if defined(TRANSPORT_AUTOREPLY) && TRANSPORT_AUTOREPLY!=2
+  " autoreply"
+#endif
+#if defined(TRANSPORT_LMTP) && TRANSPORT_LMTP!=2
+  " lmtp"
+#endif
+#if defined(TRANSPORT_PIPE) && TRANSPORT_PIPE!=2
+  " pipe"
+#endif
+#if defined(EXPERIMENTAL_QUEUEFILE) && EXPERIMENTAL_QUEUEFILE!=2
+  " queuefile"
+#endif
+#if defined(TRANSPORT_SMTP) && TRANSPORT_SMTP!=2
+  " smtp"
 #endif
 #endif
-return string_cat(g, US"\n");
+  ;
+
+uschar * d = US""              /* dynamic-module transportnames */
+#if defined(TRANSPORT_APPENDFILE) && TRANSPORT_APPENDFILE==2
+  " appendfile"
+# ifdef SUPPORT_MAILDIR
+    "/maildir"
+# endif
+# ifdef SUPPORT_MAILSTORE
+    "/mailstore"
+# endif
+# ifdef SUPPORT_MBX
+    "/mbx"
+# endif
+#endif
+#if defined(TRANSPORT_AUTOREPLY) && TRANSPORT_AUTOREPLY==2
+  " autoreply"
+#endif
+#if defined(TRANSPORT_LMTP) && TRANSPORT_LMTP==2
+  " lmtp"
+#endif
+#if defined(TRANSPORT_PIPE) && TRANSPORT_PIPE==2
+  " pipe"
+#endif
+#if defined(EXPERIMENTAL_QUEUEFILE) && EXPERIMENTAL_QUEUEFILE==2
+  " queuefile"
+#endif
+#if defined(TRANSPORT_SMTP) && TRANSPORT_SMTP==2
+  " smtp"
+#endif
+  ;
+
+if (*b) g = string_fmt_append(g, "Transports (built-in):%s\n", b);
+if (*d) g = string_fmt_append(g, "Transports (dynamic): %s\n", d);
+return g;
 }
 
 
 }
 
 
index 6bca06c00999ade79137a7f6f424fbf5a7527bf9..7fc888f5baee4bf12cace4603418817eb5a1c5f2 100644 (file)
@@ -1111,10 +1111,9 @@ extern int     transport_newlines;     /* Accurate count of number of newline ch
 extern const uschar **transport_filter_argv; /* For on-the-fly filtering */
 extern int     transport_filter_timeout; /* Timeout for same */
 
 extern const uschar **transport_filter_argv; /* For on-the-fly filtering */
 extern int     transport_filter_timeout; /* Timeout for same */
 
-extern transport_info transports_available_oldarray[]; /* Vector of available transports */
-extern transport_info * transports_available_newlist;
-extern transport_instance *transports; /* Chain of instantiated transports */
-extern transport_instance transport_defaults; /* Default values */
+extern transport_info * transports_available;  /* Listof available transports */
+extern transport_instance *transports;         /* Chain of instantiated transports */
+extern transport_instance transport_defaults;  /* Default values */
 
 extern int     transport_write_timeout;/* Set to time out individual writes */
 
 
 extern int     transport_write_timeout;/* Set to time out individual writes */
 
index 07f0a6b20d9635e23f58d474398be9f011e2a420..293d21c50b7f2b4118fdc80e0e1be5129b35dfa4 100644 (file)
@@ -227,7 +227,6 @@ function. */
 void
 route_init(void)
 {
 void
 route_init(void)
 {
-
 int old_pool = store_pool;
 store_pool = POOL_PERM;
   {
 int old_pool = store_pool;
 store_pool = POOL_PERM;
   {
index d13a493e9d4f8b448f815242b9c9311f9615d2db..8ff3d81ab17ca7983aa89e2a052268dcc1c3dcf3 100644 (file)
@@ -7,7 +7,7 @@
 
 # nb: at build time, the version of this file used will have had some
 #     extra variable definitions and prepended to it and module build rules
 
 # nb: at build time, the version of this file used will have had some
 #     extra variable definitions and prepended to it and module build rules
-#     interpolated below. This is done by scripts/routers-Makefile.
+#     interpolated below. This is done by scripts/drivers-Makefile.
 
 # MAGIC-TAG-MODS-OBJ-RULES-GO-HERE
 
 
 # MAGIC-TAG-MODS-OBJ-RULES-GO-HERE
 
index e42625ce82e6b0051132e3cb01f9c3a18b5f6f97..063fda3617f60baed0b671cecec1812b20817d38 100644 (file)
@@ -102,8 +102,7 @@ uschar buf[64];
 
 options_from_list(optionlist_transports, nelem(optionlist_transports), US"TRANSPORTS", NULL);
 
 
 options_from_list(optionlist_transports, nelem(optionlist_transports), US"TRANSPORTS", NULL);
 
-//for (transport_info * ti= transports_available; ti->drinfo.driver_name[0]; ti++)
-for (driver_info * di= (driver_info *)transports_available_newlist; di; di = di->next)
+for (driver_info * di= (driver_info *)transports_available; di; di = di->next)
   {
   spf(buf, sizeof(buf), US"_DRIVER_TRANSPORT_%T", di->driver_name);
   builtin_macro_create(buf);
   {
   spf(buf, sizeof(buf), US"_DRIVER_TRANSPORT_%T", di->driver_name);
   builtin_macro_create(buf);
@@ -146,21 +145,51 @@ the work. */
 void
 transport_init(void)
 {
 void
 transport_init(void)
 {
-for (transport_info * tblent = transports_available_oldarray;
-    *tblent->drinfo.driver_name; tblent++)
+int old_pool = store_pool;
+store_pool = POOL_PERM;
   {
   {
-  driver_info * listent = store_get(sizeof(transport_info), tblent);
-  memcpy(listent, tblent, sizeof(transport_info));
-  listent->next = (driver_info *)transports_available_newlist;
-  transports_available_newlist = (transport_info *)listent;
+  driver_info ** anchor = (driver_info **) &transports_available;
+  extern transport_info appendfile_transport_info;
+  extern transport_info autoreply_transport_info;
+  extern transport_info lmtp_transport_info;
+  extern transport_info pipe_transport_info;
+  extern transport_info queuefile_transport_info;
+  extern transport_info smtp_transport_info;
+
+  /* Add the transport drivers that are built for static linkage to the
+  list of availables. */
+
+#if defined(TRANSPORT_APPENDFILE) && TRANSPORT_APPENDFILE!=2
+  add_driver_info(anchor, &appendfile_transport_info.drinfo, sizeof(transport_info));
+#endif
+#if defined(TRANSPORT_AUTOREPLY) && TRANSPORT_AUTOREPLY!=2
+  add_driver_info(anchor, &autoreply_transport_info.drinfo, sizeof(transport_info));
+#endif
+#if defined(TRANSPORT_LMTP) && TRANSPORT_LMTP!=2
+  add_driver_info(anchor, &lmtp_transport_info.drinfo, sizeof(transport_info));
+#endif
+#if defined(TRANSPORT_PIPE) && TRANSPORT_PIPE!=2
+  add_driver_info(anchor, &pipe_transport_info.drinfo, sizeof(transport_info));
+#endif
+#if defined(EXPERIMENTAL_QUEUEFILE) && EXPERIMENTAL_QUEUEFILE!=2
+  add_driver_info(anchor, &queuefile_transport_info.drinfo, sizeof(transport_info));
+#endif
+#if defined(TRANSPORT_SMTP) && TRANSPORT_SMTP!=2
+  add_driver_info(anchor, &smtp_transport_info.drinfo, sizeof(transport_info));
+#endif
   }
   }
+store_pool = old_pool;
+
+/* Read the config file "transports" section, creating a transportsinstance list.
+For any yet-undiscovered driver, check for a loadable module and add it to
+those available. */
 
 readconf_driver_init((driver_instance **)&transports,     /* chain anchor */
 
 readconf_driver_init((driver_instance **)&transports,     /* chain anchor */
-  (driver_info **)&transports_available_newlist,   /* available drivers */
-  sizeof(transport_info),                /* size of info block */
-  &transport_defaults,                   /* default values for generic options */
-  sizeof(transport_instance),            /* size of instance block */
-  optionlist_transports,                 /* generic options */
+  (driver_info **)&transports_available, /* available drivers */
+  sizeof(transport_info),              /* size of info block */
+  &transport_defaults,                 /* default values for generic options */
+  sizeof(transport_instance),          /* size of instance block */
+  optionlist_transports,               /* generic options */
   optionlist_transports_size,
   US"transport");
 
   optionlist_transports_size,
   US"transport");
 
index 4eea141ec9481a6d8d21adbacb7e8d3dcc19c326..8f759ecf010fba6da59fe2f69b13f5476633e80a 100644 (file)
@@ -1,8 +1,16 @@
 # Make file for building a library containing all the available transports and
 # calling it transports.a. This is called from the main make file, after cd'ing
 # to the transports subdirectory.
 # Make file for building a library containing all the available transports and
 # calling it transports.a. This is called from the main make file, after cd'ing
 # to the transports subdirectory.
+#
+# Copyright (c) The Exim Maintainers 2021 - 2024
 
 
-OBJ = appendfile.o autoreply.o lmtp.o pipe.o queuefile.o smtp.o smtp_socks.o tf_maildir.o
+# nb: at build time, the version of this file used will have had some
+#     extra variable definitions and prepended to it and module build rules
+#     interpolated below. This is done by scripts/drivers-Makefile.
+
+# MAGIC-TAG-MODS-OBJ-RULES-GO-HERE
+
+all:           transports.a $(MODS)
 
 transports.a:    $(OBJ)
                 @$(RM_COMMAND) -f transports.a
 
 transports.a:    $(OBJ)
                 @$(RM_COMMAND) -f transports.a
@@ -10,18 +18,40 @@ transports.a:    $(OBJ)
                 @$(AR) transports.a $(OBJ)
                 $(RANLIB) $@
 
                 @$(AR) transports.a $(OBJ)
                 $(RANLIB) $@
 
-.SUFFIXES:       .o .c
+.SUFFIXES:       .o .c .so
 .c.o:;           @echo "$(CC) $*.c"
                 $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) $*.c
 
 .c.o:;           @echo "$(CC) $*.c"
                 $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) $*.c
 
-appendfile.o:    $(HDRS) appendfile.c appendfile.h tf_maildir.h
-autoreply.o:     $(HDRS) autoreply.c autoreply.h
-lmtp.o:          $(HDRS) lmtp.c lmtp.h
-pipe.o:          $(HDRS) pipe.c pipe.h
-queuefile.o:     $(HDRS) queuefile.c queuefile.h
-smtp.o:          $(HDRS) smtp.c smtp.h
-smtp_socks.o:    $(HDRS) smtp_socks.c smtp.h
+.c.so:;          @echo "$(CC) -shared $*.c"
+                $(FE)$(CC) -DDYNLOOKUP $(CFLAGS_DYNAMIC) $(CFLAGS) $(INCLUDE) $(DLFLAGS) $*.c -o $@
+
+
+$(OBJ) $(MOD): $(HDRS)
+
+autoreply.o autoreply.so:      autoreply.c autoreply.h
+lmtp.o lmtp.so:                        lmtp.c lmtp.h
+pipe.o pipe.so:                        pipe.c pipe.h
+queuefile.o queuefile.so:      queuefile.c queuefile.h
+
+
+# These ones depend on more than one .c source
+
+appendfile.o:  appendfile.c appendfile.h tf_maildir.c tf_maildir.h
+       @echo "$(CC) appendfile.c tf_maildir.c"
+       $(FE)$(CC) $(CFLAGS) $(INCLUDE) appendfile.c tf_maildir.c -r -o $@
+
+appendfile.so: appendfile.c appendfile.h tf_maildir.c tf_maildir.h
+       @echo "$(CC) -shared appendfile.c tf_maildir.c"
+       $(FE)$(CC) -DDYNLOOKUP $(CFLAGS_DYNAMIC) $(CFLAGS) $(INCLUDE) $(DLFLAGS) \
+               appendfile.c tf_maildir.c -o $@
+
+smtp.o:        smtp.c smtp_socks.c smtp.h
+       @echo "$(CC) smtp.c smtp_socks.c"
+       $(FE)$(CC) $(CFLAGS) $(INCLUDE) smtp.c smtp_socks.c -r -o $@
 
 
-tf_maildir.o:    $(HDRS) tf_maildir.c tf_maildir.h appendfile.h
+smtp.so: smtp.c smtp_socks.c smtp.h
+       @echo "$(CC) -shared smtp.c smtp_socks.c"
+       $(FE)$(CC) -DDYNLOOKUP $(CFLAGS_DYNAMIC) $(CFLAGS) $(INCLUDE) $(DLFLAGS) \
+               smtp.c smtp_socks.c -o $@
 
 # End
 
 # End
index 14ef13c97d9c43488f33468b924717b0c4e33d30..08a59543c5bfcd96fd63103305c235b84f71114b 100644 (file)
@@ -3319,6 +3319,31 @@ ret_panic:
   return FALSE;
 }
 
   return FALSE;
 }
 
+
+
+
+# ifdef DYNLOOKUP
+#  define appendfile_transport_info _transport_info
+# endif
+
+transport_info appendfile_transport_info = {
+.drinfo = {
+  .driver_name =       US"appendfile",
+  .options =           appendfile_transport_options,
+  .options_count =     &appendfile_transport_options_count,
+  .options_block =     &appendfile_transport_option_defaults,  /* private options defaults */
+  .options_len =       sizeof(appendfile_transport_options_block),
+  .init =              appendfile_transport_init,
+# ifdef DYNLOOKUP
+  .dyn_magic =         TRANSPORT_MAGIC,
+# endif
+  },
+.code =                appendfile_transport_entry,
+.tidyup =      NULL,
+.closedown =   NULL,
+.local =       TRUE
+};
+
 #endif /*!MACRO_PREDEF*/
 #endif /*TRANSPORT_APPENDFILE*/
 /* End of transport/appendfile.c */
 #endif /*!MACRO_PREDEF*/
 #endif /*TRANSPORT_APPENDFILE*/
 /* End of transport/appendfile.c */
index a6080695e07333dd677c0c2cf1e0ee797178709f..9223bc791d4b8b1ea895e9bf0ef2fc8025d93648 100644 (file)
@@ -806,6 +806,31 @@ DEBUG(D_transport) debug_printf("%s transport succeeded\n", trname);
 return FALSE;
 }
 
 return FALSE;
 }
 
+
+
+
+# ifdef DYNLOOKUP
+#  define autoreply_transport_info _transport_info
+# endif
+
+transport_info autoreply_transport_info = {
+.drinfo = {
+  .driver_name =       US"autoreply",
+  .options =           autoreply_transport_options,
+  .options_count =     &autoreply_transport_options_count,
+  .options_block =     &autoreply_transport_option_defaults,
+  .options_len =       sizeof(autoreply_transport_options_block),
+  .init =              autoreply_transport_init,
+# ifdef DYNLOOKUP
+  .dyn_magic =         TRANSPORT_MAGIC,
+# endif
+  },
+.code =                autoreply_transport_entry,
+.tidyup =      NULL,
+.closedown =   NULL,
+.local =       TRUE
+};
+
 #endif /*!MACRO_PREDEF*/
 #endif /*TRANSPORT_AUTOREPOL*/
 /* End of transport/autoreply.c */
 #endif /*!MACRO_PREDEF*/
 #endif /*TRANSPORT_AUTOREPOL*/
 /* End of transport/autoreply.c */
index ae92952924b7855a636dc7034f7bdd36474fc638..1bff274c1ad64700cec3f83cad960fef8e172aca 100644 (file)
@@ -808,6 +808,31 @@ MINUS_N:
   return FALSE;
 }
 
   return FALSE;
 }
 
+
+
+
+# ifdef DYNLOOKUP
+#  define lmtp_transport_info _transport_info
+# endif
+
+transport_info lmtp_transport_info = {
+.drinfo = {
+  .driver_name =       US"lmtp",
+  .options =           lmtp_transport_options,
+  .options_count =     &lmtp_transport_options_count,
+  .options_block =     &lmtp_transport_option_defaults,
+  .options_len =       sizeof(lmtp_transport_options_block),
+  .init =              lmtp_transport_init,
+# ifdef DYNLOOKUP
+  .dyn_magic =         TRANSPORT_MAGIC,
+# endif
+  },
+.code =                lmtp_transport_entry,
+.tidyup =      NULL,
+.closedown =   NULL,
+.local =       TRUE
+};
+
 #endif /*!MACRO_PREDEF*/
 #endif /*TRANSPORT_LMTP*/
 /* End of transport/lmtp.c */
 #endif /*!MACRO_PREDEF*/
 #endif /*TRANSPORT_LMTP*/
 /* End of transport/lmtp.c */
index 1b90c43149288297342907ee26818e44557573b6..b765eeae5cdf1bc82ba3e0c19d5f7c4a24699752 100644 (file)
@@ -1128,6 +1128,31 @@ if (addr->transport_return != OK)
 return FALSE;
 }
 
 return FALSE;
 }
 
+
+
+
+# ifdef DYNLOOKUP
+#  define pipe_transport_info _transport_info
+# endif
+
+transport_info pipe_transport_info = {
+.drinfo = {
+  .driver_name =       US"pipe",
+  .options =           pipe_transport_options,
+  .options_count =     &pipe_transport_options_count,
+  .options_block =     &pipe_transport_option_defaults,
+  .options_len =       sizeof(pipe_transport_options_block),
+  .init =              pipe_transport_init,
+# ifdef DYNLOOKUP
+  .dyn_magic =         TRANSPORT_MAGIC,
+# endif
+  },
+.code =                pipe_transport_entry,
+.tidyup =      NULL,
+.closedown =   NULL,
+.local =       TRUE
+};
+
 #endif /*!MACRO_PREDEF*/
 #endif /*TRASPORT_PIPE*/
 /* End of transport/pipe.c */
 #endif /*!MACRO_PREDEF*/
 #endif /*TRASPORT_PIPE*/
 /* End of transport/pipe.c */
index 2c35b3145ef683c8f1abea6443e23709b9e27e66..64031e55db41701ebe8e26b4f41ded6a547c3027 100644 (file)
@@ -285,5 +285,30 @@ put in the first address of a batch. */
 return FALSE;
 }
 
 return FALSE;
 }
 
+
+
+
+# ifdef DYNLOOKUP
+#  define queuefile_transport_info _transport_info
+# endif
+
+transport_info queuefile_transport_info = {
+.drinfo = {
+  .driver_name =       US"queuefile",
+  .options =           queuefile_transport_options,
+  .options_count =     &queuefile_transport_options_count,
+  .options_block =     &queuefile_transport_option_defaults,
+  .options_len =       sizeof(queuefile_transport_options_block),
+  .init =              queuefile_transport_init,
+# ifdef DYNLOOKUP
+  .dyn_magic =         TRANSPORT_MAGIC,
+# endif
+  },
+.code =                queuefile_transport_entry,
+.tidyup =      NULL,
+.closedown =   NULL,
+.local =       TRUE
+};
+
 #endif /*!MACRO_PREDEF*/
 #endif /*EXPERIMENTAL_QUEUEFILE*/
 #endif /*!MACRO_PREDEF*/
 #endif /*EXPERIMENTAL_QUEUEFILE*/
index 25858d982117544e2bc4ef135688aa29781c235d..d25a2b1f64e292234040d609689cb6fb123cbca0 100644 (file)
@@ -6230,6 +6230,31 @@ DEBUG(D_transport) debug_printf("Leaving %s transport\n", trname);
 return TRUE;   /* Each address has its status */
 }
 
 return TRUE;   /* Each address has its status */
 }
 
+
+
+
+# ifdef DYNLOOKUP
+#  define smtp_transport_info _transport_info
+# endif
+
+transport_info smtp_transport_info = {
+.drinfo = {
+  .driver_name =       US"smtp",
+  .options =           smtp_transport_options,
+  .options_count =     &smtp_transport_options_count,
+  .options_block =     &smtp_transport_option_defaults,
+  .options_len =       sizeof(smtp_transport_options_block),
+  .init =              smtp_transport_init,
+# ifdef DYNLOOKUP
+  .dyn_magic =         TRANSPORT_MAGIC,
+# endif
+  },
+.code =                smtp_transport_entry,
+.tidyup =      NULL,
+.closedown =   smtp_transport_closedown,
+.local =       FALSE
+};
+
 #endif /*!MACRO_PREDEF*/
 /* vi: aw ai sw=2
 */
 #endif /*!MACRO_PREDEF*/
 /* vi: aw ai sw=2
 */
index 795701bd778dd58830e19d68f17581a1df379254..5db8ac8cfbeab242298742fe0be8c0a2701482b4 100644 (file)
@@ -11,5 +11,6 @@ tls_advertise_hosts =
 keep_environment =
 
 begin routers
 keep_environment =
 
 begin routers
+begin transports
 
 # End
 
 # End
index 2922335511654bc68864ddb9a7efe6c3e48d5375..6326723249c1da38e2a8b01c4deb44fc39bf5d83 100755 (executable)
@@ -1164,7 +1164,7 @@ RESET_AFTER_EXTRA_LINE_READ:
                  | Lookups(?:\(built-in\))?:
                  | Support\ for:
                  | Routers\ \((?:built-in|dynamic)\):
                  | Lookups(?:\(built-in\))?:
                  | Support\ for:
                  | Routers\ \((?:built-in|dynamic)\):
-                 | Transports:
+                 | Transports\ \((?:built-in|dynamic)\):
                  | Malware:
                  | log\ selectors\ =
                  | cwd=
                  | Malware:
                  | log\ selectors\ =
                  | cwd=
@@ -3754,7 +3754,7 @@ while (<EXIMINFO>)
     print;
     @temp = split /(\s+)/, $1;
     push(@temp, ' ');
     print;
     @temp = split /(\s+)/, $1;
     push(@temp, ' ');
-    my %temp_routers= @temp;
+    my %temp_routers = @temp;
     @parm_routers{keys %temp_routers} = values %temp_routers;
     }
 
     @parm_routers{keys %temp_routers} = values %temp_routers;
     }
 
@@ -3762,13 +3762,15 @@ while (<EXIMINFO>)
   # that the basic transport name is set, and then the name with each of the
   # options.
 
   # that the basic transport name is set, and then the name with each of the
   # options.
 
-  elsif (/^Transports: (.*)/)
+  elsif (/^Transports \((?:built-in|dynamic)\): (.*)/)
     {
     print;
     @temp = split /(\s+)/, $1;
     my($i,$k);
     push(@temp, ' ');
     {
     print;
     @temp = split /(\s+)/, $1;
     my($i,$k);
     push(@temp, ' ');
-    %parm_transports = @temp;
+    my %temp_transports = @temp;
+    @parm_transports{keys %temp_transports} = values %temp_transports;
+
     foreach $k (keys %parm_transports)
       {
       if ($k =~ "/")
     foreach $k (keys %parm_transports)
       {
       if ($k =~ "/")