/ ddcd.cpp
ddcd.cpp
  1  /*
  2  This software is part of libcsdr, a set of simple DSP routines for
  3  Software Defined Radio.
  4  
  5  Copyright (c) 2014, Andras Retzler <randras@sdr.hu>
  6  All rights reserved.
  7  
  8  Redistribution and use in source and binary forms, with or without
  9  modification, are permitted provided that the following conditions are met:
 10      * Redistributions of source code must retain the above copyright
 11        notice, this list of conditions and the following disclaimer.
 12      * Redistributions in binary form must reproduce the above copyright
 13        notice, this list of conditions and the following disclaimer in the
 14        documentation and/or other materials provided with the distribution.
 15      * Neither the name of the copyright holder nor the
 16        names of its contributors may be used to endorse or promote products
 17        derived from this software without specific prior written permission.
 18  
 19  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 20  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 21  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 22  DISCLAIMED. IN NO EVENT SHALL ANDRAS RETZLER BE LIABLE FOR ANY
 23  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 24  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 25  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 26  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 27  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 28  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 29  */
 30  
 31  #include "ddcd.h"
 32  
 33  
 34  int host_port = 0;
 35  char host_address[100] = "127.0.0.1";
 36  int thread_cntr = 0;
 37  
 38  //CLI parameters
 39  int decimation = 0;
 40  float transition_bw = 0.05;
 41  int bufsize = 1024; //! currently unused
 42  int bufcnt = 1024;
 43  char ddc_method_str[100] = "td";
 44  ddc_method_t ddc_method;
 45  
 46  void sig_handler(int signo)
 47  {
 48  	fprintf(stderr, MSG_START "signal %d caught, exiting ddcd...\n", signo);
 49  	fflush(stderr);
 50  	exit(0);
 51  }
 52  
 53  int main(int argc, char* argv[])
 54  {
 55  	int c;
 56  	for(;;)
 57  	{
 58  		int option_index = 0;
 59  		static struct option long_options[] = {
 60  		   {"port",       required_argument, 0,  'p' },
 61  		   {"address",    required_argument, 0,  'a' },
 62  		   {"decimation", required_argument, 0,  'd' },
 63  		   {"bufsize", 	  required_argument, 0,  'b' },
 64  		   {"bufcnt", 	  required_argument, 0,  'n' },
 65  	       {"method", 	  required_argument, 0,  'm' },
 66  	       {"transition", required_argument, 0,  't' }
 67  		};
 68  		c = getopt_long(argc, argv, "p:a:d:b:n:m:t:", long_options, &option_index);
 69  		if(c==-1) break;
 70  		switch (c)
 71  		{
 72  		case 'a':
 73  			host_address[100-1]=0;
 74  			strncpy(host_address,optarg,100-1);
 75  			break;
 76  		case 'p':
 77  			host_port=atoi(optarg);
 78  			break;
 79  		case 'd':
 80  			decimation=atoi(optarg);
 81  			break;
 82  		case 'b':
 83  			bufsize=atoi(optarg);
 84  			break;
 85  		case 'n':
 86  			bufcnt=atoi(optarg);
 87  			break;
 88  		case 'm':
 89  			ddc_method_str[100-1]=0;
 90  			strncpy(ddc_method_str,optarg,100-1);
 91  			break;
 92  		case 't':
 93  			sscanf(optarg,"%g",&transition_bw);
 94  			break;
 95  		case 0:
 96  		case '?':
 97  		case ':':
 98  		default:;
 99  			print_exit(MSG_START "error in getopt_long()\n");
100  		}
101  	}
102  
103  	if(!decimation) print_exit(MSG_START "missing required command line argument, --decimation.\n");
104  	if(!host_port) print_exit(MSG_START "missing required command line argument, --port.\n");
105  	if(decimation<0) print_exit(MSG_START "invalid value for --decimation (should be >0).\n");
106  	if(decimation==1) fprintf(stderr, MSG_START "decimation = 1, just copying raw samples.\n");
107  	if(transition_bw<0||transition_bw>0.5) print_exit(MSG_START "invalid value for --transition (should be between 0 and 0.5).\n");
108  	if(bufsize<0) print_exit(MSG_START "invalid value for --bufsize (should be >0)\n");
109  	if(bufcnt<0) print_exit(MSG_START "invalid value for --bufcnt (should be >0)\n");
110  	if(decimation==1); //don't do anything then //!will have to take care about this later
111  	else if(!strcmp(ddc_method_str,"td"))
112  	{
113  		ddc_method = M_TD;
114  		fprintf(stderr, MSG_START "method is M_TD (default).\n");
115  	}
116  	else if (!strcmp(ddc_method_str,"fastddc"))
117  	{
118  		ddc_method = M_FASTDDC;
119  		fprintf(stderr, MSG_START "method is M_FASTDDC.\n");
120  	}
121  	else print_exit(MSG_START "invalid parameter given to --method.\n");
122  
123  	//set signals
124  	struct sigaction sa;
125  	memset(&sa, 0, sizeof(sa));
126  	sa.sa_handler = sig_handler;
127  	sigaction(SIGTERM, &sa, NULL);
128  	sigaction(SIGKILL, &sa, NULL);
129  	sigaction(SIGQUIT, &sa, NULL);
130  	sigaction(SIGINT, &sa, NULL);
131  	sigaction(SIGHUP, &sa, NULL);
132  
133  	struct sockaddr_in addr_host;
134      int listen_socket;
135  	std::vector<client_t*> clients;
136  	clients.reserve(100);
137      listen_socket=socket(AF_INET,SOCK_STREAM,0);
138  
139  	int sockopt = 1;
140  	if( setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&sockopt, sizeof(sockopt)) == -1 )
141  		error_exit(MSG_START "cannot set SO_REUSEADDR");  //the best description on SO_REUSEADDR ever: http://stackoverflow.com/a/14388707/3182453
142  
143  	memset(&addr_host,'0',sizeof(addr_host));
144      addr_host.sin_family = AF_INET;
145      addr_host.sin_port = htons(host_port);
146  	addr_host.sin_addr.s_addr = INADDR_ANY;
147  
148      if( (addr_host.sin_addr.s_addr=inet_addr(host_address)) == INADDR_NONE )
149  		error_exit(MSG_START "invalid host address");
150  
151  	if( bind(listen_socket, (struct sockaddr*) &addr_host, sizeof(addr_host)) < 0 )
152  		error_exit(MSG_START "cannot bind() address to the socket");
153  
154  	if( listen(listen_socket, 10) == -1 )
155  		error_exit(MSG_START "cannot listen() on socket");
156  
157  	fprintf(stderr,MSG_START "listening on %s:%d\n", inet_ntoa(addr_host.sin_addr), host_port);
158  
159  	struct sockaddr_in addr_cli;
160  	socklen_t addr_cli_len = sizeof(addr_cli);
161  	int new_socket;
162  
163  	int highfd = 0;
164  	FD_ZERO(&select_fds);
165  	FD_SET(listen_socket, &select_fds);
166  	maxfd(&highfd, listen_socket);
167  	FD_SET(input_fd, &select_fds);
168  	maxfd(&highfd, input_fd);
169  
170  	//Set stdin and listen_socket to non-blocking
171  	if(set_nonblocking(input_fd) || set_nonblocking(listen_socket))
172  		error_exit(MSG_START "cannot set_nonblocking()");
173  
174  	//Create tsmpool
175  	tsmpool* pool = new tsmpool(bufsize, bufcnt);
176  	if(!pool->ok) print_exit(MSG_START "tsmpool failed to initialize\n");
177  
178  	unsigned char* current_write_buffer = pool->get_write_buffer();
179  	int index_in_current_write_buffer = 0;
180  
181  
182  	for(;;)
183  	{
184  		//Let's wait until there is any new data to read, or any new connection!
185  		select(highfd, &select_fds, NULL, NULL, NULL);
186  
187  		//Is there a new client connection?
188  		if( (new_socket = accept(listen_socket, (struct sockaddr*)&addr_cli, &addr_cli_len)) != -1)
189  		{
190  			clients_close_all_finished();
191  			if(pthread_create(&new_client->thread, NULL, client_thread , (void*)&new_client)<0)
192  			{
193  				//We're the parent
194  				client_t* new_client = new client_t;
195  				new_client->error = 0;
196  				memcpy(&new_client->addr, &addr_cli, sizeof(new_client->addr));
197  				new_client->socket = new_socket;
198  				new_client->status = CS_CREATED;
199  				clients.push_back(new_client);
200  				fprintf(stderr, MSG_START "pthread_create() done, clients now: %d\n", clients.size());
201  			}
202  			else  fprintf(stderr, MSG_START "pthread_create() failed.\n");
203  		}
204  
205  		if(index_in_current_write_buffer >= bufsize)
206  		{
207  			current_write_buffer = pool->get_write_buffer();
208  			index_in_current_write_buffer = 0;
209  		}
210  		int retval = read(input_fd, current_write_buffer + index_in_current_write_buffer, bufsize - index_in_current_write_buffer);
211  		if(retval>0)
212  		{
213  			index_in_current_write_buffer += retval;
214  		}
215  		else if(retval==0)
216  		{
217  			//!end of input stream, close clients and exit
218  			print_exit(MSG_START "end of input, exiting.\n")
219  		}
220  	}
221  }
222  
223  #if 0
224  for (int i=0; i<clients.size(); i++)
225  {
226  	if(write(clients[i]->pipefd[1], buf, retval)==-1)
227  	{
228  
229  		if(!clients[i]->error)
230  		{
231  			print_client(clients[i], "lost buffer, failed to write pipe.");
232  			clients[i]->error=1;
233  		}
234  		//fprintf(stderr, MSG_START "errno is %d\n", errno); //usually 11
235  		//int wpstatus;
236  		//int wpresult = waitpid(clients[i]->pid, &wpstatus, WNOHANG);
237  		//fprintf(stderr, MSG_START "pid is %d\n",clients[i]->pid);
238  		//perror("somethings wrong");
239  		//if(wpresult == -1) print_client(clients[i], "error while waitpid()!");
240  		//else if(wpresult == 0)
241  		waitpid(clients[i]->pid, NULL, WNOHANG);
242  		if(!proc_exists(clients[i]->pid))
243  		{
244  			//Client exited!
245  			print_client(clients[i], "closing client from main process.");
246  			close(clients[i]->pipefd[1]);
247  			close(clients[i]->socket);
248  			delete clients[i];
249  			clients.erase(clients.begin()+i);
250  			fprintf(stderr, MSG_START "done closing client from main process.\n");
251  		}
252  	}
253  	else  { if(clients[i]->error) print_client(clients[i], "pipe okay again."); clients[i]->error=0; }
254  }
255  }
256  //TODO: at the end, server closes pipefd[1] for client
257  #endif
258  
259  void clients_close_all_finished()
260  {
261  	for(int i=0;i<clients.size();i++)
262  	{
263  		if(clients[i]->status == CS_THREAD_FINISHED) clients.erase(i);
264  	}
265  }
266  
267  void client_parser_push(char c)
268  { //!TODO
269  	command_t cmd;
270  	char* commands_cstr = commands.c_str();
271  	int newline_index = -1;
272  
273  	for(int i=0;commands_cstr[i];i++) if(commands_cstr[i]=='\n') newline_index = i;
274  	if(newline_index == -1)
275  
276  	char param_name[101];
277  	char param_value[101];
278  	for(int i=0;i<100;commands_csdr
279  
280  }
281  
282  void* client_thread (void* param) //!TODO
283  {
284  	client_t* me_the_client = (client_t*)param;
285  	me_the_client->status = CS_THREAD_RUNNING;
286  	char ctl_data_buffer;
287  	int retval;
288  	tsmpool* p1_temp;
289  	tsmpool* p2_temp;
290  	const int num_client_buffers = 20;
291  	if(ddc_method == M_TD)
292  	{
293  		p1_temp = new tsmpool(bufsize, )
294  	}
295  
296  	for(;;)
297  	{
298  		do
299  		{
300  			retval = recv(me_the_client->socket, &ctl_data_buffer, 1, 0);
301  			if(client_parser_push(ctl_data_buffer)) break;
302  		} while (retval);
303  
304  
305  		//read control data from socket
306  		//process control data
307  		//run shift
308  		//run decimation
309  		//have an exit condition (??)
310  		if(ddc_method == M_TD)
311  		{
312  
313  		}
314  	}
315  	me_the_client->status = CS_THREAD_FINISHED;
316  	pthread_exit(NULL);
317  	return NULL;
318  }
319  
320  void error_exit(const char* why)
321  {
322  	perror(why); //do we need a \n at the end of (why)?
323  	exit(1); 
324  }
325  
326  void print_exit(const char* why)
327  {
328  	fprintf(stderr, "%s", why);
329  	exit(1);
330  }
331  
332  void maxfd(int* maxfd, int fd)
333  {
334  	if(fd>=*maxfd) *maxfd=fd+1;
335  }