重读《黔之驴》

今天计划做点无聊的事。突然想起柳宗元的《黔之驴》。原文如下:

黔無驢,有好事者,船載以入;至則無可用,放之山下。虎見之,龐然大物也,以為神。蔽林間窺之,稍出近之,憖憖然莫相知。他日,驢一鳴,虎大駭遠遁,以為且噬已也,甚恐!然往來視之,覺無異能者,益習其聲,又近出前后,終不敢搏。稍近益狎,蕩倚衝冒。驢不勝怒,蹄之。虎因喜曰:「技止此耳!」因跳踉大闞,斷其喉,盡其肉,乃去。

噫!形之龐也,類有德;聲之宏也,類有能。向不出其技,虎雖猛,疑畏卒不敢取,今若是焉,悲夫!

大意是说:黔地没有驴,一个无聊人运了头驴过去。黔地老虎没见过驴,开始很害怕,后发现驴不过“技止此耳”,跳上去,吃了驴。

故事创作于作者永贞革新失败之后。后人一致认为,作者是在讽刺朝中外强中干的权贵。

以我今天的眼光来看,故事却是这样的:一个很有想法的人,给一个没有驴子的地方运了头驴。驴新来,遭到当地传统势力的敌视,最终不敌,被吃了。这个很有想法的人,就是指作者自己,驴指永贞革新,老虎指当时朝中掌控实权的权贵和地方的藩镇。

我们今天谋求创新,就要做很多看似无聊的尝试,敢于承担失败的风险。想法的落地,则需要短时间适应现实情况,否则很快夭折。即使最终项目失败了,也未必意味着想法是错误的。现在黔地已经没有老虎,留下的是驴。

Python实现数据类的方法集合

当前Python最新稳定版为3.6。主流Linux发行版官方仓库提供Python版本的有2.7和3.5,默认依然是坚强的2.7。

1、dataclass装饰器。正规军,需要Python 3.7+,还在路上。dataclass装饰器会给类添加__init__、__str__、__repr__等方法。

@dataclass
class InventoryItem:
    '''Class for keeping track of an item in inventory.'''
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

2、collections.namedtupletyping.NamedTuple。要求不多的用这两个最合适了。Python 3.6+的typing.NamedTuple才支持默认参数。

class Employee(NamedTuple):
    name: str
    id: int = 3

employee = Employee('Guido')
assert employee.id == 3
Employee = namedtuple('Employee', 'name, age, title, department, paygrade')

3、autoassign装饰器,和auto_assign装饰器。算是奇技淫巧,前者支持Python2,后者支持Python 3.5+。

4、collections.namedtuple + functools.partial。通过partial实现默认参数,也是我当前采用的方案

fields = ("name", "age", "title")
defaults = OrderedDict({"title": None})
Employee = partial(namedtuple("Employee", fields), **defaults)
employee = Employee("Guido", "18")

网上还有一些相对麻烦点的方案,比如重写namedtuple的__new__方法,就不一一列举了

在Linux Minecraft中输入中文——进阶:在告示牌输入中文

两年前的一篇文章,我讲到如何在Linux Minecraft中输入中文。脚本内容是转的别人的。该方法只能用于聊天,不能用于编辑告示牌。

其实只要把脚本修改下,就可以通用。只会复制粘贴,不会派生定制,做程序员的我真的不好意思。

原脚本内容为:

#!/bin/bash -e

chars=$(zenity --title 中文输入 --text 中文输入 --width 500 --entry 2>/dev/null)
sleep 0.1
xdotool key --delay 150 Escape t
sleep 0.2
xdotool type --delay 150 "$chars"
xdotool key Return

其中正文第一行为弹出对话框,接收用户输入。第三行输出t键。第五行输出对话框接收的输入。第六行输出回车。很明显,第一行和第五行才是重点。其它几行不过辅助性的,帮用户偷懒。

现在只要改下:

#!/bin/bash -e
 
chars=$(zenity --title 中文输入 --text 中文输入 --width 500 --entry 2>/dev/null)
#sleep 0.1
#xdotool key --delay 150 Escape t
sleep 0.2
xdotool type --delay 150 "$chars"
#xdotool key Return

只要注释掉辅助性的几行。

