<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
        <title></title>
        <description> - ruohan.chen 陈若涵</description>
        <link>http://blog.crhan.com/</link>
        <link>http://blog.crhan.com/</link>
        <lastBuildDate>2014-05-11T00:22:41-07:00</lastBuildDate>
        <pubDate>2014-05-11T00:22:41-07:00</pubDate>
        <ttl>1800</ttl>


        <item>
                <title>什么是 MTU, 什么是 MSS</title>
                <description>&lt;h2&gt;MTU 到底是怎么来的&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;MTU&lt;/strong&gt;, 是 &lt;em&gt;Maximum Transmission Unit&lt;/em&gt; 的缩写, 根据 &lt;a href=&quot;http://en.wikipedia.org/wiki/Maximum_transmission_unit&quot;&gt;Wikipedia&lt;/a&gt; 的定义, &lt;strong&gt;MTU&lt;/strong&gt; 指的是在 Network Layer (因处 &lt;a href=&quot;http://en.wikipedia.org/wiki/OSI_model#Layer_3:_network_layer&quot;&gt;OSI&lt;/a&gt; 第三层, 后以 &lt;em&gt;L3&lt;/em&gt; 代替)上传输的最大数据报单元, 而 MTU 的大小一般由 Link Layer (因处 &lt;a href=&quot;http://en.wikipedia.org/wiki/OSI_model#Layer_3:_network_layer&quot;&gt;OSI&lt;/a&gt; 第二层, 后以 &lt;em&gt;L2&lt;/em&gt; 代替) 设备决定. 比如生活中使用最广泛的以太网(&lt;a href=&quot;http://en.wikipedia.org/wiki/IEEE_802.3&quot;&gt;Ethernet, IEEE 802.3&lt;/a&gt;)的帧大小是 1518 字节, 根据 &lt;a href=&quot;http://en.wikipedia.org/wiki/Ethernet_frame&quot;&gt;Ethernet Frame&lt;/a&gt; 的定义, L2 Frame 由 14 字节 Header 和 4 字节 Trailer 组成, 所以 L3 层(也就是 IP 层)最多只能填充 1500 字节大小, 这就是 MTU 的由来.&lt;/p&gt;

&lt;h2&gt;802.3 Ethernet MTU&lt;/h2&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;+-------------+------------+-----------------+---------+----------------+
| Dest MAC(6) | Src MAC(6) | Eth Type/Len(2) | Payload | CRC Trailer(4) |
+-------------+------------+-----------------+---------+----------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;所以说, 当使用 Ethernet 介质时确定只能传最大 1518 字节的帧后, 减去 18 字节的 L2 头和尾, 留给 IP 层的就只有 1500 字节了.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;PS: 标准文档中中所说的 LLC 层因为在实际应用中基本不存在, 所以 802.3 标准 MTU 是 1492, 但是实际使用中的 MTU 是 1500.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;L2, L3 示意图&lt;/h3&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;+-------------------------------+
|        IP Layer(1500)         |
+-------------------------------+
|       Link Layer(1518)        |
+-------------------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;PPPoE MTU&lt;/h2&gt;

