Install Signal Desktop and setup the tray for i3-gaps [Linux Mint / Ubuntu]

This is post on installing + setting up Signal Desktop application on an i3 / i3-gaps setup and enabling signal’s tray icon on your status bar.


1. Linux Mint/Ubuntu [This was tested on Linux Mint 20.2 ]
2. Window Manager – i3 or i3-gaps [ I am using i3-gaps ]
3. i3-bar / py3status
4. Signal Desktop Linux [ Ref: ]

Section 1: Install Signal Desktop

1. Install signal’s offical public software signing key.

wget -O- | gpg --dearmor > signal-desktop-keyring.gpg
cat signal-desktop-keyring.gpg | sudo tee -a /usr/share/keyrings/signal-desktop-keyring.gpg > /dev/null

2. Add repository to your list of repositories

echo 'deb [arch=amd64 signed-by=/usr/share/keyrings/signal-desktop-keyring.gpg] xenial main' |\
  sudo tee -a /etc/apt/sources.list.d/signal-xenial.list

3. Update your package database and install signal

sudo apt update && sudo apt install signal-desktop

[Note: The above instructions are from as of Sep 2021. Please refer to their website for the latest install instructions.]

Section 2: Setting up Signal for i3-gaps

If you invoke signal directly via rofi or manually from your terminal using signal-desktop, it works great but it does not enable tray icon by default which could be problamatic if you need to to minimize to the system tray (or to run in the background) if you quit out of the app. Arg!!

Luckily, signal-desktop ships with an additional flag that allows you to use the tray icon which will allow the Minimize function to work properly. To run signal with the tray icon, run the following:

signal-desktop --use-tray-icon > /dev/null &

Here is a screenshot from my setup with the signal tray seen in the bar. (You could right click image and open in new window for better viewing)

Screenshot of signal-desktop application with the system tray enabled

Adding keybindings i3 config:

I am sure there is a better way to do this. For now, I created a script that calls signal-desktop with the additional argument.

vim ~/

signal-desktop --use-tray-icon > /dev/null &

Then make it executable:

chmod +x ~/

I have configured my keybinding to Mod key + S (Windows key and s) to start signal-desktop and Mod + Shift + s (Windows key + Shift + s) which will kill signal. You can modify the text in bold below as per your liking. Here is a snippet of my i3 config file for reference:

vim ~/.config/i3/config

set $mod Mod4
set $terminal terminator
set $signal_with_tray ~/
set $exec exec –no-startup-id

# Start Signal using Mod key + s
bindsym $mod+s $exec $signal_with_tray

# Kill Signal using Mod key + Shift + s
bindsym $mod+Shift+s $exec $terminal -e ‘pkill signal-desktop’



Hexadecimal basics using basic Linux utilities

This is short guide on how to convert strings and decimal values to hexadecimal and vice-versa using simple Linux utilities such as xxd and printf.

  1. To convert a string to hexadecimal, you can use xxd in Linux.

For example: To convert a string “kali” to hexadecimal, you can use the below format.

[email protected]:~$ echo -n kali | xxd
00000000: 6b61 6c69 kali

To group and display the output a single character at a time, use -c  flag with argument 1.

[email protected]:~$ echo -n kali | xxd -c1
00000000: 6b k
00000001: 61 a
00000002: 6c l
00000003: 69 i

This means the following:

00000000: 6b k <- k is a single character and its hexadecimal value is 6b
00000001: 61 a <- a is a single character and its hexadecimal value is 61
00000002: 6c l <- l is a single character and its hexadecimal value is 6c
00000003: 69 i <- i is a single character and its hexadecimal value is 69

2. Convert Hexadecimal value back to string:

[email protected]:~$ echo -n "6b61 6c69" | xxd -r -p

Dont worry about the spaces, it should still work.

[email protected]:~$ echo -n "6b616c69" | xxd -r -p
kali[email protected]:~$

3. Convert decimal (number) to hexadecimal

-Lets say you want to convert number 12 to decimal value. To do so, you could use printf as shown below.

[email protected]:~$ printf "%x\n" 12

From above output, the hexadecimal value for number 12 (aka decimal 12) is character c.

To find hexadecimal values for values from 1 to 255, you could run through it in a loop

for num in {1..255}; do printf "%x\n" $num ; done | less

Hope these help.

PS: Knowing hexadecimal does come in handy while learning/debugging different protocols.

