blob: 5ee1b48b68cd2d6043df3f05a43d77fdff9f7223 [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];
74 memset(buf, 0, MAX_INPUT_BUF+1);
75 rw = read(par->sock, buf, MAX_INPUT_BUF);
76
77 while(rw > 0)
78 {
79 // Got some data
80 if(rw)
81 {
82 buf[rw] = '\0';
83 LOG_INFO << "Client Command: " << PlainStr(buf) << std::endl;
84 int cmdKeepalive = iface->do_command((const char*)buf, rw);
85 LOG_DEBUG << "Command handling keepalive: " << cmdKeepalive << std::endl;
86
87 // Check for keepalive return value
88 int replyKeepalive = reply(par->sock, iface);
89
90 if(cmdKeepalive == KEEPALIVE_CLOSE || replyKeepalive == KEEPALIVE_CLOSE)
91 {
92 // Handler asked to close the connection
93 rw = 0;
94 break;
95 }
96 }
97 rw = read(par->sock, buf, MAX_INPUT_BUF);
98 }
99
100 // if 0 - connection is closed, <0 - error
101 LOG_DEBUG << "Closing connection. Read result: " << rw << std::endl;
102 close(par->sock);
103 delete[] buf;
104 delete iface;
105 delete par;
106 pthread_mutex_lock(&threads_counter_mutex);
107 running_threads--;
108 pthread_mutex_unlock(&threads_counter_mutex);
109 pthread_exit(0);
110}
111
112int Server::reply(int sock, CmdIface* iface)
113{
114 LOG_INFO << "Reply: " << PlainStr(iface->get_reply()) << std::endl;
115
116 switch (iface->get_reply_type())
117 {
118 case REPLY_TYPE_BUFFER:
119 return reply_buffer(sock, iface->get_reply(), iface->get_reply_len());
120
121 case REPLY_TYPE_FILE:
122 return reply_file(sock, iface->get_reply());
123
124 default:
125 LOG_ERROR << "Unexpected reply type: " << iface->get_reply_type() << std::endl;
126 return KEEPALIVE_CLOSE;
127 }
128}
129
130int Server::reply_buffer(int sock, const char* pBuf, size_t len)
131{
132 LOG_DEBUG << "Replying from a buffer (" << len << "B) Content: " << PlainStr(pBuf) << std::endl;
133
134 if (0 == len)
135 {
136 LOG_ERROR << "No reply generated by a command handler - connection will be closed" << std::endl;
137 return KEEPALIVE_CLOSE;
138 }
139
140 if (false == send_buffer(sock, pBuf, len))
141 {
142 return KEEPALIVE_CLOSE;
143 }
144
145 return KEEPALIVE_OK;
146}
147
148int Server::reply_file(int sock, const char* pFileName)
149{
150 LOG_DEBUG << "Replying from a file: " << pFileName << std::endl;
151
152 FileReader fileReader(pFileName);
153 size_t fileSize = fileReader.GetFileSize();
154
155 if (0 == fileSize)
156 {
157 LOG_ERROR << "No file content is available for reply" << std::endl;
158 return KEEPALIVE_CLOSE;
159 }
160
161 static const size_t BUF_LEN = 64 * 1024;
162
163 char* pBuf = new char[BUF_LEN];
164 size_t chunkSize = 0;
165 bool isError = false;
166
167 do
168 {
169 LOG_VERBOSE << "Requesting for a file chunk" << std::endl;
170
171 chunkSize = fileReader.ReadChunk(pBuf, BUF_LEN);
172 if (chunkSize > 0)
173 {
174 if (false == send_buffer(sock, pBuf, chunkSize))
175 {
176 LOG_ERROR << "Send error detected" << std::endl;
177 isError = true;
178 break;
179 }
180 }
181
182 // Error/Completion may occur with non-zero chunk as well
183 if (fileReader.IsError())
184 {
185 LOG_ERROR << "File read error detected" << std::endl;
186 isError = true;
187 break;
188 }
189
190 if (fileReader.IsCompleted())
191 {
192 LOG_DEBUG << "File completion detected" << std::endl;
193 break;
194 }
195
196 LOG_DEBUG << "File Chunk Delivered: " << chunkSize << "B" << std::endl;
197 }
198 while (chunkSize > 0);
199
200 delete[] pBuf;
201
202 if (isError)
203 {
204 LOG_ERROR << "Error occured while replying file content" << std::endl;
205 return KEEPALIVE_CLOSE;
206 }
207 else
208 {
209 LOG_DEBUG << "File Content successfully delivered" << std::endl;
210 return KEEPALIVE_OK;
211 }
212}
213
214bool Server::send_buffer(int sock, const char* pBuf, size_t len)
215{
216 if (!pBuf)
217 {
218 LOG_ERROR << "Cannot reply to a command - No buffer is provided" << std::endl;
219 return false;
220 }
221
222 size_t sentTillNow = 0;
223
224 while(sentTillNow < len)
225 {
226 ssize_t sentBytes = write(sock, pBuf, len);
227 if (sentBytes < 0)
228 {
229 int lastErrno = errno;
230 LOG_ERROR << "Cannot send response buffer."
231 << " Error:" << lastErrno
232 << " Message: " << strerror(lastErrno)
233 << std::endl;
234 return false;
235 }
236
237 sentTillNow += sentBytes;
238 LOG_DEBUG << "Sent response chunk."
239 << " Chunk Size: " << sentBytes
240 << " Sent till now: " << sentTillNow
241 << " Response Length: " << len
242 << std::endl;
243 }
244
245 LOG_DEBUG << "Response buffer fully sent" << std::endl;
246 return true;
247}
248
249/*
250 Close the server to allow un-bind te socket - allowing future connections without delay
251*/
252int Server::stop()
253{
254 LOG_INFO << "Stopping the server" << std::endl;
255 shutdown(sock, SHUT_RDWR);
256 return 0;
257}
258
259/*
260 Initialize server on the given port. The function returns in case of error,
261 otherwise it doesn't return
262*/
263int Server::start(int port)
264{
265 LOG_INFO << "Starting the server on port " << port << std::endl;
266
267 struct sockaddr_in address;
268 sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
269
270 if(sock < 0)
271 {
272 LOG_ERROR << "Cannot create a socket on port " << port << std::endl;
273 return -1;
274 }
275
276 address.sin_family = AF_INET;
277 address.sin_addr.s_addr = INADDR_ANY;
278 address.sin_port = htons(port);
279
280 // Set the "Re-Use" socket option - allows reconnections after wilserver exit
281 int reuse = 1;
282 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
283
284 if(bind(sock, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) < 0)
285 {
286 LOG_ERROR << "Cannot bind socket to port " << port << std::endl;
287 return -2;
288 }
289
290 if (listen(sock, 5) < 0)
291 {
292 LOG_ERROR << "Cannot listen on port " << port << std::endl;
293 return -3;
294 }
295
296 shutdown_flag = 0;
297 while (shutdown_flag == 0) {
298 pthread_t thread;
299 connection_par_t *par = new connection_par_t;
300 par->sock = accept(sock, &par->address, (socklen_t*)&par->addr_len);
301 if(par->sock < 0)
302 delete par;
303 else {
304 pthread_mutex_lock(&threads_counter_mutex);
305 running_threads++;
306 pthread_mutex_unlock(&threads_counter_mutex);
307 pthread_create(&thread, 0, &Server::server_process, (void *)par);
308 pthread_detach(thread);
309 }
310
311 }
312 // Wait till all the threads are done in case we ever exit the loop above
313 while(running_threads > 0)
314 sleep(1);
315
316 LOG_INFO << "Server shutdown" << std::endl;
317
318 return 0; // Wont get here, just to avoid the warning
319}