xref: /haiku/src/servers/app/AppServer.cpp (revision 1294543de9ac0eff000eaea1b18368c36435d08e)
1 /*
2  * Copyright 2001-2009, 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 /*!	Detect SIMD flags for use in AppServer. Checks all CPUs in the system
44 	and chooses the minimum supported set of instructions.
45 */
46 static void
47 detect_simd()
48 {
49 #if __INTEL__
50 	// Only scan CPUs for which we are certain the SIMD flags are properly
51 	// defined.
52 	const char* vendorNames[] = {
53 		"GenuineIntel",
54 		"AuthenticAMD",
55 		"CentaurHauls", // Via CPUs, MMX and SSE support
56 		"RiseRiseRise", // should be MMX-only
57 		"CyrixInstead", // MMX-only, but custom MMX extensions
58 		"GenuineTMx86", // MMX and SSE
59 		0
60 	};
61 
62 	system_info systemInfo;
63 	if (get_system_info(&systemInfo) != B_OK)
64 		return;
65 
66 	// We start out with all flags set and end up with only those flags
67 	// supported across all CPUs found.
68 	uint32 appServerSIMD = 0xffffffff;
69 
70 	for (int32 cpu = 0; cpu < systemInfo.cpu_count; cpu++) {
71 		cpuid_info cpuInfo;
72 		get_cpuid(&cpuInfo, 0, cpu);
73 
74 		// Get the vendor string and terminate it manually
75 		char vendor[13];
76 		memcpy(vendor, cpuInfo.eax_0.vendor_id, 12);
77 		vendor[12] = 0;
78 
79 		bool vendorFound = false;
80 		for (uint32 i = 0; vendorNames[i] != 0; i++) {
81 			if (strcmp(vendor, vendorNames[i]) == 0)
82 				vendorFound = true;
83 		}
84 
85 		uint32 cpuSIMD = 0;
86 		uint32 maxStdFunc = cpuInfo.regs.eax;
87 		if (vendorFound && maxStdFunc >= 1) {
88 			get_cpuid(&cpuInfo, 1, 0);
89 			uint32 edx = cpuInfo.regs.edx;
90 			if (edx & (1 << 23))
91 				cpuSIMD |= APPSERVER_SIMD_MMX;
92 			if (edx & (1 << 25))
93 				cpuSIMD |= APPSERVER_SIMD_SSE;
94 		} else {
95 			// no flags can be identified
96 			cpuSIMD = 0;
97 		}
98 		appServerSIMD &= cpuSIMD;
99 	}
100 	gAppServerSIMDFlags = appServerSIMD;
101 #endif	// __INTEL__
102 }
103 
104 
105 //	#pragma mark -
106 
107 
108 /*!
109 	\brief Constructor
110 
111 	This loads the default fonts, allocates all the major global variables, spawns the main housekeeping
112 	threads, loads user preferences for the UI and decorator, and allocates various locks.
113 */
114 AppServer::AppServer()
115 	:
116 	MessageLooper("app_server"),
117 	fMessagePort(-1),
118 	fDesktops(),
119 	fDesktopLock("AppServerDesktopLock")
120 {
121 	openlog("app_server", 0, LOG_DAEMON);
122 
123 	fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, SERVER_PORT_NAME);
124 	if (fMessagePort < B_OK)
125 		debugger("app_server could not create message port");
126 
127 	fLink.SetReceiverPort(fMessagePort);
128 
129 	sAppServer = this;
130 
131 	// Initialize SIMD flags
132 	detect_simd();
133 
134 	gInputManager = new InputManager();
135 
136 	// Create the font server and scan the proper directories.
137 	gFontManager = new FontManager;
138 	if (gFontManager->InitCheck() != B_OK)
139 		debugger("font manager could not be initialized!");
140 
141 	gFontManager->Run();
142 
143 	gScreenManager = new ScreenManager();
144 	gScreenManager->Run();
145 
146 	// Create the bitmap allocator. Object declared in BitmapManager.cpp
147 	gBitmapManager = new BitmapManager();
148 }
149 
150 
151 /*!	\brief Destructor
152 	Reached only when the server is asked to shut down in Test mode.
153 */
154 AppServer::~AppServer()
155 {
156 	delete gBitmapManager;
157 
158 	gScreenManager->Lock();
159 	gScreenManager->Quit();
160 
161 	gFontManager->Lock();
162 	gFontManager->Quit();
163 
164 	closelog();
165 }
166 
167 
168 void
169 AppServer::RunLooper()
170 {
171 	rename_thread(find_thread(NULL), "picasso");
172 	_message_thread((void*)this);
173 }
174 
175 
176 /*!	\brief Creates a desktop object for an authorized user
177 */
178 Desktop*
179 AppServer::_CreateDesktop(uid_t userID, const char* targetScreen)
180 {
181 	BAutolock locker(fDesktopLock);
182 	Desktop* desktop = NULL;
183 	try {
184 		desktop = new Desktop(userID, targetScreen);
185 
186 		status_t status = desktop->Init();
187 		if (status == B_OK) {
188 			if (!desktop->Run())
189 				status = B_ERROR;
190 		}
191 		if (status == B_OK && !fDesktops.AddItem(desktop))
192 			status = B_NO_MEMORY;
193 
194 		if (status != B_OK) {
195 			syslog(LOG_ERR, "Cannot initialize Desktop object: %s\n",
196 				strerror(status));
197 			delete desktop;
198 			return NULL;
199 		}
200 	} catch (...) {
201 		// there is obviously no memory left
202 		return NULL;
203 	}
204 
205 	return desktop;
206 }
207 
208 
209 /*!	\brief Finds the desktop object that belongs to a certain user
210 */
211 Desktop*
212 AppServer::_FindDesktop(uid_t userID, const char* targetScreen)
213 {
214 	BAutolock locker(fDesktopLock);
215 
216 	for (int32 i = 0; i < fDesktops.CountItems(); i++) {
217 		Desktop* desktop = fDesktops.ItemAt(i);
218 
219 		if (desktop->UserID() == userID
220 			&& ((desktop->TargetScreen() == NULL && targetScreen == NULL)
221 				|| (desktop->TargetScreen() != NULL && targetScreen != NULL
222 					&& strcmp(desktop->TargetScreen(), targetScreen) == 0))) {
223 			return desktop;
224 		}
225 	}
226 
227 	return NULL;
228 }
229 
230 
231 /*!	\brief Message handling function for all messages sent to the app_server
232 	\param code ID of the message sent
233 	\param buffer Attachment buffer for the message.
234 
235 */
236 void
237 AppServer::_DispatchMessage(int32 code, BPrivate::LinkReceiver& msg)
238 {
239 	switch (code) {
240 		case AS_GET_DESKTOP:
241 		{
242 			Desktop* desktop = NULL;
243 
244 			port_id replyPort;
245 			msg.Read<port_id>(&replyPort);
246 
247 			int32 userID;
248 			msg.Read<int32>(&userID);
249 
250 			char* targetScreen = NULL;
251 			msg.ReadString(&targetScreen);
252 			if (targetScreen != NULL && strlen(targetScreen) == 0) {
253 				free(targetScreen);
254 				targetScreen = NULL;
255 			}
256 
257 			int32 version;
258 			if (msg.Read<int32>(&version) < B_OK
259 				|| version != AS_PROTOCOL_VERSION) {
260 				syslog(LOG_ERR, "Application for user %ld with port %ld does "
261 					"not support the current server protocol.\n", userID,
262 					replyPort);
263 			} else {
264 				desktop = _FindDesktop(userID, targetScreen);
265 				if (desktop == NULL) {
266 					// we need to create a new desktop object for this user
267 					// TODO: test if the user exists on the system
268 					// TODO: maybe have a separate AS_START_DESKTOP_SESSION for
269 					// authorizing the user
270 					desktop = _CreateDesktop(userID, targetScreen);
271 				}
272 			}
273 
274 			free(targetScreen);
275 
276 			BPrivate::LinkSender reply(replyPort);
277 			if (desktop != NULL) {
278 				reply.StartMessage(B_OK);
279 				reply.Attach<port_id>(desktop->MessagePort());
280 			} else
281 				reply.StartMessage(B_ERROR);
282 
283 			reply.Flush();
284 			break;
285 		}
286 
287 #if TEST_MODE
288 		case B_QUIT_REQUESTED:
289 		{
290 			// We've been asked to quit, so (for now) broadcast to all
291 			// desktops to quit. This situation will occur only when the server
292 			// is compiled as a regular Be application.
293 
294 			fQuitting = true;
295 
296 			while (fDesktops.CountItems() > 0) {
297 				Desktop *desktop = fDesktops.RemoveItemAt(0);
298 
299 				thread_id thread = desktop->Thread();
300 				desktop->PostMessage(B_QUIT_REQUESTED);
301 
302 				// we just wait for the desktop to kill itself
303 				status_t status;
304 				wait_for_thread(thread, &status);
305 			}
306 
307 			delete this;
308 
309 			// we are now clear to exit
310 			exit(0);
311 			break;
312 		}
313 #endif
314 
315 		default:
316 			STRACE(("Server::MainLoop received unexpected code %ld (offset %ld)\n",
317 				code, code - SERVER_TRUE));
318 			break;
319 	}
320 }
321 
322 
323 //	#pragma mark -
324 
325 
326 int
327 main(int argc, char** argv)
328 {
329 	// There can be only one....
330 	if (find_port(SERVER_PORT_NAME) >= B_OK)
331 		return -1;
332 
333 	srand(real_time_clock_usecs());
334 
335 	AppServer* server = new AppServer;
336 	server->RunLooper();
337 
338 	return 0;
339 }
340