#!/bin/sh #------------------------------------------------------------------------------ # /usr/share/circuits/circd __FLI4LVER__ # # This program, called the circuit daemon, is responsible for managing, # controlling, and monitoring the circuits on the fli4l router. Events are used # for the communication between various parts of the circuit system, including # dial/hangup scripts, ip-up/down scripts, fli4lctrl, and the circuit daemon. # # Last Update: $Id$ #------------------------------------------------------------------------------ # that's we mom_process=$(basename $0) # our MOM process ID mom_id=$mom_process # initialize MOM . /usr/share/mom/core # to make translate_* functions work . /etc/rc.cfg # include API circd uses (and circuit setup scripts can use) . /etc/boot.d/env.inc . /etc/boot.d/forking.inc . /etc/boot.d/exittrap.inc . /etc/boot.d/locking.inc . /etc/boot.d/networking.inc # include API circuit setup scripts may additionally use . /etc/boot.d/modules.inc # some circuit setup scripts need to load modules # redirect output exec >>/var/log/circd.log 2>&1 # initialize logging script="$(basename $0)[$$]" facility=$circuit_logfacility . /usr/share/logfunc.sh # set to a non-empty string when circd is shutting down; # this prevents circuit creation and activation messages from being processed circd_shutting_down= circd_shutting_down_requestors= circd_shutting_down_message="request ignored as circd is shutting down" ############ # PROCESSES ############ # running child processes circd_children=" " # ignore all signals except # - SIGTERM which calls circd_exit # - SIGUSR1 which calls circd_save_state exit_trap_ignore_all_signals exit_trap_install_for_signal TERM circd_exit exit_trap_install_for_signal USR1 circd_save_state # Notifies circd about a terminating child process. circd_notify_parent_about_child_exit() { mom_unicast_message circd child_exited_circd_event >/dev/null } # Sends a stop_service_message to circd. circd_exit() { log_warn "SIGTERM received" mom_unicast_message circd stop_service_message >/dev/null # return non-zero to prevent circd from being terminated via "exit" return 1 } # Checks whether circd can safely exit. This is possible (a) if no outstanding # child processes are still running and (b) if all circuits are either inactive # or failed. If circd can safely exit, mom_quit_message_loop() is called. circd_check_exit() { case $(circd_get_composite_state) in failed|inactive) [ -n "${circd_children# }" ] && return 0 [ -s "$daemons_file" ] && return 0 mom_quit_message_loop 0 ;; esac } ############## # PERSISTENCE ############## # path to circd's external state circd_state_file=/var/run/circuits/circd.state # Saves circd's internal state to $circd_state_file. This function is called at # exit time and when SIGUSR1 is received. circd_save_state() { cat < $circd_state_file circd_next_creation_group_id="$circd_next_creation_group_id" circd_creation_groups="$circd_creation_groups" circd_revdeps="$circd_revdeps" circd_activate="$circd_activate" circd_fail="$circd_fail" EOF local group for group in $circd_creation_groups do local name=circd_creation_group_$group eval local value=\$$name echo "$name=\"$value\"" >> $circd_state_file done local revdep for revdep in $circd_revdeps do local name=circd_revdeps_$(circd_make_varname $revdep) eval local value=\$$name echo "$name=\"$value\"" >> $circd_state_file done } # Restores circd's internal state from $circd_state_file. This function is # called at start time when $circd_state_file exists. circd_restore_state() { eval "$(cat $circd_state_file)" } ###################### # LIFETIME MANAGEMENT ###################### # next creation group ID circd_next_creation_group_id=1 # currently active creation groups circd_creation_groups=" " # all circuits/classes for which reverse dependencies are stored circd_revdeps=" " # circuits to activate when starting circd_activate= # circuits to fail when starting circd_fail= # Encodes a circuit identifier such that it can become part of a variable name. # Input: # $1 = circuit identifier # Output: # A suitably encoded identifier that can become part of a variable name. circd_make_varname() { local name=${1//-/_} echo ${name//:/__} } # Increments the reference counter for a circuit. After creation, the reference # counter is set to 1. # Input: # $1 = circuit identifier # Exit code: # always 0 # Synchronization: # Takes a read/write lock on the circuit. circd_ref() { local id=$1 circ_refcount circuit_resolve_alias id circuit_lock $id circd_ref circuit_read_field $id circ_refcount : ${circ_refcount:=0} circ_refcount=$((circ_refcount+1)) circuit_write_field $id circ_refcount $circ_refcount circuit_unlock $id circd_ref return 0 } # Decrements the reference counter for a circuit. After creation, the reference # counter is set to 1. If the counter reaches zero, the circuit is removed by # calling circd_circuit_remove(). # Input: # $1 = circuit identifier # Exit code: # always 0 # Synchronization: # Takes a read/write lock on the circuit. circd_unref() { local id=$1 circ_refcount circuit_resolve_alias id circuit_lock $id circd_unref circuit_read_field $id circ_refcount : ${circ_refcount:=1} circ_refcount=$((circ_refcount-1)) circuit_write_field $id circ_refcount $circ_refcount circuit_unlock $id circd_unref [ $circ_refcount -eq 0 ] && circd_circuit_remove $id return 0 } # Input: # $1 = circuit identifier circd_circuit_postprocessing() { local circ=$1 circ_deps circ_bundle circ_origin dep circuit_read_field $circ circ_deps circuit_read_field $circ circ_bundle circuit_read_field $circ circ_origin for dep in $circ_deps $circ_bundle $circ_origin do # remove potential layer-3 protocol dep=${dep%/*} circuit_resolve_alias dep local depid=$(circd_make_varname $dep) local revdepsname=circd_revdeps_$depid eval local revdeps=\$$revdepsname if [ -z "$revdeps" ] then # $depid points to a yet unused circuit class, not a circuit revdeps=" " circd_revdeps="$circd_revdeps$dep " fi revdeps="$revdeps$circ " eval $revdepsname=\"\$revdeps\" log_info "circd_circuit_postprocessing: adding reverse dependency $dep --> $circ" done } # Creates a new circuit. Note that no type-specific postprocessing is done, # this has to be done by the caller. # Input: # $1 = variable receiving the circuit identifier # $2 = variable receiving an error message (if any) # Output: # ${$2} = circuit identifier # Exit code: # 0 if successful, 1 otherwise (in the latter case, $2 is set to an error # message) # Synchronization: # Takes a read/write lock on the newly created circuit. circd_circuit_add() { local errvar=$2 # start numbering with 1, to have CIRC_X <--> circX circuit_allocate_device $circuit_prefix $1 1 eval local circ_id=\$$1 circuit_lock $circ_id circuit_add [ -n "$circ_bundle" ] && circuit_resolve_alias circ_bundle # circuit device local circ_dev= # dependencies local circ_deps=$circ_deps # auto (= use global dial mode unchanged) local circ_dialmode=${circ_dialmode:-auto} # no debug information local circ_debug=${circ_debug:-0} # circuit does not hang up automatically local circ_hup_timeout=${circ_hup_timeout:-0} # don't let the peer override DNS forwarders local circ_usepeerdns=${circ_usepeerdns:-no} # no charging local circ_chargeint=${circ_chargeint:-0} # no waiting local circ_wait=${circ_wait:-0} # assume existing interface local circ_newif=no # assume that a circuit controls only one interface; set to yes if the # circuit controls multiple interfaces, e.g. via circuit clones or via # special programs (e.g. DHCPv6 client or server) local circ_multi=no # the suffix to append to a multi-circuit's interface; "-" e.g. leads to # interface names like "ppp0-1", "ppp0-2" etc. if the interface configured # for the multi-circuit is "ppp0" local circ_multi_ifsuffix= # no initial interface alias local circ_alias= # no fd passing by default local circ_fdpass=${circ_fdpass:-no} # heuristic whether circuit is a server circuit (server circuits remain in # state "ready" after being dialled, so dependencies on server circuits # have to be handled slightly different) case $circ_type in *-server) local circ_server=${circ_server:-yes} ;; *) local circ_server=${circ_server:-no} ;; esac # map circ_debug=yes to circ_debug=1 and circ_debug=no to circ_debug=0 case $circ_debug in yes) circ_debug=1 ;; no) circ_debug=0 ;; esac local circuit_file=$circuit_state_dir/all/$circ_id cat > $circuit_file </dev/null 2>&1 then $func $circuit_file $errvar case $? in 0) used_protos="$used_protos $prot" ;; 1) echo "$circ_protocols" | grep -q "\<$prot\>" && used_protos="$used_protos $prot" ;; *) rm $circuit_file circuit_unlock $circ_id circuit_add return 1 ;; esac else echo "warning: $circ_id: layer-3 protocol '$prot' not enabled, ignoring part of its configuration" >&2 fi done circuit_write_field $circ_id circ_default_route $circ_default_route # initialise type and layer-3 specific parts local func="${circ_type//-/_}_circuit_add" for prot in ${circ_protocols:-$circuit_protocols} do local protfunc=${func}_$prot if type $protfunc >/dev/null 2>&1 then $protfunc $circuit_file $errvar case $? in 0) echo "$used_protos" | grep -q "\<$prot\>" || used_protos="$used_protos $prot" ;; 1) ;; *) rm $circuit_file circuit_unlock $circ_id circuit_add return 1 ;; esac fi done # now the set of layer-3 protocols is known if [ -z "$circ_protocols" ] then local circ_protocols=$used_protos fi circ_protocols=${circ_protocols# } circuit_write_field $circ_id circ_protocols "$circ_protocols" # initialise type specific but layer-3 generic parts # needs to set: # circ_dev # may override: # circ_newif # circ_alias # circ_hup_timeout # circ_deps (only extensions are permitted!) # circ_server # circ_multi # circ_multi_ifsuffix if ! type $func >/dev/null 2>&1 then eval $errvar="\"circuit setup function \$func is missing\"" rm $circuit_file circuit_unlock $circ_id circuit_add return 1 fi if ! $func $circuit_file $errvar then rm $circuit_file circuit_unlock $circ_id circuit_add return 1 fi if [ -z "$circ_dev" ] then rm $circuit_file eval $errvar="\"interface is missing\"" circuit_unlock $circ_id circuit_add return 2 else # add circuit dependency if interface is created by another circuit case $circ_dev in {*}) local dep=${circ_dev#\{} dep=${dep%\}} if circuit_exists $dep then circuit_read_field $dep circ_newif circuit_read_field $dep circ_multi circuit_read_field $dep circ_multi_ifsuffix for prot in $circ_protocols do local circ_deps="$circ_deps $dep/$prot" done else rm $circuit_file eval $errvar="\"circuit $dep referenced by interface $circ_dev does not exist\"" circuit_unlock $circ_id circuit_add return 4 fi ;; *) if [ "$circ_newif" != yes ] && ! translate_net_if "$circ_dev" circ_dev then rm $circuit_file eval $errvar="\"interface $circ_dev does not exist\"" circuit_unlock $circ_id circuit_add return 5 fi ;; esac fi if [ $circ_hup_timeout -gt 0 ] then if [ "$circ_newif" != yes ] then rm $circuit_file eval $errvar="\"dial-on-demand requires a circuit operating on a newly created interface\"" circuit_unlock $circ_id circuit_add return 3 fi fi cat >> $circuit_file <> $circuit_file done # add circuit aliases, used e.g. by fli4lctrl circuit_register_alias $circ_name $circ_id if [ -n "$circ_alias" ] then echo "circ_alias=$circ_alias" >> $circuit_file circuit_register_alias $circ_alias $circ_id fi # initially, a circuit is inactive and has a reference count of 1 ln -s ../all/$circ_id $circuit_state_dir/inactive/$circ_id circd_ref $circ_id # set initial local dial mode circd_set_local_dialmode $circ_id $circ_dialmode # attach circuit to its bundle if necessary if [ -n "$circ_bundle" ] then circd_attach_to_bundle $circ_id "$circ_bundle" fi eval circd_revdeps_$(circd_make_varname $circ_id)=\" \" circd_revdeps="$circd_revdeps$circ_id " # perform post-processing if not part of a creation group; # register circuit at its creation group otherwise case $circ_creation_group in '') circd_circuit_postprocessing $circ_id local pp_func=${circ_type//-/_}_post_processing if type $pp_func >/dev/null 2>&1 then $pp_func $circ_id fi ;; *) eval local circuits=\$circd_creation_group_${circ_creation_group} eval circd_creation_group_${circ_creation_group}=\"\$circuits\$circ_id \" echo "circ_creation_group=\"$circ_creation_group\"" >> $circuit_file ;; esac if [ ! -f "$when_online_file.fixed" -a "$circ_default_route" = yes ] then echo "$circ_id" >> $when_online_file fi circuit_unlock $circ_id circuit_add circd_start_queue $circ_id if [ "$circ_up" = yes ] then circd_circuit_activate $circ_id fi return 0 } # Deletes a circuit. This function may only be called by circd_unref() # and requires the circuit to be in the state 'deleted'. # Input: # $1 = circuit identifier # Exit code: # 0 if successful, 1 otherwise # Synchronization: # Takes a read/write lock on the circuit. circd_circuit_remove() { local id=$1 circ_name circ_type circ_dev circ_alias circ_bundle \ circ_origin circ_protocols circ_creation_group circ_deps \ circ_class_n circ_class circuit_resolve_alias id [ -n "$id" ] || return 1 circd_stop_queue $id circuit_lock $id circd_circuit_remove circuit_read_field $id circ_name circuit_read_field $id circ_type circuit_read_field $id circ_dev circuit_read_field $id circ_alias circuit_read_field $id circ_bundle circuit_read_field $id circ_origin circuit_read_field $id circ_protocols circuit_read_field $id circ_creation_group circuit_read_field $id circ_deps circuit_read_field $id circ_class_n local prot func="${circ_type//-/_}_circuit_remove" # finalize type and layer-3 specific parts for prot in $circ_protocols do local protfunc=${func}_$prot if type $protfunc >/dev/null 2>&1 then $protfunc $id fi done # finalise type specific but layer-3 generic parts if type $func >/dev/null 2>&1 then $func $id fi # finalize type generic but layer-3 specific parts for prot in $circ_protocols do local func=circuit_remove_$prot if type $func >/dev/null 2>&1 then $func $id fi done # detach circuit from its bundle if necessary circd_detach_from_bundle $id # remove local dialmode and state entry circd_set_local_dialmode $id "" rm $circuit_state_dir/deleted/$id # remove circuit aliases circuit_deregister_alias $circ_name [ -n "$circ_alias" ] && circuit_deregister_alias $circ_alias # remove circuit file rm $circuit_state_dir/all/$id # take circuit from its creation group if necessary if [ -n "$circ_creation_group" ] then eval local circuits=\$circd_creation_group_${circ_creation_group} circuits=${circuits// $id / } eval circd_creation_group_${circ_creation_group}=\"\$circuits\" fi # remove circuit from circuit classes local i for i in $(seq $i $circ_class_n) do circuit_read_field $id circ_class_$i circ_class circuit_remove_from_class $id $circ_class done # remove circuit from reverse dependency lists circd_revdeps=${circd_revdeps/ $id / } unset circd_revdeps_$(circd_make_varname $id) for dep in $circ_deps $circ_bundle $circ_origin do # remove potential layer-3 protocol dep=${dep%/*} circuit_resolve_alias dep local depid=$(circd_make_varname $dep) local revdepsname=circd_revdeps_$depid eval local revdeps=\$$revdepsname revdeps=${revdeps/ $id / } eval $revdepsname=\"\$revdeps\" log_info "circd_circuit_remove: removing reverse dependency $dep --> $id" done circuit_unlock $id circd_circuit_remove [ -n "$circd_shutting_down" ] && circd_check_exit } # Starts a creation group. All circuits that are associated with a creation # group have their post-processing deferred to the moment when the creation # group ends. This is necessary e.g. for MRRU computation and firewall rule # installation where forward or circular references are possible. # # Input: # $1 = name of variable receiving the identifier of the new creation group # Output: # ${$1} = identifier of a new creation group circd_start_creation_group() { circd_creation_groups="$circd_creation_groups$circd_next_creation_group_id " eval circd_creation_group_${circd_next_creation_group_id}=\" \" eval $1=\$circd_next_creation_group_id circd_next_creation_group_id=$((circd_next_creation_group_id+1)) } # Ends a creation group. Post-processing is performed for all circuits that # belong to this creation group. # # Input: # $1 = creation group identifier circd_end_creation_group() { local group=$1 circ circ_type pp_func eval local circuits=\$circd_creation_group_${group} for circ in $circuits do circd_circuit_postprocessing $circ circuit_read_field $circ circ_type pp_func="${circ_type//-/_}_post_processing" if type $pp_func >/dev/null 2>&1 then $pp_func $circ fi circuit_write_field $circ circ_creation_group "" done circd_creation_groups=${circd_creation_groups// $group / } } # Clones a multi-instance circuit. If this circuit is part of a bundle, # the bundle is also cloned, providing a unique bundle for the new instance. # This requires the circuit to be cloned to have circ_multi=yes. # # The new circuit is an identical clone of the old one with the following # exceptions: # - circ_origin is set to the old circuit # - circ_name is set to the old circ_name, suffixed by ":" and a number which # makes the name unique # - circ_up and circ_dialmode are set to "yes" and "auto", respectively # # Input: # $1 = circuit identifier # $2 = name of a function that may adapt the settings of the circuit before # it is cloned # - its most important task is to change $circ_type # - if empty, "-instance" is simply appended to the circuit type # - note that the variables mentioned above are already changed when # this function is called # $3 = user-defined value passed as first argument to the adaptor function # specified in $2 # $4 = variable receiving the circuit identifier of the new instance # $5 = variable receiving an error message (if any) # Output: # ${$4} = circuit identifier # Exit code: # 0 if successful # 1 if circ_multi is not "yes" for the circuit to be cloned # 2 if the associated bundle circuit could not be cloned (can only happen to # circuits which are part of a bundle) # 3 if the circuit itself could not be cloned # In case of any failure, $5 is set to an error message. # Synchronization: # Takes a read lock on the circuit to be cloned. circd_clone() { local _id=$1 _adaptor=$2 _adaptor_arg=$3 _res=$4 _errvar=$5 circuit_resolve_alias _id local _circ_multi circuit_read_field $_id circ_multi _circ_multi if [ "$_circ_multi" != yes ] then eval $_errvar="\"circuit $_id to be cloned is not a multi-instance circuit\"" return 1 fi local _bundle_id _circ_id _vars _varnames circuit_read_field $_id circ_bundle _bundle_id if [ -n "$_bundle_id" ] then # first clone bundle circuit _vars=$(circuit_get_data $_bundle_id) _varnames=$(echo "$_vars" | extract_variable_names) local $_varnames eval "$_vars" # this is a clone of another bundle circuit local circ_origin=$_bundle_id # change circuit name to make the circuit unique circuit_allocate_device $circ_name: circ_name 1 # start circuit automatically local circ_up=yes local circ_dialmode=auto circd_circuit_add _circ_id $_errvar || return 2 # the circuit to be cloned needs to reference the bundle circuit we # have just created above _bundle_id=$_circ_id unset $_varnames fi local _vars=$(circuit_get_data $_id) local _varnames=$(echo "$_vars" | extract_variable_names) local $_varnames eval "$_vars" # this is a clone of another circuit local circ_origin=$_id local circ_bundle=$_bundle_id # change circuit name to make the circuit unique circuit_allocate_device $circ_name: circ_name 1 # start circuit automatically local circ_up=yes local circ_dialmode=auto # adapt settings above, especially the circuit type! if [ -n "$_adaptor" ] then eval \$_adaptor "$_adaptor_arg" else circ_type="${circ_type}-instance" fi if ! circd_circuit_add _circ_id $_errvar then if [ -n "$_bundle_id" ] then circuit_event_id=$_bundle_id \ down_link_circuit_event_force_hangup= \ mom_unicast_message \ circd \ down_link_circuit_event >/dev/null fi return 3 fi eval $_res=\$_circ_id return 0 } # Attaches a circuit to a bundle. This is necessary for servers where bundle # circuits have to be created on-the-fly. # Input: # $1 = circuit identifier # $2 = bundle circuit identifier # Exit code: # 0 if successful, 1 otherwise # Synchronization: # Takes a read/write lock on the circuit to be attached. circd_attach_to_bundle() { local idchild=$1 idbundle=$2 circ_bundle circuit_resolve_alias idbundle circuit_lock $idchild circd_attach_to_bundle circuit_read_field $idchild circ_bundle if [ -n "$circ_bundle" ] then if [ "$circ_bundle" = "$idbundle" ] then # no-op, attaching to bundle the circuit is already part of circuit_unlock $idchild circd_attach_to_bundle return 0 else # detach from old circuit circd_detach_from_bundle $idchild fi fi circuit_write_field $idchild circ_bundle $idbundle circuit_unlock $idchild circd_attach_to_bundle sync_lock_resource bundle_db circd_attach_to_bundle echo "$idbundle $idchild" >> $links_file sync_unlock_resource bundle_db circd_attach_to_bundle return 0 } # Detaches a circuit from its bundle. This is necessary for servers where bundle # circuits have to be created (and destroyed) on-the-fly. # Input: # $1 = circuit identifier # Exit code: # 0 if successful, 1 otherwise # Synchronization: # Takes a read/write lock on the circuit to be detached. circd_detach_from_bundle() { local idchild=$1 circ_bundle circuit_lock $idchild circd_detach_from_bundle circuit_read_field $idchild circ_bundle if [ -z "$circ_bundle" ] then circuit_unlock $idchild circd_detach_from_bundle return 1 fi circuit_write_field $idchild circ_bundle "" circuit_unlock $idchild circd_detach_from_bundle sync_lock_resource bundle_db circd_detach_from_bundle sed -i "/^$circ_bundle $idchild$/d" $links_file sync_unlock_resource bundle_db circd_detach_from_bundle # handles the case when a bundle circuit has been created on the server # side which never went online (e.g. because its very first link was never # established successfully); in order to prevent it from lingering around # in the 'ready' state we explicitly hang it up if circuit_exists $circ_bundle && [ -z "$(circuit_get_bundle_links $circ_bundle)" ] then circd_finalize_hangup $circ_bundle fi return 0 } # Starts a queue process serializing calls to the circuit control script of # some circuit. # # Input: # $1 = circuit identifier circd_start_queue() { local circ_id="$1" rm -f /var/run/circuits/$circ_id.queue { mom_id="$circ_id:q" mom_process="$mom_id" fork_call_handlers exit_trap_ignore_all_signals # otherwise MOM communication does not work in child processes! exit_trap_install_for_signal TERM false exit_trap_add circd_notify_parent_about_child_exit script="$mom_id[$PID]" echo -n "$mom_id" > /proc/self/task/$PID/comm mom_register_handler handle_dialup_circuit_queue_message dialup_circuit_queue_message mom_register_handler handle_hangup_circuit_queue_message hangup_circuit_queue_message mom_register_handler handle_quit_circuit_queue_message quit_circuit_queue_message mom_register_handler handle_circuit_queue_message circuit_queue_message > /var/run/circuits/$circ_id.queue mom_run_message_loop } & circd_children="$circd_children$circ_id:q " log_info "circd_start_queue[$circ_id]: started child $circ_id:q" } # Waits until the circuit's queue process serializing calls to the circuit # control script is ready. # # Input: # $1 = circuit identifier circd_wait_for_queue() { local circ_id="$1" # wait until queue process can receive messages while [ ! -f /var/run/circuits/$circ_id.queue ] do usleep 100000 done } # Stops a queue process serializing calls to the circuit control script of some # circuit. # # Note: This function may only be called when circd is shutting down or if a # circuit is being removed, i.e. if it can be guaranteed that # circd_start_queue() is not called afterwards. This is to avoid race # conditions. # # Input: # $1 = circuit identifier circd_stop_queue() { local circ_id="$1" mom_unicast_message \ $circ_id:q quit_circuit_queue_message >/dev/null } ####################### # DIAL MODE MANAGEMENT ####################### # contains information about the current global dialmode dialmode_file=/var/run/circuits/dialmode.global # contains information about local dialmodes of circuits local_dialmodes_file=/var/run/circuits/dialmodes.local # Returns the global dial mode. # # Output: # The dial mode. circd_get_global_dialmode() { local old_mode= read old_mode < $dialmode_file echo $old_mode } # Stores the global dial mode. # # Input: # $1 = global dial mode circd_set_global_dialmode() { echo "$1" > $dialmode_file } # Returns the local dial mode for a circuit. # # Input: # $1 = circuit identifier # Output: # The circuit's local dial mode. circd_get_local_dialmode() { sed -n "s/^$1 \(.*\)$/\1/p" $local_dialmodes_file } # Stores the local dial mode for a circuit. # # Input: # $1 = circuit identifier # $2 = circuit's local dial mode circd_set_local_dialmode() { sed -i "/^$1 /d" $local_dialmodes_file [ -n "$2" ] && echo "$1 $2" >> $local_dialmodes_file } # Returns the effective dial mode for a circuit. # # Input: # $1 = circuit identifier # Output: # The dial mode. circd_get_effective_dialmode() { case $(circd_get_global_dialmode):$(circd_get_local_dialmode $1) in off:*|*:off) echo off;; manual:*|*:manual) echo manual;; auto:*|*:auto) echo auto;; *) echo off;; esac } # Returns the effective dial modes of all circuits. Each line returned is # formatted as follows: # # # Output: # A (possibly empty) list of lines formatted as described above. circd_get_effective_dialmodes() { local oldeffmodes= local circ for circ in $(circd_get_by_state all) do echo -n "$circ " circd_get_effective_dialmode $circ done } ################### # STATE MANAGEMENT ################### # Checks whether all dependencies of a circuit are met. Each dependee needs to # be online. # # Input: # $1... = dependencies (may be empty) # Exit code: # 0 if all dependencies are online and not being hung up, 1 otherwise # # Note: a dependency specified by a class mapped to multiple circuits is # fulfilled if at least one of those circuits fulfils it circd_check_dependencies() { local de l3prot for dep in "$@" do case $dep in */*) l3prot=${dep##*/} dep=${dep%/*} ;; *) l3prot= ;; esac local circ ok= for circ in $(circuit_resolve $dep) do # TODO: remove that code once all servers are rewritten to use # the MOM instead of misusing the circuit system if [ "$circ_server" = yes ] then local circ_other_server circuit_read_field $circ circ_server circ_other_server if [ "$circ_other_server" = yes ] && circd_is_at_least_ready $circ then ok=1 break fi fi if circd_is_online $circ $l3prot then ok=1 break fi done [ -z "$ok" ] && return 1 done return 0 } # Checks whether all weak dependencies of a circuit are met. Each dependee # needs to be at least ready. # # Input: # $1 = nonempty if dependent circuit wants to go online # $2... = dependencies (may be empty) # Exit code: # 0 if all dependencies are at least ready and not being hung up, 1 otherwise circd_check_weak_dependencies() { local hangup_check=$1 dep shift for dep in "$@" do circd_is_at_least_ready $dep $hangup_check || return 1 done return 0 } # Changes the state of a circuit. If dial mode is 'auto', any state change to # 'active' triggers dialing. # # Input: # $1 = circuit identifier # $2 = new state # Exit code: # 0 if circuit exists, a nonzero value otherwise # Synchronization: # Takes a read lock on the circuit. circd_change_state() { local id=$1 oldstate= newstate=$2 circuit_resolve_alias id local tgtprefix=$circuit_state_dir/$newstate local state changed= for state in $circuit_states do local srcprefix=$circuit_state_dir/$state if [ -f $srcprefix/$id ] then oldstate="$state" break fi done case $oldstate in deleted) log_error \ "circd_change_state: deleted circuit $id cannot have its state changed to $newstate" return 1 ;; '') oldstate=unknown ln -s ../all/$id $tgtprefix/$id ;; *) if [ "$oldstate" != "$newstate" ] then mv $srcprefix/$id $tgtprefix/$id touch $tgtprefix/$id fi ;; esac if [ "$oldstate" != "$newstate" ] then log_info \ "circd_change_state: circuit $id changes state from $oldstate to $newstate" circuit_event_id=$id \ state_changed_circuit_event_old_state=$oldstate \ state_changed_circuit_event_new_state=$newstate \ mom_broadcast_message state_changed_circuit_event >/dev/null local old_router_state read old_router_state < $router_state_file case $old_router_state:$newstate in offline:online) if circd_router_is_online then echo online > $router_state_file state_changed_router_event_old_state=offline \ state_changed_router_event_new_state=online \ mom_broadcast_message state_changed_router_event >/dev/null fi ;; online:online) ;; online:*) if ! circd_router_is_online then echo offline > $router_state_file state_changed_router_event_old_state=online \ state_changed_router_event_new_state=offline \ mom_broadcast_message state_changed_router_event >/dev/null fi ;; esac circd_handle_state_change $id $oldstate $newstate fi if [ -n "$circd_shutting_down" ] then if [ "$newstate" = "inactive" -o "$newstate" = "failed" ] then # circd_check_exit() need not be called here as circd_stop_queue() # eventually leads to a child_exited_circd_event whose handler # calls circd_check_exit() circd_stop_queue $id fi fi return 0 } # Determines derived actions due to circuit state changes. # # Input: # $1 = circuit identifier # $2 = old state # $3 = new state circd_handle_state_change() { local id=$1 oldstate=$2 newstate=$3 eval local dependees=\$circd_revdeps_$(circd_make_varname $id) # take circuit classes into account local circ_class_n circ_class i circuit_read_field $id circ_class_n for i in $(seq 1 $circ_class_n) do circuit_read_field $id circ_class_$i circ_class eval local class_dependees=\$circd_revdeps_$(circd_make_varname $circ_class) dependees="$dependees $class_dependees" done local circ_id for circ_id in $id $dependees do local dialmode=$(circd_get_effective_dialmode $circ_id) local state [ "$circ_id" = "$id" ] && state=$newstate || state=$(circd_get_state $circ_id) case $state:$dialmode in active:auto) circd_circuit_autodialup $circ_id ;; ready:*|semionline:*|online:*) circd_circuit_autohangup $circ_id ;; esac done } # Returns all circuits with a given state. # # Input: # $1... = states # Output: # A list of circuits belonging to passed states. May be empty. circd_get_by_state() { local state for state do for f in $circuit_state_dir/$state/* do [ -f $f ] && basename $f done done | sort -unk1.$((circuit_prefix_len+1)) } # Returns the state of a circuit. # # Input: # $1 = circuit identifier # Output: # The circuit state. # Exit code: # 0 if circuit exists and has a known state, a nonzero value otherwise circd_get_state() { local id=$1 circuit_resolve_alias id for state in $circuit_states do local file=$circuit_state_dir/$state/$id if [ -f $file ] then echo $state return 0 fi done return 1 } # Determines if a given circuit is at least ready. This is used for bundle # members and server circuit instances which weakly depend on their bundle # circuit or server circuit, respectively. # Note that a circuit is not considered to be ready if it is in the middle of # a hangup operation. # Input: # $1 = circuit identifier # $2 = If nonempty, the circuit may not be being hung up. This is necessary # for dependency checks when a dependent circuit wants to go online. # Exit code: # 0 if the circuit is at least ready, 1 otherwise # Synchronization: # Takes a read lock on the circuit. circd_is_at_least_ready() { local id=$1 hangup_check=$2 res=1 circuit_read_lock $id circd_is_at_least_ready case $(circd_get_state $id) in ready|semionline|online) if [ -n "$hangup_check" ] then local circ_current_operation circuit_read_field $id circ_current_operation [ "$circ_current_operation" != "hangup" ] && res=0 else res=0 fi ;; esac circuit_read_unlock $id circd_is_at_least_ready return $res } # Determines the online state for a given circuit and a given layer-3 protocol. # If no layer-3 protocol is given, all configured layer-3 protocols have to be # up for the circuit to be online. # Note that a circuit is not considered to be online if it is in the middle of # a hangup operation. # Input: # $1 = circuit identifier # $2 = layer-3 protocol (may be empty) # Exit code: # 0 if the circuit/layer-3 protocol combination is online, 1 otherwise # Synchronization: # Takes a read lock on the circuit. circd_is_online() { local id=$1 l3prot=$2 res=1 state= circuit_read_lock $id circd_is_online if [ -n "$l3prot" ] then circuit_is_l3prot_up $id $l3prot && state="online" else state=$(circd_get_state $id) fi case $state in online) local circ_current_operation circuit_read_field $id circ_current_operation [ "$circ_current_operation" != "hangup" ] && res=0 ;; esac circuit_read_unlock $id circd_is_online return $res } # Determines if the router is online. circd_router_is_online() { local rc=1 local circ_id while read circ_id do local state=$(circd_get_state $circ_id) case $state in online) return 0 ;; esac done < $when_online_file return 1 } # Determines the "maximum" state of all circuits. If no circuits are available, # "inactive" is returned. circd_get_composite_state() { local rc=1 local state for state in $circuit_states do if [ -n "$(circd_get_by_state $state)" ] then echo $state return 0 fi done echo "inactive" return 0 } # Processes a change of a circuit's effective dial mode. # # Input: # $1 = circuit identifier # $2 = old effective dial mode # $3 = new effective dial mode circd_handle_effective_dialmode_change() { local circ_id=$1 oldeffmode=$2 neweffmode=$3 dialmode_event_old_mode=$oldeffmode \ dialmode_event_new_mode=$neweffmode \ effective_dialmode_changed_event_id=$circ_id \ mom_broadcast_message effective_dialmode_changed_event >/dev/null case $neweffmode in auto) # dial active circuit case $(circd_get_state $circ_id) in active) circd_circuit_autodialup $circ_id ;; esac ;; manual) # hangup ready circuit case $(circd_get_state $circ_id) in ready) circd_circuit_hangup $circ_id active ;; esac ;; off) # put circuit down case $(circd_get_state $circ_id) in ready|semionline|online) circd_circuit_hangup $circ_id active ;; esac ;; esac } # Associates passed circuit with passed layer-3 protocol and changes the # circuit's state accordingly ("online" if all configured layer-3 protocols are # up, else "semionline"). # Input: # $1 = circuit identifier # $2 = layer-3 protocol # Synchronization: # Takes a read/write lock on the circuit. circd_go_online_if_possible() { local id=$1 l3prot=$2 circ_protocols circ_protocols_up prot prot_up all_up=1 circuit_lock $id circd_go_online_if_possible circuit_read_field $id circ_protocols_up if [ "$circ_protocols_up" != "${circ_protocols_up/ $l3prot/}" ] then # layer-3 protocol is already online; this can happen as both ip(v6)-up # and prefix(v6)-up call circd_go_online_if_possible() for now circuit_unlock $id circd_go_online_if_possible return fi circ_protocols_up="$circ_protocols_up $l3prot" circuit_write_field $id circ_protocols_up "$circ_protocols_up" circuit_read_field $id circ_protocols for prot in $circ_protocols do local found= for prot_up in $circ_protocols_up do if [ "$prot" = "$prot_up" ] then found=1 break fi done if [ -z "$found" ] then all_up= break fi done if [ -n "$all_up" ] then # all layer-3 protocols are up, the circuit is online circd_change_state $id online # clear current dial operation if necessary local circ_current_operation circuit_read_field $id circ_current_operation if [ "$circ_current_operation" = "dial" ] then circuit_write_field $id circ_current_operation "" fi else # not all layer-3 protocols are up, the circuit is semionline circd_change_state $id semionline fi circuit_unlock $id circd_go_online_if_possible } # Disssociates passed circuit from passed layer-3 protocol and changes the # circuit's state accordingly ("semionline" if at least one other layer-3 # protocol is up yet, else the circuit is hung up). # Input: # $1 = circuit identifier # $2 = layer-3 protocol # Synchronization: # Takes a read/write lock on the circuit. circd_go_offline_if_necessary() { local id=$1 l3prot=$2 circ_protocols circ_protocols_up circuit_lock $id circd_go_offline_if_necessary circuit_read_field $id circ_protocols_up if [ "$circ_protocols_up" = "${circ_protocols_up/ $l3prot/}" ] then # layer-3 protocol is already offline; this can happen as both # ip(v6)-down and prefix(v6)-down call circd_go_offline_if_necessary() # for now circuit_unlock $id circd_go_offline_if_necessary return fi circ_protocols_up="${circ_protocols_up/ $l3prot/}" circuit_write_field $id circ_protocols_up "$circ_protocols_up" if [ -z "$circ_protocols_up" ] then # all layer-3 protocols are down, finish hanging up circuit circd_finalize_hangup $id else # not all layer-3 protocols are down, the circuit is semionline circd_change_state $id semionline fi circuit_unlock $id circd_go_offline_if_necessary } # Initiates dialling up a circuit by calling the corresponding circuit control # script. # # Input: # $1 = circuit identifier # Synchronization: # Takes a read/write lock on the circuit. circd_initiate_dialup() { local circ_id=$1 circuit_lock $circ_id circd_initiate_dialup circd_change_state $circ_id ready circuit_write_field $circ_id circ_current_operation "dial" circuit_unlock $circ_id circd_initiate_dialup circd_wait_for_queue $circ_id mom_unicast_message \ $circ_id:q dialup_circuit_queue_message >/dev/null } # Initiates hanging up a circuit by calling the corresponding circuit control # script. # # Input: # $1 = circuit identifier # $2 = target state (typically "active" for "hangup" and "inactive" for # "down") # Synchronization: # Takes a read/write lock on the circuit. circd_initiate_hangup() { local circ_id=$1 hangup_state=$2 circuit_lock $circ_id circd_initiate_hangup circuit_write_field $circ_id circ_current_operation "hangup" circuit_write_field $circ_id circ_hangup_state $hangup_state circuit_unlock $circ_id circd_initiate_hangup circd_wait_for_queue $circ_id mom_unicast_message \ $circ_id:q hangup_circuit_queue_message >/dev/null } # Finalizes hanging up a circuit. This function is called indirectly from # ip-down/ipv6-down/link-down. # # Input: # $1 = circuit identifier # $2 = If empty and this is a daemon-controlled circuit, finalizing the # circuit hangup is delayed until the controlling daemon terminates. If # nonempty, hanging up the circuit is finalized immediately. # Synchronization: # Takes a read/write lock on the circuit. circd_finalize_hangup() { local id=$1 force=$2 circ_force_hangup circ_protocols_up circuit_lock $id circd_finalize_hangup # merge the "force hangup" flag circuit_read_field $id circ_force_hangup : ${force:=$circ_force_hangup} circuit_write_field $id circ_force_hangup $force circuit_read_field $id circ_protocols_up if [ -n "$circ_protocols_up" ] then # there are still layer-3 procotols running # we assume we will be called again when all these protocols are down circuit_unlock $id circd_finalize_hangup return 0 fi local pid=$(circd_daemon_get_hangup_pid $id) if [ -n "$pid" -a -z "$force" ] then # delay hangup circuit_unlock $id circd_finalize_hangup return 0 fi local circ_origin circuit_read_field $id circ_origin local state=$(circd_get_state $id) if [ "$state" != "deleted" ] then # mark circuit clones (i.e. circuits created on-the-fly) as deleted; # if the last reference to it is dropped, the circuit will be removed if [ -n "$circ_origin" ] then circd_change_state $id deleted else local circ_hangup_state circuit_read_field $id circ_hangup_state : ${circ_hangup_state:=active} case $state:$circ_hangup_state in inactive:active|failed:active|active:active) # nothing to do ;; *) circd_change_state $id $circ_hangup_state ;; esac fi # clear current hangup operation if necessary local circ_current_operation circuit_read_field $id circ_current_operation if [ "$circ_current_operation" = "hangup" ] then circuit_write_field $id circ_current_operation "" fi fi # clear "force hangup" flag circuit_write_field $id circ_force_hangup "" # clear hangup state circuit_write_field $id circ_hangup_state "" # deregister daemon if necessary (this happens if a daemon terminates by # itself, calling this function indirectly from within ip*-down/link-down) circd_daemon_hung_up $id # if hanging up a circuit clone (i.e. circuits created on-the-fly), the # circuit's reference counter is decremented; if it reaches zero, the # circuit will be removed => hung up circuit clones will be eventually # removed from the list of circuits if [ -n "$circ_origin" ] then circd_unref $id fi circuit_unlock $id circd_finalize_hangup } # Activates a circuit. # # Input: # $1 = circuit identifier # Exit code: # 0 if activation succeeded, a non-zero value otherwise circd_circuit_activate() { local id=$1 circd_change_state $id active } # Dials a circuit if possible, i.e. if dependencies are fulfilled. This # function assumes the circuit's effective dial mode is "auto" and the # circuit's state is "active". # # Input: # $1 = circuit identifier circd_circuit_autodialup() { local id=$1 circ_deps circ_bundle circ_origin circ_server circuit_read_lock $id circd_circuit_autodialup circuit_read_field $id circ_deps circuit_read_field $id circ_bundle circuit_read_field $id circ_origin circuit_read_field $id circ_server circuit_read_unlock $id circd_circuit_autodialup if circd_check_dependencies $circ_deps && circd_check_weak_dependencies 1 $circ_bundle $circ_origin then log_info "circd_circuit_autodialup: dialling up $id" circd_initiate_dialup $id fi } # Hangs up a circuit if necessary, i.e. if dependencies are not fulfilled. This # function assumes the circuit's state is "ready", "semionline" or "online". # # Input: # $1 = circuit identifier circd_circuit_autohangup() { local id=$1 circ_deps circ_bundle circ_origin circ_server circuit_read_lock $id circd_circuit_autohangup circuit_read_field $id circ_deps circuit_read_field $id circ_bundle circuit_read_field $id circ_origin circuit_read_field $id circ_server circuit_read_unlock $id circd_circuit_autohangup if ! circd_check_dependencies $circ_deps || ! circd_check_weak_dependencies "" $circ_bundle $circ_origin then circd_circuit_hangup $id active fi } # Hangs up a circuit if necessary. This function assumes the circuit's state is # "ready", "semionline" or "online". It does not check the circuit's # dependencies. # # Input: # $1 = circuit identifier # $2 = target state for the circuit after hangup circd_circuit_hangup() { local id=$1 target_state=$2 circ_current_operation circuit_read_field $id circ_current_operation case $circ_current_operation in hangup) # circuit is already being hung up, do nothing ;; *) log_info "circd_circuit_hangup: hanging up $id" circd_initiate_hangup $id $target_state ;; esac } #################### # DAEMON MANAGEMENT #################### # Associates a daemon with a circuit. If either the circuit or the daemon are # already part of a registration, the old registration is removed. It follows # that at most one daemon can be associated with at most one circuit at any # time. # Input: # $1 = circuit identifier # $2 = daemon PID circd_daemon_register() { local id=$1 pid=$2 circuit_resolve_alias id sync_lock_resource daemons_db circd_daemon_register sed -i "/^$id /d;/^.* $pid$/d" $daemons_file echo "$id TERM $pid" >> $daemons_file echo "$id HANGUP $pid" >> $daemons_file sync_unlock_resource daemons_db circd_daemon_register } # Returns the PID for a daemon-controlled circuit in the context of circuit # hangup. # Input: # $1 = circuit identifier # Output: # the daemon PID or nothing if this circuit is not controlled by a daemon or # if the circuit has already been hung up circd_daemon_get_hangup_pid() { local id=$1 circuit_resolve_alias id sync_lock_resource daemons_db circd_daemon_get_hangup_pid sed -n "s|^$id HANGUP \([0-9]\+\)$|\1|p" $daemons_file sync_unlock_resource daemons_db circd_daemon_get_hangup_pid } # Disassociates a daemon from a circuit because the circuit has been hung up. # Input: # $1 = circuit identifier circd_daemon_hung_up() { local id=$1 circuit_resolve_alias id sync_lock_resource daemons_db circd_daemon_hung_up sed -i "/^$id HANGUP [0-9]\+$/d" $daemons_file sync_unlock_resource daemons_db circd_daemon_hung_up # remember time of hangup # note that we do not have to acquire the circuit lock as we are called by # circd_finalize_hangup() which already holds the lock circuit_write_field $id circ_hangup_time $(date +%s) } # Disassociates a daemon from a circuit because the daemon has terminated, # Input: # $1 = daemon PID circd_daemon_terminated() { local pid=$1 sync_lock_resource daemons_db circd_daemon_terminated sed -i "/^\([^ ]\+\) TERM $pid$/d" $daemons_file sync_unlock_resource daemons_db circd_daemon_terminated } # Hangs up a daemon-controlled circuit if the circuit has not been hung up yet. # Input: # $1 = daemon PID circd_daemon_hangup() { local pid=$1 sync_lock_resource daemons_db circd_daemon_hangup local id=$(sed -n "s|^\([^ ]\+\) HANGUP $pid$|\1|p" $daemons_file) [ -n "$id" ] && sed -i "/^$id HANGUP $pid$/d" $daemons_file sync_unlock_resource daemons_db circd_daemon_hangup [ -n "$id" ] && circd_finalize_hangup $id } # Terminates the daemon controlling a circuit if not already done so. # Input: # $1 = circuit identifier # $2 = (optional) signal name, e.g. HUP. If nothing is passed, TERM is used. # Output: # the daemon PID or nothing if no daemon has been associated with passed # circuit circd_daemon_terminate() { local id=$1 circuit_resolve_alias id sync_lock_resource daemons_db circd_daemon_terminate local pid=$(sed -n "s|^$id TERM \([0-9]\+\)$|\1|p" $daemons_file) [ -n "$pid" ] && sed -i "/^$id TERM $pid$/d" $daemons_file sync_unlock_resource daemons_db circd_daemon_terminate if [ -n "$pid" ] then kill -${2:-TERM} $pid echo "$pid" fi } # Returns 0 if passed circuit is configured for dial-on-demand, a non-zero # value otherwise. # Input: # $1 = circuit identifier circd_is_dial_on_demand() { local id=$1 local hup_timeout=0 circuit_read_field $id circ_hup_timeout [ $circ_hup_timeout -gt 0 -a "$(circd_get_effective_dialmode $id)" = auto ] local rc=$? return $rc } # Adds routes needed for dial-on-demand circuits. # Note that there is no equivalent function for route removal as dial-on-demand # circuits require a controlling daemon using newly created interfaces, so # the routes will disappear anyway when the interface is deleted by the daemon. # Input: # $1 = circuit identifier # $2 = protocol (4 or 6) # Synchronization: # Takes a read lock on the circuit. circd_add_dial_on_demand_routes() { local id=$1 circ_bundle circ_dev circ_nets_n circuit_resolve_alias id # dial-on-demand circuits that are part of a bundle do not have any # associated networks as they are configured at bundle level circuit_read_field $id circ_bundle if [ -n "$circ_bundle" ] then circuit_resolve_alias circ_bundle id=$circ_bundle fi circuit_read_lock $id circd_add_dial_on_demand_routes circuit_read_field $id circ_dev circuit_read_field $id circ_nets_ipv${2}_n circ_nets_n sync_lock_resource routes_db circd_add_dial_on_demand_routes local i net for i in $(seq 1 ${circ_nets_n:-0}) do circuit_read_field $id circ_nets_ipv${2}_$i net ip -$2 route append $net dev $circ_dev done sync_unlock_resource routes_db circd_add_dial_on_demand_routes circuit_read_unlock $id circd_add_dial_on_demand_routes } # Circuit daemon wrapper. Starts a daemon, waits for it to exit, sets the # correct state after termination and takes care of the PID file. Note that # the script assumes that the daemon does not fork itself into the background! # # Input: # $1 = circuit identifier # $2 = name of the daemon # $3 = name of cleanup function to be called when daemon exits (may be empty); # please be aware that the function is called in the context of the # daemon wrapper, not in the context of circd # $4... = daemon arguments # Exit code: # The exit code of the daemon. # Synchronization: # Takes a read lock on the circuit while installing dial-on-demand routes. circd_daemon_wrapper() { local id=$1 daemon=$2 cleanup_func=$3 shift 3 # OK, this is a bit tricky: It can happen that a server daemon wants # to call a daemon to handle an incoming connection, and that the # server daemon wants to communicate with the connection daemon through # stdin/stdout using a master/slave PTY pair (e.g. pptpd does this to # communicate with the pppd it launches for each incoming PPTP # connection). In this case, the wrapper calls fdsend to pass the file # descriptors and sets circ_fdpass to "yes". We have to read circ_fdpass # here and if set to "yes", call fdrecv as wrapper around the daemon. local circ_fdpass prog circuit_read_field $id circ_fdpass if [ "$circ_fdpass" = yes ] then local circ_fdpass_path=$(circuit_build_fdpass_path $id) prog="fdrecv $circ_fdpass_path 30 $(type -p $daemon)" else prog="$daemon" fi circd_ref $id ( mom_id="$id:d" mom_process="$mom_id" fork_call_handlers exit_trap_ignore_all_signals # otherwise MOM communication does not work in child processes! exit_trap_install_for_signal TERM false exit_trap_add circd_notify_parent_about_child_exit script="$mom_id[$PID]" echo -n "$mom_id" > /proc/self/task/$PID/comm log_info "starting $daemon $*..." mknod $circd_fifo_dir/$id p logmsg "$script" $circuit_logfacility.notice < $circd_fifo_dir/$id & $prog "$@" >$circd_fifo_dir/$id 2>&1 & local pid=$! circd_daemon_register $id $pid > /var/run/circuits/$id.daemon # establish dial-on-demand routes if necessary if circd_is_dial_on_demand $id then # let the daemon create the necessary device sleep 1 circuit_read_lock $id "$script" circd_add_dial_on_demand_routes $id 4 circd_add_dial_on_demand_routes $id 6 circuit_read_unlock $id "$script" fi log_info "waiting for $daemon with PID $pid" wait $pid local rc=$? rm -f $circd_fifo_dir/$id log_info "$daemon died with exit code $rc, asking circd to clean up" if [ -n "$cleanup_func" ] then if type $cleanup_func >/dev/null 2>&1 then $cleanup_func $id $rc else log_error \ "$id: cleanup function \"$cleanup_func\" does not exist" fi fi circuit_event_id=$id \ daemon_circuit_event_pid=$pid \ daemon_exited_circuit_event_status_code=$rc \ mom_unicast_message \ circd \ daemon_exited_circuit_event >/dev/null ) & circd_children="$circd_children$id:d " log_info "circd_daemon_wrapper[$id]: started child $id:d" # wait until daemon has been started and registered while [ ! -f /var/run/circuits/$id.daemon ] do sleep 1 done rm -f /var/run/circuits/$id.daemon return 0 } #################################### # EVENT HANDLERS: LIFETIME MESSAGES #################################### handle_create_circuit_message() { log_info \ "handle_create_circuit_message($message_id#$message_sender:$message_type): type=$create_circuit_message_type" if [ -z "$circd_shutting_down" ] then local _var for _var do local _newvar=circ_${_var#create_circuit_message_} eval local $_newvar=\$$_var done local _id= _errmsg= if circd_circuit_add _id _errmsg; then log_info \ "handle_create_circuit_message($message_id#$message_sender:$message_type): ACK, id=$_id" ack_create_circuit_reply_message_id=$_id reply_message_for=$message_id \ mom_unicast_message $message_sender ack_create_circuit_reply_message >/dev/null else log_info \ "handle_create_circuit_message($message_id#$message_sender:$message_type): NAK, errmsg=$_errmsg" nak_create_circuit_reply_message_errmsg=$_errmsg reply_message_for=$message_id \ mom_unicast_message $message_sender nak_create_circuit_reply_message >/dev/null fi else log_info \ "handle_create_circuit_message($message_id#$message_sender:$message_type): NAK, errmsg=$circd_shutting_down_message" nak_create_circuit_reply_message_errmsg=$circd_shutting_down_message reply_message_for=$message_id \ mom_unicast_message $message_sender nak_create_circuit_reply_message >/dev/null fi } handle_clone_circuit_message() { log_info \ "handle_clone_circuit_message($message_id#$message_sender:$message_type): id=$clone_circuit_message_id adaptor=$clone_circuit_message_adaptor adaptor_arg=$clone_circuit_message_adaptor_arg" if [ -z "$circd_shutting_down" ] then local _clone_id= _clone_errmsg= if circd_clone $clone_circuit_message_id "$clone_circuit_message_adaptor" "$clone_circuit_message_adaptor_arg" _clone_id _clone_errmsg then log_info \ "handle_clone_circuit_message($message_id#$message_sender:$message_type): ACK, id=$_clone_id" ack_clone_circuit_reply_message_id=$_clone_id reply_message_for=$message_id \ mom_unicast_message $message_sender ack_clone_circuit_reply_message >/dev/null else log_info \ "handle_clone_circuit_message($message_id#$message_sender:$message_type): NAK, errmsg=$_clone_errmsg" nak_clone_circuit_reply_message_errmsg=$_clone_errmsg reply_message_for=$message_id \ mom_unicast_message $message_sender nak_clone_circuit_reply_message >/dev/null fi else log_info \ "handle_clone_circuit_message($message_id#$message_sender:$message_type): NAK, errmsg=$circd_shutting_down_message" nak_clone_circuit_reply_message_errmsg=$circd_shutting_down_message reply_message_for=$message_id \ mom_unicast_message $message_sender nak_clone_circuit_reply_message >/dev/null fi } # TODO: deactivate circuit with target state "deleted" handle_destroy_circuit_message() { log_info \ "handle_destroy_circuit_message($message_id#$message_sender:$message_type): id=$destroy_circuit_message_id" circd_circuit_remove $destroy_circuit_message_id } handle_start_creation_group_circuit_message() { if [ -n "$circd_shutting_down" ] then log_error \ "handle_start_creation_group_circuit_message($message_id#$message_sender:$message_type): $circd_shutting_down_message" return 1 fi local group= circd_start_creation_group group start_creation_group_circuit_reply_message_id=$group reply_message_for=$message_id \ mom_unicast_message $message_sender start_creation_group_circuit_reply_message >/dev/null } handle_end_creation_group_circuit_message() { if [ -n "$circd_shutting_down" ] then log_error \ "handle_end_creation_group_circuit_message($message_id#$message_sender:$message_type): $circd_shutting_down_message" return 1 fi circd_end_creation_group $end_creation_group_circuit_message_id } ##################################### # EVENT HANDLERS: DIAL MODE MESSAGES ##################################### handle_get_global_dialmode_message() { local _mode=$(circd_get_global_dialmode) get_global_dialmode_reply_message_mode=$_mode reply_message_for=$message_id \ mom_unicast_message $message_sender get_global_dialmode_reply_message >/dev/null } handle_set_global_dialmode_message() { log_info \ "handle_set_global_dialmode_message($message_id#$message_sender:$message_type): mode=$set_global_dialmode_message_mode" if [ -n "$circd_shutting_down" ] then log_error \ "handle_set_global_dialmode_message($message_id#$message_sender:$message_type): $circd_shutting_down_message" return 1 fi local _old_mode=$(circd_get_global_dialmode) \ _oldeffmodes="$(circd_get_effective_dialmodes)" circd_set_global_dialmode $set_global_dialmode_message_mode dialmode_event_old_mode=$_old_mode \ dialmode_event_new_mode=$set_global_dialmode_message_mode \ mom_broadcast_message global_dialmode_changed_event >/dev/null echo "$_oldeffmodes" | while read _circ _oldeffmode do local _neweffmode=$(circd_get_effective_dialmode $_circ) if [ "$_oldeffmode" != "$_neweffmode" ] then log_info \ "handle_set_global_dialmode_message($message_id#$message_sender:$message_type): effective dial mode changed for circuit $_circ from $_oldeffmode to $_neweffmode" circd_handle_effective_dialmode_change \ $_circ $_oldeffmode $_neweffmode fi done } handle_get_local_dialmode_message() { local _mode=$(circd_get_local_dialmode $get_local_dialmode_message_id) get_local_dialmode_reply_message_id=$get_local_dialmode_message_id \ get_local_dialmode_reply_message_mode=$_mode \ reply_message_for=$message_id \ mom_unicast_message $message_sender get_local_dialmode_reply_message >/dev/null } handle_set_local_dialmode_message() { log_info \ "handle_set_local_dialmode_message($message_id#$message_sender:$message_type): id=$set_local_dialmode_message_id mode=$set_local_dialmode_message_mode" if [ -n "$circd_shutting_down" ] then log_error \ "handle_set_local_dialmode_message($message_id#$message_sender:$message_type): $circd_shutting_down_message" return 1 fi local _old_mode=$(circd_get_local_dialmode $set_local_dialmode_message_id) \ _oldeffmode=$(circd_get_effective_dialmode $set_local_dialmode_message_id) circd_set_local_dialmode $set_local_dialmode_message_id $set_local_dialmode_message_mode dialmode_event_old_mode=$_old_mode \ dialmode_event_new_mode=$set_local_dialmode_message_mode \ local_dialmode_changed_event_id=$set_local_dialmode_message_id \ mom_broadcast_message local_dialmode_changed_event >/dev/null local _neweffmode=$(circd_get_effective_dialmode $set_local_dialmode_message_id) if [ "$_oldeffmode" != "$_neweffmode" ] then log_info \ "handle_set_local_dialmode_message($message_id#$message_sender:$message_type): effective dial mode changed for circuit $set_local_dialmode_message_id from $_oldeffmode to $_neweffmode" circd_handle_effective_dialmode_change \ $set_local_dialmode_message_id $_oldeffmode $_neweffmode fi } handle_get_effective_dialmode_message() { local _mode=$(circd_get_effective_dialmode $get_effective_dialmode_message_id) get_effective_dialmode_reply_message_id=$get_effective_dialmode_message_id \ get_effective_dialmode_reply_message_mode=$_mode \ reply_message_for=$message_id \ mom_unicast_message $message_sender get_effective_dialmode_reply_message >/dev/null } handle_dialmode_message() { log_warn \ "handle_dialmode_message($message_id#$message_sender:$message_type): received but ignored" } ################################# # EVENT HANDLERS: STATE MESSAGES ################################# handle_get_by_state_circuit_message() { local _circuits=$(circd_get_by_state $get_by_state_circuit_message_states) get_by_state_circuit_reply_message_states=$get_by_state_circuit_message_states \ get_by_state_circuit_reply_message_circuits=$_circuits \ reply_message_for=$message_id \ mom_unicast_message $message_sender get_by_state_circuit_reply_message >/dev/null } handle_get_state_circuit_message() { local _state=$(circd_get_state $get_state_circuit_message_id) get_state_circuit_reply_message_id=$get_state_circuit_message_id \ get_state_circuit_reply_message_state=$_state \ reply_message_for=$message_id \ mom_unicast_message $message_sender get_state_circuit_reply_message >/dev/null } handle_get_all_states_circuit_message() { local _circ _i=1 for _circ in $(circd_get_by_state all) do eval local get_all_states_circuit_reply_message_circuit_${_i}_id=\$_circ local get_all_states_circuit_reply_message_circuit_${_i}_name \ get_all_states_circuit_reply_message_circuit_${_i}_alias \ get_all_states_circuit_reply_message_circuit_${_i}_type \ get_all_states_circuit_reply_message_circuit_${_i}_dev \ get_all_states_circuit_reply_message_circuit_${_i}_class_n \ get_all_states_circuit_reply_message_circuit_${_i}_deps \ get_all_states_circuit_reply_message_circuit_${_i}_state \ get_all_states_circuit_reply_message_circuit_${_i}_local_dialmode \ get_all_states_circuit_reply_message_circuit_${_i}_effective_dialmode circuit_read_field $_circ circ_name get_all_states_circuit_reply_message_circuit_${_i}_name circuit_read_field $_circ circ_alias get_all_states_circuit_reply_message_circuit_${_i}_alias circuit_read_field $_circ circ_type get_all_states_circuit_reply_message_circuit_${_i}_type circuit_read_field $_circ circ_dev get_all_states_circuit_reply_message_circuit_${_i}_dev circuit_read_field $_circ circ_deps get_all_states_circuit_reply_message_circuit_${_i}_deps eval get_all_states_circuit_reply_message_circuit_${_i}_state="\$(circd_get_state \$_circ)" eval get_all_states_circuit_reply_message_circuit_${_i}_local_dialmode="\$(circd_get_local_dialmode \$_circ)" eval get_all_states_circuit_reply_message_circuit_${_i}_effective_dialmode="\$(circd_get_effective_dialmode \$_circ)" local circ_class_n _j circuit_read_field $_circ circ_class_n eval get_all_states_circuit_reply_message_circuit_${_i}_class_n=\$circ_class_n for _j in $(seq 1 $circ_class_n) do local get_all_states_circuit_reply_message_circuit_${_i}_class_${_j} circuit_read_field $_circ circ_class_$_j get_all_states_circuit_reply_message_circuit_${_i}_class_${_j} done _i=$((_i+1)) done local get_all_states_circuit_reply_message_circuit_n=$((_i-1)) reply_message_for=$message_id \ mom_unicast_message \ $message_sender \ get_all_states_circuit_reply_message >/dev/null } handle_is_online_circuit_message() { log_info \ "handle_is_online_circuit_message($message_id#$message_sender:$message_type): id=$is_online_circuit_message_id l3prot=$is_online_circuit_message_l3prot" if circd_is_online \ $is_online_circuit_message_id \ $is_online_circuit_message_l3prot; then log_info \ "handle_is_online_circuit_message($message_id#$message_sender:$message_type): ACK" is_online_circuit_reply_message_id=$is_online_circuit_message_id \ is_online_circuit_reply_message_l3prot=$is_online_circuit_message_l3prot \ reply_message_for=$message_id \ mom_unicast_message $message_sender ack_is_online_circuit_reply_message >/dev/null else log_info \ "handle_is_online_circuit_message($message_id#$message_sender:$message_type): NAK" is_online_circuit_reply_message_id=$is_online_circuit_message_id \ is_online_circuit_reply_message_l3prot=$is_online_circuit_message_l3prot \ reply_message_for=$message_id \ mom_unicast_message $message_sender nak_is_online_circuit_reply_message >/dev/null fi } ########################################### # EVENT HANDLERS: GENERAL CIRCUIT MESSAGES ########################################### handle_activate_circuit_message() { log_info \ "handle_activate_circuit_message($message_id#$message_sender:$message_type): id=$activate_circuit_message_id" if [ -n "$circd_shutting_down" ] then log_error \ "handle_activate_circuit_message($message_id#$message_sender:$message_type): $circd_shutting_down_message" return 1 fi local circ_id=$activate_circuit_message_id if ! circuit_exists $circ_id then log_error \ "handle_activate_circuit_message($message_id#$message_sender:$message_type): circuit $circ_id does not exist" return 1 fi circuit_resolve_alias circ_id local state=$(circd_get_state $circ_id) case $state in deleted) ;; inactive|failed) circd_circuit_activate $circ_id ;; esac } handle_dialup_circuit_message() { log_info \ "handle_dialup_circuit_message($message_id#$message_sender:$message_type): id=$dialup_circuit_message_id" if [ -n "$circd_shutting_down" ] then log_error \ "handle_dialup_circuit_message($message_id#$message_sender:$message_type): $circd_shutting_down_message" return 1 fi local circ_id=$dialup_circuit_message_id circ_dev if ! circuit_exists $circ_id then log_error \ "handle_dialup_circuit_message($message_id#$message_sender:$message_type): circuit $circ_id does not exist" return 1 fi circuit_resolve_alias circ_id local state=$(circd_get_state $circ_id) local dialmode=$(circd_get_effective_dialmode $circ_id) circuit_read_field $circ_id circ_dev case $state:$dialmode in deleted:*) log_error "$circ_id[$circ_dev]: cannot dial using a deleted circuit" return 1 ;; inactive:*|failed:*) log_error "$circ_id[$circ_dev]: cannot dial using an inactive circuit" return 1 ;; active:off) log_error "$circ_id[$circ_dev]: cannot dial as dialmode is 'off'" return 1 ;; active:*) local circ_deps circ_bundle circ_origin circ_server circuit_read_field $circ_id circ_deps circuit_read_field $circ_id circ_bundle circuit_read_field $circ_id circ_origin circuit_read_field $circ_id circ_server if circd_check_dependencies $circ_deps && circd_check_weak_dependencies 1 $circ_bundle $circ_origin then circd_initiate_dialup $circ_id fi ;; esac } # $1 = name calling function # $2 = circuit identifier # $3 = desired target state handle_hangup_or_deactivate_circuit_message() { local caller=$1 circ_id=$2 target_state=$3 msg_type=$4 if ! circuit_exists $circ_id then log_error \ "$caller($message_id#$message_sender:$message_type): circuit $circ_id does not exist" return 1 fi circuit_resolve_alias circ_id local state=$(circd_get_state $circ_id) case $state in ready|semionline|online) circd_circuit_hangup $circ_id $target_state ;; deleted) ;; *) circuit_write_field $circ_id circ_hangup_state $target_state circd_finalize_hangup $circ_id ;; esac } handle_hangup_circuit_message() { log_info \ "handle_hangup_circuit_message($message_id#$message_sender:$message_type): id=$hangup_circuit_message_id" handle_hangup_or_deactivate_circuit_message \ "handle_hangup_circuit_message" \ $hangup_circuit_message_id \ active \ hangup_circuit_message } handle_deactivate_circuit_message() { log_info \ "handle_deactivate_circuit_message($message_id#$message_sender:$message_type): id=$deactivate_circuit_message_id" handle_hangup_or_deactivate_circuit_message \ "handle_deactivate_circuit_message" \ $deactivate_circuit_message_id \ inactive \ deactivate_circuit_message } handle_fail_circuit_message() { log_info \ "handle_fail_circuit_message($message_id#$message_sender:$message_type): id=$fail_circuit_message_id" handle_hangup_or_deactivate_circuit_message \ "handle_fail_circuit_message" \ $fail_circuit_message_id \ failed \ fail_circuit_message } handle_start_daemon_circuit_message() { log_info \ "handle_start_daemon_circuit_message($message_id#$message_sender:$message_type): id=$start_daemon_circuit_message_id daemon=$start_daemon_circuit_message_daemon daemon_args=$start_daemon_circuit_message_daemon_args cleanup_func=$start_daemon_circuit_message_cleanup_func" if [ -n "$circd_shutting_down" ] then log_error \ "handle_start_daemon_circuit_message($message_id#$message_sender:$message_type): $circd_shutting_down_message" return 1 fi if ! circuit_exists $start_daemon_circuit_message_id then log_error \ "handle_start_daemon_circuit_message($message_id#$message_sender:$message_type): circuit $start_daemon_circuit_message_id does not exist" return 1 fi eval circd_daemon_wrapper \ \$start_daemon_circuit_message_id \ \$start_daemon_circuit_message_daemon \ \"\$start_daemon_circuit_message_cleanup_func\" \ "$start_daemon_circuit_message_daemon_args" } handle_stop_daemon_circuit_message() { log_info \ "handle_stop_daemon_circuit_message($message_id#$message_sender:$message_type): id=$stop_daemon_circuit_message_id signal=$stop_daemon_circuit_message_signal" if ! circuit_exists $stop_daemon_circuit_message_id then log_error \ "handle_stop_daemon_circuit_message($message_id#$message_sender:$message_type): circuit $stop_daemon_circuit_message_id does not exist" return 1 fi circd_daemon_terminate \ $stop_daemon_circuit_message_id \ $stop_daemon_circuit_message_signal } handle_attach_to_bundle_circuit_message() { log_info \ "handle_attach_to_bundle_circuit_message($message_id#$message_sender:$message_type): idchild=$attach_to_bundle_circuit_message_idchild idbundle=$attach_to_bundle_circuit_message_idbundle" if ! circuit_exists $attach_to_bundle_circuit_message_idchild then log_error \ "handle_attach_to_bundle_circuit_message($message_id#$message_sender:$message_type): circuit $attach_to_bundle_circuit_message_idchild does not exist" return 1 fi if ! circuit_exists $attach_to_bundle_circuit_message_idbundle then log_error \ "handle_attach_to_bundle_circuit_message($message_id#$message_sender:$message_type): circuit $attach_to_bundle_circuit_message_idbundle does not exist" return 1 fi circd_attach_to_bundle \ $attach_to_bundle_circuit_message_idchild \ $attach_to_bundle_circuit_message_idbundle } handle_circuit_message() { log_warn \ "handle_circuit_message($message_id#$message_sender:$message_type): received but ignored" } ################################################## # EVENT HANDLERS: LAYER-3 PROTOCOL CIRCUIT EVENTS ################################################## handle_up_l3prot_circuit_event() { log_info \ "handle_up_l3prot_circuit_event($message_id#$message_sender:$message_type): id=$circuit_event_id l3prot=$l3prot_circuit_event_l3prot" if ! circuit_exists $circuit_event_id then log_error \ "handle_up_l3prot_circuit_event($message_id#$message_sender:$message_type): circuit $circuit_event_id does not exist" return 1 fi circd_go_online_if_possible $circuit_event_id $l3prot_circuit_event_l3prot } handle_down_l3prot_circuit_event() { log_info \ "handle_down_l3prot_circuit_event($message_id#$message_sender:$message_type): id=$circuit_event_id l3prot=$l3prot_circuit_event_l3prot" if ! circuit_exists $circuit_event_id then log_error \ "handle_down_l3prot_circuit_event($message_id#$message_sender:$message_type): circuit $circuit_event_id does not exist" return 1 fi circd_go_offline_if_necessary $circuit_event_id $l3prot_circuit_event_l3prot } handle_l3prot_circuit_event() { log_warn \ "handle_l3prot_circuit_event($message_id#$message_sender:$message_type): received but ignored" } ###################################### # EVENT HANDLERS: LINK CIRCUIT EVENTS ###################################### handle_up_link_circuit_event() { log_info \ "handle_up_link_circuit_event($message_id#$message_sender:$message_type): id=$circuit_event_id" if ! circuit_exists $circuit_event_id then log_error \ "handle_up_link_circuit_event($message_id#$message_sender:$message_type): circuit $circuit_event_id does not exist" return 1 fi circd_change_state $circuit_event_id online } handle_down_link_circuit_event() { log_info \ "handle_down_link_circuit_event($message_id#$message_sender:$message_type): id=$circuit_event_id force_hangup=$down_link_circuit_event_force_hangup" if ! circuit_exists $circuit_event_id then log_error \ "handle_down_link_circuit_event($message_id#$message_sender:$message_type): circuit $circuit_event_id does not exist" return 1 fi circd_finalize_hangup $circuit_event_id $down_link_circuit_event_force_hangup } handle_link_circuit_event() { log_warn \ "handle_link_circuit_event($message_id#$message_sender:$message_type): received but ignored" } ############################### # EVENT HANDLERS: STATE EVENTS ############################### handle_dialup_control_circuit_event() { log_info \ "handle_dialup_control_circuit_event($message_id#$message_sender:$message_type): id=$circuit_event_id status_code=$control_circuit_event_status_code" if ! circuit_exists $circuit_event_id then log_error \ "handle_dialup_control_circuit_event($message_id#$message_sender:$message_type): circuit $circuit_event_id does not exist" return 1 fi if [ $control_circuit_event_status_code -ne 0 ] then circd_change_state $circuit_event_id active circuit_write_field $circuit_event_id circ_current_operation "" fi } handle_hangup_control_circuit_event() { log_info \ "handle_hangup_control_circuit_event($message_id#$message_sender:$message_type): id=$circuit_event_id status_code=$control_circuit_event_status_code" if ! circuit_exists $circuit_event_id then log_error \ "handle_hangup_control_circuit_event($message_id#$message_sender:$message_type): circuit $circuit_event_id does not exist" return 1 fi if [ $control_circuit_event_status_code -ne 0 ] then circd_finalize_hangup $circuit_event_id fi } handle_control_circuit_event() { log_warn \ "handle_control_circuit_event($message_id#$message_sender:$message_type): received but ignored" } ################################ # EVENT HANDLERS: DAEMON EVENTS ################################ handle_daemon_exited_circuit_event() { log_info \ "handle_daemon_exited_circuit_event($message_id#$message_sender:$message_type): id=$circuit_event_id pid=$daemon_circuit_event_pid status_code=$daemon_exited_circuit_event_status_code" local \ id=$circuit_event_id \ pid=$daemon_circuit_event_pid \ status_code=$daemon_exited_circuit_event_status_code circd_daemon_terminated $pid # hangup circuit if necessary (this happens if a daemon is terminated # without having established a connection, such that ip*-down/link-down # and, consequently, circd_finalize_hangup() were never called; or if # circuit hangup has been delayed) circd_daemon_hangup $pid circd_unref $id } handle_daemon_circuit_event() { log_warn \ "handle_daemon_circuit_event($message_id#$message_sender:$message_type): received but ignored" } ################################### # EVENT HANDLERS: SERVICE MESSAGES ################################### handle_stop_service_message() { log_info \ "handle_stop_service_message($message_id#$message_sender:$message_type): shutting down" if [ $message_sender != circd ] then circd_shutting_down_requestors="$circd_shutting_down_requestors $message_sender:$message_id" fi if [ -z "$circd_shutting_down" ] then circd_shutting_down=1 # count all circuits that have to be activated or failed when restarting circd_activate= circd_fail= for circ_id in $(circd_get_by_state all) do case $(circd_get_state $circ_id) in inactive) ;; failed) circd_fail="$circd_fail $circ_id" ;; *) circd_activate="$circd_activate $circ_id" ;; esac deactivate_circuit_message_id=$circ_id \ mom_unicast_message circd deactivate_circuit_message >/dev/null done circd_check_exit #else: nothing to do, circd is already shutting down fi } handle_service_message() { log_warn \ "handle_service_message($message_id#$message_sender:$message_type): received but ignored" } ############################### # EVENT HANDLERS: CHILD EVENTS ############################### # Handles a terminating child process by removing its PID from the # circd_children list and calling circd_check_exit() if circd is shutting down. handle_child_exited_circd_event() { log_info \ "handle_child_exited_circd_event($message_id#$message_sender:$message_type)" circd_children=${circd_children/ $message_sender / } [ -n "$circd_shutting_down" ] && circd_check_exit } handle_circd_event() { log_warn \ "handle_circd_event($message_id#$message_sender:$message_type): received but ignored" } ################################# # EVENT HANDLERS: QUEUE MESSAGES ################################# handle_dialup_circuit_queue_message() { log_info \ "handle_dialup_circuit_queue_message($message_id#$message_sender:$message_type)" local vars=$(circuit_get_data $circ_id) local varnames=$(echo "$vars" | extract_variable_names) local $varnames eval "$vars" export $varnames circ_dev=$(circuit_get_interface $circ_dev) if [ "$circ_newif" != yes ] && ! translate_net_if "$circ_dev" circ_dev 1 then log_error "unknown interface $circ_dev" circuit_event_id=$circ_id \ control_circuit_event_status_code=127 \ mom_unicast_message \ circd dialup_control_circuit_event >/dev/null return else circuit_write_field $circ_id circ_dev_translated $circ_dev fi local circuit_script=${circ_type}-circuit-ctrl.sh if type -p $circuit_script >/dev/null then set -o pipefail $circuit_script dial 2>&1 | logmsg "$script" $circuit_logfacility.notice local rc=$? set +o pipefail circuit_event_id=$circ_id \ control_circuit_event_status_code=$rc \ mom_unicast_message \ circd dialup_control_circuit_event >/dev/null else # no control script circuit_event_id=$circ_id \ control_circuit_event_status_code=127 \ mom_unicast_message \ circd dialup_control_circuit_event >/dev/null fi } handle_hangup_circuit_queue_message() { log_info \ "handle_hangup_circuit_queue_message($message_id#$message_sender:$message_type)" local vars=$(circuit_get_data $circ_id) local varnames=$(echo "$vars" | extract_variable_names) local $varnames eval "$vars" export $varnames if [ -n "$circ_dev_translated" ] then circ_dev=$circ_dev_translated else circ_dev=$(circuit_get_interface $circ_dev) if [ "$circ_newif" != yes ] && ! translate_net_if "$circ_dev" circ_dev 1 then log_error "unknown interface $circ_dev" # TODO: change state to "failed" circuit_event_id=$circ_id \ control_circuit_event_status_code=127 \ mom_unicast_message \ circd hangup_control_circuit_event >/dev/null return fi fi local circuit_script=${circ_type}-circuit-ctrl.sh if type -p $circuit_script >/dev/null then set -o pipefail $circuit_script hangup 2>&1 | logmsg "$script" $circuit_logfacility.notice local rc=$? set +o pipefail circuit_event_id=$circ_id \ control_circuit_event_status_code=$rc \ mom_unicast_message \ circd hangup_control_circuit_event >/dev/null else # no control script, ask circd to finalize circuit hangup circuit_event_id=$circ_id \ control_circuit_event_status_code=127 \ mom_unicast_message \ circd hangup_control_circuit_event >/dev/null fi } handle_quit_circuit_queue_message() { log_info \ "handle_quit_circuit_queue_message($message_id#$message_sender:$message_type)" mom_quit_message_loop 0 } handle_circuit_queue_message() { log_info \ "handle_circuit_queue_message($message_id#$message_sender:$message_type): received but ignored" } ############### # MAIN PROGRAM ############### log_info "starting" # register message handlers mom_register_handler handle_create_circuit_message create_circuit_message mom_register_handler handle_clone_circuit_message clone_circuit_message # TODO: ignore until it works #mom_register_handler handle_destroy_circuit_message destroy_circuit_message mom_register_handler handle_start_creation_group_circuit_message start_creation_group_circuit_message mom_register_handler handle_end_creation_group_circuit_message end_creation_group_circuit_message mom_register_handler handle_get_global_dialmode_message get_global_dialmode_message mom_register_handler handle_set_global_dialmode_message set_global_dialmode_message mom_register_handler handle_get_local_dialmode_message get_local_dialmode_message mom_register_handler handle_set_local_dialmode_message set_local_dialmode_message mom_register_handler handle_get_effective_dialmode_message get_effective_dialmode_message mom_register_handler handle_dialmode_message dialmode_message mom_register_handler handle_get_by_state_circuit_message get_by_state_circuit_message mom_register_handler handle_get_state_circuit_message get_state_circuit_message mom_register_handler handle_get_all_states_circuit_message get_all_states_circuit_message mom_register_handler handle_is_online_circuit_message is_online_circuit_message mom_register_handler handle_activate_circuit_message activate_circuit_message mom_register_handler handle_dialup_circuit_message dialup_circuit_message mom_register_handler handle_hangup_circuit_message hangup_circuit_message mom_register_handler handle_deactivate_circuit_message deactivate_circuit_message mom_register_handler handle_fail_circuit_message fail_circuit_message mom_register_handler handle_start_daemon_circuit_message start_daemon_circuit_message mom_register_handler handle_stop_daemon_circuit_message stop_daemon_circuit_message mom_register_handler handle_attach_to_bundle_circuit_message attach_to_bundle_circuit_message mom_register_handler handle_circuit_message circuit_message mom_register_handler handle_up_l3prot_circuit_event up_l3prot_circuit_event mom_register_handler handle_down_l3prot_circuit_event down_l3prot_circuit_event mom_register_handler handle_l3prot_circuit_event l3prot_circuit_event mom_register_handler handle_up_link_circuit_event up_link_circuit_event mom_register_handler handle_down_link_circuit_event down_link_circuit_event mom_register_handler handle_link_circuit_event link_circuit_event mom_register_handler handle_dialup_control_circuit_event dialup_control_circuit_event mom_register_handler handle_hangup_control_circuit_event hangup_control_circuit_event mom_register_handler handle_control_circuit_event control_circuit_event mom_register_handler handle_daemon_exited_circuit_event daemon_exited_circuit_event mom_register_handler handle_daemon_circuit_event daemon_circuit_event mom_register_handler handle_stop_service_message stop_service_message mom_register_handler handle_service_message service_message mom_register_handler handle_child_exited_circd_event child_exited_circd_event mom_register_handler handle_circd_event circd_event # initialization of circuit system >> $local_dialmodes_file [ -f $router_state_file ] || echo "offline" > $router_state_file [ -f $dialmode_file ] || circd_set_global_dialmode off [ -f $circd_state_file ] && circd_restore_state # setup pre-existing circuits for circ_id in $(circd_get_by_state all) do circd_start_queue $circ_id done for circ_id in $circd_fail do circuit_exists $circ_id && fail_circuit_message_id=$circ_id \ mom_unicast_message circd fail_circuit_message >/dev/null done for circ_id in $circd_activate do circuit_exists $circ_id && activate_circuit_message_id=$circ_id \ mom_unicast_message circd activate_circuit_message >/dev/null done # run the message loop log_info "entering message loop" mom_run_message_loop log_info "exiting" circd_save_state for requestor in $circd_shutting_down_requestors do reply_message_for=${requestor#*:} \ mom_unicast_message \ ${requestor%:*} stop_service_reply_message >/dev/null done