23bbb33920f2186b56004c5b3d8d52d5730a6b01
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 sdb_log(SDB_LOG_WARNING, "frontend: Ignoring invalid command");
77 status = -1;
78 break;
79 }
81 /* remove the command from the buffer */
82 if (conn->cmd_len)
83 sdb_strbuf_skip(conn->buf, conn->cmd_len);
84 conn->cmd = CONNECTION_IDLE;
85 conn->cmd_len = 0;
86 return status;
87 } /* command_handle */
89 /* initialize the connection state information */
90 static int
91 command_init(sdb_conn_t *conn)
92 {
93 size_t len;
95 assert(conn && (conn->cmd == CONNECTION_IDLE) && (! conn->cmd_len));
97 conn->cmd = connection_get_int32(conn, 0);
98 conn->cmd_len = connection_get_int32(conn, sizeof(uint32_t));
100 len = 2 * sizeof(uint32_t);
101 if (conn->cmd == CONNECTION_IDLE)
102 len += conn->cmd_len;
103 sdb_strbuf_skip(conn->buf, len);
104 return 0;
105 } /* command_init */
107 /* returns negative value on error, 0 on EOF, number of octets else */
108 static ssize_t
109 connection_read(sdb_conn_t *conn)
110 {
111 ssize_t n = 0;
113 while (42) {
114 ssize_t status;
116 errno = 0;
117 status = sdb_strbuf_read(conn->buf, conn->fd, 1024);
118 if (status < 0) {
119 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
120 break;
121 return (int)status;
122 }
123 else if (! status) /* EOF */
124 break;
126 n += status;
127 }
129 return n;
130 } /* connection_read */
132 /*
133 * public API
134 */
136 int
137 sdb_connection_init(sdb_conn_t *conn)
138 {
139 if (conn->buf) {
140 sdb_log(SDB_LOG_WARNING, "frontend: Attempted to re-initialize "
141 "a frontend connection");
142 return -1;
143 }
145 conn->buf = sdb_strbuf_create(/* size = */ 128);
146 if (! conn->buf) {
147 sdb_log(SDB_LOG_ERR, "frontend: Failed to allocate a read buffer "
148 "for a new connection");
149 sdb_connection_close(conn);
150 return -1;
151 }
153 conn->cmd = CONNECTION_IDLE;
154 conn->cmd_len = 0;
155 conn->fd = -1;
156 return 0;
157 } /* sdb_connection_init */
159 void
160 sdb_connection_close(sdb_conn_t *conn)
161 {
162 size_t len;
164 if (conn->buf) {
165 len = sdb_strbuf_len(conn->buf);
166 if (len)
167 sdb_log(SDB_LOG_INFO, "frontend: Discarding incomplete command "
168 "(%zu byte%s left in buffer)", len, len == 1 ? "" : "s");
169 }
171 sdb_log(SDB_LOG_DEBUG, "frontend: Closing connection on fd=%i",
172 conn->fd);
173 close(conn->fd);
174 conn->fd = -1;
176 sdb_strbuf_destroy(conn->buf);
177 conn->buf = NULL;
178 } /* sdb_connection_fini */
180 ssize_t
181 sdb_connection_read(sdb_conn_t *conn)
182 {
183 ssize_t n = 0;
185 while (42) {
186 ssize_t status = connection_read(conn);
188 if ((conn->cmd == CONNECTION_IDLE) && (! conn->cmd_len)
189 && (sdb_strbuf_len(conn->buf) >= 2 * sizeof(int32_t)))
190 command_init(conn);
191 if ((conn->cmd != CONNECTION_IDLE)
192 && (sdb_strbuf_len(conn->buf) >= conn->cmd_len))
193 command_handle(conn);
195 if (status <= 0)
196 break;
198 n += status;
199 }
200 return n;
201 } /* sdb_connection_read */
203 ssize_t
204 sdb_connection_send(sdb_conn_t *conn, uint32_t code,
205 uint32_t msg_len, char *msg)
206 {
207 size_t len = 2 * sizeof(uint32_t) + msg_len;
208 char buffer[len];
209 char *buf;
211 uint32_t tmp;
213 if ((! conn) || (conn->fd < 0))
214 return -1;
216 tmp = htonl(code);
217 memcpy(buffer, &tmp, sizeof(uint32_t));
219 tmp = htonl(msg_len);
220 memcpy(buffer + sizeof(uint32_t), &tmp, sizeof(uint32_t));
222 if (msg_len)
223 memcpy(buffer + 2 * sizeof(uint32_t), msg, msg_len);
225 buf = buffer;
226 while (len > 0) {
227 ssize_t status;
229 /* XXX: use select() */
231 errno = 0;
232 status = write(conn->fd, buf, len);
233 if (status < 0) {
234 char errbuf[1024];
236 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
237 continue;
238 if (errno == EINTR)
239 continue;
241 sdb_log(SDB_LOG_ERR, "frontend: Failed to send msg "
242 "(code: %u, len: %u) to client: %s", code, msg_len,
243 sdb_strerror(errno, errbuf, sizeof(errbuf)));
244 return status;
245 }
247 len -= (size_t)status;
248 buf += status;
249 }
250 return (ssize_t)len;
251 } /* sdb_connection_send */
253 int
254 sdb_connection_ping(sdb_conn_t *conn)
255 {
256 if ((! conn) || (conn->cmd != CONNECTION_PING))
257 return -1;
259 /* we're alive */
260 sdb_connection_send(conn, CONNECTION_OK, 0, NULL);
261 return 0;
262 } /* sdb_connection_ping */
264 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */