2 * Small SUID helper that allows us to ping a BT device. Borrows
3 * heavily from bluez-utils' l2ping, which is licensed as GPL2+
4 * and comes with a copyright like this:
6 * Copyright (C) 2000-2001 Qualcomm Incorporated
7 * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
8 * Copyright (C) 2002-2007 Marcel Holtmann <marcel@holtmann.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
39 #include <sys/select.h>
41 #include <bluetooth/bluetooth.h>
42 #include <bluetooth/hci.h>
43 #include <bluetooth/hci_lib.h>
44 #include <bluetooth/l2cap.h>
46 #define PING_STRING "PulseAudio"
51 static void update_status(int found) {
52 static int status = -1;
54 if (!found && status != 0)
56 if (found && status <= 0)
63 int main(int argc, char *argv[]) {
64 struct sockaddr_l2 addr;
67 uint8_t buf[L2CAP_CMD_HDR_SIZE + sizeof(PING_STRING)];
85 if ((fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)) < 0) {
86 fprintf(stderr, "socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP) failed: %s", strerror(errno));
90 memset(&addr, 0, sizeof(addr));
91 addr.l2_family = AF_BLUETOOTH;
92 bacpy(&addr.l2_bdaddr, BDADDR_ANY);
94 if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
95 fprintf(stderr, "bind() failed: %s", strerror(errno));
99 memset(&addr, 0, sizeof(addr));
100 addr.l2_family = AF_BLUETOOTH;
101 str2ba(argv[1], &addr.l2_bdaddr);
103 if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
105 if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
111 fprintf(stderr, "connect() failed: %s", strerror(errno));
120 memset(&packet, 0, sizeof(packet));
121 strcpy((char*) packet.buf + L2CAP_CMD_HDR_SIZE, PING_STRING);
122 packet.hdr.ident = id;
123 packet.hdr.len = htobs(sizeof(PING_STRING));
124 packet.hdr.code = L2CAP_ECHO_REQ;
126 if ((r = send(fd, &packet, sizeof(packet), 0)) < 0) {
128 if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
135 fprintf(stderr, "send() failed: %s", strerror(errno));
139 assert(r == sizeof(packet));
141 gettimeofday(&end, NULL);
142 end.tv_sec += TIMEOUT;
145 struct timeval now, delta;
147 gettimeofday(&now, NULL);
149 if (timercmp(&end, &now, <=)) {
156 timersub(&end, &now, &delta);
161 if (select(fd+1, &fds, NULL, NULL, &delta) < 0) {
162 fprintf(stderr, "select() failed: %s", strerror(errno));
166 if ((r = recv(fd, &packet, sizeof(packet), 0)) <= 0) {
168 if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
175 fprintf(stderr, "send() failed: %s", r == 0 ? "EOF" : strerror(errno));
179 assert(r >= L2CAP_CMD_HDR_SIZE);
181 if (packet.hdr.ident != id)
184 if (packet.hdr.code == L2CAP_ECHO_RSP || packet.hdr.code == L2CAP_COMMAND_REJ) {