请大神:shutdown触发shell脚本关闭sybaes数据库的诡异之处

希望厉害的你能帮助我。
我的脚本 myscript.sh 调用脚本 closeSybase.sh
脚本 myscript.sh 里面的除了调用 closeSybase.sh 外,还用来检测 sybase 运行状态,
脚本 closeSybase.sh 的功能是关闭 sybase 数据库,
那么问题是:
我手工执行脚本 myscript.sh 时,它调用脚本 closeSybase.sh 关闭数据库执行后,在 myscript.sh 里面的语句检测 sybase 已经关闭。

而 shutdown 关机触发执行 myscript.sh 的场景下就不好使了,因为 myscript.sh 脚本里面的判断语句检测到数据库始终是运行的(我让脚本打了日志,在日志里面看到的始终是运行的)。

诡异的地方还有:如果把以上两个脚本功能合写在一个脚本里,那么关闭 sybase 的语句执行后,后面检测 sybase 运行与否的语句的检测结果却始终是运行的,无法得到更新的 sybase 运行状态

难道这是脚本的坑吗

贴脚本内容,以及“shutdown 关机触发执行 myscript.sh”具体是怎么实现的

由于代码和日志全在公司内网里,外网无法获取,不可以拷贝,否则违规,后果严重,所以我在这里只能详述一下:

shutdown 关机触发执行 myscript.sh”具体实现: 参照资料:SysVinit 引导脚本 - openSUSE
一:将 myscript.sh 脚本按照 LSB 规范书写:
脚本头部添加注释信息:
### BEGIN INIT INFO
# Provides: qnhds // 指定服务名,可以通过 chkconfig --list 查看服务,如果成功注册为服务,可以看到服务名:qnhds
# Required-Start: // 指定运行此脚本必须运行 (是必须运行) 着的服务,这里没有指定
# Should-Start: nms sybase // 指定运行此脚本需要运行 (不是必须运行) 着的服务是 nms 和 sybase 服务
# Required-Stop: // 指定运行此脚本必须停止 (是必须停止) 的服务,这里没有指定
# Should-Stop: // 指定运行此脚本需要停止 (不是必须运行) 的服务,这里没有指定
# Default-Start: 0 6 // 这一行注释信息指定了默认启动脚本的运行级别是 0 和 6,即是重启或关机时触发 myscript.sh 的执行
# Default-Stop:
# Short-Description: Sound daemon with network support
# Description: stop the nms and sybase service
### END INIT INFO

二:将 myscript.sh 脚本放到 /etc/init.d/ 目录下
三:cd /etc/init.d/ , 执行命令:chkconfig --add myscript.sh
四:cd /etc/init.d/ , 执行命令:insserv myscript.sh
做完以上步骤,就成功地把 myscript.sh 脚本注册为系统服务,并且是开机时 / 关机时启动。
该脚本打印了详细的操作日志,并且打印了 isql 连接数据库、执行停止 sybase 的详细日志。
手动执行 myscript 脚本时,可以正常运行,并且成功执行关闭 sybase 数据库的语句,随后的语句判断 sybase 数据库的状态是停止。从日志里看到的,一切正常,实现了编程目的。
但是在命令行里执行:shutdown -r now 命令时,suse 系统成功重启后,通过查看 myscript 的操作日志,发现 脚本中判断 sybase 运行状态的语句打印的日志显示:sybase 数据库是运行的,日志其他部分与手动执行产生的日志一样,没有区别。这就奇怪了,为什么在手动执行 myscript 脚本的时候可以实现停止 sybase,而 shutdown 触发执行它的时候就得不到数据库停止的结果?

@牵牛花大神

Hi,具体脚本看不到我理解,我现在想知道几件事:

  1. myscript 是怎么检测 Sybase 是否在运行的,通过进程?通过 PID 文件?通过 socket?

  2. 执行 closeSybase 后,你有没有加 wait?Systemd 是并发的…看你的描述我感觉是 closeSybase 执行后就不管它,直接去取状态了。

  3. Systemd 关机时服务的执行顺序跟开机是反的,你的 myscript 是在 ExecStart 里调用的还是在 ExecStop 里调用的?正常是 ExecStop 里。所以你的 myscript 脚本的 stop block 是怎么写的?不会写到 start block 里去了吧?因为 systemd 对 sysvinit 脚本的兼容是 start 对应 ExecStart,stop 对应 ExecStop,然后你的脚本只管关机,你觉得在 start 里执行就好。

