Wednesday, September 21, 2022

Install multiple Ubuntu instances in WSL2

 Yes, there are probably many post about this, such as https://cloudbytes.dev/snippets/how-to-install-multiple-instances-of-ubuntu-in-wsl2 and https://stackoverflow.com/questions/51584765/how-do-you-install-multiple-separate-instances-of-ubuntu-in-wsl. Still want to make this post about a special case not covered anywhere else.

Run wsl -l can show all installed distribution.

If want to remove one of the distribution, run: wsl --unregister <DistributionName>

Recently I'm playing with ROS Kinetic. Unfortunately, as mentioned http://wiki.ros.org/kinetic/Installation/Ubuntu, only old Wily (15.10) and Xenial (16.04 LTS) are supported. It's possible to get it setup with VirtualBox or VMware VM, but I'd like to go with WSL2 on Windows 11, as mentioned in my the other post, it is possible to enable Ubuntu GUI with the Windows 11 build 22000+. One problem here is the Xenial Ubuntu is no longer available from Microsoft Store, so would need to use WSL2 very useful 'import' feature. For this, would need a tar.gz file of the Ubuntu distribution, the ISO image file doesn't work with WSL import. Another problem now, seems there is no tar for the Xenial Desktop image, but the base image is available such as the amd64 one as ubuntu-base-16.04.6-base-amd64.tar.gz, and it is pretty small, just 41MB. Using command like this to import it:

wsl --import xenial c:\Users\%USERNAME%\wsl_xenial .\Downloads\ubuntu-base-16.04.6-base-amd64.tar.gz 
(Note: for imported distro, if run wsl --unregister, it will delete distro file). There is no Ubuntu desktop and only user root is created. To add new user, run following command with the root account to add the user and add it to sudo group:

adduser newuser

usermod -aG sudo newuser

Try this to verify the user group and switch to it:

groups newuser
su - newuser 

Type 'exit' to switch back to root, and run 'apt update' and 'apt install sudo', so later the new user can do apt install with sudo command. This is the minimum for installing other package as new user. 

To get the desktop environment, run: 'sudo apt install ubuntu-desktop', which installs most common packages. On WSL/Windows OS side, need to follow https://learn.microsoft.com/en-us/windows/wsl/tutorials/gui-apps, installing the AMD/nVidia/Intel's Virtual GPU (vGPU) driver first, then run: 'wsl --update' and 'wsl --shutdown' for a clean restart. May install gedit and run it to verify the GUI. Per my testing, with Ubuntu 20.04 LTS the GUI works fine. However, doesn't work for Ubuntu 16.04 Xenial, seeing segment fault and probably core dump. Tried Diagnosing "cannot open display" type issues with WSLg but not much luck. So leave it aside for now.

 

Sunday, July 10, 2022

我的小鸡们


 六月18日星期周六,对我们全家来说是悲伤的一天,来到我们家十一天的小芦花鸡大毛不幸在我们的后院失踪了,而且最可能是被鹰之类的飞禽捉走了。本来它们是在纸箱里,上面盖着。因为味有点大,太太把纸箱放后院deck上。风大把盖子吹掉了,小鸡就自己跑了出来,可我们不知道,不然一定会看着它们的,因为以前就有小鹰虎视眈眈的站fence上看我们的小鸟。
六月7日,两只小芦花鸡刚刚落户我们家,大概有一周多大,浑身黄白棕黄色的毛,一只颈背棕色毛多的,我们给它取名一撮毛,另一只颈两侧各有些棕毛,我们叫它俩撮毛,后来就简称大毛二毛。大毛二毛很招人喜爱,总是小声小气的鸡鸡个不停,甚至在边吃饭睡觉时也会唧唧,即像在呼唤同伴,又像在表达很好很舒服很好吃。失去了大毛,让我想起了鲁迅《祝福》里的祥林嫂,好在很快,我们又收养了新的小鸡,给了我们一些抚慰。
失去大毛那天,太太带着孩子去买回两只新小鸡,它们是那么小,恐怕刚出生一两天而已,刚来时,站着就要睡着了,也不大吃东西,好像不知道怎么吃。我们叫它俩团团和圆圆,寄予小鸡一家团圆。为了不再老让孩子们想起大毛伤心,我们叫二毛为芦花。

