Updating Local WP Using Podman

I have locally an instance of WordPress running using podman. The php image is built using my custom wordpress image. This is a very basic wordpress environment, and one of the downsides is that updating WordPress core, themes, or plugins from the dashboard is not possible because WordPress ends up asking for FTP access, and my container does not have FTP set up. In the past I just manually updated the files, but honestly, having the convenience of just clicking a button to update is, well… convenient. So, today I wanted to fix that.

To be able to update WordPress from the dashboard you have two options. You can either use FTP so that php can use it to update the files in the server, or you can have the owner of the WordPress directory be the same user as the user running apache, which in my container is www-data.

My first thought was to just set up FTP on the container, which would have the added benefit of augmenting the container with FTP capabilities, but I quickly lost interest in this option, mainly out of pure laziness. The next option was to have the WordPress directory modified so that it would be owned by the www-data user inside the container. I think it is possible to do this, so I started looking at the podman docs.

If you try to simply chown the directory, you will probably loose the ability to edit your WordPress files, because changing the owner of the files inside the container, also changes them outside of it, so your host’s user will no longer own the files. I believe you can user user id mappings to get around this. For example, you could probably map your host’s user to www-data inside the container, and then changing the WordPress directory ownership would not have any effect outside of the container. However, before I started looking into that, I got an easier idea: I can simply change the ownership of the WordPress directory, run the update from the dashboard, and then revert the ownership of the directory back to root inside the container. Doing that I was able to update a plugin successfully.

This is a lazy solution, but one that works. While I would like to eventually better my container to add some other tools like phpMyAdmin, and some type of mail catcher, for now I’m OK with this hacky way to update anything WordPress related.

NOTE: this is a hacky solution to a local development issue. I do not recommend this be done in any environment other than local environments where the risk of temporarily changing file ownership is mitigated by the fact that your environment is not accessible by any potentially malicious party.

Laravel’s artisan Won’t Start

Disclaimer: This is as edge as a case can be. I’m writing about it mainly for my future self, but also so you can have a laugh…

I’m working on a small update for a client that requires testing with limited memory available for php. This is because the update is about fixing a memory exhaustion issue in a cron job. The project uses Laravel.

In order to limit the memory, the quickest, and easiest way, is to use ini_set('memory_limit', '100M'). In my case, I want to limit the memory for an artisan command, which is where the memory exhaustion issue is happening, so I added that line to the main artisan file. Instead of 100M, I set it to 30M because I really want to run on very low memory, and 30M is the lowest I can go while ensuring the process has enough memory to start, but not to finish. 30 MB of memory is not a realistic amount of memory for any server, but my local test data is not as big as the data in the upper environments, and I want the job to fail fast, so I need to lower the memory limit to such a low value to ensure the process dies as soon as possible, but not earlier. Otherwise, I would have to sit for over an hour every time I want to test any changes I make.

Anyhow. I tried to run Laravel tinker, and it would not start. I spent some time scratching my head, and looking for a reason, only to later realize that I had not removed the ini_set call in the main artisan file. What a FACEPALM! I consider this to be a variation of when you forget to save the file, and then wonder why your changes are not being applied, which, by the way, has happened to me more times than I’m comfortable admitting.

HTMX, How did it Go?

Update (March 21st 2024): I continued working on my Htmx, and a couple of days after this entry I made a small todo app demo. I plan on making a course where I teach how to develop a better version of that demo. I will publish the demo app on github, just beware it is shamefully minimal.

Last week I wrote a small entry about htmx, and I said I planned on mastering it over the weekend. Well, that did not happen. Htmx has a really good, and extensive documentation, and I want to finish reading it all before I start doing some demos in it.

I did, however, get a pretty good idea of what htmx can do, and how it does it. The one line description is that htmx is for ajax. With it you can send requests and receive responses in a declarative way using plain old html attributes. I’ve always thought that if could make a living writing only html, I would. Html is simple, yet powerful, and htmx adds a lot more power to it, while keeping its simplicity. The transitions and animation support is great, and, in my opinion, the way it does it is pretty clever.

I’m already exited to start working on my demos, and I plan on making a WordPress theme using htmx for all ajax-based interactions. I may even make that into a course!

Have you used htmx before? Care to share what you’ve done with it?

What is HTMX Anyway?

I don’t remember when I first learned about htmx, but at the moment I thought it was nice, and then forgot about it. That wasn’t even long ago. I believe I had fallen into too much of a comfort state that I lost that curiosity to look into newer tools. Don’t get me wrong, I’m always learning something new, but most of the time nowadays I’m focused on learning things about the tools and frameworks I used on a daily basis, than on learning things that I may not use for a long time, if ever. That’s changed now though. An unfortunate event has forced me to get out of that comfort zone.

Today I received the Javascript Weekly newsletter, and I’ve made it recently a habit to pick at least one article and read it. This time I picked the Is html Just Another JavaScript Framework? article, and it reminded me of how cool I initially thought htmx was. And now, I’m going to start learning it. Could I master that in a weekend? Who knows! But wait for an update on Monday.

Can you spot the difference?

Can you spot the difference between these two commands?

cat file.txt | less
cat file.txt | less

They seem to be the same, yet the second one would result in “zsh: command not found:  less” when run on zsh on a Mac, and “-bash:  less: command not found” when run on bash in linux. But why?

This happened to me a few days ago, and I spent some time looking into it. After a good 5 minutes trying to spot the difference between the two lines, I decided to do an ASCII to binary conversion to see what was causing the error.

Immediately, I saw that the binary output was different, but why? I thought it may the pipe (|), but it wasn’t. The problem is the space after the pipe. On the first command, the space is a regular space character. When converted to utf-8 binary it is 00100000, where as the space on the second line is actually a NO-BREAK SPACE character whose binary representation is 11000010 10100000.

In Mac, you end up with a no-break space character by pressing the Options key, and the space key at the same time. On my keyboard, a Logitech, the options key is represented by the left “Alt” key.

Oddly enough, in the ~/.zsh_history file the command is stored as cat Inventory-02-23-2023.csv |ƒ?less; with a question mark (?) instead of the no-break space character. Or that may just be cat not knowing how to represent the no-break space, or some other weird scenario, because vim displays the line in this way cat Inventory-02-23-2023.csv |Â<83><80>less. I’m never really fully aware of how character encoding works.

Utf-8 table: https://utf8-chartable.de/unicode-utf8-table.pl?number=512&utf8=bin

Printing on a Bluetooth Thermal Printer from Chrome

I own one of those cat Bluetooth thermal printers. Short after I bought it, I started wondering if I could print to it from my computer, and I did a quick research a couple of times, but never really put much effort on it.

More recently, I started thinking about working on a custom web-based POS app for a small restaurant (Yes, I know there is a ton of ready-made apps already). Most of what that type of applications require is very simple, but the thermal printer interaction would be something that would need to be solved. I started doing some research again, and this time I took it a bit more seriously.

As it turns out, it is actually quite simple to “talk” to thermal printers. Searching for “thermal printer protocol” I learned that they usually use a simple set of instructions know as ESC/POS, and for basic printing, like the one required by a POS app, the basic commands are enough. There is even a plugin that allows you to print to thermal printer from anything that can do HTTP, and a PHP library that implements a subset of the ESC/POS protocol. However, when I tried to print using my printer, things weren’t quite as simple.

The BLE Complication

The solutions I had encounter so far did not seem to be aimed to BLE (Bluetooth Low Energy) printers. At this point I had to first figure out how to communicate with the printer, in order to send it commands.

The first issue I had was that my computer (a Mac) did not show the printer in the Bluetooth devices list. While searching for how to connect a thermal printer on a mac, I found an interesting question in stack overflow about connecting to thermal printers via the Web Bluetooth API. One of the answers there mentions the about:bluetooth-internals page that chrome has available for inspecting and connecting to nearby Bluetooth devices. Chrome did show the printer, so I connected to it, and started playing a little bit, but I couldn’t really make much sense of what I was seeing there.

BLE specs.

Disclaimer: I am not an expert in any way on this topic. Please do your own research.

BLE implements GATT, which uses the concepts of Services, and Characteristics. Services, and characteristics are identified by UUIDs. Services group sets of characteristics together, and characteristics are used to communicate (read/write) with the device. In google chrome you can inspect a devices’ services, and characteristics, as well as read from and write to characteristics.

I played a little with chrome, trying to send different ESC commands to the printer, but nothing seemed to work. Eventually, I started trying with bluetoothctl in linux, but I kept getting errors back saying the first character was not valid. At the time I was sending the ESC character as the first command, since that is what I had learned on the various resources I had read.

Learning from an existing implementation

I found that someone had already written a C++ library for arduino that works with cat printers. On that library I found the UUID of the characteristic I should send commands to, but I didn’t take the time to fully understand the code, or to even read it all. I only searched for the parts I thought I needed, and kept trying to send commands to the printer. Eventually, I called it a day, and went to bed.

The next day I went straight back to trying to get it to work. This time, however, I read the arduino library code more carefully, and learned the key piece I was missing: cat printers use their own protocol.

At this point I knew the two key parts that would eventually allow me to get the printer to work: 1) The BLE characteristic to which I should be writing commands, and 2) that cat printers use their own protocol. I was, however, missing a key ingredient: understanding of the C++ code. I spent another day trying, and failing. Most of the time was spent trying to modify the C++ code so that it would print the characters that I should send to the printer, but my lack of knowledge was making it hard for me to successfully modify the code. At this point I decided to stop, and get my feet wet in the C++ grammar.

C++: learning the basics

I spent a couple of days learning C++ in my spare time, but with the weekend gone, that spare time was limited. I watched an intro tutorial on youTube, and read bits and pieces of the C++ reference. This allowed me to eventually modify the code successfully to get the characters printed on the screen for me to copy & paste on google Chrome.

The code

I finally ended up with the following code, which is a shameless copy of a small subset of the arduino library modified to print the characters that should be sent to the printer. It has to be manually modified to set the string you want to print, as well as that string’s length, but since I just wanted a proof of concept, that was sufficient at the time. Just to state how incomplete this is, I never even compiled the code, I just ran it using https://www.mycompiler.io/.

#include <iostream>

const uint8_t cChecksumTable[] = {
	0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15,  0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, 
	0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65,  0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 
	0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5,  0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, 
	0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85,  0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, 
	0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2,  0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, 
	0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2,  0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a, 
	0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32,  0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, 
	0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42,  0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, 
	0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c,  0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, 
	0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec,  0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, 
	0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c,  0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, 
	0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c,  0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34, 
	0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b,  0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63, 
	0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b,  0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 
	0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb,  0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, 
	0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb,  0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3};

const unsigned char ucMirror[256]=
     {0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
      0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
      0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
      0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
      0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
      0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
      0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
      0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
      0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
      0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
      0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
      0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
      0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
      0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
      0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
      0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF};