编辑告示牌时,通过快捷键执行该脚本,即可在告示牌上输入中文。如果是聊天,得先输入t打开聊天栏,再通过快捷键执行该脚本,聊天编辑栏得到中文后,敲回车——或者新脚本和旧脚本共存,绑定到不同的快捷键。

以前我在游戏中结识位朋友。他发现另一个在告示牌输入中文的办法。可惜我还没学会,就与他失去了联系。

一次网件路由器OpenWrt系统TFTP刷机抢救记录

路由器网件WNDR4300,原固件为OpenWrt15.05,计划升级到OpenWrt15.05.1。下载了wndr4300-squashfs-sysupgrade.tar,通过LuCI升级。确认升级前,检查升级文件摘要匹配。

路由器重启后,不能获得IP地址,能PING通网关。不能访问luCI,SSH连接网关密码始终无效。路由器在开机后,指示灯只亮电源指示灯和有连接的LAN指示灯。

开始TFTP升级。先下载固件镜像wndr4300-ubi-factory.img。将电脑通过网线连接到路由器LAN接口。电脑网络地址改为手动设置,IP地址为192.168.1.2-254,网关192.168.1.1,子网掩码255.255.255。ping 192.168.1.1,确保可以连接到路由器。

路由器关机,按下RESET键,再开机。先指示灯全亮。等待电源指示灯由黄灯常亮变为黄灯闪烁,再变为绿灯常亮,最后变为绿灯闪烁。路由器进入TFTP刷机模式。松开RESET键。利用TFTP工具将固件镜像上传到路由器。如果是使用Linux的tftp软件上传,需要设置模式为binary。

等待LuCI可以访问,设置ROOT密码,恢复备份存档——前提是你升级前备份了存档!
如果tftp救不了你的机器,准备TTL刷机吧。

长沙寻访古旧书店(一)

首先说明,本次寻访日期为2018年1月9日。本次寻访大区域为芙蓉区,寻访依据以网友提供的线索为主。

第一块区域为湘雅附二医院一带。该区域也靠近湖南图书馆。人民路北侧,湘雅附二医院以东,有三家旧书店。三家书店都是小门面,提供二手旧书。我淘到一本人民出版社的《国际共产主义运动史》,1978年版。向老板询价,老板示意书背面。翻过来看,价格在背面用铅笔标注了,13元。

结账时,又向老板打听附近可有其它旧书店,老板告知复兴街还有家。复兴街其实只能算巷子。入口位于附二医院西边。人民路附二医院西边的口子进去不远,可以看到一家“复兴参观”,进入就是。复兴街北接白沙路。穿过林立的小餐馆,进入相对安静的居民区,就可看到一家孤立的旧书店。该店比之前三家店面稍大,卖的也都是旧书。

既然来到白沙路附近,就免不得去天心阁一带。简牍博物馆下面古玩商店不少,然而没有旧书店。询问了老板,才知道要等到周末才有旧书摊。只好作罢。

下一站八一路。从简牍博物馆到八一路,刚好路过定王台,免不得要过去寻访一番。这次没找到定王台书市对面的述古人文书店。发现了一间民生书局。民生书局卖的是出版社库存书。库存书又免不了堆积大量湖南本地出版社的库存书。淘到一本湖南教育出版社的《青藏高原科考访谈录》,2010年版。店家要价25。这次价格没有用铅笔在书背标注。见书品相较好,价格实惠,就立马掏了钱。

IMG_20180109_134850

出了民生书局,再去搜寻述古人文书店,才发现述古已经关了门,招牌上贴上了招租的广告。

最后一站是八一路的星星书店。星星书店位置是袁家岭地铁站北面。大致占据了两个半的小门面。书店以出版社库存书为主。同前面的民生书店,星星书店也不少湖南本地出版社的书,库存书又以湖南科学技术出版社的书为主。淘到《爱因斯坦传》、《西方科学起源》、《居里一家》,三本都是湖南科学技术出版社的书,而且都是全新的,前两本还有原书塑封。店里上豆瓣搜了下,前两本评论都蛮高,后一本评价人数太少,但看介绍也不错,想必不会浪费票子。又搜到一本港版袁伟时的《文化:中国与世界》,木刻文化2010年版,扉页还有作者的签名“丁总赐正 袁伟时 2011.4.27”。丁总显然工作太忙,无暇“赐正”。书一页没翻,就流落到旧书市场。