团团圆圆一来就把芦花当作妈妈,总往芦花的翅膀和肚子下钻。好在芦花很nice,从开始觉得很wierd,到后来就顺其自然,带着两个小妹妹吃饭睡觉草地里跑来跑去。鸡食很让我们发愁,不知道它们爱吃什么,于是给它们吃麦片燕麦片,玉米面拌菜,还有苍蝇。天好时我们经常在后院坐板凳上,地上铺着小鸡pupu的报纸,举着苍蝇拍打苍蝇。小鸡们知道啪一声响就有好吃的,所以经常等在身旁,甚至迫不及待的自己追苍蝇,当然总是一无所获。它们快乐的长大着。为了区分两只小的,我们在圆圆小鸡翅膀上mark黑颜色,就叫它小黑。后来我们又从别人那买回一只毛黑一点的小鸡,主人说5月底买的,叫Fluffy,可个头比芦花还小,它以前常被同养的两只小鸭子欺负,胆子很小,好在很快就和芦花团团圆圆和睦相处,我叫它毛毛。它也很快学会吃苍蝇。六月28日周二,我因有事去了办公室,不幸happens again:太太把小鸡放在纸箱里,然后放deck上。为了透气,我在纸箱上给大毛二毛开了些小窗口,可团团圆圆小,可以从窗口跑出来,太太从后院进屋,随手关拉门,没想到从纸箱外逃出来的小圆圆跟在后面,被挤,不幸。为了不让Emily伤心,我急忙从办公室赶回,几经周折,去卖鸡家又买了个小鸡,找不到大一点的,新小鸡叫声响亮,可不大会吃,总想睡觉。因太小,没法骗小孩,只好说farmer要求把小圆圆换走了。这个小家伙,我叫它球球,因为它跑起来像个滚动的棉花球。它长的好像很慢,都过了一个多星期,它还和刚来时差不多大。它睡觉喜欢钻到芦花的翅膀或肚子下,有时芦花已经趴下,小球球会跳到芦花背上,一旦芦花站起来,小球就有机会钻了。






上周日(Jul 3),太太又从别人那买回两只小鸡,有大毛二毛来时大。这样我们就有6只小鸡了。
芦花很喜欢在后院跑,每次它都是最难捉回来的。它甚至听得懂它的名字,每次听到我们喊芦花,就知道有苍蝇吃了,会从院子的不知那个角落连跑带飞的奔到我们打苍蝇的地方来。后来可能因小鸡太多了,小的小鸡时常在我们身旁等苍蝇吃,芦花再从远处赶来常吃不到,它就不大理会名字被叫到了。芦花很早就学会玩土洗澡了,有时就趴在我们种的西红柿旁的土地里,享受泥土和阳光浴。除了苍蝇,芦花和小鸡们很喜欢吃面条,也喜欢米饭。看小鸡们抢食物吃有时很好笑,小的小鸡抢到好吃的会飞快的在其它鸡面前钻来转去。芦花看着很大,可前几天我们给它称了,却还不到一磅。芦花不在外边跑时很乖,轻轻push它,它就肯安静地趴在我们的手和胳膊上。我们都在想芦花再大一点,就会给我们下蛋了。
8日周五早,Rogers的cell phone和无线网同时down了。今天,六只小鸡一早就又被放出来,吃了些菜拌玉米面,喝了些水,在后院撒欢。突然芦花有些异样,站在草地里不动,我以为它在pupu,果然它拉了一点黑稀东西,可还是很不舒服,没过多久它就开始趴下了,连我走过去摸它,它也没再像以前那样run away。我抚摸了它,没多久它变得更加痛苦,头颈开始使劲扭动,很吓人,又让我们手足无措,不知怎样才能帮它,Rogers的网络还没回来,电话是好了,可我们都不知打给谁。我只能帮它按摩腹部,有那么一刻,芦花又站起来,跑开了,让我以为一切转危为安了,可没多久芦花又趴下了。眼睛也不愿睁开了。生命在慢慢逝去。在最后的一刻里,芦花再次从我的手足挣扎出去,跑到了它心爱的西红柿旁的土地里。我把它再次抱回手里没多久,它终于解脱了,安详的躺在我的胳膊上。

我们把它埋在香椿树和桑树下,离小圆圆埋葬的地方很近,希望它在天堂与一撮毛还有小圆圆团聚。
生命有时是如此的脆弱,我们在不知不觉中付出了关爱,在那一刻被残忍的剥夺了。在我们眼前耳边缠绕着它们的身影和声音,我们恐怕不再有勇气去收养新的小鸡。
愿小芦花安息


 

Thursday, May 19, 2022

Setup Gerrit server using HTTP authentication on Ubuntu

 I know there are quite some posts about how to do that, but decide to make this after I scratched to find solution for problem I faced.

