­

Default Papiergröße/PaperSize eines Druckers mit Powershell ändern

Stand vor der Herausforderung im Loginscript die Etikettengrößen für alle verbundenen Drucker neu zu setzen damit spätere Überraschungen weil Printservice das gerne verstellt/vergisst hintangehalten werden.

$c=@'
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
using Microsoft.Win32;

   public class PrinterSettings
   {
      [DllImport("kernel32.dll", EntryPoint = "GetLastError", SetLastError = false, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
      private static extern Int32 GetLastError();

      [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
      private static extern bool ClosePrinter(IntPtr hPrinter);

      [DllImport("winspool.Drv", EntryPoint="DocumentPropertiesA", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
      private static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, [MarshalAs(UnmanagedType.LPStr)] string pDeviceName, IntPtr pDevModeOutput, ref IntPtr pDevModeInput, int fMode);

      [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
      private static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr PrinterDefaults);

      [DllImport("winspool.drv", CharSet = CharSet.Ansi, SetLastError = true)]
      private static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr pPrinter, int Command);

      [StructLayout(LayoutKind.Sequential)]
      public struct PRINTER_INFO_9
      {
         public IntPtr pDevMode;
      }

      private const short CCDEVICENAME = 32;
      private const short CCFORMNAME = 32;
      [StructLayout(LayoutKind.Sequential)]
      public struct DEVMODE
      {
         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCDEVICENAME)]
         public string dmDeviceName;
         public short dmSpecVersion;
         public short dmDriverVersion;
         public short dmSize;
         public short dmDriverExtra;
         public int dmFields;
         public short dmOrientation;
         public short dmPaperSize;
         public short dmPaperLength;
         public short dmPaperWidth;
         public short dmScale;
         public short dmCopies;
         public short dmDefaultSource;
         public short dmPrintQuality;
         public short dmColor;
         public short dmDuplex;
         public short dmYResolution;
         public short dmTTOption;
         public short dmCollate;
         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCFORMNAME)]
         public string dmFormName;
         public short dmUnusedPadding;
         public short dmBitsPerPel;
         public int dmPelsWidth;
         public int dmPelsHeight;
         public int dmDisplayFlags;
         public int dmDisplayFrequency;
      }
      private const int PRINTER_ACCESS_USE = 0x8;
      private const int DM_OUT_BUFFER = 0x2;
      private const int DM_MODIFY = 0x8;
      private const int DM_PAPERSIZE = 0x2;

      public static bool ChangePaperSize(string sPrinterName, short nSize)
      {
         DEVMODE oDM;
         IntPtr pNativeDM = IntPtr.Zero;
         int nBytesNeeded = 0;
         IntPtr hPrinter = new System.IntPtr();
         PRINTER_INFO_9 oPrinterInfo;
         IntPtr pNativePrinterInfo = IntPtr.Zero;
         IntPtr pDummy=IntPtr.Zero;

         if (!OpenPrinter(sPrinterName, out hPrinter, IntPtr.Zero))
         {
            Console.WriteLine("OpenPrinter() failed with error "+Marshal.GetLastWin32Error()+", aborting.");
            return false;
         }

         nBytesNeeded=DocumentProperties(IntPtr.Zero,hPrinter,sPrinterName,IntPtr.Zero,ref pDummy,0);
         pNativeDM=Marshal.AllocHGlobal(nBytesNeeded);
         DocumentProperties(IntPtr.Zero,hPrinter,sPrinterName,pNativeDM,ref pDummy,DM_OUT_BUFFER);
         oDM = (DEVMODE)Marshal.PtrToStructure(pNativeDM, typeof(DEVMODE));
         Marshal.FreeHGlobal(pNativeDM);

         oDM.dmPaperSize = nSize;
         oDM.dmFields |= DM_PAPERSIZE;

         pNativeDM = Marshal.AllocHGlobal(Marshal.SizeOf(oDM));
         Marshal.StructureToPtr(oDM, pNativeDM, true);

         DocumentProperties(IntPtr.Zero,hPrinter,sPrinterName,pNativeDM,ref pDummy,DM_MODIFY);

         oPrinterInfo.pDevMode = pNativeDM;
         pNativePrinterInfo = Marshal.AllocHGlobal(Marshal.SizeOf(oPrinterInfo));
         Marshal.StructureToPtr(oPrinterInfo, pNativePrinterInfo, true);
         if (!SetPrinter(hPrinter, 9, pNativePrinterInfo, 0))
         {
            Console.WriteLine("SetPrinter() failed with error "+Marshal.GetLastWin32Error()+", aborting.");
            Cleanup(pNativeDM, pNativePrinterInfo, hPrinter);
            return false;
         }
         Cleanup(pNativeDM, pNativePrinterInfo, hPrinter);
         return true;
      }

      public static void Cleanup(IntPtr pNativeDM,IntPtr pNativePrinterInfo,IntPtr hPrinter)
      {
         try
         {
            Console.WriteLine("Memory and handle cleanup");
            if (hPrinter != IntPtr.Zero) ClosePrinter(hPrinter);
            if (pNativeDM != IntPtr.Zero) Marshal.FreeHGlobal(pNativeDM);
            if (pNativePrinterInfo != IntPtr.Zero) Marshal.FreeHGlobal(pNativePrinterInfo);
         }
         catch (Exception)
         {
         }
      }
   }
