#-*- coding: utf-8 -*- # Copyright 2010 Calculate Ltd. http://www.calculate-linux.org # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os from os import path import re import sys import pwd import time import traceback from calculate.desktop._cl_keys import getKey, clearKey from datavars import DataVarsDesktop, DataVars, __version__,__app__ from calculate.lib.cl_template import (Template, ProgressTemplate, TemplatesError,templateFunction,iniParser) from calculate.lib.utils.files import runOsCommand, isMount,process, \ getRunCommands,STDOUT from calculate.lib.utils.common import getpathenv,appendProgramToEnvFile, \ removeProgramToEnvFile,mountEcryptfs, \ CommonError from calculate.core.server.func import safetyWrapper from calculate.lib.cl_lang import setLocalTranslate,getLazyLocalTranslate setLocalTranslate('cl_desktop3',sys.modules[__name__]) __ = getLazyLocalTranslate(_) from itertools import ifilter class DesktopError(Exception): """Desktop Error""" class Desktop: """ Desktop logic object Has fundamental method createHome for configure user profile """ # username userName = "" verbose = False def __init__(self): self.homeDir = "" self.clTempl = None self.clVars = None def createCryptDir(self,userName,uid,gud,userDir): """ Создать шифрование домашней директории, или подключить существующую """ userPwd = getKey(userName) if not userPwd or userPwd == "XXXXXXXX": raise DesktopError(_("User password not found")) ecryptfsPath = path.join('/home/.ecryptfs',userName) if path.exists(ecryptfsPath): for d in (".ecryptfs",".Private"): source,target = path.join(ecryptfsPath,d),path.join(userDir,d) if not path.lexists(target): os.symlink(source,target) try: if not mountEcryptfs(userName,userPwd,userDir): raise DesktopError(_("Failed to mount ecrypt")) except CommonError as e: raise DesktopError(_("Failed to mount ecrypt")+": \"%s\""%str(e)) else: e = process('/usr/bin/ecryptfs-setup-private','-u',userName,'-b','-l',userPwd,stderr=STDOUT) if e.failed(): raise DesktopError(e.read()+ _("Failed to create encrypt user profile")) def createUserDir(self, userName, uid, gid, userDir, mode=0700): """ Create user directory with need uid and gid """ if not path.exists(userDir): os.makedirs(userDir) if mode: os.chmod(userDir,mode) os.chown(userDir,uid,gid) return True else: raise DesktopError(_("Path %s exists") %userDir) def displayTemplatesApplied(self,dirsFiles): """ Display templates are applied (--verbose) """ self.printSUCCESS(_("The following files were changed")+":") for nameF in dirsFiles[1]: nameFile = nameF if nameFile[:1] != "/": nameFile = "/" + nameFile self.printSUCCESS(" "*5 + nameFile) def applyTemplatesFromUser(self): """Apply templates for user""" if self.clTempl: self.closeClTemplate() templates_locate = self.clVars.Get('cl_templates_locate') self.clVars.Set("cl_template_path", map(lambda x:x[1], filter(lambda x:x[0] in templates_locate, zip(self.clVars.Get('cl_template_location'), self.clVars.Get('cl_template_path')))), True) self.clTempl = ProgressTemplate(self.setProgress,self.clVars, cltObj=False, printERROR=self.printERROR, printWARNING=self.printWARNING, askConfirm=self.askConfirm, printSUCCESS=self.printSUCCESS, userProfile=True) dirsFiles = self.clTempl.applyTemplates() if self.clTempl.getError(): self.printERROR(self.clTempl.getError().strip()) return False else: return dirsFiles def initVars(self,datavars=None): """Primary variables initialization""" if not datavars: self.clVars = DataVarsDesktop() self.clVars.importDesktop() self.clVars.flIniFile() else: self.clVars = datavars def closeClTemplate(self): if self.clTempl: if self.clTempl.cltObj: self.clTempl.cltObj.closeFiles() self.clTempl.closeFiles() self.clTempl = None def umountUserRes(self, error): """ Umount user directory """ self.closeClTemplate() if error and self.homeDir: umountPaths = self.getMountUserPaths(self.homeDir) ret = True for umountPath in umountPaths: if not self.umountSleepPath(umountPath): ret = False break return ret @safetyWrapper(native_errors=(TemplatesError,DesktopError,CommonError), man_int=__("Configuration manually interrupted"), post_action=umountUserRes) def createHome(self, datavars=None): """ Creating user profile and userdir """ self.initVars(datavars) self.verbose = self.clVars.Get('cl_verbose_set') == 'on' #uid = os.getuid() #try: # realUserName = pwd.getpwuid(uid).pw_name #except: # realUserName = "" userName = self.clVars.Get("ur_login") uidGid = False if self.clVars.isModuleInstalled("client"): # domain host domain = self.clVars.Get("client.cl_remote_host") # authorized in domain or local hostAuth = self.clVars.Get("client.os_remote_auth") else: domain = "" hostAuth = "" uid = self.clVars.Get('ur_uid') gid = self.clVars.Get('ur_gid') if not uid or not gid: raise DesktopError(_("Failed to determine the user UID")) uid,gid = int(uid),int(gid) self.homeDir = self.clVars.Get('ur_home_path') rootPath = self.clVars.Get('cl_root_path') # real path to home dir self.homeDir = path.join(rootPath, self.homeDir[1:]) if not path.exists(self.homeDir): self.startTask(_("Creating the home directory for %s")%self.homeDir) self.createUserDir(userName,uid,gid,self.homeDir) self.endTask() if self.clVars.Get('ur_home_crypt_set') == 'on': self.createCryptDir(userName,uid,gid,self.homeDir) domainUser = self.clVars.Get('ur_domain_set') == 'on' lastTimestamp = templateFunction.getLastElog() iniEnv = path.join(self.homeDir,'.calculate/ini.env') userIni = iniParser(iniEnv) userTimestamp = userIni.getVar('main','elog').encode('utf-8') if (domainUser or not path.exists(iniEnv) or userTimestamp != lastTimestamp): # action - "user profile configuration" self.clVars.Set("cl_action", "desktop", True) # apply user profiles self.startTask(_("Setting up the user profile"),progress=True) dirsAndFiles = self.applyTemplatesFromUser() self.endTask() if not dirsAndFiles: raise DesktopError(_("Failed to apply user profile templates")) self.printSUCCESS(_("User account %s is configured")%userName + " ...") return True def getMountUserPaths(self, homeDir=False): """ Found user resources """ if not homeDir: userName = self.clVars.Get("ur_login") homeDir = self.clVars.Get("ur_home_path") if not homeDir: raise DesktopError(_("Failed to determine home directory")) dirStart, dirEnd = path.split(homeDir) mountProfileDir = path.join(dirStart, ".%s" %dirEnd) mountRemoteProfileDir = path.join(dirStart, ".%s.remote" %dirEnd) return filter(lambda x: x.startswith(homeDir) or\ x.startswith(mountProfileDir) or\ x.startswith(mountRemoteProfileDir), map(lambda x: x.split(" ")[1],\ open("/proc/mounts").readlines())) def execProg(self, cmdStrProg, inStr=False, envProg={}): """ Exec external program """ env_path = {"PATH":getpathenv()} env = {} env.update(os.environ.items() + env_path.items() + envProg.items()) retCode,programOut = runOsCommand(cmdStrProg,in_str=inStr,env_dict=env) if not retCode: return programOut return False def umountSleepPath(self, rpath): """ Unmount path, sleep by failed and repeat """ # check for mount if isMount(rpath): for waittime in [0,0.5,1,2]: time.sleep(waittime) if not self.execProg("umount %s"%rpath) is False \ or not isMount(rpath): if not isMount(rpath): return True self.execProg("fuser -km %s"%rpath) for waittime in [0.5, 0.5, 0.5, 0.5, 0.5, 0.5]: time.sleep(waittime) if not self.execProg("umount %s"%rpath) is False \ or not isMount(rpath): if not isMount(rpath): return True else: if isMount(rpath): self.printERROR(_("Failed to unmount directory %s")%rpath) return False return True @safetyWrapper(native_errors=(TemplatesError,DesktopError), man_int=__("Logout manually interrupted"), post_action=umountUserRes, success_message=__("The user logged out from the session!"), failed_message=__("Unable to logout the user")) def userLogout(self, datavars=None): """ Raise user logout throught dbus """ self.initVars(datavars) if not self.clVars.Get('ur_login') in \ self.clVars.Get('cl_desktop_online_user'): raise DesktopError(_("X session users not found")) urLogin = self.clVars.Get('ur_login') display = self.clVars.Select('cl_desktop_online_display', where='cl_desktop_online_user',eq=urLogin,limit=1) session = self.clVars.Get('cl_desktop_xsession') if session == 'xfce': logoutCommand = "org.xfce.SessionManager " \ "/org/xfce/SessionManager Logout False False" elif session == 'kde': logoutCommand = "org.kde.ksmserver /KSMServer logout 0 0 0" elif session == 'gnome': logoutCommand = "org.gnome.SessionManager " \ "/org/gnome/SessionManager Logout 1" else: raise DesktopError(_("Unable detect X session")) if process("su",urLogin,"-c", ("DISPLAY=:%s /usr/bin/qdbus "%display)+logoutCommand).failed(): raise DesktopError(_("Unable send logout command")) for i in range(0,20): if filter(lambda x: "xdm/xdm\x00--logout" in x, getRunCommands()): break time.sleep(0.5) if filter(lambda x: "xdm/xdm\x00--logout" in x, getRunCommands()): self.startTask(_("Waiting for completion of the user logout")) for i in range(0,300): if not filter(lambda x: "xdm/xdm\x00--logout" in x, getRunCommands()): return True time.sleep(1) self.endTask() else: raise DesktopError(_("Unable to wait for completion " "of the user logout")) if self.clVars.Get('ur_login') in \ self.clVars.Get('cl_desktop_online_user'): raise DesktopError(_("Wrong logout")) return True