From the Terminal

Compiling and Installing RocketRaid RR64xL drivers on Linux 5.x

wget https://raw.githubusercontent.com/dsiggi/RocketRAID/master/sys-block/rr64xl/files/rr64xl-kernel-5.patch
wget http://www.highpoint-tech.com/BIOS_Driver/RR64xL/Linux/RR64xl_Linux_Src_v1.4.0_16_09_20.tar.gz
tar xf RR64xl_Linux_Src_v1.4.0_16_09_20.tar.gz
patch < rr64xl-kernel-5.patch
cd rr64xl-linux-src-v1.4.0/product/rr64xl/linux/
sudo make KERNELDIR=/usr/src/linux

Installing XDebug on anything for VSCode in 5 minutes (XDebug 3.x)

This guide is for XDebug 3.x only.

For XDebug 2.x clear here.

 

I see a lot of over complicated guides on XDebug so I'll simplify things real quick for everyone.

Visual Studio Code has debugging support out of the box. Click on the Debug icon in the left bar (OS X: ⇧⌘D, Windows / Linux: CTRL+SHIFT+D) then click on the cog icon which should open your launch.json file or create one if none exists.


You must have the PHP XDebug extension installed of course.

Now add this to your launch.json file you have open:

        {
            "type": "php",
            "request": "launch",
            "name": "Listen For XDebug",
            "port": 9003,
            "pathMappings": {
                "/var/www/": "${workspaceRoot}"
            },
            "xdebugSettings": {
                "max_children": 256,
                "max_data": -1,
                "max_depth": 5
            },
            "ignore": [
                "**/vendor/**/*.php"
            ]
        }

Make sure you change /var/www/ to where your code is on your local server.

Set this in your php.ini

[xdebug]
xdebug.mode = debug
xdebug.start_with_request = yes
xdebug.idekey = VSCODE
xdebug.client_port = 9003
xdebug.client_host = "127.0.0.1"
xdebug.discover_client_host  = 1
xdebug.log="/tmp/xdebug.log"
xdebug.cli_color = 1

You might need to do it twice. Once for CLI and once for PHP-FPM!

 

Typical locations for your php.ini file:

  • Linux: /etc/php/{$version}/php.ini
  • macOS (Homebrew): /usr/local/etc/php/{$version}/php.ini

Don't forget to restart php-fpm!

Now start the debugger by hitting the green play button.

How Connecting to Cloudflare's Warp VPN Can Change Your Data Center Depending On Whether You Are Using IPv4 or IPv6

I recently moved to Maui, HI and have a Spectrum 960/20 land line hooked up. Cloudflare peers with DrFortress in Honolulu which allows me to ping 1.1.1.1 and get some incredible response times. At least for Hawaii:

    64 bytes from 1.1.1.1: icmp_seq=1 ttl=58 time=13.7 ms

Awesome right?

Well then I wanted to figure out how to play games on Japanese servers. For American servers I get about 40-60 ms for west coast and 110-130 for east coast. In particular my DigitalOcean VPS in NYC responds in 125 ms.

For my testing I'm using the server IPs for the game FFXIV. In this case one of the servers in the "Mana" Data Center in Japan.

Here's what I get pinging the data center with no VPN.

    ping 124.150.157.49

    64 bytes from 124.150.157.49: icmp_seq=1 ttl=47 time=177 ms

Not ideal. Of course it's because it goes through LA on it's way to Japan. Doing a traceroute confirms that. But I knew going into this that there was a direct line to Japan from Honolulu and it is, in fact, possible to send a network packet directly to Japan from Hawaii. Unfortunately the routing depended on your ISP's routing to make it go directly. So then I started playing with VPNs.

I discovered that Cloudflare actually has a free VPN called Warp and while it doesn't provide the best throughput there's no real issue with using it for low bandwidth game connections and simple web browsing.

Trying it Out

I have a Linux desktop running Gentoo and a Macbook Pro. To test this I went ahead and setup Cloudflare Warp on my Macbook and pinged the same IP. Setting it up on MacOS is as simple as installing the official app.

So let's see what we get on the Macbook.

    64 bytes from 124.150.157.49: icmp_seq=1 ttl=54 time=135.54 ms