'@

Add-Type -TypeDefinition $c -ReferencedAssemblies System.Runtime.InteropServices,System.ComponentModel
Add-Type -AssemblyName System.Drawing

$printer="\\meinprintserver\meinverbundenerdrucker"

$ps=New-Object System.Drawing.Printing.PrinterSettings
$ps.PrinterName=$printer
$size=$ps.PaperSizes|? PaperName -like "*meine Etiketten*"

[PrinterSettings]::ChangePaperSize($printer,$size.RawKind)

Größtenteils von hier geklaut und vereinfacht/bereinigt – mit minimalem Fehlerhandling: https://www.codeproject.com/Articles/6899/Changing-printer-settings-using-C

Raspi als WLAN to Ethernet Bridge

Hatte einen Raspberry 3 übrig und einen Inverter ans LAN zu bringen…und das bitte headless.

Mit Raspberry Pi Imager Raspberry Pi OS Lite (64-bit, Bookworm) MicroSD erzeugen – da kann man WLAN, SSH & Co. schön vorkonfigurieren.

Wenn das Ding dann am WLAN online ist über NetworkManager dieses auf eth0 sharen (nur IPv4)


sudo nmcli con add con-name share-wlan type ethernet ifname eth0 ipv4.method shared ipv6.method ignore

Das macht aus eth0 einen Router/DHCP/DNS Server (mit dnsmasq) mit der Adresse 10.42.0.1/24

Was sich am LAN Interface so tut (wenn man MAC Adresse zwecks Fixierung wissen will)


sudo journalctl -u NetworkManager -f

DHCP Leases findet man in


sudo cat /var/lib/NetworkManager/dnsmasq-eth0.leases

Und statisch kann man sie dann hier definieren:


sudo micro /etc/NetworkManager/dnsmasq-shared.d/static-addresses

1 Host pro Zeile:
dhcp-host=mm:mm:mm:mm:mm:mm,10.42.0.xxx

Quelle: https://fedoramagazine.org/internet-connection-sharing-networkmanager/

SonOff ZBDongle-E auf Router flashen

Am besten alles als root durchführen (oder als Normaluser bis zum pip install und danach das source/flashing als root) da naturgemäß der Normaluser nicht direkt aufs Device schreiben darf.

Python virtual environment erzeugen und aktivieren (activate.fish für Fish Shell), Flasher installieren


python -m venv ./universal-silabs-flasher
source ./universal-silabs-flasher/bin/activate 
pip install universal-silabs-flasher

Firmware runterladen (das .gbl File)

https://github.com/itead/Sonoff_Zigbee_Dongle_Firmware/tree/master/Dongle-E/Router

Dongle anstecken, Devicenamen ermitteln (bei mir /dev/ttyACM0) und flashen (passenden Filenamen einsetzen naturgemäß)


ls /dev/serial/by-id

sudo universal-silabs-flasher --device /dev/ttyACM0 flash --firmware ./Z3RouterUSBDonlge_EZNet6.10.3_V1.0.0.gbl

Abstecken und dem Zigbee Netzwerk joinen 😀

Rückflash auf Coordinator ident – man muss nur –sonoff-reset als Parameter mitgeben.

Quelle: https://github.com/NabuCasa/universal-silabs-flasher

Windows 11 Hands Free/Freisprechtelefonie deaktivieren

Situation: Bluetooth Kopfhörer mit Telefoniefunktion aber ohne Mikrofon.

Problem 1: Registriert sich aber in Windows als Mikrofon und Hands-Free/Freisprechtelefoniegerät.

Problem 2: Webex läuft vollig Amok und Audio ist ungenießbar. Nur Webex. Alle anderen Audioquellen inklusive Teams, Zoom, etc. funktionieren einwandfrei.
[Update: Teams auch]

Lösung: Hands-Free/Freisprechtelefonie Bluetooth Service beim Kopfhörer Device deaktiveren:

shell:::{A8A91A66-3A7D-4424-8D24-04E180695C7A}

Klassisches Control Panel für Geräte und Drucker geht auf:

Beim gewünschten Gerät (bei mir Sony WH-CH520) “Freisprechtelefonie” (englisch: “Hands-free Telephony”) deaktivieren.