Sybase 是闭源的,不过我们可以用 mysql 之类的来模拟的。问题是能够得到解决的,但你的让我们能够模拟出来…

从我的 iPhone 发送,使用 Tapatalk

另外,我觉得两个脚本是可以改成一个 systemd service 的…

从我的 iPhone 发送,使用 Tapatalk

首先答复 4 楼的问题:
Hi,具体脚本看不到我理解,我现在想知道几件事:

  1. myscript 是怎么检测 Sybase 是否在运行的,通过进程?通过 PID 文件?通过 socket?
    回答: 实现方式是 pronum=ps -ef | grep sybase | grep DBSVR | grep -v grep|wc -l
    # 得出进程中包含数据库相关进程的数目
  2. 执行 closeSybase 后,你有没有加 wait?Systemd 是并发的… 看你的描述我感觉是 closeSybase 执行后就不管它,直接去取状态了。
    回答:你说加 wait 的意思是指等待一段时间吗,我在代码中有 sleep 50 ,但实际发现无论 sleep 多久,哪怕是 sleep 600,sybase 运行状态始终是 true。
    后来又添加了问题 1 中的判断 sybase 相关的进程是否存在的语句, 可打印的日志显示 sybase 仍然是运行的。
  3. Systemd 关机时服务的执行顺序跟开机是反的,你的 myscript 是在 ExecStart 里调用的还是在 ExecStop 里调用的?正常是 ExecStop 里。所以你的 myscript 脚本的 stop block 是怎么写的?不会写到 start block 里去了吧?因为 systemd 对 sysvinit 脚本的兼容是 start 对应 ExecStart,stop 对应 ExecStop,然后你的脚本只管关机,你觉得在 start 里执行就好。
    回答:myscript 是在 start 代码块执行的,经过对比确认关机的时间和脚本打印日志的时间,myscript 脚本确实是在关机时启动的。
    Sybase 是闭源的,不过我们可以用 mysql 之类的来模拟的。问题是能够得到解决的,但你的让我们能够模拟出来…
    下面是牵牛花大神的回复:
    资料参考:
    zh.opensuse.org/index.php?title=openSUSE:Packaging_init_scripts&variant=zh-cn
    第一步:
    下面是启动的脚本 startMscript 内容,放在 /etc/init.d/ 目录下,

BEGIN INIT INFO

Provides: Pharbitis

Required-Start:

Should-Start: sybase

Required-Stop:

Should-Stop:

Default-Start: 0 6

Default-Stop: 1 2 3 5

Short-Description: Sound daemon with network support

Description: Starts esound server to allow remote access to sound

END INIT INFO

case “$1” in
start)
timeout 300 bash /opt/tools/myscript.sh
;;
stop)
;;
esac

下面是 myscript 脚本内容,放在 /opt/tools/ 目录下
#!/bin/bash
# 此处省略一些打印日志的代码 和其他辅助代码 , 仅列出调用 jar
# 这里使用 java 调用 closeSybase.sh 关闭 sybase 数据库。在 java 内部调用实现调用 closeSybase.sh,向该脚本传入密码参数
/opt/OSSJRE/linux/java -classpath XXX.jar com.oss.mainFrame "/opt/tools/closeSybase.sh"
sleep 50
pronum=ps -ef | grep sybase | grep DBSVR | grep -v grep|wc -l # 得出进程中包含数据库相关进程的数目
# 判断 pronum 数目是否为 0,下面省略判断及打印日志部分代码

下面是 closeSybase.sh 脚本内容,放在 /opt/tools/ 目录下 
 	 . /opt/sybase/SYBASE.sh 
cd /opt/sybase/OCS*/bin 
bb=$(cat)    # 由于当前脚本是用 -classpath 的形式调用的,变量 bb 用来接收 java 传进来的密码
 isql -SDBSVR -Usa -P${bb} -zus_english -o/opt/isql_result.log<<EOF 
