File:  [ELWIX - Embedded LightWeight unIX -] / embedtools / src / dwds.c
Revision 1.1.2.12: download - view: text, annotated - select for diffs - revision graph
Tue Nov 2 02:12:49 2010 UTC (13 years, 7 months ago) by misho
Branches: tools1_0
fix remove

#include "global.h"
#include "dwds.h"


sl_config cfg;
int Verbose, Kill, nif;
char **ifs, szConfig[MAXPATHLEN] = DWDS_CONFIG;
extern char compiled[], compiledby[], compilehost[];


static void
Usage()
{
	printf(	"-= dWDS =- WiFi dynamic WDS service managment for VAP\n"
		"=== %s === %s@%s ===\n\n"
		"  Syntax: dwds [options] <interface|any> [interface [ath0, ...]]\n"
		"\n"
		"\t-v\t\tVerbose ...\n"
		"\t-f\t\tForeground, not demonize process ...\n"
		"\t-c <config>\tConfig file [default=/etc/dwds.conf]\n"
		"\n", compiled, compiledby, compilehost);
}

static void
sigHandler(int sig)
{
	int stat;
	const u_char *v;
	char szStr[STRSIZ];

	switch (sig) {
		case SIGHUP:
			UnloadConfig(&cfg);
			if (LoadConfig(szConfig, &cfg)) {
				printf("Error:: can`t load config %s ...\n", szConfig);
				Kill++;
			} else {
				closelog();

				cfg_LoadAttribute(&cfg, CFG("dwds"), CFG("name"), CFG(szStr), STRSIZ, DWDS_NAME);
				openlog(szStr, LOG_PID | LOG_CONS, LOG_DAEMON);
				v = cfg_GetAttribute(&cfg, CFG("dwds"), CFG("syslog_upto"));
				setlogmask(v ? strtol((char*) v, NULL, 0) : 0);
			}
			break;
		case SIGTERM:
			Kill++;
			break;
		case SIGCHLD:
			while (waitpid(-1, &stat, WNOHANG) > 0);
			break;
	}
}

static int
RtMsg(struct dwds_if **wds, struct rt_msghdr *msg, size_t len)
{
	struct if_announcemsghdr *ifan;
	const u_char *v;
	struct ether_addr bssid;
	char szStr[STRSIZ] = { 0 }, szCmd[MAXPATHLEN] = { 0 }, szIdent[STRSIZ] = { 0 }, 
	     szName[IFNAMSIZ] = { 0 };
	int f, stat;

	assert(wds);
	assert(msg);

	if (msg->rtm_version != RTM_VERSION) {
		syslog(LOG_ERR, "Error:: routing message version %d not understood!\n", msg->rtm_version);
		return -1;
	}

	switch (msg->rtm_type) {
		case RTM_IFANNOUNCE:
			ifan = (struct if_announcemsghdr*) msg;
			switch (ifan->ifan_what) {
				case IFAN_ARRIVAL:
					VERB(1) syslog(LOG_INFO, "RTM_IFANNOUNCE: if# %d, what: arrival\n", 
							ifan->ifan_index);
					break;
				case IFAN_DEPARTURE:
					VERB(1) syslog(LOG_INFO, "RTM_IFANNOUNCE: if# %d, what: departure\n", 
							ifan->ifan_index);
					wifi_destroyWDS(ifan->ifan_name, wds);
					break;
			}
			break;
		case RTM_IEEE80211:
#define	V(type)	((struct type *)(&ifan[1]))
			ifan = (struct if_announcemsghdr*) msg;
			switch (ifan->ifan_what) {
				case RTM_IEEE80211_DISASSOC:
					v = cfg_GetAttribute(&cfg, CFG("dwds"), CFG("discover_on_join"));
					if (!v || !strtol((char*) v, NULL, 0))
						break;
					/* fall thru ... */
				case RTM_IEEE80211_LEAVE:
					if (!wifi_chkIface(ifan->ifan_name, ifs, nif))
						break;
					memcpy(&bssid, V(ieee80211_leave_event)->iev_addr, ETHER_ADDR_LEN);
					VERB(1) syslog(LOG_INFO, "BSSID:%s Station leave\n", ether_ntoa(&bssid));
					if (!wifi_leaveWDS(bssid, wds, szName, IFNAMSIZ)) {
						cfg_LoadAttribute(&cfg, CFG("dwds"), CFG("name"), 
								CFG(szIdent), STRSIZ, DWDS_NAME);
						/* delete state file ... */
						v = cfg_GetAttribute(&cfg, CFG("dwds"), CFG("state_dir"));
						if (v && strtol((char*) v, NULL, 0)) {
							memset(szCmd, 0, STRSIZ);
							snprintf(szCmd, MAXPATHLEN, "%s/%s-%s-%s", (char*) v, 
									szName, ether_ntoa(&bssid), szIdent);
							unlink(szCmd);
							VERB(2) syslog(LOG_DEBUG, "Debug:: delete session name %s\n", szCmd);
						}

						/* Launch script ... */
						cfg_LoadAttribute(&cfg, CFG("dwds"), CFG("disassoc_event"), 
								CFG(szStr), STRSIZ, NULL);
						if (*szStr) {
							cfg_LoadAttribute(&cfg, CFG("dwds"), CFG("name"), 
									CFG(szIdent), STRSIZ, DWDS_NAME);
							memset(szCmd, 0, MAXPATHLEN);
							snprintf(szCmd, MAXPATHLEN, "%s %s %s %s", szStr, 
									szName, ether_ntoa(&bssid), szIdent);
							VERB(3) syslog(LOG_DEBUG, "Debug:: Command line: %s\n", szCmd);

							if ((stat = system(szCmd)))
								syslog(LOG_ERR, "VAP down script %s exited "
										"with status %d\n", szStr, stat);
						}
					}
					break;

				case RTM_IEEE80211_JOIN:
				case RTM_IEEE80211_REJOIN:
				case RTM_IEEE80211_ASSOC:
				case RTM_IEEE80211_REASSOC:
					v = cfg_GetAttribute(&cfg, CFG("dwds"), CFG("discover_on_join"));
					if (!v || !strtol((char*) v, NULL, 0))
						break;
					/* fall thru ... */
				case RTM_IEEE80211_WDS:
					memcpy(&bssid, V(ieee80211_leave_event)->iev_addr, ETHER_ADDR_LEN);
					VERB(1) syslog(LOG_INFO, "BSSID:%s WDS discovery\n", ether_ntoa(&bssid));
					if (!wifi_chkIface(ifan->ifan_name, ifs, nif))
						break;
					if (!wifi_createWDS(ifan->ifan_name, bssid, wds)) {
						cfg_LoadAttribute(&cfg, CFG("dwds"), CFG("name"), 
								CFG(szIdent), STRSIZ, DWDS_NAME);
						/* create state file ... */
						v = cfg_GetAttribute(&cfg, CFG("dwds"), CFG("state_dir"));
						if (v && strtol((char*) v, NULL, 0)) {
							memset(szCmd, 0, MAXPATHLEN);
							snprintf(szCmd, MAXPATHLEN, "%s/%s-%s-%s", (char*) v, 
									(*wds)->if_name, ether_ntoa(&bssid), szIdent);
							f = open(szCmd, O_WRONLY | O_CREAT, 0644);
							if (f != -1)
								close(f);
							VERB(2) syslog(LOG_DEBUG, "Debug:: create session name %s\n", szCmd);
						}

						/* Launch script ... */
						cfg_LoadAttribute(&cfg, CFG("dwds"), CFG("assoc_event"), 
								CFG(szStr), STRSIZ, NULL);
						if (*szStr) {
							memset(szCmd, 0, MAXPATHLEN);
							snprintf(szCmd, MAXPATHLEN, "%s %s %s %s", szStr, 
									(*wds)->if_name, ether_ntoa(&bssid), szIdent);
							VERB(3) syslog(LOG_DEBUG, "Debug:: Command line: %s\n", szCmd);

							if ((stat = system(szCmd)))
								syslog(LOG_ERR, "VAP up script %s exited "
										"with status %d\n", szStr, stat);
						}
					}
					break;
			}
#undef V
			break;
	}

	return 0;
}

