daemon.cc 25.7 KB
Newer Older
Ghislain MARY's avatar
Ghislain MARY committed
1 2
#include <cstdio>
#include <sys/ioctl.h>
Yann Diorcet's avatar
Yann Diorcet committed
3
#include <iostream>
Ghislain MARY's avatar
Ghislain MARY committed
4
#include <iomanip>
Yann Diorcet's avatar
Yann Diorcet committed
5 6
#include <sstream>
#include <algorithm>
Yann Diorcet's avatar
Yann Diorcet committed
7

8
#ifdef HAVE_READLINE
9 10
#include <readline/readline.h>
#include <readline/history.h>
11
#endif
Yann Diorcet's avatar
Yann Diorcet committed
12

13
#include <poll.h>
Yann Diorcet's avatar
Yann Diorcet committed
14

15
#include "daemon.h"
16
#include "commands/adaptive-jitter-compensation.h"
Simon Morlat's avatar
Simon Morlat committed
17
#include "commands/jitterbuffer.h"
18 19 20
#include "commands/answer.h"
#include "commands/audio-codec-get.h"
#include "commands/audio-codec-move.h"
Yann Diorcet's avatar
Yann Diorcet committed
21
#include "commands/audio-codec-set.h"
22
#include "commands/audio-codec-toggle.h"
23 24
#include "commands/audio-stream-start.h"
#include "commands/audio-stream-stop.h"
Guillaume Beraudo's avatar
Guillaume Beraudo committed
25
#include "commands/audio-stream-stats.h"
26 27 28
#include "commands/call.h"
#include "commands/call-stats.h"
#include "commands/call-status.h"
29 30 31 32 33 34
#include "commands/call-pause.h"
#include "commands/call-mute.h"
#include "commands/call-resume.h"
#include "commands/call-camera.h"
#include "commands/call-transfer.h"
#include "commands/conference.h"
35
#include "commands/contact.h"
36
#include "commands/dtmf.h"
Ghislain MARY's avatar
Ghislain MARY committed
37
#include "commands/firewall-policy.h"
38
#include "commands/help.h"
Ghislain MARY's avatar
Ghislain MARY committed
39
#include "commands/ipv6.h"
Ghislain MARY's avatar
Ghislain MARY committed
40
#include "commands/media-encryption.h"
Yann Diorcet's avatar
Yann Diorcet committed
41
#include "commands/msfilter-add-fmtp.h"
Ghislain MARY's avatar
Ghislain MARY committed
42
#include "commands/play-wav.h"
43
#include "commands/pop-event.h"
Ghislain MARY's avatar
Ghislain MARY committed
44
#include "commands/port.h"
45 46 47 48 49 50
#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"
Ghislain MARY's avatar
Ghislain MARY committed
51
#include "commands/version.h"
Yann Diorcet's avatar
Yann Diorcet committed
52

Guillaume Beraudo's avatar
Guillaume Beraudo committed
53
#include "private.h"
54
using namespace std;
Yann Diorcet's avatar
Yann Diorcet committed
55

56 57 58 59 60 61 62 63 64 65 66 67 68 69
#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

70 71
const char * const ice_state_str[] = {
	"Not activated",	/* LinphoneIceStateNotActivated */
Ghislain MARY's avatar
Ghislain MARY committed
72
	"Failed",	/* LinphoneIceStateFailed */
73 74 75 76 77 78
	"In progress",	/* LinphoneIceStateInProgress */
	"Host connection",	/* LinphoneIceStateHostConnection */
	"Reflexive connection",	/* LinphoneIceStateReflexiveConnection */
	"Relayed connection"	/* LinphoneIceStateRelayConnection */
};

79 80 81 82 83 84 85 86 87 88 89
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
90
EventResponse::EventResponse(Daemon *daemon, LinphoneCall *call, LinphoneCallState state) {
Yann Diorcet's avatar
Yann Diorcet committed
91
	ostringstream ostr;
Yann Diorcet's avatar
Yann Diorcet committed
92 93 94
	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
95
	ostr << "Id: " << daemon->updateCallId(call) << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
96 97 98 99
	setBody(ostr.str().c_str());
	ms_free(remote);
}

