<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[fortime's Blog]]></title><description><![CDATA[人老了，要记一下平常折腾的东西]]></description><link>https://fortime.fyi/blog/</link><image><url>https://fortime.fyi/blog/favicon.png</url><title>fortime&apos;s Blog</title><link>https://fortime.fyi/blog/</link></image><generator>Ghost 5.80</generator><lastBuildDate>Wed, 08 Apr 2026 08:01:51 GMT</lastBuildDate><atom:link href="https://fortime.fyi/blog/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Use KeePassXC to Store Ssh Private Key]]></title><description><![CDATA[<p>A path traversal vulnerability was discovered in fnOS, which could expose all files on the system&#x2014;including SSH private keys&#x2014;to the Internet. In addition, there are occasional reports of malware stealing SSH private key files. For these reasons, I believe it would be better to secure my</p>]]></description><link>https://fortime.fyi/blog/20260205-01/</link><guid isPermaLink="false">698450a688000b0001f19ca6</guid><category><![CDATA[Linux]]></category><category><![CDATA[安全]]></category><dc:creator><![CDATA[fortime]]></dc:creator><pubDate>Thu, 05 Feb 2026 09:21:26 GMT</pubDate><content:encoded><![CDATA[<p>A path traversal vulnerability was discovered in fnOS, which could expose all files on the system&#x2014;including SSH private keys&#x2014;to the Internet. In addition, there are occasional reports of malware stealing SSH private key files. For these reasons, I believe it would be better to secure my SSH private keys.</p><p>Instead of encrypting my keys directly, I add these keys to KeePassXC, and let it add them to `ssh-agent`. There are some points I want to write down here.</p><h2 id="adding-a-encrypted-key-to-keepassxc">Adding a Encrypted Key to KeePassXC</h2><p>If the key is encrypted, we should provide the password in the password field of the KeePassXC entry.</p><p>According to the behavior of KeePassXC, I think it will decrypt the key, then add it to `ssh-agent`.</p><h2 id="communicating-with-ssh-agent">Communicating with <code>ssh-agent</code></h2><p>To communicate with <code>ssh-agent</code>, we should set <code>SSH_AUTH_SOCK</code>. Instead of setting this variable, I enter the path in the &quot;SSH_AUTH_SOCK override&quot; field and set the <code>IdentityAgent</code> in <code>~/.ssh/config</code>.</p><h2 id="multiple-keys">Multiple Keys</h2><p>I have multiple ssh private keys. By default, ssh will try to use every key to pass the authentication. Too may attempts may fail the login. So, I set <code>IdentitiesOnly</code> to yes. After that, ssh will only use keys specified by <code>IdentityFile</code> and <code>CertificateFile</code>. Working with <code>ssh-agent</code>, there is no private key in the disk. We can set the public key path to <code>IdentityFile</code>.</p><h2 id="required-user-confirmation-when-the-key-is-used">Required User Confirmation When the Key Is Used</h2><p>If I check this box in KeePassXC, ssh will fail with error &quot;agent refused operation&quot;. During the search, I found that I should install one kind of <code>ssh-askpass</code> program. I installed <code>ksshaskpass</code> while I&apos;m using <code>KWin</code>. At first, I think the program will be used once I have installed it. But, I was wrong. I should set <code>SSH_ASKPASS</code> instead. Because I found this variable in the man page of <code>ssh</code>. I set it before running <code>ssh</code>. Of course, it doesn&apos;t work. After an hour fighting with ssh config, KeePassXC setting, private key encryption and decryption, I set the variable before starting <code>ssh_agent</code>. Then, it works. Why doesn&apos;t this variable appear in the man page of <code>ssh_agent</code>. Maybe It&apos;s a chance to earn a PR.</p><p><strong>FINALLY, I EXPRESS MY SINCERE GRATITUDE TO THE LIBRARY FOR PROVIDING FREE WI-FI. THE CONDITIONAL CONNECTION RESET OF SSH CONNECTION MAKE ME LEARN A LOT.</strong></p>]]></content:encoded></item><item><title><![CDATA[A Program inside a Docker Container Access Private Service on Host without Making It Public]]></title><description><![CDATA[<p>Sometimes, we want to make a program running inside a docker container to access a private service which listens to <code>127.0.0.1</code> only on the host. Most of the time, we simplicity make the service listen to <code>0.0.0.0</code>. But this might make the service insecure.</p>]]></description><link>https://fortime.fyi/blog/20260127-01/</link><guid isPermaLink="false">697877ff88000b0001f19bee</guid><category><![CDATA[Linux]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Network]]></category><dc:creator><![CDATA[fortime]]></dc:creator><pubDate>Tue, 27 Jan 2026 09:57:33 GMT</pubDate><content:encoded><![CDATA[<p>Sometimes, we want to make a program running inside a docker container to access a private service which listens to <code>127.0.0.1</code> only on the host. Most of the time, we simplicity make the service listen to <code>0.0.0.0</code>. But this might make the service insecure. Here, I present another way to achieve the task.</p><h1 id="socat"><code>Socat</code></h1><blockquote>Socat is a command line based utility that establishes two bidirectional byte streams and<br>transfers data between them.</blockquote><p>From its man page, it is very powerful. It supports a lot of data streams. Here, I will use it to turn the tcp socket to a unix socket on the host. Inside the docker container, I will use it to turn the unix socket back to a tcp socket.</p><ul><li>Host: <code>socat UNIX-LISTEN:/tmp/tcp2unix.sock,fork TCP:localhost:8080</code></li><li>Container: <code>socat TCP-LISTEN:8080,reuseaddr,fork UNIX-CONNECT:/tmp/tcp2unix.sock</code></li></ul><p>With the socket file mounted into a container, running the command makes the private service accessible inside the container. But it is too invasive. Later, I came up with a method. How about making the <code>socat</code> program a service inside docker network too?</p><h1 id="a-socat-container">A <code>Socat</code> Container</h1><p>There is a <code>socat</code> image already: <a href="https://hub.docker.com/r/alpine/socat?ref=fortime.fyi" rel="noreferrer">docker.io/alpine/socat</a>. I can create a container with this command: <code>podman container run -d --name socat -v /tmp/tcp2unix.sock:/tmp/tcp2unix.sock alpine/socat TCP-LISTEN:8080,reuseaddr,fork UNIX-CONNECT:/tmp/tcp2unix.sock</code>.</p><p>But I can&apos;t access the <code>socat</code> container in another container. After running <code>podman network inspect podman</code>, I found both containers are not in the network. Unlike <code>docker</code>, <code>podman</code> won&apos;t add the container to the default network by default. </p><p>Adding <code>--network podman</code> to the command line, I could access the <code>socat</code> service after that. But I couldn&apos;tt access it with the container name. After inspecting the <code>podman</code> network, I found dns is disabled.</p><p>So, I created a new network.</p><pre><code>podman network create podman-proxy

podman container run -d --name socat --network podman-proxy -v /tmp/tcp2unix.sock:/tmp/tcp2unix.sock alpine/socat TCP-LISTEN:8080,reuseaddr,fork UNIX-CONNECT:/tmp/tcp2unix.sock</code></pre><h1 id="a-socat-systemd-service">A <code>Socat</code> Systemd Service</h1><p>To simplify running the command on the host, I create a systemd service.</p><pre><code># /etc/systemd/system/tcp2unix@.service
[Unit]
Description=Relay tcp socket with a unix socket
After=network.target

[Service]
ExecStart=socat UNIX-LISTEN:/run/tcp2unix/tcp2unix-%i.sock,fork TCP:localhost:%i
ExecReload=/usr/bin/kill -USR1 $MAINPID
PIDFile=/run/tcp2unix/tcp2unix-%i.pid
PrivateDevices=yes
RuntimeDirectory=tcp2unix
RuntimeDirectoryMode=0755

[Install]
WantedBy=multi-user.target</code></pre><p>Unlike the ad-hoc command, after starting the service, the private service can&apos;t be accessed. The permission of the socket file is &quot;6755&quot; and it&apos;s owned by root. So the file can&apos;t be accessed inside the container. In the man page, I found I can add <code>umask</code> to change the permission.</p><pre><code># /etc/systemd/system/tcp2unix@.service
[Unit]
Description=Relay tcp socket with a unix socket
After=network.target

[Service]
ExecStart=socat UNIX-LISTEN:/run/tcp2unix/tcp2unix-%i.sock,fork,umask=0000 TCP:localhost:%i
ExecReload=/usr/bin/kill -USR1 $MAINPID
PIDFile=/run/tcp2unix/tcp2unix-%i.pid
PrivateDevices=yes
RuntimeDirectory=tcp2unix
RuntimeDirectoryMode=0755

[Install]
WantedBy=multi-user.target</code></pre><p>After that, container inside the <code>podman-proxy</code> network can finally access the private service.</p>]]></content:encoded></item><item><title><![CDATA[Playing D2R on the OneXPlayer 2 Pro with Arch Linux]]></title><description><![CDATA[<p>D2R has already been out for four years. I used to play it on the Nintendo Switch, but recently I noticed a discount on the PC version, so I bought a copy for PC. Thanks to Wine, Valve, and the Steam Deck, playing games on Linux has become much easier.</p>]]></description><link>https://fortime.fyi/blog/20251015-01/</link><guid isPermaLink="false">68ef37e588000b0001f19ae3</guid><category><![CDATA[Linux]]></category><category><![CDATA[Game]]></category><dc:creator><![CDATA[fortime]]></dc:creator><pubDate>Fri, 17 Oct 2025 07:48:18 GMT</pubDate><content:encoded><![CDATA[<p>D2R has already been out for four years. I used to play it on the Nintendo Switch, but recently I noticed a discount on the PC version, so I bought a copy for PC. Thanks to Wine, Valve, and the Steam Deck, playing games on Linux has become much easier. There are many articles about how to play D2R on a Steam Deck. However, since I&#x2019;m using <strong><em>KWin with Wayland</em></strong>, I had a few issues to overcome.</p><h1 id="battlenet">Battle.net</h1><p>Unlike Diablo IV, we can&apos;t run D2R directly through Steam. We need to launch <code>Battle.net</code> first. There are two issues here. </p><ol><li>we can&apos;t add non-steam game in Big-Picture mode. </li><li>`Battle.net` has recently updated its server certificate, which is now signed by a new root certificate. To communicate with the server properly, we should use Proton 10 or later. Otherwise, running `Battle.net` will show <em>BLZBNTBNA00000005</em> error.</li></ol><p><strong>There are two important points to note:</strong></p><ol><li>If the <code>Battle.net</code> installer is not run with Proton 10 or later, switching to Proton 10 afterward will not resolve the issue &#x2014; a complete installation is required.</li><li>Before installing D2R, you should change the installation path in the <code>Battle.net</code> settings to a location outside the Steam folder. So that we don&apos;t need to install D2R again if we remove and install `Battle.net` again.</li></ol><h2 id="windowed-of-battlenet">Windowed of Battle.net</h2><p><code>Battle.net</code> may be run in windowed mode, a titlebar and frame is added by KWin. It will make the Battle.net running in a wrong resolution, and the play button won&apos;t be shown. We can add a KWin Window Rule to force <em>No titlebar and frame</em> and the minimum size of the window.</p><h1 id="controller">Controller</h1><p>D2R works out of the box with mouse and keyboard. But if you want to play it with a controller, you should enable <code>Steam Input</code>. After the <em>press any button</em> screen is passed, D2R can be controlled by a controller. We can touch the screen, switch the controller to mouse mode, <s>or map a button of the controller to send a input event using keyd</s>(There is no extra key in OneXPlayer 2 Pro controller. The keyboard mode switch key is hardware controlled, and the system can&apos;t read that key event.) to pass the <em>press any button</em> screen. It looks like D2R will show <em>press A button</em> screen instead of <em>press any button</em> screen if we don&apos;t attach any mouse. We can use a controller to pass that screen.</p><h2 id="enable-steam-input">Enable <code>Steam Input</code></h2><p><code>Steam Input</code> uses <code>uinput</code> to handle the event from controllers. By default, the user running steam doesn&apos;t have enough permission. We need to install <a href="https://codeberg.org/fabiscafe/game-devices-udev?ref=fortime.fyi"><code>game-devices-udev</code></a>` to change the permission of the device.</p><p>In Arch Linux, there is an AUR package, install it by running: <code>paru -S game-devices-udev</code>, change <code>paru</code> to the aur helper you are using.</p><h2 id="cursor-in-inventory-ui-with-controller">Cursor in Inventory UI with Controller</h2><p>In the inventory ui, the cursor might be not moved correctly. The position of the cursor is moved, but the cursor surface is not. We can run D2R with <code>gamescope</code> to solve this problem.</p><ol><li>Install <code>gamescope</code>. Because my steam is installed by <code>flatpak</code>, so I should install <code>gamescope</code> by <code>flatpak</code> too: <code>flatpak --user install org.freedesktop.Platform.VulkanLayer.gamescope</code>, choose the same version of <code>org.freedesktop.Platform</code> needed by steam.</li><li>Add the <code>bin</code> folder of gamescope to <code>PATH</code> of steam in <code>flatpak</code> settings, the <code>bin</code> folder is <code>/usr/lib/extensions/vulkan/gamescope/bin</code>.</li><li>Set the launch option</li></ol><pre><code># Ghost editor replace -- with &#x2013;
gamescope --hide-cursor-delay -1 -- %command%
</code></pre>
<p>      The cursor surface might not be shown at first, we can move the mouse to make it appear.</p><h2 id="gamescope-error-popups"><strong>Gamescope Error Popups</strong></h2><p>Launching D2R, there are two gamescope error popup windows: <code>CreateSwapchainKHR</code> and <code>QueuePresentKHR</code>. We can just click OK to pass them. Don&apos;t click Cancel, otherwise, D2R will display a black screen. <s>When I disable WSI layer with <code>ENABLE_GAMESCOPE_WSI=0</code>, those popups are gone. The launch option becomes: <code>ENABLE_GAMESCOPE_WSI=0 gamescope --hide-cursor-delay -1 &#x2013; %command%</code>.</s> When WSI layer is disabled, the cursor in inventory ui becomes laggy.</p><h1 id="unsolved">Unsolved</h1><h2 id="steam-freeze">Steam Freeze</h2><p>This is an issue of steam. If steam lost-focus(may) or the screen is locked(must), steam will freeze. the game might lose control or be frozen. I&apos;m not sure whether disabling <code>Steam Input</code> will prevent Steam freeze from affecting the game. I&apos;m also not looking into the lost-focus issue &#x2014; it seems to be related to the KWin compositor&apos;s behavior during the game, such as when pressing <code>Alt+Tab</code>.</p><p>It looks like even I don&apos;t open steam, D2R may freeze too. Not sure if `Battle.net` launcher is the cause in this case.</p><p><strong><em>What a heartbeat moment &#x2014; playing D2R Hardcore mode and experiencing an unexpected freeze!</em></strong></p><h1 id="running-d2r-without-opening-steam"><strong>Running D2R without Opening Steam</strong></h1><p>I think I can run D2R directly to avoid the steam freeze problem. I used <code>pstree -plas $PID</code> to find out the parent process running <code>Battle.net</code>, and used <code>cat /proc/$PID/environ | tr &apos;\0&apos; &apos;\n&apos;</code> to print all its environment variables. I kept almost all variables starting with <code>STEAM</code>. After that, I can run D2R with <code>Proton</code> directly. </p><h2 id="controller-mapping">Controller Mapping</h2><p>I found that the controller works out of box opening outside of steam. It looks like steam disables my controller using a <code>SDL</code> environment variable. I am a Nintendo controller guy. Using Xbox controller layout is bit inconvenient for me. Thanks for <code>SDL</code>, I can use a Nintendo controller layout. Here is my controller mapping edited via <code>AntiMicroX</code>:</p><pre><code class="language-bash">SDL_GAMECONTROLLERCONFIG=&quot;030081b85e0400008e020000080100001118654,Atari Xbox 360 Game Controller,platform:Linux,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,guide:b8,leftshoulder:b4,rightshoulder:b5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,&quot;
</code></pre><h2 id="gamescope">Gamescope</h2><p>To my surprise, if I run D2R with <code>gamescope</code> nothing will show but the <code>Battle.net</code> is actual running. Comparing the processes between steam-run and direct-run, I found the proton process executed by <code>gamescope</code> will create a child process and exit in direct-run. So, <code>gamescope</code> thinks there is no application running. I finally wrote a script to get the flatpak instance id with (<code>--instance-id-fd</code>) and monitor that instance id with (<code>flatpak ps</code>) to keep the process running.</p><p>Then comes the second surprise. Unlike steam-run, all windows (<code>Battle.net</code> launcher and D2R) are running inside a window. Actually, the direct-run behavior is what I would expect based on my understanding. It is what a nested compositor should do. But the scale of the cursor is wrong and I must manually set the resolution. I really want the steam-run behavior. Comparing the environment variables, I found <code>Display</code> is set to <code>:0</code> in steam-run and <code>Display</code> is set to <code>:1</code>. So, all windows of steam-run is drawing directly by <code>Kwin</code>. <strong><em>But why does the cursor in the inventory ui affects?</em></strong></p><p>During the search, I found that <code>gamescope</code> actually doesn&apos;t work with the builtin <code>Proton</code>. I should install <code>com.valvesoftware.Steam.CompatibilityTool.Proton-GE</code>. After that, all windows of steam-run will run inside the window of <code>gamescope</code>. And those error popups disappear. But the cursor in the inventory ui doesn&apos;t move. After adding `--force-grab-cursor`, the cursor moves but it twinkles.</p><h2 id="scripts">Scripts</h2><p>Here are my scripts:</p><ul><li>run-steam-battlenet</li></ul><pre><code class="language-bash">#! /usr/bin/env bash

set_envs() {
    set -a
    STEAM_BASE_FOLDER=$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam
    STEAM_CLIENT_CONFIG_FILE=$HOME/.var/app/com.valvesoftware.Steam/.local/share/steam.cfg
    STEAM_COMPAT_APP_ID=0
    STEAM_COMPAT_CLIENT_INSTALL_PATH=$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam
    STEAM_COMPAT_DATA_PATH=$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/compatdata/$EX_STEAM_GAME_ID
    STEAM_COMPAT_LIBRARY_PATHS=$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps
    STEAM_COMPAT_MEDIA_PATH=$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/shadercache/$EX_STEAM_GAME_ID/fozmediav1
    STEAM_COMPAT_MOUNTS=&quot;$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/Proton - Experimental&quot;:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/SteamLinuxRuntime_sniper
    STEAM_COMPAT_PROTON=1
    STEAM_COMPAT_TOOL_PATHS=&quot;$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/Proton - Experimental&quot;:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/SteamLinuxRuntime_sniper
    STEAM_COMPAT_TRANSCODED_MEDIA_PATH=$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/shadercache/$EX_STEAM_GAME_ID
    STEAM_EXTRA_COMPAT_TOOLS_PATHS=/app/share/steam/compatibilitytools.d:/app/utils/share/steam/compatibilitytools.d
    STEAM_FOSSILIZE_DUMP_PATH=$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/shadercache/$EX_STEAM_GAME_ID/fozpipelinesv6/steamapprun_pipeline_cache
    STEAM_RUNTIME_LIBRARY_PATH=$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_32/steam-runtime/pinned_libs_32:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_32/steam-runtime/pinned_libs_64:/app/lib/i386-linux-gnu/GL/default/lib:/app/lib32:/app/lib/i386-linux-gnu:/lib64:/app/lib:/usr/lib/x86_64-linux-gnu/GL/default/lib:/usr/lib/x86_64-linux-gnu/openh264/extra:/usr/lib/x86_64-linux-gnu:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_32/steam-runtime/lib/i386-linux-gnu:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/i386-linux-gnu:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_32/steam-runtime/lib/x86_64-linux-gnu:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/x86_64-linux-gnu:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_32/steam-runtime/lib:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib
    STEAM_ZENITY=/usr/bin/zenity
    DISPLAY=${DISPLAY_BEFORE_GAMESCOPE:-$DISPLAY}
    PROTON_ENABLE_WAYLAND=1
    set +a
}

set_controller_mapping() {
    set -a
    SDL_GAMECONTROLLERCONFIG=&quot;030081b85e0400008e020000080100001118654,Atari Xbox 360 Game Controller,platform:Linux,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,guide:b8,leftshoulder:b4,rightshoulder:b5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,&quot;
    set +a
}

start_bottle_net() {
    # Create a temp file for storing the instance id
    tmpfile_instance_id=$(mktemp /tmp/run-steam-battlenet-instance-id.XXXXXX)
    echo &quot;Steam instance id will be stored at $tmpfile_instance_id&quot;

    exec 3&gt;&quot;$tmpfile_instance_id&quot;

    /usr/bin/flatpak run --instance-id-fd=3 --branch=stable --arch=x86_64 --command=&quot;$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/Proton - Experimental/proton&quot; com.valvesoftware.Steam waitforexitandrun &quot;$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/compatdata/$EX_STEAM_GAME_ID/pfx/drive_c/Program Files (x86)/Battle.net/Battle.net Launcher.exe&quot;

    exec 3&gt;&amp;-
    set +e

    read instance_id &lt; &quot;$tmpfile_instance_id&quot;
    echo &quot;Running as Battle.net as Steam Instance[$instance_id]&quot;

    while flatpak ps --columns=instance | grep -qx &quot;$instance_id&quot;; do
        sleep 1
    done
}

delete_file() {
    if [ -n &quot;$1&quot; ]
    then
        if [ -f &quot;$1&quot; ]
        then
            rm &quot;$1&quot;
        fi
    fi
}

clean_up() {
    delete_file &quot;$tmpfile_instance_id&quot;
}

EX_STEAM_GAME_ID=3494142050

set -e
tmpfile_instance_id=&quot;&quot;
trap &apos;clean_up&apos; EXIT

set_envs
set_controller_mapping
start_bottle_net
</code></pre><ul><li><s>run-steam-battlenet-with-gamescope</s>. Since kwin 6.5.2, we can run D2R under Wayland. The cursor works almost out of box in the inventory ui. It disappears if we move the cursor too fast. In kwin 6.5.1, the cursor won&apos;t disappear after we close the inventory ui.</li></ul><pre><code class="language-bash">#! /usr/bin/env bash

export DISPLAY_BEFORE_GAMESCOPE=&quot;$DISPLAY&quot;