A whole 40 ms shaved off the ping. Time to install it on my desktop!

On Linux there's an unofficial CLI utility called wgcf which will build you a Wireguard configuration you can use to connect to the service. I had to recompile my kernel to allow wireguard but no big deal. I ping the same IP on my desktop now. Again same internet but this time a wired ethernet connection instead of wifi.

    64 bytes from 124.150.157.49: icmp_seq=1 ttl=51 time=160 ms

Huh?!? How could this be? I was promised speed!!!!

Well it turns out after visiting https://www.cloudflare.com/cdn-cgi/trace I found out what the issue was.

On my desktop I'm getting assigned to the LA data center. But on my laptop I'm getting assigned to Honolulu. All my research on this topic suggests that the "closest" is chosen via UDP broadcast probing but I wasn't entirely convinced so I went digging.

It turns out if you look at the file wgcf-profile.conf it will have this setting:

    Endpoint = engage.cloudflareclient.com:2408

With IPv6 the IP I get when resolving the above hostname is an IPv6 address which is sending me to the LAX data center. When I resolved the hostname manually with nslookup I got 162.159.192.1 for IPv4 and I was able to disregard the IPv6 address. I changed the endpoint setting to use this IPv4 address instead and now my Wireguard connection goes to the HNL data center.

Making Patches for Portage Packages

When you run emerge portage does a series of things to your package in order:

  • fetch
  • unpack
  • prepare
  • compile

To set up a patch working environment we're going to go ahead and let it do all of that up to prepare. We will do this with the ebuild command:

ebuild file.ebuild fetch
ebuild file.ebuild unpack
ebuild file.ebuild prepare

 

Now we copy the work directory to our own directory:

sudo cp -R /var/tmp/portage/www-client/ungoogled-chromium-88.0.4324.150-r2/work ~/Sources/ungoogled-chromium-88.0.4324.150-r2-work
sudo chown -R akujin:users /var/tmp/portage/www-client/ungoogled-chromium-88.0.4324.150-r2/work ~/Sources/ungoogled-chromium-88.0.4324.150-r2-work

 

Now we have our work directory where we can build our patch. The patch will be applied after prepare is done running so that is why we copied the work directory right after prepare was done running. In our copy of the work directory we need to initialize the folder as a git repo so we go ahead and run:

git init
git add .
git commit

 

Now we can make the modifications we want to make. After you're done simply stage your changes and run:

sudo sh -c "git diff --staged > /etc/portage/patches/www-client/ungoogled-chromium/macos-hotkeys-on-linux.patch"

 

You can test your new patch simply by running emerge on your package.

How to securely share a network mount through an existing SSH server

I recently wanted to share a bunch of files with a friend over the internet. These files were on my mediacenter which is a Windows machine running a RAID. This machine was already sharing these files on a Windows file share but I didn't want to expose these files to the internet. Since my workstation was a Linux box already running SSH server on port 22 with SSH key authentication already working I figured the cleanest thing would be to add a new user and set the home directory to the network share with the shell disabled. Easier said than done so I'm writing this post to discuss some blockers I encountered and how I solved them.

First I needed to add a new user. This is the command I used in Gentoo however in most Linux distros you will likely need to use adduser instead.

sudo useradd ftp -M -N -s /sbin/nologin -R /dev/null
  • -M Don't create a home directory
  • -N No user group
  • -s /sbin/nologin sets the shell to nologin to disable any logins
  • -R /dev/null sets the home directory to /dev/null to disable the home directory

Now we need to edit sshd_config for this specific user.

Near the bottom of the file we add this to the config.

Match User ftp
        AuthorizedKeysFile /etc/ssh/authorized_keys_%u
        ForceCommand internal-sftp
        AllowTCPForwarding no
        X11Forwarding no
        ChrootDirectory /home/ftp

Since the AuthorizedKeysFile depends on having a home directory by default we must override that setting to specify a different one for just this one user. Configure that as you would normally.

For security reasons we disable AllowTCPForwarding so people can't use your SFTP as a free proxy and X11 forwarding so that people can't access your X session. Finally we need to use a chroot directory.

 

