u盘程序自启
文章目录
背景
依然是折腾我的blockly4pi项目的时候,顺路折腾的东西,感觉比较有趣,可能在一些好玩的场合能用到,分享出来
题目有点噱头,实际上u盘程序不是自行启动,而是系统中有它的内应,这不是一个病毒程序: ) 不过你依然可以用它做些好玩的事情,甚至闹剧,这些就取决于想象力啦,18岁以下儿童请在家长陪同下观看( 逃:) )
在blockly4pi中,树莓派与李老师的硬件积木采用无线(目前是wifi)的方式连接,树莓派与blockly前端页面(电脑)的连接也是无线.早先的机制是树莓派发射出无线热点,其他设备连接它,考虑到无线的安全性(目前的物联网就极不安全),我们允许用户修改默认密码,在树莓派里配置wifi热点密码是个烦人的技术活,blockly擅长简化这类工作,为了方便非技术型用户使用(这是个教育项目),我照例用blockly把这个过程也积木化了:
但是依然存在一些问题
问题描述
首先是树莓派模拟出来的热点不是标准的wifi协议,原因是树莓派3内置的网卡不支持. 因为协议不标准,硬件积木连接树莓派时,可能会出现连接失败的情况
要解决这个问题,可以采用市面主流的usb网卡来发射热点,但需要手动编译hostapd,打上驱动的补丁,比较烦人
而我们不想放弃无线连接的方案(至于不采用蓝牙和xbee的原因之后有机会再说),于是我们想到的策略是硬件积木当热点。为了简化网络,我们决定区分调试和生产环境,在调试环境中使用websocket来实时做实验;而部署运行时,则采用u盘作为中介,放弃无线传输程序以换取稳定性和简洁
思路
采用u盘来传递blockly生成的程序最初是@TG主意,@TG是我现在的老板(学信息安全出身),我最初不确定这个思路是否行得通,我第一反应是这特么是个病毒:
“u盘插入电脑,立即运行其中的目标程序”
@TG说他学生时代玩这些比较熟,技术层面应该没问题,不过他也表示基本是在windows下折腾,当时windows漏洞多,至于我们用的是linux(raspbian),可行性还有待评估
我当天做了下试验,果真可行,思路上其实很容易:首先树莓派是我们自己的,上边跑什么进程,完全可自行决定,那在里边安排一个内应帮忙开门就是了(哈哈,监守自盗的守护进程),监听u盘mount事件,然后找到挂载上来的u盘里的目标程序(python脚本),将它run起来就好了
我想到watchdog之类的工具,试了一下,有几个坑:首先文档糟糕,能找到的文档,基本只是介绍监控一个目录的变化,没有对mount的监控,当然有一些fork分支(pydica-watchdog)试图去做了(我们后边再说),其中一个不能忍的地方时,事件会被随机通知一或两次,完全摸不着头脑
于是我决定自己来写,在linux中一切皆文件,这样一来即便我对底层系统事件不熟也无所谓,挂载的u盘不过是新增个的目录而已啊!
说干就干!
动手
喜欢直接读代码的老司机这时候可能要发话了:
talk is cheap show me the code (废话少说,放码过来)
好好好,这就甩你一脸源码:udisk_watch.py
主体程序非常简单:
|
|
每隔3秒扫描一遍/media/pi/
,如果有新的u盘挂载过来,则会出现类似/media/pi/u_disk1
这类目录,这个目录我们视为u盘根目录,之后在u盘根目录里通过get_newest_codetest
函数寻找最新的目标程序(我们将目标程序命名类似codetest.py),get_newest_codetest
的实现如下:
|
|
这里似乎有点啰嗦,直接找codetest.py不就好了.
补充下之所以需要寻找最新的以codetest开头的目标程序,是因为我们的blockly4pi平台会在用户拼搭好程序积木后,生成对应的程序,浏览器有个机制,会根据因为你下载了多次同名程序而导致新的程序被自动重命名,类似:codetest(1).py、codetest(2).py这样,,尽管我们可以让用户自行处理(自己命名为codetest.py放到u盘里),到考虑到非技术用户的学习成本和出错概率(输入法半角符号之类的坑),我们决定在程序中功能自行处理,用户无需对生成的程序进行处理,一股脑拖到u盘里,然后把u盘查到树莓派上,我们的程序就会自行寻找最新的程序并运行它
另外值得一提的是,我们的blockly4pi web 操作台是个纯前端页面(这意味着它可以无处不在),托管在CDN,至于如何自动为用户生成并下载codetest.py(大家知道纯前端无法用js生成并下载python脚本文件到本地)(ps:h5有新的接口,参考文末更新),用到了类似微服务的概念,有一个无状态的服务等待blockly页面的请求,然后将请求数据包裹成脚本文件,并在用户不知情的情况下,下载下来,如此一来用户自始至终不需要离开我们的实验台
回到我们的话题,通过上边的函数我们找到了最新的程序,接下来就是如何运行它,这里的关键是如何只运行一次(这是我弃用watchmedo(watchdog)的原因,它会莫名其妙地多次运行,不能忍)。这个问题有许多解决方案,本质上都是为程序添加记忆功能(状态). 我们采用扫描目录的机制,木已成舟,就继续沿着这条路,简单粗暴的做法是,在进程里存下已运行脚本的指纹(md5), 通过比对它是否发生变化决定是否运行:
|
|
全局变量last_md5_udisk_codetest
记录着上次运行的文件的指纹,generate_file_md5
函数用来采集文件指纹
以上 : )
完整的代码看这里
一些想法延伸
如果用supervisor来管理上述的脚本,可以轻松实现开机自启
如果使用pyinstaller编译上述脚本(需要调整,建议参考文末的项目,项目作者是个老司机,对不同的操作系统十分熟悉),你可以把它变为一个不依赖于python的exe文件,跑在所有的windows上,至于你想干什么,取决于想象力 :)
附录:pydica-watchdog
监控mount事件,可以使用watchdog的这个衍生版:pydica-watchdog
这个项目年久失修,直接安装会报依赖库错误(argh),你需要这样安装:
|
|
之后像watchdog一样使用它:
|
|
同watchdog一样,它也有间歇性执行两次的问题:
更新(2017.5.9)
我之前提到纯前端不能生成python脚本有误,h5有这样的接口,(就是说纯前端静态页面就可以做到),上周末在BlocklyDuino看到这个机制
核心库是FileSaver.js
文章作者 种瓜
上次更新 2017-05-01