How to fix “Error opening terminal: xterm-termite” when trying to SSH to a remote host

I am using “termite” on my ArchLinux machine but when I try to SSH to a remote linux machine(Ubuntu) to run some commands, it throws me an error “Error opening terminal: xterm-termite“. This a post on how to fix it.

Quick Fix:

If you are already SSH’ed into the remote machine and you see this issue, you could update the TERM variable on the remote system: (Run on remote system)

export TERM=xterm-256color

Permanent Fix:

The permanent fix is to edit your local ~/.bashrc file to include the following:

if [ "$TERM" = xterm ]; then
export TERM=xterm-256color

In my case, I had to add it to my Arch Linux’s ~/.bashrc file. (i.e not on the remote Ubuntu machine that I am connecting to)

Here is a screenshot from my box: (Right click image and open in new team to view in full screen)

arch linux termite i3-gaps

To verify its fixed, open a new terminal (or source ~/.bashrc again) and SSH into the remote machine. Ideally this should be fixed.

Hope this helps!

Note: If you terminal does not support 256-color, then you may need to change the TERM variable to “xterm-color” instead of “xterm-256color”.



GDB basics with C

This is a beginner level tutorial on learning basics of debugging using GDB by debugging an executable. This post will cover writing a very simple C code, compiling it and then opening the generated executable in GDB for inspecting the working/debugging. The primary objective here is to get familiar with the basics of using GDB.


  1. Any Linux x64 OS. (Linux Mint 20 used in this tutorial)
  2. C compiler – gcc
  3. gdb – for debugging / reverse engineering.
  4. GDB Dashboard (Optional – Makes GDB easier to read)

[Disclaimer: This is blog post adapted from recurse’s original gdb tutorial.(All Credits/References added in the Credits section)]

Setting up the Environment:

  • Install the following tools to setup the basic environment:

sudo apt install vim gcc gdb git python3 python3-pip -y

GDB Dashboard:

  • To make GDB easier to understand and make it non-alien, use GDB Dashboard which is a really good python plugin  for GDB.  Its epic!
  • Run the following to get the gdbinit dotfile.
wget -P ~

If you would like to check out the source, you can find it  @

For syntax highlighting, you would need the pygments module. If you are using Python2.x:

pip install pygments

If you are using Python3.x, then use pip3 to install it:

pip3 install pygments

To run gdb without providing any executable, run “gdb” and you should see the something like below:

[email protected]:gdb$ gdb
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
Find the GDB manual and other documentation resources online at:

For help, type "help".
Type "apropos word" to search for commands related to "word".

To exit out of gdb, type  “quit” .

-Below is a simple C program that initializes an integer value and then returns 0.

cat minimal.c
int main()
int i = 1337;
return 0;

-Compile the C code and make an executable, using the following flags:

gcc -g minimal.c -o minimal

-The directory should now have a executable with filename minimal.

[email protected]:gdb$ file minimal
minimal: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/, BuildID[sha1]=c918c072d119be6a9d18991e812a5414ecae67e1, for GNU/Linux 3.2.0, with debug_info, not stripped

Debugging the binary using GDB:

-To examine an executable in GDB, use the following format:

gdb <executable_file>

-Here, I would run the following to open the executable for debugging:

gdb minimal

gdb minimal output

Lets check the functions in this program, to do run “info functions”. You should be able to see that this executable has function main().

>>> info functions
All defined functions:

File minimal.c:
1: int main();

Non-debugging symbols:
0x0000000000001000 _init
0x0000000000001030 [email protected]
0x0000000000001040 _start
0x0000000000001070 deregister_tm_clones
0x00000000000010a0 register_tm_clones
0x00000000000010e0 __do_global_dtors_aux
0x0000000000001120 frame_dummy
0x0000000000001140 __libc_csu_init
0x00000000000011b0 __libc_csu_fini
0x00000000000011b8 _fini
Here is a screenshot:

gdb info functions output screenshot

The output also shows that the line number at which main() function is present in the source minimal.c.

From the info functions output, we can also see that function main() is present at line number 1 in source minimal.c.


-Lets say we need to examine what a executable is going at a given point of time or to inspect the value of a variable, then we can set one or more breakpoints and run the code upto that breakpoint. You can then examine the the value of a variable upto that breakpoint or directly examine the memory etc.

Breakpoints example: Setting breakpoints using function name