// ---------------------------------------------------------------

int
main(int argc, char **argv)
{
	char ch, szStr[STRSIZ], fg = 0;
	const u_char *v, msg[2048];
	int s;
	struct sigaction sa;
	size_t len;
	struct dwds_if *wds = NULL;

	while ((ch = getopt(argc, argv, "hvfc:")) != -1)
		switch (ch) {
			case 'v':
				Verbose++;
				break;
			case 'f':
				fg = 1;
				break;
			case 'c':
				strlcpy(szConfig, optarg, MAXPATHLEN);
				break;
			case 'h':
			default:
				Usage();
				return 1;
		}
	argc -= optind;
	argv += optind;
	if (!argc) {
		printf("Error:: not specified interface for use ...\n");
		Usage();
		return 1;
	} else {
		nif = argc;
		ifs = argv;
	}
	if (LoadConfig(szConfig, &cfg)) {
		printf("Error:: can`t load config %s ...\n", szConfig);
		return 1;
	}

	if (!fg)
		switch (fork()) {
			case -1:
				printf("Error:: when fork() #%d - %s\n", errno, strerror(errno));
				UnloadConfig(&cfg);
				return 2;
			case 0 :
				VERB(1) printf("Going to shadow land ...\n");

				setsid();

				memset(&sa, 0, sizeof sa);
				sa.sa_handler = sigHandler;
				sigemptyset(&sa.sa_mask);
				sigaction(SIGHUP, &sa, NULL);
				sigaction(SIGTERM, &sa, NULL);
				sigaction(SIGCHLD, &sa, NULL);
				break;
			default:
				goto end;
		}

	cfg_LoadAttribute(&cfg, CFG("dwds"), CFG("name"), CFG(szStr), STRSIZ, DWDS_NAME);
	openlog(szStr, LOG_PID | LOG_CONS, LOG_DAEMON);
	v = cfg_GetAttribute(&cfg, CFG("dwds"), CFG("syslog_upto"));
	setlogmask(v ? strtol((char*) v, NULL, 0) : 0);

	s = socket(PF_ROUTE, SOCK_RAW, 0);
	if (s == -1) {
		syslog(LOG_ERR, "Error:: socket() #%d - %s\n", errno, strerror(errno));
		goto end;
	}

	if (!(wds = wifi_buildWDS(s, ifs, nif))) {
		syslog(LOG_ERR, "Error:: Go to dead ...\n");
		goto end;
	}

	while (!Kill) {
		len = read(s, (void*) msg, sizeof msg);
		if (len == -1) {
			VERB(5) syslog(LOG_ERR, "Error:: read() #%d - %s\n", errno, strerror(errno));
			Kill++;
		} else
			RtMsg(&wds, (struct rt_msghdr*) msg, len);
	}

	close(s);
end:
	closelog();
	UnloadConfig(&cfg);
	return 0;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>