#-*- coding: utf-8 -*- # Copyright 2008-2010 Mir 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 import re import sys import cl_base import cl_profile import cl_utils2 import cl_utils import ldap import types import getpass import _cl_keys import time import stat import subprocess from encrypt import encrypt from signal import SIGTERM Version = "calculate-client 2.1.18" tr = cl_base.lang() tr.setLanguage(sys.modules[__name__]) pcs = cl_utils.prettyColumnStr class printNoColor: def colorPrint(self,attr,fg,bg,string): sys.stdout.write(string) class NoneProgressBar: """Abstract class of progress bar. It do nothing.""" def __init__(self,title,dialog=None): pass def openDialog(self,title,max=None): """Create dialog with progress bar, prepare date""" pass def shutdownDialog(self): """Destroy dialog""" pass def setValue(self,value): """Set value of progress bar relative of maximum""" pass def setMaximum(self,max): """Set maximum value of progress bar""" pass def setTitle(self,title): """Set title of progress dialog""" pass class GProgressBar(NoneProgressBar): """GProgressBar uses Xdialog program for display progressbar.""" def __init__(self,title,xdialog=None): self.title = title self.bgPID = 0 self.value = 0 self.pipein = None if xdialog == None: self.openDialog(self.title) def openDialog(self,title,max=None): if max != None: self.setMaximum(max) title = re.sub("<[^>]+>", "", title) self.title = title if os.system('which Xdialog &>/dev/null') == 0: pipe = subprocess.Popen('/usr/bin/Xdialog --progress "%s" 6 80'\ %(self.title), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, env=os.environ, shell=True) self.pipein = pipe.stdin self._set(1) pipe.stdout.close() pipe.stderr.close() def shutdownDialog(self): '''Принудительно уничтожить процесс dialog''' if self.pipein: self._set(100) time.sleep(0.1) self.pipein.close() self.pipein = None def _set(self,value): if self.pipein: self.pipein.write("%02d\n"%value) self.pipein.flush() def setValue(self,value): '''Установить текущее значения для прогресса''' if self.pipein and value <= self.max: progress_value = int(value / self.kmax) if progress_value > int(self.value / self.kmax) and progress_value < 100: self._set(progress_value) self.value = value def setMaximum(self,max): '''Установить максимальное значения для прогресса''' self.max = max self.kmax = (max / 100.0) or 1.0 def setTitle(self,title): '''Установить описания прогресса''' pass class KProgressBar(NoneProgressBar): """KProgressBar uses kdialog program and dbus for display progressbar.""" suffixSet = 'org.freedesktop.DBus.Properties.Set \ org.kde.kdialog.ProgressDialog' execenv = {"HOME":"/root"} max = 100 kdialog = None label = None def __init__(self,title,kdialog=None): self.title = title self.value = 0 if kdialog is None: self.openDialog(self.title) def openDialog(self,title,max=None): if max != None: self.max = max self.title = title if os.system('which kdialog >/dev/null') == 0: self.label ="LOGINKDIALOG=%d" % os.getpid() env = {} env.update(os.environ.items() + self.execenv.items() +\ [tuple(self.label.split("="))]) pipe = subprocess.Popen('/usr/bin/kdialog --progressbar "%s" %d'\ %(" "*(len(title)+20), self.max), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, env=env, shell=True) pipe.stdin.close() # wait for terminate kdialog, which say dbus dialog id if pipe.poll() is None: # ожидание в 5 сек for t in range(500): time.sleep(0.01) if pipe.poll() != None: break # waiting is ok if pipe.poll() == 0: self.kdialog = pipe.stdout.readline().strip() while not "org.kde.kdialog" in self.kdialog: s = pipe.stdout.readline() # if bad result of kdialog then shutdown dialog if s == "": pipe.stdout.close() pipe.stderr.close() self.shutdownDialog() break self.kdialog = s.strip() self.setTitle(self.title) pipe.stdout.close() pipe.stderr.close() # waiting is failed else: pipe.stdout.close() pipe.stderr.close() self.shutdownDialog() def shutdownDialog(self): '''Принудительно уничтожить процесс kdialog''' self.kdialog = None pipe = subprocess.Popen("/bin/ps axeo pid,cmd", stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, shell=True) if self.label != None: for s in pipe.stdout.readlines(): if self.label in s: try: os.kill( int(s.split()[0]), 9 ) except (OSError,ValueError): pass self.label = None def setValue(self,value): '''Установить текущее значения для прогресса''' if self.kdialog and value <= self.max: env = "" if self.execenv: env = " ".join(map(lambda x: '%s="%s"'%(x[0],x[1]),\ self.execenv)) + " " oldperc = int(self.value / self.kmax) newperc = int(value / self.kmax) if newperc > oldperc: os.system(env + '/usr/bin/qdbus %s %s value %d >/dev/null'\ %(self.kdialog,self.suffixSet, newperc)) self.value = value def setMaximum(self,max): '''Установить максимальное значения для прогресса''' if self.kdialog and self.max == 0 and max != 0: env = "" if self.execenv: env = " ".join(map(lambda x: '%s="%s"'%(x[0],x[1]),\ self.execenv)) + " " os.system(env + '/usr/bin/qdbus %s %s maximum %d >/dev/null'\ %(self.kdialog,self.suffixSet, 100)) self.max = max self.kmax = (max / 100.0) or 1.0 def setTitle(self,title): '''Установить описания прогресса''' self.title = title if self.kdialog: env = "" if self.execenv: env = " ".join(map(lambda x: '%s="%s"'%(x[0],x[1]),\ self.execenv)) + " " os.system(env + '/usr/bin/qdbus %s setLabelText "%s" >/dev/null'\ %(self.kdialog,self.title)) def ProgressBar(*args,**kwarg): """Return instance of object for progress bar""" if os.system('which kdialog &>/dev/null') == 0: return KProgressBar(*args,**kwarg) elif os.system('which Xdialog &>/dev/null') == 0: return GProgressBar(*args,**kwarg) return NoneProgressBar(*args,**kwarg) class ProgressProfile(cl_profile.profile): def __init__(self, vars): cl_profile.profile.__init__(self,vars) self.progress = ProgressBar(_("Setting up user profile") + " ...") def numberAllProfiles(self, number): self.progress.setMaximum(number) return True def numberProcessProfiles(self,number): self.progress.setValue(number) return True def close(self): self.progress.shutdownDialog() class RsyncProgressBar: '''Объект запуска rsync для получения количества созданных файлов и при необходимости вывода progressbar ''' # получение номера получаемого файла из потока rsync receiverre = re.compile("recv_generator\(.+,([0-9]+)\)", re.S) pipe = None maximum = 1 copyStarting = False def __init__(self, title, secondtitle, rsyncstr, maximum=1): self.title = title self.secondtitle = secondtitle self.maximum = maximum self.rsyncstr = rsyncstr self.progress = ProgressBar(title,1) self.progress.setMaximum(self.maximum) def getFilesNum(self): '''Получить количество файлов созданных генератором''' if self.pipe: return self.value def getExitCode(self): '''Получить код выхода rsync''' if self.pipe: return os.WEXITSTATUS(self.pipe.wait()) def runsilent(self): '''Запустить rsync без progressbar''' self.pipe = subprocess.Popen(self.rsyncstr, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, shell=True) while True: s = self.pipe.stdout.readline() if len(s) == 0: break q = self.receiverre.search(s) if q: self.value = int(q.groups()[0]) def run(self): '''Запустить rsync с progressbar''' self.progress.openDialog(self.title,0) self.pipe = subprocess.Popen(self.rsyncstr, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, shell=True) oldpercent = 0 while True: s = self.pipe.stdout.readline() if len(s) == 0: break q = self.receiverre.search(s) if q: if not self.copyStarting: self.progress.shutdownDialog() self.progress.openDialog(self.secondtitle) self.progress.setMaximum(self.maximum) self.copyStarting = True value = int(q.groups()[0]) self.progress.setValue(value) def close(self): self.progress.shutdownDialog() # Импортированные классы в cl_ldap # Запись ошибок imp_cl_err = cl_profile._error # Работа с XML imp_cl_xml = cl_profile.xmlShare # Обработка параметров командной строки imp_cl_help = cl_utils2.cl_help # Форматированный вывод imp_cl_smcon = cl_utils2.cl_smartcon class cl_client(imp_cl_err, imp_cl_xml, imp_cl_help, imp_cl_smcon, encrypt): """Основной класс для работы клиентских приложений""" # Пути к профилям объединяемых с системными # относительный путь при объединении '/' rootProfilePaths=['/usr/lib/calculate/calculate-client/profiles', '/var/calculate/remote/profiles', '/var/calculate/profiles'] def __init__(self, cmdName): # объект для форматированного вывода imp_cl_help.__init__(self, cmdName) servName = "" if "user" in cmdName: servName = _("user") self.chapter = [\ # расположение разделов на странице # имя раздела, видимый или невидимый, кол. "\n" после # названия раздела, кол. "\n" после раздела # тип раздела ("Copyright",False,0,2,""), (_("Usage"),True,0,1,""), ("Function",False,0,2,""), (_("Examples"),True,1,1,""), (_("Common options"),True,1,0,"options"), ] # имена используемых программ и их номера для доступа к переменным # self.data self.progName = { 'cl-client':0, 'cl-createhome':1, 'cl-sync':2, 'cl-passwd':3 } # Cвязь длинных опций помощи и выводимых разделов помощи с опциями self.relOptions = {"h":[_("Common options")],} # список разделов, которые на наличие в ней информации # используется для автоматического отображения/скрытия # опций help-имя # Пример: self.relOption = # { "help-all":[_("Common options"], _("Unix service options"), # _("Samba service options"), _("LDAP service options")}] # self.relChapterPass = (_("Common options"),) # это означается что опция будет активна, если только в разделах # кроме Common options есть хоть одна доступная опция. self.relChapterPass = (_("Common options"),) self.data = [\ { #Copyright #'progAccess':(3,), 'helpChapter':"Copyright", 'help':Version }, #Usage { 'progAccess':(0,), 'helpChapter':_("Usage"), 'help': cmdName + " [" + _("options") + "] " + _("domain") }, { 'progAccess':(1,), 'helpChapter':_("Usage"), 'help': cmdName + " " + _("user") }, { 'progAccess':(2,), 'helpChapter':_("Usage"), 'help': cmdName + " [" + _("options") + "] " + _("user") }, { 'progAccess':(3,), 'helpChapter':_("Usage"), 'help': cmdName }, # Function { 'progAccess':(0,), 'helpChapter':"Function", 'help':_("Changes settings for connecting to domain \ (calculate-server)") }, { 'progAccess':(1,), 'helpChapter':"Function", 'help':_("Create home directory for the new user account") }, { 'progAccess':(2,), 'helpChapter':"Function", 'help':_("Mounting resources and synchronize the user preferences") }, { 'progAccess':(3,), 'helpChapter':"Function", 'help':_("Change user password") }, # Examples { 'progAccess':(0,), 'helpChapter':_("Examples"), 'help':pcs( " cl-client 192.168.0.1", self.column_width, "# " + _("Adds settings for connecting to domain") + \ " (ip 192.168.0.1)", self.consolewidth-self.column_width ) }, { 'progAccess':(3,), 'helpChapter':_("Examples"), 'help':pcs(" cl-passwd", self.column_width, "# "+_("change password of user for Samba and Unix services")+".", self.consolewidth-self.column_width) }, # Options {'shortOption':"h", 'longOption':"help", 'helpChapter':_("Common options"), 'help':_("display this help and exit") }, {'progAccess':(0,), 'shortOption':"r", 'helpChapter':_("Common options"), 'help':_("remove the settings for connecting to a domain") }, {'progAccess':(2,), 'longOption':'progress', 'helpChapter':_("Common options"), 'help':_("show progress bar for kde startup (works only with options \ --login)") }, {'progAccess':(1,), 'longOption':'progress', 'helpChapter':_("Common options"), 'help':_("show progress bar for kde startup") }, {'progAccess':(0,1,2,3), 'longOption':"vars", 'optVal':_("TYPE_VAR"), 'helpChapter':_("Common options"), 'help':_("print variables (TYPE_VAR - all:full var)") }, {'progAccess':(0,1,2,3), 'longOption':"color", 'optVal':_("WHEN"), 'helpChapter':_("Common options"), 'help':_("control whether color is used. \ WHEN may be 'never', 'always', or 'auto'") }, {'progAccess':(1,), 'shortOption':"f", 'longOption':"force", 'helpChapter':_("Common options"), 'help':_("always join the user profiles and preferences") }, {'progAccess':(0,), 'longOption':"install", 'helpChapter':_("Common options"), 'help':_("add use of scripts this package for window manager") }, {'progAccess':(0,), 'longOption':"uninstall", 'helpChapter':_("Common options"), 'help':_("remove use of scripts this package for window manager and, \ if necessary, removes from domain") }, {'progAccess':(0,), 'longOption':"mount", 'helpChapter':_("Common options"), 'help':_("mount [remote] resource for Samba (calculate-server)") }, {'progAccess':(2,), 'longOption':"login", 'helpChapter':_("Common options"), 'help':_("mount user resource")+ " " +\ _("and synchronize the user preferences") }, {'progAccess':(2,), 'longOption':"logout", 'helpChapter':_("Common options"), 'help':_("synchronize the user preferences") + " " +\ _("and umount user resource") }, {'progAccess':(2,), 'longOption':"nosync", 'helpChapter':_("Common options"), 'help':_("not synchronize the user preferences, is used in \ conjunction with the 'login' or 'logout'") } ] self._cl_help__setParamHelp() # Удаляем ненужный аттрибут класса cl_profile.xmlShare self._createElement = False delattr(self, "_createElement") # Переменная объект Vars self.clVars = False # Переменная объект ldapFunction self.ldapObj = False # Переменная соединение с LDAP сервером self.conLdap = False # Базовый DN LDAP сервера self.baseDN = False # DN сервисов относительно базового ServicesDN = "ou=Services" relGrDN = 'ou=Groups' relUsDN = 'ou=Users' relServDN = 'ou=Unix' relDN = self.addDN(relServDN,ServicesDN) # DN пользователей, относительно базового DN self.relUsersDN = self.addDN(relUsDN, relDN) # DN групп, относительно базового DN self.relGroupsDN = self.addDN(relGrDN, relDN) # DN Samba групп self.relSambaGroupsDN = self.addDN(relGrDN,"ou=Samba", ServicesDN) # Объект хранения переменных self.clVars = False # файл с дополнительной информацией о профиле # на данный момент только количество файлов для rsync # используемое для прогрессбара self.configFileDesktop = "desktop.env" self.configFileServer = "server.env" # Файл - список файлов профиля пользователя self.listProfFile = ".calculate/files.txt" # Путь относительно домашней директории в котором находятся # конфигурационные файлы self.pathConfig = ".calculate" # При включениии репликации # Временные задержки для монтирования в секундах self.sleeps = [0.5, 2, 5] # Название ветки хранения последнего посещенного пользователя self.replBranchName = "Worked" # Аттрибут ветки хранения последнего посещенного пользователя self.replAttr = "ou" # DN системной ветки self.sysDN = self.addDN("ou=Replication","ou=LDAP", ServicesDN) replBranch = self.replAttr + "=" + self.replBranchName # DN хранения последнего посещенного пользователя self.replHostsDN = self.addDN(replBranch,self.sysDN) self.logOutFile = ".logout" self.skipHomeFile = ["Home","Disks","FTP", self.logOutFile, os.path.join(self.pathConfig,self.configFileDesktop)] # Если атрибут установлен то значит (ошибка и отмонтируются # пользовательские ресурсы) self.errorAndUnmountUserRes = False # Имя пользователя self.userName = "" def getUserPwd(self, options, optDialog, optStdIn, pwDialog=False): """Получить пароль у пользователя options - полученные опции командной строки optDialog - опция командной строки для вывода диалога для получения пароля optStdIn - опция командной строки для получения пароля из стандартного ввода (stdin) pwDialog - структура для вывода приглашения в режиме диалога """ userPwd = "" if optStdIn and options.has_key(optStdIn): pwdA = sys.stdin.readline().rstrip() pwdB = sys.stdin.readline().rstrip() elif optDialog and options.has_key(optDialog): if not pwDialog: pwDialog = [_("New password"), _("Retype new password")] pwdA = getpass.getpass(pwDialog[0]+":") pwdB = getpass.getpass(pwDialog[1]+":") if (optStdIn and options.has_key(optStdIn)) or\ (optDialog and options.has_key(optDialog)): if not pwdA or not (pwdA == pwdB): self.printERROR (_("ERROR") + ": " +\ _("passwords do not match")) return False userPwd = pwdA return userPwd def exit(self, exitCode): """Метод выхода при ошибке""" self.errorExit() sys.exit(exitCode) def errorExit(self): """Отмонтирование пользовательских ресурсов при ошибке""" if self.errorAndUnmountUserRes and self.userName: # Установка ошибки в профиль пользователя на сервере # и отмонтирование ресурсов self.umountUserResNoSync(self.userName, False, False, False, True, flagErrorSync=True) def __del__(self): """Выполняется при удалении объекта""" self.errorExit() def isRoot(self, printError=True): """Определяет является ли пользователь root""" if os.getuid() == 0 and os.getgid() == 0: return True else: if printError: self.printERROR(_("The user is not root")) return False def createClVars(self, clVars=False): """Создает объект Vars""" if not clVars: clVars = cl_base.DataVars() clVars.flClient() clVars.flIniFile() # Устанавливаем у объекта объект Vars self.clVars = clVars return True def addDN(self, *arg): """Складывает текстовые элементы DN""" DNs = [] for dn in arg: if dn: DNs.append(dn) return ','.join(DNs) def searchLdapDN(self, name, relDN, attr, retAttr=None): """Находит DN в LDAP""" baseDN = self.clVars.Get("ld_base_dn") DN = self.addDN(relDN,baseDN) #searchScope = ldap.SCOPE_SUBTREE searchScope = ldap.SCOPE_ONELEVEL searchFilter = "%s=%s" %(attr,name) retrieveAttributes = retAttr resSearch = self.ldapObj.ldapSearch(DN, searchScope, searchFilter, retrieveAttributes) return resSearch def searchPrevHost(self, userName): """Находит сервер к которому был подключен пользователь""" # Короткое имя системы osLinuxShort = self.clVars.Get("os_linux_shortname") # Имя для поиска в служебной ветке репликации userLogin = "%s@%s"%(userName,osLinuxShort) resSearch = self.searchLdapDN(userLogin, self.replHostsDN, "uid") return resSearch def searchReplBranch(self): """Находит ветку создаваемую при включении репликации В ветке находятся ((имя пользователя, имя системы, хост) ....) """ resSearch = self.searchLdapDN(self.replBranchName, self.sysDN, self.replAttr) return resSearch def searchUnixUser(self, userName): """Находит пользователя сервиса Unix""" resSearch = self.searchLdapDN(userName, self.relUsersDN, "uid") return resSearch def searchUnixGid(self, groupId): """Находит группу сервиса Unix по ёе id в ветке Unix""" resSearch = self.searchLdapDN(str(groupId), self.relGroupsDN, "gidNumber") return resSearch def searchSambaGid(self, groupId): """Находит группу сервиса Unix по ёе id в ветке Samba""" resSearch = self.searchLdapDN(str(groupId), self.relSambaGroupsDN, "gidNumber") return resSearch def getLdapObjBind(self, host, printError=True): """Получаем объект ldapFunction Соединяемся пользователем bind В выходном объекте есть соединение с LDAP сервером: self.conLdap """ self.createClVars(self.clVars) bindDn = self.clVars.Get("ld_bind_dn") bindPw = self.clVars.Get("ld_bind_pw") if not (bindDn or bindPw): if printError: self.printERROR(_("Not found LDAP bind DN or password") +\ " ...") return False ldapObj = cl_utils2.ldapFun(bindDn, bindPw, host) if ldapObj.getError(): if printError: self.printERROR (_("LDAP connect error") + ": " +\ ldapObj.getError().strip()) return False # Устанавливаем у объекта соединение и объект LDAP функций self.ldapObj = ldapObj self.conLdap = ldapObj.conLdap return True def searchLineInFile(self, name, fileName, numEl=0): """Ищет строку в которой есть название разделенное ':' в файле похожем на /etc/passwd""" if os.path.exists(fileName): FD = open(fileName) lines = FD.readlines() FD.close() lineFound = "" for line in lines: if name == line.split(":")[numEl]: lineFound = line break if lineFound: return lineFound else: return False def searchPasswdUser(self, userName): """Ищет пользователей в /etc/passwd""" filePasswd = "/etc/passwd" return self.searchLineInFile(userName, filePasswd) def searchGroupGid(self, groupId): """Ищет gid в /etc/group""" gid = str(groupId) fileGroup = "/etc/group" return self.searchLineInFile(gid, fileGroup, 2) def getUserLdapInfo(self, userName, printError=True): """Выдаем uid и gid пользователя""" searchUser = self.searchUnixUser(userName) if not searchUser: if printError: self.printERROR(_("User %s not found in Unix service")\ %str(userName)) return False uid = False gid = False fullName = "" mail = "" jid = "" group = "" if searchUser[0][0][1].has_key('uidNumber'): uid = searchUser[0][0][1]['uidNumber'][0] if searchUser[0][0][1].has_key('gidNumber'): gid = searchUser[0][0][1]['gidNumber'][0] searchGroup = self.searchUnixGid(gid) if searchGroup and searchGroup[0][0][1].has_key('cn'): group = searchGroup[0][0][1]['cn'][0] else: searchGroup = self.searchSambaGid(gid) if searchGroup and searchGroup[0][0][1].has_key('cn'): group = searchGroup[0][0][1]['cn'][0] if searchUser[0][0][1].has_key('cn'): fullName = searchUser[0][0][1]['cn'][0] if searchUser[0][0][1].has_key('mail'): mail = searchUser[0][0][1]['mail'][0] if searchUser[0][0][1].has_key('registeredAddress'): jid = searchUser[0][0][1]['registeredAddress'][0] if searchUser[0][0][1].has_key('homeDirectory'): home = searchUser[0][0][1]['homeDirectory'][0] if uid and gid: return (uid, gid, fullName, mail, jid ,home, group) else: return () def createUserDir(self, uid, gid, userDir, mode=0700): """Создание пользовательской директории""" if not os.path.exists(userDir): os.makedirs(userDir) if mode: os.chmod(userDir,mode) os.chown(userDir,uid,gid) return True else: self.printERROR(_("Path %s exists") %userDir) return False def applyProfilesFromUser(self,progress=False): """Применяем профили для пользователя""" # Cоздаем объект профиль if progress: clProf = ProgressProfile(self.clVars) else: clProf = cl_profile.profile(self.clVars) # Объединяем профили dirsFiles = clProf.applyProfiles() if progress: clProf.close() if clProf.getError(): self.printERROR(clProf.getError()) return False else: return dirsFiles def applyProfilesFromSystem(self): """Применяем профили для cистемы""" # Cоздаем объект профиль clProf = cl_profile.profile(self.clVars) # Объединяем профили dirsFiles = clProf.applyProfiles() if clProf.getError(): self.printERROR(clProf.getError()) return False else: return dirsFiles def checkUserPwdLDAP(self, server, userDN, password): """Проверка пароля Unix пользователя на сервере""" ldapInit = ldap.initialize("ldap://%s"%server) errMessage = "" try: ldapInit.bind_s(userDN, password) except ldap.INVALID_CREDENTIALS: errMessage = _("Password incorrect") return False, errMessage except ldap.LDAPError, e: errMessage = e[0]['desc'] return False, errMessage return True, errMessage def getServerDataUser(self): """Получение имени LDAP сервера и DN пользователей под пользователем""" fileName = "/etc/ldap.conf" serverName = "" usersDN = "" strServer = ("host","HOST") lenStrServer = len(strServer[0]) strDN = ("nss_base_passwd","NSS_BASE_PASSWD") lenStrDN = len(strDN[0]) splList = (" ", "\t") try: for i in open(fileName): if not serverName and\ filter(lambda x: i.startswith(x),strServer) and\ len(i)>lenStrServer: spl = i[lenStrServer] if spl in splList: serverName = i.rpartition(spl)[2].strip() if not usersDN and filter(lambda x: i.startswith(x), strDN) and\ len(i)>lenStrDN: spl = i[lenStrDN] if spl in splList: usersDN = i.rpartition(spl)[2].partition('?')[0].strip() if serverName and usersDN: break except: self.printERROR(_("Can not open %s")%fileName) return False if serverName and usersDN: return (serverName, usersDN) else: return () def setServerCommand(self, command, varsCommand, fileConfig, uid=None, gid=None): """Установить команду для сервера""" pathConfig = os.path.split(fileConfig)[0] # Создаем директорию если ее нет if not os.path.exists(pathConfig): os.makedirs(pathConfig) if not uid is None and not gid is None: os.chown(pathConfig, uid, gid) objConfig = cl_base.iniParser(fileConfig) varsRun = {"run":"on"} varsRun.update(varsCommand) if not objConfig.setVar(["command"]+command, varsRun): self.printERROR(_("Can not write variables in file %s")\ %fileConfig) return False if not uid is None and not gid is None: os.chown(fileConfig, uid, gid) return True def getDataInConfig(self, section, listVars, objConfig): """Читаем переменные listVars из секции section конф. файла""" varsConfig = {} for varName in listVars: varsConfig[varName] = objConfig.getVar(section,varName) if objConfig.getError(): return False return varsConfig def setUserPasswordToServer(self, options): """Установка пароля пользователя на сервере""" # Проверяем на root if self.isRoot(False): self.printERROR(_("The user is root")) self.printWARNING(\ _("The program can be executed from a non-root user only")) return False # DNS имя хоста data = self.getServerDataUser() if not data: self.printERROR(_("The computer is not in domain")) self.printWARNING(_("Use passwd")) return False server, usersDN = data # Получаем старый пароль пользователя curPassword = self.getUserPassword(_("Enter current password")) if not curPassword: self.printERROR(_("Current password is empty")) return False userDN = self.addDN("uid=%s"%os.environ["USER"], usersDN) # Проверяем в LDAP сервере текущий пароль пользователя ret, err = self.checkUserPwdLDAP(server, userDN, curPassword) if not ret: self.printERROR(err) return False optPasswd = options if not options: optPasswd = {"p":""} password = self.getUserPwd(optPasswd, "p", False) if password is False: return False # Переменные для записи в env файл varsConfig = {"unix_hash":self.getHashPasswd(password,"ssha"), "samba_lm_hash":self.getHashPasswd(password,"lm"), "samba_nt_hash":self.getHashPasswd(password,"nt"), "samba_nt_hash_old":self.getHashPasswd(curPassword,"nt")} if filter(lambda x: not x, varsConfig.values()): return False # ~/.calculate/server.env fileConfig = os.path.join(os.environ["HOME"], self.pathConfig, self.configFileServer) if not self.setServerCommand(["passwd_samba"], varsConfig, fileConfig): return False self.printOK(_("Changed password of user %s")%os.environ["USER"] + \ " ...") self.printWARNING(_("Password will be changed when you logout from the \ X session")) return True def getUserPassword(self, pwDialog=False): """Получить пароль у пользователя pwDialog - приглашение ввода пароля """ if not pwDialog: pwDialog = _("Password") userPwd = getpass.getpass(pwDialog+":") return userPwd def chownR(self, directory, uid, gid, dirsAndFiles=False): """изменяет владельца и группу для всех файлов и директорий внутри directory """ if dirsAndFiles: dirs, files = dirsAndFiles # меняем владельца домашней директории os.chown(directory, uid,gid) # Меняем владельца директорий for dirCh in dirs: if os.path.exists(dirCh): os.chown(dirCh, uid,gid) # Меняем владельца файлов for fileCh in files: if os.path.exists(fileCh): if os.path.islink(fileCh): os.lchown(fileCh, uid, gid) else: os.chown(fileCh, uid,gid) return True else: fileObj = cl_profile._file() scanObjs = fileObj.scanDirs([directory]) # меняем владельца домашней директории os.chown(directory, uid,gid) # Меняем владельца директорий for dirCh in scanObjs[0].dirs: os.chown(dirCh, uid,gid) # Меняем владельца файлов for fileCh in scanObjs[0].files: os.chown(fileCh, uid,gid) # Меняем владельца ссылок for linkCh in scanObjs[0].links: os.lchown(linkCh[1], uid, gid) return True def execProg(self, cmdStrProg, inStr=False, retFull=True, envProg={}): """Выполняет внешнюю программу Параметры: cmdStrProg внешняя программа inStr данные передаваемые программе на страндартный вход. Возвращаемые параметры: строка которую выведет внешняя программа или False в случае ошибки """ env_path = {"PATH":cl_utils.getpathenv()} env = {} env.update(os.environ.items() + env_path.items() + envProg.items()) retCode,programOut = cl_utils.runOsCommand(cmdStrProg,inStr,retFull,env) if not retCode: return programOut return False def getUserPasswdInfo(self, userName): """получаем uid и gid пользователя из /etc/passwd""" resPasswd = self.searchPasswdUser(userName) if resPasswd: uid = resPasswd.split(":")[2] gid = resPasswd.split(":")[3] fullName = resPasswd.split(":")[4] mail = "" group = "" jid = "" home = os.path.join("/home",userName) resGroup = self.searchGroupGid(gid) if resGroup: group = resGroup.split(":")[0] return (uid, gid, fullName, mail, jid, home, group) else: return False def setDaemonAutostart(self, daemon): """Прописывает демона в автозагрузку""" execStr = "rc-update add %s default" %daemon textLine = self.execProg(execStr) if textLine: return True else: self.printERROR(_("ERROR") + ": " + execStr) self.printERROR(_("Can not add at default runlevel")) return False def delDaemonAutostart(self, daemon): """Удаляет демона из автозагрузки""" execStr = "rc-update del %s default" %daemon textLine = self.execProg(execStr) if textLine: return True else: self.printERROR(_("ERROR") + ": " + execStr) self.printERROR(_("Can not delete from default runlevel")) return False def createHome(self, userName, applyAlways=False, progress=False): """Создание пользовательской директории с настройками для kde4""" # Имя пользователя self.userName = userName # Проверяем на root if not self.isRoot(): return False # Создаем объект переменных self.createClVars() uidGid = False # Подсоединяемся к серверу domain = self.clVars.Get("cl_remote_host") connectLdap = False if domain: if not self.getLdapObjBind(domain): return False connectLdap = True if connectLdap: # uid и gid и mail из Ldap uidGid = self.getUserLdapInfo(userName,False) if not domain: # uid и gid и mail из passwd uidGid = self.getUserPasswdInfo(userName) if not uidGid: # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True self.printERROR(_("Not found user uid and gid")) return False uid = int(uidGid[0]) gid = int(uidGid[1]) fullName = uidGid[2] mail = uidGid[3] jid = uidGid[4] homeDir = uidGid[5] group = uidGid[6] if not group: # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True self.printERROR(_("Not found user primary group (gid=%s)"%str(gid))) return False # Создаем пользовательскую директорию self.clVars.Set('cl_root_path',homeDir,True) homeExists = os.path.exists(homeDir) # Первый проход self.clVars.Set('cl_pass_step','first',True) if homeExists: self.printWARNING(_("Home dir %s exists")%homeDir) if set(os.listdir(homeDir))-set(self.skipHomeFile): if not applyAlways: # Второй и последующие проходы self.clVars.Set('cl_pass_step','next',True) self.printSUCCESS(_("Apply always profiles") + " ...") # Создаем домашнюю директорию if not os.path.exists(homeDir): self.createUserDir(uid, gid, homeDir) # Записываем переменные self.clVars.Set('ur_login', userName) self.clVars.Set('ur_fullname', fullName) self.clVars.Set('ur_mail', mail) self.clVars.Set('ur_jid', jid) self.clVars.Set('ur_group', group) # Применяем профили для пользователя dirsAndFiles = self.applyProfilesFromUser(progress) if not dirsAndFiles: # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True self.printERROR(_("Can not apply user profile")) return False self.chownR(homeDir, uid, gid, dirsAndFiles) if not homeExists: self.printSUCCESS(_("Created home dir %s")%homeDir + " ...") self.printSUCCESS(_("User account is configured") + " ...") return True def isDomain(self): """Находится ли компьютер в домене""" self.createClVars(self.clVars) foundMountRemote =self.isMount("/var/calculate/remote" ,"cifs") foundMountHome =self.isMount("/var/calculate/client-home","none",False) if not (self.clVars.Get("cl_remote_host") and foundMountRemote and\ foundMountHome): self.printERROR(_("The computer is not in domain")) return False return (foundMountRemote,foundMountHome) def killRsync(self): """Убивает все процессы rsync и cl-sync --login""" listProcess = self.execProg("ps ax",False,False) if not listProcess: return False killPid = [] flagError = False for process in listProcess: if "rsync" in process: killPid.append(process.split(" ")[0]) if "--login" in process and "cl-sync" in process: killPid.append(process.split(" ")[0]) strPid = str(os.getpid()) # удалим свой pid из списка завершаемых процессов if strPid in killPid: killPid.remove(strPid) if killPid and " ".join(killPid).strip(): textLine = self.execProg("kill -9 %s" %" ".join(killPid)) if not (textLine is None): self.printERROR(_("Can not 'kill %s'")\ %" ".join(killPid)) flagError = True if flagError: return False else: return True def isMount(self, pathMount ,typeMount, secondPath=True): """Примонтирована ли директория""" path = os.path.realpath(pathMount) foundMount = False if secondPath: reFoundMount = re.compile("on\s+%s\s+type\s+%s"%(path,typeMount)) else: reFoundMount = re.compile("%s\s+.+type\s+%s"%(path,typeMount)) resMount = self.execProg("mount",False,False) if resMount and type(resMount) == types.ListType: for string in resMount: if reFoundMount.search(string): foundMount = string break return foundMount def relevanceProfiles(self, hostAuth): """Определяем актуальность наложенных профилей в зависимости от версии программы Перед запуском обязательно должен быть определен объект переменных self.clVars Если актуальны - True, если нет False """ # 'local' или имя хоста if hostAuth != self.clVars.Get("os_remote_auth"): return False clProf = cl_profile.profile(self.clVars) # Текущая версия программы currentVersion = self.clVars.Get("cl_ver") # Версия программы которая ранее работала с профилями previousVersion = self.clVars.Get("os_remote_client") cVersion, pVersion = clProf._convertVers(currentVersion,previousVersion) # Если версии программ не равны то профили не актуальные if cVersion != pVersion: return False return True def getPathProfiles(self, listPath): """Получаем список директорий хранения профилей""" """список накладываемых профилей при установке, наложении профилей""" profpath = [] for profPath in listPath: if os.path.isdir(profPath): paths = os.listdir(profPath) for path in paths: ph = os.path.join(profPath,path) filesAndDirs = os.listdir(ph) if os.path.isdir(ph) and filesAndDirs: profpath.append(ph) return profpath def applyRelevanceProfiles(self, hostAuth): """Накладывает релевантные профили Перед запуском обязательно должен быть определен объект переменных self.clVars """ if not self.relevanceProfiles(hostAuth): # Обнуляем переменную удаленный хост if hostAuth == "local": self.clVars.Set("cl_remote_host","",True) # Изменяем базовую директорию наложения профилей self.clVars.Set("cl_root_path","/",True) # Устанавливаем действие profiles_domain self.clVars.Set("cl_pass_type","domain",True) # Наложим профили profiles/domain # Новые пути к профилям profPaths = self.getPathProfiles(self.rootProfilePaths) if not profPaths: self.printERROR(_("Empty profile paths %s")\ %", "(self.rootProfilePaths)) return False # Изменяем переменную хранения профилей self.clVars.Set("cl_profile_path",profPaths,True) # Наложим профили dirsAndFiles = self.applyProfilesFromSystem() if not dirsAndFiles: self.printERROR(_("Can not apply 'profiles/domain' profiles")) return False if hostAuth == "local": self.printOK(_("Set profiles of local mode")) else: self.printOK(_("Set profiles of network mode")) currentVersion = self.clVars.Get("cl_ver") self.clVars.Write("os_remote_client", currentVersion) self.clVars.Write("os_remote_auth", hostAuth) return True def mountRemote(self): """Монтирование remote и домашней директории если компьютер в домене а так-же ввод в домен если найдено имя хоста и пароль для подключения """ # Проверяем на root if not self.isRoot(): return False self.createClVars(self.clVars) domain = self.clVars.Get("cl_remote_host") if domain: foundMountRemote = self.isMount("/var/calculate/remote" ,"cifs") foundMountHome = self.isMount("/var/calculate/client-home","none", False) else: self.printWARNING(_("This computer is not in domain")) # Если профили не актуальны накладываем новую версию профилей if not self.applyRelevanceProfiles("local"): return False return True pathHome = "/var/calculate/client-home" if foundMountRemote: self.printWARNING(_("Samba resource [%s] is mount")%"remote" + \ " ...") if foundMountHome: self.printWARNING(str(pathHome) + " " +_("is mount")+ " ...") if foundMountHome and foundMountRemote: # Накладываем сетевые профили if domain: self.clVars.flIniFile() if not self.applyRelevanceProfiles(domain): return False return True flagLocalProfile = False if not foundMountRemote: pathRemote = "/var/calculate/remote" pwdRemote = self.clVars.Get("cl_remote_pw") if not (domain and pwdRemote): self.printERROR(_("Not found variable")+\ ": cl_remote_pw ...") return False if not os.path.exists(pathRemote): os.makedirs(pathRemote) mountStr = "mount -t cifs -o user=client //%s/remote %s"\ %(domain,pathRemote) textLine = self.execProg(mountStr, None, True, {"PASSWD":pwdRemote}) if not (textLine is None): self.printWARNING(_("Can not mount Samba resource [%s]")%\ "remote" + " ...") flagLocalProfile = True # Если профили не актуальны накладываем новую версию профилей if not self.applyRelevanceProfiles("local"): return False if foundMountHome: umountStr = "umount /home" textLine = self.execProg(umountStr) if not (textLine is None): self.printERROR(_("Can not unmount") + " /home") return False return True self.printSUCCESS(_("Mount Samba resource [%s]") % "remote" +\ " ...") if (not foundMountHome) and (not flagLocalProfile): if not os.path.exists(pathHome): os.makedirs(pathHome) mountStr = "mount -o bind %s /home" %pathHome textLine = self.execProg(mountStr) if not (textLine is None): self.printERROR(_("Can not mount") + " " + str(pathHome) +\ " ...") return False self.printSUCCESS(_("Mount") + " " + str(pathHome) + " " +\ " ...") # Накладываем сетевые профили if domain: self.clVars.flIniFile() if not self.applyRelevanceProfiles(domain): return False self.restartDBus() return True def delDomain(self): """выводим из домена""" # Проверяем на root if not self.isRoot(): return False self.createClVars() pathRemote = "/var/calculate/remote" pathHome = "/var/calculate/client-home" foundMountRemote = self.isMount(pathRemote ,"cifs") foundMountHome = self.isMount(pathHome ,"none",False) domain = self.clVars.Get("cl_remote_host") if not domain: self.printWARNING(_("The computer is not in domain")) return True if foundMountRemote: textLineUmount = self.umountSleepPath(pathRemote) if not textLineUmount: return False if foundMountHome: textLineUmount = self.umountSleepPath(pathHome ,"none", False) if not textLineUmount: return False self.clVars.Delete("cl_remote_host","local") self.clVars.Delete("cl_remote_pw","local") self.clVars.Set("cl_remote_host","",True) self.clVars.Set("cl_remote_pw","",True) # Изменяем базовую директорию наложения профилей self.clVars.Set("cl_root_path","/",True) # Наложим профили profiles/domain # Новые пути к профилям # Устанавливаем действие profiles_domain self.clVars.Set("cl_pass_type","domain",True) # Новые пути к профилям profPaths = self.getPathProfiles(self.rootProfilePaths) if not profPaths: self.printERROR(_("Empty profile paths %s")\ %", "(self.rootProfilePaths)) return False # Изменяем переменную хранения профилей self.clVars.Set("cl_profile_path",profPaths,True) # Наложим профили dirsAndFiles = self.applyProfilesFromSystem() if not dirsAndFiles: self.printERROR(_("Can not apply 'profiles/domain' profiles")) return False if not self.delDaemonAutostart("client"): return False self.printOK(_("Computer removed from domain %s")%domain + " ...") return True def uninstallClient(self): """Наложение профиля клиента по умолчанию и при необходимости отключение от домена""" if not self.isRoot(): return False self.createClVars() clVars = self.clVars messageOk = _("Removed use of scripts this package for window \ manager") + " ..." # Устанавливаем переменную профиля clVars.Set("cl_pass_run","off",True) # Изменяем базовую директорию наложения профилей clVars.Set("cl_root_path","/",True) # Устанавливаем действие profiles_client clVars.Set("cl_pass_type","install",True) # Новые пути к профилям profPaths = self.getPathProfiles(self.rootProfilePaths) if not profPaths: self.printERROR(_("Empty profile paths %s")\ %", "(self.rootProfilePaths)) return False # Изменяем переменную хранения профилей clVars.Set("cl_profile_path",profPaths,True) # Наложим профили dirsAndFiles = self.applyProfilesFromSystem() if not dirsAndFiles: self.printERROR(_("Can not apply 'profiles/client' profiles")) return False remoteHost = clVars.Get("cl_remote_host") if remoteHost: if not self.delDomain(): return False self.printOK(messageOk) return True def installClient(self, clVars=False, printSuccess=True): """Наложение профиля клиента и при необходимости подключение к домену""" if not self.isRoot(): return False if not clVars: #Создаем объект переменных self.createClVars() clVars = self.clVars messageOk = _("Added use of scripts this package for window \ manager") + " ..." # Устанавливаем переменную профиля clVars.Set("cl_pass_run","on",True) # Изменяем базовую директорию наложения профилей clVars.Set("cl_root_path","/",True) # Устанавливаем действие profiles_client clVars.Set("cl_pass_type","install",True) # Новые пути к профилям profPaths = self.getPathProfiles(self.rootProfilePaths) if not profPaths: self.printERROR(_("Empty profile paths %s")\ %", "(self.rootProfilePaths)) return False # Изменяем переменную хранения профилей clVars.Set("cl_profile_path",profPaths,True) # Наложим профили dirsAndFiles = self.applyProfilesFromSystem() if not dirsAndFiles: self.printERROR(_("Can not apply 'profiles/client' profiles")) return False if printSuccess: self.printOK(messageOk) return True def addDomain(self, domainName): """Вводим в домен""" # Проверяем на root if not self.isRoot(): return False # Создаем объект переменных self.createClVars() netDomain = self.clVars.Get("os_net_domain") # Получам имя сервера (домена) if "." in domainName: domain = domainName else: domain = "%s.%s" %(domainName,netDomain) execStr = "ping -c 2 -i 0.3 %s" %domain resPing = self.execProg(execStr, False, False) if not resPing: self.printERROR(_('Can not execute "%s"')%execStr) return False foudHost = False foudHostSamba = False foundMountRemote = False reFoundHost = re.compile("(\d+)\% packet loss") if resPing and type(resPing) == types.ListType and len(resPing)>=2: pingStr = resPing[-2].strip() reSearch = reFoundHost.search(pingStr) if reSearch and reSearch.group(1) == "0": foudHost = True if not foudHost: self.printERROR(_("Not found domain %s")%domain) return False reFoundHostSamba = re.compile("Server=\[Samba.+\]") resSmbClient = self.execProg("smbclient -N -L %s" %domain, False,False) if resSmbClient and type(resSmbClient) == types.ListType: for string in resSmbClient: if reFoundHostSamba.search(string): foudHostSamba = True break if not foudHostSamba: self.printERROR(_("Not found Samba server in %s")%domain) return False pwd = False if self.clVars.Get("cl_remote_host") and \ self.clVars.Get("cl_remote_host") != domain: if not self.delDomain(): return False elif self.clVars.Get("cl_remote_host") and \ self.clVars.Get("cl_remote_host") == domain: pwd = self.clVars.Get("cl_remote_pw") foundMountRemote =self.isMount("/var/calculate/remote" ,"cifs") foundMountHome =self.isMount("/var/calculate/client-home","none",False) if foundMountRemote: self.printWARNING(_("Samba resource [%s] is mount")%\ "remote" + " ...") else: if pwd: userPwd = pwd else: userPwd=self.getUserPassword(\ _("Domain password for the desktop")) pathRemote = "/var/calculate/remote" pwdRemote = userPwd if not os.path.exists(pathRemote): os.makedirs(pathRemote) mountStr = "mount -t cifs -o user=client //%s/remote %s"\ %(domain,pathRemote) textLine = self.execProg(mountStr, None, True, {"PASSWD":pwdRemote}) if not (textLine is None): self.printERROR(_("Can not mount Samba resource [%s]")%\ "remote" + " ...") return False else: self.printSUCCESS(_("Mount Samba resource [%s]")%"remote" + \ " ...") self.clVars.Write("cl_remote_host", domain, False, "local") self.clVars.Write("cl_remote_pw", userPwd, False, "local") pathHome = "/var/calculate/client-home" if foundMountHome: self.printWARNING(str(pathHome)+ " " +_("is mount")+ " ...") else: if not os.path.exists(pathHome): os.makedirs(pathHome) mountStr = "mount -o bind %s /home" %pathHome textLine = self.execProg(mountStr) if not (textLine is None): self.printERROR(_("Can not mount") + " " + str(pathHome) +\ " ...") return False self.printSUCCESS(_("Mount") + " " + str(pathHome) + " " +\ " ...") self.clVars.flIniFile() #считаем переменные для клиента servDn = self.clVars.Get("ld_services_dn") unixDN = self.clVars.Get("ld_unix_dn") bindDn = self.clVars.Get("ld_bind_dn") bindPw = self.clVars.Get("ld_bind_pw") # запишем их if not (servDn and unixDN and bindDn and bindPw): self.printERROR(_("Not found variables") + ":") self.printERROR("ld_services_dn or ld_unix_dn \ or ld_bind_dn or ld_bind_pw") return False # Наложим профили profiles/client if not self.installClient(self.clVars, False): return False # Наложим профили profiles/domain # Устанавливаем действие profiles_client self.clVars.Set("cl_pass_type","domain",True) # Новые пути к профилям profPaths = self.getPathProfiles(self.rootProfilePaths) if not profPaths: self.printERROR(_("Empty profile paths %s")\ %", "(self.rootProfilePaths)) return False # Изменяем переменную хранения профилей self.clVars.Set("cl_profile_path",profPaths,True) # Наложим профили dirsAndFiles = self.applyProfilesFromSystem() if not dirsAndFiles: self.printERROR(_("Can not apply 'profiles/domain' profiles")) return False # Рестартуем dbus self.restartDBus() if not self.setDaemonAutostart("client"): return False self.printOK(_("Computer added to domain %s")%domain + " ...") return True def restartDBus(self): """Перезапускаем службу D-Bus""" dbusDaemon = '/etc/init.d/dbus' if os.path.exists(dbusDaemon): if os.system(dbusDaemon + ' restart &>/dev/null') != 0: self.printWARNING(_("Error restarting")+" "+dbusDaemon+" ...") return False return True def removeNoiseFiles(self, userHome): """Удаление файлов, создающих помехи работе dm""" noiseFiles = ['.dmrc'] for nsFile in noiseFiles: rmFile = os.path.join(userHome, nsFile) if os.path.exists(rmFile): os.remove(rmFile) return True def removePrivateFiles(self, userHome): """Удаление приватных файлов""" privateFiles = ['.kde4/share/apps/kwallet/kdewallet.kwl', os.path.join(self.pathConfig, self.configFileServer)] # файлы в .ssh sshHome = ".ssh" sshPath = os.path.join(userHome,sshHome) if os.path.isdir(sshPath): # .ssh файлы относительно домашней директории пользователя privateFiles += map(lambda x:os.path.join(sshHome,x),\ filter(lambda x:\ os.path.isfile(os.path.join(sshPath,x)),\ os.listdir(sshPath))) for prFile in privateFiles: rmFile = os.path.join(userHome, prFile) if os.path.exists(rmFile): os.remove(rmFile) return True def umountSleepPath(self, path, typeMount='cifs', secondPath=True): """Отмонтирует путь при неудаче задержка потом повтор""" #Проверяем на монтирование директории if self.isMount(path, typeMount, secondPath): textLine = self.execProg("umount %s"%path) if textLine != None: i = 0 flagError = False while (i1: foundTwoSession = True self.printERROR(\ _("Second X session for user %s can not be opened.")\ %userName) break return foundTwoSession def mountSambaRes(self,userName,userPwd,uid,gid,res,path, mountUidList=['ftp','home','share']): """Монтирует Samba ресурсы""" if res in mountUidList: # Монтируем директории c uid mountStr="mount -t cifs -o user=%s,uid=%s,gid=%s,noperm"\ %(userName,uid,gid) +\ " //%s/%s %s" %(self.clVars.Get("cl_remote_host"), res, path) else: # Монтируем директории mountStr="mount -t cifs -o user=%s"%(userName)+\ " //%s/%s %s" %(self.clVars.Get("cl_remote_host"), res, path) textLine = self.execProg(mountStr, None, True, {"PASSWD":userPwd}) return textLine def removeDir(self, rmDirOrScanObjs): """Рекурсивное удаление директории входной параметр директория или результат сканирования файлов (объект) """ rmDir = False if type(rmDirOrScanObjs) == types.StringType: rmDir = rmDirOrScanObjs if not os.path.exists(rmDir): self.printERROR(_("Not found remove dir %s") %rmDir) return False fileObj = cl_profile._file() # Сканируем директорию scanObjs = fileObj.scanDirs([rmDir]) else: scanObjs = rmDirOrScanObjs for socketRm in scanObjs[0].sockets: # Удаляем сокеты if os.path.exists(socketRm): os.remove(socketRm) for linkRm in scanObjs[0].links: # Удаляем ссылки os.unlink(linkRm[1]) for fileRm in scanObjs[0].files: # Удаляем файлы os.remove(fileRm) scanObjs[0].dirs.sort(lambda x, y: cmp(len(y), len(x))) for dirRm in scanObjs[0].dirs: # Удаляем директории os.rmdir(dirRm) if rmDir: os.rmdir(rmDir) return True def getNameRemoteServer(self,userName): """Если профиль на удаленном сервере, то выдать DNS имя этого сервера """ searchPrevHost = self.searchPrevHost(userName) if searchPrevHost and searchPrevHost[0][0][1].has_key('host'): prevHost = searchPrevHost[0][0][1]['host'][0] else: prevHost = None # если местоположение актуального профиля найти не удалось # или его местоположение не на локальном сервере if not prevHost or prevHost == self.clVars.Get('cl_remote_host'): return False else: return prevHost def setDateAndVersionToConfig(self, configFileName, uid, gid, strTime): """Записывает текущую версию в файл конфигурации""" # Текущая версия программы currentVersion = self.clVars.Get("cl_ver") if self.setVarToConfig("main", {"version":currentVersion, "date":strTime}, configFileName, uid, gid): return True self.printERROR(_("can not write the version number in the file %s")\ %configFileName) return False def setVarToConfig(self, nameSection, varsDict, configFileName, uid, gid): """Записывает переменную в файл конфигурации""" # Создаем директорию для конфигурационных файлов pathConfig = os.path.split(configFileName)[0] if not os.path.exists(pathConfig): self.createUserDir(uid, gid, pathConfig, mode=False) try: if cl_base.iniParser(configFileName).setVar(nameSection, varsDict): os.chmod(configFileName, 0600) os.chown(configFileName,uid,gid) except: return False return True def clearHomeDir(self, homeDir): """Очистка домашней директории от файлов""" rmFiles = list(set(os.listdir(homeDir))-\ set(self.skipHomeFile)) for rmFile in rmFiles: delFile = os.path.join(homeDir,rmFile) if os.path.islink(delFile): os.unlink(delFile) elif os.path.isfile(delFile): os.remove(delFile) elif os.path.isdir(delFile): if not self.removeDir(delFile): return False elif stat.S_ISSOCK(os.stat(delFile)[stat.ST_MODE]): os.remove(delFile) return True def getUserMountResources(self, userName, homeDir, flagRemoteServer): """Получение монтируемых ресурсов для пользователя""" home = os.path.split(homeDir)[0] dictResources = {} names = [] # Pесурс share name = "share" dictResources[name] = {"resource":"share", "path":os.path.join(homeDir,"Disks")} names.append(name) # Ресурс профилей name = "profile" dictResources[name] = {"resource":"unix", "path":os.path.join(home,"."+userName)} names.append(name) # Ресурс - домашняя директория (файлы пользователя на сервере) name = "home" dictResources[name] = {"resource":"homes", "path":os.path.join(homeDir,"Home")} names.append(name) if self.clVars.Get("cl_remote_ftp"): # Ресурс ftp name = "ftp" dictResources[name] = {"resource":"ftp", "path":os.path.join(homeDir,"FTP")} names.append(name) if flagRemoteServer: # Ресурс профилей на удаленном сервере name = "remote_profile" dictResources[name] = {"resource":"unix", "path":os.path.join(home,"."+userName+"."+"remote")} names.append(name) return names, dictResources def syncLoginProfile(self, userName, homeDir, homeProfile, uid, gid, progress, flagClearHomeDir=True): """Синхронизация профиля пользователя с сервера в переменной 'cl_remote_host' хост для прогрессбара """ home = os.path.split(homeDir)[0] # Если на текущем сервере в ресурсе unix есть файлы # то синхронируем настройки if os.listdir(homeProfile): if not self.syncUser(userName, homeDir, "login", uid, gid,\ homeProfile, progress=progress, host=self.clVars.Get('cl_remote_host')): return False else: if flagClearHomeDir: # Удаляем ненужные файлы (очищаем домашнюю директорию) if not self.clearHomeDir(homeDir): return False return True def fileReader(self, fileName, stdin, progressObj=False): """Читает файл блоками в поток""" fd = os.open(fileName, os.O_RDONLY) if progressObj: currSize = 0 # Размер файлового буфера в байтах buffSize=131072 dataBlock = os.read(fd, buffSize) while dataBlock: if progressObj: currSize += len(dataBlock) progressObj.setValue(currSize) stdin.write(dataBlock) dataBlock = os.read(fd, buffSize) stdin.close() os.close(fd) def unpackProfile(self, homeDir, archFile, progress=False,\ remoteServer=False): """Распаковка архива в домашнюю директорию пользователя""" # Создаем прогрессбар if progress: title = _("Unpacking the archive from server") if remoteServer: title += " " + remoteServer title += " ..." progressObj = ProgressBar(title) archiveSize = os.stat(archFile).st_size progressObj.setMaximum(archiveSize) execStr = "tar -C '%s' -xzf -" %homeDir # Выполняем разархивацию pipe = subprocess.Popen(execStr, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, env=os.environ, shell=True) # Чтение файла в поток if progress: self.fileReader(archFile, pipe.stdin, progressObj) else: self.fileReader(archFile, pipe.stdin) ret = pipe.wait() pipe.stdout.close() pipe.stderr.close() if progress: progressObj.shutdownDialog() #ret = os.system(execStr) if ret: self.printERROR(_("Can not execute %s") %execStr) self.printERROR(_("Can not unpack %s") %archFile) return False return True def scanDirectory(self, scanDir, listFiles, skipPath=[], prefix=False, flagDir=False): """Генерация списка файлов и директорий""" if not prefix: prefix = os.path.join(scanDir,"") if flagDir or stat.S_ISDIR(os.lstat(scanDir)[stat.ST_MODE]): for fileOrDir in os.listdir(scanDir): absPath = os.path.join(scanDir,fileOrDir) relPath = absPath.split(prefix)[1] if relPath in skipPath: continue listFiles.append(relPath) stInfo = os.lstat(absPath) statInfo = stInfo[stat.ST_MODE] if stat.S_ISDIR(statInfo): self.scanDirectory(absPath, listFiles, skipPath, prefix, True) def getListFilesProfile(self, homeDir): """Генерация списка файлов в домашней директориии Исключая монтируемые директории """ home = os.path.join(homeDir, "") execStr = "mount" textLines = self.execProg(execStr,False,False) # Пропускаемые директории для сканирования skipPaths = [] if textLines: for line in textLines: if home in line: skipPath =\ line.partition(home)[2].rpartition(" type")[0] skipPaths.append(skipPath) # Список файлов в профиле пользователя listFiles = [] if not skipPaths: self.printERROR(_("Not found the mount point of server resources")) return False self.scanDirectory(homeDir, listFiles, skipPaths) return listFiles def removeFilesInProfile(self, homeDir, pathListFile): """Удаляем файлы которых нет в профиле пользователя""" # Получаем файлы профиля на сервере try: filesProfileTxt = open(pathListFile).read() except: self.printERROR(_("Can not open %s")%pathListFile) return False listFilesProfile = filter(lambda x: x.strip(), filesProfileTxt.split("\n")) filesProfile = set(listFilesProfile) # Получаем файлы в домашней директории listFilesHome = self.getListFilesProfile(homeDir) if listFilesHome is False: return False filesHome = set(listFilesHome) filesRemove = list(filesHome - filesProfile) filesRemove.sort(lambda x, y: cmp(len(y), len(x))) rmPath = "" try: for rmFile in filesRemove: rmPath = os.path.join(homeDir, rmFile) if os.path.islink(rmPath): os.unlink(rmPath) elif os.path.isfile(rmPath): os.remove(rmPath) elif os.path.isdir(rmPath): os.rmdir(rmPath) else: os.remove(rmPath) except: self.printERROR(_("Can nor remove %s")%rmPath) return False return True def foundArchFile(self,strCurrentTime,archPathProcess, archPathSuccess, progress=False, remoteServer=False): """Поиск архива профиля пользователя""" # Ищем архив профиля # Задержки при поиске файла архива .process if progress: title = _("Packing the archive at the server") if remoteServer: title += " " + remoteServer title += " ..." progressObj = ProgressBar(title, 0) progressObj.openDialog(title, 0) sleepsFindProcess = [0.1, 0.2, 0.5] # Задержки при проверке размера архива sleepsArch = [0.5, 1, 2] lenFP = len(sleepsFindProcess) lenA = len(sleepsArch) i = 0 # Ждем появления файла архива while(not (os.path.exists(archPathProcess) or\ os.path.exists(archPathSuccess)) and i=lenA: # rsync схема синхронизации if progress: progressObj.shutdownDialog() return False startSize = os.stat(archPathProcess).st_size time.sleep(sleepsArch[i]) if os.path.exists(archPathSuccess): break endSize = os.stat(archPathProcess).st_size if startSize == endSize: i += 1 if progress: progressObj.shutdownDialog() return True def mountUserRes(self, userName, sync=True, progress=False): """Монтирование пользовательских ресурсов и синхронизация настроек""" # Имя пользователя self.userName = userName # Проверяем на root if not self.isRoot(): return False # Проверка на повторный вход пользователя if self.isTwoSessionsUser(userName): return False self.createClVars() # В случае компьютера вне домена if not self.clVars.Get("cl_remote_host"): self.printSUCCESS(_("To be used local profile.")) return True # Проверим что компьютер в домене и смонтирован [remote] connectDomain = self.isDomain() if not connectDomain: # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False elif not connectDomain[0]: self.printERROR(_("Can not mount Samba resource [%s]")%"remote"+ \ " ...") # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False # Подсоединяемся к серверу domain = self.clVars.Get("cl_remote_host") if not self.getLdapObjBind(domain): # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False # homeDir из LDAP resLdap = self.getUserLdapInfo(userName) if not resLdap: # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False uid = int(resLdap[0]) gid = int(resLdap[1]) homeDir = resLdap[5] # Файл хранения настроек пакета configFileName = os.path.join(homeDir, self.pathConfig, self.configFileDesktop) # При отсуствии создаем домашнюю директорию if not os.path.exists(homeDir): os.makedirs(homeDir) os.chown(homeDir,uid,gid) os.chmod(homeDir,0700) # записываем в конфигурационный файл статус "в процессе" self.setVarToConfig("main", {"status_sync":"process"}, configFileName, uid, gid) # Получаем пароль пользователя из ключей ядра userPwd = _cl_keys.getKey(userName) if not userPwd: self.printERROR(_("Not found user password")) # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False # Флаг ошибки flagError = False # Имя удаленного сервера remoteServer = "" # В случае включения репликации на сервере True replOn = self.searchReplBranch() if replOn: remoteServer = self.getNameRemoteServer(userName) # Получаем монтируемые директории names, dictRes = self.getUserMountResources(userName, homeDir, remoteServer) # Путь к профилю пользователя по умолчанию defaultPath = "" # Хост пользователя по умолчанию defaultHost = self.clVars.Get("cl_remote_host") # Флаг - будет использован инкрементальный архив flagIncrArch = True # Ошибка при монтировании удаленного сервера flagErrorMountRemote = False # Ошибка при cинхронизации с удаленного сервера flagErrorSyncRemote = False # Статус синхронизации syncStatus = True osLinuxShort = self.clVars.Get("os_linux_shortname") # Если профиль на удаленном сервере if remoteServer and replOn: # имя файла архива в процессе архивации archPathProcess = "" # имя файла архива - после архивации archPathSuccess = "" # Создаем инкрементальный архив в случае репликации prevHost = "" # Дата профиля на текущем сервере dateDefaultProfile = "" # Текущее время currTime = str(float(time.time())) # Создаваемые пути при монтировании for name in names: path = dictRes[name]["path"] res = dictRes[name]["resource"] # Создаем пользовательскую директории для профиля if not os.path.exists(path): try: os.mkdir(path) os.chown(path, uid, gid) os.chmod(path,0700) except OSError: self.printERROR(_("Error creating directory")) self.printERROR(_("Permission denied: '%s'")%path) flagError = True break #Проверяем на монтирование директории if self.isMount(path, 'cifs'): continue # Монтируем Samba ресурс if name == "remote_profile": self.clVars.Set("cl_remote_host", remoteServer, True) textLine = self.mountSleepRes(userName,userPwd,uid,gid, res,path) if name == "remote_profile": self.clVars.Set("cl_remote_host", defaultHost, True) if not (textLine is None): if name == "remote_profile": flagErrorMountRemote = True break else: self.printERROR(_("Can not mount Samba resource [%s]")\ %res + " ...") flagError = True break if name == "profile" or name == "remote_profile": # Находим директорию профиля homeProfile = os.path.join(path, osLinuxShort) if not os.path.exists(homeProfile): homeProfile = os.path.join(path, "." + osLinuxShort) # Примонтирована директория профиля с текущего сервера if name == "profile": # Перенос профиля на сервере в директорию без точки if not self.upgradeProfileClient(userName, homeDir, path): flagError = True break fileConfig = os.path.join(homeProfile, self.pathConfig, self.configFileDesktop) if os.path.exists(fileConfig): objConfig = cl_base.iniParser(fileConfig) data = self.getDataInConfig("main", ["status_sync", "date"], objConfig) if data: status = data.get("status_sync") date = data.get("date") if date and status=="success": dateDefaultProfile = date # Примонтирована директория профиля с удаленного сервера # и есть дата профиля текущего сервера if name == "remote_profile" and dateDefaultProfile: # Даем команду на создание инкрементального архива проф. fileConfig = os.path.join(homeProfile, self.pathConfig, self.configFileServer) varsConfig = {"arch_date":dateDefaultProfile, "curr_time":currTime} self.setServerCommand(["pack"], varsConfig, fileConfig) # Отмонтируем директорию профиля с удаленного сервера textLine = self.umountSleepPath(path) if not textLine: flagError = True break pathProfileCurr = dictRes["profile"]["path"] homeProfileCurr = os.path.join(pathProfileCurr,osLinuxShort) # Синхронизация профиля с текущего сервера if not self.syncLoginProfile(userName,homeDir, homeProfileCurr, uid, gid, progress): flagError = True break # Монтируем директорию профиля с удаленного сервера self.clVars.Set("cl_remote_host", remoteServer, True) textLine = self.mountSleepRes(userName,userPwd,uid,gid, res,path) self.clVars.Set("cl_remote_host", defaultHost, True) if not (textLine is None): self.printERROR(_("Can not mount Samba resource [%s]")\ %res + " ...") flagError = True break extSuccess = "gz" extProcess = "process" strCurrentTime = currTime.replace(".","_") archPathTmp = os.path.join(path, "profile.%s.%s.tar"\ %(osLinuxShort, strCurrentTime)) # имя файла архива в процессе архивации archPathSuccess = "%s.%s"%(archPathTmp,extSuccess) # имя файла архива - после архивации archPathProcess = "%s.%s"%(archPathTmp,extProcess) # Ищем файл архива if not self.foundArchFile(strCurrentTime, archPathProcess, archPathSuccess, progress, remoteServer): flagIncrArch = False break # Если нет архива применяем rsync синхронизацию if not os.path.exists(archPathSuccess): flagIncrArch = False break else: # Распаковываем архив в домашнюю директорию if not self.unpackProfile(homeDir, archPathSuccess, progress, remoteServer): flagError = True if archPathSuccess: # Удаление архивного файла if os.path.exists(archPathSuccess): # Удаляем файл архива os.remove(archPathSuccess) break # Cканирование домашней директории и получение списка # файлов pathListFile = os.path.join(homeProfile, self.listProfFile) if not self.removeFilesInProfile(homeDir, pathListFile): self.errorAndUnmountUserRes = True return False else: if name == "remote_profile": flagIncrArch = False if archPathProcess or archPathSuccess: # Удаление архивных файлов rmFiles = [archPathProcess, archPathSuccess] for rmFile in rmFiles: if os.path.exists(rmFile): # Удаляем файл архива os.remove(archPathSuccess) # Если не удалось замонтировать удаленный профиль if flagErrorMountRemote: syncStatus = False # Если не удалось использовать инкрементальный архив elif flagIncrArch is False: # Синхронизируем удаленный профиль pathProfile = dictRes["remote_profile"]["path"] homeProfile = os.path.join(pathProfile, osLinuxShort) if not self.upgradeProfileClient(userName, homeDir, pathProfile): # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False # Синхронизация профиля с удаленного сервера self.clVars.Set("cl_remote_host", remoteServer, True) ret = self.syncLoginProfile(userName, homeDir, homeProfile, uid, gid, progress, False) self.clVars.Set("cl_remote_host", defaultHost, True) if not ret: flagErrorSyncRemote = True syncStatus = False # Если не удалось синхронизировать удаленный синхронизируем # текущий homeProfile = os.path.join(dictRes["profile"]["path"], osLinuxShort) if not self.syncLoginProfile(userName, homeDir, homeProfile, uid, gid, progress): # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False # Отмонтируем директорию профиля пользователя на удаленном сервере if "remote_profile" in dictRes: umountPath = dictRes["remote_profile"]["path"] textLine = self.umountSleepPath(umountPath) if not textLine: # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False # Удаляем директорию удаленного профиля у клиента if os.path.exists(umountPath): os.rmdir(umountPath) # Если профиль на текущем сервере elif not remoteServer and replOn or not replOn: # Создаваемые пути при монтировании for name in names: path = dictRes[name]["path"] res = dictRes[name]["resource"] # Создаем пользовательскую директории для профиля if not os.path.exists(path): try: os.mkdir(path) os.chown(path, uid, gid) os.chmod(path,0700) except OSError: self.printERROR(_("Error creating directory")) self.printERROR(_("Permission denied: '%s'")%path) flagError = True break # Проверяем на монтирование директории if self.isMount(path, 'cifs'): continue textLine = self.mountSleepRes(userName,userPwd,uid,gid, res, path) if not (textLine is None): self.printERROR(_("Can not mount Samba resource [%s]")\ %res + " ...") flagError = True break if name == "profile": pathProfile = path # Перенос профиля на сервере в директорию без точки if not self.upgradeProfileClient(userName, homeDir, pathProfile): flagError = True break # Директория профиля homeProfile = os.path.join(path, osLinuxShort) # Синхронизируем if not self.syncLoginProfile(userName, homeDir, homeProfile, uid, gid, progress): # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False if flagError: # Отмонтируем пользовательские ресурсы в случае ошибки self.errorAndUnmountUserRes = True return False logOutFile = os.path.join(homeDir,self.logOutFile) if syncStatus: self.setVarToConfig("main", {"status_sync":"success"}, configFileName, uid, gid) self.createUserFile(logOutFile,"SUCCESS", uid, gid) else: self.createUserFile(logOutFile,"ERROR", uid, gid) self.setVarToConfig("main", {"status_sync":"error"}, configFileName, uid, gid) self.printWARNING(_("Error in sync with the remote server %s")\ %remoteServer) self.printSUCCESS(_("Mounted user resource of the domain")) self.printOK(_("Get a user profile from the domain") + " ...") return True def mountSleepRes(self,userName,userPwd,uid,gid,res,path): """Монтирует ресурс при неудаче задержка потом повторное монитрование""" textLine = self.mountSambaRes(userName,userPwd,uid,gid,res,path) if not (textLine is None): # Проверяем на монтирование директории if self.isMount(path, 'cifs'): textLineUmount = self.umountSleepPath(path) if not textLineUmount: return False i = 0 while (i/dev/null"%(srcDir, destDir)) != 0: self.printERROR(_("Can not move %s")%srcDir + " " +\ _("to %s")%destDir) return False return True def upgradeProfileClient(self, userName, userHome, pathProfiles): """Переносит данные клиента в директорию без точки например: было /home/.user/.CLD стало /home/.user/CLD Перед вызовом этого метода обязательно должен быть определен атрибут объекта self.clVars - объект переменных """ # Директория хранения старых профилей home = os.path.split(userHome)[0] if os.path.exists(pathProfiles): osLinuxShort = self.clVars.Get("os_linux_shortname") pathNewProfile = os.path.join(pathProfiles, osLinuxShort) pathOldProfile = os.path.join(pathProfiles, "."+osLinuxShort) if not os.path.exists(pathNewProfile) and\ os.path.exists(pathOldProfile) and\ os.listdir(pathOldProfile): # Переносим профиль if not self.copyProfileDir(pathOldProfile, pathNewProfile): return False if not os.path.exists(pathNewProfile): # Создаем директорию для хранения профиля os.mkdir(pathNewProfile) os.chmod(pathNewProfile, 0700) if os.path.exists(pathOldProfile) and\ not os.listdir(pathOldProfile): os.rmdir(pathOldProfile) return True def syncUser(self, userName, userHome, sync, uid, gid, homeProfile, \ progress=False, host="default"): """Синхронизация пользовательских настроек Перед вызовом этого метода обязательно должен быть определен атрибут объекта self.clVars - объект переменных """ flagError = False execStr = "" if sync == "login": # исключаемые пути при синхронизации # /.local/share/akonadi/db_data # хранит базу acanadi # /.mozilla/firefox/calculate.default/urlclassifier3.sqlite # хранит БД для firefox # /.local/share/mime/mime.cache # отключение ошибочного кэширования изображений # /.kde4/share/apps/nepomuk/repository/main/data # база nepomuk # /.VirtualBox # содержит данные о виртуальных машинах if os.path.exists(userHome) and\ os.path.exists(homeProfile): execStr = '/usr/bin/rsync --delete-excluded --delete \ --exclude="/.googleearth" --exclude="/.kde4/share/config/phonondevicesrc" \ --exclude="*~" --exclude="/Home" --exclude="/Disks" --exclude="/FTP" \ --exclude="/.local/share/akonadi/db_data" --exclude="/.VirtualBox" \ --exclude="/.mozilla/firefox/calculate.default/urlclassifier3.sqlite" \ --exclude="/.local/share/mime/mime.cache" \ --exclude="/.kde4/share/apps/nepomuk/repository/main/data" \ --exclude="/.logout" \ --exclude="/.Xauthority" \ --exclude="/.thumbnails" \ --exclude="/.mozilla/firefox/*/Cache" \ --exclude="/.cache" \ --exclude="/.opera/cache" \ --filter="P /.googleearth" --filter="P /Home" --filter="P /Disks" \ --filter="P /.local/share/akonadi/db_data" --filter="P /.VirtualBox" \ --filter="P /.mozilla/firefox/calculate.default/urlclassifier3.sqlite" \ --filter="P /.local/share/mime/mime.cache" \ --filter="P /.kde4/share/apps/nepomuk/repository/main/data" \ --filter="P /.logout" \ --filter="P /.Xauthority" \ --filter="P /.thumbnails" \ --filter="P /.mozilla/firefox/*/Cache" \ --filter="P /.cache" \ --filter="P /.opera/cache" \ --filter="P /FTP" -a -x -v -v -v %s/ %s/' %(homeProfile,userHome) elif sync == "logout": if os.path.exists(userHome) and os.listdir(userHome) and\ os.path.exists(homeProfile): execStr = '/usr/bin/rsync --delete-excluded --delete \ --exclude="/.googleearth" --exclude="/Home" --exclude="/Disks" --exclude="/FTP"\ --exclude="*~" --exclude="/.kde4/cache-*" --exclude="/.kde4/tmp-*" \ --exclude="/.local/share/akonadi/db_data" --exclude="/.VirtualBox" \ --exclude="/.mozilla/firefox/calculate.default/urlclassifier3.sqlite" \ --exclude="/.local/share/mime/mime.cache" \ --exclude="/.Xauthority" \ --exclude="/.kde4/share/apps/nepomuk/repository/main/data" \ --exclude="/.kde4/socket-*" --exclude="/.kde4/share/config/phonondevicesrc" \ --exclude="/.thumbnails" \ --exclude="/.mozilla/firefox/*/Cache" \ --exclude="/.cache" \ --exclude="/.opera/cache" \ -a -x -v -v -v %s/ %s/'%(userHome,homeProfile) else: self.printERROR(_("Method syncUser: option sync=%s incorrect")\ %str(sync)) return False if execStr: host = "" + host +"" rsync = RsyncProgressBar(\ _("Receiving file list from %s") % host + " ...", _("Downloading the user profile from %s") % host \ + " ...", execStr) pathConfig = os.path.join(homeProfile, self.pathConfig) # Удаляем предыдущий ini файл prevIniFile = os.path.join(homeProfile,".calculate.ini") if os.path.exists(prevIniFile): os.remove(prevIniFile) # Создаем директорию для конфигурационных файлов if not os.path.exists(pathConfig): self.createUserDir(uid, gid, pathConfig, mode=False) configFileName = os.path.join(pathConfig, self.configFileDesktop) if sync == "login": # получить переменную files из секции Rsync файла # .calculate.ini try: numfiles = cl_base.iniParser(\ configFileName).getVar('rsync','files') if numfiles is False: if os.path.exists(configFileName): os.remove(configFileName) numfiles = 0 else: numfiles = int(numfiles) except: numfiles = 0 rsync.maximum = numfiles if progress: rsync.run() else: rsync.runsilent() if sync == "logout": rsync.runsilent() try: if cl_base.iniParser(configFileName).setVar('rsync', {'files':rsync.getFilesNum()}): os.chmod(configFileName, 0600) os.chown(configFileName,uid,gid) except: pass rsync.close() if rsync.getExitCode() != 0: try: if cl_base.iniParser(configFileName).setVar(\ 'rsync',{'exitcode':rsync.getExitCode()}): os.chmod(configFileName, 0600) os.chown(configFileName,uid,gid) except: pass self.printERROR(_("Can not execute rsync") + " " + str(sync) +\ " ...") flagError = True else: if sync == "login": if not (os.path.exists(userHome)): self.printERROR(_("Directory %s not exists")%userHome) else: self.printERROR(_("Directory %s not exists")%homeProfile) elif sync == "logout": if not (os.path.exists(userHome)): self.printERROR(_("Directory %s is empty or not exists")\ %userHome) else: self.printERROR(_("Directory %s not exists")%homeProfile) flagError = True if flagError: return False else: # Изменим если нужно права на директории fileObj = cl_profile._file() # Домашняя директория и директория хранения профиля changeDirs = [userHome, homeProfile] for changeDir in changeDirs: # Получаем права на директорию mode,uid,gid = fileObj.getModeFile(changeDir) # Если права не равны 0700 меняем их if mode != 0700: os.chmod(changeDir,0700) return True class tsOpt(cl_base.opt): """Класс для обработки параметров и вывода help Параметры: helpObj объект-справка содержащий необходимые опции notOptError выдавать ошибку при отсутствии опций командной строки optUser проверять хвост командной строки на наличие пользователя """ def __init__(self, helpObj, notOptError=False, optUser=True): # от cl_help получаем короткие и длинные опции shortOpt,longOpt = helpObj.getAllOpt('all', helpObj.relOptions['h']) # вызвать конструктор объекта, распознающего опции cl_base.opt.__init__(self,shortOpt,longOpt) self.nameParams = ['user'] self.sysArgv = sys.argv[1:] self.helpObj = helpObj self.__iter = 0 self.opt = {} self.params = {} self.getopt() # Обработка help self.flagHelp = False # определяем есть ли среди опций опции, которые влияют на показ # опциональных разделов (метод пересечения множеств) helpopt = \ tuple(set(self.opt.keys()).intersection(helpObj.relOptions.keys())) #Если есть опции help if len(helpopt) > 0: print helpObj.getHelp(helpObj.relOptions[helpopt[0]]) self.flagHelp = True else: if optUser and self.params.has_key('user'): if len(self.nameParams) != self.__iter: self.handlerErrOpt() # В случае остсутствия опций командной строки и имени пользователя if notOptError: if not self.opt: self.printErrorNotOpt() self.flagHelp = True if optUser and not self.params.has_key('user'): print helpObj.getHelp(helpObj.relOptions['h']) self.flagHelp = True elif optUser and not self.opt and not self.params.has_key('user'): print helpObj.getHelp(helpObj.relOptions['h']) self.flagHelp = True if not optUser and self.params.has_key('user'): print helpObj.getHelp(helpObj.relOptions['h']) self.flagHelp = True def printErrorNotOpt(self): """Сообщение в случае отсутствия опций""" print _("Options are absent.") def handlerOpt(self,option,value): # Обработчик (опция значение) #print option, value shortOpt = self.helpObj.getShortOpt(option) if not shortOpt: shortOpt = option if not shortOpt in self.opt: self.opt[shortOpt] = value def handlerErrOpt(self): # Обработчик ошибок argv = " ".join(sys.argv[1:]) print _("Unrecognized option") + ' "' + argv + '"\n' + \ _("Try") + ' "' + sys.argv[0].split("/")[-1] + ' --help" ' +\ _("for more information.") def handlerParam(self,param): # Обработчик хвостов (значение) self.__iter += 1 if self.__iter<=len(self.nameParams): self.params[self.nameParams[self.__iter-1]] = param