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 "frontend/connection.h"
31 #include "utils/strbuf.h"
33 #include <assert.h>
34 #include <errno.h>
36 #include <arpa/inet.h>
38 #include <string.h>
40 /*
41 * connection handler functions
42 */
44 static uint32_t
45 connection_get_int32(sdb_conn_t *conn, size_t offset)
46 {
47 const char *data;
48 uint32_t n;
50 assert(conn && (sdb_strbuf_len(conn->buf) >= offset + sizeof(uint32_t)));
52 data = sdb_strbuf_string(conn->buf);
53 memcpy(&n, data + offset, sizeof(n));
54 n = ntohl(n);
55 return n;
56 } /* connection_get_int32 */
58 static int
59 command_handle(sdb_conn_t *conn)
60 {
61 int status = -1;
63 assert(conn && (conn->cmd != CONNECTION_IDLE));
65 sdb_log(SDB_LOG_DEBUG, "frontend: Handling command %u (len: %u)",
66 conn->cmd, conn->cmd_len);
68 switch (conn->cmd) {
69 case CONNECTION_PING:
70 status = sdb_connection_ping(conn);
71 break;
72 case CONNECTION_STARTUP:
73 status = sdb_session_start(conn);
74 break;
75 default:
76 {
77 char errbuf[1024];
78 sdb_log(SDB_LOG_WARNING, "frontend: Ignoring invalid command");
79 snprintf(errbuf, sizeof(errbuf), "Invalid command %#x", conn->cmd);
80 sdb_connection_send(conn, CONNECTION_ERROR,
81 (uint32_t)(strlen(errbuf) + 1), errbuf);
82 status = -1;
83 break;
84 }
85 }
87 /* remove the command from the buffer */
88 if (conn->cmd_len)
89 sdb_strbuf_skip(conn->buf, conn->cmd_len);
90 conn->cmd = CONNECTION_IDLE;
91 conn->cmd_len = 0;
92 return status;
93 } /* command_handle */
95 /* initialize the connection state information */
96 static int
97 command_init(sdb_conn_t *conn)
98 {
99 size_t len;
101 assert(conn && (conn->cmd == CONNECTION_IDLE) && (! conn->cmd_len));
103 conn->cmd = connection_get_int32(conn, 0);
104 conn->cmd_len = connection_get_int32(conn, sizeof(uint32_t));
106 len = 2 * sizeof(uint32_t);
107 if (conn->cmd == CONNECTION_IDLE)
108 len += conn->cmd_len;
109 sdb_strbuf_skip(conn->buf, len);
110 return 0;
111 } /* command_init */
113 /* returns negative value on error, 0 on EOF, number of octets else */
114 static ssize_t
115 connection_read(sdb_conn_t *conn)
116 {
117 ssize_t n = 0;
119 while (42) {
120 ssize_t status;
122 errno = 0;
123 status = sdb_strbuf_read(conn->buf, conn->fd, 1024);
124 if (status < 0) {
125 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
126 break;
127 return (int)status;
128 }
129 else if (! status) /* EOF */
130 break;
132 n += status;
133 }
135 return n;
136 } /* connection_read */
138 /*
139 * public API
140 */
142 int
143 sdb_connection_init(sdb_conn_t *conn)
144 {
145 if (conn->buf) {
146 sdb_log(SDB_LOG_WARNING, "frontend: Attempted to re-initialize "
147 "a frontend connection");
148 return -1;
149 }
151 conn->buf = sdb_strbuf_create(/* size = */ 128);
152 if (! conn->buf) {
153 sdb_log(SDB_LOG_ERR, "frontend: Failed to allocate a read buffer "
154 "for a new connection");
155 sdb_connection_close(conn);
156 return -1;
157 }
159 conn->cmd = CONNECTION_IDLE;
160 conn->cmd_len = 0;
161 conn->fd = -1;
162 return 0;
163 } /* sdb_connection_init */
165 void
166 sdb_connection_close(sdb_conn_t *conn)
167 {
168 size_t len;
170 if (conn->buf) {
171 len = sdb_strbuf_len(conn->buf);
172 if (len)
173 sdb_log(SDB_LOG_INFO, "frontend: Discarding incomplete command "
174 "(%zu byte%s left in buffer)", len, len == 1 ? "" : "s");
175 }
177 sdb_log(SDB_LOG_DEBUG, "frontend: Closing connection on fd=%i",
178 conn->fd);
179 close(conn->fd);
180 conn->fd = -1;
182 sdb_strbuf_destroy(conn->buf);
183 conn->buf = NULL;
184 } /* sdb_connection_fini */
186 ssize_t
187 sdb_connection_read(sdb_conn_t *conn)
188 {
189 ssize_t n = 0;
191 while (42) {
192 ssize_t status = connection_read(conn);
194 if ((conn->cmd == CONNECTION_IDLE) && (! conn->cmd_len)
195 && (sdb_strbuf_len(conn->buf) >= 2 * sizeof(int32_t)))
196 command_init(conn);
197 if ((conn->cmd != CONNECTION_IDLE)
198 && (sdb_strbuf_len(conn->buf) >= conn->cmd_len))
199 command_handle(conn);
201 if (status <= 0)
202 break;
204 n += status;
205 }
206 return n;
207 } /* sdb_connection_read */
209 ssize_t
210 sdb_connection_send(sdb_conn_t *conn, uint32_t code,
211 uint32_t msg_len, char *msg)
212 {
213 size_t len = 2 * sizeof(uint32_t) + msg_len;
214 char buffer[len];
215 char *buf;
217 uint32_t tmp;
219 if ((! conn) || (conn->fd < 0))
220 return -1;
222 tmp = htonl(code);
223 memcpy(buffer, &tmp, sizeof(uint32_t));
225 tmp = htonl(msg_len);
226 memcpy(buffer + sizeof(uint32_t), &tmp, sizeof(uint32_t));
228 if (msg_len)
229 memcpy(buffer + 2 * sizeof(uint32_t), msg, msg_len);
231 buf = buffer;
232 while (len > 0) {
233 ssize_t status;
235 /* XXX: use select() */
237 errno = 0;
238 status = write(conn->fd, buf, len);
239 if (status < 0) {
240 char errbuf[1024];
242 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
243 continue;
244 if (errno == EINTR)
245 continue;
247 sdb_log(SDB_LOG_ERR, "frontend: Failed to send msg "
248 "(code: %u, len: %u) to client: %s", code, msg_len,
249 sdb_strerror(errno, errbuf, sizeof(errbuf)));
250 return status;
251 }
253 len -= (size_t)status;
254 buf += status;
255 }
256 return (ssize_t)len;
257 } /* sdb_connection_send */
259 int
260 sdb_connection_ping(sdb_conn_t *conn)
261 {
262 if ((! conn) || (conn->cmd != CONNECTION_PING))
263 return -1;
265 /* we're alive */
266 sdb_connection_send(conn, CONNECTION_OK, 0, NULL);
267 return 0;
268 } /* sdb_connection_ping */
270 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */