Linux note: How to start an application automatically on boot

  • Modify /etc/rc.d/rc.local file

If you wish to add a new service to start when the machine boots you should add the necessary script to the directory /etc/init.d/. Many of the scripts already present in that directory will give you an example of the kind of things that you can do.

  • Newer versions of linux support systemd.  It has more features and is more portable than debian-specific solution.

sample script:

[Unit]
Description=Example systemd service.

[Service]
Type=simple
ExecStart=/bin/bash /path/to/myscript.sh

[Install]
WantedBy=multi-user.target
  • Using crontab
$ crontab -e

@reboot /home/nick/auto_run_script.sh

Linux Screen Commands and Shortcuts

Screen is a very powerful tool to keep a process running in the background while retaining the ability to log out and log back in anytime to check on the output.

Annoyingly I keep forgetting the commands to use screen effectively so here’s a quick reference guide of commands and shortcuts for myself and maybe others.

Commands

To start a screen session:

$ screen -S <command>

To list existing screen sessions:

$ screen -ls

To reconnect to a screen session:

$ screen -r <session_name>

To reconnect to a still attached screen session:

$ screen -rd <session_name>

Shortcuts

To detach a screen session: ctrl + a + d

To open a new screen session while inside screen: ctrl + a + c

To switch back to the previous screen session: ctrl + a + p

To switch to the next screen session: ctrl + a + n

Use: screen [-opts] [cmd [args]]
 or: screen -r [host.tty]

Options:
-4            Resolve hostnames only to IPv4 addresses.
-6            Resolve hostnames only to IPv6 addresses.
-a            Force all capabilities into each window's termcap.
-A -[r|R]     Adapt all windows to the new display width & height.
-c file       Read configuration file instead of '.screenrc'.
-d (-r)       Detach the elsewhere running screen (and reattach here).
-dmS name     Start as daemon: Screen session in detached mode.
-D (-r)       Detach and logout remote (and reattach here).
-D -RR        Do whatever is needed to get a screen session.
-e xy         Change command characters.
-f            Flow control on, -fn = off, -fa = auto.
-h lines      Set the size of the scrollback history buffer.
-i            Interrupt output sooner when flow control is on.
-l            Login mode on (update /var/run/utmp), -ln = off.
-ls [match]   or
-list         Do nothing, just list our SockDir [on possible matches].
-L            Turn on output logging.
-Logfile file Set logfile name.
-m            ignore $STY variable, do create a new screen session.
-O            Choose optimal output rather than exact vt100 emulation.
-p window     Preselect the named window if it exists.
-q            Quiet startup. Exits with non-zero return code if unsuccessful.
-Q            Commands will send the response to the stdout of the querying process.
-r [session]  Reattach to a detached screen process.
-R            Reattach if possible, otherwise start a new session.
-s shell      Shell to execute rather than $SHELL.
-S sockname   Name this session <pid>.sockname instead of <pid>.<tty>.<host>.
-t title      Set title. (window's name).
-T term       Use term as $TERM for windows, rather than "screen".
-U            Tell screen to use UTF-8 encoding.
-v            Print "Screen version 4.09.00 (GNU) 30-Jan-22".
-wipe [match] Do nothing, just clean up SockDir [on possible matches].
-x            Attach to a not detached screen. (Multi display mode).
-X            Execute <cmd> as a screen command in the specified session.

References:

Multi-monitor Screenshots Using WinAPI

void GdiPlusScreenCapture(MONITORINFOEX info, IStream* istream)
{
    HDC DevC = CreateDC(nullptr, info.szDevice, nullptr, nullptr);
    DWORD Width = info.rcMonitor.right - info.rcMonitor.left;
    DWORD Height = info.rcMonitor.bottom - info.rcMonitor.top;

    HDC CaptureDC = CreateCompatibleDC(DevC);
    HBITMAP CaptureBitmap = CreateCompatibleBitmap(DevC, Width, Height);
    HGDIOBJ old_obj = SelectObject(CaptureDC, CaptureBitmap);
    BitBlt(CaptureDC, 0, 0, Width, Height, DevC, 0, 0, SRCCOPY | CAPTUREBLT);

    Gdiplus::Bitmap bitmap(CaptureBitmap, nullptr);

    CLSID pngClsid;
    CLSIDFromString(L"{557cf401-1a04-11d3-9a73-0000f81ef32e}", &pngClsid);

    bitmap.Save(istream, &pngClsid, nullptr);

    SelectObject(CaptureDC, old_obj);
    DeleteDC(CaptureDC);
    ReleaseDC(nullptr, DevC);
    DeleteObject(CaptureBitmap);
    DeleteDC(DevC);
}

void SaveScreenCapture()
{
    std::vector<MONITORINFOEX> monitorInfos;
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);

    // get the bitmap handle to the bitmap screenshot
    HDC hdc = GetDC(nullptr);
    EnumDisplayMonitors(hdc, nullptr, [](HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) -> BOOL {
    std::vector<MONITORINFOEX>* pMonData = reinterpret_cast<std::vector<MONITORINFOEX>*>(dwData);
		MONITORINFOEX info;
		info.cbSize = sizeof(MONITORINFOEX);
		GetMonitorInfo(hMonitor, &info);
		pMonData->push_back(info);
		return true;
	}, reinterpret_cast<LPARAM>(&monitorInfos));
    
    int index = 1;
    for (auto& monitorInfo : monitorInfos)
    {	//Create an empty IStream:
	IStream* pIStream = nullptr;
	if (CreateStreamOnHGlobal(nullptr, true, &pIStream) == S_OK)
        {

            GdiPlusScreenCapture(monitorInfo, pIStream);
	    HGLOBAL hg = nullptr;
	    GetHGlobalFromStream(pIStream, &hg);
	    SIZE_T bufsize = GlobalSize(hg);
	    LPVOID ptr = GlobalLock(hg);
            TCHAR jpegFile[MAX_PATH + 1] = { 0 };
	    _stprintf_s(jpegFile, MAX_PATH, "%d.jpg", index);
	    //write from memory to file for testing:
	    std::ofstream fout(jpegFile, std::ios::binary);
	    fout.write((char*)ptr, bufsize);
	    fout.close();
	    pIStream->Release();
	    GlobalUnlock(hg);
        }
    }

    ReleaseDC(nullptr, hdc);
    GdiplusShutdown(gdiplusToken);
}

Deploy Django 4.0 on Shared Hosting

Create a Python App

Log in to CPanel and open Setup Python App.

Click on Create Application

  • Select Python Version 3.9.12
  • Application Root is the directory where you will place the code files of your Django project. Make sure that it is a different folder than your domain root.
  • Application URL is the domain or subdomain where your app will run
  • In the Application Startup File, type passenger_wsgi.py
  • In the Application Entry Point, type application

After setting all these, click on the Create button.

Setup the Database

Open MySQL Databases in Cpanel

Create a new Database and note the database name. We will need it later.

Create a new MySQL user and note the username and password. We will need it later.

Add the new user to the new Database

Grant all the permissions to the user and select Make Changes

Upload your project

Open the File Manager and go to the Application root you specified in the part 1

Zip your project. Upload it to this folder and extract the zip file. Your project files should be in the same folder as the passenger_wsgi.py file. Make sure that manage.py and passenger_wsgi.py are in the same folder.

Edit the passenger_wsgi.py file.

Delete everything from this file and add the following code:

from base.wsgi import application

Where base is your project folder. It is the same folder that contains your settings.py file. It will be different if you are not using the test project that I provided. You can locate your wsgi.py file and import application from it.

Now edit your settings.py

Add your domain to the ALLOWED_HOSTS list. If there is a www version of your domain, add that too. Do not use http:// to https://ALLOWED_HOSTS = ['django.umer.link']

In the DATABASES dictionary, modify the default database. 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'databasename', 'USER': 'databaseusername', 'PASSWORD': 'databasepassword', 'HOST': 'localhost', 'PORT': '3306', }