AppArmor und Vivaldi

Weiß nicht ob das ein KDE6 oder Vivaldi oder sonst irgendwo anders gelagertes Problem ist aber ich habe einen ganzen Haufen AppArmor Fehler bekommen wenn ich aus Vivaldi etwas heruntergeladenes starten wollte (wenn das Programm in /etc/apparmor.d/abstractions/open-some-applications enthalten ist) – alle rund um /run/user/meine_uid.

Im audit.log habe ich dann viele Einträge mit “failed name lookup – deleted entry” gefunden und bin darauf gestoßen.

Ist vermutlich völlig falsch und viel zu global aber das mediate_deleted aufs top level profil in /etc/apparmor.d/opt.vivaldi-stable gepfropft und schon hats funktioniert:

profile vivaldi-stable /opt/vivaldi/vivaldi-bin flags=(attach_disconnected,mediate_deleted) {

Yen Zeichen statt Backslash im Browser

Hatte unter Linux/in Vivaldi (vermutlich aber in allen Browsern) Yen Zeichen statt Backslashes auf Webseitenteilen die mit Courier New gearbeitet haben.

Lösung: ~/.config/fontconfig/fonts.conf (oder ~/.fonts.conf oder systemweit in /etc/fonts/local.conf einfügen) – hier Verbiegung auf “Hack Nerd Font Mono”:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">

<fontconfig>
 <!-- Replace Courier with a better-looking font -->
 <match target="pattern" name="family">
   <test name="family" qual="any"><string>Courier New</string></test>
   <edit name="family" mode="assign">
     <string>Hack Nerd Font Mono</string>
   </edit>
 </match>
</fontconfig>

Quellen:
https://wiki.archlinux.org/title/Font_configuration
https://askubuntu.com/questions/28419/how-to-most-elegantly-replace-courier-new-with-another-font-system-wide

WordPress an Fediverse andocken

Wer seinen Blog ans Fediverse andocken will: https://wordpress.org/plugins/activitypub/, aus irgendeinem Grund werden die Simple Permalinks (/?p=nnn) nicht akzeptiert – hab daher auf die Variante mit Postname als URL-Teil umgestellt und damit alle gespeicherten Permalinks vernichten müssen 🙁

Im Fediverse kann man auf alle Fälle jetzt über blog@roman.gallauner.at oder https://roman.gallauner.at/@blog folgen 🙂

Dreist geklaut von: https://vkc.sh/activitypub-activate/

Dependency aus DEB entfernen

Da der TP-Link Omada Controller auf ein JSVC (Java Daemon Starter) beharrt welches es unter Bullseye (und auch Bookworm) nicht gibt habe ich hier beschrieben wie man diese Dependency umgeht (und passende JSVC Version baut).

Problem: Man kann dann nicht mehr das OS updaten weil apt die fehlende Dependency bemeckert.

Lösung: Omada DEB umbauen: Als root in ein Verzeichnis auspacken (dpkg-deb -R), control File editieren und mit dpkg-deb -b das neue Paket bauen.

sudo su
mkdir omada
cp ./Omada_SDN_Controller_v5.13.30.8_linux_x64.deb ./omada
cd omada
mkdir tmp
dpkg-deb -R Omada_SDN_Controller_v5.13.30.8_linux_x64.deb tmp
cd tmp/DEBIAN
vi control
# hier dependency entfernen
# und version anpassen (ich hab ~roman angehängt)
cd ../..
dpkg-deb -b tmp Omada_SDN_Controller_v5.13.30.8~roman_linux_x64.deb

Danach ganz normal mit dpkg -i installieren.

Wireshark DNS A-Query Display Filter

Aufgabe: Nur Pakete anzeigen die eine DNS Query nach einem A-Record darstellen – ohne die Pakete mit der Response (die ja auch Queries sind halt mit einem Response-Segment).

dns && dns.flags.response==0 && dns.flags.opcode==0 && dns.qry.type==1  

dns = klar
response = ohne Response
opcode=query
type=A

Mastodon auf URL ungleich Domäne

Wer Mastodon Server so wie ich auf Server der nicht wie Domäne heißt betreibt (Server: social.galluner.fun, Domäne für User: gallauner.fun, also z.B. @roman – also WEB_DOMAIN != LOCAL_DOMAIN) aber auch nicht gleich die Domäne 1:1 auf Mastodon weiterleiten will weil vielleicht für andere Webseite benutzt funktionieren für mich bisher folgende Weiterleitungen (natürlich inklusive allem was im Originalrequest in der URI hinten dran stand):

# für federation
/.well-known/*
# für following von anderen Instanzen
/authorize_interaction
# falls jemand auf die Idee kommt Standard <domäne>/users/<user>
/users/*

Bin mir ziemlich sicher dass da noch was dazu kommt 🙂