For installing Gerrit server on Linux itself, it is pretty straight forward. Not going to talk about the simple way as it is pretty much just a local git. Will only discuss the Standalone Daemon installation as described here: https://gerrit-review.googlesource.com/Documentation/install.html

The instruction is very simple, adding gerrit as new user to your Linux, use gerrit account to install gerrit war file. And there is a note specifically mentioned you would need manually create the gerrit application folder if user gerrit does not have the privilege to create folder there. It also means there is no need of sudoer right for setting up gerrit server.

I picked most default setting during installation, as to be honestly, I don't know exactly what the option is for. But for authentication method, I'm sure I would want the 'http' option, as I want the users are manageable and there is no LDAP or ActiveDirectory at back end for small projects/small teams. But likely nowhere in the guide mentioned that you would need to setup the HTTP server (such as Apache) to make it working. Like many others, I have set the gerrit server on default port 8080, then I tried using web browser to open http://localhost:8080, and got the error: 

Configuration Error

Check the HTTP server's authentication settings.

The HTTP server did not provide the username in the Authorization header when it forwarded the request to Gerrit Code Review.

Some post like this one clarified that gerrit server is not expecting user to directly access the port reserved for Gerrit. Instead, Gerrit is expecting the http server to forward/redirect the access to the gerrit port. If user want to access Gerrit via a port other than the default http port 80, then a port different from the gerrit port would be need to setup with the http server. And, yes, a http server needs to be setup to make things working, especially, the http server also in charge of user authentication => this is not done by Gerrit server. Below setting is for user using http://gerrit_server_IP:8000 to access gerrit with web browser.

I installed the popular apache2 on Ubuntu: sudo apt install apache2

Then create /etc/apache2/sites-available/010-gerrit.conf with content:

<VirtualHost *:8000>
    ServerName localhost

    ProxyRequests Off
    ProxyVia Off
    ProxyPreserveHost On

    <Proxy *>
          Order deny,allow
          Allow from all
    </Proxy>
    <Location "/login/">
          AuthType Basic
          AuthName "Gerrit Code Review"
          AuthBasicProvider file
          AuthUserFile /etc/apache2/gerrit.htpasswd
          Require valid-user
    </Location>

    ProxyPass / http://localhost:8080/
</VirtualHost>

For highlighted, the port 8000 is the port for user to access Gerrit via web browser; The path for AuthUserFile is the file used to store authorized user accounts for the web access (not for the Linux host). The port 8080 is where the Gerrit server is listening to. And cannot add 'no-cannon' after that as many post said, as that will give me error (Invalid ProxyPass|ProxyPassMatch parameter. Parameter must be in the form 'key=value') when I trying to restart Apache server. Also, need to add "Listen 8000" to /etc/apache2/ports.conf to make Apache accept access to the 8000 port. Then would need to enable the Gerrit config and restart Apache server with command:

sudo a2enmod proxy_http
sudo a2ensite 010-gerrit
sudo systemctl restart apache2

At this point, both Apache and Gerrit servers are up and running, but there is no valid user yet. To add the first user which would be the admin of Gerrit server, run this command to create user 'admin':

htpasswd -c /etc/apache2/gerrit.htpasswd admin

Note, usually the password file would be maintained by the web service account, so there is no need 'sudo' for running htpasswd command. Without 'sudo'. would need to run 'sudo chown' and 'sudo chgrp' against the password file to grant permission to the web service account, such as 'gerrit'.

Gerrit server is ready from now on. New user would need be added manually by gerrit account with command:

htpasswd /etc/apache2/gerrit.htpasswd new_user

Note: comparing above, no '-c' here. Otherwise it will overwrite the password file, and the previous added account (admin) would get removed.

There is many way to add a self register user service if that is desired.

Several other notes:

1) gerrit server won't automatically get started. Follow the instruction https://gerrit-review.googlesource.com/Documentation/install.html to make it automatically starts with OS boot.

And keep in mind, for manually starting gerrit, have to run gerrit.sh start with sudo as it needs root right. Make sure seeing 'Starting Gerrit Code Review: OK' after start gerrit.

2) Better update gerrit_application_directory/etc/gerrit.conf port setting of canonicalWebUrl, change to:

 canonicalWebUrl=http://gerrit_server_IP:8000/

i.e. the port number should be matched with Apache server setting for Gerrit. Otherwise, when user trying to logout or other operation, gerrit may direct the access to the Gerrit internal port 8080.