unsigned char ucFont[] = {
	// ANSI 437 character set
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x18,0x3c,0x3c,0x18,0x18,0x00,0x18,0x00,	// " " "!" (#32, 33)
	0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,0x00, 0x6c,0x6c,0xfe,0x6c,0xfe,0x6c,0x6c,0x00,	// " # (34, 35)
	0x18,0x7e,0xc0,0x7c,0x06,0xfc,0x18,0x00, 0x00,0xc6,0xcc,0x18,0x30,0x66,0xc6,0x00,	// $ % (36, 37)
	0x38,0x6c,0x38,0x76,0xdc,0xcc,0x76,0x00, 0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,	// & ' (38, 39)
	0x18,0x30,0x60,0x60,0x60,0x30,0x18,0x00, 0x60,0x30,0x18,0x18,0x18,0x30,0x60,0x00,	// ( ) (40, 41)
	0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00, 0x00,0x18,0x18,0x7e,0x18,0x18,0x00,0x00,	// * + (42, 43)
	0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30, 0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,	// , - (44, 45)
	0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00, 0x06,0x0c,0x18,0x30,0x60,0xc0,0x80,0x00,	// . / (46, 47)
	0x7c,0xce,0xde,0xf6,0xe6,0xc6,0x7c,0x00, 0x30,0x70,0x30,0x30,0x30,0x30,0xfc,0x00,	// 0 1 (48, 49)
	0x78,0xcc,0x0c,0x38,0x60,0xcc,0xfc,0x00, 0x78,0xcc,0x0c,0x38,0x0c,0xcc,0x78,0x00,	// 2 3 (50, 51)
	0x1c,0x3c,0x6c,0xcc,0xfe,0x0c,0x1e,0x00, 0xfc,0xc0,0xf8,0x0c,0x0c,0xcc,0x78,0x00,	// 4 5 (52, 53)
	0x38,0x60,0xc0,0xf8,0xcc,0xcc,0x78,0x00, 0xfc,0xcc,0x0c,0x18,0x30,0x30,0x30,0x00,	// 6 7 (54, 55)
	0x78,0xcc,0xcc,0x78,0xcc,0xcc,0x78,0x00, 0x78,0xcc,0xcc,0x7c,0x0c,0x18,0x70,0x00,	// 8 9 (56, 57)
	0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00, 0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x30,	// : ; (58, 59)
	0x18,0x30,0x60,0xc0,0x60,0x30,0x18,0x00, 0x00,0x00,0x7e,0x00,0x7e,0x00,0x00,0x00,	// < = (60, 61)
	0x60,0x30,0x18,0x0c,0x18,0x30,0x60,0x00, 0x3c,0x66,0x0c,0x18,0x18,0x00,0x18,0x00,	// > ? (62, 63)
	0x7c,0xc6,0xde,0xde,0xdc,0xc0,0x7c,0x00, 0x30,0x78,0xcc,0xcc,0xfc,0xcc,0xcc,0x00,	// @ A (64, 65)
	0xfc,0x66,0x66,0x7c,0x66,0x66,0xfc,0x00, 0x3c,0x66,0xc0,0xc0,0xc0,0x66,0x3c,0x00,	// B C (66, 67)
	0xf8,0x6c,0x66,0x66,0x66,0x6c,0xf8,0x00, 0xfe,0x62,0x68,0x78,0x68,0x62,0xfe,0x00,	// D E (68, 69)
	0xfe,0x62,0x68,0x78,0x68,0x60,0xf0,0x00, 0x3c,0x66,0xc0,0xc0,0xce,0x66,0x3a,0x00,	// F G (70, 71)
	0xcc,0xcc,0xcc,0xfc,0xcc,0xcc,0xcc,0x00, 0x78,0x30,0x30,0x30,0x30,0x30,0x78,0x00,	// H I (72, 73)
	0x1e,0x0c,0x0c,0x0c,0xcc,0xcc,0x78,0x00, 0xe6,0x66,0x6c,0x78,0x6c,0x66,0xe6,0x00,	// J K (74, 75)
	0xf0,0x60,0x60,0x60,0x62,0x66,0xfe,0x00, 0xc6,0xee,0xfe,0xfe,0xd6,0xc6,0xc6,0x00,	// L M (76, 77)
	0xc6,0xe6,0xf6,0xde,0xce,0xc6,0xc6,0x00, 0x38,0x6c,0xc6,0xc6,0xc6,0x6c,0x38,0x00,	// N O (78, 79)
	0xfc,0x66,0x66,0x7c,0x60,0x60,0xf0,0x00, 0x7c,0xc6,0xc6,0xc6,0xd6,0x7c,0x0e,0x00,	// P Q (80, 81)
	0xfc,0x66,0x66,0x7c,0x6c,0x66,0xe6,0x00, 0x7c,0xc6,0xe0,0x78,0x0e,0xc6,0x7c,0x00,	// R S (82, 83)
	0xfc,0xb4,0x30,0x30,0x30,0x30,0x78,0x00, 0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xfc,0x00,	// T U (84, 85)
	0xcc,0xcc,0xcc,0xcc,0xcc,0x78,0x30,0x00, 0xc6,0xc6,0xc6,0xc6,0xd6,0xfe,0x6c,0x00,	// V W (86, 87)
	0xc6,0xc6,0x6c,0x38,0x6c,0xc6,0xc6,0x00, 0xcc,0xcc,0xcc,0x78,0x30,0x30,0x78,0x00,	// X Y (88, 89)
	0xfe,0xc6,0x8c,0x18,0x32,0x66,0xfe,0x00, 0x78,0x60,0x60,0x60,0x60,0x60,0x78,0x00,	// Z [ (90, 91)
	0xc0,0x60,0x30,0x18,0x0c,0x06,0x02,0x00, 0x78,0x18,0x18,0x18,0x18,0x18,0x78,0x00,	// \ ] (92, 93)
	0x10,0x38,0x6c,0xc6,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,	// ^ _ (94, 95)
	0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x78,0x0c,0x7c,0xcc,0x76,0x00,	// ` a (96, 97)
	0xe0,0x60,0x60,0x7c,0x66,0x66,0xdc,0x00, 0x00,0x00,0x78,0xcc,0xc0,0xcc,0x78,0x00,	// b c (98, 99)
	0x1c,0x0c,0x0c,0x7c,0xcc,0xcc,0x76,0x00, 0x00,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00,	// d e (100, 101)
	0x38,0x6c,0x64,0xf0,0x60,0x60,0xf0,0x00, 0x00,0x00,0x76,0xcc,0xcc,0x7c,0x0c,0xf8,	// f g (102, 103)
	0xe0,0x60,0x6c,0x76,0x66,0x66,0xe6,0x00, 0x30,0x00,0x70,0x30,0x30,0x30,0x78,0x00,	// h i (104, 105)
	0x0c,0x00,0x1c,0x0c,0x0c,0xcc,0xcc,0x78, 0xe0,0x60,0x66,0x6c,0x78,0x6c,0xe6,0x00,	// j k (106, 107)
	0x70,0x30,0x30,0x30,0x30,0x30,0x78,0x00, 0x00,0x00,0xcc,0xfe,0xfe,0xd6,0xd6,0x00,	// l m (108, 109)
	0x00,0x00,0xb8,0xcc,0xcc,0xcc,0xcc,0x00, 0x00,0x00,0x78,0xcc,0xcc,0xcc,0x78,0x00,	// n o (110, 111)
	0x00,0x00,0xdc,0x66,0x66,0x7c,0x60,0xf0, 0x00,0x00,0x76,0xcc,0xcc,0x7c,0x0c,0x1e,	// p q (112, 113)
	0x00,0x00,0xdc,0x76,0x62,0x60,0xf0,0x00, 0x00,0x00,0x7c,0xc0,0x70,0x1c,0xf8,0x00,	// r s (114, 115)
	0x10,0x30,0xfc,0x30,0x30,0x34,0x18,0x00, 0x00,0x00,0xcc,0xcc,0xcc,0xcc,0x76,0x00,	// t u (116, 117)
	0x00,0x00,0xcc,0xcc,0xcc,0x78,0x30,0x00, 0x00,0x00,0xc6,0xc6,0xd6,0xfe,0x6c,0x00,	// v w (118, 119)
	0x00,0x00,0xc6,0x6c,0x38,0x6c,0xc6,0x00, 0x00,0x00,0xcc,0xcc,0xcc,0x7c,0x0c,0xf8,	// x y (120, 121)
	0x00,0x00,0xfc,0x98,0x30,0x64,0xfc,0x00, 0x1c,0x30,0x30,0xe0,0x30,0x30,0x1c,0x00,	// z { (122, 123)
	0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00, 0xe0,0x30,0x30,0x1c,0x30,0x30,0xe0,0x00,	// | } (124, 125)
	0x76,0xdc,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x10,0x38,0x6c,0xc6,0xc6,0xfe,0x00,	// ~ ⌂ (126, 127) del is printed as house
	0x7c,0xc6,0xc0,0xc6,0x7c,0x0c,0x06,0x7c, 0x00,0xcc,0x00,0xcc,0xcc,0xcc,0x76,0x00,	// Ç ü (128, 129) Ç ü	left is ANSI 437 character set
	0x1c,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00, 0x7e,0x81,0x3c,0x06,0x3e,0x66,0x3b,0x00,	// é â (130, 131) é â	right is change to CP852 (Latin-2)
	0xcc,0x00,0x78,0x0c,0x7c,0xcc,0x76,0x00, 0xe0,0x00,0x78,0x0c,0x7c,0xcc,0x76,0x00,	// ä à (132, 133) ä ů
	0x30,0x30,0x78,0x0c,0x7c,0xcc,0x76,0x00, 0x00,0x00,0x7c,0xc6,0xc0,0x78,0x0c,0x38,	// å ç (134, 135) ć ç
	0x7e,0x81,0x3c,0x66,0x7e,0x60,0x3c,0x00, 0xcc,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00,	// ê ë (136, 137) ł ë
	0xe0,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00, 0xcc,0x00,0x70,0x30,0x30,0x30,0x78,0x00,	// è ï (138, 139) Ő ő
	0x7c,0x82,0x38,0x18,0x18,0x18,0x3c,0x00, 0xe0,0x00,0x70,0x30,0x30,0x30,0x78,0x00,	// î ì (140, 141) î Ź
	0xc6,0x10,0x7c,0xc6,0xfe,0xc6,0xc6,0x00, 0x30,0x30,0x00,0x78,0xcc,0xfc,0xcc,0x00,	// Ä Å (142, 143) Ä Ć
	0x1c,0x00,0xfc,0x60,0x78,0x60,0xfc,0x00, 0x00,0x00,0x7f,0x0c,0x7f,0xcc,0x7f,0x00,	// É æ (144, 145) É Ĺ
	0x3e,0x6c,0xcc,0xfe,0xcc,0xcc,0xce,0x00, 0x78,0x84,0x00,0x78,0xcc,0xcc,0x78,0x00,	// Æ ô (146, 147) Í ô
	0x00,0xcc,0x00,0x78,0xcc,0xcc,0x78,0x00, 0x00,0xe0,0x00,0x78,0xcc,0xcc,0x78,0x00,	// ö ò (148, 149) ö Ľ
	0x78,0x84,0x00,0xcc,0xcc,0xcc,0x76,0x00, 0x00,0xe0,0x00,0xcc,0xcc,0xcc,0x76,0x00,	// û ù (150, 151) ľ Ś
	0x00,0xcc,0x00,0xcc,0xcc,0x7c,0x0c,0xf8, 0xc3,0x18,0x3c,0x66,0x66,0x3c,0x18,0x00,	// ÿ Ö (152, 153) ś Ö
	0xcc,0x00,0xcc,0xcc,0xcc,0xcc,0x78,0x00, 0x18,0x18,0x7e,0xc0,0xc0,0x7e,0x18,0x18,	// Ü ¢ (154, 155) Ü Ť
	0x38,0x6c,0x64,0xf0,0x60,0xe6,0xfc,0x00, 0xcc,0xcc,0x78,0x30,0xfc,0x30,0xfc,0x30,	// £ ¥ (156, 157) ť Ł
	0xf8,0xcc,0xcc,0xfa,0xc6,0xcf,0xc6,0xc3, 0x0e,0x1b,0x18,0x3c,0x18,0x18,0xd8,0x70,	// ₧ ƒ (158, 159) × č
	0x1c,0x00,0x78,0x0c,0x7c,0xcc,0x76,0x00, 0x38,0x00,0x70,0x30,0x30,0x30,0x78,0x00,	// á í (160, 161) á í
	0x00,0x1c,0x00,0x78,0xcc,0xcc,0x78,0x00, 0x00,0x1c,0x00,0xcc,0xcc,0xcc,0x76,0x00,	// ó ú (162, 163) ó ú
	0x00,0xf8,0x00,0xb8,0xcc,0xcc,0xcc,0x00, 0xfc,0x00,0xcc,0xec,0xfc,0xdc,0xcc,0x00,	// ñ Ñ (164, 165) Ą ą
	0x3c,0x6c,0x6c,0x3e,0x00,0x7e,0x00,0x00, 0x38,0x6c,0x6c,0x38,0x00,0x7c,0x00,0x00,	// ạ ọ (166, 167) Ž ž
	0x18,0x00,0x18,0x18,0x30,0x66,0x3c,0x00, 0x00,0x00,0x00,0xfc,0xc0,0xc0,0x00,0x00,	// ¿ ⌐ (168, 169) Ę ę
	0x00,0x00,0x00,0xfc,0x0c,0x0c,0x00,0x00, 0xc6,0xcc,0xd8,0x36,0x6b,0xc2,0x84,0x0f,	// ¬ ½ (170, 171) ¬ ź
	0xc3,0xc6,0xcc,0xdb,0x37,0x6d,0xcf,0x03, 0x18,0x00,0x18,0x18,0x3c,0x3c,0x18,0x00,	// ¼ ¡ (172, 173) Č ş
	0x00,0x33,0x66,0xcc,0x66,0x33,0x00,0x00, 0x00,0xcc,0x66,0x33,0x66,0xcc,0x00,0x00,	// « » (174, 175) 
	0x22,0x88,0x22,0x88,0x22,0x88,0x22,0x88, 0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,	// ░ ▒ (176, 177)
	0xdb,0xf6,0xdb,0x6f,0xdb,0x7e,0xd7,0xed, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,	// ▓ │ (178, 179)
	0x18,0x18,0x18,0x18,0xf8,0x18,0x18,0x18, 0x18,0x18,0xf8,0x18,0xf8,0x18,0x18,0x18,	// ┤ ╡ (180, 181) ┤ Á
	0x36,0x36,0x36,0x36,0xf6,0x36,0x36,0x36, 0x00,0x00,0x00,0x00,0xfe,0x36,0x36,0x36,	// ╢ ╖ (182, 183) Â Ě
	0x00,0x00,0xf8,0x18,0xf8,0x18,0x18,0x18, 0x36,0x36,0xf6,0x06,0xf6,0x36,0x36,0x36,	// ╕ ╣ (184, 185) Ş ╣
	0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x00,0x00,0xfe,0x06,0xf6,0x36,0x36,0x36,	// ║ ╗ (186, 187) ║ ╗
	0x36,0x36,0xf6,0x06,0xfe,0x00,0x00,0x00, 0x36,0x36,0x36,0x36,0xfe,0x00,0x00,0x00,	// ╝ ╜ (188, 189) ╝ Ż
	0x18,0x18,0xf8,0x18,0xf8,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xf8,0x18,0x18,0x18,	// ╛ ┐ (190, 191) ż ┐
	0x18,0x18,0x18,0x18,0x1f,0x00,0x00,0x00, 0x18,0x18,0x18,0x18,0xff,0x00,0x00,0x00,	// └ ┴ (192, 193) └ ┴
	0x00,0x00,0x00,0x00,0xff,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x1f,0x18,0x18,0x18,	// ┬ ├ (194, 195) ┬ ├
	0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00, 0x18,0x18,0x18,0x18,0xff,0x18,0x18,0x18,	// ─ ┼ (196, 197) ─ ┼
	0x18,0x18,0x1f,0x18,0x1f,0x18,0x18,0x18, 0x36,0x36,0x36,0x36,0x37,0x36,0x36,0x36,	// ╞ ╟ (198, 199) Ă ă
	0x36,0x36,0x37,0x30,0x3f,0x00,0x00,0x00, 0x00,0x00,0x3f,0x30,0x37,0x36,0x36,0x36,	// ╚ ╔ (200, 201) ╚ ╔
	0x36,0x36,0xf7,0x00,0xff,0x00,0x00,0x00, 0x00,0x00,0xff,0x00,0xf7,0x36,0x36,0x36,	// ╩ ╦ (202, 203) ╩ ╦
	0x36,0x36,0x37,0x30,0x37,0x36,0x36,0x36, 0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,	// ╠ ═ (204, 205) ╠ ═
	0x36,0x36,0xf7,0x00,0xf7,0x36,0x36,0x36, 0x18,0x18,0xff,0x00,0xff,0x00,0x00,0x00,	// ╬ ╧ (206, 207) ╬ ¤
	0x36,0x36,0x36,0x36,0xff,0x00,0x00,0x00, 0x00,0x00,0xff,0x00,0xff,0x18,0x18,0x18,	// ╨ ╤ (208, 209) đ Đ
	0x00,0x00,0x00,0x00,0xff,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x3f,0x00,0x00,0x00,	// ╥ ╙ (210, 211) Ď Ë
	0x18,0x18,0x1f,0x18,0x1f,0x00,0x00,0x00, 0x00,0x00,0x1f,0x18,0x1f,0x18,0x18,0x18,	// ╘ ╒ (212, 213) ď Ň
	0x00,0x00,0x00,0x00,0x3f,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0xff,0x36,0x36,0x36,	// ╓ ╫ (214, 215) Í î
	0x18,0x18,0xff,0x18,0xff,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0xf8,0x00,0x00,0x00,	// ╪ ┘ (216, 217) ě ┘
	0x00,0x00,0x00,0x00,0x1f,0x18,0x18,0x18, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,	// ┌ █ (218, 219) ┌ █
	0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,	// ▄ ▌ (220, 221) ▄ Ţ
	0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,	// ▐ ▀ (222, 223) Ů ▀
	0x00,0x00,0x76,0xdc,0xc8,0xdc,0x76,0x00, 0x00,0x78,0xcc,0xf8,0xcc,0xf8,0xc0,0xc0,	// α β (224, 225) Ó β
	0x00,0xfc,0xcc,0xc0,0xc0,0xc0,0xc0,0x00, 0x00,0x00,0xfe,0x6c,0x6c,0x6c,0x6c,0x00,	// Γ π (226, 227) Ô Ń
	0xfc,0xcc,0x60,0x30,0x60,0xcc,0xfc,0x00, 0x00,0x00,0x7e,0xd8,0xd8,0xd8,0x70,0x00,	// Σ σ (228, 229) ń ň
	0x00,0x66,0x66,0x66,0x66,0x7c,0x60,0xc0, 0x00,0x76,0xdc,0x18,0x18,0x18,0x18,0x00,	// μ Τ (230, 231) Š š
	0xfc,0x30,0x78,0xcc,0xcc,0x78,0x30,0xfc, 0x38,0x6c,0xc6,0xfe,0xc6,0x6c,0x38,0x00,	// ϕ Ө (232, 233) Ŕ Ú
	0x38,0x6c,0xc6,0xc6,0x6c,0x6c,0xee,0x00, 0x1c,0x30,0x18,0x7c,0xcc,0xcc,0x78,0x00,	// Ω δ (234, 235) ŕ Ű
	0x00,0x00,0x7e,0xdb,0xdb,0x7e,0x00,0x00, 0x06,0x0c,0x7e,0xdb,0xdb,0x7e,0x60,0xc0,	// ∞ ø (236, 237) ý Ý
	0x38,0x60,0xc0,0xf8,0xc0,0x60,0x38,0x00, 0x78,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x00,	//   ∩ (238, 239) ţ ´
	0x00,0x7e,0x00,0x7e,0x00,0x7e,0x00,0x00, 0x18,0x18,0x7e,0x18,0x18,0x00,0x7e,0x00,	// ≡ ± (240, 241) ­ ̋
	0x60,0x30,0x18,0x30,0x60,0x00,0xfc,0x00, 0x18,0x30,0x60,0x30,0x18,0x00,0xfc,0x00,	// ≥ ≤ (242, 243) ̨ ˇ
	0x0e,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0xd8,0xd8,0x70,	// ⌠ ⌡ (244, 245) ̆ §
	0x18,0x18,0x00,0x7e,0x00,0x18,0x18,0x00, 0x00,0x76,0xdc,0x00,0x76,0xdc,0x00,0x00,	// ÷ ꞊ (246, 247) ÷ ̧
	0x38,0x6c,0x6c,0x38,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,	// ○ ● (248, 249) ° ¨
	0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00, 0x0f,0x0c,0x0c,0x0c,0xec,0x6c,0x3c,0x1c,	// ▪ √ (250, 251) ̇ ű
	0x58,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00, 0x70,0x98,0x30,0x60,0xf8,0x00,0x00,0x00,	// ⁿ ² (252, 253) Ř ř
	0x00,0x00,0x3c,0x3c,0x3c,0x3c,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};	// ■   (254, 255) ■
	
