Unscaled Doesn't Mean It Won't Scale

One application I created had to show a simple, fixed image on screen. As it’s size is fixed and no resizing is needed, one might assume following code would be sufficient:

var left = (ClientRectangle.Width - bitmap.Width) / 2;
var top = (ClientRectangle.Height - bitmap.Height) / 2;
e.Graphics.DrawImageUnscaled(bitmap, left, top);
e.Graphics.DrawRectangle(SystemPens.WindowText, top, left, bitmap.Width, bitmap.Height);

Illustration

However, running this code might result in image significantly larger than the rectangle.

You see, DrawImageUnscaled will not necessarily ignore all scaling. It will attempt to preserve physical size - not size in pixels. So, if you have a screen punching above ancient 96 DPI, you will see scaling happen.

So, if you want to draw unscaled image, just use the normal DrawImage function and specify the size yourself.

Supermicro's IPMI Firewall Rules

Illustration

If your internal firewall is very restrictive or you need to expose IPMI to the outside world, you might be presented with a bit of a challenge due to quite varied port selection.

The first ports you have to allow are of course TCP 80 and 443 for web management interface. Almost all IPMI implementations have it and quite often it’s the interface with the most features. For example, Supermicro’s implementation only allows BIOS update and port number changes over web interface. This interface unfortunately stops just short of allowing console access.

To get access via IPMI tool (I use Supermicro’s IPMI View) you need to have UDP port 623 allowed through. This will allow logging into the IPMI interface and seeing machine’s status. Unfortunately, this too stops short of console access.

The key to the console (aka KVM) access is in TCP ports 3520 and 5900. These will allow you to see and type into. And only if you ever ran IPMI in nonrestrictive network would you notice something missing.

The missing piece is the menu, allowing you to mount virtual media and similar. For this you need to enable TCP port 623. This will finally allow full control over the hardware.

It’s a bit of annoyance that so many ports are needed but in general this doesn’t present the problem. Unless there are special circumstances, you shouldn’t access IPMI from the outside via port forwarding. What you should do is use VPN and then use IPMI via it.

Annotating Icons

Placing icon in tray is usually quite straightforward. You usually just take application icon and assign it to NotifyIcon. But what if you want to add a small annotation to tray icon (e.g. small shield)?

Well, you can do something like this. Just get application icon and draw another image on it. Yes, it does require a bit of calculation to get it into the bottom right corner but nothing that a little math cannot handle.

private static Icon GetAnnotatedIcon(Bitmap annotation) {
  var icon = GetApplicationIcon();

  if (icon != null) {
    var image = icon.ToBitmap();
    if (icon != null) {
      using (var g = Graphics.FromImage(image)) {
        g.DrawImage(annotation, (int)g.VisibleClipBounds.Width - annotation.Width - 2, (int)g.VisibleClipBounds.Height - annotation.Height - 2);
        g.Flush();
      }
    }
    return Icon.FromHandle(image.GetHicon());
  }
  return null;
}

There is only one magic moment above - how to get application icon. Fortunately, a bit of P/Invoke goes a long way.

private static Icon GetApplicationIcon() {
  var hLibrary = NativeMethods.LoadLibrary(Assembly.GetEntryAssembly().Location);
  if (!hLibrary.Equals(IntPtr.Zero)) {
    var hIcon = NativeMethods.LoadImage(hLibrary, "#32512", NativeMethods.IMAGE_ICON, 20, 20, 0);
    if (!hIcon.Equals(System.IntPtr.Zero)) {
      var icon = Icon.FromHandle(hIcon);
      if (icon != null) { return icon; }
    }
  }
  return null;
}

private static class NativeMethods {
  public const UInt32 IMAGE_ICON = 1;

  [DllImport("user32.dll", CharSet = CharSet.Unicode)]
  static extern internal IntPtr LoadImage(IntPtr hInstance, String lpIconName, UInt32 uType, Int32 cxDesired, Int32 cyDesired, UInt32 fuLoad);

  [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
  static extern internal IntPtr LoadLibrary(string lpFileName);
}

And yes, this code doesn’t take DPI into account so your high-definition icon might suffer - let’s leave that for some other post. :)

Changing Git Commit Message

Every once in a while I figure a typo as I am hitting git commit. Mind you, it’s not that I am making a typo once in a while - I do typos all the time - I only notice them once in a while. :)

What follows is a common pair of commands:

git reset --soft HEAD~
git commit -m"Corrected spelling."

As I did with other git operations I do often enough, I decided to make it an alias:

git config --global alias.redo '!git reset --soft HEAD~ && git commit'

Now I can use a single git command to do correction:

git redo -m"Corrected spelling."

Even better, if comment is not given, command will simply move the latest commit back into the staging area.

Might not be a huge change but it does make a common operation faster.

Git Client on NAS4Free

Using NAS4Free as my centralized storage also means I use it for Git repositories. As such it would be awesome if I could do some basic stuff with Git directly from my server’s bash command line.

It’s possible to install packages on NAS4Free - just like on any FreeBSD - you can write pkg install -y git. However, due to file system size of embedded installation that simple solution doesn’t work - it’s strange how fast those memory disks fill. Yes, one could go with the full NAS4Free install instead but there’s another way.

When it comes to file systems any BSD offers a lot of possibilities - one of which is unionfs. That one allows you to expand file system with the additional space while actually preserving stuff already present in directory.

To preserve the fleeting nature of embedded installation and due to sometime finicky nature of the united file system, I find it’s best to have a fresh directory for every boot, followed by mounting of fresh overlay file system:

rm -rf /mnt/.unionfs 2> /dev/null
mkdir -p /mnt/.unionfs/usr/local
mount_unionfs /mnt/.unionfs/usr/local /usr/local

Now installing Git actually works:

pkg install -y git

One could optionally also set Git configuration, especially user name and e-mail:

cat <<EOF > /root/.gitconfig
[system]
autocrlf = false
[core]
pager = less
whitespace = cr-at-eol
[user]
name = ^^Charlie Root^^
email = ^^root@my.home^^
EOF

If all this is placed in System, Advanced, Command scripts, it’ll get preserved with reboots.

[2018-07-22: NAS4Free has been renamed to XigmaNAS as of July 2018]