Yann Diorcet's avatar
Yann Diorcet committed
100
DtmfResponse::DtmfResponse(Daemon *daemon, LinphoneCall *call, int dtmf) {
Yann Diorcet's avatar
Yann Diorcet committed
101 102 103 104
	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
105
	ostr << "Id: " << daemon->updateCallId(call) << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
106 107 108 109
	setBody(ostr.str().c_str());
	ms_free(remote);
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
110
static ostream &printCallStatsHelper(ostream &ostr, const LinphoneCallStats *stats, const string &prefix) {
111
	ostr << prefix << "ICE state: " << ice_state_str[stats->ice_state] << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
	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
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
	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
179

Yann Diorcet's avatar
Yann Diorcet committed
180 181 182 183 184 185
	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
186 187 188 189 190
	}

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

Guillaume Beraudo's avatar
Guillaume Beraudo committed
191 192 193 194

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

Guillaume Beraudo's avatar
Guillaume Beraudo committed
196 197 198 199 200 201 202 203 204 205 206 207 208 209
	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-");
	}
210

Guillaume Beraudo's avatar
Guillaume Beraudo committed
211
	printCallStatsHelper(ostr, stats, prefix);
212

Guillaume Beraudo's avatar
Guillaume Beraudo committed
213 214 215
	setBody(ostr.str().c_str());
}

Yann Diorcet's avatar
Yann Diorcet committed
216
PayloadTypeResponse::PayloadTypeResponse(LinphoneCore *core, const PayloadType *payloadType, int index, const string &prefix, bool enabled_status) {
Yann Diorcet's avatar
Yann Diorcet committed
217
	ostringstream ostr;
Yann Diorcet's avatar
Yann Diorcet committed
218 219 220 221 222 223 224 225 226 227 228 229 230 231
	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
232 233
}

234 235 236 237 238
PayloadTypeParser::PayloadTypeParser(LinphoneCore *core, const string &mime_type, bool accept_all) : mAll(false), mSuccesful(true), mPayloadTypeNumber(-1) {
	if (accept_all && (mime_type.compare("ALL") == 0)) {
		mAll = true;
		return;
	}
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
	istringstream ist(mime_type);
	ist >> mPayloadTypeNumber;
	if (ist.fail()) {
		char type[12];
		int rate, channels;
		if (sscanf(mime_type.c_str(), "%11[^/]/%u/%u", type, &rate, &channels) != 3) {
			mSuccesful = false;
			return;
		}
		const PayloadType *pt = linphone_core_find_payload_type(core, type, rate, channels);
		if (pt == NULL) {
			mPayloadTypeNumber = -1;
		} else {
			mPayloadTypeNumber = linphone_core_get_payload_type_number(core, pt);
		}
	}
}

Ghislain MARY's avatar
Ghislain MARY committed
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
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
281 282
}

Yann Diorcet's avatar
Yann Diorcet committed
283 284
bool DaemonCommand::matches(const char *name) const {
	return strcmp(name, mName.c_str()) == 0;
Yann Diorcet's avatar
Yann Diorcet committed
285 286
}

287
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) :
Ghislain MARY's avatar
Ghislain MARY committed
288
		mLSD(0), mLogFile(NULL), mCallIds(0), mProxyIds(0), mAudioStreamIds(0) {
289
	ms_mutex_init(&mMutex, NULL);
Yann Diorcet's avatar
Yann Diorcet committed
290 291
	mServerFd = -1;
	mChildFd = -1;
292
	if (pipe_name == NULL) {
293 294 295 296 297 298 299 300 301
#ifdef HAVE_READLINE
		const char *homedir = getenv("HOME");
		rl_readline_name = "daemon";
		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
302
	} else {
303
		mServerFd = ortp_server_pipe_create(pipe_name);
Yann Diorcet's avatar
Yann Diorcet committed
304
		listen(mServerFd, 2);
305
		fprintf(stdout, "Server unix socket created, name=%s fd=%i\n", pipe_name, mServerFd);
Yann Diorcet's avatar
Yann Diorcet committed
306 307
	}

308 309 310 311 312 313
	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
314 315 316

	LinphoneCoreVTable vtable = { 0 };
	vtable.call_state_changed = callStateChanged;
Yann Diorcet's avatar
Yann Diorcet committed
317
	vtable.call_stats_updated = callStatsUpdated;
Yann Diorcet's avatar
Yann Diorcet committed
318
	vtable.dtmf_received = dtmfReceived;
319
	mLc = linphone_core_new(&vtable, config_path, factory_config_path, this);
Yann Diorcet's avatar
Yann Diorcet committed
320
	linphone_core_set_user_data(mLc, this);
321
	linphone_core_enable_video(mLc, capture_video, display_video);
Yann Diorcet's avatar
Yann Diorcet committed
322
	initCommands();
Simon Morlat's avatar
Simon Morlat committed
323
	mUseStatsEvents=true;
Yann Diorcet's avatar
Yann Diorcet committed
324 325
}