/usr/bin/gamescope --hide-cursor-delay=-1 -- &quot;$(dirname $0)/run-steam-battlenet&quot;
</code></pre><h1 id="maybe-perfect-solution-20260129">Maybe Perfect Solution (20260129)</h1><p>During the Christmas sale, I bought a second copy of D2R. However, when two instances of D2R are running at the same time, the cursor stops working properly.</p><p>Unlike the cursor controlled by a mouse, which is drawn directly by the Wayland compositor, the cursor controlled by a game controller is rendered by the game itself via the Wayland protocol. My guess is that when two applications call the relevant API simultaneously, they interfere with each other, causing the cursor to flicker or malfunction.</p><p>Based on this assumption, a possible solution would be to run each game instance in its own standalone compositor. Yes, <code>gamescope</code> is back again.</p><h2 id="run-a-game-with-gamescope-xwayland">Run A Game with <code>gamescope</code> XWayland</h2><p>In the last testing, we replace the <code>gamescope</code> XWayland socket with the one created by <code>KWin</code>. This also causes the &quot;Error Popups&quot;. But this time, I should run the game inside the one created by <code>gamescope</code>. The cursor&apos;s behavior isn&apos;t perfect.</p><p>What if I run <code>gamescope</code> inside flatpak. In the previous testing, the <code>gamescope</code> running inside flatpak doesn&apos;t work. Proton exits without waiting the game to start. It makes <code>gamescope</code> think that there is no application. This also happens in the <code>gamescope</code> outside flatpak, and I created a script to wait the game to exit. Why is proton run by steam different? After reading the source code of proton, I need to set <code>SteamGameId</code> to output logs. Right after that, the game suddenly shows, and the cursor behaves more better.</p><h2 id="controller-events-effect-even-in-a-unfocused-window">Controller Events Effect Even in a Unfocused Window</h2><p>I opened two instances of D2R. To my surprise, both characters move when I move the stick. I found this <a href="https://github.com/ValveSoftware/gamescope/issues/636?ref=fortime.fyi" rel="noreferrer">issue</a>. The game would be unfocused inside <code>gamescope</code> XWayland, and it will handle controller events.</p><p>I remember that steam uses <code>SDL_</code> environment variables to deal with controller. After some research, I found <code>SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT</code>. I can use this to make the game to handle controller events from specified controllers only. What if I use <code>uinput</code> to create different controllers for different games? Finally, I created <a href="https://github.com/fortime/non-steam-launcher?ref=fortime.fyi" rel="noreferrer">Non-Steam Launcher</a>.</p><p>Here are my script:</p><pre><code class="language-bash">#! /usr/bin/env bash

set_envs() {
    set -a
    # SteamGameId is needed otherwise proton will open nothing
    SteamGameId=$EX_STEAM_GAME_ID
    STEAM_BASE_FOLDER=$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam
    STEAM_CLIENT_CONFIG_FILE=$HOME/.var/app/com.valvesoftware.Steam/.local/share/steam.cfg
    STEAM_COMPAT_APP_ID=0
    STEAM_COMPAT_CLIENT_INSTALL_PATH=$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam
    STEAM_COMPAT_DATA_PATH=$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/compatdata/$EX_STEAM_GAME_ID
    STEAM_COMPAT_LIBRARY_PATHS=$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps
    STEAM_COMPAT_MEDIA_PATH=$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/shadercache/$EX_STEAM_GAME_ID/fozmediav1
    STEAM_COMPAT_MOUNTS=&quot;$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/Proton - Experimental&quot;:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/SteamLinuxRuntime_sniper
    STEAM_COMPAT_PROTON=1
    STEAM_COMPAT_TOOL_PATHS=&quot;$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/Proton - Experimental&quot;:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/SteamLinuxRuntime_sniper
    STEAM_COMPAT_TRANSCODED_MEDIA_PATH=$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/shadercache/$EX_STEAM_GAME_ID
    STEAM_EXTRA_COMPAT_TOOLS_PATHS=/app/share/steam/compatibilitytools.d:/app/utils/share/steam/compatibilitytools.d
    STEAM_FOSSILIZE_DUMP_PATH=$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/shadercache/$EX_STEAM_GAME_ID/fozpipelinesv6/steamapprun_pipeline_cache
    STEAM_RUNTIME_LIBRARY_PATH=$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_32/steam-runtime/pinned_libs_32:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_32/steam-runtime/pinned_libs_64:/app/lib/i386-linux-gnu/GL/default/lib:/app/lib32:/app/lib/i386-linux-gnu:/lib64:/app/lib:/usr/lib/x86_64-linux-gnu/GL/default/lib:/usr/lib/x86_64-linux-gnu/openh264/extra:/usr/lib/x86_64-linux-gnu:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_32/steam-runtime/lib/i386-linux-gnu:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/i386-linux-gnu:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_32/steam-runtime/lib/x86_64-linux-gnu:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/x86_64-linux-gnu:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_32/steam-runtime/lib:$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib
    STEAM_ZENITY=/usr/bin/zenity
    #PROTON_LOG=1
    #PROTON_LOG_DIR=/home/fortime/ws/game
    #PROTON_ENABLE_WAYLAND=1
    set +a
}

set_controller_mapping() {
    local guid vendor_product
    guid=&quot;$1&quot;
    vendor_product=&quot;$2&quot;

    set -a
    # it isn&apos;t guid, it accepts vid/pid pair
    SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT=&quot;${vendor_product}&quot;
    SDL_GAMECONTROLLERCONFIG=&quot;
${guid},Microsoft Xbox 360,platform:Linux,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,guide:b8,leftshoulder:b4,rightshoulder:b5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,
&quot;
    set +a
}

start_bottle_net() {
    /usr/bin/flatpak run --branch=stable --arch=x86_64 --command=&quot;/usr/lib/extensions/vulkan/gamescope/bin/gamescope&quot; com.valvesoftware.Steam --backend wayland --force-grab-cursor -b -W 2560 -H 1520 -s 1.5 -- &quot;/app/share/steam/compatibilitytools.d/Proton-GE/proton&quot; waitforexitandrun &quot;$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/compatdata/$EX_STEAM_GAME_ID/pfx/drive_c/Program Files (x86)/Battle.net/Battle.net Launcher.exe&quot;
}

if [ &quot;$EX_STEAM_INSIDE_INHIBIT&quot; != &quot;yes&quot; ]
then
    export EX_STEAM_INSIDE_INHIBIT=yes
    systemd-inhibit --what=idle:sleep --who=&quot;Battle.net&quot; --why=&quot;Playing games&quot; $0 &quot;$@&quot;
else
    EX_STEAM_GAME_ID=${1}

    set -e
    set_envs

    new_controller_resp=$(busctl --user --json=pretty call fyi.fortime.NonSteamLauncher /fyi/fortime/NonSteamLauncher/Controller fyi.fortime.NonSteamLauncher.Controller1 NewController)
    controller_slot=$(echo $new_controller_resp | jq -r &apos;.data[0]&apos;)
    controller_guid=$(echo $new_controller_resp | jq -r &apos;.data[1]&apos;)
    controller_vendor_product=$(echo $new_controller_resp | jq -r &apos;.data[2]&apos;)
    set_controller_mapping &quot;$controller_guid&quot; &quot;$controller_vendor_product&quot;

    # Use a default config in case of the log level in the user config is debug
    old_manual_mode=$(FCITX5_OSK_CONFIG=/tmp/null fcitx5-osk get-property manual_mode)
    fcitx5-osk set-property manual_mode true
    set +e

    # Not exit even it is failed, so the controller is always removed
    start_bottle_net

    # remove controller
    busctl --user call fyi.fortime.NonSteamLauncher /fyi/fortime/NonSteamLauncher/Controller fyi.fortime.NonSteamLauncher.Controller1 RemoveController y $controller_slot

    fcitx5-osk set-property manual_mode $old_manual_mode

fi</code></pre><p>It will create a virtual controller via dbus api and set <code>SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT</code>.</p><h2 id="todo-gamescope-doesnt-support-wayland-text-input-protocol">TODO Gamescope doesn&apos;t Support Wayland Text Input Protocol</h2>]]></content:encoded></item><item><title><![CDATA[Kde Screen Locker with TOTP]]></title><description><![CDATA[<h1 id="intro">Intro</h1><p>Recently, I&apos;ve been using my portable PC in public places. It&apos;s not safe to enter my password in such environments, so I came up with a solution that doesn&apos;t rely on biometric login: TOTP (Time-based One-Time Password).</p><h2 id="step">Step</h2><ol><li>Install <code>oath-toolkit</code> to support totp</li></ol>]]></description><link>https://fortime.fyi/blog/20250717-01/</link><guid isPermaLink="false">68789d3788000b0001f19a26</guid><category><![CDATA[Linux]]></category><dc:creator><![CDATA[fortime]]></dc:creator><pubDate>Wed, 23 Jul 2025 09:54:37 GMT</pubDate><content:encoded><![CDATA[<h1 id="intro">Intro</h1><p>Recently, I&apos;ve been using my portable PC in public places. It&apos;s not safe to enter my password in such environments, so I came up with a solution that doesn&apos;t rely on biometric login: TOTP (Time-based One-Time Password).</p><h2 id="step">Step</h2><ol><li>Install <code>oath-toolkit</code> to support totp</li><li>Setup up the TOTP</li></ol><p>Generate a seed for the totp.</p><pre><code class="language-bash">openssl rand -hex 10
</code></pre><p>Create a config file for <code>oath-toolkit</code> at <code>${HOME}/.config/oath_toolkit</code>.</p><pre><code># Option User Prefix Seed
HOTP/T30/6 ${REPLACE WITH THE USER NAME} - ${REPLACE WITH THE OUTPUT OF OPENSSL}
</code></pre><p>Change the permission of the config file.</p><pre><code class="language-bash">chmod 600 &quot;${HOME}/.config/oath_toolkit&quot;
</code></pre><ol start="3"><li>Setup the pam config for kde screen locker</li></ol><p>Insert following lines before the first `auth` line of file <code>/etc/pam.d/kde</code>.</p><pre><code># Try the password, if it is a valid totp, pass the auth
auth     sufficient                  pam_oath.so              try_first_pass usersfile=${HOME}/.config/oath_toolkit window=30 digits=6</code></pre><ul><li><code>sufficient</code> means it will skip following <code>auth</code>s if this <code>auth</code> passed. It will continue the <code>auth</code> process if it failed.</li><li>The <code>try_first_pass</code> option means <code>pam_oath.so</code> will use the previously entered password without prompting for it again.</li><li>If the password is longer than 6 characters, <code>pam_oath.so</code> will treat the last 6 characters as the TOTP code. It will then remove those 6 characters and use the remaining part as the password for the subsequent authentication process. So if you want to log in using your password, you can enter your password followed by any 6 characters.</li></ul><h2 id="work-with-sddm">Work with SDDM</h2><p>In my design, the login of <code>sddm</code> must use a password. So I don&apos;t change the pam file of <code>sddm</code>. If you want to use totp in <code>sddm</code>, you should:</p><ol><li>Create a separate config file for <code>oath-toolkit</code> which can be accessed by the user of <code>sddm</code>. </li><li>Update the pam file of <code>sddm</code>: <code>/etc/pam.d/sddm</code>, and use the correct <code>usersfile</code>.</li></ol><h2 id="permission-issue">Permission Issue</h2><p>During my research, pam has the root permission. But if the config file set to read-only for root, the <code>pam_oath.so</code> can&apos;t read the config file.</p><h1 id="reference">Reference</h1><ul><li><a href="https://wiki.archlinux.org/title/Pam_oath?ref=fortime.fyi">https://wiki.archlinux.org/title/Pam_oath</a></li></ul>]]></content:encoded></item><item><title><![CDATA[Internal Speaker Of OneXPlayer 2 Pro (8840) In Linux]]></title><description><![CDATA[<h1 id="background">Background</h1>
<p>Internal speaker of OneXPlayer 2 Pro doesn&apos;t work. There is a long <a href="https://github.com/ChimeraOS/chimeraos/issues/742?ref=fortime.fyi">thread</a> discussing this issue. From this thread, it seems that the AMP isn&apos;t initialized correctly. Someone found that the speaker works after rebooting from Windows. So I came out an idea: What if</p>]]></description><link>https://fortime.fyi/blog/20240926-01/</link><guid isPermaLink="false">66f4ee4e8a10d300011ecc65</guid><category><![CDATA[Linux]]></category><dc:creator><![CDATA[fortime]]></dc:creator><pubDate>Tue, 22 Oct 2024 05:15:46 GMT</pubDate><content:encoded><![CDATA[<h1 id="background">Background</h1>
<p>Internal speaker of OneXPlayer 2 Pro doesn&apos;t work. There is a long <a href="https://github.com/ChimeraOS/chimeraos/issues/742?ref=fortime.fyi">thread</a> discussing this issue. From this thread, it seems that the AMP isn&apos;t initialized correctly. Someone found that the speaker works after rebooting from Windows. So I came out an idea: What if initializing the AMP with Windows in a virtual machine.</p>
<h1 id="pci-passthrough">PCI passthrough</h1>
<p>To let Windows initialize the AMP, I must let the virtual machine access the hardware directly. This will need <code>VFIO</code> driver in the <code>Linux</code> kernel. I unbind the hardware from <code>snd_hda_intel</code> driver, then bind the hardware to <code>vfio-pci</code> driver. After that, the virtual machine can use the hardware.</p>
<h2 id="pci-slot-name">PCI slot name</h2>
<p>Firstly, I should find out the name of the pci slot connected by the audio device.</p>
<pre><code class="language-bash"># lspci -D: always print domain numbers
# grep -i audio: show only audio related hardware
# grep -v Radeon: remove the hdmi audio device
lspci -D|grep -i audio|grep -v Radeon
</code></pre>
<p>The output is like:</p>
<pre><code>0000:64:00.6 Audio device: Advanced Micro Devices, Inc. [AMD] Family 17h/19h HD Audio Controller
</code></pre>
<p><code>0000:64:00.6</code> is the pci slot name.</p>
<h2 id="bindunbind-vfio-pci-driver">Bind/unbind <code>vfio-pci</code> driver</h2>
<ul>
<li>bind</li>
</ul>
<pre><code class="language-bash">echo &apos;0000:64:00.6&apos; | sudo tee /sys/bus/pci/drivers/vfio-pci/unbind
echo &apos;0000:64:00.6&apos; | sudo tee /sys/bus/pci/drivers/snd_hda_intel/bind
</code></pre>
<ul>
<li>unbind</li>
</ul>
<pre><code class="language-bash">echo &apos;0000:64:00.6&apos; | sudo tee /sys/bus/pci/drivers/snd_hda_intel/unbind
echo &apos;0000:64:00.6&apos; | sudo tee /sys/bus/pci/drivers/vfio-pci/bind
</code></pre>
<h1 id="create-a-virtual-device-with-the-windows-partition">Create a virtual device with the <code>Windows</code> partition</h1>
<p>In the previous research, I found that the internal speaker won&apos;t work in a new <code>Windows</code> too. But I have the original <code>Windows</code> partition. Each time I installed <code>Linux</code> on a new machine, I would keep the <code>Windows</code> partition not touching. After some researches, I finally start a vm with the <code>Windows</code> partition. Avoiding to mass the original <code>Windows</code> partition, I used <code>dd</code> to clone the partition.</p>
<pre><code class="language-bash"># I have shrinked the size of the partition to 89GB.
dd if=/dev/nvme0n1p3 of=/var/cache/oxp2p-win11.img
</code></pre>
<h2 id="keep-the-partition-layout">Keep the partition layout</h2>
<p>I kept the partition layout of the <code>Windows</code> partition and partitions before it to avoid any implicit issue.</p>
<ul>
<li>Use the following command to print the layout.</li>
</ul>
<pre><code class="language-bash">fdisk -l /dev/nvme0n1
</code></pre>
<p>The output is like:</p>
<pre><code>/dev/nvme0n1p1       2048     206847     204800   100M EFI System
/dev/nvme0n1p2     206848     468991     262144   128M Microsoft reserved
/dev/nvme0n1p3     468992  187191295  186722304    89G Microsoft basic data
</code></pre>
<p>A sector equals to 512 bytes.</p>
<ul>
<li>Create an image to simulate those two partitions before the <code>Windows</code> partition.</li>
</ul>
<pre><code class="language-bash">dd if=/dev/zero of=part1.img bs=512 count=468992
</code></pre>
<ul>
<li>Create an image for margin after the <code>Windows</code> partition.</li>
</ul>
<pre><code class="language-bash">dd if=/dev/zero of=part3.img bs=1M count=100
</code></pre>
<p>I don&apos;t know why. But it seems there should be some addtitional spaces after the <code>Windows</code> partition.</p>
<ul>
<li>Create a device mapper.</li>
</ul>
<p>Use <code>losetup</code> to set the image file as a block device, and then use <code>dmsetup</code> to create a mapper.</p>
<pre><code class="language-bash">loop1_file=part1.img
loop2_file=/var/cache/oxp2p-win11.img
loop3_file=part3.img

disk_name=oxp2p-win11

loop1=$(sudo losetup -f)
sudo losetup $loop1 $loop1_file
loop1_size=$(sudo blockdev --getsz $loop1)
loop2=$(sudo losetup -f)
sudo losetup $loop2 $loop2_file
loop2_size=$(sudo blockdev --getsz $loop2)
loop3=$(sudo losetup -f)
sudo losetup $loop3 $loop3_file
loop3_size=$(sudo blockdev --getsz $loop3)


sudo dmsetup create $disk_name &lt;&lt;EOF
0 $loop1_size linear $loop1 0
$loop1_size $loop2_size linear $loop2 0
$(expr $loop1_size + $loop2_size) $loop3_size linear $loop3 0
EOF
</code></pre>
<h1 id="run-with-qemu">Run with qemu</h1>
<ul>
<li>Create an empty file for persisting UEFI variables</li>
</ul>
<pre><code class="language-bash">dd if=/dev/zero conv=sync bs=1M count=4 of=ovmf_vars.fd
</code></pre>
<ul>
<li>Run with qemu</li>
</ul>
<pre><code class="language-bash"># The path of /usr/share/edk2/x64/OVMF_CODE.4m.fd will be different in different distro.
# This should be run after binding the `vfio-pci` driver.
qemu-system-x86_64 -m 4096M -smp 4 -accel kvm -cpu host -drive file=/usr/share/edk2/x64/OVMF_CODE.4m.fd,if=pflash,format=raw,readonly=on -drive file=./ovmf_vars.fd,if=pflash,format=raw -drive file=/dev/mapper/oxp2p-win11,media=disk,format=
raw -device usb-ehci -device usb-kbd -device usb-mouse -device usb-tablet -usb -monitor stdio -device vfio-pci,host=64:00.6
</code></pre>
<ul>
<li>Force closing the vm after the sound came out in the login screen.</li>
<li>Unbind the <code>vfio-pci</code> driver.</li>
</ul>
<h1 id="advancehda-verb">Advance(hda-verb)</h1>
<p>TO BE CONTINUED</p>
<h1 id="reference">Reference</h1>
<ul>
<li><a href="https://jianmin.dev/2020/jul/19/boot-your-windows-partition-from-linux-using-kvm/?ref=fortime.fyi">https://jianmin.dev/2020/jul/19/boot-your-windows-partition-from-linux-using-kvm/</a></li>
<li><a href="https://www.qemu.org/docs/master/devel/vfio-iommufd.html?ref=fortime.fyi">https://www.qemu.org/docs/master/devel/vfio-iommufd.html</a></li>
<li><a href="https://null-src.com/posts/qemu-vfio-pci/?ref=fortime.fyi">https://null-src.com/posts/qemu-vfio-pci/</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[OneXPlayer 2 Pro (8840) With Arch Linux]]></title><description><![CDATA[<h1 id="installation-with-disk-encryption">Installation with disk encryption</h1>
<h2 id="partition-with-encryption">Partition with encryption</h2>
<p>In case of after-sales service, I kept windows partitions. I deleted <code>D:</code> partition and shrink <code>C:</code> partition. I create two partitions: <code>/boot</code> and <code>/</code>. I will use btrfs on <code>/</code>, so instead of using paritions, I will use subvolume for /home.</p>
<p>Encryption was applied exclusively</p>]]></description><link>https://fortime.fyi/blog/20240726-01/</link><guid isPermaLink="false">6681911c8a10d300011ec91b</guid><category><![CDATA[Linux]]></category><dc:creator><![CDATA[fortime]]></dc:creator><pubDate>Thu, 26 Sep 2024 05:12:06 GMT</pubDate><content:encoded><![CDATA[<h1 id="installation-with-disk-encryption">Installation with disk encryption</h1>
<h2 id="partition-with-encryption">Partition with encryption</h2>
<p>In case of after-sales service, I kept windows partitions. I deleted <code>D:</code> partition and shrink <code>C:</code> partition. I create two partitions: <code>/boot</code> and <code>/</code>. I will use btrfs on <code>/</code>, so instead of using paritions, I will use subvolume for /home.</p>
<p>Encryption was applied exclusively to <code>/</code> partition, and I enabled <code>Secure Boot</code> to make <code>EFI</code> partition and <code>/boot</code> partition safe.</p>
<ul>
<li>Encrypt <code>/</code> partition.<pre><code># Don&apos;t forget to back up the password.
cryptsetup luksFormat /dev/nvme0n1p5
cryptsetup open /dev/nvme0n1p5 root
</code></pre>
</li>
<li>Create a btrfs file system and mount it to <code>/mnt</code>.<pre><code>mkfs.btrfs /dev/mapper/root
mount /dev/mapper/root /mnt
</code></pre>
</li>
</ul>
<p>After that, follow <a href="https://wiki.archlinux.org/title/Installation_guide?ref=fortime.fyi#Installation">Installation Guide</a>.</p>
<h2 id="bootloader">Bootloader</h2>
<p><s>As a old linux user, I have used <code>GRUB</code> for a long time. In this installation, I still use <code>GRUB</code>. OneXPlayer 2 Pro is bit heavy. I don&apos;t want to bring the keyboard cover everywhere. So, I need the partition to be automatically decrypted when booting from <code>GRUB</code> by integrating the use of <code>Secure Boot</code> and <code>TPM</code>.</s></p>
<p>At first, I used <code>GRUB</code>. But at the time I am writing this article, I found my plan is not secure enough. According to <a href="https://wiki.archlinux.org/title/Systemd-cryptenroll?ref=fortime.fyi#Trusted_Platform_Module">this article</a>, an attacker can get the decryption key of the partition.</p>
<blockquote>
<p>A rogue partition with metadata copied from the real root filesystem (such as partition UUID) can mimic the original partition. Then, initramfs will attempt to mount the rogue partition as the root filesystem (decryption failure will fall back to password entry), leaving pre-boot PCRs unchanged. The rogue root filesystem with files controlled by an attacker is still able to receive the decryption key for the real root partition.</p>
</blockquote>
<p>To prevent this attack, we need to make sure: 1. PCRs are changed before leaving initramfs. 2. only trusted initramfs can be booted.</p>
<p>With a signed unified kernel image (<code>UKI</code>), we can make sure that initramfs can&apos;t be modified. <code>systemd</code> provides <code>systemd-pcrphase-initrd.service</code> to modify the value of PCR 11 in the state of entering and leaving initramfs. The service works in a systemd based initramfs.</p>
<p><strong>Why not <code>GRUB</code>?</strong> <code>UKI</code> is actually a <code>EFI</code> bootloader. <code>GRUB</code> can only load <code>Secure Boot</code> keys signed <code>EFI</code> bootloaders. But, we can&apos;t load our own <code>Secure Boot</code> to a OneXPlayer 2 Pro. After researching, I found that <code>rEFInd</code> supports <code>Secure Boot</code> keys and <code>MOK</code> keys signed <code>EFI</code> bootloaders.</p>
<h3 id="enable-secure-boot">Enable <code>Secure Boot</code></h3>
<p>There are three methods to enable <code>Secure Boot</code> for a linux system: <code>CA Keys</code> and <code>Shim</code> and <code>Preloader</code>.</p>
<ul>
<li><code>CA Keys</code>: Create a pair of private and public keys, then load the public key into the BIOS in setup mode. After that, only bootloaders signed by the private key can be booted.</li>
<li><code>Shim</code>: A binary signed by Microsoft. Since the shim binary is signed by Microsoft, it is validated and accepted by the firmware when verifying against certificates already present in firmware. It uses <code>MOKManager</code> to enroll a custom user public key, and then a user can use the corresponding private key to sign the bootloader and the kernel. It can also enroll the hash of a file.</li>
<li><code>Preloader</code>: Like <code>Shim</code>, but it verifies the bootloader and the kernel with a hash instead of a signature.</li>
</ul>
<p>On a OneXPlayer 2 Pro, each time you change <code>Secure Boot</code> into setup mode, it will load the default certificates after start. So, we can&apos;t use <code>CA Keys</code>.</p>
<h4 id="enable-secure-boot-with-shim">Enable <code>Secure Boot</code> with <code>Shim</code></h4>
<ol>
<li>
<p>Install <a href="https://aur.archlinux.org/packages/shim-signed/?ref=fortime.fyi">shim-signed</a> from AUR.</p>
</li>
<li>
<p>Mount <code>ESP</code>.</p>
<pre><code class="language-bash">mkdir /boot-efi
# the default efi partition is /dev/nvme0n1p1 on a OneXPlayer 2 Pro
mount /dev/nvme0n1p1 /boot-efi
</code></pre>
</li>
<li>
<p>Copy the efi files to <code>ESP</code>.</p>
<pre><code class="language-bash">cp /usr/share/shim-signed/shimx64.efi /boot-efi/EFI/BOOT/
cp /usr/share/shim-signed/mmx64.efi /boot-efi/EFI/BOOT/
</code></pre>
</li>
<li>
<p>Create a NVRAM entry for <code>Shim</code>.</p>
<pre><code class="language-bash">efibootmgr --unicode --disk /dev/nvme0n1 --part 1 --create --label &quot;Shim&quot; --loader /EFI/BOOT/shimx64.efi
</code></pre>
</li>
<li>
<p>Install <code>rEFInd</code>.</p>
<pre><code class="language-bash">pacman -S refind