shutdown SYB_BACKUP 
go 
shutdown 
go 
exit 
EOF

	cd /opt/oss/engr/tools/stopNMS_DB.sh_log/ 
	echo " line${LINENO}] stopsinglesybase end of excute..">>${logfile} 
	#sleep 50 
	echo "`ps -ef | grep sybase | grep DBSVR | grep -v grep|wc -l`">>${logfile} 
	echo " now is checking the sybase status......">>${logfile} 
	isdbrun="false" # 此变量用于标记数据库运行状态
	Line_Number=`ps -ef | grep sybase | grep DBSVR | grep -v grep|wc -l` # 得出进程中包含数据库相关进程的数目

if $Line_Number -eq 0 ]; then
# 数据库进程记录为 0,说明已经停止
echo " line${LINENO}] sybase has been shutdown ! ">>${logfile}
echo -e "echo " line${LINENO}] sybase has been shutdown ! ">>${logfile}
echo -e “\033[31m sybase has been shutdown ! \033[0m”>>${logfile}
else 33[31m sybase has been shutdown ! echo " line[${LINENO}] sybase has been shutdown ! “>>${logfile}
echo -e “\03331m sybase has been shutdown ! \0330m”>>${logfile}
else 330m”>>${logfile}
else
# 如果数据库没有完全停止,杀进程(sybase 数据库相关的进程)
pro1=ps -ef|grep RUN_DBSVR|grep -v grep | grep -v killall | awk '{print $3}'
kill -9 $pro1
pro2=ps -ef|grep -sDBSVR|grep -v grep | grep -v killall | awk '{print $3}'
kill -9 $pro2
pro3=ps -ef|grep RUN_DBSVR_back|grep -v grep | grep -v killall | awk '{print $3}'
kill -s 9 $pro3
pro4=ps -ef|grep -SDBSVR_back|grep -v grep | grep -v killall | awk '{print $3}'
kill -s 9 $pro4
#sleep 20
fi
第二步: :执行命令,添加 startMscript 脚本为关机启动项
chkconfig --add startMscript

第三步: : shutdown -r now

第四步: :SUSE 开机成功后,查看打印的日志文件,日志显示关机过程中 myscript 脚本调用 closeSybase.sh 脚本,并 sleep 50 秒后 ,判断 sybase 运行状态的结果 仍然是运行的。
为什么在 SUSE 系统里 手动直接 bash /opt/oss/myscript.sh 却可以成功停止 sybase,好迷乱啊

那我猜就是写到了 start block 里的原因了…正常应该写到 stop block 里…因为你的脚本只干了 stop…start/stop 的意思不是启动停止脚本本身,而是启动停止脚本对应的服务…所以 start 里面都是启动服务的东西,stop 里面都是停止服务的东西,具体的还要验证一下。有时候程序是运行了,但能跑不代表如预期的跑,摊手…

从我的 iPhone 发送,使用 Tapatalk

这是我的版本 /etc/init.d/sybase:

#!/bin/sh
### BEGIN INIT INFO
# Provides:	sybase (注意这里的名字一定要写成 /etc/init.d 下面的那个名字,这里是 sybase,不能大写首字母,或者写成其它什么鬼)
# Required-Start:
# Should-Start:
# Required-Stop:
# Should-Stop:
# Default-Start:	0 6
# Default-Stop:		1 2 3 4 5
# Short-Description:	Sybase daemon
# Description:	Start Sybase daemon
### END INIT INFO

case "$1" in
	start)
		/home/marguerite/start.sh
	;;
	stop)
               /home/marguerite/stop.sh
	;;
esac

跟你的没什么不同,chkconfig -a sybase 之后(chkconfig -a 就会调用 insserv,所以你后面那步 insserv 是多余的),通过 sudo systemctl status sybase.service 能看到

● sybase.service - LSB: Sybase daemon
   Loaded: loaded (/etc/init.d/sybase; bad; vendor preset: disabled)
   Active: inactive (dead)
     Docs: man:systemd-sysv-generator(8)

是被 systemd 管理。这是我的 /home/marguerite/start.sh

#!/bin/sh
echo "sybase start block runs" | systemd-cat -t sybase -p emerg

我的 /home/marguerite/stop.sh

#!/bin/sh
echo "sybase stop block runs" | systemd-cat -t sybase -p emerg

命令的意思是把某句话写到 systemd 的 journal 里,方便我确定运行了哪个脚本。
如果关机是运行的 start block 的话,journal 里就会有那句话,运行的 stop block 的话,里面就应该是另外一句。

sudo journalctl --since 22:57 | grep “sybase”

(困了,详情如何,且待下回分晓)

非常感谢 marguerite,今天在公司折腾了一天,同时搞两个需求,都是在空档时间切换着搞,环境来回搭建地折腾,好累。
看到你的回复真的很感动,我在其他论坛发布的帖子根本没有人回复,为 marguerite 的精神占赞。
明天到公司按照你的方法再搞一遍。多谢多谢

为何无法将 level 6 设置成 on 呢,经过实际运行,确认重启时会启动脚本的运行

虽说 level 6 没有设置成 on ,但关机,重启 SUSE 确实执行了脚本

我觉得你觉得脚本执行了一定是错觉。我昨晚关机再开好多次,journal 里既没有 start block 也没有 stop block,也就是说服务根本没在 halt 的时候跑。实际上证明脚本执行了的方法很简单,随便 echo 点什么,然后用 systemd-cat 输出到 journal,journal 里有你 echo 的东西就是脚本执行了。

我猜测你觉得脚本执行了是因为它被自动转换成 systemd service 后是在开机运行了你的 start block。

于是我又仔细看了一下,发现一开始就被你绕进去了。你对运行级别 runlevel 的理解不对…运行级别是“处在那个状态”的时候,不是“那个状态发生前”的时候,所以比如 0 halt 状态,这个时候系统是关机断电了,什么也执行不了,6 reboot 同理。

真正想要关机或重启前执行什么东西,应该跟普通脚本一样,运行级别的 Default-Start 是 3 5,Default-Stop 是 0 1 2 4 6。因为 0 1 2 4 6 几乎都是特殊状态,处在那种状态里基本什么都干不了。然后通过 Required-Start 和 Required-Stop 这些脚本 dependency 来实现关机或重启执行脚本的 stop block。

这个逻辑是设计好的,你只能跟着逻辑走,不能去创造自己的逻辑。这个逻辑就是,在运行级别 3 5 下,通过 Required-Start 在比如有网了之后启动 sshd,通过 Required-Stop 在没网之前就停止 sshd,所以我一直强调关机跟开机是反的。你这里最多只是需要一些特殊的服务来确定关机,类似于 systemd 里的 multiuser.target,这个查资料能找到 sysvinit 对应的。所以你想要在关机执行你写的那个 sysvinit 脚本的 start 部分,简单说就是不可能的,start 部分只能在开机执行。

所以你的问题不是那两个脚本,是你对 runlevel,sysvinit 和 systemd 的理解有点想当然。回头我写个例子出来吧。

你的脚本就算能执行我觉得也是相当随机的,因为它没有把自己的依赖关系理顺,最终关机了它肯定会被停止,但如果那个时候连根分区都卸载了,你肯定取不到进程因为没有 ps 命令之类的。没法解释更多,只能说非预期行为,因为我也不知道它究竟什么时候停止的。

从我的 iPhone 发送,使用 Tapatalk

最终能用的 /etc/init.d/sybase 在这里,主要就是改了 runlevel,加了 Default-Start 和 Default-Stop。

#!/bin/sh
### BEGIN INIT INFO
# Provides:   sybase
# Required-Start: $remote_fs $syslog
# Should-Start:
# Required-Stop: $remote_fs $syslog
# Should-Stop:
# Default-Start:   3 4 5
# Default-Stop:      0 1 2 6
# Short-Description:   Sybase daemon
# Description:   Stop Sybase daemon and check database status
### END INIT INFO

case "$1" in
   start)
   ;;
   stop)
       /opt/sybase/myscript.sh
   ;;
esac

开机,关机,重启都会运行,其中只要关机或重启都会调用 myscript.sh。开机调用的是 start block 所以什么都不做。

再具体的,你先这么改了再看。

另外建议用 systemd-cat -t sybase-daemon -p info/warning/emerg 写入 systemd journal,这样比较好调试。写自己的 $log 里对于业务是方便,但对于过程不方便,因为你不知道与此同时 systemd 在干嘛。写到一起就方便多了。

厉害,你说的对,我处从做这个需求时就搞不清楚运行级别的事情,寨主好厉害。
似乎明白了一些你强调的 start 和 stop 的意义。