Yann Diorcet's avatar
Yann Diorcet committed
326
const list<DaemonCommand*> &Daemon::getCommandList() const {
Yann Diorcet's avatar
Yann Diorcet committed
327 328 329
	return mCommands;
}

Yann Diorcet's avatar
Yann Diorcet committed
330
LinphoneCore *Daemon::getCore() {
Yann Diorcet's avatar
Yann Diorcet committed
331 332 333
	return mLc;
}

Ghislain MARY's avatar
Ghislain MARY committed
334 335 336 337
LinphoneSoundDaemon *Daemon::getLSD() {
	return mLSD;
}

Yann Diorcet's avatar
Yann Diorcet committed
338 339 340
int Daemon::updateCallId(LinphoneCall *call) {
	int val = (int) (long) linphone_call_get_user_pointer(call);
	if (val == 0) {
341 342
		linphone_call_set_user_pointer(call, (void*) (long) ++mCallIds);
		return mCallIds;
Yann Diorcet's avatar
Yann Diorcet committed
343 344
	}
	return val;
Yann Diorcet's avatar
Yann Diorcet committed
345 346
}

Yann Diorcet's avatar
Yann Diorcet committed
347
LinphoneCall *Daemon::findCall(int id) {
Yann Diorcet's avatar
Yann Diorcet committed
348 349 350 351
	const MSList *elem = linphone_core_get_calls(mLc);
	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
352 353 354 355 356
			return call;
	}
	return NULL;
}

Yann Diorcet's avatar
Yann Diorcet committed
357 358 359
int Daemon::updateProxyId(LinphoneProxyConfig *cfg) {
	int val = (int) (long) linphone_proxy_config_get_user_data(cfg);
	if (val == 0) {
360 361
		linphone_proxy_config_set_user_data(cfg, (void*) (long) ++mProxyIds);
		return mProxyIds;
Yann Diorcet's avatar
Yann Diorcet committed
362 363
	}
	return val;
Yann Diorcet's avatar
Yann Diorcet committed
364 365
}

Yann Diorcet's avatar
Yann Diorcet committed
366
LinphoneProxyConfig *Daemon::findProxy(int id) {
Yann Diorcet's avatar
Yann Diorcet committed
367 368 369 370
	const MSList *elem = linphone_core_get_proxy_config_list(mLc);
	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
371 372 373 374 375
			return proxy;
	}
	return NULL;
}

Yann Diorcet's avatar
Yann Diorcet committed
376
int Daemon::updateAudioStreamId(AudioStream *audio_stream) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
377 378
	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
379 380 381
			return it->first;
	}

382
	++mProxyIds;
Guillaume Beraudo's avatar
Guillaume Beraudo committed
383
	mAudioStreams.insert(make_pair(mProxyIds, new AudioStreamAndOther(audio_stream)));
384
	return mProxyIds;