static uint8_t CheckSum(uint8_t *pData, int iLen)
{
int i;
uint8_t cs = 0;

    for (i=0; i<iLen; i++)
        cs = cChecksumTable[(cs ^ pData[i])];
    return cs;
}

void tpPrintCatTextLine()
{
    // Send drawing mode command to cat printer.
    // 0X5178BE0001000107FF

    int CatStrLen = 20;
    char CatStr[] = "";
    uint8_t ucTemp[56];
    for (int j=0; j<8; j++) // pixel row of text
    {
      ucTemp[0] = 0x51;
      ucTemp[1] = 0x78;
      ucTemp[2] = 0xA2; // data, uncompressed
      ucTemp[3] = 0;
      ucTemp[4] = CatStrLen; // data length
      ucTemp[5] = 0;
      for (int i=0; i<CatStrLen; i++)
        ucTemp[6+i] = ucMirror[ucFont[((CatStr[i]-32)*8)+j]];
      ucTemp[6 + CatStrLen] = CheckSum(&ucTemp[6], CatStrLen);
      ucTemp[6 + CatStrLen + 1] = 0xFF;
      //tpWriteData(ucTemp, 8 + CatStrLen);
      
      for (int k = 0; k < CatStrLen + 8; k++) {
        printf("%02x", ucTemp[k]);
      }
    }
    
    
    
}