refind-install --shim /usr/share/shim-signed/shimx64.efi --localkeys
</code></pre>
</li>
<li>
<p>Use openssl to create the mok keypair.</p>
<pre><code class="language-bash">cd /etc/refind.d/keys/
openssl req -newkey rsa:2048 -nodes -keyout refind_local.key -new -x509 -sha256 -days 3650 -subj &quot;/CN=onexplayer2pro-fortime/&quot; -out refind_local.crt
openssl x509 -outform DER -in refind_local.crt -out refind_local.cer
cp refind_local.cer /boot/
</code></pre>
</li>
<li>
<p>Create a custom config of mkinitcpio.</p>
<pre><code class="language-bash">cat &lt;&lt;EOF &gt; /etc/mkinitcpio.conf.d/lkus.conf
# allow usb keyboard to input password for decryption in boot time.
MODULES=(usbhid xhci_hcd)
HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole plymouth block sd-encrypt filesystems fsck)
EOF
</code></pre>
</li>
<li>
<p>Create the config file of ukify.</p>
<pre><code class="language-bash">cat &lt;&lt;EOF &gt;/etc/kernel/ukify.conf
[UKI]
Microcode=/boot/amd-ucode.img
OSRelease=@/etc/os-release
SecureBootPrivateKey=/etc/refind.d/keys/refind_local.key
SecureBootCertificate=/etc/refind.d/keys/refind_local.crt
EOF
</code></pre>
</li>
<li>
<p>Create cmdline files for kernel parameters.</p>
<pre><code class="language-bash"># rd.luks.name: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX=name, Specify the name of the mapped device after the LUKS partition is open, where XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX is the UUID of the LUKS partition.
# rd.luks.options: Set options for the device specified by it UUID or, if not specified, for all UUIDs not specified elsewhere (e.g., crypttab). 
# resume: Specify the root device for hibernation.
# resume_offset: Specify the offset of the swap file. The offset of swap file in a btrfs file system should be calculated by `btrfs inspect-internal map-swapfile`. Refer to [Hibernation](#Hibernation)
# video: Set the video kernel parameter to rotate the builtin screen from portrait to landscape in the start of the system.
# usbcore.autosuspend: USB Ports / Controller stops working after Device Disconnect.
cat &lt;&lt;EOF &gt;/etc/kernel/cmdline
rd.luks.name=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX=root rd.luks.options=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX=tpm2-device=auto root=/dev/mapper/root rw resume=/dev/mapper/root resume_offset=yyyyyyy quiet splash video=eDP-1:panel_orientation=left_side_up usbcore.autosuspend=-1
EOF
</code></pre>
</li>
<li>
<p>Create a <code>mkinitcpio</code> post hook.</p>
<pre><code class="language-bash">cat &lt;&lt;EOF &gt;/etc/initcpio/post/ukify
#!/usr/bin/env bash

kernel=&quot;\$1&quot;
initramfs=&quot;\$2&quot;
[ -n &quot;\$kernel&quot; ] || exit 0
[ -n &quot;\$initramfs&quot; ] || exit 0

initramfs_filename=\$(basename &quot;\$initramfs&quot;)

cmdline=&quot;&quot;
uki=&quot;&quot;
ukify_log=&quot;&quot;
case &quot;\$initramfs_filename&quot; in
    &quot;initramfs-linux.img&quot;)
        cmdline=/etc/kernel/cmdline
        uki=&quot;/boot/arch-linux.efi&quot;
        ukify_log=&quot;/etc/refind.d/keys/ukify.log&quot;
        ;;
    &quot;initramfs-linux-fallback.img&quot;)
        cmdline=/etc/kernel/cmdline-fallback
        uki=&quot;/boot/arch-linux-fallback.efi&quot;
        ukify_log=&quot;/etc/refind.d/keys/ukify-fallback.log&quot;
        ;;
    *)
        exit 0
        ;;
esac

# it is hard to measure the value of PCR 11 with the uki file, we keep the log for getting the value of PCR 11. 
ukify build --config=/etc/kernel/ukify.conf --linux=&quot;\$kernel&quot; --initrd=&quot;\$initramfs&quot; --cmdline=&quot;@\$cmdline&quot; --measure --output=&quot;\$uki&quot; &gt;&quot;\$ukify_log&quot; 2&gt;&amp;1

if [ &quot;\$initramfs_filename&quot; = &quot;initramfs-linux.img&quot; ]
then
  pcr11=\$(grep &quot;11:sha256&quot; &quot;\$ukify_log&quot; | sed &apos;1!d;s/.*=//&apos;)
  if [ \$? -eq 0 ]
  then
      # use PCR 7, PCR 11 and PCR 14 to protect the key in tpm2, and use a recovery key to decrypt the partition.
      systemd-cryptenroll /dev/nvme0n1p5 --wipe-slot=tpm2 --tpm2-device=auto --tpm2-pcrs=&quot;7+11:sha256=\$pcr11+14&quot; --unlock-key-file=/etc/refind.d/keys/disk-encrypt.txt
  fi
fi
EOF

chmod 700 /etc/initcpio/post/ukify
EOF
</code></pre>
</li>
<li>
<p>Sign the grub bootloader and the kernel.</p>
<pre><code class="language-bash">refind-install --shim /usr/share/shim-signed/shimx64.efi --localkeys
</code></pre>
</li>
<li>
<p>Enroll the <code>refind_local.cer</code>.</p>
</li>
</ol>
<blockquote>
<p>Reboot and enable Secure Boot. If shim does not find the certificate grubx64.efi is signed with in MokList it will launch MokManager (mmx64.efi).</p>
<p>In MokManager select Enroll key from disk, find refind-local.cer and add it to MokList. When done select Continue boot and your boot loader will launch and it will be capable launching any binary signed with your Machine Owner Key.</p>
</blockquote>
<h2 id="secure-path">Secure path</h2>
<p><img src="https://fortime.fyi/blog/content/images/2024/10/tpm2-luks.png" alt="tpm2-luks.png" loading="lazy"></p>
<h2 id="issues">Issues</h2>
<ul>
<li>Don&apos;t forget to check if refind.efi(it is renamed to grubx64.efi) and its drivers are signed by <code>sbsign</code>. If its drivers aren&apos;t signed, they won&apos;t be loaded and related volumes won&apos;t be able to scanned.</li>
</ul>
<h1 id="hibernation">Hibernation</h1>
<p>To enable hibernation, we should create a swap partition or a swap file. Using swap file in btrfs is different with other file system. We should use utils provided by btrfs to create a swap file.</p>
<ul>
<li>Create a swap file.<pre><code class="language-bash">btrfs filesystem mkswapfile /var/cache/swapfile -s 16g
</code></pre>
</li>
<li>Calculate the offset of the swapfile.<pre><code class="language-bash">btrfs inspect-internal map-swapfile /var/cache/swapfile
</code></pre>
</li>
<li>Add a swap item in <code>/etc/fstab</code>.<pre><code># swapfile for hiberation
/var/cache/swapfile     none            swap            defaults        0 0
</code></pre>
</li>
<li>Add <code>resume</code> and <code>resume_offset</code> to kernel cmdline.</li>
</ul>
<h2 id="tpm-issue-in-hibernation">TPM issue in hibernation</h2>
<p>Measurement of PCR 11 in hibernation is different from normal boot. In hibernation, there is no measurement of <code>leave-initrd</code> before leaving initrd. The leaving of initrd happens in <code>systemd-hibernate-resume.service</code>.</p>
<p>I created a temporarily fix by stopping <code>systemd-pcrphase-initrd.service</code> before <code>systemd-hibernate-resume.service</code>.</p>
<ul>
<li>Create a initcpio install hook, create <code>/etc/initcpio/install/pcrphase-fix</code>.<pre><code class="language-bash">#!/bin/bash

build() {
    printf &apos;[Service]\nExecStartPre=/usr/bin/systemctl stop systemd-pcrphase-initrd.service&apos; | add_systemd_drop_in systemd-hibernate-resume.service override
}

help() {
    cat &lt;&lt; EOF
Fix the issue of no stop of systemd-pcrphase-initrd.service in resume.
EOF
}
</code></pre>
</li>
<li>Update the custom config of mkinitcpio.<pre><code class="language-bash">cat &lt;&lt;EOF &gt; /etc/mkinitcpio.conf.d/lkus.conf
# allow usb keyboard to input password for decryption in boot time.
MODULES=(usbhid xhci_hcd)
HOOKS=(base systemd pcrphase-fix autodetect microcode modconf kms keyboard sd-vconsole plymouth block sd-encrypt filesystems fsck)
EOF
</code></pre>
</li>
</ul>
<h1 id="mapping-buttons">Mapping buttons</h1>
<p>There is no volume up/down button of this machine. As a handheld game console, this is unforgivable. So, I want to map the x1/x2 button to volume up/down button. After using evtest monitoring the key events, these two buttons trigger a key combo instead of a single keycode. I can&apos;t use hwdb to map keys. Here, i use <code>keyd</code> to finish this work.</p>
<ul>
<li>Install and enable <code>keyd</code>.<pre><code class="language-bash">pacman -S keyd
systemctl enable keyd
</code></pre>
</li>
<li>Create a file /etc/keyd/oxp2p.conf.<pre><code>[ids]
0001:0001

[main]
leftmeta+d = volumedown
leftcontrol+leftmeta+o = volumeup
</code></pre>
</li>
<li>Restart <code>keyd</code>.<pre><code class="language-bash">systemctl start keyd
</code></pre>
</li>
</ul>
<h2 id="foldable-bluetooth-keyboard">Foldable bluetooth keyboard</h2>
<p>The key in the removable keyboard cover is too small. I buy a foldable bluetooth keyboard. Everything is ok, but Fn keys are function keys by default. I use Fn keys a lot in vim. It makes me very inconvenient. So, I add a hwdb file to switch Fn key and function key.</p>
<ul>
<li>Add a hwdb file /etc/udev/hwdb.d/90-custom-keyboard.hwdb.<pre><code>evdev:input:b0005v0A5Cp8503e011B*
  KEYBOARD_KEY_700e2=leftmeta
  KEYBOARD_KEY_700e3=leftalt
  KEYBOARD_KEY_700e6=rightmeta
  KEYBOARD_KEY_700e7=rightalt
  KEYBOARD_KEY_c0223=esc
  KEYBOARD_KEY_70029=homepage
  KEYBOARD_KEY_c0221=f1
  KEYBOARD_KEY_7003a=search
  KEYBOARD_KEY_7003b=back
  KEYBOARD_KEY_7003c=forward
  KEYBOARD_KEY_7003d=brightnessdown
  KEYBOARD_KEY_7003e=brightnessup
  KEYBOARD_KEY_c00b6=f6
  KEYBOARD_KEY_7003f=previoussong
  KEYBOARD_KEY_c00cd=f7
  KEYBOARD_KEY_70040=playpause
  KEYBOARD_KEY_c00b5=f8
  KEYBOARD_KEY_70041=nextsong
  KEYBOARD_KEY_c00b5=f8
  KEYBOARD_KEY_70041=nextsong
  KEYBOARD_KEY_c00e2=f9
  KEYBOARD_KEY_70042=mute
  KEYBOARD_KEY_c00ea=f10
  KEYBOARD_KEY_70043=volumedown
  KEYBOARD_KEY_c00e9=f11
  KEYBOARD_KEY_70044=volumeup
  KEYBOARD_KEY_c0030=f12
  KEYBOARD_KEY_70045=power
</code></pre>
</li>
<li>But its f2, f3, f4 and f5 send a key combo instead of a single key. I use <code>keyd</code> again. Add a <code>keyd</code> config file.<pre><code>[ids]
0a5c:8503

[main]
leftalt+a = f2
leftalt+c = f3
leftalt+v = f4
leftalt+x = f5
</code></pre>
</li>
</ul>
<h1 id="auto-rotation">Auto rotation</h1>
<p>The accelerator sensor showed in Windows device manager is bmi160. But the bmi160 driver in Linux can&apos;t work. After some research, I found that the bmi260 driver also matches the acpi device id <code>10EC5280</code>. So, I try this driver, and it works.<br>
Install <code>bmi260-dkms</code> from AUR.</p>
<pre><code class="language-bash"># iio-sensor-proxy: Proxies sensor devices (accelerometers, light sensors, compass) to applications through D-Bus.
paru -S bmi260-dkms iio-sensor-proxy
</code></pre>
<h2 id="wrong-direction"><s>Wrong direction</s></h2>
<p><s>After installing <code>bmi260-dkms</code>, the auto-rotate works, but the screen is 90 degrees clockwise rotated. We can use udev rules to fix it.</s></p>
<ul>
<li><s>Create a file /etc/udev/hwdb.d/90-custom-oxp.hwdb with following content.</s><pre><code>sensor:modalias:acpi:10EC5280*:dmi:*:rnONEXPLAYER2PROARP23P:rvrVersion1.0:*
  ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
</code></pre>
</li>
<li><s>Update hwdb and trigger udev to update the device.</s><pre><code class="language-bash">sudo systemd-hwdb update &amp;&amp; sudo udevadm trigger
# It seems like we should restart iio-sensor-proxy too
sudo systemctl restart iio-sensor-proxy.service
</code></pre>
</li>
</ul>
<p>I&apos;m not sure since when and which package. It should be before 2026-03-13. We don&apos;t need a custom hwdb file. The orientation of KWin is correct out-of-box. Comparing the output of <code>monitor-sensor</code>, the custom hwdb file actually works. After searching in the KWin git commit log, I think this fix(b380e32193b0d373515b7ea003d8e2a6cb94a21f) is the cause, and the tracking issue is <a href="https://bugs.kde.org/show_bug.cgi?id=513156&amp;ref=fortime.fyi">513156</a>.</p>
<h2 id="kde-touch-mode">KDE touch mode</h2>
<p>Be default, kde disables touch mode if there is any pointer device. I use keyd to map buttons, and keyd creates a virtual pointer device which will make kde disables touch mode all the time. After reviewing the source code of kwin, I found we can add a tag <code>kwin-ignore-tablet-mode</code> to the keyd pointer device to let kde ignore the device.</p>
<ul>
<li>Create a file /etc/udev/rules.d/90-tablet-mode.rules.<pre><code>ACTION==&quot;remove&quot;, GOTO=&quot;kwin_ignore_tablet_mode_end&quot;

SUBSYSTEM==&quot;input&quot;, KERNEL==&quot;event*&quot;, ATTRS{id/vendor}==&quot;0fac&quot;, ATTRS{id/product}==&quot;1ade&quot;, TAG+=&quot;kwin-ignore-tablet-mode&quot;

LABEL=&quot;kwin_ignore_tablet_mode_end&quot;
</code></pre>
</li>
</ul>
<h1 id="audio">Audio</h1>
<p><a href="https://fortime.fyi/blog/20240926-01/">Internal Speaker Of OneXPlayer 2 Pro (8840) In Linux</a></p>
<h1 id="multi-touch">Multi-touch</h1>
<p>TODO</p>
<h1 id="drop-of-signal-with-external-monitor">Drop of signal with external monitor</h1>
<p>I have two external monitors, a portable monitor and a Philips 279C9. Both of them have signal drop issue when I&apos;m playing games.</p>
<h2 id="the-portable-monitor">The portable monitor</h2>
<p>I occasionally turn off <code>Free Sync</code> of the portable monitor, and the issue disappeared. In a later research, I found the <a href="https://wiki.archlinux.org/title/Variable_refresh_rate?ref=fortime.fyi#Monitor_occasionally_drops_signal_with_FreeSync_enabled">cause</a>. I&apos;m using wayland and I often connect to other monitors, overriding edid is not a good enough solution, so I still keep <code>Free Sync</code> off.</p>
<h2 id="the-philips-279c9">The Philips 279C9</h2>
<p>At first, I think this is the same as the portable monitor. But there are no setting of <code>Free Sync</code> in the menu the monitor. After some research, I found that there will be a dropdown option in <code>KDE Display Configuration</code> if the monitor supports <code>Free Sync</code>.</p>
<p>To check if the monitor supports <code>Free Sync</code>, I install <code>wxedid</code> to inspect the edid info the monitor.</p>
<pre><code class="language-bash">sudo get-edid -b ${bus_id} &gt; /tmp/philips-edid.bin
wxedid /tmp/philips-edid.bin
</code></pre>
<p>When I was checking the edid info, I noticed that only 4k@30 is used even if the monitor supports 4k@60. Why? So I enable the drm debug log.</p>
<pre><code class="language-bash"># enable all kinds of drm debug log
echo 0x1BF &gt; /sys/module/drm/parameters/debug

# disable all drm debug log
echo 0x0 &gt; /sys/module/drm/parameters/debug

# what does 0x1BF mean, it is flags of debug category.
modinfo -p drm
</code></pre>
<p>In the debug log, it showed something like:</p>
<pre><code>[drm:create_validate_stream_for_sink [amdgpu]] Mode 3840x2160 (clk 533250) failed DC validation with error 10 (No DP link bandwidth)
</code></pre>
<p>All modelines with pixel clock of 533250 are rejected. But when I switched to <code>Windows</code>, 4k@60 is supported. It shouldn&apos;t be a quality issue of the cable. When I was trying to add more debug log, I occasionally found a comment of <code>Philips 279C9</code>. It said that 4k@60 can only be used after switching usb3.2 to usb2.0 in the monitor menu when it is connected to a Mac OS. I tried this and the option of 4k@60 appeared. When I&apos;m using 4k@60, the signal drop issue disappear too. I think it is because it will reduce the estimated bandwidth of a DP link if the type-c port is used as a usb3.2 hub.<br>
I can&#x2019;t believe I spent 10 hours troubleshooting during national day vacation! And it can be solved just by changing a setting like this. At last, I still don&apos;t know why this cause the signal drop issue, and why 4k@60 can be used in <code>Windows</code> even if usb3.2 is using. ~~If the signal drop issue exists if I am using 4k@30 and usb2.0.~~The signal drop issue disappers after switching to usb2.0.</p>
<h1 id="reference">Reference</h1>
<ul>
<li><a href="https://wiki.archlinux.org/title/Dm-crypt/?ref=fortime.fyi">https://wiki.archlinux.org/title/Dm-crypt/</a></li>
<li><a href="https://wiki.archlinux.org/title/Dm-crypt/System_configuration?ref=fortime.fyi">https://wiki.archlinux.org/title/Dm-crypt/System_configuration</a></li>
<li><a href="https://wiki.archlinux.org/title/Dm-crypt/Encrypting_an_entire_system?ref=fortime.fyi#LUKS_on_a_partition_with_TPM2_and_Secure_Boot">https://wiki.archlinux.org/title/Dm-crypt/Encrypting_an_entire_system#LUKS_on_a_partition_with_TPM2_and_Secure_Boot</a></li>
<li><a href="https://wiki.ubuntu.com/UEFI/SecureBoot?ref=fortime.fyi">https://wiki.ubuntu.com/UEFI/SecureBoot</a></li>
<li><a href="https://wiki.archlinux.org/title/Unified_Extensible_Firmware_Interface/Secure_Boot?ref=fortime.fyi">https://wiki.archlinux.org/title/Unified_Extensible_Firmware_Interface/Secure_Boot</a></li>
<li><a href="https://git.launchpad.net/~ubuntu-core-dev/grub/+git/ubuntu/tree/debian/build-efi-images?ref=fortime.fyi">https://git.launchpad.net/~ubuntu-core-dev/grub/+git/ubuntu/tree/debian/build-efi-images</a></li>
<li><a href="https://wiki.archlinux.org/title/Plymouth?ref=fortime.fyi">https://wiki.archlinux.org/title/Plymouth</a></li>
<li><a href="https://btrfs.readthedocs.io/en/latest/Swapfile.html?ref=fortime.fyi">https://btrfs.readthedocs.io/en/latest/Swapfile.html</a></li>
<li><a href="https://github.com/ublue-os/bazzite/issues/440?ref=fortime.fyi">https://github.com/ublue-os/bazzite/issues/440</a></li>
<li><a href="https://wiki.archlinux.org/title/Variable_refresh_rate?ref=fortime.fyi#Monitor_occasionally_drops_signal_with_FreeSync_enabled">https://wiki.archlinux.org/title/Variable_refresh_rate#Monitor_occasionally_drops_signal_with_FreeSync_enabled</a></li>
</ul>
<div class="kg-card kg-file-card"><a class="kg-file-card-container" href="https://fortime.fyi/blog/content/files/2024/09/tpm2-luks.drawio" title="Download" download><div class="kg-file-card-contents"><div class="kg-file-card-title">tpm2-luks</div><div class="kg-file-card-caption"></div><div class="kg-file-card-metadata"><div class="kg-file-card-filename">tpm2-luks.drawio</div><div class="kg-file-card-filesize">18 KB</div></div></div><div class="kg-file-card-icon"><svg viewbox="0 0 24 24"><defs><style>.a{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px;}</style></defs><title>download-circle</title><polyline class="a" points="8.25 14.25 12 18 15.75 14.25"/><line class="a" x1="12" y1="6.75" x2="12" y2="18"/><circle class="a" cx="12" cy="12" r="11.25"/></svg></div></a></div>]]></content:encoded></item><item><title><![CDATA[How to scroll the vim completion popup window]]></title><description><![CDATA[After popup windows is added in vim 8.2, we can display documentation provided by ycm in a popup window. Different from display documentation in a preview window, it won't change the layout. But there is one disadvantage, I can't scroll the popup window by keyboard. It is not vim-style enough.]]></description><link>https://fortime.fyi/blog/20200312-01/</link><guid isPermaLink="false">6661f49b8cd2840e638166cd</guid><category><![CDATA[技术]]></category><category><![CDATA[Vim]]></category><dc:creator><![CDATA[fortime]]></dc:creator><pubDate>Sat, 14 Mar 2020 15:07:46 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>After popup windows is added in vim 8.2, we can display documentation provided by ycm in a popup window. Different from display documentation in a preview window, it won&apos;t change the layout. But there is one disadvantage, I can&apos;t scroll the popup window by keyboard. It is not vim-style enough.</p>
<h1 id="enablethecompletionpopupwindow">Enable the Completion Popup Window</h1>
<p>Add the following line in the vimrc file.</p>
<pre><code class="language-.vimrc">set completeopt+=popup
</code></pre>
<h1 id="scrollthepopupwindow">Scroll the Popup Window</h1>
<p><em>dedowsdi</em> provide a solution about how to scroll a popup window <a href="https://vi.stackexchange.com/a/21927?ref=fortime.fyi">here</a>. There is another problem now. Not like coc, the popup window triggered by ycm may be far away from the cursor. If I follow this solution, I must let vim to scan over the whole screen to find the popup window. It is too slow.<br>
After looking into VIM REFERENCE MANUAL and doing some tests, I thought I can use <code>popup_findinfo</code> to get the id of the completion popup window. But, I&apos;m not sure it is the correct way. So I have the following code:</p>
<pre><code class="language-vim">nnoremap &lt;C-e&gt; :call ScrollPopup(1)&lt;CR&gt;
nnoremap &lt;C-y&gt; :call ScrollPopup(0)&lt;CR&gt;

