Project

General

Profile

Download (19.1 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * vslb.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2005-2008 Bill Marquette
7
 * Copyright (c) 2004-2016 Rubicon Communications, LLC (Netgate)
8
 * All rights reserved.
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions are met:
12
 *
13
 * 1. Redistributions of source code must retain the above copyright notice,
14
 *    this list of conditions and the following disclaimer.
15
 *
16
 * 2. Redistributions in binary form must reproduce the above copyright
17
 *    notice, this list of conditions and the following disclaimer in
18
 *    the documentation and/or other materials provided with the
19
 *    distribution.
20
 *
21
 * 3. All advertising materials mentioning features or use of this software
22
 *    must display the following acknowledgment:
23
 *    "This product includes software developed by the pfSense Project
24
 *    for use in the pfSense® software distribution. (http://www.pfsense.org/).
25
 *
26
 * 4. The names "pfSense" and "pfSense Project" must not be used to
27
 *    endorse or promote products derived from this software without
28
 *    prior written permission. For written permission, please contact
29
 *    coreteam@pfsense.org.
30
 *
31
 * 5. Products derived from this software may not be called "pfSense"
32
 *    nor may "pfSense" appear in their names without prior written
33
 *    permission of the Electric Sheep Fencing, LLC.
34
 *
35
 * 6. Redistributions of any form whatsoever must retain the following
36
 *    acknowledgment:
37
 *
38
 * "This product includes software developed by the pfSense Project
39
 * for use in the pfSense software distribution (http://www.pfsense.org/).
40
 *
41
 * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
42
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
44
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
45
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
47
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
48
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
50
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
51
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
52
 * OF THE POSSIBILITY OF SUCH DAMAGE.
53
 */
54

    
55
/* include all configuration functions */
56

    
57
class Monitor {
58
	private $conf = array();
59
	function __construct($config) {
60
		$this->conf = $config;
61
	}
62

    
63
	public function p() {
64
		return "check {$this->get('proto')}";
65
	}
66
	private function get($var) {
67
		return isset($this->$var) ? $this->$var : "";
68
	}
69
	protected function config($element) {
70
		return isset($this->conf[$element]) ? $this->conf[$element] : "";
71
	}
72
}
73

    
74
class TCPMonitor extends Monitor {
75
	protected $proto = 'tcp';
76
}
77

    
78
class SSLMonitor extends Monitor {
79
	protected $proto = 'ssl';
80
}
81

    
82
class ICMPMonitor extends Monitor {
83
	protected $proto = 'icmp';
84
}
85

    
86
class HTTPMonitor extends Monitor {
87
	protected $proto = 'http';
88
	function __construct($config) {
89
		parent::__construct($config);
90
	}
91
	public function p() {
92
		$method = ($this->code() != "") ? $this->code() : $this->digest();
93
		return "check {$this->proto} {$this->path()} {$this->host()} {$method}";
94
	}
95

    
96
	private function path() {
97
		return $this->config('path') != "" ? "'{$this->config('path')}'" : "";
98
	}
99

    
100
	private function host() {
101
		return $this->config('host') != "" ? "host {$this->config('host')}" : "";
102
	}
103

    
104
	private function code() {
105
		return $this->config('code') != "" ? "code {$this->config('code')}" : "";
106
	}
107

    
108
	private function digest() {
109
		return $this->config('digest') != "" ? "digest {$this->config('digest')}" : "";
110
	}
111
}
112

    
113
class HTTPSMonitor extends HTTPMonitor {
114
	protected $proto = 'https';
115
}
116

    
117
class SendMonitor extends Monitor {
118
	private $proto = 'send';
119
	function __construct($config) {
120
		parent::__construct($config);
121
	}
122
	public function p() {
123
		return "check {$this->proto} {$this->data()} expect {$this->pattern()} {$this->ssl()}";
124
	}
125

    
126

    
127
	private function data() {
128
		return $this->config('send') != "" ? "\"{$this->config('send')}\"" : "\"\"";
129
	}
130

    
131
	private function pattern() {
132
		return $this->config('expect') != "" ? "\"{$this->config('expect')}\"" : "\"\"";
133
	}
134

    
135
	private function ssl() {
136
		return $this->config('ssl') == true ? "ssl" : "";
137
	}
138
}
139

    
140
function echo_lbaction($action) {
141
	global $config;
142

    
143
	// Index actions by name
144
	$actions_a = array();
145
	for ($i = 0; isset($config['load_balancer']['lbaction'][$i]); $i++) {
146
		$actions_a[$config['load_balancer']['lbaction'][$i]['name']] = $config['load_balancer']['lbaction'][$i];
147
	}
148

    
149
	$ret = "";
150
	$ret .= "{$actions_a[$action]['direction']} {$actions_a[$action]['type']} {$actions_a[$action]['action']}";
151
	switch ($actions_a[$action]['action']) {
152
		case 'append':
153
			$ret .= " \"{$actions_a[$action]['options']['value']}\" to \"{$actions_a[$action]['options']['akey']}\"";
154
			break;
155
		case 'change':
156
			$ret .= " \"{$actions_a[$action]['options']['akey']}\" to \"{$actions_a[$action]['options']['value']}\"";
157
			break;
158
		case 'expect':
159
			$ret .= " \"{$actions_a[$action]['options']['value']}\" from \"{$actions_a[$action]['options']['akey']}\"";
160
			break;
161
		case 'filter':
162
			$ret .= " \"{$actions_a[$action]['options']['value']}\" from \"{$actions_a[$action]['options']['akey']}\"";
163
			break;
164
		case 'hash':
165
			$ret .= " \"{$actions_a[$action]['options']['akey']}\"";
166
			break;
167
		case 'log':
168
			$ret .= " \"{$actions_a[$action]['options']['akey']}\"";
169
			break;
170
	}
171
	return $ret;
172
}
173

    
174
function relayd_configure($kill_first=false) {
175
	global $config, $g;
176

    
177
	// have to do this until every call to filter.inc is
178
	// require_once() instead of require().
179
	if (!function_exists('filter_expand_alias_array')) {
180
		require_once("filter.inc");
181
	}
182
	require_once("util.inc");
183

    
184
	$vs_a = $config['load_balancer']['virtual_server'];
185
	$pool_a = $config['load_balancer']['lbpool'];
186
	$protocol_a = $config['load_balancer']['lbprotocol'];
187
	$setting = $config['load_balancer']['setting'];
188

    
189
	$check_a = array();
190

    
191
	foreach ((array)$config['load_balancer']['monitor_type'] as $type) {
192
		switch ($type['type']) {
193
			case 'icmp':
194
				$mon = new ICMPMonitor($type['options']);
195
				break;
196
			case 'tcp':
197
				$mon = new TCPMonitor($type['options']);
198
				break;
199
			case 'http':
200
				$mon = new HTTPMonitor($type['options']);
201
				break;
202
			case 'https':
203
				$mon = new HTTPSMonitor($type['options']);
204
				break;
205
			case 'send':
206
				$mon = new SendMonitor($type['options']);
207
				break;
208
		}
209
		if ($mon) {
210
			$check_a[$type['name']] = $mon->p();
211
		}
212
	}
213

    
214

    
215
	$fd = fopen("{$g['varetc_path']}/relayd.conf", "w");
216
	$conf .= "log updates \n";
217

    
218
	/* Global timeout, interval and prefork settings
219
	   if not specified by the user:
220
	   - use a 1000 ms timeout value as in pfsense 2.0.1 and above
221
	   - leave interval and prefork empty, relayd will use its default values */
222

    
223
	if (isset($setting['timeout']) && !empty($setting['timeout'])) {
224
		$conf .= "timeout ".$setting['timeout']." \n";
225
	} else {
226
		$conf .= "timeout 1000 \n";
227
	}
228

    
229
	if (isset($setting['interval']) && !empty($setting['interval'])) {
230
		$conf .= "interval ".$setting['interval']." \n";
231
	}
232

    
233
	if (isset($setting['prefork']) && !empty($setting['prefork'])) {
234
		$conf .= "prefork ".$setting['prefork']." \n";
235
	}
236

    
237
	/* reindex pools by name as we loop through the pools array */
238
	$pools = array();
239
	/* Virtual server pools */
240
	if (is_array($pool_a)) {
241
		for ($i = 0; isset($pool_a[$i]); $i++) {
242
			if (is_array($pool_a[$i]['servers'])) {
243
				if (!empty($pool_a[$i]['retry'])) {
244
					$retrytext = " retry {$pool_a[$i]['retry']}";
245
				} else {
246
					$retrytext = "";
247
				}
248
				$conf .= "table <{$pool_a[$i]['name']}> {\n";
249
				foreach ($pool_a[$i]['servers'] as $server) {
250
					if (is_subnetv4($server)) {
251
						foreach (subnetv4_expand($server) as $ip) {
252
							$conf .= "\t{$ip}{$retrytext}\n";
253
						}
254
					} else {
255
						$conf .= "\t{$server}{$retrytext}\n";
256
					}
257
				}
258
				$conf .= "}\n";
259
				/* Index by name for easier fetching when we loop through the virtual servers */
260
				$pools[$pool_a[$i]['name']] = $pool_a[$i];
261
			}
262
		}
263
	}
264
//  if (is_array($protocol_a)) {
265
//    for ($i = 0; isset($protocol_a[$i]); $i++) {
266
//      $proto = "{$protocol_a[$i]['type']} protocol \"{$protocol_a[$i]['name']}\" {\n";
267
//      if (is_array($protocol_a[$i]['lbaction'])) {
268
//        if ($protocol_a[$i]['lbaction'][0] == "") {
269
//          continue;
270
//        }
271
//        for ($a = 0; isset($protocol_a[$i]['lbaction'][$a]); $a++) {
272
//          $proto .= "  " . echo_lbaction($protocol_a[$i]['lbaction'][$a]) . "\n";
273
//        }
274
//      }
275
//      $proto .= "}\n";
276
//      $conf .= $proto;
277
//    }
278
//  }
279

    
280
	$conf .= "dns protocol \"dnsproto\" {\n";
281
	$conf .= "\t" . "tcp { nodelay, sack, socket buffer 1024, backlog 1000 }\n";
282
	$conf .= "}\n";
283

    
284
	if (is_array($vs_a)) {
285
		for ($i = 0; isset($vs_a[$i]); $i++) {
286

    
287
			$append_port_to_name = false;
288
			if (is_alias($pools[$vs_a[$i]['poolname']]['port'])) {
289
				$dest_port_array = filter_expand_alias_array($pools[$vs_a[$i]['poolname']]['port']);
290
				$append_port_to_name = true;
291
			} else {
292
				$dest_port_array = array($pools[$vs_a[$i]['poolname']]['port']);
293
			}
294
			if (is_alias($vs_a[$i]['port'])) {
295
				$src_port_array = filter_expand_alias_array($vs_a[$i]['port']);
296
				$append_port_to_name = true;
297
			} else if ($vs_a[$i]['port']) {
298
				$src_port_array = array($vs_a[$i]['port']);
299
			} else {
300
				$src_port_array = $dest_port_array;
301
			}
302

    
303
			$append_ip_to_name = false;
304
			if (is_alias($vs_a[$i]['ipaddr'])) {
305
				$ip_list = array();
306
				foreach (filter_expand_alias_array($vs_a[$i]['ipaddr']) as $item) {
307
					log_error("item is $item");
308
					if (is_subnetv4($item)) {
309
						$ip_list = array_merge($ip_list, subnetv4_expand($item));
310
					} else {
311
						$ip_list[] = $item;
312
					}
313
				}
314
				$append_ip_to_name = true;
315
			} else if (is_subnetv4($vs_a[$i]['ipaddr'])) {
316
				$ip_list = subnetv4_expand($vs_a[$i]['ipaddr']);
317
				$append_ip_to_name = true;
318
			} else {
319
				$ip_list = array($vs_a[$i]['ipaddr']);
320
			}
321

    
322
			for ($j = 0; $j < count($ip_list); $j += 1) {
323
				$ip = $ip_list[$j];
324
				for ($k = 0; $k < count($src_port_array) && $k < count($dest_port_array); $k += 1) {
325
					$src_port = $src_port_array[$k];
326
					$dest_port = $dest_port_array[$k];
327
					if (is_portrange($dest_port)) {
328
						$dest_ports = explode(':', $dest_port);
329
						$dest_port = $dest_ports[0];
330
					}
331

    
332
					$name = $vs_a[$i]['name'];
333
					if ($append_ip_to_name) {
334
						$name .= "_" . $j;
335
					}
336
					if ($append_port_to_name) {
337
						$name .= "_" . str_replace(":", "_", $src_port);
338
					}
339

    
340
					if (($vs_a[$i]['mode'] == 'relay') || ($vs_a[$i]['relay_protocol'] == 'dns')) {
341
						$conf .= "relay \"{$name}\" {\n";
342
						$conf .= "  listen on {$ip} port {$src_port}\n";
343

    
344
						if ($vs_a[$i]['relay_protocol'] == "dns") {
345
							$conf .= "  protocol \"dnsproto\"\n";
346
						} else {
347
							$conf .= "  protocol \"{$vs_a[$i]['relay_protocol']}\"\n";
348
						}
349
						$lbmode = "";
350
						if ($pools[$vs_a[$i]['poolname']]['mode'] == "loadbalance") {
351
							$lbmode = "mode loadbalance";
352
						}
353

    
354
						$conf .= "  forward to <{$vs_a[$i]['poolname']}> port {$dest_port} {$lbmode} {$check_a[$pools[$vs_a[$i]['poolname']]['monitor']]} \n";
355

    
356
						if (isset($vs_a[$i]['sitedown']) && strlen($vs_a[$i]['sitedown']) > 0 && ($vs_a[$i]['relay_protocol'] != 'dns')) {
357
							$conf .= "  forward to <{$vs_a[$i]['sitedown']}> port {$dest_port} {$lbmode} {$check_a[$pools[$vs_a[$i]['poolname']]['monitor']]} \n";
358
						}
359
						$conf .= "}\n";
360
					} else {
361
						$conf .= "redirect \"{$name}\" {\n";
362
						$conf .= "  listen on {$ip} port {$src_port}\n";
363
						$conf .= "  forward to <{$vs_a[$i]['poolname']}> port {$dest_port} {$check_a[$pools[$vs_a[$i]['poolname']]['monitor']]} \n";
364

    
365
						if (isset($config['system']['lb_use_sticky'])) {
366
							$conf .= "  sticky-address\n";
367
						}
368

    
369
						/* sitedown MUST use the same port as the primary pool - sucks, but it's a relayd thing */
370
						if (isset($vs_a[$i]['sitedown']) && strlen($vs_a[$i]['sitedown']) > 0 && ($vs_a[$i]['relay_protocol'] != 'dns')) {
371
							$conf .= "  forward to <{$vs_a[$i]['sitedown']}> port {$dest_port} {$check_a[$pools[$vs_a[$i]['sitedown']]['monitor']]} \n";
372
						}
373

    
374
						$conf .= "}\n";
375
					}
376
				}
377
			}
378
		}
379
	}
380
	fwrite($fd, $conf);
381
	fclose($fd);
382

    
383
	if (is_process_running('relayd')) {
384
		if (!empty($vs_a)) {
385
			if ($kill_first) {
386
				sigkillbyname("relayd", "TERM");
387
				/* Remove all active relayd anchors now that relayd is no longer running. */
388
				cleanup_lb_anchor("*");
389
				mwexec("/usr/local/sbin/relayd -f {$g['varetc_path']}/relayd.conf");
390
			} else {
391
				// it's running and there is a config, just reload
392
				mwexec("/usr/local/sbin/relayctl reload");
393
			}
394
		} else {
395
			/*
396
			 * XXX: Something breaks our control connection with relayd
397
			 * and makes 'relayctl stop' not work
398
			 * rule reloads are the current suspect
399
			 * mwexec('/usr/local/sbin/relayctl stop');
400
			 *  returns "command failed"
401
			 */
402
			sigkillbyname("relayd", "TERM");
403
			/* Remove all active relayd anchors now that relayd is no longer running. */
404
			cleanup_lb_anchor("*");
405
		}
406
	} else {
407
		if (!empty($vs_a)) {
408
			// not running and there is a config, start it
409
			/* Remove all active relayd anchors so it can start fresh. */
410
			cleanup_lb_anchor("*");
411
			mwexec("/usr/local/sbin/relayd -f {$g['varetc_path']}/relayd.conf");
412
		}
413
	}
414
}
415

    
416
function get_lb_redirects() {
417
/*
418
# relayctl show summary
419
Id   Type      Name                      Avlblty Status
420
1    redirect  testvs2                           active
421
5    table     test2:80                          active (3 hosts up)
422
11   host      192.168.1.2               91.55%  up
423
10   host      192.168.1.3               100.00% up
424
9    host      192.168.1.4               88.73%  up
425
3    table     test:80                           active (1 hosts up)
426
7    host      192.168.1.2               66.20%  down
427
6    host      192.168.1.3               97.18%  up
428
0    redirect  testvs                            active
429
3    table     test:80                           active (1 hosts up)
430
7    host      192.168.1.2               66.20%  down
431
6    host      192.168.1.3               97.18%  up
432
4    table     testvs-sitedown:80                active (1 hosts up)
433
8    host      192.168.1.4               84.51%  up
434
# relayctl show redirects
435
Id   Type      Name                      Avlblty Status
436
1    redirect  testvs2                           active
437
0    redirect  testvs                            active
438
# relayctl show redirects
439
Id   Type      Name                      Avlblty Status
440
1    redirect  testvs2                           active
441
		   total: 2 sessions
442
		   last: 2/60s 2/h 2/d sessions
443
		   average: 1/60s 0/h 0/d sessions
444
0    redirect  testvs                            active
445
*/
446
	$rdr_a = array();
447
	exec('/usr/local/sbin/relayctl show redirects 2>&1', $rdr_a);
448
	$relay_a = array();
449
	exec('/usr/local/sbin/relayctl show relays 2>&1', $relay_a);
450
	$vs = array();
451
	$cur_entry = "";
452
	for ($i = 0; isset($rdr_a[$i]); $i++) {
453
		$line = $rdr_a[$i];
454
		if (preg_match("/^[0-9]+/", $line)) {
455
			$regs = array();
456
			if ($x = preg_match("/^[0-9]+\s+redirect\s+([^\s]+)\s+([^\s]+)/", $line, $regs)) {
457
				$cur_entry = trim($regs[1]);
458
				$vs[trim($regs[1])] = array();
459
				$vs[trim($regs[1])]['status'] = trim($regs[2]);
460
			}
461
		} elseif (($x = preg_match("/^\s+total:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
462
			$vs[$cur_entry]['total'] = trim($regs[1]);
463
		} elseif (($x = preg_match("/^\s+last:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
464
			$vs[$cur_entry]['last'] = trim($regs[1]);
465
		} elseif (($x = preg_match("/^\s+average:(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
466
			$vs[$cur_entry]['average'] = trim($regs[1]);
467
		}
468
	}
469
	$cur_entry = "";
470
	for ($i = 0; isset($relay_a[$i]); $i++) {
471
		$line = $relay_a[$i];
472
		if (preg_match("/^[0-9]+/", $line)) {
473
			$regs = array();
474
			if ($x = preg_match("/^[0-9]+\s+relay\s+([^\s]+)\s+([^\s]+)/", $line, $regs)) {
475
				$cur_entry = trim($regs[1]);
476
				$vs[trim($regs[1])] = array();
477
				$vs[trim($regs[1])]['status'] = trim($regs[2]);
478
			}
479
		} elseif (($x = preg_match("/^\s+total:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
480
			$vs[$cur_entry]['total'] = trim($regs[1]);
481
		} elseif (($x = preg_match("/^\s+last:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
482
			$vs[$cur_entry]['last'] = trim($regs[1]);
483
		} elseif (($x = preg_match("/^\s+average:(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) {
484
			$vs[$cur_entry]['average'] = trim($regs[1]);
485
		}
486
	}
487
	return $vs;
488
}
489

    
490
function get_lb_summary() {
491
	$relayctl = array();
492
	exec('/usr/local/sbin/relayctl show summary 2>&1', $relayctl);
493
	$relay_hosts=Array();
494
	foreach ((array) $relayctl as $line) {
495
		$t = explode("\t", $line);
496
		switch (trim($t[1])) {
497
			case "table":
498
				$curpool=trim($t[2]);
499
				break;
500
			case "host":
501
				$curhost=trim($t[2]);
502
				$relay_hosts[$curpool][$curhost]['avail']=trim($t[3]);
503
				$relay_hosts[$curpool][$curhost]['state']=trim($t[4]);
504
				break;
505
		}
506
	}
507
	return $relay_hosts;
508
}
509

    
510
/* Get a list of all relayd virtual server anchors */
511
function get_lb_anchors() {
512
	/* NOTE: These names come back prepended with "relayd/" e.g. "relayd/MyVSName" */
513
	return explode("\n", trim(`/sbin/pfctl -sA -a relayd | /usr/bin/awk '{print $1;}'`));
514
}
515

    
516
/* Remove NAT rules from a relayd anchor that is no longer in use.
517
	$anchorname can either be * to clear all anchors or a specific anchor name.*/
518
function cleanup_lb_anchor($anchorname = "*") {
519
	$lbanchors = get_lb_anchors();
520
	foreach ($lbanchors as $lba) {
521
		/* Skip empty/blank results */
522
		if (empty($lba)) {
523
			continue;
524
		}
525
		if (($anchorname == "*") || ($lba == "relayd/{$anchorname}")) {
526
			/* Flush both the NAT and the Table for the anchor, so it will be completely removed by pf. */
527
			mwexec("/sbin/pfctl -a " . escapeshellarg($lba) . " -F nat");
528
			mwexec("/sbin/pfctl -a " . escapeshellarg($lba) . " -F Tables");
529
		}
530
	}
531
}
532

    
533
/* Mark an anchor for later cleanup. This will allow us to remove an old VS name */
534
function cleanup_lb_mark_anchor($name) {
535
	global $g;
536
	/* Nothing to do! */
537
	if (empty($name)) {
538
		return;
539
	}
540
	$filename = "{$g['tmp_path']}/relayd_anchors_remove";
541
	$cleanup_anchors = array();
542
	/* Read in any currently unapplied name changes */
543
	if (file_exists($filename)) {
544
		$cleanup_anchors = explode("\n", file_get_contents($filename));
545
	}
546
	/* Only add the anchor to the list if it's not already there. */
547
	if (!in_array($name, $cleanup_anchors)) {
548
		$cleanup_anchors[] = $name;
549
	}
550
	file_put_contents($filename, implode("\n", $cleanup_anchors));
551
}
552

    
553
/* Cleanup relayd anchors that have been marked for cleanup. */
554
function cleanup_lb_marked() {
555
	global $g, $config;
556
	$filename = "{$g['tmp_path']}/relayd_anchors_remove";
557
	$cleanup_anchors = array();
558
	/* Nothing to do! */
559
	if (!file_exists($filename)) {
560
		return;
561
	} else {
562
		$cleanup_anchors = explode("\n", file_get_contents($filename));
563
		/* Nothing to do! */
564
		if (empty($cleanup_anchors)) {
565
			return;
566
		}
567
	}
568

    
569
	/* Load current names so we can make sure we don't remove an anchor that is still in use. */
570
	$vs_a = $config['load_balancer']['virtual_server'];
571
	$active_vsnames = array();
572
	if (is_array($vs_a)) {
573
		foreach ($vs_a as $vs) {
574
			$active_vsnames[] = $vs['name'];
575
		}
576
	}
577

    
578
	foreach ($cleanup_anchors as $anchor) {
579
		/* Only cleanup an anchor if it is not still active. */
580
		if (!in_array($anchor, $active_vsnames)) {
581
			cleanup_lb_anchor($anchor);
582
		}
583
	}
584
	unlink_if_exists($filename);
585
}
586

    
587
?>
(58-58/65)