main: nit, make scan/rfcomm/notify strict alternatives, share shutdown code
[libble/gsi.git] / main-rfcomm.c
blob25c3801a65402c9bbd86074a4a26051dde07e80c
1 /*
2 * Simple RFCOMM server implementation, modelled after
3 * http://people.csail.mit.edu/albert/bluez-intro/x502.html
5 * Creates a server socket, accepts up to one client at a time, reflects
6 * received data to the server (echo mode), terminates upon reception of
7 * CTRL-C / SIGTERM and friends.
9 * Was specifically written as a standalone application not using libble
10 * to demonstrate interoperability.
13 #include <bluetooth/bluetooth.h>
14 #include <bluetooth/rfcomm.h>
15 #include <poll.h>
16 #include <signal.h>
17 #include <stdarg.h>
18 #include <stdint.h>
19 #include <stdio.h>
20 #include <sys/socket.h>
21 #include <unistd.h>
23 #ifndef ARRAY_SIZE
24 # define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
25 #endif
27 static bdaddr_t local_addr = *BDADDR_ANY;
28 static int channel = 1;
30 static int log_level = 1;
31 static int done = 0;
33 static void log_msg(int level, const char *fmt, ...)
35 va_list arg;
36 FILE *log_stream;
38 if (level > log_level)
39 return;
41 log_stream = stdout;
42 va_start(arg, fmt);
43 vfprintf(log_stream, fmt, arg);
44 va_end(arg);
45 fflush(log_stream);
48 static int server_socket(void)
50 int fd;
51 struct sockaddr_rc addr;
52 int rc;
54 log_msg(2, "socket()\n");
55 fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
56 if (fd < 0) {
57 perror("socket()");
58 return -1;
61 log_msg(2, "bind()\n");
62 memset(&addr, 0, sizeof(addr));
63 addr.rc_family = AF_BLUETOOTH;
64 addr.rc_channel = channel;
65 memcpy(&addr.rc_bdaddr, &local_addr, sizeof(addr.rc_bdaddr));
66 rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
67 if (rc < 0) {
68 perror("bind()");
69 close(fd);
70 return -1;
73 log_msg(2, "listen()\n");
74 rc = listen(fd, -1);
75 if (rc < 0) {
76 perror("listen()");
77 close(fd);
78 return -1;
81 log_msg(1, "listening ...\n");
82 return fd;
85 static void close_server(int fd)
88 log_msg(2, "close() server\n");
89 close(fd);
90 log_msg(1, "server closed\n");
93 static int accept_client(int srv_fd)
95 int cli_fd;
96 socklen_t len;
97 struct sockaddr_rc addr;
98 char peer[20];
100 if (srv_fd < 0)
101 return -1;
103 log_msg(2, "accept()\n");
104 len = sizeof(addr);
105 cli_fd = accept(srv_fd, (struct sockaddr *)&addr, &len);
106 if (cli_fd < 0) {
107 perror("accept()");
108 return -1;
110 ba2str(&addr.rc_bdaddr, peer);
111 log_msg(1, "accept: fd %d, client addr %s\n", cli_fd, peer);
112 return cli_fd;
115 static int process_client(int fd, const char *msg)
117 ssize_t rdlen, wrlen;
119 rdlen = strlen(msg);
120 log_msg(2, "write()\n");
121 wrlen = write(fd, msg, rdlen);
122 if (wrlen < 0) {
123 perror("write()");
124 return -1;
126 log_msg(1, "sent: fd %d, data: %s\n", fd, msg);
127 if (wrlen != rdlen) {
128 log_msg(0, "write mismatch: fd %d, rcvd %zd, sent %zd\n", fd, rdlen, wrlen);
130 return 0;
133 static int receive_client(int fd)
135 char buff[256];
136 ssize_t rdlen;
137 int rc;
139 if (fd < 0)
140 return -1;
142 log_msg(2, "read()\n");
143 rdlen = read(fd, buff, sizeof(buff));
144 if (rdlen < 0) {
145 perror("read()");
146 return -1;
148 buff[rdlen] = '\0';
149 log_msg(1, "rcvd: fd %d, data: %s\n", fd, buff);
150 rc = process_client(fd, buff);
151 if (rc < 0)
152 return -1;
153 return rdlen;
156 static void close_client(int fd)
159 log_msg(2, "close() client\n");
160 close(fd);
161 log_msg(1, "close: fd %d, client gone\n", fd);
164 static void signal_handler(int sig)
167 done++;
170 static int is_readable(int fd)
172 struct pollfd fds[1];
173 int rc;
175 memset(fds, 0, sizeof(fds));
176 fds[0].fd = fd;
177 fds[0].events = POLLIN;
178 rc = poll(fds, ARRAY_SIZE(fds), 0);
179 if (rc < 1)
180 return 0;
182 * Implementor's note: Silently accept "error" conditions here
183 * as well, to have the socket closed on failed read. Or else
184 * we'd need another has_error() test routine, too.
186 return 1;
189 int main(int argc, char *argv[])
191 int server_fd, client_fd;
192 int rc;
194 signal(SIGHUP, signal_handler);
195 signal(SIGINT, signal_handler);
196 signal(SIGTERM, signal_handler);
198 server_fd = server_socket();
199 client_fd = -1;
200 while (!done) {
201 if (is_readable(server_fd)) {
202 client_fd = accept_client(server_fd);
203 if (client_fd < 0)
204 done++;
206 if (client_fd >= 0 && is_readable(client_fd)) {
207 rc = receive_client(client_fd);
208 if (rc <= 0) {
209 close_client(client_fd);
210 client_fd = -1;
213 usleep(10000);
215 close_server(server_fd);
216 return 0;