source: src-qt4/PCDM/src/pcdm-backend.cpp @ 577dd9b

9.2-releasereleng/10.0releng/10.0.1releng/10.0.2releng/10.0.3
Last change on this file since 577dd9b was 577dd9b, checked in by Ken Moore <ken@…>, 14 months ago

Setup the PCDM main session process to run fork and setsid to start the process.
Also add the beginning of the changes to the xprocess class to change over to running C process commands rather than using a QProcess for starting the desktop session.
You just need to change the USECLIBS flag at the top of main.cpp to switch which one to use.

  • Property mode set to 100644
File size: 13.8 KB
Line 
1/* PCDM Login Manager:
2*  Written by Ken Moore (ken@pcbsd.org) 2012/2013
3*  Copyright(c) 2013 by the PC-BSD Project
4*  Available under the 3-clause BSD license
5*/
6
7#include <QProcess>
8#include <QProcessEnvironment>
9
10#include "pcdm-backend.h"
11#include "pcdm-config.h"
12#include "pcbsd-utils.h"
13
14QStringList displaynameList,usernameList,homedirList,instXNameList,instXBinList,instXCommentList,instXIconList;
15QString logFile;
16QString saveX,saveUsername, lastUser, lastDE;
17
18QStringList Backend::getAvailableDesktops(){ 
19  if(instXNameList.isEmpty()){ loadXSessionsData(); }
20  QStringList out = instXNameList;
21  return out;
22}
23
24QString Backend::getDesktopComment(QString xName){
25  if(instXNameList.isEmpty()){ loadXSessionsData(); }
26  int index = instXNameList.indexOf(xName);
27  if(index == -1){ Backend::log("PCDM: Invalid Desktop Name: " + xName); return ""; }
28  return instXCommentList[index];
29}
30
31QString Backend::getDesktopIcon(QString xName){
32  if(instXNameList.isEmpty()){ loadXSessionsData(); }
33  int index = instXNameList.indexOf(xName);
34  if(index == -1){ Backend::log("PCDM: Invalid Desktop Name: " +xName); return ""; }
35  return instXIconList[index];
36}
37
38QString Backend::getDesktopBinary(QString xName){
39  if(instXNameList.isEmpty()){ loadXSessionsData(); }
40  int index = instXNameList.indexOf(xName);
41  if(index == -1){ Backend::log("PCDM: Invalid Desktop Name: " + xName); return ""; }
42  return instXBinList[index];
43}
44
45QStringList Backend::getSystemUsers(){
46  if(usernameList.isEmpty()){
47    readSystemUsers();
48  }
49  return displaynameList;
50}
51
52QString Backend::getALUsername(){
53  //Make sure the requested user is valid
54  readSystemUsers(); //first read the available users on this system
55  QString ruser = Config::autoLoginUsername();
56  int index = usernameList.indexOf(ruser);
57  if(index == -1){ //invalid username
58    //Check if a display name was given instead
59    index = displaynameList.indexOf(ruser);
60    if(index == -1){ //invalid display name
61      log("Invalid Auto-Login user requested - skipping....");
62      ruser.clear();
63    }else{
64      //use the valid username for the given display name
65      ruser = usernameList[index]; 
66    }
67  }
68  return ruser;
69}
70
71QString Backend::getALPassword(){
72  QString rpassword = Config::autoLoginPassword();
73  return rpassword;
74}
75
76QString Backend::getUsernameFromDisplayname(QString dspname){
77  int i = displaynameList.indexOf(dspname);
78  if(i == -1){ i = usernameList.indexOf(dspname); }
79 
80  if(i == -1){ return ""; }
81  else{ return usernameList[i]; }
82}
83
84QString Backend::getDisplayNameFromUsername(QString username){
85  int i = usernameList.indexOf(username);
86  return displaynameList[i]; 
87}
88
89QString Backend::getUserHomeDir(QString username){
90  int i = usernameList.indexOf(username);
91  if( i == -1 ){ i = displaynameList.indexOf(username); }
92  return homedirList[i];
93}
94
95QStringList Backend::keyModels()
96{
97    QStringList _models;
98    QString code, desc, line;
99
100    Process p(QStringList() << "xkeyboard-models");
101
102    if (p.waitForFinished()) {
103        while (p.canReadLine()) {
104            line = p.readLine();
105            code = line;
106            code.truncate(line.indexOf(" "));
107            desc = line.remove(0, line.indexOf(" "));
108            _models.append(desc.simplified() + " - (" + code.simplified() + ")");
109        }
110    }
111    return _models;
112}
113
114QStringList Backend::keyLayouts()
115{
116    QStringList _layouts;
117    QString code, desc, line;
118
119    Process p(QStringList() << "xkeyboard-layouts");
120
121    if (p.waitForFinished()) {
122        while (p.canReadLine()) {
123            line = p.readLine();
124            code = line;
125            code.truncate(line.indexOf(" "));
126            desc = line.remove(0, line.indexOf(" "));
127            _layouts.append(desc.simplified() + " - (" + code.simplified() + ")");
128        }
129    }
130    return _layouts;
131}
132
133// Function which gets the key Variants for the target layout
134QStringList Backend::keyVariants(const QString &layout, QStringList &savedKeyVariants)
135{
136    QStringList _variants;
137    QString code, desc, line;
138
139    if ( savedKeyVariants.empty() )
140    {
141      Process p(QStringList() << "xkeyboard-variants");
142      if (p.waitForFinished()) {
143        while (p.canReadLine()) {
144            line = p.readLine();
145            savedKeyVariants << line;
146        }
147      }
148    }
149
150    for (int i = 0; i < savedKeyVariants.size(); ++i) {
151       // Look for variants for this particular layout
152       line = savedKeyVariants.at(i);
153       if ( line.indexOf(" " + layout + ":") != -1 )
154       {
155         code = line.simplified();
156         code.truncate(code.indexOf(" "));
157         desc = line.remove(0, line.indexOf(": ") + 1);
158         _variants.append(desc.simplified() + " - (" + code.simplified() + ")");
159       }
160    }
161
162    return _variants;
163}
164
165// Function which lets us run setxkbmap
166void Backend::changeKbMap(QString model, QString layout, QString variant)
167{
168   QProcess kbp;
169   QStringList args;
170   QString prog;
171   prog = "setxkbmap"; 
172   args << "-model" << model << "-layout" << layout << "-variant" << variant;
173   Backend::log("setxkbmap: " + args.join(" "));
174   kbp.start(prog, args);
175   kbp.waitForFinished();
176}
177
178QStringList Backend::languages()
179{
180    QStringList _languages;
181    QString code, desc, line;
182
183    QFile mFile;
184    mFile.setFileName("/usr/share/pc-sysinstall/conf/avail-langs");
185    if ( ! mFile.open(QIODevice::ReadOnly | QIODevice::Text))
186       return QStringList();
187
188    // Read in the meta-file for categories
189    QTextStream in(&mFile);
190    in.setCodec("UTF-8");
191    while ( !in.atEnd() ) {
192       line = in.readLine();
193       code = line;
194       code.truncate(line.indexOf(" "));
195       desc = line.remove(0, line.indexOf(" "));
196        _languages.append(desc.simplified() + " - (" + code.simplified() + ")");
197    }
198    mFile.close();
199    return _languages;
200}
201
202void Backend::openLogFile(QString logFilePath){
203  //If a log file exists, remove it
204  if(QFile::exists(logFilePath)){ QFile::remove(logFilePath); }
205  //save the path to the logfile
206  logFile = logFilePath;
207}
208
209void Backend::log(QString line){
210  QFile lFile(logFile);
211  lFile.open(QIODevice::Append);
212  QTextStream out(&lFile);
213  out << line << "\n";
214  lFile.close();
215}
216
217void Backend::checkLocalDirs(){
218  //Check for directories first
219  QString base = "/usr/local/share/PCDM";
220  QDir mainDir(base);
221  if(!mainDir.exists()){ mainDir.mkdir(base); }
222  if(!mainDir.exists("themes")){ mainDir.mkdir("themes"); }
223  //Check for sample files
224  if(!mainDir.exists("pcdm.conf.sample")){ QFile::copy(":samples/pcdm.conf",base+"/pcdm.conf.sample"); } 
225}
226
227QString Backend::getLastUser(){
228  //Load the file if necessary
229  if(lastUser.isEmpty()){
230    readSystemLastLogin(); 
231  }
232  //return the value
233  return lastUser;
234}
235
236QString Backend::getLastDE(QString user){
237  if(lastDE.isEmpty()){
238    readSystemLastLogin();
239  }
240  QString de = readUserLastDesktop(user);
241  if(de.isEmpty()){ return lastDE; }
242  else{ return de; }
243 
244}
245
246void Backend::saveLoginInfo(QString user, QString desktop){
247  writeSystemLastLogin(user,desktop); //save the system file (/usr/local/share/PCDM/.lastlogin)
248  writeUserLastDesktop(user,desktop); //save the user file (~/.lastlogin)
249}
250
251//****** PRIVATE FUNCTIONS ******
252
253void Backend::loadXSessionsData(){
254  //Clear the current variables
255  instXNameList.clear(); instXBinList.clear(); 
256  instXCommentList.clear(); instXIconList.clear();
257  //Load the default paths/locale
258  QString xDir = Config::xSessionsDir();
259  if(!xDir.endsWith("/")){ xDir.append("/"); }
260  QString xIconDir = Config::xSessionsImageDir();
261  if(!xIconDir.endsWith("/")){ xIconDir.append("/"); }
262  QString localeCode = QLocale().name(); //gets the current locale code
263  //Find all *.desktop files
264  QDir dir(xDir);
265  QStringList deFiles = dir.entryList(QDir::Files);
266  deFiles = deFiles.filter(".desktop"); //only get *.desktop files
267  //Read each file to see if that desktop is installed
268  for(int i=0; i<deFiles.length(); i++){
269    QStringList tmp = readXSessionsFile(xDir+deFiles[i],localeCode);
270    //tmp[exec, name, comment, icon, tryexec]
271    if(!tmp.isEmpty()){
272      //Complete file paths if necessary
273      //if(!tmp[0].startsWith("/")){ tmp[0] = "/usr/local/bin/"+tmp[0]; }
274      if(!tmp[3].startsWith("/")&&!tmp[3].startsWith(":")&&!tmp[3].isEmpty()){ tmp[3] = xIconDir+tmp[3]; }
275      if(!tmp[4].startsWith("/")){ tmp[4] = "/usr/local/bin/"+tmp[4]; }
276      //Check for valid DE using the "tryexec" line
277        //this allows for special startup commands on the "exec" line
278      if(QFile::exists(tmp[4])){
279        //Add the DE to list of installed xsessions
280        instXBinList << tmp[0];
281        instXNameList << tmp[1];
282        instXCommentList << tmp[2];
283        //Check to make sure we have a valid icon
284        if(!tmp[3].isEmpty() && !QFile::exists(tmp[3]) ){ tmp[3] = ""; }
285        instXIconList << tmp[3];
286        Backend::log( "PCDM: Found xsession: " + tmp.join(" ") );
287      }
288    }
289  }
290  if(instXNameList.isEmpty()){
291    //Create an entry so that we know this function has been run already
292    instXNameList << "None";
293    instXBinList << "none";
294    instXCommentList << "No xSession Environments available in" << xDir;
295    instXIconList << ":images/nodesktop.png";
296  }
297}
298
299QStringList Backend::readXSessionsFile(QString filePath, QString locale){
300//output: [Exec, Localized Name, Localized Comment, Icon, TryExec]
301  QString name, lname, comm, lcomm, icon, exec, tryexec;
302  QStringList output;
303  QString lna = "Name["+locale+"]"; //variable to look at for localized name
304  QString lco = "Comment["+locale+"]"; //variable to look at for localized comment
305  QFile file(filePath);
306  if(file.open(QIODevice::ReadOnly | QIODevice::Text)){
307    QTextStream in(&file);
308    while (!in.atEnd()){
309      QString line = in.readLine().simplified();
310      QString var = line.section("=",0,0,QString::SectionSkipEmpty).simplified();
311      QString val = line.section("=",1,50,QString::SectionSkipEmpty).simplified();
312      if(var.toLower()=="exec"){ exec = val; }
313      else if(var.toLower()=="tryexec"){ tryexec = val; }
314      else if(var.toLower()=="icon"){ icon = val; }
315      else if(var.toLower()=="name"){ name = val; }
316      else if(var.toLower()=="comment"){ comm = val; }
317      else if(var==lna){ lname = val; }
318      else if(var==lco){ lcomm = val; }
319      else{} //do nothing with other lines
320
321    }
322  }
323  //Use the unlocalized name/comment if localized values not detected
324  if(lname.isEmpty()){ lname = name; }
325  if(lcomm.isEmpty()){ lcomm = comm; }
326  //Make sure that we have a name/exec for the session, otherwise invalid file
327  if(lname.isEmpty() || exec.isEmpty() || tryexec.isEmpty()){ return output; }
328  //Check that there is an icon given
329  if(icon.isEmpty()){
330    //Try to use a built in icon if a known DE
331    if(name.toLower().contains("gnome")){icon = ":images/gnome.png"; }
332    if(name.toLower().contains("kde")){icon = ":images/kde.png"; }
333    if(name.toLower().contains("xfce")){icon = ":images/xfce.png"; }
334    if(name.toLower().contains("lxde")){icon = ":images/lxde.png"; }
335    if(name.toLower().contains("fluxbox")){icon = ":images/fluxbox.png"; }
336    if(name.toLower().contains("openbox")){icon = ":images/openbox.png"; }
337  }
338  //Format the results into the output list
339  output.clear();
340  output << exec << lname << lcomm << icon << tryexec;
341  return output;
342
343}
344
345void Backend::readSystemUsers(){
346  //make sure the lists are empty
347  usernameList.clear(); displaynameList.clear(); homedirList.clear();
348  //Get all the users from the file "/etc/passwd"
349  QStringList uList = pcbsd::Utils::runShellCommand("cat /etc/passwd");
350
351  //Remove all users that have:
352  for(int i=0; i<uList.length(); i++){
353    bool bad = FALSE;
354    // "nologin" as their shell
355    if(uList[i].section(":",6,6).contains("nologin")){bad=TRUE;}
356    // "nonexistent" as their user directory
357    else if(uList[i].section(":",5,5).contains("nonexistent")){bad=TRUE;}
358    // uid > 1000
359    else if(uList[i].section(":",2,2).toInt() < 1000){bad=TRUE;}
360
361    //See if it failed any checks
362    if(bad){ uList.removeAt(i); i--; }
363    else{
364      //Add this user to the lists if it is good
365      usernameList << uList[i].section(":",0,0).simplified();
366      displaynameList << uList[i].section(":",4,4).simplified();
367      homedirList << uList[i].section(":",5,5).simplified();
368    }
369  }
370 
371}
372
373void Backend::readSystemLastLogin(){
374    if(!QFile::exists("/usr/local/share/PCDM/.lastlogin")){
375      lastUser.clear();
376      Backend::log("PCDM: No previous login data found");
377    }else{
378      //Load the previous login data
379      QFile file("/usr/local/share/PCDM/.lastlogin");
380      if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
381        Backend::log("PCDM: Unable to open previous login data file");   
382      }else{
383        QTextStream in(&file);
384        lastUser= in.readLine();
385        lastDE= in.readLine();
386        file.close();
387      }
388    } 
389}
390
391void Backend::writeSystemLastLogin(QString user, QString desktop){
392  QFile file1("/usr/local/share/PCDM/.lastlogin");
393  if(!file1.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)){
394    Backend::log("PCDM: Unable to save last login data to system directory");     
395  }else{
396    QTextStream out(&file1);
397    out << user << "\n" << desktop;
398    file1.close();
399  }
400
401}
402
403QString Backend::readUserLastDesktop(QString user){
404  QString desktop;
405  QString LLpath = Backend::getUserHomeDir(user) + "/.lastlogin";
406  if(!QFile::exists(LLpath)){
407    Backend::log("PCDM: No previous user login data found for user: "+user);
408  }else{
409    //Load the previous login data
410    QFile file(LLpath);
411    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
412      Backend::log("PCDM: Unable to open previous user login file: "+user);   
413    }else{
414      QTextStream in(&file);
415      desktop = in.readLine();
416      file.close();
417    }
418  }
419  return desktop;
420}
421
422void Backend::writeUserLastDesktop(QString user, QString desktop){
423  QFile file2( Backend::getUserHomeDir(user) + "/.lastlogin" );
424  if(!file2.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)){
425    Backend::log("PCDM: Unable to save last login data for user:"+user);         
426  }else{
427    QTextStream out(&file2);
428    out << desktop;
429    file2.close();
430  }
431}
Note: See TracBrowser for help on using the repository browser.