app.cpp 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220
  1. /* SLiM - Simple Login Manager
  2. Copyright (C) 1997, 1998 Per Liden
  3. Copyright (C) 2004-06 Simone Rota <sip@varlock.com>
  4. Copyright (C) 2004-06 Johannes Winkelmann <jw@tks6.net>
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. */
  10. #include <sys/types.h>
  11. #include <sys/stat.h>
  12. #include <unistd.h>
  13. #include <fcntl.h>
  14. #include <stdint.h>
  15. #include <cstring>
  16. #include <cstdio>
  17. #include <iostream>
  18. #include <fstream>
  19. #include <sstream>
  20. #include <vector>
  21. #include <algorithm>
  22. #include "app.h"
  23. #include "numlock.h"
  24. #include "util.h"
  25. #ifdef HAVE_SHADOW
  26. #include <shadow.h>
  27. #endif
  28. using namespace std;
  29. #ifdef USE_PAM
  30. #include <string>
  31. int conv(int num_msg, const struct pam_message **msg,
  32. struct pam_response **resp, void *appdata_ptr){
  33. *resp = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response));
  34. Panel* panel = *static_cast<Panel**>(appdata_ptr);
  35. int result = PAM_SUCCESS;
  36. for (int i=0; i<num_msg; i++){
  37. resp[i]->resp=0;
  38. resp[i]->resp_retcode=0;
  39. switch(msg[i]->msg_style){
  40. case PAM_PROMPT_ECHO_ON:
  41. // We assume PAM is asking for the username
  42. panel->EventHandler(Panel::Get_Name);
  43. switch(panel->getAction()){
  44. case Panel::Suspend:
  45. case Panel::Halt:
  46. case Panel::Reboot:
  47. resp[i]->resp=strdup("root");
  48. break;
  49. case Panel::Console:
  50. case Panel::Exit:
  51. case Panel::Login:
  52. resp[i]->resp=strdup(panel->GetName().c_str());
  53. break;
  54. }
  55. break;
  56. case PAM_PROMPT_ECHO_OFF:
  57. // We assume PAM is asking for the password
  58. switch(panel->getAction()){
  59. case Panel::Console:
  60. case Panel::Exit:
  61. // We should leave now!
  62. result=PAM_CONV_ERR;
  63. break;
  64. default:
  65. panel->EventHandler(Panel::Get_Passwd);
  66. resp[i]->resp=strdup(panel->GetPasswd().c_str());
  67. break;
  68. }
  69. break;
  70. case PAM_ERROR_MSG:
  71. case PAM_TEXT_INFO:
  72. // We simply write these to the log
  73. // TODO: Maybe we should simply ignore them
  74. cerr << APPNAME << ": " << msg[i]->msg << endl;
  75. break;
  76. }
  77. if (result!=PAM_SUCCESS) break;
  78. }
  79. if (result!=PAM_SUCCESS){
  80. for (int i=0; i<num_msg; i++){
  81. if (resp[i]->resp==0) continue;
  82. free(resp[i]->resp);
  83. resp[i]->resp=0;
  84. };
  85. free(*resp);
  86. *resp=0;
  87. };
  88. return result;
  89. }
  90. #endif
  91. extern App* LoginApp;
  92. void CatchSignal(int sig) {
  93. cerr << APPNAME << ": unexpected signal " << sig << endl;
  94. if (LoginApp->serverStarted)
  95. LoginApp->StopServer();
  96. LoginApp->RemoveLock();
  97. exit(ERR_EXIT);
  98. }
  99. void AlarmSignal(int sig) {
  100. int pid = LoginApp->GetServerPID();
  101. if(waitpid(pid, NULL, WNOHANG) == pid) {
  102. LoginApp->StopServer();
  103. LoginApp->RemoveLock();
  104. exit(OK_EXIT);
  105. }
  106. signal(sig, AlarmSignal);
  107. alarm(2);
  108. }
  109. void User1Signal(int sig) {
  110. signal(sig, User1Signal);
  111. }
  112. #ifdef USE_PAM
  113. App::App(int argc, char** argv)
  114. : pam(conv, static_cast<void*>(&LoginPanel)),
  115. #else
  116. App::App(int argc, char** argv)
  117. :
  118. #endif
  119. mcookiesize(32) // Must be divisible by 4
  120. {
  121. int tmp;
  122. ServerPID = -1;
  123. testing = false;
  124. serverStarted = false;
  125. mcookie = string(App::mcookiesize, 'a');
  126. daemonmode = false;
  127. force_nodaemon = false;
  128. firstlogin = true;
  129. Dpy = NULL;
  130. // Parse command line
  131. // Note: we force a option for nodaemon switch to handle "-nodaemon"
  132. while((tmp = getopt(argc, argv, "vhp:n:d?")) != EOF) {
  133. switch (tmp) {
  134. case 'p': // Test theme
  135. testtheme = optarg;
  136. testing = true;
  137. if (testtheme == NULL) {
  138. cerr << "The -p option requires an argument" << endl;
  139. exit(ERR_EXIT);
  140. }
  141. break;
  142. case 'd': // Daemon mode
  143. daemonmode = true;
  144. break;
  145. case 'n': // Daemon mode
  146. daemonmode = false;
  147. force_nodaemon = true;
  148. break;
  149. case 'v': // Version
  150. std::cout << APPNAME << " version " << VERSION << endl;
  151. exit(OK_EXIT);
  152. break;
  153. case '?': // Illegal
  154. cerr << endl;
  155. case 'h': // Help
  156. cerr << "usage: " << APPNAME << " [option ...]" << endl
  157. << "options:" << endl
  158. << " -d: daemon mode" << endl
  159. << " -nodaemon: no-daemon mode" << endl
  160. << " -v: show version" << endl
  161. << " -p /path/to/theme/dir: preview theme" << endl;
  162. exit(OK_EXIT);
  163. break;
  164. }
  165. }
  166. if (getuid() != 0 && !testing) {
  167. cerr << APPNAME << ": only root can run this program" << endl;
  168. exit(ERR_EXIT);
  169. }
  170. }
  171. void App::Run() {
  172. DisplayName = DISPLAY;
  173. #ifdef XNEST_DEBUG
  174. char* p = getenv("DISPLAY");
  175. if (p && p[0]) {
  176. DisplayName = p;
  177. cout << "Using display name " << DisplayName << endl;
  178. }
  179. #endif
  180. // Read configuration and theme
  181. cfg = new Cfg;
  182. cfg->readConf(CFGFILE);
  183. string themebase = "";
  184. string themefile = "";
  185. string themedir = "";
  186. themeName = "";
  187. if (testing) {
  188. themeName = testtheme;
  189. } else {
  190. themebase = string(THEMESDIR) + "/";
  191. themeName = cfg->getOption("current_theme");
  192. string::size_type pos;
  193. if ((pos = themeName.find(",")) != string::npos) {
  194. // input is a set
  195. themeName = findValidRandomTheme(themeName);
  196. if (themeName == "") {
  197. themeName = "default";
  198. }
  199. }
  200. }
  201. #ifdef USE_PAM
  202. try{
  203. pam.start("slim");
  204. pam.set_item(PAM::Authenticator::TTY, DisplayName);
  205. pam.set_item(PAM::Authenticator::Requestor, "root");
  206. pam.set_item(PAM::Authenticator::Host, "localhost");
  207. }
  208. catch(PAM::Exception& e){
  209. cerr << APPNAME << ": " << e << endl;
  210. exit(ERR_EXIT);
  211. };
  212. #endif
  213. bool loaded = false;
  214. while (!loaded) {
  215. themedir = themebase + themeName;
  216. themefile = themedir + THEMESFILE;
  217. if (!cfg->readConf(themefile)) {
  218. if (themeName == "default") {
  219. cerr << APPNAME << ": Failed to open default theme file "
  220. << themefile << endl;
  221. exit(ERR_EXIT);
  222. } else {
  223. cerr << APPNAME << ": Invalid theme in config: "
  224. << themeName << endl;
  225. themeName = "default";
  226. }
  227. } else {
  228. loaded = true;
  229. }
  230. }
  231. if (!testing) {
  232. // Create lock file
  233. LoginApp->GetLock();
  234. // Start x-server
  235. setenv("DISPLAY", DisplayName, 1);
  236. signal(SIGQUIT, CatchSignal);
  237. signal(SIGTERM, CatchSignal);
  238. signal(SIGKILL, CatchSignal);
  239. signal(SIGINT, CatchSignal);
  240. signal(SIGHUP, CatchSignal);
  241. signal(SIGPIPE, CatchSignal);
  242. signal(SIGUSR1, User1Signal);
  243. signal(SIGALRM, AlarmSignal);
  244. #ifndef XNEST_DEBUG
  245. OpenLog();
  246. if (!force_nodaemon && cfg->getOption("daemon") == "yes") {
  247. daemonmode = true;
  248. }
  249. // Daemonize
  250. if (daemonmode) {
  251. if (daemon(0, 1) == -1) {
  252. cerr << APPNAME << ": " << strerror(errno) << endl;
  253. exit(ERR_EXIT);
  254. }
  255. UpdatePid();
  256. }
  257. CreateServerAuth();
  258. StartServer();
  259. alarm(2);
  260. #endif
  261. }
  262. // Open display
  263. if((Dpy = XOpenDisplay(DisplayName)) == 0) {
  264. cerr << APPNAME << ": could not open display '"
  265. << DisplayName << "'" << endl;
  266. if (!testing) StopServer();
  267. exit(ERR_EXIT);
  268. }
  269. // Get screen and root window
  270. Scr = DefaultScreen(Dpy);
  271. Root = RootWindow(Dpy, Scr);
  272. // for tests we use a standard window
  273. if (testing) {
  274. Window RealRoot = RootWindow(Dpy, Scr);
  275. Root = XCreateSimpleWindow(Dpy, RealRoot, 0, 0, 1280, 1024, 0, 0, 0);
  276. XMapWindow(Dpy, Root);
  277. XFlush(Dpy);
  278. } else {
  279. blankScreen();
  280. }
  281. HideCursor();
  282. // Create panel
  283. LoginPanel = new Panel(Dpy, Scr, Root, cfg, themedir);
  284. bool firstloop = true; // 1st time panel is shown (for automatic username)
  285. bool focuspass = cfg->getOption("focus_password")=="yes";
  286. bool autologin = cfg->getOption("auto_login")=="yes";
  287. if (firstlogin && cfg->getOption("default_user") != "") {
  288. LoginPanel->SetName(cfg->getOption("default_user") );
  289. #ifdef USE_PAM
  290. pam.set_item(PAM::Authenticator::User, cfg->getOption("default_user").c_str());
  291. #endif
  292. firstlogin = false;
  293. if (autologin) {
  294. Login();
  295. }
  296. }
  297. // Start looping
  298. int panelclosed = 1;
  299. Panel::ActionType Action;
  300. while(1) {
  301. if(panelclosed) {
  302. // Init root
  303. setBackground(themedir);
  304. // Close all clients
  305. if (!testing) {
  306. KillAllClients(False);
  307. KillAllClients(True);
  308. }
  309. // Show panel
  310. LoginPanel->OpenPanel();
  311. }
  312. LoginPanel->Reset();
  313. if (firstloop && cfg->getOption("default_user") != "") {
  314. LoginPanel->SetName(cfg->getOption("default_user") );
  315. }
  316. if (!AuthenticateUser(focuspass && firstloop)){
  317. panelclosed = 0;
  318. firstloop = false;
  319. LoginPanel->ClearPanel();
  320. XBell(Dpy, 100);
  321. continue;
  322. }
  323. firstloop = false;
  324. Action = LoginPanel->getAction();
  325. // for themes test we just quit
  326. if (testing) {
  327. Action = Panel::Exit;
  328. }
  329. panelclosed = 1;
  330. LoginPanel->ClosePanel();
  331. switch(Action) {
  332. case Panel::Login:
  333. Login();
  334. break;
  335. case Panel::Console:
  336. Console();
  337. break;
  338. case Panel::Reboot:
  339. Reboot();
  340. break;
  341. case Panel::Halt:
  342. Halt();
  343. break;
  344. case Panel::Suspend:
  345. Suspend();
  346. break;
  347. case Panel::Exit:
  348. Exit();
  349. break;
  350. }
  351. }
  352. }
  353. #ifdef USE_PAM
  354. bool App::AuthenticateUser(bool focuspass){
  355. // Reset the username
  356. try{
  357. if (!focuspass)
  358. pam.set_item(PAM::Authenticator::User, 0);
  359. pam.authenticate();
  360. }
  361. catch(PAM::Auth_Exception& e){
  362. switch(LoginPanel->getAction()){
  363. case Panel::Exit:
  364. case Panel::Console:
  365. return true; // <--- This is simply fake!
  366. default:
  367. break;
  368. };
  369. cerr << APPNAME << ": " << e << endl;
  370. return false;
  371. }
  372. catch(PAM::Exception& e){
  373. cerr << APPNAME << ": " << e << endl;
  374. exit(ERR_EXIT);
  375. };
  376. return true;
  377. }
  378. #else
  379. bool App::AuthenticateUser(bool focuspass){
  380. if (!focuspass){
  381. LoginPanel->EventHandler(Panel::Get_Name);
  382. switch(LoginPanel->getAction()){
  383. case Panel::Exit:
  384. case Panel::Console:
  385. cerr << APPNAME << ": Got a special command (" << LoginPanel->GetName() << ")" << endl;
  386. return true; // <--- This is simply fake!
  387. default:
  388. break;
  389. }
  390. }
  391. LoginPanel->EventHandler(Panel::Get_Passwd);
  392. char *encrypted, *correct;
  393. struct passwd *pw;
  394. switch(LoginPanel->getAction()){
  395. case Panel::Suspend:
  396. case Panel::Halt:
  397. case Panel::Reboot:
  398. pw = getpwnam("root");
  399. break;
  400. case Panel::Console:
  401. case Panel::Exit:
  402. case Panel::Login:
  403. pw = getpwnam(LoginPanel->GetName().c_str());
  404. break;
  405. }
  406. endpwent();
  407. if(pw == 0)
  408. return false;
  409. #ifdef HAVE_SHADOW
  410. struct spwd *sp = getspnam(pw->pw_name);
  411. endspent();
  412. if(sp)
  413. correct = sp->sp_pwdp;
  414. else
  415. #endif // HAVE_SHADOW
  416. correct = pw->pw_passwd;
  417. if(correct == 0 || correct[0] == '\0')
  418. return true;
  419. encrypted = crypt(LoginPanel->GetPasswd().c_str(), correct);
  420. return ((strcmp(encrypted, correct) == 0) ? true : false);
  421. }
  422. #endif
  423. int App::GetServerPID() {
  424. return ServerPID;
  425. }
  426. // Hide the cursor
  427. void App::HideCursor() {
  428. if (cfg->getOption("hidecursor") == "true") {
  429. XColor black;
  430. char cursordata[1];
  431. Pixmap cursorpixmap;
  432. Cursor cursor;
  433. cursordata[0]=0;
  434. cursorpixmap=XCreateBitmapFromData(Dpy,Root,cursordata,1,1);
  435. black.red=0;
  436. black.green=0;
  437. black.blue=0;
  438. cursor=XCreatePixmapCursor(Dpy,cursorpixmap,cursorpixmap,&black,&black,0,0);
  439. XDefineCursor(Dpy,Root,cursor);
  440. }
  441. }
  442. void App::Login() {
  443. struct passwd *pw;
  444. pid_t pid;
  445. #ifdef USE_PAM
  446. try{
  447. pam.open_session();
  448. pw = getpwnam(static_cast<const char*>(pam.get_item(PAM::Authenticator::User)));
  449. }
  450. catch(PAM::Cred_Exception& e){
  451. // Credentials couldn't be established
  452. cerr << APPNAME << ": " << e << endl;
  453. return;
  454. }
  455. catch(PAM::Exception& e){
  456. cerr << APPNAME << ": " << e << endl;
  457. exit(ERR_EXIT);
  458. };
  459. #else
  460. pw = getpwnam(LoginPanel->GetName().c_str());
  461. #endif
  462. endpwent();
  463. if(pw == 0)
  464. return;
  465. if (pw->pw_shell[0] == '\0') {
  466. setusershell();
  467. strcpy(pw->pw_shell, getusershell());
  468. endusershell();
  469. }
  470. // Setup the environment
  471. char* term = getenv("TERM");
  472. string maildir = _PATH_MAILDIR;
  473. maildir.append("/");
  474. maildir.append(pw->pw_name);
  475. string xauthority = pw->pw_dir;
  476. xauthority.append("/.Xauthority");
  477. #ifdef USE_PAM
  478. // Setup the PAM environment
  479. try{
  480. if(term) pam.setenv("TERM", term);
  481. pam.setenv("HOME", pw->pw_dir);
  482. pam.setenv("SHELL", pw->pw_shell);
  483. pam.setenv("USER", pw->pw_name);
  484. pam.setenv("LOGNAME", pw->pw_name);
  485. pam.setenv("PATH", cfg->getOption("default_path").c_str());
  486. pam.setenv("DISPLAY", DisplayName);
  487. pam.setenv("MAIL", maildir.c_str());
  488. pam.setenv("XAUTHORITY", xauthority.c_str());
  489. }
  490. catch(PAM::Exception& e){
  491. cerr << APPNAME << ": " << e << endl;
  492. exit(ERR_EXIT);
  493. }
  494. #endif
  495. // Create new process
  496. pid = fork();
  497. if(pid == 0) {
  498. #ifdef USE_PAM
  499. // Get a copy of the environment and close the child's copy
  500. // of the PAM-handle.
  501. char** child_env = pam.getenvlist();
  502. pam.end();
  503. #else
  504. const int Num_Of_Variables = 10; // Number of env. variables + 1
  505. char** child_env = static_cast<char**>(malloc(sizeof(char*)*Num_Of_Variables));
  506. int n = 0;
  507. if(term) child_env[n++]=StrConcat("TERM=", term);
  508. child_env[n++]=StrConcat("HOME=", pw->pw_dir);
  509. child_env[n++]=StrConcat("SHELL=", pw->pw_shell);
  510. child_env[n++]=StrConcat("USER=", pw->pw_name);
  511. child_env[n++]=StrConcat("LOGNAME=", pw->pw_name);
  512. child_env[n++]=StrConcat("PATH=", cfg->getOption("default_path").c_str());
  513. child_env[n++]=StrConcat("DISPLAY=", DisplayName);
  514. child_env[n++]=StrConcat("MAIL=", maildir.c_str());
  515. child_env[n++]=StrConcat("XAUTHORITY=", xauthority.c_str());
  516. child_env[n++]=0;
  517. #endif
  518. // Login process starts here
  519. SwitchUser Su(pw, cfg, DisplayName, child_env);
  520. string session = LoginPanel->getSession();
  521. string loginCommand = cfg->getOption("login_cmd");
  522. replaceVariables(loginCommand, SESSION_VAR, session);
  523. replaceVariables(loginCommand, THEME_VAR, themeName);
  524. string sessStart = cfg->getOption("sessionstart_cmd");
  525. if (sessStart != "") {
  526. replaceVariables(sessStart, USER_VAR, pw->pw_name);
  527. system(sessStart.c_str());
  528. }
  529. Su.Login(loginCommand.c_str(), mcookie.c_str());
  530. _exit(OK_EXIT);
  531. }
  532. #ifndef XNEST_DEBUG
  533. CloseLog();
  534. #endif
  535. // Wait until user is logging out (login process terminates)
  536. pid_t wpid = -1;
  537. int status;
  538. while (wpid != pid) {
  539. wpid = wait(&status);
  540. }
  541. if (WIFEXITED(status) && WEXITSTATUS(status)) {
  542. LoginPanel->Message("Failed to execute login command");
  543. sleep(3);
  544. } else {
  545. string sessStop = cfg->getOption("sessionstop_cmd");
  546. if (sessStop != "") {
  547. replaceVariables(sessStop, USER_VAR, pw->pw_name);
  548. system(sessStop.c_str());
  549. }
  550. }
  551. #ifdef USE_PAM
  552. try{
  553. pam.close_session();
  554. }
  555. catch(PAM::Exception& e){
  556. cerr << APPNAME << ": " << e << endl;
  557. };
  558. #endif
  559. // Close all clients
  560. KillAllClients(False);
  561. KillAllClients(True);
  562. // Send HUP signal to clientgroup
  563. killpg(pid, SIGHUP);
  564. // Send TERM signal to clientgroup, if error send KILL
  565. if(killpg(pid, SIGTERM))
  566. killpg(pid, SIGKILL);
  567. HideCursor();
  568. #ifndef XNEST_DEBUG
  569. // Re-activate log file
  570. OpenLog();
  571. RestartServer();
  572. #endif
  573. }
  574. void App::Reboot() {
  575. // Stop alarm clock
  576. alarm(0);
  577. #ifdef USE_PAM
  578. try{
  579. pam.end();
  580. }
  581. catch(PAM::Exception& e){
  582. cerr << APPNAME << ": " << e << endl;
  583. };
  584. #endif
  585. // Write message
  586. LoginPanel->Message((char*)cfg->getOption("reboot_msg").c_str());
  587. sleep(3);
  588. // Stop server and reboot
  589. StopServer();
  590. RemoveLock();
  591. system(cfg->getOption("reboot_cmd").c_str());
  592. exit(OK_EXIT);
  593. }
  594. void App::Halt() {
  595. // Stop alarm clock
  596. alarm(0);
  597. #ifdef USE_PAM
  598. try{
  599. pam.end();
  600. }
  601. catch(PAM::Exception& e){
  602. cerr << APPNAME << ": " << e << endl;
  603. };
  604. #endif
  605. // Write message
  606. LoginPanel->Message((char*)cfg->getOption("shutdown_msg").c_str());
  607. sleep(3);
  608. // Stop server and halt
  609. StopServer();
  610. RemoveLock();
  611. system(cfg->getOption("halt_cmd").c_str());
  612. exit(OK_EXIT);
  613. }
  614. void App::Suspend() {
  615. sleep(1);
  616. system(cfg->getOption("suspend_cmd").c_str());
  617. }
  618. void App::Console() {
  619. int posx = 40;
  620. int posy = 40;
  621. int fontx = 9;
  622. int fonty = 15;
  623. int width = (XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)) - (posx * 2)) / fontx;
  624. int height = (XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)) - (posy * 2)) / fonty;
  625. // Execute console
  626. const char* cmd = cfg->getOption("console_cmd").c_str();
  627. char *tmp = new char[strlen(cmd) + 60];
  628. sprintf(tmp, cmd, width, height, posx, posy, fontx, fonty);
  629. system(tmp);
  630. delete [] tmp;
  631. }
  632. void App::Exit() {
  633. #ifdef USE_PAM
  634. try{
  635. pam.end();
  636. }
  637. catch(PAM::Exception& e){
  638. cerr << APPNAME << ": " << e << endl;
  639. };
  640. #endif
  641. if (testing) {
  642. const char* testmsg = "This is a test message :-)";
  643. LoginPanel->Message(testmsg);
  644. sleep(3);
  645. delete LoginPanel;
  646. XCloseDisplay(Dpy);
  647. } else {
  648. delete LoginPanel;
  649. StopServer();
  650. RemoveLock();
  651. }
  652. delete cfg;
  653. exit(OK_EXIT);
  654. }
  655. int CatchErrors(Display *dpy, XErrorEvent *ev) {
  656. return 0;
  657. }
  658. void App::RestartServer() {
  659. #ifdef USE_PAM
  660. try{
  661. pam.end();
  662. }
  663. catch(PAM::Exception& e){
  664. cerr << APPNAME << ": " << e << endl;
  665. };
  666. #endif
  667. StopServer();
  668. RemoveLock();
  669. Run();
  670. }
  671. void App::KillAllClients(Bool top) {
  672. Window dummywindow;
  673. Window *children;
  674. unsigned int nchildren;
  675. unsigned int i;
  676. XWindowAttributes attr;
  677. XSync(Dpy, 0);
  678. XSetErrorHandler(CatchErrors);
  679. nchildren = 0;
  680. XQueryTree(Dpy, Root, &dummywindow, &dummywindow, &children, &nchildren);
  681. if(!top) {
  682. for(i=0; i<nchildren; i++) {
  683. if(XGetWindowAttributes(Dpy, children[i], &attr) && (attr.map_state == IsViewable))
  684. children[i] = XmuClientWindow(Dpy, children[i]);
  685. else
  686. children[i] = 0;
  687. }
  688. }
  689. for(i=0; i<nchildren; i++) {
  690. if(children[i])
  691. XKillClient(Dpy, children[i]);
  692. }
  693. XFree((char *)children);
  694. XSync(Dpy, 0);
  695. XSetErrorHandler(NULL);
  696. }
  697. int App::ServerTimeout(int timeout, char* text) {
  698. int i = 0;
  699. int pidfound = -1;
  700. static char *lasttext;
  701. for(;;) {
  702. pidfound = waitpid(ServerPID, NULL, WNOHANG);
  703. if(pidfound == ServerPID)
  704. break;
  705. if(timeout) {
  706. if(i == 0 && text != lasttext)
  707. cerr << endl << APPNAME << ": waiting for " << text;
  708. else
  709. cerr << ".";
  710. }
  711. if(timeout)
  712. sleep(1);
  713. if(++i > timeout)
  714. break;
  715. }
  716. if(i > 0)
  717. cerr << endl;
  718. lasttext = text;
  719. return (ServerPID != pidfound);
  720. }
  721. int App::WaitForServer() {
  722. int ncycles = 120;
  723. int cycles;
  724. for(cycles = 0; cycles < ncycles; cycles++) {
  725. if((Dpy = XOpenDisplay(DisplayName))) {
  726. return 1;
  727. } else {
  728. if(!ServerTimeout(1, (char *) "X server to begin accepting connections"))
  729. break;
  730. }
  731. }
  732. cerr << "Giving up." << endl;
  733. return 0;
  734. }
  735. int App::StartServer() {
  736. ServerPID = vfork();
  737. static const int MAX_XSERVER_ARGS = 256;
  738. static char* server[MAX_XSERVER_ARGS+2] = { NULL };
  739. server[0] = (char *)cfg->getOption("default_xserver").c_str();
  740. string argOption = cfg->getOption("xserver_arguments");
  741. /* Add mandatory -xauth option */
  742. argOption = argOption + " -auth " + cfg->getOption("authfile");
  743. char* args = new char[argOption.length()+2]; // NULL plus vt
  744. strcpy(args, argOption.c_str());
  745. serverStarted = false;
  746. int argc = 1;
  747. int pos = 0;
  748. bool hasVtSet = false;
  749. while (args[pos] != '\0') {
  750. if (args[pos] == ' ' || args[pos] == '\t') {
  751. *(args+pos) = '\0';
  752. server[argc++] = args+pos+1;
  753. } else if (pos == 0) {
  754. server[argc++] = args+pos;
  755. }
  756. ++pos;
  757. if (argc+1 >= MAX_XSERVER_ARGS) {
  758. // ignore _all_ arguments to make sure the server starts at
  759. // all
  760. argc = 1;
  761. break;
  762. }
  763. }
  764. for (int i=0; i<argc; i++) {
  765. if (server[i][0] == 'v' && server[i][1] == 't') {
  766. bool ok = false;
  767. Cfg::string2int(server[i]+2, &ok);
  768. if (ok) {
  769. hasVtSet = true;
  770. }
  771. }
  772. }
  773. if (!hasVtSet && daemonmode) {
  774. server[argc++] = (char*)"vt07";
  775. }
  776. server[argc] = NULL;
  777. switch(ServerPID) {
  778. case 0:
  779. signal(SIGTTIN, SIG_IGN);
  780. signal(SIGTTOU, SIG_IGN);
  781. signal(SIGUSR1, SIG_IGN);
  782. setpgid(0,getpid());
  783. execvp(server[0], server);
  784. cerr << APPNAME << ": X server could not be started" << endl;
  785. exit(ERR_EXIT);
  786. break;
  787. case -1:
  788. break;
  789. default:
  790. errno = 0;
  791. if(!ServerTimeout(0, (char *)"")) {
  792. ServerPID = -1;
  793. break;
  794. }
  795. alarm(15);
  796. pause();
  797. alarm(0);
  798. // Wait for server to start up
  799. if(WaitForServer() == 0) {
  800. cerr << APPNAME << ": unable to connect to X server" << endl;
  801. StopServer();
  802. ServerPID = -1;
  803. exit(ERR_EXIT);
  804. }
  805. break;
  806. }
  807. string numlock = cfg->getOption("numlock");
  808. if (numlock == "on") {
  809. NumLock::setOn();
  810. } else if (numlock == "off") {
  811. NumLock::setOff();
  812. }
  813. delete args;
  814. serverStarted = true;
  815. return ServerPID;
  816. }
  817. jmp_buf CloseEnv;
  818. int IgnoreXIO(Display *d) {
  819. cerr << APPNAME << ": connection to X server lost." << endl;
  820. longjmp(CloseEnv, 1);
  821. }
  822. void App::StopServer() {
  823. // Stop alars clock and ignore signals
  824. alarm(0);
  825. signal(SIGQUIT, SIG_IGN);
  826. signal(SIGINT, SIG_IGN);
  827. signal(SIGHUP, SIG_IGN);
  828. signal(SIGPIPE, SIG_IGN);
  829. signal(SIGTERM, SIG_DFL);
  830. signal(SIGKILL, SIG_DFL);
  831. signal(SIGALRM, SIG_DFL);
  832. // Catch X error
  833. XSetIOErrorHandler(IgnoreXIO);
  834. if(!setjmp(CloseEnv) && Dpy)
  835. XCloseDisplay(Dpy);
  836. // Send HUP to process group
  837. errno = 0;
  838. if((killpg(getpid(), SIGHUP) != 0) && (errno != ESRCH))
  839. cerr << APPNAME << ": can't send HUP to process group " << getpid() << endl;
  840. // Send TERM to server
  841. if(ServerPID < 0)
  842. return;
  843. errno = 0;
  844. if(killpg(ServerPID, SIGTERM) < 0) {
  845. if(errno == EPERM) {
  846. cerr << APPNAME << ": can't kill X server" << endl;
  847. exit(ERR_EXIT);
  848. }
  849. if(errno == ESRCH)
  850. return;
  851. }
  852. // Wait for server to shut down
  853. if(!ServerTimeout(10, (char *)"X server to shut down")) {
  854. cerr << endl;
  855. return;
  856. }
  857. cerr << endl << APPNAME << ": X server slow to shut down, sending KILL signal." << endl;
  858. // Send KILL to server
  859. errno = 0;
  860. if(killpg(ServerPID, SIGKILL) < 0) {
  861. if(errno == ESRCH)
  862. return;
  863. }
  864. // Wait for server to die
  865. if(ServerTimeout(3, (char*)"server to die")) {
  866. cerr << endl << APPNAME << ": can't kill server" << endl;
  867. exit(ERR_EXIT);
  868. }
  869. cerr << endl;
  870. }
  871. void App::blankScreen()
  872. {
  873. GC gc = XCreateGC(Dpy, Root, 0, 0);
  874. XSetForeground(Dpy, gc, BlackPixel(Dpy, Scr));
  875. XFillRectangle(Dpy, Root, gc, 0, 0,
  876. XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)),
  877. XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)));
  878. XFlush(Dpy);
  879. XFreeGC(Dpy, gc);
  880. }
  881. void App::setBackground(const string& themedir) {
  882. string filename;
  883. filename = themedir + "/background.png";
  884. image = new Image;
  885. bool loaded = image->Read(filename.c_str());
  886. if (!loaded){ // try jpeg if png failed
  887. filename = "";
  888. filename = themedir + "/background.jpg";
  889. loaded = image->Read(filename.c_str());
  890. }
  891. if (loaded) {
  892. string bgstyle = cfg->getOption("background_style");
  893. if (bgstyle == "stretch") {
  894. image->Resize(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)));
  895. } else if (bgstyle == "tile") {
  896. image->Tile(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)));
  897. } else if (bgstyle == "center") {
  898. string hexvalue = cfg->getOption("background_color");
  899. hexvalue = hexvalue.substr(1,6);
  900. image->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)),
  901. hexvalue.c_str());
  902. } else { // plain color or error
  903. string hexvalue = cfg->getOption("background_color");
  904. hexvalue = hexvalue.substr(1,6);
  905. image->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)),
  906. hexvalue.c_str());
  907. }
  908. Pixmap p = image->createPixmap(Dpy, Scr, Root);
  909. XSetWindowBackgroundPixmap(Dpy, Root, p);
  910. }
  911. XClearWindow(Dpy, Root);
  912. XFlush(Dpy);
  913. delete image;
  914. }
  915. // Check if there is a lockfile and a corresponding process
  916. void App::GetLock() {
  917. std::ifstream lockfile(cfg->getOption("lockfile").c_str());
  918. if (!lockfile) {
  919. // no lockfile present, create one
  920. std::ofstream lockfile(cfg->getOption("lockfile").c_str(), ios_base::out);
  921. if (!lockfile) {
  922. cerr << APPNAME << ": Could not create lock file: " << cfg->getOption("lockfile").c_str() << std::endl;
  923. exit(ERR_EXIT);
  924. }
  925. lockfile << getpid() << std::endl;
  926. lockfile.close();
  927. } else {
  928. // lockfile present, read pid from it
  929. int pid = 0;
  930. lockfile >> pid;
  931. lockfile.close();
  932. if (pid > 0) {
  933. // see if process with this pid exists
  934. int ret = kill(pid, 0);
  935. if (ret == 0 || (ret == -1 && errno == EPERM) ) {
  936. cerr << APPNAME << ": Another instance of the program is already running with PID " << pid << std::endl;
  937. exit(0);
  938. } else {
  939. cerr << APPNAME << ": Stale lockfile found, removing it" << std::endl;
  940. std::ofstream lockfile(cfg->getOption("lockfile").c_str(), ios_base::out);
  941. if (!lockfile) {
  942. cerr << APPNAME << ": Could not create new lock file: " << cfg->getOption("lockfile") << std::endl;
  943. exit(ERR_EXIT);
  944. }
  945. lockfile << getpid() << std::endl;
  946. lockfile.close();
  947. }
  948. }
  949. }
  950. }
  951. // Remove lockfile and close logs
  952. void App::RemoveLock() {
  953. remove(cfg->getOption("lockfile").c_str());
  954. }
  955. // Redirect stdout and stderr to log file
  956. void App::OpenLog() {
  957. FILE *log = fopen (cfg->getOption("logfile").c_str(),"a");
  958. if (!log) {
  959. cerr << APPNAME << ": Could not accesss log file: " << cfg->getOption("logfile") << endl;
  960. RemoveLock();
  961. exit(ERR_EXIT);
  962. }
  963. fclose(log);
  964. freopen (cfg->getOption("logfile").c_str(),"a",stdout);
  965. setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
  966. freopen (cfg->getOption("logfile").c_str(),"a",stderr);
  967. setvbuf(stderr, NULL, _IONBF, BUFSIZ);
  968. }
  969. // Relases stdout/err
  970. void App::CloseLog(){
  971. fclose(stderr);
  972. fclose(stdout);
  973. }
  974. string App::findValidRandomTheme(const string& set)
  975. {
  976. // extract random theme from theme set; return empty string on error
  977. string name = set;
  978. struct stat buf;
  979. if (name[name.length()-1] == ',') {
  980. name = name.substr(0, name.length() - 1);
  981. }
  982. Util::srandom(Util::makeseed());
  983. vector<string> themes;
  984. string themefile;
  985. Cfg::split(themes, name, ',');
  986. do {
  987. int sel = Util::random() % themes.size();
  988. name = Cfg::Trim(themes[sel]);
  989. themefile = string(THEMESDIR) +"/" + name + THEMESFILE;
  990. if (stat(themefile.c_str(), &buf) != 0) {
  991. themes.erase(find(themes.begin(), themes.end(), name));
  992. cerr << APPNAME << ": Invalid theme in config: "
  993. << name << endl;
  994. name = "";
  995. }
  996. } while (name == "" && themes.size());
  997. return name;
  998. }
  999. void App::replaceVariables(string& input,
  1000. const string& var,
  1001. const string& value)
  1002. {
  1003. string::size_type pos = 0;
  1004. int len = var.size();
  1005. while ((pos = input.find(var, pos)) != string::npos) {
  1006. input = input.substr(0, pos) + value + input.substr(pos+len);
  1007. }
  1008. }
  1009. /*
  1010. * We rely on the fact that all bits generated by Util::random()
  1011. * are usable, so we are taking full words from its output.
  1012. */
  1013. void App::CreateServerAuth() {
  1014. /* create mit cookie */
  1015. uint16_t word;
  1016. uint8_t hi, lo;
  1017. int i;
  1018. string authfile;
  1019. const char *digits = "0123456789abcdef";
  1020. Util::srandom(Util::makeseed());
  1021. for (i = 0; i < App::mcookiesize; i+=4) {
  1022. word = Util::random() & 0xffff;
  1023. lo = word & 0xff;
  1024. hi = word >> 8;
  1025. mcookie[i] = digits[lo & 0x0f];
  1026. mcookie[i+1] = digits[lo >> 4];
  1027. mcookie[i+2] = digits[hi & 0x0f];
  1028. mcookie[i+3] = digits[hi >> 4];
  1029. }
  1030. /* reinitialize auth file */
  1031. authfile = cfg->getOption("authfile");
  1032. remove(authfile.c_str());
  1033. putenv(StrConcat("XAUTHORITY=", authfile.c_str()));
  1034. Util::add_mcookie(mcookie, ":0", cfg->getOption("xauth_path"),
  1035. authfile);
  1036. }
  1037. char* App::StrConcat(const char* str1, const char* str2) {
  1038. char* tmp = new char[strlen(str1) + strlen(str2) + 1];
  1039. strcpy(tmp, str1);
  1040. strcat(tmp, str2);
  1041. return tmp;
  1042. }
  1043. void App::UpdatePid() {
  1044. std::ofstream lockfile(cfg->getOption("lockfile").c_str(), ios_base::out);
  1045. if (!lockfile) {
  1046. cerr << APPNAME << ": Could not update lock file: " << cfg->getOption("lockfile").c_str() << std::endl;
  1047. exit(ERR_EXIT);
  1048. }
  1049. lockfile << getpid() << std::endl;
  1050. lockfile.close();
  1051. }