xref: /haiku/src/servers/app/AppServer.cpp (revision 579f1dbca962a2a03df54f69fdc6e9423f91f20e)
1 /*
2  * Copyright 2001-2011, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		DarkWyrm <bpmagic@columbus.rr.com>
7  *		Axel Dörfler, axeld@pinc-software.de
8  *		Stephan Aßmus <superstippi@gmx.de>
9  * 		Christian Packmann
10  */
11 
12 
13 #include "AppServer.h"
14 
15 #include "BitmapManager.h"
16 #include "Desktop.h"
17 #include "FontManager.h"
18 #include "InputManager.h"
19 #include "ScreenManager.h"
20 #include "ServerProtocol.h"
21 
22 #include <PortLink.h>
23 
24 #include <syslog.h>
25 
26 
27 //#define DEBUG_SERVER
28 #ifdef DEBUG_SERVER
29 #	include <stdio.h>
30 #	define STRACE(x) printf x
31 #else
32 #	define STRACE(x) ;
33 #endif
34 
35 
36 // Globals
37 port_id gAppServerPort;
38 static AppServer* sAppServer;
39 BTokenSpace gTokenSpace;
40 uint32 gAppServerSIMDFlags = 0;
41 
42 
43 /*!	\brief Constructor
44 
45 	This loads the default fonts, allocates all the major global variables,
46 	spawns the main housekeeping threads, loads user preferences for the UI
47 	and decorator, and allocates various locks.
48 */
49 AppServer::AppServer()
50 	:
51 	MessageLooper("app_server"),
52 	fMessagePort(-1),
53 	fDesktops(),
54 	fDesktopLock("AppServerDesktopLock")
55 {
56 	openlog("app_server", 0, LOG_DAEMON);
57 
58 	fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, SERVER_PORT_NAME);
59 	if (fMessagePort < B_OK)
60 		debugger("app_server could not create message port");
61 
62 	fLink.SetReceiverPort(fMessagePort);
63 
64 	sAppServer = this;
65 
66 	gInputManager = new InputManager();
67 
68 	// Create the font server and scan the proper directories.
69 	gFontManager = new FontManager;
70 	if (gFontManager->InitCheck() != B_OK)
71 		debugger("font manager could not be initialized!");
72 
73 	gFontManager->Run();
74 
75 	gScreenManager = new ScreenManager();
76 	gScreenManager->Run();
77 
78 	// Create the bitmap allocator. Object declared in BitmapManager.cpp
79 	gBitmapManager = new BitmapManager();
80 }
81 
82 
83 /*!	\brief Destructor
84 	Reached only when the server is asked to shut down in Test mode.
85 */
86 AppServer::~AppServer()
87 {
88 	delete gBitmapManager;
89 
90 	gScreenManager->Lock();
91 	gScreenManager->Quit();
92 
93 	gFontManager->Lock();
94 	gFontManager->Quit();
95 
96 	closelog();
97 }
98 
99 
100 void
101 AppServer::RunLooper()
102 {
103 	rename_thread(find_thread(NULL), "picasso");
104 	_message_thread((void*)this);
105 }
106 
107 
108 /*!	\brief Creates a desktop object for an authorized user
109 */
110 Desktop*
111 AppServer::_CreateDesktop(uid_t userID, const char* targetScreen)
112 {
113 	BAutolock locker(fDesktopLock);
114 	Desktop* desktop = NULL;
115 	try {
116 		desktop = new Desktop(userID, targetScreen);
117 
118 		status_t status = desktop->Init();
119 		if (status == B_OK) {
120 			if (!desktop->Run())
121 				status = B_ERROR;
122 		}
123 		if (status == B_OK && !fDesktops.AddItem(desktop))
124 			status = B_NO_MEMORY;
125 
126 		if (status != B_OK) {
127 			syslog(LOG_ERR, "Cannot initialize Desktop object: %s\n",
128 				strerror(status));
129 			delete desktop;
130 			return NULL;
131 		}
132 	} catch (...) {
133 		// there is obviously no memory left
134 		return NULL;
135 	}
136 
137 	return desktop;
138 }
139 
140 
141 /*!	\brief Finds the desktop object that belongs to a certain user
142 */
143 Desktop*
144 AppServer::_FindDesktop(uid_t userID, const char* targetScreen)
145 {
146 	BAutolock locker(fDesktopLock);
147 
148 	for (int32 i = 0; i < fDesktops.CountItems(); i++) {
149 		Desktop* desktop = fDesktops.ItemAt(i);
150 
151 		if (desktop->UserID() == userID
152 			&& ((desktop->TargetScreen() == NULL && targetScreen == NULL)
153 				|| (desktop->TargetScreen() != NULL && targetScreen != NULL
154 					&& strcmp(desktop->TargetScreen(), targetScreen) == 0))) {
155 			return desktop;
156 		}
157 	}
158 
159 	return NULL;
160 }
161 
162 
163 /*!	\brief Message handling function for all messages sent to the app_server
164 	\param code ID of the message sent
165 	\param buffer Attachment buffer for the message.
166 
167 */
168 void
169 AppServer::_DispatchMessage(int32 code, BPrivate::LinkReceiver& msg)
170 {
171 	switch (code) {
172 		case AS_GET_DESKTOP:
173 		{
174 			Desktop* desktop = NULL;
175 
176 			port_id replyPort;
177 			msg.Read<port_id>(&replyPort);
178 
179 			int32 userID;
180 			msg.Read<int32>(&userID);
181 
182 			char* targetScreen = NULL;
183 			msg.ReadString(&targetScreen);
184 			if (targetScreen != NULL && strlen(targetScreen) == 0) {
185 				free(targetScreen);
186 				targetScreen = NULL;
187 			}
188 
189 			int32 version;
190 			if (msg.Read<int32>(&version) < B_OK
191 				|| version != AS_PROTOCOL_VERSION) {
192 				syslog(LOG_ERR, "Application for user %" B_PRId32 " with port "
193 					"%" B_PRId32 " does not support the current server "
194 					"protocol.\n", userID, replyPort);
195 			} else {
196 				desktop = _FindDesktop(userID, targetScreen);
197 				if (desktop == NULL) {
198 					// we need to create a new desktop object for this user
199 					// TODO: test if the user exists on the system
200 					// TODO: maybe have a separate AS_START_DESKTOP_SESSION for
201 					// authorizing the user
202 					desktop = _CreateDesktop(userID, targetScreen);
203 				}
204 			}
205 
206 			free(targetScreen);
207 
208 			BPrivate::LinkSender reply(replyPort);
209 			if (desktop != NULL) {
210 				reply.StartMessage(B_OK);
211 				reply.Attach<port_id>(desktop->MessagePort());
212 			} else
213 				reply.StartMessage(B_ERROR);
214 
215 			reply.Flush();
216 			break;
217 		}
218 
219 #if TEST_MODE
220 		case B_QUIT_REQUESTED:
221 		{
222 			// We've been asked to quit, so (for now) broadcast to all
223 			// desktops to quit. This situation will occur only when the server
224 			// is compiled as a regular Be application.
225 
226 			fQuitting = true;
227 
228 			while (fDesktops.CountItems() > 0) {
229 				Desktop *desktop = fDesktops.RemoveItemAt(0);
230 
231 				thread_id thread = desktop->Thread();
232 				desktop->PostMessage(B_QUIT_REQUESTED);
233 
234 				// we just wait for the desktop to kill itself
235 				status_t status;
236 				wait_for_thread(thread, &status);
237 			}
238 
239 			delete this;
240 
241 			// we are now clear to exit
242 			exit(0);
243 			break;
244 		}
245 #endif
246 
247 		default:
248 			STRACE(("Server::MainLoop received unexpected code %" B_PRId32 " "
249 				"(offset %" B_PRId32 ")\n", code, code - SERVER_TRUE));
250 			break;
251 	}
252 }
253 
254 
255 //	#pragma mark -
256 
257 
258 int
259 main(int argc, char** argv)
260 {
261 	// There can be only one....
262 	if (find_port(SERVER_PORT_NAME) >= B_OK)
263 		return -1;
264 
265 	srand(real_time_clock_usecs());
266 
267 	AppServer* server = new AppServer;
268 	server->RunLooper();
269 
270 	return 0;
271 }
272