Yann Diorcet's avatar
Yann Diorcet committed
385 386
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
387 388
AudioStreamAndOther *Daemon::findAudioStreamAndOther(int id) {
	std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
Yann Diorcet's avatar
Yann Diorcet committed
389 390 391 392 393
	if (it != mAudioStreams.end())
		return it->second;
	return NULL;
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
394 395
AudioStream *Daemon::findAudioStream(int id) {
	std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
396
	if (it != mAudioStreams.end())
Guillaume Beraudo's avatar
Guillaume Beraudo committed
397 398 399 400 401 402 403
		return it->second->stream;
	return NULL;
}

void Daemon::removeAudioStream(int id) {
	std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
	if (it != mAudioStreams.end()) {
404
		mAudioStreams.erase(it);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
405 406
		delete(it->second);
	}
407 408
}

Yann Diorcet's avatar
Yann Diorcet committed
409
void Daemon::initCommands() {
Yann Diorcet's avatar
Yann Diorcet committed
410
	mCommands.push_back(new RegisterCommand());
411
	mCommands.push_back(new ContactCommand());
Yann Diorcet's avatar
Yann Diorcet committed
412
	mCommands.push_back(new RegisterStatusCommand());
Yann Diorcet's avatar
Yann Diorcet committed
413
	mCommands.push_back(new UnregisterCommand());
Yann Diorcet's avatar
Yann Diorcet committed
414 415
	mCommands.push_back(new CallCommand());
	mCommands.push_back(new TerminateCommand());
416
	mCommands.push_back(new DtmfCommand());
Ghislain MARY's avatar
Ghislain MARY committed
417
	mCommands.push_back(new PlayWavCommand());
Yann Diorcet's avatar
Yann Diorcet committed
418 419
	mCommands.push_back(new PopEventCommand());
	mCommands.push_back(new AnswerCommand());
Yann Diorcet's avatar
Yann Diorcet committed
420
	mCommands.push_back(new CallStatusCommand());
Yann Diorcet's avatar
Yann Diorcet committed
421
	mCommands.push_back(new CallStatsCommand());
422 423 424 425 426 427
	mCommands.push_back(new CallPause());
	mCommands.push_back(new CallMute());
	mCommands.push_back(new CallResume());
	mCommands.push_back(new CallTransfer());
	mCommands.push_back(new CallCamera());
	mCommands.push_back(new Conference());
Yann Diorcet's avatar
Yann Diorcet committed
428 429 430 431
	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
432
	mCommands.push_back(new AudioCodecSetCommand());
Yann Diorcet's avatar
Yann Diorcet committed
433 434
	mCommands.push_back(new AudioStreamStartCommand());
	mCommands.push_back(new AudioStreamStopCommand());
Guillaume Beraudo's avatar
Guillaume Beraudo committed
435
	mCommands.push_back(new AudioStreamStatsCommand());
Yann Diorcet's avatar
Yann Diorcet committed
436
	mCommands.push_back(new MSFilterAddFmtpCommand());
Yann Diorcet's avatar
Yann Diorcet committed
437
	mCommands.push_back(new PtimeCommand());
Ghislain MARY's avatar
Ghislain MARY committed
438
	mCommands.push_back(new IPv6Command());
Ghislain MARY's avatar
Ghislain MARY committed
439
	mCommands.push_back(new FirewallPolicyCommand());
Ghislain MARY's avatar
Ghislain MARY committed
440
	mCommands.push_back(new MediaEncryptionCommand());
Ghislain MARY's avatar
Ghislain MARY committed
441
	mCommands.push_back(new PortCommand());
442
	mCommands.push_back(new AdaptiveBufferCompensationCommand());
Simon Morlat's avatar
Simon Morlat committed
443 444
	mCommands.push_back(new JitterBufferCommand());
	mCommands.push_back(new JitterBufferResetCommand());
Ghislain MARY's avatar
Ghislain MARY committed
445
	mCommands.push_back(new VersionCommand());
Yann Diorcet's avatar
Yann Diorcet committed
446 447
	mCommands.push_back(new QuitCommand());
	mCommands.push_back(new HelpCommand());
Yann Diorcet's avatar
Yann Diorcet committed
448 449 450 451 452 453 454
}

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

Yann Diorcet's avatar
Yann Diorcet committed
457 458
bool Daemon::pullEvent() {
	bool status = false;
Yann Diorcet's avatar
Yann Diorcet committed
459
	ostringstream ostr;
Yann Diorcet's avatar
Yann Diorcet committed
460 461
	if (!mEventQueue.empty()) {
		Response *r = mEventQueue.front();
Yann Diorcet's avatar
Yann Diorcet committed
462
		mEventQueue.pop();
Yann Diorcet's avatar
Yann Diorcet committed
463
		ostr << r->getBody() << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
464
		delete r;
Yann Diorcet's avatar
Yann Diorcet committed
465
		status = true;
Yann Diorcet's avatar
Yann Diorcet committed
466
	}
Yann Diorcet's avatar
Yann Diorcet committed
467
	ostr << "Size: " << mEventQueue.size() << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
468 469
	sendResponse(Response(ostr.str().c_str(), Response::Ok));
	return status;
Yann Diorcet's avatar
Yann Diorcet committed
470 471
}

Yann Diorcet's avatar
Yann Diorcet committed
472 473 474
void Daemon::callStateChanged(LinphoneCall *call, LinphoneCallState state, const char *msg) {
	switch (state) {
	case LinphoneCallIncomingReceived:
475 476
		linphone_call_enable_camera (call,mAutoVideo);
	case LinphoneCallOutgoingProgress:
Yann Diorcet's avatar
Yann Diorcet committed
477 478 479 480 481
	case LinphoneCallIncomingEarlyMedia:
	case LinphoneCallConnected:
	case LinphoneCallStreamsRunning:
	case LinphoneCallError:
	case LinphoneCallEnd:
Yann Diorcet's avatar
Yann Diorcet committed
482
		mEventQueue.push(new EventResponse(this, call, state));
Yann Diorcet's avatar
Yann Diorcet committed
483
		break;
Yann Diorcet's avatar
Yann Diorcet committed
484
	default:
Yann Diorcet's avatar
Yann Diorcet committed
485
		break;
Yann Diorcet's avatar
Yann Diorcet committed
486
	}
Yann Diorcet's avatar
Yann Diorcet committed
487 488
}

Yann Diorcet's avatar
Yann Diorcet committed
489
void Daemon::callStatsUpdated(LinphoneCall *call, const LinphoneCallStats *stats) {
Simon Morlat's avatar
Simon Morlat committed
490
	if (mUseStatsEvents) mEventQueue.push(new CallStatsResponse(this, call, stats, true));
Yann Diorcet's avatar
Yann Diorcet committed
491 492
}

Yann Diorcet's avatar
Yann Diorcet committed
493
void Daemon::dtmfReceived(LinphoneCall *call, int dtmf) {
Yann Diorcet's avatar
Yann Diorcet committed
494
	mEventQueue.push(new DtmfResponse(this, call, dtmf));
Yann Diorcet's avatar
Yann Diorcet committed
495 496
}

Yann Diorcet's avatar
Yann Diorcet committed
497 498 499
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
500
}
Yann Diorcet's avatar
Yann Diorcet committed
501 502 503 504
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
505 506 507 508
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
509