IMG_20180109_161608

结账时算收银员价格怎么算。收银员告知三本湖南科学技术出版社的书算四折,一本中华书局的《老子注译及评介》算六折,港版的《文化:中国与世界》打了个电话请示后表示30元。果断放弃了《老子》。买单时掏出钱包所有零钱,刚好还差三毛,问能否优惠三毛。收银美女很直接地答应了。付了钱出门时,收银美女还很客气地道了声慢走。(可惜了,我习惯了没礼貌,不知道怎么回)

IMG_20180109_143946

于是满载而归,背着沉重的背包去袁家岭搭地铁回家。

Debian stretch安装nvidia显卡驱动,并配置双显卡切换

机器是戴尔老爷机,Dell inspiron 14r 7420,显卡为Intel + Nvidia。

之前Debian wheezy上双显卡解决方案见我的这篇。建议先阅读该篇。

下面是我解决问题之后的简单总结。实际摸索过程远比这复杂。各人机器环境不同,下面的方案绝非放之四海皆准——仅供参考!

第一步,为默认源添加contrib non-free组件,并更新本地仓库缓存。

第二步,安装nvidia-driver驱动:

sudo apt-get install linux-headers-$(uname -r|sed ‘s,[^-]*-[^-]*-,,’) nvidia-driver

不要执行nvidia-xconfig!

第三步,安装bumblebee-nvidia,支持双显卡切换:

sudo apt-get install bumblebee-nvidia primus

添加当前用户到bumblebee用户组:

sudo adduser $USER bumblebee

第四步,重启

 

如果遇到问题,检查/var/log/Xorg.0.log、/var/log/user.log

参考:

NvidiaGraphicsDrivers – Debian Wiki

Bumblebee – Debian Wiki

双显卡机器安装Debian Wheezy运行Steam游戏

wordpress启用全站加密

最近切换到vultr的洛杉矶机房,放弃了之前的cloudflare加速,启用Let’s Encrypt加密。
Let’s Encrypt证书的获取和更新见这里,完整文档见这里

先讲获取和更新证书。具体的命令见官方文档。我这里只讲官方没讲清楚或需要注意的地方。
1、安装certbot。
2、获取证书。因为是nginx代理,必须webroot插件。webroot使得获取和更新证书无须停掉web服务。使用webroot需要指定一个目录存放webroot的临时文件,且nginx需要配置一个模式为.well-known/acme-challenge的location指定到该目录。
3、保持更新

再配置wordpress和nginx
1、修改wordpress常规配置中的两个地址到https。这点,直接修改数据库也可以做到。
2、启用后台加密。修改wp-config.php文件,添加define(‘FORCE_SSL_ADMIN’, true);定义。
3、最后是最关键的,配置nginx,将http重定向到https,将https转发到wordpress。

下面是我的配置:

server {
     listen         80;
     server_name    blog.wencan.org;
     return         301 https://$server_name$request_uri;
}

server {
    listen       443 ssl;
    server_name  blog.wencan.org;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/blog.wencan.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/blog.wencan.org/privkey.pem;

    #charset koi8-r;
    #access_log  /var/log/nginx/log/host.access.log  main;

    location / {
	proxy_pass http://127.0.0.1:81/;
        proxy_redirect      off;
        proxy_set_header    Host            $host;
        proxy_set_header    X-Real-IP       $remote_addr;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_set_header    X-Forwarded-Proto $scheme;
    }

    location /.well-known/acme-challenge {
        root /etc/nginx/webroot;
    }
}

关键是X-Forwarded-Proto头的配置。该location的其它内容都算是标配,http代理的标配。但这里得配置X-Forwarded-Proto头,告诉wordpress,前端是https代理!否则,网页很多链接,依然是顽固的http链接。

在Linux Minecraft中输入中文

1、安装zenity、xdotool

2、创建下面的脚本,并赋执行权限

#!/bin/bash -e

chars=$(zenity --title 中文输入 --text 中文输入 --width 500 --entry 2>/dev/null)
sleep 0.1
xdotool key --delay 150 Escape t
sleep 0.2
xdotool type --delay 150 "$chars"
xdotool key Return

