1/*
2 * Copyright 2015-2018, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "Events.h"
8
9#include <stdio.h>
10
11#include <Entry.h>
12#include <LaunchRoster.h>
13#include <Message.h>
14#include <ObjectList.h>
15#include <Path.h>
16#include <StringList.h>
17
18#include "BaseJob.h"
19#include "LaunchDaemon.h"
20#include "NetworkWatcher.h"
21#include "Utility.h"
22#include "VolumeWatcher.h"
23
24
25class EventContainer : public Event {
26protected:
27								EventContainer(Event* parent,
28									const BMessenger* target,
29									const BMessage& args);
30								EventContainer(BaseJob* owner,
31									const BMessenger& target);
32
33public:
34			void				AddEvent(Event* event);
35			BObjectList<Event>&	Events();
36
37			const BMessenger&	Target() const;
38
39	virtual	status_t			Register(EventRegistrator& registrator);
40	virtual	void				Unregister(EventRegistrator& registrator);
41
42	virtual	void				Trigger(Event* origin);
43
44	virtual	BaseJob*			Owner() const;
45	virtual	void				SetOwner(BaseJob* owner);
46
47protected:
48			void				AddEventsToString(BString& string) const;
49
50protected:
51			BaseJob*			fOwner;
52			BMessenger			fTarget;
53			BObjectList<Event>	fEvents;
54			bool				fRegistered;
55};
56
57
58class OrEvent : public EventContainer {
59public:
60								OrEvent(Event* parent, const BMessenger* target,
61									const BMessage& args);
62								OrEvent(BaseJob* owner,
63									const BMessenger& target);
64
65	virtual	void				ResetTrigger();
66
67	virtual	BString				ToString() const;
68};
69
70
71class StickyEvent : public Event {
72public:
73								StickyEvent(Event* parent);
74	virtual						~StickyEvent();
75
76	virtual	void				ResetSticky();
77	virtual	void				ResetTrigger();
78};
79
80
81class DemandEvent : public Event {
82public:
83								DemandEvent(Event* parent);
84
85	virtual	status_t			Register(EventRegistrator& registrator);
86	virtual	void				Unregister(EventRegistrator& registrator);
87
88	virtual	BString				ToString() const;
89};
90
91
92class ExternalEvent : public Event {
93public:
94								ExternalEvent(Event* parent, const char* name,
95									const BMessage& args);
96
97			const BString&		Name() const;
98			bool				Resolve(uint32 flags);
99
100			void				ResetSticky();
101	virtual	void				ResetTrigger();
102
103	virtual	status_t			Register(EventRegistrator& registrator);
104	virtual	void				Unregister(EventRegistrator& registrator);
105
106	virtual	BString				ToString() const;
107
108private:
109			BString				fName;
110			BStringList			fArguments;
111			uint32				fFlags;
112			bool				fResolved;
113};
114
115
116class FileCreatedEvent : public Event {
117public:
118								FileCreatedEvent(Event* parent,
119									const BMessage& args);
120
121	virtual	status_t			Register(EventRegistrator& registrator);
122	virtual	void				Unregister(EventRegistrator& registrator);
123
124	virtual	BString				ToString() const;
125
126private:
127			BPath				fPath;
128};
129
130
131class VolumeMountedEvent : public Event, public VolumeListener {
132public:
133								VolumeMountedEvent(Event* parent,
134									const BMessage& args);
135
136	virtual	status_t			Register(EventRegistrator& registrator);
137	virtual	void				Unregister(EventRegistrator& registrator);
138
139	virtual	BString				ToString() const;
140
141	virtual	void				VolumeMounted(dev_t device);
142	virtual	void				VolumeUnmounted(dev_t device);
143};
144
145
146class NetworkAvailableEvent : public StickyEvent, public NetworkListener {
147public:
148								NetworkAvailableEvent(Event* parent,
149									const BMessage& args);
150
151	virtual	status_t			Register(EventRegistrator& registrator);
152	virtual	void				Unregister(EventRegistrator& registrator);
153
154	virtual	BString				ToString() const;
155
156	virtual	void				NetworkAvailabilityChanged(bool available);
157};
158
159
160static Event*
161create_event(Event* parent, const char* name, const BMessenger* target,
162	const BMessage& args)
163{
164	if (strcmp(name, "or") == 0) {
165		if (args.IsEmpty())
166			return NULL;
167
168		return new OrEvent(parent, target, args);
169	}
170
171	if (strcmp(name, "demand") == 0)
172		return new DemandEvent(parent);
173	if (strcmp(name, "file_created") == 0)
174		return new FileCreatedEvent(parent, args);
175	if (strcmp(name, "volume_mounted") == 0)
176		return new VolumeMountedEvent(parent, args);
177	if (strcmp(name, "network_available") == 0)
178		return new NetworkAvailableEvent(parent, args);
179
180	return new ExternalEvent(parent, name, args);
181}
182
183
184// #pragma mark -
185
186
187Event::Event(Event* parent)
188	:
189	fParent(parent),
190	fTriggered(false)
191{
192}
193
194
195Event::~Event()
196{
197}
198
199
200bool
201Event::Triggered() const
202{
203	return fTriggered;
204}
205
206
207void
208Event::Trigger(Event* origin)
209{
210	fTriggered = true;
211	if (fParent != NULL)
212		fParent->Trigger(origin);
213}
214
215
216void
217Event::ResetTrigger()
218{
219	fTriggered = false;
220}
221
222
223BaseJob*
224Event::Owner() const
225{
226	if (fParent != NULL)
227		return fParent->Owner();
228
229	return NULL;
230}
231
232
233void
234Event::SetOwner(BaseJob* owner)
235{
236	if (fParent != NULL)
237		fParent->SetOwner(owner);
238}
239
240
241Event*
242Event::Parent() const
243{
244	return fParent;
245}
246
247
248// #pragma mark -
249
250
251EventContainer::EventContainer(Event* parent, const BMessenger* target,
252	const BMessage& args)
253	:
254	Event(parent),
255	fEvents(5, true),
256	fRegistered(false)
257{
258	if (target != NULL)
259		fTarget = *target;
260
261	char* name;
262	type_code type;
263	int32 count;
264	for (int32 index = 0; args.GetInfo(B_MESSAGE_TYPE, index, &name, &type,
265			&count) == B_OK; index++) {
266		BMessage message;
267		for (int32 messageIndex = 0; args.FindMessage(name, messageIndex,
268				&message) == B_OK; messageIndex++) {
269			AddEvent(create_event(this, name, target, message));
270		}
271	}
272}
273
274
275EventContainer::EventContainer(BaseJob* owner, const BMessenger& target)
276	:
277	Event(NULL),
278	fOwner(owner),
279	fTarget(target),
280	fEvents(5, true),
281	fRegistered(false)
282{
283}
284
285
286void
287EventContainer::AddEvent(Event* event)
288{
289	if (event != NULL)
290		fEvents.AddItem(event);
291}
292
293
294BObjectList<Event>&
295EventContainer::Events()
296{
297	return fEvents;
298}
299
300
301const BMessenger&
302EventContainer::Target() const
303{
304	return fTarget;
305}
306
307
308status_t
309EventContainer::Register(EventRegistrator& registrator)
310{
311	if (fRegistered)
312		return B_OK;
313
314	int32 count = fEvents.CountItems();
315	for (int32 index = 0; index < count; index++) {
316		Event* event = fEvents.ItemAt(index);
317		status_t status = event->Register(registrator);
318		if (status != B_OK)
319			return status;
320	}
321
322	fRegistered = true;
323	return B_OK;
324}
325
326
327void
328EventContainer::Unregister(EventRegistrator& registrator)
329{
330	int32 count = fEvents.CountItems();
331	for (int32 index = 0; index < count; index++) {
332		Event* event = fEvents.ItemAt(index);
333		event->Unregister(registrator);
334	}
335}
336
337
338void
339EventContainer::Trigger(Event* origin)
340{
341	Event::Trigger(origin);
342
343	if (Parent() == NULL && Owner() != NULL) {
344		BMessage message(kMsgEventTriggered);
345		message.AddPointer("event", origin);
346		message.AddString("owner", Owner()->Name());
347		fTarget.SendMessage(&message);
348	}
349}
350
351
352BaseJob*
353EventContainer::Owner() const
354{
355	return fOwner;
356}
357
358
359void
360EventContainer::SetOwner(BaseJob* owner)
361{
362	Event::SetOwner(owner);
363	fOwner = owner;
364}
365
366
367void
368EventContainer::AddEventsToString(BString& string) const
369{
370	string += "[";
371
372	for (int32 index = 0; index < fEvents.CountItems(); index++) {
373		if (index != 0)
374			string += ", ";
375		string += fEvents.ItemAt(index)->ToString();
376	}
377	string += "]";
378}
379
380
381// #pragma mark - or
382
383
384OrEvent::OrEvent(Event* parent, const BMessenger* target, const BMessage& args)
385	:
386	EventContainer(parent, target, args)
387{
388}
389
390
391OrEvent::OrEvent(BaseJob* owner, const BMessenger& target)
392	:
393	EventContainer(owner, target)
394{
395}
396
397
398void
399OrEvent::ResetTrigger()
400{
401	fTriggered = false;
402
403	int32 count = fEvents.CountItems();
404	for (int32 index = 0; index < count; index++) {
405		Event* event = fEvents.ItemAt(index);
406		event->ResetTrigger();
407		fTriggered |= event->Triggered();
408	}
409}
410
411
412BString
413OrEvent::ToString() const
414{
415	BString string = "or ";
416	EventContainer::AddEventsToString(string);
417	return string;
418}
419
420
421// #pragma mark - StickyEvent
422
423
424StickyEvent::StickyEvent(Event* parent)
425	:
426	Event(parent)
427{
428}
429
430
431StickyEvent::~StickyEvent()
432{
433}
434
435
436void
437StickyEvent::ResetSticky()
438{
439	Event::ResetTrigger();
440}
441
442
443void
444StickyEvent::ResetTrigger()
445{
446	// This is a sticky event; we don't reset the trigger here
447}
448
449
450// #pragma mark - demand
451
452
453DemandEvent::DemandEvent(Event* parent)
454	:
455	Event(parent)
456{
457}
458
459
460status_t
461DemandEvent::Register(EventRegistrator& registrator)
462{
463	return B_OK;
464}
465
466
467void
468DemandEvent::Unregister(EventRegistrator& registrator)
469{
470}
471
472
473BString
474DemandEvent::ToString() const
475{
476	return "demand";
477}
478
479
480// #pragma mark - External event
481
482
483ExternalEvent::ExternalEvent(Event* parent, const char* name,
484	const BMessage& args)
485	:
486	Event(parent),
487	fName(name),
488	fFlags(0),
489	fResolved(false)
490{
491	const char* argument;
492	for (int32 index = 0; args.FindString("args", index, &argument) == B_OK;
493			index++) {
494		fArguments.Add(argument);
495	}
496}
497
498
499const BString&
500ExternalEvent::Name() const
501{
502	return fName;
503}
504
505
506bool
507ExternalEvent::Resolve(uint32 flags)
508{
509	if (fResolved)
510		return false;
511
512	fResolved = true;
513	fFlags = flags;
514	return true;
515}
516
517
518void
519ExternalEvent::ResetSticky()
520{
521	if ((fFlags & B_STICKY_EVENT) != 0)
522		Event::ResetTrigger();
523}
524
525
526void
527ExternalEvent::ResetTrigger()
528{
529	if ((fFlags & B_STICKY_EVENT) == 0)
530		Event::ResetTrigger();
531}
532
533
534status_t
535ExternalEvent::Register(EventRegistrator& registrator)
536{
537	return registrator.RegisterExternalEvent(this, Name().String(), fArguments);
538}
539
540
541void
542ExternalEvent::Unregister(EventRegistrator& registrator)
543{
544	registrator.UnregisterExternalEvent(this, Name().String());
545}
546
547
548BString
549ExternalEvent::ToString() const
550{
551	return fName;
552}
553
554
555// #pragma mark - file_created
556
557
558FileCreatedEvent::FileCreatedEvent(Event* parent, const BMessage& args)
559	:
560	Event(parent)
561{
562	fPath.SetTo(args.GetString("args", NULL));
563}
564
565
566status_t
567FileCreatedEvent::Register(EventRegistrator& registrator)
568{
569	// TODO: implement!
570	return B_ERROR;
571}
572
573
574void
575FileCreatedEvent::Unregister(EventRegistrator& registrator)
576{
577}
578
579
580BString
581FileCreatedEvent::ToString() const
582{
583	BString string = "file_created ";
584	string << fPath.Path();
585	return string;
586}
587
588
589// #pragma mark -
590
591
592VolumeMountedEvent::VolumeMountedEvent(Event* parent, const BMessage& args)
593	:
594	Event(parent)
595{
596}
597
598
599status_t
600VolumeMountedEvent::Register(EventRegistrator& registrator)
601{
602	VolumeWatcher::Register(this);
603	return B_OK;
604}
605
606
607void
608VolumeMountedEvent::Unregister(EventRegistrator& registrator)
609{
610	VolumeWatcher::Unregister(this);
611}
612
613
614BString
615VolumeMountedEvent::ToString() const
616{
617	return "volume_mounted";
618}
619
620
621void
622VolumeMountedEvent::VolumeMounted(dev_t device)
623{
624	Trigger(this);
625}
626
627
628void
629VolumeMountedEvent::VolumeUnmounted(dev_t device)
630{
631}
632
633
634// #pragma mark -
635
636
637NetworkAvailableEvent::NetworkAvailableEvent(Event* parent,
638	const BMessage& args)
639	:
640	StickyEvent(parent)
641{
642}
643
644
645status_t
646NetworkAvailableEvent::Register(EventRegistrator& registrator)
647{
648	NetworkWatcher::Register(this);
649	return B_OK;
650}
651
652
653void
654NetworkAvailableEvent::Unregister(EventRegistrator& registrator)
655{
656	NetworkWatcher::Unregister(this);
657}
658
659
660BString
661NetworkAvailableEvent::ToString() const
662{
663	return "network_available";
664}
665
666
667void
668NetworkAvailableEvent::NetworkAvailabilityChanged(bool available)
669{
670	if (available)
671		Trigger(this);
672	else
673		ResetSticky();
674}
675
676
677// #pragma mark -
678
679
680/*static*/ Event*
681Events::FromMessage(const BMessenger& target, const BMessage& message)
682{
683	return create_event(NULL, "or", &target, message);
684}
685
686
687/*static*/ Event*
688Events::AddOnDemand(const BMessenger& target, Event* event)
689{
690	OrEvent* orEvent = dynamic_cast<OrEvent*>(event);
691	if (orEvent == NULL) {
692		EventContainer* container = dynamic_cast<EventContainer*>(event);
693		if (container != NULL)
694			orEvent = new OrEvent(container->Owner(), container->Target());
695		else
696			orEvent = new OrEvent(NULL, target);
697	}
698	if (orEvent != event && event != NULL)
699		orEvent->AddEvent(event);
700
701	orEvent->AddEvent(new DemandEvent(orEvent));
702	return orEvent;
703}
704
705
706/*static*/ bool
707Events::ResolveExternalEvent(Event* event, const char* name, uint32 flags)
708{
709	if (event == NULL)
710		return false;
711
712	if (EventContainer* container = dynamic_cast<EventContainer*>(event)) {
713		for (int32 index = 0; index < container->Events().CountItems();
714				index++) {
715			Event* event = container->Events().ItemAt(index);
716			if (ExternalEvent* external = dynamic_cast<ExternalEvent*>(event)) {
717				if (external->Name() == name && external->Resolve(flags))
718					return true;
719			} else if (dynamic_cast<EventContainer*>(event) != NULL) {
720				if (ResolveExternalEvent(event, name, flags))
721					return true;
722			}
723		}
724	}
725	return false;
726}
727
728
729/*static*/ void
730Events::TriggerExternalEvent(Event* event, const char* name)
731{
732	if (event == NULL)
733		return;
734
735	if (EventContainer* container = dynamic_cast<EventContainer*>(event)) {
736		for (int32 index = 0; index < container->Events().CountItems();
737				index++) {
738			Event* event = container->Events().ItemAt(index);
739			if (ExternalEvent* external = dynamic_cast<ExternalEvent*>(event)) {
740				if (external->Name() == name) {
741					external->Trigger(container);
742					return;
743				}
744			} else if (dynamic_cast<EventContainer*>(event) != NULL) {
745				TriggerExternalEvent(event, name);
746			}
747		}
748	}
749	return;
750}
751
752
753/*static*/ void
754Events::ResetStickyExternalEvent(Event* event, const char* name)
755{
756	if (event == NULL)
757		return;
758
759	if (EventContainer* container = dynamic_cast<EventContainer*>(event)) {
760		for (int32 index = 0; index < container->Events().CountItems();
761				index++) {
762			Event* event = container->Events().ItemAt(index);
763			if (ExternalEvent* external = dynamic_cast<ExternalEvent*>(event)) {
764				if (external->Name() == name) {
765					external->ResetSticky();
766					return;
767				}
768			} else if (dynamic_cast<EventContainer*>(event) != NULL) {
769				ResetStickyExternalEvent(event, name);
770			}
771		}
772	}
773	return;
774}
775
776
777/*!	This will trigger a demand event, if it exists.
778
779	\param testOnly If \c true, the deman will not actually be triggered,
780			it will only be checked if it could.
781	\return \c true, if there is a demand event, and it has been
782			triggered by this call. \c false if not.
783*/
784/*static*/ bool
785Events::TriggerDemand(Event* event, bool testOnly)
786{
787	if (event == NULL || event->Triggered())
788		return false;
789
790	if (EventContainer* container = dynamic_cast<EventContainer*>(event)) {
791		for (int32 index = 0; index < container->Events().CountItems();
792				index++) {
793			Event* childEvent = container->Events().ItemAt(index);
794			if (dynamic_cast<DemandEvent*>(childEvent) != NULL) {
795				if (testOnly)
796					return true;
797
798				childEvent->Trigger(childEvent);
799				break;
800			}
801			if (dynamic_cast<EventContainer*>(childEvent) != NULL) {
802				if (TriggerDemand(childEvent, testOnly))
803					break;
804			}
805		}
806	}
807
808	return event->Triggered();
809}
810