Guillaume Beraudo's avatar
Guillaume Beraudo committed
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
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) {
				linphone_call_stream_stats_hack(&it->second->stream->ms, &it->second->stats, ev);
				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
525
void Daemon::iterate() {
Yann Diorcet's avatar
Yann Diorcet committed
526
	linphone_core_iterate(mLc);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
527
	iterateStreamStats();
Yann Diorcet's avatar
Yann Diorcet committed
528 529 530
	if (mChildFd == -1) {
		if (!mEventQueue.empty()) {
			Response *r = mEventQueue.front();
Yann Diorcet's avatar
Yann Diorcet committed
531
			mEventQueue.pop();
Yann Diorcet's avatar
Yann Diorcet committed
532
			fprintf(stdout, "%s\n", r->getBody().c_str());
Yann Diorcet's avatar
Yann Diorcet committed
533 534 535 536 537 538
			fflush(stdout);
			delete r;
		}
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
539 540 541 542 543 544
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()) {
545
		ms_mutex_lock(&mMutex);
Yann Diorcet's avatar
Yann Diorcet committed
546
		(*it)->exec(this, args);
547
		ms_mutex_unlock(&mMutex);
Yann Diorcet's avatar
Yann Diorcet committed
548
	} else {
Yann Diorcet's avatar
Yann Diorcet committed
549 550 551 552
		sendResponse(Response("Unknown command."));
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
553 554
void Daemon::sendResponse(const Response &resp) {
	char buf[4096] = { 0 };
Yann Diorcet's avatar
Yann Diorcet committed
555
	int size;
Yann Diorcet's avatar
Yann Diorcet committed
556 557 558 559
	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
560
		}
Yann Diorcet's avatar
Yann Diorcet committed
561 562
	} else {
		fprintf(stdout, "%s", buf);
Yann Diorcet's avatar
Yann Diorcet committed
563 564 565 566
		fflush(stdout);
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
567 568 569 570 571 572
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
573
	}
Yann Diorcet's avatar
Yann Diorcet committed
574 575 576
	if (mChildFd != -1) {
		pfd[1].events = POLLIN;
		pfd[1].fd = mChildFd;
Yann Diorcet's avatar
Yann Diorcet committed
577 578
		nfds++;
	}
Yann Diorcet's avatar
Yann Diorcet committed
579 580 581
	int err = poll(pfd, nfds, 50);
	if (err > 0) {
		if (mServerFd != -1 && (pfd[0].revents & POLLIN)) {
Yann Diorcet's avatar
Yann Diorcet committed
582
			struct sockaddr_storage addr;
Yann Diorcet's avatar
Yann Diorcet committed
583 584 585 586
			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
587 588
					ms_error("Cannot accept two client at the same time");
					close(childfd);
Yann Diorcet's avatar
Yann Diorcet committed
589 590
				} else {
					mChildFd = childfd;
Yann Diorcet's avatar
Yann Diorcet committed
591 592 593 594
					return NULL;
				}
			}
		}
Yann Diorcet's avatar
Yann Diorcet committed
595
		if (mChildFd != -1 && (pfd[1].revents & POLLIN)) {
Yann Diorcet's avatar
Yann Diorcet committed
596
			int ret;
Yann Diorcet's avatar
Yann Diorcet committed
597
			if ((ret = read(mChildFd, buffer, buflen)) == -1) {
Yann Diorcet's avatar
Yann Diorcet committed
598
				ms_error("Fail to read from pipe: %s", strerror(errno));
Yann Diorcet's avatar
Yann Diorcet committed
599 600
			} else {
				if (ret == 0) {
Yann Diorcet's avatar
Yann Diorcet committed
601 602
					ms_message("Client disconnected");
					close(mChildFd);
Yann Diorcet's avatar
Yann Diorcet committed
603
					mChildFd = -1;
Yann Diorcet's avatar
Yann Diorcet committed
604 605
					return NULL;
				}
Yann Diorcet's avatar
Yann Diorcet committed
606
				buffer[ret] = 0;
Yann Diorcet's avatar
Yann Diorcet committed
607 608 609 610 611 612 613
				return buffer;
			}
		}
	}
	return NULL;
}