3、创建快捷键,关联到上面创建的脚本

4、如果能输入英文,而不能输入中文,检查locale,如果LANG值不是en_US.utf8,修改用户语言到English(United States),并重启会话

脚本来自依云’s Blog,只有第四条才是我的。

参考:
Linux 下在 Minecraft 里输入中文

在Linux Minecraft中输入中文——进阶:在告示牌输入中文

为blog配置CloudFlare CDN加速,并为OpenWrt配置ddns

一、配置wordpress域名
在wordpress后台配置两个域名均为https
这里的前提是blog本身已经支持https,不支持https的情况我没法尝试。推荐Redhat的Openshift

二、配置CloudFlare
1、注册CloudFlare
2、添加根域名
3、确认域名解析信息
4、修改域名的NS服务器到CloudFlare指定的NS服务器
等待DNS服务器修改生效

三、http自动转向https
在CloudFlare后台Page Relues添加规则:
匹配模式为http://domain/*
规则为Always use https

四、下载证书
1、ssh连接OpenWrt路由器
2、opkg install curl
3、mkdir -p /etc/ssl/certs
4、curl http://secure.globalsign.net/cacert/Root-R1.crt -o /etc/ssl/certs/GlobalSign_Root_R1.pem
5、从其它linux系统复制 /etc/ssl/certs/ca-certificates.crt到/etc/ssl/certs目录,作为根证书

五、配置OpenWrt的ddns
1、安装ddns-scripts、ddns-scripts_cloudflare、luci-app-ddns
2、刷新页面,进入Service-Daynamic DNS,删掉myddns_ipv6,编辑myddns_ipv4
3、勾选Enable、IPv4-Address,DDns Service provider选CloudFlare,Hostname/Domain填完全的域名(子域名),Username填注册邮箱地址,Password可以为真实的密码或CloudFlare API token(推荐API Token。API Token可以在My account找到),勾选Use HTTP Secure,下面的Path to CA-Certificate填上面的证书路径:/etc/ssl/certs/GlobalSign_Root_R1.pem

最后配置OpenWrt的ddns是个大麻烦。CloudFlare的api要求使用https,但OpenWrt默认使用http,浏览了这里的讨论,多次尝试,最终发现下载GlobalSign_Root_R1.pem证书后,Use HTTP Secure和Path to CA-Certificate才可见。

如果blog不需要https,一、三步不需要。

参考:
https://dev.openwrt.org/ticket/12500
https://api.cloudflare.com/

mongodb用户和权限管理

mongodb官方文档已经对用户和权限管理有详细的描述,本文尝试以另一角度来对其做出说明。
由于没在集群上做过测试,集群部分就不写了。

启用用户验证:
mongod添加–auth启动参数
(或) 配置文件中配置 security.authorization: enabled

角色:
一个用户可以有多个角色
read: 读
readWrite: 读写
userAdmin: 用户管理
dbAdmin: 数据库管理
dbOwner: 读写、用户管理、数据库管理

认证源:
每个数据库都可以做为认证源。不同认证源创建的用户可以同名。用户内部的名称为: “$认证源.$用户名”。
认证源内的用户可以拥有针对对其它数据库的角色。
mongo shell中,以当前使用的数据库作为认证源。
通过连接字符串连接时,可能需要通过查询参数指定认证源。

admin数据库:
当前示例的所有用户都保存在admin数据库的system.users集合内。
admin的用户可拥有四个对实例内所有数据库都有效的特殊权限:
readAnyDatabase
readWriteAnyDatabase
userAdminAnyDatabase
userAdminAnyDatabase

超级用户:
一个拥有admin的用户管理角色的用户即为超级用户。超级用户可以为自身或其它用户赋予任何角色。

本地例外:
如果实例开启了用户验证,却还没创建管理角色用户,可以通过本地例外机制创建一个。
通过localhost连接到实例,不过验证,在admin数据库中,创建一个管理角色用户。这个新创建的用户,应当是一个超级用户。
一种更理想的方式:关闭用户认证重启实例,想干吗干吗……

参考:
Enable Client Access Control
Users
Built-In Roles
User Management Methods