/* $OpenBSD: bt.c,v 1.6 2008/11/27 00:51:17 uwe Exp $ */ /* * Copyright (c) 2008 Uwe Stuehler * Copyright (c) 2003, 2004 Henning Brauer * Copyright (c) 2004 Alexander Guy * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "btd.h" void bt_sighdlr(int, short, void *); struct bt_interface *bt_find_inquiry_interface(struct btd *); struct bt_interface *bt_find_attach_interface(struct bt_device *); void bt_check_devices(int, short, void *); int bt_get_devinfo(struct bt_device *); int bt_device_attach(struct bt_device *); int bt_device_detach(struct bt_device *); struct event ev_sigint; struct event ev_sigterm; struct event ev_siginfo; struct event ev_check_devices; int priv_fd; void bt_sighdlr(int sig, short ev, void *arg) { struct btd *env = arg; switch (sig) { case SIGINT: case SIGTERM: (void)event_loopexit(NULL); break; case SIGINFO: conf_dump(env); break; } } pid_t bt_main(int pipe_prnt[2], struct btd *env, struct passwd *pw) { struct stat stb; pid_t pid; switch (pid = fork()) { case -1: fatal("cannot fork"); break; case 0: break; default: return pid; } setproctitle("bt engine"); event_init(); hid_init(NULL); db_open(BTD_DB, &env->db); hci_init(env); sdp_init(env); control_init(env); if (stat(pw->pw_dir, &stb) == -1) fatal("stat"); if (stb.st_uid != 0 || (stb.st_mode & (S_IWGRP|S_IWOTH)) != 0) fatalx("bad privsep dir permissions"); if (chroot(pw->pw_dir) == -1) fatal("chroot"); if (chdir("/") == -1) fatal("chdir(\"/\")"); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("can't drop privileges"); close(pipe_prnt[0]); priv_fd = pipe_prnt[1]; signal_set(&ev_sigint, SIGINT, bt_sighdlr, env); signal_set(&ev_sigterm, SIGTERM, bt_sighdlr, env); signal_set(&ev_siginfo, SIGINFO, bt_sighdlr, env); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal_add(&ev_siginfo, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGCHLD, SIG_DFL); evtimer_set(&ev_check_devices, bt_check_devices, env); log_info("bt engine ready"); (void)event_dispatch(); log_info("bt engine exiting"); exit(0); } void bt_priv_msg(enum imsg_type type) { bt_priv_send(&type, sizeof(type)); } void bt_priv_send(const void *buf, size_t n) { if (atomic_write(priv_fd, buf, n) < 0) fatal("atomic_write"); } void bt_priv_recv(void *buf, size_t n) { if (atomic_read(priv_fd, buf, n) < 0) fatal("atomic_read"); } struct bt_interface * bt_find_inquiry_interface(struct btd *env) { struct bt_interface *iface; TAILQ_FOREACH_REVERSE(iface, &env->interfaces, interfaces, entry) { if (iface->physif != NULL) return iface; } return NULL; } struct bt_interface * bt_find_attach_interface(struct bt_device *btdev) { struct bt_interface *iface; TAILQ_FOREACH(iface, &btdev->env->interfaces, entry) { if (iface->physif != NULL) return iface; } return NULL; } void bt_devices_changed(void) { struct timeval tv; memset(&tv, 0, sizeof(tv)); tv.tv_sec = 1; evtimer_add(&ev_check_devices, &tv); } void bt_check_devices(int fd, short evflags, void *arg) { struct btd *env = arg; struct bt_device *btdev; for (btdev = TAILQ_FIRST(&env->devices); btdev != NULL;) { if (!bdaddr_same(&btdev->addr, BDADDR_ANY) && (btdev->flags & (BTDF_ATTACH|BTDF_ATTACH_DONE)) == BTDF_ATTACH_DONE) { if (bt_device_detach(btdev) < 0) { btdev = TAILQ_NEXT(btdev, entry); continue; } if (!(btdev->flags & BTDF_ATTACH)) log_info("%s: device is now detached", bt_ntoa(&btdev->addr, NULL)); } if (btdev->flags & BTDF_DELETED) { struct bt_device *next; next = TAILQ_NEXT(btdev, entry); conf_delete_device(btdev); btdev = next; continue; } if (!bdaddr_same(&btdev->addr, BDADDR_ANY) && (btdev->flags & (BTDF_ATTACH|BTDF_ATTACH_DONE)) == BTDF_ATTACH) { if (bt_device_attach(btdev) < 0) { btdev = TAILQ_NEXT(btdev, entry); continue; } if (btdev->flags & BTDF_ATTACH_DONE) log_info("%s: device is now attached", bt_ntoa(&btdev->addr, NULL)); } btdev = TAILQ_NEXT(btdev, entry); } } int bt_get_devinfo(struct bt_device *btdev) { struct btd *env = btdev->env; int res; res = db_get_devinfo(&env->db, &btdev->addr, &btdev->info); if (res < 0) return -1; else if (res != 0) { struct bt_interface *iface; if ((iface = bt_find_attach_interface(btdev)) == NULL) return 0; res = sdp_get_devinfo(iface, btdev); if (res < 0) { btdev->flags &= ~BTDF_SDP_STARTED; return -1; } else if (res == 1) { btdev->flags &= ~BTDF_SDP_STARTED; log_info("%s: SDP query failed", bt_ntoa(&btdev->addr, NULL)); return 0; } else if (res == 2) { if (!(btdev->flags & BTDF_SDP_STARTED)) { btdev->flags |= BTDF_SDP_STARTED; log_info("%s: SDP query started", bt_ntoa(&btdev->addr, NULL)); } return 0; } else if (res != 0) fatalx("bt_get_devinfo: sdp_get_devinfo"); } bdaddr_copy(devinfo_raddr(&btdev->info), &btdev->addr); btdev->flags |= BTDF_DEVINFO_VALID; return 0; } int bt_device_attach(struct bt_device *btdev) { struct bt_interface *iface; void *buf; size_t n; int err; if (btdev->flags & BTDF_ATTACH_DONE) return 0; if (!(btdev->flags & BTDF_DEVINFO_VALID)) { if (bt_get_devinfo(btdev) < 0) return -1; if (!(btdev->flags & BTDF_DEVINFO_VALID)) return 0; } if (bdaddr_any(devinfo_laddr(&btdev->info))) { if ((iface = bt_find_attach_interface(btdev)) == NULL) return 0; bdaddr_copy(devinfo_laddr(&btdev->info), &iface->addr); } if (devinfo_store(&btdev->info, &buf, &n) < 0) return -1; bt_priv_msg(IMSG_ATTACH); bt_priv_send(&n, sizeof(n)); bt_priv_send(buf, n); bt_priv_recv(&err, sizeof(int)); free(buf); switch (err) { case 0: case EADDRINUSE: btdev->flags |= BTDF_ATTACH_DONE; return 0; default: log_warnx("could not attach %s (%s)", bt_ntoa(&btdev->addr, NULL), strerror(err)); return -1; } } int bt_device_detach(struct bt_device *btdev) { struct bt_interface *iface; void *buf; size_t n; int err; if ((btdev->flags & BTDF_ATTACH) || !(btdev->flags & BTDF_ATTACH_DONE)) return 0; if (!(btdev->flags & BTDF_DEVINFO_VALID)) { if (bt_get_devinfo(btdev) < 0) return -1; if (!(btdev->flags & BTDF_DEVINFO_VALID)) return 0; } if (bdaddr_any(devinfo_laddr(&btdev->info))) { if ((iface = bt_find_attach_interface(btdev)) == NULL) return 0; bdaddr_copy(devinfo_laddr(&btdev->info), &iface->addr); } if (devinfo_store(&btdev->info, &buf, &n) < 0) return -1; bt_priv_msg(IMSG_DETACH); bt_priv_send(&n, sizeof(n)); bt_priv_send(buf, n); bt_priv_recv(&err, sizeof(int)); free(buf); switch (err) { case 0: case ENODEV: btdev->flags &= ~BTDF_ATTACH_DONE; return 0; default: log_warnx("could not detach %s (%s)", bt_ntoa(&btdev->addr, NULL), strerror(err)); return -1; } } int bt_set_interface_flags(const struct btreq *btr) { int err; bt_priv_msg(IMSG_SET_INTERFACE_FLAGS); bt_priv_send(btr->btr_name, sizeof(btr->btr_name)); bt_priv_send(&btr->btr_flags, sizeof(btr->btr_flags)); bt_priv_recv(&err, sizeof(err)); return (errno = err) ? -1 : 0; }