function ScrollPopup(down)
    let winid = popup_findinfo()
    if winid == 0
        return 0
    endif

    &quot; The popup window has been hidden in the normal mode, we should make it show again.
    call popup_show(winid)
    let pp = popup_getpos(winid)
    call popup_setoptions( winid,
                \ {&apos;firstline&apos; : pp.firstline + ( a:down ? 1 : -1 ) } ) 

    return 1
endfunction
</code></pre>
<p>Then, I can read and scroll the completion popup window in normal mode. It isn&apos;t good enough still: 1. I must switch to normal mode which will close the completion popup menu. If the selection isn&apos;t the one I want, I must delete it and trigger completion again. 2. After scrolling to the bottom, the height of the popup window will reduce to one, if I keep pressing CTRL-e.</p>
<h1 id="scrollthepopupwindowwithoutclosingthepopupmenu">Scroll the Popup Window without Closing the Popup Menu</h1>
<p>Firstly, I should change <code>nnoremap</code> to <code>inoremap</code>. So the key-binding will work in insert mode.</p>
<pre><code class="language-vim">inoremap &lt;C-e&gt; :call ScrollPopup(1)&lt;CR&gt;
inoremap &lt;C-y&gt; :call ScrollPopup(0)&lt;CR&gt;
</code></pre>
<p>I haven&apos;t spend much time with vimscript even if I have used vim for years. Instead of calling <code>ScrollPopup</code>, it just inserts &quot;:call ScrollPopup(1)\n&quot;. Finally, I find out how <code>map</code> works.</p>
<pre><code>map {lhs} {rhs}
</code></pre>
<p>It just maps the left hand side keystrokes to the right hand side keystrokes. If I want to run something, I should add <code>&lt;expr&gt;</code>.</p>
<pre><code>map &lt;expr&gt; {lhs} {rhs}
</code></pre>
<p>The {rhs} is evaluated to obtain the right hand side keystrokes.</p>
<pre><code class="language-vim">inoremap &lt;expr&gt; &lt;C-e&gt; ScrollPopup(3) ? &apos;&apos; : &apos;&lt;C-e&gt;&apos;
inoremap &lt;expr&gt; &lt;C-y&gt; ScrollPopup(-3) ? &apos;&apos; : &apos;&lt;C-y&gt;&apos;
</code></pre>
<p>If scroll happened, it maps CTRL-e/CTRL-y to non-operation. Otherwise, it doesn&apos;t change the keystrokes.</p>
<h1 id="stopafterreachingthebottom">Stop after Reaching the bottom</h1>
<p><code>ScrollPopup</code> should stop changing <code>firstline</code> after the bottom of the buffer has showed. But, how does it know that? With <code>popup_getpos</code>, we have following properties:</p>
<pre><code>col         screen column of the popup, one-based
line        screen line of the popup, one-based
width       width of the whole popup in screen cells
height      height of the whole popup in screen cells
core_col    screen column of the text box
core_line   screen line of the text box
core_width  width of the text box in screen cells 
core_height height of the text box in screen cells 
firstline   line of the buffer at top (1 unless scrolled) (not the value of the &quot;firstline&quot; property)
lastline    line of the buffer at the bottom (updated when the popup is redrawn)
scrollbar   non-zero if a scrollbar is displayed
visible     one if the popup is displayed, zero if hidden
</code></pre>
<p>At a glance, a formula has come up: <code>lastline</code> - <code>firstline</code> &lt; <code>height</code>. But it doesn&apos;t work. <code>lastline</code> is not the lastline of the text. It is the lastline showed in the popup window. After some search, I found I can have the lastline by <code>win_execute(winid, &quot;echo line(&apos;$&apos;)&quot;)</code>. Executing a command in the popup window to get the lastline of the buffer.<br>
It still doesn&apos;t work as I thought. Sometimes, it won&apos;t scroll down. Occasionally, I show the line number in the popup window. <code>firstline</code> and <code>lastline</code> is the line of the text. <code>height</code> is the line of the window. With wrap on, one line of text will be many lines in the window. Finally, I have:</p>
<pre><code class="language-vim">function! ScrollPopup(down)
    let winid = popup_findinfo()
    if winid == 0
        return 0
    endif

    &quot; if the popup window is hidden, bypass the keystrokes
    let pp = popup_getpos(winid)
    if pp.visible != 1
        return 0
    endif

    let firstline = pp.firstline + a:down
    let buf_lastline = str2nr(trim(win_execute(winid, &quot;echo line(&apos;$&apos;)&quot;)))
    if firstline &lt; 1
        let firstline = 1
    elseif pp.lastline + a:down &gt; buf_lastline
        let firstline = firstline - a:down + buf_lastline - pp.lastline
    endif

    &quot; The appear of scrollbar will change the layout of the content which will cause inconsistent height.
    call popup_setoptions( winid,
                \ {&apos;scrollbar&apos;: 0, &apos;firstline&apos; : firstline } )

    return 1
endfunction

inoremap &lt;expr&gt; &lt;C-e&gt; ScrollPopup(3) ? &apos;&apos; : &apos;&lt;C-e&gt;&apos;
inoremap &lt;expr&gt; &lt;C-y&gt; ScrollPopup(-3) ? &apos;&apos; : &apos;&lt;C-y&gt;&apos;
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Arch Linux不依赖32位包安装Android编译环境]]></title><description><![CDATA[在代码中，我发现有三种通知（FLAG_FOREGROUND_SERVICE/FLAG_ONGOING_EVENT/FLAG_LOCAL_ONLY）是不会被发到电脑上的，这三种通知不发到电脑上也合理，会不会是张经理有什么特殊想法，要把微信通知标为其中一种呢？那就魔改一下kdeconnect-android。可是，aur上的android-sdk依赖了lib32的包，在我这个清真64位系统上，该怎么办呢？]]></description><link>https://fortime.fyi/blog/20191103-01/</link><guid isPermaLink="false">6661f49b8cd2840e638166cb</guid><category><![CDATA[Linux]]></category><category><![CDATA[技术]]></category><dc:creator><![CDATA[fortime]]></dc:creator><pubDate>Sun, 08 Mar 2020 11:40:47 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p><em><strong>&#x6CE8;&#x610F;&#x6B64;&#x65B9;&#x6CD5;&#x5E94;&#x8BE5;&#x53EA;&#x9002;&#x5408;&#x7F16;&#x8BD1;&#x4E0D;&#x9700;&#x8981;NDK&#x7684;&#x5305;</strong></em></p>
<h1 id>&#x80CC;&#x666F;</h1>
<p>&#x5728;Arch&#x4E0A;&#xFF0C;&#x53EA;&#x80FD;&#x4F7F;&#x7528;&#x7F51;&#x9875;&#x7248;&#x5FAE;&#x4FE1;&#xFF0C;&#x4F46;&#x7F51;&#x9875;&#x7248;&#x5FAE;&#x4FE1;&#x53C8;&#x663E;&#x793A;&#x4E0D;&#x4E86;&#x4F01;&#x4E1A;&#x5FAE;&#x4FE1;&#x7684;&#x901A;&#x77E5;&#xFF08;&#x5728;&#x8865;&#x8FD9;&#x7BC7;&#x535A;&#x5BA2;&#x65F6;&#xFF0C;&#x516C;&#x53F8;&#x5DF2;&#x7ECF;&#x5173;&#x4E86;&#x5FAE;&#x4FE1;&#x663E;&#x793A;&#x4F01;&#x4E1A;&#x5FAE;&#x4FE1;&#x6D88;&#x606F;&#x7684;&#x529F;&#x80FD;-_-<em>&#xFF01;</em>&#xFF0C;&#x505A;&#x4E8B;&#x4E0D;&#x80FD;&#x8FD9;&#x4E48;&#x62D6;&#x5EF6;&#xFF09;&#xFF0C;&#x5BFC;&#x81F4;&#x7ECF;&#x5E38;&#x665A;&#x4E86;&#x56DE;&#x590D;&#x4F01;&#x4E1A;&#x5FAE;&#x4FE1;&#x7684;&#x6D88;&#x606F;&#xFF08;&#x633A;&#x597D;&#x7684;&#xFF0C;&#x53EF;&#x4EE5;&#x4E13;&#x6CE8;&#x5199;&#x4EE3;&#x7801;&#xFF09;&#x3002;&#x6240;&#x4EE5;&#xFF0C;&#x6211;&#x5C31;&#x60F3;&#x5230;&#x624B;&#x673A;&#x4E0A;&#x7684;<code>kdeconnect</code>&#xFF0C;&#x4E3A;&#x4EC0;&#x4E48;&#x5FAE;&#x4FE1;&#x7684;&#x901A;&#x77E5;&#x4E0D;&#x80FD;&#x901A;&#x8FC7;<code>kdeconnect</code>&#x663E;&#x793A;&#x5728;&#x7535;&#x8111;&#x4E0A;&#x5462;&#xFF1F;&#x4E8E;&#x662F;&#xFF0C;&#x6211;&#x5C31;&#x628A;<code>kdeconnect-android</code>&#x7684;&#x6E90;&#x4EE3;&#x7801;clone&#x4E0B;&#x6765;&#xFF08;&#x4E00;&#x8A00;&#x4E0D;&#x5408;&#x770B;&#x6E90;&#x4EE3;&#x7801;&#xFF09;&#x3002;<br>
&#x5728;&#x4EE3;&#x7801;&#x4E2D;&#xFF0C;&#x6211;&#x53D1;&#x73B0;&#x6709;&#x4E09;&#x79CD;&#x901A;&#x77E5;&#xFF08;<code>FLAG_FOREGROUND_SERVICE</code>/<code>FLAG_ONGOING_EVENT</code>/<code>FLAG_LOCAL_ONLY</code>&#xFF09;&#x662F;&#x4E0D;&#x4F1A;&#x88AB;&#x53D1;&#x5230;&#x7535;&#x8111;&#x4E0A;&#x7684;&#xFF0C;&#x8FD9;&#x4E09;&#x79CD;&#x901A;&#x77E5;&#x4E0D;&#x53D1;&#x5230;&#x7535;&#x8111;&#x4E0A;&#x4E5F;&#x5408;&#x7406;&#xFF0C;&#x4F1A;&#x4E0D;&#x4F1A;&#x662F;&#x5F20;&#x7ECF;&#x7406;&#x6709;&#x4EC0;&#x4E48;&#x7279;&#x6B8A;&#x60F3;&#x6CD5;&#xFF0C;&#x8981;&#x628A;&#x5FAE;&#x4FE1;&#x901A;&#x77E5;&#x6807;&#x4E3A;&#x5176;&#x4E2D;&#x4E00;&#x79CD;&#x5462;&#xFF1F;&#x90A3;&#x5C31;&#x9B54;&#x6539;&#x4E00;&#x4E0B;<code>kdeconnect-android</code>&#x3002;&#x53EF;&#x662F;&#xFF0C;aur&#x4E0A;&#x7684;<code>android-sdk</code>&#x4F9D;&#x8D56;&#x4E86;lib32&#x7684;&#x5305;&#xFF0C;&#x5728;&#x6211;&#x8FD9;&#x4E2A;&#x6E05;&#x771F;64&#x4F4D;&#x7CFB;&#x7EDF;&#x4E0A;&#xFF0C;&#x8BE5;&#x600E;&#x4E48;&#x529E;&#x5462;&#xFF1F;</p>
<h1 id>&#x89E3;&#x51B3;&#x529E;&#x6CD5;</h1>
<p>&#x5728;<a href="https://developer.android.com/studio/command-line?ref=fortime.fyi">&#x8FD9;&#x91CC;</a>&#x53EF;&#x4EE5;&#x770B;&#x5230;&#x547D;&#x4EE4;&#x884C;&#x7F16;&#x8BD1;&#xFF08;&#x5C31;&#x9B54;&#x6539;&#x4E00;&#x4E0B;&#xFF0C;&#x4E0D;&#x53EF;&#x80FD;&#x53BB;&#x88C5;&#x4E2A;AS&#x5427;&#xFF09;&#x6240;&#x9700;&#x8981;&#x7684;&#x5DE5;&#x5177;&#x3002;&#x6211;&#x9700;&#x8981;&#x901A;&#x8FC7;aur&#x5B89;&#x88C5;&#x7684;&#x5305;&#x6709;<code>android-sdk</code>&#x548C;<code>android-sdk-build-tools</code>&#xFF0C;&#x5206;&#x522B;&#x6709;&#x4E09;&#x4E2A;lib32&#x7684;&#x5305;&#x88AB;&#x4F9D;&#x8D56;&#xFF1A;lib32-gcc-libs&#x3001;lib32-zlib&#x3001;lib32-glibc&#xFF0C;&#x770B;&#x4E86;&#x4E00;&#x4E0B;&#x4E24;&#x4E2A;&#x5305;&#x7684;PKGBUILD&#xFF0C;&#x53D1;&#x73B0;&#x5B83;&#x4EEC;&#x505A;&#x7684;&#x4E8B;&#x5C31;&#x662F;&#x53BB;android&#x7684;&#x5B98;&#x7F51;&#x4E0A;&#x53BB;&#x4E0B;&#x538B;&#x7F29;&#x5305;&#x5E76;&#x89E3;&#x538B;&#xFF0C;&#x8FC7;&#x7A0B;&#x4E2D;&#x6CA1;&#x6709;&#x7F16;&#x8BD1;&#x4EC0;&#x4E48;&#x4E1C;&#x897F;&#xFF0C;<code>kdeconnect-android</code>&#x4E5F;&#x6CA1;&#x6709;&#x7528;&#x5230;NDK&#x7F16;&#x8BD1;&#xFF0C;&#x90A3;&#x4E48;&#xFF0C;&#x4E0D;&#x4F9D;&#x8D56;&#x8FD9;&#x51E0;&#x4E2A;&#x5305;&#x5E94;&#x8BE5;&#x4E5F;&#x6CA1;&#x6709;&#x95EE;&#x9898;&#x3002;&#x4E0D;&#x8FC7;&#xFF0C;&#x6211;&#x8FD8;&#x662F;&#x628A;&#x4F9D;&#x8D56;&#x6539;&#x4E3A;&#x8FD9;&#x4E09;&#x4E2A;&#x5305;&#x7684;64&#x4F4D;&#x7248;&#x672C;&#x3002;</p>
<pre><code># android-sdk
depends_x86_64=(&apos;java-environment&apos; &apos;libxtst&apos; &apos;fontconfig&apos; &apos;freetype2&apos;
                &apos;lib32-gcc-libs&apos; &apos;lib32-glibc&apos; &apos;libx11&apos; &apos;libxext&apos; 
                &apos;libxrender&apos; &apos;zlib&apos;)
# &#x6539;&#x4E3A;
depends_x86_64=(&apos;java-environment&apos; &apos;libxtst&apos; &apos;fontconfig&apos; &apos;freetype2&apos;
                &apos;gcc-libs&apos; &apos;glibc&apos; &apos;libx11&apos; &apos;libxext&apos; &apos;libxrender&apos;
                &apos;zlib&apos;)

