Skip to content

Commit 7f05bd7

Browse files
Merge pull request #11 from WebPlatformForEmbedded/haseena/process
Changes to handle termination of child process
2 parents 7cf1de5 + 7cb5b34 commit 7f05bd7

3 files changed

Lines changed: 121 additions & 28 deletions

File tree

Launcher.cpp

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ SERVICE_REGISTRATION(Launcher, 1, 0);
3030

3131
// Setup skip URL for right offset.
3232
_service = service;
33+
_deactivationInProgress = false;
3334

3435
config.FromString(_service->ConfigLine());
3536

@@ -58,10 +59,12 @@ SERVICE_REGISTRATION(Launcher, 1, 0);
5859
ASSERT(_memory != nullptr);
5960
ASSERT(_activity.IsValid() == true);
6061

62+
_deactivationInProgress = true;
63+
6164
_memory->Observe(0);
62-
_observer.Unregister(&_notification);
6365

6466
_activity->Shutdown();
67+
_observer.Unregister(&_notification);
6568
_activity.Release();
6669

6770
_memory->Release();
@@ -84,21 +87,30 @@ void Launcher::Update(const ProcessObserver::Info& info)
8487

8588
// This can potentially be called on a socket thread, so the deactivation (wich in turn kills this object) must be done
8689
// on a seperate thread. Also make sure this call-stack can be unwound before we are totally destructed.
87-
if ( (_activity->Pid() == info.Id()) && (info.Event() == ProcessObserver::Info::EVENT_EXIT) ) {
90+
if (_activity->IsActive() == true) {
8891

89-
_memory->Observe(0);
90-
uint32_t result = _activity->ExitCode();
92+
_activity->Update(info);
9193

92-
if (result != Core::ERROR_NONE) {
93-
SYSLOG(Trace::Fatal, (_T("FORCED Shutdown: %s by error: %d."), _service->Callsign().c_str(), result));
94-
PluginHost::WorkerPool::Instance().Submit(PluginHost::IShell::Job::Create(_service, PluginHost::IShell::DEACTIVATED, PluginHost::IShell::FAILURE));
95-
}
96-
else if (_activity->Continuous() == false) {
97-
TRACE(Trace::Information, (_T("Launcher [%s] has run succesfully, deactivation requested."), _service->Callsign().c_str()));
98-
PluginHost::WorkerPool::Instance().Submit(PluginHost::IShell::Job::Create(_service, PluginHost::IShell::DEACTIVATED, PluginHost::IShell::AUTOMATIC));
99-
}
100-
else {
101-
TRACE(Trace::Information, (_T("Launcher [%s] has run succesfully, scheduled for the next run."), _service->Callsign().c_str()));
94+
if (_activity->IsActive() == false) {
95+
uint32_t result = _activity->ExitCode();
96+
97+
if (result != Core::ERROR_NONE) {
98+
if (_deactivationInProgress == false) {
99+
_deactivationInProgress = true;
100+
SYSLOG(Trace::Fatal, (_T("FORCED Shutdown: %s by error: %d."), _service->Callsign().c_str(), result));
101+
PluginHost::WorkerPool::Instance().Submit(PluginHost::IShell::Job::Create(_service, PluginHost::IShell::DEACTIVATED, PluginHost::IShell::FAILURE));
102+
}
103+
}
104+
else if (_activity->Continuous() == false) {
105+
if (_deactivationInProgress == false) {
106+
_deactivationInProgress = true;
107+
TRACE(Trace::Information, (_T("Launcher [%s] has run succesfully, deactivation requested."), _service->Callsign().c_str()));
108+
PluginHost::WorkerPool::Instance().Submit(PluginHost::IShell::Job::Create(_service, PluginHost::IShell::DEACTIVATED, PluginHost::IShell::AUTOMATIC));
109+
}
110+
}
111+
else {
112+
TRACE(Trace::Information, (_T("Launcher [%s] has run succesfully, scheduled for the next run."), _service->Callsign().c_str()));
113+
}
102114
}
103115
}
104116
}
@@ -125,6 +137,7 @@ bool Launcher::ScheduleParameters(const Config& config, string& message, Core::T
125137
}
126138
else if ( (timeMode == ABSOLUTE_WITH_INTERVAL) && ((interval.IsValid() == false) || (interval.TimeInSeconds() == 0)) ) {
127139
message = _T("Requested mode is ABSOLUTE WITH INTERVAL but no interval (or 0 second interval) is given.");
140+
128141
}
129142
else {
130143
// All signals green, we have valid input, calculate the ScheduleTime/Interval time

Launcher.h

Lines changed: 92 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "Module.h"
44
#include <interfaces/IMemory.h>
55
#include <linux/cn_proc.h>
6+
#include <vector>
67

78
namespace WPEFramework {
89
namespace Plugin {
@@ -544,16 +545,19 @@ class Launcher : public PluginHost::IPlugin {
544545
Job(const Job&) = delete;
545546
Job& operator=(const Job&) = delete;
546547

548+
typedef std::vector<uint32_t> ProcessList;
549+
547550
public:
548551
Job(Config* config, const Time& interval, Exchange::IMemory* memory)
549552
: _adminLock()
550-
, _pid(0)
551553
, _options(config->Command.Value().c_str())
552554
, _process(false)
553555
, _memory(memory)
554556
, _interval(interval)
555557
, _closeTime(config->CloseTime.Value())
556558
, _shutdownPhase(0)
559+
, _processListEmpty(1, 1)
560+
, _shutdownCompleted(false)
557561
{
558562
auto iter = config->Parameters.Elements();
559563

@@ -580,11 +584,58 @@ class Launcher : public PluginHost::IPlugin {
580584
uint32_t ExitCode() {
581585
return (_process.IsActive() == false ? _process.ExitCode() : Core::ERROR_NONE);
582586
}
587+
bool IsActive() const {
588+
return (_processList.size() > 0);
589+
}
583590
bool Continuous() const {
584591
return (_interval.IsValid() == true);
585592
}
586593
uint32_t Pid() {
587-
return _pid;
594+
return (_processList.front());
595+
}
596+
void Update (const ProcessObserver::Info& info) {
597+
switch (info.Event()) {
598+
case ProcessObserver::Info::EVENT_FORK:
599+
{
600+
_adminLock.Lock();
601+
602+
ProcessList::iterator position (std::find(_processList.begin(), _processList.end(), info.Id()));
603+
if (position != _processList.end()) {
604+
_processList.push_back(info.ChildId());
605+
606+
if (_shutdownPhase == 2) {
607+
::kill(info.ChildId(), SIGKILL);
608+
}
609+
}
610+
611+
_adminLock.Unlock();
612+
break;
613+
}
614+
case ProcessObserver::Info::EVENT_EXIT:
615+
{
616+
_adminLock.Lock();
617+
618+
ProcessList::iterator position (std::find(_processList.begin(), _processList.end(), info.Id()));
619+
if (position != _processList.end()) {
620+
_processList.erase(position);
621+
if ( (info.Id() ==_processList.front()) && (_process.IsActive() == false) ) {
622+
_memory->Observe(0);
623+
}
624+
else {
625+
// TODO: Probably might need to add the read exit code here for any process that exits to prevent
626+
// Zombie processes here..
627+
}
628+
if (_processList.size() == 0) {
629+
_processListEmpty.Unlock();
630+
}
631+
}
632+
633+
_adminLock.Unlock();
634+
break;
635+
}
636+
default:
637+
break;
638+
}
588639
}
589640
void Schedule (const Core::Time& time) {
590641
if (time <= Core::Time::Now()) {
@@ -600,18 +651,39 @@ class Launcher : public PluginHost::IPlugin {
600651
_adminLock.Unlock();
601652

602653
PluginHost::WorkerPool::Instance().Revoke(Core::ProxyType<Core::IDispatch>(*this));
603-
604654
if (_process.IsActive() == true) {
605655

606656
// First try a gentle touch....
607657
_process.Kill(false);
608658

609659
// Wait for a maximum configured wait time before we shoot the process!!
610-
if (_process.WaitProcessCompleted(_closeTime * 1000) != Core::ERROR_NONE) {
611-
_process.Kill(true);
612-
_process.WaitProcessCompleted(1000);
613-
}
660+
_process.WaitProcessCompleted(_closeTime * 1000);
614661
}
662+
663+
// If there was a proper shutdown, all assoicated processes should have left.
664+
// If not, we will start doing it the rude way!!
665+
if (_processList.size() != 0) {
666+
_adminLock.Lock();
667+
_shutdownPhase = 2;
668+
669+
TRACE_L1("Trying to force kill\n");
670+
for (int i = 0; i < _processList.size(); i++) {
671+
::kill(_processList[i], SIGKILL);
672+
}
673+
674+
_adminLock.Unlock();
675+
_process.WaitProcessCompleted(1000);
676+
}
677+
678+
if (_processListEmpty.Lock(1000) != Core::ERROR_NONE) {
679+
TRACE(Trace::Fatal, (_T("Could not kill all spawned processes for: %s."), _options.Command().c_str()));
680+
_processList.clear();
681+
}
682+
683+
_adminLock.Lock();
684+
_processListEmpty.Unlock();
685+
_shutdownPhase = 0;
686+
_adminLock.Unlock();
615687
}
616688

617689
private:
@@ -621,14 +693,18 @@ class Launcher : public PluginHost::IPlugin {
621693
Core::Time nextRun (Core::Time::Now());
622694

623695
// Check if the process is not active, no need to reschedule the same job again.
624-
if (_process.IsActive() == false) {
696+
if ( (_process.IsActive() == false) && (_shutdownCompleted.Lock(0) == Core::ERROR_NONE) ) {
697+
698+
ASSERT (_processList.size() == 0);
625699

626-
_process.Launch(_options, &_pid);
700+
_processList.push_back(0);
701+
_process.Launch(_options, &_processList.front());
627702

628-
TRACE(Trace::Information, (_T("Launched command: %s [%d]."), _options.Command().c_str(), _pid));
703+
TRACE(Trace::Information, (_T("Launched command: %s [%d]."), _options.Command().c_str(), Pid()));
629704
ASSERT (_memory != nullptr);
630705

631-
_memory->Observe(_pid);
706+
_memory->Observe(Pid());
707+
_shutdownCompleted.Unlock();
632708
}
633709

634710
if (_interval.IsValid() == true) {
@@ -644,13 +720,15 @@ class Launcher : public PluginHost::IPlugin {
644720

645721
private:
646722
Core::CriticalSection _adminLock;
647-
uint32_t _pid;
648723
Core::Process::Options _options;
649724
Core::Process _process;
650725
Exchange::IMemory* _memory;
651726
Time _interval;
652727
uint8_t _closeTime;
653728
uint8_t _shutdownPhase;
729+
ProcessList _processList;
730+
Core::Event _processListEmpty;
731+
Core::BinairySemaphore _shutdownCompleted;
654732
};
655733

656734
public:
@@ -662,6 +740,7 @@ class Launcher : public PluginHost::IPlugin {
662740
, _memory(nullptr)
663741
, _notification(this)
664742
, _activity()
743+
, _deactivationInProgress()
665744
{
666745
}
667746
#ifdef __WIN32__
@@ -707,6 +786,7 @@ class Launcher : public PluginHost::IPlugin {
707786
Exchange::IMemory* _memory;
708787
Core::Sink<Notification> _notification;
709788
Core::ProxyType<Job> _activity;
789+
bool _deactivationInProgress;
710790

711791
static ProcessObserver _observer;
712792
};

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ Plugin to "Launch" linux applications and scripts
7777

7878
Note:
7979
1. If field "mode" is empty or not set, it will treat the time as relative
80-
2. If relative time value is "00:00.00"/invalid format/not set, the launcher will ignore the given time and launch the application at the launcher activation time itself.
80+
2. If relative time value is "00:00.00"/not set, the launcher will ignore the given time and launch the application at the launcher activation time itself.
8181
3. If time format given is
8282
a. "XX", treat it as SS
8383
b. "XX.XX" treat it as MM.SS
@@ -100,7 +100,7 @@ Note:
100100
```
101101

102102
Note:
103-
1. If absolute time value is invalid format/not set, the launcher will ignore the given time and launch the application at the launcher activation time itself.
103+
1. If absolute time value is not set, the launcher will ignore the given time and launch the application at the launcher activation time itself.
104104
2. If time format given is
105105
a. "XX", treat it as SS, and schedule the application launch at next SSth time. i.e. if absolute time given is 25 and current time is 08:10:45, then the application will be
106106
launched at 08:11:25

0 commit comments

Comments
 (0)