Make sure to replace databasename with the database name, databaseusername with the database username and databasepassword with the database password.

Now go to the end of the file, modify STATIC_URL and add the STATIC_ROOTSTATIC_URL = '/static/' STATIC_ROOT = '/home/username/domainroot/static'

Replace the username with your CPanel username. The domainroot will be public_html if you are deploying on the main domain of your hosting account. If you are deploying on a subdomain or an add on domain, it will be name of the addon domain or the subdomain.

Now edit the __init__.py file

Add the following codeimport pymysql pymysql.install_as_MySQLdb()

Complete configuration from the Terminal

Open the Terminal in your CPanel. If you can not find the terminal, go to Manage Shell and Enable SSH Access. You can also do these steps using SSH but using the terminal is easy.

Copy the command to enter the virtual environment from your python app.

Run the copied command in the terminal and press enter to enter the virtual environment,

Install Django 4.0.4 by running the following command.pip install django==4.0.4

pymysql is required for using the MySQL database. Install it using pip. Here you will install any other modules required by your Django app.pip install pymysql

If your migrations are not complete, then make migrations.python manage.py makemigrations

Run migrate to create tables in the database.python manage.py migrate

Run collectstatic to move the static files to the static root folder specified in settings.python manage.py collectstatic

Run createsuperuser to ass a user to the Django admin panelpython manage.py createsuperuser

Finally, restart the python app.

Your Django app is deployed successfully. Open the url of your app to see if it is working fine. If you see the phusion passenger error page, you can find the error in the stderr.log file for debugging.

Reference: https://pythonfusion.com/deploy-django-on-shared-hosting/

Get user token and user info from IdentityServer4

IdentityServer4 is an OpenID Connect and OAuth 2.0 framework for ASP.NET Core.

Use the PasswordTokenRequest and RequestPasswordTokenAsync to get the access token. replace clientId and secret with the values from your Identity Server, then use the UserInfoRequest and pass your access token to GetUserInfoAsync to get the user claims:

public class TokenService
{
    private DiscoveryDocumentResponse _discDocument { get; set; }
    public TokenService()
    {
        using (var client = new HttpClient())
        {
            _discDocument = client.GetDiscoveryDocumentAsync("http://localhost:5000/.well-known/openid-configuration").Result;
        }
    }
    public async Task<TokenResponse> GetToken(string userName, string password)
    {
        using (var client = new HttpClient())
        {
            var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
            {
                Address = _discDocument.TokenEndpoint,
                ClientId = "clientId",
                ClientSecret = "secret",
                Scope = "openid",
                GrantType = "password",
                UserName = userName,
                Password = password
            });

            if (tokenResponse.IsError)
            {
                throw new Exception("Token Error");
            }
            return tokenResponse;
        }
    }

    public async Task<string> GetUserInfo(string accessToken)
    {
        using (var client = new HttpClient())
        {
            var response = await client.GetUserInfoAsync(new UserInfoRequest()
            {
                Address = _discDocument.UserInfoEndpoint,
                Token = accessToken
            });

            if (response.IsError)
            {
                throw new Exception("Invalid username or password");
            }
            return response.Raw;
        }
    }
}

New Raspberry Pi OS released

New Raspberry Pi OS Bullseye is released. It comes with lots of new features.

  * Based on Debian version 11 (bullseye)
  * Desktop components (lxpanel and all plugins, libfm, pcmanfm) now built against GTK+3
  * Applications (piwiz, pipanel, rc_gui, lxinput) now built against GTK+3
  * PiXflat GTK+3 theme updated with numerous changes to support the above
  * GTK+3 : toolbar icon size setting added
  * GTK+3 : ability to request client-side decoration on windows added
  * GTK+3 : setting for indent for frame labels in custom style added
  * mutter window manager used instead of openbox on devices with 2GB or more of RAM
  * mutter : title bar icon behaviour and appearance modified to match openbox
  * mutter : additional keyboard shortcuts added
  * mutter : various performance enhancements
  * mutter compatibility added to screen magnifier
  * Numerous changes to Appearance Settings application to support GTK+3 and mutter
  * Updater plugin added to lxpanel to detect and install software updates
  * File manager view options simplified to either list or icons, with separate menu option for thumbnails
  * New file manager toolbar icons
  * KMS used as default display driver
  * Modifications to HDMI audio output selection to support the above
  * xcompmgr enabled when openbox is running under KMS
  * New default camera subsystem based on libcamera
  * New camera demo applications (libcamera-still and libcamera-vid) have replaced raspistill and raspivid
  * Legacy camera subsystem removed from 64-bit RPi OS (still available on 32-bit)
  * Chromium upgraded to version 92.0.4515.98
  * VLC media player upgraded to version 3.0.16
  * Spurious drive removal warning after use of SD card copier removed
  * Bookshelf application now includes Custom PC magazine
  * Various translation updates - Italian, Korean, Polish, German, Armenian
  * Startup wizard now installs Japanese fonts if needed
  * Progress and information dialog boxes for lxpanel plugins now common to lxpanel, rather than in individual plugins
  * Icon handling code for lxpanel plugins now common to lxpanel
  * Package with 4K version of Raspberry Pi wallpaper added to Recommended Software
  * Python Games and Minecraft removed from Recommended Software - neither is compatible with bullseye
  * Bluetooth pairing and connection dialogs updated for compatibility with more devices
  * Bluetooth devices always disconnected before removal to speed up removal process
  * Bluetooth pairing dialog now only shows devices which offer services which are usable by Pi
  * Separate Bluetooth unpair dialog removed - unpair now an option for each individual device
  * Bug fix - mutter : header bar colours not updating when theme is changed
  * Bug fix - GTK+3 : tooltips being displayed incorrectly at bottom of screen
  * Bug fix - lxpanel : crash when using keyboard shortcut to enable magnifier when magnifier not installed
  * Bug fix - lxpanel : lockup in Bluetooth plugin when connecting to certain devices
  * Bug fix - lxpanel : discoverable mode icon could get out of sync with underlying Bluetooth system state
  * Bug fix - piwiz : missing cities in timezone list
  * Bug fix - piwiz : country-specific language packages not being installed
  * Bug fix - bookshelf : now waits for longer between packets before timing out
  * Bug fix - accented characters now displayed correctly in localisation dialogs
  * Raspberry Pi firmware e2bab29767e51c683a312df20014e3277275b8a6
  * Linux kernel 5.10.63

https://www.raspberrypi.com/software/operating-systems/

Use SharpPcap to construct and send UDP packet

SharpPcap is fully managed, cross platform (Windows, Mac, Linux) .NET library for capturing packets from live and file based devices. Now we want to send UPD packet using SharpPcap.

public int SendUdp(byte[] dgram, int bytes, IPEndPoint endPoint)
{
    //construct ethernet packet
    var ethernet = new EthernetPacket(PhysicalAddress.Parse("112233445566"), PhysicalAddress.Parse("665544332211"), EthernetType.IPv4);
    //construct local IPV4 packet
    var ipv4 = new IPv4Packet(IPAddress.Parse("192.168.0.4"), endPoint.Address);
    ethernet.PayloadPacket = ipv4;
    //construct UDP packet
    var udp = new UdpPacket(12345, (ushort)endPoint.Port);
    //add data in
    udp.PayloadData = dgram;
    ipv4.PayloadPacket = udp;

    _device.SendPacket(ethernet);
    return bytes;
}

SharpPcap can be found https://github.com/chmorgan/sharppcap.

How to change Raspberry Pi screen resolution in headless mode

My Raspberry Pi screen resolution being stuck at 800×600 with VNC access to a headless Raspberry PI with Raspberry Pi OS. I found that if I comment out the lines towards the end (as shown below) I was able to achieve a 1920×1080 resolution with my remote headless connection.

[pi4]
# Enable DRM VC4 V3D driver on top of the dispmanx display stack
#dtoverlay=vc4-fkms-v3d
#max_framebuffers=2

[all]
#dtoverlay=vc4-fkms-v3d