My initial instinct was to directly use my media center mount as /mnt/mediacenter/ but this was failing since that directory was a network share. SFTP server requires that the chroot directory is owned by root. So despite us setting this user to not use a home directory I decided to use /home/$user for that reason anyway. But in this case I set it as owned by root instead of the user like normal. Now I had to setup some link to the mediacenter mount. Soft links will not work with SFTP so instead I used the mount command like so.

sudo mount --bind /mnt/mediacenter/Library/Anime/ /home/ftp/Anime

Now we can test it. I decided to use my laptop.

There you have it. A fully secure SFTP over SSH.

This technique should work well with WSL1 and WSL2 as well if you're using Windows.

Maui Turtle

Just testing the social media image embedding

Getting MacOS style hotkeys working in GNU/Linux

Over the years I've used every operating system. Sometimes all at once. Recently I've taken to using Linux as my primary desktop operating system. Everything I need runs natively on Linux and because I have full control over my environment I never have down time due to forced updates. This is my desktop after all so I'm really switching from Windows where the Control key modifier paradigm is more or less the same as Linux. Still I didn't want to compromise on anything with this new configuration and there was one thing missing from my Macbook that I really wanted.

Of course what I'm talking about is the Command key or more colloquially known simply as the Apple Key and all the shortcuts and hot keys built into macOS that make it such a joy to use.

"Linux lets you change whatever you want."

Is a common thing that people say to bring up a positive aspect of GNU/Linux. Is this actually true though? Take for example my issue at hand: Changing copy/paste hotkeys for all programs. In truth, some tasks are so big in their scope few users would ever be capable of accomplishing them. At least not without a lot of contextual knowledge.

Getting macOS style hotkeys is actually an extremely difficult task in Linux. Especially when you want to avoid faking it by binding some keys to other keys on a per application basis.

Then there's the question of why do it at all?

The truth is I just got tired of accidentally sending SIGINTs when switching to terminal and then accidentally triggering the wrong thing when switching out of terminal. I've just gotten so used to not having to context switch my brain in macOS between programs. It's one of the things that truly makes my Macbook Pro more pleasant to use.

So that's my first reason. Muscle memory.

My second reason is simply that Command/Meta is directly next to the space bar and I just like having a shorter stretching span for my hand.

 

Making it work

For the most part GUI applications written for Linux tend to make a lot of assumptions about what hotkeys can and should be in a very opinionated way. Some types of programs require full customization (like code editors) while others like browsers tend to force certain hotkeys. Desktop managers do provide their own hotkey bindings. For example most KDE based applications will use your settings from KDE's "System Settings" to set hotkeys. Meanwhile in Gnome you can use dbind to set your keybinds.

Of course Cut/Copy/Paste is not enough. It would actually be quite jarring to use CTRL+T for opening a new tab immediately after using Command+V to paste something. Indeed if you go down this path you'll end up having to redefine your hotkeys for well..... everything. It is unfortunate as well that applications these days are extremely hostile to user settings.

Despite the hurdles in front of me I decided to try to make it work.

The first thing I did was swap Left Alt and Left Meta on my keyboard. This was actually pretty easy to do with keyboard mapping configs with the KDE GUI simply known as "System Settings". This was simply to make my normal windows keyboard place the buttons in the positions a real mac keyboard would have. None of this is necessary if you have a real Apple layout keyboard. This setting is only active while in my X session so any alternative TTYs would revert back to my stock layout. If I had a real Apple layout keyboard though this is not necessary at all.

This setting is saved to the text file ~/.config/kxkbrc.

$ cat kxkbrc 
[Layout]
DisplayNames=
LayoutList=us
LayoutLoopCount=-1
Model=pc101
Options=altwin:swap_lalt_lwin
ResetOldOptions=true
ShowFlag=false
ShowLabel=true
ShowLayoutIndicator=true
ShowSingle=false
SwitchMode=Global
Use=true

Now with this setting I am able to fake the same key mapping as a real macOS keyboard would have in my desktop session. So far so good. Now I manually changed all my hotkeys in my code editors, terminal applications, and even in the System Settings app I just mentioned to set global hot keys for all KDE applications. This took some time but was very straight forward for the most part.

I went above and beyond even changing my shortcuts for things like Select All.