3) Keep in mind the user account for the host PC and Gerrit service are separated. 'gerrit' is account on the host PC, and above 'admin' account is gerrit user account. For gerrit operation via SSH, user would need to upload the SSH key for the Gerrit account first. Also note, with latest Git/OpenSSH, it may no longer accept RSA key, so you may generate ed25519 key pair for current host user account with this command:

ssh-keygen -t ed25519 -C 'some_comment'

Then you can run gerrit ssh command, such as this one for flush project_list:

ssh -p 29418 admin@gerrit_server_IP gerrit flush-caches --cache project_list

Note above command is using the Gerrit account name 'admin' for the SSH session, as the SSH service is provided by Gerrit.

4) Gerrit supports multiple plugin, such as gitiles for browse repo code. It's pretty easy to install plugins, just drop the jar file to gerrit_application_directory/plugins folder. And in fact, the gerrit installer war is coming with several common plugins, include giltes. User can install the plugin with init command (ref link), like this:

java -jar gerrit-3.5.1.war init -d /path/to/gerrit_app --install-plugin gitiles --install-plugin hooks --install-plugin plugin-manager --install-plugin singleusergroup ...

Or just do init with --install-all-plugins to have all default plugins installed.

5) If manually put a bare repo under gerrit git folder, it won't show up in gerrit web view. Running "gerrit flush-caches --cache project_list" won't help. User may explicitly make it visible with gerrit command: gerrit set-project the_hidden_git_name --project-state ACTIVE, or take other approach mentioned in the stackoverflow page: https://stackoverflow.com/questions/27817107/unhide-a-hidden-gerrit-project

And for local git, would need to set or update the remote URL like this:

git remote set-url origin ssh://gerrit_server_IP:29418/git_name 

6) As using HTTP authentication, we are not hooking up to company's LDAP server. The mail setting might be a headache, as it is not easy to setup using company's SMTP server. Even with mail service installed on the local Linux server, email exchange between the Linux and company's email server might be a nightmare. For example, I have Postfix installed on my Linux, and I'm getting 'Rejecting Unknown Local Recipients with Postfix'. Following this I can get the error resolved, the Linux mail system did say mail has been delivered, but I have never got the email from company email server. This would be a problem as user needs to register an email for submit code review: gerrit will send a verification email before adding user email address in record. To get around this, following https://gerrit-review.googlesource.com/Documentation/cmd-set-account.html, set the email address for new user using the admin account:

ssh -p 29418 admin@localhost gerrit set-account --add-email useremail_name@company.com gerrit_username

For gerrit management, there is plugins/admin-console (googlesource.com) which provides  information via SSH commands to Gerrit Administrators.

Note:  if planning to use LDAP and not using HTTP authentication, then no need to setup Apache as Gerrit has built-in web service, may just follow https://gerrit-review.googlesource.com/Documentation/config-gerrit.html#ldap, do not enable proxy-http, i.e. gerrit.config should have setting like:

[httpd]
        #listenUrl = proxy-http://*:8081/
        listenUrl = http://*:8080/

Sunday, February 20, 2022

Recover USB thumb drive (修复U盘)

USB thumb drive is very popular due to the cheap price, portable, easy to use, and can be used as boot media. However, occasionally user may run into problem with this small piece of hardware, such as the USB device is detected as malfunction USB device, Disk needs to be format but cannot be formatted successfully, No Disk or No Media, etc. In most cases, the hardware wasn't damaged, the real problem is some essential data stored on the flash was corrupted. The flash not only contains area as user storage space, but also reserves several small space for storing this USB drive specific configuration data, and firmware which is used to manage the flash memory, yes, inside the flash drive there is a small controller chip beside the flash chip, and it may consume the firmware we mentioned here. Some vendors sell the USB board with controller, and customer can solder the desired flash chip on it, such as this one from Ali-Express, which is about $14 each:

SM3267L AE UFD PCBA, 3267AE USB3.0 FLASH DRIVE PCBA, DIY UDF Kits, TSOP48+LGA60 Special Pad,ONLY FOR some LGA60 E2NAND.|kit kits|pad padkit diy - AliExpress

The chip in the middle, SM3267L-AE, is an USB 3.0 controller from Silicon Motion(慧荣). The right side is the pads for soldering the flash chip, you can pick compatible chip from Sandisk, Samsung, Intel or any other vendors.

If you run into this problem, you may try to recover your USB thumb drive if you know the detail of the hardware, and can find the right tools to program it. Don't bother to try the vendor's online recovery tools or RMA, mostly those just a waste of time and money.

