rfcomm_sppd.c revision 1.3 1 1.3 plunky /* $NetBSD: rfcomm_sppd.c,v 1.3 2007/03/01 21:44:30 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.3 plunky __RCSID("$NetBSD: rfcomm_sppd.c,v 1.3 2007/03/01 21:44:30 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.2 plunky * now we are ready to go, so either detach or 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.2 plunky if (tcsetattr(tty_in, TCSANOW, &t) < 0)
223 1.2 plunky err(EXIT_FAILURE, "tcsetattr");
224 1.1 gdamore
225 1.2 plunky atexit(reset_tio);
226 1.2 plunky } else {
227 1.2 plunky if (daemon(0, 0) < 0)
228 1.2 plunky err(EXIT_FAILURE, "daemon() failed");
229 1.1 gdamore }
230 1.1 gdamore
231 1.2 plunky /* catch signals */
232 1.2 plunky done = 0;
233 1.2 plunky (void)signal(SIGHUP, sighandler);
234 1.2 plunky (void)signal(SIGINT, sighandler);
235 1.2 plunky (void)signal(SIGPIPE, sighandler);
236 1.2 plunky (void)signal(SIGTERM, sighandler);
237 1.2 plunky
238 1.2 plunky openlog(getprogname(), LOG_PERROR | LOG_PID, LOG_DAEMON);
239 1.2 plunky syslog(LOG_INFO, "Starting on %s...", (tty ? tty : "stdio"));
240 1.2 plunky
241 1.2 plunky n = max(tty_in, rfcomm) + 1;
242 1.2 plunky while (!done) {
243 1.2 plunky FD_ZERO(&rdset);
244 1.2 plunky FD_SET(tty_in, &rdset);
245 1.2 plunky FD_SET(rfcomm, &rdset);
246 1.1 gdamore
247 1.2 plunky if (select(n, &rdset, NULL, NULL, NULL) < 0) {
248 1.1 gdamore if (errno == EINTR)
249 1.1 gdamore continue;
250 1.1 gdamore
251 1.2 plunky syslog(LOG_ERR, "select error: %m");
252 1.1 gdamore exit(EXIT_FAILURE);
253 1.1 gdamore }
254 1.1 gdamore
255 1.2 plunky if (FD_ISSET(tty_in, &rdset))
256 1.2 plunky copy_data(tty_in, rfcomm);
257 1.1 gdamore
258 1.2 plunky if (FD_ISSET(rfcomm, &rdset))
259 1.2 plunky copy_data(rfcomm, tty_out);
260 1.2 plunky }
261 1.1 gdamore
262 1.2 plunky syslog(LOG_INFO, "Completed on %s", (tty ? tty : "stdio"));
263 1.2 plunky exit(EXIT_SUCCESS);
264 1.2 plunky }
265 1.1 gdamore
266 1.2 plunky int
267 1.2 plunky open_tty(const char *tty)
268 1.1 gdamore {
269 1.1 gdamore char pty[PATH_MAX], *slash;
270 1.1 gdamore struct group *gr = NULL;
271 1.1 gdamore gid_t ttygid;
272 1.2 plunky int master;
273 1.1 gdamore
274 1.1 gdamore /*
275 1.1 gdamore * Construct master PTY name. The slave tty name must be less then
276 1.1 gdamore * PATH_MAX characters in length, must contain '/' character and
277 1.1 gdamore * must not end with '/'.
278 1.1 gdamore */
279 1.2 plunky if (strlen(tty) >= sizeof(pty))
280 1.2 plunky errx(EXIT_FAILURE, ": tty name too long");
281 1.1 gdamore
282 1.1 gdamore strlcpy(pty, tty, sizeof(pty));
283 1.1 gdamore slash = strrchr(pty, '/');
284 1.2 plunky if (slash == NULL || slash[1] == '\0')
285 1.2 plunky errx(EXIT_FAILURE, "%s: invalid tty", tty);
286 1.1 gdamore
287 1.1 gdamore slash[1] = 'p';
288 1.2 plunky if (strcmp(pty, tty) == 0)
289 1.2 plunky errx(EXIT_FAILURE, "Master and slave tty are the same (%s)", tty);
290 1.1 gdamore
291 1.2 plunky if ((master = open(pty, O_RDWR, 0)) < 0)
292 1.2 plunky err(EXIT_FAILURE, "%s", pty);
293 1.1 gdamore
294 1.1 gdamore /*
295 1.1 gdamore * Slave TTY
296 1.1 gdamore */
297 1.1 gdamore
298 1.1 gdamore if ((gr = getgrnam("tty")) != NULL)
299 1.1 gdamore ttygid = gr->gr_gid;
300 1.1 gdamore else
301 1.1 gdamore ttygid = (gid_t)-1;
302 1.1 gdamore
303 1.2 plunky (void)chown(tty, getuid(), ttygid);
304 1.2 plunky (void)chmod(tty, S_IRUSR | S_IWUSR | S_IWGRP);
305 1.2 plunky (void)revoke(tty);
306 1.2 plunky
307 1.2 plunky return master;
308 1.2 plunky }
309 1.1 gdamore
310 1.2 plunky int
311 1.3 plunky open_client(bdaddr_t *laddr, bdaddr_t *raddr, const char *service)
312 1.2 plunky {
313 1.2 plunky struct sockaddr_bt sa;
314 1.3 plunky struct service *s;
315 1.2 plunky struct linger l;
316 1.3 plunky char *ep;
317 1.2 plunky int fd;
318 1.3 plunky uint8_t channel;
319 1.3 plunky
320 1.3 plunky for (s = services ; ; s++) {
321 1.3 plunky if (s->name == NULL) {
322 1.3 plunky channel = strtoul(optarg, &ep, 10);
323 1.3 plunky if (*ep != '\0' || channel < 1 || channel > 30)
324 1.3 plunky errx(EXIT_FAILURE, "Invalid service: %s", service);
325 1.3 plunky
326 1.3 plunky break;
327 1.3 plunky }
328 1.3 plunky
329 1.3 plunky if (strcasecmp(s->name, service) == 0) {
330 1.3 plunky if (rfcomm_channel_lookup(laddr, raddr, s->class, &channel, &errno) < 0)
331 1.3 plunky err(EXIT_FAILURE, "%s", s->name);
332 1.3 plunky
333 1.3 plunky break;
334 1.3 plunky }
335 1.3 plunky }
336 1.1 gdamore
337 1.2 plunky memset(&sa, 0, sizeof(sa));
338 1.2 plunky sa.bt_len = sizeof(sa);
339 1.2 plunky sa.bt_family = AF_BLUETOOTH;
340 1.2 plunky bdaddr_copy(&sa.bt_bdaddr, laddr);
341 1.2 plunky
342 1.2 plunky fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
343 1.2 plunky if (fd < 0)
344 1.2 plunky err(EXIT_FAILURE, "socket()");
345 1.2 plunky
346 1.2 plunky if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
347 1.2 plunky err(EXIT_FAILURE, "bind(%s)", bt_ntoa(laddr, NULL));
348 1.2 plunky
349 1.2 plunky memset(&l, 0, sizeof(l));
350 1.2 plunky l.l_onoff = 1;
351 1.2 plunky l.l_linger = 5;
352 1.2 plunky if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0)
353 1.2 plunky err(EXIT_FAILURE, "linger()");
354 1.2 plunky
355 1.2 plunky sa.bt_channel = channel;
356 1.2 plunky bdaddr_copy(&sa.bt_bdaddr, raddr);
357 1.2 plunky
358 1.2 plunky if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
359 1.2 plunky err(EXIT_FAILURE, "connect(%s, %d)", bt_ntoa(raddr, NULL),
360 1.2 plunky channel);
361 1.1 gdamore
362 1.2 plunky return fd;
363 1.2 plunky }
364 1.1 gdamore
365 1.2 plunky /*
366 1.2 plunky * In all the profiles we currently support registering, the channel
367 1.2 plunky * is the first octet in the PDU, and it seems all the rest can be
368 1.2 plunky * zero, so we just use an array of uint8_t big enough to store the
369 1.2 plunky * largest, currently LAN. See <sdp.h> for definitions..
370 1.2 plunky */
371 1.2 plunky #define pdu_len sizeof(struct sdp_lan_profile)
372 1.1 gdamore
373 1.2 plunky int
374 1.2 plunky open_server(bdaddr_t *laddr, uint8_t channel, const char *service)
375 1.1 gdamore {
376 1.2 plunky struct sockaddr_bt sa;
377 1.2 plunky struct linger l;
378 1.2 plunky socklen_t len;
379 1.2 plunky void *ss;
380 1.2 plunky int sv, fd, n;
381 1.2 plunky uint8_t pdu[pdu_len];
382 1.1 gdamore
383 1.2 plunky memset(&sa, 0, sizeof(sa));
384 1.2 plunky sa.bt_len = sizeof(sa);
385 1.2 plunky sa.bt_family = AF_BLUETOOTH;
386 1.2 plunky bdaddr_copy(&sa.bt_bdaddr, laddr);
387 1.2 plunky sa.bt_channel = channel;
388 1.2 plunky
389 1.2 plunky sv = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
390 1.2 plunky if (sv < 0)
391 1.2 plunky err(EXIT_FAILURE, "socket()");
392 1.2 plunky
393 1.2 plunky if (bind(sv, (struct sockaddr *)&sa, sizeof(sa)) < 0)
394 1.2 plunky err(EXIT_FAILURE, "bind(%s, %d)", bt_ntoa(laddr, NULL),
395 1.2 plunky channel);
396 1.2 plunky
397 1.2 plunky if (listen(sv, 1) < 0)
398 1.2 plunky err(EXIT_FAILURE, "listen()");
399 1.2 plunky
400 1.2 plunky /* Register service with SDP server */
401 1.2 plunky for (n = 0 ; ; n++) {
402 1.2 plunky if (services[n].name == NULL)
403 1.2 plunky usage();
404 1.1 gdamore
405 1.2 plunky if (strcasecmp(services[n].name, service) == 0)
406 1.2 plunky break;
407 1.1 gdamore }
408 1.1 gdamore
409 1.2 plunky memset(pdu, 0, pdu_len);
410 1.2 plunky pdu[0] = channel;
411 1.2 plunky
412 1.2 plunky ss = sdp_open_local(NULL);
413 1.2 plunky if (ss == NULL || (errno = sdp_error(ss)) != 0)
414 1.2 plunky err(EXIT_FAILURE, "sdp_open_local");
415 1.2 plunky
416 1.2 plunky if (sdp_register_service(ss, services[n].class, laddr,
417 1.2 plunky pdu, services[n].pdulen, NULL) != 0) {
418 1.2 plunky errno = sdp_error(ss);
419 1.2 plunky err(EXIT_FAILURE, "sdp_register_service");
420 1.2 plunky }
421 1.2 plunky
422 1.2 plunky len = sizeof(sa);
423 1.2 plunky fd = accept(sv, (struct sockaddr *)&sa, &len);
424 1.2 plunky if (fd < 0)
425 1.2 plunky err(EXIT_FAILURE, "accept");
426 1.2 plunky
427 1.2 plunky memset(&l, 0, sizeof(l));
428 1.2 plunky l.l_onoff = 1;
429 1.2 plunky l.l_linger = 5;
430 1.2 plunky if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0)
431 1.2 plunky err(EXIT_FAILURE, "linger()");
432 1.2 plunky
433 1.2 plunky close(sv);
434 1.2 plunky return fd;
435 1.2 plunky }
436 1.1 gdamore
437 1.2 plunky void
438 1.2 plunky copy_data(int src, int dst)
439 1.1 gdamore {
440 1.2 plunky static char buf[BUFSIZ];
441 1.2 plunky ssize_t nr, nw, off;
442 1.1 gdamore
443 1.2 plunky while ((nr = read(src, buf, sizeof(buf))) == -1) {
444 1.2 plunky if (errno != EINTR) {
445 1.2 plunky syslog(LOG_ERR, "read failed: %m");
446 1.2 plunky exit(EXIT_FAILURE);
447 1.2 plunky }
448 1.2 plunky }
449 1.1 gdamore
450 1.3 plunky if (nr == 0) /* reached EOF */
451 1.3 plunky done++;
452 1.3 plunky
453 1.2 plunky for (off = 0 ; nr ; nr -= nw, off += nw) {
454 1.2 plunky if ((nw = write(dst, buf + off, (size_t)nr)) == -1) {
455 1.2 plunky syslog(LOG_ERR, "write failed: %m");
456 1.2 plunky exit(EXIT_FAILURE);
457 1.1 gdamore }
458 1.1 gdamore }
459 1.2 plunky }
460 1.1 gdamore
461 1.2 plunky void
462 1.2 plunky sighandler(int s)
463 1.2 plunky {
464 1.1 gdamore
465 1.2 plunky done++;
466 1.2 plunky }
467 1.2 plunky
468 1.2 plunky void
469 1.2 plunky reset_tio(void)
470 1.1 gdamore {
471 1.1 gdamore
472 1.2 plunky tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
473 1.2 plunky }
474 1.2 plunky
475 1.2 plunky void
476 1.1 gdamore usage(void)
477 1.1 gdamore {
478 1.2 plunky struct service *s;
479 1.2 plunky
480 1.3 plunky fprintf(stderr, "Usage: %s [-d device] [-s service] [-t tty] -a bdaddr | -c channel\n"
481 1.2 plunky "\n"
482 1.2 plunky "Where:\n"
483 1.2 plunky "\t-a bdaddr remote device address\n"
484 1.3 plunky "\t-c channel local RFCOMM channel\n"
485 1.2 plunky "\t-d device local device address\n"
486 1.3 plunky "\t-s service service class\n"
487 1.2 plunky "\t-t tty run in background using pty\n"
488 1.3 plunky "\n", getprogname());
489 1.2 plunky
490 1.3 plunky fprintf(stderr, "Known service classes:\n");
491 1.2 plunky for (s = services ; s->name != NULL ; s++)
492 1.3 plunky fprintf(stderr, "\t%-13s%s\n", s->name, s->description);
493 1.1 gdamore
494 1.1 gdamore exit(EXIT_FAILURE);
495 1.2 plunky }
496