2 * This file is part of the libble project.
4 * Copyright (C) 2018-2019 Gerhard Sittig <gerhard.sittig@gmx.net>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * Simple RFCOMM server implementation, modelled after
22 * http://people.csail.mit.edu/albert/bluez-intro/x502.html
24 * Creates a server socket, accepts up to one client at a time, reflects
25 * received data to the server (echo mode), terminates upon reception of
26 * CTRL-C / SIGTERM and friends.
28 * Was specifically written as a standalone application not using libble
29 * to demonstrate interoperability.
32 /* {{{ includes, defines, globals */
34 #include <bluetooth/bluetooth.h>
35 #include <bluetooth/rfcomm.h>
41 #include <sys/socket.h>
45 # define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
48 static bdaddr_t local_addr
= *BDADDR_ANY
;
49 static int channel
= 1;
51 static int log_level
= 1;
54 /* }}} includes, defines, globals */
55 /* {{{ logging, progress messages */
57 static void log_msg(int level
, const char *fmt
, ...)
62 if (level
> log_level
)
67 vfprintf(log_stream
, fmt
, arg
);
72 /* }}} logging, progress messages */
73 /* {{{ helper routines */
75 static int server_socket(void)
78 struct sockaddr_rc addr
;
81 log_msg(2, "socket()\n");
82 fd
= socket(AF_BLUETOOTH
, SOCK_STREAM
, BTPROTO_RFCOMM
);
88 log_msg(2, "bind()\n");
89 memset(&addr
, 0, sizeof(addr
));
90 addr
.rc_family
= AF_BLUETOOTH
;
91 addr
.rc_channel
= channel
;
92 memcpy(&addr
.rc_bdaddr
, &local_addr
, sizeof(addr
.rc_bdaddr
));
93 rc
= bind(fd
, (struct sockaddr
*)&addr
, sizeof(addr
));
100 log_msg(2, "listen()\n");
108 log_msg(1, "listening ...\n");
112 static void close_server(int fd
)
115 log_msg(2, "close() server\n");
117 log_msg(1, "server closed\n");
120 static int accept_client(int srv_fd
)
124 struct sockaddr_rc addr
;
130 log_msg(2, "accept()\n");
132 cli_fd
= accept(srv_fd
, (struct sockaddr
*)&addr
, &len
);
137 ba2str(&addr
.rc_bdaddr
, peer
);
138 log_msg(1, "accept: fd %d, client addr %s\n", cli_fd
, peer
);
142 static int process_client(int fd
, const char *msg
)
144 ssize_t rdlen
, wrlen
;
147 log_msg(2, "write()\n");
148 wrlen
= write(fd
, msg
, rdlen
);
153 log_msg(1, "sent: fd %d, data: %s\n", fd
, msg
);
154 if (wrlen
!= rdlen
) {
155 log_msg(0, "write mismatch: fd %d, rcvd %zd, sent %zd\n", fd
, rdlen
, wrlen
);
160 static int receive_client(int fd
)
169 log_msg(2, "read()\n");
170 rdlen
= read(fd
, buff
, sizeof(buff
));
176 log_msg(1, "rcvd: fd %d, data: %s\n", fd
, buff
);
177 rc
= process_client(fd
, buff
);
183 static void close_client(int fd
)
186 log_msg(2, "close() client\n");
188 log_msg(1, "close: fd %d, client gone\n", fd
);
191 /* }}} helper routines */
192 /* {{{ main program */
194 static void signal_handler(int sig
)
200 static int is_readable(int fd
)
202 struct pollfd fds
[1];
205 memset(fds
, 0, sizeof(fds
));
207 fds
[0].events
= POLLIN
;
208 rc
= poll(fds
, ARRAY_SIZE(fds
), 0);
212 * Implementor's note: Silently accept "error" conditions here
213 * as well, to have the socket closed on failed read. Or else
214 * we'd need another has_error() test routine, too.
219 int main(int argc
, char *argv
[])
221 int server_fd
, client_fd
;
224 signal(SIGHUP
, signal_handler
);
225 signal(SIGINT
, signal_handler
);
226 signal(SIGTERM
, signal_handler
);
228 server_fd
= server_socket();
231 if (is_readable(server_fd
)) {
232 client_fd
= accept_client(server_fd
);
236 if (client_fd
>= 0 && is_readable(client_fd
)) {
237 rc
= receive_client(client_fd
);
239 close_client(client_fd
);
245 close_server(server_fd
);
249 /* }}} main program */
251 * vim:foldmethod=marker: