xref: /haiku/src/servers/launch/Conditions.cpp (revision 97dfeb96704e5dbc5bec32ad7b21379d0125e031)
1 /*
2  * Copyright 2015, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "Conditions.h"
8 
9 #include <stdio.h>
10 
11 #include <Entry.h>
12 #include <File.h>
13 #include <ObjectList.h>
14 #include <Message.h>
15 #include <Path.h>
16 #include <StringList.h>
17 
18 #include "NetworkWatcher.h"
19 #include "Utility.h"
20 
21 
22 class ConditionContainer : public Condition {
23 protected:
24 								ConditionContainer(const BMessage& args);
25 								ConditionContainer();
26 
27 public:
28 			void				AddCondition(Condition* condition);
29 
30 	virtual	bool				IsConstant(ConditionContext& context) const;
31 
32 protected:
33 			void				AddConditionsToString(BString& string) const;
34 
35 protected:
36 			BObjectList<Condition> fConditions;
37 };
38 
39 
40 class AndCondition : public ConditionContainer {
41 public:
42 								AndCondition(const BMessage& args);
43 								AndCondition();
44 
45 	virtual	bool				Test(ConditionContext& context) const;
46 	virtual	BString				ToString() const;
47 };
48 
49 
50 class OrCondition : public ConditionContainer {
51 public:
52 								OrCondition(const BMessage& args);
53 
54 	virtual	bool				Test(ConditionContext& context) const;
55 	virtual	bool				IsConstant(ConditionContext& context) const;
56 
57 	virtual	BString				ToString() const;
58 };
59 
60 
61 class NotCondition : public ConditionContainer {
62 public:
63 								NotCondition(const BMessage& args);
64 								NotCondition();
65 
66 	virtual	bool				Test(ConditionContext& context) const;
67 	virtual	BString				ToString() const;
68 };
69 
70 
71 class SafeModeCondition : public Condition {
72 public:
73 	virtual	bool				Test(ConditionContext& context) const;
74 	virtual	bool				IsConstant(ConditionContext& context) const;
75 
76 	virtual	BString				ToString() const;
77 };
78 
79 
80 class ReadOnlyCondition : public Condition {
81 public:
82 								ReadOnlyCondition(const BMessage& args);
83 
84 	virtual	bool				Test(ConditionContext& context) const;
85 	virtual	bool				IsConstant(ConditionContext& context) const;
86 
87 	virtual	BString				ToString() const;
88 
89 private:
90 			BString				fPath;
91 	mutable	bool				fIsReadOnly;
92 	mutable	bool				fTestPerformed;
93 };
94 
95 
96 class FileExistsCondition : public Condition {
97 public:
98 								FileExistsCondition(const BMessage& args);
99 
100 	virtual	bool				Test(ConditionContext& context) const;
101 	virtual	BString				ToString() const;
102 
103 private:
104 			BStringList			fPaths;
105 };
106 
107 
108 class NetworkAvailableCondition : public Condition {
109 public:
110 	virtual	bool				Test(ConditionContext& context) const;
111 	virtual	bool				IsConstant(ConditionContext& context) const;
112 
113 	virtual	BString				ToString() const;
114 };
115 
116 
117 class SettingCondition : public Condition {
118 public:
119 								SettingCondition(const BMessage& args);
120 
121 	virtual	bool				Test(ConditionContext& context) const;
122 
123 	virtual	BString				ToString() const;
124 
125 private:
126 			BPath				fPath;
127 			BString				fField;
128 			BString				fValue;
129 };
130 
131 
132 static Condition*
133 create_condition(const char* name, const BMessage& args)
134 {
135 	if (strcmp(name, "and") == 0 && !args.IsEmpty())
136 		return new AndCondition(args);
137 	if (strcmp(name, "or") == 0 && !args.IsEmpty())
138 		return new OrCondition(args);
139 	if (strcmp(name, "not") == 0 && !args.IsEmpty())
140 		return new NotCondition(args);
141 
142 	if (strcmp(name, "safemode") == 0)
143 		return new SafeModeCondition();
144 	if (strcmp(name, "read_only") == 0)
145 		return new ReadOnlyCondition(args);
146 	if (strcmp(name, "file_exists") == 0)
147 		return new FileExistsCondition(args);
148 	if (strcmp(name, "network_available") == 0)
149 		return new NetworkAvailableCondition();
150 	if (strcmp(name, "setting") == 0)
151 		return new SettingCondition(args);
152 
153 	return NULL;
154 }
155 
156 
157 // #pragma mark -
158 
159 
160 Condition::Condition()
161 {
162 }
163 
164 
165 Condition::~Condition()
166 {
167 }
168 
169 
170 bool
171 Condition::IsConstant(ConditionContext& context) const
172 {
173 	return false;
174 }
175 
176 
177 // #pragma mark -
178 
179 
180 ConditionContainer::ConditionContainer(const BMessage& args)
181 	:
182 	fConditions(10, true)
183 {
184 	char* name;
185 	type_code type;
186 	int32 count;
187 	for (int32 index = 0; args.GetInfo(B_MESSAGE_TYPE, index, &name, &type,
188 			&count) == B_OK; index++) {
189 		BMessage message;
190 		for (int32 messageIndex = 0; args.FindMessage(name, messageIndex,
191 				&message) == B_OK; messageIndex++) {
192 			AddCondition(create_condition(name, message));
193 		}
194 	}
195 }
196 
197 
198 ConditionContainer::ConditionContainer()
199 	:
200 	fConditions(10, true)
201 {
202 }
203 
204 
205 void
206 ConditionContainer::AddCondition(Condition* condition)
207 {
208 	if (condition != NULL)
209 		fConditions.AddItem(condition);
210 }
211 
212 
213 /*!	A single constant failing condition makes this constant, too, otherwise,
214 	a single non-constant condition makes this non-constant as well.
215 */
216 bool
217 ConditionContainer::IsConstant(ConditionContext& context) const
218 {
219 	bool fixed = true;
220 	for (int32 index = 0; index < fConditions.CountItems(); index++) {
221 		const Condition* condition = fConditions.ItemAt(index);
222 		if (condition->IsConstant(context)) {
223 			if (!condition->Test(context))
224 				return true;
225 		} else
226 			fixed = false;
227 	}
228 	return fixed;
229 }
230 
231 
232 void
233 ConditionContainer::AddConditionsToString(BString& string) const
234 {
235 	string += "[";
236 
237 	for (int32 index = 0; index < fConditions.CountItems(); index++) {
238 		if (index != 0)
239 			string += ", ";
240 		string += fConditions.ItemAt(index)->ToString();
241 	}
242 	string += "]";
243 }
244 
245 
246 // #pragma mark - and
247 
248 
249 AndCondition::AndCondition(const BMessage& args)
250 	:
251 	ConditionContainer(args)
252 {
253 }
254 
255 
256 AndCondition::AndCondition()
257 {
258 }
259 
260 
261 bool
262 AndCondition::Test(ConditionContext& context) const
263 {
264 	for (int32 index = 0; index < fConditions.CountItems(); index++) {
265 		Condition* condition = fConditions.ItemAt(index);
266 		if (!condition->Test(context))
267 			return false;
268 	}
269 	return true;
270 }
271 
272 
273 BString
274 AndCondition::ToString() const
275 {
276 	BString string = "and ";
277 	ConditionContainer::AddConditionsToString(string);
278 	return string;
279 }
280 
281 
282 // #pragma mark - or
283 
284 
285 OrCondition::OrCondition(const BMessage& args)
286 	:
287 	ConditionContainer(args)
288 {
289 }
290 
291 
292 bool
293 OrCondition::Test(ConditionContext& context) const
294 {
295 	if (fConditions.IsEmpty())
296 		return true;
297 
298 	for (int32 index = 0; index < fConditions.CountItems(); index++) {
299 		Condition* condition = fConditions.ItemAt(index);
300 		if (condition->Test(context))
301 			return true;
302 	}
303 	return false;
304 }
305 
306 
307 /*!	If there is a single succeeding constant condition, this is constant, too.
308 	Otherwise, it is non-constant if there is a single non-constant condition.
309 */
310 bool
311 OrCondition::IsConstant(ConditionContext& context) const
312 {
313 	bool fixed = true;
314 	for (int32 index = 0; index < fConditions.CountItems(); index++) {
315 		const Condition* condition = fConditions.ItemAt(index);
316 		if (condition->IsConstant(context)) {
317 			if (condition->Test(context))
318 				return true;
319 		} else
320 			fixed = false;
321 	}
322 	return fixed;
323 }
324 
325 
326 BString
327 OrCondition::ToString() const
328 {
329 	BString string = "or ";
330 	ConditionContainer::AddConditionsToString(string);
331 	return string;
332 }
333 
334 
335 // #pragma mark - or
336 
337 
338 NotCondition::NotCondition(const BMessage& args)
339 	:
340 	ConditionContainer(args)
341 {
342 }
343 
344 
345 NotCondition::NotCondition()
346 {
347 }
348 
349 
350 bool
351 NotCondition::Test(ConditionContext& context) const
352 {
353 	for (int32 index = 0; index < fConditions.CountItems(); index++) {
354 		Condition* condition = fConditions.ItemAt(index);
355 		if (condition->Test(context))
356 			return false;
357 	}
358 	return true;
359 }
360 
361 
362 BString
363 NotCondition::ToString() const
364 {
365 	BString string = "not ";
366 	ConditionContainer::AddConditionsToString(string);
367 	return string;
368 }
369 
370 
371 // #pragma mark - safemode
372 
373 
374 bool
375 SafeModeCondition::Test(ConditionContext& context) const
376 {
377 	return context.IsSafeMode();
378 }
379 
380 
381 bool
382 SafeModeCondition::IsConstant(ConditionContext& context) const
383 {
384 	return true;
385 }
386 
387 
388 BString
389 SafeModeCondition::ToString() const
390 {
391 	return "safemode";
392 }
393 
394 
395 // #pragma mark - read_only
396 
397 
398 ReadOnlyCondition::ReadOnlyCondition(const BMessage& args)
399 	:
400 	fPath(args.GetString("args")),
401 	fIsReadOnly(false),
402 	fTestPerformed(false)
403 {
404 }
405 
406 
407 bool
408 ReadOnlyCondition::Test(ConditionContext& context) const
409 {
410 	if (fTestPerformed)
411 		return fIsReadOnly;
412 
413 	if (fPath.IsEmpty() || fPath == "/boot")
414 		fIsReadOnly = context.BootVolumeIsReadOnly();
415 	else
416 		fIsReadOnly = Utility::IsReadOnlyVolume(fPath);
417 
418 	fTestPerformed = true;
419 
420 	return fIsReadOnly;
421 }
422 
423 
424 bool
425 ReadOnlyCondition::IsConstant(ConditionContext& context) const
426 {
427 	return true;
428 }
429 
430 
431 BString
432 ReadOnlyCondition::ToString() const
433 {
434 	BString string = "readonly ";
435 	string << fPath;
436 	return string;
437 }
438 
439 
440 // #pragma mark - file_exists
441 
442 
443 FileExistsCondition::FileExistsCondition(const BMessage& args)
444 {
445 	for (int32 index = 0;
446 			const char* path = args.GetString("args", index, NULL); index++) {
447 		fPaths.Add(Utility::TranslatePath(path));
448 	}
449 }
450 
451 
452 bool
453 FileExistsCondition::Test(ConditionContext& context) const
454 {
455 	for (int32 index = 0; index < fPaths.CountStrings(); index++) {
456 		BEntry entry;
457 		if (entry.SetTo(fPaths.StringAt(index)) != B_OK
458 			|| !entry.Exists())
459 			return false;
460 	}
461 	return true;
462 }
463 
464 
465 BString
466 FileExistsCondition::ToString() const
467 {
468 	BString string = "file_exists [";
469 	for (int32 index = 0; index < fPaths.CountStrings(); index++) {
470 		if (index != 0)
471 			string << ", ";
472 		string << fPaths.StringAt(index);
473 	}
474 	string += "]";
475 	return string;
476 }
477 
478 
479 // #pragma mark - network_available
480 
481 
482 bool
483 NetworkAvailableCondition::Test(ConditionContext& context) const
484 {
485 	return NetworkWatcher::NetworkAvailable(false);
486 }
487 
488 
489 bool
490 NetworkAvailableCondition::IsConstant(ConditionContext& context) const
491 {
492 	return false;
493 }
494 
495 
496 BString
497 NetworkAvailableCondition::ToString() const
498 {
499 	return "network_available";
500 }
501 
502 
503 // #pragma mark - setting
504 
505 
506 SettingCondition::SettingCondition(const BMessage& args)
507 {
508 	fPath.SetTo(Utility::TranslatePath(args.GetString("args", 0, NULL)));
509 	fField = args.GetString("args", 1, NULL);
510 	fValue = args.GetString("args", 2, NULL);
511 }
512 
513 
514 bool
515 SettingCondition::Test(ConditionContext& context) const
516 {
517 	BFile file(fPath.Path(), B_READ_ONLY);
518 	if (file.InitCheck() != B_OK)
519 		return false;
520 
521 	BMessage settings;
522 	if (settings.Unflatten(&file) == B_OK) {
523 		type_code type;
524 		int32 count;
525 		if (settings.GetInfo(fField, &type, &count) == B_OK) {
526 			switch (type) {
527 				case B_BOOL_TYPE:
528 				{
529 					bool value = settings.GetBool(fField);
530 					bool expect = fValue.IsEmpty();
531 					if (!expect) {
532 						expect = fValue == "true" || fValue == "yes"
533 							|| fValue == "on" || fValue == "1";
534 					}
535 					return value == expect;
536 				}
537 				case B_STRING_TYPE:
538 				{
539 					BString value = settings.GetString(fField);
540 					if (fValue.IsEmpty() && !value.IsEmpty())
541 						return true;
542 
543 					return fValue == value;
544 				}
545 			}
546 		}
547 	}
548 	// TODO: check for driver settings, too?
549 
550 	return false;
551 }
552 
553 
554 BString
555 SettingCondition::ToString() const
556 {
557 	BString string = "setting file ";
558 	string << fPath.Path() << ", field " << fField;
559 	if (!fValue.IsEmpty())
560 		string << ", value " << fValue;
561 
562 	return string;
563 }
564 
565 
566 // #pragma mark -
567 
568 
569 /*static*/ Condition*
570 Conditions::FromMessage(const BMessage& message)
571 {
572 	return create_condition("and", message);
573 }
574 
575 
576 /*static*/ Condition*
577 Conditions::AddNotSafeMode(Condition* condition)
578 {
579 	AndCondition* andCondition = dynamic_cast<AndCondition*>(condition);
580 	if (andCondition == NULL)
581 		andCondition = new AndCondition();
582 	if (andCondition != condition && condition != NULL)
583 		andCondition->AddCondition(condition);
584 
585 	NotCondition* notCondition = new NotCondition();
586 	notCondition->AddCondition(new SafeModeCondition());
587 
588 	andCondition->AddCondition(notCondition);
589 	return andCondition;
590 }
591