daemon.cc 27.8 KB
Newer Older
Sandrine Avakian's avatar
Sandrine Avakian committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
daemon.cc
Copyright (C) 2016 Belledonne Communications, Grenoble, France 

This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or (at
your option) any later version.

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

Ghislain MARY's avatar
Ghislain MARY committed
20 21
#include <cstdio>
#include <sys/ioctl.h>
Yann Diorcet's avatar
Yann Diorcet committed
22
#include <iostream>
Ghislain MARY's avatar
Ghislain MARY committed
23
#include <iomanip>
Yann Diorcet's avatar
Yann Diorcet committed
24 25
#include <sstream>
#include <algorithm>
Yann Diorcet's avatar
Yann Diorcet committed
26

27
#ifdef HAVE_READLINE
28 29
#include <readline/readline.h>
#include <readline/history.h>
30
#endif
Yann Diorcet's avatar
Yann Diorcet committed
31

32
#include <poll.h>
Yann Diorcet's avatar
Yann Diorcet committed
33

34
#include "daemon.h"
35
#include "commands/adaptive-jitter-compensation.h"
Simon Morlat's avatar
Simon Morlat committed
36
#include "commands/jitterbuffer.h"
37 38 39
#include "commands/answer.h"
#include "commands/audio-codec-get.h"
#include "commands/audio-codec-move.h"
Yann Diorcet's avatar
Yann Diorcet committed
40
#include "commands/audio-codec-set.h"
41
#include "commands/audio-codec-toggle.h"
42 43
#include "commands/audio-stream-start.h"
#include "commands/audio-stream-stop.h"
Guillaume Beraudo's avatar
Guillaume Beraudo committed
44
#include "commands/audio-stream-stats.h"
45
#include "commands/auth-infos-clear.h"
46 47 48
#include "commands/call.h"
#include "commands/call-stats.h"
#include "commands/call-status.h"
49 50 51
#include "commands/call-pause.h"
#include "commands/call-mute.h"
#include "commands/call-resume.h"
52
#include "commands/video.h"
53 54
#include "commands/call-transfer.h"
#include "commands/conference.h"
55
#include "commands/contact.h"
56
#include "commands/dtmf.h"
Ghislain MARY's avatar
Ghislain MARY committed
57
#include "commands/firewall-policy.h"
58
#include "commands/help.h"
Ghislain MARY's avatar
Ghislain MARY committed
59
#include "commands/ipv6.h"
Ghislain MARY's avatar
Ghislain MARY committed
60
#include "commands/media-encryption.h"
Yann Diorcet's avatar
Yann Diorcet committed
61
#include "commands/msfilter-add-fmtp.h"
Ghislain MARY's avatar
Ghislain MARY committed
62
#include "commands/play-wav.h"
63
#include "commands/pop-event.h"
Ghislain MARY's avatar
Ghislain MARY committed
64
#include "commands/port.h"
65 66 67 68 69 70
#include "commands/ptime.h"
#include "commands/register.h"
#include "commands/register-status.h"
#include "commands/terminate.h"
#include "commands/unregister.h"
#include "commands/quit.h"
71
#include "commands/configcommand.h"
72
#include "commands/netsim.h"
Simon Morlat's avatar
Simon Morlat committed
73
#include "commands/cn.h"
Ghislain MARY's avatar
Ghislain MARY committed
74
#include "commands/version.h"
Yann Diorcet's avatar
Yann Diorcet committed
75

Guillaume Beraudo's avatar
Guillaume Beraudo committed
76
#include "private.h"
77
using namespace std;
Yann Diorcet's avatar
Yann Diorcet committed
78

79 80 81 82 83 84 85 86 87 88 89 90 91 92
#ifndef WIN32
#else
#include <windows.h>
void usleep(int waitTime) {
	Sleep(waitTime/1000);
}
#endif

#ifdef HAVE_READLINE
#define LICENCE_GPL
#else
#define LICENCE_COMMERCIAL
#endif

93 94
const char * const ice_state_str[] = {
	"Not activated",	/* LinphoneIceStateNotActivated */
Ghislain MARY's avatar
Ghislain MARY committed
95
	"Failed",	/* LinphoneIceStateFailed */
96 97 98 99 100 101
	"In progress",	/* LinphoneIceStateInProgress */
	"Host connection",	/* LinphoneIceStateHostConnection */
	"Reflexive connection",	/* LinphoneIceStateReflexiveConnection */
	"Relayed connection"	/* LinphoneIceStateRelayConnection */
};

102 103 104 105 106 107 108 109 110 111 112
void *Daemon::iterateThread(void *arg) {
	Daemon *daemon = (Daemon *) arg;
	while (daemon->mRunning) {
		ms_mutex_lock(&daemon->mMutex);
		daemon->iterate();
		ms_mutex_unlock(&daemon->mMutex);
		usleep(20000);
	}
	return 0;
}

Yann Diorcet's avatar
Yann Diorcet committed
113
EventResponse::EventResponse(Daemon *daemon, LinphoneCall *call, LinphoneCallState state) {
Yann Diorcet's avatar
Yann Diorcet committed
114
	ostringstream ostr;
Yann Diorcet's avatar
Yann Diorcet committed
115 116 117
	char *remote = linphone_call_get_remote_address_as_string(call);
	ostr << "Event-type: call-state-changed\nEvent: " << linphone_call_state_to_string(state) << "\n";
	ostr << "From: " << remote << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
118
	ostr << "Id: " << daemon->updateCallId(call) << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
119 120 121 122
	setBody(ostr.str().c_str());
	ms_free(remote);
}

