/* SLiM - Simple Login Manager Copyright (C) 1997, 1998 Per Liden Copyright (C) 2004-06 Simone Rota Copyright (C) 2004-06 Johannes Winkelmann This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include "app.h" #include "numlock.h" #ifdef HAVE_SHADOW #include #endif using namespace std; #ifdef USE_PAM #include int conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr){ *resp = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response)); Panel* panel = *static_cast(appdata_ptr); int result = PAM_SUCCESS; for (int i=0; iresp=0; resp[i]->resp_retcode=0; switch(msg[i]->msg_style){ case PAM_PROMPT_ECHO_ON: // We assume PAM is asking for the username panel->EventHandler(Panel::Get_Name); switch(panel->getAction()){ case Panel::Suspend: case Panel::Halt: case Panel::Reboot: resp[i]->resp=strdup("root"); break; case Panel::Console: case Panel::Exit: case Panel::Login: resp[i]->resp=strdup(panel->GetName().c_str()); break; } break; case PAM_PROMPT_ECHO_OFF: // We assume PAM is asking for the password switch(panel->getAction()){ case Panel::Console: case Panel::Exit: // We should leave now! result=PAM_CONV_ERR; break; default: panel->EventHandler(Panel::Get_Passwd); resp[i]->resp=strdup(panel->GetPasswd().c_str()); break; } break; case PAM_ERROR_MSG: case PAM_TEXT_INFO: // We simply write these to the log // TODO: Maybe we should simply ignore them cerr << APPNAME << ": " << msg[i]->msg << endl; break; } if (result!=PAM_SUCCESS) break; } if (result!=PAM_SUCCESS){ for (int i=0; iresp==0) continue; free(resp[i]->resp); resp[i]->resp=0; }; free(*resp); *resp=0; }; return result; } #endif extern App* LoginApp; void CatchSignal(int sig) { cerr << APPNAME << ": unexpected signal " << sig << endl; LoginApp->StopServer(); LoginApp->RemoveLock(); exit(ERR_EXIT); } void AlarmSignal(int sig) { int pid = LoginApp->GetServerPID(); if(waitpid(pid, NULL, WNOHANG) == pid) { LoginApp->StopServer(); LoginApp->RemoveLock(); exit(OK_EXIT); } signal(sig, AlarmSignal); alarm(2); } void User1Signal(int sig) { signal(sig, User1Signal); } #ifdef USE_PAM App::App(int argc, char** argv): pam(conv, static_cast(&LoginPanel)){ #else App::App(int argc, char** argv){ #endif int tmp; ServerPID = -1; testing = false; mcookie = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; daemonmode = false; force_nodaemon = false; firstlogin = true; Dpy = NULL; // Parse command line // Note: we force a option for nodaemon switch to handle "-nodaemon" while((tmp = getopt(argc, argv, "vhp:n:d?")) != EOF) { switch (tmp) { case 'p': // Test theme testtheme = optarg; testing = true; if (testtheme == NULL) { cerr << "The -p option requires an argument" << endl; exit(ERR_EXIT); } break; case 'd': // Daemon mode daemonmode = true; break; case 'n': // Daemon mode daemonmode = false; force_nodaemon = true; break; case 'v': // Version std::cout << APPNAME << " version " << VERSION << endl; exit(OK_EXIT); break; case '?': // Illegal cerr << endl; case 'h': // Help cerr << "usage: " << APPNAME << " [option ...]" << endl << "options:" << endl << " -d: daemon mode" << endl << " -nodaemon: no-daemon mode" << endl << " -v: show version" << endl << " -p /path/to/theme/dir: preview theme" << endl; exit(OK_EXIT); break; } } if (getuid() != 0 && !testing) { cerr << APPNAME << ": only root can run this program" << endl; exit(ERR_EXIT); } } void App::Run() { DisplayName = DISPLAY; #ifdef XNEST_DEBUG char* p = getenv("DISPLAY"); if (p && p[0]) { DisplayName = p; cout << "Using display name " << DisplayName << endl; } #endif // Read configuration and theme cfg = new Cfg; cfg->readConf(CFGFILE); string themebase = ""; string themefile = ""; string themedir = ""; themeName = ""; if (testing) { themeName = testtheme; } else { themebase = string(THEMESDIR) + "/"; themeName = cfg->getOption("current_theme"); string::size_type pos; if ((pos = themeName.find(",")) != string::npos) { // input is a set themeName = findValidRandomTheme(themeName); if (themeName == "") { themeName = "default"; } } } #ifdef USE_PAM try{ pam.start("slim"); pam.set_item(PAM::Authenticator::TTY, DisplayName); pam.set_item(PAM::Authenticator::Requestor, "root"); pam.set_item(PAM::Authenticator::Host, "localhost"); } catch(PAM::Exception& e){ cerr << APPNAME << ": " << e << endl; exit(ERR_EXIT); }; #endif bool loaded = false; while (!loaded) { themedir = themebase + themeName; themefile = themedir + THEMESFILE; if (!cfg->readConf(themefile)) { if (themeName == "default") { cerr << APPNAME << ": Failed to open default theme file " << themefile << endl; exit(ERR_EXIT); } else { cerr << APPNAME << ": Invalid theme in config: " << themeName << endl; themeName = "default"; } } else { loaded = true; } } if (!testing) { // Create lock file LoginApp->GetLock(); // Start x-server setenv("DISPLAY", DisplayName, 1); signal(SIGQUIT, CatchSignal); signal(SIGTERM, CatchSignal); signal(SIGKILL, CatchSignal); signal(SIGINT, CatchSignal); signal(SIGHUP, CatchSignal); signal(SIGPIPE, CatchSignal); signal(SIGUSR1, User1Signal); signal(SIGALRM, AlarmSignal); #ifndef XNEST_DEBUG OpenLog(); if (!force_nodaemon && cfg->getOption("daemon") == "yes") { daemonmode = true; } // Daemonize if (daemonmode) { if (daemon(0, 1) == -1) { cerr << APPNAME << ": " << strerror(errno) << endl; exit(ERR_EXIT); } UpdatePid(); } CreateServerAuth(); StartServer(); alarm(2); #endif } // Open display if((Dpy = XOpenDisplay(DisplayName)) == 0) { cerr << APPNAME << ": could not open display '" << DisplayName << "'" << endl; if (!testing) StopServer(); exit(ERR_EXIT); } // Get screen and root window Scr = DefaultScreen(Dpy); Root = RootWindow(Dpy, Scr); // for tests we use a standard window if (testing) { Window RealRoot = RootWindow(Dpy, Scr); Root = XCreateSimpleWindow(Dpy, RealRoot, 0, 0, 1280, 1024, 0, 0, 0); XMapWindow(Dpy, Root); XFlush(Dpy); } else { blankScreen(); } HideCursor(); // Create panel LoginPanel = new Panel(Dpy, Scr, Root, cfg, themedir); bool firstloop = true; // 1st time panel is shown (for automatic username) bool focuspass = cfg->getOption("focus_password")=="yes"; bool autologin = cfg->getOption("auto_login")=="yes"; if (firstlogin && cfg->getOption("default_user") != "") { LoginPanel->SetName(cfg->getOption("default_user") ); #ifdef USE_PAM pam.set_item(PAM::Authenticator::User, cfg->getOption("default_user").c_str()); #endif firstlogin = false; if (autologin) { Login(); } } // Start looping int panelclosed = 1; Panel::ActionType Action; while(1) { if(panelclosed) { // Init root setBackground(themedir); // Close all clients if (!testing) { KillAllClients(False); KillAllClients(True); } // Show panel LoginPanel->OpenPanel(); } LoginPanel->Reset(); if (firstloop && cfg->getOption("default_user") != "") { LoginPanel->SetName(cfg->getOption("default_user") ); } if (!AuthenticateUser(focuspass && firstloop)){ panelclosed = 0; firstloop = false; LoginPanel->ClearPanel(); XBell(Dpy, 100); continue; } firstloop = false; Action = LoginPanel->getAction(); // for themes test we just quit if (testing) { Action = Panel::Exit; } panelclosed = 1; LoginPanel->ClosePanel(); switch(Action) { case Panel::Login: Login(); break; case Panel::Console: Console(); break; case Panel::Reboot: Reboot(); break; case Panel::Halt: Halt(); break; case Panel::Suspend: Suspend(); break; case Panel::Exit: Exit(); break; } } } #ifdef USE_PAM bool App::AuthenticateUser(bool focuspass){ // Reset the username try{ if (!focuspass) pam.set_item(PAM::Authenticator::User, 0); pam.authenticate(); } catch(PAM::Auth_Exception& e){ switch(LoginPanel->getAction()){ case Panel::Exit: case Panel::Console: return true; // <--- This is simply fake! default: break; }; cerr << APPNAME << ": " << e << endl; return false; } catch(PAM::Exception& e){ cerr << APPNAME << ": " << e << endl; exit(ERR_EXIT); }; return true; } #else bool App::AuthenticateUser(bool focuspass){ if (!focuspass){ LoginPanel->EventHandler(Panel::Get_Name); switch(LoginPanel->getAction()){ case Panel::Exit: case Panel::Console: cerr << APPNAME << ": Got a special command (" << LoginPanel->GetName() << ")" << endl; return true; // <--- This is simply fake! default: break; } } LoginPanel->EventHandler(Panel::Get_Passwd); char *encrypted, *correct; struct passwd *pw; switch(LoginPanel->getAction()){ case Panel::Suspend: case Panel::Halt: case Panel::Reboot: pw = getpwnam("root"); break; case Panel::Console: case Panel::Exit: case Panel::Login: pw = getpwnam(LoginPanel->GetName().c_str()); break; } endpwent(); if(pw == 0) return false; #ifdef HAVE_SHADOW struct spwd *sp = getspnam(pw->pw_name); endspent(); if(sp) correct = sp->sp_pwdp; else #endif // HAVE_SHADOW correct = pw->pw_passwd; if(correct == 0 || correct[0] == '\0') return true; encrypted = crypt(LoginPanel->GetPasswd().c_str(), correct); return ((strcmp(encrypted, correct) == 0) ? true : false); } #endif int App::GetServerPID() { return ServerPID; } // Hide the cursor void App::HideCursor() { if (cfg->getOption("hidecursor") == "true") { XColor black; char cursordata[1]; Pixmap cursorpixmap; Cursor cursor; cursordata[0]=0; cursorpixmap=XCreateBitmapFromData(Dpy,Root,cursordata,1,1); black.red=0; black.green=0; black.blue=0; cursor=XCreatePixmapCursor(Dpy,cursorpixmap,cursorpixmap,&black,&black,0,0); XDefineCursor(Dpy,Root,cursor); } } void App::Login() { struct passwd *pw; pid_t pid; #ifdef USE_PAM try{ pam.open_session(); pw = getpwnam(static_cast(pam.get_item(PAM::Authenticator::User))); } catch(PAM::Cred_Exception& e){ // Credentials couldn't be established cerr << APPNAME << ": " << e << endl; return; } catch(PAM::Exception& e){ cerr << APPNAME << ": " << e << endl; exit(ERR_EXIT); }; #else pw = getpwnam(LoginPanel->GetName().c_str()); #endif endpwent(); if(pw == 0) return; if (pw->pw_shell[0] == '\0') { setusershell(); strcpy(pw->pw_shell, getusershell()); endusershell(); } // Setup the environment char* term = getenv("TERM"); string maildir = _PATH_MAILDIR; maildir.append("/"); maildir.append(pw->pw_name); string xauthority = pw->pw_dir; xauthority.append("/.Xauthority"); #ifdef USE_PAM // Setup the PAM environment try{ if(term) pam.setenv("TERM", term); pam.setenv("HOME", pw->pw_dir); pam.setenv("SHELL", pw->pw_shell); pam.setenv("USER", pw->pw_name); pam.setenv("LOGNAME", pw->pw_name); pam.setenv("PATH", cfg->getOption("default_path").c_str()); pam.setenv("DISPLAY", DisplayName); pam.setenv("MAIL", maildir.c_str()); pam.setenv("XAUTHORITY", xauthority.c_str()); } catch(PAM::Exception& e){ cerr << APPNAME << ": " << e << endl; exit(ERR_EXIT); } #endif // Create new process pid = fork(); if(pid == 0) { #ifdef USE_PAM // Get a copy of the environment and close the child's copy // of the PAM-handle. char** child_env = pam.getenvlist(); pam.end(); #else const int Num_Of_Variables = 10; // Number of env. variables + 1 char** child_env = static_cast(malloc(sizeof(char*)*Num_Of_Variables)); int n = 0; if(term) child_env[n++]=StrConcat("TERM=", term); child_env[n++]=StrConcat("HOME=", pw->pw_dir); child_env[n++]=StrConcat("SHELL=", pw->pw_shell); child_env[n++]=StrConcat("USER=", pw->pw_name); child_env[n++]=StrConcat("LOGNAME=", pw->pw_name); child_env[n++]=StrConcat("PATH=", cfg->getOption("default_path").c_str()); child_env[n++]=StrConcat("DISPLAY=", DisplayName); child_env[n++]=StrConcat("MAIL=", maildir.c_str()); child_env[n++]=StrConcat("XAUTHORITY=", xauthority.c_str()); child_env[n++]=0; #endif // Login process starts here SwitchUser Su(pw, cfg, DisplayName, child_env); string session = LoginPanel->getSession(); string loginCommand = cfg->getOption("login_cmd"); replaceVariables(loginCommand, SESSION_VAR, session); replaceVariables(loginCommand, THEME_VAR, themeName); string sessStart = cfg->getOption("sessionstart_cmd"); if (sessStart != "") { replaceVariables(sessStart, USER_VAR, pw->pw_name); system(sessStart.c_str()); } Su.Login(loginCommand.c_str(), mcookie.c_str()); _exit(OK_EXIT); } #ifndef XNEST_DEBUG CloseLog(); #endif // Wait until user is logging out (login process terminates) pid_t wpid = -1; int status; while (wpid != pid) { wpid = wait(&status); } if (WIFEXITED(status) && WEXITSTATUS(status)) { LoginPanel->Message("Failed to execute login command"); sleep(3); } else { string sessStop = cfg->getOption("sessionstop_cmd"); if (sessStop != "") { replaceVariables(sessStop, USER_VAR, pw->pw_name); system(sessStop.c_str()); } } #ifdef USE_PAM try{ pam.close_session(); } catch(PAM::Exception& e){ cerr << APPNAME << ": " << e << endl; }; #endif // Close all clients KillAllClients(False); KillAllClients(True); // Send HUP signal to clientgroup killpg(pid, SIGHUP); // Send TERM signal to clientgroup, if error send KILL if(killpg(pid, SIGTERM)) killpg(pid, SIGKILL); HideCursor(); #ifndef XNEST_DEBUG // Re-activate log file OpenLog(); RestartServer(); #endif } void App::Reboot() { // Stop alarm clock alarm(0); #ifdef USE_PAM try{ pam.end(); } catch(PAM::Exception& e){ cerr << APPNAME << ": " << e << endl; }; #endif // Write message LoginPanel->Message((char*)cfg->getOption("reboot_msg").c_str()); sleep(3); // Stop server and reboot StopServer(); RemoveLock(); system(cfg->getOption("reboot_cmd").c_str()); exit(OK_EXIT); } void App::Halt() { // Stop alarm clock alarm(0); #ifdef USE_PAM try{ pam.end(); } catch(PAM::Exception& e){ cerr << APPNAME << ": " << e << endl; }; #endif // Write message LoginPanel->Message((char*)cfg->getOption("shutdown_msg").c_str()); sleep(3); // Stop server and halt StopServer(); RemoveLock(); system(cfg->getOption("halt_cmd").c_str()); exit(OK_EXIT); } void App::Suspend() { sleep(1); system(cfg->getOption("suspend_cmd").c_str()); } void App::Console() { int posx = 40; int posy = 40; int fontx = 9; int fonty = 15; int width = (XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)) - (posx * 2)) / fontx; int height = (XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)) - (posy * 2)) / fonty; // Execute console const char* cmd = cfg->getOption("console_cmd").c_str(); char *tmp = new char[strlen(cmd) + 60]; sprintf(tmp, cmd, width, height, posx, posy, fontx, fonty); system(tmp); delete [] tmp; } void App::Exit() { #ifdef USE_PAM try{ pam.end(); } catch(PAM::Exception& e){ cerr << APPNAME << ": " << e << endl; }; #endif if (testing) { const char* testmsg = "This is a test message :-)"; LoginPanel->Message(testmsg); sleep(3); delete LoginPanel; XCloseDisplay(Dpy); } else { delete LoginPanel; StopServer(); RemoveLock(); } delete cfg; exit(OK_EXIT); } int CatchErrors(Display *dpy, XErrorEvent *ev) { return 0; } void App::RestartServer() { #ifdef USE_PAM try{ pam.end(); } catch(PAM::Exception& e){ cerr << APPNAME << ": " << e << endl; }; #endif StopServer(); RemoveLock(); Run(); } void App::KillAllClients(Bool top) { Window dummywindow; Window *children; unsigned int nchildren; unsigned int i; XWindowAttributes attr; XSync(Dpy, 0); XSetErrorHandler(CatchErrors); nchildren = 0; XQueryTree(Dpy, Root, &dummywindow, &dummywindow, &children, &nchildren); if(!top) { for(i=0; i timeout) break; } if(i > 0) cerr << endl; lasttext = text; return (ServerPID != pidfound); } int App::WaitForServer() { int ncycles = 120; int cycles; for(cycles = 0; cycles < ncycles; cycles++) { if((Dpy = XOpenDisplay(DisplayName))) { return 1; } else { if(!ServerTimeout(1, (char *) "X server to begin accepting connections")) break; } } cerr << "Giving up." << endl; return 0; } int App::StartServer() { ServerPID = vfork(); static const int MAX_XSERVER_ARGS = 256; static char* server[MAX_XSERVER_ARGS+2] = { NULL }; server[0] = (char *)cfg->getOption("default_xserver").c_str(); string argOption = cfg->getOption("xserver_arguments"); /* Add mandatory -xauth option */ argOption = argOption + " -auth " + cfg->getOption("authfile"); char* args = new char[argOption.length()+2]; // NULL plus vt strcpy(args, argOption.c_str()); int argc = 1; int pos = 0; bool hasVtSet = false; while (args[pos] != '\0') { if (args[pos] == ' ' || args[pos] == '\t') { *(args+pos) = '\0'; server[argc++] = args+pos+1; } else if (pos == 0) { server[argc++] = args+pos; } ++pos; if (argc+1 >= MAX_XSERVER_ARGS) { // ignore _all_ arguments to make sure the server starts at // all argc = 1; break; } } for (int i=0; igetOption("numlock"); if (numlock == "on") { NumLock::setOn(); } else if (numlock == "off") { NumLock::setOff(); } delete args; return ServerPID; } jmp_buf CloseEnv; int IgnoreXIO(Display *d) { cerr << APPNAME << ": connection to X server lost." << endl; longjmp(CloseEnv, 1); } void App::StopServer() { // Stop alars clock and ignore signals alarm(0); signal(SIGQUIT, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGTERM, SIG_DFL); signal(SIGKILL, SIG_DFL); signal(SIGALRM, SIG_DFL); // Catch X error XSetIOErrorHandler(IgnoreXIO); if(!setjmp(CloseEnv) && Dpy) XCloseDisplay(Dpy); // Send HUP to process group errno = 0; if((killpg(getpid(), SIGHUP) != 0) && (errno != ESRCH)) cerr << APPNAME << ": can't send HUP to process group " << getpid() << endl; // Send TERM to server if(ServerPID < 0) return; errno = 0; if(killpg(ServerPID, SIGTERM) < 0) { if(errno == EPERM) { cerr << APPNAME << ": can't kill X server" << endl; exit(ERR_EXIT); } if(errno == ESRCH) return; } // Wait for server to shut down if(!ServerTimeout(10, (char *)"X server to shut down")) { cerr << endl; return; } cerr << endl << APPNAME << ": X server slow to shut down, sending KILL signal." << endl; // Send KILL to server errno = 0; if(killpg(ServerPID, SIGKILL) < 0) { if(errno == ESRCH) return; } // Wait for server to die if(ServerTimeout(3, (char*)"server to die")) { cerr << endl << APPNAME << ": can't kill server" << endl; exit(ERR_EXIT); } cerr << endl; } void App::blankScreen() { GC gc = XCreateGC(Dpy, Root, 0, 0); XSetForeground(Dpy, gc, BlackPixel(Dpy, Scr)); XFillRectangle(Dpy, Root, gc, 0, 0, XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr))); XFlush(Dpy); XFreeGC(Dpy, gc); } void App::setBackground(const string& themedir) { string filename; filename = themedir + "/background.png"; image = new Image; bool loaded = image->Read(filename.c_str()); if (!loaded){ // try jpeg if png failed filename = ""; filename = themedir + "/background.jpg"; loaded = image->Read(filename.c_str()); } if (loaded) { string bgstyle = cfg->getOption("background_style"); if (bgstyle == "stretch") { image->Resize(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr))); } else if (bgstyle == "tile") { image->Tile(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr))); } else if (bgstyle == "center") { string hexvalue = cfg->getOption("background_color"); hexvalue = hexvalue.substr(1,6); image->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)), hexvalue.c_str()); } else { // plain color or error string hexvalue = cfg->getOption("background_color"); hexvalue = hexvalue.substr(1,6); image->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)), hexvalue.c_str()); } Pixmap p = image->createPixmap(Dpy, Scr, Root); XSetWindowBackgroundPixmap(Dpy, Root, p); } XClearWindow(Dpy, Root); XFlush(Dpy); delete image; } // Check if there is a lockfile and a corresponding process void App::GetLock() { std::ifstream lockfile(cfg->getOption("lockfile").c_str()); if (!lockfile) { // no lockfile present, create one std::ofstream lockfile(cfg->getOption("lockfile").c_str(), ios_base::out); if (!lockfile) { cerr << APPNAME << ": Could not create lock file: " << cfg->getOption("lockfile").c_str() << std::endl; exit(ERR_EXIT); } lockfile << getpid() << std::endl; lockfile.close(); } else { // lockfile present, read pid from it int pid = 0; lockfile >> pid; lockfile.close(); if (pid > 0) { // see if process with this pid exists int ret = kill(pid, 0); if (ret == 0 || (ret == -1 && errno == EPERM) ) { cerr << APPNAME << ": Another instance of the program is already running with PID " << pid << std::endl; exit(0); } else { cerr << APPNAME << ": Stale lockfile found, removing it" << std::endl; std::ofstream lockfile(cfg->getOption("lockfile").c_str(), ios_base::out); if (!lockfile) { cerr << APPNAME << ": Could not create new lock file: " << cfg->getOption("lockfile") << std::endl; exit(ERR_EXIT); } lockfile << getpid() << std::endl; lockfile.close(); } } } } // Remove lockfile and close logs void App::RemoveLock() { remove(cfg->getOption("lockfile").c_str()); } // Redirect stdout and stderr to log file void App::OpenLog() { FILE *log = fopen (cfg->getOption("logfile").c_str(),"a"); if (!log) { cerr << APPNAME << ": Could not accesss log file: " << cfg->getOption("logfile") << endl; RemoveLock(); exit(ERR_EXIT); } fclose(log); freopen (cfg->getOption("logfile").c_str(),"a",stdout); setvbuf(stdout, NULL, _IOLBF, BUFSIZ); freopen (cfg->getOption("logfile").c_str(),"a",stderr); setvbuf(stderr, NULL, _IONBF, BUFSIZ); } // Relases stdout/err void App::CloseLog(){ fclose(stderr); fclose(stdout); } string App::findValidRandomTheme(const string& set) { // extract random theme from theme set; return empty string on error string name = set; struct stat buf; if (name[name.length()-1] == ',') { name = name.substr(0, name.length() - 1); } srandom(getpid()+time(NULL)); vector themes; string themefile; Cfg::split(themes, name, ','); do { int sel = random() % themes.size(); name = Cfg::Trim(themes[sel]); themefile = string(THEMESDIR) +"/" + name + THEMESFILE; if (stat(themefile.c_str(), &buf) != 0) { themes.erase(find(themes.begin(), themes.end(), name)); cerr << APPNAME << ": Invalid theme in config: " << name << endl; name = ""; } } while (name == "" && themes.size()); return name; } void App::replaceVariables(string& input, const string& var, const string& value) { string::size_type pos = 0; int len = var.size(); while ((pos = input.find(var, pos)) != string::npos) { input = input.substr(0, pos) + value + input.substr(pos+len); } } void App::CreateServerAuth() { /* create mit cookie */ int i, r; int hexcount = 0; string authfile; string cmd; const char *digits = "0123456789abcdef"; srand( time(NULL) ); for ( i = 0; i < 31; i++ ) { r = rand()%16; mcookie[i] = digits[r]; if (r>9) hexcount++; } /* MIT-COOKIE: even occurrences of digits and hex digits */ if ((hexcount%2) == 0) { r = rand()%10; } else { r = rand()%5+10; } mcookie[31] = digits[r]; /* reinitialize auth file */ authfile = cfg->getOption("authfile"); remove(authfile.c_str()); putenv(StrConcat("XAUTHORITY=", authfile.c_str())); cmd = cfg->getOption("xauth_path") + " -q -f " + authfile + " add :0 . " + mcookie; system(cmd.c_str()); } char* App::StrConcat(const char* str1, const char* str2) { char* tmp = new char[strlen(str1) + strlen(str2) + 1]; strcpy(tmp, str1); strcat(tmp, str2); return tmp; } void App::UpdatePid() { std::ofstream lockfile(cfg->getOption("lockfile").c_str(), ios_base::out); if (!lockfile) { cerr << APPNAME << ": Could not update lock file: " << cfg->getOption("lockfile").c_str() << std::endl; exit(ERR_EXIT); } lockfile << getpid() << std::endl; lockfile.close(); }