Luckily, there is popular software/tools and website all about this. To identify the chip info, the most popular tools is the ChipGenius(芯片精灵). You can find this software from many site, likely the tools originally is from Chinese site https://www.mydigit.cn/(数码之家), a Russian website, https://www.usbdev.ru/files/chipgenius/,  has a collection of almost all versions of it. There is a catch, for the latest version v4.20 and v4.21, anti-virus software will report "Trojan:Win32/CryptInject!MSR" . I'm not sure is it a real risk or false alarm, anyway, for anyone who concern about the risk, you can stick with the version 4.19, which works for most chip/devices.

Once you can identify the chip info, then you will need to find the right tools to reprogram the device. For my case, it is the above SM3267AE controller with a Sandisk chip. So I would need the SMI Massive Production Tool (MP Tool). The Russian usbdev site again provide a collection of the tools at here: https://www.usbdev.ru/files/smi/transcendsmbyusbdev/

There is a lot of tutorial of this SM MP Tool, such as this one: 慧荣SMI SM32X量产工具G1219 图文教程.恢复方法. Just several notes here per my experience:

  • 1st need to setting, the password is '320' or two space.  
  • Check 'Download ISP' if you get No Media or Capacity is 0. Do not select specific ISP file if you don't know which one is the right one. Let the tools to select the proper one.
  • may get fail for different reason, just try different setting. For format error, can ignore it. Later do the formatting from Windows.

Sunday, January 23, 2022

Convert image to printable ASCII with Python

我上大学那时还没有喷墨和激光打印机,只有9针/16针的针式打印机,只能打印ASCII字符,不能打印图像。有很酷的程序把图像转换成ASCII符。Google可以找到很多这类程序,如:

https://github.com/qeesung/image2ascii

https://github.com/uvipen/ASCII-generator

https://github.com/LazoCoder/Image-To-ASCII

这里share一个我写的比较简单的程序 (font file can be downloaded from fonts.google.com: hhttps://fonts.google.com/specimen/Courier+Prime?query=courier):

import tkinter
from tkinter import filedialog
from PIL import Image, ImageFont

def create_char_map():
characters = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
weights = []

font_name = "Courier_Prime/CourierPrime-Regular.ttf"
font_size = 16 # px
font_color = "#000000" # HEX Black
font = ImageFont.truetype(font_name, font_size)
max_weight = 0

# Loop through the characters needed and save to desired location
for character in characters:
# Get text size of character
width, height = font.getsize(character)
pix = font.getmask(character)
weight = 0
for y in range(len(pix)//width): # the actual row might be smaller than hegith
for x in range(width):
weight += pix[x+width*y]
weights.append(weight)
if weight>max_weight: max_weight = weight
# uniform the weight to 0~256*3-1
char_map = {767-int(w*767/max_weight):ch for (w, ch) in zip(weights, characters)}
# for revert image: char_map = {int(w*767/max_weight):ch for (w, ch) in zip(weights, characters)}
return char_map

def get_pixmap(path):
im = Image.open(path)
print('Original: ' + str(im.size))
size = 320
if im.size[0] > size:
resized_image = im.resize((size, int(size * im.size[1] / im.size[0])))
pixmap = resized_image.load()
pixmap_size = resized_image.size
print('Resized: ' + str(pixmap_size))
else:
pixmap = im.load()
pixmap_size = im.size
return pixmap, pixmap_size
 
def getchar(rgb_sum):
for i in range(1, len(char_keys)):
if rgb_sum<char_keys[i]:
if char_keys[i]+char_keys[i-1]>rgb_sum*2: i=i-1
break
return char_map[char_keys[i]]

def main():
#return
root = tkinter.Tk()
root.withdraw()
fin=filedialog.askopenfilename()
if(fin):
pixmap, pixmap_size = get_pixmap(fin)
file = open('output.txt', 'w')
for y in range(0, pixmap_size[1]-1, 2):
line=''
for x in range(pixmap_size[0]):
# merge two line into one printed line to keep a better ratio
rgb_sum = (pixmap[x, y][0] + pixmap[x, y][1] + pixmap[x, y][2] +
 pixmap[x, y+1][0] + pixmap[x, y+1][1] + pixmap[x, y+1][2])>>1
line+=getchar(rgb_sum)
file.write(line+'\n')
file.close()

if __name__=="__main__":
char_map = create_char_map()
char_keys = sorted(char_map)
main()