int main() {
    tpPrintCatTextLine();
    return 0;
}

What’s next

I want to port the code to Javascript, either in node, or in the browser, just to have a complete implementation of a piece of code that can print to a cat printer from the browser, or the console. Eventually, this will all be just a small personal exercise, because for now, the ultimate goal is to have a thermal printer permanently connected to my in-house server so that it can print important messages, and I don’t think a cat printer is the right choice for that. I will buy a USB printer that talks ESC/POS to do that. Ultimately, that will become the final proof of concept for the implementation of the printing component for that POS app I want to build.

Other source:
https://parzibyte.me/blog/en/2019/10/10/print-receipt-thermal-printer-javascript-css-html/
https://www.visuality.pl/posts/thermal-printer-protocols-for-image-and-text
https://mike42.me/blog/what-is-escpos-and-how-do-i-use-it
https://developer.chrome.com/articles/bluetooth/#tips
https://en.wikipedia.org/wiki/Bluetooth_Low_Energy
https://stackoverflow.com/questions/30662024/how-to-send-esc-pos-commands-to-thermal-printer-in-linux
https://mike42.me/blog/2015-03-getting-a-usb-receipt-printer-working-on-linux

chmod, ls, and sticky bits

Last week I was trying to set up a remote server so that more than one user could ssh into the server using the same directory as their home directory. While it is possible to use one directory as the home directory for more than one user, the ssh restrictions on file ownership and permission for the .ssh directory and it’s files makes it hard to use that set up for users who log in with an ssh key. This is because the users share the same ~/.ssh directory, and ssh requires that the directory be owned by the user logging in, and only that user should have write permissions.