Ghislain MARY's avatar
Ghislain MARY committed
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
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
633 634 635
static string htmlEscape(const string &orig){
	string ret=orig;
	size_t pos;
Ghislain MARY's avatar
Ghislain MARY committed
636

Simon Morlat's avatar
Simon Morlat committed
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
	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
682

Simon Morlat's avatar
Simon Morlat committed
683 684 685
	cout << "</body></html>"<<endl;
}

Ghislain MARY's avatar
Ghislain MARY committed
686

Yann Diorcet's avatar
Yann Diorcet committed
687 688
static void printHelp() {
	fprintf(stdout, "daemon-linphone [<options>]\n"
689 690 691 692 693 694 695 696 697 698 699
#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
700
			"where options are :\n"
701
			"\t--help\t\t\tPrint this notice.\n"
Ghislain MARY's avatar
Ghislain MARY committed
702
			"\t--dump-commands-help\tDump the help of every available commands.\n"
Simon Morlat's avatar
Simon Morlat committed
703
			"\t--dump-commands-html-help\tDump the help of every available commands.\n"
704
			"\t--pipe <pipename>\tCreate an unix server socket to receive commands.\n"
Ghislain MARY's avatar
Ghislain MARY committed
705
			"\t--log <path>\t\tSupply a file where the log will be saved.\n"
706 707
			"\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
708
			"\t--disable-stats-events\t\tDo not automatically raise RTP statistics events.\n"
Ghislain MARY's avatar
Ghislain MARY committed
709
			"\t--enable-lsd\t\tUse the linphone sound daemon.\n"
710 711
			"\t-C\t\t\tenable video capture.\n"
			"\t-D\t\t\tenable video display.\n");
Yann Diorcet's avatar
Yann Diorcet committed
712 713
}

