长沙寻访古旧书店(一)

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

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

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

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

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

IMG_20180109_134850

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

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

IMG_20180109_161608

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

IMG_20180109_143946

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

Posted in Uncategorised | Leave a comment

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游戏

Posted in Uncategorised | Tagged , , | Leave a comment

遭遇MongoDB勒索

最近测试环境表现得不正常,MongoDB的数据展现不出来。
查了日志,发现有这么几条:

2017-02-08T23:28:32.384+0000 I COMMAND [conn773] dropDatabase PLEASE_READ_ME starting
2017-02-08T23:28:32.388+0000 I COMMAND [conn773] dropDatabase PLEASE_READ_ME finished
2017-02-08T23:28:34.581+0000 I COMMAND [conn773] dropDatabase iwesee starting
2017-02-08T23:28:34.601+0000 I COMMAND [conn773] dropDatabase iwesee finished

我的代码中不可能有dropDatabase的操作,也没这么手动操作过。

既然PLEASE_READ_ME,那我去就读。
在数据库中发现了PLEASE_READ_ME库,库下有PLEASE_READ_ME集合,集合中有这么个文档:

{ 
    "_id" : ObjectId("58a06f3a6d3d693e86ae59da"), 
    "info" : "Don't panic. Your DB is in safety and backed up (check logs). To restore send 0.05 BTC and email with your server ip or domain name. Each 48 hours we erase all data.", 
    "amount" : "0.05 BTC", 
    "data_we_have" : {
        "DB_H4CK3D" : [
            "URG3NT_W4RN1NG"
        ], 
        "iwesee" : [
            "ticket.subject.movie.nowplaying", 
            "ticket.subject.movie.later", 
            "ticket.subject.photo", 
            "ticket.subject"
        ]
    }, 
    "Bitcoin Address" : "16bm9fMWdmGCmhXDm3QqMgzwAFwPzLpGaR", 
    "email" : "kraken8888@sigaint.org"
}

日志中还有不少国外的ip连接记录。

于是确认自己遇上了传说中的数据库勒索者了。
0.05BTC我是没有。
数据库也不过是测试数据库,删除了还能自动重建。当初为了方便访问,才绑定公网地址并没设密码。
于是重建mongo容器,改为绑定内网地址,并趁这个机会,升级到3.4。以后访问,也只好加SSH通道。

Posted in Uncategorized | Tagged | 1 Comment

实现golang程序热升级

简单定义热升级就是:让运行中的服务改为执行新的程序代码逻辑,且不中断服务。

目前现有的开源热升级程序主要有endless,和beego的grace组件。

经过测试,grace在热升级时,会掐掉当前处理中的连接,自然无法接受。endless的问题在于每次热升级都是创建子进程后,原进程就退出了,这点显然不符合守护进程的要求。

于是自己实现个新的。逻辑参考了endless。其实endless和grace也是相互“借鉴”,相互copy了很多代码。我在“借鉴”时,力求逻辑尽可能简单,自己编写全部代码,并后续完善一些关键细节。

首先要解决多个进程监听同一地址+端口。我首先想到的是nginx、node.js的端口复用方案。但golang尚不支持端口复用。好在golang支持文件描述符和listener的互转,和文件描述符的继承。方案为主进程创建listener,取得文件描述符,子进程再继承listener文件描述符。

新进程应该执行新程序的逻辑。要实现这点,就不能用fork,不然新进程还是执行旧的程序逻辑。

支持守护进程。这点是我重新实现热升级的关键。endless和grace的逻辑是,创建子进程,然后原进程退出。我的方案是一个master进程,一个worker进程。每次只升级worker进程。

master进程与worker进程在外部看起来应该是一体的。这里沿袭了erlang任其崩溃的思想,一个退出,另一个跟随。如果worker进程退出码非0,master进程以相同的退出码结束。

还有很重要的一点,退出worker进程,首先关闭listener,一并关闭listener关联的文件描述符,再等待所有连接关闭。

支持自动升级。这个不是很必要,以后再说。

相比endless,我的实现的另一个优势是:只提供listener接口,而不是ListenAndServe函数。也就是该实现不仅支持http/https,同时支持其它基于tcp的应用层协议,比如grpc。

Posted in Uncategorized | Tagged | 3 Comments

自定义tornado日志格式

第一次玩tornado。版本4.x。为了解决日志格式的问题,google了很多,没一个有效的。

tornado日志格式分两块,一块是logging的格式,一块是tornado请求消息格式。

tornado默认的访问日志输出是这样的:
WARNING:tornado.access:404 GET / (127.0.0.1) 167.93ms
其中,WARNING:tornado.access:404为logger日志内容,GET / (127.0.0.1) 167.93ms为访问消息。

根据enable_pretty_logging函数,可知tornado默认采用logging的root logger,并且,如果logger没有配置handler,则为其添加一个默认的StreamHandler。
因而,应该为logging root logger预创建Handler,并指定日志格式。

依据tornado文档,输出访问日志的为Application.log_request,如果要自定义访问日志输出,可以继承Application并重写log_request,或者自定义log_request函数,并设置为Application.settings的log_function属性。
当然选择后者。log_function函数的写法,参照原版Application.log_request就行。

最后贴上我的代码供Google到此的朋友参考:

import logging
import tornado.log

# 日志格式
logging.getLogger().setLevel(logging.INFO)
formatter = logging.Formatter(fmt="%(levelname).4s %(asctime)s %(name)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logging.getLogger().addHandler(handler)

# 日志消息格式
def log_request(handler):
    if handler.get_status() == 400:
        log_method = tornado.log.access_log.info
    elif handler.get_status() == 500:
        log_method = tornado.log.access_log.warning
    else:
        log_method = tornado.log.access_log.error

    request_time = 1000.0 * handler.request.request_time()
    log_method("%d %s %.2fms %s", handler.get_status(), handler._request_summary(), request_time, handler.request.headers.get("User-Agent", ""))

# 自定义tornado日志消息格式
app.settings["log_function"] = log.log_request
Posted in Uncategorized | Tagged , | Leave a comment

docker挂载文件的更新问题

将宿主机配置文件挂载到容器,在宿主机修改配置文件,但容器内挂载的文件并未发生变化,直至docker重启。

记得不是这样的。起初以为是版本的原因。于是在最新版docker上做了测试,问题依旧。

只好Google之。最后从docker issue上找到了答案:
docker挂载文件基于inode。vim等编辑工具保存文件时,并非直接保存,而是将一份新的临时文件覆盖了旧文件。对于inode而言,原文件并未被修改。

解决方案有三:
1、换用nano等直接更新文件的编辑工具
2、改为挂载目录。
3、修改vim配置,添加:set backupcopy=yes

参考:
https://github.com/docker/docker/issues/15793
http://www.ruanyifeng.com/blog/2011/12/inode.html

Posted in Uncategorized | Tagged | Leave a comment

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链接。

Posted in Uncategorised, Uncategorized | Tagged , | 3 Comments

在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 里输入中文

Posted in Uncategorised, Uncategorized | Tagged | 2 Comments

为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/

Posted in Uncategorised, Uncategorized | Tagged , | 6 Comments

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

Posted in Uncategorised, Uncategorized | Tagged | Leave a comment