rfcomm_sppd.c revision 1.5 1 1.5 plunky /* $NetBSD: rfcomm_sppd.c,v 1.5 2007/03/08 19:13:14 plunky Exp $ */
2 1.1 gdamore
3 1.1 gdamore /*-
4 1.1 gdamore * Copyright (c) 2006 Itronix Inc.
5 1.1 gdamore * All rights reserved.
6 1.1 gdamore *
7 1.1 gdamore * Redistribution and use in source and binary forms, with or without
8 1.1 gdamore * modification, are permitted provided that the following conditions
9 1.1 gdamore * are met:
10 1.1 gdamore * 1. Redistributions of source code must retain the above copyright
11 1.1 gdamore * notice, this list of conditions and the following disclaimer.
12 1.1 gdamore * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 gdamore * notice, this list of conditions and the following disclaimer in the
14 1.1 gdamore * documentation and/or other materials provided with the distribution.
15 1.1 gdamore * 3. The name of Itronix Inc. may not be used to endorse
16 1.1 gdamore * or promote products derived from this software without specific
17 1.1 gdamore * prior written permission.
18 1.1 gdamore *
19 1.1 gdamore * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
20 1.1 gdamore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 gdamore * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 gdamore * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
23 1.1 gdamore * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 1.1 gdamore * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 1.1 gdamore * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 1.1 gdamore * ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 gdamore * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 gdamore * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 gdamore * POSSIBILITY OF SUCH DAMAGE.
30 1.1 gdamore */
31 1.1 gdamore /*
32 1.1 gdamore * rfcomm_sppd.c
33 1.1 gdamore *
34 1.2 plunky * Copyright (c) 2007 Iain Hibbert
35 1.1 gdamore * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin (at) yahoo.com>
36 1.1 gdamore * All rights reserved.
37 1.1 gdamore *
38 1.1 gdamore * Redistribution and use in source and binary forms, with or without
39 1.1 gdamore * modification, are permitted provided that the following conditions
40 1.1 gdamore * are met:
41 1.1 gdamore * 1. Redistributions of source code must retain the above copyright
42 1.1 gdamore * notice, this list of conditions and the following disclaimer.
43 1.1 gdamore * 2. Redistributions in binary form must reproduce the above copyright
44 1.1 gdamore * notice, this list of conditions and the following disclaimer in the
45 1.1 gdamore * documentation and/or other materials provided with the distribution.
46 1.1 gdamore *
47 1.1 gdamore * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
48 1.1 gdamore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 1.1 gdamore * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 1.1 gdamore * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
51 1.1 gdamore * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52 1.1 gdamore * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53 1.1 gdamore * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54 1.1 gdamore * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55 1.1 gdamore * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56 1.1 gdamore * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57 1.1 gdamore * SUCH DAMAGE.
58 1.1 gdamore */
59 1.1 gdamore
60 1.1 gdamore #include <sys/cdefs.h>
61 1.2 plunky __COPYRIGHT("@(#) Copyright (c) 2007 Iain Hibbert\n"
62 1.2 plunky "@(#) Copyright (c) 2006 Itronix, Inc.\n"
63 1.1 gdamore "@(#) Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin (at) yahoo.com>\n"
64 1.1 gdamore "All rights reserved.\n");
65 1.5 plunky __RCSID("$NetBSD: rfcomm_sppd.c,v 1.5 2007/03/08 19:13:14 plunky Exp $");
66 1.1 gdamore
67 1.1 gdamore #include <bluetooth.h>
68 1.1 gdamore #include <ctype.h>
69 1.1 gdamore #include <err.h>
70 1.1 gdamore #include <errno.h>
71 1.1 gdamore #include <fcntl.h>
72 1.1 gdamore #include <grp.h>
73 1.1 gdamore #include <limits.h>
74 1.1 gdamore #include <paths.h>
75 1.1 gdamore #include <sdp.h>
76 1.1 gdamore #include <signal.h>
77 1.1 gdamore #include <stdarg.h>
78 1.1 gdamore #include <stdio.h>
79 1.1 gdamore #include <stdlib.h>
80 1.1 gdamore #include <string.h>
81 1.1 gdamore #include <syslog.h>
82 1.1 gdamore #include <termios.h>
83 1.1 gdamore #include <unistd.h>
84 1.1 gdamore
85 1.1 gdamore #include "rfcomm_sdp.h"
86 1.1 gdamore
87 1.2 plunky #define max(a, b) ((a) > (b) ? (a) : (b))
88 1.1 gdamore
89 1.2 plunky int open_tty(const char *);
90 1.3 plunky int open_client(bdaddr_t *, bdaddr_t *, const char *);
91 1.2 plunky int open_server(bdaddr_t *, uint8_t, const char *);
92 1.2 plunky void copy_data(int, int);
93 1.2 plunky void sighandler(int);
94 1.2 plunky void usage(void);
95 1.2 plunky void reset_tio(void);
96 1.2 plunky
97 1.2 plunky int done; /* got a signal */
98 1.2 plunky struct termios tio; /* stored termios for reset on exit */
99 1.2 plunky
100 1.2 plunky struct service {
101 1.2 plunky const char *name;
102 1.2 plunky const char *description;
103 1.2 plunky uint16_t class;
104 1.2 plunky int pdulen;
105 1.2 plunky } services[] = {
106 1.2 plunky { "DUN", "Dialup Networking",
107 1.2 plunky SDP_SERVICE_CLASS_DIALUP_NETWORKING,
108 1.2 plunky sizeof(struct sdp_dun_profile)
109 1.2 plunky },
110 1.2 plunky { "LAN", "Lan access using PPP",
111 1.2 plunky SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
112 1.2 plunky sizeof(struct sdp_lan_profile)
113 1.2 plunky },
114 1.2 plunky { "SP", "Serial Port",
115 1.2 plunky SDP_SERVICE_CLASS_SERIAL_PORT,
116 1.2 plunky sizeof(struct sdp_sp_profile)
117 1.2 plunky },
118 1.2 plunky { NULL }
119 1.2 plunky };
120 1.1 gdamore
121 1.1 gdamore int
122 1.1 gdamore main(int argc, char *argv[])
123 1.1 gdamore {
124 1.2 plunky struct termios t;
125 1.2 plunky bdaddr_t laddr, raddr;
126 1.2 plunky fd_set rdset;
127 1.3 plunky char *ep, *service, *tty;
128 1.2 plunky int n, rfcomm, tty_in, tty_out;
129 1.2 plunky uint8_t channel;
130 1.1 gdamore
131 1.1 gdamore bdaddr_copy(&laddr, BDADDR_ANY);
132 1.1 gdamore bdaddr_copy(&raddr, BDADDR_ANY);
133 1.3 plunky service = "SP";
134 1.3 plunky tty = NULL;
135 1.2 plunky channel = 0;
136 1.1 gdamore
137 1.1 gdamore /* Parse command line options */
138 1.2 plunky while ((n = getopt(argc, argv, "a:c:d:hs:t:")) != -1) {
139 1.1 gdamore switch (n) {
140 1.2 plunky case 'a': /* remote device address */
141 1.1 gdamore if (!bt_aton(optarg, &raddr)) {
142 1.1 gdamore struct hostent *he = NULL;
143 1.1 gdamore
144 1.1 gdamore if ((he = bt_gethostbyname(optarg)) == NULL)
145 1.2 plunky errx(EXIT_FAILURE, "%s: %s", optarg,
146 1.2 plunky hstrerror(h_errno));
147 1.1 gdamore
148 1.1 gdamore bdaddr_copy(&raddr, (bdaddr_t *)he->h_addr);
149 1.1 gdamore }
150 1.1 gdamore break;
151 1.1 gdamore
152 1.1 gdamore case 'c': /* RFCOMM channel */
153 1.1 gdamore channel = strtoul(optarg, &ep, 10);
154 1.3 plunky if (*ep != '\0' || channel < 1 || channel > 30)
155 1.3 plunky errx(EXIT_FAILURE, "Invalid channel: %s", optarg);
156 1.3 plunky
157 1.1 gdamore break;
158 1.1 gdamore
159 1.2 plunky case 'd': /* local device address */
160 1.2 plunky if (!bt_devaddr(optarg, &laddr))
161 1.2 plunky err(EXIT_FAILURE, "%s", optarg);
162 1.2 plunky
163 1.1 gdamore break;
164 1.1 gdamore
165 1.3 plunky case 's': /* service class */
166 1.2 plunky service = optarg;
167 1.1 gdamore break;
168 1.1 gdamore
169 1.1 gdamore case 't': /* Slave TTY name */
170 1.1 gdamore if (optarg[0] != '/')
171 1.1 gdamore asprintf(&tty, "%s%s", _PATH_DEV, optarg);
172 1.1 gdamore else
173 1.1 gdamore tty = optarg;
174 1.2 plunky
175 1.1 gdamore break;
176 1.1 gdamore
177 1.1 gdamore case 'h':
178 1.1 gdamore default:
179 1.1 gdamore usage();
180 1.1 gdamore /* NOT REACHED */
181 1.1 gdamore }
182 1.1 gdamore }
183 1.1 gdamore
184 1.2 plunky /*
185 1.2 plunky * validate options:
186 1.3 plunky * must have channel or remote address but not both
187 1.2 plunky */
188 1.3 plunky if ((channel == 0 && bdaddr_any(&raddr))
189 1.3 plunky || (channel != 0 && !bdaddr_any(&raddr)))
190 1.1 gdamore usage();
191 1.1 gdamore
192 1.2 plunky /*
193 1.2 plunky * grab ttys before we start the bluetooth
194 1.2 plunky */
195 1.1 gdamore if (tty == NULL) {
196 1.2 plunky tty_in = STDIN_FILENO;
197 1.2 plunky tty_out = STDOUT_FILENO;
198 1.2 plunky } else {
199 1.2 plunky tty_in = open_tty(tty);
200 1.2 plunky tty_out = tty_in;
201 1.2 plunky }
202 1.1 gdamore
203 1.2 plunky /* open RFCOMM */
204 1.3 plunky if (channel == 0)
205 1.3 plunky rfcomm = open_client(&laddr, &raddr, service);
206 1.2 plunky else
207 1.2 plunky rfcomm = open_server(&laddr, channel, service);
208 1.1 gdamore
209 1.2 plunky /*
210 1.4 plunky * now we are ready to go, so either detach or maybe turn
211 1.2 plunky * off some input processing, so that rfcomm_sppd can
212 1.2 plunky * be used directly with stdio
213 1.2 plunky */
214 1.2 plunky if (tty == NULL) {
215 1.2 plunky if (tcgetattr(tty_in, &t) < 0)
216 1.2 plunky err(EXIT_FAILURE, "tcgetattr");
217 1.1 gdamore
218 1.2 plunky memcpy(&tio, &t, sizeof(tio));
219 1.2 plunky t.c_lflag &= ~(ECHO | ICANON);
220 1.2 plunky t.c_iflag &= ~(ICRNL);
221 1.1 gdamore
222 1.4 plunky if (memcmp(&tio, &t, sizeof(tio))) {
223 1.4 plunky if (tcsetattr(tty_in, TCSANOW, &t) < 0)
224 1.4 plunky err(EXIT_FAILURE, "tcsetattr");
225 1.1 gdamore
226 1.4 plunky atexit(reset_tio);
227 1.4 plunky }
228 1.2 plunky } else {
229 1.2 plunky if (daemon(0, 0) < 0)
230 1.2 plunky err(EXIT_FAILURE, "daemon() failed");
231 1.1 gdamore }
232 1.1 gdamore
233 1.2 plunky /* catch signals */
234 1.2 plunky done = 0;
235 1.2 plunky (void)signal(SIGHUP, sighandler);
236 1.2 plunky (void)signal(SIGINT, sighandler);
237 1.2 plunky (void)signal(SIGPIPE, sighandler);
238 1.2 plunky (void)signal(SIGTERM, sighandler);
239 1.2 plunky
240 1.2 plunky openlog(getprogname(), LOG_PERROR | LOG_PID, LOG_DAEMON);
241 1.2 plunky syslog(LOG_INFO, "Starting on %s...", (tty ? tty : "stdio"));
242 1.2 plunky
243 1.2 plunky n = max(tty_in, rfcomm) + 1;
244 1.2 plunky while (!done) {
245 1.2 plunky FD_ZERO(&rdset);
246 1.2 plunky FD_SET(tty_in, &rdset);
247 1.2 plunky FD_SET(rfcomm, &rdset);
248 1.1 gdamore
249 1.2 plunky if (select(n, &rdset, NULL, NULL, NULL) < 0) {
250 1.1 gdamore if (errno == EINTR)
251 1.1 gdamore continue;
252 1.1 gdamore
253 1.2 plunky syslog(LOG_ERR, "select error: %m");
254 1.1 gdamore exit(EXIT_FAILURE);
255 1.1 gdamore }
256 1.1 gdamore
257 1.2 plunky if (FD_ISSET(tty_in, &rdset))
258 1.2 plunky copy_data(tty_in, rfcomm);
259 1.1 gdamore
260 1.2 plunky if (FD_ISSET(rfcomm, &rdset))
261 1.2 plunky copy_data(rfcomm, tty_out);
262 1.2 plunky }
263 1.1 gdamore
264 1.2 plunky syslog(LOG_INFO, "Completed on %s", (tty ? tty : "stdio"));
265 1.2 plunky exit(EXIT_SUCCESS);
266 1.2 plunky }
267 1.1 gdamore
268 1.2 plunky int
269 1.2 plunky open_tty(const char *tty)
270 1.1 gdamore {
271 1.1 gdamore char pty[PATH_MAX], *slash;
272 1.1 gdamore struct group *gr = NULL;
273 1.1 gdamore gid_t ttygid;
274 1.2 plunky int master;
275 1.1 gdamore
276 1.1 gdamore /*
277 1.1 gdamore * Construct master PTY name. The slave tty name must be less then
278 1.1 gdamore * PATH_MAX characters in length, must contain '/' character and
279 1.1 gdamore * must not end with '/'.
280 1.1 gdamore */
281 1.2 plunky if (strlen(tty) >= sizeof(pty))
282 1.2 plunky errx(EXIT_FAILURE, ": tty name too long");
283 1.1 gdamore
284 1.1 gdamore strlcpy(pty, tty, sizeof(pty));
285 1.1 gdamore slash = strrchr(pty, '/');
286 1.2 plunky if (slash == NULL || slash[1] == '\0')
287 1.2 plunky errx(EXIT_FAILURE, "%s: invalid tty", tty);
288 1.1 gdamore
289 1.1 gdamore slash[1] = 'p';
290 1.2 plunky if (strcmp(pty, tty) == 0)
291 1.2 plunky errx(EXIT_FAILURE, "Master and slave tty are the same (%s)", tty);
292 1.1 gdamore
293 1.2 plunky if ((master = open(pty, O_RDWR, 0)) < 0)
294 1.2 plunky err(EXIT_FAILURE, "%s", pty);
295 1.1 gdamore
296 1.1 gdamore /*
297 1.1 gdamore * Slave TTY
298 1.1 gdamore */
299 1.1 gdamore
300 1.1 gdamore if ((gr = getgrnam("tty")) != NULL)
301 1.1 gdamore ttygid = gr->gr_gid;
302 1.1 gdamore else
303 1.1 gdamore ttygid = (gid_t)-1;
304 1.1 gdamore
305 1.2 plunky (void)chown(tty, getuid(), ttygid);
306 1.2 plunky (void)chmod(tty, S_IRUSR | S_IWUSR | S_IWGRP);
307 1.2 plunky (void)revoke(tty);
308 1.2 plunky
309 1.2 plunky return master;
310 1.2 plunky }
311 1.1 gdamore
312 1.2 plunky int
313 1.3 plunky open_client(bdaddr_t *laddr, bdaddr_t *raddr, const char *service)
314 1.2 plunky {
315 1.2 plunky struct sockaddr_bt sa;
316 1.3 plunky struct service *s;
317 1.2 plunky struct linger l;
318 1.3 plunky char *ep;
319 1.2 plunky int fd;
320 1.3 plunky uint8_t channel;
321 1.3 plunky
322 1.3 plunky for (s = services ; ; s++) {
323 1.3 plunky if (s->name == NULL) {
324 1.5 plunky channel = strtoul(service, &ep, 10);
325 1.3 plunky if (*ep != '\0' || channel < 1 || channel > 30)
326 1.3 plunky errx(EXIT_FAILURE, "Invalid service: %s", service);
327 1.3 plunky
328 1.3 plunky break;
329 1.3 plunky }
330 1.3 plunky
331 1.3 plunky if (strcasecmp(s->name, service) == 0) {
332 1.3 plunky if (rfcomm_channel_lookup(laddr, raddr, s->class, &channel, &errno) < 0)
333 1.3 plunky err(EXIT_FAILURE, "%s", s->name);
334 1.3 plunky
335 1.3 plunky break;
336 1.3 plunky }
337 1.3 plunky }
338 1.1 gdamore
339 1.2 plunky memset(&sa, 0, sizeof(sa));
340 1.2 plunky sa.bt_len = sizeof(sa);
341 1.2 plunky sa.bt_family = AF_BLUETOOTH;
342 1.2 plunky bdaddr_copy(&sa.bt_bdaddr, laddr);
343 1.2 plunky
344 1.2 plunky fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
345 1.2 plunky if (fd < 0)
346 1.2 plunky err(EXIT_FAILURE, "socket()");
347 1.2 plunky
348 1.2 plunky if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
349 1.2 plunky err(EXIT_FAILURE, "bind(%s)", bt_ntoa(laddr, NULL));
350 1.2 plunky
351 1.2 plunky memset(&l, 0, sizeof(l));
352 1.2 plunky l.l_onoff = 1;
353 1.2 plunky l.l_linger = 5;
354 1.2 plunky if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0)
355 1.2 plunky err(EXIT_FAILURE, "linger()");
356 1.2 plunky
357 1.2 plunky sa.bt_channel = channel;
358 1.2 plunky bdaddr_copy(&sa.bt_bdaddr, raddr);
359 1.2 plunky
360 1.2 plunky if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
361 1.2 plunky err(EXIT_FAILURE, "connect(%s, %d)", bt_ntoa(raddr, NULL),
362 1.2 plunky channel);
363 1.1 gdamore
364 1.2 plunky return fd;
365 1.2 plunky }
366 1.1 gdamore
367 1.2 plunky /*
368 1.2 plunky * In all the profiles we currently support registering, the channel
369 1.2 plunky * is the first octet in the PDU, and it seems all the rest can be
370 1.2 plunky * zero, so we just use an array of uint8_t big enough to store the
371 1.2 plunky * largest, currently LAN. See <sdp.h> for definitions..
372 1.2 plunky */
373 1.2 plunky #define pdu_len sizeof(struct sdp_lan_profile)
374 1.1 gdamore
375 1.2 plunky int
376 1.2 plunky open_server(bdaddr_t *laddr, uint8_t channel, const char *service)
377 1.1 gdamore {
378 1.2 plunky struct sockaddr_bt sa;
379 1.2 plunky struct linger l;
380 1.2 plunky socklen_t len;
381 1.2 plunky void *ss;
382 1.2 plunky int sv, fd, n;
383 1.2 plunky uint8_t pdu[pdu_len];
384 1.1 gdamore
385 1.2 plunky memset(&sa, 0, sizeof(sa));
386 1.2 plunky sa.bt_len = sizeof(sa);
387 1.2 plunky sa.bt_family = AF_BLUETOOTH;
388 1.2 plunky bdaddr_copy(&sa.bt_bdaddr, laddr);
389 1.2 plunky sa.bt_channel = channel;
390 1.2 plunky
391 1.2 plunky sv = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
392 1.2 plunky if (sv < 0)
393 1.2 plunky err(EXIT_FAILURE, "socket()");
394 1.2 plunky
395 1.2 plunky if (bind(sv, (struct sockaddr *)&sa, sizeof(sa)) < 0)
396 1.2 plunky err(EXIT_FAILURE, "bind(%s, %d)", bt_ntoa(laddr, NULL),
397 1.2 plunky channel);
398 1.2 plunky
399 1.2 plunky if (listen(sv, 1) < 0)
400 1.2 plunky err(EXIT_FAILURE, "listen()");
401 1.2 plunky
402 1.2 plunky /* Register service with SDP server */
403 1.2 plunky for (n = 0 ; ; n++) {
404 1.2 plunky if (services[n].name == NULL)
405 1.2 plunky usage();
406 1.1 gdamore
407 1.2 plunky if (strcasecmp(services[n].name, service) == 0)
408 1.2 plunky break;
409 1.1 gdamore }
410 1.1 gdamore
411 1.2 plunky memset(pdu, 0, pdu_len);
412 1.2 plunky pdu[0] = channel;
413 1.2 plunky
414 1.2 plunky ss = sdp_open_local(NULL);
415 1.2 plunky if (ss == NULL || (errno = sdp_error(ss)) != 0)
416 1.2 plunky err(EXIT_FAILURE, "sdp_open_local");
417 1.2 plunky
418 1.2 plunky if (sdp_register_service(ss, services[n].class, laddr,
419 1.2 plunky pdu, services[n].pdulen, NULL) != 0) {
420 1.2 plunky errno = sdp_error(ss);
421 1.2 plunky err(EXIT_FAILURE, "sdp_register_service");
422 1.2 plunky }
423 1.2 plunky
424 1.2 plunky len = sizeof(sa);
425 1.2 plunky fd = accept(sv, (struct sockaddr *)&sa, &len);
426 1.2 plunky if (fd < 0)
427 1.2 plunky err(EXIT_FAILURE, "accept");
428 1.2 plunky
429 1.2 plunky memset(&l, 0, sizeof(l));
430 1.2 plunky l.l_onoff = 1;
431 1.2 plunky l.l_linger = 5;
432 1.2 plunky if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0)
433 1.2 plunky err(EXIT_FAILURE, "linger()");
434 1.2 plunky
435 1.2 plunky close(sv);
436 1.2 plunky return fd;
437 1.2 plunky }
438 1.1 gdamore
439 1.2 plunky void
440 1.2 plunky copy_data(int src, int dst)
441 1.1 gdamore {
442 1.2 plunky static char buf[BUFSIZ];
443 1.2 plunky ssize_t nr, nw, off;
444 1.1 gdamore
445 1.2 plunky while ((nr = read(src, buf, sizeof(buf))) == -1) {
446 1.2 plunky if (errno != EINTR) {
447 1.2 plunky syslog(LOG_ERR, "read failed: %m");
448 1.2 plunky exit(EXIT_FAILURE);
449 1.2 plunky }
450 1.2 plunky }
451 1.1 gdamore
452 1.3 plunky if (nr == 0) /* reached EOF */
453 1.3 plunky done++;
454 1.3 plunky
455 1.2 plunky for (off = 0 ; nr ; nr -= nw, off += nw) {
456 1.2 plunky if ((nw = write(dst, buf + off, (size_t)nr)) == -1) {
457 1.2 plunky syslog(LOG_ERR, "write failed: %m");
458 1.2 plunky exit(EXIT_FAILURE);
459 1.1 gdamore }
460 1.1 gdamore }
461 1.2 plunky }
462 1.1 gdamore
463 1.2 plunky void
464 1.2 plunky sighandler(int s)
465 1.2 plunky {
466 1.1 gdamore
467 1.2 plunky done++;
468 1.2 plunky }
469 1.2 plunky
470 1.2 plunky void
471 1.2 plunky reset_tio(void)
472 1.1 gdamore {
473 1.1 gdamore
474 1.2 plunky tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
475 1.2 plunky }
476 1.2 plunky
477 1.2 plunky void
478 1.1 gdamore usage(void)
479 1.1 gdamore {
480 1.2 plunky struct service *s;
481 1.2 plunky
482 1.3 plunky fprintf(stderr, "Usage: %s [-d device] [-s service] [-t tty] -a bdaddr | -c channel\n"
483 1.2 plunky "\n"
484 1.2 plunky "Where:\n"
485 1.2 plunky "\t-a bdaddr remote device address\n"
486 1.3 plunky "\t-c channel local RFCOMM channel\n"
487 1.2 plunky "\t-d device local device address\n"
488 1.3 plunky "\t-s service service class\n"
489 1.2 plunky "\t-t tty run in background using pty\n"
490 1.3 plunky "\n", getprogname());
491 1.2 plunky
492 1.3 plunky fprintf(stderr, "Known service classes:\n");
493 1.2 plunky for (s = services ; s->name != NULL ; s++)
494 1.3 plunky fprintf(stderr, "\t%-13s%s\n", s->name, s->description);
495 1.1 gdamore
496 1.1 gdamore exit(EXIT_FAILURE);
497 1.2 plunky }
498