xref: /haiku/src/servers/launch/LaunchDaemon.cpp (revision c086a1834bb7041ca9f64abfee7a3b3bb9f06aa9)
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 <map>
8 #include <set>
9 
10 #include <errno.h>
11 #include <grp.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 
16 #include <Directory.h>
17 #include <driver_settings.h>
18 #include <Entry.h>
19 #include <File.h>
20 #include <ObjectList.h>
21 #include <Path.h>
22 #include <PathFinder.h>
23 #include <Server.h>
24 
25 #include <AppMisc.h>
26 #include <DriverSettingsMessageAdapter.h>
27 #include <LaunchDaemonDefs.h>
28 #include <LaunchRosterPrivate.h>
29 #include <syscalls.h>
30 
31 #include "multiuser_utils.h"
32 
33 #include "InitRealTimeClockJob.h"
34 #include "InitSharedMemoryDirectoryJob.h"
35 #include "InitTemporaryDirectoryJob.h"
36 #include "Worker.h"
37 
38 
39 using namespace ::BPrivate;
40 using namespace BSupportKit;
41 using BSupportKit::BPrivate::JobQueue;
42 
43 
44 static const char* kLaunchDirectory = "launch";
45 
46 
47 const static settings_template kPortTemplate[] = {
48 	{B_STRING_TYPE, "name", NULL, true},
49 	{B_INT32_TYPE, "capacity", NULL},
50 };
51 
52 const static settings_template kJobTemplate[] = {
53 	{B_STRING_TYPE, "name", NULL, true},
54 	{B_BOOL_TYPE, "disabled", NULL},
55 	{B_STRING_TYPE, "launch", NULL},
56 	{B_STRING_TYPE, "requires", NULL},
57 	{B_BOOL_TYPE, "legacy", NULL},
58 	{B_MESSAGE_TYPE, "port", kPortTemplate},
59 	{B_BOOL_TYPE, "no_safemode", NULL},
60 	{0, NULL, NULL}
61 };
62 
63 const static settings_template kTargetTemplate[] = {
64 	{B_STRING_TYPE, "name", NULL, true},
65 	{B_BOOL_TYPE, "reset", NULL},
66 	{B_MESSAGE_TYPE, "job", kJobTemplate},
67 	{B_MESSAGE_TYPE, "service", kJobTemplate},
68 	{0, NULL, NULL}
69 };
70 
71 const static settings_template kSettingsTemplate[] = {
72 	{B_MESSAGE_TYPE, "target", kTargetTemplate},
73 	{B_MESSAGE_TYPE, "job", kJobTemplate},
74 	{B_MESSAGE_TYPE, "service", kJobTemplate},
75 	{0, NULL, NULL}
76 };
77 
78 
79 typedef std::map<BString, BMessage> PortMap;
80 
81 
82 class Session {
83 public:
84 								Session(uid_t user, const BMessenger& target);
85 
86 			uid_t				User() const
87 									{ return fUser; }
88 			const BMessenger&	Daemon() const
89 									{ return fDaemon; }
90 
91 private:
92 			uid_t				fUser;
93 			BMessenger			fDaemon;
94 };
95 
96 
97 class Target : public BJob {
98 public:
99 								Target(const char* name);
100 
101 			const char*			Name() const;
102 
103 			status_t			AddData(const char* name, BMessage& data);
104 			const BMessage&		Data() const
105 									{ return fData; }
106 
107 protected:
108 	virtual	status_t			Execute();
109 
110 private:
111 			BMessage			fData;
112 };
113 
114 
115 class Finder;
116 
117 
118 class Job : public BJob {
119 public:
120 								Job(const char* name);
121 								Job(const Job& other);
122 	virtual						~Job();
123 
124 			const char*			Name() const;
125 
126 			bool				IsEnabled() const;
127 			void				SetEnabled(bool enable);
128 
129 			bool				IsService() const;
130 			void				SetService(bool service);
131 
132 			bool				CreateDefaultPort() const;
133 			void				SetCreateDefaultPort(bool createPort);
134 
135 			void				AddPort(BMessage& data);
136 
137 			bool				LaunchInSafeMode() const;
138 			void				SetLaunchInSafeMode(bool launch);
139 
140 			const BStringList&	Arguments() const;
141 			BStringList&		Arguments();
142 			void				AddArgument(const char* argument);
143 
144 			::Target*			Target() const;
145 			void				SetTarget(::Target* target);
146 
147 			const BStringList&	Requirements() const;
148 			BStringList&		Requirements();
149 			void				AddRequirement(const char* requirement);
150 
151 			status_t			Init(const Finder& jobs,
152 									std::set<BString>& dependencies);
153 			status_t			InitCheck() const;
154 
155 			team_id				Team() const;
156 
157 			const PortMap&		Ports() const;
158 			port_id				Port(const char* name = NULL) const;
159 
160 			status_t			Launch();
161 			bool				IsLaunched() const;
162 
163 protected:
164 	virtual	status_t			Execute();
165 
166 private:
167 			Job&				operator=(const Job& other);
168 			void				_DeletePorts();
169 			status_t			_AddRequirement(BJob* dependency);
170 
171 private:
172 			BStringList			fArguments;
173 			BStringList			fRequirements;
174 			bool				fEnabled;
175 			bool				fService;
176 			bool				fCreateDefaultPort;
177 			bool				fLaunchInSafeMode;
178 			PortMap				fPortMap;
179 			status_t			fInitStatus;
180 			team_id				fTeam;
181 			::Target*			fTarget;
182 };
183 
184 
185 class Finder {
186 public:
187 	virtual	Job*				FindJob(const char* name) const = 0;
188 	virtual	Target*				FindTarget(const char* name) const = 0;
189 };
190 
191 
192 typedef std::map<BString, Job*> JobMap;
193 typedef std::map<uid_t, Session*> SessionMap;
194 typedef std::map<BString, Target*> TargetMap;
195 
196 
197 class LaunchDaemon : public BServer, public Finder {
198 public:
199 								LaunchDaemon(bool userMode, status_t& error);
200 	virtual						~LaunchDaemon();
201 
202 	virtual	Job*				FindJob(const char* name) const;
203 	virtual	Target*				FindTarget(const char* name) const;
204 			Session*			FindSession(uid_t user) const;
205 
206 	virtual	void				ReadyToRun();
207 	virtual	void				MessageReceived(BMessage* message);
208 
209 private:
210 			uid_t				_GetUserID(BMessage* message);
211 
212 			void				_ReadPaths(const BStringList& paths);
213 			void				_ReadEntry(const char* context, BEntry& entry);
214 			void				_ReadDirectory(const char* context,
215 									BEntry& directory);
216 			status_t			_ReadFile(const char* context, BEntry& entry);
217 
218 			void				_AddJobs(Target* target, BMessage& message);
219 			void				_AddJob(Target* target, bool service,
220 									BMessage& message);
221 			void				_InitJobs();
222 			void				_LaunchJobs(Target* target);
223 			void				_AddLaunchJob(Job* job);
224 			void				_AddTarget(Target* target);
225 
226 			status_t			_StartSession(const char* login,
227 									const char* password);
228 
229 			void				_RetrieveKernelOptions();
230 			void				_SetupEnvironment();
231 			void				_InitSystem();
232 			void				_AddInitJob(BJob* job);
233 
234 			bool				_IsSafeMode() const;
235 
236 private:
237 			JobMap				fJobs;
238 			TargetMap			fTargets;
239 			JobQueue			fJobQueue;
240 			SessionMap			fSessions;
241 			MainWorker*			fMainWorker;
242 			Target*				fInitTarget;
243 			bool				fSafeMode;
244 			bool				fUserMode;
245 };
246 
247 
248 static const char*
249 get_leaf(const char* signature)
250 {
251 	const char* separator = strrchr(signature, '/');
252 	if (separator != NULL)
253 		return separator + 1;
254 
255 	return signature;
256 }
257 
258 
259 // #pragma mark -
260 
261 
262 Job::Job(const char* name)
263 	:
264 	BJob(name),
265 	fEnabled(true),
266 	fService(false),
267 	fCreateDefaultPort(false),
268 	fLaunchInSafeMode(true),
269 	fInitStatus(B_NO_INIT),
270 	fTeam(-1),
271 	fTarget(NULL)
272 {
273 }
274 
275 
276 Job::Job(const Job& other)
277 	:
278 	BJob(other.Name()),
279 	fEnabled(other.IsEnabled()),
280 	fService(other.IsService()),
281 	fCreateDefaultPort(other.CreateDefaultPort()),
282 	fLaunchInSafeMode(other.LaunchInSafeMode()),
283 	fInitStatus(B_NO_INIT),
284 	fTeam(-1),
285 	fTarget(other.Target())
286 {
287 	for (int32 i = 0; i < other.Arguments().CountStrings(); i++)
288 		AddArgument(other.Arguments().StringAt(i));
289 
290 	for (int32 i = 0; i < other.Requirements().CountStrings(); i++)
291 		AddRequirement(other.Requirements().StringAt(i));
292 
293 	PortMap::const_iterator constIterator = other.Ports().begin();
294 	for (; constIterator != other.Ports().end(); constIterator++) {
295 		fPortMap.insert(
296 			std::make_pair(constIterator->first, constIterator->second));
297 	}
298 
299 	PortMap::iterator iterator = fPortMap.begin();
300 	for (; iterator != fPortMap.end(); iterator++)
301 		iterator->second.RemoveData("port");
302 }
303 
304 
305 Job::~Job()
306 {
307 	_DeletePorts();
308 }
309 
310 
311 const char*
312 Job::Name() const
313 {
314 	return Title().String();
315 }
316 
317 
318 bool
319 Job::IsEnabled() const
320 {
321 	return fEnabled;
322 }
323 
324 
325 void
326 Job::SetEnabled(bool enable)
327 {
328 	fEnabled = enable;
329 }
330 
331 
332 bool
333 Job::IsService() const
334 {
335 	return fService;
336 }
337 
338 
339 void
340 Job::SetService(bool service)
341 {
342 	fService = service;
343 }
344 
345 
346 bool
347 Job::CreateDefaultPort() const
348 {
349 	return fCreateDefaultPort;
350 }
351 
352 
353 void
354 Job::SetCreateDefaultPort(bool createPort)
355 {
356 	fCreateDefaultPort = createPort;
357 }
358 
359 
360 void
361 Job::AddPort(BMessage& data)
362 {
363 	const char* name = data.GetString("name");
364 	fPortMap.insert(std::pair<BString, BMessage>(BString(name), data));
365 }
366 
367 
368 bool
369 Job::LaunchInSafeMode() const
370 {
371 	return fLaunchInSafeMode;
372 }
373 
374 
375 void
376 Job::SetLaunchInSafeMode(bool launch)
377 {
378 	fLaunchInSafeMode = launch;
379 }
380 
381 
382 const BStringList&
383 Job::Arguments() const
384 {
385 	return fArguments;
386 }
387 
388 
389 BStringList&
390 Job::Arguments()
391 {
392 	return fArguments;
393 }
394 
395 
396 void
397 Job::AddArgument(const char* argument)
398 {
399 	fArguments.Add(argument);
400 }
401 
402 
403 ::Target*
404 Job::Target() const
405 {
406 	return fTarget;
407 }
408 
409 
410 void
411 Job::SetTarget(::Target* target)
412 {
413 	fTarget = target;
414 }
415 
416 
417 const BStringList&
418 Job::Requirements() const
419 {
420 	return fRequirements;
421 }
422 
423 
424 BStringList&
425 Job::Requirements()
426 {
427 	return fRequirements;
428 }
429 
430 
431 void
432 Job::AddRequirement(const char* requirement)
433 {
434 	fRequirements.Add(requirement);
435 }
436 
437 
438 status_t
439 Job::Init(const Finder& finder, std::set<BString>& dependencies)
440 {
441 	// Only initialize the jobs once
442 	if (fInitStatus != B_NO_INIT)
443 		return fInitStatus;
444 
445 	fInitStatus = B_OK;
446 
447 	if (fTarget != NULL)
448 		fTarget->AddDependency(this);
449 
450 	// Check dependencies
451 
452 	for (int32 index = 0; index < Requirements().CountStrings(); index++) {
453 		const BString& requires = Requirements().StringAt(index);
454 		if (dependencies.find(requires) != dependencies.end()) {
455 			// Found a cyclic dependency
456 			// TODO: log error
457 			return fInitStatus = B_ERROR;
458 		}
459 		dependencies.insert(requires);
460 
461 		Job* dependency = finder.FindJob(requires);
462 		if (dependency != NULL) {
463 			std::set<BString> subDependencies = dependencies;
464 
465 			fInitStatus = dependency->Init(finder, subDependencies);
466 			if (fInitStatus != B_OK) {
467 				// TODO: log error
468 				return fInitStatus;
469 			}
470 
471 			fInitStatus = _AddRequirement(dependency);
472 		} else {
473 			::Target* target = finder.FindTarget(requires);
474 			if (target != NULL)
475 				fInitStatus = _AddRequirement(dependency);
476 			else {
477 				// Could not find dependency
478 				fInitStatus = B_NAME_NOT_FOUND;
479 			}
480 		}
481 		if (fInitStatus != B_OK) {
482 			// TODO: log error
483 			return fInitStatus;
484 		}
485 	}
486 
487 	// Create ports
488 	// TODO: prefix system ports with "system:"
489 
490 	bool defaultPort = false;
491 
492 	for (PortMap::iterator iterator = fPortMap.begin();
493 			iterator != fPortMap.end(); iterator++) {
494 		BString name(Name());
495 		const char* suffix = iterator->second.GetString("name");
496 		if (suffix != NULL)
497 			name << ':' << suffix;
498 		else
499 			defaultPort = true;
500 
501 		const int32 capacity = iterator->second.GetInt32("capacity",
502 			B_LOOPER_PORT_DEFAULT_CAPACITY);
503 
504 		port_id port = create_port(capacity, name.String());
505 		if (port < 0) {
506 			fInitStatus = port;
507 			break;
508 		}
509 		iterator->second.SetInt32("port", port);
510 	}
511 
512 	if (fInitStatus == B_OK && fCreateDefaultPort && !defaultPort) {
513 		BMessage data;
514 		data.AddInt32("capacity", B_LOOPER_PORT_DEFAULT_CAPACITY);
515 
516 		port_id port = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, Name());
517 		if (port < 0) {
518 			// TODO: log error
519 			fInitStatus = port;
520 		} else {
521 			data.SetInt32("port", port);
522 			AddPort(data);
523 		}
524 	}
525 
526 	return fInitStatus;
527 }
528 
529 
530 status_t
531 Job::InitCheck() const
532 {
533 	return fInitStatus;
534 }
535 
536 
537 team_id
538 Job::Team() const
539 {
540 	return fTeam;
541 }
542 
543 
544 const PortMap&
545 Job::Ports() const
546 {
547 	return fPortMap;
548 }
549 
550 
551 port_id
552 Job::Port(const char* name) const
553 {
554 	PortMap::const_iterator found = fPortMap.find(name);
555 	if (found != fPortMap.end())
556 		return found->second.GetInt32("port", -1);
557 
558 	return B_NAME_NOT_FOUND;
559 }
560 
561 
562 status_t
563 Job::Launch()
564 {
565 	if (fArguments.IsEmpty()) {
566 		// TODO: Launch via signature
567 		// We cannot use the BRoster here as it tries to pre-register
568 		// the application.
569 		BString signature("application/");
570 		signature << Name();
571 		return B_NOT_SUPPORTED;
572 		//return be_roster->Launch(signature.String(), (BMessage*)NULL, &fTeam);
573 	}
574 
575 	entry_ref ref;
576 	status_t status = get_ref_for_path(fArguments.StringAt(0).String(), &ref);
577 	if (status != B_OK)
578 		return status;
579 
580 	size_t count = fArguments.CountStrings();
581 	const char* args[count + 1];
582 	for (int32 i = 0; i < fArguments.CountStrings(); i++) {
583 		args[i] = fArguments.StringAt(i);
584 	}
585 	args[count] = NULL;
586 
587 	thread_id thread = load_image(count, args,
588 		const_cast<const char**>(environ));
589 	if (thread >= 0)
590 		resume_thread(thread);
591 
592 	thread_info info;
593 	if (get_thread_info(thread, &info) == B_OK)
594 		fTeam = info.team;
595 	return B_OK;
596 //	return be_roster->Launch(&ref, count, args, &fTeam);
597 }
598 
599 
600 bool
601 Job::IsLaunched() const
602 {
603 	return fTeam >= 0;
604 }
605 
606 
607 status_t
608 Job::Execute()
609 {
610 	if (!IsLaunched())
611 		return Launch();
612 
613 	return B_OK;
614 }
615 
616 
617 void
618 Job::_DeletePorts()
619 {
620 	PortMap::const_iterator iterator = Ports().begin();
621 	for (; iterator != Ports().end(); iterator++) {
622 		port_id port = iterator->second.GetInt32("port", -1);
623 		if (port >= 0)
624 			delete_port(port);
625 	}
626 }
627 
628 
629 status_t
630 Job::_AddRequirement(BJob* dependency)
631 {
632 	if (dependency == NULL)
633 		return B_OK;
634 
635 	switch (dependency->State()) {
636 		case B_JOB_STATE_WAITING_TO_RUN:
637 		case B_JOB_STATE_STARTED:
638 		case B_JOB_STATE_IN_PROGRESS:
639 			AddDependency(dependency);
640 			break;
641 
642 		case B_JOB_STATE_SUCCEEDED:
643 			// Just queue it without any dependencies
644 			break;
645 
646 		case B_JOB_STATE_FAILED:
647 		case B_JOB_STATE_ABORTED:
648 			// TODO: return appropriate error
649 			return B_BAD_VALUE;
650 	}
651 
652 	return B_OK;
653 }
654 
655 
656 // #pragma mark -
657 
658 
659 Session::Session(uid_t user, const BMessenger& daemon)
660 	:
661 	fUser(user),
662 	fDaemon(daemon)
663 {
664 }
665 
666 
667 // #pragma mark -
668 
669 
670 Target::Target(const char* name)
671 	:
672 	BJob(name)
673 {
674 }
675 
676 
677 const char*
678 Target::Name() const
679 {
680 	return Title().String();
681 }
682 
683 
684 status_t
685 Target::AddData(const char* name, BMessage& data)
686 {
687 	return fData.AddMessage(name, &data);
688 }
689 
690 
691 status_t
692 Target::Execute()
693 {
694 	return B_OK;
695 }
696 
697 
698 // #pragma mark -
699 
700 
701 LaunchDaemon::LaunchDaemon(bool userMode, status_t& error)
702 	:
703 	BServer(kLaunchDaemonSignature, NULL,
704 		create_port(B_LOOPER_PORT_DEFAULT_CAPACITY,
705 			userMode ? "AppPort" : B_LAUNCH_DAEMON_PORT_NAME), false, &error),
706 	fInitTarget(userMode ? NULL : new Target("init")),
707 	fUserMode(userMode)
708 {
709 	fMainWorker = new MainWorker(fJobQueue);
710 	if (fInitTarget != NULL)
711 		_AddTarget(fInitTarget);
712 }
713 
714 
715 LaunchDaemon::~LaunchDaemon()
716 {
717 }
718 
719 
720 Job*
721 LaunchDaemon::FindJob(const char* name) const
722 {
723 	if (name == NULL)
724 		return NULL;
725 
726 	JobMap::const_iterator found = fJobs.find(BString(name).ToLower());
727 	if (found != fJobs.end())
728 		return found->second;
729 
730 	return NULL;
731 }
732 
733 
734 Target*
735 LaunchDaemon::FindTarget(const char* name) const
736 {
737 	if (name == NULL)
738 		return NULL;
739 
740 	TargetMap::const_iterator found = fTargets.find(BString(name).ToLower());
741 	if (found != fTargets.end())
742 		return found->second;
743 
744 	return NULL;
745 }
746 
747 
748 Session*
749 LaunchDaemon::FindSession(uid_t user) const
750 {
751 	SessionMap::const_iterator found = fSessions.find(user);
752 	if (found != fSessions.end())
753 		return found->second;
754 
755 	return NULL;
756 }
757 
758 
759 void
760 LaunchDaemon::ReadyToRun()
761 {
762 	_RetrieveKernelOptions();
763 	_SetupEnvironment();
764 	if (fUserMode) {
765 		BLaunchRoster roster;
766 		BLaunchRoster::Private(roster).RegisterSessionDaemon(this);
767 	} else
768 		_InitSystem();
769 
770 	BStringList paths;
771 	BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY, kLaunchDirectory,
772 		fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths);
773 	_ReadPaths(paths);
774 
775 	BPathFinder::FindPaths(B_FIND_PATH_SETTINGS_DIRECTORY, kLaunchDirectory,
776 		fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths);
777 	_ReadPaths(paths);
778 
779 	_InitJobs();
780 	_LaunchJobs(NULL);
781 }
782 
783 
784 void
785 LaunchDaemon::MessageReceived(BMessage* message)
786 {
787 	switch (message->what) {
788 		case B_GET_LAUNCH_DATA:
789 		{
790 			uid_t user = _GetUserID(message);
791 			if (user < 0)
792 				return;
793 
794 			BMessage reply((uint32)B_OK);
795 			Job* job = FindJob(get_leaf(message->GetString("name")));
796 			if (job == NULL) {
797 				Session* session = FindSession(user);
798 				if (session != NULL) {
799 					// Forward request to user launch_daemon
800 					if (session->Daemon().SendMessage(message) == B_OK)
801 						break;
802 				}
803 				reply.what = B_NAME_NOT_FOUND;
804 			} else {
805 				// If the job has not been launched yet, we'll pass on our
806 				// team here. The rationale behind this is that this team
807 				// will temporarily own the synchronous reply ports.
808 				reply.AddInt32("team", job->Team() < 0
809 					? current_team() : job->Team());
810 
811 				PortMap::const_iterator iterator = job->Ports().begin();
812 				for (; iterator != job->Ports().end(); iterator++) {
813 					BString name;
814 					if (iterator->second.HasString("name"))
815 						name << iterator->second.GetString("name") << "_";
816 					name << "port";
817 
818 					reply.AddInt32(name.String(),
819 						iterator->second.GetInt32("port", -1));
820 				}
821 
822 				_AddLaunchJob(job);
823 			}
824 			message->SendReply(&reply);
825 			break;
826 		}
827 
828 		case B_LAUNCH_TARGET:
829 		{
830 			uid_t user = _GetUserID(message);
831 			if (user < 0)
832 				break;
833 
834 			const char* name = message->GetString("target");
835 			const char* baseName = message->GetString("base target");
836 
837 			Target* target = FindTarget(name);
838 			if (target == NULL) {
839 				Target* baseTarget = FindTarget(baseName);
840 				if (baseTarget != NULL) {
841 					target = new Target(name);
842 
843 					// Copy all jobs with the base target into the new target
844 					for (JobMap::iterator iterator = fJobs.begin();
845 							iterator != fJobs.end();) {
846 						Job* job = iterator->second;
847 						iterator++;
848 
849 						if (job->Target() == baseTarget) {
850 							Job* copy = new Job(*job);
851 							copy->SetTarget(target);
852 
853 							fJobs.insert(std::make_pair(copy->Name(), copy));
854 						}
855 					}
856 				}
857 			}
858 			if (target == NULL) {
859 				Session* session = FindSession(user);
860 				if (session != NULL) {
861 					// Forward request to user launch_daemon
862 					if (session->Daemon().SendMessage(message) == B_OK)
863 						break;
864 				}
865 
866 				BMessage reply(B_NAME_NOT_FOUND);
867 				message->SendReply(&reply);
868 				break;
869 			}
870 
871 			BMessage data;
872 			if (message->FindMessage("data", &data) == B_OK)
873 				target->AddData(data.GetString("name"), data);
874 
875 			_LaunchJobs(target);
876 			break;
877 		}
878 
879 		case B_LAUNCH_SESSION:
880 		{
881 			uid_t user = _GetUserID(message);
882 			if (user < 0)
883 				break;
884 
885 			status_t status = B_OK;
886 			const char* login = message->GetString("login");
887 			const char* password = message->GetString("password");
888 			if (login == NULL || password == NULL)
889 				status = B_BAD_VALUE;
890 			if (status == B_OK && user != 0) {
891 				// Only the root user can start sessions
892 				status = B_PERMISSION_DENIED;
893 			}
894 			if (status == B_OK)
895 				status = _StartSession(login, password);
896 
897 			BMessage reply((uint32)status);
898 			message->SendReply(&reply);
899 			break;
900 		}
901 
902 		case B_REGISTER_SESSION_DAEMON:
903 		{
904 			uid_t user = _GetUserID(message);
905 			if (user < 0)
906 				break;
907 
908 			status_t status = B_OK;
909 
910 			BMessenger target;
911 			if (message->FindMessenger("target", &target) != B_OK)
912 				status = B_BAD_VALUE;
913 
914 			if (status == B_OK) {
915 				Session* session = new (std::nothrow) Session(user, target);
916 				if (session != NULL)
917 					fSessions.insert(std::pair<uid_t, Session*>(user, session));
918 				else
919 					status = B_NO_MEMORY;
920 			}
921 
922 			BMessage reply((uint32)status);
923 			message->SendReply(&reply);
924 			break;
925 		}
926 
927 		default:
928 			BServer::MessageReceived(message);
929 			break;
930 	}
931 }
932 
933 
934 uid_t
935 LaunchDaemon::_GetUserID(BMessage* message)
936 {
937 	uid_t user = (uid_t)message->GetInt32("user", -1);
938 	if (user < 0) {
939 		BMessage reply((uint32)B_BAD_VALUE);
940 		message->SendReply(&reply);
941 	}
942 	return user;
943 }
944 
945 
946 void
947 LaunchDaemon::_ReadPaths(const BStringList& paths)
948 {
949 	for (int32 i = 0; i < paths.CountStrings(); i++) {
950 		BEntry entry(paths.StringAt(i));
951 		if (entry.InitCheck() != B_OK || !entry.Exists())
952 			continue;
953 
954 		_ReadDirectory(NULL, entry);
955 	}
956 }
957 
958 
959 void
960 LaunchDaemon::_ReadEntry(const char* context, BEntry& entry)
961 {
962 	if (entry.IsDirectory())
963 		_ReadDirectory(context, entry);
964 	else
965 		_ReadFile(context, entry);
966 }
967 
968 
969 void
970 LaunchDaemon::_ReadDirectory(const char* context, BEntry& directoryEntry)
971 {
972 	BDirectory directory(&directoryEntry);
973 
974 	BEntry entry;
975 	while (directory.GetNextEntry(&entry) == B_OK) {
976 		_ReadEntry(context, entry);
977 	}
978 }
979 
980 
981 status_t
982 LaunchDaemon::_ReadFile(const char* context, BEntry& entry)
983 {
984 	DriverSettingsMessageAdapter adapter;
985 
986 	BPath path;
987 	status_t status = path.SetTo(&entry);
988 	if (status != B_OK)
989 		return status;
990 
991 	BMessage message;
992 	status = adapter.ConvertFromDriverSettings(path.Path(), kSettingsTemplate,
993 			message);
994 	if (status == B_OK) {
995 		_AddJobs(NULL, message);
996 
997 		BMessage targetMessage;
998 		for (int32 index = 0; message.FindMessage("target", index,
999 				&targetMessage) == B_OK; index++) {
1000 			const char* name = targetMessage.GetString("name");
1001 			if (name == NULL) {
1002 				// TODO: log error
1003 				debug_printf("Target has no name, ignoring it!\n");
1004 				continue;
1005 			}
1006 
1007 			Target* target = FindTarget(name);
1008 			if (target == NULL) {
1009 				target = new Target(name);
1010 				_AddTarget(target);
1011 			} else if (targetMessage.GetBool("reset")) {
1012 				// Remove all jobs from this target
1013 				for (JobMap::iterator iterator = fJobs.begin();
1014 						iterator != fJobs.end();) {
1015 					Job* job = iterator->second;
1016 					JobMap::iterator remove = iterator++;
1017 
1018 					if (job->Target() == target) {
1019 						fJobs.erase(remove);
1020 						delete job;
1021 					}
1022 				}
1023 			}
1024 
1025 			_AddJobs(target, targetMessage);
1026 		}
1027 	}
1028 
1029 	return status;
1030 }
1031 
1032 
1033 void
1034 LaunchDaemon::_AddJobs(Target* target, BMessage& message)
1035 {
1036 	BMessage job;
1037 	for (int32 index = 0; message.FindMessage("service", index,
1038 			&job) == B_OK; index++) {
1039 		_AddJob(target, true, job);
1040 	}
1041 
1042 	for (int32 index = 0; message.FindMessage("job", index, &job) == B_OK;
1043 			index++) {
1044 		_AddJob(target, false, job);
1045 	}
1046 }
1047 
1048 
1049 void
1050 LaunchDaemon::_AddJob(Target* target, bool service, BMessage& message)
1051 {
1052 	BString name = message.GetString("name");
1053 	if (name.IsEmpty()) {
1054 		// Invalid job description
1055 		return;
1056 	}
1057 	name.ToLower();
1058 
1059 	Job* job = FindJob(name);
1060 	if (job == NULL)
1061 		job = new Job(name);
1062 
1063 	job->SetEnabled(!message.GetBool("disabled", !job->IsEnabled()));
1064 	job->SetService(service);
1065 	job->SetCreateDefaultPort(!message.GetBool("legacy", !service));
1066 	job->SetLaunchInSafeMode(
1067 		!message.GetBool("no_safemode", !job->LaunchInSafeMode()));
1068 	job->SetTarget(target);
1069 
1070 	BMessage portMessage;
1071 	for (int32 index = 0;
1072 			message.FindMessage("port", index, &portMessage) == B_OK; index++) {
1073 		job->AddPort(portMessage);
1074 	}
1075 
1076 	const char* argument;
1077 	for (int32 index = 0;
1078 			message.FindString("launch", index, &argument) == B_OK; index++) {
1079 		job->AddArgument(argument);
1080 	}
1081 
1082 	const char* requirement;
1083 	for (int32 index = 0;
1084 			message.FindString("requires", index, &requirement) == B_OK;
1085 			index++) {
1086 		job->AddRequirement(requirement);
1087 	}
1088 	if (fInitTarget != NULL)
1089 		job->AddRequirement(fInitTarget->Name());
1090 
1091 	fJobs.insert(std::pair<BString, Job*>(job->Name(), job));
1092 }
1093 
1094 
1095 void
1096 LaunchDaemon::_InitJobs()
1097 {
1098 	for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();) {
1099 		Job* job = iterator->second;
1100 		JobMap::iterator remove = iterator++;
1101 
1102 		status_t status = B_NO_INIT;
1103 		if (job->IsEnabled() && (!_IsSafeMode() || job->LaunchInSafeMode())) {
1104 			std::set<BString> dependencies;
1105 			status = job->Init(*this, dependencies);
1106 		}
1107 
1108 		if (status != B_OK) {
1109 			if (status != B_NO_INIT) {
1110 				// TODO: log error
1111 				debug_printf("Init \"%s\" failed: %s\n", job->Name(),
1112 					strerror(status));
1113 			}
1114 
1115 			// Remove jobs that won't be used later on
1116 			fJobs.erase(remove);
1117 			delete job;
1118 		}
1119 	}
1120 }
1121 
1122 
1123 void
1124 LaunchDaemon::_LaunchJobs(Target* target)
1125 {
1126 	for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();
1127 			iterator++) {
1128 		Job* job = iterator->second;
1129 		if (job->Target() == target)
1130 			_AddLaunchJob(job);
1131 	}
1132 }
1133 
1134 
1135 void
1136 LaunchDaemon::_AddLaunchJob(Job* job)
1137 {
1138 	if (!job->IsLaunched())
1139 		fJobQueue.AddJob(job);
1140 }
1141 
1142 
1143 void
1144 LaunchDaemon::_AddTarget(Target* target)
1145 {
1146 	fTargets.insert(std::make_pair(target->Title(), target));
1147 }
1148 
1149 
1150 status_t
1151 LaunchDaemon::_StartSession(const char* login, const char* password)
1152 {
1153 	Unlock();
1154 
1155 	// TODO: enable user/group code and password authentication
1156 	// The launch_daemon currently cannot talk to the registrar, though
1157 /*
1158 	struct passwd* passwd = getpwnam(login);
1159 	if (passwd == NULL)
1160 		return B_NAME_NOT_FOUND;
1161 	if (strcmp(passwd->pw_name, login) != 0)
1162 		return B_NAME_NOT_FOUND;
1163 
1164 	// TODO: check for auto-login, and ignore password then
1165 	if (!verify_password(passwd, getspnam(login), password))
1166 		return B_PERMISSION_DENIED;
1167 
1168 	// Check if there is a user session running already
1169 	uid_t user = passwd->pw_uid;
1170 	gid_t group = passwd->pw_gid;
1171 */
1172 
1173 	if (fork() == 0) {
1174 		if (setsid() < 0)
1175 			exit(EXIT_FAILURE);
1176 
1177 /*
1178 debug_printf("session leader...\n");
1179 		if (initgroups(login, group) == -1) {
1180 debug_printf("1.ouch: %s\n", strerror(errno));
1181 			exit(EXIT_FAILURE);
1182 		}
1183 		//endgrent();
1184 		if (setgid(group) != 0) {
1185 debug_printf("2.ouch: %s\n", strerror(errno));
1186 			exit(EXIT_FAILURE);
1187 		}
1188 		if (setuid(user) != 0) {
1189 debug_printf("3.ouch: %s\n", strerror(errno));
1190 			exit(EXIT_FAILURE);
1191 		}
1192 */
1193 
1194 		// TODO: This leaks the parent application
1195 		be_app = NULL;
1196 
1197 		// TODO: take over system jobs, and reserve their names
1198 		status_t status;
1199 		LaunchDaemon* daemon = new LaunchDaemon(true, status);
1200 		if (status == B_OK)
1201 			daemon->Run();
1202 
1203 		delete daemon;
1204 		exit(EXIT_SUCCESS);
1205 	}
1206 	Lock();
1207 	return B_OK;
1208 }
1209 
1210 
1211 void
1212 LaunchDaemon::_RetrieveKernelOptions()
1213 {
1214 	char buffer[32];
1215 	size_t size = sizeof(buffer);
1216 	status_t status = _kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, buffer,
1217 		&size);
1218 	if (status == B_OK) {
1219 		fSafeMode = !strncasecmp(buffer, "true", size)
1220 			|| !strncasecmp(buffer, "yes", size)
1221 			|| !strncasecmp(buffer, "on", size)
1222 			|| !strncasecmp(buffer, "enabled", size);
1223 	} else
1224 		fSafeMode = false;
1225 }
1226 
1227 
1228 void
1229 LaunchDaemon::_SetupEnvironment()
1230 {
1231 	// Determine safemode kernel option
1232 	BString safemode = "SAFEMODE=";
1233 	safemode << _IsSafeMode() ? "yes" : "no";
1234 
1235 	putenv(safemode.String());
1236 }
1237 
1238 
1239 /*!	Basic system initialization that must happen before any jobs are launched.
1240 */
1241 void
1242 LaunchDaemon::_InitSystem()
1243 {
1244 	_AddInitJob(new InitRealTimeClockJob());
1245 	_AddInitJob(new InitSharedMemoryDirectoryJob());
1246 	_AddInitJob(new InitTemporaryDirectoryJob());
1247 
1248 	fJobQueue.AddJob(fInitTarget);
1249 }
1250 
1251 
1252 void
1253 LaunchDaemon::_AddInitJob(BJob* job)
1254 {
1255 	fInitTarget->AddDependency(job);
1256 	fJobQueue.AddJob(job);
1257 }
1258 
1259 
1260 bool
1261 LaunchDaemon::_IsSafeMode() const
1262 {
1263 	return fSafeMode;
1264 }
1265 
1266 
1267 // #pragma mark -
1268 
1269 
1270 int
1271 main()
1272 {
1273 	status_t status;
1274 	LaunchDaemon* daemon = new LaunchDaemon(false, status);
1275 	if (status == B_OK)
1276 		daemon->Run();
1277 
1278 	delete daemon;
1279 	return status == B_OK ? EXIT_SUCCESS : EXIT_FAILURE;
1280 }
1281