While this worked for KDE applications I had to also make this change for GTK+ based programs as well. Luckily this would also cover Chrome, Discord, Elements, and a number of other Electron based programs that I use on a regular basis.

I was lucky that VSCode let me change everything but it was very annoying have to redo all the system based hotkeys like Set cursor to start of the line (in macOS this is Meta+Left Arrow) for each and every place I wanted to use it.

Some websites also use Meta+C for example in Github hitting Meta+C will actually take focus and bring you to the Add Comment UI when in a thread so sometimes on Github.com even with Firefox set to use Meta+C to copy something I am not actually able to use my hotkey because the website overrides it.

With GTK I ended up editing `~/.config/gtk-3.0/gtk.css` and setting these hotkeys.

@binding-set gtk-super-cut-copy-paste
{
        bind "<super>x" { "cut-clipboard" () };
        bind "<super>c" { "copy-clipboard" () };
        bind "<super>v" { "paste-clipboard" () };
        bind "<super>a" { "select-all" (1) };
        bind "<super>z" { "undo" () };
}

* {
        -gtk-key-bindings: gtk-super-cut-copy-paste
}

I might add more but for now this got me to where I wanted with most GTK+ applications. Note that if you plan to run any of programs designed to run as root that use GTK+ such as GParted you should also link or copy this config file to the root account home directory as well.

So in the end it is possible. I was able to achieve what I wanted with all but one program. But it was a huge pain. It's really difficult to figure out what settings do what in a Linux environment even when there is documentation. It's typical for documentation to exist for say GTK+ 2.0 and GTK+ 3.0 but there is no over arching best practice guide for what one should do when they want to cover all programs in an environment.

 

 

It Doesn't Have to Be This Way

The Linux ecosystem can and should do better. The ability to do whatever you want is a double edged sword where even though you can in theory change things, in practice however, sometimes changing things is so onerous that staying sane in the process is a task in itself.

So please if you are a Linux user space developer check the GTK+ 2.0 (~/.gtkrc-2.0), GTK+ 3.0 (~/.config/gtk-3.0/gtk.css) and KDE global shortcut configs (~/.config/kdeglobals) when you probe for the environment during startup so that people can choose their own hotkeys for their desktop environment or simply use the C libraries of each and use the hotkeys present.

Better yet, if you are a developer that already supports various input methods for macOS, Linux, and Windows you can simply provide a toggle for users.

It would be beneficial to Linux Desktop environments to agree to a standard config file for hotkeys allowing users to bind keys as they see fit rather than lurching from one weird keybinding config to the next with no centralization to speak off.

Using Meta instead of Ctrl for Cut, Copy, Paste shortcuts in Firefox for Linux

Set these in about:config

accessibility.typeaheadfind isn't blocked by Meta + C / Meta+V so we need to turn it off.

ui.key.generalAccessKey:   224
ui.key.accelKey: 91
accessibility.typeaheadfind: false
accessibility.typeaheadfind.autostart: false

A few useful life cycle tracking variables every ORM should have

Right now I'm in the unusual position of being responsible for maintaining two different ORMs libraries. Occasionally I want to implement a feature that exists in one of them in the other. One such feature recently came up and I think this is something that all ORMs would benefit from whether or not they are custom written or maintained by the community. In fact after looking into things it looks like some of these already exist in one way or another in some of the most popular ORMs available.

Let's take a look at some of them.

  • isNew - False by default. Set to true only when an object that isPhantom is saved
  • isUpdated - Set to true when an object that already existed in the data store is saved.
  • isPhantom - True if this object was instantiated as a brand new object and isn't yet saved.
  • isDirty - False by default. Set to true only when an object has had any field change from it's state when it was instantiated.

Some of these properties might already exist in one form or another in your frameworks. They are simple to implement by simply adding them to the functions in the Model class at different points in the life cycle of the object. Most of them are extremely simple but as you'll see with isDirty consideration might get extremely complex in a hurry.

isNew

The point of isNew is to be able to tell that this object was created during this script execution but was previously saved. Before save it's obvious that an object is new by it's missing primary key. However after saving all evidence of this object being new has been erased. This property lets you know that this object was previously created by you so that if it's part of a large array you can iterate over the items and still know which objects are the "new" ones.

Let's take a look at how this might be useful.

<?php
$cars = Car::GetAll();