# android-sdk-build-tools
depends_x86_64=(&apos;lib32-gcc-libs&apos; &apos;lib32-zlib&apos;)
# &#x6539;&#x4E3A;
depends_x86_64=(&apos;gcc-libs&apos; &apos;zlib&apos;)
</code></pre>
<p>&#x5B89;&#x88C5;&#x597D;&#x4E86;&#x540E;&#xFF0C;&#x53BB;&#x7F16;&#x8BD1;&#x4E86;&#x4E00;&#x6CE2;<code>kdeconnect-android</code>&#xFF0C;&#x8FD8;&#x771F;&#x7684;&#x7F16;&#x8BD1;&#x901A;&#x8FC7;&#x8DD1;&#x8D77;&#x6765;&#x4E86;&#x3002;</p>
<h1 id>&#x540E;&#x8BB0;</h1>
<p>&#x7ECF;&#x8FC7;&#x6D4B;&#x8BD5;&#xFF0C;&#x53D1;&#x73B0;&#x5FAE;&#x4FE1;&#x7684;&#x901A;&#x77E5;&#x5E26;&#x4E86;<code>FLAG_LOCAL_ONLY</code>&#xFF0C;&#x6D88;&#x606F;&#x88AB;&#x6807;&#x4E3A;&#x53EA;&#x5728;&#x672C;&#x5730;&#x663E;&#x793A;&#xFF08;&#x8FD9;&#x4F1A;&#x4E0D;&#x4F1A;&#x662F;&#x6211;&#x6761;&#x540C;&#x4E8B;&#x9C7C;&#x8BF4;&#x5FAE;&#x4FE1;&#x6D88;&#x606F;&#x4E0D;&#x663E;&#x793A;&#x5728;iWatch&#x4E0A;&#x7684;&#x539F;&#x56E0;&#x5462;&#xFF1F;&#x4F46;&#x611F;&#x89C9;&#x4E0D;&#x592A;&#x53EF;&#x80FD;&#xFF0C;&#x5341;&#x6210;&#x662F;&#x4ED6;&#x81EA;&#x5DF1;&#x7684;&#x539F;&#x56E0;&#xFF09;&#x3002;<br>
&#x4E8E;&#x662F;&#xFF0C;&#x6211;&#x628A;&#x662F;&#x5426;&#x8F6C;&#x53D1;&#x4E00;&#x4E2A;&#x5E94;&#x7528;&#x7684;&#x53EA;&#x663E;&#x793A;&#x5728;&#x672C;&#x5730;&#x7684;&#x901A;&#x77E5;&#x4F5C;&#x4E3A;&#x9009;&#x9879;&#x52A0;&#x8FDB;<code>kdeconnect-android</code>&#xFF0C;&#x5E76;&#x4E14;&#x53D1;&#x8D77;MergeRequest&#xFF0C;&#x4F46;<code>kdeconnect-android</code>&#x7684;&#x5F00;&#x53D1;&#x8005;&#x4EEC;&#x8BA4;&#x4E3A;&#x8FD9;&#x4E48;&#x505A;&#x8FC7;&#x4E8E;&#x9EBB;&#x70E6;&#xFF0C;&#x5E76;&#x4E14;&#x8BA4;&#x4E3A;&#x6CA1;&#x6709;&#x8FBE;&#x5230;&#x4FEE;&#x590D;&quot;bug&quot;&#x7684;&#x76EE;&#x7684;&#xFF08;&#x7528;&#x6237;&#x8FD8;&#x8981;&#x53BB;&#x9009;&#x62E9;&#x6253;&#x5F00;&#x9009;&#x9879;&#xFF0C;&#x5FAE;&#x4FE1;&#x901A;&#x77E5;&#x624D;&#x4F1A;&#x8F6C;&#x53D1;&#xFF09;&#xFF0C;&#x4F46;&#x6211;&#x53C8;&#x8BA4;&#x4E3A;&#x628A;&#x5FAE;&#x4FE1;hardcode&#x8FDB;&#x4EE3;&#x7801;&#x5F88;&#x6076;&#x5FC3;&#xFF0C;&#x4E5F;&#x4E0D;&#x8BA4;&#x540C;&#x8FD9;&#x662F;bug&#xFF0C;&#x6240;&#x4EE5;&#x53EA;&#x80FD;&#x5173;&#x4E86;MergeRequest&#xFF0C;&#x81EA;&#x5DF1;&#x7EF4;&#x62A4;&#x4E00;&#x4EFD;&#x9B54;&#x6539;&#x4EE3;&#x7801;&#x3002;</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Apache的httpclient长连接泄漏]]></title><description><![CDATA[16年刚进公司的时候，我曾经做了个需求，把请求微信支付改为长连接。我们使用*Apache*的*httpclient*，改起来还是很容易。上线后，效果也挺显著，平均请求耗时降低了几十毫秒。可惜好景不长，程序跑了几天后，耗时变得越来越长，比短连接时还慢]]></description><link>https://fortime.fyi/blog/20180907-01/</link><guid isPermaLink="false">6661f49b8cd2840e638166c7</guid><category><![CDATA[技术]]></category><category><![CDATA[Java]]></category><category><![CDATA[Web]]></category><dc:creator><![CDATA[fortime]]></dc:creator><pubDate>Sun, 23 Sep 2018 16:34:56 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>16&#x5E74;&#x521A;&#x8FDB;&#x516C;&#x53F8;&#x7684;&#x65F6;&#x5019;&#xFF0C;&#x6211;&#x66FE;&#x7ECF;&#x505A;&#x4E86;&#x4E2A;&#x9700;&#x6C42;&#xFF0C;&#x628A;&#x8BF7;&#x6C42;&#x5FAE;&#x4FE1;&#x652F;&#x4ED8;&#x6539;&#x4E3A;&#x957F;&#x8FDE;&#x63A5;&#x3002;&#x6211;&#x4EEC;&#x4F7F;&#x7528;<em>Apache</em>&#x7684;<em>httpclient</em>&#xFF0C;&#x6539;&#x8D77;&#x6765;&#x8FD8;&#x662F;&#x5F88;&#x5BB9;&#x6613;&#x3002;&#x4E0A;&#x7EBF;&#x540E;&#xFF0C;&#x6548;&#x679C;&#x4E5F;&#x633A;&#x663E;&#x8457;&#xFF0C;&#x5E73;&#x5747;&#x8BF7;&#x6C42;&#x8017;&#x65F6;&#x964D;&#x4F4E;&#x4E86;&#x51E0;&#x5341;&#x6BEB;&#x79D2;&#x3002;&#x53EF;&#x60DC;&#x597D;&#x666F;&#x4E0D;&#x957F;&#xFF0C;&#x7A0B;&#x5E8F;&#x8DD1;&#x4E86;&#x51E0;&#x5929;&#x540E;&#xFF0C;&#x8017;&#x65F6;&#x53D8;&#x5F97;&#x8D8A;&#x6765;&#x8D8A;&#x957F;&#xFF0C;&#x6BD4;&#x77ED;&#x8FDE;&#x63A5;&#x65F6;&#x8FD8;&#x6162;&#x3002;<br>
&#x6211;&#x8BA9;&#x8FD0;&#x7EF4;&#x8DD1;<code>netstat</code>&#x540E;&#xFF0C;&#x53D1;&#x73B0;&#x4E0E;&#x5FAE;&#x4FE1;&#x7684;&#x8FDE;&#x63A5;&#x6709;&#x597D;&#x51E0;&#x4E07;&#x6761;&#xFF0C;&#x90FD;&#x662F;<em>CLOSE-WAIT</em>&#xFF0C;&#x4E00;&#x770B;&#x5C31;&#x662F;&#x8FDE;&#x63A5;&#x6CC4;&#x6F0F;&#x7684;&#x95EE;&#x9898;&#x3002;&#x4F46;&#x8FD9;&#x5C31;&#x5947;&#x602A;&#x4E86;&#xFF0C;&#x5E73;&#x5E38;&#x77ED;&#x8FDE;&#x63A5;&#x8DD1;&#x7684;&#x90FD;&#x662F;&#x597D;&#x597D;&#x7684;&#xFF0C;&#x4E3A;&#x4EC0;&#x4E48;&#x6362;&#x6210;&#x957F;&#x8FDE;&#x63A5;&#x5C31;&#x6CC4;&#x6F0F;&#x4E86;&#xFF1F;&#x96BE;&#x9053;&#x77ED;&#x8FDE;&#x63A5;&#x5173;&#x95ED;&#x7684;&#x65B9;&#x6CD5;&#xFF0C;&#x4E0D;&#x9002;&#x7528;&#x4E8E;&#x957F;&#x8FDE;&#x63A5;&#xFF1F;&#x4E8E;&#x662F;&#x5C31;&#x53BB;<code>git clone</code>&#x4E00;&#x4E0B;<em>httpclient</em>&#x7684;&#x4EE3;&#x7801;&#xFF0C;&#x5E76;<code>git checkout</code>&#x5230;&#x6211;&#x4EEC;&#x4F7F;&#x7528;&#x7684;&#x7248;&#x672C;&#xFF08;<code>git</code>&#x771F;&#x597D;&#x7528;&#xFF0C;&#x4E3A;&#x4EC0;&#x4E48;&#x6211;&#x4EEC;&#x516C;&#x53F8;&#x8FD8;&#x8981;&#x7528;<code>svn</code>&#x5462;&#xFF0C;&#x6BCF;&#x6B21;&#x770B;&#x7740;&#x540C;&#x4E8B;&#x5207;&#x6362;&#x4E2A;&#x5206;&#x652F;&#x5C31;&#x611F;&#x89C9;&#x9EBB;&#x70E6;&#xFF09;&#x3002;&#x7ECF;&#x8FC7;&#x4E00;&#x4E2A;&#x661F;&#x671F;&#x5404;&#x79CD;&#x5999;&#x60F3;&#x5929;&#x5F00;&#x7684;&#x5728;&#x4EE3;&#x7801;&#x91CC;&#x627E;&#x6CC4;&#x6F0F;&#x7684;&#x539F;&#x56E0;&#x548C;&#x5C1D;&#x8BD5;&#xFF0C;&#x6700;&#x7EC8;&#x8FD8;&#x662F;&#x6CA1;&#x6709;&#x627E;&#x5230;&#xFF0C;&#x53EA;&#x80FD;&#x7528;&#x56DE;&#x77ED;&#x8FDE;&#x63A5;&#x3002;<br>
&#x6700;&#x8FD1;&#xFF0C;&#x5728;&#x7814;&#x7A76;&#x5FAE;&#x4FE1;&#x5546;&#x6237;&#x8BC1;&#x4E66;&#x8FC7;&#x671F;&#x66F4;&#x65B0;<em>httpclient</em>&#x5BF9;&#x8C61;&#x65F6;&#xFF0C;&#x7A81;&#x7136;&#x60F3;&#x5230;&#xFF0C;&#x53CC;&#x5411;&#x8BC1;&#x4E66;&#x8FD9;&#x79CD;&#x60C5;&#x51B5;&#x7684;&#x957F;&#x8FDE;&#x63A5;&#x662F;&#x600E;&#x4E48;&#x590D;&#x7528;&#x7684;&#x5462;&#xFF1F;&#x611F;&#x89C9;&#x597D;&#x50CF;&#x53EF;&#x4EE5;&#x627E;&#x5230;&#x4E24;&#x5E74;&#x524D;&#x5FC3;&#x7ED3;&#x7684;&#x539F;&#x56E0;&#xFF0C;&#x6211;&#x5C31;&#x628A;&#x7CFB;&#x7EDF;&#x914D;&#x7F6E;&#x4E3A;&#x957F;&#x8FDE;&#x63A5;&#xFF0C;&#x7136;&#x540E;&#x53D1;&#x8D77;&#x591A;&#x6B21;&#x5FAE;&#x4FE1;&#x9000;&#x6B3E;&#x3002;&#x6BCF;&#x53D1;&#x8D77;&#x4E00;&#x6B21;&#xFF0C;&#x8FDE;&#x63A5;&#x5C31;&#x589E;&#x52A0;&#x4E00;&#x6761;&#xFF0C;&#x8FC7;&#x4E00;&#x6BB5;&#x65F6;&#x95F4;&#x540E;&#x5C31;&#x4F1A;&#x53D8;&#x4E3A;<em>CLOSE-WAIT</em>&#x3002;</p>
<h2 id="state">&#x8FDE;&#x63A5;&#x7684;<em>state</em></h2>
<p>MainClientExec&#x7684;execute&#x51FD;&#x6570;&#xFF0C;&#x5728;&#x83B7;&#x53D6;&#x8FDE;&#x63A5;&#x6C60;&#x7684;&#x8FDE;&#x63A5;&#x65F6;&#xFF0C;&#x4F1A;&#x6839;&#x636E;&#x8DEF;&#x7531;&#x548C;&#x8FDE;&#x63A5;&#x7684;<em>state</em>&#x6765;&#x9009;&#x53D6;&#x3002;&#x8FD9;&#x91CC;&#x7684;<em>state</em>&#x4E3A;&#x4E0A;&#x4E0B;&#x6587;&#x7684;<code>userToken</code>&#x3002;</p>
<pre><code class="language-java">Object userToken = context.getUserToken();

final ConnectionRequest connRequest = connManager.requestConnection(route, userToken);
</code></pre>
<p>MainClientExec&#x7684;execute&#x51FD;&#x6570;&#xFF0C;&#x5728;&#x8FD4;&#x56DE;response&#x524D;&#xFF0C;&#x4F1A;&#x83B7;&#x53D6;<code>userToken</code>&#xFF0C;&#x5E76;&#x8BBE;&#x7F6E;&#x4E3A;&#x8FDE;&#x63A5;&#x7684;state&#x3002;</p>
<pre><code class="language-java">if (userToken == null) {
    userToken = userTokenHandler.getUserToken(context);
    context.setAttribute(HttpClientContext.USER_TOKEN, userToken);
}
if (userToken != null) {
    connHolder.setState(userToken);
}

