]> code.delx.au - pulseaudio/blob - src/modules/bt-proximity-helper.c
catch up with trunk HEAD (i.e. 2118:2213)
[pulseaudio] / src / modules / bt-proximity-helper.c
1 /* $Id$ */
2
3 /*
4 * Small SUID helper that allows us to ping a BT device. Borrows
5 * heavily from bluez-utils' l2ping, which is licensed as GPL2+
6 * and comes with a copyright like this:
7 *
8 * Copyright (C) 2000-2001 Qualcomm Incorporated
9 * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
10 * Copyright (C) 2002-2007 Marcel Holtmann <marcel@holtmann.org>
11 *
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 *
27 */
28
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32
33 #undef NDEBUG
34
35 #include <assert.h>
36 #include <stdio.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <stdlib.h>
40 #include <sys/time.h>
41 #include <sys/select.h>
42
43 #include <bluetooth/bluetooth.h>
44 #include <bluetooth/hci.h>
45 #include <bluetooth/hci_lib.h>
46 #include <bluetooth/l2cap.h>
47
48 #define PING_STRING "PulseAudio"
49 #define IDENT 200
50 #define TIMEOUT 4
51 #define INTERVAL 2
52
53 static void update_status(int found) {
54 static int status = -1;
55
56 if (!found && status != 0)
57 printf("-");
58 if (found && status <= 0)
59 printf("+");
60
61 fflush(stdout);
62 status = !!found;
63 }
64
65 int main(int argc, char *argv[]) {
66 struct sockaddr_l2 addr;
67 union {
68 l2cap_cmd_hdr hdr;
69 uint8_t buf[L2CAP_CMD_HDR_SIZE + sizeof(PING_STRING)];
70 } packet;
71 int fd = -1;
72 uint8_t id = IDENT;
73 int connected = 0;
74
75 assert(argc == 2);
76
77 for (;;) {
78 fd_set fds;
79 struct timeval end;
80 ssize_t r;
81
82 if (!connected) {
83
84 if (fd >= 0)
85 close(fd);
86
87 if ((fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)) < 0) {
88 fprintf(stderr, "socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP) failed: %s", strerror(errno));
89 goto finish;
90 }
91
92 memset(&addr, 0, sizeof(addr));
93 addr.l2_family = AF_BLUETOOTH;
94 bacpy(&addr.l2_bdaddr, BDADDR_ANY);
95
96 if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
97 fprintf(stderr, "bind() failed: %s", strerror(errno));
98 goto finish;
99 }
100
101 memset(&addr, 0, sizeof(addr));
102 addr.l2_family = AF_BLUETOOTH;
103 str2ba(argv[1], &addr.l2_bdaddr);
104
105 if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
106
107 if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
108 update_status(0);
109 sleep(INTERVAL);
110 continue;
111 }
112
113 fprintf(stderr, "connect() failed: %s", strerror(errno));
114 goto finish;
115 }
116
117 connected = 1;
118 }
119
120 assert(connected);
121
122 memset(&packet, 0, sizeof(packet));
123 strcpy((char*) packet.buf + L2CAP_CMD_HDR_SIZE, PING_STRING);
124 packet.hdr.ident = id;
125 packet.hdr.len = htobs(sizeof(PING_STRING));
126 packet.hdr.code = L2CAP_ECHO_REQ;
127
128 if ((r = send(fd, &packet, sizeof(packet), 0)) < 0) {
129
130 if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
131 update_status(0);
132 connected = 0;
133 sleep(INTERVAL);
134 continue;
135 }
136
137 fprintf(stderr, "send() failed: %s", strerror(errno));
138 goto finish;
139 }
140
141 assert(r == sizeof(packet));
142
143 gettimeofday(&end, NULL);
144 end.tv_sec += TIMEOUT;
145
146 for (;;) {
147 struct timeval now, delta;
148
149 gettimeofday(&now, NULL);
150
151 if (timercmp(&end, &now, <=)) {
152 update_status(0);
153 connected = 0;
154 sleep(INTERVAL);
155 break;
156 }
157
158 timersub(&end, &now, &delta);
159
160 FD_ZERO(&fds);
161 FD_SET(fd, &fds);
162
163 if (select(fd+1, &fds, NULL, NULL, &delta) < 0) {
164 fprintf(stderr, "select() failed: %s", strerror(errno));
165 goto finish;
166 }
167
168 if ((r = recv(fd, &packet, sizeof(packet), 0)) <= 0) {
169
170 if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
171 update_status(0);
172 connected = 0;
173 sleep(INTERVAL);
174 break;
175 }
176
177 fprintf(stderr, "send() failed: %s", r == 0 ? "EOF" : strerror(errno));
178 goto finish;
179 }
180
181 assert(r >= L2CAP_CMD_HDR_SIZE);
182
183 if (packet.hdr.ident != id)
184 continue;
185
186 if (packet.hdr.code == L2CAP_ECHO_RSP || packet.hdr.code == L2CAP_COMMAND_REJ) {
187
188 if (++id >= 0xFF)
189 id = IDENT;
190
191 update_status(1);
192 sleep(INTERVAL);
193 break;
194 }
195 }
196 }
197
198 finish:
199
200 if (fd >= 0)
201 close(fd);
202
203 return 1;
204 }