Git init
[framework/multimedia/pulseaudio.git] / src / modules / bluetooth / proximity-helper.c
1 /*
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:
5  *
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>
9  *
10  *
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.
15  *
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.
20  *
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
24  *
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31 #undef NDEBUG
32
33 #include <assert.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include <unistd.h>
37 #include <stdlib.h>
38 #include <sys/time.h>
39 #include <sys/select.h>
40
41 #include <bluetooth/bluetooth.h>
42 #include <bluetooth/hci.h>
43 #include <bluetooth/hci_lib.h>
44 #include <bluetooth/l2cap.h>
45
46 #define PING_STRING "PulseAudio"
47 #define IDENT 200
48 #define TIMEOUT 4
49 #define INTERVAL 2
50
51 static void update_status(int found) {
52     static int status = -1;
53
54     if (!found && status != 0)
55         printf("-");
56     if (found && status <= 0)
57         printf("+");
58
59     fflush(stdout);
60     status = !!found;
61 }
62
63 int main(int argc, char *argv[]) {
64     struct sockaddr_l2 addr;
65     union {
66         l2cap_cmd_hdr hdr;
67         uint8_t buf[L2CAP_CMD_HDR_SIZE + sizeof(PING_STRING)];
68     }  packet;
69     int fd = -1;
70     uint8_t id = IDENT;
71     int connected = 0;
72
73     assert(argc == 2);
74
75     for (;;) {
76         fd_set fds;
77         struct timeval end;
78         ssize_t r;
79
80         if (!connected) {
81
82             if (fd >= 0)
83                 close(fd);
84
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));
87                 goto finish;
88             }
89
90             memset(&addr, 0, sizeof(addr));
91             addr.l2_family = AF_BLUETOOTH;
92             bacpy(&addr.l2_bdaddr, BDADDR_ANY);
93
94             if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
95                 fprintf(stderr, "bind() failed: %s", strerror(errno));
96                 goto finish;
97             }
98
99             memset(&addr, 0, sizeof(addr));
100             addr.l2_family = AF_BLUETOOTH;
101             str2ba(argv[1], &addr.l2_bdaddr);
102
103             if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
104
105                 if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
106                     update_status(0);
107                     sleep(INTERVAL);
108                     continue;
109                 }
110
111                 fprintf(stderr, "connect() failed: %s", strerror(errno));
112                 goto finish;
113             }
114
115             connected = 1;
116         }
117
118         assert(connected);
119
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;
125
126         if ((r = send(fd, &packet, sizeof(packet), 0)) < 0) {
127
128             if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
129                 update_status(0);
130                 connected = 0;
131                 sleep(INTERVAL);
132                 continue;
133             }
134
135             fprintf(stderr, "send() failed: %s", strerror(errno));
136             goto finish;
137         }
138
139         assert(r == sizeof(packet));
140
141         gettimeofday(&end, NULL);
142         end.tv_sec += TIMEOUT;
143
144         for (;;) {
145             struct timeval now, delta;
146
147             gettimeofday(&now, NULL);
148
149             if (timercmp(&end, &now, <=)) {
150                 update_status(0);
151                 connected = 0;
152                 sleep(INTERVAL);
153                 break;
154             }
155
156             timersub(&end, &now, &delta);
157
158             FD_ZERO(&fds);
159             FD_SET(fd, &fds);
160
161             if (select(fd+1, &fds, NULL, NULL, &delta) < 0) {
162                 fprintf(stderr, "select() failed: %s", strerror(errno));
163                 goto finish;
164             }
165
166             if ((r = recv(fd, &packet, sizeof(packet), 0)) <= 0) {
167
168                 if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
169                     update_status(0);
170                     connected = 0;
171                     sleep(INTERVAL);
172                     break;
173                 }
174
175                 fprintf(stderr, "send() failed: %s", r == 0 ? "EOF" : strerror(errno));
176                 goto finish;
177             }
178
179             assert(r >= L2CAP_CMD_HDR_SIZE);
180
181             if (packet.hdr.ident != id)
182                 continue;
183
184             if (packet.hdr.code == L2CAP_ECHO_RSP || packet.hdr.code == L2CAP_COMMAND_REJ) {
185
186                 if (++id >= 0xFF)
187                     id = IDENT;
188
189                 update_status(1);
190                 sleep(INTERVAL);
191                 break;
192             }
193         }
194     }
195
196 finish:
197
198     if (fd >= 0)
199         close(fd);
200
201     return 1;
202 }