I’m pretty sure you can actually achieve this set up with some clever tricks, but I’m not sure how safe that is, or if it is even recommended. I will have to research that when I have some time.

However, while trying to set up ssh I discovered a couple of things:

Using chmod to set setuid, setgid, and sticky bits.

You can set the setuid, setgid, and sticky bits to control what happens when files are executed, created, or deleted:

setuid: When set on an executable causes the executable to run with the privileges of the owner of the file, even if the user who ran it has fewer privileges. For example, if a root-owned executable is ran by user, the executable will run as if root had ran it. It will effectively gain access to anything that is accessible by root.

When set on a regular file, this does not have an effect, since the file cannot be executed.

When set on a directory, this will be ignored by most systems.

setgid: When set on an executable causes the executable to run with the privileges of the group.

When set on a regular file, this does not have an effect, since the file cannot be executed.

When set on a directory, it causes any file or sub-directory created in that directory to be owned by the group of that directory rather than the primary group of the user who created it. This makes it possible for users that share secondary groups to edit each other’s files within that directory.

sticky: When set on a directory causes the owner of a file in that directory to be the only one able to delete such file, even if other users have write permissions on it. Because of this, the sticky bit is also know as the Restricted Deletion Flag bit.

The behavior of the sticky bit when set on regular files is not well defined.