714 715 716 717 718 719 720 721
void Daemon::startThread() {
	ms_thread_create(&this->mThread, NULL, Daemon::iterateThread, this);
}

char *Daemon::readLine(const char *prompt) {
#ifdef HAVE_READLINE
	return readline(prompt);
#else
Guillaume Beraudo's avatar
Guillaume Beraudo committed
722
	if (cin.eof()) return NULL;
723 724 725 726 727 728 729
	cout << prompt;
	char *buff = (char *) malloc(sLineSize);
	cin.getline(buff, sLineSize);
	return buff;
#endif
}

Yann Diorcet's avatar
Yann Diorcet committed
730 731
int Daemon::run() {
	char line[sLineSize] = "daemon-linphone>";
Yann Diorcet's avatar
Yann Diorcet committed
732
	char *ret;
Yann Diorcet's avatar
Yann Diorcet committed
733
	mRunning = true;
734
	startThread();
Yann Diorcet's avatar
Yann Diorcet committed
735 736
	while (mRunning) {
		if (mServerFd == -1) {
737
			ret = readLine(line);
Yann Diorcet's avatar
Yann Diorcet committed
738
			if (ret && ret[0] != '\0') {
739
#ifdef HAVE_READLINE
Yann Diorcet's avatar
Yann Diorcet committed
740
				add_history(ret);
741
#endif
Yann Diorcet's avatar
Yann Diorcet committed
742
			}
Yann Diorcet's avatar
Yann Diorcet committed
743 744
		} else {
			ret = readPipe(line, sLineSize);
Yann Diorcet's avatar
Yann Diorcet committed
745
		}
Yann Diorcet's avatar
Yann Diorcet committed
746
		if (ret && ret[0] != '\0') {
Yann Diorcet's avatar
Yann Diorcet committed
747
			execCommand(ret);
Yann Diorcet's avatar
Yann Diorcet committed
748
		}
749
		if (mServerFd == -1 && ret != NULL) {
Yann Diorcet's avatar
Yann Diorcet committed
750 751
			free(ret);
		}
Guillaume Beraudo's avatar
Guillaume Beraudo committed
752 753 754 755
		if (!ret && mRunning) {
			mRunning = false; // ctrl+d
			cout << "Quitting..." << endl;
		}
Yann Diorcet's avatar
Yann Diorcet committed
756
	}
757
	stopThread();
Yann Diorcet's avatar
Yann Diorcet committed
758 759 760
	return 0;
}

761 762 763 764 765
void Daemon::stopThread() {
	void *ret;
	ms_thread_join(mThread, &ret);
}

Yann Diorcet's avatar
Yann Diorcet committed
766 767
void Daemon::quit() {
	mRunning = false;
Yann Diorcet's avatar
Yann Diorcet committed
768 769
}

Simon Morlat's avatar
Simon Morlat committed
770 771 772 773
void Daemon::enableStatsEvents(bool enabled){
	mUseStatsEvents=enabled;
}

