/*
 * btscanner - Displays the output of Bluetooth scans
 * Copyright (C) 2003 Pentest Limited
 * 
 * Written 2003 by Tim Hurman <timh at pentest.co.uk>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
 * RIGHTS.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE
 * FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY
 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 * 
 * ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
 * COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE
 * IS DISCLAIMED.
 */

/*
 * btscanner.c: keep a bluetooth card scanning the network to find devices
 * that are discoverable. Heavily based on hcitool.c
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <getopt.h>
#include <ncurses.h>

#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif

#ifdef HAVE_STRING_H
#include <string.h>
#endif

#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif

#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif

#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>

#include <main.h>
#include <cfg.h>
#include <log.h>
#include <scan.h>
#include <threader.h>

extern char bts_run_scan;
extern hcicfg_t *hcihead;

/* what to start */
struct
{
	enum threader_scan_types what;
	const char *desc;
	void *(*func)(void *);
} thread_starter[] = {
	{SCAN_INQ, "inquiry scan", scan_run},
	{SCAN_BF, "brute force scan", bf_run},
	{SCAN_NONE, NULL, NULL},
};

static enum threader_scan_types threader_current = 0;


/* start some threads; return the number started */
int threader_start(enum threader_scan_types what)
{
	hcicfg_t *h;
	int i, j, st;

	bts_run_scan=1;

	/* for each enabled hci device, start a scanning thread */
	for (st = 0, h = hcihead; h; h = h->next) {
		if (h->enabled) {
			/* reset the device */
			/*if (scan_reset_device(ctl, h->id))
				continue;*/

			/* start a thread */
			for (i=0; thread_starter[i].what != SCAN_NONE &&
			  thread_starter[i].what != what; i++);

			if (SCAN_NONE == thread_starter[i].what) {
				/* eep, dont know what to start */
				continue;
			}

			/* really start */
			h->died = 0;
			j = pthread_create(&(h->tid), NULL, thread_starter[i].func, h);
			if (0 != j) {
				applog(LOG_WARNING,
				  "%s::pthread_create(): %s", __FUNCTION__, strerror(errno));
				h->tid = 0;
				return -1;
			} else {
				st++;
				applog(LOG_INFO,
				  "%s::pthread_create(): created thread %d",
				  __FUNCTION__, h->tid);
			}
		}
	}

	if (st > 0)
		threader_current = what;

	return st;
}


/* kill or reap the threads */
int threader_reap(void)
{
	hcicfg_t *h;
	int i;

	for (i=0, h = hcihead; h; h = h->next) {
		/* we need to kill a thread even if it the hci dev is disabled.
		 * Hackery: the tid will never be 0 as pthreads creates some
		 * monitor threads. Therefore we can say that tid=0 means no
		 * thread is active. may need to revise this, esp on Solaris if
		 * Bluetooth ever gets there */
		if (!h->tid)
			continue;

		if(0 == h->died) {
			i++;
		} else {
			if(pthread_join(h->tid, NULL)) {
				applog(LOG_WARNING, "%s::pthread_join(): %s",
				  __FUNCTION__, strerror(errno));
			} else {
				applog(LOG_INFO, "%s::pthread_join(): joined thread %d",
				  __FUNCTION__, h->tid);
			}
			h->tid = 0;
		}
	}

	return i;
}


/* collect the threads */
int threader_stop(void)
{
	if (SCAN_NONE == threader_current) return 0;
	bts_run_scan=0;
	while (threader_reap())
		sleep(2);
/*		usleep(500000);*/
	threader_current = SCAN_NONE;
	return 0;
}


/* is there a scan running */
int threader_running(void)
{
	return threader_current;
}
const char *threader_running_desc(enum threader_scan_types what)
{
	int i;
	for (i=0; SCAN_NONE != thread_starter[i].what; i++)
		if (what == thread_starter[i].what) break;
	return thread_starter[i].desc;
}