From the previous section,we know that the executable has function main(). Lets start examining the binary using gdb to see the value of “i” at different stages of the execution. To start with,  set a breakpoint at function main using the syntax break <function_name>:

>>> break main
Breakpoint 1 at 0x1129: file minimal.c, line 2.

To get the list of breakpoints that were set, you can use “info breakpoints”.

>>> info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000001129 in main at minimal.c:2

gdb info breakpoints

The above shows that there is one breakpoint which is set at function main() whose starting address in memory is “0x1129” or “0x0000000000001129“.

Note: You can have create multiple breakpoints and they are numbered.

So we have a breakpoint in place, we can run the binary upto the breakpoint that was set. To run the program, use “run” or “r“.

gdb screenshot

This is what it looks like now. (You could now see the GDB dashboard with an insane amount of matrix looking stuff. :P)

[Note:  Right click the image and click on “View Image” / “Open image in new tab” if the image for better visibility. ]

GDB Dashboard screenshot

For this tutorial, we are more interested in few sections of Dashboard as of now –   “Source” , “Threads”, “Variables” and “Assembly” sections.

-The “Source” section has a blip on the line number which shows where exactly in the code where gdb is looking into. So, here, it shows that its in line 2 of the source. After you ran the program, if you re-run “info breakpoints” you can see that the message “breakpoint already hit 1 time“.

>> info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000555555555129 in main at minimal.c:2
breakpoint already hit 1 time


[Note:  Right click the image and click on “View Image” / “Open image in new tab” if the image for better visibility. ]

As of now, the debugger is in line 2 where the variable “i” is not yet yet initiated in the program. So, if you try to print the value of “i”, then it should show a value = 0.

To print value of a variable, use the format “print var” or “p var“.

>> p i
$1 = 0


print i output screenshot

To go to the next line of code and execute it, use command “next” or “n“.  [Note: If the next line of code was a function, it would execute the full function). In this example, once we ran “r“, the “Source” section now highlights line number 3.

gdb print i screenshot 2

At this point, if you print variable “i”, it still shows value as “0” as the initialization is not complete.

You can also check the “Assembly” section in the GDB Dashboard which shows the disassembled code. So, the highlighted section in green in the  “Assembly” shows the following:

0x0000555555555131 main+8 movl $0x539,-0x4(%rbp)

This is the disassembled code (in AT&T syntax) for “int i = 1337”. We will come back to reviewing the assembly code later.

Run command “n” again in gdb. Now, checking the “Source” section in GDB dashboard, should that we are now on line 4.

Now. check the value of  variable “i” using print.

>>> p i
$3 = 1337

This shows that the value variable “i” is 1337.

(gdb) print i
$2 = 1337

Here is the screenshot for reference.

GDB print i after initialized

To find the type of the variable and the starting memory address of that variable in memory:
>>> print &i
$4 = (int *) 0x7fffffffdb8c

GDB print directly using variable address output

-The above shows that “i” is of “int” type. Additionally, “i” is stored at memory location starting at address 0x7fffffffdb8c

-To check maximum size of int type in memory using the sizeof() function.

>>> p sizeof(int)
$5 = 4

The above output shows that int would occupy 4 bytes of space in memory.

To examine memory using gdb use “x“.

-From above outputs, we know that variable “i” is stored in memory with starting address as 0x7fffffffdb8c. We also do know that i is of type integer and integer type would occupy a maximum of 4 bytes in memory.

To examine a specific memory address, you could use the following format

(gdb) x/FMT <starting_memory_address> 

Here is information from “help x” section:

(gdb) help x
Examine memory: x/FMT ADDRESS.
ADDRESS is an expression for the memory address to examine.
FMT is a repeat count followed by a format letter and a size letter.
Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),
t(binary), f(float), a(address), i(instruction), c(char), s(string)
and z(hex, zero padded on the left).
Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).
The specified number of objects of the specified size are printed
according to the format. If a negative number is specified, memory is
examined backward from the address.

Defaults for format and size letters are those previously used.
Default count is 1. Default address is following last thing printed
with this command or "print".

Here, is an example, to examine data from memory location 0x7fffffffdb8c upto the next bytes (or 4 bytes above 0x7fffffffdb8c) ,  use the following:

>>> x/4xb 0x7fffffffdb8c
0x7fffffffdb8c: 0x39 0x05 0x00 0x00

Alternatively, you can access provide the memory location directly by passing “&i” i.e.  “x/4xb &i”

