xref: /freebsd/usr.sbin/bluetooth/bthidd/bthidd.c (revision 42b388439bd3795e09258c57a74ce9eec3651c7b)
16490c2ffSMaksim Yevmenkin /*
26490c2ffSMaksim Yevmenkin  * bthidd.c
37aebfa93SMaksim Yevmenkin  */
47aebfa93SMaksim Yevmenkin 
57aebfa93SMaksim Yevmenkin /*-
6*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
71de7b4b8SPedro F. Giffuni  *
87aebfa93SMaksim Yevmenkin  * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
96490c2ffSMaksim Yevmenkin  * All rights reserved.
106490c2ffSMaksim Yevmenkin  *
116490c2ffSMaksim Yevmenkin  * Redistribution and use in source and binary forms, with or without
126490c2ffSMaksim Yevmenkin  * modification, are permitted provided that the following conditions
136490c2ffSMaksim Yevmenkin  * are met:
146490c2ffSMaksim Yevmenkin  * 1. Redistributions of source code must retain the above copyright
156490c2ffSMaksim Yevmenkin  *    notice, this list of conditions and the following disclaimer.
166490c2ffSMaksim Yevmenkin  * 2. Redistributions in binary form must reproduce the above copyright
176490c2ffSMaksim Yevmenkin  *    notice, this list of conditions and the following disclaimer in the
186490c2ffSMaksim Yevmenkin  *    documentation and/or other materials provided with the distribution.
196490c2ffSMaksim Yevmenkin  *
206490c2ffSMaksim Yevmenkin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
216490c2ffSMaksim Yevmenkin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
226490c2ffSMaksim Yevmenkin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
236490c2ffSMaksim Yevmenkin  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
246490c2ffSMaksim Yevmenkin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
256490c2ffSMaksim Yevmenkin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
266490c2ffSMaksim Yevmenkin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
276490c2ffSMaksim Yevmenkin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
286490c2ffSMaksim Yevmenkin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
296490c2ffSMaksim Yevmenkin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
306490c2ffSMaksim Yevmenkin  * SUCH DAMAGE.
316490c2ffSMaksim Yevmenkin  *
327aebfa93SMaksim Yevmenkin  * $Id: bthidd.c,v 1.8 2006/09/07 21:06:53 max Exp $
336490c2ffSMaksim Yevmenkin  */
346490c2ffSMaksim Yevmenkin 
356490c2ffSMaksim Yevmenkin #include <sys/time.h>
366490c2ffSMaksim Yevmenkin #include <sys/queue.h>
376490c2ffSMaksim Yevmenkin #include <assert.h>
388d6f425dSTakanori Watanabe #define L2CAP_SOCKET_CHECKED
396490c2ffSMaksim Yevmenkin #include <bluetooth.h>
406490c2ffSMaksim Yevmenkin #include <err.h>
416490c2ffSMaksim Yevmenkin #include <errno.h>
426490c2ffSMaksim Yevmenkin #include <signal.h>
436490c2ffSMaksim Yevmenkin #include <stdio.h>
446490c2ffSMaksim Yevmenkin #include <stdlib.h>
456490c2ffSMaksim Yevmenkin #include <string.h>
466490c2ffSMaksim Yevmenkin #include <syslog.h>
476490c2ffSMaksim Yevmenkin #include <unistd.h>
486490c2ffSMaksim Yevmenkin #include <usbhid.h>
496490c2ffSMaksim Yevmenkin #include "bthid_config.h"
507aebfa93SMaksim Yevmenkin #include "bthidd.h"
516490c2ffSMaksim Yevmenkin 
527aebfa93SMaksim Yevmenkin static int32_t	write_pid_file	(char const *file);
537aebfa93SMaksim Yevmenkin static int32_t	remove_pid_file	(char const *file);
547aebfa93SMaksim Yevmenkin static int32_t	elapsed		(int32_t tval);
557aebfa93SMaksim Yevmenkin static void	sighandler	(int32_t s);
566490c2ffSMaksim Yevmenkin static void	usage		(void);
576490c2ffSMaksim Yevmenkin 
586490c2ffSMaksim Yevmenkin /*
596490c2ffSMaksim Yevmenkin  * bthidd
606490c2ffSMaksim Yevmenkin  */
616490c2ffSMaksim Yevmenkin 
627aebfa93SMaksim Yevmenkin static int32_t	done = 0; /* are we done? */
636490c2ffSMaksim Yevmenkin 
647aebfa93SMaksim Yevmenkin int32_t
main(int32_t argc,char * argv[])657aebfa93SMaksim Yevmenkin main(int32_t argc, char *argv[])
666490c2ffSMaksim Yevmenkin {
676490c2ffSMaksim Yevmenkin 	struct bthid_server	 srv;
686490c2ffSMaksim Yevmenkin 	struct sigaction	 sa;
697aebfa93SMaksim Yevmenkin 	char const		*pid_file = BTHIDD_PIDFILE;
707aebfa93SMaksim Yevmenkin 	char			*ep;
7144af5666SVladimir Kondratyev 	int32_t			 opt, detach, tval, uinput;
726490c2ffSMaksim Yevmenkin 
733adfd74aSMaksim Yevmenkin 	memset(&srv, 0, sizeof(srv));
747aebfa93SMaksim Yevmenkin 	memset(&srv.bdaddr, 0, sizeof(srv.bdaddr));
756490c2ffSMaksim Yevmenkin 	detach = 1;
766490c2ffSMaksim Yevmenkin 	tval = 10; /* sec */
7744af5666SVladimir Kondratyev 	uinput = 0;
786490c2ffSMaksim Yevmenkin 
7944af5666SVladimir Kondratyev 	while ((opt = getopt(argc, argv, "a:c:dH:hp:t:u")) != -1) {
806490c2ffSMaksim Yevmenkin 		switch (opt) {
816490c2ffSMaksim Yevmenkin 		case 'a': /* BDADDR */
826490c2ffSMaksim Yevmenkin 			if (!bt_aton(optarg, &srv.bdaddr)) {
837aebfa93SMaksim Yevmenkin 				struct hostent  *he;
846490c2ffSMaksim Yevmenkin 
856490c2ffSMaksim Yevmenkin 				if ((he = bt_gethostbyname(optarg)) == NULL)
866490c2ffSMaksim Yevmenkin 					errx(1, "%s: %s", optarg, hstrerror(h_errno));
876490c2ffSMaksim Yevmenkin 
886490c2ffSMaksim Yevmenkin 				memcpy(&srv.bdaddr, he->h_addr, sizeof(srv.bdaddr));
896490c2ffSMaksim Yevmenkin 			}
906490c2ffSMaksim Yevmenkin 			break;
916490c2ffSMaksim Yevmenkin 
926490c2ffSMaksim Yevmenkin 		case 'c': /* config file */
936490c2ffSMaksim Yevmenkin 			config_file = optarg;
946490c2ffSMaksim Yevmenkin 			break;
956490c2ffSMaksim Yevmenkin 
966490c2ffSMaksim Yevmenkin 		case 'd': /* do not detach */
976490c2ffSMaksim Yevmenkin 			detach = 0;
986490c2ffSMaksim Yevmenkin 			break;
996490c2ffSMaksim Yevmenkin 
1006490c2ffSMaksim Yevmenkin 		case 'H': /* hids file */
1016490c2ffSMaksim Yevmenkin 			hids_file = optarg;
1026490c2ffSMaksim Yevmenkin 			break;
1036490c2ffSMaksim Yevmenkin 
1046490c2ffSMaksim Yevmenkin 		case 'p': /* pid file */
1056490c2ffSMaksim Yevmenkin 			pid_file = optarg;
1066490c2ffSMaksim Yevmenkin 			break;
1076490c2ffSMaksim Yevmenkin 
1083adfd74aSMaksim Yevmenkin 		case 't': /* rescan interval */
1093adfd74aSMaksim Yevmenkin 			tval = strtol(optarg, (char **) &ep, 10);
1106490c2ffSMaksim Yevmenkin 			if (*ep != '\0' || tval <= 0)
1116490c2ffSMaksim Yevmenkin 				usage();
1123adfd74aSMaksim Yevmenkin 			break;
1133adfd74aSMaksim Yevmenkin 
11444af5666SVladimir Kondratyev 		case 'u': /* enable evdev support */
11544af5666SVladimir Kondratyev 			uinput = 1;
11644af5666SVladimir Kondratyev 			break;
11744af5666SVladimir Kondratyev 
1186490c2ffSMaksim Yevmenkin 		case 'h':
1196490c2ffSMaksim Yevmenkin 		default:
1206490c2ffSMaksim Yevmenkin 			usage();
1216490c2ffSMaksim Yevmenkin 			/* NOT REACHED */
1226490c2ffSMaksim Yevmenkin 		}
1236490c2ffSMaksim Yevmenkin 	}
1246490c2ffSMaksim Yevmenkin 
1256490c2ffSMaksim Yevmenkin 	openlog(BTHIDD_IDENT, LOG_PID|LOG_PERROR|LOG_NDELAY, LOG_USER);
1266490c2ffSMaksim Yevmenkin 
1276490c2ffSMaksim Yevmenkin 	/* Become daemon if required */
1286490c2ffSMaksim Yevmenkin 	if (detach && daemon(0, 0) < 0) {
1296490c2ffSMaksim Yevmenkin 		syslog(LOG_CRIT, "Could not become daemon. %s (%d)",
1306490c2ffSMaksim Yevmenkin 			strerror(errno), errno);
1316490c2ffSMaksim Yevmenkin 		exit(1);
1326490c2ffSMaksim Yevmenkin 	}
1336490c2ffSMaksim Yevmenkin 
1346490c2ffSMaksim Yevmenkin 	/* Install signal handler */
1356490c2ffSMaksim Yevmenkin 	memset(&sa, 0, sizeof(sa));
1366490c2ffSMaksim Yevmenkin 	sa.sa_handler = sighandler;
1376490c2ffSMaksim Yevmenkin 
1386490c2ffSMaksim Yevmenkin 	if (sigaction(SIGTERM, &sa, NULL) < 0 ||
1397aebfa93SMaksim Yevmenkin 	    sigaction(SIGHUP, &sa, NULL) < 0 ||
1406490c2ffSMaksim Yevmenkin 	    sigaction(SIGINT, &sa, NULL) < 0) {
1416490c2ffSMaksim Yevmenkin 		syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)",
1426490c2ffSMaksim Yevmenkin 			strerror(errno), errno);
1436490c2ffSMaksim Yevmenkin 		exit(1);
1446490c2ffSMaksim Yevmenkin 	}
1456490c2ffSMaksim Yevmenkin 
1466490c2ffSMaksim Yevmenkin 	sa.sa_handler = SIG_IGN;
1476490c2ffSMaksim Yevmenkin 	if (sigaction(SIGPIPE, &sa, NULL) < 0) {
1486490c2ffSMaksim Yevmenkin 		syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)",
1496490c2ffSMaksim Yevmenkin 			strerror(errno), errno);
1506490c2ffSMaksim Yevmenkin 		exit(1);
1516490c2ffSMaksim Yevmenkin 	}
1526490c2ffSMaksim Yevmenkin 
1533adfd74aSMaksim Yevmenkin 	sa.sa_handler = SIG_IGN;
1543adfd74aSMaksim Yevmenkin 	sa.sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT;
1553adfd74aSMaksim Yevmenkin 	if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1563adfd74aSMaksim Yevmenkin 		syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)",
1573adfd74aSMaksim Yevmenkin 			strerror(errno), errno);
1583adfd74aSMaksim Yevmenkin 		exit(1);
1593adfd74aSMaksim Yevmenkin 	}
1603adfd74aSMaksim Yevmenkin 
1616490c2ffSMaksim Yevmenkin 	if (read_config_file() < 0 || read_hids_file() < 0 ||
1626490c2ffSMaksim Yevmenkin 	    server_init(&srv) < 0 || write_pid_file(pid_file) < 0)
1636490c2ffSMaksim Yevmenkin 		exit(1);
1646490c2ffSMaksim Yevmenkin 
16544af5666SVladimir Kondratyev 	srv.uinput = uinput;
16644af5666SVladimir Kondratyev 
1676490c2ffSMaksim Yevmenkin 	for (done = 0; !done; ) {
1686490c2ffSMaksim Yevmenkin 		if (elapsed(tval))
1696490c2ffSMaksim Yevmenkin 			client_rescan(&srv);
1706490c2ffSMaksim Yevmenkin 
1716490c2ffSMaksim Yevmenkin 		if (server_do(&srv) < 0)
1726490c2ffSMaksim Yevmenkin 			break;
1736490c2ffSMaksim Yevmenkin 	}
1746490c2ffSMaksim Yevmenkin 
1756490c2ffSMaksim Yevmenkin 	server_shutdown(&srv);
1766490c2ffSMaksim Yevmenkin 	remove_pid_file(pid_file);
1776490c2ffSMaksim Yevmenkin 	clean_config();
1786490c2ffSMaksim Yevmenkin 	closelog();
1796490c2ffSMaksim Yevmenkin 
1806490c2ffSMaksim Yevmenkin 	return (0);
1816490c2ffSMaksim Yevmenkin }
1826490c2ffSMaksim Yevmenkin 
1836490c2ffSMaksim Yevmenkin /*
1846490c2ffSMaksim Yevmenkin  * Write pid file
1856490c2ffSMaksim Yevmenkin  */
1866490c2ffSMaksim Yevmenkin 
1877aebfa93SMaksim Yevmenkin static int32_t
write_pid_file(char const * file)1886490c2ffSMaksim Yevmenkin write_pid_file(char const *file)
1896490c2ffSMaksim Yevmenkin {
1907aebfa93SMaksim Yevmenkin 	FILE	*pid;
1916490c2ffSMaksim Yevmenkin 
1926490c2ffSMaksim Yevmenkin 	assert(file != NULL);
1936490c2ffSMaksim Yevmenkin 
1946490c2ffSMaksim Yevmenkin 	if ((pid = fopen(file, "w")) == NULL) {
1956490c2ffSMaksim Yevmenkin 		syslog(LOG_ERR, "Could not open file %s. %s (%d)",
1966490c2ffSMaksim Yevmenkin 			file, strerror(errno), errno);
1976490c2ffSMaksim Yevmenkin 		return (-1);
1986490c2ffSMaksim Yevmenkin 	}
1996490c2ffSMaksim Yevmenkin 
2006490c2ffSMaksim Yevmenkin 	fprintf(pid, "%d", getpid());
2016490c2ffSMaksim Yevmenkin 	fclose(pid);
2026490c2ffSMaksim Yevmenkin 
2036490c2ffSMaksim Yevmenkin 	return (0);
2046490c2ffSMaksim Yevmenkin }
2056490c2ffSMaksim Yevmenkin 
2066490c2ffSMaksim Yevmenkin /*
2076490c2ffSMaksim Yevmenkin  * Remote pid file
2086490c2ffSMaksim Yevmenkin  */
2096490c2ffSMaksim Yevmenkin 
2107aebfa93SMaksim Yevmenkin static int32_t
remove_pid_file(char const * file)2116490c2ffSMaksim Yevmenkin remove_pid_file(char const *file)
2126490c2ffSMaksim Yevmenkin {
2136490c2ffSMaksim Yevmenkin 	assert(file != NULL);
2146490c2ffSMaksim Yevmenkin 
2156490c2ffSMaksim Yevmenkin 	if (unlink(file) < 0) {
2166490c2ffSMaksim Yevmenkin 		syslog(LOG_ERR, "Could not unlink file %s. %s (%d)",
2176490c2ffSMaksim Yevmenkin 			file, strerror(errno), errno);
2186490c2ffSMaksim Yevmenkin 		return (-1);
2196490c2ffSMaksim Yevmenkin 	}
2206490c2ffSMaksim Yevmenkin 
2216490c2ffSMaksim Yevmenkin 	return (0);
2226490c2ffSMaksim Yevmenkin }
2236490c2ffSMaksim Yevmenkin 
2246490c2ffSMaksim Yevmenkin /*
2256490c2ffSMaksim Yevmenkin  * Returns true if desired time interval has elapsed
2266490c2ffSMaksim Yevmenkin  */
2276490c2ffSMaksim Yevmenkin 
2287aebfa93SMaksim Yevmenkin static int32_t
elapsed(int32_t tval)2297aebfa93SMaksim Yevmenkin elapsed(int32_t tval)
2306490c2ffSMaksim Yevmenkin {
2317aebfa93SMaksim Yevmenkin 	static struct timeval	last = { 0, 0 };
2326490c2ffSMaksim Yevmenkin 	struct timeval		now;
2336490c2ffSMaksim Yevmenkin 
2346490c2ffSMaksim Yevmenkin 	gettimeofday(&now, NULL);
2356490c2ffSMaksim Yevmenkin 
2366490c2ffSMaksim Yevmenkin 	if (now.tv_sec - last.tv_sec >= tval) {
2376490c2ffSMaksim Yevmenkin 		last = now;
2386490c2ffSMaksim Yevmenkin 		return (1);
2396490c2ffSMaksim Yevmenkin 	}
2406490c2ffSMaksim Yevmenkin 
2416490c2ffSMaksim Yevmenkin 	return (0);
2426490c2ffSMaksim Yevmenkin }
2436490c2ffSMaksim Yevmenkin 
2446490c2ffSMaksim Yevmenkin /*
2457aebfa93SMaksim Yevmenkin  * Signal handler
2466490c2ffSMaksim Yevmenkin  */
2476490c2ffSMaksim Yevmenkin 
2486490c2ffSMaksim Yevmenkin static void
sighandler(int32_t s)2497aebfa93SMaksim Yevmenkin sighandler(int32_t s)
2506490c2ffSMaksim Yevmenkin {
2516490c2ffSMaksim Yevmenkin 	syslog(LOG_NOTICE, "Got signal %d, total number of signals %d",
2526490c2ffSMaksim Yevmenkin 		s, ++ done);
2536490c2ffSMaksim Yevmenkin }
2546490c2ffSMaksim Yevmenkin 
2556490c2ffSMaksim Yevmenkin /*
2566490c2ffSMaksim Yevmenkin  * Display usage and exit
2576490c2ffSMaksim Yevmenkin  */
2586490c2ffSMaksim Yevmenkin 
2596490c2ffSMaksim Yevmenkin static void
usage(void)2606490c2ffSMaksim Yevmenkin usage(void)
2616490c2ffSMaksim Yevmenkin {
2626490c2ffSMaksim Yevmenkin 	fprintf(stderr,
2636490c2ffSMaksim Yevmenkin "Usage: %s [options]\n" \
2646490c2ffSMaksim Yevmenkin "Where options are:\n" \
2652aa65cf7SMaksim Yevmenkin "	-a address	specify address to listen on (default ANY)\n" \
2666490c2ffSMaksim Yevmenkin "	-c file		specify config file name\n" \
2676490c2ffSMaksim Yevmenkin "	-d		run in foreground\n" \
2686490c2ffSMaksim Yevmenkin "	-H file		specify known HIDs file name\n" \
2696490c2ffSMaksim Yevmenkin "	-h		display this message\n" \
2706490c2ffSMaksim Yevmenkin "	-p file		specify PID file name\n" \
2713adfd74aSMaksim Yevmenkin "	-t tval		specify client rescan interval (sec)\n" \
27244af5666SVladimir Kondratyev "	-u		enable evdev protocol support\n" \
2736490c2ffSMaksim Yevmenkin "", BTHIDD_IDENT);
2746490c2ffSMaksim Yevmenkin 	exit(255);
2756490c2ffSMaksim Yevmenkin }
2766490c2ffSMaksim Yevmenkin 
277