Code

90361fe9a65b57a4ebaf8c855d3ca5d430b98f30
[sysdb.git] / src / frontend / connection.c
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)
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)
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)
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)
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)
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)
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 : */