Merge branch 'fling'
This commit is contained in:
46
interface/ri_inst_cli.py
Normal file
46
interface/ri_inst_cli.py
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/python
|
||||
# DESCRIPTION: display module for install (cli)
|
||||
#
|
||||
# SCRIPT NAME: ri_inst_cli.py
|
||||
#
|
||||
# MENTOR: Li Zhi
|
||||
#
|
||||
# AUTHOR: Ling Fen
|
||||
#
|
||||
# EMAIL: fling@linx-info.com
|
||||
#
|
||||
# DATE: 2010-09-20
|
||||
#
|
||||
# HISTORY:
|
||||
# REVISOR DATE MODIFICATION
|
||||
# Ling Fen 2010-09-04 create
|
||||
#
|
||||
|
||||
from ri_oper import language, Rate
|
||||
def set_task(task_name):
|
||||
print task_name
|
||||
|
||||
def set_sub_task(sub_task):
|
||||
print sub_task
|
||||
|
||||
def set_task_scale():
|
||||
print '%', Rate.value
|
||||
|
||||
def set_task_over():
|
||||
string = raw_input("Rocky system install success,plase reboot now![Y?]")
|
||||
if string.lower() == "y":
|
||||
return True
|
||||
return False
|
||||
|
||||
def root_destroy():
|
||||
pass
|
||||
|
||||
def error(oper, ret):
|
||||
if language == 'chinese':
|
||||
print "%s:\n Error number: %d\n %s"%(oper.chinese_name, ret, oper.return_value[ret][1])
|
||||
else:
|
||||
print "%s:\n Error number: %d\n %s"%(oper.english_name, ret, oper.return_value[ret][0])
|
||||
|
||||
|
||||
def start(func):
|
||||
func()
|
||||
99
interface/ri_inst_tk.py
Normal file
99
interface/ri_inst_tk.py
Normal file
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/python
|
||||
#coding: utf-8
|
||||
# DESCRIPTION: display module for install (tk)
|
||||
#
|
||||
# SCRIPT NAME: ri_inst_tk.py
|
||||
#
|
||||
# MENTOR: Li Zhi
|
||||
#
|
||||
# AUTHOR: Ling Fen
|
||||
#
|
||||
# EMAIL: fling@linx-info.com
|
||||
#
|
||||
# DATE: 2010-09-20
|
||||
#
|
||||
# HISTORY:
|
||||
# REVISOR DATE MODIFICATION
|
||||
# Ling Fen 2010-09-04 create
|
||||
#
|
||||
|
||||
from Tkinter import *
|
||||
import tkMessageBox
|
||||
import os
|
||||
from ri_oper import language, Rate
|
||||
|
||||
def set_task(task_name):
|
||||
lab_operation.configure(text=task_name)
|
||||
lab_operation.update()
|
||||
|
||||
def set_sub_task(sub_task):
|
||||
txt_suboperation.insert(END,sub_task)
|
||||
txt_suboperation.see(END)
|
||||
|
||||
def set_task_scale():
|
||||
scl_progress.set(Rate.value)
|
||||
scl_progress.update()
|
||||
|
||||
def set_task_over():
|
||||
ret = tkMessageBox.askokcancel(title="Install",message="%s"%(language=='chinese' and "凝思磐石系统安装完成,请重新启动系统"or "Rocky system install completed,plase reboot"))
|
||||
return ret
|
||||
|
||||
def root_destroy():
|
||||
root.destroy()
|
||||
|
||||
def error(oper, ret):
|
||||
if language == 'chinese':
|
||||
tkMessageBox.showerror('Error',u"%s:\n 错误值: %d\n %s"%(oper.chinese_name, ret, oper.return_value[ret][1]))
|
||||
else:
|
||||
tkMessageBox.showerror('Error',"%s:\n Error number: %d\n %s"%(oper.english_name, ret, oper.return_value[ret][0]))
|
||||
|
||||
def start(func):
|
||||
root.protocol('WM_TAKE_FOCUS', func)
|
||||
root.mainloop()
|
||||
|
||||
#root widget
|
||||
root = Tk()
|
||||
root.geometry("%sx%s+0+0" %(root.winfo_screenwidth(),root.winfo_screenheight()))
|
||||
root.title('Linx')
|
||||
|
||||
# root subwidget
|
||||
stringvar_bar=StringVar()
|
||||
|
||||
#label
|
||||
lab_task=Label(root,text='%s:'%(language=="chinese" and u"任务" or "Task"))
|
||||
lab_progress_bar=Label(root,text='%s:'%(language=="chinese" and u"进度" \
|
||||
or 'Progress bar'))
|
||||
lab_operation=Label(root,text="")
|
||||
lab_linx=Label(root,text="Linx")
|
||||
|
||||
txt_suboperation=Text(root,width=70,height=10)
|
||||
|
||||
#operation lable and taskname
|
||||
lab_task.grid (row=1, column=0, sticky='SEW')
|
||||
lab_operation.grid (row=1, column=1, columnspan=2,sticky="S")
|
||||
txt_suboperation.grid(row=2, column=1,columnspan=2)
|
||||
lab_progress_bar.grid(row=3, column=0, sticky='NEW')
|
||||
lab_linx.grid (row=4, column=1, sticky='E')
|
||||
|
||||
#logo
|
||||
photo_bm = PhotoImage(file="%s/../data/linxlogo.gif"\
|
||||
%os.path.split(os.path.realpath(__file__))[0])
|
||||
logo_label=Label(root,image=photo_bm)
|
||||
logo_label.grid(row=0,column=0,padx=50,pady=50,sticky='NW')
|
||||
|
||||
#progress bar
|
||||
scl_progress=Scale(root,orient=HORIZONTAL,resolution=0.01,digits=4,from_=0,to=100,variable=stringvar_bar,label='%')
|
||||
scl_progress.grid(row=3,column=1,columnspan=2,sticky='NEW')
|
||||
|
||||
#root
|
||||
root.rowconfigure(0,weight=1)
|
||||
root.rowconfigure(1,weight=1)
|
||||
root.rowconfigure(2,weight=1)
|
||||
root.rowconfigure(3,weight=1)
|
||||
root.rowconfigure(4,weight=1)
|
||||
root.columnconfigure(0,weight=1)
|
||||
root.columnconfigure(1,weight=1)
|
||||
root.columnconfigure(2,weight=1)
|
||||
root.columnconfigure(3,weight=1)
|
||||
|
||||
|
||||
72
interface/ri_install.py
Normal file
72
interface/ri_install.py
Normal file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
#
|
||||
# DESCRIPTION: install for OS
|
||||
#
|
||||
# SCRIPT NAME: ri_install.py
|
||||
#
|
||||
# Input:
|
||||
# [-d|--display display-mode ] install display (tk or cli)
|
||||
# [-l|--language language] insatll display language (chinese or english)
|
||||
#
|
||||
# MENTOR: Li Zhi
|
||||
#
|
||||
# AUTHOR: Ling Fen
|
||||
#
|
||||
# EMAIL: fling@linx-info.com
|
||||
#
|
||||
# DATE: 2010-09-20
|
||||
import sys
|
||||
import getopt
|
||||
import ri_oper
|
||||
import os
|
||||
def install_over(ret):
|
||||
if ri_oper.Rate.value == 100 and ret == 0:
|
||||
if display.set_task_over():
|
||||
print "reboot ..."
|
||||
os.system("reboot")
|
||||
else:
|
||||
display.root_destroy()
|
||||
|
||||
def main():
|
||||
global has_run
|
||||
if has_run: return
|
||||
has_run = True
|
||||
for instance in oper_list:
|
||||
ret = instance.install()
|
||||
if ret:
|
||||
display.error(instance, ret)
|
||||
break
|
||||
else:
|
||||
ri_oper.display_sub_operation((ri_oper.language=='chinese' \
|
||||
and instance.chinese_name or instance.english_name)+\
|
||||
(ri_oper.language=='chinese' and u' 成功' or ' success'))
|
||||
install_over(ret)
|
||||
|
||||
has_run = False
|
||||
|
||||
oper_list = [ri_oper.MakeRaid(5),ri_oper.Format(5),ri_oper.Mount(5),ri_oper.InstallPkg(40),ri_oper.MakeRaidConfigure(5),ri_oper.ConfigureFstab(5),ri_oper.GenerateIssue(5),ri_oper.ConfigureNetwork(5),ri_oper.MakeServiceAutoBoot(5),ri_oper.CopyKernel(5),ri_oper.ConfigureBootloader(5),ri_oper.BootLoader(5),ri_oper.ExecFinishInstall(5)]
|
||||
|
||||
def print_usages():
|
||||
print 'Usages: %s [-l|--language language] [-d|--display display-mode]' %sys.argv[0]
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "l:d:", ["language=", "display="])
|
||||
except getopt.GetoptError:
|
||||
print_usages()
|
||||
sys.exit(1)
|
||||
|
||||
ri_oper.language='english'
|
||||
disp = 'ri_inst_cli'
|
||||
for opt,arg in opts:
|
||||
if opt in ("-l","--language"):
|
||||
ri_oper.language=arg.lower()
|
||||
elif opt in ("-d", "--display"):
|
||||
disp = 'ri_inst_%s' %arg.lower()
|
||||
exec 'import %s as display' %disp
|
||||
|
||||
ri_oper.display_operation = display.set_task
|
||||
ri_oper.display_sub_operation = display.set_sub_task
|
||||
ri_oper.display_scale = display.set_task_scale
|
||||
|
||||
display.start(main)
|
||||
312
interface/ri_oper.py
Normal file
312
interface/ri_oper.py
Normal file
@@ -0,0 +1,312 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# DESCRIPTION: operation install method
|
||||
#
|
||||
# SCRIPT NAME: ri_oper.py
|
||||
#
|
||||
# MENTOR: Li zhi
|
||||
#
|
||||
# AUTHOR: Ling Fen
|
||||
#
|
||||
# EMAIL: fling@linx-info.com
|
||||
#
|
||||
# DATE: 2010-09-20
|
||||
#
|
||||
# HISTORY:
|
||||
# REVISOR DATE MODIFICATION
|
||||
# Ling Fen 2010-09-04 create
|
||||
|
||||
import ri_data
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
if os.path.isfile("../xml/install.xml"):
|
||||
ri_data.init_from_xml()
|
||||
|
||||
display_operation = None
|
||||
display_sub_operation = None
|
||||
display_scale = None
|
||||
language = 'english'
|
||||
|
||||
class Rate:
|
||||
''' class for installing progress, value is from 0 to 100'''
|
||||
value = 0
|
||||
@staticmethod
|
||||
def increase(n, max=100):
|
||||
if Rate.value+n <= max:
|
||||
Rate.value += n
|
||||
@staticmethod
|
||||
def set(n):
|
||||
Rate.value = n
|
||||
|
||||
class Operation:
|
||||
''' This is a common base class for all install operations.
|
||||
The instances of its derived class should have the following
|
||||
data members:
|
||||
chinese_name - operation name
|
||||
english_name - operation name
|
||||
script - shell script name
|
||||
return_value - shell script return value
|
||||
score - an abstract value, for example score is 5,
|
||||
steps - the subprocess of the number of runs
|
||||
step - the subprocess run one time
|
||||
total is 100, if this operations is completed, 5% work is done.
|
||||
|
||||
and the following methods:
|
||||
get_arguments - prepare arguments for scipt
|
||||
get_stdin - prepare stdin for script
|
||||
install - install progress '''
|
||||
|
||||
def __init__(self,e,c,s,scr):
|
||||
''' Operation(base class) init method
|
||||
return_value is a dict, key is return code, value is a tuple, first
|
||||
element is English, second element is Chinese.'''
|
||||
self.return_value = {0:('success', u'成功'), \
|
||||
1:('argument error',u'参数错误'), \
|
||||
2:('deivce node do not exist',u'设备节点不存在'),\
|
||||
3:('filesystem no implement',u'文件系统没有实现'),\
|
||||
}
|
||||
self.english_name = e
|
||||
self.chinese_name = c
|
||||
self.script = s
|
||||
self.score = scr
|
||||
self.steps = 1
|
||||
self.step = 0
|
||||
|
||||
def install(self):
|
||||
if display_operation:
|
||||
display_operation(language == 'chinese' and self.chinese_name or self.english_name)
|
||||
|
||||
max = Rate.value+self.score <100 and Rate.value+self.score or 100
|
||||
process = subprocess.Popen("./%s " %self.script + ' '.join(self.get_arguments()),\
|
||||
stdin=subprocess.PIPE,stdout=subprocess.PIPE, shell=True, \
|
||||
cwd="%s/../operation"%os.path.split(os.path.realpath(__file__))[0])
|
||||
process.stdin.write(self.get_stdin())
|
||||
process.stdin.close()
|
||||
while True:
|
||||
line = process.stdout.readline()
|
||||
if not line:
|
||||
break
|
||||
if line[0] == '@' and self.step < self.steps:
|
||||
self.step += 1
|
||||
if display_sub_operation:
|
||||
display_sub_operation(line[1:])
|
||||
Rate.increase(float(self.score)/self.steps, max)
|
||||
if display_scale: display_scale()
|
||||
ret = process.wait()
|
||||
Rate.set(max)
|
||||
if display_scale: display_scale()
|
||||
return ret
|
||||
|
||||
def get_arguments(self):
|
||||
return []
|
||||
def get_stdin(self):
|
||||
return ''
|
||||
|
||||
class Format(Operation):
|
||||
''' class for format partition '''
|
||||
def __init__(self, scr):
|
||||
Operation.__init__(self, 'format partition', u'格式化分区', 'format_partition.sh', scr)
|
||||
self.return_value[127] = ('format partition utils not found', u'没有格式化硬盘功能')
|
||||
|
||||
def get_stdin(self):
|
||||
format=''
|
||||
n = 0
|
||||
for instance in ri_data.MountPoint.list:
|
||||
if instance.format == "yes" and instance.filesystem != '':
|
||||
format+="/dev/%s %s\n"%(instance.device,instance.filesystem)
|
||||
n += 1
|
||||
self.steps += n
|
||||
return format
|
||||
|
||||
class MakeRaid(Operation):
|
||||
"""class for make raid"""
|
||||
flag=False
|
||||
def __init__(self,scr):
|
||||
Operation.__init__(self,"make RAID",u"正在制作RAID磁盘阵列","mkraid_wrapper.sh",scr)
|
||||
def get_stdin(self):
|
||||
args = ''
|
||||
n = 0
|
||||
for instance in ri_data.Raid.list:
|
||||
if instance.from_os == 'no':
|
||||
MakeRaid.flag=True
|
||||
args += '-n /dev/%s -l %s -s %s -a %s\n' %(instance.device, instance.level, \
|
||||
','.join([ '/dev/%s' %sp for sp in instance.spare_components]), \
|
||||
','.join([ '/dev/%s' %ac for ac in instance.active_components]))
|
||||
n += 1
|
||||
self.steps += n
|
||||
return args
|
||||
|
||||
class MakeRaidConfigure(Operation):
|
||||
"""class for make raid configure"""
|
||||
def __init__(self,scr):
|
||||
Operation.__init__(self,"make raid configure file",u"配置raid配置文件","mkraidconf.sh",scr)
|
||||
def install(self):
|
||||
if MakeRaid.flag:
|
||||
Operation.install(self)
|
||||
else:
|
||||
max = Rate.value+self.score <100 and Rate.value+self.score or 100
|
||||
Rate.set(max)
|
||||
return 0
|
||||
|
||||
def get_arguments(self):
|
||||
return ["/etc/mdadm.conf"]
|
||||
|
||||
class Mount(Operation):
|
||||
"""class for mount partition"""
|
||||
def __init__(self,scr):
|
||||
Operation.__init__(self,'mount partition',u'挂载分区','mount_partition.sh',scr)
|
||||
self.return_value[1]=("device node doesn't exist",u"设备结点不存在")
|
||||
self.return_value[32]=("mount failure",u"挂载失败")
|
||||
|
||||
def get_stdin(self):
|
||||
mount=''
|
||||
n = 0
|
||||
for instance in ri_data.MountPoint.list:
|
||||
if instance.directory != '' and instance.filesystem !='':
|
||||
mount+="/dev/%s %s %s\n"%(instance.device,instance.directory,instance.filesystem)
|
||||
n += 1
|
||||
self.steps += n
|
||||
return mount
|
||||
|
||||
class InstallPkg(Operation):
|
||||
"""class for install packages"""
|
||||
def __init__(self,scr):
|
||||
Operation.__init__(self,'install packages',u'安装软件包','install_pkg.sh',scr)
|
||||
self.return_value[2]=("source directory pkg_source_dir doesn't exist",u"软件包所在目录不存在")
|
||||
self.return_value[3]=("package in source doesn't exist",u"软件包不存在")
|
||||
|
||||
def get_stdin(self):
|
||||
pkgname=[]
|
||||
for i in ri_data.Group.dict.values():
|
||||
if i.install=='no':
|
||||
continue
|
||||
else:
|
||||
pkgname+=i.mandatory
|
||||
if i.selection=='all':
|
||||
pkgname += [ j[0] for j in i.optional ]
|
||||
else:
|
||||
pkgname += [ j[0] for j in i.optional if j[1] == 'yes' ]
|
||||
|
||||
self.steps += len(pkgname)
|
||||
return '\n'.join(pkgname)
|
||||
|
||||
def get_arguments(self):
|
||||
return ["-s","/Rocky/packages"]
|
||||
|
||||
class ConfigureFstab(Mount):
|
||||
"""class for configure /etc/fstab"""
|
||||
def __init__(self,scr):
|
||||
Operation.__init__(self,'configure fstab',u'配置/etc/fstab文件','configure_fstab.sh',scr)
|
||||
self.return_value[1]=("fstab doesn't exist",u"/etc/fstab文件不存在")
|
||||
|
||||
class GenerateIssue(Operation):
|
||||
"""class for generate /etc/issue"""
|
||||
def __init__(self,scr):
|
||||
Operation.__init__(self,'generate issue',u'生成/etc/issue文件','generate_issue.sh',scr)
|
||||
|
||||
def get_arguments(self):
|
||||
args=[]
|
||||
# FIXME if tag file format is error?
|
||||
if os.path.isfile("/tag") :
|
||||
fd=open("/tag","r")
|
||||
string = fd.read()
|
||||
list = string.split('\n')[0].split('-')
|
||||
args = ['-v']+[list[0][5:]]+["-a"]+[list[1]]+["-r"]+[list[2]]+['-d']+['-'.join(list[3:])]
|
||||
return args
|
||||
|
||||
class ConfigureNetwork(Operation):
|
||||
"""class for configure network"""
|
||||
def __init__(self,scr):
|
||||
Operation.__init__(self,'configure network',u'配置网络','configure_network.sh',scr)
|
||||
self.return_value[2]=("ip or netmask or geteway address incorrect",u"ip或子网掩码或网关不正确")
|
||||
self.return_value[3]=("hosts/resolv.conf/ifcfg-eth0/network doesn't exist",u"文件hosts、resolv.conf、ifcfg-eht0、network 不存在")
|
||||
|
||||
def get_arguments(self):
|
||||
network=''
|
||||
data=ri_data.Network
|
||||
if data.configuration == "static":
|
||||
network+="-t static "
|
||||
if data.hostname is not '':
|
||||
network+="-h %s "%data.hostname
|
||||
if data.domain is not '':
|
||||
network+="-d %s "%data.domain
|
||||
if data.ip is not '':
|
||||
network+="-i %s "%data.ip
|
||||
if data.mask is not '':
|
||||
network+="-n %s "%data.mask
|
||||
if data.gateway is not '':
|
||||
network+="-g %s "%data.gateway
|
||||
if data.primary_dns is not '':
|
||||
network+="-p %s "%data.primary_dns
|
||||
if data.secondary_dns is not '':
|
||||
network+="-s %s "%data.secondary_dns
|
||||
else:
|
||||
network+="-t dynamic "
|
||||
if data.hostname is not '':
|
||||
network+="-h %s "%data.hostname
|
||||
return [network]
|
||||
|
||||
|
||||
class MakeServiceAutoBoot(Operation):
|
||||
"""class for make service autoboot"""
|
||||
def __init__(self,scr):
|
||||
Operation.__init__(self,"make service autoboot",u"启动服务器","mk_serv_autoboot.sh",scr)
|
||||
self.return_value[2]=("boot script doesn't exist",u"引导脚本不存在")
|
||||
|
||||
def get_stdin(self):
|
||||
serv=''
|
||||
n = 0
|
||||
for i in ri_data.Service.list:
|
||||
if i.start == "yes":
|
||||
serv+='%s %s %s %s\n'%(i.package,i.script,i.name,i.number)
|
||||
n += 1
|
||||
self.steps += n
|
||||
return serv
|
||||
|
||||
class CopyKernel(Operation):
|
||||
"""class for copy kernel"""
|
||||
def __init__(self,scr):
|
||||
Operation.__init__(self,"copy kernle",u"拷贝内核","copy_kernels.sh",scr)
|
||||
self.return_value[1]=("kernel directory/modules directory/initrd.gz/makeinitrd doesn't exist",u"内核目录、模块目录、initrd.gz、makeinitrd 不存在")
|
||||
|
||||
class ExecFinishInstall(Operation):
|
||||
"""class for exec finish install"""
|
||||
def __init__(self,scr):
|
||||
Operation.__init__(self,"exec finish install",u"安装完成执行的脚本","exec_finish_install.sh",scr)
|
||||
def get_stdin(self):
|
||||
return "99finish_install.sh\n"
|
||||
|
||||
class BootLoader(Operation):
|
||||
"""class for bootloader"""
|
||||
def __init__(self,scr):
|
||||
Operation.__init__(self,"install bootloader",u"安装引导程序","install_bootloader.sh",scr)
|
||||
self.return_value[2]=("bootloader type doesn't specify",u"没有指定引导程序的类型")
|
||||
self.return_value[3]=("bootloader no implement",u"没有执行bootloader")
|
||||
self.return_value[127]=("bootloader utils not found",u"没发现bootloader的utils")
|
||||
|
||||
def get_arguments(self):
|
||||
# only grub?
|
||||
return ["-t grub"]
|
||||
|
||||
class ConfigureBootloader(Operation):
|
||||
"""class for configure bootloader file"""
|
||||
def __init__(self,scr):
|
||||
Operation.__init__(self,"configure bootloader",u"配置bootloader","configure_bootloader_cnf.sh",scr)
|
||||
self.return_value[2]=("bootloader type doesn't specify",u"没有指定引导程序的类型")
|
||||
self.return_value[3]=("bootloader configure file doesn't exist",u"引导程序配置文件不存在")
|
||||
self.return_value[4]=("bootloader configuration file doesn't exist",u"引导程序配置文件不存在")
|
||||
|
||||
def get_arguments(self):
|
||||
bootloader=[]
|
||||
# FIXME if tag file format is error?
|
||||
if os.path.isfile("/tag"):
|
||||
fd = open("/tag","r")
|
||||
string = fd.read()
|
||||
list = string.split('-')
|
||||
for instance in ri_data.MountPoint.list:
|
||||
if instance.directory == '/':
|
||||
# only grub?
|
||||
bootloader=['-t','grub','-r',instance.device,'-k',ri_data.SerialNumber.value,'-o',list[0][5:].split('.')[0]+'.'+list[0][5:].split('.')[1]]
|
||||
break
|
||||
return bootloader
|
||||
28
operation/mkraid_wrapper.sh
Executable file
28
operation/mkraid_wrapper.sh
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
# DESCRIPTION: better deal with arguments for mkraid.sh (arguments---> stdin)
|
||||
#
|
||||
# SCRIPT NAME: mkraid_wrapper.sh
|
||||
#
|
||||
# MENTOR: Li Zhi
|
||||
#
|
||||
# AUTHOR: Ling Fen
|
||||
#
|
||||
# EMAIL: fling@linx-info.com
|
||||
#
|
||||
# DATE: 2010-09-20
|
||||
#
|
||||
# HISTORY:
|
||||
# REVISOR DATE MODIFICATION
|
||||
# Ling Fen 2010-09-18 create
|
||||
#
|
||||
|
||||
# This file is a wrapper to mkraid.sh.
|
||||
|
||||
while read line
|
||||
do
|
||||
$(dirname $0)/mkraid.sh "$line"
|
||||
ret=$?
|
||||
if [ $ret -ne 0 ];then
|
||||
exit $ret
|
||||
fi
|
||||
done
|
||||
Reference in New Issue
Block a user