Yann Diorcet's avatar
Yann Diorcet committed
123
DtmfResponse::DtmfResponse(Daemon *daemon, LinphoneCall *call, int dtmf) {
Yann Diorcet's avatar
Yann Diorcet committed
124 125 126 127
	ostringstream ostr;
	char *remote = linphone_call_get_remote_address_as_string(call);
	ostr << "Event-type: receiving-tone\nTone: " << (char) dtmf << "\n";
	ostr << "From: " << remote << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
128
	ostr << "Id: " << daemon->updateCallId(call) << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
129 130 131 132
	setBody(ostr.str().c_str());
	ms_free(remote);
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
133
static ostream &printCallStatsHelper(ostream &ostr, const LinphoneCallStats *stats, const string &prefix) {
134
	ostr << prefix << "ICE state: " << ice_state_str[stats->ice_state] << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
	ostr << prefix << "RoundTripDelay: " << stats->round_trip_delay << "\n";
	ostr << prefix << "Jitter: " << stats->jitter_stats.jitter << "\n";
//	ostr << prefix << "MaxJitter: " << stats->jitter_stats.max_jitter << "\n";
//	ostr << prefix << "SumJitter: " << stats->jitter_stats.sum_jitter << "\n";
//	ostr << prefix << "MaxJitterTs: " << stats->jitter_stats.max_jitter_ts << "\n";
	ostr << prefix << "JitterBufferSizeMs: " << stats->jitter_stats.jitter_buffer_size_ms << "\n";

	const report_block_t *rrb = NULL;
	if (stats->received_rtcp != NULL) {
		if (stats->received_rtcp->b_cont != NULL)
			msgpullup(stats->received_rtcp, -1);
		if (rtcp_is_SR(stats->received_rtcp)) {
			rrb = rtcp_SR_get_report_block(stats->received_rtcp, 0);
		} else if (rtcp_is_RR(stats->received_rtcp)) {
			rrb = rtcp_RR_get_report_block(stats->received_rtcp, 0);
		}
	}
	if (rrb) {
		unsigned int ij;
		float flost;
		ij = report_block_get_interarrival_jitter(rrb);
		flost = (float) (100.0 * report_block_get_fraction_lost(rrb) / 256.0);
		ostr << prefix << "Received-InterarrivalJitter: " << ij << "\n";
		ostr << prefix << "Received-FractionLost: " << flost << "\n";
	}

	const report_block_t *srb = NULL;
	if (stats->sent_rtcp != NULL) {
		if (stats->sent_rtcp->b_cont != NULL)
			msgpullup(stats->sent_rtcp, -1);
		if (rtcp_is_SR(stats->sent_rtcp)) {
			srb = rtcp_SR_get_report_block(stats->sent_rtcp, 0);
		} else if (rtcp_is_RR(stats->sent_rtcp)) {
			srb = rtcp_RR_get_report_block(stats->sent_rtcp, 0);
		}
	}
	if (srb) {
		unsigned int ij;
		float flost;
		ij = report_block_get_interarrival_jitter(srb);
		flost = (float) (100.0 * report_block_get_fraction_lost(srb) / 256.0);
		ostr << prefix << "Sent-InterarrivalJitter: " << ij << "\n";
		ostr << prefix << "Sent-FractionLost: " << flost << "\n";
	}
Guillaume Beraudo's avatar
Guillaume Beraudo committed
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
	return ostr;
}

CallStatsResponse::CallStatsResponse(Daemon *daemon, LinphoneCall *call, const LinphoneCallStats *stats, bool event) {
	const LinphoneCallParams *callParams = linphone_call_get_current_params(call);
	const char *prefix = "";

	ostringstream ostr;
	if (event) {
		ostr << "Event-type: call-stats\n";
		ostr << "Id: " << daemon->updateCallId(call) << "\n";
		ostr << "Type: ";
		if (stats->type == LINPHONE_CALL_STATS_AUDIO) {
			ostr << "Audio";
		} else {
			ostr << "Video";
		}
		ostr << "\n";
	} else {
		prefix = ((stats->type == LINPHONE_CALL_STATS_AUDIO) ? "Audio-" : "Video-");
	}

	printCallStatsHelper(ostr, stats, prefix);
Yann Diorcet's avatar
Yann Diorcet committed
202

Yann Diorcet's avatar
Yann Diorcet committed
203 204 205 206 207 208
	if (stats->type == LINPHONE_CALL_STATS_AUDIO) {
		const PayloadType *audioCodec = linphone_call_params_get_used_audio_codec(callParams);
		ostr << PayloadTypeResponse(linphone_call_get_core(call), audioCodec, -1, prefix, false).getBody() << "\n";
	} else {
		const PayloadType *videoCodec = linphone_call_params_get_used_video_codec(callParams);
		ostr << PayloadTypeResponse(linphone_call_get_core(call), videoCodec, -1, prefix, false).getBody() << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
209 210 211 212 213
	}

	setBody(ostr.str().c_str());
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
214 215 216 217

AudioStreamStatsResponse::AudioStreamStatsResponse(Daemon* daemon, AudioStream* stream,
		const LinphoneCallStats *stats, bool event) {
	const char *prefix = "";
218

Guillaume Beraudo's avatar
Guillaume Beraudo committed
219 220 221 222 223 224 225 226 227 228 229 230 231 232
	ostringstream ostr;
	if (event) {
		ostr << "Event-type: audio-stream-stats\n";
		ostr << "Id: " << daemon->updateAudioStreamId(stream) << "\n";
		ostr << "Type: ";
		if (stats->type == LINPHONE_CALL_STATS_AUDIO) {
			ostr << "Audio";
		} else {
			ostr << "Video";
		}
		ostr << "\n";
	} else {
		prefix = ((stats->type == LINPHONE_CALL_STATS_AUDIO) ? "Audio-" : "Video-");
	}
233

Guillaume Beraudo's avatar
Guillaume Beraudo committed
234
	printCallStatsHelper(ostr, stats, prefix);
235

Guillaume Beraudo's avatar
Guillaume Beraudo committed
236 237 238
	setBody(ostr.str().c_str());
}

Yann Diorcet's avatar
Yann Diorcet committed
239
PayloadTypeResponse::PayloadTypeResponse(LinphoneCore *core, const PayloadType *payloadType, int index, const string &prefix, bool enabled_status) {
Yann Diorcet's avatar
Yann Diorcet committed
240
	ostringstream ostr;
Yann Diorcet's avatar
Yann Diorcet committed
241 242 243 244 245 246 247 248 249 250 251 252 253 254
	if (payloadType != NULL) {
		if (index >= 0)
			ostr << prefix << "Index: " << index << "\n";
		ostr << prefix << "Payload-type-number: " << linphone_core_get_payload_type_number(core, payloadType) << "\n";
		ostr << prefix << "Clock-rate: " << payloadType->clock_rate << "\n";
		ostr << prefix << "Bitrate: " << payloadType->normal_bitrate << "\n";
		ostr << prefix << "Mime: " << payloadType->mime_type << "\n";
		ostr << prefix << "Channels: " << payloadType->channels << "\n";
		ostr << prefix << "Recv-fmtp: " << ((payloadType->recv_fmtp) ? payloadType->recv_fmtp : "") << "\n";
		ostr << prefix << "Send-fmtp: " << ((payloadType->send_fmtp) ? payloadType->send_fmtp : "") << "\n";
		if (enabled_status)
			ostr << prefix << "Enabled: " << (linphone_core_payload_type_enabled(core, payloadType) == TRUE ? "true" : "false") << "\n";
		setBody(ostr.str().c_str());
	}
Yann Diorcet's avatar
Yann Diorcet committed
255 256
}

257 258
PayloadTypeParser::PayloadTypeParser(LinphoneCore *core, const string &mime_type, bool accept_all) : mAll(false), mSuccesful(true), mPayloadType(NULL),mPosition(-1){
	int number=-1;
259 260 261 262
	if (accept_all && (mime_type.compare("ALL") == 0)) {
		mAll = true;
		return;
	}
263
	istringstream ist(mime_type);
264
	ist >> number;
265
	if (ist.fail()) {
266
		char type[64]={0};
267
		int rate, channels;
268
		if (sscanf(mime_type.c_str(), "%63[^/]/%u/%u", type, &rate, &channels) != 3) {
269 270 271
			mSuccesful = false;
			return;
		}
272
		mPayloadType = linphone_core_find_payload_type(core, type, rate, channels);
273
		if (mPayloadType) mPosition=bctbx_list_index(linphone_core_get_audio_codecs(core), mPayloadType);
274
	}else if (number!=-1){
275
		const bctbx_list_t *elem;
276 277 278 279 280
		for(elem=linphone_core_get_audio_codecs(core);elem!=NULL;elem=elem->next){
			if (number==linphone_core_get_payload_type_number(core,(PayloadType*)elem->data)){
				mPayloadType=(PayloadType*)elem->data;
				break;
			}
281 282 283 284
		}
	}
}

Ghislain MARY's avatar
Ghislain MARY committed
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
DaemonCommandExample::DaemonCommandExample(const char *command, const char *output)
	: mCommand(command), mOutput(output) {}

DaemonCommand::DaemonCommand(const char *name, const char *proto, const char *description) :
		mName(name), mProto(proto), mDescription(description) {
}

void DaemonCommand::addExample(const DaemonCommandExample *example) {
	mExamples.push_back(example);
}

const string DaemonCommand::getHelp() const {
	ostringstream ost;
	ost << getProto() << endl << endl;
	ost << "Description:" << endl << getDescription() << endl << endl;
	list<const DaemonCommandExample*> examples = getExamples();
	int c = 1;
	for (list<const DaemonCommandExample*>::iterator it = examples.begin(); it != examples.end(); ++it, ++c) {
		ost << "Example " << c << ":" << endl;
		ost << ">" << (*it)->getCommand() << endl;
		ost << (*it)->getOutput() << endl;
		ost << endl;
	}
	return ost.str();
Yann Diorcet's avatar
Yann Diorcet committed
309 310
}

Yann Diorcet's avatar
Yann Diorcet committed
311 312
bool DaemonCommand::matches(const char *name) const {
	return strcmp(name, mName.c_str()) == 0;
Yann Diorcet's avatar
Yann Diorcet committed
313 314
}

315
Daemon::Daemon(const char *config_path, const char *factory_config_path, const char *log_file, const char *pipe_name, bool display_video, bool capture_video) :
316
		mLSD(0), mLogFile(NULL), mAutoVideo(0), mCallIds(0), mProxyIds(0), mAudioStreamIds(0) {
317
	ms_mutex_init(&mMutex, NULL);
Yann Diorcet's avatar
Yann Diorcet committed
318 319
	mServerFd = -1;
	mChildFd = -1;
320
	if (pipe_name == NULL) {
321 322
#ifdef HAVE_READLINE
		const char *homedir = getenv("HOME");
jehan's avatar
jehan committed
323
		rl_readline_name = (char*)"daemon";
324 325 326 327 328 329
		if (homedir == NULL)
			homedir = ".";
		mHistfile = string(homedir) + string("/.linphone_history");
		read_history(mHistfile.c_str());
		setlinebuf(stdout);
#endif
Yann Diorcet's avatar
Yann Diorcet committed
330
	} else {
331
		mServerFd = ortp_server_pipe_create(pipe_name);
Yann Diorcet's avatar
Yann Diorcet committed
332
		listen(mServerFd, 2);
333
		fprintf(stdout, "Server unix socket created, name=%s fd=%i\n", pipe_name, mServerFd);
Yann Diorcet's avatar
Yann Diorcet committed
334 335
	}

336 337 338 339 340 341
	if (log_file != NULL) {
		mLogFile = fopen(log_file, "a+");
		linphone_core_enable_logs(mLogFile);
	} else {
		linphone_core_disable_logs();
	}
Yann Diorcet's avatar
Yann Diorcet committed
342 343 344

	LinphoneCoreVTable vtable = { 0 };
	vtable.call_state_changed = callStateChanged;
Yann Diorcet's avatar
Yann Diorcet committed
345
	vtable.call_stats_updated = callStatsUpdated;
Yann Diorcet's avatar
Yann Diorcet committed
346
	vtable.dtmf_received = dtmfReceived;
347
	mLc = linphone_core_new(&vtable, config_path, factory_config_path, this);
Yann Diorcet's avatar
Yann Diorcet committed
348
	linphone_core_set_user_data(mLc, this);
349 350
	linphone_core_enable_video_capture(mLc,capture_video);
	linphone_core_enable_video_display(mLc,display_video);
Yann Diorcet's avatar
Yann Diorcet committed
351
	initCommands();
Simon Morlat's avatar
Simon Morlat committed
352
	mUseStatsEvents=true;
Yann Diorcet's avatar
Yann Diorcet committed
353 354
}

Yann Diorcet's avatar
Yann Diorcet committed
355
const list<DaemonCommand*> &Daemon::getCommandList() const {
Yann Diorcet's avatar
Yann Diorcet committed
356 357 358
	return mCommands;
}

Yann Diorcet's avatar
Yann Diorcet committed
359
LinphoneCore *Daemon::getCore() {
Yann Diorcet's avatar
Yann Diorcet committed
360 361 362
	return mLc;
}

Ghislain MARY's avatar
Ghislain MARY committed
363 364 365 366
LinphoneSoundDaemon *Daemon::getLSD() {
	return mLSD;
}

Yann Diorcet's avatar
Yann Diorcet committed
367 368 369
int Daemon::updateCallId(LinphoneCall *call) {
	int val = (int) (long) linphone_call_get_user_pointer(call);
	if (val == 0) {
370 371
		linphone_call_set_user_pointer(call, (void*) (long) ++mCallIds);
		return mCallIds;
Yann Diorcet's avatar
Yann Diorcet committed
372 373
	}
	return val;
Yann Diorcet's avatar
Yann Diorcet committed
374 375
}

Yann Diorcet's avatar
Yann Diorcet committed
376
LinphoneCall *Daemon::findCall(int id) {
377
	const bctbx_list_t *elem = linphone_core_get_calls(mLc);
Yann Diorcet's avatar
Yann Diorcet committed
378 379 380
	for (; elem != NULL; elem = elem->next) {
		LinphoneCall *call = (LinphoneCall *) elem->data;
		if (linphone_call_get_user_pointer(call) == (void*) (long) id)
Yann Diorcet's avatar
Yann Diorcet committed
381 382 383 384 385
			return call;
	}
	return NULL;
}

Yann Diorcet's avatar
Yann Diorcet committed
386 387 388
int Daemon::updateProxyId(LinphoneProxyConfig *cfg) {
	int val = (int) (long) linphone_proxy_config_get_user_data(cfg);
	if (val == 0) {
389 390
		linphone_proxy_config_set_user_data(cfg, (void*) (long) ++mProxyIds);
		return mProxyIds;
Yann Diorcet's avatar
Yann Diorcet committed
391 392
	}
	return val;
Yann Diorcet's avatar
Yann Diorcet committed
393 394
}

Yann Diorcet's avatar
Yann Diorcet committed
395
LinphoneProxyConfig *Daemon::findProxy(int id) {
396
	const bctbx_list_t *elem = linphone_core_get_proxy_config_list(mLc);
Yann Diorcet's avatar
Yann Diorcet committed
397 398 399
	for (; elem != NULL; elem = elem->next) {
		LinphoneProxyConfig *proxy = (LinphoneProxyConfig *) elem->data;
		if (linphone_proxy_config_get_user_data(proxy) == (void*) (long) id)
Yann Diorcet's avatar
Yann Diorcet committed
400 401 402 403 404
			return proxy;
	}
	return NULL;
}

405
LinphoneAuthInfo *Daemon::findAuthInfo(int id)  {
406 407
	const bctbx_list_t *elem = linphone_core_get_auth_info_list(mLc);
	if (elem == NULL || id < 1 || (unsigned int)id > bctbx_list_size(elem)) {
408 409 410 411 412 413 414 415 416
		return NULL;
	}
	while (id > 1) {
		elem = elem->next;
		--id;
	}
	return (LinphoneAuthInfo *) elem->data;
}

Yann Diorcet's avatar
Yann Diorcet committed
417
int Daemon::updateAudioStreamId(AudioStream *audio_stream) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
418 419
	for (std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.begin(); it != mAudioStreams.end(); ++it) {
		if (it->second->stream == audio_stream)
Yann Diorcet's avatar
Yann Diorcet committed
420 421 422
			return it->first;
	}

423
	++mProxyIds;
Guillaume Beraudo's avatar
Guillaume Beraudo committed
424
	mAudioStreams.insert(make_pair(mProxyIds, new AudioStreamAndOther(audio_stream)));
425
	return mProxyIds;
Yann Diorcet's avatar
Yann Diorcet committed
426 427
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
428 429
AudioStreamAndOther *Daemon::findAudioStreamAndOther(int id) {
	std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
Yann Diorcet's avatar
Yann Diorcet committed
430 431 432 433 434
	if (it != mAudioStreams.end())
		return it->second;
	return NULL;
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
435 436
AudioStream *Daemon::findAudioStream(int id) {
	std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
437
	if (it != mAudioStreams.end())
Guillaume Beraudo's avatar
Guillaume Beraudo committed
438 439 440 441 442 443 444
		return it->second->stream;
	return NULL;
}

void Daemon::removeAudioStream(int id) {
	std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
	if (it != mAudioStreams.end()) {
445
		mAudioStreams.erase(it);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
446 447
		delete(it->second);
	}
448 449
}

Yann Diorcet's avatar
Yann Diorcet committed
450
void Daemon::initCommands() {
Yann Diorcet's avatar
Yann Diorcet committed
451
	mCommands.push_back(new RegisterCommand());
452
	mCommands.push_back(new ContactCommand());
Yann Diorcet's avatar
Yann Diorcet committed
453
	mCommands.push_back(new RegisterStatusCommand());
Yann Diorcet's avatar
Yann Diorcet committed
454
	mCommands.push_back(new UnregisterCommand());
455
	mCommands.push_back(new AuthInfosClearCommand());
Yann Diorcet's avatar
Yann Diorcet committed
456 457
	mCommands.push_back(new CallCommand());
	mCommands.push_back(new TerminateCommand());
458
	mCommands.push_back(new DtmfCommand());
Ghislain MARY's avatar
Ghislain MARY committed
459
	mCommands.push_back(new PlayWavCommand());
Yann Diorcet's avatar
Yann Diorcet committed
460 461
	mCommands.push_back(new PopEventCommand());
	mCommands.push_back(new AnswerCommand());
Yann Diorcet's avatar
Yann Diorcet committed
462
	mCommands.push_back(new CallStatusCommand());
Yann Diorcet's avatar
Yann Diorcet committed
463
	mCommands.push_back(new CallStatsCommand());
464 465 466 467
	mCommands.push_back(new CallPause());
	mCommands.push_back(new CallMute());
	mCommands.push_back(new CallResume());
	mCommands.push_back(new CallTransfer());
468 469 470
	mCommands.push_back(new Video());
	mCommands.push_back(new VideoSource());
	mCommands.push_back(new AutoVideo());
471
	mCommands.push_back(new Conference());
Yann Diorcet's avatar
Yann Diorcet committed
472 473 474 475
	mCommands.push_back(new AudioCodecGetCommand());
	mCommands.push_back(new AudioCodecEnableCommand());
	mCommands.push_back(new AudioCodecDisableCommand());
	mCommands.push_back(new AudioCodecMoveCommand());
Yann Diorcet's avatar
Yann Diorcet committed
476
	mCommands.push_back(new AudioCodecSetCommand());
Yann Diorcet's avatar
Yann Diorcet committed
477 478
	mCommands.push_back(new AudioStreamStartCommand());
	mCommands.push_back(new AudioStreamStopCommand());
Guillaume Beraudo's avatar
Guillaume Beraudo committed
479
	mCommands.push_back(new AudioStreamStatsCommand());
Yann Diorcet's avatar
Yann Diorcet committed
480
	mCommands.push_back(new MSFilterAddFmtpCommand());
Yann Diorcet's avatar
Yann Diorcet committed
481
	mCommands.push_back(new PtimeCommand());
Ghislain MARY's avatar
Ghislain MARY committed
482
	mCommands.push_back(new IPv6Command());
Ghislain MARY's avatar
Ghislain MARY committed
483
	mCommands.push_back(new FirewallPolicyCommand());
Ghislain MARY's avatar
Ghislain MARY committed
484
	mCommands.push_back(new MediaEncryptionCommand());
Ghislain MARY's avatar
Ghislain MARY committed
485
	mCommands.push_back(new PortCommand());
486
	mCommands.push_back(new AdaptiveBufferCompensationCommand());
Simon Morlat's avatar
Simon Morlat committed
487 488
	mCommands.push_back(new JitterBufferCommand());
	mCommands.push_back(new JitterBufferResetCommand());
Ghislain MARY's avatar
Ghislain MARY committed
489
	mCommands.push_back(new VersionCommand());
Yann Diorcet's avatar
Yann Diorcet committed
490 491
	mCommands.push_back(new QuitCommand());
	mCommands.push_back(new HelpCommand());
492 493
	mCommands.push_back(new ConfigGetCommand());
	mCommands.push_back(new ConfigSetCommand());
494
	mCommands.push_back(new NetsimCommand());
Simon Morlat's avatar
Simon Morlat committed
495
	mCommands.push_back(new CNCommand());
Yann Diorcet's avatar
Yann Diorcet committed
496 497 498 499 500 501 502
}

void Daemon::uninitCommands() {
	while (!mCommands.empty()) {
		delete mCommands.front();
		mCommands.pop_front();
	}
Yann Diorcet's avatar
Yann Diorcet committed
503 504
}

Yann Diorcet's avatar
Yann Diorcet committed
505 506
bool Daemon::pullEvent() {
	bool status = false;
Yann Diorcet's avatar
Yann Diorcet committed
507
	ostringstream ostr;
Yann Diorcet's avatar
Yann Diorcet committed
508 509
	if (!mEventQueue.empty()) {
		Response *r = mEventQueue.front();
Yann Diorcet's avatar
Yann Diorcet committed
510
		mEventQueue.pop();
Yann Diorcet's avatar
Yann Diorcet committed
511
		ostr << r->getBody() << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
512
		delete r;
Yann Diorcet's avatar
Yann Diorcet committed
513
		status = true;
Yann Diorcet's avatar
Yann Diorcet committed
514
	}
Yann Diorcet's avatar
Yann Diorcet committed
515
	ostr << "Size: " << mEventQueue.size() << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
516 517
	sendResponse(Response(ostr.str().c_str(), Response::Ok));
	return status;
Yann Diorcet's avatar
Yann Diorcet committed
518 519
}

Yann Diorcet's avatar
Yann Diorcet committed
520 521 522
void Daemon::callStateChanged(LinphoneCall *call, LinphoneCallState state, const char *msg) {
	switch (state) {
	case LinphoneCallIncomingReceived:
523 524
		linphone_call_enable_camera (call,mAutoVideo);
	case LinphoneCallOutgoingProgress:
Yann Diorcet's avatar
Yann Diorcet committed
525 526 527 528 529
	case LinphoneCallIncomingEarlyMedia:
	case LinphoneCallConnected:
	case LinphoneCallStreamsRunning:
	case LinphoneCallError:
	case LinphoneCallEnd:
Yann Diorcet's avatar
Yann Diorcet committed
530
		mEventQueue.push(new EventResponse(this, call, state));
Yann Diorcet's avatar
Yann Diorcet committed
531
		break;
Yann Diorcet's avatar
Yann Diorcet committed
532
	default:
Yann Diorcet's avatar
Yann Diorcet committed
533
		break;
Yann Diorcet's avatar
Yann Diorcet committed
534
	}
Yann Diorcet's avatar
Yann Diorcet committed
535 536
}

Yann Diorcet's avatar
Yann Diorcet committed
537
void Daemon::callStatsUpdated(LinphoneCall *call, const LinphoneCallStats *stats) {
538 539 540 541 542 543
	if (mUseStatsEvents) {
		/* don't queue periodical updates (3 per seconds for just bandwidth updates) */
		if (!(stats->updated & LINPHONE_CALL_STATS_PERIODICAL_UPDATE)){
			mEventQueue.push(new CallStatsResponse(this, call, stats, true));
		}
	}
Yann Diorcet's avatar
Yann Diorcet committed
544 545
}

Yann Diorcet's avatar
Yann Diorcet committed
546
void Daemon::dtmfReceived(LinphoneCall *call, int dtmf) {
Yann Diorcet's avatar
Yann Diorcet committed
547
	mEventQueue.push(new DtmfResponse(this, call, dtmf));
Yann Diorcet's avatar
Yann Diorcet committed
548 549
}

Yann Diorcet's avatar
Yann Diorcet committed
550 551 552
void Daemon::callStateChanged(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState state, const char *msg) {
	Daemon *app = (Daemon*) linphone_core_get_user_data(lc);
	app->callStateChanged(call, state, msg);
Yann Diorcet's avatar
Yann Diorcet committed
553
}
Yann Diorcet's avatar
Yann Diorcet committed
554 555 556 557
void Daemon::callStatsUpdated(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallStats *stats) {
	Daemon *app = (Daemon*) linphone_core_get_user_data(lc);
	app->callStatsUpdated(call, stats);
}
Yann Diorcet's avatar
Yann Diorcet committed
558 559 560 561
void Daemon::dtmfReceived(LinphoneCore *lc, LinphoneCall *call, int dtmf) {
	Daemon *app = (Daemon*) linphone_core_get_user_data(lc);
	app->dtmfReceived(call, dtmf);
}
Yann Diorcet's avatar
Yann Diorcet committed
562

Guillaume Beraudo's avatar
Guillaume Beraudo committed
563 564 565 566 567 568
void Daemon::iterateStreamStats() {
	for (std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.begin(); it != mAudioStreams.end(); ++it) {
		OrtpEvent *ev;
		while (it->second->queue && (NULL != (ev=ortp_ev_queue_get(it->second->queue)))){
			OrtpEventType evt=ortp_event_get_type(ev);
			if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED || evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
Simon Morlat's avatar
Simon Morlat committed
569
				linphone_call_stats_fill(&it->second->stats, &it->second->stream->ms, ev);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
570 571 572 573 574 575 576 577
				if (mUseStatsEvents) mEventQueue.push(new AudioStreamStatsResponse(this,
					it->second->stream, &it->second->stats, true));
			}
			ortp_event_destroy(ev);
		}
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
578
void Daemon::iterate() {
Yann Diorcet's avatar
Yann Diorcet committed
579
	linphone_core_iterate(mLc);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
580
	iterateStreamStats();
Yann Diorcet's avatar
Yann Diorcet committed
581 582 583
	if (mChildFd == -1) {
		if (!mEventQueue.empty()) {
			Response *r = mEventQueue.front();
Yann Diorcet's avatar
Yann Diorcet committed
584
			mEventQueue.pop();
Yann Diorcet's avatar
Yann Diorcet committed
585
			fprintf(stdout, "%s\n", r->getBody().c_str());
Yann Diorcet's avatar
Yann Diorcet committed
586 587 588 589 590 591
			fflush(stdout);
			delete r;
		}
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
592 593 594 595 596 597
void Daemon::execCommand(const char *cl) {
	char args[sLineSize] = { 0 };
	char name[sLineSize] = { 0 };
	sscanf(cl, "%511s %511[^\n]", name, args); //Read the rest of line in args
	list<DaemonCommand*>::iterator it = find_if(mCommands.begin(), mCommands.end(), bind2nd(mem_fun(&DaemonCommand::matches), name));
	if (it != mCommands.end()) {
598
		ms_mutex_lock(&mMutex);
Yann Diorcet's avatar
Yann Diorcet committed
599
		(*it)->exec(this, args);
600
		ms_mutex_unlock(&mMutex);
Yann Diorcet's avatar
Yann Diorcet committed
601
	} else {
Yann Diorcet's avatar
Yann Diorcet committed
602 603 604 605
		sendResponse(Response("Unknown command."));
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
606 607
void Daemon::sendResponse(const Response &resp) {
	char buf[4096] = { 0 };
Yann Diorcet's avatar
Yann Diorcet committed
608
	int size;
Yann Diorcet's avatar
Yann Diorcet committed
609 610 611 612
	size = resp.toBuf(buf, sizeof(buf));
	if (mChildFd != -1) {
		if (write(mChildFd, buf, size) == -1) {
			ms_error("Fail to write to pipe: %s", strerror(errno));
Yann Diorcet's avatar
Yann Diorcet committed
613
		}
Yann Diorcet's avatar
Yann Diorcet committed
614 615
	} else {
		fprintf(stdout, "%s", buf);
Yann Diorcet's avatar
Yann Diorcet committed
616 617 618 619
		fflush(stdout);
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
620 621 622 623 624 625
char *Daemon::readPipe(char *buffer, int buflen) {
	struct pollfd pfd[2] = { { 0 }, { 0 } };
	int nfds = 1;
	if (mServerFd != -1) {
		pfd[0].events = POLLIN;
		pfd[0].fd = mServerFd;
Yann Diorcet's avatar
Yann Diorcet committed
626
	}
Yann Diorcet's avatar
Yann Diorcet committed
627 628 629
	if (mChildFd != -1) {
		pfd[1].events = POLLIN;
		pfd[1].fd = mChildFd;
Yann Diorcet's avatar
Yann Diorcet committed
630 631
		nfds++;
	}
Yann Diorcet's avatar
Yann Diorcet committed
632 633 634
	int err = poll(pfd, nfds, 50);
	if (err > 0) {
		if (mServerFd != -1 && (pfd[0].revents & POLLIN)) {
Yann Diorcet's avatar
Yann Diorcet committed
635
			struct sockaddr_storage addr;
Yann Diorcet's avatar
Yann Diorcet committed
636 637 638 639
			socklen_t addrlen = sizeof(addr);
			int childfd = accept(mServerFd, (struct sockaddr*) &addr, &addrlen);
			if (childfd != -1) {
				if (mChildFd != -1) {
Yann Diorcet's avatar
Yann Diorcet committed
640 641
					ms_error("Cannot accept two client at the same time");
					close(childfd);
Yann Diorcet's avatar
Yann Diorcet committed
642 643
				} else {
					mChildFd = childfd;
Yann Diorcet's avatar
Yann Diorcet committed
644 645 646 647
					return NULL;
				}
			}
		}
Yann Diorcet's avatar
Yann Diorcet committed
648
		if (mChildFd != -1 && (pfd[1].revents & POLLIN)) {
Yann Diorcet's avatar
Yann Diorcet committed
649
			int ret;
Yann Diorcet's avatar
Yann Diorcet committed
650
			if ((ret = read(mChildFd, buffer, buflen)) == -1) {
Yann Diorcet's avatar
Yann Diorcet committed
651
				ms_error("Fail to read from pipe: %s", strerror(errno));
Yann Diorcet's avatar
Yann Diorcet committed
652 653
			} else {
				if (ret == 0) {
Yann Diorcet's avatar
Yann Diorcet committed
654 655
					ms_message("Client disconnected");
					close(mChildFd);
Yann Diorcet's avatar
Yann Diorcet committed
656
					mChildFd = -1;
Yann Diorcet's avatar
Yann Diorcet committed
657 658
					return NULL;
				}
Yann Diorcet's avatar
Yann Diorcet committed
659
				buffer[ret] = 0;
Yann Diorcet's avatar
Yann Diorcet committed
660 661 662 663 664 665 666
				return buffer;
			}
		}
	}
	return NULL;
}

Ghislain MARY's avatar
Ghislain MARY committed
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
void Daemon::dumpCommandsHelp() {
	int cols = 80;
#ifdef TIOCGSIZE
	struct ttysize ts;
	ioctl(STDIN_FILENO, TIOCGSIZE, &ts);
	cols = ts.ts_cols;
#elif defined(TIOCGWINSZ)
	struct winsize ts;
	ioctl(STDIN_FILENO, TIOCGWINSZ, &ts);
	cols = ts.ws_col;
#endif

	cout << endl;
	for (list<DaemonCommand*>::iterator it = mCommands.begin(); it != mCommands.end(); ++it) {
		cout << setfill('-') << setw(cols) << "-" << endl << endl;
		cout << (*it)->getHelp();
	}
}

Simon Morlat's avatar
Simon Morlat committed
686 687 688
static string htmlEscape(const string &orig){
	string ret=orig;
	size_t pos;
Ghislain MARY's avatar
Ghislain MARY committed
689

Simon Morlat's avatar
Simon Morlat committed
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
	while(1){
		pos=ret.find('<');
		if (pos!=string::npos){
			ret.erase(pos,1);
			ret.insert(pos,"&lt");
			continue;
		}
		pos=ret.find('>');
		if (pos!=string::npos){
			ret.erase(pos,1);
			ret.insert(pos,"&gt");
			continue;
		}
		break;
	}
	while(1){
		pos=ret.find('\n');
		if (pos!=string::npos){
			ret.erase(pos,1);
			ret.insert(pos,"<br>");
			continue;
		}
		break;
	}
	return ret;
}

void Daemon::dumpCommandsHelpHtml(){
	cout << endl;
	cout << "<!DOCTYPE html><html><body>"<<endl;
	cout << "<h1>List of linphone-daemon commands.</h1>"<<endl;
	for (list<DaemonCommand*>::iterator it = mCommands.begin(); it != mCommands.end(); ++it) {
		cout<<"<h2>"<<htmlEscape((*it)->getProto())<<"</h2>"<<endl;
		cout<<"<h3>"<<"Description"<<"</h3>"<<endl;
		cout<<"<p>"<<htmlEscape((*it)->getDescription())<<"</p>"<<endl;
		cout<<"<h3>"<<"Examples"<<"</h3>"<<endl;
		const std::list<const DaemonCommandExample*> &examples=(*it)->getExamples();
		cout<<"<p><i>";
		for(list<const DaemonCommandExample*>::const_iterator ex_it=examples.begin();ex_it!=examples.end();++ex_it){
			cout<<"<b>"<<htmlEscape("Linphone-daemon>")<<htmlEscape((*ex_it)->getCommand())<<"</b><br>"<<endl;
			cout<<htmlEscape((*ex_it)->getOutput())<<"<br>"<<endl;
			cout<<"<br><br>";
		}
		cout<<"</i></p>"<<endl;
	}
Ghislain MARY's avatar
Ghislain MARY committed
735

Simon Morlat's avatar
Simon Morlat committed
736 737 738
	cout << "</body></html>"<<endl;
}

Ghislain MARY's avatar
Ghislain MARY committed
739

Yann Diorcet's avatar
Yann Diorcet committed
740 741
static void printHelp() {
	fprintf(stdout, "daemon-linphone [<options>]\n"
742 743 744 745 746 747 748 749 750 751 752
#if defined(LICENCE_GPL) || defined(LICENCE_COMMERCIAL)
			"Licence: "
#ifdef LICENCE_GPL
			"GPL"
#endif
#ifdef LICENCE_COMMERCIAL
			"Commercial"
#endif
			"\n"
#endif

Yann Diorcet's avatar
Yann Diorcet committed
753
			"where options are :\n"
754
			"\t--help\t\t\tPrint this notice.\n"
Ghislain MARY's avatar
Ghislain MARY committed
755
			"\t--dump-commands-help\tDump the help of every available commands.\n"
Simon Morlat's avatar
Simon Morlat committed
756
			"\t--dump-commands-html-help\tDump the help of every available commands.\n"
757
			"\t--pipe <pipename>\tCreate an unix server socket to receive commands.\n"
Ghislain MARY's avatar
Ghislain MARY committed
758
			"\t--log <path>\t\tSupply a file where the log will be saved.\n"
759 760
			"\t--factory-config <path>\tSupply a readonly linphonerc style config file to start with.\n"
			"\t--config <path>\t\tSupply a linphonerc style config file to start with.\n"
Simon Morlat's avatar
Simon Morlat committed
761
			"\t--disable-stats-events\t\tDo not automatically raise RTP statistics events.\n"
Ghislain MARY's avatar
Ghislain MARY committed
762
			"\t--enable-lsd\t\tUse the linphone sound daemon.\n"
763 764
			"\t-C\t\t\tenable video capture.\n"
			"\t-D\t\t\tenable video display.\n");
Yann Diorcet's avatar
Yann Diorcet committed
765 766
}

767 768 769 770
void Daemon::startThread() {
	ms_thread_create(&this->mThread, NULL, Daemon::iterateThread, this);
}

771 772
char *Daemon::readLine(const char *prompt, bool *eof) {
	*eof=false;
773 774 775
#ifdef HAVE_READLINE
	return readline(prompt);
#else
776 777 778 779
	if (cin.eof()) {
		*eof=true;
		return NULL;
	}
780 781 782 783 784 785 786
	cout << prompt;
	char *buff = (char *) malloc(sLineSize);
	cin.getline(buff, sLineSize);
	return buff;
#endif
}

Yann Diorcet's avatar
Yann Diorcet committed
787 788
int Daemon::run() {
	char line[sLineSize] = "daemon-linphone>";
Yann Diorcet's avatar
Yann Diorcet committed
789
	char *ret;
Yann Diorcet's avatar
Yann Diorcet committed
790
	mRunning = true;
791
	startThread();
Yann Diorcet's avatar
Yann Diorcet committed
792
	while (mRunning) {
793
		bool eof=false;
Yann Diorcet's avatar
Yann Diorcet committed
794
		if (mServerFd == -1) {
795
			ret = readLine(line,&eof);
Yann Diorcet's avatar
Yann Diorcet committed
796
			if (ret && ret[0] != '\0') {
797
#ifdef HAVE_READLINE
Yann Diorcet's avatar
Yann Diorcet committed
798
				add_history(ret);
799
#endif
Yann Diorcet's avatar
Yann Diorcet committed
800
			}
Yann Diorcet's avatar
Yann Diorcet committed
801 802
		} else {
			ret = readPipe(line, sLineSize);
Yann Diorcet's avatar
Yann Diorcet committed
803
		}
Yann Diorcet's avatar
Yann Diorcet committed
804
		if (ret && ret[0] != '\0') {
Yann Diorcet's avatar
Yann Diorcet committed
805
			execCommand(ret);
Yann Diorcet's avatar
Yann Diorcet committed
806
		}
807
		if (mServerFd == -1 && ret != NULL) {
Yann Diorcet's avatar
Yann Diorcet committed
808 809
			free(ret);
		}
810
		if (eof && mRunning) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
811 812 813
			mRunning = false; // ctrl+d
			cout << "Quitting..." << endl;
		}
Yann Diorcet's avatar
Yann Diorcet committed
814
	}
815
	stopThread();
Yann Diorcet's avatar
Yann Diorcet committed
816 817 818
	return 0;
}

819 820 821 822 823
void Daemon::stopThread() {
	void *ret;
	ms_thread_join(mThread, &ret);
}

Yann Diorcet's avatar
Yann Diorcet committed
824 825
void Daemon::quit() {
	mRunning = false;
Yann Diorcet's avatar
Yann Diorcet committed
826 827
}

Simon Morlat's avatar
Simon Morlat committed
828 829 830 831
void Daemon::enableStatsEvents(bool enabled){
	mUseStatsEvents=enabled;
}

Ghislain MARY's avatar
Ghislain MARY committed
832 833 834 835
void Daemon::enableLSD(bool enabled) {
	if (mLSD) linphone_sound_daemon_destroy(mLSD);
	linphone_core_use_sound_daemon(mLc, NULL);
	if (enabled) {
836
		mLSD = linphone_sound_daemon_new(mLc->factory,NULL, 44100, 1);
Ghislain MARY's avatar
Ghislain MARY committed
837 838 839 840
		linphone_core_use_sound_daemon(mLc, mLSD);
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
841 842
Daemon::~Daemon() {
	uninitCommands();
Yann Diorcet's avatar
Yann Diorcet committed
843

Guillaume Beraudo's avatar
Guillaume Beraudo committed
844 845
	for (std::map<int, AudioStreamAndOther *>::iterator it = mAudioStreams.begin(); it != mAudioStreams.end(); ++it) {
		audio_stream_stop(it->second->stream);
Yann Diorcet's avatar
Yann Diorcet committed
846 847
	}

Ghislain MARY's avatar
Ghislain MARY committed
848
	enableLSD(false);
Yann Diorcet's avatar
Yann Diorcet committed
849 850
	linphone_core_destroy(mLc);
	if (mChildFd != -1) {
Yann Diorcet's avatar
Yann Diorcet committed
851 852
		close(mChildFd);
	}
Yann Diorcet's avatar
Yann Diorcet committed
853
	if (mServerFd != -1) {
Yann Diorcet's avatar
Yann Diorcet committed
854 855
		ortp_server_pipe_close(mServerFd);
	}
856 857 858 859 860
	if (mLogFile != NULL) {
		linphone_core_enable_logs(NULL);
		fclose(mLogFile);
	}

861 862 863
	ms_mutex_destroy(&mMutex);

#ifdef HAVE_READLINE
Yann Diorcet's avatar
Yann Diorcet committed
864 865
	stifle_history(30);
	write_history(mHistfile.c_str());
866
#endif
Yann Diorcet's avatar
Yann Diorcet committed
867 868
}

Yann Diorcet's avatar
Yann Diorcet committed
869 870
int main(int argc, char *argv[]) {
	const char *config_path = NULL;
871
	const char *factory_config_path = NULL;
872
	const char *pipe_name = NULL;
873
	const char *log_file = NULL;
Yann Diorcet's avatar
Yann Diorcet committed
874 875
	bool capture_video = false;
	bool display_video = false;
Ghislain MARY's avatar
Ghislain MARY committed
876
	bool stats_enabled = true;
Ghislain MARY's avatar
Ghislain MARY committed
877
	bool lsd_enabled = false;
Yann Diorcet's avatar
Yann Diorcet committed
878 879
	int i;

Yann Diorcet's avatar
Yann Diorcet committed
880 881
	for (i = 1; i < argc; ++i) {
		if (strcmp(argv[i], "--help") == 0) {
Yann Diorcet's avatar
Yann Diorcet committed
882 883
			printHelp();
			return 0;
Ghislain MARY's avatar
Ghislain MARY committed
884 885 886 887
		} else if (strcmp(argv[i], "--dump-commands-help") == 0) {
			Daemon app(NULL, NULL, NULL, NULL, false, false);
			app.dumpCommandsHelp();
			return 0;
Simon Morlat's avatar
Simon Morlat committed
888 889 890 891
		}else if (strcmp(argv[i], "--dump-commands-html-help") == 0) {
			Daemon app(NULL, NULL, NULL, NULL, false, false);
			app.dumpCommandsHelpHtml();
			return 0;
Yann Diorcet's avatar
Yann Diorcet committed
892
		} else if (strcmp(argv[i], "--pipe") == 0) {
893
			if (i + 1 >= argc) {
894
				fprintf(stderr, "no pipe name specify after --pipe\n");
895 896 897
				return -1;
			}
			pipe_name = argv[++i];
898 899
		} else if (strcmp(argv[i], "--factory-config") == 0) {
			if (i + 1 >= argc) {