X-Git-Url: http://git.sameswireless.fr/l2tpns.git/blobdiff_plain/99b207fddf711ac088ac59186ed7cd361b5fb9ed..133cc062ad0a02f2de24d70d4ed579786bd69a4d:/l2tpns.c?ds=sidebyside

diff --git a/l2tpns.c b/l2tpns.c
index 6a74ffd..6d3a106 100644
--- a/l2tpns.c
+++ b/l2tpns.c
@@ -4,7 +4,7 @@
 // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced
 // vim: sw=8 ts=8
 
-char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.18 2004-07-28 06:12:30 fred_nerk Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.24 2004-08-26 06:22:37 fred_nerk Exp $";
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -94,6 +94,7 @@ int bgp_configured = 0;
 struct config_descriptt config_values[] = {
 	CONFIG("debug", debug, INT),
 	CONFIG("log_file", log_filename, STRING),
+	CONFIG("pid_file", pid_file, STRING),
 	CONFIG("l2tp_secret", l2tpsecret, STRING),
 	CONFIG("primary_dns", default_dns1, IP),
 	CONFIG("secondary_dns", default_dns2, IP),
@@ -537,20 +538,28 @@ int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc)
 	if (CLI_HELP_REQUESTED)
 		return CLI_HELP_NO_ARGS;
 
-        cli_print(cli, "%7s %s", "Sess#", "IP Address");
+	cli_print(cli, "%7s %s", "Sess#", "IP Address");
 