Ghislain MARY's avatar
Ghislain MARY committed
774 775 776 777 778 779 780 781 782
void Daemon::enableLSD(bool enabled) {
	if (mLSD) linphone_sound_daemon_destroy(mLSD);
	linphone_core_use_sound_daemon(mLc, NULL);
	if (enabled) {
		mLSD = linphone_sound_daemon_new(NULL, 44100, 1);
		linphone_core_use_sound_daemon(mLc, mLSD);
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
783 784
Daemon::~Daemon() {
	uninitCommands();
Yann Diorcet's avatar
Yann Diorcet committed
785

Guillaume Beraudo's avatar
Guillaume Beraudo committed
786 787
	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
788 789
	}

Ghislain MARY's avatar
Ghislain MARY committed
790
	enableLSD(false);
Yann Diorcet's avatar
Yann Diorcet committed
791 792
	linphone_core_destroy(mLc);
	if (mChildFd != -1) {
Yann Diorcet's avatar
Yann Diorcet committed
793 794
		close(mChildFd);
	}
Yann Diorcet's avatar
Yann Diorcet committed
795
	if (mServerFd != -1) {
Yann Diorcet's avatar
Yann Diorcet committed
796 797
		ortp_server_pipe_close(mServerFd);
	}
798 799 800 801 802
	if (mLogFile != NULL) {
		linphone_core_enable_logs(NULL);
		fclose(mLogFile);
	}

803 804 805
	ms_mutex_destroy(&mMutex);

#ifdef HAVE_READLINE
Yann Diorcet's avatar
Yann Diorcet committed
806 807
	stifle_history(30);
	write_history(mHistfile.c_str());
808
#endif
Yann Diorcet's avatar
Yann Diorcet committed
809 810
}

Yann Diorcet's avatar
Yann Diorcet committed
811 812
int main(int argc, char *argv[]) {
	const char *config_path = NULL;
813
	const char *factory_config_path = NULL;
814
	const char *pipe_name = NULL;
815
	const char *log_file = NULL;
Yann Diorcet's avatar
Yann Diorcet committed
816 817
	bool capture_video = false;
	bool display_video = false;
Ghislain MARY's avatar
Ghislain MARY committed
818
	bool stats_enabled = true;
Ghislain MARY's avatar
Ghislain MARY committed
819
	bool lsd_enabled = false;
Yann Diorcet's avatar
Yann Diorcet committed
820 821
	int i;

Yann Diorcet's avatar
Yann Diorcet committed
822 823
	for (i = 1; i < argc; ++i) {
		if (strcmp(argv[i], "--help") == 0) {
Yann Diorcet's avatar
Yann Diorcet committed
824 825
			printHelp();
			return 0;
Ghislain MARY's avatar
Ghislain MARY committed
826 827 828 829
		} 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
830 831 832 833
		}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
834
		} else if (strcmp(argv[i], "--pipe") == 0) {
835
			if (i + 1 >= argc) {
836 837 838 839
				fprintf(stderr, "no pipe name specify after --pipe");
				return -1;
			}
			pipe_name = argv[++i];
840 841 842 843 844 845 846
		} else if (strcmp(argv[i], "--factory-config") == 0) {
			if (i + 1 >= argc) {
				fprintf(stderr, "no file specify after --factory-config");
				return -1;
			}
			factory_config_path = argv[i + 1];
			i++;
Yann Diorcet's avatar
Yann Diorcet committed
847
		} else if (strcmp(argv[i], "--config") == 0) {
848 849 850 851
			if (i + 1 >= argc) {
				fprintf(stderr, "no file specify after  --config");
				return -1;
			}
Yann Diorcet's avatar
Yann Diorcet committed
852
			config_path = argv[i + 1];
853 854 855 856 857 858 859 860
			i++;
		} else if (strcmp(argv[i], "--log") == 0) {
			if (i + 1 >= argc) {
				fprintf(stderr, "no file specify after --log");
				return -1;
			}
			log_file = argv[i + 1];
			i++;
Yann Diorcet's avatar
Yann Diorcet committed
861 862 863 864
		} else if (strcmp(argv[i], "-C") == 0) {
			capture_video = true;
		} else if (strcmp(argv[i], "-D") == 0) {
			display_video = true;
Simon Morlat's avatar
Simon Morlat committed
865
		}else if (strcmp(argv[i],"--disable-stats-events")==0){
Ghislain MARY's avatar
Ghislain MARY committed
866
			stats_enabled = false;
Ghislain MARY's avatar
Ghislain MARY committed
867 868
		} else if (strcmp(argv[i], "--enable-lsd") == 0) {
			lsd_enabled = true;
Yann Diorcet's avatar
Yann Diorcet committed
869 870
		}
	}
871
	Daemon app(config_path, factory_config_path, log_file, pipe_name, display_video, capture_video);
Ghislain MARY's avatar
Ghislain MARY committed
872
	app.enableStatsEvents(stats_enabled);
Ghislain MARY's avatar
Ghislain MARY committed
873
	app.enableLSD(lsd_enabled);
Yann Diorcet's avatar
Yann Diorcet committed
874
	return app.run();
Yann Diorcet's avatar
Yann Diorcet committed
875
}