// check for entity, release connection if possible
final HttpEntity entity = response.getEntity();
if (entity == null || !entity.isStreaming()) {
    // connection not needed and (assumed to be) in re-usable state
    connHolder.releaseConnection();
    return new HttpResponseProxy(response, null);
} else {
    return new HttpResponseProxy(response, connHolder);
</code></pre>
<p>&#x5BF9;&#x4E8E;&#x53CC;&#x5411;&#x8BC1;&#x4E66;&#x7684;&#x60C5;&#x51B5;&#xFF0C;<code>userToken</code>&#x4F1A;&#x662F;&#x4EC0;&#x4E48;&#x5462;&#xFF1F;</p>
<pre><code class="language-java">public Object getUserToken(final HttpContext context) {

    final HttpClientContext clientContext = HttpClientContext.adapt(context);

    Principal userPrincipal = null;

    final AuthState targetAuthState = clientContext.getTargetAuthState();
    if (targetAuthState != null) {
        userPrincipal = getAuthPrincipal(targetAuthState);
        if (userPrincipal == null) {
            final AuthState proxyAuthState = clientContext.getProxyAuthState();
            userPrincipal = getAuthPrincipal(proxyAuthState);
        }   
    }   

    if (userPrincipal == null) {
        final HttpConnection conn = clientContext.getConnection();
        if (conn.isOpen() &amp;&amp; conn instanceof ManagedHttpClientConnection) {
            final SSLSession sslsession = ((ManagedHttpClientConnection) conn).getSSLSession();
            if (sslsession != null) {
                userPrincipal = sslsession.getLocalPrincipal();
            }   
        }   
    }   

    return userPrincipal;
}
</code></pre>
<p>&#x9996;&#x5148;&#xFF0C;&#x7CFB;&#x7EDF;&#x4F1A;&#x5224;&#x65AD;&#x8FDE;&#x63A5;&#x662F;&#x5426;&#x4F7F;&#x7528;&#x4E86;http&#x7684;&#x9274;&#x6743;&#xFF0C;&#x5047;&#x5982;&#x4F7F;&#x7528;&#x4E86;&#x5C31;&#x4F7F;&#x7528;&#x9274;&#x6743;&#x7528;&#x6237;&#x4E3B;&#x4F53;&#x3002;&#x5426;&#x5219;&#xFF0C;&#x5C31;&#x68C0;&#x67E5;&#x662F;&#x5426;&#x6709;SSLSession&#xFF0C;&#x5047;&#x5982;&#x6709;&#xFF0C;&#x5C31;&#x53D6;SSLSession&#x7684;&#x672C;&#x5730;&#x8BC1;&#x4E66;&#x7684;subject&#x4F5C;&#x4E3A;<code>userToken</code>&#x3002;&#x90A3;&#x4E48;&#xFF0C;&#x53CC;&#x5411;&#x8BC1;&#x4E66;&#x7684;&#x60C5;&#x51B5;&#x5C31;&#x662F;&#x4F7F;&#x7528;&#x5546;&#x6237;&#x8BC1;&#x4E66;&#x7684;subject&#x3002;</p>
<h2 id>&#x603B;&#x7ED3;</h2>
<p>&#x4E0A;&#x4E0B;&#x6587;&#x91CC;&#x6CA1;&#x6709;&#x8BBE;&#x7F6E;<code>userToken</code>&#xFF0C;&#x53BB;&#x8FDE;&#x63A5;&#x6C60;&#x53D6;&#x8FDE;&#x63A5;&#x65F6;&#xFF0C;&#x90FD;&#x662F;&#x53D6;<em>state</em>&#x4E3A;&#x7A7A;&#x7684;&#x8FDE;&#x63A5;&#xFF0C;&#x4F46;&#x7531;&#x4E8E;&#x8FDE;&#x63A5;&#x4F7F;&#x7528;&#x8FC7;&#x540E;&#x90FD;&#x4F1A;&#x8BBE;&#x7F6E;&#x8FDE;&#x63A5;&#x7684;state&#x4E3A;&#x5546;&#x6237;&#x8BC1;&#x4E66;&#x7684;subject&#x3002;&#x6240;&#x4EE5;&#x6BCF;&#x6B21;&#x83B7;&#x53D6;&#x7684;&#x90FD;&#x662F;&#x65B0;&#x8FDE;&#x63A5;&#x3002;&#x7ECF;&#x8FC7;&#x8FD9;&#x6B21;&#x7684;&#x7ECF;&#x9A8C;&#xFF0C;<em><strong>&#x8FDE;&#x63A5;&#x6CC4;&#x6F0F;&#xFF0C;&#x4E0D;&#x4E00;&#x5B9A;&#x662F;&#x7531;&#x4E8E;&#x8FDE;&#x63A5;&#x6CA1;&#x6709;&#x5F52;&#x8FD8;&#xFF0C;&#x4E5F;&#x53EF;&#x80FD;&#x662F;&#x590D;&#x7528;&#x6CA1;&#x6709;&#x751F;&#x6548;&#x3002;</strong></em></p>
<h2 id>&#x89E3;&#x51B3;&#x65B9;&#x6848;</h2>
<p>&#x7531;&#x4E8E;&#x4F7F;&#x7528;&#x53CC;&#x5411;&#x8BC1;&#x4E66;&#x65F6;&#xFF0C;&#x90FD;&#x662F;&#x4E00;&#x4E2A;&#x5546;&#x6237;&#x751F;&#x6210;&#x4E00;&#x4E2A;<em>httpclient</em>&#x5BF9;&#x8C61;&#xFF0C;&#x6211;&#x4EEC;&#x5728;&#x8BBE;&#x7F6E;&#x8BC1;&#x4E66;&#x7684;&#x65F6;&#x5019;&#x53EF;&#x4EE5;&#x628A;&#x5546;&#x6237;&#x8BC1;&#x4E66;&#x7684;subject&#x8BBE;&#x7F6E;&#x4E3A;<code>userToken</code>&#x3002;</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[偶尔一下的折腾]]></title><description><![CDATA[是社会道德的沦丧，还是人性的缺失，今天fortime带你探索消失的网页微信通知和不能横排的ibus-rime]]></description><link>https://fortime.fyi/blog/20180720-01/</link><guid isPermaLink="false">6661f49b8cd2840e638166c4</guid><category><![CDATA[技术]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[fortime]]></dc:creator><pubDate>Sat, 28 Jul 2018 15:06:15 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h1 id>&#x6D88;&#x5931;&#x7684;&#x7F51;&#x9875;&#x5FAE;&#x4FE1;&#x901A;&#x77E5;</h1>
<p>&#x7531;&#x4E8E;<em>firefox</em>&#x7528;&#x7740;&#x7528;&#x7740;&#x5C31;&#x5360;&#x8D8A;&#x6765;&#x8D8A;&#x591A;&#x7684;&#x5185;&#x5B58;&#xFF0C;&#x5076;&#x5C14;&#x9700;&#x8981;&#x91CD;&#x542F;&#x4E00;&#x4E0B;&#xFF0C;&#x5728;&#x4E0A;&#x9762;&#x7528;&#x7F51;&#x9875;&#x5FAE;&#x4FE1;&#x7684;&#x8BDD;&#xFF0C;&#x53C8;&#x8981;&#x91CD;&#x65B0;&#x767B;&#x5F55;&#xFF0C;&#x5F53;&#x5929;&#x7684;&#x804A;&#x5929;&#x8BB0;&#x5F55;&#x53C8;&#x4F1A;&#x6CA1;&#x6709;&#x4E86;&#x3002;&#x6240;&#x4EE5;&#xFF0C;&#x6211;&#x6700;&#x8FD1;&#x7528;<em>epiphany</em>&#x7684;app&#x6A21;&#x5F0F;&#x4F7F;&#x7528;&#x7F51;&#x9875;&#x5FAE;&#x4FE1;&#x3002;&#x7528;&#x7740;&#x7528;&#x7740;&#xFF0C;&#x53D1;&#x73B0;&#x7ECF;&#x5E38;&#x6F0F;&#x6D88;&#x606F;&#xFF0C;&#x901A;&#x77E5;&#x680F;&#x7684;&#x6D88;&#x606F;&#x7ECF;&#x5E38;&#x4F1A;&#x6D88;&#x5931;&#x3002;&#x96BE;&#x9053;&#x662F;&#x56E0;&#x4E3A;<em>webkit2gtk</em>&#x6CA1;&#x6709;&#x5B9E;&#x73B0;&#x597D;<em>libnotify</em>&#xFF0C;&#x8BBE;&#x7F6E;&#x7684;expire&#x65F6;&#x95F4;&#x4E0D;&#x5BF9;&#xFF1F;</p>
<h2 id="gnomeshell">&#x6CA1;&#x6709;&#x8FC7;&#x671F;&#x65F6;&#x95F4;&#x7684;<em>GnomeShell</em>&#x901A;&#x77E5;&#x5B9E;&#x73B0;</h2>
<p>&#x901A;&#x8FC7;<em>grep</em>&#xFF0C;&#x5728;<em>WebKitWebView.cpp</em>&#x7684;<code>webkitWebViewShowNotification</code>&#x4E0B;&#x627E;&#x5230;&#x4E86;&#x8C03;&#x7528;<em>libnotify</em>&#x7684;<code>notify_notification_show</code>&#x7684;&#x4EE3;&#x7801;&#x3002;</p>
<pre><code class="language-cpp">static gboolean webkitWebViewShowNotification(WebKitWebView*, WebKitNotification* webNotification)
{
#if USE(LIBNOTIFY)
    if (!notify_is_initted())
        notify_init(g_get_prgname());

    NotifyNotification* notification = NOTIFY_NOTIFICATION(g_object_get_data(G_OBJECT(webNotification), gNotifyNotificationID))
;
    if (!notification) {
        notification = notify_notification_new(webkit_notification_get_title(webNotification),
            webkit_notification_get_body(webNotification), nullptr);

        notify_notification_add_action(notification, &quot;default&quot;, _(&quot;Acknowledge&quot;), NOTIFY_ACTION_CALLBACK(notifyNotificationClic
ked), webNotification, nullptr);
        notify_notification_set_timeout(notification, NOTIFY_EXPIRES_NEVER);

        g_signal_connect_object(notification, &quot;closed&quot;, G_CALLBACK(notifyNotificationClosed), webNotification, static_cast&lt;GCon
nectFlags&gt;(0));
        g_signal_connect(webNotification, &quot;closed&quot;, G_CALLBACK(webNotificationClosed), nullptr);
        g_object_set_data_full(G_OBJECT(webNotification), gNotifyNotificationID, notification, static_cast&lt;GDestroyNotify&gt;(g_ob
ject_unref));
    } else {
        notify_notification_update(notification, webkit_notification_get_title(webNotification),
            webkit_notification_get_body(webNotification), nullptr);
        notify_notification_set_timeout(notification, NOTIFY_EXPIRES_NEVER);
    }

    notify_notification_show(notification, nullptr);
    return TRUE;
#else
    UNUSED_PARAM(webNotification);
    return FALSE;
    #endif
}
</code></pre>
<p>&#x54C8;&#x54C8;&#xFF0C;&#x679C;&#x7136;&#x6CA1;&#x6709;&#x8C03;&#x7528;<em>libnotify</em>&#x7684;<code>notify_notification_set_timeout</code>&#xFF0C;&#x679C;&#x65AD;&#x8BBE;&#x7F6E;&#x4E3A;<code>NOTIFY_EXPIRES_NEVER</code>&#x3002;&#x6253;&#x5305;&#xFF0C;&#x6D4B;&#x8BD5;&#xFF0C;GG&#x1F62D;&#x3002;&#x96BE;&#x9053;&#x4E0D;&#x80FD;&#x6C38;&#x4E45;&#x4E0D;&#x8FC7;&#x671F;&#xFF1F;&#x6765;&#x4E00;&#x4E0B;60&#x79D2;&#xFF0C;&#x6253;&#x5305;&#xFF0C;&#x6D4B;&#x8BD5;&#xFF0C;GG&#x1F62D;&#x3002;Google&#x4E00;&#x4E0B;&#xFF0C;<em>GnomeShell</em>&#x6CA1;&#x6709;&#x5B9E;&#x73B0;<em>libnotify</em>&#x7684;&#x8FC7;&#x671F;&#x1F62B;&#x3002;&#x96BE;&#x9053;<em>firefox</em>&#x6709;&#x4EC0;&#x4E48;&#x9ED1;&#x79D1;&#x6280;&#xFF0C;&#x5B83;&#x662F;&#x600E;&#x4E48;&#x5B9E;&#x73B0;&#x901A;&#x77E5;&#x7684;&#xFF1F;</p>
<h2 id="firefoxwebkit2gtk"><em>firefox</em>&#x4E0E;<em>webkit2gtk</em>&#x7684;&#x901A;&#x77E5;&#x5B9E;&#x73B0;&#x6BD4;&#x8F83;</h2>
<pre><code class="language-cpp">
nsresult
nsAlertsIconListener::ShowAlert(GdkPixbuf* aPixbuf)
{
  if (!mBackend-&gt;IsActiveListener(mAlertName, this))
    return NS_OK;

  mNotification = notify_notification_new(mAlertTitle.get(), mAlertText.get(),
                                          nullptr, nullptr);

  if (!mNotification)
    return NS_ERROR_OUT_OF_MEMORY;

  nsCOMPtr&lt;nsIObserverService&gt; obsServ =
      do_GetService(&quot;@mozilla.org/observer-service;1&quot;);
  if (obsServ)
    obsServ-&gt;AddObserver(this, &quot;quit-application&quot;, true);

  if (aPixbuf)
    notify_notification_set_icon_from_pixbuf(mNotification, aPixbuf);

  NS_ADDREF(this);
  if (mAlertHasAction) {
    // What we put as the label doesn&apos;t matter here, if the action
    // string is &quot;default&quot; then that makes the entire bubble clickable
    // rather than creating a button.
    notify_notification_add_action(mNotification, &quot;default&quot;, &quot;Activate&quot;,
                                   notify_action_cb, this, nullptr);
  }

  // Fedora 10 calls NotifyNotification &quot;closed&quot; signal handlers with a
  // different signature, so a marshaller is used instead of a C callback to
  // get the user_data (this) in a parseable format.  |closure| is created
  // with a floating reference, which gets sunk by g_signal_connect_closure().
  GClosure* closure = g_closure_new_simple(sizeof(GClosure), this);
  g_closure_set_marshal(closure, notify_closed_marshal);
  mClosureHandler = g_signal_connect_closure(mNotification, &quot;closed&quot;, closure, FALSE);
  GError* error = nullptr;
  if (!notify_notification_show(mNotification, &amp;error)) {
    NS_WARNING(error-&gt;message);
    g_error_free(error);
    return NS_ERROR_FAILURE;
  }

  if (mAlertListener)
    mAlertListener-&gt;Observe(nullptr, &quot;alertshow&quot;, mAlertCookie.get());

  return NS_OK;
}
</code></pre>
<p>&#x54C8;&#xFF0C;&#x4E5F;&#x662F;&#x6CA1;&#x6709;&#x8BBE;&#x7F6E;&#x8D85;&#x65F6;&#x3002;&#x800C;&#x4E14;&#x8FD8;&#x6BD4;<em>webkit2gtk</em>&#x5C11;&#x4E86;&#x6CE8;&#x518C;&#x4E00;&#x4E9B;&#x56DE;&#x8C03;&#x3002;&#x5982;</p>
<pre><code class="language-cpp">g_signal_connect(webNotification, &quot;closed&quot;, G_CALLBACK(webNotificationClosed), nullptr);
</code></pre>
<p>&#x5904;&#x7406;&#x7F51;&#x9875;&#x5173;&#x95ED;&#x901A;&#x77E5;&#x7684;&#x56DE;&#x8C03;&#x5904;&#x7406;&#x3002;</p>
<h2 id="doeverythingbutnotgoodenough">&#x5FAE;&#x4FE1; - Do everything, but not good enough</h2>
<p>&#x65E2;&#x7136;<em>firefox</em>&#x6CA1;&#x6709;&#x9ED1;&#x79D1;&#x6280;&#xFF0C;&#x90A3;&#x6211;&#x53EA;&#x80FD;&#x7EE7;&#x7EED;&#x6D4B;&#x8BD5;&#x4E86;&#x3002;&#x6BCF;&#x6B21;&#x6D4B;&#x8BD5;&#x8FD8;&#x8981;&#x627E;&#x522B;&#x4EBA;&#x53D1;&#x5FAE;&#x4FE1;&#x6D88;&#x606F;&#x7ED9;&#x6211;&#xFF0C;&#x592A;&#x9EBB;&#x70E6;&#x4E86;&#xFF0C;&#x81EA;&#x5DF1;&#x5199;&#x4E2A;button&#x6765;&#x89E6;&#x53D1;&#x901A;&#x77E5;&#x3002;&#x95EE;&#x9898;&#x6765;&#x4E86;&#xFF0C;&#x6211;&#x81EA;&#x5DF1;&#x7684;&#x6D4B;&#x8BD5;&#x7F51;&#x9875;&#x53D1;&#x7684;&#x901A;&#x77E5;&#x6CA1;&#x6709;&#x6D88;&#x5931;&#x3002;&#x662F;&#x65F6;&#x5019;&#x770B;&#x4E00;&#x4E0B;&#x5FAE;&#x4FE1;&#x600E;&#x4E48;&#x53D1;&#x6D88;&#x606F;&#x4E86;&#x3002;</p>
<pre><code class="language-js">function c(e, n) { 
    h.length &gt;= M.total &amp;&amp; h.shift().close();
    var o, c;
    return g &amp;&amp; r() &amp;&amp; angular.isString(e) &amp;&amp; n &amp;&amp; (angular.isString(n.icon) || angular.isObject(n.icon)) &amp;&amp; i() === l &amp;&amp; (o = t(e, n)), c = a(o), h.push(c), M.autoClose &amp;&amp; o &amp;&amp; !o.ieVerification &amp;&amp; o.addEventListener &amp;&amp; o.addEventListener(&quot;show&quot;, function() {
        var e = c;  
        setTimeout(function() {
            e.close()
        }, M.autoClose)
    }), o
}
</code></pre>
<p><em><strong>&#x5FC3;&#x91CC;&#x4E07;&#x9A6C;&#x5954;&#x817E;&#xFF0C;&#x6211;&#x600E;&#x4E48;&#x4E0D;&#x65E9;&#x70B9;&#x770B;&#x5FAE;&#x4FE1;&#x4EE3;&#x7801;</strong></em>&#x3002;&#x4F30;&#x8BA1;&#x662F;&#x4E0D;&#x540C;&#x5E73;&#x53F0;&#x5904;&#x7406;&#x901A;&#x77E5;&#x7684;&#x903B;&#x8F91;&#x4E0D;&#x4E00;&#x6837;&#xFF0C;&#x5FAE;&#x4FE1;&#x7F51;&#x9875;&#x7248;&#x81EA;&#x5DF1;&#x7EF4;&#x62A4;&#x4E86;&#x901A;&#x77E5;&#x5217;&#x8868;&#xFF0C;&#x5E76;5&#x79D2;&#x81EA;&#x52A8;&#x5931;&#x6548;&#x3002;<strong>&#x7531;&#x4E8E;<em>firefox</em>&#x6CA1;&#x6709;&#x628A;&#x7F51;&#x9875;&#x7684;&#x901A;&#x77E5;&#x5173;&#x95ED;signal&#x8F6C;&#x53D1;&#x7ED9;<em>libnotify</em>&#xFF0C;&#x6240;&#x4EE5;&#x7CFB;&#x7EDF;&#x7684;&#x901A;&#x77E5;&#x6CA1;&#x6709;&#x6D88;&#x5931;&#xFF0C;&#x800C;&#x7531;&#x4E8E;<em>webkit2gtk</em>&#x5B9E;&#x73B0;&#x7684;&#x5B8C;&#x6574;&#xFF0C;&#x6240;&#x4EE5;&#x901A;&#x77E5;5&#x79D2;&#x540E;&#x6D88;&#x5931;&#x4E86;</strong>&#x3002;&#x8FD9;&#x6837;magic&#x7684;&#x903B;&#x8F91;&#xFF0C;&#x5FAE;&#x4FE1;&#x5E94;&#x8BE5;&#x52A0;&#x4E2A;&#x5F00;&#x5173;&#x554A;&#x3002;</p>
<h2 id>&#x89E3;&#x51B3;&#x65B9;&#x6CD5;</h2>
<p>F12&#x6267;&#x884C;&#x4E00;&#x4E0B;<code>angular.element(document.querySelector(&apos;html&apos;)).injector().get(&apos;notificationFactory&apos;).config().autoClose=0</code>&#x628A;&#x8D85;&#x65F6;&#x8BBE;&#x7F6E;&#x4E3A;0&#xFF0C;&#x5C31;&#x4E0D;&#x4F1A;&#x81EA;&#x52A8;&#x6D88;&#x5931;&#x4E86;&#x3002;</p>
<h2 id="todo">TODO</h2>
<ol>
<li>&#x5982;&#x4F55;&#x81EA;&#x52A8;&#x6267;&#x884C;F12&#x90A3;&#x884C;&#x5462;&#xFF1F;<em>epiphany</em>&#x6CA1;&#x6709;greasemonkey&#x3002;</li>
<li>webkit2gtk&#x901A;&#x77E5;&#x6CA1;&#x6709;&#x663E;&#x793A;icon&#xFF0C;&#x600E;&#x4E48;&#x53BB;&#x5230;pixbuf&#x6765;&#x663E;&#x793A;&#x5462;&#xFF1F;</li>
<li>&#x70B9;&#x51FB;&#x901A;&#x77E5;&#xFF0C;&#x5982;&#x4F55;bring forth&#x7A97;&#x53E3;&#x5462;&#xFF1F;</li>
</ol>
<h1 id="ibusrime">&#x4E0D;&#x80FD;&#x6A2A;&#x6392;&#x7684;<em>ibus-rime</em></h1>
<p>&#x597D;&#x50CF;&#x54EA;&#x6B21;&#x66F4;&#x65B0;ibus&#x540E;&#xFF0C;<em>Google Docs</em>&#x4E0A;&#x8F93;&#x5165;&#x6CA1;&#x6709;&#x5019;&#x9009;&#x5B57;&#x3002;&#x6211;&#x5C31;&#x60F3;&#x6539;&#x4E00;&#x4E0B;ibus&#x7684;&#x914D;&#x7F6E;&#xFF0C;&#x770B;&#x770B;&#x80FD;&#x4E0D;&#x80FD;&#x597D;&#x8F6C;&#x3002;&#x4E0D;&#x8FC7;&#xFF0C;&#x4EE5;&#x524D;&#x6211;&#x60F3;&#x6539;&#x4E00;&#x4E0B;<em>ibus-rime</em>&#x7684;&#x65B9;&#x5411;&#x4E00;&#x76F4;&#x90FD;&#x6CA1;&#x6709;&#x6210;&#x529F;&#x8FC7;&#x3002;&#x8FD9;&#x6B21;&#x6211;&#x5728;default.custom.yaml&#x548C;&#x8F93;&#x5165;&#x6CD5;&#x7684;custom.yaml&#x4E0A;&#x52A0;<code>style/horizontal: true</code>&#xFF0C;&#x55EF;&#xFF0C;&#x8FD8;&#x662F;&#x6CA1;&#x6709;&#x6210;&#x529F;&#x3002;&#x662F;&#x4E0D;&#x662F;&#x7531;&#x4E8E;<code>true</code>&#x4E0D;&#x5BF9;&#x5462;&#xFF1F;&#x6211;&#x628A;&#x5404;&#x79CD;true&#x90FD;&#x8BD5;&#x4E86;&#xFF0C;&#x8FD8;&#x662F;&#x4E0D;&#x884C;&#x3002;<br>
&#x53C8;&#x8981;&#x65BD;&#x5C55;&#x6E90;&#x4EE3;&#x7801;&#x5927;&#x6CD5;</p>
<pre><code class="language-c">// rime_settings.c
#include &quot;rime_config.h&quot;
#include &lt;string.h&gt;
#include &lt;ibus.h&gt;
#include &lt;rime_api.h&gt;
#include &quot;rime_settings.h&quot;

static struct ColorSchemeDefinition preset_color_schemes[] = {
  { &quot;aqua&quot;, 0xffffff, 0x0a3dfa },
  { &quot;azure&quot;, 0xffffff, 0x0a3dea },
  { &quot;ink&quot;, 0xffffff, 0x000000 },
  { &quot;luna&quot;, 0x000000, 0xffff7f },
  { NULL, 0, 0 }
};

static struct IBusRimeSettings ibus_rime_settings_default = {
  FALSE,
  IBUS_ORIENTATION_SYSTEM,
  &amp;preset_color_schemes[0],
};

struct IBusRimeSettings g_ibus_rime_settings;

static void
select_color_scheme(struct IBusRimeSettings* settings,
		    const char* color_scheme_id)
{
  struct ColorSchemeDefinition* c;
  for (c = preset_color_schemes; c-&gt;color_scheme_id; ++c) {
    if (!strcmp(c-&gt;color_scheme_id, color_scheme_id)) {
      settings-&gt;color_scheme = c;
      g_debug(&quot;selected color scheme: %s&quot;, color_scheme_id);
      return;
    }
  }
  // fallback to default
  settings-&gt;color_scheme = &amp;preset_color_schemes[0];
}

void
ibus_rime_load_settings()
{
  g_ibus_rime_settings = ibus_rime_settings_default;

  RimeConfig config = {0};
  if (!RimeConfigOpen(&quot;ibus_rime&quot;, &amp;config)) {
    g_error(&quot;error loading settings for ibus_rime&quot;);
    return;
  }

  Bool inline_preedit = False;
  if (RimeConfigGetBool(&amp;config, &quot;style/inline_preedit&quot;, &amp;inline_preedit)) {
    g_ibus_rime_settings.embed_preedit_text = !!inline_preedit;
  }

  Bool horizontal = False;
  if (RimeConfigGetBool(&amp;config, &quot;style/horizontal&quot;, &amp;horizontal)) {
    g_ibus_rime_settings.lookup_table_orientation =
      horizontal ? IBUS_ORIENTATION_HORIZONTAL : IBUS_ORIENTATION_VERTICAL;
  }

  const char* color_scheme =
    RimeConfigGetCString(&amp;config, &quot;style/color_scheme&quot;);
  if (color_scheme) {
    select_color_scheme(&amp;g_ibus_rime_settings, color_scheme);
  }

  RimeConfigClose(&amp;config);
}
</code></pre>
<p><code>RimeConfigOpen(&quot;ibus_rime&quot;, &amp;config)</code>&#x4E0D;&#x5C31;&#x662F;&#x8BFB;&#x53D6;&#x4E86;rime&#x7684;&#x914D;&#x7F6E;&#x5417;&#xFF0C;&#x90A3;&#x662F;&#x4E3A;&#x4EC0;&#x4E48;&#x6CA1;&#x6709;&#x751F;&#x6548;&#x5462;&#xFF1F;&#x7136;&#x540E;&#x6709;&#x53BB;&#x770B;&#x770B;librime&#xFF0C;&#x7A76;&#x7ADF;&#x662F;&#x600E;&#x4E48;&#x52A0;&#x8F7D;default.yaml&#x7684;&#x3002;&#x770B;&#x7740;&#x770B;&#x7740;&#xFF0C;&#x7A81;&#x7136;&#x60F3;&#x5230;<code>&quot;ibus_rime&quot;</code>&#x662F;&#x4E0D;&#x662F;&#x4E0D;&#x662F;&#x6307;default.yaml&#x5462;&#xFF1F;&#x8BF4;&#x5E72;&#x5C31;&#x5E72;&#xFF0C;&#x521B;&#x5EFA;&#x4E00;&#x4E2A;*~/.config/ibus/rime/build/ibus_rime.yaml*&#xFF0C;&#x5185;&#x5BB9;&#x4E3A;&#xFF1A;</p>
<pre><code class="language-yaml">style:
    horizontal: True
    inline_preedit: True
    color_scheme: azure
</code></pre>
<p>&#x54C7;&#xFF0C;&#x8FD8;&#x771F;&#x751F;&#x6548;&#x4E86;&#xFF0C;inline_preedit&#x4E5F;&#x51FA;&#x6765;&#x4E86;&#xFF0C;&#x989C;&#x8272;&#x4E5F;&#x6709;&#x3002;&#x5728;&#x6D4B;&#x8BD5;&#x7684;&#x8FC7;&#x7A0B;&#x4E2D;&#xFF0C;&#x53D1;&#x73B0;&#x91CD;&#x542F;&#x7CFB;&#x7EDF;&#x540E;&#xFF0C;<em>Google Docs</em>&#x662F;&#x6709;&#x663E;&#x793A;&#x4FAF;&#x9009;&#x5B57;&#x7684;&#xFF0C;&#x4F46;&#x6267;&#x884C;&#x591A;&#x51E0;&#x6B21;<code>ibus-daemon -drx</code>&#x540E;&#xFF0C;&#x53C8;&#x6CA1;&#x6709;&#x4E86;&#x3002;&#x6240;&#x4EE5;&#xFF0C;&#x8FD9;&#x53EF;&#x80FD;&#x662F;ibus&#x67D0;&#x4E9B;&#x5F88;&#x6DF1;&#x7684;&#x903B;&#x8F91;&#x6CA1;&#x6709;&#x5904;&#x7406;&#x597D;&#x3002;</p>
<h1 id>&#x603B;&#x7ED3;</h1>
<p>&#x4EE5;&#x4E0A;&#x8BF4;&#x8FD9;&#x4E48;&#x591A;&#xFF0C;&#x5176;&#x5B9E;&#xFF0C;&#x5C31;&#x662F;&#x4E3A;&#x4E86;&#x8BB0;&#x5F55;&#xFF1A;</p>
<ol>
<li><code>angular.element(document.querySelector(&apos;html&apos;)).injector().get(&apos;notificationFactory&apos;).config().autoClose=0</code></li>
<li>ibus-rime&#x7684;&#x914D;&#x7F6E;&#x6587;&#x4EF6;&#x662F;ibus_rime.yaml&#xFF0C;&#x800C;&#x4E14;&#x8981;&#x653E;build&#x4E0B;&#x3002;</li>
</ol>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[只有公钥的情况下，生成证书]]></title><description><![CDATA[只有公钥的情况下，生成证书]]></description><link>https://fortime.fyi/blog/20180723-01/</link><guid isPermaLink="false">6661f49b8cd2840e638166c5</guid><category><![CDATA[技术]]></category><category><![CDATA[安全]]></category><dc:creator><![CDATA[fortime]]></dc:creator><pubDate>Sat, 28 Jul 2018 11:13:01 GMT</pubDate><content:encoded><![CDATA[<p>&#x4F7F;&#x7528;RSA&#x7B7E;&#x540D;&#x7684;&#x60C5;&#x51B5;&#x8D8A;&#x6765;&#x8D8A;&#x591A;&#xFF0C;&#x6709;&#x516C;&#x53F8;&#x8981;&#x6C42;&#x6211;&#x4EEC;&#x63D0;&#x4F9B;X.509&#x683C;&#x5F0F;&#x8BC1;&#x4E66;&#x3002;&#x6211;&#x4EEC;&#x7684;&#x516C;&#x79C1;&#x94A5;&#x90FD;&#x662F;&#x76F4;&#x63A5;&#x901A;&#x8FC7;JAVA&#x7684;RSA KeyFactory&#x751F;&#x6210;&#x7684;&#xFF0C;&#x53BB;&#x54EA;&#x91CC;&#x627E;&#x8BC1;&#x4E66;&#xFF1F;Google&#x4E00;&#x4E0B;&#x540E;&#xFF0C;&#x6211;&#x53D1;&#x73B0;&#x4F7F;&#x7528;openssl&#x751F;&#x6210;&#x8BC1;&#x4E66;&#xFF0C;&#x8981;&#x4E0D;&#x4F7F;&#x7528;&#x79C1;&#x94A5;&#x81EA;&#x7B7E;&#xFF0C;&#x8981;&#x4E0D;&#x4F7F;&#x7528;CA&#x79C1;&#x94A5;&#x7B7E;&#xFF0C;&#x4F46;&#x4E5F;&#x662F;&#x9700;&#x8981;&#x79C1;&#x94A5;&#x751F;&#x6210;req&#x7684;csr&#x6587;&#x4EF6;&#x3002;<br>
&#x95EE;&#x9898;&#x662F;&#xFF0C;&#x79C1;&#x94A5;&#x751F;&#x6210;&#x540E;&#xFF0C;&#x6211;&#x4EEC;&#x90FD;&#x78B0;&#x4E0D;&#x5230;&#x3002;&#x6700;&#x7EC8;&#xFF0C;&#x6211;&#x53D1;&#x73B0;&#x4E86;openssl&#x7684;<code>-force_pubkey</code>&#xFF0C;&#x53EF;&#x4EE5;&#x5728;&#x4F7F;&#x7528;CA&#x79C1;&#x94A5;&#x7B7E;&#x540D;csr&#x6587;&#x4EF6;&#x751F;&#x6210;&#x8BC1;&#x4E66;&#x65F6;&#xFF0C;&#x66FF;&#x6362;csr&#x6587;&#x4EF6;&#x91CC;&#x7684;&#x516C;&#x94A5;&#x3002;</p>
<pre><code class="language-sh"># &#x5148;&#x751F;&#x6210;CA&#x8BC1;&#x4E66;&#x548C;&#x79C1;&#x94A5;
openssl req -newkey rsa:2048 -nodes -keyout cakey.pem -x509 -out cacert.cer
# &#x751F;&#x6210;&#x516C;&#x53F8;csr&#x6587;&#x4EF6;&#x548C;&#x4E34;&#x65F6;&#x79C1;&#x94A5;
openssl req -newkey rsa:2048 -nodes -keyout prikey.pem -out co.csr
# &#x82E5;&#x751F;&#x6210;csr&#x6587;&#x4EF6;&#x8F93;&#x7684;&#x4FE1;&#x606F;&#x5305;&#x542B;&#x4E2D;&#x6587;&#xFF0C;&#x9700;&#x8981;&#x589E;&#x52A0;&#x53C2;&#x6570;`-utf8`
openssl req -utf8 -newkey rsa:2048 -nodes -keyout prikey.pem -out co.csr
# &#x7B7E;&#x540D;&#x5E76;&#x66FF;&#x6362;&#x516C;&#x94A5;
openssl x509 -req -days 2000 -in co.csr -CAkey cakey.pem -CA cacert.cer -force_pubkey true_pubkey.pem -CAcreateserial -out co.cer
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Google全家桶的奇异设定]]></title><description><![CDATA[年初的时候买了Nokia 7，那个价位不刷机能够获取原生体验为数不多的机器。当时为了激活Google Now，用了网上提到的，禁止GooglePlayService获取电话的权限，当时想着是不是把它的定位权限也禁止了，这样子它就不知道我的位置了，问题也随之而来]]></description><link>https://fortime.fyi/blog/20180518-01/</link><guid isPermaLink="false">6661f49b8cd2840e638166c3</guid><category><![CDATA[技术]]></category><category><![CDATA[Rust]]></category><dc:creator><![CDATA[fortime]]></dc:creator><pubDate>Sun, 03 Jun 2018 14:25:29 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#x5E74;&#x521D;&#x7684;&#x65F6;&#x5019;&#x4E70;&#x4E86;Nokia 7&#xFF0C;&#x90A3;&#x4E2A;&#x4EF7;&#x4F4D;&#x4E0D;&#x5237;&#x673A;&#x80FD;&#x591F;&#x83B7;&#x53D6;&#x539F;&#x751F;&#x4F53;&#x9A8C;&#x4E3A;&#x6570;&#x4E0D;&#x591A;&#x7684;&#x673A;&#x5668;&#x3002;&#x5F53;&#x65F6;&#x4E3A;&#x4E86;&#x6FC0;&#x6D3B;Google Now&#xFF0C;&#x7528;&#x4E86;&#x7F51;&#x4E0A;&#x63D0;&#x5230;&#x7684;&#xFF0C;&#x7981;&#x6B62;GooglePlayService&#x83B7;&#x53D6;<strong>&#x7535;&#x8BDD;</strong>&#x7684;&#x6743;&#x9650;&#xFF0C;&#x5F53;&#x65F6;&#x60F3;&#x7740;&#x662F;&#x4E0D;&#x662F;&#x628A;&#x5B83;&#x7684;<strong>&#x5B9A;&#x4F4D;</strong>&#x6743;&#x9650;&#x4E5F;&#x7981;&#x6B62;&#x4E86;&#xFF0C;&#x8FD9;&#x6837;&#x5B50;&#x5B83;&#x5C31;&#x4E0D;&#x77E5;&#x9053;&#x6211;&#x7684;&#x4F4D;&#x7F6E;&#x4E86;&#xFF0C;&#x95EE;&#x9898;&#x4E5F;&#x968F;&#x4E4B;&#x800C;&#x6765;&#x3002;<br>
&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#x7528;&#x4E86;&#x4E00;&#x4E2A;&#x6708;&#x540E;&#xFF0C;&#x7A81;&#x7136;&#x53D1;&#x73B0;&#x4E00;&#x4E2A;&#x8BE1;&#x5F02;&#x7684;&#x73B0;&#x8C61;&#xFF0C;Google Photos&#x4E00;&#x76F4;&#x5361;&#x5728;&#x51C6;&#x5907;&#x4E0A;&#x4F20;&#x4E2D;&#xFF0C;&#x4E00;&#x4E2A;&#x6708;&#x65F6;&#x95F4;&#x91CC;&#x62CD;&#x7684;&#x7167;&#x7247;&#x90FD;&#x6CA1;&#x6709;&#x5907;&#x4EFD;&#x6210;&#x529F;&#x3002;&#x4E00;&#x5F00;&#x59CB;&#xFF0C;&#x6211;&#x4EE5;&#x4E3A;&#x662F;&#x7531;&#x4E8E;Nokia 7&#x7684;GMSCore&#x662F;&#x4E2D;&#x56FD;&#x7279;&#x4F9B;&#x7684;&#x539F;&#x56E0;&#x3002;&#x7ECF;&#x5386;Play Store&#x4E0B;&#x8F7D;404&#x7684;&#x95EE;&#x9898;&#xFF0C;&#x77E5;&#x9053;&#x4E86;services.cn.google.xml&#x7684;&#x5B58;&#x5728;&#xFF0C;&#x4EE5;&#x4E3A;Photos&#x4E5F;&#x88AB;&#x6307;&#x5411;&#x4E86;&#x56FD;&#x5185;&#x7684;&#x5730;&#x5740;&#x3002;&#x4E4B;&#x540E;&#xFF0C;&#x5C31;&#x4E00;&#x76F4;&#x60F3;&#x529E;&#x6CD5;&#x5220;&#x9664;services.cn.google.xml&#x3002;<br>
&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;4&#x6708;&#x521D;&#x7684;&#x65F6;&#x5019;&#xFF0C;&#x5728;xda&#x4E0A;&#x53D1;&#x73B0;<em>the_laser</em>&#x80FD;&#x591F;&#x751F;&#x6210;bootloader&#x7684;&#x89E3;&#x9501;&#x6587;&#x4EF6;&#xFF0C;&#x4E0D;&#x8FC7;&#xFF0C;&#x90A3;&#x65F6;&#x5DF2;&#x7ECF;&#x6CA1;&#x6709;&#x540D;&#x989D;&#x4E86;&#x3002;&#x8FC7;&#x4E86;&#x4E00;&#x4E2A;&#x591A;&#x6708;&#xFF0C;&#x597D;&#x50CF;&#x6709;&#x4EBA;&#x5356;&#x751F;&#x6210;&#x89E3;&#x9501;&#x7801;&#x7684;&#x5DE5;&#x5177;&#xFF0C;&#x6211;&#x4ECE;&#x6DD8;&#x5B9D;&#x4E0A;&#x5411;<em>&#x5149;&#x5361;</em>&#x4E70;&#x4E86;&#x4E00;&#x4EFD;&#x89E3;&#x9501;&#x6587;&#x4EF6;&#x3002;&#x89E3;&#x9501;&#x540E;&#xFF0C;&#x6211;&#x628A;services.cn.google.xml&#x5220;&#x6389;&#x4E86;&#xFF0C;&#x4EE5;&#x4E3A;&#x5907;&#x4EFD;&#x5C31;&#x4F1A;&#x6B63;&#x5E38;&#xFF0C;Voice Detection&#x548C;&#x5386;&#x53F2;&#x4F4D;&#x7F6E;&#x4E5F;&#x53EF;&#x4EE5;&#x7528;&#x4E86;&#x3002;<br>
&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#x4E8B;&#x5B9E;&#x8BC1;&#x660E;&#x6211;&#x592A;&#x5929;&#x771F;&#x4E86;&#xFF0C;&#x4E09;&#x4E2A;&#x529F;&#x80FD;&#x90FD;&#x4E0D;&#x53EF;&#x4EE5;&#x7528;&#xFF0C;&#x5386;&#x53F2;&#x4F4D;&#x7F6E;&#x867D;&#x7136;&#x53EF;&#x4EE5;&#x5F00;&#x542F;&#xFF0C;&#x4F46;&#x4F1A;&#x663E;&#x793A;&#x8FDE;&#x63A5;&#x4E0D;&#x5230;&#x5730;&#x56FE;&#x3002;&#x8FD9;&#x90FD;&#x5BFC;&#x81F4;&#x6211;&#x6000;&#x7591;&#x4EBA;&#x751F;&#x4E86;&#xFF0C;&#x4EE5;&#x4E3A;Nokia 7&#x7684;GMSCore&#x7684;&#x5E95;&#x5C42;jar&#x5305;&#x90FD;&#x662F;&#x7279;&#x4F9B;&#x7684;&#x3002;&#x4E8E;&#x662F;&#xFF0C;&#x6211;&#x5728;Nokia&#x7FA4;&#x91CC;&#x95EE;&#x95EE;&#xFF0C;&#x662F;&#x4E0D;&#x662F;&#x5176;&#x4ED6;&#x4EBA;&#x90FD;&#x7528;&#x4E0D;&#x4E86;&#x5907;&#x4EFD;&#x3002;&#x7ED3;&#x679C;&#x662F;&#xFF0C;&#x53EA;&#x6709;&#x6211;&#x7528;&#x4E0D;&#x4E86;&#x1F62D;&#x3002;&#x8FD9;&#x65F6;&#x5019;&#xFF0C;&#x6211;&#x624D;&#x6000;&#x7591;&#x662F;&#x7531;&#x4E8E;&#x7981;&#x6B62;&#x7684;&#x6743;&#x9650;&#x5BFC;&#x81F4;&#x7684;&#x3002;&#x8BF4;&#x5E72;&#x5C31;&#x5E72;&#xFF0C;&#x628A;GooglePlayService&#x7684;&#x6570;&#x636E;&#x6E05;&#x4E86;&#x4E00;&#x4E0B;&#x540E;&#xFF0C;Photos&#x7684;&#x5907;&#x4EFD;&#x53EF;&#x4EE5;&#x7528;&#x4E86;&#x3002;&#x6BD4;&#x8F83;&#x9057;&#x61BE;&#x7684;&#x662F;&#xFF0C;&#x5F53;&#x65F6;&#x6CA1;&#x6709;&#x5B9A;&#x4F4D;&#x5177;&#x4F53;&#x662F;&#x54EA;&#x4E2A;&#x6743;&#x9650;&#x5BFC;&#x81F4;&#x7684;&#xFF0C;&#x73B0;&#x5728;&#x8FD9;&#x4E48;&#x591A;&#x6570;&#x636E;&#x7684;&#x5E72;&#x6270;&#x4E0D;&#x4E00;&#x5B9A;&#x80FD;&#x591F;&#x627E;&#x5230;&#x539F;&#x56E0;&#xFF0C;&#x6240;&#x4EE5;&#x4E5F;&#x4E0D;&#x53BB;&#x7EA0;&#x7ED3;&#x54EA;&#x4E2A;&#x6743;&#x9650;&#x4E86;&#x3002;&#x6709;&#x4E2A;&#x60F3;&#x6CD5;&#xFF1A;&#x5907;&#x4EFD;&#x524D;&#xFF0C;&#x83B7;&#x53D6;&#x8FD0;&#x8425;&#x5546;&#x6570;&#x636E;&#xFF0C;&#x770B;&#x770B;&#x662F;&#x4E0D;&#x662F;&#x53EF;&#x4EE5;&#x5F00;&#x65E0;&#x9650;&#x5907;&#x4EFD;&#xFF1F;<br>
&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#x5907;&#x4EFD;&#x529F;&#x80FD;&#x6062;&#x590D;&#x4E86;&#xFF0C;&#x4F46;&#x5386;&#x53F2;&#x4F4D;&#x7F6E;&#x53CA;OK Google&#x8FD8;&#x662F;&#x4E0D;&#x80FD;&#x7528;&#x3002;&#x5BF9;&#x4E8E;&#x5386;&#x53F2;&#x4F4D;&#x7F6E;&#xFF0C;&#x6BD4;&#x8F83;&#x7B80;&#x5355;&#xFF0C;&#x7F51;&#x4E0A;&#x90FD;&#x63D0;&#x5230;&#x63D2;sim&#x5361;&#x524D;&#x662F;&#x53EF;&#x4EE5;&#x5F00;&#x542F;&#x7684;&#xFF0C;&#x4E5F;&#x63D0;&#x5230;&#x7981;&#x6B62;GooglePlayService&#x7684;<strong>&#x7535;&#x8BDD;</strong>&#x6743;&#x9650;&#x3002;&#x7ECF;&#x5386;&#x5907;&#x4EFD;&#x7684;&#x95EE;&#x9898;&#xFF0C;&#x4E0D;&#x60F3;&#x7981;&#x6B62;&#x5B83;&#x7684;&#x6743;&#x9650;&#x4E86;&#xFF0C;&#x8BF4;&#x4E0D;&#x5B9A;&#x53C8;&#x5F71;&#x54CD;&#x4EC0;&#x4E48;&#x529F;&#x80FD;&#x3002;&#x65E2;&#x7136;&#x5DF2;&#x7ECF;root&#x4E86;&#xFF0C;&#x5C31;&#x88C5;&#x4E86;LocationReportEnabler&#x6765;&#x89E3;&#x51B3;&#x3002;<br>
&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#x5BF9;&#x4E8E;OK Google&#xFF0C;&#x67E5;&#x4E86;&#x597D;&#x4E45;&#xFF0C;&#x53D1;&#x73B0;&#x9700;&#x8981;&#x628A;GoogleSearchBox&#x88C5;&#x4E3A;&#x7CFB;&#x7EDF;&#x8F6F;&#x4EF6;&#xFF0C;&#x4F30;&#x8BA1;&#x662F;&#x9700;&#x8981;&#x4EC0;&#x4E48;&#x6743;&#x9650;&#x624D;&#x80FD;&#x83B7;&#x53D6;&#x5230;CPU&#x968F;&#x65F6;&#x76D1;&#x542C;&#x8BED;&#x97F3;&#x7684;&#x529F;&#x80FD;&#x5427;&#x3002;<br>
&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#x603B;&#x7ED3;&#xFF0C;&#x60F3;&#x5728;&#x56FD;&#x5185;&#x7528;Google&#x5168;&#x5BB6;&#x6876;&#x771F;&#x662F;&#x4E0D;&#x5BB9;&#x6613;&#x554A;&#xFF0C;&#x4EE5;&#x540E;&#x8FD8;&#x662F;&#x627E;&#x5230;Android One&#x7684;&#x673A;&#x5668;&#x6765;&#x7528;&#xFF0C;&#x8FD0;&#x8425;&#x5546;&#x7F16;&#x7801;&#x662F;&#x4E0D;&#x662F;&#x53EF;&#x4EE5;&#x63D2;&#x5F20;&#x5883;&#x5916;&#x5361;&#x6765;&#x89E3;&#x51B3;&#x5462;&#xFF1F;</p>
<p>TL;DR</p>
<ol>
<li>Google Photos&#x82E5;&#x4E00;&#x76F4;&#x5361;&#x5728;&#x51C6;&#x5907;&#x5907;&#x4EFD;&#x4E2D;&#xFF0C;&#x53EF;&#x4EE5;&#x5C1D;&#x8BD5;&#x8FD8;&#x539F;GooglePlayService&#x7684;&#x6743;&#x9650;&#xFF08;&#x7535;&#x8BDD;&#xFF0C;&#x5B9A;&#x4F4D;&#xFF09;&#x3002;</li>
<li>Google&#x7684;OK Google Detection&#x9700;&#x8981;GoogleSearchBox&#x5B89;&#x88C5;&#x5728;/system/priv-app/&#x4E0B;&#x3002;</li>
<li>google&#x7684;location history&#x5355;&#x5355;&#x5220;&#x4E86;services.cn.google.xml&#x5E76;&#x4E0D;&#x8DB3;&#x591F;&#xFF0C;&#x9700;&#x8981;&#x6539;&#x8FD0;&#x8425;&#x5546;&#x7F16;&#x7801;&#xFF0C;&#x88C5;&#x4E2A;LocationReportEnabler&#x6765;&#x89E3;&#x51B3;&#x3002;</li>
</ol>
<p>--------------------------------&#x4E24;&#x5E74;&#x540E;------------------------------------<br>
&#x4E70;&#x4E86;OnePlus 8T&#x5237;&#x4E86;&#x6C27;OS&#xFF0C;&#x63D2;&#x4E0A;&#x9E2D;&#x804A;&#x4F73;&#xFF0C;&#x771F;&#x9999;&#x3002;&#x4E0D;&#x8FC7;&#x673A;&#x5668;&#x4E0A;&#x7684;&#x56FD;&#x5185;&#x5E94;&#x7528;&#x53EA;&#x6709;&#x5FAE;&#x4FE1;&#x3001;&#x652F;&#x4ED8;&#x5B9D;&#x3001;&#x5BCC;&#x9014;&#x3001;&#x9AD8;&#x5FB7;&#xFF0C;&#x6709;&#x70B9;&#x6D6A;&#x8D39;&#x7B97;&#x529B;&#x3002;</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[国密库研究备份]]></title><description><![CDATA[近一年有了两次使用国密来签名的需求，还是备份一下了解到的知识吧。避免又要google一遍，虽然我是面向google编程]]></description><link>https://fortime.fyi/blog/20180510-02/</link><guid isPermaLink="false">6661f49b8cd2840e638166c1</guid><category><![CDATA[技术]]></category><category><![CDATA[安全]]></category><dc:creator><![CDATA[fortime]]></dc:creator><pubDate>Mon, 14 May 2018 00:50:41 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h1 id>&#x6587;&#x6863;&#x5907;&#x4EFD;</h1>
<ul>
<li><a href="http://www.sca.gov.cn/sca/xwdt/2010-12/17/1002386/files/b791a9f908bb4803875ab6aeeb7b4e03.pdf?ref=fortime.fyi">SM2&#x692D;&#x5706;&#x66F2;&#x7EBF;&#x516C;&#x94A5;&#x5BC6;&#x7801;&#x7B97;&#x6CD5;</a></li>
<li><a href="http://www.sca.gov.cn/sca/xwdt/2010-12/17/1002386/files/b965ce832cc34bc191cb1cde446b860d.pdf?ref=fortime.fyi">SM2&#x692D;&#x5706;&#x66F2;&#x7EBF;&#x516C;&#x94A5;&#x5BC6;&#x7801;&#x7B97;&#x6CD5;&#x63A8;&#x8350;&#x66F2;&#x7EBF;&#x53C2;&#x6570;</a></li>
</ul>
<h1 id="sm3withsm2">SM3WITHSM2</h1>
<p>SM3WITHSM2&#x4E0E;SHA256WITHRSA&#x8FC7;&#x7A0B;&#x7C7B;&#x4F3C;&#xFF0C;<del>&#x6700;&#x5927;&#x7684;&#x533A;&#x522B;&#x5728;&#x4E8E;&#x5BF9;&#x6D88;&#x606F;&#x7684;Hash&#x503C;&#x8BA1;&#x7B97;&#xFF0C;&#x540E;&#x8005;&#x662F;SHA256(MSG)&#xFF08;&#x592A;&#x5929;&#x771F;&#x4E86;&#xFF09;&#xFF0C;&#x524D;&#x8005;&#x662F;SM3(Z+MSG)&#xFF0C;&#x591A;&#x4E86;&#x4E00;&#x4E2A;Z&#x503C;&#x3002;</del>&#xFF08;&#x8FD8;&#x662F;&#x592A;&#x5929;&#x771F;&#x4E86;&#xFF09;&#xFF0C;SHA256WITHRSA&#x7684;&#x7B97;&#x6CD5;&#x516C;&#x5F0F;: RSA.decrypt(PKCS1Padding(DER(OID, SHA256)), PrivateKey)&#xFF0C;SM3WITHSM2&#x7684;&#x662F;:SM2.sign(SM3(Z+MSG)&#xFF0C;PrivateKey)&#x3002;</p>
<h2 id="z">Z&#x503C;&#x8BA1;&#x7B97;</h2>
<blockquote>
<p>Z = Hash<sub>256</sub>(Len(ID) + ID + a + b + x<sub>G</sub> + y<sub>G</sub> + x<sub>A</sub> + y<sub>A</sub></p>
</blockquote>
<p>Hash<sub>256</sub>&#x662F;Hash&#x7ED3;&#x679C;&#x4E3A;256&#x4F4D;&#x7684;Hash&#x51FD;&#x6570;&#xFF0C;&#x4E00;&#x822C;&#x7528;&#x7684;&#x662F;SM3&#x3002;</p>
<h2 id>&#x957F;&#x5EA6;</h2>
<p>SM2&#x53C2;&#x6570;&#x7684;&#x57DF;&#x4E3A;256bit&#x7684;&#x6574;&#x6570;&#xFF0C;&#x516C;&#x94A5;(x<sub>A</sub>,y<sub>A</sub>)&#x662F;&#x5728;&#x8FD9;&#x8303;&#x56F4;&#x5185;&#x7684;&#x70B9;&#xFF0C;&#x79C1;&#x94A5;d&#x662F;&#x8FD9;&#x4E2A;&#x57DF;&#x4E0A;&#x968F;&#x673A;&#x7684;&#x4E00;&#x4E2A;&#x503C;&#xFF0C;&#x7B7E;&#x540D;&#x7ED3;&#x679C;(r,s)&#x662F;&#x5728;&#x8FD9;&#x8303;&#x56F4;&#x5185;&#x7684;&#x70B9;&#xFF0C;&#x6240;&#x4EE5;&#x79C1;&#x94A5;&#x957F;&#x5EA6;&#x4E3A;64Byte&#xFF0C;&#x5176;&#x4ED6;&#x7684;&#x957F;&#x5EA6;&#x4E3A;256bit+256bit&#xFF0C;&#x5373;64Byte&#x3002;</p>
<h2 id="sm3withsm2">SM3WITHSM2&#x53D8;&#x79CD;</h2>
<p>&#x6709;&#x4E9B;&#x516C;&#x53F8;&#x5BF9;SM2&#x7B7E;&#x540D;&#x7684;&#x5B9E;&#x73B0;&#x662F;&#x5C11;&#x4E86;&#x8BA1;&#x7B97;Z&#x503C;&#x7684;&#xFF0C;&#x76F4;&#x63A5;&#x5C31;&#x662F;SM3(MSG)&#xFF0C;&#x5F53;&#x7B7E;&#x540D;&#x9A8C;&#x8BC1;&#x4E0D;&#x901A;&#x8FC7;&#x7684;&#x65F6;&#x5019;&#xFF0C;&#x53EF;&#x4EE5;&#x5F80;&#x8FD9;&#x65B9;&#x9762;&#x60F3;&#x3002;</p>
<h1 id>&#x56FD;&#x5BC6;&#x5E93;</h1>
<ul>
<li>&#x7EAF;JAVA&#x5B9E;&#x73B0;&#xFF1A;<a href="https://github.com/bcgit/bc-java.git?ref=fortime.fyi">bcprov-jdk15on</a>&#xFF08;&#x7F16;&#x5199;&#x672C;&#x6587;&#x7AE0;&#x65F6;&#x6700;&#x65B0;&#x7248;&#x672C;&#x662F;1.59&#xFF0C;&#x53EA;&#x652F;&#x6301;&#x6807;&#x51C6;SM3WITHSM2&#xFF0C;&#x4E0D;&#x80FD;&#x6539;&#x5176;&#x4ED6;Hash&#x51FD;&#x6570;&#xFF09;</li>
<li>C&#x5B9E;&#x73B0;&#x5E26;JNI&#xFF1A;<a href="https://github.com/guanzhi/GmSSL.git?ref=fortime.fyi">GmSSL</a>&#xFF08;&#x53EF;&#x4EE5;&#x4F7F;&#x7528;&#x4E0D;&#x540C;&#x7684;Hash&#x51FD;&#x6570;&#xFF09;</li>
<li>C&#x5B9E;&#x73B0;&#xFF1A;<a href="https://github.com/openssl/openssl.git?ref=fortime.fyi">OpenSSL</a>&#xFF08;&#x5E94;&#x8BE5;&#x662F;1.1.1&#x4F1A;&#x6709;&#x6B63;&#x5F0F;&#x652F;&#x6301;&#xFF0C;&#x7F16;&#x5199;&#x672C;&#x6587;&#x7AE0;&#x65F6;&#x662F;1.1.1_pre&#xFF09;</li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[公私钥相关名词解读]]></title><description><![CDATA[X.509，ASN.1，PEM之类名词的说明]]></description><link>https://fortime.fyi/blog/20180510-01/</link><guid isPermaLink="false">6661f49b8cd2840e638166c0</guid><category><![CDATA[技术]]></category><category><![CDATA[安全]]></category><dc:creator><![CDATA[fortime]]></dc:creator><pubDate>Sun, 13 May 2018 08:08:19 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><ul>
<li>ASN.1<br>
&#x5168;&#x79F0;Abstract Syntax Notation One&#xFF0C;&#x662F;&#x4E00;&#x79CD;&#x63CF;&#x8FF0;&#x6570;&#x636E;&#x7ED3;&#x6784;&#x7684;&#x6807;&#x8BB0;&#x8BED;&#x8A00;&#x3002;</li>
<li>X.509<br>
&#x662F;&#x57FA;&#x4E8E;ASN.1&#x7684;&#x5BF9;&#x4E8E;&#x516C;&#x94A5;&#x8BC1;&#x4E66;&#x7ED3;&#x6784;&#x7684;&#x6807;&#x51C6;&#x63CF;&#x8FF0;&#x3002;ASN.1&#x4E0E;X.509&#x7684;&#x5173;&#x7CFB;&#x53EF;&#x4EE5;&#x7406;&#x89E3;&#x4E3A;&#xFF0C;ASN.1&#x5047;&#x5982;&#x662F;C&#xFF0C;X.509&#x5C31;&#x662F;&#x7528;C&#x5199;&#x7684;&#x7A0B;&#x5E8F;&#x3002;</li>
<li>PKCS #8<br>
&#x662F;RSA Security Inc&#x8BBE;&#x8BA1;&#x4E0E;&#x53D1;&#x5E03;&#x7684;&quot;Public Key Cryptography Standards&quot;&#x5176;&#x4E2D;&#x4E4B;&#x4E00;&#x79CD;&#x6807;&#x51C6;&#xFF0C;&#x662F;&#x57FA;&#x4E8E;ASN.1&#x7684;&#x5BF9;&#x4E8E;&#x79C1;&#x94A5;&#x7ED3;&#x6784;&#x7684;&#x6807;&#x51C6;&#x63CF;&#x8FF0;&#x3002;</li>
<li>DER<br>
&#x5168;&#x79F0;Distinguished Encoding Rules&#xFF0C;&#x662F;BER&#x7684;&#x53D8;&#x79CD;&#xFF0C;&#x7C7B;&#x4F3C;&#x7684;&#x8FD8;&#x6709;CER&#x3002;&#x7528;&#x4E8E;&#x4E3A;&#x7531;ASN.1&#x63CF;&#x8FF0;&#x7684;&#x6570;&#x636E;&#x7ED3;&#x6784;&#x5E8F;&#x5217;&#x5316;&#x4E8C;&#x8FDB;&#x5236;&#x6570;&#x636E;&#xFF0C;&#x4EE5;&#x4FBF;&#x4FDD;&#x5B58;&#x3002;&#x4E00;&#x822C;&#x6587;&#x4EF6;&#x540E;&#x7F00;&#x4E3A;.cer&#xFF0C;.crt&#xFF0C; .der&#x3002;</li>
<li>PEM<br>
&#x5168;&#x79F0;Privacy-enhanced Electronic Mail&#xFF0C;&#x662F;&#x4E00;&#x79CD;&#x5B58;&#x50A8;&#x6570;&#x636E;&#x7684;&#x683C;&#x5F0F;&#x3002;&#x4E3A;&#x4E86;&#x89E3;&#x51B3;&#x7535;&#x5B50;&#x90AE;&#x4EF6;&#x53EA;&#x80FD;&#x4F20;&#x8F93;ASCII&#x7801;&#x7684;&#x95EE;&#x9898;&#x3002;&#x5B83;&#x5305;&#x542B;&#x6587;&#x4EF6;&#x5934;&#x548C;&#x6587;&#x4EF6;&#x5C3E;&#x6765;&#x8BF4;&#x660E;&#x6570;&#x636E;&#x7684;&#x79CD;&#x7C7B;&#x548C;&#x9650;&#x5B9A;&#x6570;&#x636E;&#x7684;&#x8303;&#x56F4;&#x3002;&#x7528;&#x4E8E;&#x516C;&#x79C1;&#x94A5;&#xFF0C;&#x4E00;&#x822C;&#x662F;&#x5BF9;DER&#x6587;&#x4EF6;&#x7684;Base64&#x7F16;&#x7801;&#x3002;</li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[HttpClient大量https短连接导致新https请求卡顿]]></title><description><![CDATA[公司的线上服务偶尔会出现请求某个地址几秒中的卡顿，刚好那段时间腾讯云的网络经常抖动，我们还以为这是网络抖动造成的。最后，通过抓包，我们发现卡顿都是在*httpclient*创建*sslsocket*时发生的。究竟是什么问题呢]]></description><link>https://fortime.fyi/blog/20180224-01/</link><guid isPermaLink="false">6661f49b8cd2840e638166bc</guid><category><![CDATA[技术]]></category><category><![CDATA[Java]]></category><dc:creator><![CDATA[fortime]]></dc:creator><pubDate>Sun, 11 Mar 2018 12:15:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h1 id>&#x76F8;&#x5173;&#x7248;&#x672C;</h1>
<blockquote>
<p>jre&#xFF1A; 1.6.0_45-b06<br>
httpclient: 4.3.5</p>
</blockquote>
<h1 id>&#x80CC;&#x666F;</h1>
<p>&#x516C;&#x53F8;&#x7684;&#x7EBF;&#x4E0A;&#x670D;&#x52A1;&#x5076;&#x5C14;&#x4F1A;&#x51FA;&#x73B0;&#x8BF7;&#x6C42;&#x67D0;&#x4E2A;&#x5730;&#x5740;&#x51E0;&#x79D2;&#x4E2D;&#x7684;&#x5361;&#x987F;&#xFF0C;&#x521A;&#x597D;&#x90A3;&#x6BB5;&#x65F6;&#x95F4;&#x817E;&#x8BAF;&#x4E91;&#x7684;&#x7F51;&#x7EDC;&#x7ECF;&#x5E38;&#x6296;&#x52A8;&#xFF0C;&#x6211;&#x4EEC;&#x8FD8;&#x4EE5;&#x4E3A;&#x8FD9;&#x662F;&#x7F51;&#x7EDC;&#x6296;&#x52A8;&#x9020;&#x6210;&#x7684;&#x3002;&#x6700;&#x540E;&#xFF0C;&#x901A;&#x8FC7;&#x6293;&#x5305;&#xFF0C;&#x6211;&#x4EEC;&#x53D1;&#x73B0;&#x5361;&#x987F;&#x90FD;&#x662F;&#x5728;<em>httpclient</em>&#x521B;&#x5EFA;<em>ssl socket</em>&#x65F6;&#x53D1;&#x751F;&#x7684;&#x3002;&#x7A76;&#x7ADF;&#x662F;&#x4EC0;&#x4E48;&#x95EE;&#x9898;&#x5462;&#xFF1F;</p>
<h1 id>&#x95EE;&#x9898;&#x5206;&#x6790;</h1>
<p><em>httpclient</em>&#x7684;&#x4F7F;&#x7528;<code>SSLSessionContextImpl</code>&#xFF08;&#x6CE8;&#x610F;&#xFF0C;&#x8FD9;&#x4E2A;&#x7C7B;&#x5728;openjdk6&#x4E0E;jdk6&#x4E2D;&#x5B9E;&#x73B0;&#x662F;&#x4E0D;&#x4E00;&#x6837;&#xFF09;&#x6765;&#x521B;&#x5EFA;<em>ssl socket</em>&#xFF0C;&#x8FD9;&#x4E2A;&#x7C7B;&#x4F1A;&#x628A;&#x6BCF;&#x4E2A;&#x521B;&#x5EFA;&#x5B8C;&#x6210;&#x7684;<em>ssl session</em>&#x5B58;&#x653E;&#x5230;<code>sessionCache</code>&#x3002;<br>
&#x5728;<code>ClientHandshaker</code>&#x7684;&#x5B9E;&#x73B0;&#x4E2D;&#xFF0C;&#x5F53;&#x63E1;&#x624B;&#x5B8C;&#x6210;&#x540E;&#xFF0C;&#x7A0B;&#x5E8F;&#x4F1A;&#x628A;&#x5F53;&#x524D;<code>session</code>&#x7F13;&#x5B58;&#x5230;<code>sessionCache</code>&#x4E2D;&#xFF0C;&#x5373;956&#x884C;&#x3002;</p>
<pre><code class="language-java">// ClientHandshaker&#x7F13;&#x5B58;session&#x4EE3;&#x7801;
/*  954 */         if (!this.resumingSession) {
/*  955 */             if (this.session.isRejoinable()) {
/*  956 */                 ((SSLSessionContextImpl)this.sslContext.engineGetClientSessionContext()).put(this.session);
/*      */ 
/*      */ 
/*  959 */                 if (ClientHandshaker.debug != null &amp;&amp; Debug.isOn(&quot;session&quot;)) {
/*  960 */                     System.out.println(&quot;%% Cached client session: &quot; + this.session);
/*      */                 }              }
/*  962 */             else if (ClientHandshaker.debug != null &amp;&amp; Debug.isOn(&quot;session&quot;)) {
/*  963 */                 System.out.println(&quot;%% Didn&apos;t cache non-resumable client session: &quot; + this.session);
/*      */ 
/*      */             }
/*      */ 
/*      */         }
</code></pre>
<p><code>sessionCache</code>&#x4F7F;&#x7528;<code>MemoryCache</code>&#x4F5C;&#x4E3A;&#x7F13;&#x5B58;&#x5B9E;&#x73B0;&#xFF0C;<s>&#x5B83;&#x662F;&#x4E00;&#x4E2A;&#x6709;&#x751F;&#x547D;&#x5468;&#x671F;&#x3001;&#x9650;&#x5B9A;&#x5BB9;&#x91CF;&#x4E14;&#x540C;&#x6B65;&#x6DFB;&#x52A0;&#x7684;cache&#xFF0C;&#x5F53;&#x6DFB;&#x52A0;&#x7684;&#x65F6;&#x5019;&#xFF0C;&#x5B83;&#x7684;&#x5BB9;&#x91CF;&#x8FBE;&#x5230;&#x6700;&#x5927;&#xFF0C;&#x5B83;&#x5C31;&#x4F1A;&#x8FDB;&#x884C;&#x6E05;&#x7406;&#x5DE5;&#x4F5C;&#x3002;<br>
&#x5230;&#x8FD9;&#x91CC;&#xFF0C;&#x4F30;&#x8BA1;&#x5927;&#x5BB6;&#x5C31;&#x77E5;&#x9053;&#x4EC0;&#x4E48;&#x89E6;&#x53D1;&#x5361;&#x987F;&#x4E86;&#x3002;</s><br>
<code>MemoryCache</code>&#x7684;&#x5B9E;&#x73B0;&#x6BD4;&#x8F83;tricky&#xFF0C;&#x8FD9;&#x6B21;&#x95EE;&#x9898;&#x5B9A;&#x4F4D;&#x53D1;&#x751F;&#x5728;&#x4E00;&#x5E74;&#x524D;&#xFF0C;&#x5F53;&#x65F6;&#x6211;&#x4EE5;&#x4E3A;&#x5B83;&#x7684;&#x5DE5;&#x4F5C;&#x65B9;&#x5F0F;&#x662F;&#x50CF;&#x4E0A;&#x9762;&#x5212;&#x6389;&#x7684;&#x65B9;&#x5F0F;&#xFF0C;&#x5176;&#x5B9E;&#xFF0C;&#x5B83;&#x53EF;&#x4EE5;&#x5F52;&#x7EB3;&#x4E3A;&#x4E09;&#x79CD;&#x5DE5;&#x4F5C;&#x65B9;&#x5F0F;&#xFF1A;</p>
<ol>
<li>&#x9650;&#x5B9A;&#x5BB9;&#x91CF;&#xFF0C;&#x8D85;&#x4E86;&#x8FDB;&#x884C;&#x5220;&#x9664;&#xFF1B;</li>
<li>&#x4E0D;&#x9650;&#x5B9A;&#x5BB9;&#x91CF;&#xFF0C;&#x4F9D;&#x8D56;<code>SoftReference</code>&#x7684;&#x7279;&#x6027;&#xFF0C;&#x7531;jvm&#x7684;gc&#x6765;&#x56DE;&#x6536;&#xFF1B;</li>
<li>&#x4E0D;&#x9650;&#x5B9A;&#x5BB9;&#x91CF;&#xFF0C;&#x4E5F;&#x4E0D;&#x5220;&#x9664;&#xFF1B;</li>
</ol>
<pre><code class="language-java">// MemoryCache&#x7684;put&#x64CD;&#x4F5C;
/*     */     public synchronized void put(final Object o, final Object o2) {
/* 336 */         this.emptyQueue();
/*     */ 
/*     */ 
/*     */ 
/* 340 */         final CacheEntry cacheEntry = this.cacheMap.put(o, this.newEntry(o, o2, (this.lifetime == 0L) ? 0L : (System.currentTimeMillis() + this.lifetime), this.queue));
/* 341 */         if (cacheEntry != null) {
/* 342 */             cacheEntry.invalidate();
/* 343 */             return;
/*     */         }
/* 345 */         if (this.maxSize &gt; 0 &amp;&amp; this.cacheMap.size() &gt; this.maxSize) {
/* 346 */             this.expungeExpiredEntries();
/* 347 */             if (this.cacheMap.size() &gt; this.maxSize) {
/* 348 */                 final Iterator&lt;CacheEntry&gt; iterator = this.cacheMap.values().iterator();
/* 349 */                 final CacheEntry cacheEntry2 = iterator.next();
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 354 */                 iterator.remove();
/* 355 */                 cacheEntry2.invalidate();
/*     */             }
/*     */         }
/*     */     
</code></pre>
<p><em>httpclient</em>&#x4F7F;&#x7528;&#x7684;&#x662F;<code>SSLSessionContextImpl</code>&#x521B;&#x5EFA;<code>sessionCache</code>&#x65F6;<code>maxSize</code>&#x4E3A;0&#xFF08;&#x4E0D;&#x9650;&#x5B9A;&#x5BB9;&#x91CF;&#xFF09;&#xFF0C;&#x6240;&#x4EE5;&#x4F7F;&#x7528;<code>MemoryCache</code>&#x7B2C;&#x4E8C;&#x79CD;&#x5DE5;&#x4F5C;&#x65B9;&#x5F0F;&#x3002;&#x7531;&#x4E8E;&#x6211;&#x4EEC;&#x7CFB;&#x7EDF;&#x7528;&#x7684;&#x662F;&#x77ED;&#x94FE;&#x63A5;&#x4E14;&#x8BF7;&#x6C42;&#x91CF;&#x548C;&#x5E76;&#x53D1;&#x91CF;&#x5F88;&#x5927;&#xFF0C;&#x5BFC;&#x81F4;&#x6211;&#x4EEC;&#x7CFB;&#x7EDF;&#x4F1A;&#x4E0D;&#x65AD;&#x5E76;&#x53D1;&#x521B;&#x5EFA;&#x5E76;&#x7F13;&#x5B58;<em>ssl session</em>&#xFF0C;&#x5F53;&#x67D0;&#x6B21;gc&#x5220;&#x9664;&#x7684;<em>ssl session</em>&#x6BD4;&#x8F83;&#x591A;&#xFF0C;<code>this.emptyQueue()</code>&#x64CD;&#x4F5C;&#x5C31;&#x4F1A;&#x8FDB;&#x884C;&#x6BD4;&#x8F83;&#x957F;&#x65F6;&#x95F4;&#xFF0C;&#x5176;&#x4ED6;<code>put</code>&#x8C03;&#x7528;&#x5C31;&#x4F1A;&#x540C;&#x6B65;&#x7B49;&#x5F85;&#xFF0C;&#x8FD9;&#x5C31;&#x9020;&#x6210;&#x4E86;&#x521B;&#x5EFA;<em>ssl socket</em>&#x7684;&#x5361;&#x987F;&#x3002;</p>
<h1 id>&#x95EE;&#x9898;&#x603B;&#x7ED3;</h1>
<p>&#x8FD9;&#x6B21;&#x7684;&#x95EE;&#x9898;&#x7531;&#x4EE5;&#x4E0B;&#x51E0;&#x4E2A;&#x56E0;&#x7D20;&#x5171;&#x540C;&#x4F5C;&#x7528;&#x4E0B;&#x5BFC;&#x81F4;&#x7684;&#xFF1A;</p>
<ol>
<li><code>MemoryCache</code>&#x7684;&#x540C;&#x6B65;put&#x64CD;&#x4F5C;&#xFF1B;</li>
<li><code>MemoryCache</code>&#x7684;tricky&#x5DE5;&#x4F5C;&#x65B9;&#x5F0F;&#xFF1B;</li>
<li>gc&#x56DE;&#x6536;&#x7684;&#x4E0D;&#x5B9A;&#x6027;&#xFF0C;&#x5BFC;&#x81F4;<code>this.emptyQueue()</code>&#x64CD;&#x4F5C;&#x65F6;&#x95F4;&#x4E0D;&#x5747;&#x5300;&#xFF1B;</li>
<li>&#x5927;&#x91CF;&#x521B;&#x5EFA;<code>ssl session</code>&#xFF0C;&#x5982;&#x5927;&#x91CF;&#x77ED;&#x8FDE;&#x63A5;&#x8BF7;&#x6C42;&#xFF1B;</li>
</ol>
<h1 id>&#x89E3;&#x51B3;&#x65B9;&#x6848;</h1>
<p>&#x6839;&#x672C;&#x7684;&#x539F;&#x56E0;&#x662F;&#x7F13;&#x5B58;&#x5728;&#x8FDE;&#x63A5;&#x5173;&#x95ED;&#x65F6;&#x6CA1;&#x6709;&#x88AB;&#x91CA;&#x653E;&#x800C;&#x662F;&#x7531;<code>MemoryCache</code>&#x6765;&#x88AB;&#x52A8;&#x91CA;&#x653E;&#x3002;&#x57FA;&#x4E8E;&#x8FD9;&#x4E2A;&#x70B9;&#xFF0C;&#x6709;&#x4E24;&#x4E2A;&#x65B9;&#x5411;&#xFF1A;1. &#x5173;&#x95ED;&#x7F13;&#x5B58;&#xFF1B;2. &#x4E3B;&#x52A8;&#x91CA;&#x653E;&#xFF0C;&#x907F;&#x514D;<code>this.emptyQueue()</code>&#x51FA;&#x73B0;&#x8FC7;&#x957F;&#x7684;&#x64CD;&#x4F5C;&#x65F6;&#x95F4;&#xFF1B;<br>
&#x5BF9;&#x4E8E;1&#xFF0C;&#x67E5;&#x4E86;&#x4E00;&#x4E0B;&#xFF0C;&#x6CA1;&#x6709;&#x597D;&#x7684;&#x5173;&#x95ED;&#x65B9;&#x6848;&#xFF1B;<br>
&#x5BF9;&#x4E8E;2&#xFF0C;&#x53EF;&#x4EE5;&#x901A;&#x8FC7;&#x624B;&#x52A8;&#x521B;&#x5EFA;<em>httpclient</em>&#x7684;<em>ssl context</em>&#xFF0C;&#x5E76;&#x901A;&#x8FC7;<code>sslContext.getClientSessionContext().setSessionCacheSize(maxSize);</code>&#x6765;&#x8BBE;&#x5B9A;cache&#x7684;&#x5927;&#x5C0F;&#x3002;&#x6216;&#x8005;&#xFF0C;&#x5728;&#x4E0D;&#x6539;&#x4EE3;&#x7801;&#x7684;&#x60C5;&#x51B5;&#x4E0B;&#xFF0C;&#x589E;&#x52A0;java&#x542F;&#x52A8;&#x53C2;&#x6570;<code>-Djavax.net.ssl.sessionCacheSize=maxSize</code>&#x3002;</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>