962cd9cf8d9a14f9a3bc2f2a864e993958eb467d
1 /*
2 * SysDB - src/frontend/connection.c
3 * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
28 #include "sysdb.h"
29 #include "core/error.h"
30 #include "core/object.h"
31 #include "frontend/connection-private.h"
32 #include "utils/strbuf.h"
34 #include <assert.h>
35 #include <errno.h>
37 #include <arpa/inet.h>
38 #include <fcntl.h>
40 #include <string.h>
42 /*
43 * private data types
44 */
46 /* name of connection objects */
47 #define CONN_FD_PREFIX "conn#"
48 #define CONN_FD_PLACEHOLDER "XXXXXXX"
50 static int
51 connection_init(sdb_object_t *obj, va_list ap)
52 {
53 sdb_conn_t *conn;
54 int sock_fd;
55 int sock_fl;
57 assert(obj);
58 conn = CONN(obj);
60 sock_fd = va_arg(ap, int);
62 conn->buf = sdb_strbuf_create(/* size = */ 128);
63 if (! conn->buf) {
64 sdb_log(SDB_LOG_ERR, "frontend: Failed to allocate a read buffer "
65 "for a new connection");
66 return -1;
67 }
68 conn->errbuf = sdb_strbuf_create(0);
69 if (! conn->errbuf) {
70 sdb_log(SDB_LOG_ERR, "frontend: Failed to allocate an error buffer "
71 "for a new connection");
72 return -1;
73 }
75 conn->client_addr_len = sizeof(conn->client_addr);
76 conn->fd = accept(sock_fd, (struct sockaddr *)&conn->client_addr,
77 &conn->client_addr_len);
79 if (conn->fd < 0) {
80 char buf[1024];
81 sdb_log(SDB_LOG_ERR, "frontend: Failed to accept remote "
82 "connection: %s", sdb_strerror(errno,
83 buf, sizeof(buf)));
84 return -1;
85 }
87 if (conn->client_addr.ss_family != AF_UNIX) {
88 sdb_log(SDB_LOG_ERR, "frontend: Accepted connection using "
89 "unexpected family type %d", conn->client_addr.ss_family);
90 return -1;
91 }
93 sock_fl = fcntl(conn->fd, F_GETFL);
94 if (fcntl(conn->fd, F_SETFL, sock_fl | O_NONBLOCK)) {
95 char buf[1024];
96 sdb_log(SDB_LOG_ERR, "frontend: Failed to switch connection conn#%i "
97 "to non-blocking mode: %s", conn->fd,
98 sdb_strerror(errno, buf, sizeof(buf)));
99 return -1;
100 }
102 sdb_log(SDB_LOG_DEBUG, "frontend: Accepted connection on fd=%i",
103 conn->fd);
105 conn->cmd = CONNECTION_IDLE;
106 conn->cmd_len = 0;
108 /* update the object name */
109 snprintf(obj->name + strlen(CONN_FD_PREFIX),
110 strlen(CONN_FD_PLACEHOLDER), "%i", conn->fd);
111 return 0;
112 } /* connection_init */
114 static void
115 connection_destroy(sdb_object_t *obj)
116 {
117 sdb_conn_t *conn;
118 size_t len;
120 assert(obj);
121 conn = CONN(obj);
123 if (conn->buf) {
124 len = sdb_strbuf_len(conn->buf);
125 if (len)
126 sdb_log(SDB_LOG_INFO, "frontend: Discarding incomplete command "
127 "(%zu byte%s left in buffer)", len, len == 1 ? "" : "s");
128 }
130 sdb_log(SDB_LOG_DEBUG, "frontend: Closing connection on fd=%i",
131 conn->fd);
132 close(conn->fd);
133 conn->fd = -1;
135 sdb_strbuf_destroy(conn->buf);
136 conn->buf = NULL;
137 sdb_strbuf_destroy(conn->errbuf);
138 conn->errbuf = NULL;
139 } /* connection_destroy */
141 static sdb_type_t connection_type = {
142 /* size = */ sizeof(sdb_conn_t),
143 /* init = */ connection_init,
144 /* destroy = */ connection_destroy,
145 };
147 /*
148 * connection handler functions
149 */
151 static uint32_t
152 connection_get_int32(sdb_conn_t *conn, size_t offset)
153 {
154 const char *data;
155 uint32_t n;
157 assert(conn && (sdb_strbuf_len(conn->buf) >= offset + sizeof(uint32_t)));
159 data = sdb_strbuf_string(conn->buf);
160 memcpy(&n, data + offset, sizeof(n));
161 n = ntohl(n);
162 return n;
163 } /* connection_get_int32 */
165 static int
166 command_handle(sdb_conn_t *conn)
167 {
168 int status = -1;
170 assert(conn && (conn->cmd != CONNECTION_IDLE));
172 sdb_log(SDB_LOG_DEBUG, "frontend: Handling command %u (len: %u)",
173 conn->cmd, conn->cmd_len);
175 /* reset */
176 sdb_strbuf_sprintf(conn->errbuf, "");
178 switch (conn->cmd) {
179 case CONNECTION_PING:
180 status = sdb_connection_ping(conn);
181 break;
182 case CONNECTION_STARTUP:
183 status = sdb_fe_session_start(conn);
184 break;
186 case CONNECTION_LIST:
187 status = sdb_fe_list(conn);
188 break;
190 default:
191 {
192 sdb_log(SDB_LOG_WARNING, "frontend: Ignoring invalid command");
193 sdb_strbuf_sprintf(conn->errbuf, "Invalid command %#x", conn->cmd);
194 status = -1;
195 break;
196 }
197 }
199 if (status)
200 sdb_connection_send(conn, CONNECTION_ERROR,
201 (uint32_t)sdb_strbuf_len(conn->errbuf),
202 sdb_strbuf_string(conn->errbuf));
204 /* remove the command from the buffer */
205 if (conn->cmd_len)
206 sdb_strbuf_skip(conn->buf, conn->cmd_len);
207 conn->cmd = CONNECTION_IDLE;
208 conn->cmd_len = 0;
209 return status;
210 } /* command_handle */
212 /* initialize the connection state information */
213 static int
214 command_init(sdb_conn_t *conn)
215 {
216 size_t len;
218 assert(conn && (conn->cmd == CONNECTION_IDLE) && (! conn->cmd_len));
220 conn->cmd = connection_get_int32(conn, 0);
221 conn->cmd_len = connection_get_int32(conn, sizeof(uint32_t));
223 len = 2 * sizeof(uint32_t);
224 if (conn->cmd == CONNECTION_IDLE)
225 len += conn->cmd_len;
226 sdb_strbuf_skip(conn->buf, len);
227 return 0;
228 } /* command_init */
230 /* returns negative value on error, 0 on EOF, number of octets else */
231 static ssize_t
232 connection_read(sdb_conn_t *conn)
233 {
234 ssize_t n = 0;
236 while (42) {
237 ssize_t status;
239 errno = 0;
240 status = sdb_strbuf_read(conn->buf, conn->fd, 1024);
241 if (status < 0) {
242 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
243 break;
244 return (int)status;
245 }
246 else if (! status) /* EOF */
247 break;
249 n += status;
250 }
252 return n;
253 } /* connection_read */
255 /*
256 * public API
257 */
259 sdb_conn_t *
260 sdb_connection_accept(int fd)
261 {
262 if (fd < 0)
263 return NULL;
265 /* the placeholder will be replaced with the accepted file
266 * descriptor when initializing the object */
267 return CONN(sdb_object_create(CONN_FD_PREFIX CONN_FD_PLACEHOLDER,
268 connection_type, fd));
269 } /* sdb_connection_create */
271 void
272 sdb_connection_close(sdb_conn_t *conn)
273 {
274 sdb_object_deref(SDB_OBJ(conn));
275 } /* sdb_connection_close */
277 ssize_t
278 sdb_connection_read(sdb_conn_t *conn)
279 {
280 ssize_t n = 0;
282 while (42) {
283 ssize_t status = connection_read(conn);
285 if ((conn->cmd == CONNECTION_IDLE) && (! conn->cmd_len)
286 && (sdb_strbuf_len(conn->buf) >= 2 * sizeof(int32_t)))
287 command_init(conn);
288 if ((conn->cmd != CONNECTION_IDLE)
289 && (sdb_strbuf_len(conn->buf) >= conn->cmd_len))
290 command_handle(conn);
292 if (status <= 0)
293 break;
295 n += status;
296 }
297 return n;
298 } /* sdb_connection_read */
300 ssize_t
301 sdb_connection_send(sdb_conn_t *conn, uint32_t code,
302 uint32_t msg_len, const char *msg)
303 {
304 size_t len = 2 * sizeof(uint32_t) + msg_len;
305 char buffer[len];
306 char *buf;
308 uint32_t tmp;
310 if ((! conn) || (conn->fd < 0))
311 return -1;
313 tmp = htonl(code);
314 memcpy(buffer, &tmp, sizeof(uint32_t));
316 tmp = htonl(msg_len);
317 memcpy(buffer + sizeof(uint32_t), &tmp, sizeof(uint32_t));
319 if (msg_len)
320 memcpy(buffer + 2 * sizeof(uint32_t), msg, msg_len);
322 buf = buffer;
323 while (len > 0) {
324 ssize_t status;
326 /* XXX: use select() */
328 errno = 0;
329 status = write(conn->fd, buf, len);
330 if (status < 0) {
331 char errbuf[1024];
333 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
334 continue;
335 if (errno == EINTR)
336 continue;
338 sdb_log(SDB_LOG_ERR, "frontend: Failed to send msg "
339 "(code: %u, len: %u) to client: %s", code, msg_len,
340 sdb_strerror(errno, errbuf, sizeof(errbuf)));
341 return status;
342 }
344 len -= (size_t)status;
345 buf += status;
346 }
347 return (ssize_t)len;
348 } /* sdb_connection_send */
350 int
351 sdb_connection_ping(sdb_conn_t *conn)
352 {
353 if ((! conn) || (conn->cmd != CONNECTION_PING))
354 return -1;
356 /* we're alive */
357 sdb_connection_send(conn, CONNECTION_OK, 0, NULL);
358 return 0;
359 } /* sdb_connection_ping */
361 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */