Code

frontend: Handle STARTUP and PING commands.
[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                         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)
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)
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)
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)
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)
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)
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 : */