1 /* 2 * Copyright (c) 2001-2006, 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 */ 10 11 12 #include "AppServer.h" 13 14 #include "BitmapManager.h" 15 #include "Desktop.h" 16 #include "FontManager.h" 17 #include "InputManager.h" 18 #include "ScreenManager.h" 19 #include "ServerProtocol.h" 20 #include "SystemPalette.h" 21 22 #include <PortLink.h> 23 24 #include <syslog.h> 25 26 //#define DEBUG_SERVER 27 #ifdef DEBUG_SERVER 28 # include <stdio.h> 29 # define STRACE(x) printf x 30 #else 31 # define STRACE(x) ; 32 #endif 33 34 35 // Globals 36 port_id gAppServerPort; 37 static AppServer *sAppServer; 38 BTokenSpace gTokenSpace; 39 40 41 /*! 42 \brief Constructor 43 44 This loads the default fonts, allocates all the major global variables, spawns the main housekeeping 45 threads, loads user preferences for the UI and decorator, and allocates various locks. 46 */ 47 AppServer::AppServer() 48 : MessageLooper("app_server"), 49 fMessagePort(-1), 50 fDesktops(), 51 fDesktopLock("AppServerDesktopLock") 52 { 53 openlog("app_server", 0, LOG_DAEMON); 54 55 fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, SERVER_PORT_NAME); 56 if (fMessagePort < B_OK) 57 debugger("app_server could not create message port"); 58 59 fLink.SetReceiverPort(fMessagePort); 60 61 sAppServer = this; 62 63 gInputManager = new InputManager(); 64 65 // Create the font server and scan the proper directories. 66 gFontManager = new FontManager; 67 if (gFontManager->InitCheck() != B_OK) 68 debugger("font manager could not be initialized!"); 69 70 gFontManager->Run(); 71 72 gScreenManager = new ScreenManager(); 73 gScreenManager->Run(); 74 75 // the system palette needs to be initialized before the desktop, 76 // since it is used there already 77 InitializeColorMap(); 78 79 // Create the bitmap allocator. Object declared in BitmapManager.cpp 80 gBitmapManager = new BitmapManager(); 81 82 #if 0 83 _LaunchCursorThread(); 84 #endif 85 } 86 87 /*! 88 \brief Destructor 89 Reached only when the server is asked to shut down in Test mode. 90 */ 91 AppServer::~AppServer() 92 { 93 delete gBitmapManager; 94 95 gScreenManager->Lock(); 96 gScreenManager->Quit(); 97 98 gFontManager->Lock(); 99 gFontManager->Quit(); 100 101 closelog(); 102 } 103 104 105 /*! 106 \brief The call that starts it all... 107 */ 108 void 109 AppServer::RunLooper() 110 { 111 rename_thread(find_thread(NULL), "picasso"); 112 _message_thread((void *)this); 113 } 114 115 116 #if 0 117 /*! 118 \brief Starts Input Server 119 */ 120 void 121 AppServer::_LaunchInputServer() 122 { 123 // We are supposed to start the input_server, but it's a BApplication 124 // that depends on the registrar running, which is started after app_server 125 // so we wait on roster thread to launch the input server 126 127 fISThreadID = B_ERROR; 128 129 while (!BRoster::Private().IsMessengerValid(false) && !fQuitting) { 130 snooze(250000); 131 BRoster::Private::DeleteBeRoster(); 132 BRoster::Private::InitBeRoster(); 133 } 134 135 if (fQuitting) 136 return; 137 138 // we use an area for cursor data communication with input_server 139 // area id and sem id are sent to the input_server 140 141 if (fCursorArea < B_OK) 142 fCursorArea = create_area("isCursor", (void**) &fCursorAddr, B_ANY_ADDRESS, B_PAGE_SIZE, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA); 143 if (fCursorSem < B_OK) 144 fCursorSem = create_sem(0, "isSem"); 145 146 int32 arg_c = 1; 147 char **arg_v = (char **)malloc(sizeof(char *) * (arg_c + 1)); 148 #if TEST_MODE 149 arg_v[0] = strdup("/boot/home/svnhaiku/trunk/distro/x86.R1/beos/system/servers/input_server"); 150 #else 151 arg_v[0] = strdup("/system/servers/input_server"); 152 #endif 153 arg_v[1] = NULL; 154 fISThreadID = load_image(arg_c, (const char**)arg_v, (const char **)environ); 155 free(arg_v[0]); 156 157 int32 tmpbuf[2] = {fCursorSem, fCursorArea}; 158 int32 code = 0; 159 send_data(fISThreadID, code, (void *)tmpbuf, sizeof(tmpbuf)); 160 161 resume_thread(fISThreadID); 162 setpgid(fISThreadID, 0); 163 164 // we receive 165 166 thread_id sender; 167 code = receive_data(&sender, (void *)tmpbuf, sizeof(tmpbuf)); 168 fISASPort = tmpbuf[0]; 169 fISPort = tmpbuf[1]; 170 171 // if at any time, one of these ports is error prone, it might mean input_server is gone 172 // then relaunch input_server 173 } 174 #endif 175 176 177 /*! 178 \brief Creates a desktop object for an authorized user 179 */ 180 Desktop * 181 AppServer::_CreateDesktop(uid_t userID) 182 { 183 BAutolock locker(fDesktopLock); 184 Desktop* desktop = NULL; 185 try { 186 desktop = new Desktop(userID); 187 188 status_t status = desktop->Init(); 189 if (status == B_OK) { 190 if (!desktop->Run()) 191 status = B_ERROR; 192 } 193 if (status == B_OK && !fDesktops.AddItem(desktop)) 194 status = B_NO_MEMORY; 195 196 if (status < B_OK) { 197 fprintf(stderr, "Cannot initialize Desktop object: %s\n", strerror(status)); 198 delete desktop; 199 return NULL; 200 } 201 } catch (...) { 202 // there is obviously no memory left 203 return NULL; 204 } 205 206 return desktop; 207 } 208 209 210 /*! 211 \brief Finds the desktop object that belongs to a certain user 212 */ 213 Desktop * 214 AppServer::_FindDesktop(uid_t userID) 215 { 216 BAutolock locker(fDesktopLock); 217 218 for (int32 i = 0; i < fDesktops.CountItems(); i++) { 219 Desktop* desktop = fDesktops.ItemAt(i); 220 221 if (desktop->UserID() == userID) 222 return desktop; 223 } 224 225 return NULL; 226 } 227 228 229 /*! 230 \brief Message handling function for all messages sent to the app_server 231 \param code ID of the message sent 232 \param buffer Attachment buffer for the message. 233 234 */ 235 void 236 AppServer::_DispatchMessage(int32 code, BPrivate::LinkReceiver& msg) 237 { 238 switch (code) { 239 case AS_GET_DESKTOP: 240 { 241 port_id replyPort; 242 if (msg.Read<port_id>(&replyPort) < B_OK) 243 break; 244 245 int32 userID; 246 msg.Read<int32>(&userID); 247 248 Desktop* desktop = _FindDesktop(userID); 249 if (desktop == NULL) { 250 // we need to create a new desktop object for this user 251 // ToDo: test if the user exists on the system 252 // ToDo: maybe have a separate AS_START_DESKTOP_SESSION for authorizing the user 253 desktop = _CreateDesktop(userID); 254 } 255 256 BPrivate::LinkSender reply(replyPort); 257 reply.StartMessage(B_OK); 258 reply.Attach<port_id>(desktop->MessagePort()); 259 reply.Flush(); 260 break; 261 } 262 263 #if TEST_MODE 264 case B_QUIT_REQUESTED: 265 { 266 // We've been asked to quit, so (for now) broadcast to all 267 // desktops to quit. This situation will occur only when the server 268 // is compiled as a regular Be application. 269 270 fQuitting = true; 271 272 while (fDesktops.CountItems() > 0) { 273 Desktop *desktop = fDesktops.RemoveItemAt(0); 274 275 thread_id thread = desktop->Thread(); 276 desktop->PostMessage(B_QUIT_REQUESTED); 277 278 // we just wait for the desktop to kill itself 279 status_t status; 280 wait_for_thread(thread, &status); 281 } 282 283 delete this; 284 285 // we are now clear to exit 286 exit(0); 287 break; 288 } 289 #endif 290 291 default: 292 STRACE(("Server::MainLoop received unexpected code %ld (offset %ld)\n", 293 code, code - SERVER_TRUE)); 294 break; 295 } 296 } 297 298 299 // #pragma mark - 300 301 302 /*! 303 \brief Entry function to run the entire server 304 \param argc Number of command-line arguments present 305 \param argv String array of the command-line arguments 306 \return -1 if the app_server is already running, 0 if everything's OK. 307 */ 308 int 309 main(int argc, char** argv) 310 { 311 // There can be only one.... 312 if (find_port(SERVER_PORT_NAME) >= B_OK) 313 return -1; 314 315 srand(real_time_clock_usecs()); 316 317 AppServer* server = new AppServer; 318 server->RunLooper(); 319 320 return 0; 321 } 322