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

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

Make sure that PCDM sets the USER and SHELL environment variables appropriately when starting up a user session.

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