blob: 0f8cdb990ed91aef9b4865d1de0c548512bf68e3 [file] [log] [blame]
Vadim Iosevichd50ea462017-03-30 16:19:08 +03001/*
2 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
13 * * Neither the name of The Linux Foundation nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <cerrno>
34#include <unistd.h>
35#include <netinet/in.h>
36#include <sys/socket.h>
37#include <arpa/inet.h>
38#include <net/if.h>
39#include <netinet/in.h>
40#include <sys/ioctl.h>
41#include <sys/types.h>
42#include <fstream>
43#include "server.h"
44#include "cmdiface.h"
45#include "debug.h"
46#include "pmc_file.h"
47#include "file_reader.h"
48
49// Thread parameters structure
50typedef struct {
51 int sock;
52 struct sockaddr address;
53 int addr_len;
54} connection_par_t;
55
56volatile int Server::shutdown_flag = 0;
57volatile int Server::running_threads = 0;
58pthread_mutex_t Server::threads_counter_mutex = PTHREAD_MUTEX_INITIALIZER;
59
60void *Server::server_process(void *ptr)
61{
62 char *buf;
63 CmdIface *iface;
64 int rw;
65
66 if(ptr == NULL)
67 {
68 pthread_exit(0);
69 }
70
71 connection_par_t *par = (connection_par_t*)ptr;
72 iface = new CmdIface();
73 buf = new char[MAX_INPUT_BUF+1];
Vadim Iosevich82902242018-02-13 14:07:05 +020074 if (!buf)
75 {
76 LOG_ERROR << "Cannot allocate server receive buffer" << std::endl;
77 pthread_exit(0);
78 }
79
Vadim Iosevichd50ea462017-03-30 16:19:08 +030080 memset(buf, 0, MAX_INPUT_BUF+1);
81 rw = read(par->sock, buf, MAX_INPUT_BUF);
82
83 while(rw > 0)
84 {
85 // Got some data
86 if(rw)
87 {
88 buf[rw] = '\0';
89 LOG_INFO << "Client Command: " << PlainStr(buf) << std::endl;
90 int cmdKeepalive = iface->do_command((const char*)buf, rw);
91 LOG_DEBUG << "Command handling keepalive: " << cmdKeepalive << std::endl;
92
93 // Check for keepalive return value
94 int replyKeepalive = reply(par->sock, iface);
95
96 if(cmdKeepalive == KEEPALIVE_CLOSE || replyKeepalive == KEEPALIVE_CLOSE)
97 {
98 // Handler asked to close the connection
99 rw = 0;
100 break;
101 }
102 }
103 rw = read(par->sock, buf, MAX_INPUT_BUF);
104 }
105
106 // if 0 - connection is closed, <0 - error
107 LOG_DEBUG << "Closing connection. Read result: " << rw << std::endl;
108 close(par->sock);
109 delete[] buf;
110 delete iface;
111 delete par;
112 pthread_mutex_lock(&threads_counter_mutex);
113 running_threads--;
114 pthread_mutex_unlock(&threads_counter_mutex);
115 pthread_exit(0);
116}
117
118int Server::reply(int sock, CmdIface* iface)
119{
120 LOG_INFO << "Reply: " << PlainStr(iface->get_reply()) << std::endl;
121
122 switch (iface->get_reply_type())
123 {
124 case REPLY_TYPE_BUFFER:
125 return reply_buffer(sock, iface->get_reply(), iface->get_reply_len());
126
127 case REPLY_TYPE_FILE:
128 return reply_file(sock, iface->get_reply());
129
130 default:
131 LOG_ERROR << "Unexpected reply type: " << iface->get_reply_type() << std::endl;
132 return KEEPALIVE_CLOSE;
133 }
134}
135
136int Server::reply_buffer(int sock, const char* pBuf, size_t len)
137{
138 LOG_DEBUG << "Replying from a buffer (" << len << "B) Content: " << PlainStr(pBuf) << std::endl;
139
140 if (0 == len)
141 {
142 LOG_ERROR << "No reply generated by a command handler - connection will be closed" << std::endl;
143 return KEEPALIVE_CLOSE;
144 }
145
146 if (false == send_buffer(sock, pBuf, len))
147 {
148 return KEEPALIVE_CLOSE;
149 }
150
151 return KEEPALIVE_OK;
152}
153
154int Server::reply_file(int sock, const char* pFileName)
155{
156 LOG_DEBUG << "Replying from a file: " << pFileName << std::endl;
157
158 FileReader fileReader(pFileName);
159 size_t fileSize = fileReader.GetFileSize();
160
161 if (0 == fileSize)
162 {
163 LOG_ERROR << "No file content is available for reply" << std::endl;
164 return KEEPALIVE_CLOSE;
165 }
166
167 static const size_t BUF_LEN = 64 * 1024;
168
169 char* pBuf = new char[BUF_LEN];
170 size_t chunkSize = 0;
171 bool isError = false;
172
173 do
174 {
175 LOG_VERBOSE << "Requesting for a file chunk" << std::endl;
176
177 chunkSize = fileReader.ReadChunk(pBuf, BUF_LEN);
178 if (chunkSize > 0)
179 {
180 if (false == send_buffer(sock, pBuf, chunkSize))
181 {
182 LOG_ERROR << "Send error detected" << std::endl;
183 isError = true;
184 break;
185 }
186 }
187
188 // Error/Completion may occur with non-zero chunk as well
189 if (fileReader.IsError())
190 {
191 LOG_ERROR << "File read error detected" << std::endl;
192 isError = true;
193 break;
194 }
195
196 if (fileReader.IsCompleted())
197 {
198 LOG_DEBUG << "File completion detected" << std::endl;
199 break;
200 }
201
202 LOG_DEBUG << "File Chunk Delivered: " << chunkSize << "B" << std::endl;
203 }
204 while (chunkSize > 0);
205
206 delete[] pBuf;
207
208 if (isError)
209 {
210 LOG_ERROR << "Error occured while replying file content" << std::endl;
211 return KEEPALIVE_CLOSE;
212 }
213 else
214 {
215 LOG_DEBUG << "File Content successfully delivered" << std::endl;
216 return KEEPALIVE_OK;
217 }
218}
219
220bool Server::send_buffer(int sock, const char* pBuf, size_t len)
221{
222 if (!pBuf)
223 {
224 LOG_ERROR << "Cannot reply to a command - No buffer is provided" << std::endl;
225 return false;
226 }
227
228 size_t sentTillNow = 0;
229
230 while(sentTillNow < len)
231 {
232 ssize_t sentBytes = write(sock, pBuf, len);
233 if (sentBytes < 0)
234 {
235 int lastErrno = errno;
236 LOG_ERROR << "Cannot send response buffer."
237 << " Error:" << lastErrno
238 << " Message: " << strerror(lastErrno)
239 << std::endl;
240 return false;
241 }
242
243 sentTillNow += sentBytes;
244 LOG_DEBUG << "Sent response chunk."
245 << " Chunk Size: " << sentBytes
246 << " Sent till now: " << sentTillNow
247 << " Response Length: " << len
248 << std::endl;
249 }
250
251 LOG_DEBUG << "Response buffer fully sent" << std::endl;
252 return true;
253}
254
255/*
256 Close the server to allow un-bind te socket - allowing future connections without delay
257*/
258int Server::stop()
259{
260 LOG_INFO << "Stopping the server" << std::endl;
261 shutdown(sock, SHUT_RDWR);
262 return 0;
263}
264
265/*
266 Initialize server on the given port. The function returns in case of error,
267 otherwise it doesn't return
268*/
269int Server::start(int port)
270{
271 LOG_INFO << "Starting the server on port " << port << std::endl;
272
273 struct sockaddr_in address;
274 sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
275
276 if(sock < 0)
277 {
278 LOG_ERROR << "Cannot create a socket on port " << port << std::endl;
279 return -1;
280 }
281
282 address.sin_family = AF_INET;
283 address.sin_addr.s_addr = INADDR_ANY;
284 address.sin_port = htons(port);
285
286 // Set the "Re-Use" socket option - allows reconnections after wilserver exit
287 int reuse = 1;
288 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
289
290 if(bind(sock, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) < 0)
291 {
292 LOG_ERROR << "Cannot bind socket to port " << port << std::endl;
293 return -2;
294 }
295
296 if (listen(sock, 5) < 0)
297 {
298 LOG_ERROR << "Cannot listen on port " << port << std::endl;
299 return -3;
300 }
301
302 shutdown_flag = 0;
303 while (shutdown_flag == 0) {
304 pthread_t thread;
305 connection_par_t *par = new connection_par_t;
306 par->sock = accept(sock, &par->address, (socklen_t*)&par->addr_len);
307 if(par->sock < 0)
308 delete par;
309 else {
310 pthread_mutex_lock(&threads_counter_mutex);
311 running_threads++;
312 pthread_mutex_unlock(&threads_counter_mutex);
313 pthread_create(&thread, 0, &Server::server_process, (void *)par);
314 pthread_detach(thread);
315 }
316
317 }
318 // Wait till all the threads are done in case we ever exit the loop above
319 while(running_threads > 0)
320 sleep(1);
321
322 LOG_INFO << "Server shutdown" << std::endl;
323
324 return 0; // Wont get here, just to avoid the warning
325}