rzg https://blog.rzg.one/ Recent content on rzg Hugo -- gohugo.io en-us Tue, 03 Jan 2023 00:00:00 +0000 Basic Tips for Data Entry on Spreadsheets https://blog.rzg.one/post/2023/01/03/basic-tips-for-data-entry-on-spreadsheets/ Tue, 03 Jan 2023 00:00:00 +0000 https://blog.rzg.one/post/2023/01/03/basic-tips-for-data-entry-on-spreadsheets/ <p>Spreadsheets are a quick way of entering tabular data. Usually, it is done as a &ldquo;temporary&rdquo; measure, until someone comes and turns it into a proper database. Sometimes, it is used as an intermediate structure for future extra processing.</p> <p>Whatever the scenario, there are some mistakes that occur so consistently, that they cause a lot of problems later on, and will need a lot of rework before they can be made machine readable.</p> <p>Here are some basic tips, and what not to do when entering data on a spreadsheet.</p> <h3 id="do-not-enter-the-full-name-in-one-column">Do not enter the full name in one column</h3> <p>Possibly the most common issue. The person&rsquo;s name, last name, middle name, father name, all entered in the same cell.</p> <ul> <li>Why is this a problem?</li> </ul> <p>Even if they are entered correctly, names are very non standard, some people have multiple first names or last names, some names are multi word. Sometimes parts of it may be missing, like father for some, first name for others. Overall, it is near impossible to separate these later, without manual intervention.</p> <ul> <li>Solution:</li> </ul> <p>Have separate columns for first name, family name, father name, mother name, or any other name information that you have. If the information does not exist for an entry, simply leave it blank.</p> <p>As a general rule, each column/cell should contain only one piece of information.</p> <h3 id="do-not-enter-the-date-of-birth-in-one-column">Do not enter the date of birth in one column</h3> <p>Despite the name, in practice, a date of birth is not really a full proper date. Mostly because some piece of information may be missing. For example, with personal data, it is very common for some people to have missing day and/or month values.</p> <ul> <li>Why is this a problem?</li> </ul> <p>Assuming missing days or months to be 1 will be introducing wrong information. The person may actually be born on day 31 of month 12, causing an error of one full year. Moreover, it will then be impossible to differentiate them with people actually born on 1/1.</p> <ul> <li>Solution:</li> </ul> <p>The solution is to have separate columns for the three, and keep them empty or 0 in case the information is missing.</p> <p>This applies to any date information, that may not have all three components for every case.</p> <h3 id="enter-dates-in-iso-format">Enter dates in ISO format</h3> <p>The ISO-8601 standard defines date and time formats that are unambiguous, and understood by humans and machines alike.</p> <p>For dates, it comes down to the following format: YYYY-MM-DD. For example, 2022-12-01 corresponds to the first of December 2022.</p> <ul> <li>Why is this a problem?</li> </ul> <p>There are two mainstream date formats, that are directly contradictory to each other. These are DD/MM/YYYY and the infamous MM/DD/YYYY. To make matters worse, spreadsheet programs try to assume the formats of the date columns, by relying on the localization settings of the operating system where the file is opened.</p> <p>For example, if a file was written on a system set to MM/DD/YYYY, opening it on another one set to DD/MM/YYYY will interpret the dates incorrectly, some of them not even registering as dates.</p> <p>Related issue, is that people operating on the data will not know which format is used, especially when dealing internationally.</p> <ul> <li>Solution:</li> </ul> <p>Use the YYYY-MM-DD format described above. It will be understood by programs, and can be exported and imported safely.</p> <ul> <li>Related tip:</li> </ul> <p>Always use the ISO-8601 format in CSV or other text files, that you need to import into spreadsheets. It is the only format programs will correctly convert to dates.</p> <h3 id="do-not-enter-explanations-and-elaborations">Do not enter explanations and elaborations</h3> <p>Usually happens with a combination of the first mistake, and a typical example is something like: &ldquo;Name (couldn&rsquo;t read the last name)&rdquo;.</p> <ul> <li>Solution:</li> </ul> <p>If you need to convey information, that some data was illegible, put it in a dedicated column, if it is obvious and happens systemically. Or, if you need to write a lot, have a notes column. In both cases, this will be a column made obviously for manual processing.</p> <h3 id="do-not-highlight-by-colors">Do not highlight by colors</h3> <p>A lot of times, after data entry is completed, people come back to it and start coloring some lines in red, green, blue, yellow, etc, indicating some status the data of that line has achieved.</p> <ul> <li>Why is this a problem?</li> </ul> <p>While some programs may be capable of filtering or sorting by color, it is an information that does not get exported, and it cannot be post-processed without extra steps. Moreover, the meaning of the different colors is not inherent and could be lost over time.</p> <ul> <li>Solution:</li> </ul> <p>Add columns for status, action, etc, and enter the information there as codes. You can still use colors as an additional visual indicator, or use the conditional formatting capabilities available in most programs, that will let you color based on the values of the cell.</p> Personal File Hosting Service with SFTP on OpenBSD https://blog.rzg.one/post/2021/10/03/personal-file-hosting-service-with-sftp-on-openbsd/ Sun, 03 Oct 2021 00:00:00 +0000 https://blog.rzg.one/post/2021/10/03/personal-file-hosting-service-with-sftp-on-openbsd/ <p>I&rsquo;ve used Nextcloud for file hosting, calendar and contacts services, since its very first release. However, I recently had to downgrade my large servers, and found myself lacking resources for running a hungry service like that.</p> <h3 id="goal">Goal</h3> <p>To have an &ldquo;offsite&rdquo; backup of my documents, and to be able to view them on a phone. The documents are read-only on the server, and the synchronization is one way, local to server.</p> <h3 id="create-a-dedicated-user-for-file-hosting">Create a dedicated user for file hosting</h3> <p>Since <code>sftp</code> implies <code>ssh</code> access, we need to restrict things as much as possible.</p> <p>Thankfully, OpenSSH has an easy way of achieving this, by forcing an <code>sftp</code> only mode on a user, and confining it to a directory.</p> <p>Which means we need a dedicated user on the server for the file store.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>useradd -m syncuser </span></span></code></pre></div><h3 id="restrict-sftp">Restrict SFTP</h3> <p>Create a new root directory:</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir /chroot </span></span></code></pre></div><p>Add to <code>/etc/sshd_config</code> the following:</p> <pre tabindex="0"><code class="language-conf" data-lang="conf">Match User syncuser ForceCommand internal-sftp ChrootDirectory /chroot </code></pre><p>This will disable <code>ssh</code> login for <code>syncuser</code>, and restrict <code>sftp</code> to the directory <code>/chroot</code>.</p> <h3 id="mount-file-storage-directory">Mount file storage directory</h3> <p>We can keep our files directly under <code>/chroot</code>, however it&rsquo;s likely we don&rsquo;t have that much space in the root partition, especially if we followed the standard OpenBSD installation.</p> <p>To solve this, we can create a sync directory under our user&rsquo;s home, and mount it to <code>/chroot</code> with <code>nfs</code>. As an extra, mounting it with <code>-ro</code> will make the share, and thus our <code>sftp</code> server, read-only.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir /home/syncuser/sync </span></span><span style="display:flex;"><span>mkdir /chroot/sync </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>rcctl <span style="color:#fff;font-weight:bold">enable</span> portmap nfsd mountd </span></span><span style="display:flex;"><span><span style="color:#fff;font-weight:bold">echo</span> <span style="color:#0ff;font-weight:bold">&#34;/home/syncuser/sync -network=127.0.0.1 -mask=255.255.255.255 -ro&#34;</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> &gt; /etc/exports </span></span><span style="display:flex;"><span>rcctl start portmap nfsd mountd </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>mount localhost:/home/syncuser/sync/ /chroot/sync </span></span></code></pre></div><h3 id="sync-files">Sync files</h3> <p>Synchronization of our files from the local to the server can be done with <code>rsync</code>. However, since we don&rsquo;t have regular <code>ssh</code> to <code>syncuser</code>, we need a workaround.</p> <p>The solution is to send the files using another user (for example <code>privuser</code>) with regular <code>ssh</code> access, while running <code>rsync</code> on the server as <code>syncuser</code> through <code>rsync-path</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>rsync --rsync-path <span style="color:#0ff;font-weight:bold">&#39;doas -u syncuser /usr/local/bin/rsync&#39;</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> -ar --delete <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> /home/localuser/sync/ <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> &lt;privuser&gt;@&lt;server&gt;:/home/syncuser/sync/ </span></span></code></pre></div><p>The above requires a rule in <code>doas</code>:</p> <pre tabindex="0"><code class="language-conf" data-lang="conf">permit nopass privuser as syncuser cmd /usr/local/bin/rsync </code></pre><p>As mentioned before, this is just a one-way sync. For two-way sync, the tool <code>unison</code> may do the job.</p> <h3 id="view-on-phone">View on phone</h3> <p>To view and open the files on a smartphone, we need an application that can access <code>sftp</code>. The Android file manager that I use, &ldquo;Material Files&rdquo;, already has this capability.</p> <ul> <li><a href="https://github.com/zhanghai/MaterialFiles">Material Files</a></li> </ul> <h3 id="conclusion">Conclusion</h3> <p>The gain from all this, is that we can now access the <code>sftp</code> server through the restricted <code>syncuser</code>, without needing access to <code>privuser</code> on the phone.</p> <h3 id="sources">Sources</h3> <ul> <li><a href="https://undeadly.org/cgi?action=article;sid=20080220110039">Chroot in OpenSSH</a></li> <li><a href="https://dataswamp.org/~solene/2018-05-22-mount-bind.html">Mount a folder on another folder</a></li> </ul> Change git commit Authorship with git-filter-repo https://blog.rzg.one/post/2021/09/25/change-git-commit-authorship-with-git-filter-repo/ Sat, 25 Sep 2021 00:00:00 +0000 https://blog.rzg.one/post/2021/09/25/change-git-commit-authorship-with-git-filter-repo/ <p>Recently, I needed to change the email address on all commits in most of my personal git repos.</p> <p>Searching on the web, there are a few methods of doing this, most involving <code>git rebase</code>. There are also some that use <code>git-filter-branch</code>.</p> <p>Interestingly, applying <code>git-filter-branch</code> puts out a warning/recommendarion to use <code>git-filter-repo</code> instead, which is what I ended up doing in the end.</p> <p>Important to note, that all of these methods will rewrite the whole commit history, and change all the hashes. Since, I am the sole committer here, I wasn&rsquo;t too much concered about that. Needless to say, take backups!</p> <h3 id="installation">Installation</h3> <p><code>git-filter-repo</code> is not included in a standard git package. It is available on most Linux distributions though.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007f7f"># Arch Linux</span> </span></span><span style="display:flex;"><span>pacman -S git-filter-repo </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#007f7f"># OpenSUSE</span> </span></span><span style="display:flex;"><span>zypper install git-filter-repo </span></span></code></pre></div><h3 id="procedure">Procedure</h3> <p>Before doing anything, taking a full backup of the repo is a must!</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007f7f"># clone a temporary fresh bare repository</span> </span></span><span style="display:flex;"><span>git clone --bare &lt;repo&gt; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#007f7f"># change the email and name on all commits</span> </span></span><span style="display:flex;"><span>git filter-repo <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> --email-callback <span style="color:#0ff;font-weight:bold">&#39;return b&#34;test@example.com&#34;&#39;</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> --name-callback <span style="color:#0ff;font-weight:bold">&#39;return b&#34;testname&#34;&#39;</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#007f7f"># push with force</span> </span></span><span style="display:flex;"><span>git push --force </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#007f7f"># remove temporary repo</span> </span></span><span style="display:flex;"><span>rm -rf &lt;repo&gt; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#007f7f"># clone the fixed repo</span> </span></span><span style="display:flex;"><span>git clone &lt;repo&gt; </span></span></code></pre></div><ul> <li><a href="https://github.com/newren/git-filter-repo">git-filter-repo</a></li> </ul> Floating and Sticky Windows in BSPWM https://blog.rzg.one/post/2021/05/09/floating-and-sticky-windows-in-bspwm/ Sun, 09 May 2021 00:00:00 +0000 https://blog.rzg.one/post/2021/05/09/floating-and-sticky-windows-in-bspwm/ <p>BSPWM is a tiling window manager, with various interesting and unique features that I haven&rsquo;t seen anywhere else. One of these features is sticky floating windows.</p> <p>A sticky window, is one that persists on all workspaces all at once. In other words, once you mark a window as &lsquo;sticky&rsquo;, it will stick to its place even when you switch to another workspace.</p> <p>This feature works best with floating windows, because a tiled window will behave unpredictably on another workspace with existing tiles.</p> <p>We can make windows floating and sticky, manually, with the following commands.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007f7f"># set to floating</span> </span></span><span style="display:flex;"><span>bspc node -t floating </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#007f7f"># set to sticky </span> </span></span><span style="display:flex;"><span>bspc node -g sticky </span></span></code></pre></div><p>Obviously, these can be mapped to keys with <code>sxhkd</code>. But, we can also define rules, to set these properties automatically with <code>bspc rule</code>.</p> <p>These rules work based on the window class and instance names, which we can retrieve using <code>xprop</code>. Look for <code>WM_CLASS</code> and <code>WM_NAME</code>.</p> <p>At first glance, it may not seem obvious what this may be used for, so here are a few of the ways that I used it.</p> <h3 id="mpv">MPV</h3> <p>I have an ultrawide monitor, so it works well for me, to send all <code>mpv</code> windows to a corner of my screen. Moreover, I can freely switch between workspaces while still keeping the <code>mpv</code> screen in view. This can also work with the Firefox Picture-in-Picture window.</p> <p>Here is the relevant <code>bspwmrc</code> config:</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>bspc rule -a mpv <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> state=floating sticky=on follow=off focus=on <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> rectangle=640x360+2760+1040 </span></span><span style="display:flex;"><span>bspc rule -a <span style="color:#0ff;font-weight:bold">&#34;*:Toolkit:Picture-in-Picture&#34;</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> state=floating sticky=on follow=off focus=on <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> rectangle=640x360+2760+1040 </span></span></code></pre></div><h3 id="onboard">Onboard</h3> <p>Onboard is an onscreen keyboard. I use it on the desktop for typing words in languages that I don&rsquo;t know, but sometimes need.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>bspc rule -a Onboard state=floating sticky=on </span></span></code></pre></div><h3 id="terminals">Terminals</h3> <p>There are some terminal based programs, like volume controls or calculators, that this may apply as well.</p> <p>But, since we don&rsquo;t want all terminals to open floating and sticky, we need to define our own class when running, and map our rule to this class only.</p> <p>Here is my <code>sxhkd</code> definition, for terminal based programs that I intend to run as sticky/floating.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>super + x ; {c,v} </span></span><span style="display:flex;"><span> alacritty --class progterm -e {qalc -nocurrencies,pulsemixer} </span></span></code></pre></div><p>And the rule in <code>bspwmrc</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>bspc rule -a Alacritty:progterm state=floating sticky=on </span></span></code></pre></div><p>Note, that other terminals may use different switches for the class, for example <code>xterm</code> is <code>-class</code>.</p> About my Armenian Keyboard https://blog.rzg.one/post/2021/04/30/about-my-armenian-keyboard/ Fri, 30 Apr 2021 00:00:00 +0000 https://blog.rzg.one/post/2021/04/30/about-my-armenian-keyboard/ <p>I&rsquo;ve been getting questions and comments about my Armenian keyboard picture lately. So, it&rsquo;s probably time to write a post about it!</p> <h3 id="background">Background</h3> <p>The Armenian language has its own alphabet. This means, Armenians either have to memorize the places of the letters, use a roughly phonetic layout matching the Latin letters, or stick labels on every key. Needless to say, sticking labels will soon get very messy, some of them will be lost, leaving the keyboard with glue residue all over.</p> <p>While I haven&rsquo;t encountered any (I have searched), I&rsquo;m sure there must be printed keyboards available in Armenia. But, those will likely be generic cheap keyboards. So, how to achieve something very custom looking and unique like this?</p> <figure > <picture> <source srcset="https://blog.rzg.one/images/kb_poker_am.webp" type="image/webp"> <img src="https://blog.rzg.one/images/kb_poker_am.jpg" alt="mechanical keyboard with custom keycaps"> </picture> <figcaption> <p>mechanical keyboard with custom keycaps</p> </figcaption> </figure> <p>The key terms are: &ldquo;mechanical keyboard&rdquo;, &ldquo;custom keycaps&rdquo;.</p> <h3 id="mechanical-keyboards">Mechanical Keyboards</h3> <p>Mechanical keyboards are a world of their own, with serious communities and many hobbyists. Moreover, there seems to be a boom in YouTube channels lately.</p> <p>To get an idea about what it encompasses, the Mechanical Keyboards subreddit is probably the best place to start, with lots of pictures and information.</p> <ul> <li><a href="https://www.reddit.com/r/MechanicalKeyboards/">The Mechanical Keyboards subreddit</a></li> </ul> <p>While I do own four such keyboards, I wouldn&rsquo;t consider myself a hobbyist yet, and I&rsquo;m certainly not an expert. So, I will try to stick to the basics, and not give incomplete or false information. Mechanical keyboards can be costly, and they require individual research and a bit of understanding before diving into them.</p> <h3 id="switches">Switches</h3> <p>Probably the most important and defining component of a keyboard are the switches. There are three main categories, clicky, tactile and linear. Going into all the differences and people&rsquo;s preferences is a huge task, so this part is left to every person to do their own research. These days, there are thousands of videos, demonstrating every switch ever made.</p> <p>The keyboard in the picture is a &ldquo;Vortexgear Poker 3 (Pok3r)&rdquo; with &ldquo;Cherry MX Clear&rdquo; tactile switches.</p> <h3 id="custom-keycaps">Custom Keycaps</h3> <p>Mechanical Keyboards are very customizable. Almost all of them, regardless of the switch type, will have replaceable keycaps.</p> <p>Keycaps are sold separately, and as long as they are compatible with your switches, they will be replaceable. For example, Cherry and its compatible switches will work with their corresponding keycaps.</p> <p>Other than the compatibility, there is another important factor to consider, and that is the keycap profile. For example, there are keycaps where all the rows are of identical heights and sizes (like DSA), and others where each row is different.</p> <p>However, we want a bit more than replacement keycaps, since we want to design our own labels. We need keycap sellers, that also provide services for printing custom labels. One such seller is WASD, which is where I got mine from. They sell these keycaps either separately, or together with a keyboard.</p> <ul> <li><a href="https://www.wasdkeyboards.com/products/mechanical-keyboards/custom-printed-keyboards.html">WASD Custom Printed Keyboards</a></li> </ul> <h3 id="layout">Layout</h3> <p>Finally, a note about the layout.</p> <p>I thought a lot about what layout to go for, especially since the keys cannot be rearranged due to the differing heights of the rows. In the end, I went with &ldquo;Western Phonetic&rdquo;, because this is not the only keyboard I use, and on keyboards with no printed letters the phonetic one is the easiest to remember.</p> <p>Others can of course do their own layouts, whether it be &ldquo;Eastern Phonetic&rdquo;, the traditional &ldquo;Typewriter&rdquo;, or something else.</p> <hr> <p>In conclusion, I would love to see many more such keyboards! I claim that this is the only exclusive Armenian one, but if you have made your own Armenian keyboard, I would like to see it as well :) Feel free to send it to <a href="mailto:contact@rzg.one">contact@rzg.one</a>.</p> Switch Keyboard Layouts with Mouse Buttons and sxhkd https://blog.rzg.one/post/2021/04/11/switch-keyboard-layouts-with-mouse-buttons-and-sxhkd/ Sun, 11 Apr 2021 00:00:00 +0000 https://blog.rzg.one/post/2021/04/11/switch-keyboard-layouts-with-mouse-buttons-and-sxhkd/ <p>Switching keyboard layouts quickly and efficiently has been a long-standing problem for me. My latest attempt is by utilizing the extra buttons, that some mice and trackballs have.</p> <p>The solution that eventually worked for me, was switching between two layouts for every button. The decision to switch is made based on the current active layout.</p> <p>This allows me to switch between the second and third layout, by pressing the corresponding buttons, without going through the default layout.</p> <p>Getting the current layout was surprisingly involved though, and I ended up with this.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>setxkbmap -query | grep -P <span style="color:#0ff;font-weight:bold">&#39;layout:\s+\K[a-z]{2}&#39;</span> -o </span></span></code></pre></div><p>The <code>-P</code> instructs <code>grep</code> to use Perl style regular expressions, and the revelation for me that did the trick was <code>\K</code>, which effectively discards what comes before from getting captured.</p> <ul> <li><a href="https://perldoc.perl.org/perlre">Perldoc reference</a></li> </ul> <p>I also need to choose variants for every layout, so the full <code>switchkb</code> script ended up like this.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>SWITCH=<span style="color:#0ff;font-weight:bold">${</span>1<span style="color:#fff;font-weight:bold">:-</span>us<span style="color:#0ff;font-weight:bold">}</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>current=<span style="color:#fff;font-weight:bold">$(</span>setxkbmap -query | grep -P <span style="color:#0ff;font-weight:bold">&#39;layout:\s+\K[a-z]{2}&#39;</span> -o<span style="color:#fff;font-weight:bold">)</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#fff;font-weight:bold">if</span> [[ <span style="color:#0ff;font-weight:bold">&#34;</span><span style="color:#0ff;font-weight:bold">${</span>current<span style="color:#0ff;font-weight:bold">}</span><span style="color:#0ff;font-weight:bold">&#34;</span> == <span style="color:#0ff;font-weight:bold">&#34;</span><span style="color:#0ff;font-weight:bold">${</span>SWITCH<span style="color:#0ff;font-weight:bold">}</span><span style="color:#0ff;font-weight:bold">&#34;</span> ]] ; <span style="color:#fff;font-weight:bold">then</span> </span></span><span style="display:flex;"><span> setxkbmap us altgr-intl </span></span><span style="display:flex;"><span> <span style="color:#fff;font-weight:bold">exit</span> <span style="color:#ff0;font-weight:bold">0</span> </span></span><span style="display:flex;"><span><span style="color:#fff;font-weight:bold">fi</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#fff;font-weight:bold">case</span> <span style="color:#0ff;font-weight:bold">&#34;</span><span style="color:#0ff;font-weight:bold">${</span>SWITCH<span style="color:#0ff;font-weight:bold">}</span><span style="color:#0ff;font-weight:bold">&#34;</span> in </span></span><span style="display:flex;"><span> am) </span></span><span style="display:flex;"><span> setxkbmap am western </span></span><span style="display:flex;"><span> ;; </span></span><span style="display:flex;"><span> ru) </span></span><span style="display:flex;"><span> setxkbmap ru phonetic </span></span><span style="display:flex;"><span> ;; </span></span><span style="display:flex;"><span> us|*) </span></span><span style="display:flex;"><span> setxkbmap us altgr-intl </span></span><span style="display:flex;"><span> ;; </span></span><span style="display:flex;"><span><span style="color:#fff;font-weight:bold">esac</span> </span></span></code></pre></div><p>Most mice have a button under the scroll wheel, equivalent to double-click, this is <code>button2</code>. Other button names are usually <code>button8</code>, <code>button9</code>, etc. They can be retrieved with <code>xev</code>.</p> <p>Mapping the mouse buttons to the switch script was simple enough with <code>sxhkd</code>.</p> <pre tabindex="0"><code class="language-config" data-lang="config">button2 switchkb am button8 switchkb ru </code></pre><p>The above can be rewritten simply like this:</p> <pre tabindex="0"><code class="language-config" data-lang="config">button{2,8} switchkb {am,ru} </code></pre><p>Worth noting here, that I find <code>sxhkd</code> indispensable, especially with tiling window managers. In addition to its powerful and concise config style, it makes using multiple window managers trivial, by making the keyboard mapping definitions independent.</p> Copy Encrypted Partition between Disks https://blog.rzg.one/post/2021/04/10/copy-encrypted-partition-between-disks/ Sat, 10 Apr 2021 00:00:00 +0000 https://blog.rzg.one/post/2021/04/10/copy-encrypted-partition-between-disks/ <p>Recently, I had to copy/move an openSUSE Tumbleweed encrypted partition, and its EFI boot files, from one USB drive to another.</p> <p>All the below are specific to this particular case. But, I&rsquo;m sure it can be adapted to other similar situations as well.</p> <h3 id="preparing-space">Preparing Space</h3> <p>My target device already had an OS and a EFI boot partition. So, first I had to create space on the target.</p> <p>Since the source partition is encrypted, and we are moving irrespective of the internal file system, we need to make the target partition size to be at least the same size, or preferably larger.</p> <p>I did the resize using a GParted live disk.</p> <h3 id="copy-the-partition">Copy the Partition</h3> <p>This can be done with a simple <code>dd</code>. The most critical part here is to get the source <code>/dev/sdXn</code> and target <code>/dev/sdYn</code> partitions right.</p> <pre tabindex="0"><code>dd if=/dev/sdXn of=/dev/sdYn bs=1M status=progress </code></pre><h3 id="copy-the-efi-files">Copy the EFI Files</h3> <p>We also need to copy the EFI bootloader files from the original disk boot partition, to the target. These are found in small FAT16 or FAT32 partitions.</p> <p>Mount the source boot partition, and copy the following directory <code>/boot/efi/EFI/opensuse</code> to the <code>EFI</code> directory of the target disk&rsquo;s boot partition.</p> <h3 id="mount-the-target-partition">Mount the Target Partition</h3> <p>Before booting, there are a few things to fix, so we need to mount our partition on another host system.</p> <p>Obviously, if this step fails, and we can&rsquo;t see our files, the partition is corrupted and there is no use continuing further&hellip;</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cryptsetup --type luks open /dev/sdYn target </span></span><span style="display:flex;"><span>mount /dev/mapper/target /mnt </span></span></code></pre></div><h3 id="resize-the-file-system">Resize the File system</h3> <p>Since we copied into a larger area, we need to resize the file system to take the whole available space.</p> <p>Each file system has its own commands, in my case it is <code>btrfs</code>, so we can resize it as follows.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007f7f"># expanding a mounted btrfs partition</span> </span></span><span style="display:flex;"><span>btrfs filesystem resize max /mnt </span></span></code></pre></div><p>For ext2/ext3/ext4 file systems, <code>resize2fs</code> can be used.</p> <h3 id="fix-the-boot-partition-uuid">Fix the Boot Partition UUID</h3> <p>Since the boot partition has changed, we need to update the reference in the mounted <code>/mnt/etc/fstab</code> to the correct UUID of the new disk.</p> <h3 id="regenerate-the-initrd">Regenerate the Initrd</h3> <p>After doing all of the above, I rebooted to test the process. I was initially happy the bootloader was recognized, and it started booting the moved partition.</p> <p>But, it didn&rsquo;t go far, and the boot stalled for a minute or so, before complaining about the missing old boot partition UUID.</p> <p>Apparently, the openSUSE Initrd includes this UUID as well, so we need to regenerate it, by going into a chroot environment on the host machine where we mounted the target, and run mkinitrd.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007f7f"># Arch Linux has a script that prepares the chroot for us:</span> </span></span><span style="display:flex;"><span>arch-chroot /mnt </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#007f7f"># Now we are in the openSUSE environment and we can run:</span> </span></span><span style="display:flex;"><span>/usr/bin/mkinitrd </span></span></code></pre></div><p>After this the reboot worked fine, and the move is complete.</p> Manually add External Volume Entry in rEFInd https://blog.rzg.one/post/2021/04/02/manually-add-external-volume-entry-in-refind/ Fri, 02 Apr 2021 00:00:00 +0000 https://blog.rzg.one/post/2021/04/02/manually-add-external-volume-entry-in-refind/ <p>rEFInd is an excellent UEFI boot manager, that does almost everything for you. It is able to automatically find all bootable partitions on the available internal and external disks, and detect the operating systems on them.</p> <figure > <picture> <source srcset="https://blog.rzg.one/images/refind.webp" type="image/webp"> <img src="https://blog.rzg.one/images/refind.png" alt="rEFInd boot menu"> </picture> <figcaption> <p>rEFInd boot menu</p> </figcaption> </figure> <p>When not using the automatic scan however, we need to add the OS entries manually.</p> <p>Examples are available in the config file, but, to add an entry from an external hard drive, there&rsquo;s a small trick involved, that may not be apparent at first.</p> <p>A <code>volume</code> parameter needs to be specified, pointing to the exact boot partition of the external disk. All parameters coming after the <code>volume</code> definition will work relative to the new disk, while parameters coming before will assume the original disk.</p> <pre tabindex="0"><code class="language-config" data-lang="config">menuentry &#34;GuixSD&#34; { icon /EFI/refind/myicons/os_guixsd.png volume 12345678-9abc-def0-1234-56789abcdef0 loader /EFI/Guix/grubx64.efi } </code></pre><p>It should be noted, that the volume ID above is not the file-system one, but the partition <code>PARTUUID</code>, that can be retrieved using <code>blkid</code>.</p> <p>Incidentally, to get a screenshot of the boot screen, simply press F10, and the image file will be saved in the rEFInd root directory.</p> <p>The boot menu screenshot above is based on the &ldquo;Minimalistic rEFInd theme&rdquo;, while the icons are from &ldquo;rEFInd theme Regular&rdquo;.</p> <ul> <li><a href="https://www.rodsbooks.com/refind/">The rEFInd Boot Manager</a></li> <li><a href="https://github.com/EvanPurkhiser/rEFInd-minimal">Minimalistic rEFInd theme</a></li> <li><a href="https://github.com/munlik/refind-theme-regular">rEFInd theme Regular</a></li> </ul> GNU Guix System Installation with Static Networking https://blog.rzg.one/post/2021/02/10/gnu-guix-system-installation-with-static-networking/ Wed, 10 Feb 2021 00:00:00 +0000 https://blog.rzg.one/post/2021/02/10/gnu-guix-system-installation-with-static-networking/ <img class='pull-right allow-margin' alt='Guix logo' width='128px' src='https://blog.rzg.one/images/icon_guix.png' /> <p>During Guix system installation, the manual option, I had to connect to my home network, where I use static IP addresses. I stumbled a bit to get it working, but in the end it was simple enough.</p> <p>Here are some notes on the process. Assuming the interface name is <code>eth0</code> and the static IP address is <code>192.168.1.12</code>.</p> <p>First, we need to stop the networking service, as it is interfering by assigning addresses and routes.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>herd stop networking </span></span></code></pre></div><p>Then, we can assign an IP address on our device, bring it up, and add a route for our default gateway.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ip addr add 192.168.1.12/24 dev eth0 </span></span><span style="display:flex;"><span>ip link <span style="color:#fff;font-weight:bold">set</span> dev eth0 up </span></span><span style="display:flex;"><span>ip route add default via 192.168.1.1 </span></span></code></pre></div><p>That&rsquo;s it. One last thing would be to add the appropriate name servers in the installer&rsquo;s <code>/etc/resolv.conf</code>.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>nameserver 192.168.1.1 </span></span></code></pre></div><h2 id="static-address-for-the-installed-system">Static address for the installed system</h2> <p>The above was during installation, but how do we set up networking with a persistent static address in our config?</p> <p>The &ldquo;GNU Guix Reference Manual&rdquo; defines this, and can be done as follows.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>(static-networking-service </span></span><span style="display:flex;"><span> <span style="color:#0ff;font-weight:bold">&#34;eth0&#34;</span> <span style="color:#0ff;font-weight:bold">&#34;192.168.1.12&#34;</span> </span></span><span style="display:flex;"><span> #:netmask <span style="color:#0ff;font-weight:bold">&#34;255.255.255.0&#34;</span> </span></span><span style="display:flex;"><span> #:gateway <span style="color:#0ff;font-weight:bold">&#34;192.168.1.1&#34;</span> </span></span><span style="display:flex;"><span> #:name-servers &#39;(<span style="color:#0ff;font-weight:bold">&#34;192.168.1.1&#34;</span>)) </span></span></code></pre></div><h2 id="removing-default-services">Removing default services</h2> <p>In the manual they are adding the static networking service on <code>%base-services</code> which does not itself include networking.</p> <p>On the other hand, if our services are based on <code>%desktop-services</code>, then it includes <code>NetworkManager</code>, so we need to remove that, otherwise <code>guix system</code> will complain about the existence of two networking tools.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>(remove (<span style="color:#fff;font-weight:bold">lambda </span>(service) </span></span><span style="display:flex;"><span> (<span style="color:#fff;font-weight:bold">eq? </span>(service-kind service) network-manager-service-type) </span></span><span style="display:flex;"><span> %desktop-services)) </span></span></code></pre></div><p>The above removes only one service, <code>NetworkManager</code>. But, I had to remove multiple ones, and that can be achieved with the <code>or</code> procedure.</p> <p>Static networking can most likely be setup in the config with <code>NetworkManager</code> as well, but I found this simpler for my first install.</p> <p>Putting everything together, a services section, that includes just networking, looks something like this.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>(services </span></span><span style="display:flex;"><span> (cons* </span></span><span style="display:flex;"><span> (static-networking-service <span style="color:#0ff;font-weight:bold">&#34;eth0&#34;</span> <span style="color:#0ff;font-weight:bold">&#34;192.168.1.12&#34;</span> </span></span><span style="display:flex;"><span> #:netmask <span style="color:#0ff;font-weight:bold">&#34;255.255.255.0&#34;</span> </span></span><span style="display:flex;"><span> #:gateway <span style="color:#0ff;font-weight:bold">&#34;192.168.1.1&#34;</span> </span></span><span style="display:flex;"><span> #:name-servers &#39;(<span style="color:#0ff;font-weight:bold">&#34;192.168.1.1&#34;</span>)) </span></span><span style="display:flex;"><span> (remove (<span style="color:#fff;font-weight:bold">lambda </span>(service) </span></span><span style="display:flex;"><span> (<span style="color:#fff;font-weight:bold">or </span> </span></span><span style="display:flex;"><span> (<span style="color:#fff;font-weight:bold">eq? </span>(service-kind service) network-manager-service-type) </span></span><span style="display:flex;"><span> (<span style="color:#fff;font-weight:bold">eq? </span>(service-kind service) avahi-service-type))) </span></span><span style="display:flex;"><span> %desktop-services))) </span></span></code></pre></div><ul> <li><a href="https://guix.gnu.org/">GNU Guix</a></li> <li><a href="https://guix.gnu.org/manual/en/">GNU Guix Reference Manual</a></li> </ul> JavaScript Allow List in qutebrowser https://blog.rzg.one/post/2021/01/31/javascript-allow-list-in-qutebrowser/ Sun, 31 Jan 2021 00:00:00 +0000 https://blog.rzg.one/post/2021/01/31/javascript-allow-list-in-qutebrowser/ <img class='pull-right allow-margin' alt='qutebrowser logo' width='128px' src='https://blog.rzg.one/images/qutebrowser.png' /> <blockquote> <p>qutebrowser is a keyboard-focused browser with a minimal GUI. It’s based on Python and PyQt5 and free software, licensed under the GPL. It was inspired by other browsers/addons like dwb and Vimperator/Pentadactyl.</p> </blockquote> <ul> <li><a href="https://qutebrowser.org/">Official website of qutebrowser</a></li> </ul> <p><em>Updated on 2021-02-05</em></p> <p>The below solution allows an external manually configured list. However, qutebrowser has a built-in way of achieving something similar with the <code>t</code> commands.</p> <p>For example <code>tSh</code> would enable JavaScript on the opened site and reload. It will also ask to make the change permanent.</p> <p>I did not know this before writing this post, thanks to Hund for bringing this to attention.</p> <p><a href="https://fosstodon.org/web/statuses/105677750882153485">Link to discussion.</a></p> <hr> <p>I use qutebrowser sometimes, and it is an excellent fully keyboard driven browser. One thing I miss though, is the control on the loading of scripts. Once you get used to uMatrix, it is difficult to live without.</p> <p>We can however disable JavaScript altogether, and then selectively allow it for a small number of websites. Not the same level of control, but still could be useful.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>c.content.javascript.enabled = <span style="color:#fff;font-weight:bold">False</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#fff;font-weight:bold">with</span> config.pattern(<span style="color:#0ff;font-weight:bold">&#34;https://fosstodon.org/*&#34;</span>) <span style="color:#fff;font-weight:bold">as</span> p: </span></span><span style="display:flex;"><span> p.content.javascript.enabled = <span style="color:#fff;font-weight:bold">True</span> </span></span></code></pre></div><p>But, instead of adding code for every website, we can have an external plain text list, for example <code>js.sites</code>, and load it in the config.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#fff;font-weight:bold">try</span>: </span></span><span style="display:flex;"><span> <span style="color:#fff;font-weight:bold">with</span> (config.configdir / <span style="color:#0ff;font-weight:bold">&#39;js.sites&#39;</span>).open() <span style="color:#fff;font-weight:bold">as</span> js_file: </span></span><span style="display:flex;"><span> js_sites = js_file.read().split(<span style="color:#0ff;font-weight:bold">&#34;</span><span style="color:#0ff;font-weight:bold">\n</span><span style="color:#0ff;font-weight:bold">&#34;</span>) </span></span><span style="display:flex;"><span> js_file.close() </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#fff;font-weight:bold">for</span> js_site in js_sites: </span></span><span style="display:flex;"><span> <span style="color:#fff;font-weight:bold">if</span> js_site != <span style="color:#0ff;font-weight:bold">&#39;&#39;</span>: </span></span><span style="display:flex;"><span> config.set(<span style="color:#0ff;font-weight:bold">&#39;content.javascript.enabled&#39;</span>, <span style="color:#fff;font-weight:bold">True</span>, js_site) </span></span><span style="display:flex;"><span><span style="color:#fff;font-weight:bold">except</span> FileNotFoundError: </span></span><span style="display:flex;"><span> <span style="color:#fff;font-weight:bold">print</span>(<span style="color:#0ff;font-weight:bold">&#39;js.sites not found&#39;</span>) </span></span></code></pre></div><p>Now, we can add the list of websites in <code>js.sites</code>.</p> Apply Font in emacsclient https://blog.rzg.one/post/2021/01/25/apply-font-in-emacsclient/ Mon, 25 Jan 2021 00:00:00 +0000 https://blog.rzg.one/post/2021/01/25/apply-font-in-emacsclient/ <p>I set the default font in my Emacs config as follows:</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(set-frame-font <span style="color:#0ff;font-weight:bold">&#34;Source Code Pro 11&#34;</span> nil t) </span></span></code></pre></div><p>However it was not applied when Emacs was ran in client-server mode. Emacsclient behaved weird with tiny text.</p> <p>To apply in emacsclient as well, it seems another change needs to be done as well.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(add-to-list <span style="color:#0ff;font-weight:bold">&#39;default-frame-alist</span> &#39;(font . <span style="color:#0ff;font-weight:bold">&#34;Source Code Pro 11&#34;</span>)) </span></span></code></pre></div><p>As the font has to be set twice, I wanted to set a variable, which led to an interesting dive into Emacs Lisp.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(<span style="color:#fff;font-weight:bold">defvar</span> my-font) </span></span><span style="display:flex;"><span>(<span style="color:#fff;font-weight:bold">setq</span> my-font <span style="color:#0ff;font-weight:bold">&#34;Source Code Pro 11&#34;</span>) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>(set-frame-font my-font nil t) </span></span><span style="display:flex;"><span>(add-to-list <span style="color:#0ff;font-weight:bold">&#39;default-frame-alist</span> `(font . ,my-font)) </span></span></code></pre></div><p>The &ldquo;backquote&rdquo; concept took a while to discover. Initially, used the more general form <code>(cons 'font my-font)</code> until I discovered this shortcut.</p> <p>So, in this particular case, we do not want to evaluate <code>font</code>, but we do want <code>my-font</code>, thus we cannot put a quote for the whole cons. What the backquote allows, is to evaluate any symbol that has a comma in front.</p> <ul> <li><a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Quoting.html#Quoting">Manual information on quoting in general.</a></li> <li><a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Backquote.html">Manual information on the backquote.</a></li> </ul> Rofi Menu for the buku Bookmark Manager https://blog.rzg.one/post/2021/01/06/rofi-menu-for-the-buku-bookmark-manager/ Wed, 06 Jan 2021 00:00:00 +0000 https://blog.rzg.one/post/2021/01/06/rofi-menu-for-the-buku-bookmark-manager/ <p>Buku is a command-line bookmark manager, that takes the bookmarks out of the browser. I have completely converted to it, since it allows bookmarks to work independently of the browser, and makes syncing between different machines very trivial.</p> <p>I&rsquo;m not going into the usage details here, since it is well documented by <code>buku</code> themselves.</p> <ul> <li><a href="https://github.com/jarun/Buku">buku - Bookmark manager</a></li> </ul> <p>To share the bookmarks between different machines, all that is needed is the copying of the database file, located at <code>.local/share/buku/bookmarks.db</code>.</p> <p>There are various ways of invoking buku, including browser based add-ons. However, I prefer using it through a dmenu/rofi style menu.</p> <p>An existing implementation is <code>buku_run</code>, which tries to offer most of the <code>buku</code> functionality, including adding/deleting entries.</p> <ul> <li><a href="https://github.com/carnager/buku_run">buku_run - rofi frontend for buku bookmarks manager</a></li> </ul> <p>However, I find that too complicated for my needs. Especially, since it&rsquo;s very easy to make your own.</p> <p>But, the problem was, none of the <code>buku</code> output formats were suitable for me.</p> <p>One way is by using <code>buku --format</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>buku -p --format <span style="color:#ff0;font-weight:bold">5</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> | tail -n +2 <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> | awk -F<span style="color:#0ff;font-weight:bold">$&#39;\t&#39;</span> <span style="color:#0ff;font-weight:bold">&#39;{ print $1&#34;\t&#34;$3&#34;\t&#34;$2 }&#39;</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> | column -t -s <span style="color:#0ff;font-weight:bold">$&#39;\t&#39;</span> </span></span></code></pre></div><p>Getting the output in JSON format is more consistent, however, you will then need to parse that, with some lengthy complication like this:</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>buku -p -j <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> | tail -n +2 <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> | jq -r <span style="color:#0ff;font-weight:bold">&#39;.[] | &#34;\(.index)\t\(.tags)\t\(.title)&#34;&#39;</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> | column -t -s <span style="color:#0ff;font-weight:bold">$&#39;\t&#39;</span> </span></span></code></pre></div><p>Turns out we also need <code>tail -n +2</code> for both these cases, or a way to skip the first line, because when not run from a terminal, <code>buku</code> prints a message before the output.</p> <p>So, in the end, I decided to &ldquo;cheat&rdquo; and access the database directly, since it&rsquo;s standard SQLite anyway.</p> <p>Following is my complete <code>buku_menu</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#0f0;font-weight:bold">#!/usr/bin/env bash </span></span></span><span style="display:flex;"><span><span style="color:#0f0;font-weight:bold"></span> </span></span><span style="display:flex;"><span>getbookmarks() { </span></span><span style="display:flex;"><span> sqlite3 -column -separator <span style="color:#0ff;font-weight:bold">$&#39;\t&#39;</span> ~/.local/share/buku/bookmarks.db <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> <span style="color:#0ff;font-weight:bold">&#34;select id,trim(tags,&#39;,&#39;),metadata from bookmarks order by tags&#34;</span> </span></span><span style="display:flex;"><span>} </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>menu=<span style="color:#fff;font-weight:bold">$(</span>getbookmarks | rofi -dmenu -i -width <span style="color:#ff0;font-weight:bold">1000</span> -p <span style="color:#0ff;font-weight:bold">&#39;&gt; &#39;</span><span style="color:#fff;font-weight:bold">)</span> </span></span><span style="display:flex;"><span>id=<span style="color:#0ff;font-weight:bold">&#34;</span><span style="color:#0ff;font-weight:bold">${</span>menu%% *<span style="color:#0ff;font-weight:bold">}</span><span style="color:#0ff;font-weight:bold">&#34;</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>buku -o <span style="color:#0ff;font-weight:bold">&#34;</span><span style="color:#0ff;font-weight:bold">${</span>id<span style="color:#0ff;font-weight:bold">}</span><span style="color:#0ff;font-weight:bold">&#34;</span> </span></span></code></pre></div><p>I am also sorting the bookmarks by tags, which is one of the advantages of this method, as we can extract and display the data however we want.</p> Build Go Application with Version Information from Git https://blog.rzg.one/post/2020/12/25/build-go-application-with-version-information-from-git/ Fri, 25 Dec 2020 00:00:00 +0000 https://blog.rzg.one/post/2020/12/25/build-go-application-with-version-information-from-git/ <p>To automatically add version information to a Go application, we can pass that information from git through build arguments.</p> <p>This can be especially useful, if we are using git tags.</p> <p>For example, the following will get the latest <code>git tag</code>, the last commit hash, and whether there are locally modified uncommitted (dirty) files.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ git describe --always --long --dirty </span></span><span style="display:flex;"><span>v0.12.1-0-gca3521e-dirty </span></span></code></pre></div><p>More information on the options are available with <code>man git-describe</code>.</p> <p>To pass this information into our program, we can define a global variable, and assign it through build arguments.</p> <p>So, define a variable <code>appver</code> in the <code>main</code>.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#fff;font-weight:bold">package</span> main </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#fff;font-weight:bold">import</span> ( </span></span><span style="display:flex;"><span> <span style="color:#0ff;font-weight:bold">&#34;fmt&#34;</span> </span></span><span style="display:flex;"><span>) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#fff;font-weight:bold">var</span> ( </span></span><span style="display:flex;"><span> appver = <span style="color:#0ff;font-weight:bold">&#34;undefined&#34;</span> </span></span><span style="display:flex;"><span>) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#fff;font-weight:bold">func</span> main() { </span></span><span style="display:flex;"><span> fmt.Println(appver) </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><p>And build while assinging <code>main.appver</code>.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>go build -ldflags=<span style="color:#0ff;font-weight:bold">&#34;-X main.appver=</span><span style="color:#fff;font-weight:bold">$(</span>git describe --always --long --dirty<span style="color:#fff;font-weight:bold">)</span><span style="color:#0ff;font-weight:bold">&#34;</span> main.go </span></span></code></pre></div><p>Same flags will work with <code>go run</code>.</p> <p>Additionally, one can pass other variables like build time.</p> <p>To get the version of Go itself, <code>import &quot;runtime&quot;</code> and get it with <code>runtime.Version()</code>.</p> Gopher Version https://blog.rzg.one/post/2020/12/18/gopher-version/ Fri, 18 Dec 2020 00:00:00 +0000 https://blog.rzg.one/post/2020/12/18/gopher-version/ <p><em>Updated on 2022-12-18</em></p> <p>I have stopped updating the Gopher and Gemini versions of this site. At the time, I tried them out of curiosity, and I still think Gemini has interesting use cases.</p> <p>However, since I cannot do away with the regular http site, maintaining multiple versions actually makes things more complicated than otherwise.</p> <hr> <p>I have setup a Gopher mirror of this website.</p> <p>Since, the posts on this site are simple markdown files, it was easy converting them to plain text. I had to do some additional changes as well, like removing the image tags. Hopefully, I&rsquo;ll have a better, more automated solution soon.</p> <p>Why Gopher? Curiosity, interest.</p> <p>Easiest way to access is with the <code>lynx</code> browser:</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>lynx gopher://blog.rzg.one </span></span></code></pre></div><ul> <li><a href="https://en.wikipedia.org/wiki/Gopher_%28protocol%29">Gopher protocol</a></li> <li><a href="https://gopher.floodgap.com/overbite/">More info at the Overbite Project</a></li> </ul> Allow Access by Country with Firewalld https://blog.rzg.one/post/2020/12/11/allow-access-by-country-with-firewalld/ Fri, 11 Dec 2020 00:00:00 +0000 https://blog.rzg.one/post/2020/12/11/allow-access-by-country-with-firewalld/ <img class='pull-left' alt='shield icon' width='128px' src='https://blog.rzg.one/images/icon_shield.png' /> <p>There are various guides on the web, regarding how to block a whole country, or a group of IP addresses using various firewall methods.</p> <p>But, what if we need to do the opposite. Allow only one country, and block everyone else.</p> <p>This may be particularly useful for personal services, like Nextcloud, that you know will only be used by yourself.</p> <ul> <li><a href="https://nextcloud.com/">Nextcloud - Self-hosted file-sharing solution</a></li> </ul> <p>Of course, using the same method, it is also possible to allow only a few IP addresses, a city, or just one ISP. All that is needed are the group of IP blocks.</p> <p>For countries, we can get them from the IPdeny lists. The aggregated files will be more efficient, since they will have less blocks and thus fewer rules.</p> <ul> <li><a href="https://www.ipdeny.com/ipblocks/">IPdeny - Country based IP zone files</a></li> </ul> <p>Since this article uses firewalld, some quick notes on how it works.</p> <ul> <li><a href="https://wiki.archlinux.org/index.php/Firewalld">Arch Wiki - Firewalld</a></li> <li><a href="https://firewalld.org/">Firewalld project homepage</a></li> </ul> <p>By default, <code>firewalld</code> will have one active zone, with some default services attached.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007f7f"># get all active zones</span> </span></span><span style="display:flex;"><span>$ firewall-cmd --get-active-zones </span></span><span style="display:flex;"><span>public </span></span><span style="display:flex;"><span> interfaces: eth0 </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#007f7f"># list all rules on the public zone</span> </span></span><span style="display:flex;"><span>$ firewall-cmd --zone=public --list-all </span></span><span style="display:flex;"><span>public (active) </span></span><span style="display:flex;"><span> target: default </span></span><span style="display:flex;"><span> icmp-block-inversion: no </span></span><span style="display:flex;"><span> interfaces: eth0 </span></span><span style="display:flex;"><span> sources: </span></span><span style="display:flex;"><span> services: dhcpv6-client ssh </span></span><span style="display:flex;"><span> ports: </span></span><span style="display:flex;"><span> protocols: </span></span><span style="display:flex;"><span> masquerade: no </span></span><span style="display:flex;"><span> forward-ports: </span></span><span style="display:flex;"><span> source-ports: </span></span><span style="display:flex;"><span> icmp-blocks: </span></span><span style="display:flex;"><span> rich rules: </span></span></code></pre></div><p>The above rules mean, that everyone coming through the interface eth0 will be allowed on the services ssh and dhcpv6-client.</p> <hr> <p>Now back to our goal. We will need to do the following:</p> <ul> <li>Get the IP address blocks.</li> <li>Create a list, and populate it with our addresses.</li> <li>Create a new zone, and set the list to it.</li> <li>Assign all the needed services to this zone, remove them from the other zones.</li> </ul> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007f7f"># download the country IP blocks</span> </span></span><span style="display:flex;"><span>wget <span style="color:#0ff;font-weight:bold">&#34;https://www.ipdeny.com/ipblocks/data/aggregated/am-aggregated.zone&#34;</span> </span></span></code></pre></div><p>Here we create an <code>ipset</code> with the name <code>allowlist</code> (could be anything), and we populate it with the downloaded list.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007f7f"># create list</span> </span></span><span style="display:flex;"><span>firewall-cmd --permanent --new-ipset=allowlist <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> --type=hash:net --option=family=inet <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> --option=hashsize=<span style="color:#ff0;font-weight:bold">4096</span> --option=maxelem=<span style="color:#ff0;font-weight:bold">200000</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#007f7f"># populate list</span> </span></span><span style="display:flex;"><span>firewall-cmd --permanent --ipset=allowlist <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> --add-entries-from-file=./am-aggregated.zone </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#007f7f"># we can display the list like this</span> </span></span><span style="display:flex;"><span>firewall-cmd --ipset=allowlist --get-entries </span></span></code></pre></div><p>Then, we create a new zone <code>allowzone</code>, assign the <code>allowlist</code> to it, and enable some services and ports.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007f7f"># create zone</span> </span></span><span style="display:flex;"><span>firewall-cmd --permanent --new-zone allowzone </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#007f7f"># assign the list to the new zone</span> </span></span><span style="display:flex;"><span>firewall-cmd --permanent --zone=allowzone --add-source=ipset:allowlist </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#007f7f"># allow services</span> </span></span><span style="display:flex;"><span>firewall-cmd --permanent --zone=allowzone --add-service ssh </span></span><span style="display:flex;"><span>firewall-cmd --permanent --zone=allowzone --add-service http </span></span><span style="display:flex;"><span>firewall-cmd --permanent --zone=allowzone --add-service https </span></span></code></pre></div><p>Now, <code>allowzone</code> will only allow the added services for the <code>allowlist</code>, and no one else.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007f7f"># check all the rules on the new zone</span> </span></span><span style="display:flex;"><span>$ firewall-cmd --permanent --zone=allowzone --list-all </span></span><span style="display:flex;"><span>allowzone (active) </span></span><span style="display:flex;"><span> target: default </span></span><span style="display:flex;"><span> icmp-block-inversion: no </span></span><span style="display:flex;"><span> interfaces: </span></span><span style="display:flex;"><span> sources: ipset:allowlist </span></span><span style="display:flex;"><span> services: http https ssh </span></span><span style="display:flex;"><span> ports: </span></span><span style="display:flex;"><span> protocols: </span></span><span style="display:flex;"><span> masquerade: no </span></span><span style="display:flex;"><span> forward-ports: </span></span><span style="display:flex;"><span> source-ports: </span></span><span style="display:flex;"><span> icmp-blocks: </span></span><span style="display:flex;"><span> rich rules: </span></span></code></pre></div><p>We still need to remove any enabled services from the <code>public</code> zone, otherwise it will keep allowing everyone on eth0.</p> <p>Up until this point, since we are using the <code>--permanent</code> option, nothing will be applied until we reload or restart <code>firewalld</code>. And since we added a new zone, it is not yet dangerous to do so now. However, once we remove services like ssh from the active public zone, we may lock ourselves out. So, some common sense care should be applied here. Maybe start by removing a less dangerous port first.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007f7f"># remove services from public zone</span> </span></span><span style="display:flex;"><span>firewall-cmd --permanent --zone=public --remove-service ssh </span></span></code></pre></div><p>Finally, reload or restart <code>firewalld</code>.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>firewall-cmd --reload </span></span></code></pre></div> Multiple Keyboards with Different Layouts https://blog.rzg.one/post/2020/12/07/multiple-keyboards-with-different-layouts/ Mon, 07 Dec 2020 00:00:00 +0000 https://blog.rzg.one/post/2020/12/07/multiple-keyboards-with-different-layouts/ <p>I need to type in multiple languages, each having a different script. So, I have shortcuts set up to change the layout using <code>setxkbmap</code>.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007f7f"># setxkbmap [ layout [ variant ] ]</span> </span></span><span style="display:flex;"><span>setxkbmap us altgr-intl </span></span><span style="display:flex;"><span>setxkbmap am western </span></span><span style="display:flex;"><span>... </span></span></code></pre></div><p>But, sometimes, every other word is in a different language, so it is very cumbersome to switch layouts back and forth for every word.</p> <p>Turns out, it is possible to add a second keyboard, and assign a persistent layout to it. We simply need to apply <code>setxkbmap</code> on a specific device id.</p> <figure > <picture> <source srcset="https://blog.rzg.one/images/kb_poker_am.webp" type="image/webp"> <img src="https://blog.rzg.one/images/kb_poker_am.jpg" alt="my second keyboard - Armenian Phonetic layout"> </picture> <figcaption> <p>my second keyboard - Armenian Phonetic layout</p> </figcaption> </figure> <p>First we need to get the device ID of the second keyboard, which we can do with <code>xinput</code>.</p> <p>Sometimes, the same keyboard appears multiple times. I couldn&rsquo;t find a better way, other than trial and error with every device id.</p> <p>Now, we can set the global layout, and then set an alternative layout for the second keyboard.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007f7f"># each keyboard gets a different layout</span> </span></span><span style="display:flex;"><span>setxkbmap us altgr-intl </span></span><span style="display:flex;"><span>setxkbmap am western -device <span style="color:#ff0;font-weight:bold">17</span> </span></span></code></pre></div><p>Thankfully, the device id appears to persist through reboots, and stays the same.</p> VirtualBox Selection Menu https://blog.rzg.one/post/2020/12/06/virtualbox-selection-menu/ Sun, 06 Dec 2020 00:00:00 +0000 https://blog.rzg.one/post/2020/12/06/virtualbox-selection-menu/ <p>In a previous post, I showed my <code>dialog</code> based login menu.</p> <ul> <li><a href="https://blog.rzg.one/post/2020/12/01/login-menu-with-dialog/">Login Menu with dialog</a></li> </ul> <p>The menu included an option to start a VirtualBox virtual machine.</p> <p>The idea is to open a dmenu or rofi selection list of all virtual machines available. It can work either from an existing session, or by itself on login, without a window manager.</p> <ul> <li><a href="https://wiki.archlinux.org/index.php/Dmenu">Arch wiki - dmenu</a></li> <li><a href="https://wiki.archlinux.org/index.php/rofi">Arch wiki - rofi</a></li> </ul> <figure > <picture> <source srcset="https://blog.rzg.one/images/vboxmenu.webp" type="image/webp"> <img src="https://blog.rzg.one/images/vboxmenu.jpg" alt="virtual machines menu"> </picture> <figcaption> <p>virtual machines menu</p> </figcaption> </figure> <p>We can get the list of virtual machines, and strip the unnecessary parts like this.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>vboxmanage list -s vms | cut -d<span style="color:#0ff;font-weight:bold">&#39;&#34;&#39;</span> -f <span style="color:#ff0;font-weight:bold">2</span> </span></span></code></pre></div><p>This list can be passed to <code>rofi</code>, and can then be used to start the VM.</p> <p>The standard way of starting a VirtualBox VM is as follows.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>vboxmanage startvm <span style="color:#0ff;font-weight:bold">&#34;</span><span style="color:#0ff;font-weight:bold">${</span>VMNAME<span style="color:#0ff;font-weight:bold">}</span><span style="color:#0ff;font-weight:bold">&#34;</span> </span></span></code></pre></div><p>However, there is a problem with this method, because <code>vboxmanage</code> spawns another process and disappears.</p> <p>While this will work if ran from an existing session, it will not work on login, without a window manager, since the X session will end immediately and kill all processes including the virtual machine.</p> <p>Fortunately, by looking at the processes, we can see that <code>vboxmanage</code> runs the following process <code>/usr/lib/virtualbox/VirtualBoxVM</code>. We can use that directly, and the VM can now start without a window manager.</p> <p>Here is the menu script in full.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>getvms() { </span></span><span style="display:flex;"><span> vboxmanage list -s vms | cut -d<span style="color:#0ff;font-weight:bold">&#39;&#34;&#39;</span> -f <span style="color:#ff0;font-weight:bold">2</span> </span></span><span style="display:flex;"><span>} </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>CHOSEN=<span style="color:#fff;font-weight:bold">$(</span>getvms | rofi -dmenu -i -p <span style="color:#0ff;font-weight:bold">&#34;Select VirtualBox Machine&#34;</span><span style="color:#fff;font-weight:bold">)</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>[[ -z <span style="color:#0ff;font-weight:bold">${</span>CHOSEN<span style="color:#0ff;font-weight:bold">}</span> ]] &amp;&amp; <span style="color:#fff;font-weight:bold">exit</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>/usr/lib/virtualbox/VirtualBoxVM --comment <span style="color:#0ff;font-weight:bold">&#34;</span><span style="color:#0ff;font-weight:bold">${</span>CHOSEN<span style="color:#0ff;font-weight:bold">}</span><span style="color:#0ff;font-weight:bold">&#34;</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> --startvm <span style="color:#0ff;font-weight:bold">&#34;</span><span style="color:#0ff;font-weight:bold">${</span>CHOSEN<span style="color:#0ff;font-weight:bold">}</span><span style="color:#0ff;font-weight:bold">&#34;</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> --no-startvm-errormsgbox </span></span></code></pre></div><p>When ran without a window manager, powering off the VM will exit the X session automatically.</p> <p>One last note, sometimes <code>vboxmanage list</code> is very slow, and takes a few seconds to display the menu. For my case, I am taking the list directly from the directory names. Both methods have the same result.</p> Login Menu with dialog https://blog.rzg.one/post/2020/12/01/login-menu-with-dialog/ Tue, 01 Dec 2020 00:00:00 +0000 https://blog.rzg.one/post/2020/12/01/login-menu-with-dialog/ <p>Most standard Linux distribution installations come with a Display Manager, usually tied to the desktop environment. Like GDM for Gnome, LXDM for LXDE, etc. There are also a few lighter ones, like XDM/LightDM&hellip;</p> <p>But, not everyone likes a full Desktop Environment, or even a Display Manager. Some simply login on the shell and run <code>startx</code>.</p> <p>However, a Display Manager serves another purpose as well, that is selecting which environment to log into.</p> <p>So, for that I have made a custom menu with <code>dialog</code>.</p> <figure><img src="https://blog.rzg.one/images/login_dialog.png" alt="dialog selection"/><figcaption> <p>dialog selection</p> </figcaption> </figure> <p>The setup is based on <code>.zlogin</code> and <code>.xinitrc</code>.</p> <p>The first one may vary between Linux distributions, and default shells. On Arch Linux with zsh, logging in executes <code>.zlogin</code>, <code>startx</code> executes <code>.xinitrc</code>.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plaintext" data-lang="plaintext"><span style="display:flex;"><span> +-----------+ startx +-----------+ </span></span><span style="display:flex;"><span>login --&gt;| .zlogin |---------&gt;| .xinitrc | </span></span><span style="display:flex;"><span> +-----------+ +-----------+ </span></span></code></pre></div><p>So, in <code>.zlogin</code> I have a menu of all the possible window managers, and even some applications.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007f7f"># select window manager</span> </span></span><span style="display:flex;"><span><span style="color:#fff;font-weight:bold">if</span> [[ -z <span style="color:#0ff;font-weight:bold">${</span>DISPLAY<span style="color:#0ff;font-weight:bold">}</span> &amp;&amp; <span style="color:#0ff;font-weight:bold">${</span>XDG_VTNR<span style="color:#0ff;font-weight:bold">}</span> -le <span style="color:#ff0;font-weight:bold">2</span> &amp;&amp; -z <span style="color:#0ff;font-weight:bold">&#34;</span><span style="color:#0ff;font-weight:bold">${</span>SSH_TTY<span style="color:#0ff;font-weight:bold">}</span><span style="color:#0ff;font-weight:bold">&#34;</span> ]] ; <span style="color:#fff;font-weight:bold">then</span> </span></span><span style="display:flex;"><span> <span style="color:#fff;font-weight:bold">export</span> WM=<span style="color:#fff;font-weight:bold">$(</span>dialog --backtitle <span style="color:#0ff;font-weight:bold">&#34;Login Manager&#34;</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> --nook --nocancel --no-tags <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> --timeout <span style="color:#ff0;font-weight:bold">5</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> --menu <span style="color:#0ff;font-weight:bold">&#34;Select WM&#34;</span> <span style="color:#ff0;font-weight:bold">15</span> <span style="color:#ff0;font-weight:bold">60</span> <span style="color:#ff0;font-weight:bold">8</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> <span style="color:#0ff;font-weight:bold">&#34;spectrwm&#34;</span> <span style="color:#0ff;font-weight:bold">&#34;1: spectrwm&#34;</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> <span style="color:#0ff;font-weight:bold">&#34;hlwm&#34;</span> <span style="color:#0ff;font-weight:bold">&#34;2: herbstluftwm&#34;</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> <span style="color:#0ff;font-weight:bold">&#34;bspwm&#34;</span> <span style="color:#0ff;font-weight:bold">&#34;3: bspwm&#34;</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> <span style="color:#0ff;font-weight:bold">&#34;emacs&#34;</span> <span style="color:#0ff;font-weight:bold">&#34;4: Emacs&#34;</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> <span style="color:#0ff;font-weight:bold">&#34;browser&#34;</span> <span style="color:#0ff;font-weight:bold">&#34;5: Browser&#34;</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> <span style="color:#0ff;font-weight:bold">&#34;vbox&#34;</span> <span style="color:#0ff;font-weight:bold">&#34;6: VirtualBox&#34;</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> <span style="color:#0ff;font-weight:bold">&#34;tmux&#34;</span> <span style="color:#0ff;font-weight:bold">&#34;7: tmux&#34;</span> <span style="color:#0ff;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#0ff;font-weight:bold"></span> 3&gt;&amp;<span style="color:#ff0;font-weight:bold">1</span> 1&gt;&amp;<span style="color:#ff0;font-weight:bold">2</span> 2&gt;&amp;3<span style="color:#fff;font-weight:bold">)</span> </span></span><span style="display:flex;"><span> clear </span></span><span style="display:flex;"><span> <span style="color:#fff;font-weight:bold">exec</span> startx </span></span><span style="display:flex;"><span><span style="color:#fff;font-weight:bold">fi</span> </span></span></code></pre></div><p>By testing for <code>${XDG_VTNR} -le 2</code>, this menu will run only on tty1 and tty2, and not from ssh.</p> <p>In addition to the Window Managers, I also have a terminal with a tmux session, Emacs, a VirtualBox virtual machines selection menu, and Firefox.</p> <ul> <li><a href="https://blog.rzg.one/post/2020/12/06/virtualbox-selection-menu/">VirtualBox Selection Menu</a></li> </ul> <p>Based on the selection, <code>.xinitrc</code> will run the equivalent WM or application.</p> <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#fff;font-weight:bold">case</span> <span style="color:#0ff;font-weight:bold">${</span>WM<span style="color:#0ff;font-weight:bold">}</span> in </span></span><span style="display:flex;"><span> browser) </span></span><span style="display:flex;"><span> <span style="color:#fff;font-weight:bold">exec</span> firefox --kiosk --new-instance </span></span><span style="display:flex;"><span> ;; </span></span><span style="display:flex;"><span> emacs) </span></span><span style="display:flex;"><span> xsetroot -solid black </span></span><span style="display:flex;"><span> <span style="color:#fff;font-weight:bold">exec</span> emacs -fs -mm </span></span><span style="display:flex;"><span> ;; </span></span><span style="display:flex;"><span> vbox) </span></span><span style="display:flex;"><span> <span style="color:#fff;font-weight:bold">exec</span> vboxmenu </span></span><span style="display:flex;"><span> ;; </span></span><span style="display:flex;"><span> tmux) </span></span><span style="display:flex;"><span> xsetroot -solid black </span></span><span style="display:flex;"><span> <span style="color:#fff;font-weight:bold">exec</span> alacritty -d <span style="color:#ff0;font-weight:bold">382</span> <span style="color:#ff0;font-weight:bold">80</span> --position <span style="color:#ff0;font-weight:bold">0</span> <span style="color:#ff0;font-weight:bold">0</span> -e tmux </span></span><span style="display:flex;"><span> ;; </span></span><span style="display:flex;"><span> bspwm) </span></span><span style="display:flex;"><span> <span style="color:#fff;font-weight:bold">exec</span> bspwm </span></span><span style="display:flex;"><span> ;; </span></span><span style="display:flex;"><span> hlwm) </span></span><span style="display:flex;"><span> <span style="color:#fff;font-weight:bold">exec</span> herbstluftwm --locked </span></span><span style="display:flex;"><span> ;; </span></span><span style="display:flex;"><span> spectrwm|*) </span></span><span style="display:flex;"><span> <span style="color:#fff;font-weight:bold">exec</span> spectrwm </span></span><span style="display:flex;"><span> ;; </span></span><span style="display:flex;"><span><span style="color:#fff;font-weight:bold">esac</span> </span></span></code></pre></div><p>The menu will timeout in 5 seconds, and automatically login to the default option, which is spectrwm.</p> <p>Important to note, that it is perfectly possible to have two different Window Managers or applications running concurrently on different ttys.</p>