$car = new Car();
$car->save();
$cars[] = $car;

foreach($cars as $car) {
    if($car->isNew) {
        // do something special to the objects we created above even if we saved them since then
    }
}

 

isUpdated

This one is pretty simple. This let's us know something that was already in the data store, has been updated by us since initialization.

isPhantom

Let's us know if we need to Update or Insert this model when saving the object. If Insert this gets set to false right after.

 

Implementation Details

Let's table isDirty for now and come back to it. For now let's take a look at some code. Here we do a pretty basic boiler plate for the variables we want and how they are manipulated later on.

__construct()

<?php
public function __construct(..., $isDirty = false, $isPhantom = null /* you can
    implement these differently but the hydrator needs some way to set these
    for an instantiated object */)
    {
        $this->_isPhantom = false; // this should be set by your model hydrator at time of hydration
        $this->_isDirty = $this->_isPhantom || $isDirty;
        $this->_isNew = false;
        $this->_isUpdated = false;
    }

save()

public function save() {
        if ($this->isDirty) {
            // create new or update existing
            if ($this->_isPhantom) {
                // do insert
                $this->_isPhantom = false;
                $this->_isNew = true;
            } else {
                // do update
                $this->_isUpdated = true;
            }

            $this->_isDirty = false; // only if you don't use a function to detect this
        }
}

So here we can see how it all comes together from initialization to storage. But what about isDirty? What's so complex about it? Well let's take a look.

 

isDirty

There's multiple ways to implement isDirty and both have positive aspects as well as drawbacks. If your ORM uses direct PHP object properties that map to fields only one is open to you anyway. Let's take a look at what I mean.

If you use an ORM where the fields are not object properties and are in-fact "not real" that means that you can actually implement this as a simple boolean.

For example:

<?php
public function __construct(...) {
    $this->_isDirty = false;
}

public function __set($field,$value) {
    $this->_isDirty = true;
    ...
}

public function save() {
    ... 
    $this->_isDirty = false;
}

The best thing about this method is how light is it on the run time. You simple need one boolean worth of memory space for each object. Unfortunately this method also can't tell when the field was set back to it's original value or if a field was edited but the value remained the same as it was before. So while you may save some memory up front in your run time you might end up wasting a lot more memory resources in your database updating records that have no new data or simply slowing down batch processes sending writes to the database that it will end up wasting time. Furthermore this only works with dynamically allocated "magic" properties.

Your other option is to use a method to test each field for a change and return true or false. Okay but how? By keeping a copy of the "original" field values of course. And this is where this method starts to show it's down sides. You actually end up approximately doubling the amount of memory you need to make this happen. Plus that itself has complexity. For example do you change your original variables after a save? Unfortunately the answer is yes. Simple because you want isDirty to go back to false after running a save. Some things are free though. Any object that is a phantom is dirty by definition.

public function isDirty()
{
		if ($this->isPhantom) {
			return true;
		}

		$fields = static::getFields();
		foreach ($fields as $field) {
			if ($this->isFieldDirty($field)) {
				return true;
			}
		}
		return false;
}

public function isFieldDirty($field)
{
		if (isset($this->_originalValues[$field])) {
			if ($this->_originalValues[$field] !== $this->$field) {
				return true;
			} else {
				return false;
			}
		}
		return false;
}

Of course you'll need to set_originalValues in your object during hydration. New objects won't start tracking this until immediately after save. It's worth noting that Laravel's Eloquent implements isDirty in a very similar way.

 

Closing Thoughts

Most ORMs implement these one way or another out of necessity however it would be nice if ORMS could standardize on these and perhaps start using the same terminology surrounding them. Is dirty is optional for many ORMs when it's beneficial to force a save() even if nothing has been changed simply to updated the modified at timestamp for the record in the data store. Still a well designed ORM should let you do both depending on your needs.

Ignoring SAPI with Symfony VarDumper

<?php
$varCloner = new \Symfony\Component\VarDumper\Cloner\VarCloner();
$cliDumper = new \Symfony\Component\VarDumper\Dumper\CliDumper();
echo $cliDumper->dump($varCloner->cloneVar($query), true);
		

Quick way to output full text instead of HTML when SAPI is not CLI