From 8751763c6443c1ea453ecc4e7760bb2bcc9a048d Mon Sep 17 00:00:00 2001 From: Chris Buechler Date: Tue, 19 May 2009 20:24:24 -0400 Subject: [PATCH] merge Ermal's CP locking changes --- etc/inc/captiveportal.inc | 114 +++++++++---------------- etc/inc/util.inc | 38 +++++++++ usr/local/captiveportal/index.php | 18 ++-- usr/local/www/status_captiveportal.php | 49 +++++------ 4 files changed, 110 insertions(+), 109 deletions(-) diff --git a/etc/inc/captiveportal.inc b/etc/inc/captiveportal.inc index 462cbb5524..b49db26769 100644 --- a/etc/inc/captiveportal.inc +++ b/etc/inc/captiveportal.inc @@ -46,6 +46,8 @@ $lockfile = "{$g['varrun_path']}/captiveportal.lock"; function captiveportal_configure() { global $config, $g; + $captiveportallck = lock('captiveportal'); + if (isset($config['captiveportal']['enable']) && (($config['captiveportal']['interface'] == "lan") || isset($config['interfaces'][$config['captiveportal']['interface']]['enable']))) { @@ -81,7 +83,7 @@ function captiveportal_configure() { mwexec("kldload dummynet"); /* stop accounting on all clients */ - captiveportal_radius_stop_all(); + captiveportal_radius_stop_all(true); /* initialize minicron interval value */ $croninterval = $config['captiveportal']['croninterval'] ? $config['captiveportal']['croninterval'] : 60; @@ -229,9 +231,9 @@ EOD; "/etc/rc.prunecaptiveportal"); /* generate passthru mac database */ - captiveportal_passthrumac_configure(); + captiveportal_passthrumac_configure(true); /* create allowed ip database and insert ipfw rules to make it so */ - captiveportal_allowedip_configure(); + captiveportal_allowedip_configure(true); /* generate radius server database */ if ($config['captiveportal']['radiusip'] && (!isset($config['captiveportal']['auth_method']) || @@ -277,7 +279,7 @@ EOD; killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid"); killbypid("{$g['varrun_path']}/minicron.pid"); - captiveportal_radius_stop_all(); + captiveportal_radius_stop_all(true); mwexec("/sbin/sysctl net.link.ether.ipfw=0"); @@ -298,8 +300,7 @@ EOD; mwexec("/sbin/ipfw -f delete set 3"); } } - - captiveportal_unlock(); + unlock($captiveportallck); return 0; } @@ -458,7 +459,7 @@ function captiveportal_prune_old() { if (!$timeout && !$idletimeout && !isset($config['captiveportal']['reauthenticate']) && !isset($config['captiveportal']['radiussession_timeout'])) return; - captiveportal_lock(); + $captiveportallck = lock('captiveportal'); /* read database */ $cpdb = captiveportal_read_db(); @@ -585,7 +586,7 @@ function captiveportal_prune_old() { /* write database */ captiveportal_write_db($cpdb); - captiveportal_unlock(); + unlock($captiveportallck); } /* remove a single client according to the DB entry */ @@ -632,7 +633,7 @@ function captiveportal_disconnect_client($id,$term_cause = 1) { global $g, $config; - captiveportal_lock(); + $captiveportallck = lock('captiveportal'); /* read database */ $cpdb = captiveportal_read_db(); @@ -651,17 +652,19 @@ function captiveportal_disconnect_client($id,$term_cause = 1) { /* write database */ captiveportal_write_db($cpdb); - captiveportal_unlock(); + unlock($captiveportallck); } /* send RADIUS acct stop for all current clients */ -function captiveportal_radius_stop_all() { +function captiveportal_radius_stop_all($lock = false) { global $g, $config; if (!isset($config['captiveportal']['radacct_enable'])) return; - captiveportal_lock(); + if (!$lock) + $captiveportallck = lock('captiveportal'); + $cpdb = captiveportal_read_db(); $radiusservers = captiveportal_get_radius_servers(); @@ -680,13 +683,15 @@ function captiveportal_radius_stop_all() { 7); // Admin Reboot } } - captiveportal_unlock(); + if (!$lock) + unlock($captiveportallck); } -function captiveportal_passthrumac_configure() { +function captiveportal_passthrumac_configure($lock = false) { global $config, $g; - captiveportal_lock(); + if (!$lock) + $captiveportallck = lock('captiveportal'); /* clear out passthru macs, if necessary */ unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db"); @@ -696,7 +701,8 @@ function captiveportal_passthrumac_configure() { $fd = @fopen("{$g['vardb_path']}/captiveportal_mac.db", "w"); if (!$fd) { printf("Error: cannot open passthru mac DB file in captiveportal_passthrumac_configure().\n"); - captiveportal_unlock(); + if (!$lock) + unlock($captiveportallck); return 1; } @@ -725,16 +731,17 @@ function captiveportal_passthrumac_configure() { mwexec("/sbin/ipfw add 50 skipto 29900 ip from any to any MAC any {$ptm['mac']} keep-state"); } } - - captiveportal_unlock(); + if (!$lock) + unlock($captiveportallck); return 0; } -function captiveportal_allowedip_configure() { +function captiveportal_allowedip_configure($lock = false) { global $config, $g; - captiveportal_lock(); + if (!$lock) + $captiveportallck = lock('captiveportal'); /* clear out existing allowed ips, if necessary */ if (file_exists("{$g['vardb_path']}/captiveportal_ip.db")) { @@ -763,7 +770,8 @@ function captiveportal_allowedip_configure() { $fd = @fopen("{$g['vardb_path']}/captiveportal_ip.db", "w"); if (!$fd) { printf("Error: cannot open allowed ip DB file in captiveportal_allowedip_configure().\n"); - captiveportal_unlock(); + if (!$lock) + unlock($captiveportallck); return 1; } @@ -775,7 +783,8 @@ function captiveportal_allowedip_configure() { if (is_null($ruleno)) { printf("Error: system reached maximum login capacity, no free FW rulenos in captiveportal_allowedip_configure().\n"); fclose($fd); - captiveportal_unlock(); + if (!$lock) + unlock($captiveportallck); return 1; } @@ -796,7 +805,8 @@ function captiveportal_allowedip_configure() { fclose($fd); } - captiveportal_unlock(); + if (!$lock) + unlock($captiveportallck); return 0; } @@ -843,48 +853,6 @@ function captiveportal_get_radius_servers() { return false; } -/* lock captive portal information, decide that the lock file is stale after - 10 minutes and EXIT the process to not risk dataloss, issue warning in syslog every 1 minutes */ -function captiveportal_lock() { - - global $lockfile; - - $n = 1; - while ($n) { - /* open the lock file in append mode to avoid race condition */ - if ($fd = @fopen($lockfile, "x")) { - /* succeeded */ - fclose($fd); - if($n > 10) { - captiveportal_syslog("LOCKINFO: Waiting for lock for $n seconds/s!"); - } - return; - } else { - /* file locked, wait and try again */ - sleep(1); - - if(($n % 60) == 0) { - captiveportal_syslog("LOCKWARNING: waiting for lock for " . $n/60 . " minute/s!"); - if(($n % 600) == 0) { - captiveportal_syslog("LOCKERROR: waiting for lock for 10 minute/s - EXITING PROCESS!"); - die("Can't get a lock"); - } - } - } - $n++; - } - /* we never get here */ -} - -/* unlock captive portal information file */ -function captiveportal_unlock() { - - global $lockfile; - - if (file_exists($lockfile)) - unlink($lockfile); -} - /* log successful captive portal authentication to syslog */ /* part of this code from php.net */ function captiveportal_logportalauth($user,$mac,$ip,$status, $message = null) { @@ -912,16 +880,17 @@ function radius($username,$password,$clientip,$clientmac,$type) { global $g, $config; /* Start locking from the beginning of an authentication session */ - captiveportal_lock(); + $captiveportallck = lock('captiveportal'); $ruleno = captiveportal_get_next_ipfw_ruleno(); + unlock($captiveportallck); + /* if the pool is empty, return apprioriate message and fail authentication */ if (is_null($ruleno)) { $auth_list = array(); $auth_list['auth_val'] = 1; $auth_list['error'] = "System reached maximum login capacity"; - captiveportal_unlock(); return $auth_list; } @@ -934,6 +903,8 @@ function radius($username,$password,$clientip,$clientmac,$type) { $clientmac, $ruleno); + + $captiveportallck = lock('captiveportal'); if ($auth_list['auth_val'] == 2) { captiveportal_logportalauth($username,$clientmac,$clientip,$type); $sessionid = portal_allow($clientip, @@ -943,9 +914,8 @@ function radius($username,$password,$clientip,$clientmac,$type) { $auth_list, $ruleno); } - else { - captiveportal_unlock(); - } + + unlock($captiveportallck); return $auth_list; @@ -956,7 +926,7 @@ function captiveportal_read_db() { global $g; - $cpdb = array(); + $cpdb = array(); $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r"); if ($fd) { while (!feof($fd)) { @@ -1134,4 +1104,4 @@ function portal_mac_fixed($clientmac) { return FALSE ; } -?> \ No newline at end of file +?> diff --git a/etc/inc/util.inc b/etc/inc/util.inc index 47fde5b253..b7e7eaee98 100644 --- a/etc/inc/util.inc +++ b/etc/inc/util.inc @@ -343,6 +343,44 @@ function get_interface_list($mode = "active", $keyby = "physical", $vfaces = "") return $iflist; } +/* lock configuration file */ +function lock($lock) { + global $g, $cfglckkeyconsumers; + + if (!$lock) + die("WARNING: You must give a name as parameter to lock() function."); + if (!file_exists("{$g['tmp_path']}/{$lock}.lock")) + @touch("{$g['tmp_path']}/{$lock}.lock"); + $config_lock_key = ftok("{$g['tmp_path']}/{$lock}.lock", 'a'); + $cfglckkey = sem_get($config_lock_key, 1); + $cfglckkeyconsumers++; + if (!sem_acquire($cfglckkey)) { + log_error("WARNING: lock() - Could not acquire {$lock} lock!"); + sem_remove($cfglckkey); + return NULL; + } else if ($g['debug']) + log_error("lock() - Got {$file} lock."); + + return $cfglckkey; +} + +/* unlock configuration file */ +function unlock($cfglckkey = 0) { + global $g, $cfglckkeyconsumers; + + if (!$cfglckkey) + return; + + if (!sem_release($cfglckkey)) + log_error("WARNING: unlock() - Could not unlock lock."); + else { + if ($g['debug']) + log_error("Released lock."); + sem_remove($cfglckkey); + } + $cfglckkeyconsumers--; +} + /* wrapper for exec() */ function mwexec($command, $mute = true) { diff --git a/usr/local/captiveportal/index.php b/usr/local/captiveportal/index.php index 8b4d3f44a8..8e2e2a7910 100755 --- a/usr/local/captiveportal/index.php +++ b/usr/local/captiveportal/index.php @@ -219,8 +219,10 @@ function portal_allow($clientip,$clientmac,$username,$password = null, $attribut global $redirurl, $g, $config, $url_redirection, $type; /* See if a ruleno is passed, if not start locking the sessions because this means there isn't one atm */ + $lockedcpnow = false; if ($ruleno == null) { - captiveportal_lock(); + $cplock = lock('captiveportal'); + $lockedcpnow = true; $ruleno = captiveportal_get_next_ipfw_ruleno(); } @@ -228,7 +230,8 @@ function portal_allow($clientip,$clientmac,$username,$password = null, $attribut if (is_null($ruleno)) { portal_reply_page($redirurl, "error", "System reached maximum login capacity"); log_error("WARNING! Captive portal has reached maximum login capacity"); - captiveportal_unlock(); + if ($lockedcpnow == true) + unlock($cplock); exit; } @@ -325,6 +328,8 @@ function portal_allow($clientip,$clientmac,$username,$password = null, $attribut /* rewrite information to database */ captiveportal_write_db($cpdb); + if ($lockedcpnow == true) + unlock($cplock); /* redirect user to desired destination */ if ($url_redirection) $my_redirurl = $url_redirection; @@ -373,11 +378,9 @@ document.location.href="{$my_redirurl}"; EOD; } else { - captiveportal_unlock(); header("Location: " . $my_redirurl); return $sessionid; } - captiveportal_unlock(); return $sessionid; } @@ -390,7 +393,8 @@ function disconnect_client($sessionid, $logoutReason = "LOGOUT", $term_cause = 1 global $g, $config; - captiveportal_lock(); + $cplock = lock('captiveportal'); + /* read database */ $cpdb = captiveportal_read_db(); @@ -409,7 +413,7 @@ function disconnect_client($sessionid, $logoutReason = "LOGOUT", $term_cause = 1 /* write database */ captiveportal_write_db($cpdb); - captiveportal_unlock(); + unlock($cplock); } -?> \ No newline at end of file +?> diff --git a/usr/local/www/status_captiveportal.php b/usr/local/www/status_captiveportal.php index 8f57a0a113..78151e52bd 100755 --- a/usr/local/www/status_captiveportal.php +++ b/usr/local/www/status_captiveportal.php @@ -57,37 +57,26 @@ function clientcmp($a, $b) { } $cpdb = array(); -captiveportal_lock(); -$fp = @fopen("{$g['vardb_path']}/captiveportal.db","r"); - -if ($fp) { - while (!feof($fp)) { - $line = trim(fgets($fp)); - if ($line) { - $cpent = explode(",", $line); - if ($_GET['showact']) - $cpent[5] = captiveportal_get_last_activity($cpent[1]); - $cpdb[] = $cpent; - } - } - - fclose($fp); - - if ($_GET['order']) { - if ($_GET['order'] == "ip") - $order = 2; - else if ($_GET['order'] == "mac") - $order = 3; - else if ($_GET['order'] == "user") - $order = 4; - else if ($_GET['order'] == "lastact") - $order = 5; - else - $order = 0; - usort($cpdb, "clientcmp"); - } +$cpcontents = file("/var/db/captiveportal.db", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); +foreach ($cpcontents as $cpcontent) { + $cpent = explode(",", $cpcontent); + if ($_GET['showact']) + $cpent[5] = captiveportal_get_last_activity($cpent[1]); + $cpdb[] = $cpent; +} +if ($_GET['order']) { + if ($_GET['order'] == "ip") + $order = 2; + else if ($_GET['order'] == "mac") + $order = 3; + else if ($_GET['order'] == "user") + $order = 4; + else if ($_GET['order'] == "lastact") + $order = 5; + else + $order = 0; + usort($cpdb, "clientcmp"); } -captiveportal_unlock(); ?>