&lt;p&gt;另一个典型的 MTU 是拨号上网( &lt;a href=&quot;http://en.wikipedia.org/wiki/Point-to-point_protocol_over_Ethernet#MTU.2FMRU&quot;&gt;PPPoE&lt;/a&gt; )的 1492 字节, 通过以太网接入的 PPPoE 的封包结构上会在 L2 和 L3 之间插入两层, 一层 PPP 协议和一层 PPPoE 协议. 但是因为传输的底层仍然使用的是以太网, 所以上层包大小仍然受 L2 大小限制. MTU 这样算, Link Layer 长 1518 字节, 所以 PPPoE Layer 最长 1500 字节, 然后 PPPoE 头占用 6 字节, PPP 头占用 2 字节, 所以留给 IP 层的最大空间就剩下了 1492 字节:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;+-------------------------------+
|        IP Layer(1492)         |
+-------------------------------+
|       PPP Layer(1494)         |
+-------------------------------+
|       PPPoE Layer(1500)       |
+-------------------------------+
|       Link Layer(1518)        |
+-------------------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;VLAN(802.1Q) MTU&lt;/h2&gt;

&lt;p&gt;VLAN (&lt;a href=&quot;http://en.wikipedia.org/wiki/IEEE_802.1Q&quot;&gt;IEEE 802.1Q&lt;/a&gt;) 的实现是在 L2 头部扩展了一个 4 字节大小的字段, 分别是 2 字节协议识别码(TPID)和 2 字节的控制信息(TCI), 这导致了 L2 的头和尾加起来的长度变成了 22 字节, 所以 L3 的 MTU 就被压缩到了 1496 字节:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;+-------------------------------+
|        IP Layer(1496)         |
+-------------------------------+
|       Link Layer(1518)        |
+-------------------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;然而在 &lt;a href=&quot;http://en.wikipedia.org/wiki/802.3ac&quot;&gt;802.3ac&lt;/a&gt; 标准之后的以太网帧大小则扩展到了 1522 字节, 也就是说在这个标准下的 Ethernet, MTU 也可以设定成 1500 字节了.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;+-------------------------------+
|        IP Layer(1500)         |
+-------------------------------+
|       Link Layer(1522)        |
+-------------------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;TCP MSS&lt;/h2&gt;

&lt;p&gt;MSS (&lt;a href=&quot;http://en.wikipedia.org/wiki/Maximum_Segment_Size&quot;&gt;Maximum Segment Size&lt;/a&gt;)是 TCP Layer (&lt;em&gt;L4&lt;/em&gt;) 的属性, MSS 指的是 TCP payload 的长度. 当在 MTU 1500 的网络上传输时, MSS 为 1460 (即 1500 减去 20 字节 IP 头, 20 字节 TCP 头).&lt;/p&gt;

&lt;h3&gt;为什么 L3 有 MTU 后 L4 还要 MSS 呢?&lt;/h3&gt;

&lt;p&gt;MTU 和 MSS 的功能其实基本一致, 都可以根据对应的包大小进行分片, 但实现的效果却不太一样.&lt;/p&gt;

&lt;p&gt;L3 (IP) 提供的是一个不可靠的传输方式, 如果任何一个包在传输的过程中丢失了, L3 是无法发现的, 需要靠上层应用来保证. 就是说如果一个大 IP 包分片后传输, 丢了任何一个部分都无法组合出完整的 IP 包, 即是上层应用发现了传输失败, 也无法做到仅重传丢失的分片, 只能把 IP 包整个重传. 那 IP 包越大的话重传的代价也就越高.&lt;/p&gt;

&lt;p&gt;L4 (TCP) 提供的是一个可靠的传输方式, 与 L3 不同的是, TCP 自身实现了重传机制, 丢了任何一片数据报都能单独重传, 所以 TCP 为了高效传输, 是需要极力避免被 L3 分片的, 所以就有了 MSS 标志, 并且 MSS 的值就是根据 MTU 计算得出, 既避免了 L3 上的分片, 又保证的最大的传输效率.&lt;/p&gt;

&lt;h2&gt;参考资料:&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;http://tools.ietf.org/html/rfc1042#page-7&quot;&gt;RFC 1042:  A Standard for the Transmission of IP Datagrams over IEEE 802 Networks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://tools.ietf.org/html/rfc1122#page-85&quot;&gt;RFC 1122: Requirements for Internet Hosts -- Communication Layers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://tools.ietf.org/html/rfc879&quot;&gt;RFC 879: The TCP Maximum Segment Size and Related Topics&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</description>
                <link>http://blog.crhan.com//2014/05/mtu-and-mss</link>
                <guid>http://blog.crhan.com//2014/05/mtu-and-mss</guid>
                <pubDate>2014-05-11T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>IP Neighbour Table Overflow</title>
                <description>&lt;p&gt;偶然性的发现一个网络问题, 在大批量的 ping 不同 IP 的时候, 会偶发性的出现
&lt;code&gt;No buffer space available&lt;/code&gt; 的错误:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;root@localhost&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# ping 61.139.2.69&lt;/span&gt;
connect: No buffer space available
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;简单的说, 这个报错的原因就是 ARP 表满了, 不能存新项导致的, No buffer space 指的就是 ARP 表.&lt;/p&gt;

&lt;p&gt;那如何看 arp 表呢.?&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;root@localhost /root&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#ip neighbor show&lt;/span&gt;
10.137.11.247 dev bond0 lladdr 00:00:0c:9f:f3:84 REACHABLE
10.110.71.247 dev eth2 lladdr 00:00:0c:07:ac:0a STALE
10.110.66.105 dev eth2 lladdr 00:e0:ec:25:80:0c STALE

&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;root@localhost /root&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#arp -n&lt;/span&gt;
Address                  HWtype  HWaddress           Flags Mask            Iface
10.137.11.247            ether   00:00:0c:9f:f3:84   C                     bond0
10.110.71.247            ether   00:00:0c:07:ac:0a   C                     eth2
10.110.66.105            ether   00:e0:ec:25:80:0c   C                     eth2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;那 arp 表的上限是怎么定的呢.?&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;root@localhost /root&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#sysctl net.ipv4.neigh.default | grep gc&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;## 失效时间, 每次 ARP 被引用时刷新,&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;## 简单说就是一个 ARP 表项超过这个时间之后就会被标记成&amp;quot;可清除&amp;quot;&lt;/span&gt;
net.ipv4.neigh.default.gc_stale_time &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 60

&lt;span class=&quot;c&quot;&gt;## ARP 表清理间隔(30s)&lt;/span&gt;
net.ipv4.neigh.default.gc_interval &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 30

&lt;span class=&quot;c&quot;&gt;## ARP Cache 下限, 低于此限就不会清理 ARP 表&lt;/span&gt;
net.ipv4.neigh.default.gc_thresh1 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 128

&lt;span class=&quot;c&quot;&gt;## ARP Cache 软上限&lt;/span&gt;
net.ipv4.neigh.default.gc_thresh2 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 512

&lt;span class=&quot;c&quot;&gt;## ARP Cache 硬上限&lt;/span&gt;
net.ipv4.neigh.default.gc_thresh3 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 1024
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;解这个问题&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash language-bash&quot; data-lang=&quot;bash&quot;&gt;sysctl -w net.ipv4.neigh.default.gc_thresh1&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;256
sysctl -w net.ipv4.neigh.default.gc_thresh2&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1024
sysctl -w net.ipv4.neigh.default.gc_thresh3&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2048
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;参考资料&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://serverfault.com/questions/505964/no-buffer-space-available-and-tuning-with-sysctl&quot; title=&quot;No buffer space available and tuning with sysctl&quot;&gt;ServerFault: No buffer space available and tuning with sysctl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/questions/15372011/configuring-arp-age-timeout&quot; title=&quot;Configuring ARP age timeout&quot;&gt;StackOverflow: Configuring ARP age timeout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://linux-ip.net/html/tools-ip-neighbor.html&quot; title=&quot;ip neighbor: Appendix B. Ethernet Layer Tools&quot;&gt;ip neighbor: Appendix B. Ethernet Layer Tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.e-rave.nl/kernel-neighbour-table-overflow&quot; title=&quot;Kernel: Neighbour table overflow&quot;&gt;Kernel: Neighbour table overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://bbs.chinaunix.net/thread-2208434-1-1.html&quot; title=&quot;iptables 网关机 ping 提示 No buffer space available&quot;&gt;iptables 网关机 ping 提示 No buffer space available&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
                <link>http://blog.crhan.com//2014/04/ip-neighbour-table-overflow</link>
                <guid>http://blog.crhan.com//2014/04/ip-neighbour-table-overflow</guid>
                <pubDate>2014-04-20T23:47:00-07:00</pubDate>
        </item>

        <item>
                <title>ICMP PING 学习</title>
                <description>&lt;p&gt;七个月前的某一天, 我想用 Ruby 重写一个内部的工具, 其中有一个功能需要用到
PING, 出于某种代码洁癖, 我想用纯 Ruby 的方式来解决这个问题, 于是我就找到了
&lt;a href=&quot;https://github.com/djberg96/net-ping&quot; title=&quot;net-ping&quot;&gt;djberg96/net-ping&lt;/a&gt;, 并且因为某些效率上的需求, 我需要给这个工具加上一点并发.&lt;/p&gt;

&lt;p&gt;问题就出在这个并发上了, 想到并发时候第一个想法当然是 threading, 但是如同我在这个
&lt;a href=&quot;https://github.com/djberg96/net-ping/issues/22&quot; title=&quot;net-ping return inaccurate result with multithreading.&quot;&gt;issue&lt;/a&gt; 中的描述, net-ping 在 multi-thread 的情况下会发生结果不符合预期的情况.
这个问题直到这个月才得到了解决, 借此机会好好的记录一下整个学习过程.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol&quot;&gt;ICMP&lt;/a&gt;(网络控制协议)是一个跟 TCP 和 UDP 平起平坐的协议, 它与 TCP&amp;amp;UDP
最不相同的一点就在于它并不依靠端口来跟后端应用进行绑定, 系统接收到任一个 ICMP
请求都会毫无差别的, 向服务器上的所有应用传达这个数据, 所以在 &lt;code&gt;socket.recvfrom&lt;/code&gt;
时候必须加以更严格的判断, 以防被同机器的其他进程的信息干扰了.&lt;/p&gt;

&lt;p&gt;ICMPv4 的 header 十分的简单, 只有 8 个字节, 第一个字节表示消息类型(&lt;em&gt;type&lt;/em&gt;),
第二个字节表示消息代码(&lt;em&gt;code&lt;/em&gt;), 接着 2 字节的校验和(&lt;em&gt;checksum&lt;/em&gt;),
再就是根据消息类型而用处不同的 4 个字节的 header 数据了.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Ping_(networking_utility)&quot; title=&quot;Ping (networking utility)&quot;&gt;PING&lt;/a&gt; 用到了 ICMP 中的 &lt;code&gt;Echo&lt;/code&gt;(8) 和 &lt;code&gt;Echo reply&lt;/code&gt;(0) 两个消息类型.
根据 &lt;a href=&quot;http://tools.ietf.org/html/rfc792#page-14&quot; title=&quot;Echo or Echo Reply Message&quot;&gt;RFC792#page-14&lt;/a&gt; 的描述&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The echoer returns these same values in the echo reply.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;因为 echo reply 中会原封不动的回传 Identifier 和 Sequence Number,
这两个参数可以用来帮助程序判断收到的 ICMP 包是否是我们之前发出的那个包的回应,
在一般的实践上, Identifier 用来帮助程序判断是否是自己发出去的包, 而用从 1 开始递增的
Sequence Number 来帮助判断发出去包的个数和收到包的标识&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     Type      |     Code      |          Checksum             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Identifier          |        Sequence Number        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;了解了上面的信息, 如何做一个 ICMP PING 的程序就变的比较清晰了&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;先构建一个 ICMP 的 Socket, 绑定到发送的 IP 和 Port(因为实际不开端口,
所以端口为0)&lt;/li&gt;
&lt;li&gt;用 Type 8, Code 0, 并加上 Id 和 Seq 来打包一个 ICMP 数据包&lt;/li&gt;
&lt;li&gt;设定目标地址并发送包&lt;/li&gt;
&lt;li&gt;等待回应&lt;/li&gt;
&lt;li&gt;收到回应后先检查 ICMP 报文类型(Echo request 的 Type 是 0)&lt;/li&gt;
&lt;li&gt;再检查Id, 如果和之前发出去的 ID 相符, 说明是自己发出去的,
否则回到第四步继续等待, 直到超时&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;参考 Code: &lt;a href=&quot;https://github.com/djberg96/net-ping/blob/7917722a883cd0a7919881aec5abdeb35af2699c/lib/net/ping/icmp.rb&quot; title=&quot;lib/net/ping/icmp.rb&quot;&gt;djberg96/net-ping:lib/net/ping/icmp.rb&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;这个 Bug&lt;/h2&gt;

&lt;p&gt;最后提一下之前说的 Bug. 究其原因就是之前实现的时候, 在生成 Identifier
的时候生硬的使用了 pid, 所以在 multi-thread 的情况下因为拿到了相同的 pid,
导致程序无法分辨到底是哪一个 thread 发出的 ICMP 请求被返回了, 于是就出错了.&lt;/p&gt;

&lt;p&gt;解决方案也十分的简单: 使用 thread_id 即可&lt;/p&gt;
</description>
                <link>http://blog.crhan.com//2014/04/icmp-ping-learning</link>
                <guid>http://blog.crhan.com//2014/04/icmp-ping-learning</guid>
                <pubDate>2014-04-08T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>记一次 gcc x86_64 包编译失败的解决</title>
                <description>&lt;p&gt;最近闲得无聊想尝试下从头编译 centos ，过程中就属 gcc 的 x86_64 架构下编译不过的问题最为诡异，具体表现就是在 koji root.log 中 &lt;code&gt;yum-builddep&lt;/code&gt; 的环节里报找不到 &lt;code&gt;/lib/libc.so.6&lt;/code&gt; 依赖的问题。&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;DEBUG util.py:264:  Error: No Package found for /lib/libc.so.6
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;解开 gcc srpm 包看 spec 文件中的定义，发现它指定了 x86&lt;em&gt;64 包的编译要同时安装 32 和 64 位的 glibc（Glibc 提供 libc.so.6）。但是在 koji 的编译逻辑里面，编译时只会选用&lt;/em&gt;&lt;em&gt;对应架构&lt;/em&gt;_的 RPM 包，所以在 64 位包编译时依赖 32 位包肯定会发生 404 not found 的问题。&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;...
%global multilib_64_archs sparc64 ppc64 s390x x86_64
...
%ifarch %{multilib_64_archs} sparcv9 ppc
# Ensure glibc{,-devel} is installed for both multilib arches
# 这里意思是凡是在 x86_64 架构下编译这个包就要依赖 32 和 64 位的 glibc so 库
BuildRequires: /lib/libc.so.6 /usr/lib/libc.so /lib64/libc.so.6 /usr/lib64/libc.so
%endif
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这个问题我肯定不是第一个碰到的，但是我却怎么也没有找到任何有用的信息。我也去尝试下载 fedora21 的 gcc srpm 包解开来看，这段定义依旧存在，并且对应的 build 也都通过了。我也找了数个线上不同版本 os 的机器尝试找这样的一个包，都没有任何收获。&lt;/p&gt;

&lt;p&gt;直到我找到了 GoOSe linux 的 gcc 编译 &lt;a href=&quot;http://koji.gooselinux.org/mnt/koji/packages/gcc/4.4.4/13.gl6/data/logs/x86_64/root.log&quot;&gt;root.log&lt;/a&gt; 中终于注意到了一个从没见过的包 &lt;code&gt;glibc32-2.11.1-1.x86_64&lt;/code&gt;。仔细一看这个包虽然写着 x86_64 的名字，包内容却是挂羊头卖狗肉似的提供了 32 位的 libc.so。继续往下查发现这个包从未出现在任何一个发行版的 REPO 中，在 fedora 的编译系统中最后一次编译也只停留在 f12 中，接着被一直沿用到现在。也是因为这个包并没有被纳入任何一个发行版的 release，所以在下载 srpm 的时候也完全找不到它的踪迹。&lt;/p&gt;

&lt;p&gt;所以这个问题终于得到了解决！从 Fedora Koji 中下载来 &lt;a href=&quot;http://koji.fedoraproject.org/koji/buildinfo?buildID=153452&quot;&gt;glibc32&lt;/a&gt;，在我的 Koji 上 Build 好放进目标仓库，终于可以开始编译 gcc 了！&lt;/p&gt;
</description>
                <link>http://blog.crhan.com//2014/03/koji-build-gcc</link>
                <guid>http://blog.crhan.com//2014/03/koji-build-gcc</guid>
                <pubDate>2014-03-27T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>DHCP 高可用实践</title>
                <description>&lt;h2&gt;本文不会包含&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;如何跟网工沟通，以及什么是 subnet，什么是 ip helper address&lt;/li&gt;
&lt;li&gt;如何安装，或者编译安装 DHCP，以及 keepalived&lt;/li&gt;
&lt;li&gt;如何启动 DHCP 以及 keepalived&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;DHCP 高可用实现要点&lt;/h2&gt;

&lt;p&gt;DHCP 高可用分为以下几个步骤实现，这里举一个实际的栗子：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;确定使用 10.223.0.246 这个 IP 来接受 DHCP 请求&lt;/li&gt;
&lt;li&gt;网络交换机正确配置 &lt;code&gt;ip address helper 10.223.0.246&lt;/code&gt;（如若只有二层网络可以忽略这个配置）&lt;/li&gt;
&lt;li&gt;DHCP 本身的 Failover 配置（实现 lease db 的自动同步）&lt;/li&gt;
&lt;li&gt;keepalived 的 VIP 配置&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;前两步需要与网工沟通后完成，服务器的配置写在本文的最后。&lt;/p&gt;

&lt;h2&gt;DHCP Failover&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;依赖声明：因为 DHCP failover 协议尚在 draft 阶段（&lt;a href=&quot;http://tools.ietf.org/html/draft-ietf-dhc-failover-07&quot;&gt;draft-ietf-dhc-failover-07.txt&lt;/a&gt;），所以请至少使用 &lt;a href=&quot;https://www.isc.org/downloads/&quot;&gt;ISC-DHCP-4.1&lt;/a&gt; 及以上的版本，并且保持同一个 failover 集群下的 DHCP 版本一致，避免一些潜在的兼容性问题&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;# DHCP Primary                              |  # DHCP Sencondary&lt;/span&gt;
                                            &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
ddns-update-style interim&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                  &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  ddns-update-style interim&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
log-facility local6&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                        &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  log-facility local6&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
authorized&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                                 &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  authorized&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
ping-check &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                            &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  ping-check &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
ping-timeout 3&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                             &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  ping-timeout 3&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
default-lease-time 86400&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                   &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  default-lease-time 86400&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
max-lease-time 86400&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                       &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  max-lease-time 86400&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                                            &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
failover peer &lt;span class=&quot;s2&quot;&gt;&amp;quot;oob&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;                       &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  failover peer &lt;span class=&quot;s2&quot;&gt;&amp;quot;oob&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  primary&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                                  &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;    secondary&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  split 128&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                                &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;# no split in secondary&lt;/span&gt;
  mclt 3600&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                                &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;    mclt 3600&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  address 10.223.0.244&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                     &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;    address 10.223.0.245&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  port 647&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                                 &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;    port 647&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  peer address 10.223.0.245&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;    peer address 10.223.0.244&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  peer port 647&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                            &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;    peer port 647&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  max-response-delay 60&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;    max-response-delay 60&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  max-unacked-updates 10&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                   &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;    max-unacked-updates 10&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  load balance max seconds 3&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;               &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;    load balance max seconds 3&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;                                           &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
                                            &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
                                            &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
                                            &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
subnet 10.223.0.0 netmask 255.255.240.0 &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  subnet 10.223.0.0 netmask 255.255.240.0 &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  option routers 10.223.0.247&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;              &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;    option routers 10.223.0.247&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  option broadcast-address 10.223.15.255&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;    option broadcast-address 10.223.15.255&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  option subnet-mask 255.255.240.0&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;         &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;    option subnet-mask 255.255.240.0&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                                            &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
  pool &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;                                    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;    pool &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
          failover peer &lt;span class=&quot;s2&quot;&gt;&amp;quot;oob&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;              &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;            failover peer &lt;span class=&quot;s2&quot;&gt;&amp;quot;oob&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      range 10.223.0.11 10.223.0.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;        range 10.223.0.11 10.223.0.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      range 10.223.1.11 10.223.1.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;        range 10.223.1.11 10.223.1.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      range 10.223.2.11 10.223.2.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;        range 10.223.2.11 10.223.2.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      range 10.223.3.11 10.223.3.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;        range 10.223.3.11 10.223.3.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      range 10.223.4.11 10.223.4.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;        range 10.223.4.11 10.223.4.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      range 10.223.5.11 10.223.5.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;        range 10.223.5.11 10.223.5.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      range 10.223.6.11 10.223.6.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;        range 10.223.6.11 10.223.6.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      range 10.223.7.11 10.223.7.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;        range 10.223.7.11 10.223.7.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      range 10.223.8.11 10.223.8.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;        range 10.223.8.11 10.223.8.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      range 10.223.9.11 10.223.9.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;        range 10.223.9.11 10.223.9.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      range 10.223.10.11 10.223.10.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;        range 10.223.10.11 10.223.10.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      range 10.223.11.11 10.223.11.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;        range 10.223.11.11 10.223.11.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      range 10.223.12.11 10.223.12.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;        range 10.223.12.11 10.223.12.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      range 10.223.13.11 10.223.13.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;        range 10.223.13.11 10.223.13.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      range 10.223.14.11 10.223.14.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;        range 10.223.14.11 10.223.14.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      range 10.223.15.11 10.223.15.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;        range 10.223.15.11 10.223.15.240&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;                                       &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;                                           &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;参考资料: &lt;a href=&quot;http://linux.die.net/man/5/dhcpd.conf&quot;&gt;man 5 dhcpd.conf&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;Keepalived&lt;/h2&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash language-bash&quot; data-lang=&quot;bash&quot;&gt;vrrp_instance VI_1 &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  vrrp_instance VI_1 &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    state MASTER           &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      state MASTER
    interface eth2         &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      interface eth2
    virtual_router_id 51   &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      virtual_router_id 51
    priority 99            &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      priority 90 &lt;span class=&quot;c&quot;&gt;# so this is backup server&lt;/span&gt;
    advert_int 1           &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      advert_int 1
    authentication &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      authentication &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        auth_type PASS     &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;          auth_type PASS
        auth_pass 1111     &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;          auth_pass 1111
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;                      &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    virtual_ipaddress &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      virtual_ipaddress &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        10.223.0.246/32    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;          10.223.0.246/32 &lt;span class=&quot;c&quot;&gt;# VIP&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;                      &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;                          &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;参考资料&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;http://linux.die.net/man/5/keepalived.conf&quot;&gt;man 5 keepalived.conf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.cyberciti.biz/faq/rhel-centos-fedora-keepalived-lvs-cluster-configuration/&quot;&gt;CentOS / Redhat Linux: Install Keepalived To Provide IP Failover For Web Cluster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.keepalived.org/LVS-NAT-Keepalived-HOWTO.html&quot;&gt;LVS NAT + Keepalived HOWTO&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
</description>
                <link>http://blog.crhan.com//2014/01/dhcp-failover-and-keepalived-ha</link>
                <guid>http://blog.crhan.com//2014/01/dhcp-failover-and-keepalived-ha</guid>
                <pubDate>2014-01-12T00:00:00-08:00</pubDate>
        </item>

        <item>
                <title>rbenv system wide install</title>
                <description>&lt;p&gt;让 rbenv 良好的兼容 linux 下多用户共同使用一个 rbenv 的场景&lt;/p&gt;

&lt;h2&gt;起因&lt;/h2&gt;

&lt;p&gt;是 @luikore 吕大神的帖子 &lt;a href=&quot;http://ruby-china.org/topics/16073&quot;&gt;[姨妈] 终于把 rvm 换成 rbenv 了&lt;/a&gt;, 讨论 rbenv 比之 rvm 不足的地方, 我列了这么四点&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rvm 傻瓜的帮你处理一些库依赖 (特别是在 osx 下系统 readline 和 brew 安装的 GNU readline 傻傻分不清楚的时候)&lt;/li&gt;
&lt;li&gt;rvm 良好的处理了多用户服务器环境下的管理&lt;/li&gt;
&lt;li&gt;它官方有提供 offline 安装的方法, 对我来说很重要, 我有很多机器都不能上外网(生产环境安全要求严格).&lt;/li&gt;
&lt;li&gt;有一个 binary 功能, 可以让我自己打包一个适应本地环境的 ruby, 避免在每个服务器上编译(未验证)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;今天来尝试用 rbenv 解决上述问题&lt;/p&gt;

&lt;h2&gt;整理下需求:&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;目标环境: 是 linux 下的 production 运行环境, 满足多用户共同使用的需求&lt;/li&gt;
&lt;li&gt;库依赖: 只专注于解决第一个问题, 跨平台的库依赖问题不予解决(比如不同系统使用不同的包管理,不同的包名等问题)&lt;/li&gt;
&lt;li&gt;离线安装: 能够完全只依赖有限的网络条件 offline 安装 (基本解决)&lt;/li&gt;
&lt;li&gt;多用户支持: 使用 linux 组权限解决 rbenv 的 none root 用户使用问题(比如安装新 ruby, 切换默认 ruby, 全局性的安装&amp;amp;删除 gem)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;最后的结果&lt;/h2&gt;

&lt;h3&gt;多用户支持&lt;/h3&gt;

&lt;p&gt;通过 linux 的组权限解决, 不完美, 有点小问题.&lt;/p&gt;

&lt;p&gt;问题在 umask 的设置上, 系统默认是 &lt;code&gt;umask 022&lt;/code&gt; 即组权限默认抹去 &lt;strong&gt;写权限&lt;/strong&gt;. 在类似的场景下可能会出现 permission deny: 用户 A 在 gem 的默认位置上安装了 gem A, 但是用户 B 想把它删掉&lt;/p&gt;

&lt;p&gt;解决这个问题的根本办法是请在 production 环境里面使用 &lt;code&gt;bundle install --deploy&lt;/code&gt; 谢谢!&lt;/p&gt;

&lt;h3&gt;离线安装支持&lt;/h3&gt;

&lt;p&gt;这部分没有写在脚本里, 按照两步走:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;离线安装 rbenv 和 ruby-install: 简单, 打个 tar 包就好了&lt;/li&gt;
&lt;li&gt;离线安装 ruby: 简单, 建立一个 &lt;code&gt;$RBENV_PREFIX/cache&lt;/code&gt; 文件夹, 然后把 ruby 源代码放进去就好了&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;参考资料: &lt;a href=&quot;https://github.com/sstephenson/ruby-build#package-download-caching&quot;&gt;Package download caching&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;部署运行方式&lt;/h3&gt;

&lt;p&gt;linux 的安全性问题就不赘言了, 总之你需要用一个独立的帐号来运行你的 rails app, 那么你应该这么做&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;折腾个什么劲啊, 用 capistrano-rbenv 呗&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;安装脚本参考&lt;/h2&gt;

&lt;div class=&quot;show-gist&quot; data-gist-id=&quot;8064624&quot;&gt;&lt;/div&gt;
</description>
                <link>http://blog.crhan.com//2013/12/rbenv-system-wide-install</link>
                <guid>http://blog.crhan.com//2013/12/rbenv-system-wide-install</guid>
                <pubDate>2013-12-22T00:00:00-08:00</pubDate>
        </item>

        <item>
                <title>让你的 Mac 帮你回忆这一天你到底干了啥?</title>
                <description>&lt;p&gt;MacX 下的截屏实际上是调用了 &lt;code&gt;/usr/sbin/screencapture&lt;/code&gt; 这个程序, 所以只要写个脚本定时跑就行了.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PATH&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;$HOME/Pictures/screen&amp;quot;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# 存在这个位置&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; ! -d &lt;span class=&quot;nv&quot;&gt;$dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mkdir -p &lt;span class=&quot;nv&quot;&gt;$dir&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$dir&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;$(date +%Y%m%d-%H%M).png&amp;quot;&lt;/span&gt;
/usr/sbin/screencapture -o -x &lt;span class=&quot;nv&quot;&gt;$dir&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$filename&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# 截屏!&lt;/span&gt;
sips -Z 800 &lt;span class=&quot;nv&quot;&gt;$dir&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$filename&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# 优化一下大小, 缩小成 800px 宽&lt;/span&gt;
find &lt;span class=&quot;nv&quot;&gt;$dir&lt;/span&gt; -ctime 1 -name &lt;span class=&quot;s1&quot;&gt;&amp;#39;*.png&amp;#39;&lt;/span&gt; -type f -delete &lt;span class=&quot;c&quot;&gt;# 删除一天以上的截图&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;脚本取名叫 &lt;code&gt;screenshot.sh&lt;/code&gt;, 放在用户目录下的 &lt;code&gt;bin&lt;/code&gt; 目录, 然后把它加入 crontab: &lt;code&gt;crontab -e&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash language-bash&quot; data-lang=&quot;bash&quot;&gt;*/2 * * * * bash ~/bin/screenshot.sh  &amp;gt;&amp;gt; /tmp/screenshot.log 2&amp;gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这样就能每两分钟截图一次并保存在你的 &lt;code&gt;Pictures/screen&lt;/code&gt; 文件夹下啦.&lt;/p&gt;
</description>
                <link>http://blog.crhan.com//2013/01/%E8%AE%A9%E4%BD%A0%E7%9A%84-mac-%E5%B8%AE%E4%BD%A0%E5%9B%9E%E5%BF%86%E8%BF%99%E4%B8%80%E5%A4%A9%E4%BD%A0%E5%88%B0%E5%BA%95%E5%B9%B2%E4%BA%86%E5%95%A5</link>
                <guid>http://blog.crhan.com//2013/01/让你的-mac-帮你回忆这一天你到底干了啥</guid>
                <pubDate>2013-01-05T00:00:00-08:00</pubDate>
        </item>

        <item>
                <title>ssh 命令在脚本中的使用建议</title>
                <description>&lt;p&gt;以下内容摘自 &lt;code&gt;man ssh_config&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;BatchMode&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;If set to “yes”, passphrase/password querying will be disabled.  This option is useful in scripts and other batch jobs where no user is present to supply the password.  The argument must be “yes” or “no”.  The default is “no”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如果你觉得与其让 ssh 命令跳出个输入密码的提示还不如让其自动失败, 那这个选项值得打开&lt;/p&gt;

&lt;h3&gt;ConnectTimeout&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Specifies the timeout (in seconds) used when connecting to the SSH server, instead of using the default system TCP timeout.  This value is used only when the target is down or really unreachable, not when it refuses the connection.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;别等这么久了, 你在处理本地环境的机器的时候, 要是一秒还没连上机器基本上再等十秒也不能给你太大帮助, 不如将这条超时调短&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;以下摘自 &lt;code&gt;man ssh&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;-n&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Redirects stdin from /dev/null (actually, prevents reading from stdin).  This must be used when ssh is run in the background.  A common trick is to use this to run X11 programs on a remote machine.  For example, ssh -n shadows.cs.hut.fi emacs &amp;amp; will start an emacs on shadows.cs.hut.fi, and the X11 connection will be automatically forwarded over an encrypted channel.  The ssh program will be put in the background.  (This does not work if ssh needs to ask for a password or passphrase; see also the -f option.)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;我只想说括号里的这一点, 如果你配合 &lt;code&gt;while read&lt;/code&gt; 的时候使用 ssh, 请务必加上 &lt;code&gt;-n&lt;/code&gt; 选项, 否则 ssh 会读完你剩下的 &lt;strong&gt;STDIN&lt;/strong&gt; 缓冲区, 让你的下一个 while 循环直接结束&lt;/p&gt;

&lt;h3&gt;综上所述, 你可以在脚本里这样用 ssh&lt;/h3&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash language-bash&quot; data-lang=&quot;bash&quot;&gt;ssh -o &lt;span class=&quot;nv&quot;&gt;ConnectTimeout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2 -o &lt;span class=&quot;nv&quot;&gt;BatchMode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;yes -n &lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;command&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
                <link>http://blog.crhan.com//2013/01/ssh-%E5%91%BD%E4%BB%A4%E5%9C%A8%E8%84%9A%E6%9C%AC%E4%B8%AD%E7%9A%84%E4%BD%BF%E7%94%A8%E5%BB%BA%E8%AE%AE</link>
                <guid>http://blog.crhan.com//2013/01/ssh-命令在脚本中的使用建议</guid>
                <pubDate>2013-01-05T00:00:00-08:00</pubDate>
        </item>

        <item>
                <title>善于使用 bash builtin -- read</title>
                <description>&lt;p&gt;正确的在脚本中使用 while read 可以得到诸多好处, read 命令从标准输入中取得输入存入变量. 使用 read 的脚本都可以获得 linux pipe 的所有优点.&lt;/p&gt;

&lt;h2&gt;将你的脚本放入管道&lt;/h2&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read &lt;/span&gt;host ip&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;$host ip is $ip&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;比如这个脚本名叫 &lt;code&gt;echo_host_ip.sh&lt;/code&gt;.  再有一个文件 &lt;code&gt;host_info&lt;/code&gt;, 每行都是主机名和 IP 的对应关系. &lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;host1 1.2.3.4
host2 2.3.4.5
host3 3.4.5.6
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;你可以这样用&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;linux 管道: &lt;code&gt;cat host_info | ./echo_host_ip.sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;bash 标准输入导入 &lt;code&gt;./echo_host_ip.sh &amp;lt; host_info&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;直接在 bash 中输入一条信息测试 &lt;code&gt;./echo_host_ip.sh &amp;lt;&amp;lt;&amp;lt; &amp;#39;host4 4.5.6.7&amp;#39;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
                <link>http://blog.crhan.com//2013/01/%E5%96%84%E4%BA%8E%E4%BD%BF%E7%94%A8-bash-builtin-read</link>
                <guid>http://blog.crhan.com//2013/01/善于使用-bash-builtin-read</guid>
                <pubDate>2013-01-04T00:00:00-08:00</pubDate>
        </item>

        <item>
                <title>Git Status 中文乱码解决</title>
                <description>&lt;p&gt;只要一行就行了&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash language-bash&quot; data-lang=&quot;bash&quot;&gt;git config --global core.quotepath &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;前后效果对比见图&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/git-status-%E4%B9%B1%E7%A0%81.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
</description>
                <link>http://blog.crhan.com//2012/09/git-status-%E4%B8%AD%E6%96%87%E4%B9%B1%E7%A0%81%E8%A7%A3%E5%86%B3</link>
                <guid>http://blog.crhan.com//2012/09/git-status-中文乱码解决</guid>
                <pubDate>2012-09-23T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>Ruhoh Plugin 第二弹 -- Sitemap Generator</title>
                <description>&lt;p&gt;网站地图(Sitemaps)是一个让搜索引擎了解你站点所有可抓取网页的最快方法. 网站地图是一个列出 URL 以及像是最后修改时间, 更新频率, 重要度, 关联度这些信息的 XML 文件. 所以它可以让搜索引擎更加智能的抓取你的站点.&lt;/p&gt;

&lt;p&gt;我根据 &lt;a href=&quot;http://www.sitemaps.org/protocol.html#xmlTagDefinitions&quot; title=&quot;Sitemaps XML format&quot;&gt;sitemaps.org&lt;/a&gt; 的标准写了这个 Sitemap Generator. 该文档要求每个页面都需要有&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;loc&amp;gt;&lt;/code&gt;: 用以描述网页的位置&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;可以有&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;lastmod&amp;gt;&lt;/code&gt;: 用 &lt;a href=&quot;http://www.w3.org/TR/NOTE-datetime&quot;&gt;W3C Datetime&lt;/a&gt; 或者 &lt;em&gt;YYYY-MM-DD&lt;/em&gt; 来描述最后修改时间&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;changefreq&amp;gt;&lt;/code&gt;: 可用以下几种内容来描述该页面的更新频率

&lt;ul&gt;
&lt;li&gt;always&lt;/li&gt;
&lt;li&gt;hourly&lt;/li&gt;
&lt;li&gt;daily&lt;/li&gt;
&lt;li&gt;weekly&lt;/li&gt;
&lt;li&gt;monthly&lt;/li&gt;
&lt;li&gt;yearly&lt;/li&gt;
&lt;li&gt;never&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt; &lt;code&gt;&amp;lt;priority&amp;gt;&lt;/code&gt;: 用来描述同一站点里面页面的相对权重, 默认 0.5 (意在通过搜索引擎搜出你自己站点多个页面的时候可以通过这个属性进行自定义排序, 权重大的排在前面. 你设得再大也不能跑到别人前面去啦)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;参考了 &lt;a href=&quot;https://github.com/ruhoh/ruhoh.rb/&quot; title=&quot;http://ruhoh.com&quot;&gt;ruhoh.rb&lt;/a&gt; 的 &lt;a href=&quot;https://github.com/ruhoh/ruhoh.rb/blob/master/lib/ruhoh/compilers/rss.rb&quot; title=&quot;rss.rb&quot;&gt;rss.rb&lt;/a&gt; 用的 &lt;code&gt;Nokogiri::XML::Builder&lt;/code&gt; 搞成的简易工具. 顺便还支持了一些小的自定义, 具体的还请移步 &lt;a href=&quot;https://gist.github.com/3705998&quot; title=&quot;Ruhoh Sitemap Generator&quot;&gt;Ruhoh Sitemap Generator&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;show-gist&quot; data-gist-id=&quot;3705998&quot;&gt;&lt;/div&gt;
</description>
                <link>http://blog.crhan.com//2012/09/ruhoh-plugin-%E7%AC%AC%E4%BA%8C%E5%BC%B9-sitemap-generator</link>
                <guid>http://blog.crhan.com//2012/09/ruhoh-plugin-第二弹-sitemap-generator</guid>
                <pubDate>2012-09-13T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>利用 find 删除自编译的 Python</title>
                <description>&lt;p&gt;以前编译 Python 的时候没有修改 &lt;code&gt;prefix&lt;/code&gt;, 它默认就安装到 &lt;strong&gt;/usr/local&lt;/strong&gt; 文件夹中, 导致后来想要清理掉这些文件的时候遇到了一点困难. 今天终于下定决心搞定了这个问题.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;使用 &lt;strong&gt;find&lt;/strong&gt; 命令找出那段时间创建的文件, 然后再将其删除&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;首先先确定 &lt;code&gt;/usr/local/bin/python&lt;/code&gt; 文件的创建时间&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;python_mtime=$(stat -c %Y /usr/local/bin/python)
# 1345534652
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;然后确认现在时间和 Python 安装时间的时间差(单位是分钟)&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;time_period_min=$(( ( $(date +%s) - python_mtime )/60 ))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;最后就能确认出那一小段时间内被修改的文件&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;find -newermt @$(( python_mtime -1 )) -mmin +$time_period_min | tee find.output
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;文件列表搞出来了你还删不掉吗.?&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;cat find.output | xargs rm -rf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;最后献上完整的脚本&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;file_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;mtime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;stat -c%Y &lt;span class=&quot;nv&quot;&gt;$file_name&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;mtime_p_min&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$((&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;date +%s&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; mtime &lt;span class=&quot;o&quot;&gt;)/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;))&lt;/span&gt;
find . -newermt @&lt;span class=&quot;k&quot;&gt;$((&lt;/span&gt; mtime &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;))&lt;/span&gt; -mmin +&lt;span class=&quot;nv&quot;&gt;$mtime_p_min&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; tee find.output
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;PS: GNU findutils 从 4.3.3 版本开始支持 &lt;code&gt;-newerXY&lt;/code&gt; 参数, 所以本脚本要求 findutils 至少 4.3.3&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                <link>http://blog.crhan.com//2012/09/%E5%88%A9%E7%94%A8-find-%E5%88%A0%E9%99%A4%E8%87%AA%E7%BC%96%E8%AF%91%E7%9A%84-python</link>
                <guid>http://blog.crhan.com//2012/09/利用-find-删除自编译的-python</guid>
                <pubDate>2012-09-05T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>使用 Alfred 代替系统自带 Spotlight</title>
                <description>&lt;p&gt;首先明确一点, Alfred 使用的是与 SpotLight 相同的系统索引, 所以千万别用 &lt;code&gt;sudo mdutil -a -i off&lt;/code&gt; 把系统的文件索引给关了哦.&lt;/p&gt;

&lt;p&gt;首先在 System Preferences -&amp;gt; Spotlight 里面去掉 Spotlight 的全局快捷键 &lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/disable_spotlight_shortcut.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;

&lt;p&gt;接着去掉顶栏右边的放大镜&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;sudo chmod 600 /System/Library/CoreServices/Search.bundle/Contents/MacOS/Search
# 重启系统 UI
killall SystemUIServer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;然后下载 &lt;a href=&quot;http://www.alfredapp.com/&quot; title=&quot;Alfred&quot;&gt;Alfred&lt;/a&gt;, 修改快捷键到 Spotlight 原有的默认快捷键 &lt;code&gt;&amp;lt;c-space&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;最后确认一下 Alfred -&amp;gt; Alfred Preferences -&amp;gt; Advanced -&amp;gt; Keyboard -&amp;gt; Force Keybord 是不是 U.S. 键盘. 这个功能可以强制指定使用 Alfred 时使用的输入法&lt;/p&gt;
</description>
                <link>http://blog.crhan.com//2012/09/%E4%BD%BF%E7%94%A8-alfred-%E4%BB%A3%E6%9B%BF%E7%B3%BB%E7%BB%9F%E8%87%AA%E5%B8%A6-spotlight</link>
                <guid>http://blog.crhan.com//2012/09/使用-alfred-代替系统自带-spotlight</guid>
                <pubDate>2012-09-04T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>在 Mac 10.8 下使用 Passenger &amp; Rvm 的 Rails 应用部署</title>
                <description>&lt;blockquote&gt;
&lt;p&gt;本文在 MacOSX 10.8 MountainLion 下测试通过&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;安装 Homebrew&lt;/h2&gt;

&lt;p&gt;嘿, 先安装 &lt;a href=&quot;http://mxcl.github.com/homebrew/&quot; title=&quot;The missing package manager for OS X&quot;&gt;Homebrew&lt;/a&gt; 不需要我再介绍了把?&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;ruby &amp;lt;(curl -fsSkL raw.github.com/mxcl/homebrew/go)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;参考资料: &lt;a href=&quot;https://github.com/mxcl/homebrew/wiki/Installation&quot; title=&quot;InstallationNew Page Edit Page Page History&quot;&gt;Homebrew Install&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;安装 RVM&lt;/h2&gt;

&lt;p&gt;RVM 是 ruby 的环境隔离工具&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;curl -L https://get.rvm.io | bash -s stable --ruby
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;参考资料: &lt;a href=&quot;https://rvm.io/rvm/install/&quot; title=&quot;Quick (guided) Install&quot;&gt;Installing RVM&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;用 Gem 安装 Passenger&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://www.modrails.com/&quot; title=&quot;Phusion Passenger&quot;&gt;Passenger&lt;/a&gt; 是在 apache 或 nginx 上面部署 Rack 应用的程序&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;gem install passenger
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;参考资料: &lt;a href=&quot;http://trevorturk.com/2010/09/22/passenger-with-nginx-on-mac-os-x-2nd-edition-2/&quot; title=&quot;Passenger with nginx on Mac OS X (2nd edition)&quot;&gt;Passenger with nginx on Mac OS X (2nd edition)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这里还需要创建专门给 Passenger 用的, 包含 RVM 完整路径信息的专用 Ruby =&amp;gt; &lt;code&gt;passenger_ruby&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;rvm wrapper passenger
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;用 Homebrew 安装 nginx&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://nginx.org/&quot; title=&quot;nginx [engine x] is an HTTP and reverse proxy server, as well as a mail proxy server&quot;&gt;Nginx&lt;/a&gt; 是一个轻量快速流行的网页服务器&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;brew install nginx --with-passenger
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;检查一下 Passenger 模块&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;nginx -V 2&amp;gt;&amp;amp;1 | grep passenger
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;给 Nginx 配置 Passenger&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;nginx.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;worker_processes 1;
user crhan _www;

events {
  worker_connections 1024;
}

http {
  include mime.types;
  default_type application/octet-stream;
  sendfile on;
  keepalive_timeout 65;

  passenger_root /Users/crhan/.rvm/gems/ruby-1.9.3-p125/gems/passenger-3.0.15;
  passenger_ruby /Users/crhan/.rvm/bin/passenger_ruby;

  server {
    listen 80;                   
    server_name redmine.local;   
    root /Users/crhan/src/redmine/public;
    passenger_enabled on;
    rack_env production;         
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;配置中的 passenger_root 替换成 &lt;code&gt;passenger-config --root&lt;/code&gt; 的值&lt;/li&gt;
&lt;li&gt;配置中的 passenger_ruby 替换成 &lt;code&gt;which passenger_ruby&lt;/code&gt; 的值&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;参考资料: &lt;a href=&quot;http://blog.ninjahideout.com/posts/a-guide-to-a-nginx-passenger-and-rvm-server&quot; title=&quot;A Guide to a Nginx, Passenger and RVM Server&quot;&gt;A Guide to a Nginx, Passenger and RVM Server&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;测试 Nginx 并启动&lt;/h2&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;sudo nginx -t # 测试 nginx 配置
sudo nginx #启动
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
                <link>http://blog.crhan.com//2012/08/%E5%9C%A8-mac-10-8-%E4%B8%8B%E4%BD%BF%E7%94%A8-passenger-rvm-%E7%9A%84-rails-%E5%BA%94%E7%94%A8%E9%83%A8%E7%BD%B2</link>
                <guid>http://blog.crhan.com//2012/08/在-mac-10-8-下使用-passenger-rvm-的-rails-应用部署</guid>
                <pubDate>2012-08-17T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>使用 Tmux 代替 Screen</title>
                <description>&lt;p&gt;我已经慢慢的开始使用 &lt;a href=&quot;http://tmux.sourceforge.net/&quot; title=&quot;tmux is a terminal multiplexe&quot;&gt;tmux&lt;/a&gt; 来代替 &lt;a href=&quot;http://www.gnu.org/software/screen/&quot; title=&quot;GNU Screen&quot;&gt;screen&lt;/a&gt; 和 &lt;a href=&quot;https://launchpad.net/byobu/&quot; title=&quot;byobu&quot;&gt;byobu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;最初接触 screen 是因为看了 IBM DeveloperWorks 上的文章 &lt;a href=&quot;http://www.ibm.com/developerworks/cn/linux/l-cn-screen/&quot; title=&quot;linux 技巧：使用 screen 管理你的远程会话&quot;&gt;《linux 技巧：使用 screen 管理你的远程会话》&lt;/a&gt;. Screen 刚安装好不做配置的时非常不好用, 所以后来出现了 Ubuntu 出品的 Screen 配置 wrapper: &lt;a href=&quot;https://launchpad.net/byobu/&quot; title=&quot;byobu&quot;&gt;Byobu&lt;/a&gt;. &lt;a href=&quot;https://launchpad.net/byobu/&quot; title=&quot;byobu&quot;&gt;Byobu&lt;/a&gt; 挺好用的, 不过它的问题是除了 ubuntu 以外, 没有其他系统把 &lt;a href=&quot;https://launchpad.net/byobu/&quot; title=&quot;byobu&quot;&gt;byobu&lt;/a&gt; 给做进默认仓库并且对它的版本更新做出及时的反应.&lt;/p&gt;

&lt;p&gt;经过了这么两年的折腾之后, 我发现, 作为一个 SA, 顺手工具迁移性的难易成为了一个选择工具中的重要指标, 所以我才开始慢慢的向 Tmux 转移. Tmux 似乎是作为 screen 的替代品出现的, 大多数操作都很相似, 而且不需要配置也可以很舒服的使用. 另外 Tmux 还有一个很不错的特性叫分屏, 嗯, 如下图所示.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/tmux_split_screen.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;

&lt;p&gt;列举一下常用的命令吧:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tmux&lt;/code&gt;: 运行一个新的 tmux 窗口&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tmux ls&lt;/code&gt;: 查看已经在运行的 tmux 进程&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tmux attach&lt;/code&gt;: 附着(attach)已有的 tmux 进程&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tmux 默认的 Prefix 是 &lt;code&gt;C-b&lt;/code&gt;, 如果已经很熟练 Screen 的话, 可以把 Prefix 换成 &lt;code&gt;C-a&lt;/code&gt;, 只要在 &lt;strong&gt;~/.tmux.conf&lt;/strong&gt; 文件中写入:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;set-option -g prefix C-a
unbind-key C-b
bind-key C-a send-prefix
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Tmux 到底应该怎么用? 你还是另外找资料把, 比如&lt;a href=&quot;https://wiki.freebsdchina.org/software/t/tmux?utm_source=twitterfeed&amp;amp;utm_medium=twitter&quot; title=&quot;使用tmux&quot;&gt;这里&lt;/a&gt;&lt;/p&gt;
</description>
                <link>http://blog.crhan.com//2012/08/%E4%BD%BF%E7%94%A8-tmux-%E4%BB%A3%E6%9B%BF-screen</link>
                <guid>http://blog.crhan.com//2012/08/使用-tmux-代替-screen</guid>
                <pubDate>2012-08-16T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>Janus -- Vim 发行版</title>
                <description>&lt;p&gt;&lt;a href=&quot;https://github.com/carlhuda/janus&quot; title=&quot;carlhuda / janus&quot;&gt;Janus&lt;/a&gt; 的首页是这么介绍它自己的: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Janus: Vim 的发行版&lt;/p&gt;

&lt;p&gt;这是一个 Vim(Gvim &amp;amp; MacVim) 插件和键映射的发行版&lt;/p&gt;

&lt;p&gt;它可以提供一个最简的工作环境, 并且包含了大多数著名的插件和最常用的键映射&lt;/p&gt;

&lt;p&gt;这个发行版可以通过 &lt;code&gt;~/.vimrc.before&lt;/code&gt; 和 &lt;code&gt;~/.vimrc.after&lt;/code&gt; 配置来自定义 Vim RC 文件&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;在 Mac 和 Linux 下面安装 &lt;a href=&quot;https://github.com/carlhuda/janus&quot; title=&quot;carlhuda / janus&quot;&gt;Janus&lt;/a&gt; 很简单:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;curl -Lo- https://bit.ly/janus-bootstrap | bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;因为 Janus 是使用 &lt;a href=&quot;https://github.com/tpope/vim-pathogen&quot; title=&quot;tpope / vim-pathogen&quot;&gt;Pathogen&lt;/a&gt; 加载插件的, 并且自动加载 &lt;code&gt;~/.janus&lt;/code&gt; 文件夹下的插件, 所以制作一个可以方便迁移的 vim 配置只要在 github 上面新建一个叫做 &lt;code&gt;.janus&lt;/code&gt; 的项目, 然后同步即可.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;git clone https://github.com/crhan/.janus/ ~/.janus
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;然后写了一个&lt;a href=&quot;https://github.com/crhan/.janus/blob/master/mklink.sh&quot;&gt;小脚本&lt;/a&gt;放在 &lt;code&gt;.janus&lt;/code&gt; 里面, 以供每次初始化用:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;#!/usr/bin/env bash
CWD=$(cd $(dirname $0) &amp;amp;&amp;amp; pwd )
cd $CWD
for i in vimrc* gvimrc*
do
  ln -sf $CWD/$i ~/.$i
done
git submodule init
git submodule update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
                <link>http://blog.crhan.com//2012/08/janus-vim-%E5%8F%91%E8%A1%8C%E7%89%88</link>
                <guid>http://blog.crhan.com//2012/08/janus-vim-发行版</guid>
                <pubDate>2012-08-13T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>Awesome Vim-PowerLine</title>
                <description>&lt;p&gt;让你呆板的 Vim 编辑器变成高富帅? -&amp;gt; &lt;a href=&quot;https://github.com/Lokaltog/vim-powerline/&quot; title=&quot;Lokaltog / vim-powerline&quot;&gt;Vim-Powerline&lt;/a&gt; 可以做到&lt;/p&gt;

&lt;p&gt;如果你使用基于 &lt;a href=&quot;https://github.com/tpope/vim-pathogen&quot; title=&quot;tpope / vim-pathogen&quot;&gt;Pathogen&lt;/a&gt; 的 &lt;a href=&quot;https://github.com/carlhuda/janus/&quot; title=&quot;carlhuda / janus&quot;&gt;Janus Vim Distribution&lt;/a&gt; 管理 Vim 配置的话, 只需要运行这行即可.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;git clone https://github.com/Lokaltog/vim-powerline.git ~/.janus/vim-powerline
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果还想要更帅的效果, 那你就在 &lt;strong&gt;vimrc&lt;/strong&gt; 里面加上一行&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;let g:Powerline_symbols = &amp;#39;fancy&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;接着你当然会发现效果似乎和图片里的不太一样. 因为它用到了一些不存在的字符, 你需要用它提供的 &lt;a href=&quot;https://github.com/Lokaltog/vim-powerline/tree/develop/fontpatcher&quot;&gt;fontpather&lt;/a&gt; 对你需要的字体进行处理.&lt;/p&gt;

&lt;p&gt;当然当然, 网络上已经提供了好些已经打好 patch 的字体, 比如 Mac 系统上最常用的 &lt;a href=&quot;https://gist.github.com/1634235&quot; title=&quot;Monaco for vim-powerline&quot;&gt;Monaco for Vim-powerline&lt;/a&gt;, 把解压出来的文件放到 &lt;code&gt;~/Library/Fonts&lt;/code&gt; 文件夹内, 然后在终端字体里面选择 &lt;strong&gt;Monaco for vim-powerline&lt;/strong&gt; 就能正常显示了.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/vim-powerline.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
</description>
                <link>http://blog.crhan.com//2012/08/awesome-vim-powerline</link>
                <guid>http://blog.crhan.com//2012/08/awesome-vim-powerline</guid>
                <pubDate>2012-08-13T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>使用 post-receive 钩子自动更新 Ruhoh 博客</title>
                <description>&lt;p&gt;&lt;a href=&quot;http://ruhoh.com/&quot;&gt;Ruhoh&lt;/a&gt; 这个新生的东西还在缓慢的发展之中, 所以还没有成熟的 Hosting 方案出台, 官方只提供了一个基于 Github Webhook 的, 在 &lt;strong&gt;USERNAME.ruhoh.com&lt;/strong&gt; 这个三级域名下的托管方案. 不过幸运的是, 在 &lt;a href=&quot;https://github.com/mojombo/jekyll/wiki/Deployment&quot; title=&quot;Deployment&quot;&gt;Jekyll Custom Deploy Options&lt;/a&gt; 的帮助下, 我对 &lt;a href=&quot;http://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; 提供的 &lt;a href=&quot;http://www.kernel.org/pub/software/scm/git/docs/githooks.html#post-receive&quot; title=&quot;githooks(5) Manual Page&quot;&gt;&lt;em&gt;Git Post-receive hook&lt;/em&gt;&lt;/a&gt; 稍微修改之后就能直接使用了. 下面直接上干货:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;#!/bin/bash
export PATH=$HOME/bin:$HOME/.rvm/bin:$PATH
[[ -s &amp;quot;$HOME/.rvm/scripts/rvm&amp;quot; ]] &amp;amp;&amp;amp; source &amp;quot;$HOME/.rvm/scripts/rvm&amp;quot;

GIT_REPO=$HOME/ruhoh_blog/repo/crhan
TMP_GIT_CLONE=`mktemp -d`
PUBLIC_WWW=$HOME/ruhoh_blog/www/blog.crhan.com

git clone $GIT_REPO $TMP_GIT_CLONE
cd $TMP_GIT_CLONE
ruhoh compile $PUBLIC_WWW
rm -Rf $TMP_GIT_CLONE
exit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;说明一下, 因为我是用 &lt;a href=&quot;https://rvm.io/&quot;&gt;RVM&lt;/a&gt; 安装的 Ruby, 所以需要在二三两行先应用一下 &lt;a href=&quot;https://rvm.io/&quot;&gt;RVM&lt;/a&gt; 设置.&lt;/p&gt;

&lt;p&gt;另外还有一点&lt;strong&gt;很重要&lt;/strong&gt;: 如果第一行的 shebang 写的是 &lt;code&gt;#!/bin/sh&lt;/code&gt; 的话, &lt;a href=&quot;https://rvm.io/&quot;&gt;RVM&lt;/a&gt; 会无法载入, 这大抵是兼容性问题&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;参考资料: &lt;a href=&quot;https://github.com/mojombo/jekyll/wiki/Deployment&quot; title=&quot;Deployment&quot;&gt;Jekyll Deployment Post-receive Hook&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                <link>http://blog.crhan.com//2012/08/%E4%BD%BF%E7%94%A8-post-receive-%E9%92%A9%E5%AD%90%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0%E5%8D%9A%E5%AE%A2</link>
                <guid>http://blog.crhan.com//2012/08/使用-post-receive-钩子自动更新博客</guid>
                <pubDate>2012-08-12T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>Ruhoh Plugin 的第一次试水 -- Google Webmaster</title>
                <description>&lt;p&gt;比起使用现成的博客大巴, 自己搭 &lt;a href=&quot;http://wordpress.org/&quot; title=&quot;WordPress&quot;&gt;WP&lt;/a&gt; 博客起来会有很多乐趣 (以及麻烦), 而像用 &lt;a href=&quot;http://jekyllrb.com/&quot; title=&quot;Jekyll&quot;&gt;Jekyll&lt;/a&gt; 或者我现在正用着的 &lt;a href=&quot;http://ruhoh.com/&quot; title=&quot;Ruhoh&quot;&gt;Ruhoh&lt;/a&gt; 从头设计一个博客的结构, 则又有另外一番收获和体验.&lt;/p&gt;

&lt;p&gt;这次 Ruhoh 插件的首次试水的目标是增加 &lt;a href=&quot;http://webmaster.google.com/&quot; title=&quot;Google WebMaster&quot;&gt;Google WebMaster&lt;/a&gt; 的认证支持. 这种认证很简单, 只需要登陆 &lt;a href=&quot;http://webmaster.google.com/&quot; title=&quot;Google WebMaster&quot;&gt;GWM&lt;/a&gt; 然后添加网站, 然后在目标网站根目录下放置 Google 提供的文件即可. &lt;/p&gt;

&lt;p&gt;由于 &lt;a href=&quot;http://ruhoh.com/&quot; title=&quot;Ruhoh&quot;&gt;Ruhoh&lt;/a&gt; 的设计, 没法通过添加文章或者页面的方式来实现认证, 但是可以通过 &lt;a href=&quot;http://ruhoh.com/usage/plugins/&quot; title=&quot;Ruhoh Plugins&quot;&gt;Plugin&lt;/a&gt; 的方式来搞定, 并且还可以将这个设置添加到网站配置中. 只需要两步:&lt;/p&gt;

&lt;h2&gt;两步走起!&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;新建 &lt;strong&gt;plugins&lt;/strong&gt; 文件夹, 并在里面新建 &amp;#39;&lt;a href=&quot;https://github.com/crhan/crhan.ruhoh.com/blob/babaaac3a5630dbceeedc96c4bfe5ea10a46016c/plugins/google_site_verification.rb&quot; title=&quot;/plugins/google_site_verification.rb&quot;&gt;google&lt;em&gt;site&lt;/em&gt;verification.rb&lt;/a&gt;&amp;#39; 文件并输入以下内容:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;class Ruhoh
  module Compiler
    module GoogleSiteVerification
      def self.run(target, page)
        google_verify = Ruhoh::DB.site[&amp;#39;plugins&amp;#39;][&amp;quot;google_site_verification&amp;quot;] rescue nil

        if google_verify
          FileUtils.cd(target) do
            File.open(&amp;quot;#{google_verify}.html&amp;quot;, &amp;#39;w:UTF-8&amp;#39;) do |p|
              p.puts &amp;quot;google-site-verification: #{google_verify}.html&amp;quot;
            end
          end
          Ruhoh::Friend.say { green &amp;quot;processed: #{google_verify}.html&amp;quot; }
        else
          Ruhoh::Friend.say { red &amp;quot;warning: please add
```
plugins:
  google_site_verification: verify_code (with out &amp;#39;.html&amp;#39;)
```
in your `site.yml` file ;-)&amp;quot; }
        end

      end
    end #GoogleSiteVerification
  end #Compiler
end #Ruhoh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;在 &amp;#39;&lt;a href=&quot;https://github.com/crhan/crhan.ruhoh.com/blob/babaaac3a5630dbceeedc96c4bfe5ea10a46016c/site.yml#L17&quot; title=&quot;/site.yml&quot;&gt;/site.yml&lt;/a&gt;&amp;#39; 最后加入属性 (详参 &lt;a href=&quot;http://www.yaml.org/&quot; title=&quot;YAML Ain&amp;#39;t Markup Language&quot;&gt;YAML&lt;/a&gt;)&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;plugins:
  google_site_verification: googlef056ebc4b89ca27a
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(该值就是 Google 要求下载的文件名)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/google_site_verify.png&quot; alt=&quot;Google 网站管理员工具-验证网络所有权&quot; title=&quot;Google 网站管理员工具-验证网络所有权&quot;&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;hr&gt;

&lt;h2&gt;下一步计划?&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;导入以前 &lt;a href=&quot;http://b.crhan.com&quot;&gt;WordPress 博客&lt;/a&gt; 上面的文章&lt;/li&gt;
&lt;li&gt;研究一下 &lt;a href=&quot;http://en.wikipedia.org/wiki/Atom_(standard)&quot; title=&quot;Atom RSS&quot;&gt;Atom RSS&lt;/a&gt; 并且想办法输出这种 RSS, 然后使用 &lt;a href=&quot;http://feedburner.google.com/&quot; title=&quot;Feed Burner&quot;&gt;feedBurner&lt;/a&gt; 来支持 &lt;a href=&quot;https://code.google.com/p/pubsubhubbub/&quot; title=&quot;A simple, open, web-hook-based pubsub protocol &amp;amp; open source reference implementation.&quot;&gt;PubHubSubBub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;研究一下如何让 &lt;a href=&quot;https://www.google.com/reader&quot;&gt;Google Reader&lt;/a&gt; 可以直接通过添加首页的方式找到本博客的 RSS 源&lt;/li&gt;
&lt;li&gt;重新设计页面&lt;/li&gt;
&lt;/ol&gt;
</description>
                <link>http://blog.crhan.com//2012/08/ruhoh-plugin-%E7%9A%84%E7%AC%AC%E4%B8%80%E6%AC%A1%E8%AF%95%E6%B0%B4</link>
                <guid>http://blog.crhan.com//2012/08/ruhoh-plugin-的第一次试水</guid>
                <pubDate>2012-08-11T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>MacOSX 10.8 Mountain Lion 自动挂载读写 ntfs 分区</title>
                <description>&lt;p&gt;使用 &lt;strong&gt;ntfs-3g&lt;/strong&gt; 替换 macOSX 原生的 &lt;strong&gt;ntfs&lt;/strong&gt; 自动挂载程序, 实现 macOSX 自动挂载读写的ntfs分区, &lt;strong&gt;Mountain Lion&lt;/strong&gt; 测试可行.&lt;/p&gt;

&lt;p&gt;需要以下准备:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/downloads&quot; title=&quot;Command Line Tools for Xcode&quot;&gt;Command Line Tools for Xcode&lt;/a&gt; 或者 &lt;a href=&quot;http://itunes.apple.com/us/app/xcode/id497799835&quot; title=&quot;Xcode&quot;&gt;Xcode&lt;/a&gt; (软件中心下载)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://mxcl.github.com/homebrew/&quot; title=&quot;HomeBrew&quot;&gt;HomeBrew&lt;/a&gt; 或者 &lt;a href=&quot;http://www.macports.org/&quot; title=&quot;MacPorts&quot;&gt;MacPorts&lt;/a&gt; (未验证)&lt;/li&gt;
&lt;li&gt;熟悉的文本编辑器一枚(我用 &lt;a href=&quot;http://code.google.com/p/macvim/&quot; title=&quot;macVim&quot;&gt;macVim&lt;/a&gt;, 也可用 &lt;a href=&quot;http://mxcl.github.com/homebrew/&quot; title=&quot;HomeBrew&quot;&gt;HomeBrew&lt;/a&gt; 安装)&lt;/li&gt;
&lt;li&gt;终端一只 (&lt;em&gt;spotlight&lt;/em&gt; 搜索 &lt;em&gt;Terminal&lt;/em&gt; 即可, 另外推荐下 &lt;a href=&quot;http://www.iterm2.com/&quot; title=&quot;iTerm2&quot;&gt;iTerm2&lt;/a&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;hr&gt;

&lt;h2&gt;步骤&lt;/h2&gt;

&lt;h3&gt;第一步 安装 HomeBrew&lt;/h3&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;/usr/bin/ruby &amp;lt;(curl -fsSk https://raw.github.com/mxcl/homebrew/go)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;参考链接: &lt;a href=&quot;https://github.com/mxcl/homebrew/wiki/installation&quot; title=&quot;HomeBrew&quot;&gt;https://github.com/mxcl/homebrew/wiki/installation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;第二步 安装 ntfs-3g&lt;/h3&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;brew install ntfs-3g
sudo /bin/cp -rfX $(brew --prefix fuse4x-kext)/Library/Extensions/fuse4x.kext /System/Library/Extensions
sudo chmod +s /System/Library/Extensions/fuse4x.kext/Support/load_fuse4x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;参考链接: &lt;a href=&quot;http://fuse4x.org/&quot; title=&quot;fuse4x.org&quot;&gt;http://fuse4x.org/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;第三步 替换原生的挂载程序&lt;/h3&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;# 备份原有的挂载程序
sudo mv /sbin/mount_ntfs /sbin/mount_ntfs.orig
# 创建新的挂载程序并调整权限
sudo touch /sbin/mount_ntfs
sudo chmod 0755 /sbin/mount_ntfs
sudo chown 0:0 /sbin/mount_ntfs
# 编辑文件
sudo mvim /sbin/mount_ntfs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;输入以下内容:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;#!/bin/bash

VOLUME_NAME=&amp;quot;${@:$#}&amp;quot;
VOLUME_NAME=${VOLUME_NAME#/Volumes/}
USER_ID=501
GROUP_ID=20
TIMEOUT=20

if [ `/usr/bin/stat -f &amp;quot;%u&amp;quot; /dev/console` -eq 0 ]; then
  USERNAME=`/usr/bin/defaults read /library/preferences/com.apple.loginwindow | /usr/bin/grep autoLoginUser | /usr/bin/awk &amp;#39;{ print $3 }&amp;#39; | /usr/bin/sed &amp;#39;s/;//&amp;#39;`
  if [ &amp;quot;$USERNAME&amp;quot; = &amp;quot;&amp;quot; ]; then
    until [ `stat -f &amp;quot;%u&amp;quot; /dev/console` -ne 0 ] || [ $TIMEOUT -eq 0 ]; do
      sleep 1
      let TIMEOUT--
    done
    if [ $TIMEOUT -ne 0 ]; then
      USER_ID=`/usr/bin/stat -f &amp;quot;%u&amp;quot; /dev/console`
      GROUP_ID=`/usr/bin/stat -f &amp;quot;%g&amp;quot; /dev/console`
    fi
  else
    USER_ID=`/usr/bin/id -u $USERNAME`
    GROUP_ID=`/usr/bin/id -g $USERNAME`
  fi
else
  USER_ID=`/usr/bin/stat -f &amp;quot;%u&amp;quot; /dev/console`
  GROUP_ID=`/usr/bin/stat -f &amp;quot;%g&amp;quot; /dev/console`
fi

# 这里指向安装好的`ntfs-3g&amp;#39;文件
/usr/local/bin/ntfs-3g \
  -o volname=&amp;quot;${VOLUME_NAME}&amp;quot; \
  -o local \
  -o noappledouble \
  -o negative_vncache \
  -o auto_xattr \
  -o auto_cache \
  -o noatime \
  -o windows_names \
  -o user_xattr \
  -o inherit \
  -o uid=$USER_ID \
  -o gid=$GROUP_ID \
  -o allow_other \
  &amp;quot;$@&amp;quot; &amp;amp;&amp;gt; /var/log/ntfsmnt.log

exit $?;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;搞定&lt;/h2&gt;

&lt;h3&gt;附加步骤: 万一你想复原&lt;/h3&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;sudo mv /sbin/mount_ntfs{.orig,}
# 运行上面这段代码即可
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;常见问题&lt;/h3&gt;

&lt;p&gt;如果你升级了 &lt;strong&gt;Fuse4x&lt;/strong&gt;, 需要先卸载旧的内核扩展再进行上面的第二步. 首先确认一下那些基于 FUSE 的文件系统有没有跑着:&lt;/p&gt;

&lt;p&gt;mount -t fuse4x&lt;/p&gt;

&lt;p&gt;接着卸载那些 FUSE 文件系统和内核扩展:&lt;/p&gt;

&lt;p&gt;sudo kextunload -b org.fuse4x.kext.fuse4x&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;参考资料: &lt;a href=&quot;http://fernandoff.posterous.com/ntfs-write-support-on-osx-lion-with-ntfs-3g-f&quot; title=&quot;NTFS WRITE SUPPORT ON OSX LION WITH NTFS-3G&quot;&gt;NTFS WRITE SUPPORT ON OSX LION WITH NTFS-3G&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                <link>http://blog.crhan.com//2012/08/macosx-10-8-mountain-lion-%E8%87%AA%E5%8A%A8%E6%8C%82%E8%BD%BD%E8%AF%BB%E5%86%99-ntfs-%E5%88%86%E5%8C%BA</link>
                <guid>http://blog.crhan.com//2012/08/macosx-10-8-mountain-lion-自动挂载读写-ntfs-分区</guid>
                <pubDate>2012-08-10T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>RSpec and RSpec::Expectations</title>
                <description>&lt;h2&gt;Describe&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;describe&lt;/code&gt; and &lt;code&gt;context&lt;/code&gt; is &lt;strong&gt;alias&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;describe User, &amp;quot;with no roles assigned&amp;quot; { ... } 
=&amp;gt; User with no roles assigned
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;It&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;specify()&lt;/code&gt; is alias of &lt;code&gt;it()&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;describe &amp;quot;A new chess board&amp;quot; do
  before(:each) { @board = Chess::Board.new }
  specify { @board.should have(32).pieces }
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Pending&lt;/h2&gt;

&lt;p&gt;There are three method to pending test&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;pending with no block&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;describe Newspaper do
it &amp;quot;should be read all over&amp;quot; # pending here
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;pending in example&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;describe &amp;quot;onion rings&amp;quot; do
it &amp;quot;should not be mixed with french fries&amp;quot; do
    pending &amp;quot;cleaning out the fryer&amp;quot;
    fryer_with(:onion_rings).should_not include(:french_fry)
end end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;pending in block and fail (mark for bug)&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;describe &amp;quot;an empty array&amp;quot; do
    it &amp;quot;should be empty&amp;quot; do
        pending(&amp;quot;bug report 18976&amp;quot;) do
          [].should be_empty
        end
    end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;Hooks&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;before(:each)&lt;/code&gt;, &lt;code&gt;before(:all)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;after(:each)&lt;/code&gt;, &lt;code&gt;after(:all)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;around(:each)&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;around do |example|
    DB.transaction &amp;amp;example
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;rollback database each time&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;around do |example|
    DB.transaction do
        example.run
        raise Sequel::Rollback
    end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;Helper Methods&lt;/h2&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;describe Thing do
  def given_thing_with(options)
    yield Thing.new do |thing|
      thing.set_status(options[:status])
    end
  end

  it &amp;quot;should do something when ok&amp;quot; do
    given_thing_with(:status =&amp;gt; &amp;#39;ok&amp;#39;) do |thing|
      thing.do_fancy_stuff(1, true, :move =&amp;gt; &amp;#39;left&amp;#39;, :obstacles =&amp;gt; nil)
      ...
    end
  end

  it &amp;quot;should do something else when not so good&amp;quot; do
    given_thing_with(:status =&amp;gt; &amp;#39;not so good&amp;#39;) do |thing|
      thing.do_fancy_stuff(1, true, :move =&amp;gt; &amp;#39;left&amp;#39;, :obstacles =&amp;gt; nil)
      ...
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Shared Example&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Set up shared example with the &lt;code&gt;shared_examples_for()&lt;/code&gt; method&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;shared_examples_for &amp;quot;any pizza&amp;quot; do
  it &amp;quot;tastes really good&amp;quot; do
    @pizza.should taste_really_good
  end

  it &amp;quot;is available by the slice&amp;quot; do
    @pizza.should be_available_by_the_slice
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Used in other example group with the &lt;code&gt;it_behaves_like()&lt;/code&gt; method&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;describe &amp;quot;New York style thin crust pizza&amp;quot; do
  before(:each) do
    @pizza = Pizza.new(:region =&amp;gt; &amp;#39;New York&amp;#39;, :style =&amp;gt; &amp;#39;thin crust&amp;#39;)
  end

  it_behaves_like &amp;quot;any pizza&amp;quot;

  it &amp;quot;has a really great sauce&amp;quot; do
    @pizza.should have_a_really_great_sauce
  end
end

describe &amp;quot;Chicago style stuffed pizza&amp;quot; do
  before(:each) do
    @pizza = Pizza.new(:region =&amp;gt; &amp;#39;Chicago&amp;#39;, :style =&amp;gt; &amp;#39;stuffed&amp;#39;)
  end

  it_behaves_like &amp;quot;any pizza&amp;quot;

  it &amp;quot;has a ton of cheese&amp;quot; do
    @pizza.should have_a_ton_of_cheese
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;That produces this&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;New York style thin crust pizza
  has a really great sauce
  behaves like any pizza
    tastes really good
    is available by the slice
Chicago style stuffed pizza
  has a ton of cheese
  behaves like any pizza
    tastes really good
    is available by the slice
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;Matchers&lt;/h2&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;prime_numbers.should_not include(8)
list.should respond_to(:length)
lambda { Object.new.explode! }.should raise_error(NameError)
a.should == b
a.should === b
a.should eql(b)
a.should equal(b)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Do not use &lt;code&gt;should !=&lt;/code&gt;, use &lt;code&gt;should_not ==&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;Floating-Point Calc&lt;/h3&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;result.should be_close(5.25, 0.005)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Changes&lt;/h3&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;expect { User.create!(:role =&amp;gt; &amp;quot;admin&amp;quot;) }.to change{ User.admins.count }
expect { User.create!(:role =&amp;gt; &amp;quot;admin&amp;quot;) }.to change{ User.admins.count }.by(1)
expect { User.create!(:role =&amp;gt; &amp;quot;admin&amp;quot;) }.to change{ User.admins.count }.to(1)
expect { User.create!(:role =&amp;gt; &amp;quot;admin&amp;quot;) }.to change{ User.admins.count }.from(0).to(1)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Expect Errors&lt;/h3&gt;

&lt;h4&gt;with Error Type and Message Match(in String or RegEx)&lt;/h4&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;account = Account.new 50, :dollars
expect {
  account.withdraw 75, :dollars
}.to raise_error(
  InsufficientFundsError,
  /attempted to withdraw 75 dollars from an account with 50 dollars/
)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Or with No Args&lt;/h4&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;expect { do_something_risky }.to raise_error
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Expect a Throw&lt;/h3&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;course = Course.new(:seats =&amp;gt; 20)
20.times { course.register Student.new }
lambda {
  course.register Student.new
}.should throw_symbol(:course_full)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Predicate Matchers&lt;/h3&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;array.empty?.should == true
# is equivalent of
array.should be_empty
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Can Receive Args&lt;/h4&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt; user.should be_in_role(&amp;quot;admin&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Collections&lt;/h3&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;collection.should have(37).items
&amp;quot;this string&amp;quot;.should have(11).characters
day.should have_exactly(24).hours
dozen_bagels.should have_at_least(12).bagels
internet.should have_at_most(2037).killer_social_networking_apps
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;have_exactly()&lt;/code&gt; is alias of &lt;code&gt;have()&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;Subjectivity&lt;/h3&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;describe Person do
  subject { Person.new(:birthdate =&amp;gt; 19.years.ago) }
  specify { subject.should be_eligible_to_vote }
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
                <link>http://blog.crhan.com//2012/08/rspec-and-rspec-expectations</link>
                <guid>http://blog.crhan.com//2012/08/rspec-and-rspec-expectations</guid>
                <pubDate>2012-08-05T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>下一步的家庭影院构想</title>
                <description>&lt;p&gt;因为各种古怪的原因, 反正我家里是一直开着一台 PC 把它当做服务器用. 在去年的时候开始用它来下载电影, 通过 transmission 下载各大 PT 站的种子. 然后通过 samba 服务共享给家里的电脑点播.&lt;/p&gt;

&lt;p&gt;缺点很明显, 电影以文件夹的形式存放, 名字很长. 而且这些正式的名字对老爸老妈来说非常的没有意义, 没有图片和介绍, 很不直观. 如果要在大电视机上放电影的话也不方便, 得搬台电脑到电视旁边(或者另外买一个电脑做 HTPC 用? 也不好, win7 以及之前的系统都不适合进行点播操作, 而且成本高), 接线, 放映.&lt;/p&gt;

&lt;p&gt;最近想到了一个看似还不错的方案: 用 Apple TV(不到&lt;strong&gt;700&lt;/strong&gt;元) 配合 A5 设备(&lt;em&gt;iPad2&amp;amp;3, iPhone4S&lt;/em&gt;) 的 Airplay 功能, 直接通过 Apple TV 放到远端的投影设备上(&lt;em&gt;电视机&lt;/em&gt;或者&lt;em&gt;投影仪&lt;/em&gt;). 从 iPad 的放映的方法有两种方案, 最简单的是做一个支持 HTML5 Video 的网页, 后台直接整合 transmission, 做成一个内网的视频网站, 图片 &amp;amp; 文字介绍一条龙. 这样就解决的上面的所有问题. 第二个方案么就是学习下 OC, 然后做一个 APP 出来用.&lt;/p&gt;

&lt;p&gt;这样的设想, 还缺以下设备:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Apple TV -- &lt;strong&gt;0.7k&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;iPad3 -- &lt;strong&gt;3k&lt;/strong&gt; (好吧我承认这样已经比 HTPC 更贵了, 不过更有趣不是么)&lt;/li&gt;
&lt;li&gt;300M 无线路由器 -- &lt;strong&gt;200+&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;投影仪(&lt;em&gt;optional&lt;/em&gt;) -- &lt;strong&gt;5k&lt;/strong&gt; ~ &lt;strong&gt;10k&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;5.1声道音响(&lt;em&gt;optional&lt;/em&gt;) -- 无概念&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当然还是有些不确定的问题, 就是字幕的问题: 如何在网页上直接搜索并&amp;quot;外挂&amp;quot;字幕.&lt;/p&gt;
</description>
                <link>http://blog.crhan.com//2012/05/%E4%B8%8B%E4%B8%80%E6%AD%A5%E7%9A%84%E5%AE%B6%E5%BA%AD%E5%BD%B1%E9%99%A2%E6%9E%84%E6%83%B3</link>
                <guid>http://blog.crhan.com//2012/05/下一步的家庭影院构想</guid>
                <pubDate>2012-05-28T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>杭州天气的抓取(续)</title>
                <description>&lt;p&gt;自从上次搞定了那个 IE Only 的 &lt;a href=&quot;/2012/01/%E4%B8%91%E9%99%8B%E7%9A%84%E6%8A%93%E5%8F%96%E6%9D%AD%E5%B7%9E%E6%B0%94%E8%B1%A1%E7%BD%91%E7%9A%84%E6%95%B4%E7%82%B9%E5%A4%A9%E6%B0%94/&quot; title=&quot;丑陋的抓取杭州气象网的整点天气&quot;&gt;杭州气象网的整点天气数据&lt;/a&gt; 后, 在 &lt;a href=&quot;http://xufan6.com/&quot; title=&quot;萌帆君&quot;&gt;@xufan6&lt;/a&gt; 的协助下搞定了 &lt;a href=&quot;http://oss.oetiker.ch/rrdtool/&quot;&gt;RRD&lt;/a&gt; 的更新和图片的输出, 跑在家里的服务器上. 但是家里服务器略不稳定, 断网断电不可避免, 于是用 PHP 又把这个功能给实现了一遍, 放到了 SAE 上, 顺便也抓了湿度, 可见度, 气压数据, 实现了 JSON 的输出, 已期望二次利用, 但因为在忙毕设, 没有花心思做这个, 所以现在只有输出最近24小时的信息.&lt;/p&gt;

&lt;p&gt;SAE 网址在这: &lt;a title=&quot;杭州&amp;quot;实时&amp;quot;气象数据&quot; href=&quot;http://hangzhoutemper.sinaapp.com&quot;&gt;杭州&amp;quot;实时&amp;quot;气象数据&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;接着 &lt;a href=&quot;http://xufan6.com&quot;&gt;@xufan6&lt;/a&gt; 帮忙搞了 RSS 输出，用 Feedburner 抓取后更新到 &lt;a href=&quot;http://twitter.com/hzqxsk&quot;&gt;@hzqxsk&lt;/a&gt; 上，配合已有的 &lt;a href=&quot;http://twitter.com/hztqyb&quot;&gt;@hztqyb&lt;/a&gt; 一起参考，对杭州的出行和衣着还是有点帮助的&lt;/p&gt;
</description>
                <link>http://blog.crhan.com//2012/02/%E6%9D%AD%E5%B7%9E%E5%A4%A9%E6%B0%94%E7%9A%84%E6%8A%93%E5%8F%96-%E7%BB%AD</link>
                <guid>http://blog.crhan.com//2012/02/杭州天气的抓取-续</guid>
                <pubDate>2012-02-26T00:00:00-08:00</pubDate>
        </item>

        <item>
                <title>丑陋的抓取杭州气象网的整点天气</title>
                <description>&lt;p&gt;很丑的实现了在屎一样的 &lt;a href=&quot;http://www.hzqx.com/gzhfw/qxsk.asp&quot;&gt;杭州气象网&lt;/a&gt; 上抓取正点温度数据的命令..坐等 &lt;a href=&quot;http://xufan6.com/&quot; title=&quot;萌帆君&quot;&gt;@xufan6&lt;/a&gt; 用这个收集数据, 然后把这些历史数据做成图片来看看杭州的气象趋势. 下面就直接给出命令, 欢迎直接粘贴尝试&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;curl &amp;quot;http://www.hzqx.com/gzhfw/qxsk.asp&amp;quot; -s | iconv -f gb2312 -t utf-8 -c | sed -e &amp;#39;s/&amp;amp;nbsp//g&amp;#39; -e &amp;#39;s/;//g&amp;#39; | grep -oP &amp;#39;line_1.*line_2&amp;#39; | sed -e &amp;#39;s/v:line title=/\n/g&amp;#39; | perl -ne &amp;#39;print &amp;quot;$1,$2,$3,$4\n&amp;quot; if /时间：(\d{1,2})时[#&amp;amp;\w&amp;amp;\d]*?温度：([\d.]*?)℃(?:[#&amp;amp;\w&amp;amp;\d]*?最低温度：([\d.]*?)℃)?(?:[#&amp;amp;\w&amp;amp;\d]*?最高温度：([\d.]*?)℃)?/&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;打出的内容分别是: 时间,温度,最低温度,最高温度&lt;/p&gt;

&lt;p&gt;后两项如果没有的话就留空&lt;/p&gt;
</description>
                <link>http://blog.crhan.com//2012/01/%E4%B8%91%E9%99%8B%E7%9A%84%E6%8A%93%E5%8F%96%E6%9D%AD%E5%B7%9E%E6%B0%94%E8%B1%A1%E7%BD%91%E7%9A%84%E6%95%B4%E7%82%B9%E5%A4%A9%E6%B0%94</link>
                <guid>http://blog.crhan.com//2012/01/丑陋的抓取杭州气象网的整点天气</guid>
                <pubDate>2012-01-20T00:00:00-08:00</pubDate>
        </item>

        <item>
                <title>Gentoo 使用 LDAP 整合 linux 登陆</title>
                <description>&lt;ul&gt;
&lt;li&gt;LDAP的初步介绍请看: &lt;a href=&quot;http://www.l-penguin.idv.tw/article/ldap-1.htm&quot; title=&quot;LDAP 入門&quot;&gt;l-penguin的LDAP入门&lt;/a&gt; &amp;amp;&amp;amp; &lt;a href=&quot;http://www.l-penguin.idv.tw/article/ldap-3.htm&quot; title=&quot;LDAP - 整合 Linux user login&quot;&gt;LDAP - 整合 Linux user login&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Gentoo 的 LDAP 重要参考: &lt;a href=&quot;http://www.gentoo.org/doc/en/ldap-howto.xml&quot; title=&quot;Gentoo Guide to OpenLDAP Authentication&quot;&gt;Gentoo Guide to OpenLDAP Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.howtoforge.com/linux_ldap_authentication&quot; title=&quot;LDAP Authentication In Linux&quot;&gt;LDAP Authentication In Linux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;LDAP目录树的缺省结构: &lt;a href=&quot;http://docs.oracle.com/cd/E24847_01/html/E22302/ldapsecure-89.html#scrolltoc&quot; title=&quot;缺省目录信息树 (Directory Information Tree, DIT)&quot;&gt;缺省目录信息树 (Directory Information Tree, DIT)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;简略步骤&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;安装 &lt;a href=&quot;http://gpo.zugaina.org/net-nds/openldap&quot; title=&quot;openldap&quot;&gt;openLDAP&lt;/a&gt; &amp;amp;&amp;amp; &lt;a href=&quot;http://gpo.zugaina.org/sys-auth/nss_ldap&quot; title=&quot;nss_ldap&quot;&gt;nss_ldap&lt;/a&gt; &amp;amp;&amp;amp; &lt;a href=&quot;http://linux.die.net/man/5/pam_ldap&quot; title=&quot;man 5 pam_ldap&quot;&gt;pam_ldap&lt;/a&gt; 组件&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;emerge openldap nss_ldap pam_ldap
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;配置 &lt;a href=&quot;http://linux.die.net/man/5/slapd.conf&quot; title=&quot;man 5 slapd.conf&quot;&gt;slapd.conf&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;include /etc/openldap/schema/core.schema
include /etc/openldap/schema/cosine.schema
include /etc/openldap/schema/inetorgperson.schema
include /etc/openldap/schema/nis.schema
include /etc/openldap/schema/misc.schema
include /etc/openldap/schema/openldap.schema
 
pidfile     /var/run/openldap/slapd.pid
argsfile    /var/run/openldap/slapd.args
 
modulepath  /usr/lib64/openldap/openldap
 
# userPassword 属性仅供验证, 不可显示
access to attrs=userPassword
    by dn=&amp;quot;uid=crhan,ou=SA,ou=Tech,ou=People,dc=myzjut,dc=org&amp;quot; write
    by anonymous auth
    by self write
    by * none
access to *
    by self write
    by users read
    by anonymous read
access to dn.base=&amp;quot;&amp;quot; by * read
access to dn.base=&amp;quot;cn=Subschema&amp;quot; by * read
 
database    hdb
suffix      &amp;quot;dc=myzjut,dc=org&amp;quot;
checkpoint  32  30 
rootdn      &amp;quot;cn=Manager,dc=myzjut,dc=org&amp;quot;
rootpw      secret
directory   /opt/openldap-data
index   objectClass eq
index   cn,uid      eq
index   uidNumber   eq
index   gidNumber   eq
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;配置&lt;a href=&quot;http://docs.oracle.com/cd/E24847_01/html/E22302/a12swit-89620.html&quot; title=&quot;关于名称服务转换器&quot;&gt;名称服务转换器&lt;/a&gt; &lt;a href=&quot;http://linux.die.net/man/5/nsswitch.conf&quot; title=&quot;man 5 nsswitch.conf&quot;&gt;nsswitch.conf&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;passwd:      files ldap
shadow:      files ldap
group:       files ldap
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;配置 &lt;a href=&quot;http://www.padl.com/OSS/nss_ldap.html&quot; title=&quot;nss_ldap&quot;&gt;/etc/ldap.conf&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;base dc=myzjut,dc=org
uri ldap://ldap.yx.zjut.in
ldap_version 3
scope sub
bind_timelimit 2
bind_policy soft
 
pam_filter objectclass=posixAccount
pam_login_attribute uid
pam_member_attribute memberUid
# 默认密码的加密方式 (passwd修改密码相关)
pam_password exop
 
nss_base_passwd ou=People,dc=myzjut,dc=org?sub
nss_base_shadow ou=People,dc=myzjut,dc=org?sub
nss_base_group  ou=Group,dc=myzjut,dc=org?sub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;加入 &lt;a href=&quot;http://linux.die.net/man/5/pam_ldap&quot; title=&quot;man 5 pam_ldap&quot;&gt;pam_ldap&lt;/a&gt; 验证模块到 &lt;code&gt;/etc/pam.d/system-auth&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;auth            required        pam_env.so 
auth            sufficient      pam_unix.so try_first_pass likeauth nullok 
auth            sufficient      pam_ldap.so use_first_pass
auth            required  pam_deny.so
 
account         sufficient      pam_unix.so 
account         sufficient      pam_ldap.so
 
password        required        pam_cracklib.so difok=2 minlen=8 dcredit=2 ocredit=2 retry=3 
password        sufficient      pam_unix.so try_first_pass use_authtok nullok sha512 shadow 
password        sufficient      pam_ldap.so use_first_pass
password                required  pam_deny.so
 
session         required        pam_limits.so 
session         required        pam_env.so 
session         required        pam_unix.so 
session         optional        pam_ldap.so
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;搞定&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;添加 LDAP 用户&lt;/h2&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;ldapadd -x -D &amp;#39;cn=Manager,dc=myzjut,dc=org&amp;#39; -w secret -f init.ldif
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;使用以下 &lt;code&gt;init.ldif&lt;/code&gt; 文件添加用户&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;dn: dc=myzjut,dc=org
objectClass: dcObject
objectClass: organization
o: MyZJUT
dc: myzjut

dn: cn=Manager,dc=myzjut,dc=org
objectClass: organizationalRole
cn: Manager

dn: ou=People,dc=myzjut,dc=org
ou: People
objectClass: top
objectClass: organizationalUnit

dn: ou=Tech,ou=People,dc=myzjut,dc=org
ou: 技术部
ou: Tech
objectClass: top
objectClass: organizationalUnit

dn: ou=SA,ou=Tech,ou=People,dc=myzjut,dc=org
ou: 服务器管理
ou: SA
objectClass: top
objectClass: organizationalUnit

dn: ou=Dev,ou=Tech,ou=People,dc=myzjut,dc=org
ou: 后台开发
ou: Dev
objectClass: top
objectClass: organizationalUnit

dn: cn=test test.local,ou=SA,ou=Tech,ou=People,dc=myzjut,dc=org
givenName: test
sn: test.local
cn: test test.local
uid: test_ldap
uidNumber: 5000
gidNumber: 600
homeDirectory: /home/users/ttest.local
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: top
loginShell: /bin/bash
userPassword: 123123

dn: ou=Group,dc=myzjut,dc=org
ou: Group
objectClass: organizationalUnit
objectClass: top

dn: cn=SA,ou=Group,dc=myzjut,dc=org
cn: SA
objectClass: posixGroup
objectClass: top
gidNumber: 600
memberUid: ttest.local
description: group for SA from myZjut

dn: ou=Hosts,dc=myzjut,dc=org
ou: Hosts
objectClass: organizationalUnit
objectClass: top
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;验证&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;名称服务, 若完全按照上面的步骤, 输入下面的命令, 应该都有输出&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;getent passwd | grep test_ldap
getent group  | grep SA
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;修改密码, 使用 &lt;code&gt;passwd&lt;/code&gt; 修改密码时显示 &lt;strong&gt;LDAP&lt;/strong&gt; 字样&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;passwd test_ldap
=&amp;gt; Enter login(LDAP) password: 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;恭喜成功&lt;/h2&gt;
</description>
                <link>http://blog.crhan.com//2012/01/gentoo-%E4%BD%BF%E7%94%A8-ldap-%E6%95%B4%E5%90%88-linux-%E7%99%BB%E9%99%86</link>
                <guid>http://blog.crhan.com//2012/01/gentoo-使用-ldap-整合-linux-登陆</guid>
                <pubDate>2012-01-06T00:00:00-08:00</pubDate>
        </item>

        <item>
                <title>搭建 Girocco</title>
                <description>&lt;h2&gt;起因&lt;/h2&gt;

&lt;p&gt;由于学校社团的开发团队开始慢慢扩张, 
为了避免自己被开发没日没夜的代码提交等问题困扰, 
所以决定找一个好用一点的版本控制软件和代码托管的网站. 
因为涉及的都是内部的代码, 再加上教育网到国外代码托管网站的速度太慢, 
所以寻找部署一个开源解决方案就变的异常重要. 
因为在 &lt;a href=&quot;http://en.wikipedia.org/wiki/Distributed_Version_Control_System&quot;&gt;dvcs&lt;/a&gt; 里面我只会 &lt;a href=&quot;http://git-scm.com/&quot;&gt;git&lt;/a&gt; , 
所以就选择了 &lt;a href=&quot;http://git-scm.com/&quot;&gt;git&lt;/a&gt; 作为了我们的版本控制软件. 
最开始选择的Git代码托管方案是 &lt;a href=&quot;http://www.ossxp.com/doc/git/gitolite.html&quot;&gt;Gitolite&lt;/a&gt;, 
因为所有的权限控制都需要修改特殊版本库 &amp;quot;&lt;strong&gt;gitolite-admin&lt;/strong&gt;&amp;quot;,
所以第一次给所有人开设账号变得 &lt;strong&gt;非常麻烦&lt;/strong&gt;
( 比如先要普及关于公私钥对的知识等等). 
在使用了一段时间之后, 还遇到了如下问题:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;用户随意建立的版本库无法自行删除 (似乎使用 &lt;a href=&quot;http://www.worldhello.net/2011/11/30/05-gitolite-adc.html&quot;&gt;gitolite-adc&lt;/a&gt;可以解决,但是尚未尝试 )&lt;/li&gt;
&lt;li&gt;用户需要更新公钥时候必须麻烦管理员进行操作&lt;/li&gt;
&lt;li&gt;gitweb界面不够美观&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;所以尝试寻找一些新的解决方案, 在 &lt;a href=&quot;http://stackoverflow.com/questions/438163/whats-the-best-web-interface-for-git-repositories&quot; title=&quot;What&amp;#39;s the best Web interface for Git repositories?&quot;&gt;stackoverflow的帖子&lt;/a&gt;
里面选择了看起来最简单的 &lt;a href=&quot;http://repo.or.cz/w/girocco.git/&quot;&gt;Girocco&lt;/a&gt; 进行尝试.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://repo.or.cz/w/girocco.git/&quot;&gt;Girocco&lt;/a&gt; 是 &lt;a href=&quot;http://repo.or.cz/&quot;&gt;repo.or.cz&lt;/a&gt; 采用的代码托管方案, 简单易用.
&lt;a href=&quot;http://repo.or.cz/w/girocco.git/&quot;&gt;Girocco&lt;/a&gt; 的 &lt;a href=&quot;http://repo.or.cz/w/girocco.git/blob/HEAD:/README&quot;&gt;介绍页面在此&lt;/a&gt; . 在介绍中,他把自己与 &lt;a href=&quot;http://gitorious.org/&quot;&gt;Gitorious&lt;/a&gt;, &lt;a href=&quot;https://github.com&quot;&gt;Github&lt;/a&gt;, &lt;a href=&quot;http://progit.org/book/zh/ch4-7.html&quot;&gt;Gitosis&lt;/a&gt; &amp;amp; &lt;a href=&quot;http://www.ossxp.com/doc/git/gitolite.html&quot;&gt;Gitolite&lt;/a&gt; 进行了对比, 称 &lt;a href=&quot;http://www.ossxp.com/doc/git/gitolite.html&quot;&gt;Gitolite&lt;/a&gt; 和 &lt;a href=&quot;http://progit.org/book/zh/ch4-7.html&quot;&gt;Gitosis&lt;/a&gt; 根本不能算是一种 &lt;em&gt;hosting solution&lt;/em&gt;, &lt;a href=&quot;http://gitorious.org/&quot;&gt;Gitorious&lt;/a&gt; 又不能体现 &lt;em&gt;gitweb&lt;/em&gt; 的简约美, 而 &lt;a href=&quot;https://github.com&quot;&gt;Github&lt;/a&gt; 不开源.&lt;/p&gt;

&lt;h2&gt;经过&lt;/h2&gt;

&lt;h3&gt;第一步 检出代码 &amp;amp; 修改安装文件&lt;/h3&gt;

&lt;p&gt;先把代码检出 &lt;code&gt;git://repo.or.cz/girocco.git&lt;/code&gt; 按照 &lt;em&gt;INSTALL&lt;/em&gt; 文件的说明, 先按需修改 &lt;em&gt;Girocco/Config.pm&lt;/em&gt; 文件, 然后 &lt;code&gt;make install&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;our $basedir = &amp;quot;/opt/Girocco/base&amp;quot;;
our $reporoot = &amp;quot;/opt/Girocco/repo&amp;quot;;
our $chroot = &amp;quot;/opt/Girocco/j&amp;quot;;
our $webroot = &amp;quot;/opt/Girocco/WWW&amp;quot;;
our $cgiroot = &amp;quot;/opt/Girocco/WWW&amp;quot;;
our $webreporoot = &amp;quot;/opt/Girocco/WWW/r&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这时候出了点问题, 发现这个项目并不是这么完善. 我的搭建环境是:&lt;br&gt;
&lt;strong&gt;&amp;#39;Gentoo hardened/linux/amd64/no-multilib 3.0.4-hardened-r4&amp;#39;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Girocco&lt;/em&gt; 提供的 &lt;em&gt;jailsetup.sh&lt;/em&gt; 中的 &lt;strong&gt;shabong&lt;/strong&gt; 是 &lt;strong&gt;/bin/sh&lt;/strong&gt; 然后在第七行写着 &lt;code&gt;. shlib.sh&lt;/code&gt; 结果运行中提示 &lt;strong&gt;file not found&lt;/strong&gt; 错误. 后来尝试性的改成了 &lt;code&gt;. ./shlib.sh&lt;/code&gt; 就找到这个文件了, 这是第一个没有良好兼容性的问题.&lt;/p&gt;

&lt;p&gt;重新运行, 接着又提示找不到 &lt;strong&gt;git-index-pack&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;for i in git git-index-pack git-receive-pack git-shell git-update-server-info git-upload-archive git-upload-pack git-unpack-objects; do
    pull_in_bin /usr/bin/$i bin
done
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;接着我在 &lt;em&gt;/usr/libexec/git-core/&lt;/em&gt; 里面找到了所有的文件, 于是把循环体改成了 &lt;code&gt;pull_in_bin /usr/libexec/git-core/$i bin&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;还有一处改动也在类似的位置, 他又写死了 &lt;strong&gt;nc&lt;/strong&gt; 的路径 &lt;code&gt;/bin/nc.openbsd&lt;/code&gt;, 只好根据 &lt;em&gt;gentoo&lt;/em&gt; 的设定, 把他改成了 &lt;code&gt;/usr/bin/nc&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;最后还要修改一个脚本变量, 在 &lt;strong&gt;jobs/fixupcheck.sh&lt;/strong&gt; 的第15, 17行, 将两个变量改成与 &lt;strong&gt;Girocco/Config.pm&lt;/strong&gt; 中的设定一致&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;reporoot=&amp;quot;/opt/Girocco/repo&amp;quot;
chroot=&amp;quot;/opt/Girocco/j&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;然后把他复制到 &lt;strong&gt;/root&lt;/strong&gt; 目录下&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;cp jobs /root/repomgr -r
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;第二步 设置 apache 服务器&lt;/h3&gt;

&lt;p&gt;设置 &lt;em&gt;apache&lt;/em&gt;, 没有什么难点, 就根据第一步中设置的 &lt;em&gt;Girocco/Config.pm&lt;/em&gt; 设置修改他提供给你的 &lt;strong&gt;apache.conf&lt;/strong&gt; 文件即可.&lt;/p&gt;

&lt;h3&gt;第三步 设置 chroot 环境&lt;/h3&gt;

&lt;h4&gt;a. 自动挂载repo目录至 chroot 环境&lt;/h4&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;cat &amp;gt;&amp;gt; /etc/fstab &amp;lt;&amp;lt;EOF
/proc /opt/Girocco/j/proc none defaults,bind 0 0
/opt/Girocco/repo /opt/Girocco/j/srv/git none defaults,bind 0 0
EOF
mount -a
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;b. 让 syslog-ng 监听 chroot 的 log 设备&lt;/h4&gt;

&lt;p&gt;在 &lt;strong&gt;/etc/syslog-ng/syslog-ng/conf&lt;/strong&gt; 的 &lt;strong&gt;source&lt;/strong&gt; 部分加上额外的监听, 并重启进程:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;source jail1{
    unix-stream(&amp;quot;/opt/Girocco/j/dev/log&amp;quot;);
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;参考资料: &lt;a href=&quot;http://www.campin.net/syslog-ng/faq.html#chroot&quot;&gt;syslog-ng faq&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;c. 启动 chroot 中的 sshd, 监听 git 请求&lt;/h3&gt;

&lt;p&gt;因为启动 sshd 的时候他总是告诉我找不到 &lt;strong&gt;/etc/ssh/ssh&lt;em&gt;host&lt;/em&gt;ecdsa_key&lt;/strong&gt; 文件, 要给他强制指定一个 &lt;em&gt;RSA key&lt;/em&gt;: 在 &lt;em&gt;sshd&lt;/em&gt; 的配置文件 &lt;strong&gt;/opt/Girocco/j/etc/ssh/sshd_config&lt;/strong&gt; 加上一行&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;HostKey /etc/ssh/ssh_host_rsa_key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;然后还提示缺少 &lt;strong&gt;/var/empty&lt;/strong&gt; 文件夹&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;mkdir /opt/Girocco/j/var/empty
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;最后启动 &lt;em&gt;sshd&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;chroot /opt/Girocco/j /sbin/sshd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;第四步 添加 crontab&lt;/h3&gt;

&lt;p&gt;使用命令 &lt;code&gt;crontab -e&lt;/code&gt; 添加两个任务&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;*/2  * * * * /usr/bin/nice -n 18 /root/fixupcheck.sh # adjust frequency based on number of repos
*/30 * * * * /usr/bin/nice -n 18 /opt/Girocco/base/jobd/jobd.sh -q --all-once
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;完成&lt;/h3&gt;

&lt;hr&gt;

&lt;h2&gt;结果&lt;/h2&gt;

&lt;p&gt;搭建完了才发现, 我们其实并不需要一个权限这么开放的系统, 就现在而言, &lt;a href=&quot;http://repo.or.cz/w/girocco.git/&quot;&gt;Girocco&lt;/a&gt; 并没有显示出比 &lt;a href=&quot;http://www.ossxp.com/doc/git/gitolite.html&quot;&gt;Gitolite&lt;/a&gt; 好得多的优越性, 还不足以驱动我去把已有的 &lt;a href=&quot;http://www.ossxp.com/doc/git/gitolite.html&quot;&gt;Gitolite&lt;/a&gt; 给替换掉&lt;/p&gt;

&lt;p&gt;遗憾之余只好决定把这段经历记录下来, 以慰藉我失去的那 &lt;big&gt;&lt;strong&gt;5个小时&lt;/strong&gt;&lt;/big&gt;时光&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.flickr.com/photos/cncrhan/6572992149/&quot;&gt;&lt;img src=&quot;/assets/girocco_crhan.jpeg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
</description>
                <link>http://blog.crhan.com//2011/12/%E6%90%AD%E5%BB%BA-girocco</link>
                <guid>http://blog.crhan.com//2011/12/搭建-girocco</guid>
                <pubDate>2011-12-16T00:00:00-08:00</pubDate>
        </item>

        <item>
                <title>Set up Time Machine Server on Gentoo for Lion</title>
                <description>&lt;blockquote&gt;
&lt;p&gt;重点参考来源： http://www.kremalicious.com/2008/06/ubuntu-as-mac-file-server-and-time-machine-volume/&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;Set up server on gentoo&lt;/h2&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;# macOX 10.7 need netatalk-2.2.0 or above
echo &amp;#39;net-fs/netatalk ~amd64&amp;#39; &amp;gt;&amp;gt; /etc/portage/package.keywords
emerge netatalk avahi

# Make folder `/opt/TimeMachie&amp;#39; a shared volumn to your mac.
# Option `tm&amp;#39; is important, which means this volumn is used by timemachine
echo &amp;#39;/opt/TimeMachine TimeMachine cnidscheme:dbd options:tm&amp;#39; &amp;gt;&amp;gt; /etc/netatalk/AppleVolumes.default
echo &amp;#39;- -tcp -noddp -uamlist uams_dhx.so,uams_dhx2.so -nosavepassword&amp;#39; &amp;gt;&amp;gt; /etc/netatalk/afpd.conf
cat &amp;lt;&amp;lt; EOF &amp;gt;/etc/avahi/services/afpd.service
&amp;lt;!--?xml version=&amp;quot;1.0&amp;quot; standalone=&amp;#39;no&amp;#39;?--&amp;gt;&amp;lt;!--*-nxml-*--&amp;gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; standalone=&amp;#39;no&amp;#39;?&amp;gt;&amp;lt;!--*-nxml-*--&amp;gt;
&amp;lt;!DOCTYPE service-group SYSTEM &amp;quot;avahi-service.dtd&amp;quot;&amp;gt;
&amp;lt;service-group&amp;gt;
&amp;lt;!-- Customize this to get a different name for your server in the Finder. --&amp;gt;
&amp;lt;name replace-wildcards=&amp;quot;yes&amp;quot;&amp;gt;%h&amp;lt;/name&amp;gt;
&amp;lt;service&amp;gt;
&amp;lt;type&amp;gt;_device-info._tcp&amp;lt;/type&amp;gt;
&amp;lt;port&amp;gt;0&amp;lt;/port&amp;gt;
&amp;lt;!-- Customize this to get a different icon in the Finder. --&amp;gt;
&amp;lt;txt-record&amp;gt;model=Xserver&amp;lt;/txt-record&amp;gt;
&amp;lt;/service&amp;gt;
&amp;lt;service&amp;gt;
&amp;lt;type&amp;gt;_afpovertcp._tcp&amp;lt;/type&amp;gt;
&amp;lt;port&amp;gt;548&amp;lt;/port&amp;gt;
&amp;lt;/service&amp;gt;
&amp;lt;/service-group&amp;gt;
EOF

rc-update add avahi-daemon default
rc-update add netatalk default
rc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Set up Time Machine on OS X lion&lt;/h2&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;# Make Time Machine could use disks through the network
defaults write com.apple.systempreferences TMShowUnsupportedNetworkVolumes 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/assets/time_machine.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
</description>
                <link>http://blog.crhan.com//2011/10/set-up-time-machine-server-on-gentoo-for-lion</link>
                <guid>http://blog.crhan.com//2011/10/set-up-time-machine-server-on-gentoo-for-lion</guid>
                <pubDate>2011-10-03T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>使用GNU parallel 并行更新 git 子模块</title>
                <description>&lt;p&gt;如果你像我一样使用 &lt;a href=&quot;http://www.vim.org/scripts/script.php?script_id=2332&quot; title=&quot;pathogen&quot;&gt;pathogen&lt;/a&gt; 来管理你的 vim 插件，并且在 &lt;strong&gt;.vim/bundle/&lt;/strong&gt; 文件夹下面添加了好多个 &lt;strong&gt;git submodule&lt;/strong&gt; 的话，这个技巧应该对你有点帮助。&lt;/p&gt;

&lt;p&gt;这里用到了GNU的 parallel 工具，如果你是 gentoo ，那你可以通过 &lt;code&gt;moreutils&lt;/code&gt; 包来获得它。&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;emerge moreutils
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;用法很简单，这里就直接给出可用的脚本了，将下面的脚本放到git的根目录下运行即可&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin&amp;quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;CWD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;dirname &lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;sub_modules&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;git submodule status &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;awk &lt;span class=&quot;s1&quot;&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
 
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; -n &lt;span class=&quot;s2&quot;&gt;&amp;quot;$sub_modules&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    if &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; -x &lt;span class=&quot;s2&quot;&gt;&amp;quot;$(which parallel)&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
        parallel -i sh -c &lt;span class=&quot;s2&quot;&gt;&amp;quot;cd {};pwd;git pull&amp;quot;&lt;/span&gt; -- &lt;span class=&quot;nv&quot;&gt;$sub_modules&lt;/span&gt;
    else
        echo &lt;span class=&quot;s1&quot;&gt;&amp;#39; * Error： Util `Parallel` not found&amp;#39;&lt;/span&gt;
    fi
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    echo &lt;span class=&quot;s2&quot;&gt;&amp;quot; * Error: Not any git submodules find&amp;quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
</description>
                <link>http://blog.crhan.com//2011/09/%E4%BD%BF%E7%94%A8gnu-parallel-%E5%B9%B6%E8%A1%8C%E6%9B%B4%E6%96%B0-git-%E5%AD%90%E6%A8%A1%E5%9D%97</link>
                <guid>http://blog.crhan.com//2011/09/使用gnu-parallel-并行更新-git-子模块</guid>
                <pubDate>2011-09-14T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>使用 xmodemap 工具使 Capslock 按键成为附加的 Ctrl 键</title>
                <description>&lt;p&gt;结果很简单，过程很曲折，完整的代码如下，将其放入 &lt;code&gt;~/.Xmodmap&lt;/code&gt; 中即可：&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;!add capsLK additional ctrl
remove lock = Caps_Lock
add control = Caps_Lock
keycode 66 = Control_L
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;PS: keycode 66 就是CapsLk键&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;参考资料：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.csdn.net/lqk1985/article/details/5152115&quot; title=&quot;xmodmap修改键映射&quot;&gt;CSDN xmodmap修改键映射&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://c2.com/cgi/wiki?RemapCapsLock&quot; title=&quot;Remap Caps Lock&quot;&gt;Remap Caps Lock&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
</description>
                <link>http://blog.crhan.com//2011/09/%E4%BD%BF%E7%94%A8-xmodemap-%E5%B7%A5%E5%85%B7%E4%BD%BF-capslock-%E6%8C%89%E9%94%AE%E6%88%90%E4%B8%BA%E9%99%84%E5%8A%A0%E7%9A%84-ctrl-%E9%94%AE</link>
                <guid>http://blog.crhan.com//2011/09/使用-xmodemap-工具使-capslock-按键成为附加的-ctrl-键</guid>
                <pubDate>2011-09-12T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>Bash 脚本心得</title>
                <description>&lt;p&gt;受到了 &lt;a href=&quot;http://imtx.me&quot;&gt;主席&lt;/a&gt; 的鼓励，分享一下我前段时间实习写bash脚本学到的一些东西。&lt;/p&gt;

&lt;p&gt;之前对 &lt;strong&gt;shell&lt;/strong&gt; 脚本的认识还十分浅薄，认为他不过是一系列命令的组合罢了。经过这段时间的实践之后，发现 &lt;strong&gt;shell&lt;/strong&gt; 脚本远不止于此，他最精华的部分在于对变量的处理而产生很多丰富的功能实现&lt;/p&gt;

&lt;h2&gt;变量&lt;/h2&gt;

&lt;h3&gt;哈希实现&lt;/h3&gt;

&lt;p&gt;当你想要一个哈希数组的时候，你并不只有一种方法去拥有它。最简单的当然是&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;declare -A hash_name
hash_name{key}=value
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;除此之外，你也可以用最普通的变量名来实现哈希，方法来自于 &lt;a href=&quot;http://imtx.me&quot;&gt;StackOverflow&lt;/a&gt;：&lt;/p&gt;

&lt;p&gt;利用 &lt;code&gt;${!var_var_name}&lt;/code&gt; 和 &lt;code&gt;${!var_prefix*}&lt;/code&gt; 的 shell 自动展开来实现。&lt;/p&gt;

&lt;p&gt;前者是通过定义 &lt;code&gt;var_var_name&lt;/code&gt; 变量来显示以该变量的值为变量名的变量的值。举个例子:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;ocal SYSTEM_64=&amp;quot;rhel 6.0 x86_64 release&amp;quot;
local i=&amp;quot;SYSTEM_64&amp;quot;
echo ${!i}
# output: rhel 6.0 x86_64 releasen
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;后者可以展开以 &lt;code&gt;var_prefix&lt;/code&gt; 为前缀的所有变量名，把这两点结合起来也就能实现一个哈希数组的遍历：&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;local SYSTEM_32=&amp;quot;rhel 6.0 x86 release&amp;quot;
local SYSTEM_64=&amp;quot;rhel 6.0 x86_64 release&amp;quot;
for i in &amp;quot;${!SYSTEM_@}&amp;quot;
do
    echo ${!i}
done
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这样实现的好处还是比较明显的：你可以不受到shell的一维数组的限制，实现多维数组。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;参考： man bash &amp;quot;Parameter Expansion&amp;quot; 部分&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;一维变量元素检查 &amp;amp;&amp;amp; 重新生成&lt;/h3&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;${parameter:+word}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;也许你也会像我一样，让脚本的传入一些文件名，并且还希望这些文件是必然存在的。可惜大部分时候并非如此，而且你也绝对不会希望因为使用者的错误输入而导致整个脚本朝着一个不可预期的方向前进。&lt;/p&gt;

&lt;p&gt;当然，处理起来很简单，只需要循环test一次就可以解决，但是保存文件名的变量的重新生成也许就没有这么完美了，似乎没有简单的方法可以避免变量值中多出来的分割符？&lt;/p&gt;

&lt;p&gt;bash会告诉你事实，并非如此，你只需要&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;var_name=${var_name:+${var_name} }${new_var}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;就可以实现了&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;参考： man bash &amp;quot;Parameter Expansion&amp;quot; 部分&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;$* 和 $@&lt;/h3&gt;

&lt;p&gt;这两个变量会展开所有的 &lt;strong&gt;positional parameters&lt;/strong&gt;($0, $1, $2, etc...)，而如果他们在双引号内展开，展开的结果会非常不一样，但又不这么容易被察觉。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$*&lt;/code&gt; 变量在双引号内展开之后，结果还是在一个双引号之内的字符串。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$@&lt;/code&gt; 变量在双引号内展开之后，结果是多个被空格分隔的字符串。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如何发现他们的不同？试试看下面这个脚本，保存并传入多个参数后运行&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;#!/bin/bash
echo &amp;quot;Positional Parameters in $@&amp;quot;
for i in &amp;quot;$@&amp;quot;
do
    echo $i
done
echo &amp;quot;Positional Parameters in $@&amp;quot;
for i in &amp;quot;$*&amp;quot;
do
    echo $i
done
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;日志 &amp;amp;&amp;amp; exec 信息流重导向&lt;/h2&gt;

&lt;p&gt;完善的log输出对出错后的问题定位的贡献是毋庸置疑的，但是如果在bash中运行了大量程序，可能你的屏幕会被搞的一团糟，这时候你可能会想到自定义信息流的重导向。&lt;/p&gt;

&lt;p&gt;重导向？不想让它上屏？很简单啦，&lt;code&gt;1&amp;gt;/dev/null 2&amp;gt;&amp;amp;1&lt;/code&gt;。但是这很麻烦诶，每个命令后面都要打一次吗？当然也不是啦，你可以 &lt;code&gt;{ cmd; cmd; cmd; } 1&amp;gt;/dev/null 2&amp;gt;&amp;amp;1&lt;/code&gt; 将一个范围内的指令的输出给统一重导向掉。但但是，如果你认为这还是很麻烦，如果能像 perl 的 &lt;code&gt;select&lt;/code&gt; 语句一样，把所有的默认输出都改到一个地方怎么办？那只能请出身为 &lt;strong&gt;shell builtin&lt;/strong&gt; 的 &lt;code&gt;exec&lt;/code&gt; 大神啦。&lt;code&gt;exec&lt;/code&gt;可以改变shell中默认的文件描述符。&lt;/p&gt;

&lt;p&gt;这一部分内容在 &lt;a href=&quot;http://tldp.org/LDP/abs/html/io-redirection.html&quot; title=&quot;io redirection&quot;&gt;tldp&lt;/a&gt; 上讲的特别清楚，他详细的讲述了什么是文件描述符 (file descriptor)，也告诉你了 bash 默认把 &lt;strong&gt;0&lt;/strong&gt; 号文件描述符打开为 &lt;strong&gt;stdin&lt;/strong&gt;，&lt;strong&gt;1&lt;/strong&gt; 号打开为 &lt;strong&gt;STDOUT&lt;/strong&gt;，&lt;strong&gt;2&lt;/strong&gt; 号打开为 &lt;strong&gt;STDERR&lt;/strong&gt;。而正常的 &lt;strong&gt;*nux&lt;/strong&gt; 程序都会把标准输出导向 &lt;strong&gt;&amp;amp;1&lt;/strong&gt;（表示一号描述符），把标准错误导向 &lt;strong&gt;&amp;amp;2&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;了解这些之后，生成有意义的输出就变得简单多了。&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;exec 6&amp;gt;&amp;amp;1 #将 &amp;amp;6 的输出，重定向到 &amp;amp;1, 此时的 &amp;amp;1 是 stdout
exec 7&amp;gt;&amp;amp;2 #将 &amp;amp;7 的输出，重定向到 &amp;amp;2, 此时的 &amp;amp;2 是 stderr
exec 1&amp;gt;/dev/null #将 &amp;amp;1 的输出，全部丢弃，（当然你也可以将它导入到一个文件做留档）
exec 2&amp;gt;/dev/null #同上
echo &amp;quot;This message will be directed to &amp;#39;/dev/null&amp;#39;&amp;quot;
echo &amp;quot;This message will be directed to STDOUT&amp;quot; &amp;gt;&amp;amp;6
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;工作目录切换 &amp;amp;&amp;amp; 外部脚本引用&lt;/h2&gt;

&lt;p&gt;如果脚本里面需要用到 &lt;strong&gt;相同目录下的其他脚本&lt;/strong&gt;，怎么办？虽然这个问题困扰了我挺久，但是解决起来还是比较方便的，只要在脚本开头切换工作目录即可&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;CWD=&amp;quot;$( cd &amp;quot;$( dirname &amp;quot;$0&amp;quot; )&amp;quot; &amp;amp;&amp;amp; pwd )&amp;quot;
cd $CWD
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;小结&lt;/h2&gt;

&lt;p&gt;bash 的功能很强大，写完这个 bash 脚本后，3000 多行的 bash 手册我也才翻熟了三分之一，shell 脚本虽小，但也能做到五脏俱全，能写完这个脚本，我打心底里感谢 &lt;strong&gt;Funtoo Fundation&lt;/strong&gt; 出品的 &lt;a href=&quot;http://www.funtoo.org/wiki/Keychain&quot;&gt;&lt;strong&gt;keychain&lt;/strong&gt;&lt;/a&gt; 脚本，从里面获得了不少灵感。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/-cSo1Y6rDcl0/TmCORE-iaJI/AAAAAAAAAjw/wn295ulkR8E/s720/DSCF1599-1.JPG&quot; alt=&quot;&quot; title=&quot;fly&quot;&gt;&lt;/p&gt;
</description>
                <link>http://blog.crhan.com//2011/09/bash-%E8%84%9A%E6%9C%AC%E5%BF%83%E5%BE%97</link>
                <guid>http://blog.crhan.com//2011/09/bash-脚本心得</guid>
                <pubDate>2011-09-11T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>彩色 Bash 提示符和 $PS1</title>
                <description>&lt;p&gt;今天又折腾了一下自己的PS1，蓝本是从 &lt;a href=&quot;http://imtx.me/archives/1298.html&quot;&gt;TX主席&lt;/a&gt; 那里弄来的。&lt;/p&gt;

&lt;p&gt;修改的地方有：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;在 shell prompt 之前增加了一个换行&lt;/li&gt;
&lt;li&gt;当前工作目录的显示换成了始终显示完整路径&lt;/li&gt;
&lt;li&gt;修改了一些颜色&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;\n\[\e[01;37m\][`a=$?;if [ $a -ne 0 ]; then echo -n -e &amp;quot;\[\e[01;32;41m\]{$a}&amp;quot;; fi`\[\033[01;32m\]\u\[\033[01;33m\]@\[\033[01;35m\]\h\[\033[00m\] \[\033[01;34m\]`pwd``B=$(git branch 2&amp;gt;/dev/null | sed -e &amp;quot;/^ /d&amp;quot; -e &amp;quot;s/* \(.*\)/\1/&amp;quot;); if [ &amp;quot;$B&amp;quot; != &amp;quot;&amp;quot; ]; then S=&amp;quot;git&amp;quot;; elif [ -e .bzr ]; then S=bzr; elif [ -e .hg ]; then S=&amp;quot;hg&amp;quot;; elif [ -e .svn ]; then S=&amp;quot;svn&amp;quot;; else S=&amp;quot;&amp;quot;; fi; if [ &amp;quot;$S&amp;quot; != &amp;quot;&amp;quot; ]; then if [ &amp;quot;$B&amp;quot; != &amp;quot;&amp;quot; ]; then M=$S:$B; else M=$S; fi; fi; [[ &amp;quot;$M&amp;quot; != &amp;quot;&amp;quot; ]] &amp;amp;&amp;amp; echo -n -e &amp;quot;\[\e[33;40m\]($M)\[\033[01;32m\]\[\e[00m\]&amp;quot;`\[\033[01;34m\]\[\e[01;37m\]]\n\[\e[01;34m\]$ \[\e[00m\]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;同样，我的bashrc，vimrc，vim插件，gentoo的配置，也都在 &lt;a href=&quot;https://github.com/crhan/myconf&quot;&gt;Github 里&lt;/a&gt; 可以随意查阅。&lt;/p&gt;

&lt;p&gt;效果图如下:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/PS1.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
</description>
                <link>http://blog.crhan.com//2011/09/%E5%BD%A9%E8%89%B2-bash-%E6%8F%90%E7%A4%BA%E7%AC%A6%E5%92%8C-ps1</link>
                <guid>http://blog.crhan.com//2011/09/彩色-bash-提示符和-ps1</guid>
                <pubDate>2011-09-10T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>改名了改名了</title>
                <description>&lt;p&gt;无耻的又改名了，文章没几篇，改名两三次，自己也要遭不住了&lt;/p&gt;

&lt;p&gt;这次在 &lt;a href=&quot;http://imtx.me&quot; title=&quot;TX主席&quot;&gt;TX主席&lt;/a&gt; 的怂恿下，我也加入了 im 家族，名字改成了 &lt;strong&gt;I&amp;#39;m Crhan&lt;/strong&gt;。并且开始尝试在 vim 下使用 &lt;a href=&quot;https://github.com/connermcd/VimRepress&quot;&gt;vimpress&lt;/a&gt; 发表俺的 blog~很有 geek 的感觉哦~~&lt;/p&gt;

&lt;p&gt;自己要加油啊！&lt;/p&gt;
</description>
                <link>http://blog.crhan.com//2011/09/%E6%94%B9%E5%90%8D%E4%BA%86%E6%94%B9%E5%90%8D%E4%BA%86</link>
                <guid>http://blog.crhan.com//2011/09/改名了改名了</guid>
                <pubDate>2011-09-04T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>Gentoo 更新 libreoffice-3.4 之后无法正确运行</title>
                <description>&lt;p&gt;前几天更新了 libreoffice3.4 之后居然无法开启 office 程序，运行的时候提示&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Failed to execute child process &amp;quot;libreoffice3.4&amp;quot; (No such file or directory).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;解决的方法很简单，参考 &lt;a href=&quot;https://groups.google.com/d/topic/linux.gentoo.user/NtVmtpmUIqk/discussion&quot; title=&quot;gentoo user 用户组&quot;&gt;gentoo-user&lt;/a&gt; 用户组的提示，原因在这几个 libreoffice 程序的 desktop 执行脚本的 Exec 是错误的。应为 &amp;quot;libreoffice&amp;quot;，而在文件里面写的是 &amp;quot;libreoffice3.4&amp;quot;。那么解决方法就是将其都修改成 libreoffice 或者在 &amp;quot;/usr/bin&amp;quot;，下面添加一个软链接也可。&lt;/p&gt;

&lt;p&gt;批量修改运行脚本：&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;sed -i &amp;#39;s/\(libreoffice\)3.4/\1/&amp;#39; /usr/share/applications/libreoffice-*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;建立软链接：&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;ln -s libreoffice /usr/bin/libreoffice3.4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
                <link>http://blog.crhan.com//2011/06/gentoo-%E6%9B%B4%E6%96%B0-libreoffice-3-4-%E4%B9%8B%E5%90%8E%E6%97%A0%E6%B3%95%E6%AD%A3%E7%A1%AE%E8%BF%90%E8%A1%8C</link>
                <guid>http://blog.crhan.com//2011/06/gentoo-更新-libreoffice-3-4-之后无法正确运行</guid>
                <pubDate>2011-06-15T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>Gentoo 升级 Gnome3 (早已过时)</title>
                <description>&lt;h3&gt;&lt;ins datetime=&quot;2012-08-11T12:23:43+00:00&quot;&gt;&lt;strong&gt;本文内容早已过时&lt;/strong&gt;&lt;/ins&gt;&lt;/h3&gt;

&lt;hr&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;Gentoo 里面升级到 Gnome3 还是比较方便的，只要添加 gnome 的 overlay，然后更新就可以完成了，下面是一点点详述

# 安装 layman
emerge layman

# 添加 gnome overlay
layman -a gnome

# 添加 overlay 配置
echo &amp;quot;source /var/lib/layman/make.conf&amp;quot; &amp;gt;&amp;gt; /etc/make.conf

# 让 eix-sync 自动更新所有 layman 里的 overlay
echo &amp;quot;*&amp;quot; &amp;gt;&amp;gt; /etc/eix-sync.conf

# 更新 portage 树
eix-sync

# 用 gnome overlay 提供的文件 unmask 所有 gnome3 相关的包
[ -d /etc/portage/profile/ ] || mkdir -p /etc/portage/profile/
ln -sf /var/lib/layman/gnome/status/portage-configs/package.use.mask.gnome3 /etc/portage/profile/package.use.mask
ln -sf /var/lib/layman/gnome/status/portage-configs/package.unmask.gnome3 /etc/portage/profile/package.unmask


# 升级
emerge -uDav world

# 根据提示进行操作，该mask的mask，该unmask的unmask，gentoo的提示很完善，跟着做就ok了。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在这些步骤里面只有一件事情碰到了一点困难，就是更新 evolution3.0 的时候居然编译失败，错误显示是被 /usr/lib64/libebook-1.2.so 所引用的 /usr/lib64/libcamel-1.2.so.19 找不到。去irc上面求助之后终于解决。&lt;/p&gt;

&lt;p&gt;这是因为 evolution-data-server 有点坏掉了，将它 re-emerge 之后就 ok 了。当然确定是这个包的问题也用到了一个小技巧，就是 &lt;code&gt;qfile libcamel-1.2.so&lt;/code&gt;。这个命令会告诉你包含这个文件的包，是由此而确定到 evolution-data-server 的。&lt;/p&gt;

&lt;p&gt;还有一件事情就是终于弄懂了 emerge 的时候有些 USE flag 被括号包围怎么去除括号： 添加到 &lt;strong&gt;/etc/portage/profile/package.use.mask&lt;/strong&gt; 中，比如像这样：&lt;code&gt;gnome-base/gconf -introspection&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;现在就是还有一个小问题，首先是 pidgin 对 gnome3 的信息机制支持不是特别的好，然后我就想换用 empathy。但是 empathy 的 Accounts 页面显示不出来，并且 &amp;quot;System Settings&amp;quot; 里面的 &amp;quot;Messages and VoIP Management&amp;quot; 页面也是显示空白&lt;del&gt;，暂时还没有解决&lt;/del&gt;。&lt;/p&gt;

&lt;p&gt;&lt;span style=&quot;color: #ff6600;&quot;&gt;&lt;strong&gt;2011-06-15更新：&lt;/strong&gt;&lt;/span&gt;&lt;ins datetime=&quot;2011-06-15T12:23:43+00:00&quot;&gt;解决方法是，给 &amp;quot;net-voip/telepathy-connection-managers&amp;quot; 添加USE FLAGS，然后重新编译这个程序即可&lt;/ins&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;参考文章：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://bone.twbbs.org.tw/blog/archives/2139&quot; title=&quot;Install GNOME3 on Gentoo Linux.&quot;&gt;Install GNOME3 on Gentoo Linux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.gentoo.org/proj/en/overlays/userguide.xml#doc_chap2&quot; title=&quot;Gentoo Overlays: Users&amp;#39; Guide&quot;&gt;Gentoo Overlays: Users&amp;#39; Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
</description>
                <link>http://blog.crhan.com//2011/05/gentoo-%E5%8D%87%E7%BA%A7-gnome3-%E6%97%A9%E5%B7%B2%E8%BF%87%E6%97%B6</link>
                <guid>http://blog.crhan.com//2011/05/gentoo-升级-gnome3-早已过时</guid>
                <pubDate>2011-05-22T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>使用 udev 解决小红点的移动速度问题</title>
                <description>&lt;p&gt;用小黑的朋友们最爱的就是小红点了吧，但是小红点的移动速度对于大多数人来讲确实是有一点慢，那么当然需要将它调教的更灵敏一些咯。本篇先介绍了如何手动修改小红点的两项属性，接着介绍了如何使用 udev rules 来让小红点被检测到的时候自动设置属性。&lt;/p&gt;

&lt;h2&gt;如何设置&lt;/h2&gt;

&lt;p&gt;其实很简单咯，在 &lt;a href=&quot;http://www.thinkwiki.org/wiki/How_to_configure_the_TrackPoint#Sensitivity_.26_Speed&quot; title=&quot;Sensitivity and Speed&quot;&gt;ThinkWiki&lt;/a&gt; 上面就给出最简单的解决方案：&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;echo -n 120 &amp;gt; /sys/devices/platform/i8042/serio1/serio2/speed
echo -n 250 &amp;gt; /sys/devices/platform/i8042/serio1/serio2/sensitivity
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这个操作一定要拥有 root 权限，如果只是 sudo 的话只能提升 echo 的权限，然后就会因为重导向（redirection）的权限不足而无法修改。&lt;/p&gt;

&lt;p&gt;想必你一定不会满足于每次启动都使用 root 权限运行这两行指令，
那么如何自动在启动时候修改这两个值就是一个问题:
放在 rc.local 中？有些人成功了，而大多数人都失败了。
原因大抵是 rc.local 文件执行的时候小红点还没有被检测到，
也就是说上述的对应文件还没有产生，所以所有操作都变成了徒劳。&lt;/p&gt;

&lt;h2&gt;如何启动自动设置？&lt;/h2&gt;

&lt;p&gt;这里我采用的方法是写 &lt;a href=&quot;http://en.wikipedia.org/wiki/Udev&quot; title=&quot;the device manager for the Linux kernel&quot;&gt;udev&lt;/a&gt; 的 rules，
同样在 &lt;a href=&quot;http://www.thinkwiki.org/wiki/How_to_configure_the_TrackPoint#Sensitivity_.26_Speed&quot; title=&quot;Sensitivity and Speed&quot;&gt;ThinkWiki&lt;/a&gt;上也有说明：但是对于我的 udev r151 来说，wiki上的配置并不可行，可能是因为udev的rules规则修改了。
接着我参考了 dsd 的 &lt;a href=&quot;http://www.reactivated.net/writing_udev_rules.html&quot; title=&quot;writing udev rules&quot;&gt;Writing udev rules&lt;/a&gt;，在/etc/udev/rules.d里添加一个规则文件，比如叫做 &lt;strong&gt;10-trackpoint.rules&lt;/strong&gt;。内容：&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;SUBSYSTEM==&amp;quot;serio&amp;quot;, DRIVERS==&amp;quot;psmouse&amp;quot;, ENV{SERIO_TYPE}==&amp;quot;05&amp;quot;, WAIT_FOR=&amp;quot;/sys/devices/platform/i8042/serio1/serio2/sensitivity&amp;quot;, ATTR{sensitivity}=&amp;quot;156&amp;quot;, ATTR{speed}=&amp;quot;255&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这一条的意思是如果匹配到了前面三条，并且 &amp;quot;&lt;strong&gt;wait_for&lt;/strong&gt;&amp;quot; 的文件出现，
那么就给后面两个属性赋值。那么问题就是如何确定前面的匹配？
我的做法是从 Thinkwiki 上找到了小红点对应的位置
&lt;code&gt;/sys/devices/platform/i8042/serio1/serio2&lt;/code&gt; 
然后用 udevadmin 去测试它&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;udevadm test /sys/devices/platform/i8042/serio1/serio2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;他会在最后给出这个设备的信息：&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text language-text&quot; data-lang=&quot;text&quot;&gt;UDEV_LOG=6
DEVPATH=/devices/platform/i8042/serio1/serio2
DRIVER=psmouse
SERIO_TYPE=05
SERIO_PROTO=00
SERIO_ID=00
SERIO_EXTRA=00
MODALIAS=serio:ty05pr00id00ex00
ACTION=add
SUBSYSTEM=serio
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;应该可以看懂了吧？用 ENV 去匹配 SERIO_TYPE 的值用 DRIVER 去匹配 psmouse 等等&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;注意：&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;我的小红点的 &lt;strong&gt;ERIO_TYPE==05&lt;/strong&gt;，而你的不一定。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;我的小红点的 udev 位置是 &lt;strong&gt;/sys/devices/platform/i8042/serio1/serio2&lt;/strong&gt;，而你的也不一定（尽管大多数都是这个）&lt;ins datetime=&quot;2011-09-11T06:33:36+00:00&quot;/&gt;（Sep 11,2011 新增：根据 &lt;a href=&quot;http://renkai.org/2011/08/linux%E4%B8%8Bthinkpad%E5%B0%8F%E7%BA%A2%E7%82%B9%E7%9A%84%E9%80%9F%E5%BA%A6%E5%92%8C%E7%81%B5%E6%95%8F%E5%BA%A6%E8%AE%BE%E7%BD%AE/&quot; title=&quot;linux下thinkpad小红点的速度和灵敏度设置&quot;&gt;这篇文章&lt;/a&gt;，他的小红点就是 &lt;strong&gt;serio4/serio5&lt;/strong&gt;）&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;在 udev r151 规则中的 ATTRS 似乎只可以用来匹配，但是不能用来赋值，如果 ATTRS 用了一个等号的话就会出 &amp;quot;&lt;strong&gt;invalid ATTRS operation&lt;/strong&gt;&amp;quot; 错误提示。而 ATTR 既可以用来赋值也可以用来匹配，比如如果上面改成 &lt;code&gt;ATTR{sensitivity}==&amp;quot;156&amp;quot;&lt;/code&gt;，那就是说如果匹配了这条之后才修改 speed 属性。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
                <link>http://blog.crhan.com//2011/05/%E4%BD%BF%E7%94%A8-udev-%E8%A7%A3%E5%86%B3%E5%B0%8F%E7%BA%A2%E7%82%B9%E7%9A%84%E7%A7%BB%E5%8A%A8%E9%80%9F%E5%BA%A6%E9%97%AE%E9%A2%98</link>
                <guid>http://blog.crhan.com//2011/05/使用-udev-解决小红点的移动速度问题</guid>
                <pubDate>2011-05-04T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>CM7 内置 openvpn 设置</title>
                <description>&lt;p&gt;CM7 早已内置了 openvpn 的功能，但是似乎在官网上面没有写明如何使用，这给我的openvpn 设置带来了一点麻烦，因为我 openvpn 总共有四个文件：ca.crt, client.crt, client.key 和 client.ovpn，我找不到地方能把它们全部添加进 android 的存储凭证中。不过还好 &lt;a href=&quot;https://twitter.com/#!/dickeny/status/65096019158904835&quot;&gt;@dickeny&lt;/a&gt; 给了我一点提示:
可以把几个证书文件打包成 &lt;a href=&quot;http://en.wikipedia.org/wiki/PKCS12&quot;&gt;pkcs12&lt;/a&gt; 格式放入 SD 卡中。接着就找到了篇文章参考: &lt;a href=&quot;http://olorin.info/blog/2010/03/cyanogenmod-openvpn-gui-how-to-load-keyscerts&quot;&gt;CyanogenMod OpenVPN GUI – how to load keys&amp;amp;certs&lt;/a&gt;(&lt;em&gt;已404&lt;/em&gt;),
还有openvpn上的文件功能解释：&lt;a href=&quot;http://openvpn.net/index.php/open-source/documentation/howto.html#pki&quot;&gt;OpenVpn HOWTO&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;简单的描述一下：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;准备好openvpn所需要的四个文件（可以没有其中的client.ovpn）&lt;/li&gt;
&lt;li&gt;制作pkcs12包&lt;br&gt;
&lt;code&gt;openssl pkcs12 -export -in client.crt -inkey client.key  -certfile ca.crt -name nameYouWant -out packName.p12&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;将pkcs12包放进SD卡&lt;/li&gt;
&lt;li&gt;进入 设置 --&amp;gt; 位置和安全 --&amp;gt; 凭证存储 --&amp;gt; 从SD卡安装 --&amp;gt; 选择pkcs12包&lt;/li&gt;&lt;/li&gt;
&lt;li&gt;进入 设置 --&amp;gt; 无线和网络 --&amp;gt; 虚拟专用网设置 --&amp;gt; 添加虚拟专用网DONE!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;一般的openvpn的连接信息（IP地址，端口，协议等）均在ovpn文件里面有描述，或者向vpn提供商询问即可。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;参考文章: &lt;a href=&quot;http://olorin.info/blog/2010/03/cyanogenmod-openvpn-gui-how-to-load-keyscerts&quot;&gt;CyanogenMod OpenVPN GUI – how to load keys&amp;amp;certs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                <link>http://blog.crhan.com//2011/05/cm7-%E5%86%85%E7%BD%AE-openvpn-%E8%AE%BE%E7%BD%AE</link>
                <guid>http://blog.crhan.com//2011/05/cm7-内置-openvpn-设置</guid>
                <pubDate>2011-05-04T00:00:00-07:00</pubDate>
        </item>

        <item>
                <title>Hello Gentoo</title>
                <description>&lt;p&gt;起一个名字总是一件困难的事情，对我而言也特别是如此了，从自己的网名、帐号名到项目名、类名、函数名、实例名，每个都能让我纠结半天，&lt;del&gt;不过这次的名字“捣腾的C”的出现，却并没有这么艰难。当然咯，名字里的“C”并不代表任何语言，因为我并不精通C这一系列语言，所以这只是我常用代号“crhan”中的一个字母罢了。&lt;/del&gt;更没有想到的是我这么快又换了一个名字。这并不是一个什么经过大脑的名字，更换它完全是因为在这个主题中居中这么几个中文字实在是觉得不美观，于是就换了。&lt;/p&gt;

&lt;p&gt;其实每个搞IT的人都是一路捣腾过来的，现在回想起来自己玩电脑的历程果断是不停地捣腾。我六岁时迷上电脑，，当时win3.1尚未上市时捣腾dos，win出来之后就捣腾win以及它上面的各种应用，一直从win3.1，95，98，xp，2000，vista到7。越捣腾越觉得windows实在是难以驾驭，或者说windows的技术又封闭又更新得太快，接着就转向了GNU/Linux。从09年开始用uBuntu9.10，一路磕磕碰碰到去年的10.10，经历过好几次心态的转变。从痛苦（Windows下的XXX软件linux怎么没有！），装B（宣称已经XX个月没有碰Windows了！！），到平静对待（有些软件就是需要在windows下跑）。现在主要工作环境都在Laptop的Linux下面，但是偶尔也用用台机上的win。从去年开始加入HZLUG之后，参加了两次线下活动，对于Linux菜鸟的我来说实在是收获甚大，@yegle 的gentoo介绍也吸引了我，接着就从2011年4月份开始，走上了Gentoo Linux的捣腾道路。&lt;/p&gt;

&lt;p&gt;因为自己实在是一个Linux菜鸟，并且搜索功底并不扎实，碰到问题有时并不能很快的搜到想要的答案，折腾Gentoo一个月之后也算是颇有收获，可能想要把自己学会的一点东西用文字沉淀下来，希望也能给后来的Gentoo使用者一点点帮助吧。&lt;/p&gt;
</description>
                <link>http://blog.crhan.com//2011/05/hello-gentoo</link>
                <guid>http://blog.crhan.com//2011/05/hello-gentoo</guid>
                <pubDate>2011-05-03T00:00:00-07:00</pubDate>
        </item>


</channel>
</rss>