Using chmod you can set this bits using either symbolic, or octal (numeric) mode. In symbolic mode s is used for the setuid, and the setgid bits, while t is used for the sticky bit.

Being able to set this bits allows you to have greater control over your files and executables that is possible with only permissions bits (rwx). For example, you can do part of what I was trying to achieve.

By setting the setgid bit in a directory, you can allow users who do not share primary groups to share write permission on a directory. You may think you can already do that by giving write permissions to “others” in the directory, but that will open the directory to be writeable by all users of the system. Instead, we want to give permissions only to users who belong in a group, but whose primary group is not that group.

For example, suppose we have set up a system with users (User:Group) John:John, and Jenny:Jenny. Because of User Private Groups, each user’s main group is a group named the same as the user. Both of this users are also part of a group named Accounting, and they need to be able to edit files in the /home/acme/accounting directory. But, user Alice should not be able to edit files in that directory. Fortunately, Alice is not part of the Accounting group.

In this scenario, we can set group ownership of the /home/acme/accounting directory to the Accounting group, and give write permissions to that group. Because John is part of the Accounting group, he can create the jun-2023 file. When this file is created, its ownership is set to John:John, which means that when Jenny tries to edit it she can’t. Se is not John, and she is not part of the John group. To fix this problem, we can set the setgid bit in the accounting directory. This way, when John creates jun-2023 the file ownership is set to John:Accounting. Now that the group ownership of the file is set to the Accounting group, Jenny has write access to it because she is part of the Accounting group. Alice, who is not part of the Accounting group, still has no write access to jun-2023.

It is worth noting that the setgid bit has to be set before the file is created in order for the file to inherit the directory’s group ownership.

If you wanted to make it so that Jenny cannot delete jun-2023. All you have to do is set the sticky bit on the accounting directory. This would prevent any user who is not the owner of a file from being able to delete that file.

You may be thinking, why not just make John, and Jenny share Accounting as their primary group. This would have the same effect, but what happens when all employees need write access to all files /home/acme/employee directory? At this point you are back with the same scenario: users who do not share primary group needing to edit files in the same directory.

ls

Once you have set these bits, you need to be able to recognize them when listing the contents of a directory. The ls command can help you with that.