-	for (i = 0; i < 256; ++i) {
-		if (!d[i]) continue;
+	for (i = 0; i < 256; ++i)
+	{
+		if (!d[i])
+			continue;
 		e = (char**) d[i];
-		for (j = 0; j < 256; ++j) {
-			if (!e[j]) continue;
+		for (j = 0; j < 256; ++j)
+		{
+			if (!e[j])
+				continue;
 			f = (char**) e[j];
-			for (k = 0; k < 256; ++k) {
-				if (!f[k]) continue;
+			for (k = 0; k < 256; ++k)
+			{
+				if (!f[k])
+					continue;
 				g = (char**)f[k];
-				for (l = 0; l < 256; ++l) {
-					if (!g[l]) continue;
-					cli_print(cli, "%7d %d.%d.%d.%d",  (int) g[l], i, j, k, l);
+				for (l = 0; l < 256; ++l)
+				{
+					if (!g[l])
+						continue;
+					cli_print(cli, "%7d %d.%d.%d.%d", (int) g[l], i, j, k, l);
 					++count;
 				}
 			}
@@ -572,7 +581,8 @@ sessionidt sessionbyuser(char *username)
 	int s;
 	CSTAT(call_sessionbyuser);
 
-	for (s = 1; s < MAXSESSION ; ++s) {
+	for (s = 1; s < MAXSESSION ; ++s)
+	{
 		if (session[s].walled_garden)
 			continue;		// Skip walled garden users.
 
@@ -741,7 +751,8 @@ void processipout(u8 * buf, int len)
 		static int rate = 0;	// Number of ICMP packets we've sent this second.
 		static int last = 0;	// Last time we reset the ICMP packet counter 'rate'.
 
-		if (last != time_now) {
+		if (last != time_now)
+		{
 			last = time_now;
 			rate = 0;
 		}
@@ -781,7 +792,8 @@ void processipout(u8 * buf, int len)
 	// Add on L2TP header
 	{
 		u8 *p = makeppp(b, sizeof(b), buf, len, t, s, PPPIP);
-		if (!p) {
+		if (!p)
+		{
 			log(3, session[s].ip, s, t, "failed to send packet in processipout.\n");
 			return;
 		}
@@ -807,7 +819,8 @@ void send_ipout(sessionidt s, u8 *buf, int len)
 
 	u8 b[MAXETHER + 20];
 
-	if (len < 0 || len > MAXETHER) {
+	if (len < 0 || len > MAXETHER)
+	{
 		log(1,0,0,0, "Odd size IP packet: %d bytes\n", len);
 		return;
 	}
@@ -832,7 +845,8 @@ void send_ipout(sessionidt s, u8 *buf, int len)
 	// Add on L2TP header
 	{
 		u8 *p = makeppp(b, sizeof(b),  buf, len, t, s, PPPIP);
-		if (!p) {
+		if (!p)
+		{
 			log(3, session[s].ip, s, t, "failed to send packet in send_ipout.\n");
 			return;
 		}
@@ -959,11 +973,16 @@ int throttle_session(sessionidt s, int throttle)
 	if (!session[s].tunnel)
 		return 0;	// No-one home.
 
-        if (!*session[s].user)
-                return 0; // User not logged in
+	if (!*session[s].user)
+	        return 0; // User not logged in
 
-	if (throttle) {
-		if (session[s].tbf_in || session[s].tbf_out) {
+	if (throttle)
+	{
+		int rate_in = throttle & 0x0000FFFF;
+		int rate_out = throttle >> 16;
+
+		if (session[s].tbf_in || session[s].tbf_out)
+		{
 			if (throttle == session[s].throttle)
 				return 1;
 
@@ -973,10 +992,12 @@ int throttle_session(sessionidt s, int throttle)
 			free_tbf(session[s].tbf_out);
 		}
 
-		session[s].tbf_in = new_tbf(s, throttle*1024/4, throttle*1024/8, send_ipin);
-		session[s].tbf_out = new_tbf(s, throttle*1024/4, throttle*1024/8, send_ipout);
+		if (rate_in) session[s].tbf_in = new_tbf(s, rate_in * 1024 / 4, rate_in * 1024 / 8, send_ipin);
+		if (rate_out) session[s].tbf_out = new_tbf(s, rate_out * 1024 / 4, rate_out * 1024 / 8, send_ipout);
 
-		if (throttle != session[s].throttle) {	// Changed. Flood to slaves.
+		if (throttle != session[s].throttle)
+		{
+			// Changed. Flood to slaves.
 			session[s].throttle = throttle;
 			cluster_send_session(s);
 		}
@@ -995,7 +1016,9 @@ int throttle_session(sessionidt s, int throttle)
 	free_tbf(session[s].tbf_out);
 	session[s].tbf_out = 0;
 
-	if (throttle != session[s].throttle) {	// Changed. Flood to slaves.
+	if (throttle != session[s].throttle)
+	{
+		// Changed. Flood to slaves.
 		session[s].throttle = throttle;
 		cluster_send_session(s);
 	}
@@ -1029,7 +1052,8 @@ void sessionshutdown(sessionidt s, char *reason)
 	}
 
 	// RADIUS Stop message
-	if (session[s].opened && !walled_garden && !dead) {
+	if (session[s].opened && !walled_garden && !dead)
+	{
 		u16 r = session[s].radius;
 		if (!r)
 		{
@@ -1104,7 +1128,8 @@ void sendipcp(tunnelidt t, sessionidt s)
 	}
 
 	q = makeppp(buf,sizeof(buf), 0, 0, t, s, PPPIPCP);
-	if (!q) {
+	if (!q)
+	{
 		log(3, session[s].ip, s, t, "failed to send packet in sendipcp.\n");
 		return;
 	}
@@ -1286,7 +1311,8 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
 		char called[MAXTEL] = ""; // called number
 		char calling[MAXTEL] = ""; // calling number
 
-		if (!config->cluster_iam_master) {
+		if (!config->cluster_iam_master)
+		{
 			master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port);
 			return;
 		}
@@ -1307,7 +1333,8 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
 				//
 				// Is this a duplicate of the first packet? (SCCRQ)
 				//
-			for ( i = 1; i <= config->cluster_highest_tunnelid ; ++i) {
+			for (i = 1; i <= config->cluster_highest_tunnelid ; ++i)
+			{
 				if (tunnel[i].state != TUNNELOPENING ||
 					tunnel[i].ip != ntohl(*(ipt *) & addr->sin_addr) ||
 					tunnel[i].port != ntohs(addr->sin_port) )
@@ -1317,7 +1344,8 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
 			}
 		}
 
-		if (!t) {
+		if (!t)
+		{
 			if (!(t = new_tunnel()))
 			{
 				log(1, ntohl(addr->sin_addr.s_addr), 0, 0, "No more tunnels\n");
@@ -1456,17 +1484,21 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
 					{
 						u16 rescode = ntohs(*(u16 *)(b));
 						const char* resdesc = "(unknown)";
-						if (message == 4) { /* StopCCN */
+						if (message == 4)
+						{ /* StopCCN */
 							if (rescode <= MAX_STOPCCN_RESULT_CODE)
 								resdesc = stopccn_result_codes[rescode];
-						} else if (message == 14) { /* CDN */
+						}
+						else if (message == 14)
+						{ /* CDN */
 							if (rescode <= MAX_CDN_RESULT_CODE)
 								resdesc = cdn_result_codes[rescode];
 						}
 
 						log(4, ntohl(addr->sin_addr.s_addr), s, t, "   Result Code %d: %s\n",
 							rescode, resdesc);
-						if (n >= 4) {
+						if (n >= 4)
+						{
 							u16 errcode = ntohs(*(u16 *)(b + 2));
 							const char* errdesc = "(unknown)";
 							if (errcode <= MAX_ERROR_CODE)
@@ -1779,6 +1811,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
 					// TBA
 					break;
 				case 12:      // ICCN
+					if ( amagic == 0) amagic = time_now;
 					session[s].magic = amagic; // set magic number
 					session[s].l2tp_flags = aflags; // set flags received
 					log(3, ntohl(addr->sin_addr.s_addr), s, t, "Magic %X Flags %X\n", amagic, aflags);
@@ -2034,7 +2067,7 @@ int regular_cleanups(void)
 		if (!session[s].tunnel)	// Session isn't in use
 			continue;
 
-		if (!session[s].die && session[s].ip && !(session[s].flags & SF_IPCP_ACKED) )
+		if (!session[s].die && session[s].ip && !(session[s].flags & SF_IPCP_ACKED))
 		{
 			// IPCP has not completed yet. Resend
 			log(3, session[s].ip, s, session[s].tunnel, "No ACK for initial IPCP ConfigReq... resending\n");
@@ -2064,7 +2097,8 @@ int regular_cleanups(void)
 			u8 b[MAXCONTROL] = {0};
 
 			u8 *q = makeppp(b, sizeof(b), 0, 0, session[s].tunnel, s, PPPLCP);
-			if (!q) {
+			if (!q)
+			{
 				log(3, session[s].ip, s, t, "failed to send ECHO packet.\n");
 				continue;
 			}
@@ -2078,7 +2112,6 @@ int regular_cleanups(void)
 					(int)(time_now - session[s].last_packet));
 			tunnelsend(b, 24, session[s].tunnel); // send it
 			if (++count >= MAX_ACTIONS) break;
-			continue;
 		}
 
 		// Check for actions requested from the CLI
@@ -2114,8 +2147,9 @@ int regular_cleanups(void)
 
 			if (a & CLI_SESS_THROTTLE)
 			{
-				log(2, 0, s, session[s].tunnel, "Throttling session by CLI (to %d)\n",
-				    cli_session_actions[s].throttle);
+				log(2, 0, s, session[s].tunnel, "Throttling session by CLI (to %dkb/s up and %dkb/s down)\n",
+				    cli_session_actions[s].throttle & 0xFFFF,
+				    cli_session_actions[s].throttle >> 16);
 
 				throttle_session(s, cli_session_actions[s].throttle);
 			}
@@ -2130,7 +2164,6 @@ int regular_cleanups(void)
 				cluster_send_session(s);
 
 			if (++count >= MAX_ACTIONS) break;
-			continue;
 		}
 	}
 
@@ -2156,18 +2189,31 @@ int regular_cleanups(void)
 int still_busy(void)
 {
 	int i;
-	static int last_talked = 0;
-	for (i = config->cluster_highest_tunnelid ; i > 0 ; --i) {
+	static clockt last_talked = 0;
+	static clockt start_busy_wait = 0;
+	if (start_busy_wait == 0)
+		start_busy_wait = TIME;
+
+	for (i = config->cluster_highest_tunnelid ; i > 0 ; --i)
+	{
 		if (!tunnel[i].controlc)
 			continue;
 
-		if (last_talked != TIME) {
+		if (last_talked != TIME)
+		{
 			log(2,0,0,0, "Tunnel %d still has un-acked control messages.\n", i);
 			last_talked = TIME;
 		}
 		return 1;
 	}
 
+	// We stop waiting for radius after BUSY_WAIT_TIMEOUT 1/10th seconds
+	if (abs(TIME - start_busy_wait) > BUSY_WAIT_TIMEOUT)
+	{
+		log(1, 0, 0, 0, "Giving up waiting for RADIUS to be empty. Shutting down anyway.\n");
+		return 0;
+	}
+
 	for (i = 1; i < MAXRADIUS; i++)
 	{
 		if (radius[i].state == RADIUSNULL)
@@ -2175,7 +2221,8 @@ int still_busy(void)
 	        if (radius[i].state == RADIUSWAIT)
 			continue;
 
-		if (last_talked != TIME) {
+		if (last_talked != TIME)
+		{
 			log(2,0,0,0, "Radius session %d is still busy (sid %d)\n", i, radius[i].session);
 			last_talked = TIME;
 		}
@@ -2300,7 +2347,8 @@ void mainloop(void)
 			for (i = 0; i < config->num_radfds; i++)
 				if (FD_ISSET(radfds[i], &r))
 					processrad(buf, recv(radfds[i], buf, sizeof(buf), 0), i);
-			if (FD_ISSET(cluster_sockfd, &r)) {
+			if (FD_ISSET(cluster_sockfd, &r))
+			{
 				int size;
 				size = recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen);
 				processcluster(buf, size, addr.sin_addr.s_addr);
@@ -2350,7 +2398,8 @@ void mainloop(void)
 			// Runs on all machines both master and slave.
 		{
 			static clockt last_run = 0;
-			if (last_run != TIME) {
+			if (last_run != TIME)
+			{
 				last_run = TIME;
 				tbf_run_timer();
 			}
@@ -2360,10 +2409,15 @@ void mainloop(void)
 		 * something to read, else under load this will never actually run....
 		 *
 		 */
-		if (config->cluster_iam_master && next_clean <= time_now) {
-			if (regular_cleanups()) { // Did it finish?
+		if (config->cluster_iam_master && next_clean <= time_now)
+		{
+			if (regular_cleanups())
+			{
+				// Did it finish?
 				next_clean = time_now + 1 ;	// Didn't finish. Check quickly.
-			} else {
+			}
+			else
+			{
 				next_clean = time_now + config->cleanup_interval; // Did. Move to next interval.
 			}
 		}
@@ -2473,7 +2527,8 @@ void initdata(void)
 	memset(ip_address_pool, 0, sizeof(ippoolt) * MAXIPPOOL);
 
 		// Put all the sessions on the free list marked as undefined.
-	for (i = 1; i < MAXSESSION - 1; i++) {
+	for (i = 1; i < MAXSESSION - 1; i++)
+	{
 		session[i].next = i + 1;
 		session[i].tunnel = T_UNDEF;	// mark it as not filled in.
 	}
@@ -2481,9 +2536,8 @@ void initdata(void)
 	sessionfree = 1;
 
 		// Mark all the tunnels as undefined (waiting to be filled in by a download).
-	for (i = 1; i < MAXTUNNEL- 1; i++) {
+	for (i = 1; i < MAXTUNNEL- 1; i++)
 		tunnel[i].state = TUNNELUNDEF;	// mark it as not filled in.
-	}
 
 	if (!*hostname)
 	{
@@ -2605,7 +2659,8 @@ void rebuild_address_pool(void)
 		//
 		// Zero the IP pool allocation, and build
 		// a map from IP address to pool index.
-	for (i = 1; i < MAXIPPOOL; ++i) {
+	for (i = 1; i < MAXIPPOOL; ++i)
+	{
 		ip_address_pool[i].assigned = 0;
 		ip_address_pool[i].session = 0;
 		if (!ip_address_pool[i].address)
@@ -2614,13 +2669,16 @@ void rebuild_address_pool(void)
 		cache_ipmap(ip_address_pool[i].address, -i);	// Map pool IP to pool index.
 	}
 
-	for (i = 0; i < MAXSESSION; ++i) {
+	for (i = 0; i < MAXSESSION; ++i)
+	{
 		int ipid;
 		if (!session[i].ip || !session[i].tunnel)
 			continue;
 		ipid = - lookup_ipmap(htonl(session[i].ip));
 
-		if (session[i].ip_pool_index < 0) {	// Not allocated out of the pool.
+		if (session[i].ip_pool_index < 0)
+		{
+			// Not allocated out of the pool.
 			if (ipid < 1)			// Not found in the pool either? good.
 				continue;
 
@@ -2631,7 +2689,8 @@ void rebuild_address_pool(void)
 		}
 
 
-		if (ipid > MAXIPPOOL || ipid < 0) {
+		if (ipid > MAXIPPOOL || ipid < 0)
+		{
 			log(0, 0, i, 0, "Session %d has a pool IP that's not found in the pool! (%d)\n", i, ipid);
 			ipid = -1;
 			session[i].ip_pool_index = ipid;
@@ -2787,54 +2846,55 @@ void snoop_send_packet(char *packet, u16 size, ipt destination, u16 port)
 
 void dump_acct_info()
 {
-    char filename[1024];
-    char timestr[64];
-    time_t t = time(NULL);
-    int i;
-    FILE *f = NULL;
+	char filename[1024];
+	char timestr[64];
+	time_t t = time(NULL);
+	int i;
+	FILE *f = NULL;
 
 
-    CSTAT(call_dump_acct_info);
+	CSTAT(call_dump_acct_info);
 
-    strftime(timestr, 64, "%Y%m%d%H%M%S", localtime(&t));
-    snprintf(filename, 1024, "%s/%s", config->accounting_dir, timestr);
+	strftime(timestr, 64, "%Y%m%d%H%M%S", localtime(&t));
+	snprintf(filename, 1024, "%s/%s", config->accounting_dir, timestr);
 
-    for (i = 0; i < MAXSESSION; i++)
-    {
-	if (!session[i].opened || !session[i].ip || !session[i].cin || !session[i].cout || !*session[i].user || session[i].walled_garden)
-		continue;
-	if (!f)
+	for (i = 0; i < MAXSESSION; i++)
 	{
-	    time_t now = time(NULL);
-	    if (!(f = fopen(filename, "w")))
-	    {
-		    log(0, 0, 0, 0, "Can't write accounting info to %s: %s\n", filename, strerror(errno));
-		    return;
-	    }
-	    log(3, 0, 0, 0, "Dumping accounting information to %s\n", filename);
-	    fprintf(f, "# dslwatch.pl dump file V1.01\n"
-		"# host: %s\n"
-		"# time: %ld\n"
-		"# uptime: %ld\n"
-		"# format: username ip qos uptxoctets downrxoctets\n",
-		    hostname,
-		    now,
-		    now - basetime);
-	}
-
-	log(4, 0, 0, 0, "Dumping accounting information for %s\n", session[i].user);
-	fprintf(f, "%s %s %d %u %u\n",
-	    session[i].user,				// username
-	    inet_toa(htonl(session[i].ip)),		// ip
-	    (session[i].throttle) ? 2 : 1,		// qos
-	    (u32)session[i].cin,		// uptxoctets
-	    (u32)session[i].cout);		// downrxoctets
-
-	session[i].pin = session[i].cin = 0;
-	session[i].pout = session[i].cout = 0;
-    }
-
-    if (f) fclose(f);
+		if (!session[i].opened || !session[i].ip || !session[i].cin || !session[i].cout || !*session[i].user || session[i].walled_garden)
+			continue;
+		if (!f)
+		{
+			time_t now = time(NULL);
+			if (!(f = fopen(filename, "w")))
+			{
+				log(0, 0, 0, 0, "Can't write accounting info to %s: %s\n", filename, strerror(errno));
+				return ;
+			}
+			log(3, 0, 0, 0, "Dumping accounting information to %s\n", filename);
+			fprintf(f, "# dslwatch.pl dump file V1.01\n"
+			        "# host: %s\n"
+			        "# time: %ld\n"
+			        "# uptime: %ld\n"
+			        "# format: username ip qos uptxoctets downrxoctets\n",
+			        hostname,
+			        now,
+			        now - basetime);
+		}
+
+		log(4, 0, 0, 0, "Dumping accounting information for %s\n", session[i].user);
+		fprintf(f, "%s %s %d %u %u\n",
+		        session[i].user,		// username
+		        inet_toa(htonl(session[i].ip)),	// ip
+		        (session[i].throttle) ? 2 : 1,	// qos
+		        (u32)session[i].cin,		// uptxoctets
+		        (u32)session[i].cout);		// downrxoctets
+
+		session[i].pin = session[i].cin = 0;
+		session[i].pout = session[i].cout = 0;
+	}
+
+	if (f)
+		fclose(f);
 }
 
 // Main program
@@ -2940,11 +3000,11 @@ int main(int argc, char *argv[])
 	signal(SIGPIPE, SIG_IGN);
 	bgp_setup(config->as_number);
 	bgp_add_route(config->bind_address, 0xffffffff);
-        if (*config->bgp_peer[0])
+	if (*config->bgp_peer[0])
 		bgp_start(&bgp_peers[0], config->bgp_peer[0],
 		    config->bgp_peer_as[0], 0); /* 0 = routing disabled */
 
-        if (*config->bgp_peer[1])
+	if (*config->bgp_peer[1])
 		bgp_start(&bgp_peers[1], config->bgp_peer[1],
 		    config->bgp_peer_as[1], 0);
 #endif /* BGP */
@@ -2995,6 +3055,10 @@ int main(int argc, char *argv[])
 	/* remove plugins (so cleanup code gets run) */
 	plugins_done();
 
+	// Remove the PID file if we wrote it
+	if (config->wrote_pid && *config->pid_file == '/')
+		unlink(config->pid_file);
+
 	/* kill CLI children */
 	signal(SIGTERM, SIG_IGN);
 	kill(0, SIGTERM);
@@ -3081,168 +3145,178 @@ void sigchild_handler(int signal)
 
 void read_state()
 {
-    struct stat sb;
-    int i;
-    ippoolt itmp;
-    FILE *f;
-    char magic[sizeof(DUMP_MAGIC)-1];
-    u32 buf[2];
+	struct stat sb;
+	int i;
+	ippoolt itmp;
+	FILE *f;
+	char magic[sizeof(DUMP_MAGIC) - 1];
+	u32 buf[2];
 
-    if (!config->save_state)
-    {
-	unlink(STATEFILE);
-	return;
-    }
+	if (!config->save_state)
+	{
+		unlink(STATEFILE);
+		return ;
+	}
 
-    if (stat(STATEFILE, &sb) < 0)
-    {
-	unlink(STATEFILE);
-	return;
-    }
+	if (stat(STATEFILE, &sb) < 0)
+	{
+		unlink(STATEFILE);
+		return ;
+	}
 
-    if (sb.st_mtime < (time(NULL) - 60))
-    {
-	log(0, 0, 0, 0, "State file is too old to read, ignoring\n");
+	if (sb.st_mtime < (time(NULL) - 60))
+	{
+		log(0, 0, 0, 0, "State file is too old to read, ignoring\n");
+		unlink(STATEFILE);
+		return ;
+	}
+
+	f = fopen(STATEFILE, "r");
 	unlink(STATEFILE);
-	return;
-    }
-
-    f = fopen(STATEFILE, "r");
-    unlink(STATEFILE);
-
-    if (!f)
-    {
-	log(0, 0, 0, 0, "Can't read state file: %s\n", strerror(errno));
-	exit(1);
-    }
-
-    if (fread(magic, sizeof(magic), 1, f) != 1 || strncmp(magic, DUMP_MAGIC, sizeof(magic)))
-    {
-	log(0, 0, 0, 0, "Bad state file magic\n");
-	exit(1);
-    }
-
-    log(1, 0, 0, 0, "Reading state information\n");
-    if (fread(buf, sizeof(buf), 1, f) != 1 || buf[0] > MAXIPPOOL || buf[1] != sizeof(ippoolt))
-    {
-	log(0, 0, 0, 0, "Error/mismatch reading ip pool header from state file\n");
-	exit(1);
-    }
-
-    if (buf[0] > ip_pool_size)
-    {
-	log(0, 0, 0, 0, "ip pool has shrunk!  state = %d, current = %d\n", buf[0], ip_pool_size);
-	exit(1);
-    }
-
-    log(2, 0, 0, 0, "Loading %u ip addresses\n", buf[0]);
-    for (i = 0; i < buf[0]; i++)
-    {
-	if (fread(&itmp, sizeof(itmp), 1, f) != 1)
-	{
-	    log(0, 0, 0, 0, "Error reading ip %d from state file: %s\n", i, strerror(errno));
-	    exit(1);
-	}
-
-	if (itmp.address != ip_address_pool[i].address)
-	{
-	    log(0, 0, 0, 0, "Mismatched ip %d from state file: pool may only be extended\n", i);
-	    exit(1);
-	}
-
-	memcpy(&ip_address_pool[i], &itmp, sizeof(itmp));
-    }
-
-    if (fread(buf, sizeof(buf), 1, f) != 1 || buf[0] != MAXTUNNEL || buf[1] != sizeof(tunnelt))
-    {
-	log(0, 0, 0, 0, "Error/mismatch reading tunnel header from state file\n");
-	exit(1);
-    }
-
-    log(2, 0, 0, 0, "Loading %u tunnels\n", MAXTUNNEL);
-    if (fread(tunnel, sizeof(tunnelt), MAXTUNNEL, f) != MAXTUNNEL)
-    {
-	log(0, 0, 0, 0, "Error reading tunnel data from state file\n");
-	exit(1);
-    }
-
-    for (i = 0; i < MAXTUNNEL; i++)
-    {
-	tunnel[i].controlc = 0;
-	tunnel[i].controls = NULL;
-	tunnel[i].controle = NULL;
-	if (*tunnel[i].hostname)
-	    log(3, 0, 0, 0, "Created tunnel for %s\n", tunnel[i].hostname);
-    }
-
-    if (fread(buf, sizeof(buf), 1, f) != 1 || buf[0] != MAXSESSION || buf[1] != sizeof(sessiont))
-    {
-	log(0, 0, 0, 0, "Error/mismatch reading session header from state file\n");
-	exit(1);
-    }
-
-    log(2, 0, 0, 0, "Loading %u sessions\n", MAXSESSION);
-    if (fread(session, sizeof(sessiont), MAXSESSION, f) != MAXSESSION)
-    {
-	log(0, 0, 0, 0, "Error reading session data from state file\n");
-	exit(1);
-    }
-
-    for (i = 0; i < MAXSESSION; i++)
-    {
-	session[i].tbf_in = 0;
-	session[i].tbf_out = 0;
-	if (session[i].opened)
-	{
-	    log(2, 0, i, 0, "Loaded active session for user %s\n", session[i].user);
-	    if (session[i].ip)
-		sessionsetup(session[i].tunnel, i);
-	}
-    }
-
-    fclose(f);
-    log(0, 0, 0, 0, "Loaded saved state information\n");
+
+	if (!f)
+	{
+		log(0, 0, 0, 0, "Can't read state file: %s\n", strerror(errno));
+		exit(1);
+	}
+
+	if (fread(magic, sizeof(magic), 1, f) != 1 || strncmp(magic, DUMP_MAGIC, sizeof(magic)))
+	{
+		log(0, 0, 0, 0, "Bad state file magic\n");
+		exit(1);
+	}
+
+	log(1, 0, 0, 0, "Reading state information\n");
+	if (fread(buf, sizeof(buf), 1, f) != 1 || buf[0] > MAXIPPOOL || buf[1] != sizeof(ippoolt))
+	{
+		log(0, 0, 0, 0, "Error/mismatch reading ip pool header from state file\n");
+		exit(1);
+	}
+
+	if (buf[0] > ip_pool_size)
+	{
+		log(0, 0, 0, 0, "ip pool has shrunk!  state = %d, current = %d\n", buf[0], ip_pool_size);
+		exit(1);
+	}
+
+	log(2, 0, 0, 0, "Loading %u ip addresses\n", buf[0]);
+	for (i = 0; i < buf[0]; i++)
+	{
+		if (fread(&itmp, sizeof(itmp), 1, f) != 1)
+		{
+			log(0, 0, 0, 0, "Error reading ip %d from state file: %s\n", i, strerror(errno));
+			exit(1);
+		}
+
+		if (itmp.address != ip_address_pool[i].address)
+		{
+			log(0, 0, 0, 0, "Mismatched ip %d from state file: pool may only be extended\n", i);
+			exit(1);
+		}
+
+		memcpy(&ip_address_pool[i], &itmp, sizeof(itmp));
+	}
+
+	if (fread(buf, sizeof(buf), 1, f) != 1 || buf[0] != MAXTUNNEL || buf[1] != sizeof(tunnelt))
+	{
+		log(0, 0, 0, 0, "Error/mismatch reading tunnel header from state file\n");
+		exit(1);
+	}
+
+	log(2, 0, 0, 0, "Loading %u tunnels\n", MAXTUNNEL);
+	if (fread(tunnel, sizeof(tunnelt), MAXTUNNEL, f) != MAXTUNNEL)
+	{
+		log(0, 0, 0, 0, "Error reading tunnel data from state file\n");
+		exit(1);
+	}
+
+	for (i = 0; i < MAXTUNNEL; i++)
+	{
+		tunnel[i].controlc = 0;
+		tunnel[i].controls = NULL;
+		tunnel[i].controle = NULL;
+		if (*tunnel[i].hostname)
+			log(3, 0, 0, 0, "Created tunnel for %s\n", tunnel[i].hostname);
+	}
+
+	if (fread(buf, sizeof(buf), 1, f) != 1 || buf[0] != MAXSESSION || buf[1] != sizeof(sessiont))
+	{
+		log(0, 0, 0, 0, "Error/mismatch reading session header from state file\n");
+		exit(1);
+	}
+
+	log(2, 0, 0, 0, "Loading %u sessions\n", MAXSESSION);
+	if (fread(session, sizeof(sessiont), MAXSESSION, f) != MAXSESSION)
+	{
+		log(0, 0, 0, 0, "Error reading session data from state file\n");
+		exit(1);
+	}
+
+	for (i = 0; i < MAXSESSION; i++)
+	{
+		session[i].tbf_in = 0;
+		session[i].tbf_out = 0;
+		if (session[i].opened)
+		{
+			log(2, 0, i, 0, "Loaded active session for user %s\n", session[i].user);
+			if (session[i].ip)
+				sessionsetup(session[i].tunnel, i);
+		}
+	}
+
+	fclose(f);
+	log(0, 0, 0, 0, "Loaded saved state information\n");
 }
 
 void dump_state()
 {
-    FILE *f;
-    u32 buf[2];
+	FILE *f;
+	u32 buf[2];
 
-    if (!config->save_state)
-	return;
+	if (!config->save_state)
+		return;
 
-    do {
-	if (!(f = fopen(STATEFILE, "w")))
-	    break;
+	do
+	{
+		if (!(f = fopen(STATEFILE, "w")))
+			break;
 
-	log(1, 0, 0, 0, "Dumping state information\n");
+		log(1, 0, 0, 0, "Dumping state information\n");
 
-	if (fwrite(DUMP_MAGIC, sizeof(DUMP_MAGIC)-1, 1, f) != 1) break;
+		if (fwrite(DUMP_MAGIC, sizeof(DUMP_MAGIC) - 1, 1, f) != 1)
+			break;
 
-	log(2, 0, 0, 0, "Dumping %u ip addresses\n", ip_pool_size);
-	buf[0] = ip_pool_size;
-	buf[1] = sizeof(ippoolt);
-	if (fwrite(buf, sizeof(buf), 1, f) != 1) break;
-	if (fwrite(ip_address_pool, sizeof(ippoolt), ip_pool_size, f) != ip_pool_size) break;
+		log(2, 0, 0, 0, "Dumping %u ip addresses\n", ip_pool_size);
+		buf[0] = ip_pool_size;
+		buf[1] = sizeof(ippoolt);
+		if (fwrite(buf, sizeof(buf), 1, f) != 1)
+			break;
+		if (fwrite(ip_address_pool, sizeof(ippoolt), ip_pool_size, f) != ip_pool_size)
+			break;
 
-	log(2, 0, 0, 0, "Dumping %u tunnels\n", MAXTUNNEL);
-	buf[0] = MAXTUNNEL;
-	buf[1] = sizeof(tunnelt);
-	if (fwrite(buf, sizeof(buf), 1, f) != 1) break;
-	if (fwrite(tunnel, sizeof(tunnelt), MAXTUNNEL, f) != MAXTUNNEL) break;
+		log(2, 0, 0, 0, "Dumping %u tunnels\n", MAXTUNNEL);
+		buf[0] = MAXTUNNEL;
+		buf[1] = sizeof(tunnelt);
+		if (fwrite(buf, sizeof(buf), 1, f) != 1)
+			break;
+		if (fwrite(tunnel, sizeof(tunnelt), MAXTUNNEL, f) != MAXTUNNEL)
+			break;
 
-	log(2, 0, 0, 0, "Dumping %u sessions\n", MAXSESSION);
-	buf[0] = MAXSESSION;
-	buf[1] = sizeof(sessiont);
-	if (fwrite(buf, sizeof(buf), 1, f) != 1) break;
-	if (fwrite(session, sizeof(sessiont), MAXSESSION, f) != MAXSESSION) break;
+		log(2, 0, 0, 0, "Dumping %u sessions\n", MAXSESSION);
+		buf[0] = MAXSESSION;
+		buf[1] = sizeof(sessiont);
+		if (fwrite(buf, sizeof(buf), 1, f) != 1)
+			break;
+		if (fwrite(session, sizeof(sessiont), MAXSESSION, f) != MAXSESSION)
+			break;
 
-	if (fclose(f) == 0) return; // OK
-    } while (0);
+		if (fclose(f) == 0)
+			return ; // OK
+	}
+	while (0);
 
-    log(0, 0, 0, 0, "Can't write state information: %s\n", strerror(errno));
-    unlink(STATEFILE);
+	log(0, 0, 0, 0, "Can't write state information: %s\n", strerror(errno));
+	unlink(STATEFILE);
 }
 
 void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **challenge_response)
@@ -3271,13 +3345,13 @@ void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **ch
 
 static int facility_value(char *name)
 {
-    int i;
-    for (i = 0; facilitynames[i].c_name; i++)
-    {
-	if (strcmp(facilitynames[i].c_name, name) == 0)
-	    return facilitynames[i].c_val;
-    }
-    return 0;
+	int i;
+	for (i = 0; facilitynames[i].c_name; i++)
+	{
+		if (strcmp(facilitynames[i].c_name, name) == 0)
+			return facilitynames[i].c_val;
+	}
+	return 0;
 }
 
 void update_config()
@@ -3401,6 +3475,22 @@ void update_config()
 		timeout = config->cluster_hb_timeout;
 	}
 
+	// Write PID file
+	if (*config->pid_file == '/' && !config->wrote_pid)
+	{
+		FILE *f;
+		if ((f = fopen(config->pid_file, "w")))
+		{
+			fprintf(f, "%d\n", getpid());
+			fclose(f);
+			config->wrote_pid = 1;
+		}
+		else
+		{
+			log(0, 0, 0, 0, "Can't write to PID file %s: %s\n", config->pid_file, strerror(errno));
+		}
+	}
+
 	config->reload_config = 0;
 }
 
@@ -3409,7 +3499,8 @@ void read_config_file()
 	FILE *f;
 
 	if (!config->config_file) return;
-	if (!(f = fopen(config->config_file, "r"))) {
+	if (!(f = fopen(config->config_file, "r")))
+	{
 		fprintf(stderr, "Can't open config file %s: %s\n", config->config_file, strerror(errno));
 		return;
 	}
@@ -3476,7 +3567,9 @@ int sessionsetup(tunnelidt t, sessionidt s)
 	for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++)
 		routeset(s, session[s].route[r].ip, session[s].route[r].mask, session[s].ip, 1);
 
-	if (!session[s].sid) {	// did this session just finish radius?
+	if (!session[s].sid)
+	{
+		// did this session just finish radius?
 		log(3, session[s].ip, s, t, "Sending initial IPCP to client\n");
 		sendipcp(t, s);
 		session[s].sid = ++last_sid;
@@ -3520,7 +3613,8 @@ int load_session(sessionidt s, sessiont *new)
 
 		// Sanity checks.
 	if (new->ip_pool_index >= MAXIPPOOL ||
-		new->tunnel >= MAXTUNNEL) {
+		new->tunnel >= MAXTUNNEL)
+	{
 		log(0,0,s,0, "Strange session update received!\n");
 			// FIXME! What to do here?
 		return 0;
@@ -3551,7 +3645,9 @@ int load_session(sessionidt s, sessiont *new)
 				uncache_ipmap(session[s].ip);
 		}
 
-		if (new->ip) {		// If there's a new one, add it.
+		if (new->ip)
+		{
+			// If there's a new one, add it.
 			if (new->ip_pool_index == -1)
 				routeset(s, new->ip, 0, 0, 1);
 			else
@@ -3835,7 +3931,8 @@ void become_master(void)
 	int s;
 	run_plugins(PLUGIN_BECOME_MASTER, NULL);
 
-	for (s = 1; s <= config->cluster_highest_sessionid ; ++s) {
+	for (s = 1; s <= config->cluster_highest_sessionid ; ++s)
+	{
 		if (!session[s].tunnel) // Not an in-use session.
 			continue;
 
@@ -3857,7 +3954,8 @@ int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc
 	time(&time_now);
 	for (i = 0; i < 64;++i) buckets[i] = 0;
 
-	for (s = 1; s <= config->cluster_highest_sessionid ; ++s) {
+	for (s = 1; s <= config->cluster_highest_sessionid ; ++s)
+	{
 		int idle;
 		if (!session[s].tunnel)
 			continue;
@@ -3873,7 +3971,8 @@ int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc
 		++buckets[idle];
 	}
 
-	for (i = 0; i < 63; ++i) {
+	for (i = 0; i < 63; ++i)
+	{
 		cli_print(cli, "%3d seconds  : %7.2f%% (%6d)", i * 5, (double) buckets[i] * 100.0 / count , buckets[i]);
 	}
 	cli_print(cli, "lots of secs : %7.2f%% (%6d)", (double) buckets[63] * 100.0 / count , buckets[i]);
@@ -3893,7 +3992,8 @@ int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc
 	time(&time_now);
 	for (i = 0; i < 64;++i) buckets[i] = 0;
 
-	for (s = 1; s <= config->cluster_highest_sessionid ; ++s) {
+	for (s = 1; s <= config->cluster_highest_sessionid ; ++s)
+	{
 		int open = 0, d;
 		if (!session[s].tunnel)
 			continue;
@@ -3901,7 +4001,8 @@ int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc
 		d = time_now - session[s].opened;
 		if (d < 0)
 			d = 0;
-		while (d > 1 && open < 32) {
+		while (d > 1 && open < 32)
+		{
 			++open;
 			d >>= 1; // half.
 		}
@@ -3910,7 +4011,8 @@ int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc
 	}
 
 	s = 1;
-	for (i = 0; i  < 30; ++i) {
+	for (i = 0; i  < 30; ++i)
+	{
 		cli_print(cli, " < %8d seconds : %7.2f%% (%6d)", s, (double) buckets[i] * 100.0 / count , buckets[i]);
 		s <<= 1;
 	}