GDB examine memory location screenshot

The above output shows raw byte by byte representation in memory. Here, 0x39 is one byte in memory and 0x05 is another byte etc.
We also know that int would occupy 4 bytes in memory. Here, this integer takes only 2 bytes out of 4 bytes in memory.

Important: This raw memory representation “0x39 0x05 0x00 0x00” is in “little-endian” format(the least significant bytes of a number come first in memory).
So, you would need to read the hex bytes  “0x39 0x05 0x00 0x00” in reverse order. Hence, the value reversed 0x00 0x00 0x05 0x39 is 00000539.
To get the actual value, we need to convert the hex value to decimal.

You could use trusty bash  to covert hex to decimal by using format “echo $(( 16#$hexNum ))“. Here is what it does look like:

[email protected]:~$ echo $(( 16#00000539 ))

So, “1337” is the decimal value stored in memory location 4 bytes starting from 0x7fffffffdb8c.

So, we now know the value of integer variable starting at memory location 0x7fffffffdb8c is 1337. (i.e. the decimal value of i stored in memory is 1337).

An alternative is to use a online hex to decimal converter such as to covert 00000539 to decimal.

-You can also print the raw data using the variable itself by using the following format:

(gdb) x/4xb &i

0x7fffffffdb5c: 0x39 0x05 0x00 0x00

Other formats in GDB:

-To print in decimal format, use x/1dw:

(gdb) x/1dw &i
0x7fffffffdb7c: 1337

-Alternatively, give the memory location as well.

(gdb) x/1dw 0x7fffffffdb7c
0x7fffffffdb7c: 1337

Misc Information:

To check the number of threads, run “info threads“.

>>> info threads
Id Target Id Frame 
* 1 process 6570 "minimal" main () at minimal.c:4

This shows there is 1 thread with PID 6570.

Other Interesting Stuff:

Here is a screenshot of the dissembled code of “minimal” binary.


The one that we are interested in for now is the instruction highlighted below:

0x0000555555555129 main+0 endbr64
0x000055555555512d main+4 push %rbp
0x000055555555512e main+5 mov %rsp,%rbp
0x0000555555555131 main+8 movl $0x539,-0x4(%rbp)
0x0000555555555138 main+15 mov $0x0,%eax
0x000055555555513d main+20 pop %rbp
0x000055555555513e main+21 retq

The instruction “movl $0x539,-0x4(%rbp)” means => move the value “0x539” to the memory location of register rbp -4.

Differences between AT&T and Intel Syntax:

By default, GDB defaults to displaying in AT&T syntax. Here is what main function dissembled looks like in AT&T syntax:

[email protected]:gdb$ gdb minimal
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
Find the GDB manual and other documentation resources online at:

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from minimal...
>>> disass /m main
Dump of assembler code for function main:
2 {
0x0000000000001129 <+0>: endbr64
0x000000000000112d <+4>: push %rbp
0x000000000000112e <+5>: mov %rsp,%rbp

3 int i = 1337;
0x0000000000001131 <+8>: movl $0x539,-0x4(%rbp)

4 return 0;
0x0000000000001138 <+15>: mov $0x0,%eax

5 }
0x000000000000113d <+20>: pop %rbp
0x000000000000113e <+21>: retq

End of assembler dump.

-If you don’t like this syntax, you can make GDB use Intel syntax using “set disassembly-flavor intel“.

Here, is an example of main function dissasembled in Intel syntax:

[email protected]:gdb$ gdb minimal
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
Find the GDB manual and other documentation resources online at:

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from minimal...
>>> set disassembly-flavor intel
>>> disass /m main
Dump of assembler code for function main:
2 {
0x0000000000001129 <+0>: endbr64
0x000000000000112d <+4>: push rbp
0x000000000000112e <+5>: mov rbp,rsp

3 int i = 1337;
0x0000000000001131 <+8>: mov DWORD PTR [rbp-0x4],0x539

4 return 0;
0x0000000000001138 <+15>: mov eax,0x0

5 }
0x000000000000113d <+20>: pop rbp
0x000000000000113e <+21>: ret

End of assembler dump.

Here, you should see subtle differences  such as the AT&T syntax displays “movl $0x539,-0x4(%rbp)” while the same in Intel syntax is “mov DWORD PTR [rbp-0x4],0x539“.

Thats it for now. I will add more tutorials on GDB in the near future.

Happy Debugging!


Below are all the credits/references/sources that made writing this blog post possible. <- Credits to recurse. I used this as my primary resource to learn GDB and then post my understanding of GDB and C here. Do check them out. <- Creator of the Epic GDB dashboard <- Great place to start. <- Simple online convertor
Credits to my m8 @bytesareana for decoding the stuff with the memory location.

Decoding IR Signals of a Blue Star Air Conditioner using an Arduino

This is part of my project to understand the IR protocol and try to decode IR signals that being send from my AC’s remote to the AC itself. So that the idea is: If I can figure out the different IR signals that being that transmitted, then I should be able to replay the traffic and control the Aircon from my PC or use my own custom IR hub instead of sending data to some third party – Alexa or Google Assistant. This may be applicable on other devices as well that use an IR transmitter/receiver like a TV/remote controlled fan/hubs.

Disclaimer: I am no means an electronics expert. This is just me experimenting and playing around trying to make sense of how IR works with no prior knowledge of how the protocol works. If you are electronics expert, then this post is probably not for you are better off pressing CTRL+W on your keyboard.


  1. KY-022 Infrared Receiver (38kHz)
  2. Arduino UNO U3
  3. Breadboard
  4. 3x Male to Male Jumper Cables.
  5. Arduino ID with IRremote library installed (Link)
  6. Computer to write your code. Duh!

Here is a picture of the KY-22 IR receiver.

ky-022 IR receiever picture

Circuit Connection:

Connect pin labelled “Y” on the KY-022 to Pin 11 on the Arduino
Connect pin labelled G to GND pin on the Arduino
Connect pin labelled R pin to the 5V pin on Arduino

-Below is the source for the IR decoder:

#include <IRremote.h>

int RECV_PIN = 11; // define input pin on Arduino.
IRrecv irrecv(RECV_PIN); 
decode_results results; // decode_results class is defined in IRremote.h

IRsend irsend; 

void setup() { 
  Serial.begin(9600); // Set Serial monitor baud rate
  irrecv.enableIRIn(); // Start the receiver 

void loop() {

  if (irrecv.decode(&results)) {
    Serial.println(results.value, HEX); 
    irrecv.resume(); // Receive the next value 

Upload the code to the Arduino and then navigate to Tools>Serial Monitor.

Now point your IR remote to the IR receiver sensor and press any key in the remote. Now, you should see the HEX value that was send from the remote in the serial console.

For example, a test case I pointed my AC’s remote to the IR receiver(KY-022) and pressed the power button. This result printed on the Serial Monitor was HEX value 90900E0A. Note: While pressing the power On button, the remote had 25 degrees set.

Here is a sample screenshot of the setup.

sending and decoding IR picture using KY-022

Here is a screenshot of the source and the data from the serial session.

arduino code and serial monitor displaying decoded IR signal

Similarly when pressing the Power Off button in the return, a different IR code of 80900C0A was send.

Hell Yeah!!!!! Ok, maybe I got excited bit too much.

I know this probably sounds dumb, but I did notice something interesting on how this is all put together. So, lets say the temperature of the AC in the remote is set to 16C. Now, if I press the “+” button to increase the temperature to 17C, then I see a different code and if I press it again again, I see a different code being send.

Here what the different hex codes for different temperatures look like(used by pressing the increment/+ sign on the remote):

Temp 16: 90000E0A
Temp 17: 90800E0A
Temp 18: 90400E0A
Temp 19: 90C00E0A
Temp 20: 90200E0A
Temp 21: 90A00E0A
Temp 22: 90600E0A
Temp 23: 90E00E0A
Temp 24: 90100E0A
Temp 25: 90900E0A
Temp 26: 90500E0A
Temp 27: 90D00E0A
Temp 28: 90300E0A
Temp 29: 90B00E0A
Temp 30: 90700E0A

Trying to make sense of IR (Sort Of):

So, in a programming point of view, I was expecting something like “new temperature” = “current temperature” + 1 and then set the value rite? Well this sort of the same with a slight difference which I guess is the way IR protocol works. So, the handheld IR remote has some logic in itself. Here is my assumption of what is happening.

  1. Say, the handheld IR remote for the Air Conditioner has current temperate as 16 degree Celsius (16C).
  2. Now lets say the user, clicks the + sign on the AC remote (IR transmitter) to increase the temperature from 16C to 17C. The IR device knows the old value as 16 from its on-board memory and now knows the new temperate has to be set to 17C. So here, the arithmetic operation i.e. increment is done on the client side i.e on the IR remote itself.
  3. The IR remote has a hard coded value of 17C somewhere in its code. The remote simply sends the IR signal with IR code for 17C. I.e. It will send 90800E0A to the IR Receiver.
  4. The IR receiver (in this case the AC), receives the IR code for 17C and then sets the temperature to 17C. Here, the AC has hardcoded values for each IR code for each temperature in its code. Something like: If “IR code received” == “90800E0A“, then set temperature to 17C.
  5. It looks more like each temperate value is hard coded and the IR remote has these values hard-coded.

Next, I will need to learn how to get a IR transmitter and replay these codes so that I can control my Aircon and other electronics from my PC. Maybe create a DIY IR hub and hook it to my PC?

So, I did buy a IR transmitter to replay the signals but the transmitter that I received was DOA. Well that went well. Haha!

ir transmitter picture

Anyways, will probably order a new transmitter to test this out post the whole COVID-19 shiZstorm.




PS: Credits to Arduino modules for making it super simple to understand this. Below are credits/resources that helped out.


Mount TrueNAS Core Samba share on Linux

This is a guide that describes on how to mount a remote Samba share configured on TrueNAS on to a Linux machine.


Login in your Linux machine. (I am using a Linux Mint 19.3 in this demo. This should technically work on other Debian/Ubuntu based systems as well).

Use the following commands to get your current user’s user ID (UID) and group ID (GID) respectively.

id -u $USER
id -G $USER

Create a file /etc/.truenas_creds. This is where you would store the samba credentials.

Replace text in red with the username and password of the remote SMB share which was configured in TrueNas.

cat /etc/.truenas_creds

Modify the file permissions so that root is the owner and set the file permission to 600.

sudo chown root: /etc/.truenas_creds
sudo chmod 600 /etc/.truenas_creds

In your linux machine, create a folder to where you want the contents of the remote samba share to be mounts. For example: create a directory named /mnt/truenas/.

sudo mkdir /mnt/truenas/

-Below is a sample syntax that can be used for populating /etc/fstab.

//ip-of-nas-server/enter-remote-samba-share/location /enter-local-mount/location/here/ cifs credentials=/etc/.truenas_creds,iocharset=utf8,uid=enter_your_uid_here,gid=enter_your_gid_here,noperm 0 0

-Here is what that I added in /etc/fstab.

// /mnt/truenas/ cifs credentials=/etc/.truenas_creds,iocharset=utf8,uid=1000,gid=1000,noperm 0 0

My TrueNAS server’s IP =>

Remote samba share => /mnt/truenas

Local mount location => /mnt/truenas/

Credentials for samba share => /etc/.truenas_creds

-Once complete, run the following to mount all entries looking at /etc/fstab.

mount -a

-If there are no errors in the above command, check your local mount path to verify that the mount was successful.

ls -l /mnt/truenas/




Update to the latest ESXI 6.7 patch version on an Intel NUC (offline update)

I recently bought an Intel NUC for my homelab (NUC7i5BNH with 64Gigs of RAM) which has been running ESXi for the past few months and I have been happy with its performance so far. Fear for the worst, I noticed a few minor UI issues in the build that I was running which had been really bugging me. For Example: When using the search bar in the HTML5 UI, it partially hides the last search result in the list which was super annoying.

Luckily, VMware fixed these issues in one of the newer 6.7 patch releases and this blog post details on how I updated ESXi 6.7 U3 to the latest patch release on my NUC. To be precise, the upgrade was from ESXi-6.7.0-20190802001-standard (Build 14320388) to ESXi-6.7.0-20200604001 (Build 16316930). PS: I am not using vCenter in my homelab.

Short Version:

1. Backup your VMs/ESXI configuration. Download the latest patch from VMware’s website (Link).
2. Upload the ESXI patch release to the server’s datastore.
3. SSH into the NUC and get the full location of uploaded patch in your datastore. Find the profiles bundled within the patch file.
4. Put the ESXI server in maintenance mode and start the update.
4. Once update is complete, reboot the NUC, disable maintenance mode and you are good to go.

[Disclaimer: Please review VMware’s best practices for performing an ESXI upgrade which also includes taking backup of your ESXI configuration + VMs. if you plan to upgrade for a production system, make sure all necessary backups are taken before attempting the procedure. My NUC which is the 5th gen, the update worked right of the box without any customization. If you are running the new 10th generation Intel NUC, then you may need have make some modification to the VIBs for the upgrade to work.]

Long Version:

Go to VMware’s patch page  and download the latest patch version for the ESXI release (Link).

In my case, I downloaded the latest patch which at the time of writing this post was

Before the upgrade, my Intel NUC was running the below build:

[[email protected]:~] vmware -vl
VMware ESXi 6.7.0 build-14320388
VMware ESXi 6.7.0 Update 3

-To check the volumes in the datastore. I did upload my patch to the ssd datastore which is a symlink.

[[email protected]:~] ls /vmfs/volumes/ -l
total 1792
drwxr-xr-x    1 root     root             8 Jan  1  1970 317122ee-8c7e716b-adf9-8414e6a14676
drwxr-xr-x    1 root     root             8 Jan  1  1970 5e4867ab-521f3ade-c1f4-94c691adc828
drwxr-xr-t    1 root     root         86016 Jun 26 18:10 5e486a9d-f29d23c4-81b6-94c691adc828
drwxr-xr-x    1 root     root             8 Jan  1  1970 dafb9eea-924c7ebf-4eba-5b7ab8fcbb15
lrwxr-xr-x    1 root     root            35 Jun 26 18:16 ssd -> 5e486a9d-f29d23c4-81b6-94c691adc828

Verify the full path/location of the uploaded image.

[[email protected]:~] ls /vmfs/volumes/5e486a9d-f29d23c4-81b6-94c691adc828/

Using esxcli, find the profiles that are bundled with the offline installer. The profile name will be used later when performing the upgrade.

[[email protected]:~] esxcli software sources profile list -d /vmfs/volumes/5e486a9d-f29d23c4-81b6-94c691adc828/
Name                             Vendor        Acceptance Level  Creation Time        Modification Time
-------------------------------  ------------  ----------------  -------------------  -------------------
ESXi-6.7.0-20200604001-standard  VMware, Inc.  PartnerSupported  2020-06-04T02:21:11  2020-06-04T02:21:11
ESXi-6.7.0-20200604001-no-tools  VMware, Inc.  PartnerSupported  2020-06-04T02:21:11  2020-06-04T02:21:11
[[email protected]:~]

Place the ESXI host in maintenance mode before starting the upgrade.

[[email protected]:~] esxcli system maintenanceMode set -e true

Verify that ESXI is running in Maintenance mode.

[[email protected]:~] esxcli system maintenanceMode get

The output “Enabled” means that the host is in maintenance mode.

To perform the patch upgrade, pass the path and the profile as well.

[[email protected]:~] esxcli software profile update -d /vmfs/volumes/5e486a9d-f29d23c4-81b6-94c691adc828/ -p ESXi-6.7.0-20200604001-standard
Update Result
Message: The update completed successfully, but the system needs to be rebooted for the changes to be effective.
Reboot Required: true
VIBs Installed: VMW_bootbank_ixgben_1.7.1.16-2vmw.670.3.104.16075168, 
[[email protected]:~]

Once the update is complete, reboot the ESXI server.

[[email protected]:~] reboot

Once the host is back up, SSH into the ESXI host. The host will still be in maintenance mode which is expected.

[[email protected]:~] esxcli system maintenanceMode get

Disable maintenance mode

[[email protected]:~] esxcli system maintenanceMode set -e false
[[email protected]:~] esxcli system maintenanceMode get

Now, verify that the update completed and is running the new build.

[[email protected]:~] vmware -vl
VMware ESXi 6.7.0 build-16316930
VMware ESXi 6.7.0 Update 3
[[email protected]:~]

Well, Thats it folks!! Hope this helps.


PS: I anticipated the update process to be super confusing and tedious. However, it turned out to be fairly simple thanks to jeffreykusters and VMware’s instructions which I have credited them for below).



How to verify GPG key of a downloaded CentOS ISO and verify its checksum [Detailed]

This is a tutorial on how to check checksums of a CentOS image using GPG to verify the integrity of a downloaded CentOS ISO image.

1. Download the public key from the CentOS website

Open a terminal and create a directory where you need to download the ISO and the checksum file.

mkdir validate && cd validate/

Download the GPG public key from the official CentOS website. (You can find the full list of CentOS keys here. )


Below are the contents of the downloaded file which shows that this is a public key.

cat RPM-GPG-KEY-CentOS-Official
Version: GnuPG v2.0.22 (GNU/Linux)


2. Check and verify the fingerprint of the downloaded public key.

Using gpg, check the fingerprint of the downloaded public key file using the following command:

gpg --dry-run --import --import-options import-show  ./RPM-GPG-KEY-CentOS-Official
pub   rsa4096 2019-05-03 [SC]
uid                      CentOS (CentOS Official Signing Key) <[email protected]>

gpg: Total number processed: 1

From the output, the fingerprint is 99DB70FAE1D7CE227FB6488205B555B38483C65D.

Now, we need to verify if the fingerprint matches the one documented in official CentOS page.

So, go to and search for the above fingerprint.


verify fingerprint matches from centos official keys documentation page

[Note: You could split the fingerprint into blocks of four characters as shown below: 99DB 70FA E1D7 CE22 7FB6 4882 05B5 55B3 8483 C65D ]

If the fingerprint matches the one documented in the official CentOS key documentation page, then the public key is untampered and can be trusted.

[Note: If you are using a newer version of gpg, then you can use “gpg –quiet –with-fingerprint ./RPM-GPG-KEY-CentOS-Official“. I did not use the –with-fingerprint flag as the version of gpg installed in my machine does not show the fingerprints with the –with-fingerprint flag.]

3. Import the Public key to your GNUPG keyring

Now, import the downloaded key to your gnupg keyring.

gpg --import ./RPM-GPG-KEY-CentOS-Official
gpg: key 05B555B38483C65D: public key "CentOS (CentOS Official Signing Key) <[email protected]>" imported
gpg: Total number processed: 1
gpg:               imported: 1

The above shows that it processed and imported 1 key to you local store.

To list the keys currently in your gnupng keyring, you can use the following:

gpg --list-keys
pub rsa4096 2019-05-03 [SC]
uid [ unknown] CentOS (CentOS Official Signing Key) <[email protected]

4. Download the CentOS ISO image

Download the CentOS ISO file:


5. Download the CHECKSUM.asc file and verify that it is not tampered with.

Once the download is complete, download the “CHECKSUM.asc” file from the CentOS website:


If you read the contents of the CHECKSUMS.asc file, you will see that it contains a PGP signed message with the checksums for the CentOS ISO image.

cat CHECKSUM.asc
Hash: SHA256

# CentOS-8.1.1911-x86_64-boot.iso: 625999872 bytes
SHA256 (CentOS-8.1.1911-x86_64-boot.iso) = 7fea13202bf2f26989df4175aace8fdc16e1137f7961c33512cbfad844008948
# CentOS-8.1.1911-x86_64-dvd1.iso: 7554990080 bytes
SHA256 (CentOS-8.1.1911-x86_64-dvd1.iso) = 3ee3f4ea1538e026fff763e2b284a6f20b259d91d1ad5688f5783a67d279423b
Version: GnuPG v1


To verify that downloaded “CHECSUMS.asc” file is not tampered and is indeed from CentOS, use the –verify flag.

gpg --verify CHECKSUM.asc
gpg: Signature made Tue Jan 14 19:52:52 2020 IST
gpg: using RSA key 05B555B38483C65D
gpg: Good signature from "CentOS (CentOS Official Signing Key) <[email protected]>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 99DB 70FA E1D7 CE22 7FB6 4882 05B5 55B3 8483 C65D

The above outputs shows the the signature is good and CHECSUM.asc file is good and not tampered.

At this point, you should have the following files in the directory:

ls -l
total 7377932
-rwxrwxrwx 1 r3con r3con 1179 Jun 3 11:10 CHECKSUM.asc
-rwxrwxrwx 1 r3con r3con 7554990080 Jan 4 03:17 CentOS-8.1.1911-x86_64-dvd1.iso
-rwxrwxrwx 1 r3con r3con 1683 Sep 12 2019 RPM-GPG-KEY-CentOS-Official

6. Verifying the checksum of the downloaded image using the CHECKSUM.asc file.

To verify the checksums of the downloaded ISO image using the following command:

sha256sum -c CHECKSUM.asc 2>&1 | grep OK
CentOS-8.1.1911-x86_64-dvd1.iso: OK

succesfull sha256 checksum verification

This shows that the checksum matches and the downloaded ISO file is intact or not tampered with.

Hope this helps. Cheers


CentOS Documentation

GNUPG Fingerprint workaround