When you pass the -l option to the ls command, it uses the long-list format, which displays information about the bits set on files, and directories. When I was trying to set the server to have more than one user share home directory when logging in via ssh, I set the setgid, and sticky bits on the directory that would be the home directory for these users. At some point I saw that ls was showing something like drwxrwsr-- for the shared directory. What is that “s” in the second set of permissions (....rws...)? More over, after realizing that I was not going to be able to do the set up I wanted, I tried to remove the setgid bit and ended up with drwxrwSr--. The “s” is now capitalized!

It turns out ls replaces the x by and s when the setuid or setgid bits are set, and their corresponding execute bit is also set. But if the execute bit is missing, the s is turned capital. In my case I tried to use numeric mode to remove the setgid bit, but that is not possible. I ended up removing the execute bit only, which is why I was seing drwxrwSr--.

I strongly recommend reading the man pages for ls and chmod, as well as the “What information is listed” section of the Core Utils manual to get a better understanding of the topics that I merely touched on here, as well as the other sources listed bellow.

Other sources:
https://en.wikipedia.org/wiki/Setuid
https://www.youtube.com/watch?v=zU43cReOBsc
https://linux.die.net/man/8/sshd (Read the Files section to gain insight on file ownership restrictions)

npm Log Errors.

After updating a project a few months ago, I noticed that trying to run tests on it would result on an error printed on screen, and tests would not run. Npm was complaining that it could not write to the ~/.npm/_logs directory. At the time I didn’t pay much attention to it, but last week I went back to working on that project implementing a new feature that requires new tests to be implemented. This time I had to look into the issue and resolve it if I wanted to be able to work without distractions.

The project is set up in a container using podman. It consists of a few different scripts that can be run individually to accomplish different things, most of them related to gathering data from different sources, processing it, and sending it to a services that stores this data. Originally, the project used to run in node 14 without any issues. I used to launch a new containers using podman run every time I wanted to test the feature I was working on (run a single script), but recently I started to launch a single container instance running bash on it, and just run my scripts there. Back when I first noticed the problem, I was still running a container every time I wanted to do anything in that project, and trying to run tests would just print the error, and exit without printing the test results.

Last week, when I went back to that project, I launched a single container running batch, and I noticed that while running the tests still printed the error, the test results would follow. I believe this is because since the main container’s job, which is bash, does not terminate, the container continues to run giving node a chance to run the tests, where as when the main job of the container is to run tests, the container exits as soon as the error is printed out. I worked for a bit like this, but I quickly realized that I needed to fix the issue if I wanted to fully concentrate on my work.

The problem was that npm was trying to write a file in the user’s home directory, but it could not. The initial question was why? The user should have full write access to its own directory, but it turns out the user had no home directory. This is because when I created the image for the container I created a system user to be used with the container. System users do not get a home directory by default. Fixing the issue was quite easy: add a home directory for the user. To do this, I just added -m to the useradd command in my Dockerfile for the image. I re-built the image, and now the error is gone.

At this point it wasn’t still clear to me why this error started popping up, until I remembered I had updated the project from node 14 to node 16. I started to search for people reporting similar issues, and I found a couple issue reports. Although, these were about read-only systems. My containers are not read-only, but since the user did not have a home directory where it could write freely, the effect ended up being the same.

I’ve identified npm v8.8.0 as the possible point where this behavior was introduced, but I’m not sure that is 100% right. A commenter in one of the issue reports said that npm had changed to open a stream to the log file as soon as it starts, rather than waiting until a log is actually going to be written. This seems to be that cause of the issue.

I don’t really expect many people to encounter this issue, and, as always, there are a few different ways to fix it. For example, you could disable logging, or change the location of the cache directory, which is by default ~/.npm, or just, like in my case, make sure npm has write access to where it tries to write log files.

vim too slow to start & close

A few days ago I noticed vim started to take too long to start for the first time on each boot, and to close every time it was used. This was in a laptop that I’m slowly setting up to be able to work on-the-go, so I didn’t do much about it other than a few searches in the net. All the results I got seemed to be regarding poorly set up vimrc files, but I did not have a vimrc file.

Today I started considering switching to another editor, but I quickly abandoned that idea. Instead I decided to make sure I really didn’t have a vimrc file, so I listed all files in my home dir. There I saw a .viminfo file, and decided to check what that was about. Opening it with vim took about 20 – 30 seconds. I decided to remove that file, and now vim works as expected.

The file was about 195MB, and it seemed like most of it was a fragment of an sql file for a db dump of one of my work projects. There was also a .viminfo.tmp file which I also removed. .viminfo is a file generated by vim to help vim remember what you were doing on past sessions.

Read more about .viminfo

Linux Powertop

For a long time I’ve suspected battery to last much less when switching a laptop from its original windows to Fedora (I noticed that in the past with Ubuntu too), so a few days ago I decided to research a little bit — It turns out my suspicions were true.

Fortunately, fixing this issue is really easy. I’m pretty sure there are ways to get even more time out of a battery charge if you are willing to dive deeper in this subject. However, for now I’m happy with the results I got from installing powertop and using its auto-tuner.

powertop is a small program for the linux command line that is similar to top in that it gives you a list of running processes, but unlike top, that is concerned with resources, powertop is only concerned with power usage. You can also manually tune different aspects of your system to get more out of your battery life, but if you don’t feel like diving too deep into this subject, powertop provides both, a simple command to auto-tune your system, and a service to get your system auto-tuned every time it starts.

To run the auto-tuner just do sudo powertop --auto-tune. Note that this command needs to be run with root privileges.

If you wish to enable the service, just do sudo systemctl enable powertop.service.

In the future I’ll probably end up diving deep into this subject to get even more out of my battery, but for now this is enough to satisfy both, my needs, and my curiosity. I’ll be sure to install powertop as part of my set up every time I do a fresh OS install.